The Latest EVM: “Ethereum Is A Trust-Free Closure System”

Over the past two weeks, our lead C++ developer, Gavin Wood, and I have spent a lot of time meeting with the local Ethereum communities in San Francisco and Silicon Valley. We are so excited to see so much interest in our project that in just two months, we have weekly meetup groups with more than 30 participants each time, just like the Bitcoin meetup. We were very excited to see what happened. People in the community are taking it upon themselves to create educational videos, plan events, experiment with contracts, and some have even started writing their own implementations of Ethereum in node.js. But at the same time, we had the opportunity to take another look at the Ethereum protocol, see what is still incomplete, and agree on a number of changes that will probably be integrated into PoC 3.5 with only minimal changes. . client.

Transactions as closures

In ES1 and ES2, there was one very unintuitive feature in the MKTX opcode that allowed contracts to send transactions that triggered other contracts. MKTX is naturally expected to be like a function call, but it handles the entire transaction immediately and then continues. The rest of the code didn’t actually work this way in MKTX. Instead, execution of the call is deferred until the end. When MKTX is called, a new transaction is pushed to the top of the block’s transaction stack, and execution of the second transaction occurs when the first transaction finishes executing. begins. For example, this is what I would expect to work:

x = array() x[0] = “George” x[1] = My pub key

mktx(NAMECOIN,10^20,x,2)

if Contract.storage(NAMECOIN)[“george”] == MYPUBKEY: Registration successful = 1 else: Registration successful = 0

// Do something more…

Attempt to register “george” using the namecoin contract and check if the registration was successful using the EXTRO opcode. This should work. But of course not.

EVM3 (no longer ES3) fixes this issue. It takes the ideas of ES2 (creating the concept of reusable code, functions, and software libraries) and ES1 to improve simplicity by keeping code as a continuous set of instructions in state. This is achieved by keeping the same and merging the two. The concept of “message call”. A message invocation is an operation performed from within a contract that takes a destination address, an ether value, and data as input and calls the contract with that ether value and data, but unlike a transaction, it takes the data as output. return. . Therefore, there is also a new RETURN opcode that allows contract execution to return data.

This system makes the contract even stronger. Traditional types of contracts that execute certain data upon receipt of a message call may still exist. But now, other he two design patterns are also available. First, you can now create your own data feed contracts. For example, Bloomberg could publish a contract that pushes various asset prices and other market data, and include an API in that contract that returns internal data as long as at least one Finney is sent on an incoming message call. Prices should not be too high. Otherwise, per block he gets the data from the Bloomberg contract once, and the contract that provides a cheaper pass-through is more profitable. But even with fees representing perhaps a quarter of transaction fees, such a data feed business could eventually become very viable. The EXTRO opcode has been removed to facilitate this functionality. The contract is opaque from inside the system, but the Merkle tree is clearly visible from the outside.

Second, it is possible to create contracts that represent functionality. For example, you can use SHA256 contracts or ECMUL contracts to compute the respective functions. There’s one problem with this. 20 bytes may be a bit much to store the address for calling a particular function. However, this can be solved by creating a single “stdlib” contract containing hundreds of clauses for common functionality. The contract stores the address of this contract once as a variable and can access it many times after that simply as “x” (technically, “PUSH 0 MLOAD”). This is EVM3’s way of integrating the standard library concept, another key idea of ​​ES2.

ether and gas

Another important change is that instead of the contract paying for the performance of the contract, the transaction pays. When submitting a transaction, you must include the BASEFEE and the maximum number of steps you are willing to pay. At the beginning of the transaction execution, BASEFEE multiplied by maxsteps is immediately deducted from the balance. Next, a new counter called GAS is instantiated starting with the number of remaining steps. The transaction will then begin executing as before. Each step costs 1 GAS and the execution will either stop naturally, at which point the BASEFEE provided for all remaining GAS will be returned to the sender, or the execution will continue until he runs out of GAS will be done. In this case, all executions will be canceled, but full fees will still be payable.

This approach has two important advantages. First, miners can know in advance the maximum amount of GAS that will be consumed in a transaction. Second, and more importantly, contract makers have time to focus on making the contract “defensible” against dummy deals that attempt to sabotage the contract by forcing payment of fees. can be significantly shortened. For example, consider the old five-line namecoin.

stop if tx.value < block.basefee * 200: !contract.storage[tx.data[0]]or tx.data[0] = 100: Contract.Storage[tx.data[0]]= tx.data[1]

