From f3357704ba2d1e8f55943b061e47f025e2d57f02 Mon Sep 17 00:00:00 2001 From: harryli Date: Wed, 29 Jun 2022 06:37:56 +0800 Subject: [PATCH] eip1559 support --- Makefile | 4 + .../abciapp/src/abci/server/callback/mod.rs | 3 +- src/components/contracts/baseapp/Cargo.toml | 51 +- src/components/contracts/baseapp/src/lib.rs | 82 +- .../contracts/modules/account/src/impls.rs | 10 + .../contracts/modules/ethereum/Cargo.toml | 4 +- .../contracts/modules/ethereum/src/impls.rs | 217 +++-- .../contracts/modules/ethereum/src/lib.rs | 63 +- .../modules/ethereum/tests/ethereum_db.rs | 7 +- .../ethereum/tests/ethereum_integration.rs | 9 +- .../contracts/modules/evm/Cargo.toml | 9 +- .../modules/evm/precompile/basic/Cargo.toml | 2 +- .../modules/evm/precompile/basic/src/lib.rs | 20 +- .../modules/evm/precompile/frc20/Cargo.toml | 2 +- .../modules/evm/precompile/frc20/src/lib.rs | 68 +- .../modules/evm/precompile/frc20/src/tests.rs | 8 +- .../modules/evm/precompile/modexp/Cargo.toml | 2 +- .../modules/evm/precompile/modexp/src/lib.rs | 48 +- .../evm/precompile/modexp/src/tests.rs | 22 +- .../modules/evm/precompile/runtime/Cargo.toml | 26 + .../modules/evm/precompile/runtime/src/lib.rs | 126 +++ .../evm/precompile/sha3fips/Cargo.toml | 2 +- .../evm/precompile/sha3fips/src/lib.rs | 6 +- .../modules/evm/precompile/utils/Cargo.toml | 5 +- .../modules/evm/precompile/utils/src/lib.rs | 3 +- .../contracts/modules/evm/src/impls.rs | 7 + .../contracts/modules/evm/src/lib.rs | 15 +- .../contracts/modules/evm/src/precompile.rs | 36 +- .../modules/evm/src/runtime/runner.rs | 316 ++++++- .../modules/evm/src/runtime/stack.rs | 31 +- .../evm/test-vector-support/Cargo.toml | 2 +- .../modules/evm/tests/evm_integration.rs | 3 +- .../template/tests/template_integration.rs | 18 +- .../contracts/primitives/core/Cargo.toml | 2 +- .../contracts/primitives/evm/Cargo.toml | 4 +- .../contracts/primitives/evm/src/lib.rs | 22 +- .../contracts/primitives/mocks/Cargo.toml | 2 +- .../contracts/primitives/mocks/src/lib.rs | 6 +- .../contracts/primitives/rpc-core/Cargo.toml | 2 + .../primitives/rpc-core/src/types/block.rs | 2 + .../rpc-core/src/types/call_request.rs | 4 + .../primitives/rpc-core/src/types/mod.rs | 2 +- .../primitives/rpc-core/src/types/receipt.rs | 3 + .../rpc-core/src/types/transaction.rs | 68 +- .../rpc-core/src/types/transaction_request.rs | 67 +- .../contracts/primitives/traits/Cargo.toml | 2 +- .../primitives/traits/src/account.rs | 2 + .../contracts/primitives/traits/src/base.rs | 7 +- .../contracts/primitives/traits/src/evm.rs | 3 + .../contracts/primitives/types/Cargo.toml | 2 +- .../primitives/types/src/actions/ethereum.rs | 8 +- .../primitives/types/src/actions/evm.rs | 38 + .../primitives/types/src/actions/mod.rs | 8 + .../primitives/types/src/assemble.rs | 7 +- .../contracts/primitives/utils/Cargo.toml | 2 + .../contracts/primitives/wasm/Cargo.toml | 2 +- .../contracts/primitives/wasm/src/wasm.rs | 63 +- src/components/contracts/rpc/Cargo.toml | 4 +- src/components/contracts/rpc/src/eth.rs | 859 ++++++++++++++---- .../contracts/rpc/src/eth_filter.rs | 2 +- .../contracts/rpc/src/eth_pubsub.rs | 17 +- src/components/contracts/rpc/src/lib.rs | 3 +- src/components/finutils/src/common/utils.rs | 3 +- .../finutils/src/txn_builder/mod.rs | 13 +- src/ledger/src/staking/ops/replace_staker.rs | 3 +- src/ledger/src/store/mod.rs | 3 +- src/libs/merkle_tree/src/lib.rs | 2 +- 67 files changed, 2017 insertions(+), 447 deletions(-) create mode 100644 src/components/contracts/modules/evm/precompile/runtime/Cargo.toml create mode 100644 src/components/contracts/modules/evm/precompile/runtime/src/lib.rs diff --git a/Makefile b/Makefile index ccb88a064..ed243153a 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,10 @@ build_release_goleveldb: tendermint_goleveldb cargo build --release --bins -p abciapp -p finutils $(call pack,release) +check: tendermint_goleveldb + cargo check --release --bins -p abciapp -p finutils + $(call pack,release) + # Build for goleveldb build_release_musl_goleveldb: tendermint_goleveldb cargo build --release --bins -p abciapp -p finutils --target=x86_64-unknown-linux-musl diff --git a/src/components/abciapp/src/abci/server/callback/mod.rs b/src/components/abciapp/src/abci/server/callback/mod.rs index fda0b258f..84ad64321 100644 --- a/src/components/abciapp/src/abci/server/callback/mod.rs +++ b/src/components/abciapp/src/abci/server/callback/mod.rs @@ -381,7 +381,8 @@ pub fn end_block( if td_height <= CFG.checkpoint.disable_evm_block_height || td_height >= CFG.checkpoint.enable_frc20_height { - let _ = s.account_base_app.write().end_block(req); + // let _ = + s.account_base_app.write().end_block(req); } // mint coinbase, cache system transactions to ledger diff --git a/src/components/contracts/baseapp/Cargo.toml b/src/components/contracts/baseapp/Cargo.toml index 265a6e14e..3194b54ca 100644 --- a/src/components/contracts/baseapp/Cargo.toml +++ b/src/components/contracts/baseapp/Cargo.toml @@ -1,47 +1,48 @@ [package] -name = "baseapp" -version = "0.1.0" authors = ["FindoraNetwork"] +description = "Base application for tendermint abci" edition = "2021" homepage = "https://findora.org/technology" -repository = "https://github.com/findoranetwork/platform/" -description = "Base application for tendermint abci" +name = "baseapp" readme = "README.md" +repository = "https://github.com/findoranetwork/platform/" +version = "0.1.0" [dependencies] -abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2" } -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } -ethereum-types = { version = "0.12", default-features = false } +abci = {git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2"} +ethereum = {version = "0.10.0", default-features = false, features = ["with-serde"]} +ethereum-types = {version = "0.12", default-features = false} futures = "0.3.16" lazy_static = "1.4.0" -ledger = { path = "../../../ledger" } +ledger = {path = "../../../ledger"} log = "0.4" parking_lot = "0.11.1" -primitive-types = { version = "0.10.1", default-features = false, features = ["rlp", "byteorder", "serde"] } +primitive-types = {version = "0.10.1", default-features = false, features = ["rlp", "byteorder", "serde"]} protobuf = "2.16" ruc = "1.0" -serde = { version = "1.0.124", features = ["derive"] } +serde = {version = "1.0.124", features = ["derive"]} serde_json = "1.0.40" -storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v0.1.4" } +storage = {git = "https://github.com/FindoraNetwork/storage.git", tag = "v0.1.4"} # primitives -fp-core = { path = "../primitives/core" } -fp-evm = { path = "../primitives/evm" } -fp-traits = { path = "../primitives/traits" } -fp-types = { path = "../primitives/types" } -fp-utils = { path = "../primitives/utils" } +fp-core = {path = "../primitives/core"} +fp-evm = {path = "../primitives/evm"} +fp-traits = {path = "../primitives/traits"} +fp-types = {path = "../primitives/types"} +fp-utils = {path = "../primitives/utils"} # modules -module-account = { path = "../modules/account"} -module-ethereum = { path = "../modules/ethereum"} -module-evm = { path = "../modules/evm"} -module-template = { path = "../modules/template"} -module-xhub = { path = "../modules/xhub"} +module-account = {path = "../modules/account"} +module-ethereum = {path = "../modules/ethereum"} +module-evm = {path = "../modules/evm"} +module-template = {path = "../modules/template"} +module-xhub = {path = "../modules/xhub"} -evm-precompile-basic = { path = "../modules/evm/precompile/basic" } -evm-precompile-frc20 = { path = "../modules/evm/precompile/frc20" } -evm-precompile-modexp = { path = "../modules/evm/precompile/modexp" } -evm-precompile-sha3fips = { path = "../modules/evm/precompile/sha3fips" } +evm-precompile-basic = {path = "../modules/evm/precompile/basic"} +evm-precompile-frc20 = {path = "../modules/evm/precompile/frc20"} +evm-precompile-modexp = {path = "../modules/evm/precompile/modexp"} +evm-precompile-runtime = {path = "../modules/evm/precompile/runtime"} +evm-precompile-sha3fips = {path = "../modules/evm/precompile/sha3fips"} [features] abci_mock = [] diff --git a/src/components/contracts/baseapp/src/lib.rs b/src/components/contracts/baseapp/src/lib.rs index 54ee25357..588d986f9 100644 --- a/src/components/contracts/baseapp/src/lib.rs +++ b/src/components/contracts/baseapp/src/lib.rs @@ -12,7 +12,8 @@ mod notify; use crate::modules::ModuleManager; use abci::Header; -use ethereum::BlockV0 as Block; +use ethereum::BlockV2 as Block; +use evm_precompile_runtime::{self, FindoraPrecompiles}; use fp_core::{ account::SmartAccount, context::{Context, RunTxMode}, @@ -51,6 +52,23 @@ const CHAIN_STATE_PATH: &str = "state.db"; const CHAIN_HISTORY_DATA_PATH: &str = "history.db"; const CHAIN_STATE_MIN_VERSIONS: u64 = 4 * 60 * 24 * 90; +const INITIAL_BASE_FEE: u64 = 1000000000; +const ELASTICITY_MULTIPLIER: u64 = 2; +const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; + +#[inline(always)] +pub fn get_initial_base_fee() -> U256 { + U256::from(INITIAL_BASE_FEE) +} +#[inline(always)] +pub fn get_elasticity_multiplier() -> U256 { + U256::from(ELASTICITY_MULTIPLIER) +} +#[inline(always)] +pub fn get_base_fee_max_change_denominator() -> U256 { + U256::from(BASE_FEE_MAX_CHANGE_DENOMINATOR) +} + pub struct BaseApp { /// application name from abci.Info pub name: String, @@ -107,6 +125,10 @@ impl module_ethereum::Config for BaseApp { type Runner = module_evm::runtime::runner::ActionRunner; } +parameter_types! { + pub PrecompilesValue: FindoraPrecompiles = FindoraPrecompiles::<_>::new(); +} + impl module_evm::Config for BaseApp { type AccountAsset = module_account::App; type AddressMapping = EthereumAddressMapping; @@ -126,6 +148,8 @@ impl module_evm::Config for BaseApp { evm_precompile_sha3fips::Sha3FIPS512, evm_precompile_frc20::FRC20, ); + type PrecompilesType = FindoraPrecompiles; + type PrecompilesValue = PrecompilesValue; } impl module_xhub::Config for BaseApp { @@ -423,4 +447,60 @@ impl BaseProvider for BaseApp { None } } + + /// Return the base fee at the given height. + #[allow(clippy::comparison_chain, clippy::question_mark)] + fn base_fee(&self, id: Option) -> Option { + let mut comp_gas = std::vec::Vec::new(); + let block = self.current_block(id)?; + let mut parent_block = + self.current_block(Some(BlockId::Hash(block.header.parent_hash))); + if parent_block.is_none() { + return None; + } + let parent_gas_used = parent_block.as_ref().unwrap().header.gas_used; + let parent_gas_target = parent_block.as_ref().unwrap().header.gas_limit + / get_elasticity_multiplier(); + comp_gas.push((parent_gas_used, parent_gas_target)); + + while parent_block.as_ref().is_some() { + parent_block = self.current_block(Some(BlockId::Hash( + parent_block.as_ref().unwrap().header.parent_hash, + ))); + let parent_gas_used = parent_block.as_ref().unwrap().header.gas_used; + let parent_gas_target = parent_block.as_ref().unwrap().header.gas_limit + / get_elasticity_multiplier(); + comp_gas.push((parent_gas_used, parent_gas_target)); + } + + let mut base_fee = get_initial_base_fee(); + comp_gas.pop(); + while !comp_gas.is_empty() { + let (parent_gas_used, parent_gas_target) = comp_gas.pop().unwrap(); + if parent_gas_used > parent_gas_target { + let gas_used_delta = parent_gas_used - parent_gas_target; + let base_fee_delta = std::cmp::max( + base_fee * gas_used_delta + / parent_gas_target + / get_base_fee_max_change_denominator(), + U256::from(1), + ); + base_fee += base_fee_delta; + } else if parent_gas_used < parent_gas_target { + let gas_used_delta = parent_gas_target - parent_gas_used; + let base_fee_delta = base_fee * gas_used_delta + / parent_gas_target + / get_base_fee_max_change_denominator(); + base_fee -= base_fee_delta; + } + } + + Some(base_fee) + } + + /// Return `true` if the request BlockId is post-eip1559. + /// Do not be used. + fn is_eip1559(&self, _id: Option) -> bool { + false + } } diff --git a/src/components/contracts/modules/account/src/impls.rs b/src/components/contracts/modules/account/src/impls.rs index 6a2d3da4e..c087abe50 100644 --- a/src/components/contracts/modules/account/src/impls.rs +++ b/src/components/contracts/modules/account/src/impls.rs @@ -154,4 +154,14 @@ impl AccountAsset
for App { ) -> Result<()> { Allowances::insert(ctx.state.write().borrow_mut(), owner, spender, &amount) } + + fn income(ctx: &Context, who: &Address, value: U256) -> Result<()> { + if value.is_zero() { + return Ok(()); + } + + let mut sa = Self::account_of(ctx, who, None).c(d!("account does not exist"))?; + sa.balance = sa.balance.checked_add(value).c(d!("balance overflow"))?; + AccountStore::insert(ctx.state.write().borrow_mut(), who, &sa) + } } diff --git a/src/components/contracts/modules/ethereum/Cargo.toml b/src/components/contracts/modules/ethereum/Cargo.toml index 061eebb12..b06d9b78a 100644 --- a/src/components/contracts/modules/ethereum/Cargo.toml +++ b/src/components/contracts/modules/ethereum/Cargo.toml @@ -10,9 +10,9 @@ readme = "README.md" [dependencies] abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2" } -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } log = "0.4" rand = "0.8" rlp = "0.5" diff --git a/src/components/contracts/modules/ethereum/src/impls.rs b/src/components/contracts/modules/ethereum/src/impls.rs index 25f1c9a33..644459799 100644 --- a/src/components/contracts/modules/ethereum/src/impls.rs +++ b/src/components/contracts/modules/ethereum/src/impls.rs @@ -1,9 +1,7 @@ use crate::storage::*; use crate::{App, Config, ContractLog, TransactionExecuted}; use config::abci::global_cfg::CFG; -use ethereum::{ - BlockV0 as Block, LegacyTransactionMessage, Receipt, TransactionV0 as Transaction, -}; +use ethereum::{BlockV2 as Block, Receipt, TransactionV2 as Transaction}; use ethereum_types::{Bloom, BloomInput, H160, H256, H64, U256}; use evm::{ExitFatal, ExitReason}; use fp_core::{ @@ -12,8 +10,8 @@ use fp_core::{ use fp_events::Event; use fp_evm::{BlockId, CallOrCreateInfo, Runner, TransactionStatus}; use fp_storage::{Borrow, BorrowMut}; +use fp_types::actions::evm as EvmAction; use fp_types::crypto::HA256; -use fp_types::{actions::evm as EvmAction, crypto::secp256k1_ecdsa_recover}; use fp_utils::{proposer_converter, timestamp_converter}; use log::{debug, info}; use ruc::*; @@ -23,14 +21,28 @@ impl App { pub fn recover_signer(transaction: &Transaction) -> Option { let mut sig = [0u8; 65]; let mut msg = [0u8; 32]; - sig[0..32].copy_from_slice(&transaction.signature.r()[..]); - sig[32..64].copy_from_slice(&transaction.signature.s()[..]); - sig[64] = transaction.signature.standard_v(); - msg.copy_from_slice( - &LegacyTransactionMessage::from(transaction.clone()).hash()[..], - ); - - let pubkey = secp256k1_ecdsa_recover(&sig, &msg).ok()?; + match transaction { + Transaction::Legacy(t) => { + sig[0..32].copy_from_slice(&t.signature.r()[..]); + sig[32..64].copy_from_slice(&t.signature.s()[..]); + sig[64] = t.signature.standard_v(); + msg.copy_from_slice( + ðereum::LegacyTransactionMessage::from(t.clone()).hash()[..], + ); + } + Transaction::EIP1559(t) => { + sig[0..32].copy_from_slice(&t.r[..]); + sig[32..64].copy_from_slice(&t.s[..]); + sig[64] = t.odd_y_parity as u8; + msg.copy_from_slice( + ðereum::EIP1559TransactionMessage::from(t.clone()).hash()[..], + ); + } + _ => { + return None; + } + } + let pubkey = fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg).ok()?; Some(H160::from(H256::from_slice( Keccak256::digest(&pubkey).as_slice(), ))) @@ -134,29 +146,83 @@ impl App { let source = Self::recover_signer(&transaction) .ok_or_else(|| eg!("ExecuteTransaction: InvalidSignature"))?; - let transaction_hash = - H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + // let transaction_hash = + // H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + + let transaction_hash = transaction.hash(); let mut pending_txs: Vec<_> = PendingTransactions::get(ctx.db.read().borrow()).unwrap_or_default(); let transaction_index = pending_txs.len() as u32; - let gas_limit = transaction.gas_limit; + let nonce; + let gas_price; + let gas_limit; + let action; + let value; + let input; + let max_priority_fee_per_gas; + let max_fee_per_gas; + let _chain_id; + let mut _access_list = vec![]; + + let is_eip1559 = match &transaction { + Transaction::Legacy(legacy_transaction_transaction) => { + let transaction = legacy_transaction_transaction; + nonce = transaction.nonce; + gas_price = transaction.gas_price; + max_fee_per_gas = transaction.gas_price; + max_priority_fee_per_gas = U256::from(0); + gas_limit = transaction.gas_limit; + action = transaction.action; + value = transaction.value; + input = transaction.input.clone(); + _chain_id = match transaction.signature.chain_id() { + Some(chain_id) => chain_id, + None => return Err(eg!("Must provide chainId")), + }; + + false + } + Transaction::EIP1559(eip1559_transaction_transaction) => { + let transaction = eip1559_transaction_transaction; + nonce = transaction.nonce; + gas_limit = transaction.gas_limit; + action = transaction.action; + value = transaction.value; + input = transaction.input.clone(); + max_fee_per_gas = transaction.max_fee_per_gas; + max_priority_fee_per_gas = transaction.max_priority_fee_per_gas; + gas_price = transaction.max_fee_per_gas; + _chain_id = transaction.chain_id; + _access_list = transaction.access_list.clone(); + + true + } + _ => { + return Err(eg!("Transaction Type Error")); + } + }; + + // let gas_limit = transaction.gas_limit; let execute_ret = Self::execute_transaction( ctx, source, - transaction.input.clone(), - transaction.value, - transaction.gas_limit, - Some(transaction.gas_price), - Some(transaction.nonce), - transaction.action, + input, + value, + gas_limit, + Some(gas_price), + Some(max_fee_per_gas), + Some(max_priority_fee_per_gas), + Some(nonce), + action, + is_eip1559, ); if let Err(e) = execute_ret { let mut to = Default::default(); - if let ethereum::TransactionAction::Call(target) = transaction.action { + if let ethereum::TransactionAction::Call(target) = action { to = target; } events.push(Event::emit_event( @@ -311,42 +377,85 @@ impl App { value: U256, gas_limit: U256, gas_price: Option, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, nonce: Option, action: ethereum::TransactionAction, + is_eip1559: bool, ) -> Result<(Option, Option, CallOrCreateInfo)> { - match action { - ethereum::TransactionAction::Call(target) => { - let res = C::Runner::call( - ctx, - EvmAction::Call { - source: from, - target, - input, - value, - gas_limit: gas_limit.low_u64(), - gas_price, - nonce, - }, - C::config(), - )?; - - Ok((Some(target), None, CallOrCreateInfo::Call(res))) + if is_eip1559 { + match action { + ethereum::TransactionAction::Call(target) => { + let res = C::Runner::call_eip1559( + ctx, + EvmAction::CallEip1559 { + source: from, + target, + input, + value, + gas_limit: gas_limit.low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }, + C::config(), + )?; + + Ok((Some(target), None, CallOrCreateInfo::Call(res))) + } + ethereum::TransactionAction::Create => { + let res = C::Runner::create_eip1559( + ctx, + EvmAction::CreateEip1559 { + source: from, + init: input, + value, + gas_limit: gas_limit.low_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }, + C::config(), + )?; + + Ok((None, Some(res.value), CallOrCreateInfo::Create(res))) + } } - ethereum::TransactionAction::Create => { - let res = C::Runner::create( - ctx, - EvmAction::Create { - source: from, - init: input, - value, - gas_limit: gas_limit.low_u64(), - gas_price, - nonce, - }, - C::config(), - )?; - - Ok((None, Some(res.value), CallOrCreateInfo::Create(res))) + } else { + match action { + ethereum::TransactionAction::Call(target) => { + let res = C::Runner::call( + ctx, + EvmAction::Call { + source: from, + target, + input, + value, + gas_limit: gas_limit.low_u64(), + gas_price, + nonce, + }, + C::config(), + )?; + + Ok((Some(target), None, CallOrCreateInfo::Call(res))) + } + ethereum::TransactionAction::Create => { + let res = C::Runner::create( + ctx, + EvmAction::Create { + source: from, + init: input, + value, + gas_limit: gas_limit.low_u64(), + gas_price, + nonce, + }, + C::config(), + )?; + + Ok((None, Some(res.value), CallOrCreateInfo::Create(res))) + } } } } diff --git a/src/components/contracts/modules/ethereum/src/lib.rs b/src/components/contracts/modules/ethereum/src/lib.rs index 8184c5620..509efcf82 100644 --- a/src/components/contracts/modules/ethereum/src/lib.rs +++ b/src/components/contracts/modules/ethereum/src/lib.rs @@ -4,6 +4,7 @@ mod basic; mod impls; +use ethereum::TransactionV2 as Transaction; use ethereum_types::{H160, H256, U256}; use evm::Config as EvmConfig; use fp_core::context::RunTxMode; @@ -52,7 +53,7 @@ pub trait Config { } pub mod storage { - use ethereum::{BlockV0 as Block, Receipt, TransactionV0 as Transaction}; + use ethereum::{BlockV2 as Block, Receipt, TransactionV2 as Transaction}; use ethereum_types::U256; use fp_evm::TransactionStatus; use fp_storage::*; @@ -160,53 +161,73 @@ impl ValidateUnsigned for App { fn validate_unsigned(ctx: &Context, call: &Self::Call) -> Result<()> { let Action::Transact(transaction) = call; - if let Some(chain_id) = transaction.signature.chain_id() { - if chain_id != C::ChainId::get() { - return Err(eg!(format!( - "InvalidChainId, got {}, but expected {}", - chain_id, - C::ChainId::get() - ))); + + let (nonce, gas_price, gas_limit, value, chain_id); + + match transaction { + Transaction::Legacy(t) => { + nonce = t.nonce; + gas_price = t.gas_price; + gas_limit = t.gas_limit; + value = t.value; + chain_id = match t.signature.chain_id() { + Some(chain_id) => chain_id, + None => return Err(eg!("Must provide chainId")), + }; + } + Transaction::EIP1559(t) => { + nonce = t.nonce; + gas_limit = t.gas_limit; + gas_price = t.max_fee_per_gas; + value = t.value; + chain_id = t.chain_id; } - } else { - return Err(eg!("Must provide chainId".to_string())); + _ => { + return Err(eg!("Transaction Type Error")); + } + } + + if chain_id != C::ChainId::get() { + return Err(eg!(format!( + "InvalidChainId, got {}, but expected {}", + chain_id, + C::ChainId::get() + ))); } let origin = Self::recover_signer(transaction) .ok_or_else(|| eg!("InvalidSignature, can not recover signer address"))?; // Same as go ethereum, Min gas limit is 21000. - if transaction.gas_limit < U256::from(21000) - || transaction.gas_limit > C::BlockGasLimit::get() - { + if gas_limit < U256::from(21000) || gas_limit > C::BlockGasLimit::get() { return Err(eg!(format!( "InvalidGasLimit: got {}, the gas limit must be in range [21000, {}]", - transaction.gas_limit, + gas_limit, C::BlockGasLimit::get() ))); } - if transaction.gas_price < C::FeeCalculator::min_gas_price() { + if gas_price < C::FeeCalculator::min_gas_price() { return Err(eg!(format!( "InvalidGasPrice: got {}, but the minimum gas price is {}", - transaction.gas_price, + gas_price, C::FeeCalculator::min_gas_price() ))); } let account_id = C::AddressMapping::convert_to_account_id(origin); - let nonce = C::AccountAsset::nonce(ctx, &account_id); + let nonce2 = C::AccountAsset::nonce(ctx, &account_id); let balance = C::AccountAsset::balance(ctx, &account_id); - if transaction.nonce < nonce { + if nonce < nonce2 { return Err(eg!(format!( "InvalidNonce: origin: {:?}, got {}, but expected {}", - origin, transaction.nonce, nonce + origin, nonce, nonce2 ))); } - let fee = transaction.gas_price.saturating_mul(transaction.gas_limit); - let total_payment = transaction.value.saturating_add(fee); + let fee = gas_price.saturating_mul(gas_limit); + let total_payment = value.saturating_add(fee); if balance < total_payment { return Err(eg!(format!( "InsufficientBalance, origin: {:?}, actual balance {}, but expected payment {}", diff --git a/src/components/contracts/modules/ethereum/tests/ethereum_db.rs b/src/components/contracts/modules/ethereum/tests/ethereum_db.rs index c33f729e7..ad9198f11 100644 --- a/src/components/contracts/modules/ethereum/tests/ethereum_db.rs +++ b/src/components/contracts/modules/ethereum/tests/ethereum_db.rs @@ -60,9 +60,11 @@ fn test_eth_db_migrate_txn_index() { let transaction_hash = H256::from_slice(Keccak256::digest(&rlp::encode(&txn)).as_slice()); + //Save Transaction index txns.push((HA256::new(transaction_hash), (U256::from(i), i))); - let _ = TransactionIndex::insert( + // let _ = + TransactionIndex::insert( ctx.state.write().borrow_mut(), &HA256::new(transaction_hash), &(U256::from(i), i), @@ -70,7 +72,8 @@ fn test_eth_db_migrate_txn_index() { } //Call migrate on ethereum module. - let _ = module_ethereum::App::::migrate(ctx.borrow_mut()); + // let _ = + module_ethereum::App::::migrate(ctx.borrow_mut()); //Confirm transaction index values were migrated to rocksdb instance from the context. for txn in txns { diff --git a/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs b/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs index 82225e099..897160743 100644 --- a/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs +++ b/src/components/contracts/modules/ethereum/tests/ethereum_integration.rs @@ -91,7 +91,8 @@ fn test_abci_begin_block() { let mut header = Header::default(); header.height = 3; req.set_header(header); - let _ = BASE_APP.lock().unwrap().begin_block(&req); + // let _ = + BASE_APP.lock().unwrap().begin_block(&req); } fn test_abci_deliver_tx() { @@ -135,11 +136,13 @@ fn test_abci_deliver_tx() { fn test_abci_end_block() { let mut req = RequestEndBlock::default(); req.height = 3; - let _ = BASE_APP.lock().unwrap().end_block(&req); + // let _ = + BASE_APP.lock().unwrap().end_block(&req); } fn test_abci_commit() { - let _ = BASE_APP.lock().unwrap().commit(&RequestCommit::new()); + // let _ = + BASE_APP.lock().unwrap().commit(&RequestCommit::new()); assert_eq!( BASE_APP .lock() diff --git a/src/components/contracts/modules/evm/Cargo.toml b/src/components/contracts/modules/evm/Cargo.toml index 1163a2365..01bdb63f6 100644 --- a/src/components/contracts/modules/evm/Cargo.toml +++ b/src/components/contracts/modules/evm/Cargo.toml @@ -11,10 +11,10 @@ readme = "README.md" [dependencies] abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2" } ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } -evm-runtime = { version = "0.30.0", default-features = false } -evm-gasometer = { version = "0.30.0", default-features = false } -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } +evm-runtime = { version = "0.33.0", default-features = false } +evm-gasometer = { version = "0.33.0", default-features = false } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } impl-trait-for-tuples = "0.2" log = "0.4" rlp = { version = "0.5", default-features = false } @@ -38,6 +38,7 @@ config = { path = "../../../config"} storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v0.1.4" } ledger = { path = "../../../../ledger" } + [dev-dependencies] baseapp = { path = "../../baseapp" } fp-mocks = { path = "../../primitives/mocks" } diff --git a/src/components/contracts/modules/evm/precompile/basic/Cargo.toml b/src/components/contracts/modules/evm/precompile/basic/Cargo.toml index 578ce2bde..b2b8d20c1 100644 --- a/src/components/contracts/modules/evm/precompile/basic/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/basic/Cargo.toml @@ -9,7 +9,7 @@ description = "Basic precompiles for EVM module." readme = "README.md" [dependencies] -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } module-evm = { path = "../../../../modules/evm"} ripemd160 = "0.9" diff --git a/src/components/contracts/modules/evm/precompile/basic/src/lib.rs b/src/components/contracts/modules/evm/precompile/basic/src/lib.rs index d762c6ed1..34cfc3ae3 100644 --- a/src/components/contracts/modules/evm/precompile/basic/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/basic/src/lib.rs @@ -16,7 +16,9 @@ // limitations under the License. use core::cmp::min; +use evm::executor::stack::PrecompileFailure; use evm::{ExitError, ExitSucceed}; +use module_evm::precompile::PrecompileResult; use module_evm::precompile::{LinearCostPrecompile, PrecompileId}; /// The identity precompile. @@ -35,7 +37,7 @@ impl LinearCostPrecompile for Identity { fn execute( input: &[u8], _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { Ok((ExitSucceed::Returned, input.to_vec())) } } @@ -56,7 +58,7 @@ impl LinearCostPrecompile for ECRecover { fn execute( i: &[u8], _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { let mut input = [0u8; 128]; input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); @@ -97,7 +99,7 @@ impl LinearCostPrecompile for Ripemd160 { fn execute( input: &[u8], _cost: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { use ripemd160::Digest; let mut ret = [0u8; 32]; @@ -122,7 +124,7 @@ impl LinearCostPrecompile for Sha256 { fn execute( input: &[u8], _cost: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { let ret = fp_utils::hashing::sha2_256(input); Ok((ExitSucceed::Returned, ret.to_vec())) } @@ -145,7 +147,7 @@ impl LinearCostPrecompile for ECRecoverPublicKey { fn execute( i: &[u8], _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { let mut input = [0u8; 128]; input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); @@ -157,8 +159,12 @@ impl LinearCostPrecompile for ECRecoverPublicKey { sig[32..64].copy_from_slice(&input[96..128]); sig[64] = input[63]; - let pubkey = fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg) - .map_err(|_| ExitError::Other("Public key recover failed".into()))?; + let pubkey = + fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg).map_err(|_| { + PrecompileFailure::Error { + exit_status: ExitError::Other("Public key recover failed".into()), + } + })?; Ok((ExitSucceed::Returned, pubkey.to_vec())) } diff --git a/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml b/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml index bd9466bb9..9c80abc78 100644 --- a/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/frc20/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" [dependencies] ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } evm-precompile-utils = { path = "../utils"} fp-traits = { path = "../../../../primitives/traits" } log = "0.4" diff --git a/src/components/contracts/modules/evm/precompile/frc20/src/lib.rs b/src/components/contracts/modules/evm/precompile/frc20/src/lib.rs index cde405aad..d776fab5a 100644 --- a/src/components/contracts/modules/evm/precompile/frc20/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/frc20/src/lib.rs @@ -3,12 +3,16 @@ mod tests; use core::marker::PhantomData; use ethereum_types::{H160, U256}; -use evm::{executor::PrecompileOutput, Context, ExitError, ExitSucceed}; +use evm::{ + executor::stack::{PrecompileFailure, PrecompileOutput}, + Context, ExitError, ExitSucceed, +}; use evm_precompile_utils::{ error, Address, EvmDataReader, EvmDataWriter, EvmResult, Gasometer, LogsBuilder, }; use fp_traits::{account::AccountAsset, evm::AddressMapping}; use log::debug; +use module_evm::precompile::PrecompileResult; use module_evm::{ precompile::{FinState, Precompile, PrecompileId}, Config, @@ -83,19 +87,57 @@ impl Precompile for FRC20 { target_gas: Option, context: &Context, state: &FinState, - ) -> Result { + ) -> PrecompileResult { let mut input = EvmDataReader::new(input); - - match &input.read_selector()? { - Call::Name => Self::name(input, target_gas), - Call::Symbol => Self::symbol(input, target_gas), - Call::Decimals => Self::decimals(input, target_gas), - Call::TotalSupply => Self::total_supply(state, input, target_gas), - Call::BalanceOf => Self::balance_of(state, input, target_gas), - Call::Allowance => Self::allowance(state, input, target_gas), - Call::Approve => Self::approve(state, input, target_gas, context), - Call::Transfer => Self::transfer(state, input, target_gas, context), - Call::TransferFrom => Self::transfer_from(state, input, target_gas, context), + let selector = match input.read_selector::() { + Ok(v) => v, + Err(e) => { + return Err(PrecompileFailure::Error { exit_status: e }); + } + }; + + match &selector { + Call::Name => match Self::name(input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + Call::Symbol => match Self::symbol(input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + Call::Decimals => match Self::decimals(input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + Call::TotalSupply => match Self::total_supply(state, input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + + Call::BalanceOf => match Self::balance_of(state, input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + + Call::Allowance => match Self::allowance(state, input, target_gas) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + Call::Approve => match Self::approve(state, input, target_gas, context) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + Call::Transfer => match Self::transfer(state, input, target_gas, context) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + }, + + Call::TransferFrom => { + match Self::transfer_from(state, input, target_gas, context) { + Ok(v) => Ok(v), + Err(e) => Err(PrecompileFailure::Error { exit_status: e }), + } + } } } } diff --git a/src/components/contracts/modules/evm/precompile/frc20/src/tests.rs b/src/components/contracts/modules/evm/precompile/frc20/src/tests.rs index 4dec6e2bb..32cea5b34 100644 --- a/src/components/contracts/modules/evm/precompile/frc20/src/tests.rs +++ b/src/components/contracts/modules/evm/precompile/frc20/src/tests.rs @@ -23,7 +23,9 @@ fn selector_less_than_four_bytes() { }, &BASE_APP.lock().unwrap().deliver_state, ), - Err(error("tried to parse selector out of bounds")) + Err(PrecompileFailure::Error { + exit_status: error("tried to parse selector out of bounds") + }) ); } @@ -42,7 +44,9 @@ fn no_selector_exists_but_length_is_right() { }, &BASE_APP.lock().unwrap().deliver_state, ), - Err(error("unknown selector")) + Err(PrecompileFailure::Error { + exit_status: error("unknown selector") + }) ); } diff --git a/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml b/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml index 670dcf35b..3a4fc5eaa 100644 --- a/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/modexp/Cargo.toml @@ -9,7 +9,7 @@ description = "MODEXP precompiles for EVM module." readme = "README.md" [dependencies] -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } module-evm = { path = "../../../../modules/evm"} num = { version = "0.3", features = ["alloc"] } diff --git a/src/components/contracts/modules/evm/precompile/modexp/src/lib.rs b/src/components/contracts/modules/evm/precompile/modexp/src/lib.rs index 21dbd2a92..a7b696930 100644 --- a/src/components/contracts/modules/evm/precompile/modexp/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/modexp/src/lib.rs @@ -20,7 +20,11 @@ mod tests; use core::cmp::max; use core::ops::BitAnd; -use evm::{executor::PrecompileOutput, Context, ExitError, ExitSucceed}; +use evm::{ + executor::stack::{PrecompileFailure, PrecompileOutput}, + Context, ExitError, ExitSucceed, +}; +use module_evm::precompile::PrecompileResult; use module_evm::precompile::{FinState, Precompile, PrecompileId}; use num::{BigUint, FromPrimitive, One, ToPrimitive, Zero}; use std::cmp::Ordering; @@ -104,11 +108,13 @@ impl Precompile for Modexp { target_gas: Option, _context: &Context, _state: &FinState, - ) -> core::result::Result { + ) -> PrecompileResult { if input.len() < 96 { - return Err(ExitError::Other( - "input must contain at least 96 bytes".into(), - )); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "input must contain at least 96 bytes".into(), + ), + }); }; // reasonable assumption: this must fit within the Ethereum EVM's max stack size @@ -118,23 +124,29 @@ impl Precompile for Modexp { buf.copy_from_slice(&input[0..32]); let base_len_big = BigUint::from_bytes_be(&buf); if base_len_big > max_size_big { - return Err(ExitError::Other("unreasonably large base length".into())); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("unreasonably large base length".into()), + }); } buf.copy_from_slice(&input[32..64]); let exp_len_big = BigUint::from_bytes_be(&buf); if exp_len_big > max_size_big { - return Err(ExitError::Other( - "unreasonably large exponent length".into(), - )); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "unreasonably large exponent length".into(), + ), + }); } buf.copy_from_slice(&input[64..96]); let mod_len_big = BigUint::from_bytes_be(&buf); if mod_len_big > max_size_big { - return Err(ExitError::Other( - "unreasonably large exponent length".into(), - )); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other( + "unreasonably large exponent length".into(), + ), + }); } // bounds check handled above @@ -145,7 +157,9 @@ impl Precompile for Modexp { // input length should be at least 96 + user-specified length of base + exp + mod let total_len = base_len + exp_len + mod_len + 96; if input.len() < total_len { - return Err(ExitError::Other("insufficient input size".into())); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("insufficient input size".into()), + }); } // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to handle empty base first. @@ -170,7 +184,9 @@ impl Precompile for Modexp { ); if let Some(gas_left) = target_gas { if gas_left < gas_cost { - return Err(ExitError::OutOfGas); + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); } }; @@ -190,7 +206,9 @@ impl Precompile for Modexp { // always true except in the case of zero-length modulus, which leads to // output of length and value 1. match mod_len.cmp(&bytes.len()) { - Ordering::Less => Err(ExitError::Other("failed".into())), + Ordering::Less => Err(PrecompileFailure::Error { + exit_status: ExitError::Other("failed".into()), + }), Ordering::Equal => Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, cost: gas_cost, diff --git a/src/components/contracts/modules/evm/precompile/modexp/src/tests.rs b/src/components/contracts/modules/evm/precompile/modexp/src/tests.rs index f0b79006c..940e8ff4d 100644 --- a/src/components/contracts/modules/evm/precompile/modexp/src/tests.rs +++ b/src/components/contracts/modules/evm/precompile/modexp/src/tests.rs @@ -32,7 +32,11 @@ fn test_empty_input() -> std::result::Result<(), ExitError> { Err(e) => { assert_eq!( e, - ExitError::Other("input must contain at least 96 bytes".into()) + PrecompileFailure::Error { + exit_status: ExitError::Other( + "input must contain at least 96 bytes".into() + ) + } ); Ok(()) } @@ -66,7 +70,12 @@ fn test_insufficient_input() -> std::result::Result<(), ExitError> { panic!("Test not expected to pass"); } Err(e) => { - assert_eq!(e, ExitError::Other("insufficient input size".into())); + assert_eq!( + e, + PrecompileFailure::Error { + exit_status: ExitError::Other("insufficient input size".into()) + } + ); Ok(()) } } @@ -99,7 +108,14 @@ fn test_excessive_input() -> std::result::Result<(), ExitError> { panic!("Test not expected to pass"); } Err(e) => { - assert_eq!(e, ExitError::Other("unreasonably large base length".into())); + assert_eq!( + e, + PrecompileFailure::Error { + exit_status: ExitError::Other( + "unreasonably large base length".into() + ) + } + ); Ok(()) } } diff --git a/src/components/contracts/modules/evm/precompile/runtime/Cargo.toml b/src/components/contracts/modules/evm/precompile/runtime/Cargo.toml new file mode 100644 index 000000000..ed6054556 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/runtime/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "evm-precompile-runtime" +version = "0.1.0" +authors = ["FindoraNetwork"] +edition = "2021" +homepage = "https://findora.org/technology" +repository = "https://github.com/findoranetwork/platform/" +description = "MODEXP precompiles for EVM module." +readme = "README.md" + + + + + + + +[dependencies] +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } +module-evm = { path = "../../../../modules/evm"} +ethereum-types = "0.12" +evm-precompile-basic = { default-features = false, path = "../basic" } +evm-precompile-sha3fips = { default-features = false, path = "../sha3fips" } +evm-precompile-modexp = { default-features = false, path = "../modexp" } +storage = { git = "https://github.com/FindoraNetwork/storage.git", tag = "v0.1.4" } +fp-core = { path = "../../../../primitives/core" } +parking_lot = "0.11.1" \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/runtime/src/lib.rs b/src/components/contracts/modules/evm/precompile/runtime/src/lib.rs new file mode 100644 index 000000000..81e0dc2c7 --- /dev/null +++ b/src/components/contracts/modules/evm/precompile/runtime/src/lib.rs @@ -0,0 +1,126 @@ +use ethereum_types::H160; +use evm::executor::stack::PrecompileSet; +use evm::Context; +use module_evm::precompile::{Precompile, PrecompileResult}; +use std::{env::temp_dir, marker::PhantomData, path::PathBuf, time::SystemTime}; + +use evm_precompile_basic::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use evm_precompile_modexp::Modexp; +use evm_precompile_sha3fips::Sha3FIPS256; +use fp_core::context::Context as Context2; +use module_evm::Config; +use parking_lot::RwLock; +use std::path::Path; +use std::sync::Arc; +use storage::{ + db::{FinDB, RocksDB}, + state::ChainState, +}; + +pub struct FindoraPrecompiles(PhantomData); + +impl FindoraPrecompiles +where + R: Config, +{ + pub fn new() -> Self { + Self(Default::default()) + } + pub fn used_addresses() -> std::vec::Vec { + std::vec![1, 2, 3, 4, 5, 1024, 1025] + .into_iter() + .map(|x| hash(x)) + .collect() + } +} + +pub fn create_temp_db_path() -> PathBuf { + let time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let mut path = temp_dir(); + path.push(format!("temp-findora-db–{}", time)); + path +} + +impl PrecompileSet for FindoraPrecompiles +where + R: Config, +{ + fn execute( + &self, + address: H160, + input: &[u8], + target_gas: Option, + context: &Context, + _is_static: bool, + ) -> Option { + let _base_dir = create_temp_db_path(); + let basedir = _base_dir.as_path(); + let fdb_path = basedir.join("state.db"); + let fdb = FinDB::open(fdb_path).unwrap(); + let chain_state = Arc::new(RwLock::new(ChainState::new( + fdb, + "findora_db".to_owned(), + 4 * 60 * 24 * 90, + ))); + + let rdb_path = basedir.join("history.db"); + let rdb = RocksDB::open(rdb_path.as_path()).unwrap(); + let chain_db = + Arc::new(RwLock::new(ChainState::new(rdb, "rocks_db".to_owned(), 0))); + + let deliver_state: Context2 = Context2::new(chain_state, chain_db); + + match address { + // Ethereum precompiles : + a if a == hash(1) => Some(ECRecover::execute( + input, + target_gas, + context, + &deliver_state, + )), + a if a == hash(2) => { + Some(Sha256::execute(input, target_gas, context, &deliver_state)) + } + a if a == hash(3) => Some(Ripemd160::execute( + input, + target_gas, + context, + &deliver_state, + )), + a if a == hash(4) => Some(Identity::execute( + input, + target_gas, + context, + &deliver_state, + )), + a if a == hash(5) => { + Some(Modexp::execute(input, target_gas, context, &deliver_state)) + } + // Non-Frontier specific nor Ethereum precompiles : + a if a == hash(1024) => Some(Sha3FIPS256::execute( + input, + target_gas, + context, + &deliver_state, + )), + a if a == hash(1025) => Some(ECRecoverPublicKey::execute( + input, + target_gas, + context, + &deliver_state, + )), + _ => None, + } + } + + fn is_precompile(&self, address: H160) -> bool { + Self::used_addresses().contains(&address) + } +} + +fn hash(a: u64) -> H160 { + H160::from_low_u64_be(a) +} diff --git a/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml b/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml index 72e623608..42448d64f 100644 --- a/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/sha3fips/Cargo.toml @@ -9,6 +9,6 @@ description = "SHA3 FIPS202 precompiles for EVM module." readme = "README.md" [dependencies] -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } module-evm = { path = "../../../../modules/evm"} tiny-keccak = { version = "2.0", features = ["fips202"] } diff --git a/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs b/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs index 83a467ecf..51d3cb9a6 100644 --- a/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/sha3fips/src/lib.rs @@ -15,7 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +use evm::executor::stack::PrecompileFailure; use evm::{ExitError, ExitSucceed}; +use module_evm::precompile::PrecompileResult; use module_evm::precompile::{LinearCostPrecompile, PrecompileId}; use tiny_keccak::Hasher; @@ -34,7 +36,7 @@ impl LinearCostPrecompile for Sha3FIPS256 { fn execute( input: &[u8], _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { let mut output = [0; 32]; let mut sha3 = tiny_keccak::Sha3::v256(); sha3.update(input); @@ -58,7 +60,7 @@ impl LinearCostPrecompile for Sha3FIPS512 { fn execute( input: &[u8], _: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError> { + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure> { let mut output = [0; 64]; let mut sha3 = tiny_keccak::Sha3::v512(); sha3.update(input); diff --git a/src/components/contracts/modules/evm/precompile/utils/Cargo.toml b/src/components/contracts/modules/evm/precompile/utils/Cargo.toml index 95d5c4852..8e01483b6 100644 --- a/src/components/contracts/modules/evm/precompile/utils/Cargo.toml +++ b/src/components/contracts/modules/evm/precompile/utils/Cargo.toml @@ -6,10 +6,11 @@ edition = "2021" description = "Utils to write EVM precompiles." [dependencies] -ethereum = { version = "0.8.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } log = "0.4" num_enum = { version = "0.5.3", default-features = false } precompile-utils-macro = { path = "macro" } sha3 = { version = "0.8", default-features = false } +module-evm = { path = "../../../../modules/evm"} \ No newline at end of file diff --git a/src/components/contracts/modules/evm/precompile/utils/src/lib.rs b/src/components/contracts/modules/evm/precompile/utils/src/lib.rs index 356688be1..98805d71b 100644 --- a/src/components/contracts/modules/evm/precompile/utils/src/lib.rs +++ b/src/components/contracts/modules/evm/precompile/utils/src/lib.rs @@ -19,8 +19,9 @@ mod data; #[cfg(test)] mod tests; -use ethereum::Log; +// use ethereum::Log; use ethereum_types::{H160, H256}; +use evm::backend::Log; use evm::ExitError; pub use data::{Address, EvmData, EvmDataReader, EvmDataWriter}; diff --git a/src/components/contracts/modules/evm/src/impls.rs b/src/components/contracts/modules/evm/src/impls.rs index d5ebccbfa..de3b17d87 100644 --- a/src/components/contracts/modules/evm/src/impls.rs +++ b/src/components/contracts/modules/evm/src/impls.rs @@ -105,4 +105,11 @@ impl OnChargeEVMTransaction for App { C::AccountAsset::refund(ctx, &account_id, already_withdrawn)?; C::AccountAsset::burn(ctx, &account_id, corrected_fee) } + + fn pay_priority_fee(ctx: &Context, tip: U256) -> Result<()> { + let author = App::::find_proposer(ctx); + let account_id = C::AddressMapping::convert_to_account_id(author); + + C::AccountAsset::income(ctx, &account_id, tip) + } } diff --git a/src/components/contracts/modules/evm/src/lib.rs b/src/components/contracts/modules/evm/src/lib.rs index 11d0f597f..5011db828 100644 --- a/src/components/contracts/modules/evm/src/lib.rs +++ b/src/components/contracts/modules/evm/src/lib.rs @@ -27,8 +27,9 @@ use fp_traits::{ use fp_evm::TransactionStatus; -use ethereum::{Log, Receipt, TransactionAction, TransactionSignature, TransactionV0}; +use ethereum::{Log, Receipt, TransactionAction, TransactionSignature, TransactionV2}; +use evm::executor::stack::PrecompileSet as EvmPrecompileSet; use fp_types::{ actions::{evm::Action, xhub::NonConfidentialOutput}, crypto::{Address, HA160}, @@ -60,6 +61,8 @@ pub trait Config { type FeeCalculator: FeeCalculator; /// Precompiles associated with this EVM engine. type Precompiles: PrecompileSet; + type PrecompilesType: EvmPrecompileSet; + type PrecompilesValue: Get; } pub mod storage { @@ -98,7 +101,7 @@ impl App { _lowlevel: Vec, transaction_index: u32, transaction_hash: H256, - ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + ) -> Result<(TransactionV2, TransactionStatus, Receipt)> { let function = self.contracts.bridge.function("withdrawFRC20").c(d!())?; let asset = Token::FixedBytes(Vec::from(_asset)); @@ -159,7 +162,7 @@ impl App { _lowlevel: Vec, transaction_index: u32, transaction_hash: H256, - ) -> Result<(TransactionV0, TransactionStatus, Receipt)> { + ) -> Result<(TransactionV2, TransactionStatus, Receipt)> { let function = self.contracts.bridge.function("withdrawFRA").c(d!())?; let bytes: &[u8] = _from.as_ref(); @@ -245,13 +248,13 @@ impl App { from: H160, to: H160, logs: Vec, - ) -> (TransactionV0, TransactionStatus, Receipt) { + ) -> (TransactionV2, TransactionStatus, Receipt) { let signature_fake = H256([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, ]); - let tx = TransactionV0 { + let tx = TransactionV2::Legacy(ethereum::LegacyTransaction { nonce: U256::zero(), gas_price, gas_limit, @@ -260,7 +263,7 @@ impl App { .unwrap(), input, action, - }; + }); let mut logs_bloom = Bloom::default(); Self::logs_bloom(&logs, &mut logs_bloom); diff --git a/src/components/contracts/modules/evm/src/precompile.rs b/src/components/contracts/modules/evm/src/precompile.rs index df5a0101a..828a067be 100644 --- a/src/components/contracts/modules/evm/src/precompile.rs +++ b/src/components/contracts/modules/evm/src/precompile.rs @@ -1,10 +1,15 @@ use crate::runtime::stack::FindoraStackState; use ethereum_types::H160; -use evm::{executor::PrecompileOutput, Context, ExitError, ExitSucceed}; +use evm::{ + executor::stack::{PrecompileFailure, PrecompileOutput}, + Context, ExitError, ExitSucceed, +}; use impl_trait_for_tuples::impl_for_tuples; pub use fp_core::context::Context as FinState; +pub type PrecompileResult = Result; + /// Custom precompiles to be used by EVM engine. pub trait PrecompileSet { /// Try to execute the code address as precompile. If the code address is not @@ -19,7 +24,7 @@ pub trait PrecompileSet { context: &Context, state: &mut FindoraStackState<'context, 'vicinity, 'config, T>, is_static: bool, - ) -> Option>; + ) -> Option; } /// One single precompile used by EVM engine. @@ -32,7 +37,7 @@ pub trait Precompile { target_gas: Option, context: &Context, state: &FinState, - ) -> core::result::Result; + ) -> PrecompileResult; } /// precompile contract address is generated by PrecompileId. @@ -55,7 +60,7 @@ impl PrecompileSet for Tuple { context: &Context, state: &mut FindoraStackState<'context, 'vicinity, 'config, T>, _is_static: bool, - ) -> Option> { + ) -> Option { for_tuples!( #( if address == H160::from_low_u64_be(Tuple::contract_id()) { return Some(Tuple::execute(input, target_gas, context, state.ctx)) @@ -73,7 +78,7 @@ pub trait LinearCostPrecompile { fn execute( input: &[u8], cost: u64, - ) -> core::result::Result<(ExitSucceed, Vec), ExitError>; + ) -> core::result::Result<(ExitSucceed, Vec), PrecompileFailure>; } impl Precompile for T { @@ -82,7 +87,7 @@ impl Precompile for T { target_gas: Option, _: &Context, _: &FinState, - ) -> core::result::Result { + ) -> PrecompileResult { let cost = ensure_linear_cost(target_gas, input.len() as u64, T::BASE, T::WORD)?; let (exit_status, output) = T::execute(input, cost)?; @@ -101,17 +106,22 @@ fn ensure_linear_cost( len: u64, base: u64, word: u64, -) -> Result { +) -> Result { let cost = base - .checked_add( - word.checked_mul(len.saturating_add(31) / 32) - .ok_or(ExitError::OutOfGas)?, - ) - .ok_or(ExitError::OutOfGas)?; + .checked_add(word.checked_mul(len.saturating_add(31) / 32).ok_or( + PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }, + )?) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; if let Some(target_gas) = target_gas { if cost > target_gas { - return Err(ExitError::OutOfGas); + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); } } diff --git a/src/components/contracts/modules/evm/src/runtime/runner.rs b/src/components/contracts/modules/evm/src/runtime/runner.rs index 32021d144..eff1bf2e4 100644 --- a/src/components/contracts/modules/evm/src/runtime/runner.rs +++ b/src/components/contracts/modules/evm/src/runtime/runner.rs @@ -1,11 +1,11 @@ use super::stack::FindoraStackState; -use crate::precompile::PrecompileSet; use crate::{App, Config}; use ethereum_types::{H160, H256, U256}; use evm::{ - executor::{StackExecutor, StackSubstateMetadata}, + executor::stack::{StackExecutor, StackSubstateMetadata}, ExitReason, }; +use fp_core::macros::Get; use fp_core::{context::Context, ensure}; use fp_evm::*; use fp_traits::evm::{FeeCalculator, OnChargeEVMTransaction}; @@ -22,7 +22,7 @@ pub struct ActionRunner { impl ActionRunner { #[allow(clippy::too_many_arguments)] /// Execute an EVM operation. - pub fn execute<'config, F, R>( + pub fn execute<'config, 'precompiles, F, R>( ctx: &Context, source: H160, value: U256, @@ -30,11 +30,17 @@ impl ActionRunner { gas_price: Option, nonce: Option, config: &'config evm::Config, + precompiles: &'precompiles C::PrecompilesType, f: F, ) -> Result> where F: FnOnce( - &mut StackExecutor<'config, FindoraStackState<'_, '_, 'config, C>>, + &mut StackExecutor< + 'config, + 'precompiles, + FindoraStackState<'_, '_, 'config, C>, + C::PrecompilesType, + >, ) -> (ExitReason, R), { // Gas price check is skipped when performing a gas estimation. @@ -57,7 +63,7 @@ impl ActionRunner { let metadata = StackSubstateMetadata::new(gas_limit, config); let state = FindoraStackState::new(ctx, &vicinity, metadata); let mut executor = - StackExecutor::new_with_precompile(state, config, C::Precompiles::execute); + StackExecutor::new_with_precompiles(state, config, precompiles); let total_fee = gas_price .checked_mul(U256::from(gas_limit)) @@ -135,6 +141,170 @@ impl ActionRunner { }) } + // eip1559 support + pub fn execute_eip1559<'config, 'precompiles, F, R>( + ctx: &Context, + source: H160, + value: U256, + gas_limit: u64, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, + nonce: Option, + config: &'config evm::Config, + precompiles: &'precompiles C::PrecompilesType, + f: F, + ) -> Result> + where + F: FnOnce( + &mut StackExecutor< + 'config, + 'precompiles, + FindoraStackState<'_, '_, 'config, C>, + C::PrecompilesType, + >, + ) -> (ExitReason, R), + { + let base_fee = C::FeeCalculator::min_gas_price(); + // Gas price check is skipped when performing a gas estimation. + let max_fee_per_gas = match max_fee_per_gas { + Some(max_fee_per_gas) => { + ensure!(max_fee_per_gas >= base_fee, "GasPriceTooLow"); + max_fee_per_gas + } + None => Default::default(), + }; + + let vicinity = Vicinity { + gas_price: max_fee_per_gas, + origin: source, + }; + + let metadata = StackSubstateMetadata::new(gas_limit, config); + let state = FindoraStackState::::new(ctx, &vicinity, metadata); + let mut executor = + StackExecutor::new_with_precompiles(state, config, precompiles); + + // After eip-1559 we make sure the account can pay both the evm execution and priority fees. + let max_base_fee = max_fee_per_gas + .checked_mul(U256::from(gas_limit)) + .ok_or(eg!("FeeOverflow"))?; + let max_priority_fee = if let Some(max_priority_fee) = max_priority_fee_per_gas { + max_priority_fee + .checked_mul(U256::from(gas_limit)) + .ok_or(eg!("FeeOverflow"))? + } else { + U256::zero() + }; + + let total_fee = max_base_fee + .checked_add(max_priority_fee) + .ok_or(eg!("FeeOverflow"))?; + + let total_payment = + value.checked_add(total_fee).ok_or(eg!("PaymentOverflow"))?; + let source_account = App::::account_basic(ctx, &source); + ensure!(source_account.balance >= total_payment, "BalanceLow"); + + if let Some(nonce) = nonce { + ensure!( + source_account.nonce == nonce, + format!( + "InvalidNonce, expected: {}, actual: {}", + source_account.nonce, nonce + ) + ); + } + // Deduct fee from the `source` account. + App::::withdraw_fee(ctx, &source, total_fee)?; + + // Execute the EVM call. + let (reason, retv) = f(&mut executor); + + let used_gas = U256::from(executor.used_gas()); + let (actual_fee, actual_priority_fee) = + if let Some(max_priority_fee) = max_priority_fee_per_gas { + let actual_priority_fee = max_priority_fee + .checked_mul(used_gas) + .ok_or(eg!("FeeOverflow"))?; + let actual_fee = executor + .fee(base_fee) + .checked_add(actual_priority_fee) + .unwrap_or_else(U256::max_value); + (actual_fee, Some(actual_priority_fee)) + } else { + (executor.fee(base_fee), None) + }; + log::debug!( + target: "evm", + "Execution {:?} [source: {:?}, value: {}, gas_limit: {}, actual_fee: {}]", + reason, + source, + value, + gas_limit, + actual_fee + ); + // The difference between initially withdrawn and the actual cost is refunded. + // + // Considered the following request: + // +-----------+---------+--------------+ + // | Gas_limit | Max_Fee | Max_Priority | + // +-----------+---------+--------------+ + // | 20 | 10 | 6 | + // +-----------+---------+--------------+ + // + // And execution: + // +----------+----------+ + // | Gas_used | Base_Fee | + // +----------+----------+ + // | 5 | 2 | + // +----------+----------+ + // + // Initially withdrawn (10 + 6) * 20 = 320. + // Actual cost (2 + 6) * 5 = 40. + // Refunded 320 - 40 = 280. + // Tip 5 * 6 = 30. + // Burned 320 - (280 + 30) = 10. Which is equivalent to gas_used * base_fee. + App::::correct_and_deposit_fee(ctx, &source, actual_fee, total_fee)?; + if let Some(actual_priority_fee) = actual_priority_fee { + App::::pay_priority_fee(ctx, actual_priority_fee)?; + } + + let state = executor.into_state(); + + for address in state.substate.deletes { + log::debug!( + target: "evm", + "Deleting account at {:?}", + address + ); + App::::remove_account(ctx, &address.into()) + } + + for log in &state.substate.logs { + log::trace!( + target: "evm", + "Inserting log for {:?}, topics ({}) {:?}, data ({}): {:?}]", + log.address, + log.topics.len(), + log.topics, + log.data.len(), + log.data + ); + // Pallet::::deposit_event(Event::::Log(Log { + // address: log.address, + // topics: log.topics.clone(), + // data: log.data.clone(), + // })); + } + + Ok(ExecutionInfo { + value: retv, + exit_reason: reason, + used_gas, + logs: state.substate.logs, + }) + } + pub fn inital_system_contract( ctx: &Context, bytecode: Vec, @@ -151,11 +321,19 @@ impl ActionRunner { let metadata = StackSubstateMetadata::new(gas_limit, &config); let state = FindoraStackState::::new(ctx, &vicinity, metadata); + let precompiles = C::PrecompilesValue::get(); let mut executor = - StackExecutor::new_with_precompile(state, &config, C::Precompiles::execute); + StackExecutor::new_with_precompiles(state, &config, &precompiles); - let result = - executor.transact_create2(source, U256::zero(), bytecode, salt, gas_limit); + let access_list = Vec::new(); + let result = executor.transact_create2( + source, + U256::zero(), + bytecode, + salt, + gas_limit, + access_list, + ); let state = executor.into_state(); @@ -204,11 +382,13 @@ impl ActionRunner { let metadata = StackSubstateMetadata::new(gas_limit, &config); let state = FindoraStackState::::new(ctx, &vicinity, metadata); + let precompiles = C::PrecompilesValue::get(); let mut executor = - StackExecutor::new_with_precompile(state, &config, C::Precompiles::execute); + StackExecutor::new_with_precompiles(state, &config, &precompiles); + let access_list = Vec::new(); let (result, data) = - executor.transact_call(source, target, value, input, gas_limit); + executor.transact_call(source, target, value, input, gas_limit, access_list); let gas_used = U256::from(executor.used_gas()); let state = executor.into_state(); @@ -249,6 +429,8 @@ impl ActionRunner { impl Runner for ActionRunner { fn call(ctx: &Context, args: Call, config: &evm::Config) -> Result { + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); Self::execute( ctx, args.source, @@ -257,6 +439,7 @@ impl Runner for ActionRunner { args.gas_price, args.nonce, config, + &precompiles, |executor| { executor.transact_call( args.source, @@ -264,12 +447,45 @@ impl Runner for ActionRunner { args.value, args.input, args.gas_limit, + access_list, + ) + }, + ) + } + + fn call_eip1559( + ctx: &Context, + args: CallEip1559, + config: &evm::Config, + ) -> Result { + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); + Self::execute_eip1559( + ctx, + args.source, + args.value, + args.gas_limit, + args.max_fee_per_gas, + args.max_priority_fee_per_gas, + args.nonce, + config, + &precompiles, + |executor| { + executor.transact_call( + args.source, + args.target, + args.value, + args.input, + args.gas_limit, + access_list, ) }, ) } fn create(ctx: &Context, args: Create, config: &evm::Config) -> Result { + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); Self::execute( ctx, args.source, @@ -278,6 +494,7 @@ impl Runner for ActionRunner { args.gas_price, args.nonce, config, + &precompiles, |executor| { let address = executor.create_address(evm::CreateScheme::Legacy { caller: args.source, @@ -288,6 +505,7 @@ impl Runner for ActionRunner { args.value, args.init, args.gas_limit, + access_list, ), address, ) @@ -301,6 +519,8 @@ impl Runner for ActionRunner { config: &evm::Config, ) -> Result { let code_hash = H256::from_slice(Keccak256::digest(&args.init).as_slice()); + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); Self::execute( ctx, args.source, @@ -309,6 +529,81 @@ impl Runner for ActionRunner { args.gas_price, args.nonce, config, + &precompiles, + |executor| { + let address = executor.create_address(evm::CreateScheme::Create2 { + caller: args.source, + code_hash, + salt: args.salt, + }); + ( + executor.transact_create2( + args.source, + args.value, + args.init, + args.salt, + args.gas_limit, + access_list, + ), + address, + ) + }, + ) + } + + fn create_eip1559( + ctx: &Context, + args: CreateEip1559, + config: &evm::Config, + ) -> Result { + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); + Self::execute_eip1559( + ctx, + args.source, + args.value, + args.gas_limit, + args.max_fee_per_gas, + args.max_priority_fee_per_gas, + args.nonce, + config, + &precompiles, + |executor| { + let address = executor.create_address(evm::CreateScheme::Legacy { + caller: args.source, + }); + ( + executor.transact_create( + args.source, + args.value, + args.init, + args.gas_limit, + access_list, + ), + address, + ) + }, + ) + } + + fn create2_eip1559( + ctx: &Context, + args: Create2Eip1559, + config: &evm::Config, + ) -> Result { + let code_hash = H256::from_slice(Keccak256::digest(&args.init).as_slice()); + let precompiles = C::PrecompilesValue::get(); + let access_list = Vec::new(); + Self::execute_eip1559( + ctx, + args.source, + args.value, + args.gas_limit, + args.max_fee_per_gas, + args.max_priority_fee_per_gas, + args.nonce, + config, + &precompiles, |executor| { let address = executor.create_address(evm::CreateScheme::Create2 { caller: args.source, @@ -322,6 +617,7 @@ impl Runner for ActionRunner { args.init, args.salt, args.gas_limit, + access_list, ), address, ) diff --git a/src/components/contracts/modules/evm/src/runtime/stack.rs b/src/components/contracts/modules/evm/src/runtime/stack.rs index 76d4c7af7..40f4749ca 100644 --- a/src/components/contracts/modules/evm/src/runtime/stack.rs +++ b/src/components/contracts/modules/evm/src/runtime/stack.rs @@ -3,12 +3,13 @@ use config::abci::global_cfg::CFG; use ethereum_types::{H160, H256, U256}; use evm::{ backend::Backend, - executor::{StackState, StackSubstateMetadata}, + executor::stack::{Accessed, StackState, StackSubstateMetadata}, ExitError, Transfer, }; use fp_core::{context::Context, macros::Get}; use fp_evm::{Log, Vicinity}; use fp_storage::{BorrowMut, DerefMut}; +use fp_traits::evm::FeeCalculator; use fp_traits::{account::AccountAsset, evm::BlockHashMapping}; use fp_utils::timestamp_converter; use log::info; @@ -111,6 +112,19 @@ impl<'context, 'config> FindoraStackSubstate<'context, 'config> { data, }); } + + pub fn recursive_is_cold bool>(&self, f: &F) -> bool { + let local_is_accessed = + self.metadata.accessed().as_ref().map(f).unwrap_or(false); + if local_is_accessed { + false + } else { + self.parent + .as_ref() + .map(|p| p.recursive_is_cold(f)) + .unwrap_or(true) + } + } } /// Findora backend for EVM. @@ -212,6 +226,10 @@ impl<'context, 'vicinity, 'config, C: Config> Backend fn original_storage(&self, _address: H160, _index: H256) -> Option { None } + + fn block_base_fee_per_gas(&self) -> U256 { + C::FeeCalculator::min_gas_price() + } } impl<'context, 'vicinity, 'config, C: Config> StackState<'config> @@ -353,4 +371,15 @@ impl<'context, 'vicinity, 'config, C: Config> StackState<'config> // only empty and non-empty accounts. This avoids many of the // subtle issues in EIP-161. } + + fn is_cold(&self, address: H160) -> bool { + self.substate + .recursive_is_cold(&|a| a.accessed_addresses.contains(&address)) + } + + fn is_storage_cold(&self, address: H160, key: H256) -> bool { + self.substate.recursive_is_cold(&|a: &Accessed| { + a.accessed_storage.contains(&(address, key)) + }) + } } diff --git a/src/components/contracts/modules/evm/test-vector-support/Cargo.toml b/src/components/contracts/modules/evm/test-vector-support/Cargo.toml index 5e124ab83..ec34d9c8f 100644 --- a/src/components/contracts/modules/evm/test-vector-support/Cargo.toml +++ b/src/components/contracts/modules/evm/test-vector-support/Cargo.toml @@ -10,6 +10,6 @@ repository = "https://github.com/findoranetwork/platform/" hex = { version = "0.4.2"} serde = { version = "1.0.124", features = ["derive"] } serde_json = { version = "1.0"} -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } module-evm = { path = "../../../modules/evm"} fp-mocks = { path = "../../../primitives/mocks" } diff --git a/src/components/contracts/modules/evm/tests/evm_integration.rs b/src/components/contracts/modules/evm/tests/evm_integration.rs index 4c499bb18..a8d0688b3 100644 --- a/src/components/contracts/modules/evm/tests/evm_integration.rs +++ b/src/components/contracts/modules/evm/tests/evm_integration.rs @@ -149,7 +149,8 @@ fn test_deploy_deliver_tx() -> (H160, ethabi::Contract) { } fn test_deploy_commit(contract_address: H160) { - let _ = BASE_APP.lock().unwrap().commit(&RequestCommit::new()); + // let _ = + BASE_APP.lock().unwrap().commit(&RequestCommit::new()); let ctx = BASE_APP .lock() diff --git a/src/components/contracts/modules/template/tests/template_integration.rs b/src/components/contracts/modules/template/tests/template_integration.rs index 85c5f1587..00144a6f3 100644 --- a/src/components/contracts/modules/template/tests/template_integration.rs +++ b/src/components/contracts/modules/template/tests/template_integration.rs @@ -39,7 +39,8 @@ fn test_abci_info() { fn test_abci_init_chain() { let mut req = RequestInitChain::default(); req.chain_id = "findora test".to_string(); - let _ = BASE_APP.lock().unwrap().init_chain(&req); + // let _ = + BASE_APP.lock().unwrap().init_chain(&req); assert_eq!( &BASE_APP @@ -91,11 +92,14 @@ fn test_abci_begin_block() { let mut header = Header::default(); header.height = 1; req.set_header(header.clone()); - let _ = BASE_APP.lock().unwrap().begin_block(&req); - let _ = BASE_APP.lock().unwrap().commit(&RequestCommit::new()); + // let _ = + BASE_APP.lock().unwrap().begin_block(&req); + // let _ = + BASE_APP.lock().unwrap().commit(&RequestCommit::new()); header.height = 2; req.set_header(header); - let _ = BASE_APP.lock().unwrap().begin_block(&req); + // let _ = + BASE_APP.lock().unwrap().begin_block(&req); } fn test_abci_deliver_tx() { @@ -126,11 +130,13 @@ fn test_abci_deliver_tx() { fn test_abci_end_block() { let mut req = RequestEndBlock::default(); req.height = 2; - let _ = BASE_APP.lock().unwrap().end_block(&req); + // let _ = + BASE_APP.lock().unwrap().end_block(&req); } fn test_abci_commit() { - let _ = BASE_APP.lock().unwrap().commit(&RequestCommit::new()); + // let _ = + BASE_APP.lock().unwrap().commit(&RequestCommit::new()); assert_eq!( BASE_APP .lock() diff --git a/src/components/contracts/primitives/core/Cargo.toml b/src/components/contracts/primitives/core/Cargo.toml index d7a912438..2fa33df0b 100644 --- a/src/components/contracts/primitives/core/Cargo.toml +++ b/src/components/contracts/primitives/core/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" [dependencies] abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2", optional = true } -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } impl-trait-for-tuples = "0.2" parking_lot = "0.11.1" primitive-types = { version = "0.10.1", default-features = false, features = ["rlp", "byteorder", "serde"] } diff --git a/src/components/contracts/primitives/evm/Cargo.toml b/src/components/contracts/primitives/evm/Cargo.toml index 4b50f4983..8feaea68b 100644 --- a/src/components/contracts/primitives/evm/Cargo.toml +++ b/src/components/contracts/primitives/evm/Cargo.toml @@ -9,9 +9,9 @@ description = "Findora Primitive EVM abstractions types" readme = "README.md" [dependencies] -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } ruc = "1.0" serde = { version = "1.0.124", features = ["derive"] } diff --git a/src/components/contracts/primitives/evm/src/lib.rs b/src/components/contracts/primitives/evm/src/lib.rs index 2b972ea60..06cbcc9c3 100644 --- a/src/components/contracts/primitives/evm/src/lib.rs +++ b/src/components/contracts/primitives/evm/src/lib.rs @@ -4,7 +4,9 @@ use ethereum_types::{Bloom, H160, H256, U256}; use evm::ExitReason; use fp_core::context::Context; -use fp_types::actions::evm::{Call, Create, Create2}; +use fp_types::actions::evm::{ + Call, CallEip1559, Create, Create2, Create2Eip1559, CreateEip1559, +}; use ruc::*; use serde::{Deserialize, Serialize}; @@ -55,6 +57,24 @@ pub trait Runner { fn create2(ctx: &Context, args: Create2, config: &evm::Config) -> Result; + + fn call_eip1559( + ctx: &Context, + args: CallEip1559, + config: &evm::Config, + ) -> Result; + + fn create_eip1559( + ctx: &Context, + args: CreateEip1559, + config: &evm::Config, + ) -> Result; + + fn create2_eip1559( + ctx: &Context, + args: Create2Eip1559, + config: &evm::Config, + ) -> Result; } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/src/components/contracts/primitives/mocks/Cargo.toml b/src/components/contracts/primitives/mocks/Cargo.toml index 6526b2531..c67667ba7 100644 --- a/src/components/contracts/primitives/mocks/Cargo.toml +++ b/src/components/contracts/primitives/mocks/Cargo.toml @@ -10,7 +10,7 @@ description = "Findora primitive mock functions for testing" [dependencies] abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2" } baseapp = { path = "../../baseapp" } -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } lazy_static = "1.4.0" libsecp256k1 = { version = "0.5", features = ["static-context", "hmac"] } primitive-types = { version = "0.10.1", default-features = false, features = ["rlp", "byteorder", "serde"] } diff --git a/src/components/contracts/primitives/mocks/src/lib.rs b/src/components/contracts/primitives/mocks/src/lib.rs index 5730deb7b..5d6517491 100644 --- a/src/components/contracts/primitives/mocks/src/lib.rs +++ b/src/components/contracts/primitives/mocks/src/lib.rs @@ -7,7 +7,7 @@ pub use baseapp::{ }; pub use fp_types::{actions::Action, assemble::UncheckedTransaction}; -use ethereum::{TransactionAction, TransactionSignature, TransactionV0 as Transaction}; +use ethereum::{TransactionAction, TransactionSignature, TransactionV2 as Transaction}; use fp_traits::account::AccountAsset; use fp_traits::evm::{AddressMapping, EthereumAddressMapping}; use fp_types::crypto::{Address, MultiSignature}; @@ -148,7 +148,7 @@ impl UnsignedTransaction { ) .unwrap(); - Transaction { + Transaction::Legacy(ethereum::LegacyTransaction { nonce: self.nonce, gas_price: self.gas_price, gas_limit: self.gas_limit, @@ -156,6 +156,6 @@ impl UnsignedTransaction { value: self.value, input: self.input.clone(), signature: sig, - } + }) } } diff --git a/src/components/contracts/primitives/rpc-core/Cargo.toml b/src/components/contracts/primitives/rpc-core/Cargo.toml index 0dc5db345..f923fe562 100644 --- a/src/components/contracts/primitives/rpc-core/Cargo.toml +++ b/src/components/contracts/primitives/rpc-core/Cargo.toml @@ -9,6 +9,8 @@ description = "RPC traits of Ethereum." license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +rlp = "0.5" +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = "0.12" futures = "0.3.16" jsonrpc-core = "18.0" diff --git a/src/components/contracts/primitives/rpc-core/src/types/block.rs b/src/components/contracts/primitives/rpc-core/src/types/block.rs index 5786136da..855ffa17f 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/block.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/block.rs @@ -90,6 +90,8 @@ pub struct Block { pub transactions: BlockTransactions, /// Size in bytes pub size: Option, + /// Base Fee for post-EIP1559 blocks. + pub base_fee_per_gas: Option, } /// Block header representation. diff --git a/src/components/contracts/primitives/rpc-core/src/types/call_request.rs b/src/components/contracts/primitives/rpc-core/src/types/call_request.rs index 6ea0e8bba..10acdd9c5 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/call_request.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/call_request.rs @@ -31,6 +31,10 @@ pub struct CallRequest { pub to: Option, /// Gas Price pub gas_price: Option, + /// EIP-1559 Max base fee the caller is willing to pay + pub max_fee_per_gas: Option, + /// EIP-1559 Priority fee the caller is paying to the block author + pub max_priority_fee_per_gas: Option, /// Gas pub gas: Option, /// Value diff --git a/src/components/contracts/primitives/rpc-core/src/types/mod.rs b/src/components/contracts/primitives/rpc-core/src/types/mod.rs index daa204156..422f78580 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/mod.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/mod.rs @@ -56,5 +56,5 @@ pub use self::transaction::{ LocalTransactionStatus, PendingTransaction, PendingTransactions, RichRawTransaction, Transaction, }; -pub use self::transaction_request::TransactionRequest; +pub use self::transaction_request::{TransactionMessage, TransactionRequest}; pub use self::work::Work; diff --git a/src/components/contracts/primitives/rpc-core/src/types/receipt.rs b/src/components/contracts/primitives/rpc-core/src/types/receipt.rs index 5d1ed929e..846609176 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/receipt.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/receipt.rs @@ -54,4 +54,7 @@ pub struct Receipt { // NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it #[serde(skip_serializing_if = "Option::is_none", rename = "status")] pub status_code: Option, + + /// Effective gas price. Pre-eip1559 this is just the gasprice. Post-eip1559 this is base fee + priority fee. + pub effective_gas_price: U256, } diff --git a/src/components/contracts/primitives/rpc-core/src/types/transaction.rs b/src/components/contracts/primitives/rpc-core/src/types/transaction.rs index 0ff6822fc..592545956 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/transaction.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/transaction.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . use crate::types::Bytes; +use ethereum::{AccessListItem, TransactionV2}; use ethereum_types::{H160, H256, H512, U256, U64}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; @@ -46,7 +47,11 @@ pub struct Transaction { /// Transfered value pub value: U256, /// Gas Price - pub gas_price: U256, + pub gas_price: Option, + /// eip1559. Max BaseFeePerGas the user is willing to pay. + pub max_fee_per_gas: Option, + /// eip1559.The miner's tip. + pub max_priority_fee_per_gas: Option, /// Gas pub gas: U256, /// Data @@ -67,6 +72,67 @@ pub struct Transaction { pub r: U256, /// The S field of the signature. pub s: U256, + /// eip1559. Pre-pay to warm storage access. + pub access_list: Option>, +} + +impl From for Transaction { + fn from(transaction: TransactionV2) -> Self { + let serialized = rlp::encode(&transaction); + let hash = transaction.hash(); + let raw = Bytes(serialized.to_vec()); + match transaction { + TransactionV2::Legacy(t) => Transaction { + hash, + nonce: t.nonce, + block_hash: None, + block_number: None, + transaction_index: None, + from: H160::default(), + to: None, + value: t.value, + gas_price: Some(t.gas_price), + max_fee_per_gas: Some(t.gas_price), + max_priority_fee_per_gas: Some(t.gas_price), + gas: t.gas_limit, + input: Bytes(t.clone().input), + creates: None, + raw, + public_key: None, + chain_id: t.signature.chain_id().map(U64::from), + standard_v: U256::from(t.signature.standard_v()), + v: U256::from(t.signature.v()), + r: U256::from(t.signature.r().as_bytes()), + s: U256::from(t.signature.s().as_bytes()), + access_list: None, + }, + TransactionV2::EIP1559(t) => Transaction { + hash, + nonce: t.nonce, + block_hash: None, + block_number: None, + transaction_index: None, + from: H160::default(), + to: None, + value: t.value, + gas_price: None, + max_fee_per_gas: Some(t.max_fee_per_gas), + max_priority_fee_per_gas: Some(t.max_priority_fee_per_gas), + gas: t.gas_limit, + input: Bytes(t.clone().input), + creates: None, + raw, + public_key: None, + chain_id: Some(U64::from(t.chain_id)), + standard_v: U256::from(t.odd_y_parity as u8), + v: U256::from(t.odd_y_parity as u8), + r: U256::from(t.r.as_bytes()), + s: U256::from(t.s.as_bytes()), + access_list: Some(t.access_list), + }, + _ => Transaction::default(), + } + } } /// Local Transaction Status diff --git a/src/components/contracts/primitives/rpc-core/src/types/transaction_request.rs b/src/components/contracts/primitives/rpc-core/src/types/transaction_request.rs index bbddf8d5f..ed4aeed6e 100644 --- a/src/components/contracts/primitives/rpc-core/src/types/transaction_request.rs +++ b/src/components/contracts/primitives/rpc-core/src/types/transaction_request.rs @@ -22,8 +22,15 @@ use crate::types::Bytes; use ethereum_types::{H160, U256}; use serde::{Deserialize, Serialize}; +use ethereum::{AccessListItem, EIP1559TransactionMessage, LegacyTransactionMessage}; + +pub enum TransactionMessage { + Legacy(LegacyTransactionMessage), + EIP1559(EIP1559TransactionMessage), +} + /// Transaction request coming from RPC -#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct TransactionRequest { @@ -33,6 +40,10 @@ pub struct TransactionRequest { pub to: Option, /// Gas Price pub gas_price: Option, + /// Max BaseFeePerGas the user is willing to pay. + pub max_fee_per_gas: Option, + /// The miner's tip. + pub max_priority_fee_per_gas: Option, /// Gas pub gas: Option, /// Value of transaction in wei @@ -41,4 +52,58 @@ pub struct TransactionRequest { pub data: Option, /// Transaction's nonce pub nonce: Option, + /// Pre-pay to warm storage access. + pub access_list: Option>, +} + +impl Into> for TransactionRequest { + fn into(self) -> Option { + match ( + self.gas_price, + self.max_fee_per_gas, + self.access_list.clone(), + ) { + // Legacy + (Some(_), None, None) => { + Some(TransactionMessage::Legacy(LegacyTransactionMessage { + nonce: U256::zero(), + gas_price: self.gas_price.unwrap_or_default(), + gas_limit: self.gas.unwrap_or_default(), + value: self.value.unwrap_or(U256::zero()), + input: self.data.map(|s| s.into_vec()).unwrap_or_default(), + action: match self.to { + Some(to) => ethereum::TransactionAction::Call(to), + None => ethereum::TransactionAction::Create, + }, + chain_id: None, + })) + } + // EIP1559 + (None, Some(_), _) | (None, None, None) => { + // Empty fields fall back to the canonical transaction schema. + Some(TransactionMessage::EIP1559(EIP1559TransactionMessage { + nonce: U256::zero(), + max_fee_per_gas: self.max_fee_per_gas.unwrap_or_default(), + max_priority_fee_per_gas: self + .max_priority_fee_per_gas + .unwrap_or(U256::from(0)), + gas_limit: self.gas.unwrap_or_default(), + value: self.value.unwrap_or(U256::zero()), + input: self.data.map(|s| s.into_vec()).unwrap_or_default(), + action: match self.to { + Some(to) => ethereum::TransactionAction::Call(to), + None => ethereum::TransactionAction::Create, + }, + chain_id: 0, + access_list: self + .access_list + .unwrap_or(Vec::new()) + .into_iter() + .map(|item| item) + .collect(), + })) + } + _ => None, + } + } } diff --git a/src/components/contracts/primitives/traits/Cargo.toml b/src/components/contracts/primitives/traits/Cargo.toml index b98734ce0..27b491f69 100644 --- a/src/components/contracts/primitives/traits/Cargo.toml +++ b/src/components/contracts/primitives/traits/Cargo.toml @@ -9,7 +9,7 @@ description = "Findora Primitive Trait definitions for all module interactions" readme = "README.md" [dependencies] -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } primitive-types = { version = "0.10.1", default-features = false, features = ["rlp", "byteorder", "serde"] } ruc = "1.0" diff --git a/src/components/contracts/primitives/traits/src/account.rs b/src/components/contracts/primitives/traits/src/account.rs index 65bb14fd1..0227483d1 100644 --- a/src/components/contracts/primitives/traits/src/account.rs +++ b/src/components/contracts/primitives/traits/src/account.rs @@ -56,6 +56,8 @@ pub trait AccountAsset
{ spender: &Address, amount: U256, ) -> Result<()>; + + fn income(ctx: &Context, who: &Address, value: U256) -> Result<()>; } /// Outputs the current transaction fee. diff --git a/src/components/contracts/primitives/traits/src/base.rs b/src/components/contracts/primitives/traits/src/base.rs index f90a3c5ca..e408fdca2 100644 --- a/src/components/contracts/primitives/traits/src/base.rs +++ b/src/components/contracts/primitives/traits/src/base.rs @@ -1,4 +1,4 @@ -use ethereum::{BlockV0 as Block, Receipt}; +use ethereum::{BlockV2 as Block, Receipt}; use fp_core::account::SmartAccount; use fp_evm::BlockId; use fp_types::crypto::Address; @@ -32,4 +32,9 @@ pub trait BaseProvider { index: H256, height: Option, ) -> Option; + + /// Return the base fee at the given height. + fn base_fee(&self, id: Option) -> Option; + /// Return `true` if the request BlockId is post-eip1559. + fn is_eip1559(&self, id: Option) -> bool; } diff --git a/src/components/contracts/primitives/traits/src/evm.rs b/src/components/contracts/primitives/traits/src/evm.rs index b2103fa97..e95626045 100644 --- a/src/components/contracts/primitives/traits/src/evm.rs +++ b/src/components/contracts/primitives/traits/src/evm.rs @@ -74,4 +74,7 @@ pub trait OnChargeEVMTransaction { corrected_fee: U256, already_withdrawn: U256, ) -> Result<()>; + + /// Introduced in EIP1559 to handle the priority tip payment to the block Author. + fn pay_priority_fee(ctx: &Context, tip: U256) -> Result<()>; } diff --git a/src/components/contracts/primitives/types/Cargo.toml b/src/components/contracts/primitives/types/Cargo.toml index fb17edbaf..d3544701b 100644 --- a/src/components/contracts/primitives/types/Cargo.toml +++ b/src/components/contracts/primitives/types/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" [dependencies] bech32 = "0.7.2" -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } fixed-hash = "0.7.0" hex = "0.4.2" globutils = { path = "../../../../libs/globutils" } diff --git a/src/components/contracts/primitives/types/src/actions/ethereum.rs b/src/components/contracts/primitives/types/src/actions/ethereum.rs index 5d2403a08..4efd37397 100644 --- a/src/components/contracts/primitives/types/src/actions/ethereum.rs +++ b/src/components/contracts/primitives/types/src/actions/ethereum.rs @@ -1,7 +1,13 @@ -use ethereum::TransactionV0 as Transaction; +use ethereum::TransactionV0 as LegacyTransaction; +use ethereum::TransactionV2 as Transaction; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Action { Transact(Transaction), } + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum LegacyAction { + Transact(LegacyTransaction), +} diff --git a/src/components/contracts/primitives/types/src/actions/evm.rs b/src/components/contracts/primitives/types/src/actions/evm.rs index 378d06a6d..9510a461c 100644 --- a/src/components/contracts/primitives/types/src/actions/evm.rs +++ b/src/components/contracts/primitives/types/src/actions/evm.rs @@ -6,6 +6,9 @@ pub enum Action { Call(Call), Create(Create), Create2(Create2), + CallEip1559(CallEip1559), + CreateEip1559(CreateEip1559), + Create2Eip1559(Create2Eip1559), } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -19,6 +22,18 @@ pub struct Call { pub nonce: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CallEip1559 { + pub source: H160, + pub target: H160, + pub input: Vec, + pub value: U256, + pub gas_limit: u64, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, + pub nonce: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Create { pub source: H160, @@ -29,6 +44,17 @@ pub struct Create { pub nonce: Option, } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct CreateEip1559 { + pub source: H160, + pub init: Vec, + pub value: U256, + pub gas_limit: u64, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, + pub nonce: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Create2 { pub source: H160, @@ -39,3 +65,15 @@ pub struct Create2 { pub gas_price: Option, pub nonce: Option, } + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Create2Eip1559 { + pub source: H160, + pub init: Vec, + pub salt: H256, + pub value: U256, + pub gas_limit: u64, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, + pub nonce: Option, +} diff --git a/src/components/contracts/primitives/types/src/actions/mod.rs b/src/components/contracts/primitives/types/src/actions/mod.rs index b049ae7d2..0f006107e 100644 --- a/src/components/contracts/primitives/types/src/actions/mod.rs +++ b/src/components/contracts/primitives/types/src/actions/mod.rs @@ -12,3 +12,11 @@ pub enum Action { XHub(xhub::Action), Template(template::Action), } + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum LegacyAction { + Ethereum(ethereum::LegacyAction), + Evm(evm::Action), + XHub(xhub::Action), + Template(template::Action), +} diff --git a/src/components/contracts/primitives/types/src/assemble.rs b/src/components/contracts/primitives/types/src/assemble.rs index 41cdb5a16..a2c709a7a 100644 --- a/src/components/contracts/primitives/types/src/assemble.rs +++ b/src/components/contracts/primitives/types/src/assemble.rs @@ -1,8 +1,8 @@ use crate::actions::ethereum::Action as EtherAction; -use crate::actions::Action; +use crate::actions::{Action, LegacyAction}; use crate::crypto::{Address, Signature}; use crate::transaction; -use ethereum::TransactionV0 as Transaction; +use ethereum::TransactionV2 as Transaction; use primitive_types::U256; use ruc::*; use serde::{Deserialize, Serialize}; @@ -28,6 +28,9 @@ impl CheckFee { } } +pub type LegacyUncheckedTransaction = + transaction::UncheckedTransaction; + /// Unchecked transaction type as expected by this application. pub type UncheckedTransaction = transaction::UncheckedTransaction; diff --git a/src/components/contracts/primitives/utils/Cargo.toml b/src/components/contracts/primitives/utils/Cargo.toml index aa301fa37..c9a853b69 100644 --- a/src/components/contracts/primitives/utils/Cargo.toml +++ b/src/components/contracts/primitives/utils/Cargo.toml @@ -9,6 +9,8 @@ description = "Findora Primitive utility functions" readme = "README.md" [dependencies] +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } +ethereum-types = { version = "0.12", default-features = false } futures = "0.3.16" base64 = "0.10" bip0039 = "0.8.0" diff --git a/src/components/contracts/primitives/wasm/Cargo.toml b/src/components/contracts/primitives/wasm/Cargo.toml index ebfba1643..bbe2da14b 100644 --- a/src/components/contracts/primitives/wasm/Cargo.toml +++ b/src/components/contracts/primitives/wasm/Cargo.toml @@ -14,7 +14,7 @@ wasm-opt = false [dependencies] base64 = "0.10" -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = { version = "0.12", default-features = false } fp-types = { path = "../../primitives/types" } fp-utils = { path = "../../primitives/utils" } diff --git a/src/components/contracts/primitives/wasm/src/wasm.rs b/src/components/contracts/primitives/wasm/src/wasm.rs index 6964aa6f3..b0b5d926c 100644 --- a/src/components/contracts/primitives/wasm/src/wasm.rs +++ b/src/components/contracts/primitives/wasm/src/wasm.rs @@ -1,11 +1,17 @@ #![allow(clippy::unused_unit)] use core::fmt::Display; -use ethereum::{LegacyTransactionMessage, TransactionV0 as Transaction}; +use ethereum::{ + LegacyTransactionMessage, TransactionV0 as LegacyTransaction, + TransactionV2 as Transaction, +}; use ethereum_types::{H160, H256}; use fp_types::{ - actions::{ethereum::Action as EthAction, Action}, - assemble::UncheckedTransaction, + actions::{ + ethereum::Action as EthAction, ethereum::LegacyAction as LegacyEthAction, + Action, LegacyAction, + }, + assemble::{LegacyUncheckedTransaction, UncheckedTransaction}, crypto::secp256k1_ecdsa_recover, }; use fp_utils::tx::EvmRawTxWrapper; @@ -19,7 +25,7 @@ pub(crate) fn error_to_jsvalue(e: T) -> JsValue { } #[inline(always)] -pub fn recover_signer(transaction: &Transaction) -> Option { +pub fn legacy_recover_signer(transaction: &LegacyTransaction) -> Option { let mut sig = [0u8; 65]; let mut msg = [0u8; 32]; sig[0..32].copy_from_slice(&transaction.signature.r()[..]); @@ -33,6 +39,37 @@ pub fn recover_signer(transaction: &Transaction) -> Option { ))) } +#[inline(always)] +pub fn recover_signer(transaction: &Transaction) -> Option { + let mut sig = [0u8; 65]; + let mut msg = [0u8; 32]; + match transaction { + Transaction::Legacy(t) => { + sig[0..32].copy_from_slice(&t.signature.r()[..]); + sig[32..64].copy_from_slice(&t.signature.s()[..]); + sig[64] = t.signature.standard_v(); + msg.copy_from_slice( + ðereum::LegacyTransactionMessage::from(t.clone()).hash()[..], + ); + } + Transaction::EIP1559(t) => { + sig[0..32].copy_from_slice(&t.r[..]); + sig[32..64].copy_from_slice(&t.s[..]); + sig[64] = t.odd_y_parity as u8; + msg.copy_from_slice( + ðereum::EIP1559TransactionMessage::from(t.clone()).hash()[..], + ); + } + _ => { + return None; + } + } + let pubkey = fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg).ok()?; + Some(H160::from(H256::from_slice( + Keccak256::digest(&pubkey).as_slice(), + ))) +} + pub fn recover_tx_signer(raw_tx: String) -> Result { let tx_bytes = base64::decode_config(&raw_tx, base64::URL_SAFE) .c(d!()) @@ -71,6 +108,9 @@ pub fn evm_tx_hash(raw_tx: String) -> Result { } } +use std::fs; +use std::io::Write; + #[cfg(test)] #[allow(missing_docs)] mod test { @@ -82,10 +122,12 @@ mod test { let raw_tx = String::from("ZXZtOnsic2lnbmF0dXJlIjpudWxsLCJmdW5jdGlvbiI6eyJFdGhlcmV1bSI6eyJUcmFuc2FjdCI6eyJub25jZSI6IjB4MSIsImdhc19wcmljZSI6IjB4MTc0ODc2ZTgwMCIsImdhc19saW1pdCI6IjB4NTIwOCIsImFjdGlvbiI6eyJDYWxsIjoiMHgyYWQzMjg0NmM2ZGQyZmZkM2VkYWRiZTUxY2Q1YWUwNGFhNWU1NzVlIn0sInZhbHVlIjoiMHg1NmJjNzVlMmQ2MzEwMDAwMCIsImlucHV0IjpbXSwic2lnbmF0dXJlIjp7InYiOjEwODIsInIiOiIweGY4YWVmN2Y4MDUzZDg5ZmVlMzk1MGM0ZDcwMjA4MGJmM2E4MDcyYmVkNWQ4NGEzYWYxOWEzNjAwODFiNjM2YTIiLCJzIjoiMHgyOTYyOTlhOGYyNDMwYjg2ZmQzZWI5NzZlYWJjNzMwYWMxY2ZiYmJlMzZlYjY5ZWFlMzM4Y2ZmMzNjNGE5OGMxIn19fX19"); let tx_bytes = base64::decode_config(&raw_tx, base64::URL_SAFE).unwrap(); let evm_tx = EvmRawTxWrapper::unwrap(&tx_bytes).unwrap(); - let unchecked_tx: UncheckedTransaction<()> = + let unchecked_tx: LegacyUncheckedTransaction<()> = serde_json::from_slice(evm_tx).unwrap(); - if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function { - let signer = recover_signer(&tx).unwrap(); + if let LegacyAction::Ethereum(LegacyEthAction::Transact(tx)) = + unchecked_tx.function + { + let signer = legacy_recover_signer(&tx).unwrap(); assert_eq!( format!("{:?}", signer), "0xa5225cbee5052100ec2d2d94aa6d258558073757" @@ -99,10 +141,13 @@ mod test { fn evm_tx_hash_works() { let raw_tx = String::from("eyJzaWduYXR1cmUiOm51bGwsImZ1bmN0aW9uIjp7IkV0aGVyZXVtIjp7IlRyYW5zYWN0Ijp7Im5vbmNlIjoiMHg5IiwiZ2FzX3ByaWNlIjoiMHhlOGQ0YTUxMDAwIiwiZ2FzX2xpbWl0IjoiMHg1MjA4IiwiYWN0aW9uIjp7IkNhbGwiOiIweGE1MjI1Y2JlZTUwNTIxMDBlYzJkMmQ5NGFhNmQyNTg1NTgwNzM3NTcifSwidmFsdWUiOiIweDk4YTdkOWI4MzE0YzAwMDAiLCJpbnB1dCI6W10sInNpZ25hdHVyZSI6eyJ2IjoxMDgyLCJyIjoiMHg4MDBjZjQ5ZTAzMmJhYzY4MjY3MzdhZGJhZDEzN2Y0MTk5OTRjNjgxZWE1ZDUyYjliMGJhZDJmNDAyYjMwMTI0IiwicyI6IjB4Mjk1Mjc3ZWY2NTYzNDAwY2VkNjFiODhkM2ZiNGM3YjMyY2NkNTcwYThiOWJiOGNiYmUyNTkyMTRhYjdkZTI1YSJ9fX19fQ=="); let tx_bytes = base64::decode_config(&raw_tx, base64::URL_SAFE).unwrap(); - let unchecked_tx: UncheckedTransaction<()> = + let unchecked_tx: LegacyUncheckedTransaction<()> = serde_json::from_slice(tx_bytes.as_slice()).unwrap(); - if let Action::Ethereum(EthAction::Transact(tx)) = unchecked_tx.function { + if let LegacyAction::Ethereum(LegacyEthAction::Transact(tx)) = + unchecked_tx.function + { let hash = H256::from_slice(Keccak256::digest(&rlp::encode(&tx)).as_slice()); + println!("{:?}", hash); assert_eq!( format!("{:?}", hash), "0x0eeb0ff455b1b57b821634cf853e7247e584a675610f13097cc49c2022505df3" diff --git a/src/components/contracts/rpc/Cargo.toml b/src/components/contracts/rpc/Cargo.toml index 37ac21784..faedf2572 100644 --- a/src/components/contracts/rpc/Cargo.toml +++ b/src/components/contracts/rpc/Cargo.toml @@ -13,9 +13,9 @@ abci = { git = "https://github.com/FindoraNetwork/rust-abci", tag = "v0.7.2" } async-std = "1.9.0" baseapp = { path = "../baseapp" } base64 = "0.12" -ethereum = { version = "0.9.0", default-features = false, features = ["with-serde"] } +ethereum = { version = "0.10.0", default-features = false, features = ["with-serde"] } ethereum-types = { version = "0.12", default-features = false } -evm = { version = "0.29.0", default-features = false, features = ["with-serde"] } +evm = { version = "0.33.0", default-features = false, features = ["with-serde"] } fp-rpc-core = { path = "../primitives/rpc-core" } fp-rpc-server = { path = "../primitives/rpc-server" } futures = { version = "0.3.16", features = ["compat", "thread-pool"] } diff --git a/src/components/contracts/rpc/src/eth.rs b/src/components/contracts/rpc/src/eth.rs index fac74bdeb..34595f5b2 100644 --- a/src/components/contracts/rpc/src/eth.rs +++ b/src/components/contracts/rpc/src/eth.rs @@ -1,17 +1,20 @@ use crate::{error_on_execution_failure, internal_err}; use baseapp::{extensions::SignedExtra, BaseApp}; use ethereum::{ - BlockV0 as EthereumBlock, LegacyTransactionMessage as EthereumTransactionMessage, - LegacyTransactionMessage, TransactionV0 as EthereumTransaction, + BlockV2 as EthereumBlock, + // TransactionMessage as EthereumTransactionMessage, + // LegacyTransactionMessage, + TransactionV2 as EthereumTransaction, }; use ethereum_types::{BigEndianHash, H160, H256, H512, H64, U256, U64}; use evm::{ExitError, ExitReason}; use fp_evm::{BlockId, Runner, TransactionStatus}; use fp_rpc_core::types::{ Block, BlockNumber, BlockTransactions, Bytes, CallRequest, Filter, FilteredParams, - Index, Log, Receipt, Rich, RichBlock, SyncStatus, Transaction, TransactionRequest, - Work, + Index, Log, Receipt, Rich, RichBlock, SyncStatus, Transaction, TransactionMessage, + TransactionRequest, Work, }; +// use fp_rpc_core::types::TransactionMessage as EthereumTransactionMessage; use fp_rpc_core::EthApi; use fp_traits::{ base::BaseProvider, @@ -19,7 +22,7 @@ use fp_traits::{ }; use fp_types::{ actions, - actions::evm::{Call, Create}, + actions::evm::{Call, CallEip1559, Create, CreateEip1559}, assemble::UncheckedTransaction, }; use fp_utils::ecdsa::SecpPair; @@ -186,23 +189,68 @@ impl EthApi for EthApiImpl { }; let chain_id = match self.chain_id() { - Ok(chain_id) => chain_id, + Ok(Some(chain_id)) => chain_id.as_u64(), + Ok(None) => { + return Box::pin(future::err(internal_err("chain id not available"))) + } Err(e) => return Box::pin(future::err(e)), }; - let message = EthereumTransactionMessage { - nonce, - gas_price: request.gas_price.unwrap_or_else( - ::FeeCalculator::min_gas_price, - ), - gas_limit: request.gas.unwrap_or_else(U256::max_value), - value: request.value.unwrap_or_else(U256::zero), - input: request.data.map(|s| s.into_vec()).unwrap_or_default(), - action: match request.to { - Some(to) => ethereum::TransactionAction::Call(to), - None => ethereum::TransactionAction::Create, - }, - chain_id: chain_id.map(|s| s.as_u64()), + // let hash = self.client.info().best_hash; + + let gas_price = request.gas_price.unwrap_or_else( + ::FeeCalculator::min_gas_price, + ); + let block = self.account_base_app.read().current_block(None); + let gas_limit = match request.gas { + Some(gas_limit) => gas_limit, + None => { + if let Some(block) = block { + block.header.gas_limit + } else { + ::BlockGasLimit::get() + } + } + }; + + let max_fee_per_gas = request.max_fee_per_gas; + + let message: Option = request.into(); + // let message = EthereumTransactionMessage { + // nonce, + // gas_price: request.gas_price.unwrap_or_else( + // ::FeeCalculator::min_gas_price, + // ), + // gas_limit: request.gas.unwrap_or_else(U256::max_value), + // value: request.value.unwrap_or_else(U256::zero), + // input: request.data.map(|s| s.into_vec()).unwrap_or_default(), + // action: match request.to { + // Some(to) => ethereum::TransactionAction::Call(to), + // None => ethereum::TransactionAction::Create, + // }, + // chain_id: chain_id.map(|s| s.as_u64()), + // }; + + let message = match message { + Some(TransactionMessage::Legacy(mut m)) => { + m.nonce = nonce; + m.chain_id = Some(chain_id); + m.gas_limit = gas_limit; + m.gas_price = gas_price; + TransactionMessage::Legacy(m) + } + Some(TransactionMessage::EIP1559(mut m)) => { + m.nonce = nonce; + m.chain_id = chain_id; + m.gas_limit = gas_limit; + if max_fee_per_gas.is_none() { + m.max_fee_per_gas = self.gas_price().unwrap_or_default(); + } + TransactionMessage::EIP1559(m) + } + _ => { + return Box::pin(future::err(internal_err("Transaction Type Error"))); + } }; let mut transaction = None; @@ -218,14 +266,27 @@ impl EthApi for EthApiImpl { } } + // for signer in &self.signers { + // if signer.accounts().contains(&from) { + // match signer.sign(message, &from) { + // Ok(t) => transaction = Some(t), + // Err(e) => return Box::pin(future::err(e)), + // } + // break; + // } + // } + let transaction = match transaction { Some(transaction) => transaction, None => { return Box::pin(future::err(internal_err("no signer available"))); } }; - let transaction_hash = - H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + // let transaction_hash = + // H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + + let transaction_hash = transaction.hash(); + let function = actions::Action::Ethereum(actions::ethereum::Action::Transact(transaction)); let txn = serde_json::to_vec( @@ -266,12 +327,29 @@ impl EthApi for EthApiImpl { from, to, gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, gas, value, data, nonce, } = request; + let is_eip1559 = max_fee_per_gas.is_some() + && max_priority_fee_per_gas.is_some() + && max_fee_per_gas.unwrap() > U256::from(0) + && max_priority_fee_per_gas.unwrap() > U256::from(0); + + let (gas_price, max_fee_per_gas, max_priority_fee_per_gas) = { + let details = + fee_details(gas_price, max_fee_per_gas, max_priority_fee_per_gas)?; + ( + details.gas_price, + details.max_fee_per_gas, + details.max_priority_fee_per_gas, + ) + }; + let block = self.account_base_app.read().current_block(None); // use given gas limit or query current block's limit let gas_limit = match gas { @@ -304,51 +382,105 @@ impl EthApi for EthApiImpl { ctx.header.proposer_address = Vec::from(block.header.beneficiary.as_bytes()) } - match to { - Some(to) => { - let call = Call { - source: from.unwrap_or_default(), - target: to, - input: data, - value: value.unwrap_or_default(), - gas_limit: gas_limit.as_u64(), - gas_price, - nonce, - }; + if is_eip1559 { + match to { + Some(to) => { + let call = CallEip1559 { + source: from.unwrap_or_default(), + target: to, + input: data, + value: value.unwrap_or_default(), + gas_limit: gas_limit.as_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }; - let info = ::Runner::call( - &ctx, call, &config, - ) - .map_err(|err| { - internal_err(format!("evm runner call error: {:?}", err)) - })?; - debug!(target: "eth_rpc", "evm runner call result: {:?}", info); + let info = + ::Runner::call_eip1559( + &ctx, call, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner call error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner call result: {:?}", info); + + error_on_execution_failure(&info.exit_reason, &info.value)?; + + Ok(Bytes(info.value)) + } + None => { + let create = CreateEip1559 { + source: from.unwrap_or_default(), + init: data, + value: value.unwrap_or_default(), + gas_limit: gas_limit.as_u64(), + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }; - error_on_execution_failure(&info.exit_reason, &info.value)?; + let info = + ::Runner::create_eip1559( + &ctx, create, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner create error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner create result: {:?}", info); - Ok(Bytes(info.value)) + error_on_execution_failure(&info.exit_reason, &[])?; + + Ok(Bytes(info.value[..].to_vec())) + } } - None => { - let create = Create { - source: from.unwrap_or_default(), - init: data, - value: value.unwrap_or_default(), - gas_limit: gas_limit.as_u64(), - gas_price, - nonce, - }; + } else { + match to { + Some(to) => { + let call = Call { + source: from.unwrap_or_default(), + target: to, + input: data, + value: value.unwrap_or_default(), + gas_limit: gas_limit.as_u64(), + gas_price, + nonce, + }; - let info = ::Runner::create( - &ctx, create, &config, - ) - .map_err(|err| { - internal_err(format!("evm runner create error: {:?}", err)) - })?; - debug!(target: "eth_rpc", "evm runner create result: {:?}", info); + let info = ::Runner::call( + &ctx, call, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner call error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner call result: {:?}", info); - error_on_execution_failure(&info.exit_reason, &[])?; + error_on_execution_failure(&info.exit_reason, &info.value)?; - Ok(Bytes(info.value[..].to_vec())) + Ok(Bytes(info.value)) + } + None => { + let create = Create { + source: from.unwrap_or_default(), + init: data, + value: value.unwrap_or_default(), + gas_limit: gas_limit.as_u64(), + gas_price, + nonce, + }; + + let info = ::Runner::create( + &ctx, create, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner create error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner create result: {:?}", info); + + error_on_execution_failure(&info.exit_reason, &[])?; + + Ok(Bytes(info.value[..].to_vec())) + } } } } @@ -404,7 +536,7 @@ impl EthApi for EthApiImpl { } fn block_by_hash(&self, hash: H256, full: bool) -> Result> { - debug!(target: "eth_rpc", "block_by_hash, hash:{:?}, full:{:?}", hash, full); + debug!(target: "eth_rpc", "block_by_hash, hash:{:?}, fx/ull:{:?}", hash, full); let block = self .account_base_app @@ -415,12 +547,18 @@ impl EthApi for EthApiImpl { .read() .current_transaction_statuses(Some(BlockId::Hash(hash))); + let base_fee = self + .account_base_app + .read() + .base_fee(Some(BlockId::Hash(hash))); + match (block, statuses) { (Some(block), Some(statuses)) => Ok(Some(rich_block_build( block, statuses.into_iter().map(Some).collect(), Some(hash), full, + base_fee, ))), _ => Ok(None), } @@ -438,17 +576,20 @@ impl EthApi for EthApiImpl { let statuses = self .account_base_app .read() - .current_transaction_statuses(id); + .current_transaction_statuses(id.clone()); match (block, statuses) { (Some(block), Some(statuses)) => { let hash = block.header.hash(); + let base_fee = self.account_base_app.read().base_fee(id); + Ok(Some(rich_block_build( block, statuses.into_iter().map(Some).collect(), Some(hash), full, + base_fee, ))) } _ => Ok(None), @@ -528,16 +669,52 @@ impl EthApi for EthApiImpl { } fn send_raw_transaction(&self, bytes: Bytes) -> BoxFuture> { - let transaction = match rlp::decode::(&bytes.0[..]) { - Ok(transaction) => transaction, - Err(_) => { - return Box::pin(future::err(internal_err("decode transaction failed"))); + let slice = &bytes.0[..]; + if slice.is_empty() { + return Box::pin(future::err(internal_err("transaction data is empty"))); + } + let first = slice.get(0).unwrap(); + let transaction = if first > &0x7f { + // Legacy transaction. Decode and wrap in envelope. + match rlp::decode::(slice) { + Ok(transaction) => ethereum::TransactionV2::Legacy(transaction), + Err(_) => { + return Box::pin(future::err(internal_err( + "decode transaction failed", + ))); + } + } + } else { + // Typed Transaction. + // `ethereum` crate decode implementation for `TransactionV2` expects a valid rlp input, + // and EIP-1559 breaks that assumption by prepending a version byte. + // We re-encode the payload input to get a valid rlp, and the decode implementation will strip + // them to check the transaction version byte. + let extend = rlp::encode(&slice); + match rlp::decode::(&extend[..]) { + Ok(transaction) => transaction, + Err(_) => { + return Box::pin(future::err(internal_err( + "decode transaction failed", + ))) + } } }; + + // let transaction = match rlp::decode::(&bytes.0[..]) { + // Ok(transaction) => transaction, + // Err(_) => { + // return Box::pin(future::err(internal_err("decode transaction failed"))); + // } + // }; + debug!(target: "eth_rpc", "send_raw_transaction :{:?}", transaction); - let transaction_hash = - H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + // let transaction_hash = + // H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()); + + let transaction_hash = transaction.hash(); + let function = actions::Action::Ethereum(actions::ethereum::Action::Transact(transaction)); let txn = serde_json::to_vec( @@ -578,6 +755,24 @@ impl EthApi for EthApiImpl { ) -> Result { debug!(target: "eth_rpc", "estimate_gas, block number {:?} request:{:?}", number, request); + let is_eip1559 = request.max_fee_per_gas.is_some() + && request.max_priority_fee_per_gas.is_some() + && request.max_fee_per_gas.unwrap() > U256::from(0) + && request.max_priority_fee_per_gas.unwrap() > U256::from(0); + + let (gas_price, max_fee_per_gas, max_priority_fee_per_gas) = { + let details = fee_details( + request.gas_price, + request.max_fee_per_gas, + request.max_priority_fee_per_gas, + )?; + ( + details.gas_price, + details.max_fee_per_gas, + details.max_priority_fee_per_gas, + ) + }; + let (block_id, pending) = match number.unwrap_or(BlockNumber::Latest) { BlockNumber::Num(num) => { let range = self.version_range()?; @@ -670,7 +865,9 @@ impl EthApi for EthApiImpl { let CallRequest { from, to, - gas_price, + gas_price: _, + max_fee_per_gas: _, + max_priority_fee_per_gas: _, gas, value, data, @@ -685,55 +882,112 @@ impl EthApi for EthApiImpl { let mut config = ::config().clone(); config.estimate = true; - match to { - Some(to) => { - let call = Call { - source: from.unwrap_or_default(), - target: to, - input: data.map(|d| d.0).unwrap_or_default(), - value: value.unwrap_or_default(), - gas_limit, - gas_price, - nonce, - }; - - let info = ::Runner::call( - &ctx, call, &config, - ) - .map_err(|err| { - internal_err(format!("evm runner call error: {:?}", err)) - })?; - debug!(target: "eth_rpc", "evm runner call result: {:?}", info); - - Ok(ExecuteResult { - data: info.value, - exit_reason: info.exit_reason, - used_gas: info.used_gas, - }) + if is_eip1559 { + match to { + Some(to) => { + let call = CallEip1559 { + source: from.unwrap_or_default(), + target: to, + input: data.map(|d| d.0).unwrap_or_default(), + value: value.unwrap_or_default(), + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }; + + let info = + ::Runner::call_eip1559( + &ctx, call, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner call error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner call result: {:?}", info); + + Ok(ExecuteResult { + data: info.value, + exit_reason: info.exit_reason, + used_gas: info.used_gas, + }) + } + None => { + let create = CreateEip1559 { + source: from.unwrap_or_default(), + init: data.map(|d| d.0).unwrap_or_default(), + value: value.unwrap_or_default(), + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + }; + + let info = ::Runner::create_eip1559( + &ctx, create, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner create error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner create result: {:?}", info); + + Ok(ExecuteResult { + data: vec![], + exit_reason: info.exit_reason, + used_gas: info.used_gas, + }) + } } - None => { - let create = Create { - source: from.unwrap_or_default(), - init: data.map(|d| d.0).unwrap_or_default(), - value: value.unwrap_or_default(), - gas_limit, - gas_price, - nonce, - }; - - let info = ::Runner::create( - &ctx, create, &config, - ) - .map_err(|err| { - internal_err(format!("evm runner create error: {:?}", err)) - })?; - debug!(target: "eth_rpc", "evm runner create result: {:?}", info); - - Ok(ExecuteResult { - data: vec![], - exit_reason: info.exit_reason, - used_gas: info.used_gas, - }) + } else { + match to { + Some(to) => { + let call = Call { + source: from.unwrap_or_default(), + target: to, + input: data.map(|d| d.0).unwrap_or_default(), + value: value.unwrap_or_default(), + gas_limit, + gas_price, + nonce, + }; + + let info = ::Runner::call( + &ctx, call, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner call error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner call result: {:?}", info); + + Ok(ExecuteResult { + data: info.value, + exit_reason: info.exit_reason, + used_gas: info.used_gas, + }) + } + None => { + let create = Create { + source: from.unwrap_or_default(), + init: data.map(|d| d.0).unwrap_or_default(), + value: value.unwrap_or_default(), + gas_limit, + gas_price, + nonce, + }; + + let info = ::Runner::create( + &ctx, create, &config, + ) + .map_err(|err| { + internal_err(format!("evm runner create error: {:?}", err)) + })?; + debug!(target: "eth_rpc", "evm runner create result: {:?}", info); + + Ok(ExecuteResult { + data: vec![], + exit_reason: info.exit_reason, + used_gas: info.used_gas, + }) + } } } }; @@ -806,10 +1060,23 @@ impl EthApi for EthApiImpl { } } + let is_eip1559 = !matches!( + &block.transactions[index], + EthereumTransaction::Legacy(_) + ); + // match &block.transactions[index] { + // EthereumTransaction::Legacy(_) => false, + // _ => true, + // }; + + let base_fee = self.account_base_app.read().base_fee(id); + Ok(Some(transaction_build( block.transactions[index].clone(), Some(block), Some(statuses[index].clone()), + is_eip1559, + base_fee, ))) } _ => Ok(None), @@ -839,10 +1106,26 @@ impl EthApi for EthApiImpl { return Ok(None); } + let is_eip1559 = !matches!( + &block.transactions[index], + EthereumTransaction::Legacy(_) + ); + // match &block.transactions[index] { + // EthereumTransaction::Legacy(_) => false, + // _ => true, + // }; + + let base_fee = self + .account_base_app + .read() + .base_fee(Some(BlockId::Hash(hash))); + Ok(Some(transaction_build( block.transactions[index].clone(), Some(block), Some(statuses[index].clone()), + is_eip1559, + base_fee, ))) } _ => Ok(None), @@ -862,7 +1145,7 @@ impl EthApi for EthApiImpl { let statuses = self .account_base_app .read() - .current_transaction_statuses(id); + .current_transaction_statuses(id.clone()); match (block, statuses) { (Some(block), Some(statuses)) => { @@ -870,10 +1153,23 @@ impl EthApi for EthApiImpl { return Ok(None); } + let is_eip1559 = !matches!( + &block.transactions[index], + EthereumTransaction::Legacy(_) + ); + // match &block.transactions[index] { + // EthereumTransaction::Legacy(_) => true, + // _ => false, + // }; + + let base_fee = self.account_base_app.read().base_fee(id); + Ok(Some(transaction_build( block.transactions[index].clone(), Some(block), Some(statuses[index].clone()), + is_eip1559, + base_fee, ))) } _ => Ok(None), @@ -898,8 +1194,10 @@ impl EthApi for EthApiImpl { .current_transaction_statuses(id.clone()); let receipts = self.account_base_app.read().current_receipts(id.clone()); - match (block, statuses, receipts) { - (Some(block), Some(statuses), Some(receipts)) => { + let base_fee = self.account_base_app.read().base_fee(id.clone()); + + match (block, statuses, receipts, base_fee) { + (Some(block), Some(statuses), Some(receipts), Some(base_fee)) => { if id.is_none() { if let Some(idx) = statuses.iter().position(|t| t.transaction_hash == hash) @@ -919,6 +1217,15 @@ impl EthApi for EthApiImpl { let mut cumulative_receipts = receipts; cumulative_receipts.truncate((status.transaction_index + 1) as usize); + let transaction = block.transactions[index].clone(); + let effective_gas_price = match transaction { + EthereumTransaction::Legacy(t) => t.gas_price, + EthereumTransaction::EIP1559(t) => base_fee + .checked_add(t.max_priority_fee_per_gas) + .unwrap_or_else(U256::max_value), + EthereumTransaction::EIP2930(t) => t.gas_price, + }; + return Ok(Some(Receipt { transaction_hash: Some(status.transaction_hash), transaction_index: Some(status.transaction_index.into()), @@ -969,6 +1276,7 @@ impl EthApi for EthApiImpl { status_code: Some(U64::from(receipt.state_root.to_low_u64_be())), logs_bloom: receipt.logs_bloom, state_root: None, + effective_gas_price, })); } _ => Ok(None), @@ -1064,33 +1372,62 @@ impl EthApi for EthApiImpl { } pub fn sign_transaction_message( - message: LegacyTransactionMessage, + message: TransactionMessage, private_key: &H256, ) -> ruc::Result { - let signing_message = libsecp256k1::Message::parse_slice(&message.hash()[..]) - .map_err(|_| eg!("invalid signing message"))?; - let secret = &libsecp256k1::SecretKey::parse_slice(&private_key[..]) - .map_err(|_| eg!("invalid secret"))?; - let (signature, recid) = libsecp256k1::sign(&signing_message, secret); - - let v = match message.chain_id { - None => 27 + recid.serialize() as u64, - Some(chain_id) => 2 * chain_id + 35 + recid.serialize() as u64, + return match message { + TransactionMessage::Legacy(m) => { + let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..]) + .map_err(|_| eg!("invalid signing message"))?; + let secret = &libsecp256k1::SecretKey::parse_slice(&private_key[..]) + .map_err(|_| eg!("invalid secret"))?; + let (signature, recid) = libsecp256k1::sign(&signing_message, secret); + + let v = match m.chain_id { + None => 27 + recid.serialize() as u64, + Some(chain_id) => 2 * chain_id + 35 + recid.serialize() as u64, + }; + let rs = signature.serialize(); + let r = H256::from_slice(&rs[0..32]); + let s = H256::from_slice(&rs[32..64]); + + Ok(EthereumTransaction::Legacy(ethereum::LegacyTransaction { + nonce: m.nonce, + gas_price: m.gas_price, + gas_limit: m.gas_limit, + action: m.action, + value: m.value, + input: m.input, + signature: ethereum::TransactionSignature::new(v, r, s) + .ok_or(eg!("signer generated invalid signature"))?, + })) + } + TransactionMessage::EIP1559(m) => { + let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..]) + .map_err(|_| eg!("invalid signing message"))?; + let secret = &libsecp256k1::SecretKey::parse_slice(&private_key[..]) + .map_err(|_| eg!("invalid secret"))?; + let (signature, recid) = libsecp256k1::sign(&signing_message, secret); + let rs = signature.serialize(); + let r = H256::from_slice(&rs[0..32]); + let s = H256::from_slice(&rs[32..64]); + + Ok(EthereumTransaction::EIP1559(ethereum::EIP1559Transaction { + chain_id: m.chain_id, + nonce: m.nonce, + max_priority_fee_per_gas: m.max_priority_fee_per_gas, + max_fee_per_gas: m.max_fee_per_gas, + gas_limit: m.gas_limit, + action: m.action, + value: m.value, + input: m.input.clone(), + access_list: m.access_list, + odd_y_parity: recid.serialize() != 0, + r, + s, + })) + } }; - let rs = signature.serialize(); - let r = H256::from_slice(&rs[0..32]); - let s = H256::from_slice(&rs[32..64]); - - Ok(EthereumTransaction { - nonce: message.nonce, - gas_price: message.gas_price, - gas_limit: message.gas_limit, - action: message.action, - value: message.value, - input: message.input, - signature: ethereum::TransactionSignature::new(v, r, s) - .ok_or(eg!("signer generated invalid signature"))?, - }) } fn rich_block_build( @@ -1098,6 +1435,7 @@ fn rich_block_build( statuses: Vec>, hash: Option, full_transactions: bool, + base_fee: Option, ) -> RichBlock { Rich { inner: Block { @@ -1134,10 +1472,21 @@ fn rich_block_build( .iter() .enumerate() .map(|(index, transaction)| { + let is_eip1559 = !matches!( + &block.transactions[index], + EthereumTransaction::Legacy(_) + ); + // match &transaction { + // EthereumTransaction::Legacy(_) => true, + // _ => false, + // }; + transaction_build( transaction.clone(), Some(block.clone()), Some(statuses[index].clone().unwrap_or_default()), + is_eip1559, + base_fee, ) }) .collect(), @@ -1160,82 +1509,150 @@ fn rich_block_build( } }, size: Some(U256::from(rlp::encode(&block).len() as u32)), + base_fee_per_gas: base_fee, }, extra_info: BTreeMap::new(), } } fn transaction_build( - transaction: EthereumTransaction, - block: Option, + ethereum_transaction: EthereumTransaction, + block: Option>, status: Option, + is_eip1559: bool, + base_fee: Option, ) -> Transaction { - let pubkey = match public_key(&transaction) { + let mut transaction: Transaction = ethereum_transaction.clone().into(); + + if let EthereumTransaction::EIP1559(_) = ethereum_transaction { + if block.is_none() && status.is_none() { + transaction.gas_price = transaction.max_fee_per_gas; + } else { + let base_fee = base_fee.unwrap_or(U256::zero()); + let max_priority_fee_per_gas = + transaction.max_priority_fee_per_gas.unwrap_or(U256::zero()); + transaction.gas_price = Some( + base_fee + .checked_add(max_priority_fee_per_gas) + .unwrap_or_else(U256::max_value), + ); + } + } else if !is_eip1559 { + transaction.max_fee_per_gas = None; + transaction.max_priority_fee_per_gas = None; + } + + let pubkey = match public_key(ðereum_transaction) { Ok(p) => Some(p), Err(_e) => None, }; - let hash = if let Some(status) = &status { - status.transaction_hash - } else { - H256::from_slice(Keccak256::digest(&rlp::encode(&transaction)).as_slice()) - }; - - Transaction { - hash, - nonce: transaction.nonce, - block_hash: block.as_ref().map(|block| { - H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()) - }), - block_number: block.as_ref().map(|block| block.header.number), - transaction_index: status - .as_ref() - .map(|status| U256::from(status.transaction_index)), - from: status.as_ref().map_or( - { - match pubkey { - Some(pk) => { - H160::from(H256::from_slice(Keccak256::digest(&pk).as_slice())) - } - _ => H160::default(), - } - }, - |status| status.from, - ), - to: status.as_ref().map_or( - { - match transaction.action { - ethereum::TransactionAction::Call(to) => Some(to), - _ => None, + // Block hash. + // transaction.block_hash = block.as_ref().map_or(None, |block| { + // Some(H256::from_slice( + // Keccak256::digest(&rlp::encode(&block.header)).as_slice(), + // )) + // }); + transaction.block_hash = block.as_ref().map(|block| { + H256::from_slice(Keccak256::digest(&rlp::encode(&block.header)).as_slice()) + }); + + // Block number. + transaction.block_number = block.as_ref().map(|block| block.header.number); + // Transaction index. + + transaction.transaction_index = status + .as_ref() + .map(|status| U256::from(status.transaction_index)); + + transaction.from = status.as_ref().map_or( + { + match pubkey { + Some(pk) => { + H160::from(H256::from_slice(Keccak256::digest(&pk).as_slice())) } - }, - |status| status.to, - ), - value: transaction.value, - gas_price: transaction.gas_price, - gas: transaction.gas_limit, - input: Bytes(transaction.clone().input), - creates: status.as_ref().and_then(|status| status.contract_address), - raw: Bytes(rlp::encode(&transaction).to_vec()), - public_key: pubkey.as_ref().map(H512::from), - chain_id: transaction.signature.chain_id().map(U64::from), - standard_v: U256::from(transaction.signature.standard_v()), - v: U256::from(transaction.signature.v()), - r: U256::from(transaction.signature.r().as_bytes()), - s: U256::from(transaction.signature.s().as_bytes()), - } + _ => H160::default(), + } + }, + |status| status.from, + ); + // To. + transaction.to = status.as_ref().map_or( + { + let action = match ethereum_transaction { + EthereumTransaction::Legacy(t) => t.action, + EthereumTransaction::EIP1559(t) => t.action, + EthereumTransaction::EIP2930(t) => t.action, + }; + match action { + ethereum::TransactionAction::Call(to) => Some(to), + _ => None, + } + }, + |status| status.to, + ); + // Creates. + // transaction.creates = status + // .as_ref() + // .map_or(None, |status| status.contract_address); + transaction.creates = status.as_ref().and_then(|status| status.contract_address); + + // Public key. + transaction.public_key = pubkey.as_ref().map(H512::from); + + // // Block hash. + // transaction.block_hash = ; + // // Block number. + // transaction.block_number = ; + // // Transaction index. + // transaction.transaction_index = ; + // // From. + // transaction.from; + // // To. + // transaction.to; + // // Creates. + // transaction.creates; + // // Public key. + // transaction.public_key; + + transaction } pub fn public_key(transaction: &EthereumTransaction) -> ruc::Result<[u8; 64]> { + // let mut sig = [0u8; 65]; + // let mut msg = [0u8; 32]; + // sig[0..32].copy_from_slice(&transaction.signature.r()[..]); + // sig[32..64].copy_from_slice(&transaction.signature.s()[..]); + // sig[64] = transaction.signature.standard_v(); + // msg.copy_from_slice( + // &EthereumTransactionMessage::from(transaction.clone()).hash()[..], + // ); + + // fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg) + let mut sig = [0u8; 65]; let mut msg = [0u8; 32]; - sig[0..32].copy_from_slice(&transaction.signature.r()[..]); - sig[32..64].copy_from_slice(&transaction.signature.s()[..]); - sig[64] = transaction.signature.standard_v(); - msg.copy_from_slice( - &EthereumTransactionMessage::from(transaction.clone()).hash()[..], - ); - + match transaction { + EthereumTransaction::Legacy(t) => { + sig[0..32].copy_from_slice(&t.signature.r()[..]); + sig[32..64].copy_from_slice(&t.signature.s()[..]); + sig[64] = t.signature.standard_v(); + msg.copy_from_slice( + ðereum::LegacyTransactionMessage::from(t.clone()).hash()[..], + ); + } + EthereumTransaction::EIP1559(t) => { + sig[0..32].copy_from_slice(&t.r[..]); + sig[32..64].copy_from_slice(&t.s[..]); + sig[64] = t.odd_y_parity as u8; + msg.copy_from_slice( + ðereum::EIP1559TransactionMessage::from(t.clone()).hash()[..], + ); + } + _ => { + return Err(eg!("Transaction Type Error")); + } + } fp_types::crypto::secp256k1_ecdsa_recover(&sig, &msg) } @@ -1366,3 +1783,43 @@ fn native_block_id(number: Option) -> Option { BlockNumber::Pending => None, } } + +struct FeeDetails { + gas_price: Option, + max_fee_per_gas: Option, + max_priority_fee_per_gas: Option, +} + +fn fee_details( + request_gas_price: Option, + request_max_fee: Option, + request_priority: Option, +) -> Result { + match (request_gas_price, request_max_fee, request_priority) { + (gas_price, None, None) => { + // Legacy request, all default to gas price. + Ok(FeeDetails { + gas_price, + max_fee_per_gas: gas_price, + max_priority_fee_per_gas: gas_price, + }) + } + (_, max_fee, max_priority) => { + // eip-1559 + // Ensure `max_priority_fee_per_gas` is less or equal to `max_fee_per_gas`. + if let Some(max_priority) = max_priority { + let max_fee = max_fee.unwrap_or_default(); + if max_priority > max_fee { + return Err(internal_err( + "Invalid input: `max_priority_fee_per_gas` greater than `max_fee_per_gas`" + )); + } + } + Ok(FeeDetails { + gas_price: max_fee, + max_fee_per_gas: max_fee, + max_priority_fee_per_gas: max_priority, + }) + } + } +} diff --git a/src/components/contracts/rpc/src/eth_filter.rs b/src/components/contracts/rpc/src/eth_filter.rs index 18c71814d..7dce5b914 100644 --- a/src/components/contracts/rpc/src/eth_filter.rs +++ b/src/components/contracts/rpc/src/eth_filter.rs @@ -1,6 +1,6 @@ use crate::{filter_block_logs, internal_err}; use baseapp::BaseApp; -use ethereum::BlockV0 as EthereumBlock; +use ethereum::BlockV2 as EthereumBlock; use ethereum_types::{H256, U256}; use fp_evm::{BlockId, TransactionStatus}; use fp_rpc_core::types::{ diff --git a/src/components/contracts/rpc/src/eth_pubsub.rs b/src/components/contracts/rpc/src/eth_pubsub.rs index 40e0b2204..6383ec508 100644 --- a/src/components/contracts/rpc/src/eth_pubsub.rs +++ b/src/components/contracts/rpc/src/eth_pubsub.rs @@ -1,5 +1,5 @@ use baseapp::BaseApp; -use ethereum::{BlockV0 as EthereumBlock, Receipt}; +use ethereum::{BlockV2 as EthereumBlock, Receipt}; use ethereum_types::{H256, U256}; use fp_evm::BlockId; use fp_rpc_core::{ @@ -226,12 +226,15 @@ impl SubscriptionResult { let mut log_index: u32 = 0; for (receipt_index, receipt) in receipts.into_iter().enumerate() { let transaction_hash: Option = if !receipt.logs.is_empty() { - Some(H256::from_slice( - Keccak256::digest(&rlp::encode( - &block.transactions[receipt_index as usize], - )) - .as_slice(), - )) + let transaction = &block.transactions[receipt_index as usize]; + Some(transaction.hash()) + + // Some(H256::from_slice( + // Keccak256::digest(&rlp::encode( + // &block.transactions[receipt_index as usize], + // )) + // .as_slice(), + // )) } else { None }; diff --git a/src/components/contracts/rpc/src/lib.rs b/src/components/contracts/rpc/src/lib.rs index 7ce565f1a..127914b25 100644 --- a/src/components/contracts/rpc/src/lib.rs +++ b/src/components/contracts/rpc/src/lib.rs @@ -103,7 +103,8 @@ mod waiting { fn drop(&mut self) { if let Some(server) = self.0.take() { server.close_handle().close(); - let _ = server.wait(); + // let _ = + server.wait(); } } } diff --git a/src/components/finutils/src/common/utils.rs b/src/components/finutils/src/common/utils.rs index 339b98eb1..cb7cb9ea1 100644 --- a/src/components/finutils/src/common/utils.rs +++ b/src/components/finutils/src/common/utils.rs @@ -53,7 +53,8 @@ pub fn send_tx(tx: &Transaction) -> Result<()> { let tx_bytes = serde_json::to_vec(tx).c(d!())?; - let _ = attohttpc::post(&url) + // let _ = + attohttpc::post(&url) .header(attohttpc::header::CONTENT_TYPE, "application/json") .bytes(&tx_bytes) .send() diff --git a/src/components/finutils/src/txn_builder/mod.rs b/src/components/finutils/src/txn_builder/mod.rs index 485e02b24..2d616031a 100644 --- a/src/components/finutils/src/txn_builder/mod.rs +++ b/src/components/finutils/src/txn_builder/mod.rs @@ -518,6 +518,7 @@ impl TransactionBuilder { /// * `txo_sid` - TxoSID of the BAR to convert /// * `input_record` - OpenAssetRecord of the BAR to convert /// * `enc_key` - XPublicKey of OwnerMemo encryption of receiver + #[allow(clippy::too_many_arguments)] pub fn add_operation_bar_to_abar( &mut self, seed: [u8; 32], @@ -2143,7 +2144,8 @@ mod tests { let uid_fee = ledger_state.add_abar(&fee_abar).unwrap(); ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); - let _ = ledger_state.compute_and_save_state_commitment_data(1); + // let _ = + ledger_state.compute_and_save_state_commitment_data(1); let mt_leaf_info = ledger_state.get_abar_proof(uid).unwrap(); let mt_leaf_fee_info = ledger_state.get_abar_proof(uid_fee).unwrap(); @@ -2322,7 +2324,8 @@ mod tests { // add abar to merkle tree let uid = ledger_state.add_abar(&abar).unwrap(); ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); - let _ = ledger_state.compute_and_save_state_commitment_data(1); + // let _ = + ledger_state.compute_and_save_state_commitment_data(1); let mt_leaf_info = ledger_state.get_abar_proof(uid).unwrap(); oabar.update_mt_leaf_info(mt_leaf_info); @@ -2364,14 +2367,16 @@ mod tests { // add abar to merkle tree let uid1 = ledger_state.add_abar(&abar1).unwrap(); ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); - let _ = ledger_state.compute_and_save_state_commitment_data(2); + // let _ = + ledger_state.compute_and_save_state_commitment_data(2); let mt_leaf_info1 = ledger_state.get_abar_proof(uid1).unwrap(); oabar1.update_mt_leaf_info(mt_leaf_info1); // add abar to merkle tree for negative amount ledger_state.compute_and_append_txns_hash(&BlockEffect::default()); - let _ = ledger_state.compute_and_save_state_commitment_data(2); + // let _ = + ledger_state.compute_and_save_state_commitment_data(2); let (oabar_out1, _keypair_out1, _dec_key_out1, _) = gen_oabar_and_keys(&mut prng1, amount1, asset_type1); diff --git a/src/ledger/src/staking/ops/replace_staker.rs b/src/ledger/src/staking/ops/replace_staker.rs index e4752fbe5..4612a6567 100644 --- a/src/ledger/src/staking/ops/replace_staker.rs +++ b/src/ledger/src/staking/ops/replace_staker.rs @@ -50,7 +50,8 @@ impl ReplaceStakerOps { ///verify the body with the public key pub fn verify(&self) -> Result<()> { if let Some(new_params) = &self.body.new_tendermint_params { - let _ = tendermint::PublicKey::from_raw_ed25519(&new_params.pubkey) + // let _ = + tendermint::PublicKey::from_raw_ed25519(&new_params.pubkey) .c(d!("Invalid tendermint public key."))?; if new_params.address.len() != 20 { diff --git a/src/ledger/src/store/mod.rs b/src/ledger/src/store/mod.rs index 35e947b30..34e1921ba 100644 --- a/src/ledger/src/store/mod.rs +++ b/src/ledger/src/store/mod.rs @@ -601,7 +601,8 @@ impl LedgerState { // Initializing Merkle tree to set Empty tree root hash, which is a hash of null children let store = PrefixedStore::new("abar_store", &mut abar_state); - let _ = PersistentMerkleTree::new(store)?; + // let _ = + PersistentMerkleTree::new(store)?; let mut ledger = LedgerState { status: LedgerStatus::new(&basedir, &snapshot_file).c(d!())?, diff --git a/src/libs/merkle_tree/src/lib.rs b/src/libs/merkle_tree/src/lib.rs index 215798899..a229abb1d 100644 --- a/src/libs/merkle_tree/src/lib.rs +++ b/src/libs/merkle_tree/src/lib.rs @@ -1892,7 +1892,7 @@ impl AppendOnlyMerkle { match self.files[level].read_exact(buffer) { Ok(()) => Ok(mem::transmute::<_, Block>(s)), Err(e) => { - mem::forget(s); + // mem::forget(s); Err(eg!(e)) } }