Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Forkless state transition with upgradable WASM executor (FuelLabs#1716)
Closes FuelLabs#1547 ## Change overview The change adds the WASM version of the `fuel_core_executor::Executor` and the upgradable executor that works with native(std) and WASM(no-std) versions of the executor. Currently, it uses either the WASM or the std version. But in the follow-up PRs it will decide which version to use based on the block header. For now, this behavior is controlled by the "wasm-executor" feature. The CI runs tests two times, one with the WASM executor and the second with std, to verify that behaviors are the same. ## Details on how WASM integration is done The `fuel-core-upgradable-executor` uses the `wasmtime` library to compile and run the WASM bytecode. The `wastime::Engine` allows customization of how the `wastime::Module` compiles the bytecode. For simplicity, we are using the default configuration that works on all platforms for now, but later, it is possible to do more specific compilation. We are using `wastime::Linker` to add host functions that can be called from the WASM runtime via `extern "C"` API: ```rust #[link(wasm_import_module = "host_v0")] extern "C" { /// Returns the size of the value from the storage. pub(crate) fn storage_size_of_value( key_ptr: Ptr32<[u8]>, key_len: u32, column: u32, ) -> u64; /// Returns the value from the storage. pub(crate) fn storage_get( key_ptr: Ptr32<[u8]>, key_len: u32, column: u32, out_ptr: Ptr32Mut<[u8]>, out_len: u32, ) -> ReturnResult; } ``` The host functions are closures that capture data required for the work, such as the storage or relayer. Host functions have access to the `ExecutionState`, which accumulates intermediate data. Data is passed between the host and runtime through memory, where one side gives the pointer where data should be stored, and another side writes the memory using this pointer and the size. The host is not responsible for allocating and deallocating memory; the memory management is done within WASM runtime. Currently, when we return something from the runtime to the host, we "leak" it to avoid deallocation. It allows the host to read the value from the memory. Side changes: - The coinbase contract id is not a part of the `Config` anymore. It is part of the `Components` and seats along with the `gas_price` because it is defined by the block producer and can always be changed on the fly. - The usage of `anyhow::Error` in the `ExecutorError` was replaced by `String` because it doesn't implement `serde` ser/des. - The `ExecutionOptions`, `WriteOperation`, `ExecutionResult`, `TransactionExecutionResult`, `TransactionExecutionStatus`, `ExecutorError`, and `TransactionValidityError` support `serde` ser/des. - The `fuel-core-executor` only provides the `ExecutionInstance` without `Executor`. The logic of the `Executor` was moved to the `fuel-core-upgradable-executor`. --------- Co-authored-by: Voxelot <[email protected]>