2 lines, unchecked. It’s much simpler. Focus on the logic, not the protocol details. The main weakness of this approach is that when you send a transaction to a contract, you need to calculate in advance how long it will take to execute (or at least set a reasonable limit that you are willing to pay) That’s it. Contracts have the power to put you in an endless loop, use up all your gas, and force you to pay your bills with no effect. However, this is probably not a problem. When you send a transaction to someone, you’re already implicitly trusting that person not to throw your money down the drain (or at least not complain if you throw it away), and the contract is Whether it is reasonable or not depends on the contract. The contract may also choose to include a flag indicating the amount of gas required (I recommend adding “PUSH 4 JMP” at the beginning of the running code here as a voluntary standard).

One important extension of this idea is the concept of message calls. In other words, when a contract makes a message call, that contract also specifies the amount of gas that the contract on the other end of the call must use. Similar to the top level, the receiving contract may finish executing in time or run out of gas, at which point execution returns to the start of the call, but gas is still consumed. Alternatively, the gas field can be zeroed out by contract. In that case, they would subcontract all the remaining gas. The main reason this is needed is so that automated contracts and human-controlled contracts can interact with each other. An automated contract cannot use a human-controlled contract without fully trusting the owner if the only option available is to call the contract using all remaining gas. This makes m-of-n data feed applications essentially infeasible. On the one hand, this introduces the weakness of requiring the execution engine to include the ability to return to a previous point in time (specifically, the start of a message call).

New terminology guide

With all the new concepts we’ve introduced, we’ve standardized some new terminology we use. I hope this helps resolve discussions on various topics.

  • external actor: A person or other entity that can connect to an Ethereum node, but is outside the Ethereum world. You can interact with Ethereum by depositing signed transactions and inspecting the blockchain and associated state. Have one (or more) unique accounts.
  • address: A 160-bit code used to identify your account.
  • account: Accounts have a unique balance and number of transactions that are maintained as part of the Ethereum state. They can be owned by external actors or by inherently autonomous objects (as identities) within Ethereum. If an account identifies autonomous objects, Ethereum also maintains storage state specific to that account. Each account has a single address that identifies it.
  • transaction: Data signed by an external actor. This represents a message or a new autonomous object. Transactions are recorded in each block of the blockchain.
  • autonomous object: A virtual object that exists only within the virtual state of Ethereum. has a unique address. Included only as a state of the VM’s storage component.
  • Storage conditions: Information specific to a particular autonomous object that is maintained between runs.
  • message: Data (as sets of bytes) and values ​​(designated as ethers) that are passed between two accounts in a fully trusted manner through deterministic operations on autonomous objects or cryptographically secure signatures of transactions.
  • message call: The act of passing messages from one account to another. If the destination account is an autonomous object, the VM is started in the state of that object and processes the message. If the message sender is an autonomous object, the call passes the data returned from her VM operation.
  • gas: Basic network cost unit. Fees are paid only in Ether (as of PoC-3.5) and are freely convertible to and from gas as needed. Gas does not exist outside of the internal Ethereum calculation engine. That price is set by transactions, and miners are free to ignore transactions where the gas price is too low.

long term perspective

We will soon release a complete formal specification for the above changes, including a new version of the whitepaper that takes all of these changes into account, and a new version of the client that implements them. There may be further changes to EVM after that, but changes to ETH-HLL will be minimal. Therefore, creating contracts in ETH-HLL is completely safe and will continue to work even if the language changes.

We still don’t have a final idea on how to handle mandatory fees. The current stopgap approach has a block limit of 1000000 operations (i.e. his GAS consumed) per block. Economically, mandatory fees and mandatory block limits are essentially equivalent. However, block limits are a bit more general, allowing you to theoretically get a limited number of transactions for free. A blog post summarizing the latest thinking on pricing issues will be posted soon. Stack traces, another idea I had in mind, may also be implemented later.

In the long term, perhaps beyond Ethereum 1.0, the holy grail is to attack the last two “essential” parts of the system and see if they can also be converted into contracts: Ether and ECDSA. In such a system, Ether remains a privileged currency within the system. The current idea is to pre-mine the Ether contract to index “1” so that it requires 19 fewer bytes to use it. However, the execution engine is simpler because the concept of currency is removed. Instead, everything becomes about contracts and message calls. Another interesting advantage is that this allows us to separate Aether and his ECDSA, making Aether optionally quantum resistant. If you prefer, you can also create an Ether account using an NTRU or Lamport contract instead. However, the drawback is that proof of stake is not possible without a unique currency at the protocol level. That might be a good reason not to go in this direction.

Related Article

0 Comments

Leave a Comment