Part I
Occasionally, Ethereum is likened to a singleton Virtual Machine. While this is accurate in certain respects; I believe it has further implications. Firstly, what is a singleton in a decentralized system? It is simply a collection of values on which a particular threshold of participants has reached agreement. A Virtual Machine represents a computational setting that is separated from the physical machine and from other environments.
A hypervisor enables the physical machine to be divided into numerous VMs. According to this characterization, a typical hypervisor is the web browser where webpages function as VMs. Another example of a hypervisor could be Ethereum, where each contract obtains its own distinct computational setting.
There are various disparities between the conventional web browser and Ethereum, yet one of the more intriguing aspects is how VMs converse and collaborate with one another. Web browsers do not facilitate direct interactions between VMs, while Ethereum, conversely, offers straightforward mechanisms for VM interaction; specifically the opcodes CALL, DELEGATECALL, CALLCODE, CREATE. In this article, we will delve into the question; What other regulations might exist? Can we create a generalized framework for VM interactions? And from this framework, can we reason about distributed hypervisors?
Much of this article will resemble ambient calculus, yet there are several notable distinctions between ambient calculus and the content presented here. The diagrams can be interpreted as bigraphs, but they should also be self-explanatory. Part I will outline the principles of ambients and then apply them to Ethereum. Part II will explore scaling in the context of ambients as defined by Part I.
What is an Ambient?
An ambient represents a confined area in which computation can take place. A boundary determines what is within and what lies outside of an ambient. For ambients, we refer to this boundary as a membrane. The region within an ambient forms a hierarchical namespace. Objects can reside inside an ambient, and these objects can be accessed via the namespace. There are three fundamental components in ambient calculus: Objects, Namespaces, and Messages.
Hierarchical Namespaces
One of the most recognized namespaces is the file system tree. Namespaces enable us to identify objects using paths or names. Here, namespaces possess the following characteristics:
- For every possible path, there exists either a null or an object
- At any juncture in the namespace, one can navigate both up and down. This is what hierarchical means.
- Every path retains an associated root. The root exclusively identifies the content for all paths beneath it. Think of the root as a pointer to the content of the path.
- Paths can be both read from and written to
- Messages may be dispatched along paths to objects
Object Types
What constitutes an object? It is simply a value. In practical computing, it is merely data. This data can be interpreted in various ways. Any object can be viewed as data. The pink circle signifies some data existing within the grey ambient.
Objects can also be perceived as ambients. This permits ambients to contain sub-ambients. Here, the orange and grey circles represent ambients.
Lastly, messages can also be regarded as objects. Messages are distinctive as they are identified as objects in transit or conceptualized as objects possessing velocity.
To summarize; Objects can take the form of the following types
Objects :: = Data Port Ambient Message
Messages
As previously mentioned, messages are objects that are currently in transit. Messages may be conveyed through a namespace and via channels. Messages possess specific traits established by the system’s message handler. These traits are not inherently part of the message, but as you will discover later, they simplify interacting with messages.
- To – The path leading to the message’s destination. This remains unchanged.
- From – The originator of the message. This remains constant.
- Type – The classification of the message. This is fixed.
- Data – The content of the message. This is immutable.
- Heading – The destination relative to its current position. If `Heading` is `null`, the message has reached its destination and will proceed no further. This is not explicitly encoded in the message but set by the system’s message handler. This is changeable.
- Direction – The direction in which the message is traveling. It can either be leaving the ambient or entering the ambient. This is mutable.
Message Types
Messages have the following classifications, each linked to commands used to transmit them.
Set(path, value) - Assigns a path a specified value
Get(path) - Retrieves the value at the specified path
SetRoot(path, root) - assigns the root of `path` to `root`
GetRoot(path) - Retrieves the root of the path
Call(path, data) - Transmits a message along the specified path
Connect(to, from, options) - establishes a channel between two paths.
Removing
It may not be immediately clear how to eliminate an ambient or other entities. To accomplish this, we utilize the `Set` and `SetRoot` messages.
The Set message assigns a value to a path. Setting a path to null equates to erasing the contents of that path. For instance Set(‘pinkAmbient’, null) Here, the pink ambient is set to null. Take note that the orange ambient remains intact.
The SetRoot message establishes the root of a path. If the root is defined as null, all path values below the root will turn null. For example CopyRoot(‘pinkAmbient’, null) will set the pink ambient’s root to null, which will also render the orange ambient null.
Certainly, if we were to execute something like SetRoot(‘a’, ‘pinkAmbientsRoot’), we would transfer the pink Ambient and all its contents to path “a”
Iterating through a Namespace.
Often, it is beneficial to traverse all the ambients within a specified namespace. One potential method would be to `get` each path in the namespace. However, the challenge lies in the fact that most namespaces are limitless. A more efficient approach would be to provide a dedicated iteration method. Let us introduce a message
Next(path) - Given a path, return the subsequent non-null path in the namespace.
This suggests that namespaces must maintain an order. Additionally, this gives us an excellent means of constructing more elaborate ambient operations such as merging multiple ambients. We also require this to facilitate type checking.
Membrane computing
The border of the ambient is its membrane. It has the ability to filter messages entering and exiting it. For example, if the grey ambient dispatches a Set(‘blueAmbient’, null) message to the path of the ‘blueAmbient’, it will traverse the membrane of the orange ambient. The orange ambient can determine whether to permit the message to pass through.
A Membrane API
Let’s explore a brief example of what programming ambients might resemble.
Ambient A aims to send a message to ambient B, but the message must pass through Ambient C. Since A is a sub-ambient of C, C holds authority over this message. Below is how an API for handling messages might appear. Assume we have a function named ‘onMessage’ that triggers whenever the ambient receives a message. Here’s an illustration of how C’s membrane could be structured.
/** * Permit any message to traverse the membrane except those from Ambient D * @method onMessage * @param message - the message that is departing the ambient * @returns Boolean */
function onMessage(message) { if(Message.sender != ”A” && Message.direction == ‘out’){ Message.heading = ‘D’ } }
C intercepts any messages emanating from the path ‘A’ that are outgoing. Rather than allowing the message to reach its intended destination, C redirects the message to location “D”. Take note of how C modifies the heading of the message. If C sets Message.heading to null, then the message would cease there. C can solely decide on the direction to forward the message or to halt it.
The capacity of ambients to filter and determine which messages can travel through them is crucial. This is referred to as Membrane computing. It will empower you to construct adaptable and easily combinable contracts, particularly regarding the management of sub-contracts.
Mapping ambients to Ethereum
Now that we grasp the fundamentals of ambients, let us apply them to one of our favored data structures, the Merkle Tree. To begin, you might have already realized that a contract in Ethereum resembles an ambient and that the namespace is supplied by the Merkle tree.
Namespace ::= the Merkle tree
This can be illustrated as follows
In Ethereum, each ambient possesses an address that is 20 bytes in length and appears as follows: 0x1158c3c9a70e85d8358972810ed984c8e6ffcf0f. Ethereum ambients have storage capabilities that enable them to retain arbitrary values indefinitely. The storage is accessed and manipulated with the SSTORE and SLOAD opcodes, which are equivalent to the set and get messages. Likewise, the Call command is analogous.
SetRoot, GetRoot, and Connect currently do not have counterparts in Ethereum. SetRoot and GetRoot would interact with the underlying Merkle.trie.
We are about to shift our focus from the present Ethereum to Ethereum + Ambients. Let’s assume the contract 0x1158c3c9a70e85d8358972810ed984c8e6ffcf0f assigns the value ‘doge’ at the address ‘coin’ which is 636f696e in hexadecimal. The address 0x1158c3c9a70e85d8358972810ed984c8e6ffcf0f/636f696e would then store the value ‘doge’. Additionally, ‘doge’ might also be viewed as code if a Call was initiated to that address.
Personal Accounts
Let’s take a personal Ethereum account as an illustration. For simplicity, we will refer to the account’s address as “accountA”, represented here as the grey ambient. This ambient would retain the essential signature validation logic as illustrated in the currency and crypto abstraction. If the user desired to impose spending restrictions on themselves, they could establish a “Savings Account” allowing only a specific amount of ether to be spent daily. Furthermore, the user might develop their own custom Name Registry or other financial applications. The hierarchical structure of the ambients enables you to construct an administrative “zone”. This approach allows for highly modular code, as the “savings account” and additional contracts don’t require embedded code to verify admin status or other credentials; this could be managed by the accountA’s ambient itself.
The following segment will delve into concepts concerning scalability in relation to ambients.
The fundamental notion of scalability is quite straightforward. Most strategies proposed thus far entail these characteristics:
- Distributing a segment of the state into a shard that is processed independently from the other shards
- Some form of cross validation; where a portion of a shard’s operations is verified by other shards, typically triggered by cross shard communication.
We also presuppose the existence of a Proof of Stake mechanism akin to Casper, which is implemented within a set of ambients. In conjunction with Casper, we have a currency ambient that monitors the ether balance of each account ambient. These ambients are aggregated within the system ambient. There might be numerous additional ambients within the system ambient, but for the time being, we will focus solely on these.
For the moment, we will simply presume that Casper operates effectively and generates the correct state for the “Ethereum Ambient”.
Sharding
If Ethereum achieves success, the frequency of transactions is expected to rise over time. Eventually, a significant volume of transactions would lead to an escalation in gas prices. Upon reaching a particular threshold determined by a Threshold function, the Casper ambient will generate a shard. It is important to note that only from the perspective of the Casper ambient is Ethereum sharded. Others perceive Ethereum as a unified namespace extending across numerous ambients.
A specific threshold is required to produce a shard in Casper. While this is not the emphasis of this article, we can envision some parameters upon which it might be based. It could utilize a gasPrice to transaction ratio, or perhaps a voting or bidding system, or a combination of all of them.
In addition to the Threshold function, we will assume the following about Casper:
- Anyone has the right to dispute a state transition.
- Validators are randomly allocated to shards, forming a validation group that executes Casper for that shard.
- Validators may be assigned to multiple shards
- New shards must be initially approved by all validators
- The total amount of bonds in a validation group for a shard should equate to the value of the shard itself.
Creation of Shards
- For now, we will proceed with the notion that new shards commence as an empty ambient. However, be aware that this might not always hold true—an exceptionally successful decentralized application could potentially compensate the Casper contract sufficiently to incentivize the validator to establish a shard for it. But for now, we regard it as empty.
- The initial step for the new shard ambient involves duplicating the system contracts to it. However, we don’t want an exact replica of the current system ambient, as that would include the present state. We desire an empty currency contract and an empty Casper contract, among others. To accomplish this, the Ethereum ambient must possess an “abstract” system ambient from which to perform the copying. We can envision the abstract system ambient containing a message handler that solely permitted messages aimed at copying it. It could look something like this:
function onMessage(message) { // disallows messages getting any subambient // roots from the abstract system if(message.type !== `getRoot` || message.headed !== '') { message = null // terminates the message } }
The new shard would send a `getRoot` request to the abstract system, subsequently utilizing `setRoot` internally to copy the abstract system’s namespace.
- A component of the threshold function might involve pledges from other ambients to transition to a new shard upon its creation. Once the new shard is established, all accounts that pledged to migrate are automatically transferred to the new shard. This occurs following the establishment of the system ambient. The accounts are also duplicated with the `CopyRoot` command.
- After they have been duplicated, their original addresses are substituted with a port (generated by the “Connect” command), creating a link to their new account on the new shard.
- The currency contract subsequently assigns the ether amount for the shard to the total of the accounts that committed to moving.
- Lastly, in the new shard’s currency contract, the account values are filled in with the amounts from the copied accounts.
Fractal chains?
The ultimate outcome will be that the upper-level ambients will no longer “perceive” the separate accounts contained within the new shard; rather, it will only recognize the total value of the accounts in the new shard ($82 as depicted in the diagram). Meanwhile, the currency contract of the new shard will monitor the individual accounts within the shard. This resembles a fractal in that a portion of the entire structure is represented in each segment of the framework.
Moreover, if anyone utilizes the former address of an ambient that has relocated, their messages will be redirected to them through the channels. There are some drawbacks to utilizing the channels; 1) it will incur higher expenses 2) there will be increased latency.
Financial Isolation – Counterfeiting Assaults
The shards are perceived as forming a hierarchy; each shard ambient monitors its accounts and the aggregate of the accounts in its child shards.
This establishes a robust assurance of the accuracy of account balances. No shard can fabricate counterfeit currency and transfer it to another shard. Furthermore, the security is cumulative. This implies that the more shards a message traverses, the stronger the assurance of its correctness. We are presuming that every validation group will verify the transaction passing through it. If a transaction moves from shard C to C.A.B, then shards C, C.A, and C.A.B will all verify the transaction and request a Merkle proof of the sender’s account from shard C. If the transaction is deemed invalid after the validators have approved it, then the validators in all three groups would forfeit their deposits. If accounts were deceived, they would first receive a refund from the validators’ deposits.
Let’s examine a long-range counterfeit assault. This scenario occurs when a validation group on a shard creates an account with an invalid amount of currency tied to it and then simply leaves it within the shard. If they attempt to transfer it out of the shard, the parent validation group will demand a full transaction log indicating how the accounts acquired their funds. At this point, the attack would fail unless the parent validation group was also compromised. In a long-range attack, the attackers await a compromise of the parent validation group. The best method to counter this is to hold each validation group accountable for the entire history of its shard and refrain from releasing the bonds to unbonded validators after multiple epochs. This would incentivize the current validation group to scrutinize the work of the preceding validation groups.
A method by which a validation group can promptly check the previous validation group’s work is to simply aggregate the transaction graph. We can visualize all messages that transfer currency as forming a directed graph. Given that we know the global amount of currency within the shard, a validation group just needs to tally the total amount the accounts possessed for each block in the preceding epoch and verify it against the established global sum.
To summarize, several characteristics that can bolster security include:
- Provide the Parent Validation group with an incentive to verify the work of its subordinates.
- Encourage validators to examine previous work
Validation Group Groups (Hierarchical Validation Groups)
Validators may need to stake a significantly high bond to engage in validation. The required amount of bond is determined by the target number of validators, which in turn is influenced by the number of shards present.
However, this presents a challenge because a higher number of validators would complicate coordinating a bribery attack on a shard, but conversely, Casper may become inefficient with a large number of validators. One possible solution to this issue is to have validators themselves made up of validation groups. The validation group would operate in a separate ambient on a different blockchain from Ethereum.
Within the validation group ambient, the work is further divided into smaller tasks. Each individual validator would be assigned several ambients from the shard that their validator group was allocated to. This should effectively enable even a modest device to partake in validation, increasing the total number of participants that the bribers would need to potentially coordinate with.
Channels Beyond the Ethereum Ambient
To accomplish this, the validation group would establish a new ambient linked by a channel to the validator group’s ambient. You may question how it is feasible to connect to an ambient outside of Ethereum. Nonetheless, it is quite straightforward.
Initially, there would only be a validators account managed by multisig on the Ethereum blockchain. Subsequently, the validators would establish their blockchain (depicted as an ambient) which would contain analogous system ambients and Casper ambients as Ethereum. After creation, the validator group would connect the two ambients via a channel. Any message entering or exiting the ports must receive agreement from all validators, hence the channel should also be safeguarded by a multisig. The code for the multisig would reside in the ports message handler. The channel could only be pursued by those operating both sets of ambients. Nodes running solely the Ethereum ambient would observe the channel but would be unable to pursue it.
This establishes a template that could be applicable elsewhere as it offers a universal way to link arbitrary ambients to the Ethereum blockchain. These ambients could represent the state of your personal computing device or a random data feed. Beyond the examples provided here, numerous other design patterns exist that make thinking in ambients advantageous. While there are still many gaps, ambients could serve as a valuable model for computational environments. Ambients introduce a novel dimension to Ethereum’s hypervisor. Quite literally as well. It permits contracts to be even more modular and provides an efficient way to create administrative domains and simulate many everyday scenarios.
NOTES and ISSUES
Here are some further considerations to reflect on.
- SetRoot would need to fail if the root was not present in the existing namespace. If SetRoot was utilized explicitly with the parent namespace (../
), then that tree would be duplicated to the namespace. If this occurred between shards, the tree would be serialized into a transaction. - Message
- All messages are presumed to be asynchronous. Messages may have a timeout.
- All messages have a response. The response needs to be recorded as a transaction on both the requesting shard and the responding shard.
- Blocks would require two components; incoming transactions and outgoing transactions.
- Capture and eliminate – The sibling ambient assigns a value to a path above another sibling with code to establish an ambient that removes all of its sub-ambients.
- Solution 1 any operation that could influence a sibling ambient must pass through its message handler
- Solution 2 an ambient could define a message handler for all internal messages that expressly prohibited certain types of messages.
- Solution 3 reintroduce capabilities as presented in ambient calculus