design
design copied to clipboard
WASM modules as libraries
EVM doesn't have a real concept of libraries, rather it was added retroactively with DELEGATECALL and that Solidity ensures a contract defined as a library cannot make use of SSTORE
/SLOAD
. The VM however still needs to consider it the same as other contracts and ensure that proper rollback mechanism is in place.
A WASM code is called a module
, which defines the memory needed and has one or more functions
.
One of the premises of using WASM is that we wouldn't need precompiled contracts given the speed loss caused by the bytecode is insignificant compared to EVM. Not using precompiles can also lead to a lot of code duplication.
I think it could be useful supporting a way to store WASM modules on the blockchain, which could be loaded by contracts during the linking stage. Perhaps these modules would be special contracts, which are not meant to be executed.
Ideas welcome how this could fit into the blockchain model we have.
Another way we could use it is to replace or supplement CALLs, for(import "contract_address" "method"). This could also offer clients an opportunity to populate a cache of contracts based on the import table.
So with https://github.com/WebAssembly/design/blob/master/DynamicLinking.md we could make this a reality.
Basically, we could define that an import where the module is an Ethereum address, will load the code from that address, and look up the export table to find the referenced method.
e.g.
- a library (deployed at
0x4200000000000000000000000000000000000001
) has an exported methodsha3
:
(module
(export "sha3" $sha3)
(func $sha3
(param $offset i32)
(param $length i32)
(param $outputOffset i32)
..
)
)
- a contract can use it in the following manner:
(module
(import $sha3 "0x4200000000000000000000000000000000000001" "sha3" (param i32 i32 i32))
)
The important bit is that this will run as a single WebAssembly instance and we don't need to do CALL
based message passing.
Appropriate cost must be defined. When the module is loaded, each import statement must cost gas in order to avoid DoS attacks (where a contract imports a lot of addresses, each of them must be looked up).
Additionally, to have pure libraries, we can define a library as a module, which doesn't have a symbol exported as "main". Every contract must have "main" exported as the entry point.
This avoids the problem of having libraries which can receive normal contract invocations.
Additionally, to have pure libraries, we can define a library as a module, which doesn't have a symbol exported as "main". Every contract must have "main" exported as the entry point.
still not clear on why having main would be a problem. If main was imported and ran it would run in the current environment. So that storage from the imported contract would not be mutable from from a thirdparty contract. Also allowing main
would give us a way to easily use EVM1 contracts as libs.
The problem is not when importing library:main into the contract, but it enabled the library to be executed as a contract as not only loaded as a module.
I don't think eWASM contracts + EVM1 libraries can be mixed.
but it enabled the library to be executed as a contract as not only loaded as a module
Yeah but you still can do that. For example.
(func $main_redux
(call $main)
)
(export $main)
(export $main_redux)
I don't think you can outright disable main
If we define a contract as an eWASM module, which has an export called main
and no other exports; and we define a library as an eWASM module, which doesn't have an export called main
, but has at least one other export,then:
- when executing a CALL, the destination account and its code is loaded, and if it is eWASM, it follows the contract semantics. e.g., no main - failure ("not a contract")
- if a contract (which passed the above criteria) is loaded, it can import other libraries
If a destination account has code and cannot be executed, then it cannot receive financial value transfers either and there's no way it can access its account storage.
A new idea: offer deploy time linking.
Following the above semantics we do run into a problem: should this dynamic linking be done at runtime? If so, how do we charge for that?
A possible solution is to do linking at deploy time and this linking would be done by a metered process, such as the sentinel contract.
One downside is that the state could blow up (linking in large libraries). Therefore it seems to make more sense that the state contains the unlinked binary, but during deployment we do charge for linking. Then clients can make a decision (and choose whichever is better for them): a) keep the linked binary in the local database b) link at runtime
Second caveat: linking with the library at the time of deployment has a benefit, that no versioning is needed, but it closes the door from upgradeable libraries.
I think that deployed contracts could potentially have a flag in the magic number which specifies they are a "library" module. This would override the normal ECI properties and allow them to specify different entry points for library functionality. The EEI could then have a "callLibrary" method, which tells the VM to instantiate that library module and call the specified export, or trap if it doesn't exist. Unsure how this would work with the linear memories though.
On Mon, Jul 16, 2018, 11:43 Alex Beregszaszi [email protected] wrote:
A new idea: offer deploy time linking.
Following the above semantics https://github.com/ewasm/design/issues/17#issuecomment-242222472 we do run into a problem: should this dynamic linking be done at runtime? If so, how do we charge for that?
A possible solution is to do linking at deploy time and this linking would be done by a metered process, such as the sentinel contract.
One downside is that the state could blow up (linking in large libraries). Therefore it seems to make more sense that the state contains the unlinked binary, but during deployment we do charge for linking. Then clients can make a decision (and choose whichever is better for them): a) keep the linked binary in the local database b) link at runtime
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ewasm/design/issues/17#issuecomment-405291574, or mute the thread https://github.com/notifications/unsubscribe-auth/AHEQh_eTP_q-vZT1dbBuGPfJShTf9Xvpks5uHLStgaJpZM4JXCfe .
Good ideas and good points. I just want to write down the design problem.
ewasm contracts will be Wasm modules. So we have the option to use Wasm's import/export infrastructure to allow contracts to link against each other's memory, globals, funcs, and table. Import/export linking is done at module instantiation-time, so ewasm contracts can be linked at contract deploy-time or contract call-time. Using Wasm linking may allow us to eliminate some (or all) *CALL
opcodes. There are trade-offs in simplicity, bloat, upgradable libraries, run-time efficiency, maybe others.
This design problem affects things like metering, account abstraction, maybe others.
This is a major design problem. It would be useful to list possibilities, with description/pros/cons of each.
Another possibility for calling functions in other modules: Using call_indirect
.
Brief description:
- A contract can import the table and use
call_indirect
to call functions based on their index in that table. - The Wasm embedding API allows creating, growing, and writing a table. So the Ethereum node can create a custom table with everything a contract requests.
- Importing occurs at module instantiation-time, so it can be done at contract deploy-time or call-time. In fact, a module instance's table address can be updated any time, but this destroys invariants from validation.
Pros:
- Wasm already supports importing tables (and importing functions).
- If importing occurs at call-time, then there is no need to modify the Wasm binary with names of the imported functions, like we would have to do if we allow importing arbitrary functions at call-time.
Cons:
- Type-checking at run-time. But the Ethereum node may also perform this type-checking to prevent traps.
- Static analysis is difficult -- since the function called depends on an index which may only be known at run-time.
Relocations specification: https://github.com/webassembly/tool-conventions/blob/master/Linking.md
It also touches on the producers section.