Skip to content

Instantly share code, notes, and snippets.

@axic
Created June 8, 2018 21:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save axic/cc094cf1c339d3fbd5351687ea475cdc to your computer and use it in GitHub Desktop.
Save axic/cc094cf1c339d3fbd5351687ea475cdc to your computer and use it in GitHub Desktop.
eWASM and Backwards Compatibility
=================================
High Level
----------
Pros:
- ease transition: legacy EVM compatible with eWASM
Cons:
- EVM is awkward -- maintaining design decisions and invariants of EVM may hinder eWASM
- shards are eWASM only -- opportunity for fresh start
- if we set a standard of backwards compatibility, we will make it even harder for future VMs to break it
Technical Details (ehildenb before call)
----------------------------------------
A client that supports both EVM and ewasm will need to support all the cruft of EVM as well as whatever we decide for ewasm.
This means that things like `eei.evm_call`, `eei.evm_callcode`, `eei.evm_delegatecall`, etc... will all need to be supported by the EEI either way.
The question is whether these particular methods will be exposed to ewasm, or if different (cleaner) methods will be exposed instead, something like `eei.call`.
We could think of several ways to do it differently, for instance:
- `eei.call` could have a flag indicating whether to do the call in static mode or not instead of a separate op.
- Similarly, could have a flag whether to execute with callee permissions or our permissions.
- Gives us 4 different calls, lets contracts decide as they need.
- Con: harder to tell statically which type of call it is (similar issue people have with dynamic jump destinations).
Another example: suppose we add a method `eei.transferFunds`, which just transfers some funds to another account (and nothing else).
There is currently no analog in EVM, must first `CREATE` an account with the funds to be transferred, which `SELFDESTRUCT`s to the target account.
Could add a primitive like this in the EEI, and allow ewasm contracts to call it directly (already makes ewasm more useful than EVM).
Simply do not expose this method as an EVM opcode.
Opens the question about having different subsets of the EEI exposed to different VMs, each VM could decide which ones to support.
Would enable experimenting with different EEI methods on new shards (think ewasm 1.1, ewasm 1.2).
This allows us to run EVM and ewasm on the same chain, but does not give us step-for-step compatibility between the two.
Step-for-step compatibility is only useful if we want to completely deprecate the EVM.
What's the point of deprecating it if we inherit all of its complexity in the new VM?
evm2wasm is still useful to do a "best effort" translation of the test set as a *starting* point for the ewasm test set (*not* as a finishing point).
### chfast's points
- Try to have the lowest possible level EEI functions.
For example, if the cast of a CALL is complex because it has to load the account information first from database, require the contract to load the account "manually" first.
I'm not sure we can design it this way, but we should try.
- I would keep the compatibility with EVM 1 by having EEI functions like transferFunds(). But we can allow them to be used only in evm2wasm mode.
E.g. implementing SELFDESTRUCT in evm2wasm:
```
SELFDESTUCT(beneficiary):
if beneficiary != this.address:
transferFunds(beneficiary, this.balance)
this.selfdestruct()
```
### ehildenb response
I agree on having more "primitive building blocks" in the eei, and allowing the various VMs to string them together how needed.
We could even then have the eei functions reuse the other various eei functions functionality at the client level.
What I'm not sure about is whether we should "allow them to be used only in evm2wasm mode".
Instead I think we should think of it as "this is the entire eei, and each VM will have some way that it maps its opcodes to sequences of these methods".
For example (taking example from above):
- The one above has EVM command `SELFDESTRUCT` map to the eei sequence `transferFunds ; selfdestruct` for the EVM
- But you could imagine that in ewasm we just expose `eei.selfdestruct` and `eei.transferfunds`
- you can choose to either *just* selfdestruct (just a call to `eei.selfdestruct`), or
- to *just* transfer funds, or
- to do both (like how EVM does it).
- Similarly, we could simply not expose the `eei.callcode` opcode to the ewasm contracts, because no one wants to use it anyway.
Loose Notes From Call
=====================
### Smaller Building Block Functions
- Want to break EEI functions into "building block" functions whenever this abstraction is convenient for supporting different execution (EVM, eWASM, evm2wasm, etc.).
- Two main components, breaking of gas costs and breaking of functionality.
- If we break into multiple smaller building blocks, then maybe the metering has to recognize something like:
- "Even though I see two calls `eei.transferFunds` and `eei.selfdestruct`, I should just charge for `SELFDESTRUCT`".
- @axic: I don't think that would be a good idea given the complexity, rather each of those low level calls would need to have their own gas rules defined, which the metering contract could follow.
- (axic): (if possible) I would like to avoid having privileged methods which are only available to evm2wasm. The EEI should exactly match which methods are available to contracts. (after some clarification regarding the newfound use of the term EEI, this sentiment has changed)
### Gas Costs
- Maintain gas costs? [clarification needed]
- Othogonal issues because the gas costs may just be passed up using the `eei.useGas` method. [clarification needed]
### Metering at Deploytime?
- (everett/paul) it doesn't make sense to store metered code
- if gas costs change, then all stored code would need to be re-metererd (first unmetered or in-place, see https://github.com/ewasm/design/issues/97 )
- (everett) metering is an optimization, and clients could choose different styles (e.g. full static-analysis rather than just basic-block metering)
- (paul): Shouldn't privilege any any execution method. In 5 years, there may be hardware cores monitoring gas use.
- (@axic): I think if we introduce the rule that `eei.useGas` statements are invalid in submitted bytecode, then we have the flexibility to store metered or unmetered bytecode, since even if it stored in a metering way (e.g. with `useGas` statements), it could still be removed cleanly prior to other transformations.
- (casey): my question is about whether all gas logic/charging can be done by injected metering, so no gas charging is done inside interface methods. this way EEI implementors do not have to worry about gas at all (gas deduction is handled entirely by the metering injection contract).
- (everett): probably not. interface methods will have to do gas charging which depends on the state
- (axic): its fine if clients do not store the metered code. the deploy-time metering is still run and charged at deployment time, but clients do not have to store the metered code, they can store the un-metered code (or store un-metered in the state, but keep metered one in the local database of the client).
- (everett/paul) Agreed. Good to have canonical metering, and practical for clients to use for now.
### Naming conventions proposed in call (mostly ehildenb, axic):
- eei: Ethereum Environment Interface: abstract state updates on the blockchain that can be triggered by contracts. Abstracted to all execution (EVM, eWASM, evm2ewasm, etc.), not just eWASM.
- evmc: Ethereum VM Codes: status codes reported back by execution engines to the client to indicate how execution ended.
- ewasm interface: subset of eei exposed as imported module for use by ewasm VM.
- evm interface: subset of eei exposed to EVM VMs for implementing Ethereum state-change opcodes.
- evm2wasm interface: subset of eei exposed to contracts transpiled with evm2wasm (largely the same as ewasm interface, except for places where we decide to break backwards compatibilty).
### API (mostly axic)
- (axic) I could agree to remove some of the "obsolete" methods from the "ewasm interface", but keep them available when running a translated code by evm2wasm
- Cleaned up API - potential next steps (@axic):
- clean up call related methods
- remove selfdestruct
- But keeping callcode + selfdestruct for evm2wasm.
- Potential API (@axic):
- `eei.call(flags, destination, ...)` where `flags`: 1 (static_mode), 2 (take_parent_call_value), 4 (take_parent_state)
Notes After Call
================
### Outcome of the call (mostly by axic, some responses by ehildenb)
1. Need to name the abstract interface of the client (proposed by @everett: EEI)
1. Need to name the methods contract can import (temporary called: "ewasm interface"; @everett: other examples would be "evm interface", "evm2wasm interface")
1. Seems to be no opposition to
- a. cleaning up the current "ewasm interface" (e.g. removing callcode, perhaps selfdestruct, and making the design more consistent)
- b. still keep some of the removed ones (callcode/selfdestruct) only to be used by bytecode output by evm2wasm (and to be used by existing EVM implementations)
1. Should keep the "sentinel contract" to validate, insert a type of metering (the "consensus metering" or "canonical metering") and use all that as a cost to deployment.
1. Need to discuss what other ways are there to charge the cost of metering (what happens if it is done at runtime, can we charge every time?)
1. Need decision on proposal by @axic: reject usegas statements in incoming bytecode (can be handled by saying that "eei.useGas is not in the ewasm interface").
1. Need to document what other metering options are there and why would clients choose to not use the "canonical metering" (performance).
1. Need decision on storing metering or "un-metered" bytecode in the state.
In either case the client can decide to keep the other version for its own purposes. e.g. one client may do its own metering, while others may rely on the "canonical metering" for every execution.
1. Need to discuss whether the "canonical metering" should use the more primitive method of inserting a usegas statement for each instruction.
1. Need to discuss the role of testing the "ewasm interface" - a code generator with hera could be used to generate a starting test set, in which (un)expected deviations after hera changes could be detected.
1. Need to discuss the role of evm2wasm more.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment