This site is in maintenance mode. Features may be unstable.
Warning! On-chain actions are not disabled.
Patract Hub's treasury proposal for Europa (sandbox) v0.2
Github: https://github.com/patractlabs/europa Europa is a sandbox environment that runs `FRAME Contracts pallet`, and it is also a framework that provides a sandbox environment for Substrate Runtime. Therefore, developers can use the executable files compiled by Europa to debug the `ink!` contract, and they can also use the library provided by Europa as a framework to implement a sandbox environment for the Runtime in their Substrate project. Europa provides the minimal service components that support Runtime to package and execute transactions normally. Therefore, although Europa can be regarded as a chain node, this node has no consensus system, no network connection, and only retains the Runtime executor, RPC and some other necessary services. In addition, as a WASM contract running sandbox, we modified `FRAME Contracts pallet` to provide more detailed debugging information, including contract execution process information (in `FRAME Contracts` layer) and WASM execution information (in WASMI execution layer). In order to facilitate debugging and add new features, Europa removed all WASM components in Substrate, so any `std` code can be added to the entire system of Europa to meet experimental requirements. ## Europa's Future development plans v0.1: Have an independent runtime environment to facilitate more subsequent expansion directions. v0.2: Modify at `FRAME Contracts pallet` level to provide more information。 v0.3: Improve the development experience, strengthen collaboration with other tools, and extend the sandbox to be compatible with other runtime modules。 We submitted a proposal for [Europa v0.1](https://polkadot.polkassembly.io/post/102) two months ago, and wrote the [treasury report](https://polkadot.polkassembly.io/post/166). In v0.1, Europa has completed the basic framework of the Runtime sandbox, removed unnecessary modules related to the chain, removed the functional components of WASM, and provided many necessary tool components as a sandbox. ## Detailed design of Europa v0.2 ### 1. Fork `FRAME Contracts Pallet` to provide more debug information Although `FRAME Contracts pallet` has been iterative, its error messages are not friendly to contract development. Although in Substrate's [Commit #6773](https://github.com/paritytech/substrate/pull/6773) and [Commit #7017](https://github.com/paritytech/substrate/pull/7017), the error handling of contracts has been improved a lot, but these error messages are mainly concentrated in the `FRAME Contracts pallet` layer, missing a lot of error information about WASM execution layer. For example, WASM execution crash errors or out-of-bounds errors do not have detailed information. For example, this part of the code in Substrate [`frame/contracts/src/wasm/runtime.rs:LL367`](https://github.com/paritytech/ substrate/blob/master/frame/contracts/src/wasm/runtime.rs#L367) ```rust // Any other kind of a trap should result in a failure. Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) => Err(Error::::ContractTrapped)? } } ``` As shown above, because `sp_sandbox::Error` only provides three types of errors, and these error types do not contain more detailed reasons for this error. For example, developers only know that it is an execution error of `Execution` and an out of bounds error of `OutOfBounds`, And all have been converted into `ContractTrapped`. Therefore, the reason for the internal crash of the contract cannot be known to the outside world. In our fork development of `FRAME Contracts pallet`, we will supplement and perfect this short board. In the execution environment of the contract `wasmi`, all error messages are discarded. For example, in this part of the code in Substrate [`primitives/sandbox/with_std.rs:L287`](https://github.com/paritytech/substrate/blob/master/primitives/sandbox/with\_std.rs#L284), The information of `_err` of `Err(_err)` is discarded. ```rust match result { Ok(None) => Ok(ReturnValue::Unit), Ok(Some(val)) => Ok(ReturnValue::Value(val.into())), Err(_err) => Err(Error::Execution), } ``` Although the `debug_message` field is provided in the contract RPC call `contracts_call` to provide the function of returning more information before the formal execution in the future, more detailed information still needs some customized development to provide. For details, please refer to the discussion in [ink! issue #589](https://github.com/paritytech/ink/issues/589#issuecomment-731745755). Therefore, the developers cannot well know the error information in the current `FRAME Contracts` layer and the `wasmi` interpretor during the execution of the contract. On the other hand, when a contract calls a contract, the current `FRAME Contracts pallet` cannot tell the developer which layer of the contracts has a problem. In other words, there is no `Contract Stack trace` information about the execution process of the contract in the `FRAME Contracts pallet`. In addition, as a tool for contract debugging, the contract module lacks a lot of display for internal execution information, such as displaying the value of the `selector` called by the current contract and the parameters passed by the contract. On the other hand, we can count the gas consumption of the contract calling the contract (not the gas consumption of a single contract, but the gas consumption during the execution), and the number of times of reading, writing, storing, and accessing other interfaces. ### 2. 2. Provide log printing during contract execution Developers need to know some variables or information during the execution of the contract. The easiest way is to print logs during the contract execution. For printing logs information, currently `seal_println(_ctx, _ptr: u32, _len: u32)` is provided in `FRAME Contracts pallet`, and `::ink_env::debug_println()` is provided in `ink!`. This group of functions plus `ink_prelude::format!` can be combined into a feature similar to the `println!` macro in rust to print information to the console. Therefore, after the debugging switch (flag) of the contract is turned on, the following printing can be provided: ```rust let s = ::ink_prelude::format!("print in contract:{}", 1_u32); ::ink_env::debug_println(&s); // == println! ``` Set `log=runtime=debug`, then start the node, You can see the logs: ```bash Dec 01 20:02:13.307 DEBUG execute_block:apply_extrinsic: print in contract:1 {ext} ``` The logs printed in the node by this method are all DEBUG level, and the `target` of the logs are all `runtime`. But compared with `println!`, the existing `log` crate in Rust provides the function of printing logs at different levels (like DEBUG|INFO...), and can use the `target` to filter the log, so the usage of `log` in debugging contracts is more powerful than `println!`. We hope to provide the following functions in the contract by modifying `FRAME contract pallet` and `ink!` or providing other additional libraries: ```rust impl Erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(initial_supply: Balance) -> Self { use log::{info, trace}; info!(target: "contract1", "it's info log: {}", initial_supply); trace!(target: "contract1", "it's trace log: {}", initial_supply); // ... } } ``` After you executed the contract, you can see the logs in node: ```bash Dec 01 20:02:13.307 INFO contract1: it's info log:123 {ext} Dec 01 20:02:13.307 TRACE contract2: it's trace log:123 {ext} ``` ### 3. Fork `wasmi` interpreter to provide `Backtrace` functions etc. As a WASM interpreter built by parity, `wasmi` has implemented all the standard functions of WASM. But for contract developers, the execution process of `wasmi` is similar to a black box. The contract developer only knows that the contract is executed by the `wasmi` interpreter after deployment, but cannot know the execution status of the contract in `wasmi`. If you encounter a crash such as `panic`, `wasmi` does not retain the current context and cannot report back the situation in the `function stack`. On the other hand, WASM does not currently provide a good breakpoint debugging and Single-step debugging method, so it is not possible to interrupt during WASM execution to view the information during execution. Therefore, we plan to fork `wasmi` and provide functions similar to `Backtrace` information. **We may also add Breakpoint Debugging in the future** In the customized `HardHat EVM`, abnormal [`stack traces`](https://hardhat.org/hardhat-network/#solidity-stack-traces) will be printed during the execution of the Solidity code: > ```text > Error: Transaction reverted: function selector was not recognized and there's no fallback function > at ERC721Mock. (contracts/mocks/ERC721Mock.sol:9) > at ERC721Mock._checkOnERC721Received (contracts/token/ERC721/ERC721.sol:334) > at ERC721Mock._safeTransferFrom (contracts/token/ERC721/ERC721.sol:196) > at ERC721Mock.safeTransferFrom (contracts/token/ERC721/ERC721.sol:179) > at ERC721Mock.safeTransferFrom (contracts/token/ERC721/ERC721.sol:162) > at TruffleContract.safeTransferFrom (node_modules/@nomiclabs/truffle-contract/lib/execute.js:157:24) > at Context. (test/token/ERC721/ERC721.behavior.js:321:26) > ``` In `wasmtime`, when the inside execution of `panic` appears, similar output will be printed: ```text Error: failed to run main module `target/wasm32-unknown-unknown/debug/bt.wasm` Caused by: 0: failed to invoke command default 1: wasm trap: unreachable wasm backtrace: 0: 0x1cda - !__rust_start_panic 1: 0x1cce - !rust_panic 2: 0x1c9e - !std::panicking::rust_panic_with_hook::hc5713da015ebaa19 3: 0x26e - !std::panicking::begin_panic::{{closure}}::h8e62ab0ea555186f 4: 0xfba - !std::sys_common::backtrace::__rust_end_short_backtrace::h34a944558df1326a 5: 0x15c - !std::panicking::begin_panic::h03c636dac2b8fb70 6: 0x1a65 - !_start ``` In `wasmer`, when the inside execution of `panic` appears, similar output will be printed: ```text error: failed to run `target/wasm32-unknown-unknown/debug/bt.wasm` │ 1: RuntimeError: unreachable at __rust_start_panic (bt.wasm[67]:0x1cda) at rust_panic (bt.wasm[66]:0x1cce) at std::panicking::rust_panic_with_hook::hc5713da015ebaa19 (bt.wasm[65]:0x1c9e) at std::panicking::begin_panic::{{closure}}::h8e62ab0ea555186f (bt.wasm[2]:0x26e) at std::sys_common::backtrace::__rust_end_short_backtrace::h34a944558df1326a (bt.wasm[36]:0xfba) at std::panicking::begin_panic::h03c636dac2b8fb70 (bt.wasm[0]:0x15c) at _start (bt.wasm[55]:0x1a65) ╰─> 2: unreachable ``` In `wasmi`, when the inside execution of `panic` appears , only the following output will be printed: ```text thread 'main' panicked at ': Trap(Trap { kind: Unreachable })', src/bin/instantiate.rs:87:14 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` Learning from the `HardHat EVM`, `wasmer` and `wasmtime`, we will make similar modifications to `wasmi` to achieve similar functions. We will migrate the relevant code of [`wasmer`](https://github.com/wasmerio/wasmer) and [`wasmtime`](https://github.com/bytecodealliance/wasmtime) to develop similar functions into `wasmi`. We estimate that we should at least implement the following two parts in `wasmi` in order to provide a printing function similar to `BackTrace` information in `wasmi`: 1. Keep the stack information of the WASM execution stack. 2. Parse the `Name Section` in the `*.wasm` source file with compilation information to provide corresponding debugging information. ## Detailed plan of v0.2 (6 weeks, 30 Nov ~ 11 Jan): * M1: Fork `FRAME Contracts pallet`. (3 developers \* 2 weeks) * 1. Enrich the error types of the module, associate more error messages related to the internal `wasmi` interpreter, and attach some context information to the error and print it in the log. 2. Provide the function of tracking the contract call stack. 3. Add Event Tracking to the function defined in the `define_env!` macro to provide statistics on contract execution information. 4. Provide a function to print statistical data of a period of contract execution to the log. * M2: Provide `log` tools and function/macro in contracts. (3 developers \* 1 weeks) * 1. Provide components similar to `RuntimeLogger` in Substrate Runtime 2. Provide the corresponding method in `define_env!` of `FRAME Contracts Pallet` 3. Provide wrapper functions and other functions for `RuntimeLogger` in `ink!` * M3: Fork `wasmi`. (3 developers \* 2 weeks) * 1. Investigate the design of `wasmer`, `wasmtime` and `wasmi` 2. Design and implement functions similar to `Backtrace` for `wasmi` * M4 : Integration and testing (3 developers \* 1 weeks) * The above-mentioned forked repos are independent of Europa repo, and the corresponding functions will not be submitted to the original repo. If there are similar functions in the original repo plan, the corresponding functions will be merged after discussion with the original repo author. ## Cost of v0.2 (18 developers \* weeks) * Operating activities: $3600 ( Rent and Devices: $200 per developer * week ) * Employee payments: $36000 ($1900 per developer * week) * —————————— + * Total Cost: $37800 * Exchange Rate: $5.0 / DOT * Treasury Proposal: 7560 DOT ## Verification : Github source & Youtube demo & Treasury Report 1. Construct incorrect contracts and execute logs printing to determine whether it meets expectations 1. Can trigger all wrong contracts in `wasmi` 1. Can trigger all wrong contracts in `FRAME Contracts Pallet` 1. Construct a contract to call a contract example and crash in it 1. Display the call statistics of the `define_env!` interface during contract execution 1. Execute the log printing function, provide formatted printing examples of different data, and judge whether it meets expectations 1. Construct a contract that crashes under different conditions and record the log after execution. Then judge whether the backtrace information of the contract execution is completely printed, and check whether it matches the actual execution of the collapsed contract.
Comments