Things to decide for phase 2
- Virtual machine
- Decide definitely doing EWASM (default)?
- What does the FFI look like?
- How is code serialized? Doing any fancy AST merklization?
- Cross-shard message structure.
- Here is a possible design with some details: https://ethresear.ch/t/phase-2-pre-spec-cross-shard-mechanics/4970
- Mechanics: cross-shard messages, contract yanking
- Decide we're not doing layer 1 synchronous cross-shard calls and leaving it to layer 2 (default) or do something else?
- Contract storage structure
- Linear code, linear storage, max combined size 12kb (to fit a yank into a single block) ?
- Contract addressing
- Default approach: CREATE2 for everything
- Possible alternative: optional sequential addresses for contract creation (rationale: with much smaller addresses, using libraries may be much more efficient)
- Access lists
- Total (must specify all addresses) vs partial (can specify prefixes) vs none at all
- Account abstraction model
- https://ethresear.ch/t/tradeoffs-in-account-abstraction-proposals/263
- https://ethresear.ch/t/maximally-simple-account-abstraction-without-gas-refunds/5007
- Major tradeoff: do we abstract nonces or not? That is, do we keep in-protocol nonce-checking of transactions or do we leave that to the contract code?
- Rationale for abstraction: simpler protocol; also prevents complexities arising from interaction between multiple users attempting withdrawal from one contract at the same time, where N-1 of them must resend with a higher nonce, also removes need to worry about replay prevention in the hibernation/waking cases in-protocol
- Rationale for non-abstraction: potential for more guarantees for network peers; preserves invariant that any given txhash appears only once in a chain
- Gas cost model
- Computation
- Data
- Access: charge per Merkle branch, don't charge if the Merkle branch has already been used in that block?
- Adopt fee market reform proposal from https://ethresear.ch/t/draft-position-paper-on-resource-pricing/2838 ?
- Storage fees
- https://ethresear.ch/t/a-minimal-state-execution-proposal/4445
- https://ethresear.ch/t/state-fees-formerly-state-rent-pre-eip-proposal-version-3/4996
- Hibernation and waking: https://ethresear.ch/t/cross-shard-receipt-and-hibernation-waking-anti-double-spending/4748 (referenced in https://ethresear.ch/t/phase-2-pre-spec-cross-shard-mechanics/4970)
- Decide on fixed fee?
- Note: even with a fixed fee, there is an implied cap on the storage size of each shard because of gas limits (at least, this is if we charge a gas cost that scales linearly with TTL extension)
What is the above missing?
I think this looks like a great list to start the discussion!
We have already started collecting ideas regarding ewasm here: https://github.com/ewasm/design/issues. I think some of these can be applied to the "Eth1.x" version of ewasm, but definitely don't see any reason they couldn't be applied to Eth2.0.
Regarding contract addressing:
- Possible alternative: optional sequential addresses for contract creation (rationale: with much smaller addresses, using libraries may be much more efficient)
We are exploring "contract linking" in its various forms as an alternative to this. That would eliminate "calls" needed to interact with libraries.
That would eliminate "calls" needed to interact with libraries.
Interesting! Though this still seems like it would not remove the need for blocks with transactions using that library to need to provide a merkle branch of the code to prove execution statelessly.
More broadly, I feel like one question that the EWASM team could help a lot by figuring out is, what kind of an interface would EWASM have with respect to the wider system? The simplest possible interface for example would be the ability to run execute_code(code, data), with the ability to escape internally with call(address, data) and ffi(data); then everything else (eg. providing environment variables, reading/writing storage) can be done at the outside level with the FFI.
If the interface is settled, then work on everything-outside-the-VM and everything-inside-the-VM could just proceed separately.
How far away is the EWASM team from being able to lay down an opinion on this?
what kind of an interface would EWASM have with respect to the wider system? The simplest possible interface for example would be the ability to run execute_code(code, data), with the ability to escape internally with call(address, data) and ffi(data)
It just struck me that this configuration feels a lot like an operating system with a kernel mode and a user mode--where, in this case, Ewasm contract code is running in user mode and has one or more system calls it can make which switch the context back to "kernel" (EEI) mode. I wonder if we shouldn't take design cues from existing operating system design. @axic, @jakelang and others know way more about this topic than I do.
The simplest possible interface for example would be the ability to run execute_code(code, data), with the ability to escape internally with call(address, data) and ffi(data)
I am not sure I understand what these functions would do. Is execute_code an API for the client to execute contract code? Is it an API exposed to contracts?
From my personal perspective I have two goals:
- reduce the opportunity for contracts to deal with raw code
- make use of webassembly imports/exports and reduce the need to come up with arbitrary ABI encoding
The main reason for 1) is to simplify the points in the system where code validation must occur.
While I would wish to create really nice high level abstractions with 2), this may be limited by execution overhead that we must also take into consideration.
Did a semi-long write up of a potential async interface here: https://github.com/ewasm/design/issues/185
what kind of an interface would EWASM have with respect to the wider system? The simplest possible interface for example would be the ability to run execute_code(code, data), with the ability to escape internally with call(address, data) and ffi(data); then everything else (eg. providing environment variables, reading/writing storage) can be done at the outside level with the FFI.
Ewasm is just wasm (it works on any generic wasm engine). It looks like this:
Memory = new WebAssembly.Memory({initial: 10}) // 10 pages at 64kb each
Gas = new WebAssembly.Global({value:'i32', mutable:true}, 3141590);
Imports = { Memory, "ethereum": { Gas, callDataCopy, storageLoad, storageWrite, callToContract, returnDataCopy, finish, revert, useGas } }
code = db.get(address) // code has useGas statements that were injected at deployment time
contractModule = new WebAssembly.instantiate(code, imports).
contractModule.run("main") // execute_code(code, data)
// Ewasm host functions implemented here
function useGas(amount) {
Gas.value -= amount
if (Gas.value < 0) throw "OOG"
}
function callDataCopy(offset) {
Memory.set("0x00000calldata", offset)
}
function finish(resultOffset) {
console.log("contract call returned data:", Memory.get(resultOffset))
}
function storageLoad() {}
function callToContract() {} // call(address, data)
...
If the interface is settled, then work on everything-outside-the-VM and everything-inside-the-VM could just proceed separately. How far away is the EWASM team from being able to lay down an opinion on this?
Well the wasm interface is settled (for wasm 1.0). You can do ret = ffi(data), where ret and data are one of four types: i32, i64, f32, f64. In Ewasm (a subset of wasm) you're restricted to i32 and i64. Also, you can pass multiple arguments ffi(data1, data2, ...), but only one return value (wasm 2.0 might support multiple return values). If you want to pass an argument that is more than 32 bits of data, you pass a pointer to a memory location.
The Ewasm interface is the set of FFI functions ("host functions" in webassembly docs). For Ewasm 1.0, the host functions basically mirror those EVM opcodes that can't be reduced to pure wasm instructions. There's like 32 of them, so the Ewasm 1.0 interface is 32 host functions minus a couple redundancies (e.g. CALLDATACOPY => callDataCopy(resultOffset, dataOffset, size) and CALLDATALOAD => callDataCopy(resultOffset, dataOffset, 32)).
For Ewasm 2.0, the set of FFI/host-functions would depend on how phase 2 works. Personally, I consider the host functions as outside-the-VM (i.e. host functions are a part of the client, not a part of the VM).
Then what falls inside-the-VM that can proceed separately? Mainly just making code run faster (optimizing interpreters, making compilers robust) and making sure that code terminates (metering, and reducing the slowdown between metered and unmetered code).
A relatively minor issue, but learning lessons from Eth 1, how about a mandatory checksum for Eth 2.0 addresses?
EIP55 retro-fitted a checksum format to Eth 1, but this is still not fully adopted/enforced. Including a checksum as part of the protocol would be a step forward for user safety. I realise that the Eth 2 address format is not yet specified, this notwithstanding.
I would argue that Eth2 validator addresses at phase 0 should have mandatory checksums, and the checksum should be verified by the Eth1 deposit contract.