diff --git a/.github/workflows/contract.md b/.github/workflows/contract.md index 2f0730ba..06ddd2da 100644 --- a/.github/workflows/contract.md +++ b/.github/workflows/contract.md @@ -1,5 +1,5 @@ -# TrustEVM Contract CI -This GitHub Actions workflow builds the TrustEVM contract and its associated tests. +# EOS EVM Contract CI +This GitHub Actions workflow builds the EOS EVM contract and its associated tests. ### Index 1. [Triggers](#triggers) @@ -30,17 +30,17 @@ This workflow performs the following steps: 1. Attach Documentation 1. Checkout the repo with no submodules. 1. Attach an annotation to the GitHub Actions build summary page containing CI documentation. -1. TrustEVM Contract Build +1. EOS EVM Contract Build > This is a build matrix with and without tests enabled. 1. Authenticate to the `trustevm-ci-submodule-checkout` GitHub app using the [AntelopeIO/github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) action to obtain an ephemeral token. 1. Checkout the repo and submodules using the ephemeral token. 1. Download the CDT binary using the [AntelopeIO/asset-artifact-download-action](https://github.com/AntelopeIO/asset-artifact-download-action) action. 1. Install the CDT binary. - 1. Build the TrustEVM contract using `make` and `cmake`. + 1. Build the EOS EVM contract using `make` and `cmake`. 1. Upload the contract build folder to GitHub Actions. 1. If tests are enabled, download the `leap-dev` binary using [AntelopeIO/asset-artifact-download-action](https://github.com/AntelopeIO/asset-artifact-download-action) action. 1. If tests are enabled, install the `leap-dev` binary. - 1. If tests are enabled, build the TrustEVM contract tests using `make` and `cmake`. + 1. If tests are enabled, build the EOS EVM contract tests using `make` and `cmake`. 1. If tests are enabled, upload the build folder for the contract test code to GitHub Actions. 1. If tests are enabled, run them and ignore the outcome. 1. If tests are enabled, attach xUnit-formatted test metrics as an artifact. @@ -56,12 +56,12 @@ This workflow produces the following outputs: ## GitHub App Integration This workflow uses the [AntelopeIO/github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) GitHub action to assume the role of a GitHub application installed to the AntelopeIO organization to clone the private submodules. It requests a token from the GitHub app, clones everything using this token under the identity of the app, then the token expires. This is advantageous over a persistent API key from a GitHub service account because this does not consume a paid user seat, the "account" associated with the app cannot be logged into in the GitHub web UI, the app is scoped to exactly the permissions it needs to perform the clones for this repo _and nothing more_, and the API key expires very quickly so a bad actor who exfiltrates this key from the CI system should find it is not useful. -**The downside is that if TrustEVM adds additional private submodules, the GitHub app must be granted permissions to these new submodules.** The CI system will not work until this happens. +**The downside is that if EOS EVM adds additional private submodules, the GitHub app must be granted permissions to these new submodules.** The CI system will not work until this happens. ## See Also - [asset-artifact-download-action](https://github.com/AntelopeIO/asset-artifact-download-action) GitHub Action - [github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) GitHub action -- [TrustEVM Documentation](../../README.md) +- [EOS EVM Documentation](../../README.md) For assistance with the CI system, please open an issue in this repo or reach out in the `#help-automation` channel via IM. diff --git a/.github/workflows/contract.yml b/.github/workflows/contract.yml index a631f5e7..1e2faa4f 100644 --- a/.github/workflows/contract.yml +++ b/.github/workflows/contract.yml @@ -1,4 +1,4 @@ -name: TrustEVM Contract CI +name: EOS EVM Contract CI on: push: @@ -27,7 +27,7 @@ jobs: strategy: matrix: DWITH_TEST_ACTIONS: ['on', 'off'] - name: TrustEVM Contract Build - Tests ${{ matrix.DWITH_TEST_ACTIONS }} + name: EOS EVM Contract Build - Tests ${{ matrix.DWITH_TEST_ACTIONS }} env: CC: gcc-10 CXX: g++-10 @@ -61,7 +61,7 @@ jobs: - name: Install CDT run: sudo apt-get install -y ./cdt*.deb - - name: Build TrustEVM Contract + - name: Build EOS EVM Contract run: .github/workflows/build-contract.sh env: DWITH_TEST_ACTIONS: ${{ matrix.DWITH_TEST_ACTIONS }} @@ -87,7 +87,7 @@ jobs: - name: Install Leap run: sudo apt-get install -y ./leap*.deb - - name: Build TrustEVM Contract Tests + - name: Build EOS EVM Contract Tests run: .github/workflows/build-contract-test.sh - name: Upload Artifacts @@ -97,7 +97,7 @@ jobs: path: contract-test.tar.gz if-no-files-found: error - - name: Test TrustEVM Contract + - name: Test EOS EVM Contract run: .github/workflows/test-contract.sh env: DWITH_TEST_ACTIONS: ${{ matrix.DWITH_TEST_ACTIONS }} diff --git a/.github/workflows/node.md b/.github/workflows/node.md index d30ce564..82579ad3 100644 --- a/.github/workflows/node.md +++ b/.github/workflows/node.md @@ -1,5 +1,5 @@ -# TrustEVM Node CI -This GitHub Actions workflow builds the TrustEVM node. +# EOS EVM Node CI +This GitHub Actions workflow builds eos-evm-node and eos-evm-rpc. ### Index 1. [Triggers](#triggers) @@ -27,21 +27,21 @@ This workflow performs the following steps: 1. Attach Documentation 1. Checkout the repo with no submodules. 1. Attach an annotation to the GitHub Actions build summary page containing CI documentation. -1. TrustEVM Node Build +1. EOS EVM Node Build 1. Authenticate to the `trustevm-ci-submodule-checkout` GitHub app using the [AntelopeIO/github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) action to obtain an ephemeral token. 1. Checkout the repo and submodules using the ephemeral token. - 1. Build the TrustEVM node using `cmake` and `make`. - 1. Upload the node build folder to GitHub Actions if the `upload-artifacts` input is set to `true`. + 1. Build eos-evm-node and eos-evm-rpc using `cmake` and `make`. + 1. Upload the build folder to GitHub Actions if the `upload-artifacts` input is set to `true`. ## Outputs This workflow produces the following outputs: -1. Build Artifacts - `build.tar.gz` containing the built artifacts of TrustEVM Node, if the `upload-artifacts` input is set to `true`. +1. Build Artifacts - `build.tar.gz` containing the built artifacts of eos-evm-node and eos-evm-rpc, if the `upload-artifacts` input is set to `true`. -> 💾️ Build artifacts are only attached on-demand for this pipeline because they are >117 MB each, but we only get 2 GB of cumulative artifact storage in GitHub Actions while TrustEVM is a private repo. Obtain artifacts by performing a manual build with `upload-artifacts` set to `true`. +> 💾️ Build artifacts are only attached on-demand for this pipeline because they are >117 MB each, but we only get 2 GB of cumulative artifact storage in GitHub Actions while eos-evm is a private repo. Obtain artifacts by performing a manual build with `upload-artifacts` set to `true`. ## See Also - [github-app-token-action](https://github.com/AntelopeIO/github-app-token-action) GitHub action -- [TrustEVM Documentation](../../README.md) +- [EOS EVM Documentation](../../README.md) For assistance with the CI system, please open an issue in this repo or reach out in the `#help-automation` channel via IM. diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index bc03d9cf..52ac1cbb 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -1,4 +1,4 @@ -name: TrustEVM Node CI +name: EOS EVM Node CI on: push: @@ -32,7 +32,7 @@ jobs: run: cat .github/workflows/node.md >> $GITHUB_STEP_SUMMARY build: - name: TrustEVM Node Build + name: EOS EVM Node Build runs-on: ubuntu-20.04 steps: @@ -50,7 +50,7 @@ jobs: submodules: 'recursive' token: ${{ steps.auth.outputs.token }} - - name: Build TrustEVM Node + - name: Build EOS EVM Node run: .github/workflows/build-node.sh env: CC: gcc-10 diff --git a/.gitmodules b/.gitmodules index 56d3c447..c2c3ad7d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,10 +6,10 @@ url = https://github.com/lemire/CRoaringUnityBuild [submodule "silkworm"] path = silkworm - url = git@github.com:eosnetworkfoundation/silkworm + url = https://github.com/eosnetworkfoundation/silkworm [submodule "contract/external/ethash"] path = contract/external/ethash - url = git@github.com:eosnetworkfoundation/ethash + url = https://github.com/eosnetworkfoundation/ethash [submodule "contract/external/intx"] path = contract/external/intx url = https://github.com/chfast/intx diff --git a/CMakeLists.txt b/CMakeLists.txt index 7429d86b..b810f14a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ HunterGate( FILEPATH "${CMAKE_SOURCE_DIR}/cmake/Hunter/config.cmake" ) -project(trustevm_node) -set(PROJECT_VERSION 0.3.0) +project(eos_evm_node) +set(PROJECT_VERSION 0.5.1) string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ ${PROJECT_VERSION}) set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1}) diff --git a/README.md b/README.md index f40ea099..dfa699eb 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,43 @@ -# TrustEVM -Main Repository of the Trust Ethereum Virtual Machine (TrustEVM-node, TrustEVM-RPC, EVM smart contract) on the Antelope Network. +# EOS EVM + +This is the main repository of the EOS EVM project. EOS EVM is a compatibility layer deployed on top of the EOS blockchain which implements the Ethereum Virtual Machine (EVM). It enables developers to deploy and run their applications on top of the EOS blockchain infrastructure but to build, test, and debug those applications using the common languages and tools they are used to using with other EVM compatible blockchains. It also enables users of those applications to interact with the application in ways they are familiar with (e.g. using a MetaMask wallet). + +The EOS EVM consists of multiple components that are tracked across different repositories. + +The repositories containing code relevant to the EOS EVM project include: +1. https://github.com/eosnetworkfoundation/blockscout: A fork of the [blockscout](https://github.com/blockscout/blockscout) blockchain explorer with adaptations to make it suitable for the EOS EVM project. +2. https://github.com/eosnetworkfoundation/evm_bridge_frontend: Frontend to operate the EVM trustless bridge. +3. This repository. + +This repository in particular hosts the source to build three significant components of EOS EVM: +1. EOS EVM Contract: This is the Antelope smart contract that implements the main runtime for the EVM. The source code for the smart contract can be found in the `contracts` directory. The main build artifacts are `evm_runtime.wasm` and `evm_runtime.abi`. +2. EOS EVM Node and RPC: These are programs, EOS EVM Node (`eos-evm-node` executable) and EOS EVM RPC (`eos-evm-rpc` executable), that are based on Silkworm and which together allow a node to service a subset of the RPC methods supported in the Ethereum JSON-RPC which involve reading the state of the (virtual) EVM blockchain enabled by the EOS EVM Contract. The `eos-evm-node` program relies on a SHiP connection to a [Leap](https://github.com/AntelopeIO/leap) node that is connected to the blockchain network hosting the desired EOS EVM Contract (i.e. the EOS network in the case of the EOS EVM Mainnet). +3. TX-Wrapper: This is a Node.js application which specifically services two RPC methods of the Ethereum JSON-RPC: `eth_sendRawTransaction` and `eth_gasPrice`. It relies on chain API access to a [Leap](https://github.com/AntelopeIO/leap) node connected to the blockchain network hosting the desired EOS EVM Contract. The source code for TX-Wrapper can be found in the `peripherals/tx_wrapper` directory. + +Beyond code, there are additional useful resources relevant to the EOS EVM project. +1. https://github.com/eosnetworkfoundation/evm-public-docs: A repository to hold technical documentation for an audience interested in following and participating in the operations of the EOS EVM project. The genesis JSON needed to stand up a EOS EVM Node that works with the EVM on the EOS blockchain can also be found in that repository. +2. https://docs.eosnetwork.com/docs/latest/eos-evm/: Official documentation for the EOS EVM. ## Overview -The TrustEVM node consumes Antelope(EOS) blocks from Antelope node (nodeos) via state history (SHIP) endpoint, and build up the virtual Ethereum blockchain in a deterministic way. -The TrustEVM RPC will talk with the TrustEVM node, and provide read-only Ethereum compatible RPC services for clients (such as MetaMask) -Clients can also push Ethereum compatible transactions to the Antelope blockchain, via proxy and Transaction Wrapper (TX-Wrapper), which encapsulates Ethereum raw transactions into Antelope transactions. All ethereum transactions will be validated and executed by the EVM smart contracts deployed in the Antelope Blockchain network. +The EOS EVM Node consumes Antelope (EOS) blocks from a Leap node via state history (SHiP) endpoint and builds the virtual EVM blockchain in a deterministic way. +The EOS EVM RPC will talk with the EOS EVM node, and provide read-only Ethereum compatible RPC services for clients (such as MetaMask). + +Clients can also push Ethereum compatible transactions (aka EVM transactions) to the EOS blockchain, via proxy and Transaction Wrapper (TX-Wrapper), which encapsulates EVM transactions into Antelope transactions. All EVM transactions will be validated and executed by the EOS EVM Contract deployed on the EOS blockchain. ``` | | WRITE +-----------------+ - | +------------------------->| TX-Wrapper | + | +------------------------->| TX-Wrapper | | | +-------v---------+ - | | | Antelope node | ---> connect to the other nodes in the Antelope blockchain network + | | | Leap node | ---> connect to the other nodes in the blockchain network client | | +-------+---------+ request | +-----+-----+ | ---------+------>| Proxy | | | +-----------+ v | | +-----------------+ | READ | +--------------+ | | - | +---->| TrustEVM RPC |---->| TrustEVM Node + + | +---->| EOS EVM RPC |---->| EOS EVM Node + | +--------------+ | | | +-----------------+ ``` @@ -28,12 +46,12 @@ Clients can also push Ethereum compatible transactions to the Antelope blockchai ### checkout the source code: ``` -git clone https://github.com/eosnetworkfoundation/TrustEVM.git -cd TrustEVM +git clone https://github.com/eosnetworkfoundation/eos-evm.git +cd eos-evm git submodule update --init --recursive ``` -### compile TrustEVM-node, TrustEVM-rpc, unittests +### compile eos-evm-node, eos-evm-rpc, unittests Prerequisites: - Ubuntu 20 or later or other compatible Linux @@ -48,8 +66,8 @@ make -j8 ``` You'll get the list of binaries with other tools: ``` -cmd/trustevm-node -cmd/trustevm-rpc +cmd/eos-evm-node +cmd/eos-evm-rpc cmd/unit_test cmd/silkrpc_toolbox cmd/silkrpcdaemon @@ -84,25 +102,25 @@ make -j ``` You should get the following output files: ``` -TrustEVM/contract/build/evm_runtime/evm_runtime.wasm -TrustEVM/contract/build/evm_runtime/evm_runtime.abi +eos-evm/contract/build/evm_runtime/evm_runtime.wasm +eos-evm/contract/build/evm_runtime/evm_runtime.abi ``` -## Unittests +## Unit tests -We need to compile the leap project in Antelope in order to compile unittests: +We need to compile the Leap project in Antelope in order to compile unit tests: following the instruction in https://github.com/AntelopeIO/leap to compile leap -To compile unittests: +To compile unit tests: ``` -cd TrustEVM/contract/tests +cd eos-evm/contract/tests mkdir build cd build cmake -Deosio_DIR=//build/lib/cmake/eosio .. make -j4 unit_test ``` -to run unittest: +to run unit test: ``` cd contract/tests/build ./unit_test @@ -111,18 +129,18 @@ cd contract/tests/build ## Deployments For local testnet deployment and testings, please refer to -https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md +https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md For public testnet deployment, please refer to -https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/public_testnet_deployment_plan.md +https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/public_testnet_deployment_plan.md ## CI This repo contains the following GitHub Actions workflows for CI: -- TrustEVM Contract CI - build the TrustEVM contract and its associated tests - - [Pipeline](https://github.com/eosnetworkfoundation/TrustEVM/actions/workflows/contract.yml) +- EOS EVM Contract CI - build the EOS EVM Contract and its associated tests + - [Pipeline](https://github.com/eosnetworkfoundation/eos-evm/actions/workflows/contract.yml) - [Documentation](./.github/workflows/contract.md) -- TrustEVM Node CI - build the TrustEVM node - - [Pipeline](https://github.com/eosnetworkfoundation/TrustEVM/actions/workflows/node.yml) +- EOS EVM Node CI - build the EOS EVM node + - [Pipeline](https://github.com/eosnetworkfoundation/eos-evm/actions/workflows/node.yml) - [Documentation](./.github/workflows/node.md) See the pipeline documentation for more information. diff --git a/cmd/CMakeLists.txt b/cmd/CMakeLists.txt index 353eaecf..475bdfd6 100644 --- a/cmd/CMakeLists.txt +++ b/cmd/CMakeLists.txt @@ -84,13 +84,13 @@ add_library(rpc_plugin rpc_plugin.cpp) target_include_directories(rpc_plugin PUBLIC ${CMAKE_SOURCE_DIR}) target_link_libraries(rpc_plugin PUBLIC ${SILKRPC_LIBRARIES} ${SILKRPC_DAEMON_LIBRARIES} sys_plugin appbase ${Boost_PROGRAM_OPTIONS_LIBRARY}) -add_executable(trustevm-node node.cpp) -target_include_directories(trustevm-node PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) -target_link_libraries(trustevm-node PRIVATE engine_plugin shipreceiver_plugin sys_plugin blockchain_plugin rpc_plugin trustevm_node-buildinfo) +add_executable(eos-evm-node node.cpp) +target_include_directories(eos-evm-node PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) +target_link_libraries(eos-evm-node PRIVATE engine_plugin shipreceiver_plugin sys_plugin blockchain_plugin rpc_plugin eos_evm_node-buildinfo) -add_executable(trustevm-rpc rpc.cpp) -target_include_directories(trustevm-rpc PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) -target_link_libraries(trustevm-rpc PRIVATE rpc_plugin trustevm_node-buildinfo) +add_executable(eos-evm-rpc rpc.cpp) +target_include_directories(eos-evm-rpc PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) +target_link_libraries(eos-evm-rpc PRIVATE rpc_plugin eos_evm_node-buildinfo) # Unit tests enable_testing() @@ -126,4 +126,4 @@ include_directories( ) target_compile_definitions(consensus PRIVATE SILKWORM_CONSENSUS_TEST_DIR="${CMAKE_SOURCE_DIR}/silkworm/third_party/tests") -target_link_libraries(consensus PRIVATE engine_plugin shipreceiver_plugin sys_plugin blockchain_plugin rpc_plugin trustevm_node-buildinfo evmc::loader CLI11::CLI11) \ No newline at end of file +target_link_libraries(consensus PRIVATE engine_plugin shipreceiver_plugin sys_plugin blockchain_plugin rpc_plugin eos_evm_node-buildinfo evmc::loader CLI11::CLI11) \ No newline at end of file diff --git a/cmd/block_conversion_plugin.cpp b/cmd/block_conversion_plugin.cpp index e2871ff1..876abe5f 100644 --- a/cmd/block_conversion_plugin.cpp +++ b/cmd/block_conversion_plugin.cpp @@ -11,6 +11,25 @@ #include using sys = sys_plugin; + +silkworm::log::BufferBase& operator << ( silkworm::log::BufferBase& ss, const eosio::checksum256& id ) { + ss << silkworm::to_hex(id.extract_as_byte_array()); + return ss; +} + +silkworm::log::BufferBase& operator << ( silkworm::log::BufferBase& ss, const channels::native_block& block ) { + ss << "#" << block.block_num << " (" + << "id:" << block.id << "," + << "prev:" << block.prev + << ")"; + return ss; +} + +silkworm::log::BufferBase& operator << ( silkworm::log::BufferBase& ss, const silkworm::Block& block ) { + ss << "#" << block.header.number << ", txs:" << block.transactions.size() << ", hash:" << silkworm::to_hex(block.header.hash()); + return ss; +} + class block_conversion_plugin_impl : std::enable_shared_from_this { public: block_conversion_plugin_impl() @@ -27,23 +46,24 @@ class block_conversion_plugin_impl : std::enable_shared_from_this().get_head_canonical_header(); - if (!head_header) { - sys::error("Unable to read canonical header"); + auto head_block = appbase::app().get_plugin().get_head_block(); + if (!head_block) { + sys::error("Unable to read head block"); return; } - evm_blocks.push_back(silkworm::Block{.header=*head_header}); + SILK_INFO << "load_head: " << *head_block; + evm_blocks.push_back(*head_block); channels::native_block nb; std::optional start_from_block = appbase::app().get_plugin().get_start_from_block(); if( start_from_block ) { nb = *start_from_block; } else { - nb.id = eosio::checksum256(head_header->mix_hash.bytes); - nb.block_num = utils::to_block_num(head_header->mix_hash.bytes); - nb.timestamp = head_header->timestamp*1e6; + nb.id = eosio::checksum256(head_block->header.mix_hash.bytes); + nb.block_num = utils::to_block_num(head_block->header.mix_hash.bytes); + nb.timestamp = head_block->header.timestamp*1e6; } - SILK_DEBUG << "Loaded native block: [" << head_header->number << "][" << nb.block_num << "],[" << nb.timestamp << "]"; + SILK_INFO << "Loaded native block: [" << head_block->header.number << "][" << nb.block_num << "],[" << nb.timestamp << "]"; native_blocks.push_back(nb); auto genesis_header = appbase::app().get_plugin().get_genesis_header(); @@ -89,7 +109,11 @@ class block_conversion_plugin_impl : std::enable_shared_from_this + void for_each_reverse_action(const channels::native_block& block, F&& f) { + for(auto trx=block.transactions.rbegin(); trx != block.transactions.rend(); ++trx) { + for(auto act=trx->actions.rbegin(); act != trx->actions.rend(); ++act) { + f(*act); + } + } + } + inline void init() { SILK_DEBUG << "block_conversion_plugin_impl INIT"; load_head(); native_blocks_subscription = appbase::app().get_channel().subscribe( - [this](auto b) { + [this](auto new_block) { static size_t count = 0; - //SILK_INFO << "--Enter Block #" << b->block_num; - // Keep the last block before genesis timestamp - if (b->timestamp <= bm.value().genesis_timestamp) { - SILK_DEBUG << "Before genesis: " << bm->genesis_timestamp << " Block #" << b->block_num << " timestamp: " << b->timestamp; + if (new_block->timestamp <= bm.value().genesis_timestamp) { + SILK_WARN << "Before genesis: " << bm->genesis_timestamp << " Block #" << new_block->block_num << " timestamp: " << new_block->timestamp; native_blocks.clear(); - native_blocks.push_back(*b); + native_blocks.push_back(*new_block); return; } - // Add received native block to our local reversible chain. - // If the received native block can't be linked we remove the forked blocks until the fork point. - // Also we remove the forked block transactions from the corresponding evm block as they where previously included - // and update the upper bound block accordingly - while( !native_blocks.empty() && native_blocks.back().id != b->prev ) { - //SILK_DEBUG << "Fork at Block #" << b->block_num; - const auto& forked_block = native_blocks.back(); + // Check if received native block can't be linked + if( !native_blocks.empty() && native_blocks.back().id != new_block->prev ) { + + SILK_WARN << "Can't link new block " << *new_block; + + // Find fork block + auto fork_block = std::find_if(native_blocks.begin(), native_blocks.end(), [&new_block](const auto& nb){ return nb.id == new_block->prev; }); + if( fork_block == native_blocks.end() ) { + SILK_CRIT << "Unable to find fork block " << new_block->prev; + throw std::runtime_error("Unable to find fork block"); + } - while( timestamp_to_evm_block_num(forked_block.timestamp) < evm_blocks.back().header.number ) + SILK_WARN << "Fork at Block " << *fork_block; + + // Remove EVM blocks after the fork + while( !evm_blocks.empty() && timestamp_to_evm_block_num(fork_block->timestamp) < evm_blocks.back().header.number ) { + SILK_WARN << "Removing forked EVM block " << evm_blocks.back(); evm_blocks.pop_back(); + } - auto& last_evm_block = evm_blocks.back(); - for_each_action(forked_block, [&last_evm_block](const auto&){ - last_evm_block.transactions.pop_back(); - }); + // Remove forked native blocks up until the fork point + while( !native_blocks.empty() && native_blocks.back().id != fork_block->id ) { + + // Check if the native block to be removed has transactions + // and they belong to the EVM block of the fork point + if( native_blocks.back().transactions.size() > 0 && timestamp_to_evm_block_num(native_blocks.back().timestamp) == timestamp_to_evm_block_num(fork_block->timestamp) ) { + + // Check that we can remove transactions contained in the forked native block + if (evm_blocks.empty() || timestamp_to_evm_block_num(native_blocks.back().timestamp) != evm_blocks.back().header.number) { + SILK_CRIT << "Unable to remove transactions" + << "(empty: " << evm_blocks.empty() + << ", evmblock(native):" << timestamp_to_evm_block_num(native_blocks.back().timestamp) <<")" + << ", evm number:" << evm_blocks.back().header.number <<")"; + throw std::runtime_error("Unable to remove transactions"); + } + + // Remove transactions in forked native block + SILK_WARN << "Removing transactions in forked native block " << native_blocks.back(); + for_each_reverse_action(native_blocks.back(), [this](const auto& act){ + auto dtx = deserialize_tx(act.data); + auto txid_a = ethash::keccak256(dtx.rlpx.data(), dtx.rlpx.size()); + + silkworm::Bytes transaction_rlp{}; + silkworm::rlp::encode(transaction_rlp, evm_blocks.back().transactions.back()); + auto txid_b = ethash::keccak256(transaction_rlp.data(), transaction_rlp.size()); + + // Ensure that the transaction to be removed is the correct one + if( std::memcmp(txid_a.bytes, txid_b.bytes, sizeof(txid_a.bytes)) != 0) { + SILK_CRIT << "Unable to remove transaction " + << ",txid_a:" << silkworm::to_hex(txid_a.bytes) + << ",txid_b:" << silkworm::to_hex(txid_b.bytes); + throw std::runtime_error("Unable to remove transaction"); + } + + SILK_WARN << "Removing trx: " << silkworm::to_hex(txid_a.bytes); + evm_blocks.back().transactions.pop_back(); + }); + } - native_blocks.pop_back(); - if( native_blocks.size() && timestamp_to_evm_block_num(native_blocks.back().timestamp) == last_evm_block.header.number ) - set_upper_bound(last_evm_block, native_blocks.back()); - } + // Remove forked native block + SILK_WARN << "Removing forked native block " << native_blocks.back(); + native_blocks.pop_back(); + } - if( native_blocks.empty() || native_blocks.back().id != b->prev ) { - SILK_CRIT << "Unbale to link block #" << b->block_num; - throw std::runtime_error("Unbale to link block"); + // Ensure upper bound native block correspond to this EVM block + if( evm_blocks.empty() || timestamp_to_evm_block_num(native_blocks.back().timestamp) != evm_blocks.back().header.number ) { + SILK_CRIT << "Unable to set upper bound " + << "(empty: " << evm_blocks.empty() + << ", evmblock(native):" << timestamp_to_evm_block_num(native_blocks.back().timestamp) <<")" + << ", evm number:" << evm_blocks.back().header.number <<")"; + throw std::runtime_error("Unable to set upper bound"); + } + + // Reset upper bound + SILK_WARN << "Reset upper bound for EVM Block " << evm_blocks.back() << " to: " << native_blocks.back(); + set_upper_bound(evm_blocks.back(), native_blocks.back()); } - native_blocks.push_back(*b); - // Process the last native block received. - // We extend the evm chain if necessary up until the block where the received block belongs - auto evm_num = timestamp_to_evm_block_num(b->timestamp); - //SILK_INFO << "Expecting evm number: " << evm_num; + // Enqueue received block + native_blocks.push_back(*new_block); + + // Extend the EVM chain if necessary up until the block where the received block belongs + auto evm_num = timestamp_to_evm_block_num(new_block->timestamp); while(evm_blocks.back().header.number < evm_num) { auto& last_evm_block = evm_blocks.back(); - //if( last_evm_block.header.number != 0 ) { - last_evm_block.header.transactions_root = compute_transaction_root(last_evm_block); - if (!(last_evm_block.header.number % 1000)) - SILK_INFO << "Generating EVM #" << last_evm_block.header.number; - evm_blocks_channel.publish(80, std::make_shared(last_evm_block)); - //} - evm_blocks.push_back(new_block(last_evm_block.header.number+1, last_evm_block.header.hash())); + last_evm_block.header.transactions_root = compute_transaction_root(last_evm_block); + evm_blocks_channel.publish(80, std::make_shared(last_evm_block)); + evm_blocks.push_back(generate_new_evm_block(last_evm_block.header.number+1, last_evm_block.header.hash())); } // Add transactions to the evm block auto& curr = evm_blocks.back(); - for_each_action(*b, [this, &curr](const auto& act){ + for_each_action(*new_block, [this, &curr](const auto& act){ auto dtx = deserialize_tx(act.data); silkworm::ByteView bv = {(const uint8_t*)dtx.rlpx.data(), dtx.rlpx.size()}; silkworm::Transaction evm_tx; @@ -196,33 +275,26 @@ class block_conversion_plugin_impl : std::enable_shared_from_this lib_timestamp; - if(b->lib < native_blocks.back().block_num) { - auto it = std::find_if(native_blocks.begin(), native_blocks.end(), [&b](const auto& nb){ return nb.block_num == b->lib; }); - if(it != native_blocks.end()) { - lib_timestamp = it->timestamp; - } - } else { - lib_timestamp = b->timestamp; + auto it = std::upper_bound(native_blocks.begin(), native_blocks.end(), new_block->lib, [](uint32_t lib, const auto& nb) { return lib < nb.block_num; }); + if(it != native_blocks.begin()) { + --it; + lib_timestamp = it->timestamp; } if( lib_timestamp ) { auto evm_lib = timestamp_to_evm_block_num(*lib_timestamp) - 1; - //SILK_DEBUG << "EVM LIB: #" << evm_lib; // Remove irreversible native blocks while(timestamp_to_evm_block_num(native_blocks.front().timestamp) < evm_lib) { - //SILK_DEBUG << "Remove IRR native: #" << native_blocks.front().block_num; native_blocks.pop_front(); } // Remove irreversible evm blocks while(evm_blocks.front().header.number < evm_lib) { - //SILK_DEBUG << "Remove IRR EVM: #" << evm_blocks.front().header.number; evm_blocks.pop_front(); } } diff --git a/cmd/chain_state.hpp b/cmd/chain_state.hpp deleted file mode 100644 index a43669cc..00000000 --- a/cmd/chain_state.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -// classes and utilies for keeping some external (parent) chain state for resumption - -struct chain_state { - static inline constexpr std::string_view state_file_name = "chain_state.bin"; - - chain_state() = default; - inline explicit chain_state(std::string fn) : filename(std::move(fn)+"/"+std::string(state_file_name.data(), state_file_name.size())) {} - - std::string filename; - uint32_t head_block_num = 0; - uint32_t head_trust_block_num = 0; - - inline void write() const { - std::ofstream f(filename, std::ios::binary); - f.write((const char*)&head_block_num, sizeof(uint32_t)); - f.write((const char*)&head_trust_block_num, sizeof(uint32_t)); - } - - inline void read() { - std::ifstream f(filename, std::ios::binary); - f.read((char*)&head_block_num, sizeof(uint32_t)); - f.read((char*)&head_trust_block_num, sizeof(uint32_t)); - } - - inline bool exists() const { - std::filesystem::path cs = {filename}; - return std::filesystem::exists(cs); - } -}; \ No newline at end of file diff --git a/cmd/engine_plugin.cpp b/cmd/engine_plugin.cpp index 4d462acb..e71a8f20 100644 --- a/cmd/engine_plugin.cpp +++ b/cmd/engine_plugin.cpp @@ -72,7 +72,7 @@ class engine_plugin_impl : std::enable_shared_from_this { node_settings.network_id = node_settings.chain_config->chain_id; eth.reset(new silkworm::EthereumBackEnd(node_settings, &db_env)); - eth->set_node_name("Trust Node"); + eth->set_node_name("EOS EVM Node"); SILK_INFO << "Created Ethereum Backend with network id <" << node_settings.network_id << ">, etherbase <" << node_settings.etherbase->bytes << ">"; server.reset(new silkworm::rpc::BackEndKvServer(server_settings, *eth.get())); @@ -95,6 +95,17 @@ class engine_plugin_impl : std::enable_shared_from_this { return silkworm::db::read_canonical_header(txn, head_num); } + std::optional get_head_block() { + auto header = get_head_canonical_header(); + if(!header) return {}; + + silkworm::db::ROTxn txn(db_env); + silkworm::Block block; + auto res = read_block_by_number(txn, header->number, false, block); + if(!res) return {}; + return block; + } + std::optional get_genesis_header() { silkworm::db::ROTxn txn(db_env); return silkworm::db::read_canonical_header(txn, 0); @@ -154,10 +165,15 @@ void engine_plugin::plugin_shutdown() { mdbx::env* engine_plugin::get_db() { return &my->db_env; } + std::optional engine_plugin::get_head_canonical_header() { return my->get_head_canonical_header(); } +std::optional engine_plugin::get_head_block() { + return my->get_head_block(); +} + std::optional engine_plugin::get_genesis_header() { return my->get_genesis_header(); } diff --git a/cmd/engine_plugin.hpp b/cmd/engine_plugin.hpp index db3a545c..e236a88e 100644 --- a/cmd/engine_plugin.hpp +++ b/cmd/engine_plugin.hpp @@ -22,6 +22,7 @@ class engine_plugin : public appbase::plugin { uint32_t get_threads() const; std::string get_chain_data_dir() const; std::optional get_head_canonical_header(); + std::optional get_head_block(); std::optional get_genesis_header(); private: diff --git a/cmd/node.cpp b/cmd/node.cpp index 7923814e..10def355 100644 --- a/cmd/node.cpp +++ b/cmd/node.cpp @@ -6,7 +6,7 @@ #include -#include +#include #include //#include @@ -18,9 +18,9 @@ #include "rpc_plugin.hpp" std::string get_version_from_build_info() { - const auto build_info{trustevm_node_get_buildinfo()}; + const auto build_info{eos_evm_node_get_buildinfo()}; - std::string application_version{"trustevm node version: "}; + std::string application_version{"EOS EVM Node version: "}; application_version.append(build_info->project_version); return application_version; } diff --git a/cmd/rpc.cpp b/cmd/rpc.cpp index 29e24be4..8b976d4a 100644 --- a/cmd/rpc.cpp +++ b/cmd/rpc.cpp @@ -22,12 +22,12 @@ #include "rpc_plugin.hpp" #include "sys_plugin.hpp" -#include +#include std::string get_version_from_build_info() { - const auto build_info{trustevm_node_get_buildinfo()}; + const auto build_info{eos_evm_node_get_buildinfo()}; - std::string application_version{"trustevm node version: "}; + std::string application_version{"EOS EVM RPC version: "}; application_version.append(build_info->project_version); return application_version; } diff --git a/cmd/rpc_plugin.cpp b/cmd/rpc_plugin.cpp index 67e278f0..0fb7edb7 100644 --- a/cmd/rpc_plugin.cpp +++ b/cmd/rpc_plugin.cpp @@ -35,8 +35,8 @@ void rpc_plugin::set_program_options( appbase::options_description& cli, appbase "http port for JSON RPC of the form
:") ("rpc-engine-port", boost::program_options::value()->default_value("127.0.0.1:8882"), "engine port for JSON RPC of the form
:") - ("trust-evm-node", boost::program_options::value()->default_value("127.0.0.1:8001"), - "address to trust-evm-node of the form
:") + ("eos-evm-node", boost::program_options::value()->default_value("127.0.0.1:8001"), + "address to eos-evm-node of the form
:") ("rpc-threads", boost::program_options::value()->default_value(16), "number of threads for use with rpc") ("chaindata", boost::program_options::value()->default_value("./"), @@ -45,7 +45,7 @@ void rpc_plugin::set_program_options( appbase::options_description& cli, appbase "maximum number of rpc readers") ("api-spec", boost::program_options::value()->default_value("eth"), "comma separated api spec, possible values: debug,engine,eth,net,parity,erigon,txpool,trace,web3") - ("chain-id", boost::program_options::value()->default_value(silkworm::kTrustConfig.chain_id), + ("chain-id", boost::program_options::value()->default_value(silkworm::kEOSEVMLocalTestnetConfig.chain_id), "override chain-id") ; } @@ -82,9 +82,9 @@ void rpc_plugin::plugin_initialize( const appbase::variables_map& options ) try const auto threads = options.at("rpc-threads").as(); const auto max_readers = options.at("rpc-max-readers").as(); - // TODO when we resolve issues with silkrpc compiling in trust-evm-node then remove - // the `trust-evm-node` options and use silk_engine for the address and configuration - const auto& node_port = options.at("trust-evm-node").as(); + // TODO when we resolve issues with silkrpc compiling in eos-evm-node then remove + // the `eos-evm-node` options and use silk_engine for the address and configuration + const auto& node_port = options.at("eos-evm-node").as(); //const auto node_settings = engine.get_node_settings(); const auto& data_dir = options.at("chaindata").as(); @@ -143,7 +143,7 @@ void rpc_plugin::plugin_initialize( const appbase::variables_map& options ) try void rpc_plugin::plugin_startup() { my->daemon_thread = std::thread([this]() { silkworm::log::set_thread_name("rpc-daemon"); - silkrpc::Daemon::run(my->settings, {"trust-evm-rpc", "version: "+appbase::app().full_version_string()}); + silkrpc::Daemon::run(my->settings, {"eos-evm-rpc", "version: "+appbase::app().full_version_string()}); }); } diff --git a/cmd/ship_receiver_plugin.cpp b/cmd/ship_receiver_plugin.cpp index 9aac5043..1ea17162 100644 --- a/cmd/ship_receiver_plugin.cpp +++ b/cmd/ship_receiver_plugin.cpp @@ -259,6 +259,11 @@ class ship_receiver_plugin_impl : std::enable_shared_from_thisnumber + << ", hash:" << silkworm::to_hex(head_header->hash()) + << ", mixHash:" << silkworm::to_hex(head_header->mix_hash); + auto start_from = utils::to_block_num(head_header->mix_hash.bytes) + 1; SILK_INFO << "Canonical header start from block: " << start_from; @@ -310,7 +315,7 @@ void ship_receiver_plugin::set_program_options( appbase::options_description& cl ("ship-endpoint", boost::program_options::value()->default_value("127.0.0.1:8999"), "SHiP host address") ("ship-core-account", boost::program_options::value()->default_value("evmevmevmevm"), - "Account on the core blockchain that hosts the Trust EVM runtime") + "Account on the core blockchain that hosts the EOS EVM Contract") ("ship-start-from-block-id", boost::program_options::value(), "Override Antelope block id to start syncing from" ) ("ship-start-from-block-timestamp", boost::program_options::value(), diff --git a/cmd/ship_receiver_plugin.hpp b/cmd/ship_receiver_plugin.hpp index c519e5a7..4162bc01 100644 --- a/cmd/ship_receiver_plugin.hpp +++ b/cmd/ship_receiver_plugin.hpp @@ -9,7 +9,6 @@ #include "engine_plugin.hpp" #include "sys_plugin.hpp" -#include "chain_state.hpp" #include "channels.hpp" class ship_receiver_plugin : public appbase::plugin { diff --git a/contract/LICENSE b/contract/LICENSE new file mode 100644 index 00000000..2a698715 --- /dev/null +++ b/contract/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2022-2023 EOS Network Foundation (ENF) and its contributors. All rights reserved. + +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/contract/README.md b/contract/README.md index 43a03fb1..41ce613c 100644 --- a/contract/README.md +++ b/contract/README.md @@ -1,23 +1,3 @@ -# EVM runtime contract +# EOS EVM Contract -### Build Contract -``` -git clone //https://github.com/eosnetworkfoundation/TrustEVM -cd TrustEVM/contract -git submodule update --init --recursive -mkdir build -cd build -cmake .. -make -j4 -``` - - -# Build unit tests -Contract must be built using `-DWITH_TEST_ACTIONS` -``` -cd tests -mkdir build -cd build -cmake -Deosio_DIR=/path-to-mandel/build/lib/cmake/eosio .. -make -j4 unit_test -``` +To build, see instructions in the [README](../README.md) at the root of the project. \ No newline at end of file diff --git a/contract/include/evm_runtime/evm_contract.hpp b/contract/include/evm_runtime/evm_contract.hpp index c13e1c84..f6955fba 100644 --- a/contract/include/evm_runtime/evm_contract.hpp +++ b/contract/include/evm_runtime/evm_contract.hpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -36,8 +37,8 @@ class [[eosio::contract]] evm_contract : public contract /** * @brief Initialize EVM contract * - * @param chainid Chain ID of the EVM. Choose 15555 for a production network. - * For test networks, choose either 15556 for a public test network or 25555 for a local test + * @param chainid Chain ID of the EVM. Choose 17777 for a production network. + * For test networks, choose either 15555 for a public test network or 25555 for a local test * network. * @param fee_params See documentation of fee_parameters struct. */ @@ -67,16 +68,18 @@ class [[eosio::contract]] evm_contract : public contract */ [[eosio::action]] void freeze(bool value); + [[eosio::action]] void exec(const exec_input& input, const std::optional& callback); + [[eosio::action]] void pushtx(eosio::name miner, const bytes& rlptx); [[eosio::action]] void open(eosio::name owner); [[eosio::action]] void close(eosio::name owner); - [[eosio::on_notify(TOKEN_ACCOUNT_NAME "::transfer")]] void + [[eosio::on_notify("*::transfer")]] void transfer(eosio::name from, eosio::name to, eosio::asset quantity, std::string memo); - [[eosio::action]] void withdraw(eosio::name owner, eosio::asset quantity); + [[eosio::action]] void withdraw(eosio::name owner, eosio::asset quantity, const eosio::binary_extension &to); /// @return true if all garbage has been collected [[eosio::action]] bool gc(uint32_t max); @@ -128,7 +131,7 @@ class [[eosio::contract]] evm_contract : public contract check((_config.get().status & static_cast(status_flags::frozen)) == 0, "contract is frozen"); } - silkworm::Receipt execute_tx(eosio::name miner, silkworm::Block& block, silkworm::Transaction& tx, silkworm::ExecutionProcessor& ep); + silkworm::Receipt execute_tx(eosio::name miner, silkworm::Block& block, silkworm::Transaction& tx, silkworm::ExecutionProcessor& ep, bool enforce_chain_id); uint64_t get_and_increment_nonce(const name owner); diff --git a/contract/include/evm_runtime/state.hpp b/contract/include/evm_runtime/state.hpp index 6455d380..d5e4cfbb 100644 --- a/contract/include/evm_runtime/state.hpp +++ b/contract/include/evm_runtime/state.hpp @@ -26,11 +26,12 @@ struct db_stats { struct state : State { name _self; name _ram_payer; + bool _read_only; mutable std::map addr2id; mutable std::map addr2code; mutable db_stats stats; - explicit state(name self, name ram_payer) : _self(self), _ram_payer(ram_payer){} + explicit state(name self, name ram_payer, bool read_only=false) : _self(self), _ram_payer(ram_payer), _read_only{read_only}{} std::optional read_account(const evmc::address& address) const noexcept override; diff --git a/contract/include/evm_runtime/tables.hpp b/contract/include/evm_runtime/tables.hpp index cdf90f52..1b774853 100644 --- a/contract/include/evm_runtime/tables.hpp +++ b/contract/include/evm_runtime/tables.hpp @@ -87,7 +87,7 @@ struct [[eosio::table]] [[eosio::contract("evm_contract")]] gcstore { typedef multi_index< "gcstore"_n, gcstore> gc_store_table; -struct balance_with_dust { +struct [[eosio::table("inevm")]] [[eosio::contract("evm_contract")]] balance_with_dust { asset balance = asset(0, token_symbol); uint64_t dust = 0; diff --git a/contract/include/evm_runtime/types.hpp b/contract/include/evm_runtime/types.hpp index 903f1e18..aa050640 100644 --- a/contract/include/evm_runtime/types.hpp +++ b/contract/include/evm_runtime/types.hpp @@ -37,6 +37,32 @@ namespace evm_runtime { evmc::address to_address(const bytes& addr); evmc::bytes32 to_bytes32(const bytes& data); uint256 to_uint256(const bytes& value); + + struct exec_input { + std::optional context; + std::optional from; + bytes to; + bytes data; + std::optional value; + + EOSLIB_SERIALIZE(exec_input, (context)(from)(to)(data)(value)); + }; + + struct exec_callback { + eosio::name contract; + eosio::name action; + + EOSLIB_SERIALIZE(exec_callback, (contract)(action)); + }; + + struct exec_output { + int32_t status; + bytes data; + std::optional context; + + EOSLIB_SERIALIZE(exec_output, (status)(data)(context)); + }; + } //namespace evm_runtime namespace eosio { diff --git a/contract/src/CMakeLists.txt b/contract/src/CMakeLists.txt index 8de9b468..362b409d 100644 --- a/contract/src/CMakeLists.txt +++ b/contract/src/CMakeLists.txt @@ -20,7 +20,7 @@ if (WITH_LOGTIME) endif() add_compile_definitions(ANTELOPE) -add_compile_definitions(PROJECT_VERSION="0.3.0") +add_compile_definitions(PROJECT_VERSION="0.5.1") # ethash list(APPEND SOURCES @@ -32,9 +32,6 @@ list(APPEND SOURCES # evmone list(APPEND SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/instructions_calls.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/advanced_analysis.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/advanced_instructions.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/advanced_execution.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/vm.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/eof.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/evmone/lib/evmone/baseline.cpp @@ -87,5 +84,5 @@ target_compile_options(evm_runtime PUBLIC --no-missing-ricardian-clause) if (WITH_LARGE_STACK) target_link_options(evm_runtime PUBLIC --stack-size=50000000) else() - target_link_options(evm_runtime PUBLIC --stack-size=19984) + target_link_options(evm_runtime PUBLIC --stack-size=46080) endif() diff --git a/contract/src/actions.cpp b/contract/src/actions.cpp index 618a175a..f2cb43de 100644 --- a/contract/src/actions.cpp +++ b/contract/src/actions.cpp @@ -25,6 +25,11 @@ #define LOGTIME(MSG) #endif +extern "C" { +__attribute__((eosio_wasm_import)) +void set_action_return_value(void*, size_t); +} + namespace silkworm { // provide no-op bloom Bloom logs_bloom(const std::vector& logs) { @@ -142,24 +147,54 @@ void evm_contract::freeze(bool value) { void check_result( ValidationResult r, const Transaction& txn, const char* desc ) { if( r == ValidationResult::kOk ) return; - - if( r == ValidationResult::kMissingSender ) { - eosio::print("txn.from.has_value is empty\n"); - } else if ( r == ValidationResult::kSenderNoEOA ) { - eosio::print("get_code_hash is empty\n"); - } else if ( r == ValidationResult::kWrongNonce ) { - eosio::print("invalid nonce:", txn.nonce, "\n"); - } else if ( r == ValidationResult::kInsufficientFunds ) { - eosio::print("get_balance of from insufficient\n"); - } else if ( r == ValidationResult::kBlockGasLimitExceeded ) { - eosio::print("available_gas\n"); + std::string err_msg = std::string(desc) + ": " + std::to_string(uint64_t(r)); + + switch (r) { + case ValidationResult::kWrongChainId: + err_msg += " Wrong chain id"; + break; + case ValidationResult::kUnsupportedTransactionType: + err_msg += " Unsupported transaction type"; + break; + case ValidationResult::kMaxFeeLessThanBase: + err_msg += " Max fee per gas less than block base fee"; + break; + case ValidationResult::kMaxPriorityFeeGreaterThanMax: + err_msg += " Max priority fee per gas greater than max fee per gas"; + break; + case ValidationResult::kInvalidSignature: + err_msg += " Invalid signature"; + break; + case ValidationResult::kIntrinsicGas: + err_msg += " Intrinsic gas too low"; + break; + case ValidationResult::kNonceTooHigh: + err_msg += " Nonce too high"; + break; + case ValidationResult::kMissingSender: + err_msg += " Missing sender"; + break; + case ValidationResult::kSenderNoEOA: + err_msg += " Sender is not EOA"; + break; + case ValidationResult::kWrongNonce: + err_msg += " Wrong nonce"; + break; + case ValidationResult::kInsufficientFunds: + err_msg += " Insufficient funds"; + break; + case ValidationResult::kBlockGasLimitExceeded: + err_msg += " Block gas limit exceeded"; + break; + default: + break; } - - eosio::print( "ERR: ", uint64_t(r), "\n" ); - eosio::check( false, desc ); + + eosio::print(err_msg.c_str()); + eosio::check( false, std::move(err_msg)); } -Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& tx, silkworm::ExecutionProcessor& ep ) { +Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& tx, silkworm::ExecutionProcessor& ep, bool enforce_chain_id) { //when being called as an inline action, clutch in allowance for reserved addresses & signatures by setting from_self=true const bool from_self = get_sender() == get_self(); @@ -209,6 +244,10 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& else if(is_reserved_address(*tx.from)) check(from_self, "bridge signature used outside of bridge transaction"); + if(enforce_chain_id && !from_self) { + check(tx.chain_id.has_value(), "tx without chain-id"); + } + ValidationResult r = consensus::pre_validate_transaction(tx, ep.evm().block().header.number, ep.evm().config(), ep.evm().block().header.base_fee_per_gas); check_result( r, tx, "pre_validate_transaction error" ); @@ -293,6 +332,49 @@ Receipt evm_contract::execute_tx( eosio::name miner, Block& block, Transaction& return receipt; } +void evm_contract::exec(const exec_input& input, const std::optional& callback) { + + assert_unfrozen(); + + const auto& current_config = _config.get(); + std::optional> found_chain_config = lookup_known_chain(current_config.chainid); + check( found_chain_config.has_value(), "failed to find expected chain config" ); + + evm_common::block_mapping bm(current_config.genesis_time.sec_since_epoch()); + + Block block; + evm_common::prepare_block_header(block.header, bm, get_self().value, + bm.timestamp_to_evm_block_num(eosio::current_time_point().time_since_epoch().count())); + + evm_runtime::state state{get_self(), get_self(), true}; + IntraBlockState ibstate{state}; + + EVM evm{block, ibstate, *found_chain_config.value().second}; + + Transaction txn; + txn.to = to_address(input.to); + txn.data = Bytes{input.data.begin(), input.data.end()}; + txn.from = input.from.has_value() ? to_address(input.from.value()) : evmc::address{}; + txn.value = input.value.has_value() ? to_uint256(input.value.value()) : 0; + + const CallResult vm_res{evm.execute(txn, 0x7ffffffffff)}; + + exec_output output{ + .status = int32_t(vm_res.status), + .data = bytes{vm_res.data.begin(), vm_res.data.end()}, + .context = input.context + }; + + if(callback.has_value()) { + const auto& cb = callback.value(); + action(std::vector{}, cb.contract, cb.action, output + ).send(); + } else { + auto output_bin = eosio::pack(output); + set_action_return_value(output_bin.data(), output_bin.size()); + } +} + void evm_contract::pushtx( eosio::name miner, const bytes& rlptx ) { LOGTIME("EVM START"); @@ -324,10 +406,11 @@ void evm_contract::pushtx( eosio::name miner, const bytes& rlptx ) { check(tx.max_priority_fee_per_gas == tx.max_fee_per_gas, "max_priority_fee_per_gas must be equal to max_fee_per_gas"); check(tx.max_fee_per_gas >= current_config.gas_price, "gas price is too low"); - auto receipt = execute_tx(miner, block, tx, ep); + auto receipt = execute_tx(miner, block, tx, ep, true); engine.finalize(ep.state(), ep.evm().block(), ep.evm().revision()); ep.state().write_to_db(ep.evm().block().header.number); + LOGTIME("EVM END"); } void evm_contract::open(eosio::name owner) { @@ -437,7 +520,7 @@ void evm_contract::handle_evm_transfer(eosio::asset quantity, const std::string& void evm_contract::transfer(eosio::name from, eosio::name to, eosio::asset quantity, std::string memo) { assert_unfrozen(); - eosio::check(quantity.symbol == token_symbol, "received unexpected token"); + eosio::check(get_code() == token_account && quantity.symbol == token_symbol, "received unexpected token"); if(to != get_self() || from == get_self()) return; @@ -450,7 +533,7 @@ void evm_contract::transfer(eosio::name from, eosio::name to, eosio::asset quant eosio::check(false, "memo must be either 0x EVM address or already opened account name to credit deposit to"); } -void evm_contract::withdraw(eosio::name owner, eosio::asset quantity) { +void evm_contract::withdraw(eosio::name owner, eosio::asset quantity, const eosio::binary_extension &to) { assert_unfrozen(); require_auth(owner); @@ -463,7 +546,7 @@ void evm_contract::withdraw(eosio::name owner, eosio::asset quantity) { }); token::transfer_action transfer_act(token_account, {{get_self(), "active"_n}}); - transfer_act.send(get_self(), owner, quantity, std::string("Withdraw from EVM balance")); + transfer_act.send(get_self(), to.has_value() ? *to : owner, quantity, std::string("Withdraw from EVM balance")); } bool evm_contract::gc(uint32_t max) { @@ -491,7 +574,7 @@ bool evm_contract::gc(uint32_t max) { ByteView bv{(const uint8_t*)orlptx->data(), orlptx->size()}; eosio::check(rlp::decode(bv,tx) == DecodingResult::kOk && bv.empty(), "unable to decode transaction"); - execute_tx(eosio::name{}, block, tx, ep); + execute_tx(eosio::name{}, block, tx, ep, false); } engine.finalize(ep.state(), ep.evm().block(), ep.evm().revision()); ep.state().write_to_db(ep.evm().block().header.number); diff --git a/contract/src/state.cpp b/contract/src/state.cpp index 25799d8b..269dda90 100644 --- a/contract/src/state.cpp +++ b/contract/src/state.cpp @@ -94,7 +94,7 @@ void state::begin_block(uint64_t block_number) {} void state::update_account(const evmc::address& address, std::optional initial, std::optional current) { - + check(!_read_only, "ro state"); const bool equal{current == initial}; if(equal) return; @@ -182,6 +182,7 @@ bool state::gc(uint32_t max) { } void state::update_account_code(const evmc::address& address, uint64_t, const evmc::bytes32& code_hash, ByteView code) { + check(!_read_only, "ro state"); account_code_table codes(_self, _self.value); auto inxc = codes.get_index<"by.codehash"_n>(); auto itrc = inxc.find(make_key(code_hash)); @@ -225,6 +226,7 @@ void state::update_account_code(const evmc::address& address, uint64_t, const ev void state::update_storage(const evmc::address& address, uint64_t incarnation, const evmc::bytes32& location, const evmc::bytes32& initial, const evmc::bytes32& current) { + check(!_read_only, "ro state"); account_table accounts(_self, _self.value); auto inx = accounts.get_index<"by.address"_n>(); auto itr = inx.find(make_key(address)); diff --git a/contract/src/utils.cpp b/contract/src/utils.cpp index ad6cddd4..03203e8f 100644 --- a/contract/src/utils.cpp +++ b/contract/src/utils.cpp @@ -41,6 +41,7 @@ bytes to_bytes(const evmc::address& addr) { evmc::address to_address(const bytes& addr) { evmc::address res; + eosio::check(addr.size() == 20, "wrong length"); memcpy(res.bytes, addr.data(), 20); return res; } diff --git a/contract/tests/CMakeLists.txt b/contract/tests/CMakeLists.txt index 59ec8224..ab15b757 100644 --- a/contract/tests/CMakeLists.txt +++ b/contract/tests/CMakeLists.txt @@ -32,6 +32,8 @@ add_eosio_test_executable( unit_test ${CMAKE_SOURCE_DIR}/mapping_tests.cpp ${CMAKE_SOURCE_DIR}/gas_fee_tests.cpp ${CMAKE_SOURCE_DIR}/blockhash_tests.cpp + ${CMAKE_SOURCE_DIR}/exec_tests.cpp + ${CMAKE_SOURCE_DIR}/chainid_tests.cpp ${CMAKE_SOURCE_DIR}/main.cpp ${CMAKE_SOURCE_DIR}/silkworm/core/silkworm/rlp/encode.cpp ${CMAKE_SOURCE_DIR}/silkworm/core/silkworm/rlp/decode.cpp diff --git a/contract/tests/basic_evm_tester.cpp b/contract/tests/basic_evm_tester.cpp index afdd092b..a9d7ece5 100644 --- a/contract/tests/basic_evm_tester.cpp +++ b/contract/tests/basic_evm_tester.cpp @@ -1,4 +1,5 @@ #include "basic_evm_tester.hpp" +#include namespace fc { @@ -94,10 +95,15 @@ key256_t evm_eoa::address_key256() const return fixed_bytes<32>(buffer).get_array(); } -void evm_eoa::sign(silkworm::Transaction& trx) +void evm_eoa::sign(silkworm::Transaction& trx) { + sign(trx, basic_evm_tester::evm_chain_id); +} + +void evm_eoa::sign(silkworm::Transaction& trx, std::optional evm_chain_id) { silkworm::Bytes rlp; - trx.chain_id = basic_evm_tester::evm_chain_id; + if(evm_chain_id.has_value()) + trx.chain_id = evm_chain_id.value(); trx.nonce = next_nonce++; silkworm::rlp::encode(rlp, trx, true, false); ethash::hash256 hash{silkworm::keccak256(rlp)}; @@ -166,6 +172,44 @@ transaction_trace_ptr basic_evm_tester::transfer_token(name from, name to, asset token_account_name, "transfer"_n, from, mvo()("from", from)("to", to)("quantity", quantity)("memo", memo)); } +action basic_evm_tester::get_action( account_name code, action_name acttype, vector auths, + const bytes& data )const { try { + const auto& acnt = control->get_account(code); + auto abi = acnt.get_abi(); + chain::abi_serializer abis(abi, abi_serializer::create_yield_function( abi_serializer_max_time )); + + string action_type_name = abis.get_action_type(acttype); + FC_ASSERT( action_type_name != string(), "unknown action type ${a}", ("a",acttype) ); + + action act; + act.account = code; + act.name = acttype; + act.authorization = auths; + act.data = data; + return act; +} FC_CAPTURE_AND_RETHROW() } + +transaction_trace_ptr basic_evm_tester::push_action( const account_name& code, + const action_name& acttype, + const account_name& actor, + const bytes& data, + uint32_t expiration, + uint32_t delay_sec) +{ + vector auths; + auths.push_back( permission_level{actor, config::active_name} ); + try { + signed_transaction trx; + trx.actions.emplace_back( get_action( code, acttype, auths, data ) ); + set_transaction_headers( trx, expiration, delay_sec ); + for (const auto& auth : auths) { + trx.sign( get_private_key( auth.actor, auth.permission.to_string() ), control->get_chain_id() ); + } + + return push_transaction( trx ); +} FC_CAPTURE_AND_RETHROW( (code)(acttype)(auths)(data)(expiration)(delay_sec) ) } + + void basic_evm_tester::init(const uint64_t chainid, const uint64_t gas_price, const uint32_t miner_cut, @@ -242,6 +286,10 @@ basic_evm_tester::generate_tx(const evmc::address& to, const intx::uint256& valu .value = value, }; } +transaction_trace_ptr basic_evm_tester::exec(const exec_input& input, const std::optional& callback) { + auto binary_data = fc::raw::pack>(input, callback); + return basic_evm_tester::push_action(evm_account_name, "exec"_n, evm_account_name, bytes{binary_data.begin(), binary_data.end()}); +} void basic_evm_tester::pushtx(const silkworm::Transaction& trx, name miner) { diff --git a/contract/tests/basic_evm_tester.hpp b/contract/tests/basic_evm_tester.hpp index a4db6066..56b79940 100644 --- a/contract/tests/basic_evm_tester.hpp +++ b/contract/tests/basic_evm_tester.hpp @@ -90,6 +90,25 @@ struct fee_parameters std::optional ingress_bridge_fee; }; +struct exec_input { + std::optional context; + std::optional from; + bytes to; + bytes data; + std::optional value; +}; + +struct exec_callback { + name contract; + name action; +}; + +struct exec_output { + int32_t status; + bytes data; + std::optional context; +}; + } // namespace evm_test @@ -100,6 +119,10 @@ FC_REFLECT(evm_test::account_object, (id)(address)(nonce)(balance)) FC_REFLECT(evm_test::storage_slot, (id)(key)(value)) FC_REFLECT(evm_test::fee_parameters, (gas_price)(miner_cut)(ingress_bridge_fee)) +FC_REFLECT(evm_test::exec_input, (context)(from)(to)(data)(value)) +FC_REFLECT(evm_test::exec_callback, (contract)(action)) +FC_REFLECT(evm_test::exec_output, (status)(data)(context)) + namespace evm_test { class evm_eoa { @@ -111,6 +134,7 @@ class evm_eoa key256_t address_key256() const; void sign(silkworm::Transaction& trx); + void sign(silkworm::Transaction& trx, std::optional chain_id); ~evm_eoa(); @@ -126,6 +150,8 @@ class evm_eoa class basic_evm_tester : public testing::validating_tester { public: + using testing::validating_tester::push_action; + static constexpr name token_account_name = "eosio.token"_n; static constexpr name faucet_account_name = "faucet"_n; static constexpr name evm_account_name = "evm"_n; @@ -147,6 +173,16 @@ class basic_evm_tester : public testing::validating_tester transaction_trace_ptr transfer_token(name from, name to, asset quantity, std::string memo = ""); + action get_action( account_name code, action_name acttype, vector auths, + const bytes& data )const; + + transaction_trace_ptr push_action( const account_name& code, + const action_name& acttype, + const account_name& actor, + const bytes& data, + uint32_t expiration = DEFAULT_EXPIRATION_DELTA, + uint32_t delay_sec = 0 ); + void init(const uint64_t chainid = evm_chain_id, const uint64_t gas_price = suggested_gas_price, const uint32_t miner_cut = suggested_miner_cut, @@ -162,6 +198,7 @@ class basic_evm_tester : public testing::validating_tester silkworm::Transaction generate_tx(const evmc::address& to, const intx::uint256& value, uint64_t gas_limit = 21000) const; + transaction_trace_ptr exec(const exec_input& input, const std::optional& callback); void pushtx(const silkworm::Transaction& trx, name miner = evm_account_name); evmc::address deploy_contract(evm_eoa& eoa, evmc::bytes bytecode); diff --git a/contract/tests/blockhash_tests.cpp b/contract/tests/blockhash_tests.cpp index 7c562496..d2457c05 100644 --- a/contract/tests/blockhash_tests.cpp +++ b/contract/tests/blockhash_tests.cpp @@ -13,7 +13,7 @@ struct blockhash_evm_tester : basic_evm_tester { BOOST_AUTO_TEST_SUITE(blockhash_evm_tests) BOOST_FIXTURE_TEST_CASE(blockhash_tests, blockhash_evm_tester) try { - // tests/leap/nodeos_trust_evm_server/contracts/Blockhash.sol + // tests/leap/nodeos_eos_evm_server/contracts/Blockhash.sol const std::string blockhash_bytecode = "608060405234801561001057600080fd5b506102e8806100206000396000f3fe608060405234801561001057600080fd5b5060" "04361061007c5760003560e01c8063c059239b1161005b578063c059239b146100c7578063c835de3c146100e5578063edb572" diff --git a/contract/tests/chainid_tests.cpp b/contract/tests/chainid_tests.cpp new file mode 100644 index 00000000..99e3ac89 --- /dev/null +++ b/contract/tests/chainid_tests.cpp @@ -0,0 +1,42 @@ +#include "basic_evm_tester.hpp" +#include + +using namespace evm_test; +struct chain_id_tester : basic_evm_tester { + chain_id_tester() { + create_accounts({"alice"_n}); + transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); + init(); + } +}; + +BOOST_AUTO_TEST_SUITE(chainid_evm_tests) +BOOST_FIXTURE_TEST_CASE(chainid_tests, chain_id_tester) try { + + // Bridge transactions are sent without chain id specified (must succeed) + evm_eoa evm1; + const int64_t to_bridge = 1000000; //100.000 EOS + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + auto balance_evm1 = evm_balance(evm1); + BOOST_REQUIRE(balance_evm1.has_value() && *balance_evm1 == 100_ether); + + // Transactions without chain id specified must fail + evm_eoa evm2; + auto txn = generate_tx(evm2.address, 1_ether, 21000); + evm1.sign(txn, {}); + BOOST_REQUIRE_EXCEPTION(pushtx(txn), eosio_assert_message_exception, + [](const eosio_assert_message_exception& e) {return testing::expect_assert_message(e, "assertion failure with message: tx without chain-id");}); + + // reuse nonce since previous signed transaction was rejected + evm1.next_nonce--; + + // Transactions with chain id must succeed + evm_eoa evm3; + txn = generate_tx(evm3.address, 20_ether, 21000); + evm1.sign(txn, basic_evm_tester::evm_chain_id); + pushtx(txn); + auto balance_evm3 = evm_balance(evm3); + BOOST_REQUIRE(balance_evm3.has_value() && *balance_evm3 == 20_ether); + +} FC_LOG_AND_RETHROW() +BOOST_AUTO_TEST_SUITE_END() diff --git a/contract/tests/contracts.hpp.in b/contract/tests/contracts.hpp.in index 5519d347..f606450c 100644 --- a/contract/tests/contracts.hpp.in +++ b/contract/tests/contracts.hpp.in @@ -37,6 +37,9 @@ struct contracts { static std::vector evm_runtime_wasm() { return read_wasm("${CMAKE_CURRENT_SOURCE_DIR}/../build/evm_runtime/evm_runtime.wasm"); } static std::vector evm_runtime_abi() { return read_abi("${CMAKE_CURRENT_SOURCE_DIR}/../build/evm_runtime/evm_runtime.abi"); } + static std::vector evm_read_callback_wasm() { return read_wasm("${CMAKE_CURRENT_SOURCE_DIR}/contracts/evm_read_callback/evm_read_callback.wasm"); } + static std::vector evm_read_callback_abi() { return read_abi("${CMAKE_CURRENT_SOURCE_DIR}/contracts/evm_read_callback/evm_read_callback.abi"); } + static std::string eth_test_folder() { return "${CMAKE_CURRENT_SOURCE_DIR}/../../silkworm/third_party/tests"; } diff --git a/contract/tests/contracts/evm_read_callback/evm_read_callback.abi b/contract/tests/contracts/evm_read_callback/evm_read_callback.abi new file mode 100644 index 00000000..c628b1df --- /dev/null +++ b/contract/tests/contracts/evm_read_callback/evm_read_callback.abi @@ -0,0 +1,46 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [], + "structs": [ + { + "name": "callback", + "base": "", + "fields": [ + { + "name": "output", + "type": "exec_output" + } + ] + }, + { + "name": "exec_output", + "base": "", + "fields": [ + { + "name": "status", + "type": "int32" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "context", + "type": "bytes?" + } + ] + } + ], + "actions": [ + { + "name": "callback", + "type": "callback", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [] +} \ No newline at end of file diff --git a/contract/tests/contracts/evm_read_callback/evm_read_callback.cpp b/contract/tests/contracts/evm_read_callback/evm_read_callback.cpp new file mode 100644 index 00000000..75c3991a --- /dev/null +++ b/contract/tests/contracts/evm_read_callback/evm_read_callback.cpp @@ -0,0 +1,8 @@ +#include "evm_read_callback.hpp" +ACTION evm_read_callback::callback( const exec_output& output ) { + output_table outputs(get_self(), get_self().value); + outputs.emplace(get_self(), [&](auto& row){ + row.id = outputs.available_primary_key(); + row.output = output; + }); +} \ No newline at end of file diff --git a/contract/tests/contracts/evm_read_callback/evm_read_callback.hpp b/contract/tests/contracts/evm_read_callback/evm_read_callback.hpp new file mode 100644 index 00000000..5758ae94 --- /dev/null +++ b/contract/tests/contracts/evm_read_callback/evm_read_callback.hpp @@ -0,0 +1,33 @@ +#include +using namespace eosio; + +typedef std::vector bytes; + +struct exec_output { + int32_t status; + bytes data; + std::optional context; + + EOSLIB_SERIALIZE(exec_output, (status)(data)(context)); +}; + +struct exec_output_row { + uint64_t id; + exec_output output; + + uint64_t primary_key()const { + return id; + } + + + EOSLIB_SERIALIZE(exec_output_row, (id)(output)); +}; + +typedef multi_index<"output"_n, exec_output_row> output_table; + +CONTRACT evm_read_callback : public contract { + public: + using contract::contract; + + ACTION callback( const exec_output& output ); +}; \ No newline at end of file diff --git a/contract/tests/contracts/evm_read_callback/evm_read_callback.wasm b/contract/tests/contracts/evm_read_callback/evm_read_callback.wasm new file mode 100755 index 00000000..8e5ab67c Binary files /dev/null and b/contract/tests/contracts/evm_read_callback/evm_read_callback.wasm differ diff --git a/contract/tests/contracts/solidity/Distributor.sol b/contract/tests/contracts/solidity/Distributor.sol new file mode 100644 index 00000000..632b790e --- /dev/null +++ b/contract/tests/contracts/solidity/Distributor.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract Distributor { + function distribute(address[] calldata to, uint256[] calldata amt) payable external { + uint256 total = 0; + for (uint256 i = 0; i < to.length; i++) { + payable(to[i]).transfer(amt[i]); + total += amt[i]; + } + + require(total == msg.value); + } +} diff --git a/contract/tests/contracts/solidity/RevertTest.sol b/contract/tests/contracts/solidity/RevertTest.sol new file mode 100644 index 00000000..c62524ad --- /dev/null +++ b/contract/tests/contracts/solidity/RevertTest.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract RevertTest { + error ErrorAfterSend(); + + function sendThenFail(address to) payable external { + bool sent = payable(to).send(msg.value); + assert(sent); + revert ErrorAfterSend(); + } + + function recover(address toA, address toB) payable external { + try this.sendThenFail{gas: 10000, value: msg.value}(toA) { + assert(false); + } catch(bytes memory err) { + //how? + //require(bytes4(err) == ErrorAfterSend.selector); + } + + payable(toB).transfer(msg.value); + } +} diff --git a/contract/tests/evm_runtime_tests.cpp b/contract/tests/evm_runtime_tests.cpp index fc49e654..517c7aff 100644 --- a/contract/tests/evm_runtime_tests.cpp +++ b/contract/tests/evm_runtime_tests.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include "eosio.system_tester.hpp" diff --git a/contract/tests/exec_tests.cpp b/contract/tests/exec_tests.cpp new file mode 100644 index 00000000..761de7be --- /dev/null +++ b/contract/tests/exec_tests.cpp @@ -0,0 +1,251 @@ +#include "basic_evm_tester.hpp" +#include + +using intx::operator""_u256; + +using namespace evm_test; +using eosio::testing::eosio_assert_message_is; +struct exec_output_row { + uint64_t id; + exec_output output; +}; +FC_REFLECT(exec_output_row, (id)(output)) + +struct exec_evm_tester : basic_evm_tester { + exec_evm_tester() { + create_accounts({"alice"_n}); + transfer_token(faucet_account_name, "alice"_n, make_asset(10000'0000)); + init(); + } + + evmc::address deploy_evm_token_contract(evm_eoa& eoa) { + + // tests/leap/nodeos_eos_evm_server/contracts/Token.sol + const std::string token_bytecode = + "60806040523480156200001157600080fd5b506040518060400160405280600781526020017f59756e69706572000000000000000000000000000000000000000000000000008152506040518060400160405280600381526020017f59554e000000000000000000000000000000000000000000000000000000000081525081600390816200008f9190620004e6565b508060049081620000a19190620004e6565b505050620000e633620000b9620000ec60201b60201c565b60ff16600a620000ca919062000750565b620f4240620000da9190620007a1565b620000f560201b60201c565b620008d8565b60006012905090565b600073ffffffffffffff" + "ffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160362000167576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200015e906200084d565b60405180910390fd5b6200017b600083836200026260201b60201c565b80600260008282546200018f91906200086f565b92505081905550806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffff" + "ffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620002429190620008bb565b60405180910390a36200025e600083836200026760201b60201c565b5050565b505050565b505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002ee57607f821691505b6020821081036200030457620003036200" + "02a6565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026200036e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826200032f565b6200037a86836200032f565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003c7620003c1620003bb8462000392565b6200039c565b62000392565b9050919050565b6000819050919050565b620003e383620003a6565b620003fb620003f282620003ce565b8484546200033c565b82555050" + "5050565b600090565b6200041262000403565b6200041f818484620003d8565b505050565b5b8181101562000447576200043b60008262000408565b60018101905062000425565b5050565b601f821115620004965762000460816200030a565b6200046b846200031f565b810160208510156200047b578190505b620004936200048a856200031f565b83018262000424565b50505b505050565b600082821c905092915050565b6000620004bb600019846008026200049b565b1980831691505092915050565b6000620004d68383620004a8565b9150826002028217905092915050565b620004f1826200026c565b67ffffffffffffffff8111156200" + "050d576200050c62000277565b5b620005198254620002d5565b620005268282856200044b565b600060209050601f8311600181146200055e576000841562000549578287015190505b620005558582620004c8565b865550620005c5565b601f1984166200056e866200030a565b60005b82811015620005985784890151825560018201915060208501945060208101905062000571565b86831015620005b85784890151620005b4601f891682620004a8565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600081" + "60011c9050919050565b6000808291508390505b60018511156200065b57808604811115620006335762000632620005cd565b5b6001851615620006435780820291505b80810290506200065385620005fc565b945062000613565b94509492505050565b60008262000676576001905062000749565b8162000686576000905062000749565b81600181146200069f5760028114620006aa57620006e0565b600191505062000749565b60ff841115620006bf57620006be620005cd565b5b8360020a915084821115620006d957620006d8620005cd565b5b5062000749565b5060208310610133831016604e8410600b84101617156200071a5782820a90" + "5083811115620007145762000713620005cd565b5b62000749565b62000729848484600162000609565b92509050818404811115620007435762000742620005cd565b5b81810290505b9392505050565b60006200075d8262000392565b91506200076a8362000392565b9250620007997fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848462000664565b905092915050565b6000620007ae8262000392565b9150620007bb8362000392565b9250828202620007cb8162000392565b91508282048414831517620007e557620007e4620005cd565b5b5092915050565b600082825260208201905092915050565b7f45" + "524332303a206d696e7420746f20746865207a65726f206164647265737300600082015250565b600062000835601f83620007ec565b91506200084282620007fd565b602082019050919050565b60006020820190508181036000830152620008688162000826565b9050919050565b60006200087c8262000392565b9150620008898362000392565b9250828201905080821115620008a457620008a3620005cd565b5b92915050565b620008b58162000392565b82525050565b6000602082019050620008d26000830184620008aa565b92915050565b61122f80620008e86000396000f3fe608060405234801561001057600080fd5b50600436106100" + "a95760003560e01c80633950935111610071578063395093511461016857806370a082311461019857806395d89b41146101c8578063a457c2d7146101e6578063a9059cbb14610216578063dd62ed3e14610246576100a9565b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100fc57806323b872dd1461011a578063313ce5671461014a575b600080fd5b6100b6610276565b6040516100c39190610b0c565b60405180910390f35b6100e660048036038101906100e19190610bc7565b610308565b6040516100f39190610c22565b60405180910390f35b61010461032b565b6040516101119190610c4c565b604051809103" + "90f35b610134600480360381019061012f9190610c67565b610335565b6040516101419190610c22565b60405180910390f35b610152610364565b60405161015f9190610cd6565b60405180910390f35b610182600480360381019061017d9190610bc7565b61036d565b60405161018f9190610c22565b60405180910390f35b6101b260048036038101906101ad9190610cf1565b6103a4565b6040516101bf9190610c4c565b60405180910390f35b6101d06103ec565b6040516101dd9190610b0c565b60405180910390f35b61020060048036038101906101fb9190610bc7565b61047e565b60405161020d9190610c22565b60405180910390f35b61" + "0230600480360381019061022b9190610bc7565b6104f5565b60405161023d9190610c22565b60405180910390f35b610260600480360381019061025b9190610d1e565b610518565b60405161026d9190610c4c565b60405180910390f35b60606003805461028590610d8d565b80601f01602080910402602001604051908101604052809291908181526020018280546102b190610d8d565b80156102fe5780601f106102d3576101008083540402835291602001916102fe565b820191906000526020600020905b8154815290600101906020018083116102e157829003601f168201915b5050505050905090565b60008061031361059f565b90506103" + "208185856105a7565b600191505092915050565b6000600254905090565b60008061034061059f565b905061034d858285610770565b6103588585856107fc565b60019150509392505050565b60006012905090565b60008061037861059f565b905061039981858561038a8589610518565b6103949190610ded565b6105a7565b600191505092915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b6060600480546103fb90610d8d565b80601f01602080910402602001604051908101604052809291908181" + "5260200182805461042790610d8d565b80156104745780601f1061044957610100808354040283529160200191610474565b820191906000526020600020905b81548152906001019060200180831161045757829003601f168201915b5050505050905090565b60008061048961059f565b905060006104978286610518565b9050838110156104dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104d390610e93565b60405180910390fd5b6104e982868684036105a7565b60019250505092915050565b60008061050061059f565b905061050d8185856107fc565b60019150509291505056" + "5b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603610616576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161060d90610f25565b60405180910390fd5b60" + "0073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603610685576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067c90610fb7565b60405180910390fd5b80600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffff" + "ffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516107639190610c4c565b60405180910390a3505050565b600061077c8484610518565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146107f657818110156107e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107df90611023565b60405180910390fd5b6107f584848484036105a7565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ff" + "ffffffffffffffffffffffffffffffffffffff160361086b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610862906110b5565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190611147565b60405180910390fd5b6108e5838383610a72565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16" + "81526020019081526020016000205490508181101561096b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610962906111d9565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffff" + "ffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610a599190610c4c565b60405180910390a3610a6c848484610a77565b50505050565b505050565b505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ab6578082015181840152602081019050610a9b565b60008484015250505050565b6000601f19601f8301169050919050565b6000610ade82610a7c565b610ae88185610a87565b9350610af8818560208601610a98565b610b0181610ac2565b840191505092915050565b6000602082019050818103" + "6000830152610b268184610ad3565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b5e82610b33565b9050919050565b610b6e81610b53565b8114610b7957600080fd5b50565b600081359050610b8b81610b65565b92915050565b6000819050919050565b610ba481610b91565b8114610baf57600080fd5b50565b600081359050610bc181610b9b565b92915050565b60008060408385031215610bde57610bdd610b2e565b5b6000610bec85828601610b7c565b9250506020610bfd85828601610bb2565b9150509250929050565b60008115159050919050565b610c1c81" + "610c07565b82525050565b6000602082019050610c376000830184610c13565b92915050565b610c4681610b91565b82525050565b6000602082019050610c616000830184610c3d565b92915050565b600080600060608486031215610c8057610c7f610b2e565b5b6000610c8e86828701610b7c565b9350506020610c9f86828701610b7c565b9250506040610cb086828701610bb2565b9150509250925092565b600060ff82169050919050565b610cd081610cba565b82525050565b6000602082019050610ceb6000830184610cc7565b92915050565b600060208284031215610d0757610d06610b2e565b5b6000610d1584828501610b7c565b9150" + "5092915050565b60008060408385031215610d3557610d34610b2e565b5b6000610d4385828601610b7c565b9250506020610d5485828601610b7c565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610da557607f821691505b602082108103610db857610db7610d5e565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610df882610b91565b9150610e0383610b91565b9250828201905080821115610e1b57610e1a610d" + "be565b5b92915050565b7f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760008201527f207a65726f000000000000000000000000000000000000000000000000000000602082015250565b6000610e7d602583610a87565b9150610e8882610e21565b604082019050919050565b60006020820190508181036000830152610eac81610e70565b9050919050565b7f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460008201527f7265737300000000000000000000000000000000000000000000000000000000602082015250565b6000610f0f602483610a87565b9150610f1a82610e" + "b3565b604082019050919050565b60006020820190508181036000830152610f3e81610f02565b9050919050565b7f45524332303a20617070726f766520746f20746865207a65726f20616464726560008201527f7373000000000000000000000000000000000000000000000000000000000000602082015250565b6000610fa1602283610a87565b9150610fac82610f45565b604082019050919050565b60006020820190508181036000830152610fd081610f94565b9050919050565b7f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000600082015250565b600061100d601d83610a87565b915061101882610fd756" + "5b602082019050919050565b6000602082019050818103600083015261103c81611000565b9050919050565b7f45524332303a207472616e736665722066726f6d20746865207a65726f20616460008201527f6472657373000000000000000000000000000000000000000000000000000000602082015250565b600061109f602583610a87565b91506110aa82611043565b604082019050919050565b600060208201905081810360008301526110ce81611092565b9050919050565b7f45524332303a207472616e7366657220746f20746865207a65726f206164647260008201527f657373000000000000000000000000000000000000000000000000" + "0000000000602082015250565b6000611131602383610a87565b915061113c826110d5565b604082019050919050565b6000602082019050818103600083015261116081611124565b9050919050565b7f45524332303a207472616e7366657220616d6f756e742065786365656473206260008201527f616c616e63650000000000000000000000000000000000000000000000000000602082015250565b60006111c3602683610a87565b91506111ce82611167565b604082019050919050565b600060208201905081810360008301526111f2816111b6565b905091905056fea26469706673582212209f06a5f990bd2f3566d6e762a8f54261d285a7dd" + "ad2b5e289965e9058fa33af264736f6c63430008110033"; + + // Deploy token contract + return deploy_contract(eoa, evmc::from_hex(token_bytecode).value()); + } + + void erc20_transfer(const evmc::address& contract_addr, evm_eoa& evm1, const evm_eoa& evm2, uint64_t amount) { + + auto txn = generate_tx(contract_addr, 0, 500'000); + + silkworm::Bytes data; + data += evmc::from_hex("a9059cbb").value(); // sha3(transfer(address,uint256))[:4] + data += silkworm::to_bytes32(evm2.address); // to + data += evmc::bytes32{amount}; // value + txn.data = data; + + evm1.sign(txn); + pushtx(txn); + } + + transaction_trace_ptr erc20_balance(const evmc::address& contract_addr, const evm_eoa& account, std::optional callback={}, std::optional context={}) { + exec_input input; + input.context = context; + input.to = bytes{std::begin(contract_addr.bytes), std::end(contract_addr.bytes)}; + + silkworm::Bytes data; + data += evmc::from_hex("70a08231").value(); // sha3(balanceOf(address))[:4] + data += silkworm::to_bytes32(account.address); + input.data = bytes{data.begin(), data.end()}; + + return exec(input, callback); + } + +}; + +BOOST_AUTO_TEST_SUITE(exec_evm_tests) +BOOST_FIXTURE_TEST_CASE(read_erc20_balance_from_return_value, exec_evm_tester) try { + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + // Deploy contract + auto token_addr = deploy_evm_token_contract(evm1); + + // Transfer 1234 YUN + evm_eoa evm2; + erc20_transfer(token_addr, evm1, evm2, 1234); + + // Read evm2 balance + auto res = erc20_balance(token_addr, evm2); + + BOOST_REQUIRE(res); + BOOST_REQUIRE(res->action_traces.size() == 1); + + // Since callback information was not provided the result of the + // execution is returned in the action return_value + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + BOOST_REQUIRE(out.data.size() == 32); + + auto balance = intx::be::unsafe::load(reinterpret_cast(out.data.data())); + BOOST_REQUIRE(balance == 1234); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(read_erc20_balance_in_callback, exec_evm_tester) try { + + // Create the account that will receive the callback + // and set the handling code that will store the callback data into a table + create_accounts({"receiver"_n}); + set_code("receiver"_n, testing::contracts::evm_read_callback_wasm()); + set_abi("receiver"_n, testing::contracts::evm_read_callback_abi().data()); + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + // Deploy contract + auto token_addr = deploy_evm_token_contract(evm1); + + // Transfer 4321 YUN + evm_eoa evm2; + erc20_transfer(token_addr, evm1, evm2, 4321); + + // Read evm2 balance (the result will be stored in the state) + erc20_balance(token_addr, evm2, exec_callback{"receiver"_n, "callback"_n}); + auto row = fc::raw::unpack(get_row_by_account( "receiver"_n, "receiver"_n, "output"_n, name{0})); + + BOOST_REQUIRE(row.output.status == 0); + BOOST_REQUIRE(row.output.data.size() == 32); + BOOST_REQUIRE(row.output.context.has_value() == false); + + auto balance = intx::be::unsafe::load(reinterpret_cast(row.output.data.data())); + BOOST_REQUIRE(balance == 4321); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(exec_non_view_function, exec_evm_tester) try { + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + // Deploy contract + auto token_addr = deploy_evm_token_contract(evm1); + + // Try to transfer 1111 YUN tokens from evm1 to evm2 using the exec action + evm_eoa evm2; + + silkworm::Bytes data; + data += evmc::from_hex("a9059cbb").value(); // sha3(transfer(address,uint256))[:4] + data += silkworm::to_bytes32(evm2.address); // to + data += evmc::bytes32{1111}; // value + + exec_input input; + input.context = {}; + input.from = bytes{std::begin(evm1.address.bytes), std::end(evm1.address.bytes)}; + input.to = bytes{std::begin(token_addr.bytes), std::end(token_addr.bytes)}; + input.data = bytes{data.begin(), data.end()}; + input.value = {}; + + exec(input, {}); + + // Read evm1 balance + auto res = erc20_balance(token_addr, evm1); + + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.status == 0); + BOOST_REQUIRE(out.data.size() == 32); + + auto balance = intx::be::unsafe::load(reinterpret_cast(out.data.data())); + BOOST_REQUIRE(balance == 1000000000000000000000000_u256); + +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(exec_with_context, exec_evm_tester) try { + + // Fund evm1 address with 100 EOS + evm_eoa evm1; + const int64_t to_bridge = 1000000; + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), evm1.address_0x()); + + // Deploy contract + auto token_addr = deploy_evm_token_contract(evm1); + + const char context[] = "ABCD"; + + // Recover context (action trace) + auto res = erc20_balance(token_addr, evm1, {}, bytes{std::begin(context), std::end(context)}); + auto out = fc::raw::unpack(res->action_traces[0].return_value); + BOOST_REQUIRE(out.context.has_value() == true); + const auto& ctx = out.context.value(); + dlog("ctx: ${c}", ("c",ctx)); + BOOST_REQUIRE(memcmp(ctx.data(), context, strlen(context))==0); + + // Recover context (callback) + create_accounts({"receiver"_n}); + set_code("receiver"_n, testing::contracts::evm_read_callback_wasm()); + set_abi("receiver"_n, testing::contracts::evm_read_callback_abi().data()); + + erc20_balance(token_addr, evm1, exec_callback{"receiver"_n, "callback"_n}, bytes{std::begin(context), std::end(context)-1}); + auto row = fc::raw::unpack(get_row_by_account( "receiver"_n, "receiver"_n, "output"_n, name{0})); + + BOOST_REQUIRE(row.output.context.has_value() == true); + const auto& ctx2 = row.output.context.value(); + dlog("ctx: ${c}", ("c",ctx2)); + BOOST_REQUIRE(memcmp(ctx2.data(), context, strlen(context)) == 0); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE(wrong_input_params, exec_evm_tester) try { + + exec_input input; + + // No "to" specified + BOOST_REQUIRE_EXCEPTION(exec(input, {}), + eosio_assert_message_exception, eosio_assert_message_is("wrong length")); + + // At least "to" must be specified + evm_eoa evm1; + input.to = bytes{std::begin(evm1.address.bytes), std::end(evm1.address.bytes)}; + exec(input, {}); + + // Correct length for "from" + input.from = bytes{std::begin(evm1.address.bytes), std::end(evm1.address.bytes)}; + exec(input, {}); + + // Incorrect length for "from" + input.from = bytes{std::begin(evm1.address.bytes), std::end(evm1.address.bytes)-1}; + BOOST_REQUIRE_EXCEPTION(exec(input, {}), + eosio_assert_message_exception, eosio_assert_message_is("wrong length")); + +} FC_LOG_AND_RETHROW() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/contract/tests/main.cpp b/contract/tests/main.cpp index 7bae0a12..2341d1f8 100644 --- a/contract/tests/main.cpp +++ b/contract/tests/main.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "eosio.system_tester.hpp" diff --git a/contract/tests/mapping_tests.cpp b/contract/tests/mapping_tests.cpp index ce664060..7ccca48c 100644 --- a/contract/tests/mapping_tests.cpp +++ b/contract/tests/mapping_tests.cpp @@ -98,7 +98,7 @@ try { BOOST_REQUIRE(produce_blocks_until_timestamp_satisfied(timestamp_at_second_boundary)); init(); - time_point_sec expected_genesis_time = control->pending_block_time(); // Rounds down to nearest second. + time_point_sec expected_genesis_time = time_point_sec(control->pending_block_time()); // Rounds down to nearest second. time_point_sec actual_genesis_time = get_genesis_time(); ilog("Genesis time: ${time}", ("time", actual_genesis_time)); @@ -115,7 +115,7 @@ try { BOOST_REQUIRE(produce_blocks_until_timestamp_satisfied(timestamp_not_at_second_boundary)); init(); - time_point_sec expected_genesis_time = control->pending_block_time(); // Rounds down to nearest second. + time_point_sec expected_genesis_time = time_point_sec(control->pending_block_time()); // Rounds down to nearest second. time_point_sec actual_genesis_time = get_genesis_time(); ilog("Genesis time: ${time}", ("time", actual_genesis_time)); diff --git a/contract/tests/native_token_tests.cpp b/contract/tests/native_token_tests.cpp index 09eb074a..081dc8e3 100644 --- a/contract/tests/native_token_tests.cpp +++ b/contract/tests/native_token_tests.cpp @@ -102,9 +102,8 @@ BOOST_FIXTURE_TEST_CASE(basic_deposit_withdraw, native_token_evm_tester_EOS) try } FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE(weird_names, native_token_evm_tester_EOS) try { - //just try some weird account names as memos - +BOOST_FIXTURE_TEST_CASE(invalid_memos, native_token_evm_tester_EOS) try { + //try some weird account names as memos BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "BANANA"), eosio_assert_message_exception, eosio_assert_message_is("character is not in allowed character set for names")); @@ -117,6 +116,25 @@ BOOST_FIXTURE_TEST_CASE(weird_names, native_token_evm_tester_EOS) try { BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), ""), eosio_assert_message_exception, eosio_assert_message_is("memo must be either 0x EVM address or already opened account name to credit deposit to")); + //try some invalid addresses as memos + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "0x"), + eosio_assert_message_exception, eosio_assert_message_is("character is not in allowed character set for names")); + + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "0xf00"), + eosio_assert_message_exception, eosio_assert_message_is("character is not in allowed character set for names")); + + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "0xf00000000000"), + eosio_assert_message_exception, eosio_assert_message_is("memo must be either 0x EVM address or already opened account name to credit deposit to")); + + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "0x1234567890123456789012345678901234567890123456789012345678901234567890"), + eosio_assert_message_exception, eosio_assert_message_is("memo must be either 0x EVM address or already opened account name to credit deposit to")); + + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "0x5b38da6a701c568545dzfcb03fcb875f56beddc4"), + eosio_assert_message_exception, eosio_assert_message_is("unable to parse destination address")); + + BOOST_REQUIRE_EXCEPTION(transfer_token("alice"_n, "evm"_n, make_asset(1'0000), "xx5b38da6a701c568545dafcb03fcb875f56beddc4"), + eosio_assert_message_exception, eosio_assert_message_is("memo must be either 0x EVM address or already opened account name to credit deposit to")); + } FC_LOG_AND_RETHROW() BOOST_FIXTURE_TEST_CASE(non_standard_native_symbol, native_token_evm_tester_SPOON) try { @@ -129,6 +147,55 @@ BOOST_FIXTURE_TEST_CASE(non_standard_native_symbol, native_token_evm_tester_SPOO } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(non_standard_native_symbol2, native_token_evm_tester_EOS) try { + //the symbol 4,EOS is fixed as the expected native symbol. try transfering in a different symbol from eosio.token + + create_accounts({"usdt"_n}); + set_code("usdt"_n, testing::contracts::eosio_token_wasm()); + set_abi("usdt"_n, testing::contracts::eosio_token_abi().data()); + produce_block(); + + symbol usdt_symbol = symbol::from_string("4,USDT"); + push_action("usdt"_n, + "create"_n, + "usdt"_n, + mvo()("issuer", "usdt"_n)("maximum_supply", asset(10'000'000'000'0000, usdt_symbol))); + push_action("usdt"_n, + "issue"_n, + "usdt"_n, + mvo()("to", "usdt"_n)("quantity", asset(1'000'000'000'0000, usdt_symbol))("memo", "")); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(push_action("usdt"_n, + "transfer"_n, + "usdt"_n, + mvo()("from", "usdt")("to", evm_account_name)("quantity", asset(1'000'0000, usdt_symbol))("memo", "")), + eosio_assert_message_exception, eosio_assert_message_is("received unexpected token")); + + create_accounts({"fakeeos"_n}); + set_code("fakeeos"_n, testing::contracts::eosio_token_wasm()); + set_abi("fakeeos"_n, testing::contracts::eosio_token_abi().data()); + produce_block(); + + symbol eos_symbol = symbol::from_string("4,EOS"); + push_action("fakeeos"_n, + "create"_n, + "fakeeos"_n, + mvo()("issuer", "fakeeos"_n)("maximum_supply", asset(10'000'000'000'0000, eos_symbol))); + push_action("fakeeos"_n, + "issue"_n, + "fakeeos"_n, + mvo()("to", "fakeeos"_n)("quantity", asset(1'000'000'000'0000, eos_symbol))("memo", "")); + produce_block(); + + BOOST_REQUIRE_EXCEPTION(push_action("fakeeos"_n, + "transfer"_n, + "fakeeos"_n, + mvo()("from", "fakeeos")("to", evm_account_name)("quantity", asset(1'000'0000, eos_symbol))("memo", "")), + eosio_assert_message_exception, eosio_assert_message_is("received unexpected token")); + +} FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(transfer_notifier_without_init, native_token_evm_tester_noinit) try { //make sure to not accept transfer notifications unless contract has been inited @@ -411,4 +478,307 @@ BOOST_FIXTURE_TEST_CASE(evm_eos_disallow_reserved_zero, native_token_evm_tester_ eosio_assert_message_exception, eosio_assert_message_is("reserved 0 address cannot be used")); } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(withdraw_to, native_token_evm_tester_EOS) try { + + // transfer 10 EOS to evm_account_name + transfer_token(faucet_account_name, evm_account_name, make_asset(10'0000), evm_account_name.to_string()); + + // withdraw from "evm" to itself will fails + BOOST_REQUIRE_EXCEPTION( + push_action(evm_account_name, "withdraw"_n, evm_account_name, mvo()("owner", evm_account_name)("quantity", make_asset(9'0000))), + eosio_assert_message_exception, [](const eosio_assert_message_exception& e) { + return testing::expect_assert_message(e, "assertion failure with message: cannot transfer to self"); + }); + + // withdraw from "evm" with extra paramter "to" set to itself + BOOST_REQUIRE_EXCEPTION(push_action(evm_account_name, "withdraw"_n, evm_account_name, + mvo()("owner", evm_account_name)("quantity", make_asset(9'0000))("to", "evm")), + eosio_assert_message_exception, [](const eosio_assert_message_exception& e) { + return testing::expect_assert_message( + e, "assertion failure with message: cannot transfer to self"); + }); + + // withdraw from "evm" with extra paramter "to" set to non exist account + BOOST_REQUIRE_EXCEPTION(push_action(evm_account_name, "withdraw"_n, evm_account_name, + mvo()("owner", evm_account_name)("quantity", make_asset(9'0000))("to", "nonexistacc")), + eosio_assert_message_exception, [](const eosio_assert_message_exception& e) { + return testing::expect_assert_message( + e, "assertion failure with message: to account does not exist"); + }); + + // withdraw to eosio + push_action(evm_account_name, "withdraw"_n, evm_account_name, mvo()("owner", evm_account_name)("quantity", make_asset(9'0000))("to", "eosio")); + + // cannot withdraw more + BOOST_REQUIRE_EXCEPTION(push_action(evm_account_name, "withdraw"_n, evm_account_name, + mvo()("owner", evm_account_name)("quantity", make_asset(5'0000))("to", "eosio")), + eosio_assert_message_exception, [](const eosio_assert_message_exception& e) { + return testing::expect_assert_message( + e, "assertion failure with message: overdrawn balance"); + }); + + // test with another account + create_accounts({"miner1"_n}); + open("miner1"_n); + transfer_token(faucet_account_name, evm_account_name, make_asset(15'0000), "miner1"); + + BOOST_REQUIRE_EXCEPTION( + push_action(evm_account_name, "withdraw"_n, "miner1"_n, mvo()("owner", "miner1"_n)("quantity", make_asset(15'0001))), + eosio_assert_message_exception, [](const eosio_assert_message_exception& e) { + return testing::expect_assert_message(e, "assertion failure with message: overdrawn balance"); + }); + + // able to withdraw without "to" specified + push_action(evm_account_name, "withdraw"_n, "miner1"_n, mvo()("owner", "miner1"_n)("quantity", make_asset(15'0000))); +} +FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE(evm_eos_loopback, native_token_evm_tester_EOS) try { + //alice does an ingress bridge to her reserved address, which just circles it back around as an egress bridge to herself + { + const int64_t to_bridge = 10'0000; + const int64_t alice_native_before = native_balance("alice"_n); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), silkworm::to_hex(make_reserved_address("alice"_n.to_uint64_t()), true)); + + BOOST_REQUIRE_EQUAL(alice_native_before, native_balance("alice"_n)); + } + + //try it again, but this time send it to another account + { + const int64_t to_bridge = 10'0000; + const int64_t alice_native_before = native_balance("alice"_n); + const int64_t bob_native_before = native_balance("bob"_n); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), silkworm::to_hex(make_reserved_address("bob"_n.to_uint64_t()), true)); + + BOOST_REQUIRE_EQUAL(alice_native_before - native_balance("alice"_n), to_bridge); + BOOST_REQUIRE_EQUAL(native_balance("bob"_n) - bob_native_before, to_bridge); + } + + //try the above again, this time with a bridge free + const int64_t bridge_fee = 1000; + setfeeparams(fee_parameters{.ingress_bridge_fee = make_asset(bridge_fee)}); + produce_block(); + + //alice to her own reserved address again.. + { + const int64_t to_bridge = 10'0000; + const int64_t alice_native_before = native_balance("alice"_n); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), silkworm::to_hex(make_reserved_address("alice"_n.to_uint64_t()), true)); + + BOOST_REQUIRE_EQUAL(alice_native_before - native_balance("alice"_n), bridge_fee); + } + + //alice to bob's reserved address again.. + { + const int64_t to_bridge = 10'0000; + const int64_t alice_native_before = native_balance("alice"_n); + const int64_t bob_native_before = native_balance("bob"_n); + transfer_token("alice"_n, "evm"_n, make_asset(to_bridge), silkworm::to_hex(make_reserved_address("bob"_n.to_uint64_t()), true)); + + BOOST_REQUIRE_EQUAL(alice_native_before - native_balance("alice"_n), to_bridge); + BOOST_REQUIRE_EQUAL(native_balance("bob"_n) - bob_native_before, to_bridge - bridge_fee); + } +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(evm_eos_send_and_revert, native_token_evm_tester_EOS) try { + // RevertTest.sol + const std::string send_and_revert_bytecode = + "608060405234801561001057600080fd5b50610236806100206000396000f3fe6080604052600436106100295760003560e01c" + "806312c385411461002e578063648bf77414610043575b600080fd5b61004161003c366004610195565b610056565b005b6100" + "416100513660046101b7565b6100a4565b6040516000906001600160a01b038316903480156108fc029184818181858888f193" + "5050505090508061008b5761008b6101ea565b60405163cc57499d60e01b815260040160405180910390fd5b6040516312c385" + "4160e01b81526001600160a01b038316600482015230906312c38541906127109034906024016000604051808303818589803b" + "1580156100ea57600080fd5b5088f194505050505080156100fd575060015b610137573d80801561012b576040519150601f19" + "603f3d011682016040523d82523d6000602084013e610130565b606091505b505061013f565b61013f6101ea565b6040516001" + "600160a01b038216903480156108fc02916000818181858888f19350505050158015610174573d6000803e3d6000fd5b505050" + "565b80356001600160a01b038116811461019057600080fd5b919050565b6000602082840312156101a757600080fd5b6101b0" + "82610179565b9392505050565b600080604083850312156101ca57600080fd5b6101d383610179565b91506101e16020840161" + "0179565b90509250929050565b634e487b7160e01b600052600160045260246000fdfea264697066735822122068a077777f85" + "bfd56183f4a75a5d535181050f262de5766cc862098d59758f0a64736f6c63430008110033"; + + evm_eoa evm1, evm2; + transfer_token("alice"_n, "evm"_n, make_asset(50'0000), evm1.address_0x()); + transfer_token("alice"_n, "evm"_n, make_asset(50'0000), evm2.address_0x()); + BOOST_REQUIRE(!!evm_balance(evm2)); + open("bob"_n); + open("carol"_n); + + const evmc::address send_and_revert_addr = deploy_contract(evm1, evmc::from_hex(send_and_revert_bytecode).value()); + + //evm2 is going to send the send_and_revert_addr 1 EOS; initially the contract will send it to bob's reserved + // address, but that will be reverted, and then it will be sent to carol's reserved address. + const int64_t to_bridge = 1'0000; + const intx::uint256 evm2_before = *evm_balance(evm2); + + silkworm::Transaction txn = generate_tx(send_and_revert_addr, 100_szabo * to_bridge, 100'000); + txn.data = evmc::from_hex("0x648bf774" //recover(address,address) + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000" //bob reserved + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb41af488000000000").value(); //carol reserved + evm2.sign(txn); + pushtx(txn); + + BOOST_REQUIRE(vault_balance("bob"_n) == (balance_and_dust{make_asset(0), 0ULL})); + BOOST_REQUIRE(vault_balance("carol"_n) == (balance_and_dust{make_asset(to_bridge), 0})); + BOOST_REQUIRE(evm2_before - *evm_balance(evm2) == 100_szabo * to_bridge + 75215_u256 * get_config().gas_price); +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(evm_eos_fanout, native_token_evm_tester_EOS) try { + // Distributor.sol + const std::string fanout_bytecode = + "608060405234801561001057600080fd5b50610284806100206000396000f3fe60806040526004361061001e5760003560e01c" + "80632929abe614610023575b600080fd5b610036610031366004610154565b610038565b005b6000805b848110156100f45785" + "8582818110610056576100566101c0565b905060200201602081019061006b91906101d6565b6001600160a01b03166108fc85" + "8584818110610089576100896101c0565b905060200201359081150290604051600060405180830381858888f1935050505015" + "80156100bb573d6000803e3d6000fd5b508383828181106100ce576100ce6101c0565b90506020020135826100e0919061021c" + "565b9150806100ec81610235565b91505061003c565b5034811461010157600080fd5b5050505050565b60008083601f840112" + "61011a57600080fd5b50813567ffffffffffffffff81111561013257600080fd5b6020830191508360208260051b8501011115" + "61014d57600080fd5b9250929050565b6000806000806040858703121561016a57600080fd5b843567ffffffffffffffff8082" + "111561018257600080fd5b61018e88838901610108565b909650945060208701359150808211156101a757600080fd5b506101" + "b487828801610108565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b60006020828403" + "12156101e857600080fd5b81356001600160a01b03811681146101ff57600080fd5b9392505050565b634e487b7160e01b6000" + "52601160045260246000fd5b8082018082111561022f5761022f610206565b92915050565b6000600182016102475761024761" + "0206565b506001019056fea26469706673582212209964e90f15129fadc3f3ade8e9fcd3b3d9c3f27617b3bcc28cf29cc5e3e5" + "b5dc64736f6c63430008110033"; + + evm_eoa evm1, evm2; + transfer_token("alice"_n, "evm"_n, make_asset(50'0000), evm1.address_0x()); + transfer_token("alice"_n, "evm"_n, make_asset(10'0000), evm2.address_0x()); + BOOST_REQUIRE(!!evm_balance(evm2)); + + const evmc::address fanout_addr = deploy_contract(evm1, evmc::from_hex(fanout_bytecode).value()); + + //evm2 is going to try to send 3EOS, 1 to reserved alice, 1 to reserved bob, and 1 to reserved carol + const int64_t to_bridge = 3'0000; + const int64_t alice_native_start = native_balance("alice"_n); + const int64_t bob_native_start = native_balance("bob"_n); + const int64_t carol_native_start = native_balance("carol"_n); + + silkworm::Transaction txn = generate_tx(fanout_addr, 100_szabo * to_bridge, 300'000); + + txn.data = evmc::from_hex("0x2929abe6" //distribute(address[],uint256[]) + "0000000000000000000000000000000000000000000000000000000000000040" //offset to 'to' list + "00000000000000000000000000000000000000000000000000000000000000c0" //offset to 'amt' list + "0000000000000000000000000000000000000000000000000000000000000003" //3 addresses + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb345c850000000000" //alice reserved + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000" //bob reserved + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb41af488000000000" //carol reserved + "0000000000000000000000000000000000000000000000000000000000000003" //3 amounts + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" //1EOS + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" //1EOS + "0000000000000000000000000000000000000000000000000de0b6b3a7640000").value(); //1EOS + evm2.sign(txn); + + //0 of 3 are open + BOOST_REQUIRE_EXCEPTION(pushtx(txn), + eosio_assert_message_exception, eosio_assert_message_is("only one non-open account for egress bridging allowed in single transaction")); + + open("alice"_n); + //only 1 of the 3 are open + BOOST_REQUIRE_EXCEPTION(pushtx(txn), + eosio_assert_message_exception, eosio_assert_message_is("only one non-open account for egress bridging allowed in single transaction")); + + open("bob"_n); + + //nothing should have been sent to alice's nor bob's open balance yet + BOOST_REQUIRE(vault_balance("alice"_n) == (balance_and_dust{make_asset(0), 0ULL})); + BOOST_REQUIRE(vault_balance("bob"_n) == (balance_and_dust{make_asset(0), 0ULL})); + //nor should have any of their native balances changed + BOOST_REQUIRE(native_balance("alice"_n) == alice_native_start); + BOOST_REQUIRE(native_balance("bob"_n) == bob_native_start); + BOOST_REQUIRE(native_balance("carol"_n) == carol_native_start); + + //2 of 3 open, this should go through + { + const intx::uint256 evm2_before = *evm_balance(evm2); + pushtx(txn); + + BOOST_REQUIRE(vault_balance("alice"_n) == (balance_and_dust{make_asset(1'0000), 0ULL})); + BOOST_REQUIRE(vault_balance("bob"_n) == (balance_and_dust{make_asset(1'0000), 0ULL})); + BOOST_REQUIRE(native_balance("carol"_n) == carol_native_start + 1'0000); + + BOOST_REQUIRE(evm2_before - *evm_balance(evm2) == 100_szabo * to_bridge + 122826_u256 * get_config().gas_price); + } + + open("carol"_n); + evm2.sign(txn); + + //all 3 open + { + const intx::uint256 evm2_before = *evm_balance(evm2); + pushtx(txn); + + BOOST_REQUIRE(vault_balance("alice"_n) == (balance_and_dust{make_asset(2'0000), 0ULL})); + BOOST_REQUIRE(vault_balance("bob"_n) == (balance_and_dust{make_asset(2'0000), 0ULL})); + BOOST_REQUIRE(vault_balance("carol"_n) == (balance_and_dust{make_asset(1'0000), 0ULL})); + BOOST_REQUIRE(native_balance("carol"_n) == carol_native_start + 1'0000); + + BOOST_REQUIRE(evm2_before - *evm_balance(evm2) == 100_szabo * to_bridge + 122826_u256 * get_config().gas_price); + } +} FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(evm_eos_fanout_out_of_gas, native_token_evm_tester_EOS) try { + // Distributor.sol + const std::string fanout_bytecode = + "608060405234801561001057600080fd5b50610284806100206000396000f3fe60806040526004361061001e5760003560e01c" + "80632929abe614610023575b600080fd5b610036610031366004610154565b610038565b005b6000805b848110156100f45785" + "8582818110610056576100566101c0565b905060200201602081019061006b91906101d6565b6001600160a01b03166108fc85" + "8584818110610089576100896101c0565b905060200201359081150290604051600060405180830381858888f1935050505015" + "80156100bb573d6000803e3d6000fd5b508383828181106100ce576100ce6101c0565b90506020020135826100e0919061021c" + "565b9150806100ec81610235565b91505061003c565b5034811461010157600080fd5b5050505050565b60008083601f840112" + "61011a57600080fd5b50813567ffffffffffffffff81111561013257600080fd5b6020830191508360208260051b8501011115" + "61014d57600080fd5b9250929050565b6000806000806040858703121561016a57600080fd5b843567ffffffffffffffff8082" + "111561018257600080fd5b61018e88838901610108565b909650945060208701359150808211156101a757600080fd5b506101" + "b487828801610108565b95989497509550505050565b634e487b7160e01b600052603260045260246000fd5b60006020828403" + "12156101e857600080fd5b81356001600160a01b03811681146101ff57600080fd5b9392505050565b634e487b7160e01b6000" + "52601160045260246000fd5b8082018082111561022f5761022f610206565b92915050565b6000600182016102475761024761" + "0206565b506001019056fea26469706673582212209964e90f15129fadc3f3ade8e9fcd3b3d9c3f27617b3bcc28cf29cc5e3e5" + "b5dc64736f6c63430008110033"; + + evm_eoa evm1, evm2; + transfer_token("alice"_n, "evm"_n, make_asset(50'0000), evm1.address_0x()); + transfer_token("alice"_n, "evm"_n, make_asset(10'0000), evm2.address_0x()); + BOOST_REQUIRE(!!evm_balance(evm2)); + + const evmc::address fanout_addr = deploy_contract(evm1, evmc::from_hex(fanout_bytecode).value()); + + const int64_t to_bridge = 3'0000; + const int64_t carol_native_start = native_balance("carol"_n); + + silkworm::Transaction txn = generate_tx(fanout_addr, 100_szabo * to_bridge, 100'000); + + txn.data = evmc::from_hex("0x2929abe6" //distribute(address[],uint256[]) + "0000000000000000000000000000000000000000000000000000000000000040" //offset to 'to' list + "00000000000000000000000000000000000000000000000000000000000000c0" //offset to 'amt' list + "0000000000000000000000000000000000000000000000000000000000000003" //3 addresses + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb345c850000000000" //alice reserved + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000" //bob reserved + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb41af488000000000" //carol reserved + "0000000000000000000000000000000000000000000000000000000000000003" //3 amounts + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" //1EOS + "0000000000000000000000000000000000000000000000000de0b6b3a7640000" //1EOS + "0000000000000000000000000000000000000000000000000de0b6b3a7640000").value(); //1EOS + evm2.sign(txn); + + open("alice"_n); + open("bob"_n); + + const intx::uint256 evm2_before = *evm_balance(evm2); + const int64_t evm_vault_before = vault_balance(evm_account_name).balance.get_amount(); + + pushtx(txn); + + //transaction will have run out of gas. make sure no one received any tokens and evm2 was only deducted its max gas + + BOOST_REQUIRE(vault_balance("alice"_n) == (balance_and_dust{make_asset(0'0000), 0ULL})); + BOOST_REQUIRE(vault_balance("bob"_n) == (balance_and_dust{make_asset(0'0000), 0ULL})); + BOOST_REQUIRE(native_balance("carol"_n) == carol_native_start + 0'0000); + + BOOST_REQUIRE(evm2_before - *evm_balance(evm2) == 100000_u256 * get_config().gas_price); + + //also check that the miner & contract were credited + BOOST_REQUIRE_EQUAL(vault_balance(evm_account_name).balance.get_amount() - evm_vault_before, get_config().gas_price / 1000000000); + +} FC_LOG_AND_RETHROW() + BOOST_AUTO_TEST_SUITE_END() diff --git a/docs/compilation_and_testing_guide.md b/docs/compilation_and_testing_guide.md index 0be2c39b..ce783f9a 100644 --- a/docs/compilation_and_testing_guide.md +++ b/docs/compilation_and_testing_guide.md @@ -1,4 +1,4 @@ - ## Compile EVM smart contract ## + ## Compile EVM Contract Prerequisite: @@ -10,10 +10,10 @@ Prerequisite: /usr/local/bin/eosio-wast2wasm /usr/local/bin/eosio-wasm2wast -Checkout Trust EVM repo: +Checkout eos-evm repo: ``` -git clone https://github.com/eosnetworkfoundation/TrustEVM.git -cd TrustEVM +git clone https://github.com/eosnetworkfoundation/eos-evm.git +cd eos-evm git submodule update --init --recursive ``` @@ -27,8 +27,8 @@ make -j ``` You should now see the compile contract - TrustEVM/contract/build/evm_runtime/evm_runtime.wasm - TrustEVM/contract/build/evm_runtime/evm_runtime.abi + eos-evm/contract/build/evm_runtime/evm_runtime.wasm + eos-evm/contract/build/evm_runtime/evm_runtime.abi [Optional, but required for EVM token testings] to compile contract with debug actions, use @@ -40,7 +40,7 @@ cmake .. Note: if compilation errors occur, you may need to comment out some of the debug actions -## Compile trustevm-node, trustevm-rpc, unit_test ## +## Compile eos-evm-node, eos-evm-rpc, unit_test Prerequisite: cmake 3.19 or later @@ -52,13 +52,13 @@ run ./rebuild_gcc_release.sh You will get the following binaries: ``` -TrustEVM/build/cmd/trustevm-node -TrustEVM/build/cmd/trustevm-rpc -TrustEVM/build/cmd/unit_test +eos-evm/build/cmd/eos-evm-node +eos-evm/build/cmd/eos-evm-rpc +eos-evm/build/cmd/unit_test ``` -## Deploy EVM contract to nodeos ## +## Deploy EVM contract to nodeos Prerequisites: @@ -115,8 +115,8 @@ Create account evmevmevmevm (here private key is 5JURSKS1BrJ1TagNBw1uVSzTQL2m9eH ``` Set EVM contract into account evmevmevmevm ``` -./cleos set code evmevmevmevm ../TrustEVM/contract/build/evm_runtime/evm_runtime.wasm -./cleos set abi evmevmevmevm ../TrustEVM/contract/build/evm_runtime/evm_runtime.abi +./cleos set code evmevmevmevm ../eos-evm/contract/build/evm_runtime/evm_runtime.wasm +./cleos set abi evmevmevmevm ../eos-evm/contract/build/evm_runtime/evm_runtime.abi ``` (Optional) Verify if account has set code, you will got the non-zero code hash which means contract is deployed, for example: ``` @@ -130,6 +130,11 @@ The EVM contract will not allow any actions except `init` until its chain id & n ./cleos push action evmevmevmevm init '{"chainid": 15555}' ``` +add eosio.code to active permission +``` +./cleos set account permission evmevmevmevm active --add-code +``` + ## Set balance and transfer native EVM token via EVM smart contract: ## Prerequisite: @@ -161,7 +166,7 @@ python3 ./get_balance.py 2787b98fc4e731d0456b3941f0b3fe2e01439961 ``` You’ll get 0 as the current balance -Please find the get_balance.py from https://github.com/eosnetworkfoundation/TrustEVM/tree/kayan-rpc-fix/testing-utils +Please find the get_balance.py from https://github.com/eosnetworkfoundation/eos-evm/tree/kayan-rpc-fix/testing-utils Push debug action setbal: @@ -179,7 +184,7 @@ You should see a non-zero number. Whether or not it looks like garbage, but that Step 3: send balance -please find send_via_cleos.py from https://github.com/eosnetworkfoundation/TrustEVM/tree/kayan-rpc-fix/testing-utils +please find send_via_cleos.py from https://github.com/eosnetworkfoundation/eos-evm/tree/kayan-rpc-fix/testing-utils command: ``` @@ -209,7 +214,7 @@ assertion failure with message: validate_transaction error: 20, kSenderNoEOA: (looks like this means the “from” account can’t be the contract account) Other errors are defined in: -TrustEVM/silkworm/core/silkworm/consensus/validation.hpp +eos-evm/silkworm/core/silkworm/consensus/validation.hpp ## Playing with ethereum contract @@ -284,7 +289,7 @@ At this moment please get back the contract address via -[Debug ONLY, doesn't work with TrustEVM-RPC] Set EVM bytecode to EVM contract on EOSIO via debug action updatecode: +[Debug ONLY, doesn't work with eos-evm-RPC] Set EVM bytecode to EVM contract on EOSIO via debug action updatecode: Caveat: according to the EVM bytecode standard, the byte code should have the following 3 parts: - Deploy code @@ -360,7 +365,7 @@ Take the above solidity contract in https://remix.ethereum.org/, executing the ``` 0x6057361d000000000000000000000000000000000000000000000000000000000000007b ``` -please find send_data_via_cleos.py from https://github.com/eosnetworkfoundation/TrustEVM/tree/kayan-rpc-fix/testing-utils +please find send_data_via_cleos.py from https://github.com/eosnetworkfoundation/eos-evm/tree/kayan-rpc-fix/testing-utils To use the script: @@ -449,29 +454,29 @@ to get all the storages, for example: -# Connect TrustEVM-node with TrustEVM-RPC [Experimental] +# Connect eos-evm-node with eos-evm-rpc [Experimental] ## Prerequisite: -- use branch kayan-rpc-fix of this repo (https://github.com/eosnetworkfoundation/TrustEVM/tree/kayan-rpc-fix) +- use branch kayan-rpc-fix of this repo (https://github.com/eosnetworkfoundation/eos-evm/tree/kayan-rpc-fix) - only use account 2787b98fc4e731d0456b3941f0b3fe2e01439961 (private key a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954) as genesis account (the balance was hacked) -- Compile leap, cdt, TrustEVM contracts, TrustEVM-node & TrustEVM-RPC binary +- Compile Leap, CDT, EOS EVM Contract, eos-evm-node, and eos-evm-rpc binaries - Completed the above tests ## Steps 1. From a clean database, start nodeos with SHIP -2. clean start TrustEVM-Node, for example: +2. clean start eos-evm-node, for example: ``` -./build/cmd/trustevm-node --evm-abi ./evm.abi --chain-data ./chain-data --ship-chain-state-dir ./ship-chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 --ship-genesis 2 +./build/cmd/eos-evm-node --evm-abi ./evm.abi --chain-data ./chain-data --ship-chain-state-dir ./ship-chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 --ship-genesis 2 ``` -3. start TrustEVM-RPC in the same machine, for example: +3. start eos-evm-rpc in the same machine, for example: ``` -./build/cmd/trustevm-rpc --trust-evm-node=127.0.0.1:8080 --chaindata=./chain-data +./build/cmd/eos-evm-rpc --eos-evm-node=127.0.0.1:8080 --chaindata=./chain-data ``` ## Make sure RPC response: - eth_getBlockByNumber: ``` -kayan-u20@kayan-u20:~/workspaces/TrustEVM$ curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0,"jsonrpc":"2.0"}' {"error":{"code":100,"message":"unknown bucket: SyncStage"},"id":0,"jsonrpc":"2.0"} ``` At the very beginning it is normal to see "unknown bucket: SyncStage" because there's no block @@ -484,13 +489,13 @@ python3 ./send_data_via_cleos.py 2787b98fc4e731d0456b3941f0b3fe2e01439961 "" 0 6 ## get blocknumber again ``` -kayan-u20@kayan-u20:~/workspaces/TrustEVM$ curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0,"jsonrpc":"2.0"}' {"id":0,"jsonrpc":"2.0","result":"0x1"} ``` ## now try get block by number ``` -kayan-u20@kayan-u20:~/workspaces/TrustEVM$ curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBlockByNumber","params":["0x1",true],"id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBlockByNumber","params":["0x1",true],"id":0,"jsonrpc":"2.0"}' {"id":0,"jsonrpc":"2.0","result":{"difficulty":"0x","extraData":"0x","gasLimit":"0xffffffffffffffff","gasUsed":"0x0","hash":"0x62438d9e228c32a3033a961161f913b700e0d6aecf0ecb141e92ae41d1fb9845","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","number":"0x1","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","size":"0x3cc","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x183c5f2fea0","totalDifficulty":"0x","transactions":[{"blockHash":"0x62438d9e228c32a3033a961161f913b700e0d6aecf0ecb141e92ae41d1fb9845","blockNumber":"0x1","from":"0x2787b98fc4e731d0456b3941f0b3fe2e01439961","gas":"0xf4240","gasPrice":"0x3b9aca00","hash":"0xc4372998d1f7fc02a24fbb381947f7a10ed0826c404b7533e8431df9e48a27d0","input":"0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea26469706673582212209a159a4f3847890f10bfb87871a61eba91c5dbf5ee3cf6398207e292eee22a1664736f6c63430008070033","nonce":"0x0","r":"0x8cd1b11f5a5a9a811ad415b3f3d360a4d8aa4a8bae20467ad3649cfbad25a5ae","s":"0x5eab2829885d473747727d54caae01a8076244c3f6a4af8cad742a248b7a19ec","to":null,"transactionIndex":"0x0","type":"0x0","v":"0x79aa","value":"0x0"}],"transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","uncles":[]}} ``` @@ -501,7 +506,7 @@ python3 ./send_data_via_cleos.py 2787b98fc4e731d0456b3941f0b3fe2e01439961 3f4b0f ## and then execute the view action "retrieve" from RPC ``` -kayan-u20@kayan-u20:~/workspaces/TrustEVM$ curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_call","params":[{"from":" 2787b98fc4e731d0456b3941f0b3fe2e01439961","to":"3f4b0f92007341792aa61e065484e48e583ebeb9","data":"0x2e64cec1"},"latest"],"id":11}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_call","params":[{"from":" 2787b98fc4e731d0456b3941f0b3fe2e01439961","to":"3f4b0f92007341792aa61e065484e48e583ebeb9","data":"0x2e64cec1"},"latest"],"id":11,"jsonrpc":"2.0"}' {"id":11,"jsonrpc":"2.0","result":"0x000000000000000000000000000000000000000000000000000000000000007b"} ``` @@ -512,7 +517,7 @@ python3 ./send_via_cleos.py 2787b98fc4e731d0456b3941f0b3fe2e01439961 0x9edf02200 ## get balance via RPC ``` -kayan-u20@kayan-u20:~/workspaces/TrustEVM$ curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBalance","params":["9edf022004846bc987799d552d1b8485b317b7ed","latest"],"id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBalance","params":["9edf022004846bc987799d552d1b8485b317b7ed","latest"],"id":0,"jsonrpc":"2.0"}' {"id":0,"jsonrpc":"2.0","result":"0x100"} ``` (Note the balance of 2787b98fc4e731d0456b3941f0b3fe2e01439900 may not work, because it is hacked) diff --git a/docs/local_testnet_deployment_plan.md b/docs/local_testnet_deployment_plan.md index 8ba6be84..a9c485c6 100644 --- a/docs/local_testnet_deployment_plan.md +++ b/docs/local_testnet_deployment_plan.md @@ -32,7 +32,7 @@ List of compiled system contracts from https://github.com/eosnetworkfoundation/e - eosio.token.wasm (optional, if you want to test token economy) - eosio.system.wasm (optional, if you want to test resources: RAM, NET, CPU) -Compiled EVM contracts in DEBUG mode, from this repo, see https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/compilation_and_testing_guide.md for details. +Compiled EVM contracts in DEBUG mode, from this repo, see https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/compilation_and_testing_guide.md for details. The compilation result should be these two files: @@ -41,19 +41,19 @@ The compilation result should be these two files: Compiled binaries from this repo: -- trustevm-node: silkworm node process that receive data from the main Antelope chain and convert to the EVM chain -- trustevm-rpc: silkworm rpc server that provide service for view actions and other read operations +- eos-evm-node: silkworm node process that receive data from the main Antelope chain and convert to the EVM chain +- eos-evm-rpc: silkworm rpc server that provide service for view actions and other read operations -## Run A Local Node With Trust EVM Service +## Run A Local Node With EOS EVM Service -In order to run a Trust EVM service, and thus have setup the Antelope blockchain with capabilities to push EVM transactions, we need to have the follow items inside one physical server / VM. +In order to run an EOS EVM service, and thus have setup the Antelope blockchain with capabilities to push EVM transactions, we need to have the follow items inside one physical server / VM. 1. [Run A Local Antelope Node](#1-run-a-local-antelope-node) 2. [Blockchain Bootstrap And Initialization](#2-blockchain-bootstrap-and-initialization) 3. [Deploy And Initialize EVM Contract](#3-deploy-and-initialize-evm-contract) 4. [Setup The Transaction Wrapper Service](#4-setup-the-transaction-wrapper-service) -5. [Start TrustEVM-node (a.k.a. Silkworm Node)](#5-start-trustevm-node-aka-silkworm-node) -6. [Start TrustEVM-RPC (a.k.a. Silkworm RPC)](#6-start-trustevm-rpc-aka-silkworm-rpc) +5. [Start eos-evm-node (a.k.a. Silkworm Node)](#5-start-eos-evm-node-aka-silkworm-node) +6. [Start eos-evm-rpc (a.k.a. Silkworm RPC)](#6-start-eos-evm-rpc-aka-silkworm-rpc) 7. [Setup The Flask Proxy](#7-setup-the-flask-proxy) ### 1. Run A Local Antelope Node @@ -353,8 +353,8 @@ Create account evmevmevmevm with key pair EOS8kE63z4NcZatvVWY4jxYdtLg6UEA123raMG Deploy evm_runtime contract, wasm and abi file, to account evmevmevmevm: ```shell -./cleos set code evmevmevmevm ../TrustEVM/contract/build/evm_runtime/evm_runtime.wasm -./cleos set abi evmevmevmevm ../TrustEVM/contract/build/evm_runtime/evm_runtime.abi +./cleos set code evmevmevmevm ../eos-evm/contract/build/evm_runtime/evm_runtime.wasm +./cleos set abi evmevmevmevm ../eos-evm/contract/build/evm_runtime/evm_runtime.abi ``` Set chain ID & native token configuration (in this example, gas price is 150 Gwei, miner_cut is 10%) @@ -363,6 +363,11 @@ Set chain ID & native token configuration (in this example, gas price is 150 Gwe \"}}" -p evmevmevmevm ``` +Add eosio.code to active permission +``` +./cleos set account permission evmevmevmevm active --add-code +``` + after the init action we need a small amount of token (1 EOS) to be transferred into the contract account (with memo=contract account), for example: ``` ./cleos transfer eosio evmevmevmevm "1.0000 EOS" "evmevmevmevm" @@ -405,7 +410,7 @@ Notice that the value `000000000000000000000000000000010000000000000000000000000 ### 4. Setup The Transaction Wrapper Service -Setup the transaction wrapper service to wrap ETH write requests into Antelope transactions. +Setup the transaction wrapper service to wrap EVM transactions into Antelope transactions. This is also required in mainnet for service providers who want to be EVM transaction miners and get miner rewards in EOS. #### Install The Necessary nodejs Tools @@ -416,7 +421,7 @@ npm install eosjs npm install ethereumjs-util ``` -#### Create Sender Antelope Account +#### Create Sender Antelope Account (miner account) Create an additional Antelope account, a.k.a. the sender account, as the wrapper account for signing wrapped Antelope transactions. @@ -436,19 +441,21 @@ run the open action on evm contract to open the account balance row: Prepare the `.env` file to configure Antelope RPC endpoint, listening port, EVM contract account, sender account and other details: ```txt -EOS_RPC="http://127.0.0.1:8888" +EOS_RPC="http://127.0.0.1:8888|http://192.168.1.100:8888" EOS_KEY="5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior" HOST="127.0.0.1" PORT="18888" EOS_EVM_ACCOUNT="evmevmevmevm" EOS_SENDER="a123" +EOS_PERMISSION="active" +EXPIRE_SEC=60 ``` -In this environment settings, Tx Wrapper will listen to 127.0.0.1:18888, use `5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior` to wrap and sign the in-coming ETH trasnactions into Antelope transactions, and then push them into the Antelope RPC endpoint http://127.0.0.1:8888 +In the above environment settings, Tx Wrapper will listen to 127.0.0.1:18888, use `5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior` to wrap and sign the in-coming ETH trasnactions into Antelope transactions (contract=evmevmevmevm, action_name=pushtx, with expire second set to 60 and using permission a123@active), and then push them into the Antelope RPC endpoint http://127.0.0.1:8888. If the endpoint http://127.0.0.1:8888 is unavailable, it will try the next endpoint http://192.168.1.100:8888. #### Start Tx Wrapper Service -Start the Tx Wrapper service and use the `index.js` from https://github.com/eosnetworkfoundation/TrustEVM/tree/main/peripherals/tx_wrapper: +Start the Tx Wrapper service and use the `index.js` from https://github.com/eosnetworkfoundation/eos-evm/tree/main/peripherals/tx_wrapper: ```shell node index.js @@ -817,7 +824,7 @@ Verify on Antelope blockchain to ensure nonce & balance were updated: #### [Debug only] Investigate The Current EVM Storage State On Antelope -Since we don't support running View actions directly from Antelope node (read requests will go to TrustEVM-RPC), it is quite complicated to investigate the storage of EVM directly from Antelope. However, If you really want to do that. These are the steps: +Since we don't support running View actions directly from Antelope node (read requests will go to eos-evm-rpc), it is quite complicated to investigate the storage of EVM directly from Antelope. However, If you really want to do that. These are the steps: ##### Identify The "id" Field Of The Contract Address @@ -860,9 +867,9 @@ Example output: } ``` -### 5. Start TrustEVM-node (a.k.a. Silkworm Node) +### 5. Start eos-evm-node (a.k.a. Silkworm Node) -A TrustEVM-node is a node process of the virtual ethereum blockchain that validates virtual ethereum blocks and serves the read requests coming from TrustEVM-RPC. It will not produce blocks. However, it will consume blocks from Antelope node and convert Antelope blocks into Virutal Ethereum blocks in a deterministic way. +A eos-evm-node is a node process of the virtual ethereum blockchain that validates virtual ethereum blocks and serves the read requests coming from eos-evm-rpc. It will not produce blocks. However, it will consume blocks from Antelope node and convert Antelope blocks into Virutal Ethereum blocks in a deterministic way. To set it up, we need to first make up a genesis of the virtual ethereum blockchain that maps to the same EVM state of the evm account of the Antelope chain that just initialized in the previous steps. @@ -942,7 +949,17 @@ This determines the value of the "timestamp" field in EVM genesis. Set the "mixHash" field to be "0x + Antelope starting block id", e.g. "0x000000026d392f1bfeddb000555bcb03ca6e31a54c0cf9edc23cede42bda17e6" -Set the "nonce" field to be the hex encoding of the value of the Antelope name of the account on which the EVM contract is deployed. So if the `evmevmevmevm` account name is used, then set the nonce to "0x56e4adc95b92b720". If the `eosio.evm` account name is used, then set the nonce to "0x56e4adc95b92b720". This is re-purposed to be the block time (in mill-second) of the EVM chain. +Set the "nonce" field to be the hex encoding of the value of the Antelope name of the account on which the EVM contract is deployed. So if the `evmevmevmevm` account name is used, then set the nonce to "0x56e4adc95b92b720". If the `eosio.evm` account name is used, then set the nonce to "0x5530ea015b900000". + +The function `convert_name_to_value` from https://github.com/eosnetworkfoundation/eos-evm/blob/main/tests/leap/antelope_name.py can be used to get the appropriate nonce value using Python: + +```shell +>>> from antelope_name import convert_name_to_value +>>> print(f'0x{convert_name_to_value("evmevmevmevm"):x}') +0x56e4adc95b92b720 +>>> print(f'0x{convert_name_to_value("eosio.evm"):x}') +0x5530ea015b900000 +``` In the "alloc" part, setup the genesis EVM account balance (should be all zeros) @@ -968,7 +985,7 @@ Final EVM genesis example: "trust": {} }, "difficulty": "0x01", - "extraData": "TrustEVM", + "extraData": "EOSEVM", "gasLimit": "0x7ffffffffff", "mixHash": "0x000000026d392f1bfeddb000555bcb03ca6e31a54c0cf9edc23cede42bda17e6", "nonce": "0x56e4adc95b92b720", @@ -977,36 +994,36 @@ Final EVM genesis example: ``` -#### Start The TrustEVM Process +#### Start The EOS EVM Process -Run the below commamnd to start the TrustEVM node: +Run the below commamnd to start the eos-evm-node: ```shell mkdir ./chain-data -./trustevm-node --chain-data ./chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 --genesis-json=./genesis.json +./eos-evm-node --chain-data ./chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 --genesis-json=./genesis.json ``` -### 6. Start TrustEVM-RPC (a.k.a. Silkworm RPC) +### 6. Start eos-evm-rpc (a.k.a. Silkworm RPC) -The TrustEVM-RPC process provides Ethereum compatible RPC service for clients. It queries state (including blocks, accounts, storage) from TrustEVM-node, and it can also run view actions requested by clients. +The eos-evm-rpc process provides Ethereum compatible RPC service for clients. It queries state (including blocks, accounts, storage) from eos-evm-node, and it can also run view actions requested by clients. -#### Start The TrustEVM-RPC process +#### Start The eos-evm-rpc process -Run below commmand to start the Trust-EVM node: +Run below commmand to start the eos-evm-node: ```shell -./trustevm-rpc --api-spec=eth,net --http-port=0.0.0.0:8881 --trust-evm-node=127.0.0.1:8080 --chaindata=./chain-data +./eos-evm-rpc --api-spec=eth,net --http-port=0.0.0.0:8881 --eos-evm-node=127.0.0.1:8080 --chaindata=./chain-data ``` -The `--chain-data` parameter value must point to the same directory of the chain-data in TrustEVM-node. -In the above command, TrustEVM-rpc will listen on port 8881 for RPC requests. +The `--chain-data` parameter value must point to the same directory of the chain-data in eos-evm-node. +In the above command, eos-evm-rpc will listen on port 8881 for RPC requests. #### Verify The RPC Response To verify the RPC response run below command: ```shell -curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_blockNumber","id":0,"jsonrpc":"2.0"}' ``` You'll recevie a response similar to the one below: @@ -1020,7 +1037,7 @@ You'll recevie a response similar to the one below: Request: ```shell -curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBlockByNumber","params":["0x1",true],"id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBlockByNumber","params":["0x1",true],"id":0,"jsonrpc":"2.0"}' ``` Response: @@ -1034,7 +1051,7 @@ Response: Request: ```shell -curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBalance","params":["9edf022004846bc987799d552d1b8485b317b7ed","latest"],"id":0}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_getBalance","params":["9edf022004846bc987799d552d1b8485b317b7ed","latest"],"id":0,"jsonrpc":"2.0"}' ``` response: @@ -1049,7 +1066,7 @@ Request: data - 0x2e64cec1 is the hash of a solidity function `retrieve() public view returns (uint256)` ```shell -curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_call","params":[{"from":" 2787b98fc4e731d0456b3941f0b3fe2e01439961","to":"3f4b0f92007341792aa61e065484e48e583ebeb9","data":"0x2e64cec1"},"latest"],"id":11}' +curl --location --request POST 'localhost:8881/' --header 'Content-Type: application/json' --data-raw '{"method":"eth_call","params":[{"from":" 2787b98fc4e731d0456b3941f0b3fe2e01439961","to":"3f4b0f92007341792aa61e065484e48e583ebeb9","data":"0x2e64cec1"},"latest"],"id":11,"jsonrpc":"2.0"}' ``` Response: @@ -1060,14 +1077,14 @@ Response: #### Setup Proxy To Separate Read Requests From Write Requests -The proxy program will separate Ethereum's write requests (such as eth_sendRawTransaction,eth_gasPrice) from other requests (treated as read requests). The write requests should go to Transaction Wrapper (which wrap the ETH transaction into Antelope transaction and sign it and push to the Antelope blockchain). The read requests should go to TrustEVM-RPC. +The proxy program will separate Ethereum's write requests (such as eth_sendRawTransaction,eth_gasPrice) from other requests (treated as read requests). The write requests should go to Transaction Wrapper (which wrap the ETH transaction into Antelope transaction and sign it and push to the Antelope blockchain). The read requests should go to eos-evm-rpc. In order to get it working, docker is required. To install docker in Linux, see https://docs.docker.com/engine/install/ubuntu/ -You can find the proxy tool here: TrustEVM/perfipherals/proxy +You can find the proxy tool here: eos-evm/peripherals/proxy ```shell -cd TrustEVM/peripherals/proxy/ +cd eos-evm/peripherals/proxy/ ``` - Edit the file `nginx.conf`, find the follow settings: @@ -1083,7 +1100,7 @@ cd TrustEVM/peripherals/proxy/ ``` - Change the IP and port of the write session to your Transaction Wrapper server endpoint. -- Change the IP and port of the read session to your TrustEVM-RPC server endpoint +- Change the IP and port of the read session to your eos-evm-rpc server endpoint - Build the docker image for the proxy program: ```shell @@ -1126,7 +1143,7 @@ Example response: #### [Optional] Setup Metamask Chrome extension -- Ensure TrustEVM-RPC is running with `--api-spec=eth,debug,net,trace` +- Ensure eos-evm-rpc is running with `--api-spec=eth,debug,net,trace` - Install Metamask Plugin in Chrome - Click Account ICON on the top right, the Find Settings -> Networks -> Add Network @@ -1145,11 +1162,11 @@ After setting up Metamask, you should able to import or create accounts via this #### [Optional] Setup EVM block explorer -In this example, we will use the blockscout explorer (https://github.com/elmato/blockscout). Any other Ethereum compatible block explorer will also works. +In this example, we will use the blockscout explorer (https://github.com/eosnetworkfoundation/blockscout). Any other Ethereum compatible block explorer will also works. Requirements: -- TrustEVM-RPC is running with `--api-spec=eth,debug,net,trace` parameter. This is the source the block explore will retrieve data from. +- eos-evm-rpc is running with `--api-spec=eth,debug,net,trace` parameter. This is the source the block explore will retrieve data from. - docker in Linux - Python3 @@ -1157,9 +1174,9 @@ Requirements: Setup the Flask proxy to convert the bulk requests into single requests. -Since TrustEVM-RPC does not support bulk requests, we need a simple proxy script to convert those requests into multiple single requests: +Since eos-evm-rpc does not support bulk requests, we need a simple proxy script to convert those requests into multiple single requests: -This is an example proxy script "flask_proxy.py" that convert requests and forward them to the TrustEVM-RPC endpoint (for example http://127.0.0.1:8881): +This is an example proxy script "flask_proxy.py" that convert requests and forward them to the eos-evm-rpc endpoint (for example http://127.0.0.1:8881): ```python #!/usr/bin/env python3 @@ -1215,9 +1232,9 @@ python3 ./flask_proxy.py Checkout and run blockscout in the same machine that the flask proxy is running ```shell -git clone https://github.com/elmato/blockscout +git clone https://github.com/eosnetworkfoundation/blockscout cd blockscout -git checkout trust +git checkout evm cd docker make cd ../docker-compose diff --git a/docs/pmo/TrustEVM Project Schedule 9-14-22.pdf b/docs/pmo/TrustEVM Project Schedule 9-14-22.pdf deleted file mode 100644 index 9bbad63e..00000000 Binary files a/docs/pmo/TrustEVM Project Schedule 9-14-22.pdf and /dev/null differ diff --git a/docs/pmo/TrustEVM Project Schedule 9-22-22.pdf b/docs/pmo/TrustEVM Project Schedule 9-22-22.pdf deleted file mode 100644 index 2a2deec9..00000000 Binary files a/docs/pmo/TrustEVM Project Schedule 9-22-22.pdf and /dev/null differ diff --git a/docs/pmo/gitkeep b/docs/pmo/gitkeep deleted file mode 100644 index 8b137891..00000000 --- a/docs/pmo/gitkeep +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/public_testnet_deployment_plan.md b/docs/public_testnet_deployment_plan.md index 8c55e217..63bc570f 100644 --- a/docs/public_testnet_deployment_plan.md +++ b/docs/public_testnet_deployment_plan.md @@ -2,11 +2,11 @@ This document describes how to enable EVM support for public testnets, such as Jungle testnet, without token economy. -For local testnet deployments, refer to the [Enable EVM Support For Local Testnet](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md) guide. +For local testnet deployments, refer to the [Enable EVM Support For Local Testnet](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md) guide. To enable EVM support, the following procedures must be performed by: - [Block Producers](#for-block-producers) -- [TrustEVM team](#for-trustevm-team) +- [EOS EVM team](#for-eos-evm-team) - [EVM Service Providers](#for-evm-service-providers) ## For Block Producers @@ -346,9 +346,9 @@ The following protocol features are required to support EVM in Antelope: ] ``` -## For TrustEVM Team +## For EOS EVM Team -The TrustEVM Team must perform the following steps: +The EOS EVM Team must perform the following steps: ### 1. Create The EVM Account @@ -365,180 +365,36 @@ See the [cleos create account](https://docs.eosnetwork.com/leap/latest/cleos/com See the [cleos create key pair](https://docs.eosnetwork.com/leap/latest/cleos/command-reference/create/key) reference for details on how to create a key pair. -### 2. Deploy The Debug EVM Contract +### 2. Deploy EVM Contract -Later in this procedure you must use the `setbal` smart contract action to set the balance of the inital account. This action is available only in the `debug` version of the smart contract. +For details on how to compile the EVM smart contract see the [Compilation And Testing Guide](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/compilation_and_testing_guide.md). -Compile the `debug` version of the EVM smart contract and note the wasm and abi path, e.g. `EVM_DEBUG_PATH`. - -For details on how to compile the EVM smart contract see the [Compilation And Testing Guide](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/compilation_and_testing_guide.md). - -Run the following `cleos` commands to deploy the `debug` version of the EVM contract to the EVM account: +Run the following `cleos` commands to EVM contract to the EVM account: ```sh -./cleos set code evmevmevmevm EVM_DEBUG_PATH/evm_runtime.wasm -./cleos set abi evmevmevmevm EVM_DEBUG_PATH/evm_runtime.abi +./cleos set code evmevmevmevm EVM_PATH_to_evm_runtime.wasm +./cleos set abi evmevmevmevm EVM_PATH_to_evm_runtime.abi ``` ### 2a. Initialize EVM contract The EVM contract will not allow any actions except `init` until its chain id & native token is configured. Exact values to use here are TBD. ``` -./cleos push action evmevmevmevm init '{"chainid": 15555}' -``` - -### 3. Setup The Initial EVM Token Balance - -We need to set the EVM token balance for the initial ETH account, which is specially managed by TrustEVM team. - -For example: -```sh -./cleos push action evmevmevmevm setbal '{"addy":"2787b98fc4e731d0456b3941f0b3fe2e01439961", "bal":"0000000000000000000000000000000100000000000000000000000000000000"}' -p evmevmevmevm -``` - -> :warning: Be careful: the balance string value must be in the form of exactly 64 hex-digits (meaning a 256-bit integer) - - -### 4. Deploy The Release EVM Contract - -Compile the `release` version of the EVM smart contract and note the wasm and abi path, e.g. `EVM_RELEASE_PATH`. - -For details on how to compile the EVM smart contract see the [Compilation And Testing Guide](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/compilation_and_testing_guide.md). - -Run the following `cleos` commands to deploy the `release` version of the EVM contract to the EVM account: - -```sh -./cleos set code evmevmevmevm EVM_RELEASE_PATH/evm_runtime.wasm -./cleos set abi evmevmevmevm EVM_RELEASE_PATH/evm_runtime.abi -``` - -### 5. Initialize The Participant Accounts Balances - -Send EVM tokens from the initial account to participant accounts. - -Use the token distribution script, [distribute_to_accounts.py](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/peripherals/token_distribution/distribute_to_accounts.py), to distribute tokens to the initial EVM accounts: - -#### Prepare The Account Balance CSV File - -You need to list all the account balances in a `.csv` file, without the header row, where the first column represents the account name and the second column respresents the balance number in decimal, such as: - -``` -0x00000000219ab540356cbb839cbe05303d7705fa,14708999007718564869804029 -0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2,3930560351933256293096325 -0xf977814e90da44bfa03b6295a0616a897441acec,2604596263728749700480557 -0xda9dfa130df4de4673b89022ee50ff26f6ea73cf,2113030086367224616200000 -0x0716a17fbaee714f1e6ab0f9d59edbc5f09815c0,1998606574289842563751000 -0xbe0eb53f46cd790cd13851d5eff43d12404d33e8,1996008352588563830743490 -0x742d35cc6634c0532925a3b844bc454e4438f44e,1383424850937541446672785 -``` - -#### Prepare Enviroment Variables - -Have your EVM private key of the EVM sender account, and the `nodeos`'s RPC endpoint defined in the corresponding enviroment variables, as in the following example: - -```sh -export EVM_SENDER_KEY=a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954 -export NODEOS_ENDPOINT=http://127.0.0.1:8888 -``` - -#### Find Out The Starting Nonce Number - -Find out the starting nonce number and the current balance of the sender account. Use the following command to find the starting nonce number of any existing EVM account: - -```sh -./cleos get table evmevmevmevm evmevmevmevm account --index 2 --key-type sha256 --limit 1 -L EVM_ACCOUNT_NAME -``` - -For example: -```sh -./cleos get table evmevmevmevm evmevmevmevm account --index 2 --key-type sha256 --limit 1 -L 2787b98fc4e731d0456b3941f0b3fe2e01439961 -``` -```json -{ - "rows": [{ - "id": 0, - "eth_address": "2787b98fc4e731d0456b3941f0b3fe2e01439961", - "nonce": 1005, - "balance": "00000000000000000000000000000000ffffffffffbee2eeb107b6ea020924bd", - "eos_account": "", - "code": "", - "code_hash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" - } - ], - "more": true, - "next_key": "27ca5d05bb99a31c1cb793c8679f605ea58c1dd3000000000000000000000000" -} -``` - -Ensure the sender account has enough balance to distribute to all the accounts in the distribution list. - -##### Locate the starting_nonce variable - -Open token distribution script, [distribute_to_accounts.py](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/peripherals/token_distribution/distribute_to_accounts.py), in your editor and search for the `starting_nonce` variable. It should look similar to the code lines below: - -```python -# staring_nonce is the nonce number that maps to the transfer of the first account in the list -# it is used to ensure each transfer is idempotent -# be careful when you change it -starting_nonce = 3 -``` - -##### Update the starting_nonce variable - -In the token distribution script, [distribute_to_accounts.py](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/peripherals/token_distribution/distribute_to_accounts.py), update the `starting_nonce` value to match your current sender account's nonce number. The nonce number is to mark the progress of distribution. You should not change the `starting_nonce` once it is correctly set. Save the script. - -#### Prepare Account And Keys - -Prepare your Antelope wrapping account, such as `evmevmevmevm`, and import your private key to your local Antelope wallet (managed by `keosd`). - -For example: -```sh -./cleos wallet create -n w123 --to-console -./cleos wallet import -n w123 --private-key 5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior +./cleos push action evmevmevmevm init "{\"chainid\":$EVM_CHAINID,\"fee_params\":{\"gas_price\":150000000000,\"miner_cut\":10000,\"ingress_bridge_fee\":\"0.0100 EOS\"}}" -x 60 -p evmevmevmevm ``` -#### Verify Other Parameters In The Script - -In the token distribution script, [distribute_to_accounts.py](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/peripherals/token_distribution/distribute_to_accounts.py), these parameters should read as follows: -```python -EVM_CONTRACT = os.getenv("EVM_CONTRACT", "evmevmevmevm") -NODEOS_ENDPOINT = os.getenv("NODEOS_ENDPOINT", "http://127.0.0.1:8888") -EOS_SENDER = os.getenv("EOS_SENDER", "evmevmevmevm") -EVM_SENDER_KEY = os.getenv("EVM_SENDER_KEY", None) -EOS_SENDER_KEY = os.getenv("EOS_SENDER_KEY", None) -EVM_CHAINID = int(os.getenv("EVM_CHAINID", "15555")) +add eosio.code to active permission ``` - -#### Run The Distribution Script - -Command syntax: -```sh -python3 ./distribute_to_accounts.py FROM_ACCOUNT DISTRIBUTION_CSV +./cleos set account permission evmevmevmevm active --add-code ``` -For example: -```sh -export EVM_SENDER_KEY=a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954 -export NODEOS_ENDPOINT=http://127.0.0.1:8888 -python3 ./distribute_to_accounts.py 2787b98fc4e731d0456b3941f0b3fe2e01439961 ~/Downloads/eth_acc_bals_100k.csv +transfer initial balance ``` - -The script will sign the transactions and call `cleos` to push the transactions to the `NODEOS_ENDPOINT`. - -> :information_source: You can stop and restart the script from time to time and it can continue from the last point of distribution. But do not forget that the `account` and the `starting_nonce` in your script must not be changed. - -#### Final Verification - -For the final verification, check the nonce number again after the entire script is finished. - -```sh -./cleos get table evmevmevmevm evmevmevmevm account --index 2 --key-type sha256 --limit 1 -L EVM_ACCOUNT_NAME +./cleos transfer eosio evmevmevmevm "1.0000 EOS" "evmevmevmevm" ``` -Make sure the current nonce number has been increased by x, where x is the number of accounts in the distribution list. - ## For EVM Service Providers -This part is very similar to the [Enable EVM Support For Local Testnet](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md) guide. +This part is very similar to the [Enable EVM Support For Local Testnet](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md) guide. EVM service providers will need to provide ETH compatible EVM services as follows: ### 1. Run an Antelope Node @@ -547,33 +403,38 @@ Run at least one Antelope node to sync with the public testnet, running in irrev Example command: ```sh -./build/programs/nodeos/nodeos --data-dir=./data-dir --config-dir=./data-dir --genesis-json=./data-dir/genesis.json --disable-replay-opts --read-mode=irreversible +./build/programs/nodeos/nodeos --data-dir=./data-dir --config-dir=./data-dir --genesis-json=./data-dir/genesis.json --disable-replay-opts ``` -### 2. Run a TrustEVM Node +### 2. Run eos-evm-node -Run at least one TrustEVM node, a.k.a. silkworm node, to sync with the Antelope node. +Run at least one eos-evm-node, a.k.a. silkworm node, to sync with the Antelope node. -Refer to the *Start up TrustEVM-node (silkworm node)* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md#5-start-up-trustevm-node-silkworm-node) guide for more details. +Refer to the *Start up eos-evm-node (silkworm node)* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md#5-start-up-eos-evm-node-silkworm-node) guide for more details. ```sh -./build/cmd/trustevm-node --chain-data ./chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 +./build/cmd/eos-evm-node --chain-data ./chain-data --plugin block_conversion_plugin --plugin blockchain_plugin --nocolor 1 --verbosity=5 ``` -### 3. Run a TrustEVM-RPC Node +### 3. Run eos-evm-rpc -Run at least one TrustEVM-RPC, a.k.a. silkworm rpc, process to sync with the TrustEVM node. -The TrustEVM-RPC must be deployed on the same machine with TrustEVM-node, as it needs to access the same chain-data folder. +Run at least one eos-evm-rpc, a.k.a. silkworm rpc, process to sync with the eos-evm-node. +The eos-evm-rpc must be deployed on the same machine with eos-evm-node, as it needs to access the same chain-data folder. ```sh -./build/cmd/trustevm-rpc --trust-evm-node=127.0.0.1:8080 --chaindata=./chain-data +./build/cmd/eos-evm-rpc --eos-evm-node=127.0.0.1:8080 --chaindata=./chain-data ``` -Refer to the *Start up TrustEVM-RPC (silkworm RPC)* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md#6-start-up-trustevm-rpc-silkworm-rpc) guide for more RPC setup details. +Refer to the *Start up eos-evm-rpc (silkworm RPC)* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md#6-start-up-eos-evm-rpc-silkworm-rpc) guide for more RPC setup details. ### 4. Ensure Enough Resources -You must have at least one Antelope account, the sender account, with enough CPU/NET/RAM resources. The service providers can create one or more testnet accounts with some CPU, NET, and RAM resources. +You must have at least one Antelope account, the sender account, with enough CPU/NET/RAM resources. The service providers can create one or more testnet accounts with some CPU, NET, and RAM resources. We use account a123 as example: + +open sender account balance +``` +./cleos push action evmevmevmevm open '{"owner":"a123"}' -p a123 +``` ### 5. Run a Transaction Wrapper Service @@ -582,20 +443,25 @@ Run at least one Transaction Wrapper service to wrap ETH transactions into Antel For example: ```conf -EOS_RPC="http://127.0.0.1:8888" +# one or more EOS_RPC endpoints, separated by '|' +EOS_RPC="http://127.0.0.1:8888|http://192.168.1.1:8888" EOS_KEY="5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior" + +# the listening IP & port of this service HOST="127.0.0.1" PORT="18888" -EOS_EVM_ACCOUNT="evmevmevmevm" +EOS_PERMISSION="active" +EXPIRE_SEC=60 +EOS_EVM_ACCOUNT="evmevmevm" EOS_SENDER="a123" ``` In the above environment settings, the Transaction Wrapper is set to: -- Listen to `127.0.0.1:18888` Antelope endpoint +- Listen to `127.0.0.1:18888` - Use the `5JURSKS1BrJ1TagNBw1uVSzTQL2m9eHGkjknWeZkjSt33Awtior` key to wrap and sign the incoming ETH trasnactions into Antelope transactions and push them into the Antelope RPC endpoint `http://127.0.0.1:8888` -Use the `index.js` file from https://github.com/eosnetworkfoundation/TrustEVM/tree/main/peripherals/tx_wrapper: +Use the `index.js` file from https://github.com/eosnetworkfoundation/eos-evm/tree/main/peripherals/tx_wrapper: ```sh node index.js @@ -603,9 +469,9 @@ node index.js ### 6. Run a Proxy Service -Run at least one Proxy service to separate the read service to TrustEVM-RPC node and the write service to Transaction Wrapper. +Run at least one Proxy service to separate the read service to eos-evm-rpc node and the write service to Transaction Wrapper. -Refer to the *Setup proxy to separate read requests and write requests* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/TrustEVM/blob/main/docs/local_testnet_deployment_plan.md#7-setup-proxy-to-separate-read-requests-and-write-requests) guide for details. +Refer to the *Setup proxy to separate read requests and write requests* section in the [Enable EVM For Local Testnet](https://github.com/eosnetworkfoundation/eos-evm/blob/main/docs/local_testnet_deployment_plan.md#7-setup-proxy-to-separate-read-requests-and-write-requests) guide for details. ## RPC Provider Architecture @@ -621,7 +487,7 @@ Refer to the *Setup proxy to separate read requests and write requests* section | +-----------+ v [3] | | +-----------------+ | | | | - | +------------------>| TrustEVM Node + + | +------------------>| EOS EVM Node + | READ | | | +-----------------+ ``` @@ -640,8 +506,8 @@ Refer to the *Setup proxy to separate read requests and write requests* section - AMD Ryzen 9 5950X (*or other CPU with good single threaded performance*) - 4TB NVMe -### TrustEVM Node +### eos-evm-node - 64 GB - AMD Ryzen 9 5950X (*or other CPU with good single threaded performance*) -- 4TB NVMe \ No newline at end of file +- 4TB NVMe diff --git a/peripherals/flask_proxy/Dockerfile b/peripherals/flask_proxy/Dockerfile deleted file mode 100644 index 493d04ff..00000000 --- a/peripherals/flask_proxy/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM python:3 - -WORKDIR /usr/src/app - -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -CMD [ "gunicorn", "--bind", "0.0.0.0:80", "--workers", "4", "flask_proxy:app" ] \ No newline at end of file diff --git a/peripherals/flask_proxy/README.MD b/peripherals/flask_proxy/README.MD deleted file mode 100644 index c1b9f937..00000000 --- a/peripherals/flask_proxy/README.MD +++ /dev/null @@ -1,22 +0,0 @@ -This proxy is a temporary solution for supporting batch requests to silkworm. It should be removed when the silkworm can support batch requests. - -It is built with flask. So we use gunicorn to deploy it as flask server is not designed for prod. A Dockerfile is prepared in case we want to use docker. - -Use env READ_ENDPOINT to config the endpoint. - -## Build and Run with Docker -### Build -``` -sudo docker build . -t flask_proxy -``` -### Run - - -``` -sudo docker run -eREAD_ENDPOINT=READ_ENDPOINT=http://xxx:xxx -d --log-driver=none --publish=5000:80 --name=flask_proxy --restart=always flask_proxy -``` - -## Run on host using gunicorn -``` -gunicorn -eREAD_ENDPOINT=http://xxx:xxx -w4 -b0.0.0.0:5000 flask_proxy:app -``` diff --git a/peripherals/flask_proxy/flask_proxy.py b/peripherals/flask_proxy/flask_proxy.py deleted file mode 100644 index 360a5912..00000000 --- a/peripherals/flask_proxy/flask_proxy.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -import random -import os -import json -import time -import calendar -from datetime import datetime - -from flask import Flask, request, jsonify -from flask_cors import CORS -from eth_hash.auto import keccak -import requests -import json - -from binascii import unhexlify - -readEndpoint=os.getenv('READ_ENDPOINT') -app = Flask(__name__) -CORS(app) - -@app.route("/", methods=["POST"]) -def default(): - def forward_request(req): - return requests.post(readEndpoint, json.dumps(req), headers={"Content-Type":"application/json"}).json() - - request_data = request.get_json() - if type(request_data) == dict: - return jsonify(forward_request(request_data)) - - res = [] - for r in request_data: - res.append(forward_request(r)) - - return jsonify(res) - - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000) diff --git a/peripherals/flask_proxy/requirements.txt b/peripherals/flask_proxy/requirements.txt deleted file mode 100644 index 61d9d200..00000000 --- a/peripherals/flask_proxy/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -eth-hash==0.5.1 -Flask==2.2.2 -Flask-Cors==3.0.10 -requests==2.22.0 -simplejson==3.16.0 -gunicorn==20.1.0 \ No newline at end of file diff --git a/peripherals/health_helper/README.MD b/peripherals/health_helper/README.MD new file mode 100644 index 00000000..ce1c3712 --- /dev/null +++ b/peripherals/health_helper/README.MD @@ -0,0 +1,22 @@ +## Health check helper + +This tiny script will keep getting last block from the node and compare the timestamp with current time. If the time difference is larger than certain threshold, the node is considered stale. + +The scipt will response to http GET calls and return 200 if the node is healthy and 500 if the node is stale. + + +### To run + +The script is simple enough so no npm install is necessary. +``` +RPC_ENDPOINT=http://xx.xx.xx.xx:xx node index.js +``` + +### Environment variables +RPC_ENDPOINT: Endpoint of the node. No default value. Required. + +LISTEN_PORT: Port to listen and report state. Default to 8080. + +CHECK_INTERVAL: How often should the helper query last block (in ms). Default to 5000. + +STALE_THRESHOLD: The time difference threshold for a node to be considered stale (in second). Default to 60. diff --git a/peripherals/health_helper/index.js b/peripherals/health_helper/index.js new file mode 100644 index 00000000..860715e9 --- /dev/null +++ b/peripherals/health_helper/index.js @@ -0,0 +1,68 @@ +var http = require('http'); + +const url = process.env.RPC_ENDPOINT || 'http://localhost:80' +const listen_port = process.env.LISTEN_PORT || 8000 +const check_interval = process.env.CHECK_INTERVAL || 5000 +const stale_threshold = process.env.STALE_THRESHOLD || 60 + +var last_block = 0; +var stale = 1; + +function intervalFunc() { + let post_data = '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1}' + const options = { + hostname: url.hostname, + port: url.port, + path: '/', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': post_data.length + } + } + + let post_req = http + .request(options, response => { + var data = ""; + + response.on('data', function (chunk) { + data += chunk; + }); + + response.on('end', function () { + try { + const obj = JSON.parse(data); + + last_block = obj.result.timestamp + let timestamp = Number(last_block) + let now = Math.floor(Date.now() / 1000) + if (now - timestamp > stale_threshold) { + stale = 1; + } + else { + stale = 0 + } + + } + catch (_) { + stale = 1; + } + }) + }) + .on("error", err => { + console.log("Error: " + err.message); + stale = 1; + }); + + post_req.write(post_data); + post_req.end(); +} + +setInterval(intervalFunc, check_interval); + +http.createServer(function (req, res) { + res.writeHead(stale > 0 ? 500 : 200, { 'Content-Type': 'text/html' }); + res.write(last_block.toString()); + res.end(); +}).listen(listen_port); + diff --git a/peripherals/proxy/Dockerfile b/peripherals/proxy/Dockerfile index d6c9ad4c..5d3f882c 100644 --- a/peripherals/proxy/Dockerfile +++ b/peripherals/proxy/Dockerfile @@ -1,6 +1,11 @@ FROM openresty/openresty:alpine -COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf +ARG WRITE_ENDPOINT +ARG READ_ENDPOINT +ARG TEST_ENDPOINT +COPY nginx.conf /usr/local/openresty/nginx/conf/nginx.conf +RUN sed -i "s/WRITE_ENDPOINT/${WRITE_ENDPOINT}/g" /usr/local/openresty/nginx/conf/nginx.conf +RUN sed -i "s/READ_ENDPOINT/${READ_ENDPOINT}/g" /usr/local/openresty/nginx/conf/nginx.conf +RUN sed -i "s/TEST_ENDPOINT/${TEST_ENDPOINT}/g" /usr/local/openresty/nginx/conf/nginx.conf COPY eth-jsonrpc-access.lua /usr/local/openresty/nginx/eth-jsonrpc-access.lua - EXPOSE 80 443 diff --git a/peripherals/proxy/README.md b/peripherals/proxy/README.md index 5ab29b16..950ee2c0 100644 --- a/peripherals/proxy/README.md +++ b/peripherals/proxy/README.md @@ -3,25 +3,24 @@ Sample config for location: ``` location / { - set $jsonrpc_write_calls 'eth_sendRawTransaction'; - set $jsonrpc_blacklist 'eth_mining'; + set $jsonrpc_write_calls 'eth_sendRawTransaction,eth_gasPrice'; + set $jsonrpc_read_calls 'xxx,xxx'; + set $jsonrpc_test_calls 'xxx'; access_by_lua_file 'eth-jsonrpc-access.lua'; proxy_pass http://$proxy; } ``` -**The access log and error log are directed to stdout and sterr by default.** - -To build +To build (You can change the endpoints through build-arg): ``` -sudo docker build -t evm/tx_proxy . +sudo docker build -t evm/tx_proxy --build-arg WRITE_ENDPOINT=host.docker.internal:18888 --build-arg READ_ENDPOINT=host.docker.internal:8881 --build-arg TEST_ENDPOINT=host.docker.internal:8882 . ``` -To run +To run: ``` mkdir -p logs -sudo docker run -p 80:80 -v ${PWD}/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf evm/tx_proxy:latest > ./logs/access.log 2>./logs/error.log & +sudo docker run --add-host=host.docker.internal:host-gateway -p 80:80 -v ${PWD}/logs:/var/log/nginx -d --restart=always --name=tx_proxy evm/tx_proxy + ``` -Or use -d instead of the & approach. Make sure logs are handled properly in that case. \ No newline at end of file diff --git a/peripherals/proxy/eth-jsonrpc-access.lua b/peripherals/proxy/eth-jsonrpc-access.lua index f7d975a4..67e7371f 100644 --- a/peripherals/proxy/eth-jsonrpc-access.lua +++ b/peripherals/proxy/eth-jsonrpc-access.lua @@ -23,6 +23,12 @@ local function contains(arr, val) return false end +-- parse conf +local test_calls = nil +if not empty(ngx.var.jsonrpc_test_calls) then + test_calls = split(ngx.var.jsonrpc_test_calls) +end + -- parse conf local write_calls = nil if not empty(ngx.var.jsonrpc_write_calls) then @@ -30,9 +36,9 @@ if not empty(ngx.var.jsonrpc_write_calls) then end -- parse conf -local blacklist = nil -if not empty(ngx.var.jsonrpc_blacklist) then - blacklist = split(ngx.var.jsonrpc_blacklist) +local read_calls = nil +if not empty(ngx.var.jsonrpc_read_calls) then + read_calls = split(ngx.var.jsonrpc_read_calls) end -- get request content @@ -75,16 +81,15 @@ if version ~= "2.0" then return end --- if blacklist is configured, check that the method is not blacklisted -if blacklist ~= nil then - if contains(blacklist, method) then - ngx.exit(ngx.HTTP_FORBIDDEN) +-- Proxy calls to "read" +if read_calls ~= nil then + if contains(read_calls, method) then + ngx.var.proxy = 'read' return end end --- Proxy certain calls to "write" and rest to "read" -ngx.var.proxy = 'read' +-- Proxy calls to "write" if write_calls ~= nil then if contains(write_calls, method) then ngx.var.proxy = 'write' @@ -92,4 +97,13 @@ if write_calls ~= nil then end end +-- Proxy calls to "test" +if test_calls ~= nil then + if contains(test_calls, method) then + ngx.var.proxy = 'test' + return + end +end + +ngx.exit(ngx.HTTP_FORBIDDEN) return diff --git a/peripherals/proxy/nginx.conf b/peripherals/proxy/nginx.conf index 3c98d170..949577fc 100644 --- a/peripherals/proxy/nginx.conf +++ b/peripherals/proxy/nginx.conf @@ -1,6 +1,9 @@ worker_processes 5; +#daemon off; +error_log /var/log/nginx/error.log; +pid /var/log/nginx/nginx.pid; worker_rlimit_nofile 8192; - +user root root; events { worker_connections 4096; } @@ -10,32 +13,79 @@ http { index index.html index.htm index.php; upstream write { - server 172.31.73.5:3335; + server WRITE_ENDPOINT; } upstream read { - server 172.31.66.97:3336; + server READ_ENDPOINT; + keepalive 1000; + } + + upstream test { + server TEST_ENDPOINT; + keepalive 1000; } default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] $status ' '"$request" $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + log_format postdata '$remote_addr [$time_local] $upstream_status $remote_addr $request_body'; sendfile on; tcp_nopush on; + map $upstream_status $log_postdata { + default 0; + ~^5 1; + } + server { listen 80; server_name localhost; location / { + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' '*'; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + if ($request_method = 'POST') { + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' '*'; + } + if ($request_method = 'GET') { + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Credentials' 'true'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; + add_header 'Access-Control-Allow-Headers' '*'; + add_header 'Content-Type' 'text/html'; + return 200 'This is an API endpoint that only accepts JSON-RPC requests.
Please visit https://docs.eosnetwork.com/docs/latest/eos-evm/ for information about EOS EVM. \n'; + } + + resolver 127.0.0.11; set $proxy "read"; set $jsonrpc_write_calls 'eth_sendRawTransaction,eth_gasPrice'; - set $jsonrpc_blacklist 'web3_sha3,net_peerCount,net_listening,eth_syncing,eth_coinbase,eth_mining,eth_hashrate,eth_accounts,eth_getUncleCountByBlockHash,eth_getUncleCountByBlockNumber,eth_sign,eth_signTransaction,eth_sendTransaction,eth_getUncleByBlockHashAndIndex,eth_getUncleByBlockNumberAndIndex,eth_getCompilers,eth_compileLLL,eth_compileSolidity,eth_compileSerpent,eth_newFilter,eth_newBlockFilter,eth_newPendingTransactionFilter,eth_uninstallFilter,eth_getFilterChanges,eth_getFilterLogs,eth_getLogs,eth_getWork,eth_submitWork,eth_submitHashrate,db_putString,db_getString,db_putHex,db_getHex,shh_post,shh_version,shh_newIdentity,shh_hasIdentity,shh_newGroup,shh_addToGroup,shh_newFilter,shh_uninstallFilter,shh_getFilterChanges,shh_getMessages'; + set $jsonrpc_read_calls 'net_version,eth_blockNumber,eth_chainId,eth_protocolVersion,eth_getBlockByHash,eth_getBlockByNumber,eth_getBlockTransactionCountByHash,eth_getBlockTransactionCountByNumber,eth_getUncleByBlockHashAndIndex,eth_getUncleByBlockNumberAndIndex,eth_getUncleCountByBlockHash,eth_getUncleCountByBlockNumber,eth_getTransactionByHash,eth_getRawTransactionByHash,eth_getTransactionByBlockHashAndIndex,eth_getRawTransactionByBlockHashAndIndex,eth_getTransactionByBlockNumberAndIndex,eth_getRawTransactionByBlockNumberAndIndex,eth_getTransactionReceipt,eth_getBlockReceipts,eth_estimateGas,eth_getBalance,eth_getCode,eth_getTransactionCount,eth_getStorageAt,eth_call,eth_callBundle,eth_createAccessList'; + set $jsonrpc_test_calls 'eth_getLogs,debug_traceBlockByHash,debug_traceBlockByNumber,debug_traceTransaction,debug_traceCall,debug_traceCallMany,trace_call,trace_callMany,trace_rawTransaction,trace_replayBlockTransactions,trace_replayTransaction,trace_block,trace_filter,trace_get,trace_transaction'; access_by_lua_file 'eth-jsonrpc-access.lua'; proxy_pass http://$proxy; proxy_set_header content-type "application/json"; proxy_set_header accept "application/json"; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_http_version 1.1; + access_log /var/log/nginx/error/${proxy}-post-data.log postdata if=$log_postdata; } } diff --git a/peripherals/tx_wrapper/README.md b/peripherals/tx_wrapper/README.md index 2f93fb5c..cc6af93d 100644 --- a/peripherals/tx_wrapper/README.md +++ b/peripherals/tx_wrapper/README.md @@ -1,5 +1,11 @@ # evm_poc_tx_wrapper +## Prerequisite +You need to have a miner account (for example: a123) with enough CPU & NET resource. Then open the evm account balance +``` +./cleos push action eosio.evm open '{"owner":"a123"}' -p a123 +``` + ## Build ``` yarn @@ -9,11 +15,25 @@ yarn Create a .env file: ``` # This is sample keys. Do NOT Commit .env! -EOS_RPC="http://127.0.0.1:8888" + +# one or more EOS_RPC endpoints, separated by '|' +EOS_RPC="http://127.0.0.1:8888|http://192.168.1.1:8888" EOS_KEY="5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" -HOST="127.0.0.1" -PORT="3335" +HOST="0.0.0.0" +PORT="18888" + +# the evm contract account, eosio.evm for testnet & mainnet. +EOS_EVM_ACCOUNT="eosio.evm" + +# the miner account +EOS_SENDER="a123" + +# the miner's permission to use +EOS_PERMISSION="active" + +EXPIRE_SEC=60 ``` + Then ``` node index.js diff --git a/peripherals/tx_wrapper/index.js b/peripherals/tx_wrapper/index.js index e9dcce50..1256e503 100644 --- a/peripherals/tx_wrapper/index.js +++ b/peripherals/tx_wrapper/index.js @@ -1,7 +1,7 @@ const { Api, JsonRpc, RpcError } = require("eosjs"); const { JsSignatureProvider } = require("eosjs/dist/eosjs-jssig"); // development only -// const fetch = require("node-fetch"); // node only; not needed in browsers -const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); +const fetch = require("node-fetch"); // node only; not needed in browsers +//const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); const { TextEncoder, TextDecoder } = require("util"); // node only; native TextEncoder/Decoder const RpcServer = require("http-jsonrpc-server"); @@ -29,6 +29,11 @@ if (!process.env.EOS_SENDER) { process.exit(); } +if (!process.env.EOS_PERMISSION) { + console.log("Missing EOS_PERMISSION in .env file!"); + process.exit(); +} + if (!process.env.EOS_RPC) { console.log("Missing EOS_RPC in .env file!"); process.exit(); @@ -50,22 +55,49 @@ if (!process.env.PORT || !validateNum(process.env.PORT, 1, 65535)) { process.exit(); } +expire_sec = 300; +if (!process.env.EXPIRE_SEC) { + console.log("Missing EXPIRE_SEC, default to " + expire_sec); +} else { + expire_sec = +process.env.EXPIRE_SEC; +} + // Setting up EOS -const rpc = new JsonRpc(process.env.EOS_RPC, { fetch }); +rpc_list = process.env.EOS_RPC.split("|"); +console.log("number of RPC endpoints = " + rpc_list.length + ", using " + rpc_list[0]); +rpc_index = 0; + +rpc = new JsonRpc(rpc_list[rpc_index], { fetch }); const defaultPrivateKey = process.env.EOS_KEY; const signatureProvider = new JsSignatureProvider([defaultPrivateKey]); -const api = new Api({ +api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), textEncoder: new TextEncoder(), }); +function next_rpc_endpoint() { + rpc_index = (rpc_index + 1) % rpc_list.length; + console.log("changing RPC endpoint to " + rpc_list[rpc_index]); + rpc = new JsonRpc(rpc_list[rpc_index], { fetch }); + api = new Api({ + rpc, + signatureProvider, + textDecoder: new TextDecoder(), + textEncoder: new TextEncoder(), + }); +} + // EOS Helpers +var pushcount=0; async function push_tx(strRlptx) { - console.log('----rlptx-----'); + id=pushcount; + pushcount = pushcount + 1; + console.log("----rlptx(" + id + ")-----"); console.log(strRlptx); + t0 = Date.now(); const result = await api.transact( { actions: [ @@ -74,7 +106,7 @@ async function push_tx(strRlptx) { name: "pushtx", authorization: [{ actor : process.env.EOS_SENDER, - permission : "active", + permission : process.env.EOS_PERMISSION, } ], data: { @@ -86,10 +118,11 @@ async function push_tx(strRlptx) { }, { blocksBehind: 3, - expireSeconds: 3000, + expireSeconds: +expire_sec, } ); - console.log('----response----'); + latency = Date.now() - t0; + console.log("----response(" + id + ", " + latency + "ms) ----"); console.log(result); return result; } @@ -104,7 +137,7 @@ async function eth_sendRawTransaction(params) { var lastGetTableCallTime = 0 var gasPrice = "0x1"; async function eth_gasPrice(params) { - if ( (new Date() - lastGetTableCallTime) >= 500 ) { + if ( (new Date() - lastGetTableCallTime) >= 1000 ) { try { const result = await rpc.get_table_rows({ json: true, // Get the response as json @@ -115,11 +148,12 @@ async function eth_gasPrice(params) { reverse: false, // Optional: Get reversed data show_payer: false // Optional: Show ram payer }); - console.log("result:", result); + console.log("result:", result.rows[0].gas_price); gasPrice = "0x" + parseInt(result.rows[0].gas_price).toString(16); lastGetTableCallTime = new Date(); } catch(e) { console.log("Error getting gas price from nodeos: " + e); + next_rpc_endpoint(); } } return gasPrice; @@ -151,9 +185,24 @@ const rpcServer = new RpcServer({ rpcServer.setMethod("eth_sendRawTransaction", eth_sendRawTransaction); rpcServer.setMethod("eth_gasPrice", eth_gasPrice); +process.on('SIGTERM', function onSigterm () { + console.info('Got SIGTERM. Graceful shutdown start', new Date().toISOString()) + shutdown(); +}) + +function shutdown() { + rpcServer.close(function onServerClosed (err) { + if (err) { + console.error(err) + } + process.exit() + }) +} + // Main loop rpcServer.listen(+process.env.PORT, process.env.HOST).then(() => { console.log( "server is listening at " + process.env.HOST + ":" + process.env.PORT ); }); + diff --git a/product/Audit - BlockSec.md b/product/Audit - BlockSec.md deleted file mode 100644 index 52757a8c..00000000 --- a/product/Audit - BlockSec.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -tags: Proposal ---- - -# Product Brief - Audit of BlockSec - -## Prerequisites -#### Depends on Task #8, #12, #14, #15 -#### Scope including EVM-Runtime based on Silkworm, Token Mechansim, and dependencies libraries - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### N/A -#### Target audience: Who is the target audience and why? -#### N/A -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### N/A -#### Purpose: Define the product’s purpose briefly -#### N/A -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks - - -## Status -#### We have signed a contract with Blocksec, they will start to audit from 05/Sep to 14/Oct - - -## Statement Of Work -#### Static code review -#### Dynamic differential testing -#### Private deployment of the differential testing framework and continuous testing -#### Audit Scope: Code in the directory of "contract/tests" and "contract/external/nlohmann is not in the scope - - -## Support -#### Need a Brief or instruction of the code structure -#### Help with the engineers from BlockSec when they meet or have some questions -#### Check the audit report to make sure all bugs are fixed - - -## Background -#### The BlockSec focuses on the security of the blockchain ecosystem and collaborates with leading DeFi projects to secure their products. The team is founded by top-notch security researchers who are ex-Peckshield and experienced experts from both academia and industry. They have published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, and successfully protected digital assets that are worth more than 5 million dollars by blocking multiple attacks. - - -## Open questions diff --git a/product/Audit - Sentnl.io.md b/product/Audit - Sentnl.io.md deleted file mode 100644 index 4cf1d9cc..00000000 --- a/product/Audit - Sentnl.io.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -tags: Proposal ---- - -# Product Brief - Audit of Sentnl - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Depends on Task #8, #12, #14, #15 -#### Scope including EVM-Runtime based on Silkworm, Token Mechansim, and dependencies libraries - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### N/A -#### Target audience: Who is the target audience and why? -#### N/A -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### N/A -#### Purpose: Define the product’s purpose briefly -#### N/A -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Status -#### We have signed a contract with Sentnl.io, they will start to audit from 17/Aug to 23/Sep - -## Statement Of Work -#### Fuzzy test - -## Support -#### Need a Brief or instruction of the code structure -#### Help with the engineers from Sentnl when they meet or have some questions, Matias has helped a lot. -#### Check the audit report to make sure all bugs are fixed - -## Background -#### Sentnl have audited the code of multiple large cap organisations including Ripple, Ethereum and Difinity including over 40 smart contract on both EOSIO and Ethereum based chains. Our engineers have been responsible for various vulnerabilities in EVM based software, including the most recent GETH bug. - -## Open questions diff --git a/product/Chainlink Cooperation.md b/product/Chainlink Cooperation.md deleted file mode 100644 index cd87f0ce..00000000 --- a/product/Chainlink Cooperation.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief - Chainlink Operation - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Testnet based on Silkworm has been launched and Introduction of ecosystem of EVM to Chainlink - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### N/A -#### Target audience: Who is the target audience and why? -#### N/A -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### N/A -#### Purpose: Define the product’s purpose briefly -#### Need a protocol to feed price to dex -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions - -## Status -#### We have a formal discussion with Chainlink groups and need more updates of Defi ecosystem to show that why we need Chainlink and How Chainlink benefit from EVM \ No newline at end of file diff --git a/product/Developer Tutorial.md b/product/Developer Tutorial.md deleted file mode 100644 index d3c9a3b0..00000000 --- a/product/Developer Tutorial.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief - Developer Tutorial - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Testnet based on Silkworm is deployed successfully - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### Our developers and regular users need a tutorial doc to learn how to develop/user/deploy on EVM, and what the important tools and links are -#### Target audience: Who is the target audience and why? -#### Developers is the main target, for them to know more technical details about the EVM, that will help them with development for their Dapps -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### Documentaions on docs.trust.one -#### Purpose: Define the product’s purpose briefly -#### A detailed technical introducion of Trust EVM for developers, and it will help them have a better understanding of how to deploy Dapps on EVM -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A - -#### Functionality -#### Including these contents: -#### a. Getting Started -#### b. Important Links for interaction: RPC Endpoints, Metamask, Trustscan, Truffle, Verify a contract -#### c. Compatibility: EVM, Gas, JSON-RPC -#### d. Token Economy -#### e. Integrate -#### f. FAQs - -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions \ No newline at end of file diff --git a/product/EVM2EVM Bridge Cooperation.md b/product/EVM2EVM Bridge Cooperation.md deleted file mode 100644 index c752f281..00000000 --- a/product/EVM2EVM Bridge Cooperation.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief of EVM2EVM Bridge Cooperation - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Silkworm testnet launch to test the bridge - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### Who are holding BTC, ETH, USDT and some other crypto assets want to trade, play and make profit in Trust EVM, so they will bridge their assets to exchange for new assets or to stake to get a APR in EVM -#### Target audience: Who is the target audience and why? -#### Target Chain: BSC, Polygon and Ethereum is the first priority to bridge. Why? If we stick to the Gamefi strategy for the bear market, BSC, Polygon and Ethereum are still active and have a lot of players to play game in it. -#### Target users: Who are holding BTC, ETH, and stablecoins especially USDT, USDC and BUSD, we will attact them to join Trust EVM to play games and experience the performance of EOS. -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### For now in the bear market, we insist on GameFi strategy instead of DeFi, we need projects deployed on BSC, Polygon and other EVM compatible chains, also we will need a bridge to attract crypto assets in this way so that we can get a High DAU(Daily Active User) for our EVM - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### EVM2EVM bridge will be done through partnership with existing bridge solutions. We have partnership with Multichain and Meson.fi now for non-stable and stable assets. -#### Purpose: Define the product’s purpose briefly -#### N/A -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### Securtiy risk is the biggest risk for both users and EVM projects, need to cooperate with Recovery+ -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions diff --git a/product/Nodes - Rollout & Airdrop & Initial.md b/product/Nodes - Rollout & Airdrop & Initial.md deleted file mode 100644 index 614e92de..00000000 --- a/product/Nodes - Rollout & Airdrop & Initial.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -tags: Proposal ---- - -# Nodes - Rollout & Airdrop & Initial - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### The tech design of the Token mechanism - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### A new opportunity to be an RPC nodes who will sever the Trust EVM network and will get paid by EVM Tokens. It's a staking buusiness so that many exchanges and groups want to be part of it - -#### Target audience: Who is the target audience and why? -#### BPs of EOS network and some groups who are running staking business such as Everstake - -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### Conducive to unifying the interests of all EOS nodes and contributors - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### Online and Offline recruitment - -#### Purpose: Define the product’s purpose briefly -#### Trust EVM need a ditributed RPC Network builded by multiple RPC nodes all over the world - -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### Have more than 30 nodes to start the mainnet - -#### Assumptions -#### N/A - -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### 1. APR is not high enough to attract users to stake EVM tokens, so we may not have enough nodes to start the mainnet -#### 2. The delay of the development of token mechanism will also have a bad influence on the staking - - -#### Functionality -#### 1. APR estimation, staking workflow, and requirements of configurations for Nodes' operationg hardwares need to be prepared - 1 weeks -#### 2. Offline recruitment with the BPs of EOS - 2 weeks -#### 3. Business development with groups like Everstake and Exchange - parallel with 2# in 2 weeks -#### 4. Airdrop the tokens to old BPs - 1 weeks -#### 5. Token sales to staking nodes - parallel with 2#,3# in 2 weeks -#### 6. A Website to identify ID, staking EVM tokens to regiter to be one nodes - 2 weeks -#### 7. Test all RPC nodes and pre-launch - 3 days - -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions diff --git a/product/The Graph Cooperation.md b/product/The Graph Cooperation.md deleted file mode 100644 index e9c102d5..00000000 --- a/product/The Graph Cooperation.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief - The Graph Cooperation - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Testnet based on Silkworm, we also need to make sure Silkwormn has supported the Graph - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### N/A -#### Target audience: Who is the target audience and why? -#### N/A -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### N/A -#### Purpose: Define the product’s purpose briefly -#### N/A -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions - - -## Status -#### Need to confirm that the silkworm support the Graph or not? \ No newline at end of file diff --git a/product/Token Economy Mechanism - Variable Simulation.md b/product/Token Economy Mechanism - Variable Simulation.md deleted file mode 100644 index 61c1f894..00000000 --- a/product/Token Economy Mechanism - Variable Simulation.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief of Token Economy Mechanism - Variable Simulation - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Task #13, #14 - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### N/A -#### Target audience: Who is the target audience and why? -#### The whole EVM team needs to know the simulation of token price, circulation and the influence by vesting time -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### At the beginning of the project, controlling liquidity to realize the basic support for value capture for token price, to seek for the balance of inflation - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### A excel with a math model to simulate possiblities of the bonding curve of EVM token, such as Staking APR, Token price, vesting influence, etc. -#### Purpose: Define the product’s purpose briefly -#### To simulate the circulation of token in the market and the price of token based on the value capture model, with that to finalize the design of the token economy -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### Token based APR > 20% in bull market/ APR > 7.5% in bear market, moderate inflation, reduce circulation if market is terrible -#### Assumptions -#### N/A -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### The market is so terrible and EVM token needs to avoid launching Liquidity pool. -#### Functionality -#### N/A -#### Features -#### N/A -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - - -## Open questions diff --git a/product/TrustInTime.md b/product/TrustInTime.md deleted file mode 100644 index 3d1d26c2..00000000 --- a/product/TrustInTime.md +++ /dev/null @@ -1,13 +0,0 @@ -# Trust In Time - -When creating a blockchain within a blockchain a few decisions that normally don’t have to be made come up. One of these decisions is what you base your block numbering on for constructing the nested blockchain’s blocks. - -There are a couple of approaches that can be used; correlating the blocks one-to-one with the foundational blockchain or abstracting to some external basis. If one uses the mapping of foundational blocks to the nested blockchain’s blocks then as the foundational chain improves performance in the future then the time scale for the nested blockchain is also changed. This can lead to problems where the nested blockchain is using the assumptions about the time interval for block creation, because if the foundational chain initially has half second blocks and moves to quarter second blocks then the nested chain immediately becomes a quarter second. This may sound like a benefit, but the nested chain has no ability to opt in to new when it makes sense for it. - -What we opted to do is use time itself as the basis for the nested blockchain block timestamps. Because we’ve decoupled it from the foundational chain, the nested blockchain can determine if, when and how it creates blocks at shorter intervals. - -One more factor is what time scale and interval do we want to use to determine the interval of block production. If we look at EVM, its standard assumes for each block the correlated timestamp is distinct. This leads to another standard of EVM which is that EVM only has resolution of time of seconds, so from this we can only go as far a one second block time as to not work against the standard, cause compatibility issues or make it yet another caveat that smart contract developers and DApp developers have to be cognizant of. If you were to use a shorter duration like a half of second then for that same timestamp you would have two blocks with the same timestamp, i.e. breaking the invariant that for each successive block the timestamp is distinct and monotonic. - -The other factor into this is that the latency difference between 0.5 second block time and 1 second block time is X% . If you look at things like https://twitter.com/ape_swap/status/1585339130627489792, we can see that Telos, which has 0.5s block time, overall latency is still quite high. When we look at interfaces and services designed for EVM based projects, many have multi-second polling so the overall latency disparity is purely academic until block times become greater than a few seconds. - -Given the low value that comes with going for 0.5s block time, we made the delineation to go with the lowest latency that still adheres to the EVM specification which is 1 second block times. diff --git a/product/Web Wallet Integration.md b/product/Web Wallet Integration.md deleted file mode 100644 index 403b4e8d..00000000 --- a/product/Web Wallet Integration.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief - Web Wallet integration/ Nodes Operating Management - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -#### Task #47, #60, it's a future task to have a user interface for RPC nodes to stake and claim their EVM tokens, the operators may use Metamask at the beginning - -## Problem - -#### Opportunity: What are the needs of our target user groups? -#### The basic requirement is that RPC nodes need a UI to support verified to be a nodes / EVM staking to nodes/ EVM rewards claim -#### The RPC nodes operators may need a bridge protocol and liquidity pool to be integrated with the wallet so that they can exchange and transfer EOS and EVM conveniently - -#### Target audience: Who is the target audience and why? -#### Basically The RPC nodes operators, they need to stake and claim EVM tokens from the nodes - -#### Strategic alignment: How does this problem align with our core strategic pillars? -#### N/A - -## Solution - -#### Solution name: How should we refer to this product opportunity? -#### N/A -#### Purpose: Define the product’s purpose briefly -#### The RPC nodes operators may need a bridge protocol and liquidity pool to be integrated with the wallet so that they can exchange and transfer EOS and EVM conveniently -#### Success definition: What are the top metrics for the product (up to 5) to define success? -#### N/A -#### Assumptions -#### UI for staking and claim fo EVM token is not that convenient to user, the web wallet is not necessary for now -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -#### N/A -#### Functionality -#### a. UI support -#### b. Staking -#### c. Claim -#### d. Status and KPI of RPC nodes -#### e. Transfer and exchange -#### Features -#### One UI to compelet all the to-dos for RPC nodes operators -#### User stories -#### N/A -#### Additional tasks -- [ ] #issue number - -## Open questions \ No newline at end of file diff --git a/product/block_explorer.md b/product/block_explorer.md deleted file mode 100644 index 1644c8c4..00000000 --- a/product/block_explorer.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -tags: Proposal ---- - -# Block Explorer - -## Prerequisites -#### Dependencies: -Silkworm testnet -## Problem -We need block explorers for both testnet and mainnet - -## Solution - -#### Solution name: Block Explorer -#### Purpose: -Setup a block explorer for both the testnet and mainnet. -This work will probably be done via outsourcing, the brief is just high level description and will not go through too detailed things. - -#### Success definition: -Support etherscan.io level access (500k access per day) -#### Assumptions -#### Risks: -Silkworm's support for Web3 API maybe not that complete. It's possibble that block exploreer will rely on some of those unfinished API - -#### Functionality -All what etherscan support. -Plus: -- Link from EVM block to corresponding EOS block in certain EOS explorer -- Find EVM block via EOS block id (if possible) - -#### Features -#### User stories -#### Additional tasks -- [ ] #issue number - -## Open questions - -Talked to some outsourcing guys with experience in block explorer development. -The existing API seems enough, except for eth_getTransactionReceipt, which is marked as partially implemented for silkworm. -This issue will be a blocker if not properly solved. diff --git a/product/block_mapping.md b/product/block_mapping.md deleted file mode 100644 index 89de0eb0..00000000 --- a/product/block_mapping.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -tags: Proposal ---- - -# Block Mapping - -## Prerequisites -#### Dependencies: N/A - -## Problem - -#### Opportunity: -Currently block mapping between TrustEVM on EOS and TrustEVM node do not map to the same numbers. To allow for block hash to work consistently between these two we need a consistent mapping system. -#### Target audience: -- API node operators -- Contract developers -#### Strategic alignment: -It will allow for consistency between on an Antelope chain and TrustEVM node which will allow for more tooling to work correctly and some allow for more operations at the EVM layer. - -## Solution - -#### Solution name: -Consistent Block Mapping -#### Purpose: -Define and implement the block mapping used by TrustEVM on EOS and by TrustEVM node. -#### Success definition: -The block numbers on TrustEVM contract and TrustEVM node are the same. Running block hash produces the same hash. -#### Assumptions: -The contract block mapping needs to be finished first in this feature. - -#### Risks: -The inclusion of producing empty blocks to take up slack might be slow. -#### Functionality: -A "genesis" timestamp will be captured. Proposing a new action called `init` in TrustEVM contract. Much like `init` for the system contract needs to be run first, this `init` will need to be run first. - -```c++ -[[eosio::action]] -void init() { - // this action may also be used for setting - // the initial genesis state too - running_state.set_genesis(current_time_point()); -} - -int64_t get_evm_block_number() const { - const auto& genesis = running_state.get_genesis().sec_since_epoch(); - const auto& now = current_time_point().sec_since_epoch(); - - return now - genesis; -} - -. -. -. - -[[eosio::action]] -void pushtx(...) { - // inside of pushtx - block.header.number = eosio::internal_use_do_not_use::get_block_num(); - block.header.timestamp = eosio::current_time_point().sec_since_epoch(); -``` -Based on the block timestamp of the Antelope block and the time from initialization we simply get the slot block number for the time frame we are in. - -#### Features -#### User stories -#### Additional tasks - -## Open questions diff --git a/product/delegate_staking_contract.md b/product/delegate_staking_contract.md deleted file mode 100644 index e0491a45..00000000 --- a/product/delegate_staking_contract.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -tags: Proposal ---- - -# Delegate Staking Contract - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -The staking contract - -## Problem - -We want to provide a way for regular users to delegate their evm to us to stake - -## Solution - -#### Solution name: Delegate Staking Contract -#### Purpose: -A contract for users to delegate stake their evm - -#### Success definition: -Functioning is enough, should be no major performance requirement. -#### Assumptions -#### Risks: What risks should be considered? -- If the lock mechanism of the origin staking contract is too restrictve, this contract may be hard to design -- If there's any problem, it's quite possible that funds got stuck in the contract. So extra care need to be taken. - -#### Functionality -There are two potential easy designs: -1 siimilar to a aggragator: when user stake here, he got a share of the pool. The fund in the pool will be staked in the origin staking contract and some script will collect rewards and add them to the pool (and invest again) periodically. There will be no concept of reward from a user's point of view. The value of the share of pool will grow and this is what user gain. -2 serve kind of like a proxy. The user will be as if he is directly staking to the origin contract. Rewards are calculated use the same formula as the origin contract. - -The first design is quite common for yield aggragators. -The advantage of the second one is the behavior is similar, but the risk is there are less code to reference. - - -#### Features -See user stories -#### User stories -- A detailed API definition first (deliverable) -- A Contract in solidity (deliverable) - - User API - - Methods for user to stake and withdraw EVM - - if there are no enough fund, the contract should withdraw from the orgin contract. - - Methods for Operator to claim rewards (only if we pick the second design) - - Methods for Operator to view related data - - Admin API - - Methods for admin to blacklist users - - Methods for admin to view related data in an aggragate way - - Methods for re-invest (only if we pick the first design) - - Methods for admin to set the return rate for the users. - - Extra functionality - - Make the contract be capable of accepting EVM tokens as the source of payment. -#### Additional tasks -- [ ] #issue number - -## Open questions -- Need to pick a design -- Need to determine the behavior when there are locking mechanisms in the origin contract. i.e. Some withdraw from user my trigger withdraw in locking period. diff --git a/product/design_doc/Copy of proxy design.drawio.png b/product/design_doc/Copy of proxy design.drawio.png deleted file mode 100644 index 2fec852d..00000000 Binary files a/product/design_doc/Copy of proxy design.drawio.png and /dev/null differ diff --git a/product/design_doc/EOS-EVM design silkworm.drawio.png b/product/design_doc/EOS-EVM design silkworm.drawio.png deleted file mode 100644 index b9a32e13..00000000 Binary files a/product/design_doc/EOS-EVM design silkworm.drawio.png and /dev/null differ diff --git a/product/design_doc/EOS-EVM design.drawio.png b/product/design_doc/EOS-EVM design.drawio.png deleted file mode 100644 index cc6ee798..00000000 Binary files a/product/design_doc/EOS-EVM design.drawio.png and /dev/null differ diff --git a/product/design_doc/Staking Contract Design.docx b/product/design_doc/Staking Contract Design.docx deleted file mode 100644 index 1c0b2eaf..00000000 Binary files a/product/design_doc/Staking Contract Design.docx and /dev/null differ diff --git a/product/design_doc/proxy design.drawio.png b/product/design_doc/proxy design.drawio.png deleted file mode 100644 index 417f1d0c..00000000 Binary files a/product/design_doc/proxy design.drawio.png and /dev/null differ diff --git a/product/design_doc/read_incentive_system.md b/product/design_doc/read_incentive_system.md deleted file mode 100644 index 98090476..00000000 --- a/product/design_doc/read_incentive_system.md +++ /dev/null @@ -1,126 +0,0 @@ -# Read Incentive System Design - -## Requirements - -- The proxy should direct traffic to upstream in a weighted manner. -- The upstream and weight should be updated on the fly according to some on-chain data. -- Payment need to be calculated from access log periodically and save to the chain. - -## General Design - -We consider using Nginx as the core of the proxy. The reason for that is: - -1. Nginx is widely used proxy software -2. Nginx with upsync module can easily meet our need -3. It might be hard to develop an efficient and reliable proxy in such short time - -![Figure 1: General Design](https://github.com/eosnetworkfoundation/TrustEVM/blob/yarkinwho-product-desc-update/product/design_doc/proxy%20design.drawio.png) - -Figure 1: General Design - -The system can be divided into several modules: - -1 The proxy: - -An Nginx system configured to serve traffic using a weighted round robin algorithm with the ability to sync upstream address and weight on the fly. - -2 Upstream helper - -A small service read the staking info from the staking contract on TrustEVM and translate them into upstream and weight for Nginx to sync from. It can either directly allow Nginx to query - -3 Workload verifier - -A small service read the access log of Nginx and calculate the income for each upstream. It will write the result to the staking contract on TrustEVM. - -4 Staking contract on TrustEVM - -A Contract on TrustEVM that RPC providers can stake and put endpoint info on. - -## Module Design - -### The proxy - -The current design is simply using Nginx with upsync module. - -https://github.com/weibocom/nginx-upsync-module - -The upsync module can dynamically update upstream info from external source without reloading the config. We can let the nginx to sync upstream list from either the Upstream helper directly or use some storage such as redis or consul for it. - -![Figure 2: Design with some storage service](https://github.com/eosnetworkfoundation/TrustEVM/blob/yarkinwho-product-desc-update/product/design_doc/Copy%20of%20proxy%20design.drawio.png) - -Figure 2: Design with some storage service - -Reliability and Performance: - -This module should be designed with enough concerns regarding reliablity and performance. -- Proxy needs high availability design -- Access log needs backup - -### Upstream helper - -The helper that read the contract for staking info and translate it to nginx upstream info. - -It then either allow nginx to directly read from it or save the info to some storage service such as redis and consul. - -A tiny python/nodejs can do the work nicely. If we want to write to storage instead of serving http requests directly, even a bash script with curl might be enough. - -This module should be developed together with the proxy. - -Reliability and Performance: - -This module should have relatively low reliability and performance requirement. - -Dependency: - -Staking contract API - -### Workload verifier - -The verifier will periodically check the access log, count the traffic for each operator and save the result to the staking contract. - -The access log should be text files in standard nginx format  with $upstream_addr configured in log format. - -A simple python/nodejs can do the job nicely. C++ is fine if figure out a good lib to use. - -If we want to distinct read/write, the Nginx have to look into the request and that will introduce some overhead. - - -Reliability and Performance: - -This module should have relatively low reliability and performance requirement. - -Dependency: - -Staking contract API - -### Staking contract - -The contract must have the following features: - -1. Operator can stake EVM -2. Operator can set upstream address (optionally separate read/write) when staking -3. Allow list to let admin set operator/workload verifier -4. Workload verifier can update the payment -5. Operator can claim payment -6. The contract can accept EVM tokens as the source of payment - -It should be a standard Ethereum contract in solidity as simple as possible. - - -Reliability and Performance: - -This module should have relatively low reliability and performance requirement. - -The API should be defined first so other modules can start working. - -## Important issues -- Should the operator be paid according to what they have done (calculated from access log), or be paid according to staking shares only while access log is used for monitor the activity. Foundation can punish malicious oprators according to the log. - - We can go with the second option in the begining and add the functionality later -- Should we distinct read and write when recording access - - Consensus is NO. The gain is too little. -- Should we use a kv storage in upstream helper - - Yes. -- How our current proxy based read-incentive system deal with performance problems? e.g. what happen if the proxy is in us and the upstream node is in asia? - - We will go with single proxy in the begining. - - We can add more in different region - - We can add more complicated mechanism later diff --git a/product/design_doc/staking_contract_details.md b/product/design_doc/staking_contract_details.md deleted file mode 100644 index 3aff6fdf..00000000 --- a/product/design_doc/staking_contract_details.md +++ /dev/null @@ -1,58 +0,0 @@ -1 How often is due amount updated -- Per block -- Per xxx seconds -- Per day -- Even Longer - -Note: update per block is actually easy for implementation as we have examples from sushi like pools. - -Answer: Use whatever leads to a simple solution, so block may be better - -2 If we choose to update longer than per block, how to calculate due amount if operator add staking in the middle of an update period -- The share change will only affect starting from next period -- Try to calculate a proportion? - -Answer: N/A - -3 How often should fund be added to the contract -- Per xxx seconds -- Per day -- Even longer - -Answer: It's not a blocker here, we can decide later - -4 How is the reward rate be determined -- Fixed rate (of course manually configurable) -- Fixed rate per period (e.g. allow configure the rate for the next day/month so we have a stable update cycle, change to the rate only affect following cycles) - -Answer: Go for the easy one, so maybe fix rate - -5 behavior when out of fund -- Keep recording due amount? - -Answer: Fail the call by telling them try later. Keep recording due amount. We should try to have enough margin there. - -6 Should we lock the staking for a while? This will help preventing the exploit of withdraw before proxy query for staking and deposit back after the query. -- Lock one upstream update period? -- Lock longer (like months) - -Answer: Yes, or only remove reward if you want to withdraw during lock period - -7 How often should upstream been updated -- Hours? -- Day? -- Longer? - -Answer: Try to find a reasonable short time that do not affect performance - -8 Some potential design -- A modified single token pool -- Multiple pools, each represent one operator, so we have the potential of delegation from first day. (can be disabled by only allow operator to deposit for his pool) - -Answer: One single pool should be fine. - -9 Maybe use wrapped EVM? Code will be slightly simpler. - -Answer: decide later - -10 We can accept change behavior if we want to update the mechanism to accept real traffic data diff --git a/product/design_doc/translator_design.md b/product/design_doc/translator_design.md deleted file mode 100644 index 336f6e5b..00000000 --- a/product/design_doc/translator_design.md +++ /dev/null @@ -1,71 +0,0 @@ -The translator module does not have detailed design doc as there are too many unknown things before the development start. Now it’s a good time to review the design and have some docs. - -# The Proof of Concept Translator - -The proof of concept translator is coded with Nodejs. The reason for selecting Nodejs is that the Nodejs version of Ethereum client project is quite complete and active. Also, js/ts projects are relatively easy to develop. - -The proof of concept translator works as the following: - -- Call get_block to Nodeos through http API -- For every 10 EOS blocks, the Translator generate one EVM block - 1. Transactions in EOS blocks are included sequentially - 2. Gas limit is locked to 0x7ffffffffff - 3. Difficulty is locked to 1 - 4. ExtraData field is used to store the blockid of the last of the 10 EOS blocks - 5. Timestamp is the timestamp of the last of the 10 EOS blocks -- Sync the new blocks to a slightly modified (gas limit and difficulty) Geth (with PoW check off obviously) client via p2p protocol - -The Geth client is then used to serve all read access. - -![Figure 1: Overview of the system.](https://github.com/eosnetworkfoundation/TrustEVM/blob/yarkinwho-product-desc-update/product/design_doc/EOS-EVM%20design.drawio.png) - -Figure 1: Overview of the system. - -## Limitation and Findings - -- **It’s Nodejs.** Heathen! -- **http API is used.** Use it for easier development instead of using ship protocol. -- **The p2p connection to the Geth is quite unstable.** The instability usually happens when there is something wrong during the sync. If Geth missed one block, it will never retry fetching that block from the same peer and will wait for another peer to fix this issue eventually, which will never happen in our design. Even with a lot of hacks to force retry, there are still some instability remains, mainly happen when EOSIO micro-forks causing the EVM chain to switch branch. We didn’t spend more time to debugging for that as the p2p thing will be removed in silkworm design anyway. -- **No version/fork control.** The translator will never aware if there’s any broken changes in the system. -- **Block height and Block hash.** The EVM runtime is not aware of the translator so it has no knowledge of block height and block hash used in the fake chain. The testnet have such function disabled in contract -- **Lacking a way to detect data difference between EVM runtime and Translator.** The translator has the root of data calculated using the standard Ethereum approach but the runtime does not have such thing. -- **Faster block speed is possible.** Faster (e.g. 5 to 1 mapping or 2 to 1 mapping) block speed is tested and seems fine. We even tested 1 to 1 mapping by skipping timestamp validation in Geth code. And it works at least for simple smoke tests. But for user experience, extremely fast mapping (1 to 1) shows no clear advantages as most client (e.g. metamask) will not query for transaction result that frequently anyway. This hints that maybe it is better to use more conservative 2 to 1 (or even slower) mapping in the final design so that we can avoid changing Ethereum timestamp logic. - -# The Silkworm based Translator - -The Silkworm based translator will have the following advantages: - -- It’s Modem C++. -- Silkworm has the faster Erigon design. -- Ideally it can serve as a plugin of Nodeos so it can be notified for new blocks just in time and can read required data directly from memory. -- It can have a standalone RPC process reading from the same database. This is good for deployment. -- No Geth is needed so we do not need to debug the p2p craps again. -- It can be configured to use the exact same VM code as the EVM runtime for better consistency. - -![Figure 2: System with Silkworm](https://github.com/eosnetworkfoundation/TrustEVM/blob/yarkinwho-product-desc-update/product/design_doc/EOS-EVM%20design%20silkworm.drawio.png) - -Figure 2: System with Silkworm - -## Current state - -It is working with the same 10 to 1 mapping. Matias and Bucky is currently working on making it share same VM code as the EVM runtime and increase some stability there. - -# Works to solve the discovered issues - -Clearly, some issues discovered in the PoC are solved automatically with the Silkworm but some are not: - -- Nodejs: Solved by Silkworm -- http API used: Solved by Silkworm -- p2p unstable: Solved by Silkworm -- No version control: Proposed solution: - - EVM runtime save some version number in certain table together with the id of the block that the new version/fork should apply. - - Translator should reject to process after that if it is not patched to the new version. -- In contract block height and hash: Proposed solution: - - Use timestamp to calculate block height - - e.g. if T is defined as genesis and it’s 1s block, then any transactions in any EOS block with timestamp in [T+h, T+h+1) belongs to the h’th EVM block. (assume genesis height is 0) - - Calculate hash from block height and some other deterministic things -- Lacking data integrity check: Proposed solution: - - Take snapshots periodically for recovery and checks. - - Use some external service to scan. - - Were there any issue, EVM runtime states shall prevail. - - Patch the silkworm and recover from snapshots. diff --git a/product/evm_token_bridge.md b/product/evm_token_bridge.md deleted file mode 100644 index e0b95219..00000000 --- a/product/evm_token_bridge.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -tags: Proposal ---- - -# EVM token bridge - -## Prerequisites - -#### Dependencies: What work must be completed before this part of the solution is actionable? - -Depends on changes for the new [gas fee algorithm](./gas_fee_algorithm.md). - -## Problem - -#### Opportunity: What are the needs of our target user groups? - -Gas fees in the EVM tokens are collected on the EOS side of Trust EVM. There needs to be a way to move these tokens from the EOS side to the EVM side to be used within EVM contracts. This is in particular necessary to distribute some of the gas fees collected by the EVM runtime contract to a staking contract as part of the Trust EVM token economics. - -#### Target audience: Who is the target audience and why? - -The new mechanism allows for moving EVM tokens between the two sides (EOS and EVM) of Trust EVM, which is valuable to any user who wishes to both use applications within the Trust EVM and applications on EOS that use the EVM token. But the primary audience for this mechanism are Trust EVM miners (the entities that provide EVM transactions for processing to the EOS blockchain) as well as anyone participating in the Trust EVM staking contract (which supports Trust EVM RPC providers). - -#### Strategic alignment: How does this problem align with our core strategic pillars? - -The new mechanism is a requirement for the Trust EVM token economics which creates the necessary incentives for RPC providers to provide the critical services needed for the operation of the Trust EVM platform. - -## Solution - -#### Solution name: How should we refer to this product opportunity? - -EVM token bridge - -#### Purpose: Define the product’s purpose briefly - -Enable EVM tokens held as eosio.token compatible balances on the EOS side to be moved over to the EVM side as the EVM's native token, and vice versa. Additionally, allow fees collected by the EVM runtime contract to also be moved from the EOS side to the EVM side to the particular EVM address hosting the Trust EVM staking contract. - -#### Success definition: What are the top metrics for the product (up to 5) to define success? - -1. The fees collected in the EVM runtime contract can be moved by anyone over to the Trust EVM staking contract to be distributed according to the logic of that staking contract. -2. An EOS account with EVM tokens can move their tokens to a Trust EVM address. -2. A Trust EVM user holding EVM tokens at some address can send an EVM transaction to move those tokens to the possession of a particular EOS account. - -#### Assumptions - -There is an official Trust EVM staking contract deployed within Trust EVM at some address which can be provided to the EVM runtime contract through a privileged configuration action before the `collectfee` action can be used. - -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ - -The solution requires reserving a range of EVM addresses. It should be virtually impossible for a legitimately constructed address (whether for externally-owned account or contract account) to conflict; someone could try to brute-force an externally-owned account address to conflict but the EVM runtime contract will reject EVM transactions that create conflicting addresses. But there is some small risk that the Ethereum protocol could evolve in the future to use addresses in the reserved range, which, if it occurred, would require the Trust EVM protocol to further diverge from other EVMs to integrate those updates while working around the conflict. - -#### Functionality - -Add support for eosio.token-compatible functionality to the EVM runtime contract. This means adding the `issue`, `retire`, `open`, `close`, and `transfer` actions. The `transfer` will be restricted to require the recipient to `open` a balance before funds can be sent to the recipient to avoid the complications with senders paying for the RAM of the recipients table entries. Note that the recipient account's balance entry must also be opened to send EVM tokens to the recipient from the EVM side. Otherwise, that EVM transaction would be rejected (meaning not included in the EOS blockchain) and not just reverted to avoid reproduction issues for the silkworm engine. For the same reason of avoiding reproduction issues in the silkworm engine, sending EVM tokens from the EVM side to the EOS side will increase the balance without creating a `transfer` action or running third-party smart contract code of the recipient. - -A new `sendevm` action will be added to the EVM runtime contract which allows for the EOS side to initiate a transfer of EVM tokens from a balance associated to an EOS account to a specified Trust EVM address. Not only does this transfer the EVM tokens to the specified address, but it actually makes a proper EVM call to the address which would run any smart contract code at that address using call data specified in the `sendevm` action. This means that the caller of the `sendevm` action needs to have EVM tokens that they can use to pay the gas of the EVM transaction generated as a result of calling the `sendevm` action. This action can be called by EOS smart contracts as an inline action which allows a mechanism for EOS smart contracts to interact with an EVM contract; however, the initial version of `sendevm` implemented as part of this product feature will not make any data returned by the EVM contract accessible to the calling EOS smart contract. EVM transactions processed as a result of the `sendevm` action should operate in a mode where a revert of the calling code should abort the transaction; this maintains the atomicity expectations of EOS smart contracts and is particularly important since there would be no feedback mechanism to tell the calling EOS smart contract whether the EVM call reverted. - -A new `collectfee` action will be added to the EVM runtime contract which is like a specialized `sendevm` action in behavior. It moves the fees collected by the EVM runtime contract to a particular EVM address which is expected to host the Trust EVM staking contract. This action can be called by anyone willing to pay the CPU/NET costs of the EOS transaction and the gas fees of the EVM transaction generated as a result of calling the `collectfee` action because the address that the EVM tokens are moved to is determined by the EVM runtime contract and not the caller. This action cannot function until the address of the Trust EVM staking contract is first established as part of the EVM runtime contract state, which must be done first by some other new action added to the EVM runtime contract which can only be called by the appropriate parties responsible for maintaining the EVM runtime contract. - -#### Features - -#### User stories -#### Additional tasks - -## Open questions diff --git a/product/evm_token_bridge/EVM_flow.svg b/product/evm_token_bridge/EVM_flow.svg deleted file mode 100644 index abeb91d1..00000000 --- a/product/evm_token_bridge/EVM_flow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/product/evm_token_bridge/README.md b/product/evm_token_bridge/README.md deleted file mode 100644 index a2760163..00000000 --- a/product/evm_token_bridge/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# EVM token bridge - -The EVM token bridge is a mechanism that allows EVM tokens to move between the EOS environment and the EVM environment via the EVM runtime contract. As part of this mechanism, the EVM runtime contract also acts as a mostly `eosio.token`-compatible contract to host the EVM token, and includes the standard actions `issue`, `retire`, `open`, `close`, and `transfer`. It is not possible to create arbitrary tokens (there is no `create` action). The EVM runtime contract acting as a token contract only supports the `EVM` token which is set up on initialization of the EVM runtime contract. - -The flow of tokens from the EOS environment to the EVM environment is further enhanced to allow an arbitrary EOS smart contract to call into an EVM contract and get back the returned results (facilitated via the `sendevm` action). The EVM call comes from a sender address within a reserved address range which maps to the particular EOS account that initiated the call. EVM transactions with senders from this reserved address range do not require a valid signature but can only be submitted by the EVM runtime contract itself as an inline action. The EVM protocol rules are further adjusted such that addresses in the reserved range are considered to have always existed and have an unlimited supply of EVM tokens available to them. - -The flow of tokens from the EVM environment to the EOS environment is more limited in that it does not trigger any arbitrary EOS smart contract code to run as a result of the token transfer. An EVM contract can however direct the EVM tokens to a balance owned by a particular EOS account by sending to then EVM address within the reserved address range which maps to that EOS account. It is necessary for the EOS account to first `open` the balance before anyone can send funds to it; if an EVM contract sends EVM tokens to the special EVM address mapping to an EOS account that does not have a balance open within the EVM token contract, the EVM call will not just be reverted but the entire EOS transaction executing the EVM call will be aborted (this is to maintain compatibility with the Silkworm implementation). - -The token supply within the EVM environment starts at zero and new tokens are introduced by pulling them from the void of the special reserved addresses via EVM transactions constructed by the EVM runtime contract. The expected way to bootstrap the EVM token distribution is to first `issue` the new EVM token within the EOS environment to an EOS account managed by the Trust EVM team, then transfer those EVM tokens via the bridge to an EVM address also managed by the same Trust EVM team, and finally distribute the EVM tokens rom that EVM address to other EVM addresses as needed using regular EVM transactions. - -See the [EVM token flow diagram](./evm_token_flow.md) for a visual summary of many of the important flow paths that EVM tokens take through the EVM token bridge mechanism. That diagram also shows how a portion of collected fees are distributed to a special staking contract within the EVM environment that is managed by the Trust EVM team. \ No newline at end of file diff --git a/product/evm_token_bridge/evm_token_flow.md b/product/evm_token_bridge/evm_token_flow.md deleted file mode 100644 index 852b486c..00000000 --- a/product/evm_token_bridge/evm_token_flow.md +++ /dev/null @@ -1,29 +0,0 @@ -# EVM token flow - -![EVM flow diagram](./EVM_flow.svg) - -**Transaction A**: - -1. The `pushtx` action within EOS transaction A is processed by the EVM runtime contract. It carries out initial validation on the fields. -2. Sufficient EVM tokens are pulled from the EVM balance of account `0xacc2...` to cover the maximum gas costs (`L` times the gas price which is calculated to be the minimum of `M` and the sum of `P` plus the base gas price) and the value `V`. -3. The value `V` of the EVM transaction goes to the `to` address `0xacc3...`. -4. At the end of processing the EVM transaction, assuming the transaction does not revert e.g. due to running out of gas, the actual gas consumed is known including how that breaks down to a storage portion (gas attributable to additional storage costs) and a computation portion (all other gas consumed). The storage portion of the gas multiplied by the base gas price determines how much of the initial gas fee collected first goes to the RAM maintainer EVM balance. In addition, the EVM runtime contract calculates a gas fee refund due to reduction of storage costs or possibly overpayment to RAM maintainer EVM balance. This gas fee refund is pulled back from the RAM maintainer EVM balance and it could potentially be greater than the portion of the initial gas fee moved into the RAM maintainer EVM balance a moment prior. -5. The gas fee refund plus the product between the gas price and the difference between the gas limit `L` and the actual gas consumed determines the total fee that is returned back to the sender. Note that because the gas fee refund can be considerably greater than the storage portion of the gas multiple by the base gas price, it is possible that the total fee returned back to the sender could even be greater than the initial gas fee collected from the sender. Also note that if one were to subtract the initial gas fee collected from the sender (which is the total amount pulled by step 2 minus `V`) by the total fee returned back to the sender (which is the total amount pushed back by step 5), that difference (aka the net gas fee) must equal to sum of the net change to the RAM maintainer EVM balance and the computation portion of the gas fee (which is the sum of the product of `P` times the storage portion of the gas plus the product of the gas price times the computation portion of the gas). -6. A fraction (configurable in the EVM runtime contract) of the computation portion of the gas fee is sent to the `gas_fee_recipient` account as "write rewards" compensation for the CPU/NET costs incurred by the miner. -7. The remaining fraction of the computation portion of the gas fee is sent to the special fee collection EVM balance. This is revenue collected by Trust EVM which it uses to incentive RPC providers to provide their services. -8. From the perspective of the EVM environment, the sender's balance is reduced by the sum of `V` and the net gas fee (and since the net gas fee can be negative, this could mean that the sender's balance could in fact increase). For the sake of consistency, this means the net gas fee must be exactly reproducible by a node following the constructed virtual EVM blockchain using an EVM-based protocol with modifications to the gas calculation algorithm (specifically it needs to know how to compute the gas fee refund). From the perspective of the EVM environment, the net gas fee disappears into (or, if negative, appears from) the void. To ensure the accounting is balanced within the EOS environment, the net gas fee must be subtracted from the internal outstanding EVM balance (the arrow is bidirectional because the net gas fee could be negative). - -**Transaction B**: - -1. The `collectfee` action within EOS transaction B is processed by the EVM runtime contract. It can be called by anyone who is willing to pay for the gas costs of the internal EVM transaction it will create to move the EVM tokens collected in the special fee collection EVM balance within the EOS environment to the official Trust EVM staking contract within the EVM environment. -2. All of the EVM tokens collected in the fee collection EVM balance, let us call this amount `F`, is removed from that balance. -3. The collected fees amounting to `F` EVM tokens are then added to the balance of the account specified in the `gas_payer` field of the `collectfee`, in this case `charlie`. The fees will never in effect be transferred to `charlie` by the end of the transaction; they are simply temporarily held at this balance since `charlie` acts as the intermediary to move the collected fees to the official Trust EVM staking contract as the intended final recipient of the fees. -4. The EVM runtime contract then generates an inline `pushtx` action to itself. The `gas_fee_recipient` field is set to the `gas_fee_recipient` specified in the `collectfee` action, in this case also `charlie`. The `from` field is set to a special address in the reserved range which corresponds to the `charlie` account (the account specified as the `gas_payer` in the `collectfee` action). The `flags` field does not include the `allow_revert` flag which means that if the initial EVM call is reverted, the transaction will be aborted. This is important to ensure that no matter what happens with the EVM transaction, the EOS transaction will never successfully complete with `charlie` having possession over the collected fees. Finally, the `rlptx` encodes the EVM transaction that will be processed. The `value` field of this transaction is `F`. The `to` field is set to the address for the official Trust EVM staking contract within the EVM environment which is known to the EVM runtime contract. The signature fields `v`, `r`, `s` do not encode a valid signature for the EVM transaction. Instead, they encode the `from` address. The remaining fields are set appropriately based either on values specified in the `collectfee` action (e.g. `max_fee_per_gas` or `gas_limit`) or automatically (e.g. `nonce` or `data`). -5. The inline `pushtx` action generated by the `collectfee` action is processed by the EVM runtime contract. Because the `from` address is within the reserved range, it will ensure that this is an inline action sent by the EVM runtime contract itself, or else it will abort the transaction. In addition, because the `from` address is in the reserved range, it will not validate the signature as it normally would, but instead will ensure that it has the proper encoding of the `from` address. -6. Sufficient EVM tokens are pulled from the EVM balance of the `charlie` account (note that this is now a balance within the EOS environment) to cover the maximum gas costs (`gas_limit` times the gas price which is calculated to be the minimum of `max_fee_per_gas` and the sum of `max_priority_fee_per_gas` plus the base gas price) and the value `F`. The EVM runtime contract knows which account within the EOS environment to pull funds from when processing this EVM transaction because it knows the account name mapped to by the `from` address within the reserved range. -7. The value `F` of the EVM transaction goes to the `to` address `0xacc1...` which is the address of the staking contract. This makes the collected fees available to the staking contract to be later distributed to the various stakers proportionally. -8. Like in the case of transaction A, a net change occurs in the RAM maintainer EVM balance by adding the storage portion of the gas multiplied by the base gas price and the subtracting the gas fee refund. The arrow is bidirectional because that net change can be negative. -9. Like in the case of transaction A, the gas fee refund plus the product between the gas price and the difference between `gas_limit` and the actual gas consumed determines the total fee that is returned back to the sender (which in this case means the funds go back to the EVM balance for `charlie` within the EOS environment). -10. Like in the case of transaction A, a fraction of the computation portion of the gas fee is sent to the `gas_fee_recipient` account, which in the case is again `charlie`, as "write rewards" compensation for the CPU/NET costs incurred by the miner. -11. The remaining fraction of the computation portion of the gas fee is again sent to the special fee collection EVM balance. This means that even after processing the `collectfee` action, the fee collection EVM balance will always end up with some small positive balance remaining. -12. Similar to the case of transaction A, to ensure the accounting is balanced within the EOS environment, the internal outstanding EVM balance must be adjusted. In this case, the net gas fee both appears from and disappears into the void. However, now the actual value `F` is appearing from the void. So to maintain accounting balance, `F` must also be added into the internal outstanding EVM balance. \ No newline at end of file diff --git a/product/evm_token_bridge_dust.md b/product/evm_token_bridge_dust.md deleted file mode 100644 index e23d5723..00000000 --- a/product/evm_token_bridge_dust.md +++ /dev/null @@ -1,59 +0,0 @@ -## Bridge Dust Buckets - -### Background - -Ethereum chains store their native token value in a 256-bit number which unit is 'wei'. 1 wei is 10-18 ETH, that is, representation of the token value stores 18 digits of decimal precision. - -Antelope gives considerable flexibility to how a contract can handle its data, but the standard expected practice for a token is to represent its value via an `asset` type which allows for a signed 63-bit value. The `asset` also includes a definition of a `symbol`, and the `symbol` defines the decimal precision as an 8-bit value. For example, if the `symbol` states a decimal precision of 6 digits, then the max value representable in the `asset` is 262/106, i.e. 4611686018427.387904 - -This difference in number range means that while an Antelope token's `asset` value could represent the full decimal precision of an Ethereum native token value, the range of values will be substantially less when configured that way. Consider that 262/1018 ≈ 4.6! If the Antelope token were to represent the entire decimal precision of an Ethereum native token it would be impossible to represent 5.000000000000000000 EVM as an Antelope token. Thus, the Antelope token must use considerably less decimal precision so that it can represent the maximum possible value for all expected minted EVM native tokens. - -### Dust Buckets - -The exact value of the Antelope token's precision with be left to the `init` action to define, and the token contract math is expected to handle any value between 0 and 18. 6 digits will be used in the examples within this document. - -The reduction in precision when bridging the token from EVM to Antelope would mean that if a user bridges 5.123456789012345678 EVM from EVM to Antelope, Antelope can only represent 5.123456 EVM as an `asset` which users can then `transfer` etc. What to do with the left over "dust" 0.000000789012345678 EVM? - -We've elected to have the user's Antelope account retain ownership of this dust (it isn't burned or transferred to another pool). This means that the Antelope contract must maintain an additional dust balance in addition to the standard Antelope `asset` balance a token contract would have. - -To minimize impact on RAM usage, this dust balance will be appended to the typical token balance row. That is, the table row definition becomes something like: -```c++ - struct [[eosio::table]] account { - asset balance; - uint64_t dust; - - uint64_t primary_key()const { return balance.symbol.code().raw(); } - }; -``` -A 64-bit value is enough to hold all ranges of possible dust: if the contract is `init`ed with an Antelope token precision of 0, `dust` could have a value up to 1018-1 which can still fit in 264. - -Upon a transfer from EVM to Antelope, the token value modulo configured precision will be accumulated in `dust`. If `dust` ever exceeds the minimal value represented via the Antelope token's precision, that excess value is transferred from `dust` to the Antelope token's standard balance. - -This dust balance is inaccessible to the user except for one action: transferring the token from Antelope to EVM (via an EVM transaction). This action has the possibility to bridge the dust back to EVM if the transfer specifies a value utilizing precision more than the Antelope token precision. i.e. this is the only time the user's `balance`+`dust` is truly considered one balance. - -### Examples - -| action | `balance` | `dust` | notes | -|-----------------------------------------------------|--------------|--------------|-----------------------------------------------------------------| -| user `open`s balance | 0.000000 EVM | 0 | -| EVM->Antelope transfer
5.123456789012345678 EVM | 5.123456 EVM | 789012345678 | -| antelope token `transfer`
4.000000 EVM | 1.123456 EVM | 789012345678 | -| EVM->Antelope transfer
0.000000789012345678 EVM | 1.123457 EVM | 578024691356 | `dust` "rolled over" | -| Antelope->EVM transfer
1.000000400000000000 EVM | 0.123457 EVM | 178024691356 | value less than Antelope `balance` precision pulled from `dust` | -| antelope token `transfer`
0.123457 EVM | 0.000000 EVM | 178024691356 | user unable to `close` | -| Antelope->EVM transfer
0.000000178024691356 EVM | 0.000000 EVM | 0 | user can `close` | - - -### Compatibility Impacts - -The `eosio.token` interface is the de facto token interface on Antelope chains. Adding more data in to the `account` table rows arguably changes the interface to no longer be fully compliant even though no compliance specification has ever been published unlike ERC20, for example. (there are other deviations too: we do not have a `create` action). - -The expectation is that no other contracts, block explorers, or wallets will care about the extra row data: they'll simply deserialize the `balance` and stop; ignoring the remaining bytes in the row. Being disturbed by this extra data would seemingly only come about if some consumer was being deliberately obtuse. - -Nevertheless, it is worth empirically testing to ensure the token balance is properly displayed for users in typical use cases. A contract was deployed on Jungle4 lacking both a `create` action and including the extra `account` table row data. Eosq correctly displayed that a token holder had the expected `balance`, ignoring the `dust`. It may be worth trying on another explorers and/or wallets. - -### Responsibility Of Deployer & `init` Caller - -The caller of the contract's `init` action will need to set the Antelope token's precision with consideration of the EVM native token's tokenomics (supply, inflation, etc). For example, while a setting of 0 will "only" allow the Antelope token to represent 4611686018427387904 EVM which is less than what the EVM native token could represent, realistically that may well exceed the maximum supply & inflation of the token in practice. - -The bottom line is that the onus is on the caller of `init` to set this number appropriately. The contract will have to be prepared to assert on transactions that overflow the asset should the combination of `init`ed precision and native EVM supply be 'misconfigured'. \ No newline at end of file diff --git a/product/external_fork_handling.md b/product/external_fork_handling.md deleted file mode 100644 index 3949c347..00000000 --- a/product/external_fork_handling.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -tags: Proposal ---- - -# External Fork Handling - -## Prerequisites -#### Dependencies: -Block mapping - -## Problem - -#### Opportunity: -Support forks occuring and not relying on Silkworm's "native" forking logic. -#### Target audience: -- TrustEVM node admins -- RPC providers -#### Strategic alignment: -Ensure quick and succinct forking resolution immediately when determined from Antelope blocks. - -## Solution - -#### Solution name: -External Fork Handling -#### Purpose: -When the Antelope chain that is running TrustEVM forks that forking will impact the "fake" EVM chain and we need to handle it. -#### Success definition: -We can successfully rollback the TrustEVM node chain immediately. The forking depth should not cause failure but can impact overall performance. -#### Risks: -The TrustEVM node software already has code inplace to handle this forking. The ultimate issue is that libmdbx has some deep internal failures that cause it fail at seemingly random times. So, the risk is that the solution to fix libmdbx will be too intensive, we won't be able to fix without redesign, we won't be able to determine the root cause of failure. -#### Functionality -On each new block we create a new `RWTxn` object from libmdbx that is using the previous `RWTxn` object as its parent state. - -A map (`txns`) between block height and `RWTxn` object will be maintained. - -When the system receives a LIB advancement signal then it will commit any "open" transactions in `txns` and remove them where the block height ≤ the current LIB block height. - -When we recieve a fork signal we will remove the "open" transactions in `txns` where the block height ≥ the forking block height. - -As mentioned before this current logic has already been implemented, but we will need to fix libmdbx. Or, we will need to create a different solution that doesn't use libmdbx transactional system. - -So, we could create a datatype called 'transaction' where we have a map of bytes to bytes. A method called `commit` would simply do nothing and erase the internal map. A method called `rollback` will reapply the the entries back to the system. - -We then modify silkworm so that when we insert or update a KV entry we copy the old entry to the map. And then allow it to work as usual. We only update the map once per "transaction". - -We can then use the same logic from using the `RWTxn` approach. - -#### Features -N/A -#### User stories -N/A - -## Open questions -Do we have a research period of a week to determine if we can resolve the issues with libmdbx? Or go straight into the other approach. diff --git a/product/gas_fee_algorithm.md b/product/gas_fee_algorithm.md deleted file mode 100644 index 2c8640eb..00000000 --- a/product/gas_fee_algorithm.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -tags: Proposal ---- - -# Gas fee algorithm - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? - -A functional EVM runtime contract using the standard EVM algorithm for calculating gas consumption and gas rebate for an EVM transaction is required as a base to make modifications to. This existing contract is not required to have already implemented any particular algorithm for how the base gas fee is dynamically changed, since that algorithm is to be significantly changed compared to what Ethereum uses as their algorithm. - -## Problem - -#### Opportunity: What are the needs of our target user groups? - -Our target users expect to pay a single fee in the native token of Trust EVM, called the EVM token, per transaction to cover all the resource costs of processing that transaction. Due to the nature of the EOS environment on top of which Trust EVM runs, the EVM runtime contract must use the collected fee from the user to provide incentive to all parties covering the EOS costs of processing this transaction which includes RAM and CPU/NET. And it must continue to provide sufficient incentive even as the price of RAM and CPU/NET independently fluctuate on the EOS network all while continuing to present the costs to the users as a simple single gas fee (though varying over time) on each transaction. - -#### Target audience: Who is the target audience and why? - -The target audience are users and developers who are familiar with interacting with applications and tools in the wider EVM ecosystem, particularly the Ethereum ecosystem. We want Trust EVM to maintain as much compatibility as is feasible with the Ethereum EVM to allow the plethora of EVM-based tools in the ecosystem to be immediately leveraged with Trust EVM. We also want to meet the expectations of this target audience when it comes to how they manage and pay for resources, particularly that they expect to just pay a short-term predictable gas fee per transaction, to reduce cognitive friction with their user experience in interacting with applications built on the Trust EVM platform. -#### Strategic alignment: How does this problem align with our core strategic pillars? - -A core pillar of Trust EVM is to attract developers and users in the wider EVM ecosystem. Maintaining compatibility with their existing tools and expectations helps make Trust EVM attractive. However, compatibility must be balanced with a resource model that can sustainably continue to enable operation. This means the resource and pricing model must ensure the fees collected from users are priced accordingly to cover the costs of "miners" who submit the EVM transactions for processing on the EOS blockchain and to cover the costs of allocating enough RAM for the EVM runtime contract so that it does not run out of storage space needed to continue processing EVM transactions. If the availability of the EVM runtime contract is compromised because of bad pricing, the Trust EVM platform would obviously look significantly less attractive to our target audience. - -## Solution - -#### Solution name: How should we refer to this product opportunity? - -The Trust EVM gas fee algorithm. - -#### Purpose: Define the product’s purpose briefly - -Dynamically adjust the base gas price and the amount of gas to refund for freeing up storage space to ensure a sufficient fee is charged per transaction to cover the varying costs of RAM and CPU/NET on the EOS network. Additionally, direct a portion of the collected fees to a balance managed by the EVM runtime contract to pull from as needed to cover the costs of maintaining sufficient RAM for the EVM runtime contract as well as to potentially provide a discount on the gas fee paid by the user (perhaps to the point of having a negative gas fee in some cases) as a strong incentive to free up unneeded storage space used by the EVM runtime contract. The remaining portion of the collected fees should also be sufficient, after the EVM runtime contract takes a cut, to cover the CPU/NET costs for the "miner" that submits the EVM transaction to the EVM runtime contract via an EOS transaction that they pay the CPU/NET costs for by the fact that they are the first authorizer of the EOS transaction. - -#### Success definition: What are the top metrics for the product (up to 5) to define success? - -1. RAM allocation for the EOS account that the EVM runtime contract is deployed remains sufficient to prevent it from failing to process EVM transactions successfully. -2. Users and developers of contracts running on Trust EVM always have an incentive to free up storage slots that are no longer needed by a contract. -3. It remains profitable to miners to submit EVM transactions to the EOS blockchain for processing, assuming users are willing to pay the gas fee for their EVM transaction dictated by the gas consumption and base gas price calculated by the algorithm. -4. The gas fee that users must pay for their EVM transaction, as calculated dynamically by the algorithm, does not become significantly higher than what is needed to cover the storage and computation costs of processing the transaction on EOS plus some arbitrary profit margin chosen by the Trust EVM governance system. - -#### Assumptions - -The algorithm to calculate the gas consumed by a transaction must be the same as the one used in Ethereum. The algorithm to calculate the gas refund (due to freeing up storage) can be different. The algorithm to adjust the base gas price can also be different than how it is done for Ethereum. - -Whatever algorithm is used for adjusting the base gas price or calculate gas consumed / refunded must be identical whether computed by the EVM runtime contract or by the downstream silkworm engine. - -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ - -Significant changes to the gas price algorithm could a risk that existing EVM tools and client application software choose a max gas price for the transaction which becomes insufficient by the time the transaction gets processed on the EOS blockchain. - -If the new algorithms are not designed well, there is a risk that insufficient EVM tokens will be raised from consuming more storage slots to cover the actual RAM costs imposed by the EOS blockchain on the EVM runtime contract. In that case, a benefactor for Trust EVM, e.g. the foundation, may need to put in addition money into the project to ensure the EVM runtime contract has enough RAM to continue processing transactions. - -#### Functionality - -As gas consumed is calculated according to the standard Ethereum gas algorithm, the EVM runtime contract separately keeps track of gas consumption attributable to purely computational (or network bandwidth) costs versus gas consumption attributable to storage costs. It also separately keeps track of the gas refund due to freeing up existing computation (i.e. the storage gas refund), but this would be following a new algorithm and not the standard Ethereum gas algorithm. - -Using the base gas price calculated for the start of the virtual EVM block, the EVM runtime contract calculates: -* the gas fee for computation which depends on the gas consumption attributable to computational costs; -* the gas fee for storage which depends on the gas consumption attributable to storage costs less the storage gas refund; -* the net gas fee pulled from the sender's balance to exactly cover the two gas fees above. - -A cut of the gas fee for computation is taken by the EVM runtime contract itself and the rest goes to the balance of the account specified as the `gas_recipient` in the `pushtx` action. - -The gas fee for storage is directed to a special balance in the EVM runtime contract that is meant to cover the costs of purchasing more RAM for the EVM runtime contract as well as act as the source of value to cover the storage gas refund costs which act as an incentive for users and smart contract developers to free up unused storage space in EVM contracts. - -To ensure the gas fee for storage covers the expense of maintaining enough RAM for the EVM runtime contract, the base gas price needs to take into account the actual cost of RAM (priced in terms of EVM tokens) on the EOS blockchain. As that cost increases, the base gas price may need to increase as well. - -The other major input into the new base gas price algorithm is the cost of CPU/NET resources on the EOS blockchain. If that gets too expensive, it may become the driving factor raising the base gas price for the EVM. This is to ensure enough gas fee is collected from each EVM transaction to incentivize a miner to process the transaction. - -To ensure that the silkworm engine is able to exactly reproduce the algorithm but to also avoid creating complicated dependencies to inputs like the cost of RAM or CPU/NET, the EVM runtime contract will provide the base gas price that it used to process each transaction, perhaps as an action return value. The EVM runtime contract must restrict itself to only change the base gas price on virtual block boundaries to maintain compatibility with silkworm engine expectations; the new gas price is fed into silkworm engine through the block headers of the fake EVM chain that it processes. - -#### Features - -#### User stories -#### Additional tasks - - -## Open questions - -Are we allowed to make the algorithm that determines how much of a gas refund is provided be parameterized dynamically to allow for an extra degree of freedom in aligning storage freeing incentives properly? - -Is there any bound that must be enforced on how quickly the base gas price can rise? \ No newline at end of file diff --git a/product/product-brief-template.md b/product/product-brief-template.md deleted file mode 100644 index 0ffa29b7..00000000 --- a/product/product-brief-template.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -tags: Proposal ---- - -# Product brief template - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -Answer - -## Problem - -#### Opportunity: What are the needs of our target user groups? -Answer -#### Target audience: Who is the target audience and why? -Answer -#### Strategic alignment: How does this problem align with our core strategic pillars? -Answer - -## Solution - -#### Solution name: How should we refer to this product opportunity? -Answer -#### Purpose: Define the product’s purpose briefly -Answer -#### Success definition: What are the top metrics for the product (up to 5) to define success? -Answer -#### Assumptions -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -Answer -#### Functionality -Answer -#### Features -Answer -#### User stories -Answer -#### Additional tasks -- [ ] #issue number - -## Open questions diff --git a/product/proxy_for_rpc.md b/product/proxy_for_rpc.md deleted file mode 100644 index 75ab81ef..00000000 --- a/product/proxy_for_rpc.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -tags: Proposal ---- - -# Proxy for Read Incentive System - -## Prerequisites -#### Dependencies: -The API of the Staking Contract - -## Problem - -#### Opportunity: -We want to fairly assign works to RPC providers and get them rewarded fairly. -The central proxy approach is a worth trying one. - - -## Solution - -#### Solution name: Proxy for RPC (Maybe balance loader?) -#### Purpose: -Develop a proxy that can forward incoming traffic according to weights based on the staking of the RPC providers. -Record detailed access log so that other module can use to calculate rpc rewards. -#### Success definition: -The proxy should running with all the required functionalities and can achieve required performane (TBD) -#### Assumptions -#### Risks: -This task will need multiple 3rd party tools. This may impose some risk in safety/license. -#### Functionality -- The proxy should direct traffic to upstream in a weighted manner. -- The upstream and weight should be updated periodically on the fly according to some on-chain data. -- Log enough access log so other modules can calculate rpc rewards from. -#### Features -See user stories -#### User stories -- A proxy that based on nginx (deliverable) - - Access log is formatted to meet the need of other modules - - Upsync module for nginx is used to update upstream -- Storage service (e.g. Redis or Consul) to hold upstream info for upsync module to read (deliverable) -- A small helper service reading staking info from chain, translate it into upstream info and save it to storage service (deliverable) - - -#### Additional tasks - - -## Open questions -Need to decide whether we should use docker or not. -The central proxy solution may have sub-optimized performance if upstream servers are located in a far way location. This is something beyond fix with this task alone. diff --git a/product/read_incentive_system_design.md b/product/read_incentive_system_design.md deleted file mode 100644 index 161a7554..00000000 --- a/product/read_incentive_system_design.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -tags: Proposal ---- - -# Read Incentive System - -## Prerequisites -#### Dependencies: - -## Problem -Write transactions in our system are processed by the EVM runtime. Thus it is simple to track and reward RPC node who served write transactions. -However, read access is hard to track in the same way. In addition, we are trying to design a PoS system out of it. -Therefore, we need a special design to properly arrange duty, track operation and assign rewards. -## Solution - -#### Solution name: Read Incentive System Design - -#### Purpose: -Design a system that can solve the problem defined above. - -#### Success definition: -#### Assumptions -#### Risks: -#### Functionality -See https://github.com/eosnetworkfoundation/TrustEVM/blob/yarkinwho-product-desc-update/product/design_doc/read_incentive_system.md for the design -#### Features -#### User stories -See -#45 -#46 -#47 -#60 - -#### Additional tasks - - -## Open questions diff --git a/product/staking_contract.md b/product/staking_contract.md deleted file mode 100644 index b3109da5..00000000 --- a/product/staking_contract.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -tags: Proposal ---- - -# Staking Contract - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? - -## Problem - -The RPC operators supposed to stake and claim rewards on EVM universe. So we need a Staking Contract for it. -Meanwhile, we want to push info such as upstream addresses in the same place so it would easier to make things consistent. - -## Solution - -#### Solution name: Staking Contract -#### Purpose: -Staking contract for operators to stake and claim rewards. -The contract should also allow interaction with proxy/workload verifier -#### Success definition: -Functioning is enough, should be no major performance requirement. -#### Assumptions -#### Risks: What risks should be considered? -We may want more complicated mechanism for the staking, which will cause this staking contract to face constant overhaul. -#### Functionality -- The staking contract should allow permitted RPC operators to stake EVM to take part in the RPC reward mechanism. -- The staking contract should serve as a location to save PRC operator upstream info and rewards info. -- The staking contract should expose data for users to view. -- The staking contract should be able to accept and hold tokens as source of rewards. -#### Features -See user stories -#### User stories -- A detailed API definition first (deliverable) -- A Staking Contract in solidity (deliverable) - - Operator API - - Methods for Operator to stake EVM - - Methods for Operator to set upstream address (optionally separate read/write) when staking - - Methods for Operator to claim rewards, rewards is coming from the EVM the contract holds - - Methods for Operator to view related data - - Proxy API - - Methods for proxy to read upstream address and weight(based on staking amount) - - Workload Verifier API (Not needed for the first version) - - Methods for workload verifier to update the payment - - Admin API - - Methods for admin to set allow list for valid operator/workload verifier - - Methods for admin to view related data in an aggragate way - - Extra functionality - - Make the contract be capable of accepting EVM tokens as the source of payment. -#### Additional tasks -- [ ] #issue number - -## Open questions -- Need decision on the detailed mechanism for payment: - - What is the frequency should the contract receive tokens - - As frequent as possible/reasonable - - Whether this token is for next payment cycle or previous one. - - Next - - How should payment be calculated.(The general idea would be by share of payment, but how in detail, whether it should be similar to a Farm or something else) - - like sushiswap farm - - What if some operator join/quit in the middle of a payment cycle - - like sushiswap farm diff --git a/product/staking_ui.md b/product/staking_ui.md deleted file mode 100644 index c56393d1..00000000 --- a/product/staking_ui.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -tags: Proposal ---- - -# Staking UI - -## Prerequisites -#### Dependencies: -Staking Contract API - -## Problem -The RPC operators need an UI for their convenience. - - -## Solution - -#### Solution name: Staking UI -#### Purpose: -Provide an UI for the Staking Contract -#### Success definition: -Operators and admins can use the UI to do things they should be able to do according to the Staking Contract design. -No performance requirement. -#### Assumptions -#### Risks: -The UI maybe eventually integreted into other projects such as the TrustEVM portal, this may cause this task to be cancelled. -#### Functionality -UI should support all methods operator and admin can call. -UI should provide a page for operator and admin to view important data. - -#### Features -see user stories. - -#### User stories -- UI supports connecting to wallets. At least MetaMask. -- Operator Main Page (deliverable) - - UI supports operator to stake and set upstream info - - UI supports operator to claim rewards - - UI supports operator to view his current staking/upstream info/pending rewards -- Admin Main Page (deliverable) - - UI supports admin to view all operators' info in an aggregate view - - UI supports admin to set allowlist for users (operator or workload verifier) - - UI supports admin to set other parameters if there's any defined in Staking Contract API - -#### Additional tasks -- [ ] #issue number - - -## Open questions - -We may also want to incluse some off-chain tasks here: -- Apply to be operator -- Arrange for quitting -- Provide supporting materials for Application - diff --git a/product/testnet_launch.md b/product/testnet_launch.md deleted file mode 100644 index 05269181..00000000 --- a/product/testnet_launch.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -tags: Proposal ---- - -# Testnet Launch - -## Prerequisites -#### Dependencies: What work must be completed before this part of the solution is actionable? -All the design and implementation - -## Problem -We need a plan and checklist for testnet launch so that we can do that in a smooth way. - -## Solution - -- We should carry out a checklist when the implementation is close to end -- Review the checklist -- Follow the checklist to perform the launch -- (Optionally) try the checklist with some internal launch. - -#### Assumptions -#### Risks: What risks should be considered? https://www.svpg.com/four-big-risks/ -Answer -#### Functionality -Answer -#### Features -Answer -#### User stories -- We need a checklist -- Then follow the checklist to do the launch -#### Additional tasks -- [ ] #issue number - -## Open questions diff --git a/product/workload_verifier.md b/product/workload_verifier.md deleted file mode 100644 index 5b46851c..00000000 --- a/product/workload_verifier.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -tags: Proposal ---- - -# Workload Verifier - -## Prerequisites -#### Dependencies: -Proxy for RPC log format -Staking contract API - -## Problem - -We need to calculate rewards for RPC nodes according to access log - -## Solution - -#### Solution name: workload_verifier -#### Purpose: -Calculate rewards from access log periodically and save the result to the chain. - -#### Success definidtion: -Working and can achieve the required call frequency. -#### Assumptions -#### Risks: -If the token economy model changes, it might happen that we may need complicated logic in reward calculation, or this module may also need to actively probe if a rpc node is working honestly. Such requirement may cause the module to be rewritten. -#### Functionality -- process accesslog and calculate rewards for each rpc node -- periodically update the reward to the Staking Contract -#### Features -See user stories -#### User stories -- A service that tracking reading rewards (deliverable) - - The service can read access log - - The service can calcualte rewards according to what it read - - The service can save the result to the Staking Contract - - Can config the update period via config file -#### Additional tasks -- [ ] #issue number - -## Open questions diff --git a/silkrpc/commands/eth_api.cpp b/silkrpc/commands/eth_api.cpp index d78ce8d9..fbf1995f 100644 --- a/silkrpc/commands/eth_api.cpp +++ b/silkrpc/commands/eth_api.cpp @@ -972,7 +972,7 @@ boost::asio::awaitable EthereumRpcApi::handle_eth_get_transaction_count(co if (account) { reply = make_json_content(request["id"], to_quantity(account->nonce)); } else { - reply = make_json_content(request["id"], "0x"); + reply = make_json_content(request["id"], "0x0"); } } catch (const std::exception& e) { SILKRPC_ERROR << "exception: " << e.what() << " processing request: " << request.dump() << "\n"; @@ -1051,6 +1051,8 @@ boost::asio::awaitable EthereumRpcApi::handle_eth_call(const nlohmann::jso EVMExecutor executor{*context_.io_context(), tx_database, *chain_config_ptr, workers_, block_number}; const auto block_with_hash = co_await core::read_block_by_number(*block_cache_, tx_database, block_number); silkworm::Transaction txn{call.to_transaction()}; + if(!txn.from.has_value()) txn.from = evmc::address{0}; + const auto execution_result = co_await executor.call(block_with_hash.block, txn); if (execution_result.pre_check_error) { @@ -1411,7 +1413,7 @@ boost::asio::awaitable EthereumRpcApi::handle_eth_get_logs(const nlohmann: SILKRPC_DEBUG << "block_numbers.cardinality(): " << block_numbers.cardinality() << "\n"; - if (filter.topics.has_value()) { + if (filter.topics.has_value() && !filter.topics.value().empty()) { auto topics_bitmap = co_await get_topics_bitmap(tx_database, filter.topics.value(), start, end); SILKRPC_TRACE << "topics_bitmap: " << topics_bitmap.toString() << "\n"; if (topics_bitmap.isEmpty()) { diff --git a/silkrpc/commands/rpc_api_table.cpp b/silkrpc/commands/rpc_api_table.cpp index 93dfba85..7ce1fe54 100644 --- a/silkrpc/commands/rpc_api_table.cpp +++ b/silkrpc/commands/rpc_api_table.cpp @@ -87,7 +87,7 @@ void RpcApiTable::add_eth_handlers() { handlers_[http::method::k_eth_chainId] = &commands::RpcApi::handle_eth_chain_id; handlers_[http::method::k_eth_protocolVersion] = &commands::RpcApi::handle_eth_protocol_version; handlers_[http::method::k_eth_syncing] = &commands::RpcApi::handle_eth_syncing; - handlers_[http::method::k_eth_gasPrice] = &commands::RpcApi::handle_eth_gas_price; + //handlers_[http::method::k_eth_gasPrice] = &commands::RpcApi::handle_eth_gas_price; handlers_[http::method::k_eth_getBlockByHash] = &commands::RpcApi::handle_eth_get_block_by_hash; handlers_[http::method::k_eth_getBlockByNumber] = &commands::RpcApi::handle_eth_get_block_by_number; handlers_[http::method::k_eth_getBlockTransactionCountByHash] = &commands::RpcApi::handle_eth_get_block_transaction_count_by_hash; @@ -117,8 +117,8 @@ void RpcApiTable::add_eth_handlers() { handlers_[http::method::k_eth_getFilterChanges] = &commands::RpcApi::handle_eth_get_filter_changes; handlers_[http::method::k_eth_uninstallFilter] = &commands::RpcApi::handle_eth_uninstall_filter; handlers_[http::method::k_eth_getLogs] = &commands::RpcApi::handle_eth_get_logs; - handlers_[http::method::k_eth_sendRawTransaction] = &commands::RpcApi::handle_eth_send_raw_transaction; - handlers_[http::method::k_eth_sendTransaction] = &commands::RpcApi::handle_eth_send_transaction; + //handlers_[http::method::k_eth_sendRawTransaction] = &commands::RpcApi::handle_eth_send_raw_transaction; + //handlers_[http::method::k_eth_sendTransaction] = &commands::RpcApi::handle_eth_send_transaction; handlers_[http::method::k_eth_signTransaction] = &commands::RpcApi::handle_eth_sign_transaction; handlers_[http::method::k_eth_getProof] = &commands::RpcApi::handle_eth_get_proof; handlers_[http::method::k_eth_mining] = &commands::RpcApi::handle_eth_mining; diff --git a/silkrpc/core/estimate_gas_oracle.cpp b/silkrpc/core/estimate_gas_oracle.cpp index 675fc259..cab21da2 100644 --- a/silkrpc/core/estimate_gas_oracle.cpp +++ b/silkrpc/core/estimate_gas_oracle.cpp @@ -80,11 +80,13 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call SILKRPC_DEBUG << "hi: " << hi << ", lo: " << lo << ", cap: " << cap << "\n"; silkworm::Transaction transaction{call.to_transaction()}; + if(!transaction.from.has_value()) transaction.from = evmc::address{0}; + while (lo + 1 < hi) { auto mid = (hi + lo) / 2; transaction.gas_limit = mid; - auto failed = co_await try_execution(transaction); + auto [failed, _]= co_await try_execution(transaction); if (failed) { lo = mid; @@ -95,10 +97,19 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call if (hi == cap) { transaction.gas_limit = hi; - auto failed = co_await try_execution(transaction); + auto [failed, res] = co_await try_execution(transaction); SILKRPC_DEBUG << "HI == cap tested again with " << (failed ? "failure" : "succeed") << "\n"; if (failed) { + if(res && res->error_code != evmc_status_code::EVMC_OUT_OF_GAS) { + const auto error_message = EVMExecutor<>::get_error_message(res->error_code, res->data); + SILKRPC_DEBUG << "result message " << error_message << ", code " << res->error_code << "\n"; + if (res->data.empty()) { + throw EstimateGasException{-32000, error_message}; + } else { + throw EstimateGasException{3, error_message, res->data}; + } + } throw EstimateGasException{-1, "gas required exceeds allowance (" + std::to_string(cap) + ")"}; } } @@ -107,28 +118,19 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call co_return hi; } -boost::asio::awaitable EstimateGasOracle::try_execution(const silkworm::Transaction& transaction) { +boost::asio::awaitable>> EstimateGasOracle::try_execution(const silkworm::Transaction& transaction) { const auto result = co_await executor_(transaction); - bool failed = true; if (result.pre_check_error) { SILKRPC_DEBUG << "result error " << result.pre_check_error.value() << "\n"; - } else if (result.error_code == evmc_status_code::EVMC_SUCCESS) { - SILKRPC_DEBUG << "result SUCCESS\n"; - failed = false; - } else if (result.error_code == evmc_status_code::EVMC_INSUFFICIENT_BALANCE) { - SILKRPC_DEBUG << "result INSUFFICIENTE BALANCE\n"; - } else { - const auto error_message = EVMExecutor<>::get_error_message(result.error_code, result.data); - SILKRPC_DEBUG << "result message " << error_message << ", code " << result.error_code << "\n"; - // if (result.data.empty()) { - // throw EstimateGasException{-32000, error_message}; - // } else { - // throw EstimateGasException{3, error_message, result.data}; - // } + if(!result.pre_check_error.value().starts_with("intrinsic gas too low")) + throw EstimateGasException{-32000, result.pre_check_error.value()}; + + co_return std::make_tuple>(true, {}); } - co_return failed; + bool failed = result.error_code != evmc_status_code::EVMC_SUCCESS; + co_return std::make_tuple>(std::move(failed), std::move(result)); } } // namespace silkrpc::ego diff --git a/silkrpc/core/estimate_gas_oracle.hpp b/silkrpc/core/estimate_gas_oracle.hpp index 9f5d9c6f..7ef91bdd 100644 --- a/silkrpc/core/estimate_gas_oracle.hpp +++ b/silkrpc/core/estimate_gas_oracle.hpp @@ -88,7 +88,7 @@ class EstimateGasOracle { boost::asio::awaitable estimate_gas(const Call& call, uint64_t block_number); private: - boost::asio::awaitable try_execution(const silkworm::Transaction& transaction); + boost::asio::awaitable>> try_execution(const silkworm::Transaction& transaction); const BlockHeaderProvider& block_header_provider_; const AccountReader& account_reader_; diff --git a/silkrpc/core/evm_executor.cpp b/silkrpc/core/evm_executor.cpp index 537029c9..5e503dd0 100644 --- a/silkrpc/core/evm_executor.cpp +++ b/silkrpc/core/evm_executor.cpp @@ -234,6 +234,12 @@ boost::asio::awaitable EVMExecutor::call( assert(txn.from.has_value()); state_.access_account(*txn.from); + if(silkworm::is_reserved_address(*txn.from)) { + //must mirror contract's initial state of reserved address + state_.set_balance(*txn.from, txn.value + intx::uint256(txn.gas_limit) * txn.max_fee_per_gas); + state_.set_nonce(*txn.from, txn.nonce); + } + const evmc_revision rev{evm.revision()}; const intx::uint256 base_fee_per_gas{evm.block().header.base_fee_per_gas.value_or(0)}; const intx::uint128 g0{silkworm::intrinsic_gas(txn, rev >= EVMC_HOMESTEAD, rev >= EVMC_ISTANBUL)}; diff --git a/silkrpc/json/types.cpp b/silkrpc/json/types.cpp index c6cf510d..e41c573a 100644 --- a/silkrpc/json/types.cpp +++ b/silkrpc/json/types.cpp @@ -479,15 +479,17 @@ void from_json(const nlohmann::json& json, Filter& filter) { } if (json.count("topics") != 0) { auto topics = json.at("topics"); - for (auto& topic_item : topics) { - if (topic_item.is_null()) { - topic_item = FilterSubTopics{evmc::bytes32{}}; - } - if (topic_item.is_string()) { - topic_item = FilterSubTopics{evmc::bytes32{topic_item}}; + if (topics != nlohmann::detail::value_t::null) { + for (auto& topic_item : topics) { + if (topic_item.is_null()) { + topic_item = FilterSubTopics{}; + } + if (topic_item.is_string()) { + topic_item = FilterSubTopics{topic_item}; + } } + filter.topics = topics.get(); } - filter.topics = topics.get(); } if (json.count("blockHash") != 0) { filter.block_hash = json.at("blockHash").get(); diff --git a/silkrpc/json/types_test.cpp b/silkrpc/json/types_test.cpp index afde1758..55b62904 100644 --- a/silkrpc/json/types_test.cpp +++ b/silkrpc/json/types_test.cpp @@ -1246,7 +1246,7 @@ TEST_CASE("deserialize filter with topic", "[silkrpc::json][from_json]") { CHECK(f.to_block == 4007424u); CHECK(f.addresses == std::vector{0x6090a6e47849629b7245dfa1ca21d94cd15878ef_address}); CHECK(f.topics == std::vector>{ - {0x0000000000000000000000000000000000000000000000000000000000000000_bytes32}, + {}, {0x374f3a049e006f36f6cf91b02a3b0ee16c858af2f75858733eb0e927b5b7126c_bytes32} }); CHECK(f.block_hash == std::nullopt); diff --git a/silkworm b/silkworm index 0ed53623..f84defea 160000 --- a/silkworm +++ b/silkworm @@ -1 +1 @@ -Subproject commit 0ed5362304a1cca16bf0a0cbd6cc7693bcfa1668 +Subproject commit f84defea05636c210fc19aa26bae3474425030c2 diff --git a/stakingcontract/MultiCall.sol b/stakingcontract/MultiCall.sol deleted file mode 100644 index 99f2e0b8..00000000 --- a/stakingcontract/MultiCall.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -contract Multicall { - function multiCall(address[] calldata targets, bytes[] calldata data) external view returns (bytes[] memory) { - require(targets.length == data.length, "target length != data length"); - bytes[] memory results = new bytes[](data.length); - - for (uint i; i < targets.length; i++) { - (bool success, bytes memory result) = targets[i].staticcall(data[i]); - require(success, 'call failed'); - results[i] = result; - } - - return results; - } -} diff --git a/stakingcontract/TestReportVerbose.md b/stakingcontract/TestReportVerbose.md deleted file mode 100644 index 707c2a5d..00000000 --- a/stakingcontract/TestReportVerbose.md +++ /dev/null @@ -1,530 +0,0 @@ -TrustEVM Unit Tests Verbose: - -Running 18 tests for test/TrustEVMStakingV2.t.sol:TrustEVMTest -[PASS] testAdminAddStaker() (gas: 111630) -[PASS] testAdminChangeRewardsPerBlock() (gas: 20774) -Logs: - The Rewards Per Block are now: 500000000000000000 - -[PASS] testEVMContractStake() (gas: 224485) -Logs: - New staker balance: 9000000000000000000 - TrustEVM Contract Balance: 1000000000000000000 - -[PASS] testEmergencyWithdraw() (gas: 199309) -Logs: - Block Number: 1 - Staking amount: 5000000000000000000 - - - Block Number: 10 - -- staker Emergencywithdraw() -- - Staking amount: 0 - Owed: 0 - Reward Debt: 0 - -[PASS] testGetAmount() (gas: 220020) -[PASS] testIsAdmin() (gas: 7980) -[PASS] testPendingEVMForOneStaker() (gas: 495997) -Logs: - #### Initialization & Formulas ################################################################# - ################################################################################################ - EVM Rewards Per Block (1 eth): 1000000000000000000 - EVM Rewards = Number of Blocks * EVM Rewards Per Block - Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance) - PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12)- Reward Debt - ################################################################################################ - Staking Contract Balance: 10000000000000000000 - User staking amount: 5000000000000000000 - ################################################################################################ - Block Number: 2 - Reward Debt: 0 - Accumulated EVM Per Share: 200000000000 - Last Reward Block: 2 - EVM Rewards Per Block: 0 - Pending EVM: 1000000000000000000 - --------------- - Block Number: 3 - Reward Debt: 0 - Accumulated EVM Per Share: 200000000000 - Last Reward Block: 2 - EVM Rewards Per Block: 1000000000000000000 - Pending EVM: 1500000000000000000 - --------------- - Block Number: 4 - Reward Debt: 0 - Accumulated EVM Per Share: 200000000000 - Last Reward Block: 2 - EVM Rewards Per Block: 2000000000000000000 - Pending EVM: 2000000000000000000 - --------------- - Block Number: 5 - Reward Debt: 0 - Accumulated EVM Per Share: 200000000000 - Last Reward Block: 2 - EVM Rewards Per Block: 3000000000000000000 - Pending EVM: 2500000000000000000 - -[PASS] testRemoveStaker() (gas: 91892) -[PASS] testRevokeStakerRole() (gas: 92408) -[PASS] testRewardsAlgorithmInspectOneStaker() (gas: 2340092) -Logs: - #### Initialization & Formulas ################################################################# - ################################################################################################ - EVM Rewards Per Block (1 EVM): 1000000000000000000 - EVM Rewards = Number of Blocks * EVM Rewards Per Block - Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance) // This updates only when deposit or withdraw - PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12) - (Reward Debt - Owed Amount) - ################################################################################################ - - - Block Number: 1 - Last Reward Block: 1 - Staking Pool Balance: 7000000000000000000 - Rewards Pool Balance: 0 - Staker1 Initial Staking Amount: 7000000000000000000 - Accumulated EVM Per Share: 0 - EVM Rewards: 0 - Reward Debt: 0 - Owed: 0 - Pending EVM: 0 - - - Block Number: 2 - Last Reward Block: 1 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 0 - Reward Debt: 0 - Owed: 0 - Pending EVM: 999999999999000000 - - - Block Number: 3 - Last Reward Block: 1 - Rewards Pool Balance: 0 - EVM Rewards before withdraw: 2000000000000000000 - --Withdraw(0)-- - Accumulated EVM Per Share: 285714285714 - Last Reward Block: 3 - EVM Rewards after withdraw: 0 - Reward Debt: 1999999999998000000 - Owed: 1999999999998000000 - Pending EVM: 1999999999998000000 - - - Block Number: 4 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 285714285714 - Reward Debt: 1999999999998000000 - Owed: 1999999999998000000 - Pending EVM: 2999999999997000000 - - - Block Number: 5 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 2000000000000000000 - Accumulated EVM Per Share: 285714285714 - Reward Debt: 1999999999998000000 - Owed: 1999999999998000000 - Pending EVM: 3999999999996000000 - - - Block Number: 6 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 3000000000000000000 - Accumulated EVM Per Share: 285714285714 - Reward Debt: 1999999999998000000 - Owed: 1999999999998000000 - Pending EVM: 4999999999995000000 - - - Block Number: 7 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards before withdraw: 4000000000000000000 - --Staker1 Withdraw(0)-- - Accumulated EVM Per Share: 857142857142 - Last Reward Block: 7 - EVM Rewards after withdraw: 0 - Reward Debt: 5999999999994000000 - Owed: 5999999999994000000 - Pending EVM: 5999999999994000000 - - - Block Number: 8 - Last Reward Block: 7 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 857142857142 - Reward Debt: 5999999999994000000 - Owed: 5999999999994000000 - Pending EVM: 6999999999993000000 - @@ Add Rewards: 20 ether @@ - - - Block Number: 9 - Last Reward Block: 7 - Rewards Pool Balance: 20000000000000000000 - EVM Rewards before withdraw: 2000000000000000000 - --Staker1 Withdraw(0)-- - Accumulated EVM Per Share: 1142857142856 - Last Reward Block: 9 - EVM Rewards after withdraw: 0 - Reward Debt: 7999999999992000000 - @@ Here is where the transfer of rewards to the user occurs @@ Owed: 0 - Pending EVM: 0 - - - New Rewards Pool Balance: 12000000000008000000 - - - To see the transfer from the contract to the Staker1 type 'forge test -vvvv' - - - This line shows the transaction from: - 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2::fallback{value: 7999999999992000000}() - - - -[PASS] testRewardsAlgorithmInspectThreeStakers() (gas: 3191805) -Logs: - #### Initialization & Formulas ################################################################# - ################################################################################################ - EVM Rewards Per Block (1 EVM): 1000000000000000000 - EVM Rewards = Number of Blocks * EVM Rewards Per Block - Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance) // This updates only when deposit or withdraw - PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12) - (Reward Debt - Owed Amount) - ################################################################################################ - - - Block Number: 1 - Last Reward Block: 1 - Staking Pool Balance: 30000000000000000000 - Rewards Pool Balance: 0 - Accumulated EVM Per Share: 0 - EVM Rewards: 0 - ----------------------------------------- - Staker1 Initial Staking Amount: 15000000000000000000 - Reward Debt: 0 - Owed: 0 - Pending EVM: 0 - ----------------------------------------- - Staker2 Initial Staking Amount: 10000000000000000000 - Reward Debt: 0 - Owed: 0 - Pending EVM: 0 - ----------------------------------------- - Staker3 Initial Staking Amount: 5000000000000000000 - Reward Debt: 0 - Owed: 0 - Pending EVM: 0 - ----------------------------------------- - - - Block Number: 2 - Last Reward Block: 1 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 0 - ----------------------------------------- - Staker1 - Reward Debt: 0 - Owed: 0 - Pending EVM: 499999999995000000 - ----------------------------------------- - Staker2 - Reward Debt: 0 - Owed: 0 - Pending EVM: 333333333330000000 - ----------------------------------------- - Staker3 - Reward Debt: 0 - Owed: 0 - Pending EVM: 166666666665000000 - - - Block Number: 3 - Last Reward Block: 1 - Rewards Pool Balance: 0 - EVM Rewards before withdraw: 2000000000000000000 - --Staker1 Withdraw(0)-- - Accumulated EVM Per Share: 66666666666 - Last Reward Block: 3 - EVM Rewards after withdraw: 0 - ----------------------------------------- - Staker1 - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 999999999990000000 - ----------------------------------------- - Staker2 - Reward Debt: 0 - Owed: 0 - Pending EVM: 666666666660000000 - ----------------------------------------- - Staker3 - Reward Debt: 0 - Owed: 0 - Pending EVM: 333333333330000000 - - - Block Number: 4 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 66666666666 - ----------------------------------------- - Staker1 - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 1499999999985000000 - ----------------------------------------- - Staker2 - Reward Debt: 0 - Owed: 0 - Pending EVM: 999999999990000000 - ----------------------------------------- - Staker3 - Reward Debt: 0 - Owed: 0 - Pending EVM: 499999999995000000 - - - Block Number: 5 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 2000000000000000000 - Accumulated EVM Per Share: 66666666666 - ----------------------------------------- - Staker1 - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 1999999999980000000 - ----------------------------------------- - Staker2 - Reward Debt: 0 - Owed: 0 - Pending EVM: 1333333333320000000 - ----------------------------------------- - Staker3 - Reward Debt: 0 - Owed: 0 - Pending EVM: 666666666660000000 - - - Block Number: 6 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards: 3000000000000000000 - Accumulated EVM Per Share: 66666666666 - ----------------------------------------- - Staker1 - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 2499999999990000000 - ----------------------------------------- - Staker2 - Reward Debt: 0 - Owed: 0 - Pending EVM: 1666666666660000000 - ----------------------------------------- - Staker3 - Reward Debt: 0 - Owed: 0 - Pending EVM: 833333333330000000 - - - Block Number: 7 - Last Reward Block: 3 - Rewards Pool Balance: 0 - EVM Rewards before withdraw: 4000000000000000000 - Staker1 Pending EVM before withdraw: 2999999999985000000 - --Staker2 Withdraw(0)-- - --Staker3 Withdraw(0)-- - Accumulated EVM Per Share: 199999999999 - Last Reward Block: 7 - EVM Rewards after withdraw: 0 - ----------------------------------------- - Staker1 - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 2999999999985000000 - ----------------------------------------- - Staker2 - Reward Debt: 1999999999990000000 - Owed: 1999999999990000000 - Pending EVM: 1999999999990000000 - ----------------------------------------- - Staker3 - Reward Debt: 999999999995000000 - Owed: 999999999995000000 - Pending EVM: 999999999995000000 - - - Block Number: 8 - Last Reward Block: 7 - Rewards Pool Balance: 0 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 199999999999 - ----------------------------------------- - Staker1 before withdraw - Reward Debt: 999999999990000000 - Owed: 999999999990000000 - Pending EVM: 3499999999980000000 - --Staker1 Withdraw(0)-- - Accumulated EVM Per Share: 233333333332 - EVM Rewards: 0 - ----------------------------------------- - Staker1 - Reward Debt: 3499999999980000000 - Owed: 3499999999980000000 - Pending EVM: 3499999999980000000 - ----------------------------------------- - Staker2 - Reward Debt: 1999999999990000000 - Owed: 1999999999990000000 - Pending EVM: 2333333333320000000 - ----------------------------------------- - Staker3 - Reward Debt: 999999999995000000 - Owed: 999999999995000000 - Pending EVM: 1166666666660000000 - @@ Add Rewards: 20 ether @@ - - - Block Number: 9 - Last Reward Block: 8 - Rewards Pool Balance: 20000000000000000000 - EVM Rewards before withdraw: 1000000000000000000 - Staker1 Reward Debt before withdraw: 3499999999980000000 - Staker1 Owed before withdraw: 3499999999980000000 - --Staker1 Withdraw(5 ether)-- - Accumulated EVM Per Share: 266666666665 - Last Reward Block: 9 - EVM Rewards after withdraw: 0 - ----------------------------------------- - Staker1 - @@ Here is where the transfer of rewards to the staker1 occurs @@ - New Staker1 Staking Amount: 10000000000000000000 - Reward Debt: 2666666666650000000 - Owed: 0 - Pending EVM: 0 - ----------------------------------------- - Staker2 - Reward Debt: 1999999999990000000 - Owed: 1999999999990000000 - Pending EVM: 2666666666650000000 - ----------------------------------------- - Staker3 - Reward Debt: 999999999995000000 - Owed: 999999999995000000 - Pending EVM: 1333333333325000000 - - - New Rewards Pool Balance: 16000000000025000000 - - - To see the transfer from the contract to the staker/user type 'forge test -vvvv' - - - This line shows the transaction from: - Staker1: 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2::fallback{value: 3999999999975000000}() - - - Block Number: 10 - Last Reward Block: 9 - Rewards Pool Balance: 16000000000025000000 - EVM Rewards: 1000000000000000000 - Accumulated EVM Per Share: 266666666665 - ----------------------------------------- - Staker1 - Reward Debt: 2666666666650000000 - Owed: 0 - Pending EVM: 400000000000000000 - ----------------------------------------- - Staker2 - Reward Debt: 1999999999990000000 - Owed: 1999999999990000000 - Pending EVM: 3066666666650000000 - ----------------------------------------- - Staker3 - Reward Debt: 999999999995000000 - Owed: 999999999995000000 - Pending EVM: 1533333333325000000 - - - Block Number: 11 - Last Reward Block: 9 - Rewards Pool Balance: 16000000000025000000 - EVM Rewards: 2000000000000000000 - Accumulated EVM Per Share: 266666666665 - Last Reward Block: 9 - EVM Rewards: 2000000000000000000 - Accumulated EVM Per Share: 266666666665 - ----------------------------------------- - Staker1 - Reward Debt: 2666666666650000000 - Owed: 0 - Pending EVM: 800000000000000000 - ----------------------------------------- - Staker2 - Reward Debt: 1999999999990000000 - Owed: 1999999999990000000 - Pending EVM: 3466666666650000000 - ----------------------------------------- - Staker3 - Reward Debt: 999999999995000000 - Owed: 999999999995000000 - Pending EVM: 1733333333325000000 - - - --Staker3 Withdraw(5 ether)-- - - - New Rewards Pool Balance: 14266666666700000000 - Staker3 After withdraw - New Staker3 Staking Amount: 0 - Reward Debt: 0 - Owed: 0 - Pending EVM: 0 - - - To see the transfer from the contract to the staker/user type 'forge test -vvvv' - - - Rewards transfer from the stack trace (staker3): 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB::fallback{value: 1733333333325000000}() - Staking transfer from the stack trace (staker3): 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB::fallback{value: 5000000000000000000}() - - - -[PASS] testSetEVMRewardsPerBlockByUserReverts() (gas: 10935) -[PASS] testSetUpstreamURLOnlyStaker() (gas: 138317) -[PASS] testStakingAmount() (gas: 227647) -Logs: - User staking amount: 7000000000000000000 - -[PASS] testStakingBalance() (gas: 219233) -[PASS] testWithdrawStakeAfterEndingPeriod() (gas: 301234) -[PASS] testWithdrawStakeRevertsBeforeEndingPeriodReverts() (gas: 221125) -[PASS] testclaimOwedRewards() (gas: 285526) -Logs: - Block Number: 1 - Staking amount: 7000000000000000000 - - - Block Number: 15 - -- staker withdraw(7 ether) -- all staking balance -- - Staking amount after withdrawal: 0 - (rewards were not paid with the withdrawal because there are no rewards in the pool balance) Owed: 14000000000000000000 - Staker removed from the Whitelist - @@ Admin added rewards: 50 ether @@ - Rewards Pool Balance (before claim owed rewards): 50000000000000000000 - Staker Claimed Owed Rewards - New Rewards Pool Balance after claim owed rewards: 36000000000000000000 - -Test result: ok. 18 passed; 0 failed; finished in 2.86ms diff --git a/stakingcontract/TrustEVMStaking.sol b/stakingcontract/TrustEVMStaking.sol deleted file mode 100644 index 12935f89..00000000 --- a/stakingcontract/TrustEVMStaking.sol +++ /dev/null @@ -1,258 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControlEnumerable.sol"; - -contract TrustEVMStaking is AccessControlEnumerable { - - struct StakingPool { - uint256 balance; - uint256 lastRewardBlock; - uint256 accumulatedEvmPerShare; - } - - struct UserInfo { - uint256 amount; - uint256 rewardDebt; - string upstreamUrl; - uint256 owedAmount; - uint256 stakingPeriodEndBlock; - } - - mapping(address => UserInfo) public stakingUserInfo; - - StakingPool public stakingPool = StakingPool({balance: 0, lastRewardBlock: 0, accumulatedEvmPerShare: 0}); - uint256 public rewardsPoolBalance; - - uint256 public evmRewardsPerBlock; - - uint256 public evmStakingPeriodLength; - - bytes32 public constant STAKER_ROLE = keccak256('STAKER'); - - event Stake(address indexed user, uint256 amount); - event Withdraw(address indexed user, uint256 amount); - event EmergencyWithdraw(address indexed user, uint256 amount); - event AddedReward(uint256 amount, uint256 blockNumber); - event UpdatePool(uint256 lastRewardBlock, uint256 balance, uint256 accumulatedEvmPerShare); - event AddToAllowList(address indexed user); - event RemoveFromAllowList(address indexed user); - - constructor(address _adminAccount, uint256 _evmRewardsPerBlock, uint256 _evmStakingPeriodLength) - { - _setupRole(DEFAULT_ADMIN_ROLE, _adminAccount); - _setRoleAdmin(STAKER_ROLE, DEFAULT_ADMIN_ROLE); - - evmRewardsPerBlock = _evmRewardsPerBlock; - evmStakingPeriodLength = _evmStakingPeriodLength; - } - - function pendingEvm(address _user) external returns (uint256) - { - if (block.number > stakingPool.lastRewardBlock && stakingPool.balance != 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - stakingPool.accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - return (stakingUserInfo[_user].amount * stakingPool.accumulatedEvmPerShare / 1e12) - (stakingUserInfo[_user].rewardDebt - stakingUserInfo[_user].owedAmount); - } - - function updatePool() public - { - if (block.number > stakingPool.lastRewardBlock) { - if (stakingPool.balance > 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - - stakingPool.accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - stakingPool.lastRewardBlock = block.number; - - emit UpdatePool(stakingPool.lastRewardBlock, stakingPool.balance, stakingPool.accumulatedEvmPerShare); - } - } - - function stake() public payable onlyStaker - { - updatePool(); - - if (stakingUserInfo[msg.sender].amount > 0) { - uint256 pending = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - stakingUserInfo[msg.sender].rewardDebt; - - stakingUserInfo[msg.sender].owedAmount += pending; // we were going to send it but instead we add it to what we owe the user - } - - stakingUserInfo[msg.sender].amount += msg.value; - // MasterChefV1 Algorithm - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = block.number + evmStakingPeriodLength; - - stakingPool.balance += msg.value; - - emit Stake(msg.sender, msg.value); - } - - function withdraw(uint256 _amount) public onlyStaker - { - // Checks - require(stakingUserInfo[msg.sender].amount >= _amount, "requested amount exceeds balance"); - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - - // Effects - updatePool(); - - // Here I substract the 'owed rewards' to rewardDebt to send them over with the 'current Rewards' - uint256 pendingAndOwed = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - (stakingUserInfo[msg.sender].rewardDebt - stakingUserInfo[msg.sender].owedAmount); - - stakingUserInfo[msg.sender].owedAmount = 0; - - // Interactions - safeEvmTransfer(msg.sender, pendingAndOwed); // Transferring the 'rewards' here. - - // Effects - stakingUserInfo[msg.sender].amount -= _amount; - - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingPool.balance -= _amount; - - // Interactions - (bool sent, ) = payable(msg.sender).call{value: _amount}(""); // Transferring the 'staked amount' here. - require(sent, "failed to send Ether"); - - emit Withdraw(msg.sender, _amount); - } - - function emergencyWithdraw() public - { - // Effects - uint256 amount = stakingUserInfo[msg.sender].amount; - - // Claim gas refunds - stakingUserInfo[msg.sender].amount = 0; - stakingUserInfo[msg.sender].rewardDebt = 0; - stakingUserInfo[msg.sender].owedAmount = 0; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = 0; - - stakingPool.balance -= amount; - - // Interaction - (bool sent, ) = payable(msg.sender).call{value: amount}(""); - require(sent, "failed to send EVM"); - - emit EmergencyWithdraw(msg.sender, amount); - } - - function claimOwedRewards() public - { - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - require(stakingUserInfo[msg.sender].owedAmount > 0, "no rewards owed"); - - uint256 owed = stakingUserInfo[msg.sender].owedAmount; - stakingUserInfo[msg.sender].owedAmount = 0; - - safeEvmTransfer(msg.sender, owed); - } - - function addRewards() public payable onlyAdmin - { - rewardsPoolBalance += msg.value; - - emit AddedReward(msg.value, block.number); - } - - function safeEvmTransfer(address _to, uint256 _amount) internal { - - if (_amount > rewardsPoolBalance) { - - // Effects - uint256 poolBalance = rewardsPoolBalance; - - stakingUserInfo[_to].owedAmount += _amount - poolBalance; - - rewardsPoolBalance = 0; - - // Interactions - (bool sent, ) = _to.call{value: poolBalance}(""); - require(sent, "failed to send EVM"); - - } else { - // Effects - rewardsPoolBalance -= _amount; - - // Interactions - (bool sent, ) = _to.call{value: _amount}(""); - require(sent, "failed to send EVM"); - } - } - - function setEvmRewardsPerBlock(uint256 _newRewardsPerBlock) external onlyAdmin { - evmRewardsPerBlock = _newRewardsPerBlock; - } - - function getUpstreamUrl(address _user) external view returns (string memory) { - return stakingUserInfo[_user].upstreamUrl; - } - - function setUpstreamUrl (string memory _newUrl) public onlyStaker { - stakingUserInfo[msg.sender].upstreamUrl = _newUrl; - } - - function getAmount(address _user) external view returns (uint256) { - return stakingUserInfo[_user].amount; - } - - function evmBalance() public view returns (uint256) { - return address(this).balance; - } - - // Access Control // - - modifier onlyAdmin() - { - require(isAdmin(msg.sender), 'Restricted to admins'); - _; - } - - modifier onlyStaker() - { - require(isStaker(msg.sender), 'Restricted to stakers'); - _; - } - - function isAdmin(address account) public virtual view returns (bool) - { - return hasRole(DEFAULT_ADMIN_ROLE, account); - } - - function isStaker(address account) public virtual view returns (bool) - { - return hasRole(STAKER_ROLE, account); - } - - function addStaker(address account) public virtual onlyAdmin - { - grantRole(STAKER_ROLE, account); - - emit AddToAllowList(account); - } - - function addAdmin(address account) public virtual onlyAdmin - { - grantRole(DEFAULT_ADMIN_ROLE, account); - } - - function removeStaker(address account) public virtual onlyAdmin - { - revokeRole(STAKER_ROLE, account); - - emit RemoveFromAllowList(account); - } - - function renounceAdmin() public virtual - { - renounceRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - -} diff --git a/stakingcontract/TrustEVMStakingV2.sol b/stakingcontract/TrustEVMStakingV2.sol deleted file mode 100644 index 25c88c11..00000000 --- a/stakingcontract/TrustEVMStakingV2.sol +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; - -contract TrustEVMStaking is AccessControlEnumerable { - - struct StakingPool { - uint256 balance; - uint256 lastRewardBlock; - uint256 accumulatedEvmPerShare; - } - - struct UserInfo { - uint256 amount; - uint256 rewardDebt; - string upstreamUrl; - uint256 owedAmount; - uint256 stakingPeriodEndBlock; - } - - mapping(address => UserInfo) public stakingUserInfo; - - StakingPool public stakingPool = StakingPool({balance: 0, lastRewardBlock: 0, accumulatedEvmPerShare: 0}); - uint256 public rewardsPoolBalance; - - uint256 public evmRewardsPerBlock; - - uint256 public evmStakingPeriodLength; - - bytes32 public constant STAKER_ROLE = keccak256('STAKER'); - - event Stake(address indexed user, uint256 amount); - event Withdraw(address indexed user, uint256 amount); - event EmergencyWithdraw(address indexed user, uint256 amount); - event AddedReward(uint256 amount, uint256 blockNumber); - event UpdatePool(uint256 lastRewardBlock, uint256 balance, uint256 accumulatedEvmPerShare); - event AddToAllowList(address indexed user); - event RemoveFromAllowList(address indexed user); - - constructor(address _adminAccount, uint256 _evmRewardsPerBlock, uint256 _evmStakingPeriodLength) - { - _setupRole(DEFAULT_ADMIN_ROLE, _adminAccount); - _setRoleAdmin(STAKER_ROLE, DEFAULT_ADMIN_ROLE); - - evmRewardsPerBlock = _evmRewardsPerBlock; - evmStakingPeriodLength = _evmStakingPeriodLength; - } - - function pendingEvm(address _user) external view returns (uint256) - { - uint256 accumulatedEvmPerShare = stakingPool.accumulatedEvmPerShare; - - if (block.number > stakingPool.lastRewardBlock && stakingPool.balance != 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - - accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - return (stakingUserInfo[_user].amount * accumulatedEvmPerShare / 1e12) - (stakingUserInfo[_user].rewardDebt - stakingUserInfo[_user].owedAmount); - } - - function getOwedAmount(address _user) external view returns (uint) - { - return stakingUserInfo[_user].owedAmount; - } - - function updatePool() public - { - if (block.number > stakingPool.lastRewardBlock) { - if (stakingPool.balance > 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - - stakingPool.accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - stakingPool.lastRewardBlock = block.number; - - emit UpdatePool(stakingPool.lastRewardBlock, stakingPool.balance, stakingPool.accumulatedEvmPerShare); - } - } - - function stake() public payable onlyStaker - { - updatePool(); - - if (stakingUserInfo[msg.sender].amount > 0) { - uint256 pending = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - stakingUserInfo[msg.sender].rewardDebt; - - stakingUserInfo[msg.sender].owedAmount += pending; - } - - stakingUserInfo[msg.sender].amount += msg.value; - // MasterChefV1 Algorithm - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = block.number + evmStakingPeriodLength; - - stakingPool.balance += msg.value; - - emit Stake(msg.sender, msg.value); - } - - function withdraw(uint256 _amount) public onlyStaker - { - // Checks - require(stakingUserInfo[msg.sender].amount >= _amount, "requested amount exceeds balance"); - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - - // Effects - updatePool(); - - // Substract the 'owed rewards' to rewardDebt to send them over with the 'current Rewards' - uint256 pendingAndOwed = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - (stakingUserInfo[msg.sender].rewardDebt - stakingUserInfo[msg.sender].owedAmount); - - stakingUserInfo[msg.sender].owedAmount = 0; - - // Interactions - safeEvmTransfer(msg.sender, pendingAndOwed); // Transferring the 'rewards' here. - - // Effects - stakingUserInfo[msg.sender].amount -= _amount; - - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingPool.balance -= _amount; - - // Interactions - (bool sent, ) = payable(msg.sender).call{value: _amount}(""); // Transferring the 'staked amount' here. - require(sent, "failed to send EVM"); - - emit Withdraw(msg.sender, _amount); - } - - function emergencyWithdraw() public - { - // Effects - uint256 amount = stakingUserInfo[msg.sender].amount; - - // Claim gas refunds - stakingUserInfo[msg.sender].amount = 0; - stakingUserInfo[msg.sender].rewardDebt = 0; - stakingUserInfo[msg.sender].owedAmount = 0; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = 0; - - stakingPool.balance -= amount; - - // Interaction - (bool sent, ) = payable(msg.sender).call{value: amount}(""); - require(sent, "failed to send EVM"); - - emit EmergencyWithdraw(msg.sender, amount); - } - - // Staker can claim rewards when: - // 1- Staked amount is 0 or greater. - // 2- Staking period is over. - // 3- Owed amount is greater than 0. - - function claimOwedRewards() public - { - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - require(stakingUserInfo[msg.sender].owedAmount > 0, "no rewards owed"); - - uint256 owed = stakingUserInfo[msg.sender].owedAmount; - stakingUserInfo[msg.sender].owedAmount = 0; - - safeEvmTransfer(msg.sender, owed); - } - - function addRewards() public payable onlyAdmin - { - rewardsPoolBalance += msg.value; - - emit AddedReward(msg.value, block.number); - } - - function safeEvmTransfer(address _to, uint256 _amount) internal { - - if (_amount > rewardsPoolBalance) { - - // Effects - uint256 poolBalance = rewardsPoolBalance; - - stakingUserInfo[_to].owedAmount += _amount - poolBalance; - - rewardsPoolBalance = 0; - - // Interactions - (bool sent, ) = _to.call{value: poolBalance}(""); - require(sent, "failed to send EVM"); - - } else { - // Effects - rewardsPoolBalance -= _amount; - - // Interactions - (bool sent, ) = _to.call{value: _amount}(""); - require(sent, "failed to send EVM"); - } - } - - function setEvmRewardsPerBlock(uint256 _newRewardsPerBlock) external onlyAdmin { - evmRewardsPerBlock = _newRewardsPerBlock; - } - - function getUpstreamUrl(address _user) external view returns (string memory) { - return stakingUserInfo[_user].upstreamUrl; - } - - function setUpstreamUrl (string memory _newUrl) public onlyStaker { - stakingUserInfo[msg.sender].upstreamUrl = _newUrl; - } - - function getAmount(address _user) external view returns (uint256) { - return stakingUserInfo[_user].amount; - } - - function evmBalance() public view returns (uint256) { - return address(this).balance; - } - - modifier onlyAdmin() - { - require(isAdmin(msg.sender), 'Restricted to admins'); - _; - } - - modifier onlyStaker() - { - require(isStaker(msg.sender), 'Restricted to stakers'); - _; - } - - function isAdmin(address account) public virtual view returns (bool) - { - return hasRole(DEFAULT_ADMIN_ROLE, account); - } - - function isStaker(address account) public virtual view returns (bool) - { - return hasRole(STAKER_ROLE, account); - } - - function addStaker(address account) public virtual onlyAdmin - { - grantRole(STAKER_ROLE, account); - - emit AddToAllowList(account); - } - - function addAdmin(address account) public virtual onlyAdmin - { - grantRole(DEFAULT_ADMIN_ROLE, account); - } - - function removeStaker(address account) public virtual onlyAdmin - { - revokeRole(STAKER_ROLE, account); - - emit RemoveFromAllowList(account); - } - - function renounceAdmin() public virtual - { - renounceRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - -} diff --git a/stakingcontract/TrustEVMStakingV2.t.sol b/stakingcontract/TrustEVMStakingV2.t.sol deleted file mode 100644 index 93aacb52..00000000 --- a/stakingcontract/TrustEVMStakingV2.t.sol +++ /dev/null @@ -1,953 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; - -import "forge-std/Test.sol"; -import "../src/TrustEVMStakingV2.sol"; - -contract TrustEVMTest is Test { - TrustEVMStaking trustEVMStaking; - - address admin = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; - address staker = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; - address stakerTwo = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; - address stakerThree = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; - bytes32 STAKER_ROLE = 0x0103b18bff1250f48d347408776979b4841009bbecc6ddf42ad618d875ec7c4b; - - function setUp() public { - trustEVMStaking = new TrustEVMStaking(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 1000000000000000000, 5); - } - - function testIsAdmin() public view { - bool isAdmin = trustEVMStaking.isAdmin(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4); - assert(isAdmin); - } - - function testAdminAddStaker() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - bool isStaker = trustEVMStaking.isStaker(staker); - assert(isStaker); - } - - function testAdminChangeRewardsPerBlock() public { - vm.startPrank(admin); - trustEVMStaking.setEvmRewardsPerBlock(500000000000000000); - uint rewardsPerBlock = trustEVMStaking.evmRewardsPerBlock(); - assertEq(rewardsPerBlock, 500000000000000000); - emit log_named_uint("The Rewards Per Block are now", rewardsPerBlock); - } - - function testSetEVMRewardsPerBlockByUserReverts() public { - vm.expectRevert("Restricted to admins"); - trustEVMStaking.setEvmRewardsPerBlock(500000000000000000); - } - - function testSetUpstreamURLOnlyStaker() public { - vm.prank(admin); - trustEVMStaking.addStaker(staker); - vm.startPrank(staker); - trustEVMStaking.setUpstreamUrl("http://www.stakerurl.com"); - string memory newUpstreamURL = trustEVMStaking.getUpstreamUrl(staker); - assertEq("http://www.stakerurl.com", newUpstreamURL); - } - - function testStakingBalance() public { - vm.prank(admin); - trustEVMStaking.addStaker(staker); - vm.deal(staker, 10 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 7 ether}(); - uint256 contractBalance = trustEVMStaking.evmBalance(); - assertEq(contractBalance, 7 ether); - } - - function testRevokeStakerRole() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - bool isStaker = trustEVMStaking.isStaker(staker); - assert(isStaker); - trustEVMStaking.revokeRole(STAKER_ROLE, staker); - isStaker = trustEVMStaking.isStaker(staker); - assertEq(isStaker, false); - } - - function testRemoveStaker() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - bool isStaker = trustEVMStaking.isStaker(staker); - assert(isStaker); - trustEVMStaking.removeStaker(staker); - isStaker = trustEVMStaking.isStaker(staker); - assertEq(isStaker, false); - } - - function testEVMContractStake() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 1 ether}(); - assertEq(trustEVMStaking.evmBalance(), 1 ether); - emit log_named_uint('New staker balance', staker.balance); - emit log_named_uint('TrustEVM Contract Balance', trustEVMStaking.evmBalance()); - } - - function testStakingAmount() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 7 ether}(); - (uint256 amount,,,,) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint('User staking amount', amount); - } - - function testGetAmount() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 7 ether}(); - uint getAmount = trustEVMStaking.getAmount(staker); - assertEq(getAmount, 7 ether); - } - - function testWithdrawStakeRevertsBeforeEndingPeriodReverts() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.startPrank(staker); - trustEVMStaking.stake{value: 7 ether}(); - vm.roll(3); // moves 5 blocks ahead - vm.expectRevert("staking period is not complete"); - trustEVMStaking.withdraw(5 ether); - } - - function testWithdrawStakeAfterEndingPeriod() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.startPrank(staker); - trustEVMStaking.stake{value: 7 ether}(); - vm.roll(7); // moves 7 blocks ahead - trustEVMStaking.withdraw(5 ether); - } - - function testPendingEVMForOneStaker() public { - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - trustEVMStaking.addStaker(stakerTwo); - vm.stopPrank(); - vm.deal(staker, 5 ether); - vm.deal(stakerTwo, 5 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 5 ether}(); - vm.prank(stakerTwo); - vm.roll(2); - trustEVMStaking.stake{value: 5 ether}(); - - uint rewardsPerBlock = trustEVMStaking.evmRewardsPerBlock(); - (uint256 amount,uint256 rewardDebt,,,) = trustEVMStaking.stakingUserInfo(staker); - emit log("#### Initialization & Formulas #################################################################"); - emit log("################################################################################################"); - emit log_named_uint("EVM Rewards Per Block (1 eth)", rewardsPerBlock); - emit log("EVM Rewards = Number of Blocks * EVM Rewards Per Block"); - emit log("Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance)"); - emit log("PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12)- Reward Debt"); - - emit log("################################################################################################"); - uint stakingBalance = trustEVMStaking.evmBalance(); - emit log_named_uint("Staking Contract Balance", stakingBalance); - emit log_named_uint("User staking amount", amount); - emit log("################################################################################################"); - - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Reward Debt", rewardDebt); - (uint256 balance,uint256 lastRewardBlock,uint256 accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - uint EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards Per Block", EVMReward); - uint pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - - vm.roll(3); - emit log("---------------"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Reward Debt", rewardDebt); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards Per Block", EVMReward); - emit log_named_uint("Pending EVM", pending); - - vm.roll(4); - emit log("---------------"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Reward Debt", rewardDebt); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards Per Block", EVMReward); - emit log_named_uint("Pending EVM", pending); - - vm.roll(5); - emit log("---------------"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Reward Debt", rewardDebt); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards Per Block", EVMReward); - emit log_named_uint("Pending EVM", pending); - } - - function testRewardsAlgorithmInspectOneStaker() public { - trustEVMStaking = new TrustEVMStaking(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 1000000000000000000, 0); - - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - vm.stopPrank(); - vm.deal(staker, 10 ether); - vm.startPrank(staker); - trustEVMStaking.stake{value: 7 ether}(); - uint rewardsPerBlock = trustEVMStaking.evmRewardsPerBlock(); - (uint256 amount,uint256 rewardDebt,string memory upstreamUrl,uint256 owedAmount, uint256 stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log("#### Initialization & Formulas #################################################################"); - emit log("################################################################################################"); - emit log_named_uint("EVM Rewards Per Block (1 EVM)", rewardsPerBlock); - emit log("EVM Rewards = Number of Blocks * EVM Rewards Per Block"); - emit log("Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance) // This updates only when deposit or withdraw"); - emit log("PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12) - (Reward Debt - Owed Amount)"); - emit log("################################################################################################"); - emit log("\n"); - - emit log_named_uint('Block Number', block.number); - uint stakingBalance = trustEVMStaking.evmBalance(); - (uint256 balance,uint256 lastRewardBlock,uint256 accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - emit log_named_uint("Staking Pool Balance", stakingBalance); - uint rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - emit log_named_uint("Staker1 Initial Staking Amount", amount); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - uint EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - uint pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - - vm.roll(2); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(3); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - trustEVMStaking.withdraw(0); - emit log("--Withdraw(0)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(4); - emit log("\n"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(5); - emit log("\n"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(6); - emit log("\n"); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(7); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - trustEVMStaking.withdraw(0); - emit log("--Staker1 Withdraw(0)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(8); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - vm.stopPrank(); - vm.deal(admin, 20 ether); - vm.prank(admin); - trustEVMStaking.addRewards{value: 20 ether}(); - emit log("@@ Add Rewards: 20 ether @@"); - - vm.roll(9); - vm.startPrank(staker); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - trustEVMStaking.withdraw(0); - emit log("--Staker1 Withdraw(0)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("@@ Here is where the transfer of rewards to the user occurs @@ Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("\n"); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("New Rewards Pool Balance", rewardsNewBalance); - emit log("\n"); - - emit log("To see the transfer from the contract to the Staker1 type 'forge test -vvvv'"); - emit log("\n"); - emit log("This line shows the transaction from:"); - emit log("0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2::fallback{value: 7999999999992000000}()"); - emit log("\n"); - } - - function testRewardsAlgorithmInspectThreeStakers() public { - trustEVMStaking = new TrustEVMStaking(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 1000000000000000000, 0); - - vm.startPrank(admin); - trustEVMStaking.addStaker(staker); - trustEVMStaking.addStaker(stakerTwo); - trustEVMStaking.addStaker(stakerThree); - vm.stopPrank(); - vm.deal(staker, 20 ether); - vm.deal(stakerTwo, 20 ether); - vm.deal(stakerThree, 20 ether); - vm.prank(staker); - trustEVMStaking.stake{value: 15 ether}(); - vm.prank(stakerTwo); - trustEVMStaking.stake{value: 10 ether}(); - vm.prank(stakerThree); - trustEVMStaking.stake{value: 5 ether}(); - uint rewardsPerBlock = trustEVMStaking.evmRewardsPerBlock(); - (uint256 amount,uint256 rewardDebt,string memory upstreamUrl,uint256 owedAmount, uint256 stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log("#### Initialization & Formulas #################################################################"); - emit log("################################################################################################"); - emit log_named_uint("EVM Rewards Per Block (1 EVM)", rewardsPerBlock); - emit log("EVM Rewards = Number of Blocks * EVM Rewards Per Block"); - emit log("Accumulated EVM Per Share = Accumulated EVM Per Share + (EVM Rewards * 1e12 / Staking Contract Balance) // This updates only when deposit or withdraw"); - emit log("PendingEVM = (Staked Amount * Accumulated EVM Per Share / 1e12) - (Reward Debt - Owed Amount)"); - emit log("################################################################################################"); - emit log("\n"); - - emit log_named_uint('Block Number', block.number); - uint stakingBalance = trustEVMStaking.evmBalance(); - (uint256 balance,uint256 lastRewardBlock,uint256 accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - emit log_named_uint("Staking Pool Balance", stakingBalance); - uint rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - uint EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - emit log("-----------------------------------------"); - emit log_named_uint("Staker1 Initial Staking Amount", amount); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - uint pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - (amount, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(stakerTwo); - emit log_named_uint("Staker2 Initial Staking Amount", amount); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - (amount, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(stakerThree); - emit log_named_uint("Staker3 Initial Staking Amount", amount); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - - vm.roll(2); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(3); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - vm.prank(staker); - trustEVMStaking.withdraw(0); - emit log("--Staker1 Withdraw(0)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(4); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(5); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(6); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(7); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Staker1 Pending EVM before withdraw", pending); - - vm.prank(stakerTwo); - trustEVMStaking.withdraw(0); - emit log("--Staker2 Withdraw(0)--"); - vm.prank(stakerThree); - trustEVMStaking.withdraw(0); - emit log("--Staker3 Withdraw(0)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(8); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1 before withdraw"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("--Staker1 Withdraw(0)--"); - vm.prank(staker); - trustEVMStaking.withdraw(0); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - vm.stopPrank(); - vm.deal(admin, 20 ether); - vm.prank(admin); - trustEVMStaking.addRewards{value: 20 ether}(); - emit log("@@ Add Rewards: 20 ether @@"); - - vm.roll(9); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards before withdraw", EVMReward); - (amount, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Staker1 Reward Debt before withdraw", rewardDebt); - emit log_named_uint("Staker1 Owed before withdraw", owedAmount); - vm.prank(staker); - trustEVMStaking.withdraw(5 ether); - emit log("--Staker1 Withdraw(5 ether)--"); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards after withdraw", EVMReward); - - emit log("-----------------------------------------"); - emit log("Staker1"); - emit log("@@ Here is where the transfer of rewards to the staker1 occurs @@"); - (amount, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("New Staker1 Staking Amount", amount); - - emit log_named_uint("Reward Debt", rewardDebt); // doesn't match - - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("\n"); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("New Rewards Pool Balance", rewardsNewBalance); - emit log("\n"); - - emit log("To see the transfer from the contract to the staker/user type 'forge test -vvvv'"); - emit log("\n"); - emit log("This line shows the transaction from:"); - emit log("Staker1: 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2::fallback{value: 3999999999975000000}() "); - - vm.roll(10); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - - vm.roll(11); - emit log("\n"); - emit log_named_uint('Block Number', block.number); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance", rewardsNewBalance); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Last Reward Block", lastRewardBlock); - EVMReward = (block.number - lastRewardBlock) * rewardsPerBlock; - emit log_named_uint("EVM Rewards", EVMReward); - (balance, lastRewardBlock, accumulatedEvmPerShare) = trustEVMStaking.stakingPool(); - emit log_named_uint("Accumulated EVM Per Share", accumulatedEvmPerShare); - - emit log("-----------------------------------------"); - emit log("Staker1"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - pending = trustEVMStaking.pendingEvm(staker); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker2"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerTwo); - pending = trustEVMStaking.pendingEvm(stakerTwo); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("-----------------------------------------"); - emit log("Staker3"); - (, rewardDebt, upstreamUrl, owedAmount, stakingPeriodEndBlock) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("\n"); - emit log("--Staker3 Withdraw(5 ether)--"); - emit log("\n"); - vm.prank(stakerThree); - trustEVMStaking.withdraw(5 ether); - rewardsNewBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("New Rewards Pool Balance", rewardsNewBalance); - emit log("Staker3 After withdraw"); - (amount, rewardDebt, upstreamUrl, owedAmount, ) = trustEVMStaking.stakingUserInfo(stakerThree); - pending = trustEVMStaking.pendingEvm(stakerThree); - emit log_named_uint("New Staker3 Staking Amount", amount); - emit log_named_uint("Reward Debt", rewardDebt); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Pending EVM", pending); - emit log("\n"); - emit log("To see the transfer from the contract to the staker/user type 'forge test -vvvv'"); - emit log("\n"); - emit log("Rewards transfer from the stack trace (staker3): 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB::fallback{value: 1733333333325000000}() "); - emit log("Staking transfer from the stack trace (staker3): 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB::fallback{value: 5000000000000000000}()"); - emit log("\n"); - } - - function testclaimOwedRewards() public { - vm.deal(admin, 100 ether); - vm.prank(admin); - trustEVMStaking.addStaker(staker); - - vm.deal(staker, 10 ether); - vm.startPrank(staker); - trustEVMStaking.stake{value: 7 ether}(); - (uint256 amount,uint256 rewardDebt,,uint256 owedAmount,) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Block Number", block.number); - emit log_named_uint("Staking amount", amount); - emit log("\n"); - - vm.roll(15); - emit log_named_uint("Block Number", block.number); - trustEVMStaking.withdraw(7 ether); - emit log("-- staker withdraw(7 ether) -- all staking balance --"); - (amount, rewardDebt,, owedAmount,) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Staking amount after withdrawal", amount); - emit log_named_uint("(rewards were not paid with the withdrawal because there are no rewards in the pool balance) Owed", owedAmount); - vm.stopPrank(); - - vm.startPrank(admin); - trustEVMStaking.removeStaker(staker); - emit log("Staker removed from the Whitelist"); - trustEVMStaking.addRewards{value:50 ether}(); - emit log("@@ Admin added rewards: 50 ether @@"); - uint256 rewardsPoolBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("Rewards Pool Balance (before claim owed rewards)", rewardsPoolBalance); - vm.stopPrank(); - - vm.prank(staker); - trustEVMStaking.claimOwedRewards(); - emit log("Staker Claimed Owed Rewards"); - rewardsPoolBalance = trustEVMStaking.rewardsPoolBalance(); - emit log_named_uint("New Rewards Pool Balance after claim owed rewards", rewardsPoolBalance); - assertEq(rewardsPoolBalance, 36000000000000000000); - } - - function testEmergencyWithdraw() public { - vm.deal(admin, 100 ether); - vm.prank(admin); - trustEVMStaking.addStaker(staker); - - vm.deal(staker, 10 ether); - vm.startPrank(staker); - trustEVMStaking.stake{value: 5 ether}(); - (uint256 amount,uint256 rewardDebt,,uint256 owedAmount,) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Block Number", block.number); - emit log_named_uint("Staking amount", amount); - emit log("\n"); - - vm.roll(10); - emit log_named_uint("Block Number", block.number); - trustEVMStaking.emergencyWithdraw(); - emit log("-- staker Emergencywithdraw() --"); - - (amount, rewardDebt,, owedAmount,) = trustEVMStaking.stakingUserInfo(staker); - emit log_named_uint("Staking amount", amount); - emit log_named_uint("Owed", owedAmount); - emit log_named_uint("Reward Debt", rewardDebt); - - assertEq(amount, 0); - assertEq(owedAmount, 0); - assertEq(rewardDebt, 0); - } -} diff --git a/stakingcontract/TrustEVMStakingV2_upgradeable.sol b/stakingcontract/TrustEVMStakingV2_upgradeable.sol deleted file mode 100644 index b80a4acc..00000000 --- a/stakingcontract/TrustEVMStakingV2_upgradeable.sol +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -contract TrustEVMStaking is AccessControlEnumerable, Initializable { - - struct StakingPool { - uint256 balance; - uint256 lastRewardBlock; - uint256 accumulatedEvmPerShare; - } - - struct UserInfo { - uint256 amount; - uint256 rewardDebt; - string upstreamUrl; - uint256 owedAmount; - uint256 stakingPeriodEndBlock; - } - - mapping(address => UserInfo) public stakingUserInfo; - - StakingPool public stakingPool = StakingPool({balance: 0, lastRewardBlock: 0, accumulatedEvmPerShare: 0}); - uint256 public rewardsPoolBalance; - - uint256 public evmRewardsPerBlock; - - uint256 public evmStakingPeriodLength; - - bytes32 public constant STAKER_ROLE = keccak256('STAKER'); - - event Stake(address indexed user, uint256 amount); - event Withdraw(address indexed user, uint256 amount); - event EmergencyWithdraw(address indexed user, uint256 amount); - event AddedReward(uint256 amount, uint256 blockNumber); - event UpdatePool(uint256 lastRewardBlock, uint256 balance, uint256 accumulatedEvmPerShare); - event AddToAllowList(address indexed user); - event RemoveFromAllowList(address indexed user); - - function initialize(address _adminAccount, uint256 _evmRewardsPerBlock, uint256 _evmStakingPeriodLength) public initializer { - _setupRole(DEFAULT_ADMIN_ROLE, _adminAccount); - _setRoleAdmin(STAKER_ROLE, DEFAULT_ADMIN_ROLE); - - evmRewardsPerBlock = _evmRewardsPerBlock; - evmStakingPeriodLength = _evmStakingPeriodLength; - } - - function pendingEvm(address _user) external view returns (uint256) - { - uint256 accumulatedEvmPerShare = stakingPool.accumulatedEvmPerShare; - - if (block.number > stakingPool.lastRewardBlock && stakingPool.balance != 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - - accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - return (stakingUserInfo[_user].amount * accumulatedEvmPerShare / 1e12) - (stakingUserInfo[_user].rewardDebt - stakingUserInfo[_user].owedAmount); - } - - function getOwedAmount(address _user) external view returns (uint) - { - return stakingUserInfo[_user].owedAmount; - } - - function updatePool() public - { - if (block.number > stakingPool.lastRewardBlock) { - if (stakingPool.balance > 0) { - uint256 blocks = block.number - stakingPool.lastRewardBlock; - uint256 evmReward = blocks * evmRewardsPerBlock; - - stakingPool.accumulatedEvmPerShare += evmReward * 1e12 / stakingPool.balance; - } - - stakingPool.lastRewardBlock = block.number; - - emit UpdatePool(stakingPool.lastRewardBlock, stakingPool.balance, stakingPool.accumulatedEvmPerShare); - } - } - - function stake() public payable onlyStaker - { - updatePool(); - - if (stakingUserInfo[msg.sender].amount > 0) { - uint256 pending = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - stakingUserInfo[msg.sender].rewardDebt; - - stakingUserInfo[msg.sender].owedAmount += pending; - } - - stakingUserInfo[msg.sender].amount += msg.value; - // MasterChefV1 Algorithm - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = block.number + evmStakingPeriodLength; - - stakingPool.balance += msg.value; - - emit Stake(msg.sender, msg.value); - } - - function withdraw(uint256 _amount) public onlyStaker - { - // Checks - require(stakingUserInfo[msg.sender].amount >= _amount, "requested amount exceeds balance"); - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - - // Effects - updatePool(); - - // Substract the 'owed rewards' to rewardDebt to send them over with the 'current Rewards' - uint256 pendingAndOwed = (stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12) - (stakingUserInfo[msg.sender].rewardDebt - stakingUserInfo[msg.sender].owedAmount); - - stakingUserInfo[msg.sender].owedAmount = 0; - - // Interactions - safeEvmTransfer(msg.sender, pendingAndOwed); // Transferring the 'rewards' here. - - // Effects - stakingUserInfo[msg.sender].amount -= _amount; - - stakingUserInfo[msg.sender].rewardDebt = stakingUserInfo[msg.sender].amount * stakingPool.accumulatedEvmPerShare / 1e12; - stakingPool.balance -= _amount; - - // Interactions - (bool sent, ) = payable(msg.sender).call{value: _amount}(""); // Transferring the 'staked amount' here. - require(sent, "failed to send EVM"); - - emit Withdraw(msg.sender, _amount); - } - - function emergencyWithdraw() public - { - // Effects - uint256 amount = stakingUserInfo[msg.sender].amount; - - // Claim gas refunds - stakingUserInfo[msg.sender].amount = 0; - stakingUserInfo[msg.sender].rewardDebt = 0; - stakingUserInfo[msg.sender].owedAmount = 0; - stakingUserInfo[msg.sender].stakingPeriodEndBlock = 0; - - stakingPool.balance -= amount; - - // Interaction - (bool sent, ) = payable(msg.sender).call{value: amount}(""); - require(sent, "failed to send EVM"); - - emit EmergencyWithdraw(msg.sender, amount); - } - - // Staker can claim rewards when: - // 1- Staked amount is 0 or greater. - // 2- Staking period is over. - // 3- Owed amount is greater than 0. - - function claimOwedRewards() public - { - require(block.number > stakingUserInfo[msg.sender].stakingPeriodEndBlock, "staking period is not complete"); - require(stakingUserInfo[msg.sender].owedAmount > 0, "no rewards owed"); - - uint256 owed = stakingUserInfo[msg.sender].owedAmount; - stakingUserInfo[msg.sender].owedAmount = 0; - - safeEvmTransfer(msg.sender, owed); - } - - function addRewards() public payable onlyAdmin - { - rewardsPoolBalance += msg.value; - - emit AddedReward(msg.value, block.number); - } - - function safeEvmTransfer(address _to, uint256 _amount) internal { - - if (_amount > rewardsPoolBalance) { - - // Effects - uint256 poolBalance = rewardsPoolBalance; - - stakingUserInfo[_to].owedAmount += _amount - poolBalance; - - rewardsPoolBalance = 0; - - // Interactions - (bool sent, ) = _to.call{value: poolBalance}(""); - require(sent, "failed to send EVM"); - - } else { - // Effects - rewardsPoolBalance -= _amount; - - // Interactions - (bool sent, ) = _to.call{value: _amount}(""); - require(sent, "failed to send EVM"); - } - } - - function setEvmRewardsPerBlock(uint256 _newRewardsPerBlock) external onlyAdmin { - evmRewardsPerBlock = _newRewardsPerBlock; - } - - function getUpstreamUrl(address _user) external view returns (string memory) { - return stakingUserInfo[_user].upstreamUrl; - } - - function setUpstreamUrl (string memory _newUrl) public onlyStaker { - stakingUserInfo[msg.sender].upstreamUrl = _newUrl; - } - - function getAmount(address _user) external view returns (uint256) { - return stakingUserInfo[_user].amount; - } - - function evmBalance() public view returns (uint256) { - return address(this).balance; - } - - modifier onlyAdmin() - { - require(isAdmin(msg.sender), 'Restricted to admins'); - _; - } - - modifier onlyStaker() - { - require(isStaker(msg.sender), 'Restricted to stakers'); - _; - } - - function isAdmin(address account) public virtual view returns (bool) - { - return hasRole(DEFAULT_ADMIN_ROLE, account); - } - - function isStaker(address account) public virtual view returns (bool) - { - return hasRole(STAKER_ROLE, account); - } - - function addStaker(address account) public virtual onlyAdmin - { - grantRole(STAKER_ROLE, account); - - emit AddToAllowList(account); - } - - function addAdmin(address account) public virtual onlyAdmin - { - grantRole(DEFAULT_ADMIN_ROLE, account); - } - - function removeStaker(address account) public virtual onlyAdmin - { - revokeRole(STAKER_ROLE, account); - - emit RemoveFromAllowList(account); - } - - function renounceAdmin() public virtual - { - renounceRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - -} diff --git a/stakingcontract/readme.md b/stakingcontract/readme.md deleted file mode 100644 index 78e8e779..00000000 --- a/stakingcontract/readme.md +++ /dev/null @@ -1,18 +0,0 @@ -TrustEVM Unit Tests Reports (using Foundry) - -Install Rust: https://rustup.rs/ -Install Foundry: Forge and Cast CLI commands can be installed by running cargo install --git https://github.com/gakonst/foundry --locked -Reference: Foundry Website: https://www.paradigm.xyz/2021/12/introducing-the-foundry-ethereum-development-toolbox - -1- Initialize a Foundry repository in the terminal: -* forge init -2- Type: -* cd -3- Copy the test file (extension .t.sol) into “test” folder. -4- Copy the smart contract file (extension .sol) into “src” folder. - -Running Tests in the terminal within : -* forge test -vvvv (See all the traces) -* forge test -vvv (Verbose +) **Recommended for a complete report of the rewards algorithm. -* forge test -vv (Verbose) -* forge test (Overview) diff --git a/stakingcontract/readme_upgradeable.md b/stakingcontract/readme_upgradeable.md deleted file mode 100644 index 5c91fc33..00000000 --- a/stakingcontract/readme_upgradeable.md +++ /dev/null @@ -1,118 +0,0 @@ -TrustEVM Upgradeable Contract Instructions using HardHat and Goerli Testnet: - -1- On the terminal, create a project folder: -* mkdir TrustEVMStakingContract -2- On the terminal, call directory into project folder: -* cd TrustEVMStakingContract -3 - Place provided “package.json” into the project folder (TrustEVMStakingContract). -4- To install the packages, on the Terminal run: -* npm i -5- On the terminal run: -* npx hardhat -6- Select “Create a JavaScript project” from the options -* .gitignore - y -* Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · n -7- Inside the “contracts” folder, delete the sample contract (Lock.sol) -8- Inside the “test” folder, delete the sample test (Lock.js) -9- Inside the “scripts” folder, delete ‘deploy.js’ -10- Copy ‘TrustEVMStakingContract_v1.sol’ inside “contracts” folder. -11- Place the provided ‘hardhat.config.js’ into the root directory. -12- Create a .env file on the root directory and populate: - INFURA_API_KEY= - ETHERSCAN_API_KEY= - PRIVATE_KEY= -13- Create a file within the ‘scripts’ folder called ‘deploy_trustevmstaking_v1.js’ with the following content: -const { ethers, upgrades } = require("hardhat"); - -async function main() { - const TrustEVMStakingContract = await ethers.getContractFactory("TrustEVMStakingContract_v1"); - const trustEVMStakingContract = await upgrades.deployProxy(TrustEVMStakingContract, [admin_address, evmRewardsPerBlock, evmStakingPeriodLength], {initializer: "initialize"}); - await trustEVMStakingContract.deployed(); - console.log(“TrustEVM Staking Contract deployed to:", trustEVMStakingContract.address); -} - -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error); - process.exit(1); - }); - -Important Note: replace admin_address, evmRewardsPerBlock, evmStakingPeriodLength with actual values. - * For Example: [0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 1000000000000000000, 100] - -14- Deploy Version 1 of the TrustEVM Staking Contract: -* npx hardhat run --network goerli scripts/deploy_trustevmstaking_v1.js -15- Three smart contracts will be deployed: The implementation contract (in this case TrustEVMStakingContract_v1), the Proxy contract and the Admin Proxy contract. -Verify the implementation contract in Etherscan: -* npx hardhat verify --network goerli -16- We can check the initialization values in the implementation contract but they will all be zero or default values, because the storage is the Proxy contract and the function calls are delegated to the Implementation contract. -17- In Etherscan find the TransparentUpgradeableProxy contract, go to the ‘Contract’ tab and select ‘More Options’ >> ‘Is This a Proxy?’. -18- Verify the Proxy contract and go back to the ‘Contract’ tab, and now use ‘Read As Proxy’, here we will find the initialization values passed upon deployment (this is the constructor version in upgradeable contracts) - -Upgrading to TrustEVMStakingContract_v2 - -1- Place the smart contract TrustEVMStakingContract_v2.sol in the “contracts” folder. -2- Create a new script file called ‘upgrade_trustevmstakingcontract_v2.js’ within the “scripts” folder with the following contents: -const { ethers, upgrades } = require("hardhat"); - -const PROXY = For example: “0xfAC375Bd68205Cc452D2CDb068E184e2f285e0c3"; - -async function main() { - const TrustEVMStakingContractV2 = await ethers.getContractFactory("TrustEVMStakingContract_v2”); - await upgrades.upgradeProxy(PROXY, TrustEVMStakingContractV2); - console.log("TrustEVM Staking Contract V2 Upgraded"); -} - -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error); - process.exit(1); - }); - -3- The implementation contract will be upgraded in the Proxy contract. -4- Verify the new implementation contract on Etherscan (optional): -* env $(cat .env) npx hardhat verify --network goerli -5- Repeat this step, in Etherscan find the TransparentUpgradeableProxy contract, go to the ‘Contract’ tab and select ‘More Options’ >> ‘Is This a Proxy?’. -6- Verify the Proxy contract and go back to the ‘Contract’ tab, and now use ‘Read As Proxy’ and ‘Write As Proxy’ to interact with the new implementation contract (TrustEVM Staking Contract V2) - -Example of basic rules for initializers (that replace constructors in upgradeable contracts): -* Notice that the initializer is going to run only once when the first version of the contract is deployed, and will not run in subsequent versions of the implementation (v2, v3, etc). -* We disable the initializer function explicitly in the further versions of the contract to disallow security risks. - -Box_v1.sol - -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -contract Box is Initializable { - uint public val; - - function initialize(uint _val) public initializer { - val = _val; - } -} - - -Box_v2.sol (This contract extends v1 by adding one more function and keeping the order of the state variables in the exact position as layed out in v1) - -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -contract BoxV2 is Initializable { - uint public val; - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function inc() external { - val += 1; - } -} diff --git a/tests/integration/goerly/eth_getLogs/test_06.tar b/tests/integration/goerly/eth_getLogs/test_06.tar new file mode 100644 index 00000000..0c04793f Binary files /dev/null and b/tests/integration/goerly/eth_getLogs/test_06.tar differ diff --git a/tests/integration/goerly/eth_getLogs/test_16.tar b/tests/integration/goerly/eth_getLogs/test_16.tar new file mode 100644 index 00000000..e6e6eb36 Binary files /dev/null and b/tests/integration/goerly/eth_getLogs/test_16.tar differ diff --git a/tests/integration/goerly/eth_getLogs/test_17.json b/tests/integration/goerly/eth_getLogs/test_17.json new file mode 100644 index 00000000..82f07635 --- /dev/null +++ b/tests/integration/goerly/eth_getLogs/test_17.json @@ -0,0 +1,39 @@ +[ + { + "request": { + "jsonrpc":"2.0", + "method":"eth_getLogs", + "params":[ + { + "fromBlock": "0x402e78", + "toBlock": "0x402e7f", + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000015b1281f4e58215b2c3243d864bdf8b9dddc0da2"] + } + ], + "id":3 + }, + "response": { + "jsonrpc":"2.0", + "id":3, + "result": [ + { + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "blockHash": "0xd4ed5825ca74c91f530e90ff0dab38907b69f0921c18af34b89d1c52e96b6916", + "blockNumber": "0x402e79", + "data": "0x00000000000000000000000000000000000000000000000013cfb1f7e4a92235", + "logIndex": "0x2", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000015b1281f4e58215b2c3243d864bdf8b9dddc0da2", + "0x00000000000000000000000045b224f0cd64ed5179502da42ed4e32228485b3b" + ], + "transactionHash": "0x6f480b3545789de65862c1079b0e46e117cc1690de830deec082440f5806dfd4", + "transactionIndex": "0x0" + } + ] + } + } +] + diff --git a/tests/integration/goerly/eth_getLogs/test_18.json b/tests/integration/goerly/eth_getLogs/test_18.json new file mode 100644 index 00000000..7cc44a92 --- /dev/null +++ b/tests/integration/goerly/eth_getLogs/test_18.json @@ -0,0 +1,39 @@ +[ + { + "request": { + "jsonrpc":"2.0", + "method":"eth_getLogs", + "params":[ + { + "fromBlock": "0x402e78", + "toBlock": "0x402e7f", + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "topics": [null, "0x00000000000000000000000015b1281f4e58215b2c3243d864bdf8b9dddc0da2"] + } + ], + "id":3 + }, + "response": { + "jsonrpc":"2.0", + "id":3, + "result": [ + { + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "blockHash": "0xd4ed5825ca74c91f530e90ff0dab38907b69f0921c18af34b89d1c52e96b6916", + "blockNumber": "0x402e79", + "data": "0x00000000000000000000000000000000000000000000000013cfb1f7e4a92235", + "logIndex": "0x2", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000015b1281f4e58215b2c3243d864bdf8b9dddc0da2", + "0x00000000000000000000000045b224f0cd64ed5179502da42ed4e32228485b3b" + ], + "transactionHash": "0x6f480b3545789de65862c1079b0e46e117cc1690de830deec082440f5806dfd4", + "transactionIndex": "0x0" + } + ] + } + } +] + diff --git a/tests/integration/goerly/eth_getLogs/test_19.json b/tests/integration/goerly/eth_getLogs/test_19.json new file mode 100644 index 00000000..572151b6 --- /dev/null +++ b/tests/integration/goerly/eth_getLogs/test_19.json @@ -0,0 +1,39 @@ +[ + { + "request": { + "jsonrpc":"2.0", + "method":"eth_getLogs", + "params":[ + { + "fromBlock": "0x402e78", + "toBlock": "0x402e7f", + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", null, "0x00000000000000000000000045b224f0cd64ed5179502da42ed4e32228485b3b"] + } + ], + "id":3 + }, + "response": { + "jsonrpc":"2.0", + "id":3, + "result": [ + { + "address": "0xf74a5ca65e4552cff0f13b116113ccb493c580c5", + "blockHash": "0xd4ed5825ca74c91f530e90ff0dab38907b69f0921c18af34b89d1c52e96b6916", + "blockNumber": "0x402e79", + "data": "0x00000000000000000000000000000000000000000000000013cfb1f7e4a92235", + "logIndex": "0x2", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000015b1281f4e58215b2c3243d864bdf8b9dddc0da2", + "0x00000000000000000000000045b224f0cd64ed5179502da42ed4e32228485b3b" + ], + "transactionHash": "0x6f480b3545789de65862c1079b0e46e117cc1690de830deec082440f5806dfd4", + "transactionIndex": "0x0" + } + ] + } + } +] + diff --git a/tests/leap/nodeos_trust_evm_server.py b/tests/leap/nodeos_eos_evm_server.py similarity index 95% rename from tests/leap/nodeos_trust_evm_server.py rename to tests/leap/nodeos_eos_evm_server.py index b2083cf3..1082cfc8 100755 --- a/tests/leap/nodeos_trust_evm_server.py +++ b/tests/leap/nodeos_eos_evm_server.py @@ -27,9 +27,9 @@ from antelope_name import convert_name_to_value ############################################################### -# nodeos_trust_evm_server +# nodeos_eos_evm_server # -# Set up a TrustEVM env +# Set up a EOS EVM env # # This test sets up 2 producing nodes and one "bridge" node using test_control_api_plugin. # One producing node has 3 of the elected producers and the other has 1 of the elected producers. @@ -39,13 +39,13 @@ # The bridge node has the test_control_api_plugin, which exposes a restful interface that the test script uses to kill # the "bridge" node when /fork endpoint called. # -# --trust-evm-contract-root should point to root of TrustEVM contract build dir +# --eos-evm-contract-root should point to root of EOS EVM Contract build dir # --genesis-json file to save generated EVM genesis json -# --read-endpoint trustnode-rpc endpoint (read endpoint) +# --read-endpoint eos-evm-rpc endpoint (read endpoint) # # Example: # cd ~/ext/leap/build -# ~/ext/TrustEVM/tests/leap/nodeos_trust_evm_server.py --trust-evm-contract-root ~/ext/TrustEVM/contract/build --leave-running +# ~/ext/eos-evm/tests/leap/nodeos_eos_evm_server.py --eos-evm-contract-root ~/ext/eos-evm/contract/build --leave-running # # Launches wallet at port: 9899 # Example: bin/cleos --wallet-url http://127.0.0.1:9899 ... @@ -63,9 +63,9 @@ errorExit=Utils.errorExit appArgs=AppArgs() -appArgs.add(flag="--trust-evm-contract-root", type=str, help="TrustEVM contract build dir", default=None) -appArgs.add(flag="--genesis-json", type=str, help="File to save generated genesis json", default="trust-evm-genesis.json") -appArgs.add(flag="--read-endpoint", type=str, help="EVM read enpoint (trustevm-rpc)", default="http://localhost:8881") +appArgs.add(flag="--eos-evm-contract-root", type=str, help="EOS EVM Contract build dir", default=None) +appArgs.add(flag="--genesis-json", type=str, help="File to save generated genesis json", default="eos-evm-genesis.json") +appArgs.add(flag="--read-endpoint", type=str, help="EVM read endpoint (eos-evm-rpc)", default="http://localhost:8881") args=TestHelper.parse_args({"--keep-logs","--dump-error-details","-v","--leave-running","--clean-run" }, applicationSpecificArgs=appArgs) debug=args.v @@ -73,11 +73,11 @@ dumpErrorDetails=args.dump_error_details keepLogs=args.keep_logs killAll=args.clean_run -trustEvmContractRoot=args.trust_evm_contract_root +eosEvmContractRoot=args.eos_evm_contract_root gensisJson=args.genesis_json readEndpoint=args.read_endpoint -assert trustEvmContractRoot is not None, "--trust-evm-contract-root is required" +assert eosEvmContractRoot is not None, "--eos-evm-contract-root is required" totalProducerNodes=2 totalNonProducerNodes=1 @@ -114,6 +114,8 @@ # producer nodes will be mapped to 0 through totalProducerNodes-1, so the number totalProducerNodes will be the non-producing node specificExtraNodeosArgs[totalProducerNodes]="--plugin eosio::test_control_api_plugin " extraNodeosArgs="--contracts-console" + if useEosVmOC: + extraNodeosArgs += " --wasm-runtime eos-vm-jit --eos-vm-oc-enable" Print("Stand up cluster") if cluster.launch(topo="bridge", pnodes=totalProducerNodes, @@ -199,7 +201,7 @@ # setup evm - contractDir=trustEvmContractRoot + "/evm_runtime" + contractDir=eosEvmContractRoot + "/evm_runtime" wasmFile="evm_runtime.wasm" abiFile="evm_runtime.abi" Utils.Print("Publish evm_runtime contract") @@ -236,7 +238,7 @@ "trust": {} }, "difficulty": "0x01", - "extraData": "TrustEVM", + "extraData": "EOSEVM", "gasLimit": "0x7ffffffffff", "mixHash": "0x"+block["id"], "nonce": f'{convert_name_to_value(evmAcc.name):#0x}', @@ -356,8 +358,8 @@ Utils.Print("Generated EVM json genesis file in: %s" % gensisJson) Utils.Print("") Utils.Print("You can now run:") - Utils.Print(" trustevm-node --plugin=blockchain_plugin --ship-endpoint=127.0.0.1:8999 --genesis-json=%s --chain-data=/tmp --verbosity=4" % gensisJson) - Utils.Print(" trustevm-rpc --trust-evm-node=127.0.0.1:8080 --http-port=0.0.0.0:8881 --chaindata=/tmp --api-spec=eth,debug,net,trace") + Utils.Print(" eos-evm-node --plugin=blockchain_plugin --ship-endpoint=127.0.0.1:8999 --genesis-json=%s --chain-data=/tmp --verbosity=4" % gensisJson) + Utils.Print(" eos-evm-rpc --eos-evm-node=127.0.0.1:8080 --http-port=0.0.0.0:8881 --chaindata=/tmp --api-spec=eth,debug,net,trace") Utils.Print("") Utils.Print("Web3 endpoint:") Utils.Print(" http://localhost:5000") @@ -410,6 +412,15 @@ def forward_request(req): "jsonrpc": "2.0", "result": '0x'+keccak(unhexlify(req['params'][0][2:])).hex() } + + if req['method'] == "eth_gasPrice": + gas_price=int(prodNode1.getTable(evmAcc.name, evmAcc.name, "config")['rows'][0]['gas_price']) + return { + "id": req['id'], + "jsonrpc": "2.0", + "result": f'{gas_price:#0x}' + } + return requests.post(readEndpoint, json.dumps(req), headers={"Content-Type":"application/json"}).json() request_data = request.get_json() diff --git a/tests/leap/nodeos_eos_evm_server/PERFORMANCE.md b/tests/leap/nodeos_eos_evm_server/PERFORMANCE.md new file mode 100644 index 00000000..374fef11 --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/PERFORMANCE.md @@ -0,0 +1,144 @@ +# Mesuring EVM contract performance + +### Create working folder +``` +mkdir ~/evmperf +cd ~/evmperf +``` + + +### Build EVM contract +_set stack-size in src/CMakeList.txt to_ **16384** _before building_ + +``` +cd ~/evmperf +git clone https://github.com/eosnetworkfoundation/eos-evm +cd eos-evm/contract +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release -DWITH_LOGTIME=ON .. +make -j4 +``` + +### Build eos-evm-node and eos-evm-rpc +``` +cd ~/evmperf/eos-evm +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +make -j4 eos-evm-node eos-evm-rpc +``` + + +### Build leap v3.2.2-logtime +``` +cd ~/evmperf +git clone https://github.com/elmato/leap +cd leap +git checkout v3.2.2-logtime +git submodules update --init --recursive +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +make -j4 +``` + +### Setup nodeos_eos_evm_server.py python env +``` +cd ~/evmperf/leap/build/tests +ln -s ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server.py nodeos_eos_evm_server.py +sed -i 's/SYS/EOS/g' core_symbol.py +python3 -m venv venv +source venv/bin/activate +pip install 'web3<6' flask flask-cors +``` + + +### Launch nodeos and deploy EVM contract +_use --use-eos-vm-oc=1 when launching **nodeos_eos_evm_server.py** if you want to measure OC performance_ + +``` +cd ~/evmperf/leap/build/tests +source venv/bin/activate +cd .. +./tests/nodeos_eos_evm_server.py --leave-running --eos-evm-contract-root ~/evmperf/eos-evm/contract/build +``` + +(_wait until nodeos_eos_evm_server start listening at localhost:5000_) + +### Launch eos-evm-node +``` +cd ~/evmperf/eos-evm/build/cmd +rm -rf chaindata etl-temp config-dir +./eos-evm-node --plugin=blockchain_plugin --ship-endpoint=127.0.0.1:8999 --genesis-json=$HOME/evmperf/leap/build/eos-evm-genesis.json --verbosity=4 +``` + +### Launch eos-evm-rpc +``` +cd ~/evmperf/eos-evm/build/cmd +./eos-evm-rpc --eos-evm-node=127.0.0.1:8080 --http-port=0.0.0.0:8881 --chaindata=./ --api-spec=eth,debug,net,trace --verbosity=4 +``` + +### Install scripts dependencies +``` +cd ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server +yarn install +``` + + +### Deploy Uniswap V2 +``` +cd ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server +npx hardhat run scripts/deploy-uniswap.js +``` + +### Deploy Recursive contract +``` +cd ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server +npx hardhat run scripts/deploy-recursive.js +``` + +### Allow Uniswap router to transfer AAA tokens +``` +cd ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server +npx hardhat approve --erc20 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 --router 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 --amount 10000 +``` + +### Tail logs _(separate console)_ +``` +cd ~/evmperf/eos-evm/tests/leap/nodeos_eos_evm_server +./extract-logtime-cmd.sh ~/evmperf/leap/build/var/lib/node_01/stderr.txt +``` + +### Execute AAA=>BBB swaps +``` +while true +do +npx hardhat swap --router 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 --path 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9 --amount 0.5 +done + +``` + +### Execute AAA=>BBB=>CCC swaps +``` +while true +do +npx hardhat swap --router 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 --path 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9,0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9,0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 --amount 0.5 +done +``` + +### Execute erc20 transfers +``` +while true +do +npx hardhat transfer --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --to 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --contract 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 --amount 0.1 +done +``` + +### Execute native transfers +``` +while true +do +npx hardhat native-transfer --from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --to 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 --amount 1 +done +``` \ No newline at end of file diff --git a/tests/leap/nodeos_trust_evm_server/README.md b/tests/leap/nodeos_eos_evm_server/README.md similarity index 100% rename from tests/leap/nodeos_trust_evm_server/README.md rename to tests/leap/nodeos_eos_evm_server/README.md diff --git a/tests/leap/nodeos_trust_evm_server/contracts/BlockNum.sol b/tests/leap/nodeos_eos_evm_server/contracts/BlockNum.sol similarity index 100% rename from tests/leap/nodeos_trust_evm_server/contracts/BlockNum.sol rename to tests/leap/nodeos_eos_evm_server/contracts/BlockNum.sol diff --git a/tests/leap/nodeos_trust_evm_server/contracts/Blockhash.sol b/tests/leap/nodeos_eos_evm_server/contracts/Blockhash.sol similarity index 100% rename from tests/leap/nodeos_trust_evm_server/contracts/Blockhash.sol rename to tests/leap/nodeos_eos_evm_server/contracts/Blockhash.sol diff --git a/tests/leap/nodeos_trust_evm_server/contracts/Eventor.sol b/tests/leap/nodeos_eos_evm_server/contracts/Eventor.sol similarity index 100% rename from tests/leap/nodeos_trust_evm_server/contracts/Eventor.sol rename to tests/leap/nodeos_eos_evm_server/contracts/Eventor.sol diff --git a/tests/leap/nodeos_trust_evm_server/contracts/Lock.sol b/tests/leap/nodeos_eos_evm_server/contracts/Lock.sol similarity index 100% rename from tests/leap/nodeos_trust_evm_server/contracts/Lock.sol rename to tests/leap/nodeos_eos_evm_server/contracts/Lock.sol diff --git a/tests/leap/nodeos_eos_evm_server/contracts/Recursive.sol b/tests/leap/nodeos_eos_evm_server/contracts/Recursive.sol new file mode 100644 index 00000000..dc399b21 --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/contracts/Recursive.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.7.0 <0.9.0; + +contract Recursive { + + event Call(uint256 _value); + function start(uint256 _depth) public { + emit Call(_depth); + if( _depth == 0 ) + return; + Recursive(this).start(_depth-1); + } +} \ No newline at end of file diff --git a/tests/leap/nodeos_trust_evm_server/contracts/Storage.sol b/tests/leap/nodeos_eos_evm_server/contracts/Storage.sol similarity index 100% rename from tests/leap/nodeos_trust_evm_server/contracts/Storage.sol rename to tests/leap/nodeos_eos_evm_server/contracts/Storage.sol diff --git a/tests/leap/nodeos_trust_evm_server/contracts/Token.sol b/tests/leap/nodeos_eos_evm_server/contracts/Token.sol similarity index 72% rename from tests/leap/nodeos_trust_evm_server/contracts/Token.sol rename to tests/leap/nodeos_eos_evm_server/contracts/Token.sol index e36f7ea4..1d8b6271 100644 --- a/tests/leap/nodeos_trust_evm_server/contracts/Token.sol +++ b/tests/leap/nodeos_eos_evm_server/contracts/Token.sol @@ -5,8 +5,7 @@ pragma solidity >=0.7.0 <0.9.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract Token is ERC20 { - - constructor () ERC20("Yuniper", "YUN") { + constructor (string memory _name, string memory _symbol) ERC20(_name, _symbol) { _mint(msg.sender, 1000000 * (10 ** uint256(decimals()))); } } diff --git a/tests/leap/nodeos_eos_evm_server/contracts/WEOS.sol b/tests/leap/nodeos_eos_evm_server/contracts/WEOS.sol new file mode 100644 index 00000000..7d512991 --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/contracts/WEOS.sol @@ -0,0 +1,756 @@ +// Copyright (C) 2015, 2016, 2017 Dapphub + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma solidity >=0.4.22 <0.6; + +contract WEOS9 { + string public name = "Wrapped EOS"; + string public symbol = "WEOS"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Deposit(address indexed dst, uint wad); + event Withdrawal(address indexed src, uint wad); + + mapping (address => uint) public balanceOf; + mapping (address => mapping (address => uint)) public allowance; + + function() external payable { + deposit(); + } + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + emit Deposit(msg.sender, msg.value); + } + function withdraw(uint wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + msg.sender.transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint) { + return address(this).balance; + } + + function approve(address guy, uint wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + emit Transfer(src, dst, wad); + + return true; + } +} + + +/* + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +*/ diff --git a/tests/leap/nodeos_eos_evm_server/extract-logtime-cmd.sh b/tests/leap/nodeos_eos_evm_server/extract-logtime-cmd.sh new file mode 100755 index 00000000..879040ba --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/extract-logtime-cmd.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo INIT,DECODE,RECOVER,EXECUTE,SAVE,CLEANUP,TOTAL +tail -f $1 | grep --line-buffered -Po "tx: \K\{(.*)\}" | jq -r '[.timelogs[0][1], .timelogs[1][1]-.timelogs[0][1], .timelogs[2][1]-.timelogs[1][1], .timelogs[3][1]-.timelogs[2][1], .timelogs[4][1]-.timelogs[3][1], .elapsed-.timelogs[4][1], .elapsed] | join(",")' diff --git a/tests/leap/nodeos_trust_evm_server/hardhat.config.js b/tests/leap/nodeos_eos_evm_server/hardhat.config.js similarity index 71% rename from tests/leap/nodeos_trust_evm_server/hardhat.config.js rename to tests/leap/nodeos_eos_evm_server/hardhat.config.js index 6ba8c1c6..a9ebda14 100644 --- a/tests/leap/nodeos_trust_evm_server/hardhat.config.js +++ b/tests/leap/nodeos_eos_evm_server/hardhat.config.js @@ -1,6 +1,6 @@ require("@nomicfoundation/hardhat-toolbox"); require("@nomiclabs/hardhat-web3"); -require("@b10k.io/hardhat-uniswap-v2-deploy-plugin"); +require("@onmychain/hardhat-uniswap-v2-deploy-plugin"); // task action function receives the Hardhat Runtime Environment as second argument task("accounts", "Prints accounts", async (_, { web3 }) => { @@ -11,6 +11,10 @@ task("blockNumber", "Prints the current block number", async (_, { web3 }) => { console.log(await web3.eth.getBlockNumber()); }); +task("gasPrice", "Prints the current gas price", async (_, { web3 }) => { + console.log(await web3.eth.getGasPrice()); +}); + task("block", "Prints block") .addParam("blocknum", "The block number") .setAction(async (taskArgs) => { @@ -32,6 +36,20 @@ task("nonce", "Prints an account's nonce") console.log(nonce); }); +task("native-transfer", "Send native tokens") + .addParam("from", "from account") + .addParam("to", "to account") + .addParam("amount", "amount to trasfer") + .setAction(async (taskArgs) => { + const signer = await ethers.getSigner(taskArgs.from); + const res = await signer.sendTransaction({ + to: taskArgs.to, + value: eth(taskArgs.amount) + }); + console.log(res.hash); +}); + + task("transfer", "Send ERC20 tokens") .addParam("from", "from account") .addParam("to", "to account") @@ -40,8 +58,8 @@ task("transfer", "Send ERC20 tokens") .setAction(async (taskArgs) => { const Token = await ethers.getContractFactory('Token') const token = Token.attach(taskArgs.contract) - const res = await token.connect(await ethers.getSigner(taskArgs.from)).transfer(taskArgs.to, ethers.utils.parseEther(taskArgs.amount.toString()),{gasLimit:50000}); - console.log(res); + const res = await token.connect(await ethers.getSigner(taskArgs.from)).transfer(taskArgs.to, ethers.utils.parseEther(taskArgs.amount.toString())); + console.log(res.hash); }); task("send-loop", "Send ERC20 token in a loop") @@ -172,8 +190,8 @@ task("swap4eth", "Swap exact tokens for ETH") .addParam("weth9", "The weth9 contract address") .addParam("router", "The router contract address") .setAction(async (taskArgs) => { + const signer = await ethers.getSigner(0); - //console.log(signer); const Token = await ethers.getContractFactory('Token') const token = Token.attach(taskArgs.erc20) @@ -181,31 +199,72 @@ task("swap4eth", "Swap exact tokens for ETH") const ROUTER = require("@uniswap/v2-periphery/build/UniswapV2Router02.json"); const WETH9 = require("@uniswap/v2-periphery/build/WETH9.json"); - //await ethers.getSigner const Router = new ethers.ContractFactory(ROUTER.abi, ROUTER.bytecode); const router = Router.attach(taskArgs.router).connect(signer); const Weth9 = new ethers.ContractFactory(WETH9.abi, WETH9.bytecode); const weth9 = Weth9.attach(taskArgs.weth9).connect(signer); - const AMOUNT_WETH9 = eth(1000); - const AMOUNT_TOKEN = eth(1000); + //TODO: check allowance + await token.approve(router.address, eth(1)); - await weth9.approve(router.address, AMOUNT_WETH9); - await token.approve(router.address, AMOUNT_TOKEN); - - const receipt = await router.addLiquidityETH( - token.address, - eth(1000), - eth(1000), - eth(100), - signer.address, - ethers.constants.MaxUint256, - { value: eth(1000) } + const receipt = await router.swapExactTokensForETH( + eth(1), //amountIn + 0, //amountOutMin + [token.address, weth9.address], //path + signer.address, //to + Date.now() + 1000*60*10, ); - console.log(receipt); + console.log(receipt.hash); +}); + +task("approve", "") +.addParam("erc20", "erc20 contract") +.addParam("router", "The router contract address") +.addParam("amount", "amount to approve") +.setAction(async (taskArgs) => { + const signer = await ethers.getSigner(0); + const Token = await ethers.getContractFactory('Token') + const token = Token.attach(taskArgs.erc20); + + const ROUTER = require("@uniswap/v2-periphery/build/UniswapV2Router02.json"); + const Router = new ethers.ContractFactory(ROUTER.abi, ROUTER.bytecode); + const router = Router.attach(taskArgs.router).connect(signer); + + const receipt = await token.approve(router.address, eth(taskArgs.amount)); + console.log(receipt.hash); +}); + +task("swap", "swap two erc20") +.addParam("path", "erc20 contract address path") +.addParam("router", "The router contract address") +.addParam("amount", "Amount-in") +.setAction(async (taskArgs) => { + const signer = await ethers.getSigner(0); + + const Token = await ethers.getContractFactory('Token') + + var tokens = []; + taskArgs.path.split(',').forEach((address) => { + tokens.push({token:Token.attach(address)}) + }); + + const ROUTER = require("@uniswap/v2-periphery/build/UniswapV2Router02.json"); + const WETH9 = require("@uniswap/v2-periphery/build/WETH9.json"); + + const Router = new ethers.ContractFactory(ROUTER.abi, ROUTER.bytecode); + const router = Router.attach(taskArgs.router).connect(signer); + + const receipt = await router.swapExactTokensForTokens( + eth(taskArgs.amount), //amountIn + 0, //amountOutMin + taskArgs.path.split(','), //path + signer.address, //to + Date.now() + 1000*60*10, + ); + console.log(receipt.hash); }); @@ -246,18 +305,31 @@ task("add-liquidity", "Adds liquidity to an ERC-20⇄WETH pool with ETH") { value: eth(1000) } ); - console.log(receipt); + console.log(receipt.hash); }); +task("call-recursive", "Call recursive contract") + .addParam("contract", "The contract address") + .addParam("depth", "Call depth") + .setAction(async (taskArgs) => { + const signer = await ethers.getSigner(0); + //console.log(signer); + + const Recursive = await ethers.getContractFactory('Recursive') + const recursive = Recursive.attach(taskArgs.contract) + + const receipt = await recursive.start(taskArgs.depth); + console.log(receipt.hash); +}); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { - defaultNetwork: "ltrust", + defaultNetwork: "EOSEVMLocalTestnet", networks: { - ltrust: { + EOSEVMLocalTestnet: { url: "http://localhost:5000", accounts: { mnemonic: "test test test test test test test test test test test junk", @@ -267,8 +339,8 @@ module.exports = { passphrase: "", }, }, - ttrust: { - url: "https://api-testnet.trust.one", + EOSEVMTestnet: { + url: "https://api.testnet.evm.eosnetwork.com", accounts: { mnemonic: "test test test test test test test test test test test junk", path: "m/44'/60'/0'/0", @@ -278,5 +350,20 @@ module.exports = { }, } }, - solidity: "0.8.17", + solidity: { + compilers : [ + { + version: "0.8.17", + }, + { + version: "0.5.16", + settings: { + "optimizer": { + "enabled": false, + "runs": 200, + } + }, + } + ] + } }; diff --git a/tests/leap/nodeos_trust_evm_server/package.json b/tests/leap/nodeos_eos_evm_server/package.json similarity index 92% rename from tests/leap/nodeos_trust_evm_server/package.json rename to tests/leap/nodeos_eos_evm_server/package.json index aa2edaaf..4d1f85bd 100644 --- a/tests/leap/nodeos_trust_evm_server/package.json +++ b/tests/leap/nodeos_eos_evm_server/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "@b10k.io/hardhat-uniswap-v2-deploy-plugin": "^1.0.4", + "@onmychain/hardhat-uniswap-v2-deploy-plugin": "^1.0.4", "@ethersproject/abi": "^5.4.7", "@ethersproject/providers": "^5.4.7", "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", diff --git a/tests/leap/nodeos_eos_evm_server/performance-measurements-0.5.0.md b/tests/leap/nodeos_eos_evm_server/performance-measurements-0.5.0.md new file mode 100644 index 00000000..9b93a2d2 --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/performance-measurements-0.5.0.md @@ -0,0 +1,42 @@ +### EVM 0.5.0 performance measurements + +The numbers presented in the table below are derived from an analysis performed on the EVM transaction execution inside the EVM runtime contract following the instructions in the [PERFORMANCE](PERFORMANCE.md) document. + +The detailed spreadsheet can be accessed at [this link](https://docs.google.com/spreadsheets/d/1AcfoAmHvSWM6iv2zMpj81Erm0mH0gb28Zjve8d1Zjtc/edit?usp=sharing). + +Each column on the spreadsheet corresponds to a different aspect of the transaction processing time: + +- **Init**: This measures the time it takes from when the transaction is received by the chain controller until control is passed to the smart contract. + +- **Decode**: Time taken to decode the EVM transaction. + +- **Recover**: Time taken to recover the sender of the EVM transaction. + +- **Execute**: This records the actual transaction execution time. + +- **Save**: This column captures the duration of the finalization phase where the changes are committed to the blockchain state. + +- **Cleanup**: This measures the time taken to tear down the execution environment once the transaction has been processed. + +- **Total**: The sum of all the times recorded (time billed by the chain) + +Below is a table with the best times recorded in microseconds (us) for `eos-vm-jit` and `eos-vm-jit (OC)`: + +```markdown + | eos-vm-jit | eos-vm-jit (OC) | +|:-----------------:|:----------:|:---------------:| +| native transfer | 167 us | 124 us | +| erc20 transfer | 343 us | 171 us | +| uniswap v2 swap | 1602 us | 424 us | +``` + +#### Measurement Environment + +The measurements for this analysis were taken on a machine with the following specifications: + +- **CPU**: Intel(R) Xeon(R) E-2286G @ 4.00GHz +- **Memory**: 32GB RAM +- **Operating System**: Ubuntu 22.04 + +The version of Leap used for these tests was `3.2.2-logtime`. + diff --git a/tests/leap/nodeos_trust_evm_server/scripts/deploy-blockhash.js b/tests/leap/nodeos_eos_evm_server/scripts/deploy-blockhash.js similarity index 100% rename from tests/leap/nodeos_trust_evm_server/scripts/deploy-blockhash.js rename to tests/leap/nodeos_eos_evm_server/scripts/deploy-blockhash.js diff --git a/tests/leap/nodeos_trust_evm_server/scripts/deploy-blocknum.js b/tests/leap/nodeos_eos_evm_server/scripts/deploy-blocknum.js similarity index 100% rename from tests/leap/nodeos_trust_evm_server/scripts/deploy-blocknum.js rename to tests/leap/nodeos_eos_evm_server/scripts/deploy-blocknum.js diff --git a/tests/leap/nodeos_trust_evm_server/scripts/deploy-eventor.js b/tests/leap/nodeos_eos_evm_server/scripts/deploy-eventor.js similarity index 100% rename from tests/leap/nodeos_trust_evm_server/scripts/deploy-eventor.js rename to tests/leap/nodeos_eos_evm_server/scripts/deploy-eventor.js diff --git a/tests/leap/nodeos_eos_evm_server/scripts/deploy-recursive.js b/tests/leap/nodeos_eos_evm_server/scripts/deploy-recursive.js new file mode 100644 index 00000000..9b4fe7c0 --- /dev/null +++ b/tests/leap/nodeos_eos_evm_server/scripts/deploy-recursive.js @@ -0,0 +1,25 @@ +// We require the Hardhat Runtime Environment explicitly here. This is optional +// but useful for running the script in a standalone fashion through `node