From aedf08bb95d41f65389c0a4a9ae58609f8f3a2c2 Mon Sep 17 00:00:00 2001 From: mountcount Date: Tue, 9 Apr 2024 14:02:54 +0800 Subject: [PATCH 001/137] chore: remove repetitive words Signed-off-by: mountcount --- .../core/src/pos/consensus/consensus-types/src/block_data.rs | 2 +- crates/cfxcore/core/src/pos/consensus/state_computer.rs | 2 +- .../pos/storage/backup/backup-cli/src/coordinators/backup.rs | 2 +- .../core/src/pos/storage/storage-interface/src/state_view.rs | 2 +- crates/cfxcore/core/src/pos/types/src/account_state_blob.rs | 2 +- crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs | 2 +- .../core/src/pos/types/src/transaction/authenticator.rs | 2 +- crates/cfxcore/core/src/sync/synchronization_graph.rs | 4 ++-- .../storage/src/impls/delta_mpt/cache/algorithm/recent_lfu.rs | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/cfxcore/core/src/pos/consensus/consensus-types/src/block_data.rs b/crates/cfxcore/core/src/pos/consensus/consensus-types/src/block_data.rs index dfc6752aa5..cefcbd6c89 100644 --- a/crates/cfxcore/core/src/pos/consensus/consensus-types/src/block_data.rs +++ b/crates/cfxcore/core/src/pos/consensus/consensus-types/src/block_data.rs @@ -36,7 +36,7 @@ pub enum BlockType { /// A genesis block is the first committed block in any epoch that is /// identically constructed on all validators by any (potentially /// different) LedgerInfo that justifies the epoch change - /// from the previous epoch. The genesis block is used as the the first + /// from the previous epoch. The genesis block is used as the first /// root block of the BlockTree for all epochs. Genesis, } diff --git a/crates/cfxcore/core/src/pos/consensus/state_computer.rs b/crates/cfxcore/core/src/pos/consensus/state_computer.rs index 279e17c262..07fe973abb 100644 --- a/crates/cfxcore/core/src/pos/consensus/state_computer.rs +++ b/crates/cfxcore/core/src/pos/consensus/state_computer.rs @@ -111,7 +111,7 @@ impl StateComputer for ExecutionProxy { }); // Here to start to do state synchronization where ChunkExecutor inside // will process chunks and commit to Storage. However, after - // block execution and commitments, the the sync state of + // block execution and commitments, the sync state of // ChunkExecutor may be not up to date so it is required to // reset the cache of ChunkExecutor in State Sync when requested // to sync. diff --git a/crates/cfxcore/core/src/pos/storage/backup/backup-cli/src/coordinators/backup.rs b/crates/cfxcore/core/src/pos/storage/backup/backup-cli/src/coordinators/backup.rs index bea34ab977..bd736b3b4d 100644 --- a/crates/cfxcore/core/src/pos/storage/backup/backup-cli/src/coordinators/backup.rs +++ b/crates/cfxcore/core/src/pos/storage/backup/backup-cli/src/coordinators/backup.rs @@ -111,7 +111,7 @@ impl BackupCoordinator { // On new DbState retrieved: // `watch_db_state` informs `backup_epoch_endings` via channel 1, - // and the the latter informs the other backup type workers via channel 2, after epoch + // and the latter informs the other backup type workers via channel 2, after epoch // ending is properly backed up, if necessary. This way, the epoch ending LedgerInfo needed // for proof verification is always available in the same backup storage. let (tx1, rx1) = watch::channel::>(None); diff --git a/crates/cfxcore/core/src/pos/storage/storage-interface/src/state_view.rs b/crates/cfxcore/core/src/pos/storage/storage-interface/src/state_view.rs index 430f51eaa7..fcaf2b3592 100644 --- a/crates/cfxcore/core/src/pos/storage/storage-interface/src/state_view.rs +++ b/crates/cfxcore/core/src/pos/storage/storage-interface/src/state_view.rs @@ -48,7 +48,7 @@ pub struct VerifiedStateView<'a> { /// The cache of verified account states from `reader` and /// `speculative_state_view`, represented by a hashmap with an account - /// address as key and a pair of an ordered account state map and an an + /// address as key and a pair of an ordered account state map and an /// optional account state proof as value. When the VM queries an /// `access_path`, this cache will first check whether `reader_cache` is /// hit. If hit, it will return the corresponding value of that diff --git a/crates/cfxcore/core/src/pos/types/src/account_state_blob.rs b/crates/cfxcore/core/src/pos/types/src/account_state_blob.rs index 5c728d2e6e..db9b1251aa 100644 --- a/crates/cfxcore/core/src/pos/types/src/account_state_blob.rs +++ b/crates/cfxcore/core/src/pos/types/src/account_state_blob.rs @@ -165,7 +165,7 @@ impl AccountStateWithProof { } } - /// Verifies the the account state blob with the proof, both carried by + /// Verifies the account state blob with the proof, both carried by /// `self`. /// /// Two things are ensured if no error is raised: diff --git a/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs b/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs index e843e74b0e..8505c7a871 100644 --- a/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs +++ b/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs @@ -140,7 +140,7 @@ impl Position { /// This method takes in a node position and return its sibling position /// /// The observation is that, after stripping out the right-most common bits, - /// two sibling nodes flip the the next right-most bits with each other. + /// two sibling nodes flip the next right-most bits with each other. /// To find out the right-most common bits, first remove all the right-most /// ones because they are corresponding to level's indicator. Then /// remove next zero right after. diff --git a/crates/cfxcore/core/src/pos/types/src/transaction/authenticator.rs b/crates/cfxcore/core/src/pos/types/src/transaction/authenticator.rs index 2c1a0d6f89..4bf2f774dd 100644 --- a/crates/cfxcore/core/src/pos/types/src/transaction/authenticator.rs +++ b/crates/cfxcore/core/src/pos/types/src/transaction/authenticator.rs @@ -27,7 +27,7 @@ use rand::{rngs::OsRng, Rng}; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, str::FromStr}; -/// A `TransactionAuthenticator` is an an abstraction of a signature scheme. It +/// A `TransactionAuthenticator` is an abstraction of a signature scheme. It /// must know: (1) How to check its signature against a message and public key /// (2) How to convert its public key into an `AuthenticationKeyPreimage` /// structured as (public_key | signaure_scheme_id). diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 7314786641..34861a5fa8 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -572,7 +572,7 @@ impl SynchronizationGraphInner { minimal_status, ) { debug!( - "Block {:?} not not ready for its pos_reference: {:?}", + "Block {:?} not ready for its pos_reference: {:?}", self.arena[index].block_header.hash(), self.pos_verifier.get_pivot_decision( self.arena[index] @@ -2092,7 +2092,7 @@ pub enum BlockInsertionResult { // We should request again to get // the correct transactions for full verification. RequestAgain, - // This is only for the case the the header is removed, possibly because + // This is only for the case the header is removed, possibly because // we switch phases. // We ignore the block without verification. Ignored, diff --git a/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/recent_lfu.rs b/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/recent_lfu.rs index 89812e5b2e..4c82fe4533 100644 --- a/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/recent_lfu.rs +++ b/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/recent_lfu.rs @@ -436,7 +436,7 @@ impl CacheAlgorithm evicted_r_lfu_metadata.cache_index; hole.pointer_pos.write(evicted_r_lfu_metadata); - // The caller should read the the returned + // The caller should read the returned // CacheAccessResult and // removes the evicted keys. // set_removed isn't necessary but prevent From 75db0a4b90c83c38d6ba1651670224f1674d1bd0 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Wed, 24 Apr 2024 19:59:12 +0800 Subject: [PATCH 002/137] Add EIP2930 transaction enum. --- .../transaction_pool_inner.rs | 2 +- crates/client/src/rpc/impls/cfx.rs | 10 +- crates/client/src/rpc/types/receipt.rs | 10 +- crates/primitives/src/transaction.rs | 118 +++++++++++++++--- 4 files changed, 114 insertions(+), 26 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index ec206be178..f1952d7884 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -1217,7 +1217,7 @@ impl TransactionPoolInner { } } Transaction::Ethereum(ref utx) => { - need_balance += utx.value.clone(); + need_balance += utx.value().clone(); need_balance += estimate_gas_fee; } } diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 25e5d6df1a..60306f7ef4 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -102,6 +102,7 @@ use cfxcore::{ consensus_parameters::DEFERRED_STATE_EPOCH_COUNT, }; use diem_types::account_address::AccountAddress; +use primitives::transaction::EthereumTransaction; use serde::Serialize; #[derive(Debug)] @@ -1070,7 +1071,14 @@ impl RpcImpl { Transaction::Native(ref mut unsigned) if tx_data_len > 0 => { unsigned.data = vec![0; tx_data_len]; } - Transaction::Ethereum(ref mut unsigned) if tx_data_len > 0 => { + Transaction::Ethereum(EthereumTransaction::Eip155( + ref mut unsigned, + )) if tx_data_len > 0 => { + unsigned.data = vec![0; tx_data_len]; + } + Transaction::Ethereum(EthereumTransaction::Eip2930( + ref mut unsigned, + )) if tx_data_len > 0 => { unsigned.data = vec![0; tx_data_len]; } _ => {} diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index e8bf52312d..f2b3aab66e 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -129,23 +129,23 @@ impl Receipt { } Transaction::Ethereum(ref unsigned) => { if include_eth_receipt { - if Action::Create == unsigned.action + if Action::Create == *unsigned.action() && outcome_status == TransactionStatus::Success { let (created_address, _) = contract_address( CreateContractAddress::FromSenderNonce, 0, &transaction.sender, - &unsigned.nonce, - &unsigned.data, + unsigned.nonce(), + unsigned.data(), ); let address = Some(RpcAddress::try_from_h160( created_address, network, )?); - (address, unsigned.action.clone(), Space::Ethereum) + (address, unsigned.action().clone(), Space::Ethereum) } else { - (None, unsigned.action.clone(), Space::Ethereum) + (None, unsigned.action().clone(), Space::Ethereum) } } else { bail!(format!("Does not support EIP-155 transaction in Conflux space RPC. get_receipt for tx: {:?}",transaction)); diff --git a/crates/primitives/src/transaction.rs b/crates/primitives/src/transaction.rs index ea7d72d971..bdfd9809f2 100644 --- a/crates/primitives/src/transaction.rs +++ b/crates/primitives/src/transaction.rs @@ -314,7 +314,9 @@ impl Eip155Transaction { SignedTransaction { transaction: TransactionWithSignature { transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum(self), + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(self), + ), // we use sender address for `r` and `s` so that phantom // transactions with matching fields from different senders // will have different hashes @@ -338,7 +340,9 @@ impl Eip155Transaction { SignedTransaction { transaction: TransactionWithSignature { transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum(self), + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(self), + ), r: U256::one(), s: U256::one(), v: 0, @@ -418,10 +422,44 @@ impl Encodable for Eip155Transaction { // } // } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Eip2930Transaction { + /// Nonce. + pub nonce: U256, + /// Gas price. + pub gas_price: U256, + /// Gas paid up front for transaction execution. + pub gas: U256, + /// Action, can be either call or contract create. + pub action: Action, + /// Transferred value. + pub value: U256, + /// The chain id of the transaction + pub chain_id: Option, + /// Transaction data. + pub data: Bytes, + pub max_priority_fee_per_gas: U256, + pub max_fee_per_gas: U256, + pub access_list: AccessList, +} + +impl Encodable for Eip2930Transaction { + fn rlp_append(&self, s: &mut RlpStream) { todo!() } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct AccessList {} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum EthereumTransaction { + Eip155(Eip155Transaction), + Eip2930(Eip2930Transaction), +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Transaction { Native(NativeTransaction), - Ethereum(Eip155Transaction), + Ethereum(EthereumTransaction), } impl Default for Transaction { @@ -433,20 +471,49 @@ impl From for Transaction { } impl From for Transaction { - fn from(tx: Eip155Transaction) -> Self { Self::Ethereum(tx) } + fn from(tx: Eip155Transaction) -> Self { + Self::Ethereum(EthereumTransaction::Eip155(tx)) + } } macro_rules! access_common_ref { - ($field:ident, $ty:ident) => { + ($field:ident, $ty:ty) => { pub fn $field(&self) -> &$ty { match self { Transaction::Native(tx) => &tx.$field, - Transaction::Ethereum(tx) => &tx.$field, + Transaction::Ethereum(tx) => &tx.$field(), + } + } + }; +} + +macro_rules! eth_access_common_ref { + ($field:ident, $ty:ty) => { + pub fn $field(&self) -> &$ty { + match self { + EthereumTransaction::Eip155(tx) => &tx.$field, + EthereumTransaction::Eip2930(tx) => &tx.$field, } } }; } +impl EthereumTransaction { + eth_access_common_ref!(gas, U256); + + eth_access_common_ref!(gas_price, U256); + + eth_access_common_ref!(data, Bytes); + + eth_access_common_ref!(nonce, U256); + + eth_access_common_ref!(action, Action); + + eth_access_common_ref!(value, U256); + + eth_access_common_ref!(chain_id, Option); +} + #[allow(unused)] macro_rules! access_common { ($field:ident, $ty:ident) => { @@ -474,7 +541,7 @@ impl Transaction { pub fn chain_id(&self) -> Option { match self { Transaction::Native(tx) => Some(tx.chain_id), - Transaction::Ethereum(tx) => tx.chain_id, + Transaction::Ethereum(tx) => tx.chain_id().clone(), } } @@ -488,7 +555,12 @@ impl Transaction { pub fn nonce_mut(&mut self) -> &mut U256 { match self { Transaction::Native(tx) => &mut tx.nonce, - Transaction::Ethereum(tx) => &mut tx.nonce, + Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { + &mut tx.nonce + } + Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { + &mut tx.nonce + } } } } @@ -503,7 +575,10 @@ impl Transaction { Transaction::Native(tx) => { s.append(tx); } - Transaction::Ethereum(tx) => { + Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { + s.append(tx); + } + Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { s.append(tx); } } @@ -573,7 +648,7 @@ impl Encodable for TransactionWithSignatureSerializePart { s.append(&self.r); s.append(&self.s); } - Transaction::Ethereum(ref tx) => { + Transaction::Ethereum(EthereumTransaction::Eip155(ref tx)) => { let Eip155Transaction { nonce, gas_price, @@ -598,6 +673,9 @@ impl Encodable for TransactionWithSignatureSerializePart { s.append(&self.r); s.append(&self.s); } + Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => { + todo!() + } } } } @@ -642,15 +720,17 @@ impl Decodable for TransactionWithSignatureSerializePart { }; Ok(TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum(Eip155Transaction { - nonce, - gas_price, - gas, - action, - value, - chain_id, - data, - }), + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(Eip155Transaction { + nonce, + gas_price, + gas, + action, + value, + chain_id, + data, + }), + ), v, r, s, From 56901f7934bf9f33a02c0e85cd64aa00efce94ea Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 18 Mar 2024 18:31:43 +0800 Subject: [PATCH 003/137] add fourbyte tracer' --- Cargo.lock | 697 ++++++++++++++++-- Cargo.toml | 5 +- crates/cfxcore/execute-helper/Cargo.toml | 3 +- .../src/observer/exec_tracer/mod.rs | 30 +- .../execute-helper/src/observer/fourbyte.rs | 65 ++ .../execute-helper/src/observer/gasman.rs | 1 + .../execute-helper/src/observer/mod.rs | 1 + 7 files changed, 728 insertions(+), 74 deletions(-) create mode 100644 crates/cfxcore/execute-helper/src/observer/fourbyte.rs diff --git a/Cargo.lock b/Cargo.lock index a2fa89fc7a..9d4fec1182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,38 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-primitives" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +dependencies = [ + "alloy-rlp", + "bytes 1.4.0", + "cfg-if 1.0.0", + "const-hex", + "derive_more", + "hex-literal", + "itoa 1.0.6", + "k256", + "keccak-asm", + "proptest", + "rand 0.8.5", + "ruint", + "serde", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +dependencies = [ + "arrayvec 0.7.4", + "bytes 1.4.0", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -170,6 +202,130 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint 0.4.4", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.4", + "num-traits", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint 0.4.4", + "num-traits", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.4", + "num-traits", + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint 0.4.4", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "array-macro" version = "1.0.5" @@ -264,6 +420,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", +] + [[package]] name = "autocfg" version = "0.1.8" @@ -295,6 +462,12 @@ dependencies = [ "serde", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -338,7 +511,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1374191e2dd25f9ae02e3aa95041ed5d747fc77b3c102b49fe2dd9a8117a6244" dependencies = [ - "num-bigint", + "num-bigint 0.2.6", "num-integer", "num-traits", ] @@ -349,7 +522,7 @@ version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -399,6 +572,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + [[package]] name = "bitvec" version = "0.17.4" @@ -707,11 +886,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -767,6 +947,7 @@ dependencies = [ name = "cfx-execute-helper" version = "2.0.2" dependencies = [ + "alloy-primitives", "cfx-bytes", "cfx-executor", "cfx-internal-common", @@ -1211,7 +1392,7 @@ dependencies = [ "parity-crypto", "parity-secp256k1", "parity-wordlist", - "quick-error 1.2.3", + "quick-error", "rand 0.7.3", "rustc-hex", "serde", @@ -1332,7 +1513,7 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", "textwrap 0.11.0", "unicode-width", @@ -1405,7 +1586,7 @@ dependencies = [ "metrics", "mio 0.6.23", "network", - "num-bigint", + "num-bigint 0.2.6", "order-stat", "parity-version", "parking_lot 0.11.2", @@ -1450,7 +1631,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1556,12 +1737,37 @@ dependencies = [ "short-hex-str", ] +[[package]] +name = "const-hex" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba00838774b4ab0233e355d26710fbfc8327a05c017f6dc4873f876d1f79f78" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex 0.4.3", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.3" @@ -1779,6 +1985,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.6", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1964,7 +2182,17 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4" dependencies = [ - "const-oid", + "const-oid 0.6.2", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid 0.9.6", + "zeroize", ] [[package]] @@ -1978,6 +2206,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2 1.0.71", + "quote 1.0.33", + "rustc_version 0.4.0", + "syn 1.0.109", +] + [[package]] name = "destructure_traitobject" version = "0.2.0" @@ -2034,7 +2275,7 @@ dependencies = [ "once_cell", "openssl", "parking_lot 0.11.2", - "pkcs8", + "pkcs8 0.7.6", "proptest", "proptest-derive", "rand 0.8.5", @@ -2335,11 +2576,12 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid 0.9.6", "crypto-common", "subtle", ] @@ -2386,6 +2628,20 @@ dependencies = [ "strsim 0.10.0", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki 0.7.3", +] + [[package]] name = "ed25519" version = "1.5.3" @@ -2393,7 +2649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "serde", - "signature", + "signature 1.6.4", ] [[package]] @@ -2423,6 +2679,25 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array 0.14.6", + "group", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -2499,6 +2774,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "erased-serde" version = "0.3.25" @@ -2735,6 +3016,17 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec 0.7.4", + "auto_impl", + "bytes 1.4.0", +] + [[package]] name = "ff" version = "0.13.0" @@ -2789,6 +3081,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fixedbitset" version = "0.2.0" @@ -2865,7 +3169,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags", + "bitflags 1.3.2", "fuchsia-zircon-sys", ] @@ -3034,6 +3338,7 @@ checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -3114,7 +3419,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -3164,7 +3469,7 @@ dependencies = [ "fnv", "futures 0.1.31", "http 0.1.21", - "indexmap", + "indexmap 1.9.2", "log", "slab", "string", @@ -3183,7 +3488,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.9", - "indexmap", + "indexmap 1.9.2", "slab", "tokio 1.26.0", "tokio-util", @@ -3218,6 +3523,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heap-map" version = "0.1.0" @@ -3336,7 +3647,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -3438,7 +3749,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -3589,6 +3900,15 @@ dependencies = [ "parity-scale-codec 2.3.1", ] +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec 3.6.9", +] + [[package]] name = "impl-rlp" version = "0.2.1" @@ -3681,6 +4001,16 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "influx_db_client" version = "0.5.1" @@ -3944,6 +4274,20 @@ dependencies = [ "slab", ] +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2 0.10.6", + "signature 2.2.0", +] + [[package]] name = "keccak" version = "0.1.3" @@ -3953,6 +4297,16 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "keccak-hash" version = "0.5.1" @@ -4595,7 +4949,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ - "num-bigint", + "num-bigint 0.2.6", "num-complex", "num-integer", "num-iter", @@ -4614,6 +4968,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -4663,16 +5028,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg 1.1.0", - "num-bigint", + "num-bigint 0.2.6", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg 1.1.0", "libm", @@ -4746,7 +5111,7 @@ version = "0.10.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b277f87dacc05a6b709965d1cbafac4649d6ce9f3ce9ceb88508b5666dfec9" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "foreign-types", "libc", @@ -4878,7 +5243,21 @@ dependencies = [ "bitvec 0.20.4", "byte-slice-cast 1.2.2", "impl-trait-for-tuples 0.2.2", - "parity-scale-codec-derive", + "parity-scale-codec-derive 2.3.1", + "serde", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +dependencies = [ + "arrayvec 0.7.4", + "bitvec 1.0.1", + "byte-slice-cast 1.2.2", + "impl-trait-for-tuples 0.2.2", + "parity-scale-codec-derive 3.6.9", "serde", ] @@ -4894,6 +5273,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "parity-secp256k1" version = "0.7.0" @@ -5077,6 +5468,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "path-absolutize" version = "3.0.14" @@ -5138,7 +5535,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -5159,6 +5556,17 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + [[package]] name = "petgraph" version = "0.5.1" @@ -5166,7 +5574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 1.9.2", ] [[package]] @@ -5226,12 +5634,12 @@ checksum = "5423c5ee112e07717eb3634de80e5d34f0019dd65f3eead74028d9c579ce7dd4" dependencies = [ "aes 0.7.5", "block-modes 0.8.1", - "der", + "der 0.4.5", "hmac 0.11.0", "pbkdf2 0.9.0", "scrypt 0.8.1", "sha2 0.9.9", - "spki", + "spki 0.4.1", ] [[package]] @@ -5240,13 +5648,23 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447" dependencies = [ - "der", + "der 0.4.5", "pkcs5", "rand_core 0.6.4", - "spki", + "spki 0.4.1", "zeroize", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.3", +] + [[package]] name = "pkg-config" version = "0.3.26" @@ -5429,6 +5847,17 @@ dependencies = [ "uint 0.9.5", ] +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash 0.8.0", + "impl-codec 0.6.0", + "uint 0.9.5", +] + [[package]] name = "primitives" version = "0.2.0" @@ -5474,7 +5903,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.7", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -5528,7 +5967,7 @@ dependencies = [ "cfg-if 0.1.10", "fnv", "lazy_static", - "quick-error 1.2.3", + "quick-error", "spin", ] @@ -5548,20 +5987,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set 0.5.3", - "bitflags", - "byteorder", + "bit-vec 0.6.3", + "bitflags 2.4.2", "lazy_static", "num-traits", - "quick-error 2.0.1", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift 0.3.0", - "regex-syntax", + "regex-syntax 0.8.2", "rusty-fork", "tempfile", "unarray", @@ -5600,12 +6038,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quote" version = "0.6.13" @@ -5903,7 +6335,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -5951,7 +6383,7 @@ checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -5960,6 +6392,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -6006,6 +6444,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -6081,6 +6529,36 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ruint" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f308135fef9fc398342da5472ce7c484529df23743fb7c734e0f3d472971e62" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes 1.4.0", + "fastrlp", + "num-bigint 0.4.4", + "num-traits", + "parity-scale-codec 3.6.9", + "primitive-types 0.12.2", + "proptest", + "rand 0.8.5", + "rlp 0.5.2", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86854cf50259291520509879a5c294c3c9a4c334e9ff65071c51e42ef1e2343" + [[package]] name = "runtime" version = "0.1.0" @@ -6116,6 +6594,15 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -6131,7 +6618,7 @@ version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -6152,7 +6639,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] @@ -6302,6 +6789,20 @@ dependencies = [ "sha2 0.10.6", ] +[[package]] +name = "sec1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48518a2b5775ba8ca5b46596aae011caa431e6ce7e4a67ead66d92f08884220e" +dependencies = [ + "base16ct", + "der 0.7.8", + "generic-array 0.14.6", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + [[package]] name = "secp256k1" version = "0.20.3" @@ -6340,7 +6841,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -6363,7 +6864,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -6378,6 +6888,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.193" @@ -6466,7 +6985,7 @@ version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ - "indexmap", + "indexmap 1.9.2", "ryu", "serde", "yaml-rust 0.4.5", @@ -6527,7 +7046,7 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6542,6 +7061,16 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if 1.0.0", +] + [[package]] name = "sha3-macro" version = "0.1.0" @@ -6595,6 +7124,16 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -6689,7 +7228,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32" dependencies = [ - "der", + "der 0.4.5", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der 0.7.8", ] [[package]] @@ -7433,9 +7982,20 @@ version = "0.19.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" dependencies = [ - "indexmap", + "indexmap 1.9.2", "toml_datetime", - "winnow", + "winnow 0.3.6", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow 0.5.40", ] [[package]] @@ -7561,6 +8121,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "uint" version = "0.8.5" @@ -7727,6 +8293,12 @@ dependencies = [ "percent-encoding 2.2.0", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -8050,6 +8622,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -8136,9 +8717,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.5.7" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 8911aa4b0e..9f5c4ad8e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,4 +81,7 @@ overflow-checks = true [profile.release] overflow-checks = true # Temporarily run with debug assertion before main-net release. -debug-assertions = true \ No newline at end of file +debug-assertions = true + +[workspace.dependencies] +alloy-primitives = "0.6" \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 2716d7de84..7734fca432 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -48,4 +48,5 @@ strum_macros = "0.20" pow-types = {path = "../core/src/pos/types/pow-types" } # impl-trait-for-tuples = "^0.2" # impl-tools = "^0.10" -typemap = "0.3" \ No newline at end of file +typemap = "0.3" +alloy-primitives.workspace = true \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs index 9cc11808e4..44c083958c 100644 --- a/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs @@ -42,6 +42,20 @@ pub struct ExecTracer { valid_indices: CheckpointLog, } +impl ExecTracer { + pub fn drain(self) -> Vec { + let mut validity: Vec = vec![false; self.traces.len()]; + for index in self.valid_indices.drain() { + validity[index] = true; + } + self.traces + .into_iter() + .zip(validity.into_iter()) + .map(|(action, valid)| ExecTrace { action, valid }) + .collect() + } +} + impl DrainTrace for ExecTracer { fn drain_trace(self, map: &mut ShareDebugMap) { map.insert::(self.drain()); @@ -49,6 +63,7 @@ impl DrainTrace for ExecTracer { } pub struct ExecTraceKey; + impl typemap::Key for ExecTraceKey { type Value = Vec; } @@ -138,18 +153,5 @@ impl CallTracer for ExecTracer { } impl OpcodeTracer for ExecTracer {} -impl StorageTracer for ExecTracer {} -impl ExecTracer { - pub fn drain(self) -> Vec { - let mut validity: Vec = vec![false; self.traces.len()]; - for index in self.valid_indices.drain() { - validity[index] = true; - } - self.traces - .into_iter() - .zip(validity.into_iter()) - .map(|(action, valid)| ExecTrace { action, valid }) - .collect() - } -} +impl StorageTracer for ExecTracer {} diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs new file mode 100644 index 0000000000..1c9ce0cacb --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs @@ -0,0 +1,65 @@ +//! Fourbyte tracing inspector +//! +//! Solidity contract functions are addressed using the first four byte of the +//! Keccak-256 hash of their signature. Therefore when calling the function of a +//! contract, the caller must send this function selector as well as the +//! ABI-encoded arguments as call data. +//! +//! The 4byteTracer collects the function selectors of every function executed +//! in the lifetime of a transaction, along with the size of the supplied call +//! data. The result is a map of SELECTOR-CALLDATASIZE to number of occurrences +//! entries, where the keys are SELECTOR-CALLDATASIZE and the values are number +//! of occurrences of this key. For example: +//! +//! ```json +//! { +//! "0x27dc297e-128": 1, +//! "0x38cc4831-0": 2, +//! "0x524f3889-96": 1, +//! "0xadf59f99-288": 1, +//! "0xc281d19e-0": 1 +//! } +//! ``` + +use alloy_primitives::Selector; +use cfx_executor::{ + executive_observer::{ + CallTracer, CheckpointTracer, InternalTransferTracer, + }, + observer::{OpcodeTracer, StorageTracer}, +}; +use cfx_vm_types::ActionParams; +use std::{collections::HashMap, convert::TryFrom}; + +/// Fourbyte tracing inspector that records all function selectors and their +/// calldata sizes. +#[derive(Clone, Debug, Default)] +pub struct FourByteInspector { + /// The map of SELECTOR to number of occurrences entries + inner: HashMap<(Selector, usize), u64>, +} + +impl FourByteInspector { + /// Returns the map of SELECTOR to number of occurrences entries + pub const fn inner(&self) -> &HashMap<(Selector, usize), u64> { + &self.inner + } +} + +impl CallTracer for FourByteInspector { + fn record_call(&mut self, params: &ActionParams) { + if let Some(input) = ¶ms.data { + if input.len() > 4 { + let selector = Selector::try_from(&input[..4]) + .expect("input is at least 4 bytes"); + let calldata_size = input[4..].len(); + *self.inner.entry((selector, calldata_size)).or_default() += 1; + } + } + } +} + +impl CheckpointTracer for FourByteInspector {} +impl InternalTransferTracer for FourByteInspector {} +impl StorageTracer for FourByteInspector {} +impl OpcodeTracer for FourByteInspector {} diff --git a/crates/cfxcore/execute-helper/src/observer/gasman.rs b/crates/cfxcore/execute-helper/src/observer/gasman.rs index 3e6260abc6..a5637a43de 100644 --- a/crates/cfxcore/execute-helper/src/observer/gasman.rs +++ b/crates/cfxcore/execute-helper/src/observer/gasman.rs @@ -59,6 +59,7 @@ impl DrainTrace for GasMan { } pub struct GasLimitEstimation; + impl typemap::Key for GasLimitEstimation { type Value = U256; } diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index 77227600f3..2f6e2e53cc 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -1,4 +1,5 @@ pub mod exec_tracer; +pub mod fourbyte; pub mod gasman; pub mod geth_tracer; mod utils; From 3d02c18b26fe162b87839ce8b74e375361b49a7c Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 18 Mar 2024 19:05:13 +0800 Subject: [PATCH 004/137] the first initial version --- Cargo.lock | 567 ++- Cargo.toml | 12 +- bins/conflux/src/command/helpers.rs | 4 +- crates/cfx_store/src/account/mod.rs | 1 + crates/cfx_store/src/json/mod.rs | 1 + crates/cfxcore/core/Cargo.toml | 1 + .../consensus_inner/consensus_executor.rs | 274 +- crates/cfxcore/core/src/consensus/mod.rs | 25 + .../transaction_pool_inner.rs | 1 + crates/cfxcore/execute-helper/Cargo.toml | 13 +- .../src/observer/exec_tracer/mod.rs | 8 +- .../execute-helper/src/observer/fourbyte.rs | 49 +- .../execute-helper/src/observer/gasman.rs | 4 +- .../src/observer/geth_tracer/arena.rs | 102 + .../src/observer/geth_tracer/builder/geth.rs | 393 ++ .../src/observer/geth_tracer/builder/mod.rs | 1 + .../src/observer/geth_tracer/config.rs | 324 ++ .../src/observer/geth_tracer/db_adapter.rs | 127 + .../src/observer/geth_tracer/gas.rs | 21 + .../src/observer/geth_tracer/mod.rs | 704 ++- .../src/observer/geth_tracer/types.rs | 733 +++ .../src/observer/geth_tracer/utils.rs | 154 + .../execute-helper/src/observer/mod.rs | 12 +- .../cfxcore/execute-helper/src/tx_outcome.rs | 17 +- crates/cfxcore/executor/Cargo.toml | 2 +- crates/cfxcore/executor/src/context.rs | 19 +- .../components/activation.rs | 1 + .../src/internal_contract/components/mod.rs | 1 + crates/cfxcore/executor/src/machine/mod.rs | 16 +- .../executor/src/machine/vm_factory.rs | 8 +- .../src/observer/internal_transfer_tracer.rs | 2 +- .../executor/src/observer/opcode_tracer.rs | 39 +- .../executor/src/observer/storage_tracer.rs | 4 +- .../executor/src/observer/tracer_trait.rs | 7 +- .../cfxcore/executor/src/stack/executable.rs | 7 +- .../cfxcore/executor/src/stack/frame_start.rs | 2 +- .../src/state/state_object/collateral.rs | 2 +- crates/cfxcore/vm-interpreter/Cargo.toml | 2 +- .../vm-interpreter/src/instructions.rs | 13 +- .../vm-interpreter/src/interpreter/mod.rs | 103 +- .../vm-interpreter/src/interpreter/stack.rs | 2 + crates/cfxcore/vm-interpreter/src/lib.rs | 3 +- crates/cfxcore/vm-types/Cargo.toml | 2 +- .../cfxcore/vm-types/src/call_create_type.rs | 2 +- crates/cfxcore/vm-types/src/context.rs | 12 +- crates/cfxcore/vm-types/src/error.rs | 2 +- .../vm-types/src/instruction_result.rs | 23 + .../cfxcore/vm-types/src/interpreter_info.rs | 22 + crates/cfxcore/vm-types/src/lib.rs | 4 + crates/client/Cargo.toml | 1 + crates/client/src/rpc.rs | 15 +- crates/client/src/rpc/impls/eth.rs | 3 + crates/client/src/rpc/impls/eth/debug.rs | 88 + crates/client/src/rpc/rpc_apis.rs | 7 +- .../client/src/rpc/traits/eth_space/debug.rs | 21 + crates/client/src/rpc/traits/eth_space/mod.rs | 1 + crates/rpc_types_trace/Cargo.toml | 22 + crates/rpc_types_trace/src/block.rs | 47 + crates/rpc_types_trace/src/common.rs | 36 + crates/rpc_types_trace/src/geth/call.rs | 138 + crates/rpc_types_trace/src/geth/four_byte.rs | 42 + crates/rpc_types_trace/src/geth/mod.rs | 627 +++ crates/rpc_types_trace/src/geth/mux.rs | 141 + crates/rpc_types_trace/src/geth/noop.rs | 35 + crates/rpc_types_trace/src/geth/pre_state.rs | 384 ++ crates/rpc_types_trace/src/lib.rs | 5 + crates/rpc_types_trace/src/parity.rs | 761 +++ crates/rpc_types_trace/src/state.rs | 120 + .../test_data/call_tracer/default.json | 21 + .../test_data/call_tracer/legacy.json | 19 + .../test_data/call_tracer/only_top_call.json | 10 + .../test_data/call_tracer/with_log.json | 20 + .../test_data/default/structlogs_01.json | 4112 +++++++++++++++++ .../test_data/pre_state_tracer/default.json | 20 + .../test_data/pre_state_tracer/diff_mode.json | 41 + .../test_data/pre_state_tracer/legacy.json | 25 + crates/serde/Cargo.toml | 19 + crates/serde/src/lib.rs | 18 + crates/serde/src/num.rs | 46 + crates/util/cfx-vm-tracer-derive/Cargo.toml | 2 +- rust-toolchain | 2 +- 81 files changed, 10548 insertions(+), 149 deletions(-) create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs create mode 100644 crates/cfxcore/vm-types/src/instruction_result.rs create mode 100644 crates/cfxcore/vm-types/src/interpreter_info.rs create mode 100644 crates/client/src/rpc/impls/eth/debug.rs create mode 100644 crates/client/src/rpc/traits/eth_space/debug.rs create mode 100644 crates/rpc_types_trace/Cargo.toml create mode 100644 crates/rpc_types_trace/src/block.rs create mode 100644 crates/rpc_types_trace/src/common.rs create mode 100644 crates/rpc_types_trace/src/geth/call.rs create mode 100644 crates/rpc_types_trace/src/geth/four_byte.rs create mode 100644 crates/rpc_types_trace/src/geth/mod.rs create mode 100644 crates/rpc_types_trace/src/geth/mux.rs create mode 100644 crates/rpc_types_trace/src/geth/noop.rs create mode 100644 crates/rpc_types_trace/src/geth/pre_state.rs create mode 100644 crates/rpc_types_trace/src/lib.rs create mode 100644 crates/rpc_types_trace/src/parity.rs create mode 100644 crates/rpc_types_trace/src/state.rs create mode 100644 crates/rpc_types_trace/test_data/call_tracer/default.json create mode 100644 crates/rpc_types_trace/test_data/call_tracer/legacy.json create mode 100644 crates/rpc_types_trace/test_data/call_tracer/only_top_call.json create mode 100644 crates/rpc_types_trace/test_data/call_tracer/with_log.json create mode 100644 crates/rpc_types_trace/test_data/default/structlogs_01.json create mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/default.json create mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json create mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json create mode 100644 crates/serde/Cargo.toml create mode 100644 crates/serde/src/lib.rs create mode 100644 crates/serde/src/num.rs diff --git a/Cargo.lock b/Cargo.lock index 9d4fec1182..121d792ada 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -119,6 +119,18 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -128,6 +140,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "alloy-primitives" version = "0.6.4" @@ -156,10 +174,88 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ + "alloy-rlp-derive", "arrayvec 0.7.4", "bytes 1.4.0", ] +[[package]] +name = "alloy-rlp-derive" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" +dependencies = [ + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", +] + +[[package]] +name = "alloy-rpc-trace-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "alloy-serde", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "itertools 0.12.1", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" +dependencies = [ + "const-hex", + "dunce", + "heck 0.4.1", + "indexmap 2.2.5", + "proc-macro-error", + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", + "syn-solidity", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "alloy-sol-types" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -420,6 +516,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "aurora-engine-modexp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfacad86e9e138fca0670949eb8ed4ffdf73a55bded8887efe0863cd1a3a6f70" +dependencies = [ + "hex 0.4.3", + "num 0.4.1", +] + [[package]] name = "auto_impl" version = "1.2.0" @@ -574,9 +680,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitvec" @@ -772,6 +878,17 @@ dependencies = [ "tokio 1.26.0", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", +] + [[package]] name = "bstr" version = "1.4.0" @@ -844,6 +961,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "bzip2-sys" @@ -856,6 +976,20 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3130f3d8717cc02e668a896af24984d5d5d4e8bf12e278e982e0f1bd88a0f9af" +dependencies = [ + "blst", + "cc", + "glob", + "hex 0.4.3", + "libc", + "serde", +] + [[package]] name = "c_linked_list" version = "1.1.1" @@ -948,12 +1082,15 @@ name = "cfx-execute-helper" version = "2.0.2" dependencies = [ "alloy-primitives", + "alloy-rpc-trace-types", + "alloy-sol-types", "cfx-bytes", "cfx-executor", "cfx-internal-common", "cfx-parameters", "cfx-statedb", "cfx-types", + "cfx-vm-interpreter", "cfx-vm-tracer-derive", "cfx-vm-types", "error-chain", @@ -962,10 +1099,12 @@ dependencies = [ "malloc_size_of_derive", "pow-types", "primitives", + "revm", "rlp 0.4.6", "rlp_derive", "serde", "serde_derive", + "serde_json", "solidity-abi", "strum_macros 0.20.1", "typemap", @@ -998,7 +1137,7 @@ dependencies = [ "log", "malloc_size_of", "malloc_size_of_derive", - "num", + "num 0.2.1", "parity-crypto", "parking_lot 0.11.2", "pow-types", @@ -1014,7 +1153,7 @@ dependencies = [ "solidity-abi-derive", "strum 0.20.0", "strum_macros 0.20.1", - "substrate-bn", + "substrate-bn 0.6.0 (git+https://github.com/paritytech/bn?rev=63f8c587356a67b33c7396af98e065b66fca5dda)", "tiny-keccak 2.0.2", "typemap", ] @@ -1046,7 +1185,7 @@ version = "0.1.0" dependencies = [ "cfx-types", "criterion", - "num", + "num 0.2.1", "rand 0.8.5", "rand_xorshift 0.3.0", "static_assertions", @@ -1084,6 +1223,16 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "cfx-serde" +version = "2.0.2" +dependencies = [ + "alloy-primitives", + "cfx-types", + "serde", + "serde_json", +] + [[package]] name = "cfx-statedb" version = "1.0.0" @@ -1227,6 +1376,7 @@ dependencies = [ name = "cfxcore" version = "2.0.2" dependencies = [ + "alloy-rpc-trace-types", "anyhow", "async-oneshot", "async-trait", @@ -1302,7 +1452,7 @@ dependencies = [ "mirai-annotations", "move-core-types", "network", - "num", + "num 0.2.1", "num-derive", "num-traits", "once_cell", @@ -1346,7 +1496,7 @@ dependencies = [ "strum 0.20.0", "strum_macros 0.20.1", "subscription-service", - "substrate-bn", + "substrate-bn 0.6.0 (git+https://github.com/paritytech/bn?rev=63f8c587356a67b33c7396af98e065b66fca5dda)", "tempdir", "thiserror", "threadpool", @@ -1525,6 +1675,7 @@ dependencies = [ name = "client" version = "2.0.2" dependencies = [ + "alloy-rpc-trace-types", "anyhow", "app_dirs", "bigdecimal", @@ -1737,6 +1888,18 @@ dependencies = [ "short-hex-str", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "const-hex" version = "1.11.3" @@ -2628,6 +2791,18 @@ dependencies = [ "strsim 0.10.0", ] +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "ecdsa" version = "0.16.9" @@ -2698,6 +2873,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -2761,6 +2942,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "enumn" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +dependencies = [ + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", +] + [[package]] name = "env_logger" version = "0.5.13" @@ -3439,7 +3631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr", + "bstr 1.4.0", "fnv", "log", "regex", @@ -3513,7 +3705,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" dependencies = [ - "ahash", + "ahash 0.3.8", "autocfg 1.1.0", ] @@ -3528,6 +3720,10 @@ name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] [[package]] name = "heap-map" @@ -3548,6 +3744,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -3583,6 +3785,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -4101,6 +4306,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -4276,9 +4490,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if 1.0.0", "ecdsa", @@ -4950,10 +5164,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" dependencies = [ "num-bigint 0.2.6", - "num-complex", + "num-complex 0.2.4", + "num-integer", + "num-iter", + "num-rational 0.2.4", + "num-traits", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint 0.4.4", + "num-complex 0.4.5", "num-integer", "num-iter", - "num-rational", + "num-rational 0.4.1", "num-traits", ] @@ -4989,6 +5217,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -5033,6 +5270,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg 1.1.0", + "num-bigint 0.4.4", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -5083,9 +5332,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -5205,7 +5454,7 @@ dependencies = [ "ripemd160", "rustc-hex", "scrypt 0.5.0", - "secp256k1", + "secp256k1 0.20.3", "sha2 0.9.9", "subtle", "tiny-keccak 2.0.2", @@ -5993,7 +6242,7 @@ checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set 0.5.3", "bit-vec 0.6.3", - "bitflags 2.4.2", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -6386,6 +6635,12 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -6444,6 +6699,66 @@ dependencies = [ "winreg", ] +[[package]] +name = "revm" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fd3ed4b62dc61c647552d8b781811ae25ec74d23309055077e4dfb392444d2" +dependencies = [ + "auto_impl", + "cfg-if 1.0.0", + "dyn-clone", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0a1818f8c876b0d71a0714217c34da7df8a42c0462750768779d55680e4554" +dependencies = [ + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9645a70f1df1e5bd7fa8718b9ba486fac9c3f0467aa6b58e7f590d5f6fd0f7" +dependencies = [ + "aurora-engine-modexp", + "c-kzg", + "k256", + "once_cell", + "revm-primitives", + "ripemd", + "secp256k1 0.28.2", + "sha2 0.10.6", + "substrate-bn 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "revm-primitives" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323ad597cf75ac9cb1d161be29fcc3562426f0278a1d04741697fca556e1ceea" +dependencies = [ + "alloy-primitives", + "auto_impl", + "bitflags 2.5.0", + "bitvec 1.0.1", + "cfg-if 1.0.0", + "dyn-clone", + "enumn", + "hashbrown 0.14.3", + "hex 0.4.3", + "serde", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -6454,6 +6769,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -6529,6 +6853,19 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rpc-types-trace" +version = "2.0.2" +dependencies = [ + "alloy-primitives", + "cfx-bytes", + "cfx-serde", + "cfx-types", + "serde", + "serde_json", + "similar-asserts", +] + [[package]] name = "ruint" version = "1.12.1" @@ -6810,7 +7147,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" dependencies = [ "rand 0.6.5", - "secp256k1-sys", + "secp256k1-sys 0.4.2", +] + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "rand 0.8.5", + "secp256k1-sys 0.9.2", ] [[package]] @@ -6822,6 +7169,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + [[package]] name = "secret-store" version = "0.1.0" @@ -6962,6 +7318,7 @@ version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ + "indexmap 1.9.2", "itoa 1.0.6", "ryu", "serde", @@ -7134,6 +7491,26 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" +dependencies = [ + "bstr 0.2.17", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" +dependencies = [ + "console", + "similar", +] + [[package]] name = "siphasher" version = "0.3.10" @@ -7339,7 +7716,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8baacebd7b7c9b864d83a6ba7a246232983e277b86fa5cdec77f565715a4b136" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 0.4.30", "quote 0.6.13", "syn 0.15.44", @@ -7351,7 +7728,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2 1.0.71", "quote 1.0.33", "syn 1.0.109", @@ -7366,6 +7743,19 @@ dependencies = [ "diem-types", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + [[package]] name = "substrate-bn" version = "0.6.0" @@ -7417,6 +7807,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" +dependencies = [ + "paste", + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -8529,7 +8931,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -8538,13 +8940,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -8553,7 +8955,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -8562,13 +8973,29 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -8577,42 +9004,90 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "winnow" version = "0.3.6" @@ -8715,6 +9190,26 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 9f5c4ad8e1..72f6b93bb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,9 +51,13 @@ members = [ "crates/util/solidity-abi-derive", "crates/util/throttling", "crates/util/treap-map", - "crates/util/version" + "crates/util/version", + "crates/serde", + "crates/rpc_types_trace", ] +resolver = "2" + [workspace.package] version = "2.0.2" authors = ["peilun-conflux", "ChenxingLi"] @@ -84,4 +88,8 @@ overflow-checks = true debug-assertions = true [workspace.dependencies] -alloy-primitives = "0.6" \ No newline at end of file +serde_derive = "1.0" +serde = { version = "1.0", features = ["derive", "alloc"] } +serde_json = "1.0" +alloy-primitives = "0.6" +alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "410850b" } \ No newline at end of file diff --git a/bins/conflux/src/command/helpers.rs b/bins/conflux/src/command/helpers.rs index a41a5cef2a..195d68118a 100644 --- a/bins/conflux/src/command/helpers.rs +++ b/bins/conflux/src/command/helpers.rs @@ -19,14 +19,14 @@ // See http://www.gnu.org/licenses/ use cfxkey::Password; +#[allow(unused_imports)] +pub use dir::helpers::{replace_home, replace_home_and_local}; use rpassword::read_password; use std::{ fs::File, io::{self, BufRead, BufReader, Write}, }; -pub use dir::helpers::{replace_home, replace_home_and_local}; - const PASSWORD_STDIN_ERROR: &str = "Unable to ask for password on non-interactive terminal."; diff --git a/crates/cfx_store/src/account/mod.rs b/crates/cfx_store/src/account/mod.rs index e6225fd60f..1b45f4e231 100644 --- a/crates/cfx_store/src/account/mod.rs +++ b/crates/cfx_store/src/account/mod.rs @@ -20,6 +20,7 @@ mod kdf; mod safe_account; mod version; +#[allow(unused_imports)] pub use self::{ cipher::{Aes128Ctr, Cipher}, crypto::Crypto, diff --git a/crates/cfx_store/src/json/mod.rs b/crates/cfx_store/src/json/mod.rs index 6e811bb204..d8a1c0b29d 100644 --- a/crates/cfx_store/src/json/mod.rs +++ b/crates/cfx_store/src/json/mod.rs @@ -29,6 +29,7 @@ mod vault_file; mod vault_key_file; mod version; +#[allow(unused_imports)] pub use self::{ bytes::Bytes, cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams}, diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index 0bcf21f39e..6e279d9aa9 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -137,6 +137,7 @@ impl-trait-for-tuples = "^0.2" impl-tools = "^0.10" treap-map = {path = "../../util/treap-map" } cfx-packing-pool = { path = "../packing-pool" } +alloy-rpc-trace-types.workspace = true [dev-dependencies] diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index d913aff919..0a9673140a 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -62,7 +62,7 @@ use crate::{ use cfx_execute_helper::{ estimation::{EstimateExt, EstimateRequest, EstimationContext}, exec_tracer::TransactionExecTraces, - observer::Observer, + observer::{geth_tracer::TracingInspectorConfig, Observer}, tx_outcome::make_process_tx_outcome, }; use cfx_executor::{ @@ -79,6 +79,11 @@ use cfx_executor::{ }; use cfx_vm_types::{Env, Spec}; +use alloy_rpc_trace_types::geth::{ + GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, + GethTrace, +}; + lazy_static! { static ref CONSENSIS_EXECUTION_TIMER: Arc = register_meter_with_group("timer", "consensus::handle_epoch_execution"); @@ -646,6 +651,18 @@ impl ConsensusExecutor { self.handler.call_virtual(tx, epoch_id, epoch_size, request) } + pub fn collect_epoch_geth_trace( + &self, epoch_num: u64, epoch_block_hashes: Vec, + tx_hash: Option, opts: GethDebugTracingOptions, + ) -> RpcResult> { + self.handler.collect_epoch_geth_trace( + epoch_num, + epoch_block_hashes, + tx_hash, + opts, + ) + } + pub fn stop(&self) { // `stopped` is used to allow the execution thread to stopped even the // queue is not empty and `ExecutionTask::Stop` has not been @@ -1916,6 +1933,261 @@ impl ConsensusExecutionHandler { Ok(r?) } + pub fn collect_epoch_geth_trace( + &self, epoch_num: u64, epoch_block_hashes: Vec, + tx_hash: Option, opts: GethDebugTracingOptions, + ) -> RpcResult> { + let epoch_id = epoch_block_hashes.last().expect("Not empty"); + let _ = epoch_num; + // let epoch_size = epoch_block_hashes.len(); + // let _ = epoch_size; + + // Get blocks in this epoch after skip checking + let epoch_blocks = self + .data_man + .blocks_by_hash_list( + &epoch_block_hashes, + true, /* update_cache */ + ) + .expect("blocks exist"); + let pivot_block = epoch_blocks.last().expect("Not empty"); + + let recover_mpt_during_construct_pivot_state = false; + let mut state = State::new(StateDb::new( + self.data_man + .storage_manager + .get_state_for_next_epoch( + StateIndex::new_for_next_epoch( + pivot_block.block_header.parent_hash(), + &self + .data_man + .get_epoch_execution_commitment( + pivot_block.block_header.parent_hash(), + ) + // Unwrapping is safe because the state exists. + .unwrap() + .state_root_with_aux_info, + pivot_block.block_header.height() - 1, + self.data_man.get_snapshot_epoch_count(), + ), + recover_mpt_during_construct_pivot_state, + ) + .expect("No db error") + // Unwrapping is safe because the state exists. + .expect("State exists"), + )) + .expect("Failed to initialize state"); + + let start_block_number = self + .data_man + .get_epoch_execution_context(&epoch_id) + .map(|v| v.start_block_number) + .expect("should exist"); + + self.execute_epoch_tx_to_collect_trace( + *epoch_id, + &mut state, + &epoch_blocks, + start_block_number, + tx_hash, + opts, + ) + .map_err(|err| err.into()) + } + + fn execute_epoch_tx_to_collect_trace( + &self, epoch_id: EpochId, state: &mut State, + epoch_blocks: &Vec>, start_block_number: u64, + tx_hash: Option, opts: GethDebugTracingOptions, + ) -> DbResult> { + // Prefetch accounts for transactions. + // The return value _prefetch_join_handles is used to join all threads + // before the exit of this function. + let prefetch_join_handles = match self + .execution_state_prefetcher + .as_ref() + { + Some(prefetcher) => { + let mut accounts = vec![]; + for block in epoch_blocks.iter() { + for transaction in block.transactions.iter() { + accounts.push(&transaction.sender); + match transaction.action() { + Action::Call(ref address) => accounts.push(address), + _ => {} + } + } + } + + prefetch_accounts(prefetcher, epoch_id, state, accounts) + } + None => PrefetchTaskHandle { + task_epoch_id: epoch_id, + state, + prefetcher: None, + accounts: vec![], + }, + }; + + prefetch_join_handles.wait_for_task(); + drop(prefetch_join_handles); + + // TODO whether need to revert the state change after the execution + + let pivot_block = epoch_blocks.last().expect("Epoch not empty"); + + let mut block_number = start_block_number; + let mut last_block_hash = + pivot_block.block_header.parent_hash().clone(); + let last_block_header = + &self.data_man.block_header_by_hash(&last_block_hash); + + let mut traces = Vec::new(); + + for block in epoch_blocks.iter() { + self.maybe_update_state(state, block_number)?; + + let pos_id = last_block_header + .as_ref() + .and_then(|header| header.pos_reference().as_ref()); + let pos_view_number = + pos_id.and_then(|id| self.pos_verifier.get_pos_view(id)); + let pivot_decision_epoch = pos_id + .and_then(|id| self.pos_verifier.get_pivot_decision(id)) + .and_then(|hash| self.data_man.block_header_by_hash(&hash)) + .map(|header| header.height()); + + let epoch_height = pivot_block.block_header.height(); + let chain_id = self.machine.params().chain_id_map(epoch_height); + let mut env = Env { + chain_id, + number: block_number, + author: block.block_header.author().clone(), + timestamp: pivot_block.block_header.timestamp(), + difficulty: block.block_header.difficulty().clone(), + accumulated_gas_used: U256::zero(), + last_hash: last_block_hash, + gas_limit: U256::from(block.block_header.gas_limit()), + epoch_height, + pos_view: pos_view_number, + finalized_epoch: pivot_decision_epoch, + transaction_epoch_bound: self + .verification_config + .transaction_epoch_bound, + }; + let spec = self.machine.spec(env.number); + if !spec.cip43_contract { + state.bump_block_number_accumulate_interest(); + } + let machine = self.machine.as_ref(); + + state.inc_distributable_pos_interest(env.number)?; + initialize_internal_contract_accounts( + state, + self.machine.internal_contracts().initialized_at(env.number), + ); + block_number += 1; + + last_block_hash = block.hash(); + + for (_idx, transaction) in block.transactions.iter().enumerate() { + let need_trace = match tx_hash { + Some(ref hash) => transaction.hash() == *hash, + None => true, // trace all tx + }; + + let (observer, supported) = if need_trace { + match &opts.tracer { + Some(t) => match t { + GethDebugTracerType::BuiltInTracer(bt) => match bt { + GethDebugBuiltInTracerType::FourByteTracer => { + (Observer::with_no_tracing(), false) + } + GethDebugBuiltInTracerType::CallTracer => { + let call_config = opts + .tracer_config + .clone() + .into_call_config() + .map_err(|e| format!("{e}"))?; + (Observer::geth_tracer( + TracingInspectorConfig::from_geth_call_config(&call_config), + ), + true, + ) + } + GethDebugBuiltInTracerType::PreStateTracer => { + (Observer::with_no_tracing(), false) + } + GethDebugBuiltInTracerType::NoopTracer => { + (Observer::with_no_tracing(), false) + } + GethDebugBuiltInTracerType::MuxTracer => { + (Observer::with_no_tracing(), false) + } + }, + GethDebugTracerType::JsTracer(_) => { + (Observer::with_no_tracing(), false) + } + }, + None => ( + Observer::geth_tracer( + TracingInspectorConfig::from_geth_config( + &opts.config, + ), + ), + true, + ), + } + } else { + (Observer::with_no_tracing(), false) + }; + + let options = TransactOptions { + observer, + settings: TransactSettings::all_checks(), + }; + let execution_outcome = + ExecutiveContext::new(state, &env, machine, &spec) + .transact(transaction, options)?; + let r = make_process_tx_outcome( + execution_outcome, + &mut env.accumulated_gas_used, + transaction.hash, + ); + + if need_trace && supported { + if let Some(t) = &opts.tracer { + if t == &GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::CallTracer, + ) { + if r.geth_trace.is_some() { + traces.push(( + transaction.hash(), + r.geth_trace.unwrap(), + )); + } + } + } else { + if r.geth_trace.is_some() { + traces.push(( + transaction.hash(), + r.geth_trace.unwrap(), + )); + } + } + } + } + } + + // dummy trace for the block + // traces.push(( + // H256::default(), + // GethTrace::NoopTracer(NoopFrame::default()), + // )); + + Ok(traces) + } + fn maybe_update_state( &self, state: &mut State, block_number: BlockNumber, ) -> DbResult<()> { diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index c3b035d6bb..a1129063ac 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -45,6 +45,7 @@ use cfx_execute_helper::{ }; use cfx_executor::{executive::ExecutionOutcome, state::State}; +use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethTrace}; use cfx_internal_common::ChainIdParams; use cfx_parameters::{ consensus::*, @@ -1435,6 +1436,30 @@ impl ConsensusGraph { .call_virtual(tx, &epoch_id, epoch_size, request) } + pub fn collect_epoch_geth_trace( + &self, epoch_num: u64, tx_hash: Option, + opts: GethDebugTracingOptions, + ) -> RpcResult> { + // only allow to call against stated epoch + let epoch = EpochNumber::Number(epoch_num); + self.validate_stated_epoch(&epoch)?; + + let epoch_block_hashes = if let Ok(v) = + self.get_block_hashes_by_epoch(epoch) + { + v + } else { + bail!("cannot get block hashes in the specified epoch, maybe it does not exist?"); + }; + + self.executor.collect_epoch_geth_trace( + epoch_num, + epoch_block_hashes, + tx_hash, + opts, + ) + } + /// Get the number of processed blocks (i.e., the number of calls to /// on_new_block() pub fn get_processed_block_count(&self) -> usize { diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index ec206be178..d93af467f0 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -981,6 +981,7 @@ impl TransactionPoolInner { .unwrap_or(state_nonce) } + #[allow(dead_code)] fn recalculate_readiness_with_local_info( &mut self, addr: &AddressWithSpace, ) { diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 7734fca432..7907192065 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-execute-helper" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] # substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } @@ -18,7 +18,7 @@ cfx-statedb = { path = "../../dbs/statedb" } cfx-vm-tracer-derive = { path= "../../util/cfx-vm-tracer-derive" } cfx-types = { path = "../../cfx_types" } cfx-vm-types = { path = "../vm-types" } -# cfx-vm-interpreter = { path = "../vm-interpreter" } +cfx-vm-interpreter = { path = "../vm-interpreter" } cfx-executor = { path = "../executor" } error-chain = { version = "0.12", default-features = false } # keccak-hash = "0.5" @@ -35,6 +35,9 @@ rlp_derive = { git = "https://github.com/Conflux-Chain/conflux-parity-deps.git", # rustc-hex = "2.1" serde = { version = "1.0", features = ["rc"] } serde_derive = "1.0" +serde_json = { version = "1.0", default-features = false, features = [ + "alloc", +]} # serde_json = "1.0" solidity-abi = { path= "../../util/solidity-abi" } # solidity-abi-derive = { path="../../util/solidity-abi-derive" } @@ -49,4 +52,8 @@ pow-types = {path = "../core/src/pos/types/pow-types" } # impl-trait-for-tuples = "^0.2" # impl-tools = "^0.10" typemap = "0.3" -alloy-primitives.workspace = true \ No newline at end of file + +alloy-primitives.workspace = true +alloy-rpc-trace-types.workspace = true +alloy-sol-types = "0.6" +revm = { version = "7.2", default-features = false, features = ["std"] } # TODO need to be removed \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs index 44c083958c..cedb23d152 100644 --- a/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/exec_tracer/mod.rs @@ -24,11 +24,10 @@ pub use trace_types::{ use super::utils::CheckpointLog; use cfx_executor::{ - executive_observer::{ + observer::{ AddressPocket, CallTracer, CheckpointTracer, DrainTrace, - InternalTransferTracer, + InternalTransferTracer, OpcodeTracer, StorageTracer, }, - observer::{OpcodeTracer, StorageTracer}, stack::{FrameResult, FrameReturn}, }; use cfx_types::U256; @@ -152,6 +151,5 @@ impl CallTracer for ExecTracer { } } -impl OpcodeTracer for ExecTracer {} - impl StorageTracer for ExecTracer {} +impl OpcodeTracer for ExecTracer {} diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs index 1c9ce0cacb..b13ef50f99 100644 --- a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs +++ b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs @@ -21,15 +21,15 @@ //! } //! ``` -use alloy_primitives::Selector; -use cfx_executor::{ - executive_observer::{ - CallTracer, CheckpointTracer, InternalTransferTracer, - }, - observer::{OpcodeTracer, StorageTracer}, +use super::geth_tracer::GethTraceKey; +use alloy_primitives::{hex, Selector}; +use alloy_rpc_trace_types::geth::{FourByteFrame, GethTrace}; +use cfx_executor::observer::{ + CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, + OpcodeTracer, StorageTracer, }; use cfx_vm_types::ActionParams; -use std::{collections::HashMap, convert::TryFrom}; +use std::collections::HashMap; /// Fourbyte tracing inspector that records all function selectors and their /// calldata sizes. @@ -40,10 +40,14 @@ pub struct FourByteInspector { } impl FourByteInspector { + pub fn new() -> Self { Self::default() } + /// Returns the map of SELECTOR to number of occurrences entries pub const fn inner(&self) -> &HashMap<(Selector, usize), u64> { &self.inner } + + pub fn drain(self) -> FourByteFrame { FourByteFrame::from(self) } } impl CallTracer for FourByteInspector { @@ -63,3 +67,34 @@ impl CheckpointTracer for FourByteInspector {} impl InternalTransferTracer for FourByteInspector {} impl StorageTracer for FourByteInspector {} impl OpcodeTracer for FourByteInspector {} + +impl DrainTrace for FourByteInspector { + fn drain_trace(self, map: &mut typemap::ShareDebugMap) { + map.insert::(GethTrace::FourByteTracer(self.drain())); + } +} + +// impl AsTracer for FourByteInspector { +// fn as_tracer<'a>(&'a mut self) -> Box { +// Box::new(self) +// } +// } + +impl From for FourByteFrame { + fn from(value: FourByteInspector) -> Self { + Self( + value + .inner + .into_iter() + .map(|((selector, calldata_size), count)| { + let key = format!( + "0x{}-{}", + hex::encode(&selector[..]), + calldata_size + ); + (key, count) + }) + .collect(), + ) + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/gasman.rs b/crates/cfxcore/execute-helper/src/observer/gasman.rs index a5637a43de..2fed79d8ab 100644 --- a/crates/cfxcore/execute-helper/src/observer/gasman.rs +++ b/crates/cfxcore/execute-helper/src/observer/gasman.rs @@ -1,8 +1,8 @@ use cfx_executor::{ - executive_observer::{ + observer::{ CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, + OpcodeTracer, StorageTracer, }, - observer::{OpcodeTracer, StorageTracer}, stack::FrameResult, }; use cfx_parameters::{ diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs new file mode 100644 index 0000000000..8648f21ea8 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs @@ -0,0 +1,102 @@ +use super::types::{CallTrace, CallTraceNode, LogCallOrder}; + +/// An arena of recorded traces. +/// +/// This type will be populated via the +/// [TracingInspector](super::TracingInspector). +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallTraceArena { + /// The arena of recorded trace nodes + pub(crate) arena: Vec, +} + +impl CallTraceArena { + /// Pushes a new trace into the arena, returning the trace ID + /// + /// This appends a new trace to the arena, and also inserts a new entry in + /// the node's parent node children set if `attach_to_parent` is `true`. + /// E.g. if calls to precompiles should not be included in the call + /// graph this should be called with [PushTraceKind::PushOnly]. + pub(crate) fn push_trace( + &mut self, mut entry: usize, kind: PushTraceKind, new_trace: CallTrace, + ) -> usize { + loop { + match new_trace.depth { + // The entry node, just update it + 0 => { + self.arena[0].trace = new_trace; + return 0; + } + // We found the parent node, add the new trace as a child + _ if self.arena[entry].trace.depth == new_trace.depth - 1 => { + let id = self.arena.len(); + let node = CallTraceNode { + parent: Some(entry), + trace: new_trace, + idx: id, + ..Default::default() + }; + self.arena.push(node); + + // also track the child in the parent node + if kind.is_attach_to_parent() { + let parent = &mut self.arena[entry]; + let trace_location = parent.children.len(); + parent + .ordering + .push(LogCallOrder::Call(trace_location)); + parent.children.push(id); + } + + return id; + } + _ => { + // We haven't found the parent node, go deeper + entry = *self.arena[entry] + .children + .last() + .expect("Disconnected trace"); + } + } + } + } + + /// Returns the nodes in the arena + pub fn nodes(&self) -> &[CallTraceNode] { &self.arena } + + /// Consumes the arena and returns the nodes + pub fn into_nodes(self) -> Vec { self.arena } + + /// Clears the arena + /// + /// Note that this method has no effect on the allocated capacity of the + /// arena. + #[inline] + pub fn clear(&mut self) { self.arena.clear(); } +} + +/// How to push a trace into the arena +pub(crate) enum PushTraceKind { + /// This will _only_ push the trace into the arena. + PushOnly, + /// This will push the trace into the arena, and also insert a new entry in + /// the node's parent node children set. + PushAndAttachToParent, +} + +impl PushTraceKind { + #[inline] + const fn is_attach_to_parent(&self) -> bool { + matches!(self, Self::PushAndAttachToParent) + } +} + +impl Default for CallTraceArena { + fn default() -> Self { + // The first node is the root node + Self { + arena: vec![Default::default()], + } + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs new file mode 100644 index 0000000000..777fb96ae1 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -0,0 +1,393 @@ +//! Geth trace builder + +use crate::observer::geth_tracer::{ + types::{CallTraceNode, CallTraceStepStackItem}, + TracingInspectorConfig, +}; +use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rpc_trace_types::geth::{ + AccountChangeKind, AccountState, CallConfig, CallFrame, DefaultFrame, + DiffMode, GethDefaultTracingOptions, PreStateConfig, PreStateFrame, + PreStateMode, StructLog, +}; +use revm::{ + db::DatabaseRef, + primitives::{AccountInfo, ResultAndState, KECCAK_EMPTY}, +}; +use std::collections::{BTreeMap, HashMap, VecDeque}; + +/// A type for creating geth style traces +#[derive(Clone, Debug)] +pub struct GethTraceBuilder { + /// Recorded trace nodes. + nodes: Vec, + /// How the traces were recorded + _config: TracingInspectorConfig, +} + +impl GethTraceBuilder { + /// Returns a new instance of the builder + pub fn new( + nodes: Vec, _config: TracingInspectorConfig, + ) -> Self { + Self { nodes, _config } + } + + /// Fill in the geth trace with all steps of the trace and its children + /// traces in the order they appear in the transaction. + fn fill_geth_trace( + &self, main_trace_node: &CallTraceNode, + opts: &GethDefaultTracingOptions, + storage: &mut HashMap>, + struct_logs: &mut Vec, + ) { + // A stack with all the steps of the trace and all its children's steps. + // This is used to process the steps in the order they appear in the + // transactions. Steps are grouped by their Call Trace Node, in + // order to process them all in the order they appear in the + // transaction, we need to process steps of call nodes when they appear. + // When we find a call step, we push all the steps of the child trace on + // the stack, so they are processed next. The very next step is + // the last item on the stack + let mut step_stack = + VecDeque::with_capacity(main_trace_node.trace.steps.len()); + + main_trace_node.push_steps_on_stack(&mut step_stack); + + // Iterate over the steps inside the given trace + while let Some(CallTraceStepStackItem { + trace_node, + step, + call_child_id, + }) = step_stack.pop_back() + { + let mut log = step.convert_to_geth_struct_log(opts); + + // Fill in memory and storage depending on the options + if opts.is_storage_enabled() { + let contract_storage = + storage.entry(step.contract).or_default(); + if let Some(change) = step.storage_change { + contract_storage + .insert(change.key.into(), change.value.into()); + log.storage = Some(contract_storage.clone()); + } + } + + if opts.is_return_data_enabled() { + log.return_data = Some(trace_node.trace.output.clone()); + } + + // Add step to geth trace + struct_logs.push(log); + + // If the step is a call, we first push all the steps of the child + // trace on the stack, so they are processed next + if let Some(call_child_id) = call_child_id { + let child_trace = &self.nodes[call_child_id]; + child_trace.push_steps_on_stack(&mut step_stack); + } + } + } + + /// Generate a geth-style trace e.g. for `debug_traceTransaction` + /// + /// This expects the gas used and return value for the + /// [ExecutionResult](revm::primitives::ExecutionResult) of the executed + /// transaction. + pub fn geth_traces( + &self, receipt_gas_used: u64, return_value: Bytes, + opts: GethDefaultTracingOptions, + ) -> DefaultFrame { + if self.nodes.is_empty() { + return Default::default(); + } + // Fetch top-level trace + let main_trace_node = &self.nodes[0]; + let main_trace = &main_trace_node.trace; + + let mut struct_logs = Vec::new(); + let mut storage = HashMap::new(); + self.fill_geth_trace( + main_trace_node, + &opts, + &mut storage, + &mut struct_logs, + ); + + DefaultFrame { + // If the top-level trace succeeded, then it was a success + failed: !main_trace.success, + gas: receipt_gas_used, + return_value, + struct_logs, + } + } + + /// Generate a geth-style traces for the call tracer. + /// + /// This decodes all call frames from the recorded traces. + /// + /// This expects the gas used for the + /// [ExecutionResult](revm::primitives::ExecutionResult) of the executed + /// transaction. + pub fn geth_call_traces( + &self, opts: CallConfig, gas_used: u64, + ) -> CallFrame { + if self.nodes.is_empty() { + return Default::default(); + } + + let include_logs = opts.with_log.unwrap_or_default(); + // first fill up the root + let main_trace_node = &self.nodes[0]; + let mut root_call_frame = + main_trace_node.geth_empty_call_frame(include_logs); + root_call_frame.gas_used = U256::from(gas_used); + + // selfdestructs are not recorded as individual call traces but are + // derived from the call trace and are added as additional + // `CallFrame` objects to the parent call + if let Some(selfdestruct) = + main_trace_node.geth_selfdestruct_call_trace() + { + root_call_frame.calls.push(selfdestruct); + } + + if opts.only_top_call.unwrap_or_default() { + return root_call_frame; + } + + // fill all the call frames in the root call frame with the recorded + // traces. traces are identified by their index in the arena + // so we can populate the call frame tree by walking up the call tree + let mut call_frames = Vec::with_capacity(self.nodes.len()); + call_frames.push((0, root_call_frame)); + + for (idx, trace) in self.nodes.iter().enumerate().skip(1) { + // selfdestructs are not recorded as individual call traces but are + // derived from the call trace and are added as + // additional `CallFrame` objects to the parent call + if let Some(selfdestruct) = trace.geth_selfdestruct_call_trace() { + call_frames + .last_mut() + .expect("not empty") + .1 + .calls + .push(selfdestruct); + } + + // include logs only if call and all its parents were successful + let include_logs = + include_logs && !self.call_or_parent_failed(trace); + call_frames.push((idx, trace.geth_empty_call_frame(include_logs))); + } + + // pop the _children_ calls frame and move it to the parent + // this will roll up the child frames to their parent; this works + // because `child idx > parent idx` + loop { + let (idx, call) = call_frames.pop().expect("call frames not empty"); + let node = &self.nodes[idx]; + if let Some(parent) = node.parent { + let parent_frame = &mut call_frames[parent]; + // we need to ensure that calls are in order they are called: + // the last child node is the last call, but + // since we walk up the tree, we need to always + // insert at position 0 + parent_frame.1.calls.insert(0, call); + } else { + debug_assert!( + call_frames.is_empty(), + "only one root node has no parent" + ); + return call; + } + } + } + + /// Returns true if the given trace or any of its parents failed. + fn call_or_parent_failed(&self, node: &CallTraceNode) -> bool { + if node.trace.is_error() { + return true; + } + + let mut parent_idx = node.parent; + while let Some(idx) = parent_idx { + let next = &self.nodes[idx]; + if next.trace.is_error() { + return true; + } + + parent_idx = next.parent; + } + false + } + + /// Returns the accounts necessary for transaction execution. + /// + /// The prestate mode returns the accounts necessary to execute a given + /// transaction. diff_mode returns the differences between the + /// transaction's pre and post-state. + /// + /// * `state` - The state post-transaction execution. + /// * `diff_mode` - if prestate is in diff or prestate mode. + /// * `db` - The database to fetch state pre-transaction execution. + /// TODO(pana): adapt the DatabaseRef and ResultAndState's State + pub fn geth_prestate_traces( + &self, ResultAndState { state, .. }: &ResultAndState, + prestate_config: PreStateConfig, db: DB, + ) -> Result { + let account_diffs = state.iter().map(|(addr, acc)| (*addr, acc)); + + if prestate_config.is_default_mode() { + let mut prestate = PreStateMode::default(); + // we only want changed accounts for things like balance changes etc + for (addr, changed_acc) in account_diffs { + let db_acc = db.basic_ref(addr)?.unwrap_or_default(); + let code = load_account_code(&db, &db_acc); + let mut acc_state = AccountState::from_account_info( + db_acc.nonce, + db_acc.balance, + code, + ); + + // insert the original value of all modified storage slots + for (key, slot) in changed_acc.storage.iter() { + acc_state.storage.insert( + (*key).into(), + slot.previous_or_original_value.into(), + ); + } + + prestate.0.insert(addr, acc_state); + } + + Ok(PreStateFrame::Default(prestate)) + } else { + let mut state_diff = DiffMode::default(); + let mut account_change_kinds = + HashMap::with_capacity(account_diffs.len()); + for (addr, changed_acc) in account_diffs { + let db_acc = db.basic_ref(addr)?.unwrap_or_default(); + + let pre_code = load_account_code(&db, &db_acc); + + let mut pre_state = AccountState::from_account_info( + db_acc.nonce, + db_acc.balance, + pre_code, + ); + + let mut post_state = AccountState::from_account_info( + changed_acc.info.nonce, + changed_acc.info.balance, + changed_acc + .info + .code + .as_ref() + .map(|code| code.original_bytes()), + ); + + // handle storage changes + for (key, slot) in changed_acc + .storage + .iter() + .filter(|(_, slot)| slot.is_changed()) + { + pre_state.storage.insert( + (*key).into(), + slot.previous_or_original_value.into(), + ); + post_state + .storage + .insert((*key).into(), slot.present_value.into()); + } + + state_diff.pre.insert(addr, pre_state); + state_diff.post.insert(addr, post_state); + + // determine the change type + let pre_change = if changed_acc.is_created() { + AccountChangeKind::Create + } else { + AccountChangeKind::Modify + }; + let post_change = if changed_acc.is_selfdestructed() { + AccountChangeKind::SelfDestruct + } else { + AccountChangeKind::Modify + }; + + account_change_kinds.insert(addr, (pre_change, post_change)); + } + + // ensure we're only keeping changed entries + state_diff.retain_changed().remove_zero_storage_values(); + + self.diff_traces( + &mut state_diff.pre, + &mut state_diff.post, + account_change_kinds, + ); + Ok(PreStateFrame::Diff(state_diff)) + } + } + + /// Returns the difference between the pre and post state of the transaction + /// depending on the kind of changes of that account (pre,post) + fn diff_traces( + &self, pre: &mut BTreeMap, + post: &mut BTreeMap, + change_type: HashMap, + ) { + post.retain(|addr, post_state| { + // Don't keep destroyed accounts in the post state + if change_type + .get(addr) + .map(|ty| ty.1.is_selfdestruct()) + .unwrap_or(false) + { + return false; + } + if let Some(pre_state) = pre.get(addr) { + // remove any unchanged account info + post_state.remove_matching_account_info(pre_state); + } + + true + }); + + // Don't keep created accounts the pre state + pre.retain(|addr, _pre_state| { + // only keep accounts that are not created + change_type + .get(addr) + .map(|ty| !ty.0.is_created()) + .unwrap_or(true) + }); + } +} + +/// Loads the code for the given account from the account itself or the database +/// +/// Returns None if the code hash is the KECCAK_EMPTY hash +#[inline] +pub(crate) fn load_account_code( + db: DB, db_acc: &AccountInfo, +) -> Option { + db_acc + .code + .as_ref() + .map(|code| code.original_bytes()) + .or_else(|| { + if db_acc.code_hash == KECCAK_EMPTY { + None + } else { + db.code_by_hash_ref(db_acc.code_hash) + .ok() + .map(|code| code.original_bytes()) + } + }) + .map(Into::into) +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs new file mode 100644 index 0000000000..ae68f630bb --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs @@ -0,0 +1 @@ +pub mod geth; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs new file mode 100644 index 0000000000..ada91c736c --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs @@ -0,0 +1,324 @@ +use alloy_rpc_trace_types::{ + geth::{CallConfig, GethDefaultTracingOptions, PreStateConfig}, + parity::TraceType, +}; +use std::collections::HashSet; + +/// Gives guidance to the [TracingInspector](crate::tracing::TracingInspector). +/// +/// Use [TracingInspectorConfig::default_parity] or +/// [TracingInspectorConfig::default_geth] to get the default configs for +/// specific styles of traces. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TracingInspectorConfig { + /// Whether to record every individual opcode level step. + pub record_steps: bool, + /// Whether to record individual memory snapshots. + pub record_memory_snapshots: bool, + /// Whether to record individual stack snapshots. + pub record_stack_snapshots: StackSnapshotType, + /// Whether to record state diffs. + pub record_state_diff: bool, + /// Whether to ignore precompile calls. + pub exclude_precompile_calls: bool, + /// Whether to record logs + pub record_logs: bool, +} + +impl TracingInspectorConfig { + /// Returns a config with everything enabled. + pub const fn all() -> Self { + Self { + record_steps: true, + record_memory_snapshots: true, + record_stack_snapshots: StackSnapshotType::Full, + record_state_diff: false, + exclude_precompile_calls: false, + record_logs: true, + } + } + + /// Returns a config with everything is disabled. + pub const fn none() -> Self { + Self { + record_steps: false, + record_memory_snapshots: false, + record_stack_snapshots: StackSnapshotType::None, + record_state_diff: false, + exclude_precompile_calls: false, + record_logs: false, + } + } + + /// Returns a config for parity style traces. + /// + /// This config does _not_ record opcode level traces and is suited for + /// `trace_transaction` + pub const fn default_parity() -> Self { + Self { + record_steps: false, + record_memory_snapshots: false, + record_stack_snapshots: StackSnapshotType::None, + record_state_diff: false, + exclude_precompile_calls: true, + record_logs: false, + } + } + + /// Returns a config for geth style traces. + /// + /// This config does _not_ record opcode level traces and is suited for + /// `debug_traceTransaction` + /// + /// This will configure the default output of geth's default + /// [StructLogTracer](alloy_rpc_trace_types::geth::DefaultFrame). + pub const fn default_geth() -> Self { + Self { + record_steps: true, + record_memory_snapshots: false, + record_stack_snapshots: StackSnapshotType::Full, + record_state_diff: true, + exclude_precompile_calls: false, + record_logs: false, + } + } + + /// Returns the [TracingInspectorConfig] depending on the enabled + /// [TraceType]s + /// + /// Note: the parity statediffs can be populated entirely via the execution + /// result, so we don't need statediff recording + #[inline] + pub fn from_parity_config(trace_types: &HashSet) -> Self { + let needs_vm_trace = trace_types.contains(&TraceType::VmTrace); + let snap_type = if needs_vm_trace { + StackSnapshotType::Pushes + } else { + StackSnapshotType::None + }; + Self::default_parity() + .set_steps(needs_vm_trace) + .set_stack_snapshots(snap_type) + .set_memory_snapshots(needs_vm_trace) + } + + /// Returns a config for geth style traces based on the given + /// [GethDefaultTracingOptions]. + /// + /// This will configure the output of geth's default + /// [StructLogTracer](alloy_rpc_trace_types::geth::DefaultFrame) according + /// to the given config. + #[inline] + pub fn from_geth_config(config: &GethDefaultTracingOptions) -> Self { + Self { + record_memory_snapshots: config.enable_memory.unwrap_or_default(), + record_stack_snapshots: if config.disable_stack.unwrap_or_default() + { + StackSnapshotType::None + } else { + StackSnapshotType::Full + }, + record_state_diff: !config.disable_storage.unwrap_or_default(), + ..Self::default_geth() + } + } + + /// Returns a config for geth's + /// [CallTracer](alloy_rpc_trace_types::geth::CallFrame). + /// + /// This returns [Self::none] and enables + /// [TracingInspectorConfig::record_logs] if configured in + /// the given [CallConfig] + #[inline] + pub fn from_geth_call_config(config: &CallConfig) -> Self { + Self::none() + // call tracer is similar parity tracer with optional support for + // logs + .set_record_logs(config.with_log.unwrap_or_default()) + } + + /// Returns a config for geth's + /// [PrestateTracer](alloy_rpc_trace_types::geth::PreStateFrame). + /// + /// Note: This currently returns [Self::none] because the prestate tracer + /// result currently relies on the execution result entirely, see + /// [GethTraceBuilder::geth_prestate_traces](crate::tracing::geth::GethTraceBuilder::geth_prestate_traces) + #[inline] + pub const fn from_geth_prestate_config(_config: &PreStateConfig) -> Self { + Self::none() + } + + /// Configure whether calls to precompiles should be ignored. + /// + /// If set to `true`, calls to precompiles without value transfers will be + /// ignored. + pub const fn set_exclude_precompile_calls( + mut self, exclude_precompile_calls: bool, + ) -> Self { + self.exclude_precompile_calls = exclude_precompile_calls; + self + } + + /// Disable recording of individual opcode level steps + pub const fn disable_steps(self) -> Self { self.set_steps(false) } + + /// Enable recording of individual opcode level steps + pub const fn steps(self) -> Self { self.set_steps(true) } + + /// Configure whether individual opcode level steps should be recorded + pub const fn set_steps(mut self, record_steps: bool) -> Self { + self.record_steps = record_steps; + self + } + + /// Disable recording of individual memory snapshots + pub const fn disable_memory_snapshots(self) -> Self { + self.set_memory_snapshots(false) + } + + /// Enable recording of individual memory snapshots + pub const fn memory_snapshots(self) -> Self { + self.set_memory_snapshots(true) + } + + /// Configure whether the tracer should record memory snapshots + pub const fn set_memory_snapshots( + mut self, record_memory_snapshots: bool, + ) -> Self { + self.record_memory_snapshots = record_memory_snapshots; + self + } + + /// Disable recording of individual stack snapshots + pub const fn disable_stack_snapshots(self) -> Self { + self.set_stack_snapshots(StackSnapshotType::None) + } + + /// Enable recording of individual stack snapshots + pub const fn stack_snapshots(self) -> Self { + self.set_stack_snapshots(StackSnapshotType::Full) + } + + /// Configure how the tracer should record stack snapshots + pub const fn set_stack_snapshots( + mut self, record_stack_snapshots: StackSnapshotType, + ) -> Self { + self.record_stack_snapshots = record_stack_snapshots; + self + } + + /// Disable recording of state diffs + pub const fn disable_state_diffs(self) -> Self { + self.set_state_diffs(false) + } + + /// Configure whether the tracer should record state diffs + pub const fn set_state_diffs(mut self, record_state_diff: bool) -> Self { + self.record_state_diff = record_state_diff; + self + } + + /// Sets state diff recording to true. + /// + /// Also enables steps recording since state diff recording requires steps + /// recording. + pub const fn with_state_diffs(self) -> Self { + self.set_steps_and_state_diffs(true) + } + + /// Configure whether the tracer should record steps and state diffs. + /// + /// This is a convenience method for setting both + /// [TracingInspectorConfig::set_steps] and + /// [TracingInspectorConfig::set_state_diffs] since tracking state diffs + /// requires steps tracing. + pub const fn set_steps_and_state_diffs( + mut self, steps_and_diffs: bool, + ) -> Self { + self.record_steps = steps_and_diffs; + self.record_state_diff = steps_and_diffs; + self + } + + /// Disable recording of individual logs + pub const fn disable_record_logs(self) -> Self { + self.set_record_logs(false) + } + + /// Enable recording of individual logs + pub const fn record_logs(self) -> Self { self.set_record_logs(true) } + + /// Configure whether the tracer should record logs + pub const fn set_record_logs(mut self, record_logs: bool) -> Self { + self.record_logs = record_logs; + self + } +} + +/// How much of the stack to record. Nothing, just the items pushed, or the full +/// stack +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StackSnapshotType { + /// Don't record stack snapshots + None, + /// Record only the items pushed to the stack + Pushes, + /// Record the full stack + Full, +} + +impl StackSnapshotType { + /// Returns true if this is the [StackSnapshotType::Full] variant + #[inline] + pub const fn is_full(self) -> bool { matches!(self, Self::Full) } + + /// Returns true if this is the [StackSnapshotType::Pushes] variant + #[inline] + pub const fn is_pushes(self) -> bool { matches!(self, Self::Pushes) } +} + +/// What kind of tracing style this is. +/// +/// This affects things like error messages. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum TraceStyle { + /// Parity style tracer + Parity, + /// Geth style tracer + #[allow(dead_code)] + Geth, +} + +impl TraceStyle { + /// Returns true if this is a parity style tracer. + pub(crate) const fn is_parity(self) -> bool { matches!(self, Self::Parity) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parity_config() { + let mut s = HashSet::new(); + s.insert(TraceType::StateDiff); + let config = TracingInspectorConfig::from_parity_config(&s); + // not required + assert!(!config.record_steps); + assert!(!config.record_state_diff); + + let mut s = HashSet::new(); + s.insert(TraceType::VmTrace); + let config = TracingInspectorConfig::from_parity_config(&s); + assert!(config.record_steps); + assert!(!config.record_state_diff); + + let mut s = HashSet::new(); + s.insert(TraceType::VmTrace); + s.insert(TraceType::StateDiff); + let config = TracingInspectorConfig::from_parity_config(&s); + assert!(config.record_steps); + // not required for StateDiff + assert!(!config.record_state_diff); + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs new file mode 100644 index 0000000000..0986b7fcf6 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs @@ -0,0 +1,127 @@ +use super::utils::{convert_h256, convert_u256, to_h160}; +use cfx_executor::state::State; +use cfx_statedb::Error as StateDbError; +use cfx_types::{AddressWithSpace, Space}; +use revm::{ + db::{DatabaseRef, EmptyDB}, + primitives::{AccountInfo, Address, Bytecode, Bytes, B256, U256}, +}; +use std::{convert::Infallible, error, fmt}; + +// An adapter impl revm::db::DatabaseRef trait +pub struct RevmDbAdapter<'a> { + state: &'a State, + empty_db: EmptyDB, + space: Space, +} + +impl<'a> RevmDbAdapter<'a> { + fn new(state: &'a State) -> Self { + RevmDbAdapter { + state, + empty_db: EmptyDB::new(), + space: Space::Ethereum, // default is ethereum + } + } +} + +#[derive(Debug)] +pub enum RevmDbError { + Custom(String), +} + +impl error::Error for RevmDbError {} + +impl fmt::Display for RevmDbError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Custom(s) => write!(f, "Custom: {s}"), + } + } +} + +impl From for RevmDbError { + fn from(e: StateDbError) -> Self { RevmDbError::Custom(format!("{e}")) } +} + +impl From for RevmDbError { + fn from(e: Infallible) -> Self { RevmDbError::Custom(format!("{e}")) } +} + +impl<'a> DatabaseRef for RevmDbAdapter<'a> { + type Error = RevmDbError; + + /// Get basic account information. + fn basic_ref( + &self, address: Address, + ) -> Result, Self::Error> { + let space_address = AddressWithSpace { + address: to_h160(address), + space: self.space, + }; + let exist = self.state.exists(&space_address); + if let Ok(true) = exist { + let balance = self + .state + .balance(&space_address) + .map(|v| convert_u256(v))?; + + let nonce = self.state.nonce(&space_address).map(|v| v.as_u64())?; + + let code_hash = self + .state + .code_hash(&space_address) + .map(|v| convert_h256(v))?; + + // TODO code and code_hash need consider internal_contracts + let _code = self.state.code(&space_address).map(|v| { + v.map(|b| Bytecode::new_raw(Bytes::from(b.to_vec()))) + })?; + + let acc_info = AccountInfo { + balance, + nonce, + code_hash, + code: None, + }; + return Ok(Some(acc_info)); + } + + self.empty_db.basic_ref(address).map_err(|e| e.into()) + } + + /// TODO(pana) Get account code by its hash. + fn code_by_hash_ref( + &self, code_hash: B256, + ) -> Result { + self.empty_db + .code_by_hash_ref(code_hash) + .map_err(Into::into) + } + + /// Get storage value of address at index. + fn storage_ref( + &self, address: Address, index: U256, + ) -> Result { + let space_address = AddressWithSpace { + address: to_h160(address), + space: self.space, + }; + + let key = index.to_be_bytes::<32>(); // TODO: check big endian is correct + let result = self + .state + .storage_at(&space_address, &key) + .map(|v| convert_u256(v))?; + + return Ok(result); + } + + /// Get block hash by block number. + /// unused + fn block_hash_ref(&self, number: U256) -> Result { + let res = self.empty_db.block_hash_ref(number)?; + + Ok(res) + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs new file mode 100644 index 0000000000..1cbd58f0c6 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs @@ -0,0 +1,21 @@ +/// Helper [Inspector] that keeps track of gas. +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, Default)] +pub struct GasInspector { + gas_remaining: u64, + last_gas_cost: u64, +} + +impl GasInspector { + pub fn gas_remaining(&self) -> u64 { self.gas_remaining } + + pub fn last_gas_cost(&self) -> u64 { self.last_gas_cost } + + pub fn set_gas_remainning(&mut self, remainning: u64) { + self.gas_remaining = remainning; + } + + pub fn set_last_gas_cost(&mut self, gas_cost: u64) { + self.last_gas_cost = gas_cost; + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 9fb67daa56..6531f63b5f 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -1,39 +1,709 @@ -use cfx_executor::observer::{ - CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, - OpcodeTracer, StorageTracer, +#![allow(unused)] +mod arena; +mod builder; +mod config; +mod db_adapter; +mod gas; +mod types; +mod utils; + +pub use arena::CallTraceArena; +pub use builder::geth::{self, GethTraceBuilder}; +use cfx_types::H160; +pub use config::{StackSnapshotType, TracingInspectorConfig}; + +use arena::PushTraceKind; +use gas::GasInspector; +use types::{ + CallKind, CallTrace, CallTraceNode, CallTraceStep, LogCallOrder, + RecordedMemory, +}; +use utils::{ + convert_h160, convert_h256, convert_u256, gas_used, stack_push_count, +}; + +use alloy_primitives::{Address, Bytes, LogData, U256}; +use revm::interpreter::{Gas, InstructionResult, InterpreterResult, OpCode}; + +use cfx_executor::{ + observer::{ + CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, + OpcodeTracer, StorageTracer, + }, + stack::{FrameResult, FrameReturn}, }; +use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; + +use alloy_rpc_trace_types::geth::{ + CallFrame, DefaultFrame, GethTrace, NoopFrame, +}; + +#[derive(Clone, Debug)] +pub struct TracingInspector { + /// Configures what and how the inspector records traces. + config: TracingInspectorConfig, + /// Records all call traces + traces: CallTraceArena, + /// Tracks active calls + trace_stack: Vec, + /// Tracks active steps + step_stack: Vec, + /// Tracks the return value of the last call + last_call_return_data: Option, + /// The gas inspector used to track remaining gas. + gas_inspector: GasInspector, + // call depth + depth: usize, + // gas stack, used to trace gas_spent in call_result/create_result + gas_stack: Vec, +} + +impl TracingInspector { + /// Returns a new instance for the given config + pub fn new(config: TracingInspectorConfig) -> Self { + Self { + config, + traces: Default::default(), + trace_stack: vec![], + step_stack: vec![], + last_call_return_data: None, + gas_inspector: Default::default(), + depth: 0, + gas_stack: vec![], + } + } + + /// Resets the inspector to its initial state of [Self::new]. + /// This makes the inspector ready to be used again. + /// + /// Note that this method has no effect on the allocated capacity of the + /// vector. + #[inline] + pub fn fuse(&mut self) { + let Self { + traces, + trace_stack, + step_stack, + last_call_return_data, + gas_inspector, + // kept + config: _, + depth, + gas_stack, + } = self; + traces.clear(); + trace_stack.clear(); + step_stack.clear(); + gas_stack.clear(); + last_call_return_data.take(); + *gas_inspector = Default::default(); + *depth = 0; + } + + /// Resets the inspector to it's initial state of [Self::new]. + #[inline] + pub fn fused(mut self) -> Self { + self.fuse(); + self + } + + /// Returns the config of the inspector. + pub const fn config(&self) -> &TracingInspectorConfig { &self.config } + + /// Gets a reference to the recorded call traces. + pub const fn get_traces(&self) -> &CallTraceArena { &self.traces } + + /// Gets a mutable reference to the recorded call traces. + pub fn get_traces_mut(&mut self) -> &mut CallTraceArena { &mut self.traces } + + /// Manually the gas used of the root trace. + /// + /// This is useful if the root trace's gasUsed should mirror the actual gas + /// used by the transaction. + /// + /// This allows setting it manually by consuming the execution result's gas + /// for example. + #[inline] + pub fn set_transaction_gas_used(&mut self, gas_used: u64) { + if let Some(node) = self.traces.arena.first_mut() { + node.trace.gas_used = gas_used; + } + } + + /// Convenience function for [ParityTraceBuilder::set_transaction_gas_used] + /// that consumes the type. + #[inline] + pub fn with_transaction_gas_used(mut self, gas_used: u64) -> Self { + self.set_transaction_gas_used(gas_used); + self + } + + /// Consumes the Inspector and returns a [ParityTraceBuilder]. + // #[inline] + // pub fn into_parity_builder(self) -> ParityTraceBuilder { + // ParityTraceBuilder::new(self.traces.arena, self.spec_id, self.config) + // } + + /// Consumes the Inspector and returns a [GethTraceBuilder]. + #[inline] + pub fn into_geth_builder(self) -> GethTraceBuilder { + GethTraceBuilder::new(self.traces.arena, self.config) + } + + /// Returns true if we're no longer in the context of the root call. + fn is_deep(&self) -> bool { + // the root call will always be the first entry in the trace stack + !self.trace_stack.is_empty() + } + + /// Returns true if this a call to a precompile contract. + /// + /// Returns true if the `to` address is a precompile contract and the value + /// is zero. + #[inline] + fn is_precompile_call(&self, _to: &Address, value: U256) -> bool { + // TODO(pana) check to is in precompile list + // precompile list is in Machine object + if false { + // only if this is _not_ the root call + return self.is_deep() && value.is_zero(); + } + false + } + + /// Returns the currently active call trace. + /// + /// This will be the last call trace pushed to the stack: the call we + /// entered most recently. + #[track_caller] + #[inline] + fn active_trace(&self) -> Option<&CallTraceNode> { + self.trace_stack.last().map(|idx| &self.traces.arena[*idx]) + } + + /// Returns the last trace [CallTrace] index from the stack. + /// + /// This will be the currently active call trace. + /// + /// # Panics + /// + /// If no [CallTrace] was pushed + #[track_caller] + #[inline] + fn last_trace_idx(&self) -> usize { + self.trace_stack + .last() + .copied() + .expect("can't start step without starting a trace first") + } + + /// _Removes_ the last trace [CallTrace] index from the stack. + /// + /// # Panics + /// + /// If no [CallTrace] was pushed + #[track_caller] + #[inline] + fn pop_trace_idx(&mut self) -> usize { + self.trace_stack + .pop() + .expect("more traces were filled than started") + } + + /// Starts tracking a new trace. + /// + /// Invoked on [Inspector::call]. + #[allow(clippy::too_many_arguments)] + fn start_trace_on_call( + &mut self, address: Address, input_data: Bytes, value: U256, + kind: CallKind, caller: Address, mut gas_limit: u64, + maybe_precompile: Option, tx_gas_limit: u64, depth: usize, + ) { + // This will only be true if the inspector is configured to exclude + // precompiles and the call is to a precompile + let push_kind = if maybe_precompile.unwrap_or(false) { + // We don't want to track precompiles + PushTraceKind::PushOnly + } else { + PushTraceKind::PushAndAttachToParent + }; + + if self.trace_stack.is_empty() { + // this is the root call which should get the original gas limit of + // the transaction, because initialization costs are + // already subtracted from gas_limit For the root call + // this value should use the transaction's gas limit See and + gas_limit = tx_gas_limit; + } + + self.trace_stack.push(self.traces.push_trace( + 0, + push_kind, + CallTrace { + depth, + address, + kind, + data: input_data, + value, + status: InstructionResult::Continue, + caller, + maybe_precompile, + gas_limit, + ..Default::default() + }, + )); + } + + /// Fills the current trace with the outcome of a call. + /// + /// Invoked on [Inspector::call_end]. + /// + /// # Panics + /// + /// This expects an existing trace [Self::start_trace_on_call] + fn fill_trace_on_call_end( + &mut self, result: InterpreterResult, created_address: Option
, + gas_spent: u64, + ) { + let InterpreterResult { + result, + output, + gas: _, + } = result; + + let trace_idx = self.pop_trace_idx(); + let trace = &mut self.traces.arena[trace_idx].trace; + + if trace_idx == 0 { + // this is the root call which should get the gas used of the + // transaction refunds are applied after execution, + // which is when the root call ends + // Conflux have no refund + trace.gas_used = gas_used(gas_spent, 0); + } else { + trace.gas_used = gas_spent; + } + + trace.status = result; + trace.success = trace.status.is_ok(); + trace.output = output.clone(); + + self.last_call_return_data = Some(output); + + if let Some(address) = created_address { + // A new contract was created via CREATE + trace.address = address; + } + } + + /// Starts tracking a step + /// + /// Invoked on [Inspector::step] + /// + /// # Panics + /// + /// This expects an existing [CallTrace], in other words, this panics if not + /// within the context of a call. + fn start_step(&mut self, interp: &dyn InterpreterInfo, depth: u64) { + let trace_idx = self.last_trace_idx(); + let trace = &mut self.traces.arena[trace_idx]; + + self.step_stack.push(StackStep { + trace_idx, + step_idx: trace.trace.steps.len(), + }); + + let memory = self + .config + .record_memory_snapshots + .then(|| RecordedMemory::new(interp.mem().to_vec())) + .unwrap_or_default(); + + let stack = if self.config.record_stack_snapshots.is_full() { + Some( + interp + .stack() + .into_iter() + .map(|v| convert_u256(v)) + .collect(), + ) + } else { + None + }; + + let op = OpCode::new(interp.current_opcode()) + .or_else(|| { + // if the opcode is invalid, we'll use the invalid opcode to + // represent it because this is invoked before + // the opcode is executed, the evm will eventually return a + // `Halt` with invalid/unknown opcode as result + let invalid_opcode = 0xfe; + OpCode::new(invalid_opcode) + }) + .expect("is valid opcode;"); + + trace.trace.steps.push(CallTraceStep { + depth, + pc: interp.program_counter() as usize, + op, + contract: convert_h160(interp.contract_address()), + stack, + push_stack: None, + memory_size: memory.len(), + memory, + gas_remaining: self.gas_inspector.gas_remaining(), + gas_refund_counter: 0, // conflux has no gas refund + + // fields will be populated end of call + gas_cost: 0, + storage_change: None, + status: InstructionResult::Continue, + }); + } + + /// Fills the current trace with the output of a step. + /// + /// Invoked on [Inspector::step_end]. + fn fill_step_on_step_end(&mut self, interp: &dyn InterpreterInfo) { + let StackStep { + trace_idx, + step_idx, + } = self + .step_stack + .pop() + .expect("can't fill step without starting a step first"); + let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx]; + + if self.config.record_stack_snapshots.is_pushes() { + let num_pushed = stack_push_count(step.op.get()); + let start = interp.stack().len() - num_pushed; + let push_stack = interp.stack()[start..].to_vec(); + step.push_stack = + Some(push_stack.into_iter().map(|v| convert_u256(v)).collect()); + } + + if self.config.record_memory_snapshots { + // resize memory so opcodes that allocated memory is correctly + // displayed + if interp.mem().len() > step.memory.len() { + step.memory.resize(interp.mem().len()); + } + } + if self.config.record_state_diff { + let _op = step.op.get(); + + // TODO setup the storage_change + } + + // The gas cost is the difference between the recorded gas remaining at + // the start of the step the remaining gas here, at the end of + // the step. + // todo: Figure out why this can overflow. https://github.com/paradigmxyz/evm-inspectors/pull/38 + step.gas_cost = step + .gas_remaining + .saturating_sub(self.gas_inspector.gas_remaining()); + + // TODO set the status + // step.status = interp.instruction_result; + } +} + pub struct GethTracer { - // TODO[geth-tracer]: Fill data here + inner: TracingInspector, +} + +impl GethTracer { + pub fn new(config: TracingInspectorConfig) -> Self { + Self { + inner: TracingInspector::new(config), + } + } + + pub fn drain(self) -> GethTrace { + // TODO return the right kind of frame according to the config + GethTrace::NoopTracer(NoopFrame::default()) + } } impl DrainTrace for GethTracer { fn drain_trace(self, map: &mut typemap::ShareDebugMap) { - // TODO[geth-tracer]: Compute output for one transaction here. - map.insert::(()); + map.insert::(self.drain()); } } -pub struct GethTracerKey; +pub struct GethTraceKey; -impl typemap::Key for GethTracerKey { - // TODO[geth-tracer]: Define your output type here - type Value = (); +impl typemap::Key for GethTraceKey { + type Value = GethTrace; } -impl CheckpointTracer for GethTracer { - // TODO[geth-tracer]: Implement hook handler in needed. -} - -impl CallTracer for GethTracer {} +impl CheckpointTracer for GethTracer {} impl InternalTransferTracer for GethTracer {} impl StorageTracer for GethTracer {} +impl CallTracer for GethTracer { + fn record_call(&mut self, params: &ActionParams) { + self.inner.depth += 1; + self.inner.gas_stack.push(params.gas.clone()); + + // determine correct `from` and `to` based on the call scheme + let (from, to) = match params.call_type { + CallType::DelegateCall | CallType::CallCode => ( + convert_h160(params.address), + convert_h160(params.code_address), + ), + _ => (convert_h160(params.sender), convert_h160(params.address)), + }; + + let value = if matches!(params.call_type, CallType::DelegateCall) { + // for delegate calls we need to use the value of the top trace + if let Some(parent) = self.inner.active_trace() { + parent.trace.value + } else { + convert_u256(params.value.value()) + } + } else { + convert_u256(params.value.value()) + }; + + // if calls to precompiles should be excluded, check whether this is a + // call to a precompile + let maybe_precompile = self + .inner + .config + .exclude_precompile_calls + .then(|| self.inner.is_precompile_call(&to, value)); + + self.inner.start_trace_on_call( + to, + params.data.clone().unwrap_or_default().into(), + value, + params.call_type.into(), + from, + params.gas.as_u64(), + maybe_precompile, + params.gas.as_u64(), /* TODO should use tx gas_limit not frame + * gas_limit */ + self.inner.depth, + ); + } + + fn record_call_result(&mut self, result: &FrameResult) { + self.inner.depth -= 1; + let mut gas_spent = + self.inner.gas_stack.pop().expect("should have value"); + + if let Ok(r) = result { + gas_spent = gas_spent - r.gas_left; + } + + let instruction_result = to_instruction_result(result); + + if instruction_result.is_error() { + self.inner.gas_inspector.set_gas_remainning(0); + } + + let output = result + .as_ref() + .map(|f| Bytes::from(f.return_data.to_vec())) + .unwrap_or_default(); + + let outcome = InterpreterResult { + result: instruction_result, + output, + gas: Gas::default(), + }; + + self.inner + .fill_trace_on_call_end(outcome, None, gas_spent.as_u64()); + } + + fn record_create(&mut self, params: &ActionParams) { + self.inner.depth += 1; + self.inner.gas_stack.push(params.gas.clone()); + + let value = if matches!(params.call_type, CallType::DelegateCall) { + // for delegate calls we need to use the value of the top trace + if let Some(parent) = self.inner.active_trace() { + parent.trace.value + } else { + convert_u256(params.value.value()) + } + } else { + convert_u256(params.value.value()) + }; + + self.inner.start_trace_on_call( + Address::default(), // call_result will set this address + params.data.clone().unwrap_or_default().into(), + value, + params.call_type.into(), + convert_h160(params.sender), + params.gas.as_u64(), + Some(false), + params.gas.as_u64(), + self.inner.depth, + ); + } + + fn record_create_result(&mut self, result: &FrameResult) { + self.inner.depth -= 1; + let mut gas_spent = + self.inner.gas_stack.pop().expect("should have value"); + + if let Ok(r) = result { + gas_spent = gas_spent - r.gas_left; + } + + let instruction_result = to_instruction_result(result); + + if instruction_result.is_error() { + self.inner.gas_inspector.set_gas_remainning(0); + } + + let output = result + .as_ref() + .map(|f| Bytes::from(f.return_data.to_vec())) + .unwrap_or_default(); + + let outcome = InterpreterResult { + result: instruction_result, + output, + gas: Gas::default(), + }; + + let create_address = + if let Ok(FrameReturn { create_address, .. }) = result { + create_address.as_ref().map(|h| convert_h160(*h)) + } else { + None + }; + + self.inner.fill_trace_on_call_end( + outcome, + create_address, + gas_spent.as_u64(), + ); + } +} + impl OpcodeTracer for GethTracer { fn do_trace_opcode(&self, enabled: &mut bool) { - *enabled |= true; - // TODO[geth-tracer]: Tell the executor if trace_opcode is enabled. + if self.inner.config.record_steps { + *enabled |= true; + } + } + + fn initialize_interp(&mut self, gas_limit: cfx_types::U256) { + self.inner + .gas_inspector + .set_gas_remainning(gas_limit.as_u64()); } + + fn step(&mut self, interp: &dyn InterpreterInfo, depth: usize) { + self.inner + .gas_inspector + .set_gas_remainning(interp.gas_remainning().as_u64()); + + if self.inner.config.record_steps { + self.inner.start_step(interp, depth as u64); + } + } + + fn step_end(&mut self, interp: &dyn InterpreterInfo) { + let remainning = interp.gas_remainning().as_u64(); + let last_gas_cost = self + .inner + .gas_inspector + .gas_remaining() + .saturating_sub(remainning); + self.inner.gas_inspector.set_gas_remainning(remainning); + self.inner.gas_inspector.set_last_gas_cost(last_gas_cost); + + // trace + if self.inner.config.record_steps { + self.inner.fill_step_on_step_end(interp); + } + } + + fn log( + &mut self, _address: &cfx_types::Address, topics: Vec, + data: &[u8], + ) { + if self.inner.config.record_logs { + let trace_idx = self.inner.last_trace_idx(); + let trace = &mut self.inner.traces.arena[trace_idx]; + trace.ordering.push(LogCallOrder::Log(trace.logs.len())); + trace.logs.push(LogData::new_unchecked( + topics.iter().map(|f| convert_h256(*f)).collect(), + Bytes::from(data.to_vec()), + )); + } + } + + fn selfdestruct( + &mut self, _contract: &cfx_types::Address, target: &cfx_types::Address, + _value: cfx_types::U256, + ) { + let trace_idx = self.inner.last_trace_idx(); + let trace = &mut self.inner.traces.arena[trace_idx].trace; + trace.selfdestruct_refund_target = Some(convert_h160(*target as H160)) + } +} + +#[derive(Clone, Copy, Debug)] +struct StackStep { + trace_idx: usize, + step_idx: usize, +} + +pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { + let result = match frame_result { + Ok(_r) => InstructionResult::Return, // todo check this + Err(err) => match err { + Error::OutOfGas => InstructionResult::OutOfGas, + Error::BadJumpDestination { destination: _ } => { + InstructionResult::InvalidJump + } + Error::BadInstruction { instruction: _ } => { + InstructionResult::OpcodeNotFound + } + Error::StackUnderflow { + instruction: _, + wanted: _, + on_stack: _, + } => InstructionResult::StackUnderflow, + Error::OutOfStack { .. } => InstructionResult::StackOverflow, + Error::SubStackUnderflow { .. } => { + InstructionResult::StackUnderflow + } + Error::OutOfSubStack { + wanted: _, + limit: _, + } => InstructionResult::StackOverflow, + Error::InvalidSubEntry => InstructionResult::NotActivated, // + Error::NotEnoughBalanceForStorage { + required: _, + got: _, + } => InstructionResult::OutOfFunds, + Error::ExceedStorageLimit => InstructionResult::OutOfGas, /* treat storage as gas */ + Error::BuiltIn(_) => InstructionResult::PrecompileError, + Error::InternalContract(_) => InstructionResult::PrecompileError, /* treat internalContract as builtIn */ + Error::MutableCallInStaticContext => { + InstructionResult::StateChangeDuringStaticCall + } + Error::StateDbError(_) => InstructionResult::FatalExternalError, + Error::Wasm(_) => InstructionResult::NotActivated, + Error::OutOfBounds => InstructionResult::OutOfOffset, + Error::Reverted => InstructionResult::Revert, + Error::InvalidAddress(_) => todo!(), /* when selfdestruct refund */ + // address is invalid will + // emit this error + Error::ConflictAddress(_) => InstructionResult::CreateCollision, + }, + }; + result } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs new file mode 100644 index 0000000000..67df8c9fb6 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs @@ -0,0 +1,733 @@ +//! Types for representing call trace items. + +use crate::observer::geth_tracer::{ + config::TraceStyle, utils, utils::convert_memory, +}; +use alloy_primitives::{Address, Bytes, LogData, U256, U64}; +use alloy_rpc_trace_types::{ + geth::{CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog}, + parity::{ + Action, ActionType, CallAction, CallOutput, CallType, CreateAction, + CreateOutput, SelfdestructAction, TraceOutput, TransactionTrace, + }, +}; +use cfx_vm_types::CallType as CfxCallType; +use revm::interpreter::{ + opcode, CallContext, CallScheme, CreateScheme, InstructionResult, OpCode, +}; +use std::collections::VecDeque; + +/// A trace of a call. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallTrace { + /// The depth of the call + pub depth: usize, + /// Whether the call was successful + pub success: bool, + /// caller of this call + pub caller: Address, + /// The destination address of the call or the address from the created + /// contract. + /// + /// In other words, this is the callee if the [CallKind::Call] or the + /// address of the created contract if [CallKind::Create]. + pub address: Address, + /// Whether this is a call to a precompile + /// + /// Note: This is an Option because not all tracers make use of this + pub maybe_precompile: Option, + /// Holds the target for the __selfdestruct__ refund target + /// + /// This is only set if a selfdestruct was executed. + /// + /// Note: This not necessarily guarantees that the status is + /// [InstructionResult::SelfDestruct] There's an edge case where a new + /// created contract is immediately selfdestructed. + pub selfdestruct_refund_target: Option
, + /// The kind of call this is + pub kind: CallKind, + /// The value transferred in the call + pub value: U256, + /// The calldata for the call, or the init code for contract creations + pub data: Bytes, + /// The return data of the call if this was not a contract creation, + /// otherwise it is the runtime bytecode of the created contract + pub output: Bytes, + /// The gas cost of the call + pub gas_used: u64, + /// The gas limit of the call + pub gas_limit: u64, + /// The status of the trace's call + pub status: InstructionResult, + /// call context of the runtime + pub call_context: Option>, + /// Opcode-level execution steps + pub steps: Vec, +} + +impl CallTrace { + /// Returns true if the status code is an error or revert, See + /// [InstructionResult::Revert] + #[inline] + pub const fn is_error(&self) -> bool { !self.status.is_ok() } + + /// Returns true if the status code is a revert + #[inline] + pub fn is_revert(&self) -> bool { self.status == InstructionResult::Revert } + + /// Returns the error message if it is an erroneous result. + pub(crate) fn as_error_msg(&self, kind: TraceStyle) -> Option { + // See also + self.is_error().then(|| match self.status { + InstructionResult::Revert => if kind.is_parity() { + "Reverted" + } else { + "execution reverted" + } + .to_string(), + InstructionResult::OutOfGas | InstructionResult::MemoryOOG => { + if kind.is_parity() { + "Out of gas" + } else { + "out of gas" + } + .to_string() + } + InstructionResult::OpcodeNotFound => if kind.is_parity() { + "Bad instruction" + } else { + "invalid opcode" + } + .to_string(), + InstructionResult::StackOverflow => "Out of stack".to_string(), + InstructionResult::InvalidJump => if kind.is_parity() { + "Bad jump destination" + } else { + "invalid jump destination" + } + .to_string(), + InstructionResult::PrecompileError => if kind.is_parity() { + "Built-in failed" + } else { + "precompiled failed" + } + .to_string(), + status => format!("{:?}", status), + }) + } +} + +/// A node in the arena +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallTraceNode { + /// Parent node index in the arena + pub parent: Option, + /// Children node indexes in the arena + pub children: Vec, + /// This node's index in the arena + pub idx: usize, + /// The call trace + pub trace: CallTrace, + /// Recorded logs, if enabled + pub logs: Vec, + /// Ordering of child calls and logs + pub ordering: Vec, +} + +impl CallTraceNode { + /// Returns the call context's execution address + /// + /// See `Inspector::call` impl of + /// [TracingInspector](crate::tracing::TracingInspector) + pub const fn execution_address(&self) -> Address { + if self.trace.kind.is_delegate() { + self.trace.caller + } else { + self.trace.address + } + } + + /// Pushes all steps onto the stack in reverse order + /// so that the first step is on top of the stack + pub(crate) fn push_steps_on_stack<'a>( + &'a self, stack: &mut VecDeque>, + ) { + stack.extend(self.call_step_stack().into_iter().rev()); + } + + /// Returns a list of all steps in this trace in the order they were + /// executed + /// + /// If the step is a call, the id of the child trace is set. + pub(crate) fn call_step_stack(&self) -> Vec> { + let mut stack = Vec::with_capacity(self.trace.steps.len()); + let mut child_id = 0; + for step in self.trace.steps.iter() { + let mut item = CallTraceStepStackItem { + trace_node: self, + step, + call_child_id: None, + }; + + // If the opcode is a call, put the child trace on the stack + if step.is_calllike_op() { + // The opcode of this step is a call but it's possible that this + // step resulted in a revert or out of gas error in which case there's no actual child call executed and recorded: + if let Some(call_id) = self.children.get(child_id).copied() { + item.call_child_id = Some(call_id); + child_id += 1; + } + } + stack.push(item); + } + stack + } + + /// Returns true if this is a call to a precompile + #[inline] + pub fn is_precompile(&self) -> bool { + self.trace.maybe_precompile.unwrap_or(false) + } + + /// Returns the kind of call the trace belongs to + #[inline] + pub const fn kind(&self) -> CallKind { self.trace.kind } + + /// Returns the status of the call + #[inline] + pub const fn status(&self) -> InstructionResult { self.trace.status } + + /// Returns true if the call was a selfdestruct + /// + /// A selfdestruct is marked by the refund target being set. + /// + /// See also `TracingInspector::selfdestruct` + /// + /// Note: We can't rely in the [Self::status] being + /// [InstructionResult::SelfDestruct] because there's an edge case where + /// a new created contract (CREATE) is immediately selfdestructed. + #[inline] + pub const fn is_selfdestruct(&self) -> bool { + self.trace.selfdestruct_refund_target.is_some() + } + + /// Converts this node into a parity `TransactionTrace` + pub fn parity_transaction_trace( + &self, trace_address: Vec, + ) -> TransactionTrace { + let action = self.parity_action(); + let result = if self.trace.is_error() && !self.trace.is_revert() { + // if the trace is a selfdestruct or an error that is not a revert, + // the result is None + None + } else { + Some(self.parity_trace_output()) + }; + let error = self.trace.as_error_msg(TraceStyle::Parity); + TransactionTrace { + action, + error, + result, + trace_address, + subtraces: self.children.len(), + } + } + + /// Returns the `Output` for a parity trace + pub fn parity_trace_output(&self) -> TraceOutput { + match self.kind() { + CallKind::Call + | CallKind::StaticCall + | CallKind::CallCode + | CallKind::DelegateCall => TraceOutput::Call(CallOutput { + gas_used: U64::from(self.trace.gas_used), + output: self.trace.output.clone(), + }), + CallKind::Create | CallKind::Create2 => { + TraceOutput::Create(CreateOutput { + gas_used: U64::from(self.trace.gas_used), + code: self.trace.output.clone(), + address: self.trace.address, + }) + } + } + } + + /// If the trace is a selfdestruct, returns the `Action` for a parity trace. + pub fn parity_selfdestruct_action(&self) -> Option { + if self.is_selfdestruct() { + Some(Action::Selfdestruct(SelfdestructAction { + address: self.trace.address, + refund_address: self + .trace + .selfdestruct_refund_target + .unwrap_or_default(), + balance: self.trace.value, + })) + } else { + None + } + } + + /// If the trace is a selfdestruct, returns the `CallFrame` for a geth call + /// trace + pub fn geth_selfdestruct_call_trace(&self) -> Option { + if self.is_selfdestruct() { + Some(CallFrame { + typ: "SELFDESTRUCT".to_string(), + from: self.trace.caller, + to: self.trace.selfdestruct_refund_target, + value: Some(self.trace.value), + ..Default::default() + }) + } else { + None + } + } + + /// If the trace is a selfdestruct, returns the `TransactionTrace` for a + /// parity trace. + pub fn parity_selfdestruct_trace( + &self, trace_address: Vec, + ) -> Option { + let trace = self.parity_selfdestruct_action()?; + Some(TransactionTrace { + action: trace, + error: None, + result: None, + trace_address, + subtraces: 0, + }) + } + + /// Returns the `Action` for a parity trace. + /// + /// Caution: This does not include the selfdestruct action, if the trace is + /// a selfdestruct, since those are handled in addition to the call + /// action. + pub fn parity_action(&self) -> Action { + match self.kind() { + CallKind::Call + | CallKind::StaticCall + | CallKind::CallCode + | CallKind::DelegateCall => Action::Call(CallAction { + from: self.trace.caller, + to: self.trace.address, + value: self.trace.value, + gas: U64::from(self.trace.gas_limit), + input: self.trace.data.clone(), + call_type: self.kind().into(), + }), + CallKind::Create | CallKind::Create2 => { + Action::Create(CreateAction { + from: self.trace.caller, + value: self.trace.value, + gas: U64::from(self.trace.gas_limit), + init: self.trace.data.clone(), + }) + } + } + } + + /// Converts this call trace into an _empty_ geth [CallFrame] + pub fn geth_empty_call_frame(&self, include_logs: bool) -> CallFrame { + let mut call_frame = CallFrame { + typ: self.trace.kind.to_string(), + from: self.trace.caller, + to: Some(self.trace.address), + value: Some(self.trace.value), + gas: U256::from(self.trace.gas_limit), + gas_used: U256::from(self.trace.gas_used), + input: self.trace.data.clone(), + output: (!self.trace.output.is_empty()) + .then(|| self.trace.output.clone()), + error: None, + revert_reason: None, + calls: Default::default(), + logs: Default::default(), + }; + + if self.trace.kind.is_static_call() { + // STATICCALL frames don't have a value + call_frame.value = None; + } + + // we need to populate error and revert reason + if !self.trace.success { + call_frame.revert_reason = + utils::maybe_revert_reason(self.trace.output.as_ref()); + + // Note: the call tracer mimics parity's trace transaction and geth maps errors to parity style error messages, + call_frame.error = self.trace.as_error_msg(TraceStyle::Parity); + } + + if include_logs && !self.logs.is_empty() { + call_frame.logs = self + .logs + .iter() + .map(|log| CallLogFrame { + address: Some(self.execution_address()), + topics: Some(log.topics().to_vec()), + data: Some(log.data.clone()), + }) + .collect(); + } + + call_frame + } +} + +/// A unified representation of a call. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "UPPERCASE"))] +pub enum CallKind { + /// Represents a regular call. + #[default] + Call, + /// Represents a static call. + StaticCall, + /// Represents a call code operation. + CallCode, + /// Represents a delegate call. + DelegateCall, + /// Represents a contract creation operation. + Create, + /// Represents a contract creation operation using the CREATE2 opcode. + Create2, +} + +impl CallKind { + /// Returns true if the call is a create + #[inline] + pub const fn is_any_create(&self) -> bool { + matches!(self, Self::Create | Self::Create2) + } + + /// Returns true if the call is a delegate of some sorts + #[inline] + pub const fn is_delegate(&self) -> bool { + matches!(self, Self::DelegateCall | Self::CallCode) + } + + /// Returns true if the call is [CallKind::StaticCall]. + #[inline] + pub const fn is_static_call(&self) -> bool { + matches!(self, Self::StaticCall) + } +} + +impl std::fmt::Display for CallKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Call => { + write!(f, "CALL") + } + Self::StaticCall => { + write!(f, "STATICCALL") + } + Self::CallCode => { + write!(f, "CALLCODE") + } + Self::DelegateCall => { + write!(f, "DELEGATECALL") + } + Self::Create => { + write!(f, "CREATE") + } + Self::Create2 => { + write!(f, "CREATE2") + } + } + } +} + +impl From for CallKind { + fn from(scheme: CallScheme) -> Self { + match scheme { + CallScheme::Call => Self::Call, + CallScheme::StaticCall => Self::StaticCall, + CallScheme::CallCode => Self::CallCode, + CallScheme::DelegateCall => Self::DelegateCall, + } + } +} + +impl From for CallKind { + fn from(create: CreateScheme) -> Self { + match create { + CreateScheme::Create => Self::Create, + CreateScheme::Create2 { .. } => Self::Create2, + } + } +} + +impl From for CallKind { + fn from(ct: CfxCallType) -> Self { + match ct { + CfxCallType::None => Self::Create, // TODO(pana) check this + CfxCallType::Call => Self::Call, + CfxCallType::CallCode => Self::CallCode, + CfxCallType::DelegateCall => Self::DelegateCall, + CfxCallType::StaticCall => Self::StaticCall, + } + } +} + +impl From for ActionType { + fn from(kind: CallKind) -> Self { + match kind { + CallKind::Call + | CallKind::StaticCall + | CallKind::DelegateCall + | CallKind::CallCode => Self::Call, + CallKind::Create => Self::Create, + CallKind::Create2 => Self::Create, + } + } +} + +impl From for CallType { + fn from(ty: CallKind) -> Self { + match ty { + CallKind::Call => Self::Call, + CallKind::StaticCall => Self::StaticCall, + CallKind::CallCode => Self::CallCode, + CallKind::DelegateCall => Self::DelegateCall, + CallKind::Create => Self::None, + CallKind::Create2 => Self::None, + } + } +} + +pub(crate) struct CallTraceStepStackItem<'a> { + /// The trace node that contains this step + pub(crate) trace_node: &'a CallTraceNode, + /// The step that this stack item represents + pub(crate) step: &'a CallTraceStep, + /// The index of the child call in the CallArena if this step's opcode is a + /// call + pub(crate) call_child_id: Option, +} + +/// Ordering enum for calls and logs +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum LogCallOrder { + /// Contains the index of the corresponding log + Log(usize), + /// Contains the index of the corresponding trace node + Call(usize), +} + +/// Represents a tracked call step during execution +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallTraceStep { + // Fields filled in `step` + /// Call depth + pub depth: u64, + /// Program counter before step execution + pub pc: usize, + /// Opcode to be executed + #[cfg_attr(feature = "serde", serde(with = "opcode_serde"))] + pub op: OpCode, + /// Current contract address + pub contract: Address, + /// Stack before step execution + pub stack: Option>, + /// The new stack items placed by this step if any + pub push_stack: Option>, + /// All allocated memory in a step + /// + /// This will be empty if memory capture is disabled + pub memory: RecordedMemory, + /// Size of memory at the beginning of the step + pub memory_size: usize, + /// Remaining gas before step execution + pub gas_remaining: u64, + /// Gas refund counter before step execution + pub gas_refund_counter: u64, + // Fields filled in `step_end` + /// Gas cost of step execution + pub gas_cost: u64, + /// Change of the contract state after step execution (effect of the + /// SLOAD/SSTORE instructions) + pub storage_change: Option, + /// Final status of the step + /// + /// This is set after the step was executed. + pub status: InstructionResult, +} + +// === impl CallTraceStep === + +impl CallTraceStep { + /// Converts this step into a geth [StructLog] + /// + /// This sets memory and stack capture based on the `opts` parameter. + pub(crate) fn convert_to_geth_struct_log( + &self, opts: &GethDefaultTracingOptions, + ) -> StructLog { + let mut log = StructLog { + depth: self.depth, + error: self.as_error(), + gas: self.gas_remaining, + gas_cost: self.gas_cost, + op: self.op.to_string(), + pc: self.pc as u64, + refund_counter: (self.gas_refund_counter > 0) + .then_some(self.gas_refund_counter), + // Filled, if not disabled manually + stack: None, + // Filled in `CallTraceArena::geth_trace` as a result of compounding + // all slot changes + return_data: None, + // Filled via trace object + storage: None, + // Only enabled if `opts.enable_memory` is true + memory: None, + // This is None in the rpc response + memory_size: None, + }; + + if opts.is_stack_enabled() { + log.stack.clone_from(&self.stack); + } + + if opts.is_memory_enabled() { + log.memory = Some(self.memory.memory_chunks()); + } + + log + } + + /// Returns true if the step is a STOP opcode + #[inline] + pub(crate) const fn is_stop(&self) -> bool { + matches!(self.op.get(), opcode::STOP) + } + + /// Returns true if the step is a call operation, any of + /// CALL, CALLCODE, DELEGATECALL, STATICCALL, CREATE, CREATE2 + #[inline] + pub(crate) const fn is_calllike_op(&self) -> bool { + matches!( + self.op.get(), + opcode::CALL + | opcode::DELEGATECALL + | opcode::STATICCALL + | opcode::CREATE + | opcode::CALLCODE + | opcode::CREATE2 + ) + } + + // Returns true if the status code is an error or revert, See + // [InstructionResult::Revert] + #[inline] + pub(crate) const fn is_error(&self) -> bool { + self.status as u8 >= InstructionResult::Revert as u8 + } + + /// Returns the error message if it is an erroneous result. + #[inline] + pub(crate) fn as_error(&self) -> Option { + self.is_error().then(|| format!("{:?}", self.status)) + } +} + +/// Represents the source of a storage change - e.g., whether it came +/// from an SSTORE or SLOAD instruction. +#[allow(clippy::upper_case_acronyms)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum StorageChangeReason { + /// SLOAD opcode + SLOAD, + /// SSTORE opcode + SSTORE, +} + +/// Represents a storage change during execution. +/// +/// This maps to evm internals: +/// [JournalEntry::StorageChange](revm::JournalEntry::StorageChange) +/// +/// It is used to track both storage change and warm load of a storage slot. For +/// warm load in regard to EIP-2929 AccessList had_value will be None. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct StorageChange { + /// key of the storage slot + pub key: U256, + /// Current value of the storage slot + pub value: U256, + /// The previous value of the storage slot, if any + pub had_value: Option, + /// How this storage was accessed + pub reason: StorageChangeReason, +} + +/// Represents the memory captured during execution +/// +/// This is a wrapper around the [SharedMemory](revm::interpreter::SharedMemory) +/// context memory. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct RecordedMemory(pub(crate) Vec); + +impl RecordedMemory { + #[inline] + pub(crate) fn new(mem: Vec) -> Self { Self(mem) } + + /// Returns the memory as a byte slice + #[inline] + pub fn as_bytes(&self) -> &[u8] { &self.0 } + + #[inline] + pub(crate) fn resize(&mut self, size: usize) { self.0.resize(size, 0); } + + /// Returns the size of the memory + #[inline] + pub fn len(&self) -> usize { self.0.len() } + + /// Returns whether the memory is empty + #[inline] + pub fn is_empty(&self) -> bool { self.0.is_empty() } + + /// Converts the memory into 32byte hex chunks + #[inline] + pub fn memory_chunks(&self) -> Vec { + convert_memory(self.as_bytes()) + } +} + +impl AsRef<[u8]> for RecordedMemory { + fn as_ref(&self) -> &[u8] { self.as_bytes() } +} + +#[cfg(feature = "serde")] +mod opcode_serde { + use super::OpCode; + use serde::{Deserialize, Deserializer, Serializer}; + + pub(super) fn serialize( + op: &OpCode, serializer: S, + ) -> Result + where S: Serializer { + serializer.serialize_u8(op.get()) + } + + pub(super) fn deserialize<'de, D>( + deserializer: D, + ) -> Result + where D: Deserializer<'de> { + let op = u8::deserialize(deserializer)?; + Ok(OpCode::new(op).unwrap_or_else(|| { + OpCode::new(revm::interpreter::opcode::INVALID).unwrap() + })) + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs new file mode 100644 index 0000000000..189e15ce04 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs @@ -0,0 +1,154 @@ +//! Util functions for revm related ops +use alloy_primitives::{hex, B256}; +use alloy_sol_types::{ContractError, GenericRevertReason}; +use cfx_types::{Address, H160, H256, U256}; +use revm::{ + interpreter::opcode, + primitives::{Address as RAddress, U256 as RU256}, +}; + +/// creates the memory data in 32byte chunks +/// see +#[inline] +pub(crate) fn convert_memory(data: &[u8]) -> Vec { + let mut memory = Vec::with_capacity((data.len() + 31) / 32); + for idx in (0..data.len()).step_by(32) { + let len = std::cmp::min(idx + 32, data.len()); + memory.push(hex::encode(&data[idx..len])); + } + memory +} + +/// Get the gas used, accounting for refunds +#[inline] +pub(crate) fn gas_used(spent: u64, refunded: u64) -> u64 { + let refund_quotient = 5; + spent - (refunded).min(spent / refund_quotient) +} + +/// Returns a non empty revert reason if the output is a revert/error. +#[inline] +pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option { + let reason = match GenericRevertReason::decode(output)? { + GenericRevertReason::ContractError(err) => { + match err { + // return the raw revert reason and don't use the revert's + // display message + ContractError::Revert(revert) => revert.reason, + err => err.to_string(), + } + } + GenericRevertReason::RawString(err) => err, + }; + if reason.is_empty() { + None + } else { + Some(reason) + } +} + +/// Returns the number of items pushed on the stack by a given opcode. +/// This used to determine how many stack etries to put in the `push` element +/// in a parity vmTrace. +/// The value is obvious for most opcodes, but SWAP* and DUP* are a bit weird, +/// and we handle those as they are handled in parity vmtraces. +/// For reference: +pub(crate) const fn stack_push_count(step_op: u8) -> usize { + match step_op { + opcode::PUSH0..=opcode::PUSH32 => 1, + opcode::SWAP1..=opcode::SWAP16 => { + (step_op - opcode::SWAP1) as usize + 2 + } + opcode::DUP1..=opcode::DUP16 => (step_op - opcode::DUP1) as usize + 2, + opcode::CALLDATALOAD + | opcode::SLOAD + | opcode::MLOAD + | opcode::CALLDATASIZE + | opcode::LT + | opcode::GT + | opcode::DIV + | opcode::SDIV + | opcode::SAR + | opcode::AND + | opcode::EQ + | opcode::CALLVALUE + | opcode::ISZERO + | opcode::ADD + | opcode::EXP + | opcode::CALLER + | opcode::KECCAK256 + | opcode::SUB + | opcode::ADDRESS + | opcode::GAS + | opcode::MUL + | opcode::RETURNDATASIZE + | opcode::NOT + | opcode::SHR + | opcode::SHL + | opcode::EXTCODESIZE + | opcode::SLT + | opcode::OR + | opcode::NUMBER + | opcode::PC + | opcode::TIMESTAMP + | opcode::BALANCE + | opcode::SELFBALANCE + | opcode::MULMOD + | opcode::ADDMOD + | opcode::BASEFEE + | opcode::BLOCKHASH + | opcode::BYTE + | opcode::XOR + | opcode::ORIGIN + | opcode::CODESIZE + | opcode::MOD + | opcode::SIGNEXTEND + | opcode::GASLIMIT + | opcode::DIFFICULTY + | opcode::SGT + | opcode::GASPRICE + | opcode::MSIZE + | opcode::EXTCODEHASH + | opcode::SMOD + | opcode::CHAINID + | opcode::COINBASE => 1, + _ => 0, + } +} + +// convert from cfx U256 to Revm U256 +pub fn convert_u256(u: U256) -> RU256 { + let mut be_bytes: [u8; 32] = [0; 32]; + u.to_big_endian(&mut be_bytes); + RU256::from_be_bytes(be_bytes) +} + +pub fn convert_h160(h: H160) -> RAddress { RAddress::from_slice(h.as_bytes()) } + +pub fn convert_h256(h: H256) -> B256 { B256::from(h.0) } + +pub fn to_h160(address: RAddress) -> Address { + Address::from_slice(address.as_slice()) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_sol_types::{GenericContractError, SolInterface}; + + #[test] + fn decode_revert_reason() { + let err = GenericContractError::Revert("my revert".into()); + let encoded = err.abi_encode(); + let reason = maybe_revert_reason(&encoded).unwrap(); + assert_eq!(reason, "my revert"); + } + + // + #[test] + fn decode_revert_reason_with_error() { + let err = hex!("08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024556e697377617056323a20494e53554646494349454e545f494e5055545f414d4f554e5400000000000000000000000000000000000000000000000000000080"); + let reason = maybe_revert_reason(&err[..]).unwrap(); + assert_eq!(reason, "UniswapV2: INSUFFICIENT_INPUT_AMOUNT"); + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index 2f6e2e53cc..64f91e7712 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -10,7 +10,7 @@ use gasman::GasMan; use cfx_executor::executive_observer::{AsTracer, DrainTrace, TracerTrait}; use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; -use self::geth_tracer::GethTracer; +use self::geth_tracer::{GethTracer, TracingInspectorConfig}; #[derive(AsTracer, DrainTrace)] pub struct Observer { @@ -19,8 +19,6 @@ pub struct Observer { pub geth_tracer: Option, } -// TODO[geth-tracer]: instantiation your tracer here. - impl Observer { pub fn with_tracing() -> Self { Observer { @@ -45,4 +43,12 @@ impl Observer { geth_tracer: None, } } + + pub fn geth_tracer(config: TracingInspectorConfig) -> Self { + Observer { + tracer: None, + gas_man: None, + geth_tracer: Some(GethTracer::new(config)), + } + } } diff --git a/crates/cfxcore/execute-helper/src/tx_outcome.rs b/crates/cfxcore/execute-helper/src/tx_outcome.rs index e758d9e3c2..6a0618e4b2 100644 --- a/crates/cfxcore/execute-helper/src/tx_outcome.rs +++ b/crates/cfxcore/execute-helper/src/tx_outcome.rs @@ -5,7 +5,8 @@ use cfx_types::{H256, U256}; use pow_types::StakingEvent; use primitives::Receipt; -use crate::observer::geth_tracer::GethTracerKey; +use crate::observer::geth_tracer::GethTraceKey; +use alloy_rpc_trace_types::geth::GethTrace; use super::{ observer::exec_tracer::{ExecTrace, ExecTraceKey}, @@ -19,6 +20,7 @@ pub struct ProcessTxOutcome { pub tx_staking_events: Vec, pub tx_exec_error_msg: String, pub consider_repacked: bool, + pub geth_trace: Option, } fn tx_traces(outcome: &ExecutionOutcome) -> Vec { @@ -28,15 +30,17 @@ fn tx_traces(outcome: &ExecutionOutcome) -> Vec { .unwrap_or_default() } +fn geth_traces(outcome: &ExecutionOutcome) -> Option { + outcome + .try_as_executed() + .and_then(|executed| executed.ext_result.get::().cloned()) +} + pub fn make_process_tx_outcome( outcome: ExecutionOutcome, accumulated_gas_used: &mut U256, tx_hash: H256, ) -> ProcessTxOutcome { - // TODO[geth-tracer]: extract your trace result here. - let _maybe_geth_trace = outcome - .try_as_executed() - .and_then(|executed| executed.ext_result.get::()); - let tx_traces = tx_traces(&outcome); + let geth_trace = geth_traces(&outcome); let tx_exec_error_msg = outcome.error_message(); let consider_repacked = outcome.consider_repacked(); let receipt = outcome.make_receipt(accumulated_gas_used); @@ -52,5 +56,6 @@ pub fn make_process_tx_outcome( tx_staking_events, tx_exec_error_msg, consider_repacked, + geth_trace, } } diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index ccd6c694a5..3321f0ef6f 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-executor" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index 425e8d6c92..e0620f57a6 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -344,6 +344,8 @@ impl<'a> ContextTrait for Context<'a> { return Err(vm::Error::MutableCallInStaticContext); } + self.tracer.log(&self.origin.address, topics.clone(), data); + let address = self.origin.address.clone(); self.substate.logs.push(LogEntry { address, @@ -406,8 +408,15 @@ impl<'a> ContextTrait for Context<'a> { return Err(vm::Error::MutableCallInStaticContext); } + let contract_address = self.origin.address; + let contract_address_with_space = + self.origin.address.with_space(self.space); + let balance = self.state.balance(&contract_address_with_space)?; + self.tracer + .selfdestruct(&contract_address, refund_address, balance); + suicide_impl( - &self.origin.address.with_space(self.space), + &contract_address_with_space, &refund_address.with_space(self.space), self.state, &self.spec, @@ -447,6 +456,14 @@ impl<'a> ContextTrait for Context<'a> { // // TODO // } + fn trace_step(&mut self, interpreter: &dyn vm::InterpreterInfo) { + self.tracer.step(interpreter, self.depth); + } + + fn trace_step_end(&mut self, interpreter: &dyn vm::InterpreterInfo) { + self.tracer.step_end(interpreter); + } + fn opcode_trace_enabled(&self) -> bool { let mut enabled = false; self.tracer.do_trace_opcode(&mut enabled); diff --git a/crates/cfxcore/executor/src/internal_contract/components/activation.rs b/crates/cfxcore/executor/src/internal_contract/components/activation.rs index 3fa7926ea5..9646b9e93c 100644 --- a/crates/cfxcore/executor/src/internal_contract/components/activation.rs +++ b/crates/cfxcore/executor/src/internal_contract/components/activation.rs @@ -1,4 +1,5 @@ use cfx_vm_types::Spec; +#[allow(unused_imports)] pub use primitives::BlockNumber; pub trait IsActive { diff --git a/crates/cfxcore/executor/src/internal_contract/components/mod.rs b/crates/cfxcore/executor/src/internal_contract/components/mod.rs index 7bf6679e27..a52af0d7e3 100644 --- a/crates/cfxcore/executor/src/internal_contract/components/mod.rs +++ b/crates/cfxcore/executor/src/internal_contract/components/mod.rs @@ -14,6 +14,7 @@ pub use contract::{InternalContractTrait, SolFnTable}; pub use contract_map::InternalContractMap; pub use event::SolidityEventTrait; pub use executable::InternalContractExec; +#[allow(unused_imports)] pub use function::{ ExecutionTrait, InterfaceTrait, SimpleExecutionTrait, SolidityFunctionTrait, UpfrontPaymentTrait, diff --git a/crates/cfxcore/executor/src/machine/mod.rs b/crates/cfxcore/executor/src/machine/mod.rs index 97e596b06c..1bdfe5c746 100644 --- a/crates/cfxcore/executor/src/machine/mod.rs +++ b/crates/cfxcore/executor/src/machine/mod.rs @@ -23,7 +23,7 @@ pub type SpecCreationRules = dyn Fn(&mut Spec, BlockNumber) + Sync + Send; pub struct Machine { params: CommonParams, - vm: VmFactory, + vm_factory: VmFactory, builtins: Arc>, builtins_evm: Arc>, internal_contracts: Arc, @@ -63,7 +63,7 @@ impl Machine { spec } - /// Builtin-contracts for the chain.. + /// Builtin-contracts for the core space pub fn builtins(&self) -> &BTreeMap { &*self.builtins } /// Builtin-contracts for the chain.. @@ -72,15 +72,15 @@ impl Machine { } /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { self.vm.clone() } + pub fn vm_factory(&self) -> VmFactory { self.vm_factory.clone() } - pub fn vm_factory_ref(&self) -> &VmFactory { &self.vm } + pub fn vm_factory_ref(&self) -> &VmFactory { &self.vm_factory } } -pub fn new_machine(params: CommonParams, vm: VmFactory) -> Machine { +pub fn new_machine(params: CommonParams, vm_factory: VmFactory) -> Machine { Machine { params, - vm, + vm_factory, builtins: Arc::new(BTreeMap::new()), builtins_evm: Arc::new(Default::default()), internal_contracts: Arc::new(InternalContractMap::default()), @@ -172,7 +172,7 @@ fn new_builtin_map( } pub fn new_machine_with_builtin( - params: CommonParams, vm: VmFactory, + params: CommonParams, vm_factory: VmFactory, ) -> Machine { let builtin = new_builtin_map(¶ms, Space::Native); let builtin_evm = new_builtin_map(¶ms, Space::Ethereum); @@ -180,7 +180,7 @@ pub fn new_machine_with_builtin( let internal_contracts = InternalContractMap::new(¶ms); Machine { params, - vm, + vm_factory, builtins: Arc::new(builtin), builtins_evm: Arc::new(builtin_evm), internal_contracts: Arc::new(internal_contracts), diff --git a/crates/cfxcore/executor/src/machine/vm_factory.rs b/crates/cfxcore/executor/src/machine/vm_factory.rs index af623b7e6d..54024c53f2 100644 --- a/crates/cfxcore/executor/src/machine/vm_factory.rs +++ b/crates/cfxcore/executor/src/machine/vm_factory.rs @@ -8,23 +8,23 @@ use cfx_vm_types::{ActionParams, Exec, Spec}; /// Virtual machine factory #[derive(Default, Clone)] pub struct VmFactory { - evm: EvmFactory, + evm_factory: EvmFactory, } impl VmFactory { pub fn create( &self, params: ActionParams, spec: &Spec, depth: usize, ) -> Box { - self.evm.create(params, spec, depth) + self.evm_factory.create(params, spec, depth) } pub fn new(cache_size: usize) -> Self { VmFactory { - evm: EvmFactory::new(VMType::Interpreter, cache_size), + evm_factory: EvmFactory::new(VMType::Interpreter, cache_size), } } } impl From for VmFactory { - fn from(evm: EvmFactory) -> Self { VmFactory { evm } } + fn from(evm_factory: EvmFactory) -> Self { VmFactory { evm_factory } } } diff --git a/crates/cfxcore/executor/src/observer/internal_transfer_tracer.rs b/crates/cfxcore/executor/src/observer/internal_transfer_tracer.rs index e88f28cfee..ba594a9e94 100644 --- a/crates/cfxcore/executor/src/observer/internal_transfer_tracer.rs +++ b/crates/cfxcore/executor/src/observer/internal_transfer_tracer.rs @@ -20,7 +20,7 @@ pub trait InternalTransferTracer { ) { } - fn trace_convert_stroage_points( + fn trace_convert_storage_points( &mut self, addr: Address, from_balance: U256, from_collateral: U256, ) { if !from_balance.is_zero() { diff --git a/crates/cfxcore/executor/src/observer/opcode_tracer.rs b/crates/cfxcore/executor/src/observer/opcode_tracer.rs index 46ffb3aa88..74139e2b3b 100644 --- a/crates/cfxcore/executor/src/observer/opcode_tracer.rs +++ b/crates/cfxcore/executor/src/observer/opcode_tracer.rs @@ -1,3 +1,6 @@ +use cfx_types::{Address, H256, U256}; +use cfx_vm_types::InterpreterInfo; + use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; @@ -5,5 +8,39 @@ use impl_trait_for_tuples::impl_for_tuples; #[autoimpl(for &mut T)] pub trait OpcodeTracer { fn do_trace_opcode(&self, _enabled: &mut bool) {} - // TODO[geth-tracer]: Define your hook here for EVM opcode + + /// Called before the interpreter is initialized. + #[inline] + fn initialize_interp(&mut self, gas_limit: U256) { let _ = gas_limit; } + + /// Called on each step of the interpreter. + /// + /// Information about the current execution, including the memory, stack and + /// more is available on `interp` (see [Interpreter]). + fn step(&mut self, interp: &dyn InterpreterInfo, depth: usize) { + let _ = interp; + let _ = depth; + } + + /// Called after `step` when the instruction has been executed. + fn step_end(&mut self, interp: &dyn InterpreterInfo) { let _ = interp; } + + /// Called when a log is emitted. + #[inline] + fn log(&mut self, address: &Address, topics: Vec, data: &[u8]) { + let _ = address; + let _ = topics; + let _ = data; + } + + /// Called when a contract has been self-destructed with funds transferred + /// to target. + #[inline] + fn selfdestruct( + &mut self, contract: &Address, target: &Address, value: U256, + ) { + let _ = contract; + let _ = target; + let _ = value; + } } diff --git a/crates/cfxcore/executor/src/observer/storage_tracer.rs b/crates/cfxcore/executor/src/observer/storage_tracer.rs index a2f16dbae3..50e595bff4 100644 --- a/crates/cfxcore/executor/src/observer/storage_tracer.rs +++ b/crates/cfxcore/executor/src/observer/storage_tracer.rs @@ -3,6 +3,4 @@ use impl_trait_for_tuples::impl_for_tuples; #[impl_for_tuples(3)] #[autoimpl(for &mut T)] -pub trait StorageTracer { - // TODO[geth-tracer]: Define your hook here for EVM opcode -} +pub trait StorageTracer {} diff --git a/crates/cfxcore/executor/src/observer/tracer_trait.rs b/crates/cfxcore/executor/src/observer/tracer_trait.rs index 129f796d60..45d0d3a2fd 100644 --- a/crates/cfxcore/executor/src/observer/tracer_trait.rs +++ b/crates/cfxcore/executor/src/observer/tracer_trait.rs @@ -1,15 +1,14 @@ use super::{ - call_tracer::CallTracer, checkpoint_tracer::CheckpointTracer, - internal_transfer_tracer::InternalTransferTracer, - opcode_tracer::OpcodeTracer, StorageTracer, + CallTracer, CheckpointTracer, InternalTransferTracer, OpcodeTracer, + StorageTracer, }; pub trait TracerTrait: CheckpointTracer + CallTracer + InternalTransferTracer - + OpcodeTracer + StorageTracer + + OpcodeTracer { } diff --git a/crates/cfxcore/executor/src/stack/executable.rs b/crates/cfxcore/executor/src/stack/executable.rs index 90b5644a25..1dea509e34 100644 --- a/crates/cfxcore/executor/src/stack/executable.rs +++ b/crates/cfxcore/executor/src/stack/executable.rs @@ -1,7 +1,7 @@ use super::{FrameLocal, Resumable}; use crate::{ builtin::BuiltinExec, context::Context, - internal_contract::InternalContractExec, + internal_contract::InternalContractExec, stack::RuntimeRes, }; use cfx_statedb::Result as DbResult; use cfx_types::{AddressSpaceUtil, U256}; @@ -46,6 +46,7 @@ use ExecutableOutcome::*; /// contracts, simple transfers, or the execution of EVM bytecode. pub fn make_executable<'a>( frame_local: &FrameLocal<'a>, params: ActionParams, + resources: &mut RuntimeRes<'a>, ) -> Box { let is_create = frame_local.create_address.is_some(); let code_address = params.code_address.with_space(params.space); @@ -73,6 +74,10 @@ pub fn make_executable<'a>( if is_create || params.code.is_some() { trace!("CallCreate"); + + // call the initialize_interp hook to log gas_limit + resources.tracer.initialize_interp(params.gas.clone()); + let factory = frame_local.machine.vm_factory_ref(); Box::new(factory.create(params, frame_local.spec, frame_local.depth)) } else { diff --git a/crates/cfxcore/executor/src/stack/frame_start.rs b/crates/cfxcore/executor/src/stack/frame_start.rs index 47d772cbe6..9273f945e1 100644 --- a/crates/cfxcore/executor/src/stack/frame_start.rs +++ b/crates/cfxcore/executor/src/stack/frame_start.rs @@ -113,7 +113,7 @@ impl<'a> FreshFrame<'a> { )? }; - let executable = make_executable(&frame_local, params); + let executable = make_executable(&frame_local, params, resources); run_executable(executable, frame_local, resources) } } diff --git a/crates/cfxcore/executor/src/state/state_object/collateral.rs b/crates/cfxcore/executor/src/state/state_object/collateral.rs index b533a08d63..8e216715da 100644 --- a/crates/cfxcore/executor/src/state/state_object/collateral.rs +++ b/crates/cfxcore/executor/src/state/state_object/collateral.rs @@ -174,7 +174,7 @@ fn settle_collateral_for_address( && (!sub.is_zero() || !inc.is_zero()) { let (from_balance, from_collateral) = state.initialize_cip107(addr)?; - tracer.trace_convert_stroage_points( + tracer.trace_convert_storage_points( *addr, from_balance, from_collateral, diff --git a/crates/cfxcore/vm-interpreter/Cargo.toml b/crates/cfxcore/vm-interpreter/Cargo.toml index 07ab4e4b6b..a9758436cb 100644 --- a/crates/cfxcore/vm-interpreter/Cargo.toml +++ b/crates/cfxcore/vm-interpreter/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-interpreter" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] bit-set = "0.4" diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index 2fbeac1a7a..c3c64df3e1 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -160,6 +160,10 @@ enum_with_from_u8! { #[doc = "get balance of own account"] SELFBALANCE = 0x47, + // BASEFEE=0x48 + // BLOBHASH=0x49 + // BLOBBASEFEE=0x4A + #[doc = "remove item from stack"] POP = 0x50, #[doc = "load word from memory"] @@ -347,10 +351,13 @@ enum_with_from_u8! { DELEGATECALL = 0xf4, #[doc = "create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160"] CREATE2 = 0xf5, - #[doc = "stop execution and revert state changes. Return output data."] - REVERT = 0xfd, #[doc = "like CALL but it does not take value, nor modify the state"] STATICCALL = 0xfa, + #[doc = "stop execution and revert state changes. Return output data."] + REVERT = 0xfd, + + // INVALID = 0xfe + #[doc = "halt execution and register account for later deletion"] SUICIDE = 0xff, } @@ -368,6 +375,8 @@ impl Instruction { return instruction; } + pub fn u8(self) -> u8 { self as u8 } + /// Returns number of bytes to read for `PUSHN` instruction /// PUSH1 -> 1 pub fn push_bytes(&self) -> Option { diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index d31d61d028..06d83429c8 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -46,8 +46,8 @@ use cfx_bytes::Bytes; use cfx_types::{Address, BigEndianHash, Space, H256, U256, U512}; use cfx_vm_types::{ self as vm, ActionParams, ActionValue, CallType, ContractCreateResult, - CreateContractAddress, GasLeft, MessageCallResult, ParamsType, ReturnData, - Spec, TrapError, TrapKind, + CreateContractAddress, GasLeft, InstructionResult, InterpreterInfo, + MessageCallResult, ParamsType, ReturnData, Spec, TrapError, TrapKind, }; use keccak_hash::keccak; use std::{cmp, convert::TryFrom, marker::PhantomData, mem, sync::Arc}; @@ -92,26 +92,6 @@ impl CodeReader { fn len(&self) -> usize { self.code.len() } } -enum InstructionResult { - Ok, - UnusedGas(Gas), - JumpToPosition(U256), - JumpToSubroutine(U256), - ReturnFromSubroutine(usize), - StopExecutionNeedsReturn { - /// Gas left. - gas: Gas, - /// Return data offset. - init_off: U256, - /// Return data size. - init_size: U256, - /// Apply or revert state changes. - apply: bool, - }, - StopExecution, - Trap(TrapKind), -} - /// ActionParams without code, so that it can be feed into CodeReader. #[derive(Debug)] #[allow(dead_code)] @@ -196,6 +176,7 @@ pub struct Interpreter { resume_output_range: Option<(U256, U256)>, resume_result: Option>, last_stack_ret_len: usize, + instruction_result: Option>, _type: PhantomData, } @@ -305,7 +286,7 @@ impl vm::ResumeCreate for Interpreter { } } -impl Interpreter { +impl Interpreter { /// Create a new `Interpreter` instance with shared cache. pub fn new( mut params: ActionParams, cache: Arc, spec: &Spec, @@ -341,11 +322,13 @@ impl Interpreter { last_stack_ret_len: 0, resume_output_range: None, resume_result: None, + instruction_result: None, _type: PhantomData, } } /// Execute a single step on the VM. + /// First check gas and reader is ok, then will call step_inner #[inline(always)] pub fn step(&mut self, context: &mut dyn vm::Context) -> InterpreterResult { if self.done { @@ -381,6 +364,11 @@ impl Interpreter { let result = match self.resume_result.take() { Some(result) => result, None => { + // invoke tracer step hook + if self.do_trace { + context.trace_step(self); + } + let opcode = self.reader.code[self.reader.position]; let instruction = Instruction::from_u8_versioned(opcode, context.spec()); @@ -489,6 +477,8 @@ impl Interpreter { Ok(x) => x, }; + self.instruction_result = Some(result.clone()); + evm_debug!({ self.informant.after_instruction(instruction) }); result @@ -517,6 +507,11 @@ impl Interpreter { // ); // } + // invoke trace step_end hook + if self.do_trace { + context.trace_step_end(self); + } + // Advance match result { InstructionResult::JumpToPosition(position) => { @@ -1598,6 +1593,68 @@ impl Interpreter { } } +impl InterpreterInfo for Interpreter { + fn gas_remainning(&self) -> U256 { + self.gasometer + .as_ref() + .map(|gm| gm.current_gas.as_u256()) + .unwrap_or_default() + } + + fn opcode(&self, pc: u64) -> Option { + let pc = pc as usize; + if pc >= self.reader.len() { + return None; + } + Some(self.reader.code[pc]) + } + + fn current_opcode(&self) -> u8 { self.reader.code[self.reader.position] } + + fn program_counter(&self) -> u64 { self.reader.position as u64 } + + fn mem(&self) -> &Vec { &self.mem } + + fn return_stack(&self) -> &Vec { &self.return_stack } + + fn stack(&self) -> Vec { self.stack.content() } + + fn contract_address(&self) -> Address { self.params.address } + + fn instruction_result(&self) -> Option> { + self.instruction_result.clone().map(|v| match v { + InstructionResult::Ok => InstructionResult::Ok, + InstructionResult::UnusedGas(gas) => { + InstructionResult::UnusedGas(gas.as_u256()) + } + InstructionResult::JumpToPosition(v) => { + InstructionResult::JumpToPosition(v) + } + InstructionResult::JumpToSubroutine(v) => { + InstructionResult::JumpToSubroutine(v) + } + InstructionResult::ReturnFromSubroutine(v) => { + InstructionResult::ReturnFromSubroutine(v) + } + InstructionResult::StopExecutionNeedsReturn { + gas, + init_off, + init_size, + apply, + } => InstructionResult::StopExecutionNeedsReturn { + gas: gas.as_u256(), + init_off, + init_size, + apply, + }, + InstructionResult::StopExecution => { + InstructionResult::StopExecution + } + InstructionResult::Trap(v) => InstructionResult::Trap(v), + }) + } +} + fn get_and_reset_sign(value: U256) -> (U256, bool) { let U256(arr) = value; let sign = arr[3].leading_zeros() == 0; diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs index 23a4e87e2f..a7ef96d70f 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs @@ -54,6 +54,8 @@ impl VecStack { logs: [zero; instructions::MAX_NO_OF_TOPICS], } } + + pub fn content(&self) -> Vec { self.stack.clone() } } impl Stack for VecStack { diff --git a/crates/cfxcore/vm-interpreter/src/lib.rs b/crates/cfxcore/vm-interpreter/src/lib.rs index ec401ba6df..97f56c212c 100644 --- a/crates/cfxcore/vm-interpreter/src/lib.rs +++ b/crates/cfxcore/vm-interpreter/src/lib.rs @@ -8,7 +8,7 @@ extern crate log; mod evm; #[macro_use] pub mod factory; -mod instructions; +pub mod instructions; mod interpreter; mod vmtype; #[macro_use] @@ -21,5 +21,6 @@ pub use self::{ evm::{CostType, FinalizationResult, Finalize}, factory::Factory, instructions::GasPriceTier, + interpreter::{Interpreter, InterpreterResult}, vmtype::VMType, }; diff --git a/crates/cfxcore/vm-types/Cargo.toml b/crates/cfxcore/vm-types/Cargo.toml index fc82424730..3650c0ac13 100644 --- a/crates/cfxcore/vm-types/Cargo.toml +++ b/crates/cfxcore/vm-types/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-types" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] cfx-bytes = { path = "../../cfx_bytes" } diff --git a/crates/cfxcore/vm-types/src/call_create_type.rs b/crates/cfxcore/vm-types/src/call_create_type.rs index 037feeb9c9..c05c0e8b04 100644 --- a/crates/cfxcore/vm-types/src/call_create_type.rs +++ b/crates/cfxcore/vm-types/src/call_create_type.rs @@ -73,7 +73,7 @@ impl Decodable for CallType { } /// The type of the create-like instruction. -#[derive(Clone, Eq, PartialEq, Debug, Serialize)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize)] #[serde(rename_all = "lowercase")] pub enum CreateType { /// Not a create diff --git a/crates/cfxcore/vm-types/src/context.rs b/crates/cfxcore/vm-types/src/context.rs index 2d5aa8b6d4..3d69d05774 100644 --- a/crates/cfxcore/vm-types/src/context.rs +++ b/crates/cfxcore/vm-types/src/context.rs @@ -26,7 +26,7 @@ use super::{ error::{Result, TrapKind}, return_data::ReturnData, spec::Spec, - Error, + Error, InterpreterInfo, }; use cfx_bytes::Bytes; use cfx_db_errors::statedb::Result as DbResult; @@ -253,9 +253,15 @@ pub trait Context { // ) { // } - fn opcode_trace_enabled(&self) -> bool { false } + fn trace_step(&mut self, interpreter: &dyn InterpreterInfo) { + let _ = interpreter; + } + + fn trace_step_end(&mut self, interpreter: &dyn InterpreterInfo) { + let _ = interpreter; + } - // TODO[geth-tracer]: customize your tracer hook. + fn opcode_trace_enabled(&self) -> bool { false } /// Check if running in static context. fn is_static(&self) -> bool; diff --git a/crates/cfxcore/vm-types/src/error.rs b/crates/cfxcore/vm-types/src/error.rs index d7e7f37989..e5944eaf79 100644 --- a/crates/cfxcore/vm-types/src/error.rs +++ b/crates/cfxcore/vm-types/src/error.rs @@ -27,7 +27,7 @@ use cfx_types::{Address, U256}; use solidity_abi::ABIDecodeError; use std::fmt; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum TrapKind { Call(ActionParams), Create(ActionParams), diff --git a/crates/cfxcore/vm-types/src/instruction_result.rs b/crates/cfxcore/vm-types/src/instruction_result.rs new file mode 100644 index 0000000000..b20b7b0505 --- /dev/null +++ b/crates/cfxcore/vm-types/src/instruction_result.rs @@ -0,0 +1,23 @@ +use super::TrapKind; +use cfx_types::U256; + +#[derive(Clone)] +pub enum InstructionResult { + Ok, + UnusedGas(Gas), + JumpToPosition(U256), + JumpToSubroutine(U256), + ReturnFromSubroutine(usize), + StopExecutionNeedsReturn { + /// Gas left. + gas: Gas, + /// Return data offset. + init_off: U256, + /// Return data size. + init_size: U256, + /// Apply or revert state changes. + apply: bool, + }, + StopExecution, + Trap(TrapKind), +} diff --git a/crates/cfxcore/vm-types/src/interpreter_info.rs b/crates/cfxcore/vm-types/src/interpreter_info.rs new file mode 100644 index 0000000000..3fc3adf62b --- /dev/null +++ b/crates/cfxcore/vm-types/src/interpreter_info.rs @@ -0,0 +1,22 @@ +use super::InstructionResult; +use cfx_types::{Address, U256}; + +pub trait InterpreterInfo { + fn gas_remainning(&self) -> U256; + + fn program_counter(&self) -> u64; + + fn current_opcode(&self) -> u8; + + fn opcode(&self, pc: u64) -> Option; + + fn mem(&self) -> &Vec; + + fn stack(&self) -> Vec; + + fn return_stack(&self) -> &Vec; + + fn contract_address(&self) -> Address; + + fn instruction_result(&self) -> Option>; +} diff --git a/crates/cfxcore/vm-types/src/lib.rs b/crates/cfxcore/vm-types/src/lib.rs index 8a692c0973..68c169d32f 100644 --- a/crates/cfxcore/vm-types/src/lib.rs +++ b/crates/cfxcore/vm-types/src/lib.rs @@ -7,6 +7,8 @@ mod call_create_type; mod context; mod env; mod error; +mod instruction_result; +mod interpreter_info; mod return_data; mod spec; @@ -25,6 +27,8 @@ pub use self::{ separate_out_db_error, Error, ExecTrapError, ExecTrapResult, Result, TrapError, TrapKind, TrapResult, }, + instruction_result::InstructionResult, + interpreter_info::InterpreterInfo, return_data::{GasLeft, ReturnData}, spec::{CleanDustMode, Spec, WasmCosts}, }; diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 22682de63e..092099afdd 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -100,6 +100,7 @@ static_assertions = "1.1.0" parity-version = {path = "../util/version" } solidity-abi = {path= "../util/solidity-abi" } bls-signatures = {git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore"]} +alloy-rpc-trace-types.workspace = true [dev-dependencies] criterion = "0.3" diff --git a/crates/client/src/rpc.rs b/crates/client/src/rpc.rs index 44de09f122..89ddb3d90a 100644 --- a/crates/client/src/rpc.rs +++ b/crates/client/src/rpc.rs @@ -77,11 +77,14 @@ use crate::{ rpc::{ error_codes::request_rejected_too_many_request_error, impls::{ - eth::EthHandler, eth_filter::EthFilterClient, - trace::EthTraceHandler, RpcImplConfiguration, + eth::{EthHandler, GethDebugHandler}, + eth_filter::EthFilterClient, + trace::EthTraceHandler, + RpcImplConfiguration, }, interceptor::{RpcInterceptor, RpcProxy}, rpc_apis::{Api, ApiSet}, + traits::eth_space::debug::Debug, }, }; use futures01::lazy; @@ -344,6 +347,11 @@ fn setup_rpc_apis( throttling_section, ); } + Api::EthDebug => { + info!("Add geth debug method"); + let geth_debug = GethDebugHandler::new(rpc.consensus.clone()); + handler.extend_with(geth_debug.to_delegate()); + } Api::Test => { handler.extend_with( TestRpcImpl::new(common.clone(), rpc.clone()).to_delegate(), @@ -493,6 +501,9 @@ fn setup_rpc_apis_light( Api::Eth => { warn!("Light nodes do not support evm ports."); } + Api::EthDebug => { + warn!("Light nodes do not support evm ports."); + } Api::Debug => { handler.extend_with( LightDebugRpcImpl::new(common.clone(), rpc.clone()) diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 91545a4cab..c50a5bf9a1 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -49,6 +49,9 @@ use rlp::Rlp; use rustc_hex::ToHex; use std::{cmp::min, convert::TryInto}; +mod debug; +pub use debug::GethDebugHandler; + pub struct EthHandler { config: RpcImplConfiguration, consensus: SharedConsensusGraph, diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs new file mode 100644 index 0000000000..556a9dda2e --- /dev/null +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -0,0 +1,88 @@ +use crate::rpc::traits::eth_space::debug::Debug; +use alloy_rpc_trace_types::geth::{ + GethDebugBuiltInTracerType, + GethDebugTracerType::{BuiltInTracer, JsTracer}, + GethDebugTracingOptions, GethTrace, NoopFrame, +}; +use cfx_types::H256; +use cfxcore::{ConsensusGraph, SharedConsensusGraph}; +use jsonrpc_core::{Error as RpcError, Result as JsonRpcResult}; +// use primitives::EpochNumber; + +pub struct GethDebugHandler { + consensus: SharedConsensusGraph, +} + +impl GethDebugHandler { + pub fn new(consensus: SharedConsensusGraph) -> Self { + GethDebugHandler { consensus } + } + + fn consensus_graph(&self) -> &ConsensusGraph { + self.consensus + .as_any() + .downcast_ref::() + .expect("downcast should succeed") + } +} + +impl Debug for GethDebugHandler { + fn db_get(&self, _key: String) -> JsonRpcResult> { + Ok(Some("To be implemented!".into())) + } + + fn debug_trace_transaction( + &self, hash: H256, opts: Option, + ) -> JsonRpcResult { + let opts = opts.unwrap_or_default(); + + // early return if tracer is not supported or NoopTracer is requested + if let Some(tracer_type) = &opts.tracer { + match tracer_type { + BuiltInTracer(builtin_tracer) => match builtin_tracer { + GethDebugBuiltInTracerType::FourByteTracer => { + return Err(RpcError::invalid_params("not supported")) + } + GethDebugBuiltInTracerType::CallTracer => (), + GethDebugBuiltInTracerType::PreStateTracer => { + return Err(RpcError::invalid_params("not supported")) + } + GethDebugBuiltInTracerType::NoopTracer => { + return Ok(GethTrace::NoopTracer(NoopFrame::default())) + } + GethDebugBuiltInTracerType::MuxTracer => { + return Err(RpcError::invalid_params("not supported")) + } + }, + JsTracer(_) => { + return Err(RpcError::invalid_params("not supported")) + } + } + } + + let tx_index = self + .consensus + .get_data_manager() + .transaction_index_by_hash(&hash, false /* update_cache */) + .ok_or(RpcError::invalid_params("invalid tx hash"))?; + + let epoch_num = self + .consensus + .get_block_epoch_number(&tx_index.block_hash) + .ok_or(RpcError::invalid_params("invalid tx hash"))?; + + let epoch_traces = self + .consensus_graph() + .collect_epoch_geth_trace(epoch_num, Some(hash), opts) + .map_err(|_e| RpcError::invalid_params("invalid tx hash"))?; + + // filter by tx hash + let trace = epoch_traces + .into_iter() + .find(|(tx_hash, _)| tx_hash == &hash) + .map(|(_, trace)| trace) + .ok_or(RpcError::invalid_params("invalid tx hash")); + + trace + } +} diff --git a/crates/client/src/rpc/rpc_apis.rs b/crates/client/src/rpc/rpc_apis.rs index 11502f2aac..c62f0ce61c 100644 --- a/crates/client/src/rpc/rpc_apis.rs +++ b/crates/client/src/rpc/rpc_apis.rs @@ -12,13 +12,14 @@ use std::{ pub enum Api { Cfx, Eth, - Debug, + Debug, // core space parity style debug Pubsub, Test, Trace, TxPool, Pos, EthPubsub, + EthDebug, } impl FromStr for Api { @@ -36,6 +37,7 @@ impl FromStr for Api { "txpool" => Ok(TxPool), "pos" => Ok(Pos), "ethpubsub" => Ok(EthPubsub), + "ethdebug" => Ok(EthDebug), _ => Err("Unknown api type".into()), } } @@ -53,13 +55,14 @@ impl Display for Api { Api::TxPool => write!(f, "txpool"), Api::Pos => write!(f, "pos"), Api::EthPubsub => write!(f, "ethpubsub"), + Api::EthDebug => write!(f, "ethdebug"), } } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum ApiSet { - All, + All, // core space all apis Safe, Evm, // Ethereum api set List(HashSet), diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs new file mode 100644 index 0000000000..c4364fc8c0 --- /dev/null +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -0,0 +1,21 @@ +use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethTrace}; +use cfx_types::H256; +use jsonrpc_core::Result as JsonRpcResult; +use jsonrpc_derive::rpc; + +/// methods compatible with geth debug namespace methods https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug +#[rpc(server)] +pub trait Debug { + #[rpc(name = "debug_dbGet")] + fn db_get(&self, key: String) -> JsonRpcResult>; + + /// The `debug_traceTransaction` debugging method will attempt to run the + /// transaction in the exact same manner as it was executed on the + /// network. It will replay any transaction that may have been executed + /// prior to this one before it will finally attempt to execute the + /// transaction that corresponds to the given hash. + #[rpc(name = "traceTransaction")] + fn debug_trace_transaction( + &self, tx_hash: H256, opts: Option, + ) -> JsonRpcResult; +} diff --git a/crates/client/src/rpc/traits/eth_space/mod.rs b/crates/client/src/rpc/traits/eth_space/mod.rs index 7a1d873fd8..27d7f2f5a5 100644 --- a/crates/client/src/rpc/traits/eth_space/mod.rs +++ b/crates/client/src/rpc/traits/eth_space/mod.rs @@ -1,3 +1,4 @@ +pub mod debug; pub mod eth; pub mod eth_pubsub; pub mod trace; diff --git a/crates/rpc_types_trace/Cargo.toml b/crates/rpc_types_trace/Cargo.toml new file mode 100644 index 0000000000..df4bccbf4a --- /dev/null +++ b/crates/rpc_types_trace/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "rpc-types-trace" +edition = "2021" +version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +repository.workspace = true +license-file.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde.workspace = true +serde_json.workspace = true +alloy-primitives = { workspace = true, features = ["rlp", "serde"] } +cfx-types = { path = "../cfx_types" } +cfx-bytes = { path = "../cfx_bytes" } +cfx-serde = { path = "../serde" } +similar-asserts = "1.5" \ No newline at end of file diff --git a/crates/rpc_types_trace/src/block.rs b/crates/rpc_types_trace/src/block.rs new file mode 100644 index 0000000000..8540eb7949 --- /dev/null +++ b/crates/rpc_types_trace/src/block.rs @@ -0,0 +1,47 @@ +use cfx_types::{Address, H256, U256, U64}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// BlockOverrides is a set of header fields to override. +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[serde(default, rename_all = "camelCase", deny_unknown_fields)] +pub struct BlockOverrides { + /// Overrides the block number. + /// + /// For `eth_callMany` this will be the block number of the first simulated + /// block. Each following block increments its block number by 1 + // Note: geth uses `number`, erigon uses `blockNumber` + #[serde( + default, + skip_serializing_if = "Option::is_none", + alias = "blockNumber" + )] + pub number: Option, + /// Overrides the difficulty of the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub difficulty: Option, + /// Overrides the timestamp of the block. + // Note: geth uses `time`, erigon uses `timestamp` + #[serde( + default, + skip_serializing_if = "Option::is_none", + alias = "timestamp" + )] + pub time: Option, + /// Overrides the gas limit of the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_limit: Option, + /// Overrides the coinbase address of the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub coinbase: Option
, + /// Overrides the prevrandao of the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub random: Option, + /// Overrides the basefee of the block. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub base_fee: Option, + /// A dictionary that maps blockNumber to a user-defined hash. It could be + /// queried from the solidity opcode BLOCKHASH. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub block_hash: Option>, +} diff --git a/crates/rpc_types_trace/src/common.rs b/crates/rpc_types_trace/src/common.rs new file mode 100644 index 0000000000..9e0d5fa2a8 --- /dev/null +++ b/crates/rpc_types_trace/src/common.rs @@ -0,0 +1,36 @@ +//! Types used by tracing backends. + +use cfx_types::H256 as TxHash; +use serde::{Deserialize, Serialize}; + +/// The result of a single transaction trace. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum TraceResult { + /// Untagged success variant + Success { + /// Trace results produced by the tracer + result: Ok, + /// transaction hash + #[serde(skip_serializing_if = "Option::is_none", rename = "txHash")] + tx_hash: Option, + }, + /// Untagged error variant + Error { + /// Trace failure produced by the tracer + error: Err, + /// transaction hash + #[serde(skip_serializing_if = "Option::is_none", rename = "txHash")] + tx_hash: Option, + }, +} + +impl TraceResult { + /// Returns the hash of the transaction that was traced. + pub const fn tx_hash(&self) -> Option { + *match self { + TraceResult::Success { tx_hash, .. } => tx_hash, + TraceResult::Error { tx_hash, .. } => tx_hash, + } + } +} diff --git a/crates/rpc_types_trace/src/geth/call.rs b/crates/rpc_types_trace/src/geth/call.rs new file mode 100644 index 0000000000..5c2e1387ae --- /dev/null +++ b/crates/rpc_types_trace/src/geth/call.rs @@ -0,0 +1,138 @@ +use alloy_primitives::Bytes; +use cfx_serde::from_int_or_hex; +use cfx_types::{Address, H256 as B256, U256}; +use serde::{Deserialize, Serialize}; + +/// The response object for `debug_traceTransaction` with `"tracer": +/// "callTracer"`. +/// +/// +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct CallFrame { + /// The address of that initiated the call. + pub from: Address, + /// How much gas was left before the call. + #[serde(default, deserialize_with = "from_int_or_hex")] + pub gas: U256, + /// How much gas was used by the call. + #[serde(default, deserialize_with = "from_int_or_hex", rename = "gasUsed")] + pub gas_used: U256, + /// The address of the contract that was called. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub to: Option
, + /// Calldata input. + pub input: Bytes, + /// Output of the call, if any. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output: Option, + /// Error message, if any. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Why this call reverted, if it reverted. + #[serde( + default, + rename = "revertReason", + skip_serializing_if = "Option::is_none" + )] + pub revert_reason: Option, + /// Recorded child calls. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub calls: Vec, + /// Logs emitted by this call. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub logs: Vec, + /// Value transferred. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, + /// The type of the call. + #[serde(rename = "type")] + pub typ: String, +} + +/// Represents a recorded call. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct CallLogFrame { + /// The address of the contract that was called. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub address: Option
, + /// The topics of the log. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub topics: Option>, + /// The data of the log. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub data: Option, +} + +/// The configuration for the call tracer. +#[derive( + Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize, +)] +#[serde(rename_all = "camelCase")] +pub struct CallConfig { + /// When set to true, this will only trace the primary (top-level) call and + /// not any sub-calls. It eliminates the additional processing for each + /// call frame. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub only_top_call: Option, + /// When set to true, this will include the logs emitted by the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub with_log: Option, +} + +impl CallConfig { + /// Sets the only top call flag. + pub const fn only_top_call(mut self) -> Self { + self.only_top_call = Some(true); + self + } + + /// Sets the with log flag. + pub const fn with_log(mut self) -> Self { + self.with_log = Some(true); + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::geth::*; + + // See + const DEFAULT: &str = + include_str!("../../test_data/call_tracer/default.json"); + const LEGACY: &str = + include_str!("../../test_data/call_tracer/legacy.json"); + const ONLY_TOP_CALL: &str = + include_str!("../../test_data/call_tracer/only_top_call.json"); + const WITH_LOG: &str = + include_str!("../../test_data/call_tracer/with_log.json"); + + #[test] + fn test_serialize_call_trace() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.config.disable_storage = Some(false); + opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::CallTracer, + )); + opts.tracing_options.tracer_config = serde_json::to_value(CallConfig { + only_top_call: Some(true), + with_log: Some(true), + }) + .unwrap() + .into(); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"disableStorage":false,"tracer":"callTracer","tracerConfig":{"onlyTopCall":true,"withLog":true}}"# + ); + } + + #[test] + fn test_deserialize_call_trace() { + // let _trace: CallFrame = serde_json::from_str(DEFAULT).unwrap(); + // let _trace: CallFrame = serde_json::from_str(LEGACY).unwrap(); + let _trace: CallFrame = serde_json::from_str(ONLY_TOP_CALL).unwrap(); + let _trace: CallFrame = serde_json::from_str(WITH_LOG).unwrap(); + } +} diff --git a/crates/rpc_types_trace/src/geth/four_byte.rs b/crates/rpc_types_trace/src/geth/four_byte.rs new file mode 100644 index 0000000000..d67e2816d9 --- /dev/null +++ b/crates/rpc_types_trace/src/geth/four_byte.rs @@ -0,0 +1,42 @@ +//! Geth 4byte tracer types. + +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// The 4byte tracer response object. +/// +/// +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct FourByteFrame(pub BTreeMap); + +#[cfg(test)] +mod tests { + use super::*; + use crate::geth::*; + + const DEFAULT: &str = r#"{ + "0x27dc297e-128": 1, + "0x38cc4831-0": 2, + "0x524f3889-96": 1, + "0xadf59f99-288": 1, + "0xc281d19e-0": 1 + }"#; + + #[test] + fn test_serialize_four_byte_trace() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::FourByteTracer, + )); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"tracer":"4byteTracer"}"# + ); + } + + #[test] + fn test_deserialize_four_byte_trace() { + let _trace: FourByteFrame = serde_json::from_str(DEFAULT).unwrap(); + } +} diff --git a/crates/rpc_types_trace/src/geth/mod.rs b/crates/rpc_types_trace/src/geth/mod.rs new file mode 100644 index 0000000000..1986ae6716 --- /dev/null +++ b/crates/rpc_types_trace/src/geth/mod.rs @@ -0,0 +1,627 @@ +#![allow(unused)] +use crate::{block::BlockOverrides, state::StateOverride}; +use alloy_primitives::Bytes; +use cfx_types::{H256 as B256, U256}; +use serde::{ + de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize, Serializer, +}; +use std::{collections::BTreeMap, time::Duration}; + +pub use call::*; +use four_byte::FourByteFrame; +use mux::*; +use noop::NoopFrame; +pub use pre_state::*; + +mod call; +mod four_byte; +mod mux; +mod noop; +mod pre_state; + +/// Result type for geth style transaction trace +pub type TraceResult = crate::common::TraceResult; + +/// blockTraceResult represents the results of tracing a single block when an +/// entire chain is being traced. ref +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlockTraceResult { + /// Block number corresponding to the trace task + pub block: U256, + /// Block hash corresponding to the trace task + pub hash: B256, + /// Trace results produced by the trace task + pub traces: Vec, +} + +/// Geth Default struct log trace frame +/// +/// +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DefaultFrame { + /// Whether the transaction failed + pub failed: bool, + /// How much gas was used. + pub gas: u64, + /// Output of the transaction + #[serde(serialize_with = "cfx_serde::serialize_hex_string_no_prefix")] + pub return_value: Bytes, + /// Recorded traces of the transaction + pub struct_logs: Vec, +} + +/// Represents a struct log entry in a trace +/// +/// +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct StructLog { + /// program counter + pub pc: u64, + /// opcode to be executed + pub op: String, + /// remaining gas + pub gas: u64, + /// cost for executing op + #[serde(rename = "gasCost")] + pub gas_cost: u64, + /// Current call depth + pub depth: u64, + /// Error message if any + #[serde(default, skip_serializing_if = "Option::is_none")] + pub error: Option, + /// EVM stack + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stack: Option>, + /// Last call's return data. Enabled via enableReturnData + #[serde( + default, + rename = "returnData", + skip_serializing_if = "Option::is_none" + )] + pub return_data: Option, + /// ref + #[serde(default, skip_serializing_if = "Option::is_none")] + pub memory: Option>, + /// Size of memory. + #[serde( + default, + rename = "memSize", + skip_serializing_if = "Option::is_none" + )] + pub memory_size: Option, + /// Storage slots of current contract read from and written to. Only + /// emitted for SLOAD and SSTORE. Disabled via disableStorage + #[serde( + default, + skip_serializing_if = "Option::is_none", + serialize_with = "serialize_string_storage_map_opt" + )] + pub storage: Option>, + /// Refund counter + #[serde( + default, + rename = "refund", + skip_serializing_if = "Option::is_none" + )] + pub refund_counter: Option, +} + +/// Tracing response objects +/// +/// Note: This deserializes untagged, so it's possible that a custom javascript +/// tracer response matches another variant, for example a js tracer that +/// returns `{}` would be deserialized as [GethTrace::NoopTracer] +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum GethTrace { + /// The response for the default struct log tracer + Default(DefaultFrame), + /// The response for call tracer + CallTracer(CallFrame), + /// The response for four byte tracer + FourByteTracer(FourByteFrame), + /// The response for pre-state byte tracer + PreStateTracer(PreStateFrame), + /// An empty json response + NoopTracer(NoopFrame), + /// The response for mux tracer + MuxTracer(MuxFrame), + /// Any other trace response, such as custom javascript response objects + JS(serde_json::Value), +} + +impl From for GethTrace { + fn from(value: DefaultFrame) -> Self { GethTrace::Default(value) } +} + +impl From for GethTrace { + fn from(value: FourByteFrame) -> Self { GethTrace::FourByteTracer(value) } +} + +impl From for GethTrace { + fn from(value: CallFrame) -> Self { GethTrace::CallTracer(value) } +} + +impl From for GethTrace { + fn from(value: PreStateFrame) -> Self { GethTrace::PreStateTracer(value) } +} + +impl From for GethTrace { + fn from(value: NoopFrame) -> Self { GethTrace::NoopTracer(value) } +} + +impl From for GethTrace { + fn from(value: MuxFrame) -> Self { GethTrace::MuxTracer(value) } +} + +/// Available built-in tracers +/// +/// See +#[derive(Debug, Copy, PartialEq, Eq, Clone, Hash, Deserialize, Serialize)] +pub enum GethDebugBuiltInTracerType { + /// The 4byteTracer collects the function selectors of every function + /// executed in the lifetime of a transaction, along with the size of + /// the supplied call data. The result is a [FourByteFrame] where the + /// keys are SELECTOR-CALLDATASIZE and the values are number of + /// occurrences of this key. + #[serde(rename = "4byteTracer")] + FourByteTracer, + /// The callTracer tracks all the call frames executed during a + /// transaction, including depth 0. The result will be a nested list of + /// call frames, resembling how EVM works. They form a tree + /// with the top-level call at root and sub-calls as children of the higher + /// levels. + #[serde(rename = "callTracer")] + CallTracer, + /// The prestate tracer has two modes: prestate and diff. The prestate mode + /// returns the accounts necessary to execute a given transaction. diff + /// mode returns the differences between the transaction's pre and + /// post-state (i.e. what changed because the transaction happened). + /// The prestateTracer defaults to prestate mode. It reexecutes the given + /// transaction and tracks every part of state that is touched. This is + /// similar to the concept of a stateless witness, the difference being + /// this tracer doesn't return any cryptographic proof, rather only the + /// trie leaves. The result is an object. The keys are addresses of + /// accounts. + #[serde(rename = "prestateTracer")] + PreStateTracer, + /// This tracer is noop. It returns an empty object and is only meant for + /// testing the setup. + #[serde(rename = "noopTracer")] + NoopTracer, + /// The mux tracer is a tracer that can run multiple tracers at once. + #[serde(rename = "muxTracer")] + MuxTracer, +} + +/// Available tracers +/// +/// See and +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum GethDebugTracerType { + /// built-in tracer + BuiltInTracer(GethDebugBuiltInTracerType), + /// custom JS tracer + JsTracer(String), +} + +impl From for GethDebugTracerType { + fn from(value: GethDebugBuiltInTracerType) -> Self { + GethDebugTracerType::BuiltInTracer(value) + } +} + +/// Configuration of the tracer +/// +/// This is a simple wrapper around serde_json::Value. +/// with helpers for deserializing tracer configs. +#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] +#[serde(transparent)] +pub struct GethDebugTracerConfig(pub serde_json::Value); + +// === impl GethDebugTracerConfig === + +impl GethDebugTracerConfig { + /// Returns if this is a null object + pub fn is_null(&self) -> bool { self.0.is_null() } + + /// Consumes the config and tries to deserialize it into the given type. + pub fn from_value( + self, + ) -> Result { + serde_json::from_value(self.0) + } + + /// Returns the [CallConfig] if it is a call config. + pub fn into_call_config(self) -> Result { + if self.0.is_null() { + return Ok(Default::default()); + } + self.from_value() + } + + /// Returns the raw json value + pub fn into_json(self) -> serde_json::Value { self.0 } + + /// Returns the [PreStateConfig] if it is a prestate config. + pub fn into_pre_state_config( + self, + ) -> Result { + if self.0.is_null() { + return Ok(Default::default()); + } + self.from_value() + } + + /// Returns the [MuxConfig] if it is a mux config. + pub fn into_mux_config(self) -> Result { + if self.0.is_null() { + return Ok(Default::default()); + } + self.from_value() + } +} + +impl From for GethDebugTracerConfig { + fn from(value: serde_json::Value) -> Self { GethDebugTracerConfig(value) } +} + +/// Bindings for additional `debug_traceTransaction` options +/// +/// See +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct GethDebugTracingOptions { + /// The common tracing options + #[serde(default, flatten)] + pub config: GethDefaultTracingOptions, + /// The custom tracer to use. + /// + /// If `None` then the default structlog tracer is used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tracer: Option, + /// Config specific to given `tracer`. + /// + /// Note default struct logger config are historically embedded in main + /// object. + /// + /// tracerConfig is slated for Geth v1.11.0 + /// See + /// + /// This could be [CallConfig] or [PreStateConfig] depending on the tracer. + #[serde(default, skip_serializing_if = "GethDebugTracerConfig::is_null")] + pub tracer_config: GethDebugTracerConfig, + /// A string of decimal integers that overrides the JavaScript-based + /// tracing calls default timeout of 5 seconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeout: Option, +} + +impl GethDebugTracingOptions { + /// Sets the tracer to use + pub fn with_tracer(mut self, tracer: GethDebugTracerType) -> Self { + self.tracer = Some(tracer); + self + } + + /// Sets the timeout to use for tracing + pub fn with_timeout(mut self, duration: Duration) -> Self { + self.timeout = Some(format!("{}ms", duration.as_millis())); + self + } + + /// Configures a [CallConfig] + pub fn call_config(mut self, config: CallConfig) -> Self { + self.tracer_config = GethDebugTracerConfig( + serde_json::to_value(config).expect("is serializable"), + ); + self + } + + /// Configures a [PreStateConfig] + pub fn prestate_config(mut self, config: PreStateConfig) -> Self { + self.tracer_config = GethDebugTracerConfig( + serde_json::to_value(config).expect("is serializable"), + ); + self + } +} + +/// Default tracing options for the struct looger. +/// +/// These are all known general purpose tracer options that may or not be +/// supported by a given tracer. For example, the `enableReturnData` option is a +/// noop on regular `debug_trace{Transaction,Block}` calls. +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default, +)] +#[serde(rename_all = "camelCase")] +pub struct GethDefaultTracingOptions { + /// enable memory capture + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enable_memory: Option, + /// Disable memory capture + /// + /// This is the opposite of `enable_memory`. + /// + /// Note: memory capture used to be enabled by default on geth, but has since been flipped and is now disabled by default. + /// However, at the time of writing this, erigon still defaults to enabled + /// and supports the `disableMemory` option. So we keep this option for + /// compatibility, but if it's missing OR `enableMemory` is present + /// `enableMemory` takes precedence. + /// + /// See also + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_memory: Option, + /// disable stack capture + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_stack: Option, + /// Disable storage capture + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_storage: Option, + /// Enable return data capture + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enable_return_data: Option, + /// Disable return data capture + /// + /// This is the opposite of `enable_return_data`, and only supported for + /// compatibility reasons. See also `disable_memory`. + /// + /// If `enable_return_data` is present, `enable_return_data` always takes + /// precedence. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub disable_return_data: Option, + /// print output during capture end + #[serde(default, skip_serializing_if = "Option::is_none")] + pub debug: Option, + /// maximum length of output, but zero means unlimited + #[serde(default, skip_serializing_if = "Option::is_none")] + pub limit: Option, +} + +impl GethDefaultTracingOptions { + /// Enables memory capture. + pub const fn enable_memory(self) -> Self { self.with_enable_memory(true) } + + /// Disables memory capture. + pub const fn disable_memory(self) -> Self { self.with_disable_memory(true) } + + /// Disables stack capture. + pub const fn disable_stack(self) -> Self { self.with_disable_stack(true) } + + /// Disables storage capture. + pub const fn disable_storage(self) -> Self { + self.with_disable_storage(true) + } + + /// Enables return data capture. + pub const fn enable_return_data(self) -> Self { + self.with_enable_return_data(true) + } + + /// Disables return data capture. + pub const fn disable_return_data(self) -> Self { + self.with_disable_return_data(true) + } + + /// Enables debug mode. + pub const fn debug(self) -> Self { self.with_debug(true) } + + /// Sets the enable_memory field. + pub const fn with_enable_memory(mut self, enable: bool) -> Self { + self.enable_memory = Some(enable); + self + } + + /// Sets the disable_memory field. + pub const fn with_disable_memory(mut self, disable: bool) -> Self { + self.disable_memory = Some(disable); + self + } + + /// Sets the disable_stack field. + pub const fn with_disable_stack(mut self, disable: bool) -> Self { + self.disable_stack = Some(disable); + self + } + + /// Sets the disable_storage field. + pub const fn with_disable_storage(mut self, disable: bool) -> Self { + self.disable_storage = Some(disable); + self + } + + /// Sets the enable_return_data field. + pub const fn with_enable_return_data(mut self, enable: bool) -> Self { + self.enable_return_data = Some(enable); + self + } + + /// Sets the disable_return_data field. + pub const fn with_disable_return_data(mut self, disable: bool) -> Self { + self.disable_return_data = Some(disable); + self + } + + /// Sets the debug field. + pub const fn with_debug(mut self, debug: bool) -> Self { + self.debug = Some(debug); + self + } + + /// Sets the limit field. + pub const fn with_limit(mut self, limit: u64) -> Self { + self.limit = Some(limit); + self + } + + /// Returns `true` if return data capture is enabled + pub fn is_return_data_enabled(&self) -> bool { + self.enable_return_data + .or_else(|| self.disable_return_data.map(|disable| !disable)) + .unwrap_or(false) + } + + /// Returns `true` if memory capture is enabled + pub fn is_memory_enabled(&self) -> bool { + self.enable_memory + .or_else(|| self.disable_memory.map(|disable| !disable)) + .unwrap_or(false) + } + + /// Returns `true` if stack capture is enabled + pub fn is_stack_enabled(&self) -> bool { + !self.disable_stack.unwrap_or(false) + } + + /// Returns `true` if storage capture is enabled + pub fn is_storage_enabled(&self) -> bool { + !self.disable_storage.unwrap_or(false) + } +} + +/// Bindings for additional `debug_traceCall` options +/// +/// See +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct GethDebugTracingCallOptions { + /// All the options + #[serde(flatten)] + pub tracing_options: GethDebugTracingOptions, + /// The state overrides to apply + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state_overrides: Option, + /// The block overrides to apply + #[serde(default, skip_serializing_if = "Option::is_none")] + pub block_overrides: Option, +} + +/// Serializes a storage map as a list of key-value pairs _without_ 0x-prefix +fn serialize_string_storage_map_opt( + storage: &Option>, s: S, +) -> Result { + match storage { + None => s.serialize_none(), + Some(storage) => { + let mut m = s.serialize_map(Some(storage.len()))?; + for (key, val) in storage.iter() { + let key = format!("{:?}", key); + let val = format!("{:?}", val); + // skip the 0x prefix + m.serialize_entry(&key.as_str()[2..], &val.as_str()[2..])?; + } + m.end() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tracer_config() { + let s = "{\"tracer\": \"callTracer\"}"; + let opts = serde_json::from_str::(s).unwrap(); + assert_eq!( + opts.tracer, + Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::CallTracer + )) + ); + let _call_config = + opts.tracer_config.clone().into_call_config().unwrap(); + let _prestate_config = + opts.tracer_config.into_pre_state_config().unwrap(); + } + + #[test] + fn test_memory_capture() { + let mut config = GethDefaultTracingOptions::default(); + + // by default false + assert!(!config.is_memory_enabled()); + + config.disable_memory = Some(false); + // disable == false -> enable + assert!(config.is_memory_enabled()); + + config.enable_memory = Some(false); + // enable == false -> disable + assert!(!config.is_memory_enabled()); + } + + #[test] + fn test_return_data_capture() { + let mut config = GethDefaultTracingOptions::default(); + + // by default false + assert!(!config.is_return_data_enabled()); + + config.disable_return_data = Some(false); + // disable == false -> enable + assert!(config.is_return_data_enabled()); + + config.enable_return_data = Some(false); + // enable == false -> disable + assert!(!config.is_return_data_enabled()); + } + + // + #[test] + fn serde_default_frame() { + let input = include_str!("../../test_data/default/structlogs_01.json"); + let _frame: DefaultFrame = serde_json::from_str(input).unwrap(); + } + + #[test] + fn test_serialize_storage_map() { + let s = r#"{ + "pc":3349, + "op":"SLOAD", + "gas":23959, + "gasCost":2100, + "depth":1, + "stack":[], + "memory":[], + "storage":{ + "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a":"0000000000000000000000000000000000000000000000000000000000000000" + } + }"#; + let log: StructLog = serde_json::from_str(s).unwrap(); + let val = serde_json::to_value(&log).unwrap(); + let input = serde_json::from_str::(s).unwrap(); + similar_asserts::assert_eq!(input, val); + } + + #[test] + fn test_trace_result_serde() { + let s = r#"{ + "result": { + "from": "0xccc5499e15fedaaeaba68aeb79b95b20f725bc56", + "gas": "0x186a0", + "gasUsed": "0xdb91", + "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "input": "0xa9059cbb000000000000000000000000e3f85a274c1edbea2f2498cf5978f41961cf8b5b0000000000000000000000000000000000000000000000000000000068c8f380", + "value": "0x0", + "type": "CALL" + }, + "txHash": "0x7cc741c553d4098f319c894d9db208999ca49ee1b5c53f6a9992e687cbffb69e" + }"#; + let result: TraceResult = serde_json::from_str(s).unwrap(); + let hash = result.tx_hash().unwrap(); + assert_eq!( + hash, + "7cc741c553d4098f319c894d9db208999ca49ee1b5c53f6a9992e687cbffb69e" + .parse::() + .unwrap() + ); + + let de = serde_json::to_value(&result).unwrap(); + let val = serde_json::from_str::(s).unwrap(); + similar_asserts::assert_eq!(val, de); + } +} diff --git a/crates/rpc_types_trace/src/geth/mux.rs b/crates/rpc_types_trace/src/geth/mux.rs new file mode 100644 index 0000000000..71b8bbfd70 --- /dev/null +++ b/crates/rpc_types_trace/src/geth/mux.rs @@ -0,0 +1,141 @@ +//! Geth `muxTracer` types. + +use super::{GethDebugBuiltInTracerType, GethDebugTracerConfig, GethTrace}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// A `muxTracer` config that contains the configuration for running multiple +/// tracers in one go. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MuxConfig( + pub HashMap>, +); + +/// A `muxTracer` frame response that contains the results of multiple tracers +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct MuxFrame(pub HashMap); + +#[cfg(test)] +mod tests { + use super::{super::*, *}; + + const FOUR_BYTE_FRAME: &str = r#"{ + "0x27dc297e-128": 1, + "0x38cc4831-0": 2, + "0x524f3889-96": 1, + "0xadf59f99-288": 1, + "0xc281d19e-0": 1 + }"#; + + const CALL_FRAME_WITH_LOG: &str = + include_str!("../../test_data/call_tracer/with_log.json"); + + const PRESTATE_FRAME: &str = r#"{ + "0x0000000000000000000000000000000000000002": { + "balance": "0x0" + }, + "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { + "balance": "0x2638035a26d133809" + }, + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "balance": "0x7a48734599f7284", + "nonce": 1133 + }, + "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { + "balance": "0x0", + "code": "0x", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": + "0x000000000000000000000000000000000000000000000000000000000024aea6", + "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": + "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", + "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": + "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", + "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": + "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" + } + } + }"#; + + #[test] + fn test_serialize_mux_tracer_config() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::MuxTracer, + )); + + let call_config = CallConfig { + only_top_call: Some(true), + with_log: Some(true), + }; + let prestate_config = PreStateConfig { + diff_mode: Some(true), + }; + + opts.tracing_options.tracer_config = + serde_json::to_value(MuxConfig(HashMap::from([ + (GethDebugBuiltInTracerType::FourByteTracer, None), + ( + GethDebugBuiltInTracerType::CallTracer, + Some(GethDebugTracerConfig( + serde_json::to_value(call_config).unwrap(), + )), + ), + ( + GethDebugBuiltInTracerType::PreStateTracer, + Some(GethDebugTracerConfig( + serde_json::to_value(prestate_config).unwrap(), + )), + ), + ]))) + .unwrap() + .into(); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"tracer":"muxTracer","tracerConfig":{"4byteTracer":null,"callTracer":{"onlyTopCall":true,"withLog":true},"prestateTracer":{"diffMode":true}}}"#, + ); + } + + #[test] + fn test_deserialize_mux_frame() { + let expected = HashMap::from([ + ( + GethDebugBuiltInTracerType::FourByteTracer, + GethTrace::FourByteTracer( + serde_json::from_str(FOUR_BYTE_FRAME).unwrap(), + ), + ), + ( + GethDebugBuiltInTracerType::CallTracer, + GethTrace::CallTracer( + serde_json::from_str(CALL_FRAME_WITH_LOG).unwrap(), + ), + ), + ( + GethDebugBuiltInTracerType::PreStateTracer, + GethTrace::PreStateTracer( + serde_json::from_str(PRESTATE_FRAME).unwrap(), + ), + ), + ]); + + let raw_frame = serde_json::to_string(&expected).unwrap(); + let trace: MuxFrame = serde_json::from_str(&raw_frame).unwrap(); + + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::FourByteTracer], + expected[&GethDebugBuiltInTracerType::FourByteTracer] + ); + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::CallTracer], + expected[&GethDebugBuiltInTracerType::CallTracer] + ); + assert_eq!( + trace.0[&GethDebugBuiltInTracerType::PreStateTracer], + expected[&GethDebugBuiltInTracerType::PreStateTracer] + ); + } +} diff --git a/crates/rpc_types_trace/src/geth/noop.rs b/crates/rpc_types_trace/src/geth/noop.rs new file mode 100644 index 0000000000..869e32bbab --- /dev/null +++ b/crates/rpc_types_trace/src/geth/noop.rs @@ -0,0 +1,35 @@ +//! Noop tracer response. + +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// An empty frame response that's only an empty json object `{}`. +/// +/// +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct NoopFrame(BTreeMap<(), ()>); + +#[cfg(test)] +mod tests { + use super::{super::*, *}; + + const DEFAULT: &str = r"{}"; + + #[test] + fn test_serialize_noop_trace() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::NoopTracer, + )); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"tracer":"noopTracer"}"# + ); + } + + #[test] + fn test_deserialize_noop_trace() { + let _trace: NoopFrame = serde_json::from_str(DEFAULT).unwrap(); + } +} diff --git a/crates/rpc_types_trace/src/geth/pre_state.rs b/crates/rpc_types_trace/src/geth/pre_state.rs new file mode 100644 index 0000000000..2e37033252 --- /dev/null +++ b/crates/rpc_types_trace/src/geth/pre_state.rs @@ -0,0 +1,384 @@ +//! Pre-state Geth tracer types. + +use alloy_primitives::Bytes; +use cfx_serde::num::from_int_or_hex_opt; +use cfx_types::{Address, H256 as B256, U256}; +use serde::{Deserialize, Serialize}; +use std::collections::{btree_map, BTreeMap}; + +/// A tracer that records [AccountState]s. +/// The prestate tracer has two modes: prestate and diff +/// +/// +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum PreStateFrame { + /// The default mode returns the accounts necessary to execute a given + /// transaction. + /// + /// It re-executes the given transaction and tracks every part of state + /// that is touched. + Default(PreStateMode), + /// Diff mode returns the differences between the transaction's pre and + /// post-state (i.e. what changed because the transaction happened). + Diff(DiffMode), +} + +impl PreStateFrame { + /// Returns true if this trace was requested without diffmode. + pub const fn is_default(&self) -> bool { + matches!(self, PreStateFrame::Default(_)) + } + + /// Returns true if this trace was requested with diffmode. + pub const fn is_diff(&self) -> bool { + matches!(self, PreStateFrame::Diff(_)) + } + + /// Returns the account states after the transaction is executed if this + /// trace was requested without diffmode. + pub const fn as_default(&self) -> Option<&PreStateMode> { + match self { + PreStateFrame::Default(mode) => Some(mode), + _ => None, + } + } + + /// Returns the account states before and after the transaction is executed + /// if this trace was requested with diffmode. + pub const fn as_diff(&self) -> Option<&DiffMode> { + match self { + PreStateFrame::Diff(mode) => Some(mode), + _ => None, + } + } +} + +/// Includes all the account states necessary to execute a given transaction. +/// +/// This corresponds to the default mode of the [PreStateConfig]. +/// +/// The [AccountState]'s storage will include all touched slots of an account. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct PreStateMode(pub BTreeMap); + +/// Represents the account states before and after the transaction is executed. +/// +/// This corresponds to the [DiffMode] of the [PreStateConfig]. +/// +/// This will only contain changed [AccountState]s, created accounts will not be +/// included in the pre state and selfdestructed accounts will not be included +/// in the post state. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct DiffMode { + /// The account states after the transaction is executed. + pub post: BTreeMap, + /// The account states before the transaction is executed. + pub pre: BTreeMap, +} + +// === impl DiffMode === + +impl DiffMode { + /// The sets of the [DiffMode] should only contain changed [AccountState]s. + /// + /// This will remove all unchanged [AccountState]s from the sets. + /// + /// In other words it removes entries that are equal (unchanged) in both the + /// pre and post sets. + pub fn retain_changed(&mut self) -> &mut Self { + self.pre.retain(|address, pre| { + if let btree_map::Entry::Occupied(entry) = self.post.entry(*address) + { + if entry.get() == pre { + // remove unchanged account state from both sets + entry.remove(); + return false; + } + } + + true + }); + self + } + + /// Removes all zero values from the storage of the [AccountState]s. + pub fn remove_zero_storage_values(&mut self) { + for state in self.pre.values_mut().chain(self.post.values_mut()) { + state.storage.retain(|_, value| *value != B256::zero()); + } + } +} + +/// Helper type for [DiffMode] to represent a specific set +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DiffStateKind { + /// Corresponds to the pre state of the [DiffMode] + Pre, + /// Corresponds to the post state of the [DiffMode] + Post, +} + +impl DiffStateKind { + /// Returns true if this is the pre state of the [DiffMode] + pub const fn is_pre(&self) -> bool { matches!(self, DiffStateKind::Pre) } + + /// Returns true if this is the post state of the [DiffMode] + pub const fn is_post(&self) -> bool { matches!(self, DiffStateKind::Post) } +} + +/// Represents the state of an account +#[derive( + Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, +)] +pub struct AccountState { + /// The optional balance of the account. + #[serde( + default, + deserialize_with = "from_int_or_hex_opt", + skip_serializing_if = "Option::is_none" + )] + pub balance: Option, + /// The optional code of the account. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code: Option, + /// The optional nonce of the account. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nonce: Option, + /// The storage of the account. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub storage: BTreeMap, +} + +impl AccountState { + /// Creates a new `AccountState` with the given account info. + /// + /// If balance is zero, it will be omitted. + /// If nonce is zero, it will be omitted. + /// If code is empty, it will be omitted. + pub fn from_account_info( + nonce: u64, balance: U256, code: Option, + ) -> Self { + Self { + balance: Some(balance), + code: code.filter(|code| !code.is_empty()), + nonce: (nonce != 0).then_some(nonce), + storage: Default::default(), + } + } + + /// Removes balance,nonce or code if they match the given account info. + /// + /// This is useful for comparing pre vs post state and only keep changed + /// values in post state. + pub fn remove_matching_account_info(&mut self, other: &AccountState) { + if self.balance == other.balance { + self.balance = None; + } + if self.nonce == other.nonce { + self.nonce = None; + } + if self.code == other.code { + self.code = None; + } + } +} + +/// Helper type to track the kind of change of an [AccountState]. +#[derive( + Debug, Copy, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, +)] +pub enum AccountChangeKind { + /// The account was modified. + #[default] + Modify, + /// The account was created. + Create, + /// The account was selfdestructed. + SelfDestruct, +} + +impl AccountChangeKind { + /// Returns true if the account was created + pub const fn is_created(&self) -> bool { + matches!(self, AccountChangeKind::Create) + } + + /// Returns true the account was modified + pub const fn is_modified(&self) -> bool { + matches!(self, AccountChangeKind::Modify) + } + + /// Returns true the account was modified + pub const fn is_selfdestruct(&self) -> bool { + matches!(self, AccountChangeKind::SelfDestruct) + } +} + +/// The config for the prestate tracer. +#[derive( + Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize, +)] +#[serde(rename_all = "camelCase")] +pub struct PreStateConfig { + /// If `diffMode` is set to true, the response frame includes all the + /// account and storage diffs for the transaction. If it's missing or + /// set to false it only returns the accounts and storage necessary to + /// execute the transaction. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub diff_mode: Option, +} + +impl PreStateConfig { + /// Returns true if this trace was requested with diffmode. + #[inline] + pub fn is_diff_mode(&self) -> bool { self.diff_mode.unwrap_or_default() } + + /// Is default mode if diff_mode is not set + #[inline] + pub fn is_default_mode(&self) -> bool { !self.is_diff_mode() } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::geth::*; + + // See + const DEFAULT: &str = + include_str!("../../test_data/pre_state_tracer/default.json"); + const LEGACY: &str = + include_str!("../../test_data/pre_state_tracer/legacy.json"); + const DIFF_MODE: &str = + include_str!("../../test_data/pre_state_tracer/diff_mode.json"); + + #[test] + fn test_serialize_pre_state_trace() { + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.config.disable_storage = Some(false); + opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( + GethDebugBuiltInTracerType::PreStateTracer, + )); + opts.tracing_options.tracer_config = + serde_json::to_value(PreStateConfig { + diff_mode: Some(true), + }) + .unwrap() + .into(); + + assert_eq!( + serde_json::to_string(&opts).unwrap(), + r#"{"disableStorage":false,"tracer":"prestateTracer","tracerConfig":{"diffMode":true}}"# + ); + } + + #[test] + fn test_deserialize_pre_state_trace() { + let trace: PreStateFrame = serde_json::from_str(DEFAULT).unwrap(); + match trace { + PreStateFrame::Default(PreStateMode(_)) => {} + _ => unreachable!(), + } + let _trace: PreStateFrame = serde_json::from_str(LEGACY).unwrap(); + let trace: PreStateFrame = serde_json::from_str(DIFF_MODE).unwrap(); + match trace { + PreStateFrame::Diff(DiffMode { + pre: _pre, + post: _post, + }) => {} + _ => unreachable!(), + } + } + + #[test] + fn test_is_diff_mode() { + assert!(PreStateConfig { + diff_mode: Some(true) + } + .is_diff_mode()); + assert!(!PreStateConfig { + diff_mode: Some(false) + } + .is_diff_mode()); + assert!(!PreStateConfig { diff_mode: None }.is_diff_mode()); + } + + #[test] + fn parse_prestate_default_resp() { + let s = r#"{ + "0x0000000000000000000000000000000000000002": { + "balance": "0x0" + }, + "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { + "balance": "0x2638035a26d133809" + }, + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "balance": "0x7a48734599f7284", + "nonce": 1133 + }, + "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { + "balance": "0x0", + "code": "0x", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": + "0x000000000000000000000000000000000000000000000000000000000024aea6", + "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": + "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", + "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": + "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", + "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": + "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" + } + } + }"#; + let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); + assert!(pre_state.is_default()); + } + #[test] + fn parse_prestate_diff_resp() { + let s = r#"{ + "post": { + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "nonce": 1135 + } + }, + "pre": { + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "balance": "0x7a48429e177130a", + "nonce": 1134 + } + } + }"#; + let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); + assert!(pre_state.is_diff()); + } + + #[test] + fn test_retain_changed_accounts() { + let s = r#"{ + "post": { + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "nonce": 1135 + } + }, + "pre": { + "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { + "balance": "0x7a48429e177130a", + "nonce": 1134 + } + } + }"#; + let diff: DiffMode = serde_json::from_str(s).unwrap(); + let mut diff_changed = diff.clone(); + diff_changed.retain_changed(); + // different entries + assert_eq!(diff_changed, diff); + + diff_changed.pre = diff_changed.post.clone(); + diff_changed.retain_changed(); + assert!(diff_changed.post.is_empty()); + assert!(diff_changed.pre.is_empty()); + } +} diff --git a/crates/rpc_types_trace/src/lib.rs b/crates/rpc_types_trace/src/lib.rs new file mode 100644 index 0000000000..ba0addf77c --- /dev/null +++ b/crates/rpc_types_trace/src/lib.rs @@ -0,0 +1,5 @@ +mod block; +pub mod common; +pub mod geth; +pub mod parity; +mod state; diff --git a/crates/rpc_types_trace/src/parity.rs b/crates/rpc_types_trace/src/parity.rs new file mode 100644 index 0000000000..c0202e0a33 --- /dev/null +++ b/crates/rpc_types_trace/src/parity.rs @@ -0,0 +1,761 @@ +//! Types for trace module. +//! +//! See + +use alloy_primitives::Bytes; +use cfx_types::{Address, H256 as B256, U256, U64}; +use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; +use std::{ + collections::BTreeMap, + ops::{Deref, DerefMut}, +}; + +/// Different Trace diagnostic targets. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum TraceType { + /// Default trace + Trace, + /// Provides a full trace of the VM’s state throughout the execution of the + /// transaction, including for any subcalls. + VmTrace, + /// Provides information detailing all altered portions of the Ethereum + /// state made due to the execution of the transaction. + StateDiff, +} + +/// The Outcome of a traced transaction with optional settings +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TraceResults { + /// Output of the trace + pub output: Bytes, + /// Enabled if [TraceType::StateDiff] is provided + pub state_diff: Option, + /// Enabled if [TraceType::Trace] is provided, otherwise an empty vec + #[serde(default)] + pub trace: Vec, + /// Enabled if [TraceType::VmTrace] is provided + pub vm_trace: Option, +} + +// === impl TraceResults === + +impl TraceResults { + /// Sets the gas used of the root trace. + /// + /// The root trace's gasUsed should mirror the actual gas used by the + /// transaction. + /// + /// This allows setting it manually by consuming the execution result's gas + /// for example. + pub fn set_root_trace_gas_used(&mut self, gas_used: u64) { + if let Some(r) = self.trace.first_mut().and_then(|t| t.result.as_mut()) + { + r.set_gas_used(gas_used) + } + } +} + +/// A `FullTrace` with an additional transaction hash +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TraceResultsWithTransactionHash { + /// The recorded trace. + #[serde(flatten)] + pub full_trace: TraceResults, + /// Hash of the traced transaction. + pub transaction_hash: B256, +} + +/// A changed value +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct ChangedType { + /// Original value + pub from: T, + /// New value + pub to: T, +} + +/// Represents how a value changed. +/// +/// This is used for statediff. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +pub enum Delta { + /// Existing value didn't change. + #[default] + #[serde(rename = "=")] + Unchanged, + /// New storage value added. + #[serde(rename = "+")] + Added(T), + /// Existing storage value removed. + #[serde(rename = "-")] + Removed(T), + /// Existing storage value changed. + #[serde(rename = "*")] + Changed(ChangedType), +} + +// === impl Delta === + +impl Delta { + /// Creates a new [Delta::Changed] variant + pub const fn changed(from: T, to: T) -> Self { + Self::Changed(ChangedType { from, to }) + } + + /// Returns true if the value is unchanged + pub const fn is_unchanged(&self) -> bool { + matches!(self, Delta::Unchanged) + } + + /// Returns true if the value is added + pub const fn is_added(&self) -> bool { matches!(self, Delta::Added(_)) } + + /// Returns true if the value is removed + pub const fn is_removed(&self) -> bool { matches!(self, Delta::Removed(_)) } + + /// Returns true if the value is changed + pub const fn is_changed(&self) -> bool { matches!(self, Delta::Changed(_)) } +} + +/// The diff of an account after a transaction +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AccountDiff { + /// How the balance changed, if at all + pub balance: Delta, + /// How the code changed, if at all + pub code: Delta, + /// How the nonce changed, if at all + pub nonce: Delta, + /// All touched/changed storage values + pub storage: BTreeMap>, +} + +/// New-type for list of account diffs +#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] +#[serde(transparent)] +pub struct StateDiff(pub BTreeMap); + +impl Deref for StateDiff { + type Target = BTreeMap; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for StateDiff { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +/// Represents the various types of actions recorded during tracing +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "type", content = "action")] +pub enum Action { + /// Regular call + Call(CallAction), + /// A CREATE call + Create(CreateAction), + /// Parity style traces never renamed suicide to selfdestruct: + /// + /// For compatibility reasons, this is serialized as `suicide`: + #[serde(rename = "suicide", alias = "selfdestruct")] + Selfdestruct(SelfdestructAction), + /// Rewards if any (pre POS) + Reward(RewardAction), +} + +impl Action { + /// Returns true if this is a call action + pub const fn is_call(&self) -> bool { matches!(self, Action::Call(_)) } + + /// Returns true if this is a create action + pub const fn is_create(&self) -> bool { matches!(self, Action::Create(_)) } + + /// Returns true if this is a selfdestruct action + pub const fn is_selfdestruct(&self) -> bool { + matches!(self, Action::Selfdestruct(_)) + } + + /// Returns true if this is a reward action + pub const fn is_reward(&self) -> bool { matches!(self, Action::Reward(_)) } + + /// Returns what kind of action this is + pub const fn kind(&self) -> ActionType { + match self { + Action::Call(_) => ActionType::Call, + Action::Create(_) => ActionType::Create, + Action::Selfdestruct(_) => ActionType::Selfdestruct, + Action::Reward(_) => ActionType::Reward, + } + } +} + +/// An external action type. +/// +/// Used as enum identifier for [Action] +#[derive(Debug, Copy, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ActionType { + /// Contract call. + Call, + /// Contract creation. + Create, + /// Contract suicide/selfdestruct. + #[serde(rename = "suicide", alias = "selfdestruct")] + Selfdestruct, + /// A block reward. + Reward, +} + +/// Call type. +#[derive( + Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, +)] +#[serde(rename_all = "lowercase")] +pub enum CallType { + /// None + #[default] + None, + /// Call + Call, + /// Call code + CallCode, + /// Delegate call + DelegateCall, + /// Static call + StaticCall, +} + +/// Represents a certain [CallType] of a _call_ or message transaction. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CallAction { + /// Address of the sending account. + pub from: Address, + /// The type of the call. + pub call_type: CallType, + /// The gas available for executing the call. + pub gas: U64, + /// The input data provided to the call. + pub input: Bytes, + /// Address of the destination/target account. + pub to: Address, + /// Value transferred to the destination account. + pub value: U256, +} + +/// Represents a _create_ action, either a `CREATE` operation or a CREATE +/// transaction. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateAction { + /// The address of the creator. + pub from: Address, + /// The gas available for the creation init code. + pub gas: U64, + /// The init code. + pub init: Bytes, + /// The value with which the new account is endowed. + pub value: U256, +} + +/// What kind of reward. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum RewardType { + /// Block rewards + Block, + /// Reward for uncle block + Uncle, +} + +/// Recorded reward of a block. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RewardAction { + /// Author's address. + pub author: Address, + /// Reward type. + pub reward_type: RewardType, + /// Reward amount. + pub value: U256, +} + +/// Represents a _selfdestruct_ action fka `suicide`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SelfdestructAction { + /// destroyed/suicided address. + pub address: Address, + /// Balance of the contract just before it was destroyed. + pub balance: U256, + /// destroyed contract heir. + pub refund_address: Address, +} + +/// Outcome of a CALL. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CallOutput { + /// Gas used by the call. + pub gas_used: U64, + /// The output data of the call. + pub output: Bytes, +} + +/// Outcome of a CREATE. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateOutput { + /// Address of the created contract. + pub address: Address, + /// Contract code. + pub code: Bytes, + /// Gas used by the call. + pub gas_used: U64, +} + +/// Represents the output of a trace. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum TraceOutput { + /// Output of a regular call transaction. + Call(CallOutput), + /// Output of a CREATE transaction. + Create(CreateOutput), +} + +// === impl TraceOutput === + +impl TraceOutput { + /// Returns the gas used by this trace. + pub const fn gas_used(&self) -> U64 { + match self { + TraceOutput::Call(call) => call.gas_used, + TraceOutput::Create(create) => create.gas_used, + } + } + + /// Sets the gas used by this trace. + pub fn set_gas_used(&mut self, gas_used: u64) { + match self { + TraceOutput::Call(call) => call.gas_used = U64::from(gas_used), + TraceOutput::Create(create) => { + create.gas_used = U64::from(gas_used) + } + } + } +} + +/// A parity style trace of a transaction. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TransactionTrace { + /// Represents what kind of trace this is + #[serde(flatten)] + pub action: Action, + /// The error message if the transaction failed. + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + /// Output of the trace, can be CALL or CREATE + pub result: Option, + /// How many subtraces this trace has. + pub subtraces: usize, + /// The identifier of this transaction trace in the set. + /// + /// This gives the exact location in the call trace + /// [index in root CALL, index in first CALL, index in second CALL, …]. + pub trace_address: Vec, +} + +/// A wrapper for [TransactionTrace] that includes additional information about +/// the transaction. +#[derive(Clone, Debug, Eq, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LocalizedTransactionTrace { + /// Trace of the transaction and its result. + #[serde(flatten)] + pub trace: TransactionTrace, + /// Hash of the block, if not pending. + /// + /// Note: this deviates from which always returns a block number + pub block_hash: Option, + /// Block number the transaction is included in, None if pending. + /// + /// Note: this deviates from which always returns a block number + pub block_number: Option, + /// Hash of the transaction + pub transaction_hash: Option, + /// Transaction index within the block, None if pending. + pub transaction_position: Option, +} + +// Implement Serialize manually to ensure consistent ordering of fields to match +// other client's format +impl Serialize for LocalizedTransactionTrace { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + let mut s = + serializer.serialize_struct("LocalizedTransactionTrace", 9)?; + + let TransactionTrace { + action, + error, + result, + subtraces, + trace_address, + } = &self.trace; + + match action { + Action::Call(call_action) => { + s.serialize_field("action", call_action)?; + } + Action::Create(create_action) => { + s.serialize_field("action", create_action)?; + } + Action::Selfdestruct(selfdestruct_action) => { + s.serialize_field("action", selfdestruct_action)?; + } + Action::Reward(reward_action) => { + s.serialize_field("action", reward_action)?; + } + } + if let Some(block_hash) = self.block_hash { + s.serialize_field("blockHash", &block_hash)?; + } + if let Some(block_number) = self.block_number { + s.serialize_field("blockNumber", &block_number)?; + } + + if let Some(error) = error { + s.serialize_field("error", error)?; + } + + match result { + Some(TraceOutput::Call(call)) => { + s.serialize_field("result", call)?; + } + Some(TraceOutput::Create(create)) => { + s.serialize_field("result", create)?; + } + None => {} + } + + s.serialize_field("subtraces", &subtraces)?; + s.serialize_field("traceAddress", &trace_address)?; + + if let Some(transaction_hash) = &self.transaction_hash { + s.serialize_field("transactionHash", transaction_hash)?; + } + if let Some(transaction_position) = &self.transaction_position { + s.serialize_field("transactionPosition", transaction_position)?; + } + + s.serialize_field("type", &action.kind())?; + + s.end() + } +} + +/// A record of a full VM trace for a CALL/CREATE. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VmTrace { + /// The code to be executed. + pub code: Bytes, + /// All executed instructions. + pub ops: Vec, +} + +/// A record of a single VM instruction, opcode level. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VmInstruction { + /// The gas cost for this instruction. + pub cost: u64, + /// Information concerning the execution of the operation. + pub ex: Option, + /// The program counter. + pub pc: usize, + /// Subordinate trace of the CALL/CREATE if applicable. + pub sub: Option, + /// Stringified opcode. + #[serde(skip_serializing_if = "Option::is_none")] + pub op: Option, + /// Index of the instruction in the set. + #[serde(skip_serializing_if = "Option::is_none")] + pub idx: Option, +} + +/// A record of an executed VM operation. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VmExecutedOperation { + /// The total gas used. + pub used: u64, + /// The stack item placed, if any. + pub push: Vec, + /// If altered, the memory delta. + pub mem: Option, + /// The altered storage value, if any. + pub store: Option, +} + +/// A diff of some chunk of memory. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MemoryDelta { + /// Offset into memory the change begins. + pub off: usize, + /// The changed data. + pub data: Bytes, +} + +/// A diff of some storage value. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct StorageDelta { + /// Storage key. + pub key: U256, + /// Storage value belonging to the key. + pub val: U256, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::{json, Value}; + use std::str::FromStr; + + #[test] + fn test_transaction_trace() { + let s = r#"{ + "action": { + "from": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", + "callType": "call", + "gas": "0x10bfc", + "input": "0xf6cd1e8d0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec6952892271c8ee13f12e118484e03149281c9600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010480862479000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000160f5f00288e9e1cc8655b327e081566e580a71d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011c37937e080000fffffffffffffffffffffffffffffffffffffffffffffffffee3c86c81f8000000000000000000000000000000000000000000000000000000000000", + "to": "0x160f5f00288e9e1cc8655b327e081566e580a71d", + "value": "0x244b" + }, + "error": "Reverted", + "result": { + "gasUsed": "0x9daf", + "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" + }, + "subtraces": 3, + "traceAddress": [], + "type": "call" + }"#; + let val = serde_json::from_str::(s).unwrap(); + serde_json::to_value(val).unwrap(); + } + + #[test] + fn test_selfdestruct_suicide() { + let input = r#"{ + "action": { + "address": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", + "refundAddress": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", + "balance": "0x244b" + }, + "error": "Reverted", + "result": { + "gasUsed": "0x9daf", + "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" + }, + "subtraces": 3, + "traceAddress": [], + "type": "suicide" + }"#; + let val = serde_json::from_str::(input).unwrap(); + assert!(val.action.is_selfdestruct()); + + let json = serde_json::to_value(val.clone()).unwrap(); + let expect = serde_json::from_str::(input).unwrap(); + similar_asserts::assert_eq!(json, expect); + let s = serde_json::to_string(&val).unwrap(); + let json = serde_json::from_str::(&s).unwrap(); + similar_asserts::assert_eq!(json, expect); + + let input = input.replace("suicide", "selfdestruct"); + let val = serde_json::from_str::(&input).unwrap(); + assert!(val.action.is_selfdestruct()); + } + + #[derive(Debug)] + struct TraceTestCase { + trace: LocalizedTransactionTrace, + expected_json: Value, + } + + #[test] + fn test_serialization_order() { + let test_cases = vec![ + TraceTestCase { + trace: LocalizedTransactionTrace { + trace: TransactionTrace { + action: Action::Call(CallAction { + from: "4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), + call_type: CallType::DelegateCall, + gas: U64::from(3148955), + input: Bytes::from_str("0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000").unwrap(), + to: "99b5fa03a5ea4315725c43346e55a6a6fbd94098".parse::
().unwrap(), + value: U256::from(0), + }), + error: None, + result: Some(TraceOutput::Call(CallOutput { gas_used: U64::from(32364), output: Bytes::new() })), + subtraces: 0, + trace_address: vec![0, 10, 0], + }, + block_hash: Some(B256::zero()), + block_number: Some(18557272), + transaction_hash: Some(B256::from_str("54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), + transaction_position: Some(102), + }, + expected_json: json!({ + "action": { + "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", + "callType": "delegatecall", + "gas": "0x300c9b", + "input": "0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000", + "to": "0x99b5fa03a5ea4315725c43346e55a6a6fbd94098", + "value": "0x0" + }, + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "blockNumber": 18557272, + "result": { + "gasUsed": "0x7e6c", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0, + 10, + 0 + ], + "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", + "transactionPosition": 102, + "type": "call" + }), + }, + TraceTestCase { + trace: LocalizedTransactionTrace { + trace: TransactionTrace { + action: Action::Create(CreateAction{ + from: "4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), + gas: U64::from(3438907), + init: Bytes::from_str("0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap(), + value: U256::from(0), + }), + error: None, + result: Some(TraceOutput::Create(CreateOutput { gas_used: U64::from(183114), address: "7eb6c6c1db08c0b9459a68cfdcedab64f319c138".parse::
().unwrap(), code: Bytes::from_str("0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap() })), + subtraces: 0, + trace_address: vec![0, 7, 0, 0], + }, + block_hash: Some(B256::from_str("d5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d").unwrap()), + block_number: Some(18557272), + transaction_hash: Some(B256::from_str("54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), + transaction_position: Some(102), + }, + expected_json: json!({ + "action": { + "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", + "gas": "0x34793b", + "init": "0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", + "value": "0x0" + }, + "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", + "blockNumber": 18557272, + "result": { + "address": "0x7eb6c6c1db08c0b9459a68cfdcedab64f319c138", + "code": "0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", + "gasUsed": "0x2cb4a" + }, + "subtraces": 0, + "traceAddress": [ + 0, + 7, + 0, + 0 + ], + "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", + "transactionPosition": 102, + "type": "create" + }), + } + ]; + + for (i, test_case) in test_cases.iter().enumerate() { + let serialized = serde_json::to_string(&test_case.trace).unwrap(); + let actual_json: Value = serde_json::from_str(&serialized).unwrap(); + + assert_eq!( + actual_json, test_case.expected_json, + "Test case {} failed; Trace: {:?}", + i, test_case.trace + ); + } + } + + #[test] + fn test_deserialize_serialize() { + let reference_data = r#"{ + "action": { + "from": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", + "callType": "call", + "gas": "0x4a0d00", + "input": "0x12", + "to": "0x4f4495243837681061c4743b74b3eedf548d56a5", + "value": "0x0" + }, + "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", + "blockNumber": 18557272, + "result": { + "gasUsed": "0x17d337", + "output": "0x" + }, + "subtraces": 1, + "traceAddress": [], + "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", + "transactionPosition": 102, + "type": "call" +}"#; + + let trace: LocalizedTransactionTrace = + serde_json::from_str(reference_data).unwrap(); + assert!(trace.trace.action.is_call()); + let serialized = serde_json::to_string_pretty(&trace).unwrap(); + similar_asserts::assert_eq!(serialized, reference_data); + } + + #[test] + fn test_deserialize_serialize_selfdestruct() { + let reference_data = r#"{ + "action": { + "address": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", + "balance": "0x0", + "refundAddress": "0x4f4495243837681061c4743b74b3eedf548d56a5" + }, + "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", + "blockNumber": 18557272, + "result": { + "gasUsed": "0x17d337", + "output": "0x" + }, + "subtraces": 1, + "traceAddress": [], + "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", + "transactionPosition": 102, + "type": "suicide" +}"#; + + let trace: LocalizedTransactionTrace = + serde_json::from_str(reference_data).unwrap(); + assert!(trace.trace.action.is_selfdestruct()); + let serialized = serde_json::to_string_pretty(&trace).unwrap(); + similar_asserts::assert_eq!(serialized, reference_data); + } +} diff --git a/crates/rpc_types_trace/src/state.rs b/crates/rpc_types_trace/src/state.rs new file mode 100644 index 0000000000..948993851a --- /dev/null +++ b/crates/rpc_types_trace/src/state.rs @@ -0,0 +1,120 @@ +//! bindings for state overrides in eth_call + +use alloy_primitives::Bytes; +use cfx_types::{Address, H256 as B256, U256, U64}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// A set of account overrides +pub type StateOverride = HashMap; + +/// Custom account override used in call +#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] +#[serde(default, rename_all = "camelCase", deny_unknown_fields)] +pub struct AccountOverride { + /// Fake balance to set for the account before executing the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub balance: Option, + /// Fake nonce to set for the account before executing the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nonce: Option, + /// Fake EVM bytecode to inject into the account before executing the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub code: Option, + /// Fake key-value mapping to override all slots in the account storage + /// before executing the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state: Option>, + /// Fake key-value mapping to override individual slots in the account + /// storage before executing the call. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state_diff: Option>, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + #[should_panic(expected = "invalid type")] + fn test_invalid_json_structure() { + let invalid_json = r#"{ + "0x1234567890123456789012345678901234567890": { + "balance": true + } + }"#; + + let _: StateOverride = serde_json::from_str(invalid_json).unwrap(); + } + + #[test] + fn test_large_values_in_override() { + let large_values_json = r#"{ + "0x1234567890123456789012345678901234567890": { + "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "nonce": "0xffffffffffffffff" + } + }"#; + + let state_override: StateOverride = + serde_json::from_str(large_values_json).unwrap(); + let acc = state_override + .get( + &Address::from_str("1234567890123456789012345678901234567890") + .unwrap(), + ) + .unwrap(); + assert_eq!(acc.balance, Some(U256::MAX)); + assert_eq!(acc.nonce, Some(U64::MAX)); + } + + #[test] + fn test_default_account_override() { + let acc_override = AccountOverride::default(); + assert!(acc_override.balance.is_none()); + assert!(acc_override.nonce.is_none()); + assert!(acc_override.code.is_none()); + assert!(acc_override.state.is_none()); + assert!(acc_override.state_diff.is_none()); + } + + #[test] + fn test_state_override() { + let s = r#"{ + "0x0000000000000000000000000000000000000124": { + "code": "0x6080604052348015600e575f80fd5b50600436106026575f3560e01c80632096525514602a575b5f80fd5b60306044565b604051901515815260200160405180910390f35b5f604e600242605e565b5f0360595750600190565b505f90565b5f82607757634e487b7160e01b5f52601260045260245ffd5b50069056fea2646970667358221220287f77a4262e88659e3fb402138d2ee6a7ff9ba86bae487a95aa28156367d09c64736f6c63430008140033" + } + }"#; + let state_override: StateOverride = serde_json::from_str(s).unwrap(); + let acc = state_override + .get( + &Address::from_str("0000000000000000000000000000000000000124") + .unwrap(), + ) + .unwrap(); + assert!(acc.code.is_some()); + } + #[test] + fn test_state_override_state_diff() { + let s = r#"{ + "0x1b5212AF6b76113afD94cD2B5a78a73B7d7A8222": { + "balance": "0x39726378b58c400000", + "stateDiff": {} + }, + "0xdAC17F958D2ee523a2206206994597C13D831ec7": { + "stateDiff": { + "0xede27e4e7f3676edbf125879f17a896d6507958df3d57bda6219f1880cae8a41": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } + }"#; + let state_override: StateOverride = serde_json::from_str(s).unwrap(); + let acc = state_override + .get( + &Address::from_str("1b5212AF6b76113afD94cD2B5a78a73B7d7A8222") + .unwrap(), + ) + .unwrap(); + assert!(acc.state_diff.is_some()); + } +} diff --git a/crates/rpc_types_trace/test_data/call_tracer/default.json b/crates/rpc_types_trace/test_data/call_tracer/default.json new file mode 100644 index 0000000000..553b2a3979 --- /dev/null +++ b/crates/rpc_types_trace/test_data/call_tracer/default.json @@ -0,0 +1,21 @@ +{ + "calls": [ + { + "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", + "gas": "0x6d05", + "gasUsed": "0x0", + "input": "0x", + "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", + "type": "CALL", + "value": "0x6f05b59d3b20000" + } + ], + "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb", + "gas": "0x10738", + "gasUsed": "0x9751", + "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", + "type": "CALL", + "value": "0x0" +} diff --git a/crates/rpc_types_trace/test_data/call_tracer/legacy.json b/crates/rpc_types_trace/test_data/call_tracer/legacy.json new file mode 100644 index 0000000000..b89e7ae86c --- /dev/null +++ b/crates/rpc_types_trace/test_data/call_tracer/legacy.json @@ -0,0 +1,19 @@ +{ + "calls": [ + { + "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", + "input": "0x", + "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", + "type": "CALL", + "value": "0x6f05b59d3b20000" + } + ], + "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb", + "gas": "0x10738", + "gasUsed": "0x9751", + "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", + "type": "CALL", + "value": "0x0" +} diff --git a/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json b/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json new file mode 100644 index 0000000000..327bb42787 --- /dev/null +++ b/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json @@ -0,0 +1,10 @@ +{ + "from": "0x4f5777744b500616697cb655dcb02ee6cd51deb5", + "gas": "0x2dced", + "gasUsed": "0x1a9e5", + "to": "0x200edd17f30485a8735878661960cd7a9a95733f", + "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000", + "output": "0xba51a6df00000000000000000000000000000000000000000000000000000000", + "value": "0x8ac7230489e80000", + "type": "CALL" +} diff --git a/crates/rpc_types_trace/test_data/call_tracer/with_log.json b/crates/rpc_types_trace/test_data/call_tracer/with_log.json new file mode 100644 index 0000000000..746dcef8d3 --- /dev/null +++ b/crates/rpc_types_trace/test_data/call_tracer/with_log.json @@ -0,0 +1,20 @@ +{ + "from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", + "gas": "0x1f36d", + "gasUsed": "0xc6a5", + "to": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd", + "input": "0xa9059cbb000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb0000000000000000000000000000000000000000000000000000000000989680", + "logs": [ + { + "address": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x000000000000000000000000d1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", + "0x000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb" + ], + "data": "0x0000000000000000000000000000000000000000000000000000000000989680" + } + ], + "value": "0x0", + "type": "CALL" +} diff --git a/crates/rpc_types_trace/test_data/default/structlogs_01.json b/crates/rpc_types_trace/test_data/default/structlogs_01.json new file mode 100644 index 0000000000..2025640f81 --- /dev/null +++ b/crates/rpc_types_trace/test_data/default/structlogs_01.json @@ -0,0 +1,4112 @@ +{ + "structLogs": [ + { + "pc": 0, + "op": "PUSH1", + "gas": 24595, + "gasCost": 3, + "depth": 1, + "stack": [], + "memory": [] + }, + { + "pc": 2, + "op": "PUSH1", + "gas": 24592, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x80" + ], + "memory": [] + }, + { + "pc": 4, + "op": "MSTORE", + "gas": 24589, + "gasCost": 12, + "depth": 1, + "stack": [ + "0x80", + "0x40" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000" + ] + }, + { + "pc": 5, + "op": "CALLVALUE", + "gas": 24577, + "gasCost": 2, + "depth": 1, + "stack": [], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6, + "op": "DUP1", + "gas": 24575, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 7, + "op": "ISZERO", + "gas": 24572, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8, + "op": "PUSH2", + "gas": 24569, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x0", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 11, + "op": "JUMPI", + "gas": 24566, + "gasCost": 10, + "depth": 1, + "stack": [ + "0x0", + "0x1", + "0x10" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 16, + "op": "JUMPDEST", + "gas": 24556, + "gasCost": 1, + "depth": 1, + "stack": [ + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 17, + "op": "POP", + "gas": 24555, + "gasCost": 2, + "depth": 1, + "stack": [ + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 18, + "op": "PUSH1", + "gas": 24553, + "gasCost": 3, + "depth": 1, + "stack": [], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 20, + "op": "CALLDATASIZE", + "gas": 24550, + "gasCost": 2, + "depth": 1, + "stack": [ + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 21, + "op": "LT", + "gas": 24548, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x4", + "0x44" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 22, + "op": "PUSH2", + "gas": 24545, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 25, + "op": "JUMPI", + "gas": 24542, + "gasCost": 10, + "depth": 1, + "stack": [ + "0x0", + "0x1fb" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 26, + "op": "PUSH1", + "gas": 24532, + "gasCost": 3, + "depth": 1, + "stack": [], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 28, + "op": "CALLDATALOAD", + "gas": 24529, + "gasCost": 3, + "depth": 1, + "stack": [ + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 29, + "op": "PUSH1", + "gas": 24526, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb46500000000000000000000000000000000000111abe46ff893f3b2fdf1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 31, + "op": "SHR", + "gas": 24523, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb46500000000000000000000000000000000000111abe46ff893f3b2fdf1", + "0xe0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 32, + "op": "DUP1", + "gas": 24520, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 33, + "op": "PUSH4", + "gas": 24517, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 38, + "op": "GT", + "gas": 24514, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465", + "0x6352211e" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 39, + "op": "PUSH2", + "gas": 24511, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 42, + "op": "JUMPI", + "gas": 24508, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0", + "0x11a" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 43, + "op": "DUP1", + "gas": 24498, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 44, + "op": "PUSH4", + "gas": 24495, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 49, + "op": "GT", + "gas": 24492, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465", + "0x95d89b41" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 50, + "op": "PUSH2", + "gas": 24489, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 53, + "op": "JUMPI", + "gas": 24486, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0", + "0xad" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 54, + "op": "DUP1", + "gas": 24476, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 55, + "op": "PUSH4", + "gas": 24473, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 60, + "op": "GT", + "gas": 24470, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465", + "0xdb006a75" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 61, + "op": "PUSH2", + "gas": 24467, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 64, + "op": "JUMPI", + "gas": 24464, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x1", + "0x7c" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 124, + "op": "JUMPDEST", + "gas": 24454, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 125, + "op": "DUP1", + "gas": 24453, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 126, + "op": "PUSH4", + "gas": 24450, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 131, + "op": "EQ", + "gas": 24447, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465", + "0x95d89b41" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 132, + "op": "PUSH2", + "gas": 24444, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 135, + "op": "JUMPI", + "gas": 24441, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x0", + "0x3c7" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 136, + "op": "DUP1", + "gas": 24431, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 137, + "op": "PUSH4", + "gas": 24428, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 142, + "op": "EQ", + "gas": 24425, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0xa22cb465", + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 143, + "op": "PUSH2", + "gas": 24422, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 146, + "op": "JUMPI", + "gas": 24419, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x1", + "0x3cf" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 975, + "op": "JUMPDEST", + "gas": 24409, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 976, + "op": "PUSH2", + "gas": 24408, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 979, + "op": "PUSH2", + "gas": 24405, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 982, + "op": "CALLDATASIZE", + "gas": 24402, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 983, + "op": "PUSH1", + "gas": 24400, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 985, + "op": "PUSH2", + "gas": 24397, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 988, + "op": "JUMP", + "gas": 24394, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x2231" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8753, + "op": "JUMPDEST", + "gas": 24386, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8754, + "op": "PUSH1", + "gas": 24385, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8756, + "op": "DUP1", + "gas": 24382, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8757, + "op": "PUSH1", + "gas": 24379, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8759, + "op": "DUP4", + "gas": 24376, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x40" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8760, + "op": "DUP6", + "gas": 24373, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x40", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8761, + "op": "SUB", + "gas": 24370, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x40", + "0x4", + "0x44" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8762, + "op": "SLT", + "gas": 24367, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x40", + "0x40" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8763, + "op": "ISZERO", + "gas": 24364, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8764, + "op": "PUSH2", + "gas": 24361, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8767, + "op": "JUMPI", + "gas": 24358, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x1", + "0x2243" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8771, + "op": "JUMPDEST", + "gas": 24348, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8772, + "op": "PUSH2", + "gas": 24347, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8775, + "op": "DUP4", + "gas": 24344, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8776, + "op": "PUSH2", + "gas": 24341, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8779, + "op": "JUMP", + "gas": 24338, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x211a" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8474, + "op": "JUMPDEST", + "gas": 24330, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8475, + "op": "DUP1", + "gas": 24329, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8476, + "op": "CALLDATALOAD", + "gas": 24326, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8477, + "op": "PUSH1", + "gas": 24323, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8479, + "op": "PUSH1", + "gas": 24320, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8481, + "op": "PUSH1", + "gas": 24317, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8483, + "op": "SHL", + "gas": 24314, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0xa0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8484, + "op": "SUB", + "gas": 24311, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x10000000000000000000000000000000000000000" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8485, + "op": "DUP2", + "gas": 24308, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xffffffffffffffffffffffffffffffffffffffff" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8486, + "op": "AND", + "gas": 24305, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xffffffffffffffffffffffffffffffffffffffff", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8487, + "op": "DUP2", + "gas": 24302, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8488, + "op": "EQ", + "gas": 24299, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8489, + "op": "PUSH2", + "gas": 24296, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8492, + "op": "JUMPI", + "gas": 24293, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x18b8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6328, + "op": "JUMPDEST", + "gas": 24283, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6329, + "op": "SWAP2", + "gas": 24282, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x224c", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6330, + "op": "SWAP1", + "gas": 24279, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x4", + "0x224c" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6331, + "op": "POP", + "gas": 24276, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x224c", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 6332, + "op": "JUMP", + "gas": 24274, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x224c" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8780, + "op": "JUMPDEST", + "gas": 24266, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8781, + "op": "SWAP2", + "gas": 24265, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x0", + "0x0", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8782, + "op": "POP", + "gas": 24262, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8783, + "op": "PUSH1", + "gas": 24260, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8785, + "op": "DUP4", + "gas": 24257, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x20" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8786, + "op": "ADD", + "gas": 24254, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x20", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8787, + "op": "CALLDATALOAD", + "gas": 24251, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x24" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8788, + "op": "DUP1", + "gas": 24248, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8789, + "op": "ISZERO", + "gas": 24245, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8790, + "op": "ISZERO", + "gas": 24242, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8791, + "op": "DUP2", + "gas": 24239, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8792, + "op": "EQ", + "gas": 24236, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8793, + "op": "PUSH2", + "gas": 24233, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8796, + "op": "JUMPI", + "gas": 24230, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1", + "0x2260" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8800, + "op": "JUMPDEST", + "gas": 24220, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8801, + "op": "DUP1", + "gas": 24219, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8802, + "op": "SWAP2", + "gas": 24216, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8803, + "op": "POP", + "gas": 24213, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8804, + "op": "POP", + "gas": 24211, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8805, + "op": "SWAP3", + "gas": 24209, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x44", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8806, + "op": "POP", + "gas": 24206, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x1", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x44" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8807, + "op": "SWAP3", + "gas": 24204, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x3dd", + "0x1", + "0x4", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8808, + "op": "SWAP1", + "gas": 24201, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x4", + "0x3dd" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8809, + "op": "POP", + "gas": 24198, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x3dd", + "0x4" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 8810, + "op": "JUMP", + "gas": 24196, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x3dd" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 989, + "op": "JUMPDEST", + "gas": 24188, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 990, + "op": "PUSH2", + "gas": 24187, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 993, + "op": "JUMP", + "gas": 24184, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xc94" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3220, + "op": "JUMPDEST", + "gas": 24176, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3221, + "op": "PUSH1", + "gas": 24175, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3223, + "op": "PUSH1", + "gas": 24172, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3225, + "op": "PUSH1", + "gas": 24169, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3227, + "op": "SHL", + "gas": 24166, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0x1", + "0xa0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3228, + "op": "SUB", + "gas": 24163, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0x10000000000000000000000000000000000000000" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3229, + "op": "DUP3", + "gas": 24160, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xffffffffffffffffffffffffffffffffffffffff" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3230, + "op": "AND", + "gas": 24157, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xffffffffffffffffffffffffffffffffffffffff", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3231, + "op": "CALLER", + "gas": 24154, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3232, + "op": "EQ", + "gas": 24152, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3233, + "op": "ISZERO", + "gas": 24149, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3234, + "op": "PUSH2", + "gas": 24146, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3237, + "op": "JUMPI", + "gas": 24143, + "gasCost": 10, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x1", + "0xced" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3309, + "op": "JUMPDEST", + "gas": 24133, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3310, + "op": "CALLER", + "gas": 24132, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3311, + "op": "PUSH1", + "gas": 24130, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3313, + "op": "DUP2", + "gas": 24127, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3314, + "op": "DUP2", + "gas": 24124, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3315, + "op": "MSTORE", + "gas": 24121, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0" + ], + "memory": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3316, + "op": "PUSH1", + "gas": 24118, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3318, + "op": "PUSH1", + "gas": 24115, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x5" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3320, + "op": "SWAP1", + "gas": 24112, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x5", + "0x20" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3321, + "op": "DUP2", + "gas": 24109, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x5" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3322, + "op": "MSTORE", + "gas": 24106, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x5", + "0x20" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3323, + "op": "PUSH1", + "gas": 24103, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3325, + "op": "DUP1", + "gas": 24100, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3326, + "op": "DUP4", + "gas": 24097, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x40" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3327, + "op": "KECCAK256", + "gas": 24094, + "gasCost": 42, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x40", + "0x0" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3328, + "op": "PUSH1", + "gas": 24052, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3330, + "op": "PUSH1", + "gas": 24049, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x1" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3332, + "op": "PUSH1", + "gas": 24046, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x1", + "0x1" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3334, + "op": "SHL", + "gas": 24043, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x1", + "0x1", + "0xa0" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3335, + "op": "SUB", + "gas": 24040, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x1", + "0x10000000000000000000000000000000000000000" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3336, + "op": "DUP8", + "gas": 24037, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0xffffffffffffffffffffffffffffffffffffffff" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3337, + "op": "AND", + "gas": 24034, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0xffffffffffffffffffffffffffffffffffffffff", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3338, + "op": "DUP1", + "gas": 24031, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3339, + "op": "DUP6", + "gas": 24028, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3340, + "op": "MSTORE", + "gas": 24025, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x0" + ], + "memory": [ + "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3341, + "op": "SWAP1", + "gas": 24022, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3342, + "op": "DUP4", + "gas": 24019, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3343, + "op": "MSTORE", + "gas": 24016, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0x20" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "0000000000000000000000000000000000000000000000000000000000000005", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3344, + "op": "SWAP3", + "gas": 24013, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x0", + "0x20", + "0x40", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3345, + "op": "DUP2", + "gas": 24010, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3346, + "op": "SWAP1", + "gas": 24007, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x0", + "0x40" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3347, + "op": "KECCAK256", + "gas": 24004, + "gasCost": 42, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x40", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3348, + "op": "DUP1", + "gas": 23962, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3349, + "op": "SLOAD", + "gas": 23959, + "gasCost": 2100, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ], + "storage": { + "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a": "0000000000000000000000000000000000000000000000000000000000000000" + } + }, + { + "pc": 3350, + "op": "PUSH1", + "gas": 21859, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3352, + "op": "NOT", + "gas": 21856, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0", + "0xff" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3353, + "op": "AND", + "gas": 21853, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3354, + "op": "DUP7", + "gas": 21850, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3355, + "op": "ISZERO", + "gas": 21847, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3356, + "op": "ISZERO", + "gas": 21844, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3357, + "op": "SWAP1", + "gas": 21841, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x0", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3358, + "op": "DUP2", + "gas": 21838, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x1", + "0x0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3359, + "op": "OR", + "gas": 21835, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x1", + "0x0", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3360, + "op": "SWAP1", + "gas": 21832, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x1", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3361, + "op": "SWAP2", + "gas": 21829, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", + "0x1", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3362, + "op": "SSTORE", + "gas": 21826, + "gasCost": 20000, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x1", + "0x1", + "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ], + "storage": { + "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a": "0000000000000000000000000000000000000000000000000000000000000001" + } + }, + { + "pc": 3363, + "op": "SWAP1", + "gas": 1826, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x40", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3364, + "op": "MLOAD", + "gas": 1823, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x1", + "0x40" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3365, + "op": "SWAP1", + "gas": 1820, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x1", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3366, + "op": "DUP2", + "gas": 1817, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x80", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080" + ] + }, + { + "pc": 3367, + "op": "MSTORE", + "gas": 1814, + "gasCost": 9, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x80", + "0x1", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000" + ] + }, + { + "pc": 3368, + "op": "SWAP2", + "gas": 1805, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x20", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3369, + "op": "SWAP3", + "gas": 1802, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x80", + "0x20", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3370, + "op": "SWAP2", + "gas": 1799, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x80", + "0x20", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3371, + "op": "PUSH32", + "gas": 1796, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x20", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3404, + "op": "SWAP2", + "gas": 1793, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x20", + "0x80", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3405, + "op": "ADD", + "gas": 1790, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0x80", + "0x20" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3406, + "op": "PUSH1", + "gas": 1787, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0xa0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3408, + "op": "MLOAD", + "gas": 1784, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0xa0", + "0x40" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3409, + "op": "DUP1", + "gas": 1781, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0xa0", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3410, + "op": "SWAP2", + "gas": 1778, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0xa0", + "0x80", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3411, + "op": "SUB", + "gas": 1775, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0x80", + "0x80", + "0xa0" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3412, + "op": "SWAP1", + "gas": 1772, + "gasCost": 3, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0x80", + "0x20" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3413, + "op": "LOG3", + "gas": 1769, + "gasCost": 1756, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", + "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", + "0x20", + "0x80" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3414, + "op": "POP", + "gas": 13, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8", + "0x1" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3415, + "op": "POP", + "gas": 11, + "gasCost": 2, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b", + "0x111abe46ff893f3b2fdf1f759a8a8" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 3416, + "op": "JUMP", + "gas": 9, + "gasCost": 8, + "depth": 1, + "stack": [ + "0xa22cb465", + "0x27b" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 635, + "op": "JUMPDEST", + "gas": 1, + "gasCost": 1, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + }, + { + "pc": 636, + "op": "STOP", + "gas": 0, + "gasCost": 0, + "depth": 1, + "stack": [ + "0xa22cb465" + ], + "memory": [ + "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", + "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", + "0000000000000000000000000000000000000000000000000000000000000080", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000001" + ] + } + ], + "gas": 46107, + "failed": false, + "returnValue": "" +} \ No newline at end of file diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/default.json b/crates/rpc_types_trace/test_data/pre_state_tracer/default.json new file mode 100644 index 0000000000..43e69b11bd --- /dev/null +++ b/crates/rpc_types_trace/test_data/pre_state_tracer/default.json @@ -0,0 +1,20 @@ +{ + "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": { + "balance": "0xc820f93200f4000", + "nonce": 94 + }, + "0x332b656504f4eabb44c8617a42af37461a34e9dc": { + "balance": "0x11faea4f35e5af80000", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": { + "balance": "0xbf681825be002ac452", + "nonce": 28922 + }, + "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": { + "balance": "0xb3d0ac5cb94df6f6b0", + "nonce": 1 + } +} diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json b/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json new file mode 100644 index 0000000000..0654d26f54 --- /dev/null +++ b/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json @@ -0,0 +1,41 @@ +{ + "pre": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "nonce": 22 + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x0" + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "nonce": 1, + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "nonce": 29072 + } + }, + "post": { + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x6f05b59d3b20000" + }, + "0x1585936b53834b021f68cc13eeefdec2efc8e724": { + "balance": "0x420eed1bd6c00" + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d869a3b70062eb9bd5", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b95e" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d7725724a9044b75", + "nonce": 29073 + } + } +} diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json b/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json new file mode 100644 index 0000000000..dbefb198c4 --- /dev/null +++ b/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json @@ -0,0 +1,25 @@ +{ + "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { + "balance": "0x0", + "code": "0x", + "nonce": 22, + "storage": {} + }, + "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { + "balance": "0x4d87094125a369d9bd5", + "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" + } + }, + "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { + "balance": "0x1780d77678137ac1b775", + "code": "0x", + "nonce": 29072, + "storage": {} + } +} diff --git a/crates/serde/Cargo.toml b/crates/serde/Cargo.toml new file mode 100644 index 0000000000..9280259719 --- /dev/null +++ b/crates/serde/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cfx-serde" +edition = "2021" +version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +repository.workspace = true +license-file.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +alloy-primitives.workspace = true +serde.workspace = true +serde_json = { workspace = true, features = ["alloc"] } +cfx-types = { path = "../cfx_types" } diff --git a/crates/serde/src/lib.rs b/crates/serde/src/lib.rs new file mode 100644 index 0000000000..5d04f39733 --- /dev/null +++ b/crates/serde/src/lib.rs @@ -0,0 +1,18 @@ +pub mod num; + +pub use num::*; + +use serde::Serializer; + +/// Serialize a byte vec as a hex string _without_ the "0x" prefix. +/// +/// This behaves the same as [`hex::encode`](alloy_primitives::hex::encode). +pub fn serialize_hex_string_no_prefix( + x: T, s: S, +) -> Result +where + S: Serializer, + T: AsRef<[u8]>, +{ + s.serialize_str(&alloy_primitives::hex::encode(x.as_ref())) +} diff --git a/crates/serde/src/num.rs b/crates/serde/src/num.rs new file mode 100644 index 0000000000..10364c356b --- /dev/null +++ b/crates/serde/src/num.rs @@ -0,0 +1,46 @@ +use cfx_types::U256; +use core::str::FromStr; +use serde::{de, Deserialize, Deserializer}; + +/// An enum that represents either a [serde_json::Number] integer, or a hex +/// [U256]. +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum NumberOrHexU256 { + /// An integer + Int(serde_json::Number), + /// A hex U256 + Hex(U256), +} + +impl NumberOrHexU256 { + /// Tries to convert this into a [U256]]. + pub fn try_into_u256(self) -> Result { + match self { + NumberOrHexU256::Int(num) => { + U256::from_str(num.to_string().as_str()).map_err(E::custom) + } + NumberOrHexU256::Hex(val) => Ok(val), + } + } +} + +/// Deserializes the input into a U256, accepting both 0x-prefixed hex and +/// decimal strings with arbitrary precision, defined by serde_json's +/// [`Number`](serde_json::Number). +pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result +where D: Deserializer<'de> { + NumberOrHexU256::deserialize(deserializer)?.try_into_u256() +} + +/// Deserializes the input into an `Option`, using [`from_int_or_hex`] to +/// deserialize the inner value. +pub fn from_int_or_hex_opt<'de, D>( + deserializer: D, +) -> Result, D::Error> +where D: Deserializer<'de> { + match Option::::deserialize(deserializer)? { + Some(val) => val.try_into_u256().map(Some), + None => Ok(None), + } +} diff --git a/crates/util/cfx-vm-tracer-derive/Cargo.toml b/crates/util/cfx-vm-tracer-derive/Cargo.toml index ca849a830e..f1d62a4ca3 100644 --- a/crates/util/cfx-vm-tracer-derive/Cargo.toml +++ b/crates/util/cfx-vm-tracer-derive/Cargo.toml @@ -2,7 +2,7 @@ name = "cfx-vm-tracer-derive" version = "0.1.0" authors = ["Your Name "] -edition = "2018" +edition = "2021" # Set the crate type to "proc-macro" [lib] diff --git a/rust-toolchain b/rust-toolchain index 5e3a425662..32a6ce3c71 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.73.0 +1.76.0 From 350f2ba0600cba2ce71255e0531efd609210cb8f Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 26 Apr 2024 10:49:25 +0800 Subject: [PATCH 005/137] upgrade revm and alloy-rpc-types version --- Cargo.lock | 122 +++++++++++++----- Cargo.toml | 5 +- crates/cfxcore/core/Cargo.toml | 2 +- .../consensus_inner/consensus_executor.rs | 2 +- crates/cfxcore/core/src/consensus/mod.rs | 2 +- crates/cfxcore/execute-helper/Cargo.toml | 6 +- .../execute-helper/src/observer/fourbyte.rs | 2 +- .../src/observer/geth_tracer/builder/geth.rs | 2 +- .../src/observer/geth_tracer/config.rs | 2 +- .../src/observer/geth_tracer/mod.rs | 2 +- .../src/observer/geth_tracer/types.rs | 2 +- .../cfxcore/execute-helper/src/tx_outcome.rs | 2 +- crates/client/Cargo.toml | 2 +- crates/client/src/rpc/impls/eth/debug.rs | 2 +- .../client/src/rpc/traits/eth_space/debug.rs | 2 +- 15 files changed, 110 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 121d792ada..90e130a22b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,11 +146,49 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "alloy-consensus" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", + "sha2 0.10.6", +] + +[[package]] +name = "alloy-eips" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "once_cell", + "serde", +] + +[[package]] +name = "alloy-genesis" +version = "0.1.0" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", + "serde_json", +] + [[package]] name = "alloy-primitives" -version = "0.6.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +checksum = "50c715249705afa1e32be79dabfd35e2ef0f1cc02ad2cf48c9d1e20026ee637b" dependencies = [ "alloy-rlp", "bytes 1.4.0", @@ -191,35 +229,39 @@ dependencies = [ ] [[package]] -name = "alloy-rpc-trace-types" +name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", - "alloy-rpc-types", + "alloy-rlp", "alloy-serde", + "alloy-sol-types", + "itertools 0.12.1", "serde", "serde_json", + "thiserror", ] [[package]] -name = "alloy-rpc-types" +name = "alloy-rpc-types-trace" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" dependencies = [ "alloy-primitives", - "alloy-rlp", + "alloy-rpc-types", "alloy-serde", - "itertools 0.12.1", "serde", "serde_json", - "thiserror", ] [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy?rev=410850b#410850b305a28297483d819b669b04ba31796359" +source = "git+https://github.com/alloy-rs/alloy?rev=4e22b9e#4e22b9e1de80f1b1cc5dfdcd9461d44b27cf27ca" dependencies = [ "alloy-primitives", "serde", @@ -228,12 +270,12 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.6.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86ec0a47740b20bc5613b8712d0d321d031c4efc58e9645af96085d5cccfc27" +checksum = "bef9a94a27345fb31e3fcb5f5e9f592bb4847493b07fa1e47dd9fde2222f2e28" dependencies = [ + "alloy-sol-macro-input", "const-hex", - "dunce", "heck 0.4.1", "indexmap 2.2.5", "proc-macro-error", @@ -244,11 +286,26 @@ dependencies = [ "tiny-keccak 2.0.2", ] +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31fe73cd259527e24dc2dbfe64bc95e5ddfcd2b2731f670a11ff72b2be2c25b" +dependencies = [ + "const-hex", + "dunce", + "heck 0.5.0", + "proc-macro2 1.0.71", + "quote 1.0.33", + "syn 2.0.42", + "syn-solidity", +] + [[package]] name = "alloy-sol-types" -version = "0.6.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad09ec5853fa700d12d778ad224dcdec636af424d29fad84fb9a2f16a5b0ef09" +checksum = "afaffed78bfb17526375754931e045f96018aa810844b29c7aef823266dd4b4b" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -1082,7 +1139,7 @@ name = "cfx-execute-helper" version = "2.0.2" dependencies = [ "alloy-primitives", - "alloy-rpc-trace-types", + "alloy-rpc-types-trace", "alloy-sol-types", "cfx-bytes", "cfx-executor", @@ -1376,7 +1433,7 @@ dependencies = [ name = "cfxcore" version = "2.0.2" dependencies = [ - "alloy-rpc-trace-types", + "alloy-rpc-types-trace", "anyhow", "async-oneshot", "async-trait", @@ -1675,7 +1732,7 @@ dependencies = [ name = "client" version = "2.0.2" dependencies = [ - "alloy-rpc-trace-types", + "alloy-rpc-types-trace", "anyhow", "app_dirs", "bigdecimal", @@ -3750,6 +3807,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -4499,7 +4562,6 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2 0.10.6", - "signature 2.2.0", ] [[package]] @@ -6701,9 +6763,9 @@ dependencies = [ [[package]] name = "revm" -version = "7.2.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fd3ed4b62dc61c647552d8b781811ae25ec74d23309055077e4dfb392444d2" +checksum = "72a454c1c650b2b2e23f0c461af09e6c31e1d15e1cbebe905a701c46b8a50afc" dependencies = [ "auto_impl", "cfg-if 1.0.0", @@ -6716,9 +6778,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "3.4.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0a1818f8c876b0d71a0714217c34da7df8a42c0462750768779d55680e4554" +checksum = "d322f2730cd300e99d271a1704a2dfb8973d832428f5aa282aaa40e2473b5eec" dependencies = [ "revm-primitives", "serde", @@ -6726,9 +6788,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "5.1.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9645a70f1df1e5bd7fa8718b9ba486fac9c3f0467aa6b58e7f590d5f6fd0f7" +checksum = "931f692f3f4fc72ec39d5d270f8e9d208c4a6008de7590ee96cf948e3b6d3f8d" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -6743,9 +6805,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323ad597cf75ac9cb1d161be29fcc3562426f0278a1d04741697fca556e1ceea" +checksum = "cbbc9640790cebcb731289afb7a7d96d16ad94afeb64b5d0b66443bd151e79d6" dependencies = [ "alloy-primitives", "auto_impl", @@ -7809,9 +7871,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.6.4" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3d0961cd53c23ea94eeec56ba940f636f6394788976e9f16ca5ee0aca7464a" +checksum = "70aba06097b6eda3c15f6eebab8a6339e121475bcf08bbe6758807e716c372a1" dependencies = [ "paste", "proc-macro2 1.0.71", diff --git a/Cargo.toml b/Cargo.toml index 72f6b93bb1..9e023e7031 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,5 +91,6 @@ debug-assertions = true serde_derive = "1.0" serde = { version = "1.0", features = ["derive", "alloc"] } serde_json = "1.0" -alloy-primitives = "0.6" -alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "410850b" } \ No newline at end of file + +alloy-primitives = "0.7.1" +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "4e22b9e" } \ No newline at end of file diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index 6e279d9aa9..09ac70a66a 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -137,7 +137,7 @@ impl-trait-for-tuples = "^0.2" impl-tools = "^0.10" treap-map = {path = "../../util/treap-map" } cfx-packing-pool = { path = "../packing-pool" } -alloy-rpc-trace-types.workspace = true +alloy-rpc-types-trace.workspace = true [dev-dependencies] diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 0a9673140a..85a937077b 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -79,7 +79,7 @@ use cfx_executor::{ }; use cfx_vm_types::{Env, Spec}; -use alloy_rpc_trace_types::geth::{ +use alloy_rpc_types_trace::geth::{ GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, GethTrace, }; diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index a1129063ac..fad1323870 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -45,7 +45,7 @@ use cfx_execute_helper::{ }; use cfx_executor::{executive::ExecutionOutcome, state::State}; -use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethTrace}; +use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethTrace}; use cfx_internal_common::ChainIdParams; use cfx_parameters::{ consensus::*, diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 7907192065..5f480df976 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -54,6 +54,6 @@ pow-types = {path = "../core/src/pos/types/pow-types" } typemap = "0.3" alloy-primitives.workspace = true -alloy-rpc-trace-types.workspace = true -alloy-sol-types = "0.6" -revm = { version = "7.2", default-features = false, features = ["std"] } # TODO need to be removed \ No newline at end of file +alloy-sol-types = "0.7.1" +revm = { version = "8.0", default-features = false, features = ["std"] } # TODO need to be removed +alloy-rpc-types-trace.workspace = true \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs index b13ef50f99..f9612d5955 100644 --- a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs +++ b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs @@ -23,7 +23,7 @@ use super::geth_tracer::GethTraceKey; use alloy_primitives::{hex, Selector}; -use alloy_rpc_trace_types::geth::{FourByteFrame, GethTrace}; +use alloy_rpc_types_trace::geth::{FourByteFrame, GethTrace}; use cfx_executor::observer::{ CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, OpcodeTracer, StorageTracer, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs index 777fb96ae1..b7987db091 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -5,7 +5,7 @@ use crate::observer::geth_tracer::{ TracingInspectorConfig, }; use alloy_primitives::{Address, Bytes, B256, U256}; -use alloy_rpc_trace_types::geth::{ +use alloy_rpc_types_trace::geth::{ AccountChangeKind, AccountState, CallConfig, CallFrame, DefaultFrame, DiffMode, GethDefaultTracingOptions, PreStateConfig, PreStateFrame, PreStateMode, StructLog, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs index ada91c736c..7803ad0425 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs @@ -1,4 +1,4 @@ -use alloy_rpc_trace_types::{ +use alloy_rpc_types_trace::{ geth::{CallConfig, GethDefaultTracingOptions, PreStateConfig}, parity::TraceType, }; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 6531f63b5f..8125d55213 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -35,7 +35,7 @@ use cfx_executor::{ use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; -use alloy_rpc_trace_types::geth::{ +use alloy_rpc_types_trace::geth::{ CallFrame, DefaultFrame, GethTrace, NoopFrame, }; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs index 67df8c9fb6..71c0c06a58 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs @@ -4,7 +4,7 @@ use crate::observer::geth_tracer::{ config::TraceStyle, utils, utils::convert_memory, }; use alloy_primitives::{Address, Bytes, LogData, U256, U64}; -use alloy_rpc_trace_types::{ +use alloy_rpc_types_trace::{ geth::{CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog}, parity::{ Action, ActionType, CallAction, CallOutput, CallType, CreateAction, diff --git a/crates/cfxcore/execute-helper/src/tx_outcome.rs b/crates/cfxcore/execute-helper/src/tx_outcome.rs index 6a0618e4b2..942f194a7e 100644 --- a/crates/cfxcore/execute-helper/src/tx_outcome.rs +++ b/crates/cfxcore/execute-helper/src/tx_outcome.rs @@ -6,7 +6,7 @@ use pow_types::StakingEvent; use primitives::Receipt; use crate::observer::geth_tracer::GethTraceKey; -use alloy_rpc_trace_types::geth::GethTrace; +use alloy_rpc_types_trace::geth::GethTrace; use super::{ observer::exec_tracer::{ExecTrace, ExecTraceKey}, diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 092099afdd..77a48dbcaa 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -100,7 +100,7 @@ static_assertions = "1.1.0" parity-version = {path = "../util/version" } solidity-abi = {path= "../util/solidity-abi" } bls-signatures = {git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore"]} -alloy-rpc-trace-types.workspace = true +alloy-rpc-types-trace.workspace = true [dev-dependencies] criterion = "0.3" diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 556a9dda2e..eb3f9fd88d 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -1,5 +1,5 @@ use crate::rpc::traits::eth_space::debug::Debug; -use alloy_rpc_trace_types::geth::{ +use alloy_rpc_types_trace::geth::{ GethDebugBuiltInTracerType, GethDebugTracerType::{BuiltInTracer, JsTracer}, GethDebugTracingOptions, GethTrace, NoopFrame, diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index c4364fc8c0..98f544b0bc 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -1,4 +1,4 @@ -use alloy_rpc_trace_types::geth::{GethDebugTracingOptions, GethTrace}; +use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethTrace}; use cfx_types::H256; use jsonrpc_core::Result as JsonRpcResult; use jsonrpc_derive::rpc; From 880850b6731d873428d3db2c5e159ddcdf4e4a3d Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Thu, 25 Apr 2024 15:27:18 +0800 Subject: [PATCH 006/137] Add eip1559. --- crates/client/src/rpc/impls/cfx.rs | 5 ++ crates/primitives/src/transaction.rs | 111 +++++++++++++++++++++++---- 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 60306f7ef4..c309809e3e 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -1076,6 +1076,11 @@ impl RpcImpl { )) if tx_data_len > 0 => { unsigned.data = vec![0; tx_data_len]; } + Transaction::Ethereum(EthereumTransaction::Eip1559( + ref mut unsigned, + )) if tx_data_len > 0 => { + unsigned.data = vec![0; tx_data_len]; + } Transaction::Ethereum(EthereumTransaction::Eip2930( ref mut unsigned, )) if tx_data_len > 0 => { diff --git a/crates/primitives/src/transaction.rs b/crates/primitives/src/transaction.rs index bdfd9809f2..3d81f7b7b9 100644 --- a/crates/primitives/src/transaction.rs +++ b/crates/primitives/src/transaction.rs @@ -424,35 +424,87 @@ impl Encodable for Eip155Transaction { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Eip2930Transaction { - /// Nonce. + pub chain_id: u32, pub nonce: U256, - /// Gas price. pub gas_price: U256, - /// Gas paid up front for transaction execution. pub gas: U256, - /// Action, can be either call or contract create. pub action: Action, - /// Transferred value. pub value: U256, - /// The chain id of the transaction - pub chain_id: Option, - /// Transaction data. pub data: Bytes, + pub access_list: AccessList, +} + +impl Encodable for Eip2930Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(8); + s.append(&self.chain_id); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append_list(&self.access_list); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Eip1559Transaction { + pub chain_id: u32, + pub nonce: U256, pub max_priority_fee_per_gas: U256, pub max_fee_per_gas: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub data: Bytes, pub access_list: AccessList, } -impl Encodable for Eip2930Transaction { - fn rlp_append(&self, s: &mut RlpStream) { todo!() } +impl Encodable for Eip1559Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(9); + s.append(&self.chain_id); + s.append(&self.nonce); + s.append(&self.max_priority_fee_per_gas); + s.append(&self.max_fee_per_gas); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append_list(&self.access_list); + } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct AccessList {} +pub struct AccessListItem { + pub address: Address, + pub storage_keys: Vec, +} + +impl Encodable for AccessListItem { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + s.append(&self.address); + s.append_list(&self.storage_keys); + } +} + +impl Decodable for AccessListItem { + fn decode(rlp: &Rlp) -> Result { + Ok(Self { + address: rlp.val_at(0)?, + storage_keys: rlp.list_at(1)?, + }) + } +} + +pub type AccessList = Vec; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum EthereumTransaction { Eip155(Eip155Transaction), + Eip1559(Eip1559Transaction), Eip2930(Eip2930Transaction), } @@ -493,6 +545,7 @@ macro_rules! eth_access_common_ref { match self { EthereumTransaction::Eip155(tx) => &tx.$field, EthereumTransaction::Eip2930(tx) => &tx.$field, + EthereumTransaction::Eip1559(tx) => &tx.$field, } } }; @@ -501,8 +554,6 @@ macro_rules! eth_access_common_ref { impl EthereumTransaction { eth_access_common_ref!(gas, U256); - eth_access_common_ref!(gas_price, U256); - eth_access_common_ref!(data, Bytes); eth_access_common_ref!(nonce, U256); @@ -511,7 +562,21 @@ impl EthereumTransaction { eth_access_common_ref!(value, U256); - eth_access_common_ref!(chain_id, Option); + pub fn gas_price(&self) -> &U256 { + match self { + EthereumTransaction::Eip155(tx) => &tx.gas_price, + EthereumTransaction::Eip1559(tx) => &tx.max_fee_per_gas, + EthereumTransaction::Eip2930(tx) => &tx.gas_price, + } + } + + pub fn chain_id(&self) -> Option { + match self { + EthereumTransaction::Eip155(tx) => tx.chain_id, + EthereumTransaction::Eip1559(tx) => Some(tx.chain_id), + EthereumTransaction::Eip2930(tx) => Some(tx.chain_id), + } + } } #[allow(unused)] @@ -558,11 +623,23 @@ impl Transaction { Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { &mut tx.nonce } + Transaction::Ethereum(EthereumTransaction::Eip1559(tx)) => { + &mut tx.nonce + } Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { &mut tx.nonce } } } + + pub fn type_id(&self) -> Option { + match self { + Transaction::Native(_) + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => None, + Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => Some(1), + Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => Some(2), + } + } } impl Transaction { @@ -578,6 +655,9 @@ impl Transaction { Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { s.append(tx); } + Transaction::Ethereum(EthereumTransaction::Eip1559(tx)) => { + s.append(tx); + } Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { s.append(tx); } @@ -676,6 +756,9 @@ impl Encodable for TransactionWithSignatureSerializePart { Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => { todo!() } + Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => { + todo!() + } } } } From 6ef83e120ccdc61f017669e10ae1358b0d6fa07c Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 26 Apr 2024 16:53:12 +0800 Subject: [PATCH 007/137] initial connected version --- .../consensus_inner/consensus_executor.rs | 73 ++++----- crates/cfxcore/execute-helper/Cargo.toml | 2 +- .../execute-helper/src/observer/fourbyte.rs | 12 +- .../src/observer/geth_tracer/builder/geth.rs | 1 - .../src/observer/geth_tracer/mod.rs | 143 ++++++++++++++---- .../src/observer/geth_tracer/types.rs | 2 +- .../execute-helper/src/observer/mod.rs | 13 +- crates/cfxcore/executor/src/machine/mod.rs | 4 + crates/client/src/rpc/impls/eth/debug.rs | 19 +-- 9 files changed, 173 insertions(+), 96 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 85a937077b..c6cb420686 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -2096,12 +2096,14 @@ impl ConsensusExecutionHandler { None => true, // trace all tx }; - let (observer, supported) = if need_trace { + // choose the observer according to the options + let observer = if need_trace { + let tx_gas_limit = transaction.gas_limit().as_u64(); match &opts.tracer { Some(t) => match t { GethDebugTracerType::BuiltInTracer(bt) => match bt { GethDebugBuiltInTracerType::FourByteTracer => { - (Observer::with_no_tracing(), false) + Observer::with_no_tracing() } GethDebugBuiltInTracerType::CallTracer => { let call_config = opts @@ -2109,37 +2111,49 @@ impl ConsensusExecutionHandler { .clone() .into_call_config() .map_err(|e| format!("{e}"))?; - (Observer::geth_tracer( + Observer::geth_tracer( TracingInspectorConfig::from_geth_call_config(&call_config), - ), - true, - ) + tx_gas_limit, + Arc::clone(&self.machine), + ) } GethDebugBuiltInTracerType::PreStateTracer => { - (Observer::with_no_tracing(), false) + let pre_state_config = opts + .tracer_config + .clone() + .into_pre_state_config() + .map_err(|e| format!("{e}"))?; + Observer::geth_tracer( + TracingInspectorConfig::from_geth_prestate_config(&pre_state_config), + tx_gas_limit, + Arc::clone(&self.machine), + ) } GethDebugBuiltInTracerType::NoopTracer => { - (Observer::with_no_tracing(), false) + Observer::with_no_tracing() } GethDebugBuiltInTracerType::MuxTracer => { - (Observer::with_no_tracing(), false) + Observer::with_no_tracing() } }, GethDebugTracerType::JsTracer(_) => { - (Observer::with_no_tracing(), false) + Observer::with_no_tracing() } }, - None => ( + None => + // default is opcode tracer + { Observer::geth_tracer( TracingInspectorConfig::from_geth_config( &opts.config, ), - ), - true, - ), + tx_gas_limit, + Arc::clone(&self.machine), + ) + } } } else { - (Observer::with_no_tracing(), false) + Observer::with_no_tracing() }; let options = TransactOptions { @@ -2149,42 +2163,19 @@ impl ConsensusExecutionHandler { let execution_outcome = ExecutiveContext::new(state, &env, machine, &spec) .transact(transaction, options)?; + let r = make_process_tx_outcome( execution_outcome, &mut env.accumulated_gas_used, transaction.hash, ); - if need_trace && supported { - if let Some(t) = &opts.tracer { - if t == &GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::CallTracer, - ) { - if r.geth_trace.is_some() { - traces.push(( - transaction.hash(), - r.geth_trace.unwrap(), - )); - } - } - } else { - if r.geth_trace.is_some() { - traces.push(( - transaction.hash(), - r.geth_trace.unwrap(), - )); - } - } + if need_trace && r.geth_trace.is_some() { + traces.push((transaction.hash(), r.geth_trace.unwrap())); } } } - // dummy trace for the block - // traces.push(( - // H256::default(), - // GethTrace::NoopTracer(NoopFrame::default()), - // )); - Ok(traces) } diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 5f480df976..2a4fb40ee4 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -55,5 +55,5 @@ typemap = "0.3" alloy-primitives.workspace = true alloy-sol-types = "0.7.1" -revm = { version = "8.0", default-features = false, features = ["std"] } # TODO need to be removed +revm = { version = "8.0", default-features = false, features = ["std"] } alloy-rpc-types-trace.workspace = true \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs index f9612d5955..c1b4920ff9 100644 --- a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs +++ b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs @@ -47,7 +47,9 @@ impl FourByteInspector { &self.inner } - pub fn drain(self) -> FourByteFrame { FourByteFrame::from(self) } + pub fn drain(self) -> GethTrace { + GethTrace::FourByteTracer(FourByteFrame::from(self)) + } } impl CallTracer for FourByteInspector { @@ -70,16 +72,10 @@ impl OpcodeTracer for FourByteInspector {} impl DrainTrace for FourByteInspector { fn drain_trace(self, map: &mut typemap::ShareDebugMap) { - map.insert::(GethTrace::FourByteTracer(self.drain())); + map.insert::(self.drain()); } } -// impl AsTracer for FourByteInspector { -// fn as_tracer<'a>(&'a mut self) -> Box { -// Box::new(self) -// } -// } - impl From for FourByteFrame { fn from(value: FourByteInspector) -> Self { Self( diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs index b7987db091..0802c010ce 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -233,7 +233,6 @@ impl GethTraceBuilder { /// * `state` - The state post-transaction execution. /// * `diff_mode` - if prestate is in diff or prestate mode. /// * `db` - The database to fetch state pre-transaction execution. - /// TODO(pana): adapt the DatabaseRef and ResultAndState's State pub fn geth_prestate_traces( &self, ResultAndState { state, .. }: &ResultAndState, prestate_config: PreStateConfig, db: DB, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 8125d55213..5347dc5bb8 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -9,7 +9,7 @@ mod utils; pub use arena::CallTraceArena; pub use builder::geth::{self, GethTraceBuilder}; -use cfx_types::H160; +use cfx_types::{Space, H160}; pub use config::{StackSnapshotType, TracingInspectorConfig}; use arena::PushTraceKind; @@ -23,9 +23,14 @@ use utils::{ }; use alloy_primitives::{Address, Bytes, LogData, U256}; -use revm::interpreter::{Gas, InstructionResult, InterpreterResult, OpCode}; +use revm::{ + db::InMemoryDB, + interpreter::{Gas, InstructionResult, InterpreterResult, OpCode}, + primitives::{ExecutionResult, ResultAndState, State}, +}; use cfx_executor::{ + machine::Machine, observer::{ CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, OpcodeTracer, StorageTracer, @@ -36,10 +41,13 @@ use cfx_executor::{ use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; use alloy_rpc_types_trace::geth::{ - CallFrame, DefaultFrame, GethTrace, NoopFrame, + CallConfig, CallFrame, DefaultFrame, GethDebugBuiltInTracerType, + GethDefaultTracingOptions, GethTrace, NoopFrame, PreStateConfig, }; -#[derive(Clone, Debug)] +use std::{convert, sync::Arc}; + +#[derive(Clone)] pub struct TracingInspector { /// Configures what and how the inspector records traces. config: TracingInspectorConfig, @@ -57,11 +65,18 @@ pub struct TracingInspector { depth: usize, // gas stack, used to trace gas_spent in call_result/create_result gas_stack: Vec, + // + tx_gas_limit: u64, + // + machine: Arc, } impl TracingInspector { /// Returns a new instance for the given config - pub fn new(config: TracingInspectorConfig) -> Self { + pub fn new( + config: TracingInspectorConfig, tx_gas_limit: u64, + machine: Arc, + ) -> Self { Self { config, traces: Default::default(), @@ -71,6 +86,8 @@ impl TracingInspector { gas_inspector: Default::default(), depth: 0, gas_stack: vec![], + tx_gas_limit, + machine, } } @@ -91,6 +108,8 @@ impl TracingInspector { config: _, depth, gas_stack, + tx_gas_limit, + machine, } = self; traces.clear(); trace_stack.clear(); @@ -99,6 +118,7 @@ impl TracingInspector { last_call_return_data.take(); *gas_inspector = Default::default(); *depth = 0; + *tx_gas_limit = 0; } /// Resets the inspector to it's initial state of [Self::new]. @@ -162,10 +182,14 @@ impl TracingInspector { /// Returns true if the `to` address is a precompile contract and the value /// is zero. #[inline] - fn is_precompile_call(&self, _to: &Address, value: U256) -> bool { - // TODO(pana) check to is in precompile list - // precompile list is in Machine object - if false { + fn is_precompile_call(&self, to: &H160, value: U256, space: Space) -> bool { + // TODO: check according block height + let is_precompile = match space { + Space::Native => self.machine.builtins().contains_key(&to), + Space::Ethereum => self.machine.builtins_evm().contains_key(&to), + }; + + if is_precompile { // only if this is _not_ the root call return self.is_deep() && value.is_zero(); } @@ -399,7 +423,7 @@ impl TracingInspector { // The gas cost is the difference between the recorded gas remaining at // the start of the step the remaining gas here, at the end of // the step. - // todo: Figure out why this can overflow. https://github.com/paradigmxyz/evm-inspectors/pull/38 + // todo(evm-inspector): Figure out why this can overflow. https://github.com/paradigmxyz/evm-inspectors/pull/38 step.gas_cost = step .gas_remaining .saturating_sub(self.gas_inspector.gas_remaining()); @@ -411,18 +435,75 @@ impl TracingInspector { pub struct GethTracer { inner: TracingInspector, + tracer_type: Option, } impl GethTracer { - pub fn new(config: TracingInspectorConfig) -> Self { + pub fn new( + config: TracingInspectorConfig, tx_gas_limit: u64, + machine: Arc, + ) -> Self { Self { - inner: TracingInspector::new(config), + inner: TracingInspector::new(config, tx_gas_limit, machine), + tracer_type: None, } } pub fn drain(self) -> GethTrace { - // TODO return the right kind of frame according to the config - GethTrace::NoopTracer(NoopFrame::default()) + let trace = match self.tracer_type { + Some(t) => match t { + GethDebugBuiltInTracerType::FourByteTracer => todo!(), + GethDebugBuiltInTracerType::CallTracer => { + // TODO + let gas_used = 1000000u64; + let opts = CallConfig::default(); + let frame = self + .inner + .into_geth_builder() + .geth_call_traces(opts, gas_used); + GethTrace::CallTracer(frame) + } + GethDebugBuiltInTracerType::PreStateTracer => { + // TODO replace the empty state with a real state + let gas_used = 1000000u64; + let opts = PreStateConfig::default(); + let result = ResultAndState { + result: ExecutionResult::Revert { + gas_used, + output: Bytes::default(), + }, + state: State::default(), + }; + let db = InMemoryDB::default(); + let frame = self + .inner + .into_geth_builder() + .geth_prestate_traces(&result, opts, db) + .unwrap(); + GethTrace::PreStateTracer(frame) + } + GethDebugBuiltInTracerType::NoopTracer => { + GethTrace::NoopTracer(NoopFrame::default()) + } + GethDebugBuiltInTracerType::MuxTracer => { + GethTrace::NoopTracer(NoopFrame::default()) + } // not supported + }, + None => { + // TODO + let gas_used = 1000000u64; + let return_value = Bytes::default(); + let opts = GethDefaultTracingOptions::default(); + let frame = self.inner.into_geth_builder().geth_traces( + gas_used, + return_value, + opts, + ); + GethTrace::Default(frame) + } + }; + + trace } } @@ -451,32 +532,31 @@ impl CallTracer for GethTracer { // determine correct `from` and `to` based on the call scheme let (from, to) = match params.call_type { - CallType::DelegateCall | CallType::CallCode => ( - convert_h160(params.address), - convert_h160(params.code_address), - ), - _ => (convert_h160(params.sender), convert_h160(params.address)), + CallType::DelegateCall | CallType::CallCode => { + (params.address, params.code_address) + } + _ => (params.sender, params.address), }; - let value = if matches!(params.call_type, CallType::DelegateCall) { + let value = if matches!(params.call_type, CallType::DelegateCall) + && self.inner.active_trace().is_some() + { // for delegate calls we need to use the value of the top trace - if let Some(parent) = self.inner.active_trace() { - parent.trace.value - } else { - convert_u256(params.value.value()) - } + let parent = self.inner.active_trace().unwrap(); + parent.trace.value } else { convert_u256(params.value.value()) }; // if calls to precompiles should be excluded, check whether this is a // call to a precompile - let maybe_precompile = self - .inner - .config - .exclude_precompile_calls - .then(|| self.inner.is_precompile_call(&to, value)); + let maybe_precompile = + self.inner.config.exclude_precompile_calls.then(|| { + self.inner.is_precompile_call(&to, value, params.space) + }); + let to = convert_h160(to); + let from = convert_h160(from); self.inner.start_trace_on_call( to, params.data.clone().unwrap_or_default().into(), @@ -485,8 +565,7 @@ impl CallTracer for GethTracer { from, params.gas.as_u64(), maybe_precompile, - params.gas.as_u64(), /* TODO should use tx gas_limit not frame - * gas_limit */ + self.inner.tx_gas_limit, self.inner.depth, ); } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs index 71c0c06a58..787600416c 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs @@ -467,7 +467,7 @@ impl From for CallKind { impl From for CallKind { fn from(ct: CfxCallType) -> Self { match ct { - CfxCallType::None => Self::Create, // TODO(pana) check this + CfxCallType::None => Self::CallCode, /* TODO(pana) check this is appropriate, or add a None variant to CallKind */ CfxCallType::Call => Self::Call, CfxCallType::CallCode => Self::CallCode, CfxCallType::DelegateCall => Self::DelegateCall, diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index 64f91e7712..aa8c5d2591 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -7,8 +7,12 @@ mod utils; use exec_tracer::ExecTracer; use gasman::GasMan; -use cfx_executor::executive_observer::{AsTracer, DrainTrace, TracerTrait}; +use cfx_executor::{ + executive_observer::{AsTracer, DrainTrace, TracerTrait}, + machine::Machine, +}; use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; +use std::sync::Arc; use self::geth_tracer::{GethTracer, TracingInspectorConfig}; @@ -44,11 +48,14 @@ impl Observer { } } - pub fn geth_tracer(config: TracingInspectorConfig) -> Self { + pub fn geth_tracer( + config: TracingInspectorConfig, tx_gas_limit: u64, + machine: Arc, + ) -> Self { Observer { tracer: None, gas_man: None, - geth_tracer: Some(GethTracer::new(config)), + geth_tracer: Some(GethTracer::new(config, tx_gas_limit, machine)), } } } diff --git a/crates/cfxcore/executor/src/machine/mod.rs b/crates/cfxcore/executor/src/machine/mod.rs index 1bdfe5c746..eed4aa555c 100644 --- a/crates/cfxcore/executor/src/machine/mod.rs +++ b/crates/cfxcore/executor/src/machine/mod.rs @@ -66,6 +66,10 @@ impl Machine { /// Builtin-contracts for the core space pub fn builtins(&self) -> &BTreeMap { &*self.builtins } + pub fn builtins_evm(&self) -> &BTreeMap { + &*self.builtins_evm + } + /// Builtin-contracts for the chain.. pub fn internal_contracts(&self) -> &InternalContractMap { &*self.internal_contracts diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index eb3f9fd88d..2eb129c058 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -1,4 +1,6 @@ -use crate::rpc::traits::eth_space::debug::Debug; +use crate::rpc::{ + error_codes::invalid_params_msg, traits::eth_space::debug::Debug, +}; use alloy_rpc_types_trace::geth::{ GethDebugBuiltInTracerType, GethDebugTracerType::{BuiltInTracer, JsTracer}, @@ -7,7 +9,6 @@ use alloy_rpc_types_trace::geth::{ use cfx_types::H256; use cfxcore::{ConsensusGraph, SharedConsensusGraph}; use jsonrpc_core::{Error as RpcError, Result as JsonRpcResult}; -// use primitives::EpochNumber; pub struct GethDebugHandler { consensus: SharedConsensusGraph, @@ -44,9 +45,7 @@ impl Debug for GethDebugHandler { return Err(RpcError::invalid_params("not supported")) } GethDebugBuiltInTracerType::CallTracer => (), - GethDebugBuiltInTracerType::PreStateTracer => { - return Err(RpcError::invalid_params("not supported")) - } + GethDebugBuiltInTracerType::PreStateTracer => (), GethDebugBuiltInTracerType::NoopTracer => { return Ok(GethTrace::NoopTracer(NoopFrame::default())) } @@ -64,24 +63,26 @@ impl Debug for GethDebugHandler { .consensus .get_data_manager() .transaction_index_by_hash(&hash, false /* update_cache */) - .ok_or(RpcError::invalid_params("invalid tx hash"))?; + .ok_or(invalid_params_msg("invalid tx hash"))?; let epoch_num = self .consensus .get_block_epoch_number(&tx_index.block_hash) - .ok_or(RpcError::invalid_params("invalid tx hash"))?; + .ok_or(invalid_params_msg("invalid tx hash"))?; let epoch_traces = self .consensus_graph() .collect_epoch_geth_trace(epoch_num, Some(hash), opts) - .map_err(|_e| RpcError::invalid_params("invalid tx hash"))?; + .map_err(|e| { + invalid_params_msg(&format!("invalid tx hash: {e}")) + })?; // filter by tx hash let trace = epoch_traces .into_iter() .find(|(tx_hash, _)| tx_hash == &hash) .map(|(_, trace)| trace) - .ok_or(RpcError::invalid_params("invalid tx hash")); + .ok_or(invalid_params_msg("trace generation failed")); trace } From c1eebf5e07346f8f2a46f39c9a39f8cedbafee75 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:11:22 +0800 Subject: [PATCH 008/137] Move transaction types to separate files. --- crates/cfxcore/core/src/genesis_block.rs | 2 +- .../src/transaction_pool/nonce_pool/mod.rs | 3 +- .../transaction_pool_inner.rs | 3 +- crates/cfxcore/core/src/verification.rs | 2 +- .../cfxcore/execute-helper/src/estimation.rs | 3 +- .../execute-helper/src/phantom_tx/mod.rs | 4 +- .../cfxcore/executor/src/executive/tests.rs | 5 +- crates/client/benches/benchmark.rs | 4 +- crates/client/src/rpc/impls/cfx.rs | 2 +- crates/client/src/rpc/impls/eth.rs | 7 +- crates/client/src/rpc/types/call_request.rs | 4 +- .../client/src/rpc/types/eth/transaction.rs | 4 +- crates/client/src/rpc/types/transaction.rs | 5 +- crates/primitives/src/lib.rs | 6 +- .../src/transaction/eth_transaction.rs | 242 +++++++++++++ .../{transaction.rs => transaction/mod.rs} | 317 +----------------- .../src/transaction/native_transaction.rs | 139 ++++++++ crates/transactiongen/src/lib.rs | 2 +- crates/util/treap-map/src/tests.rs | 5 +- 19 files changed, 428 insertions(+), 331 deletions(-) create mode 100644 crates/primitives/src/transaction/eth_transaction.rs rename crates/primitives/src/{transaction.rs => transaction/mod.rs} (71%) create mode 100644 crates/primitives/src/transaction/native_transaction.rs diff --git a/crates/cfxcore/core/src/genesis_block.rs b/crates/cfxcore/core/src/genesis_block.rs index 810cb590a2..4e8ec27031 100644 --- a/crates/cfxcore/core/src/genesis_block.rs +++ b/crates/cfxcore/core/src/genesis_block.rs @@ -49,7 +49,7 @@ use cfx_executor::{ }; use cfx_vm_types::{CreateContractAddress, Env}; use diem_types::account_address::AccountAddress; -use primitives::transaction::NativeTransaction; +use primitives::transaction::native_transaction::NativeTransaction; pub fn default(dev_or_test_mode: bool) -> HashMap { if !dev_or_test_mode { diff --git a/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs index 0233331dd8..187cd22f8c 100644 --- a/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs @@ -390,7 +390,8 @@ mod nonce_pool_test { use cfx_types::{Address, U128, U256}; use keylib::{Generator, KeyPair, Random}; use primitives::{ - Action, NativeTransaction, SignedTransaction, Transaction, + transaction::native_transaction::NativeTransaction, Action, + SignedTransaction, Transaction, }; use rand::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index f1952d7884..92b5002a3e 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -1330,7 +1330,8 @@ mod test_transaction_pool_inner { use cfx_types::{Address, AddressSpaceUtil, U256}; use keylib::{Generator, KeyPair, Random}; use primitives::{ - Action, NativeTransaction, SignedTransaction, Transaction, + transaction::native_transaction::NativeTransaction, Action, + SignedTransaction, Transaction, }; use std::sync::Arc; diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 587b242f3d..40b7c41eea 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -22,7 +22,7 @@ use cfx_types::{ use cfx_vm_types::Spec; use primitives::{ block::BlockHeight, - transaction::{NativeTransaction, TransactionError}, + transaction::{native_transaction::NativeTransaction, TransactionError}, Action, Block, BlockHeader, BlockReceipts, MerkleHash, Receipt, SignedTransaction, Transaction, TransactionWithSignature, }; diff --git a/crates/cfxcore/execute-helper/src/estimation.rs b/crates/cfxcore/execute-helper/src/estimation.rs index 8985ed0694..f3c56dc853 100644 --- a/crates/cfxcore/execute-helper/src/estimation.rs +++ b/crates/cfxcore/execute-helper/src/estimation.rs @@ -17,7 +17,8 @@ use cfx_types::{ }; use cfx_vm_types::{self as vm, Env, Spec}; use primitives::{ - transaction::Action, NativeTransaction, SignedTransaction, Transaction, + transaction::{native_transaction::NativeTransaction, Action}, + SignedTransaction, Transaction, }; use std::{ cmp::{max, min}, diff --git a/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs b/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs index a3cf93a0a8..4a799326f2 100644 --- a/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs +++ b/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs @@ -2,8 +2,8 @@ mod recover; use cfx_types::{Address, AddressSpaceUtil, Bloom, Space, U256}; use primitives::{ - Action, Eip155Transaction, LogEntry, Receipt, SignedTransaction, - TransactionStatus, + transaction::eth_transaction::Eip155Transaction, Action, LogEntry, Receipt, + SignedTransaction, TransactionStatus, }; pub use recover::{build_bloom_and_recover_phantom, recover_phantom}; diff --git a/crates/cfxcore/executor/src/executive/tests.rs b/crates/cfxcore/executor/src/executive/tests.rs index 0142977201..2d1bb0c513 100644 --- a/crates/cfxcore/executor/src/executive/tests.rs +++ b/crates/cfxcore/executor/src/executive/tests.rs @@ -29,8 +29,9 @@ use cfx_vm_types::{ }; use cfxkey::{Generator, Random}; use primitives::{ - storage::STORAGE_LAYOUT_REGULAR_V0, transaction::Action, EpochId, - NativeTransaction, Transaction, + storage::STORAGE_LAYOUT_REGULAR_V0, + transaction::{native_transaction::NativeTransaction, Action}, + EpochId, Transaction, }; use rustc_hex::FromHex; use std::{ diff --git a/crates/client/benches/benchmark.rs b/crates/client/benches/benchmark.rs index d03528efab..c546fed0ba 100644 --- a/crates/client/benches/benchmark.rs +++ b/crates/client/benches/benchmark.rs @@ -17,7 +17,9 @@ use cfxkey::{Generator, KeyPair, Random}; use client::{archive::ArchiveClient, configuration::Configuration}; use criterion::{criterion_group, criterion_main, Criterion}; use parking_lot::{Condvar, Mutex}; -use primitives::{Action, NativeTransaction, Transaction}; +use primitives::{ + transaction::native_transaction::NativeTransaction, Action, Transaction, +}; use std::{sync::Arc, time::Duration}; fn txexe_benchmark(c: &mut Criterion) { diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index c309809e3e..540204f38f 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -102,7 +102,7 @@ use cfxcore::{ consensus_parameters::DEFERRED_STATE_EPOCH_COUNT, }; use diem_types::account_address::AccountAddress; -use primitives::transaction::EthereumTransaction; +use primitives::transaction::eth_transaction::EthereumTransaction; use serde::Serialize; #[derive(Debug)] diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 91545a4cab..1bd7142970 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -41,9 +41,10 @@ use cfxcore::{ use clap::crate_version; use jsonrpc_core::{Error as RpcError, Result as RpcResult}; use primitives::{ - filter::LogFilter, receipt::EVM_SPACE_SUCCESS, Action, - BlockHashOrEpochNumber, Eip155Transaction, EpochNumber, SignedTransaction, - StorageKey, StorageValue, TransactionStatus, TransactionWithSignature, + filter::LogFilter, receipt::EVM_SPACE_SUCCESS, + transaction::eth_transaction::Eip155Transaction, Action, + BlockHashOrEpochNumber, EpochNumber, SignedTransaction, StorageKey, + StorageValue, TransactionStatus, TransactionWithSignature, }; use rlp::Rlp; use rustc_hex::ToHex; diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index a9f07397b2..6678608a6c 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -16,7 +16,9 @@ use cfxcore::rpc_errors::invalid_params_check; use cfxcore_accounts::AccountProvider; use cfxkey::Password; use primitives::{ - transaction::Action, NativeTransaction as PrimitiveTransaction, + transaction::{ + native_transaction::NativeTransaction as PrimitiveTransaction, Action, + }, SignedTransaction, Transaction, TransactionWithSignature, }; use std::{cmp::min, sync::Arc}; diff --git a/crates/client/src/rpc/types/eth/transaction.rs b/crates/client/src/rpc/types/eth/transaction.rs index 989e287994..a9b43c39fd 100644 --- a/crates/client/src/rpc/types/eth/transaction.rs +++ b/crates/client/src/rpc/types/eth/transaction.rs @@ -21,7 +21,9 @@ use crate::rpc::types::Bytes; use cfx_types::{H160, H256, H512, U256, U64}; use cfx_vm_types::{contract_address, CreateContractAddress}; -use primitives::{transaction::eip155_signature, Action, SignedTransaction}; +use primitives::{ + transaction::eth_transaction::eip155_signature, Action, SignedTransaction, +}; use rlp::Encodable; use serde::Serialize; diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index e7b084e5fc..7fdefc21f8 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -9,7 +9,10 @@ use cfx_addr::Network; use cfx_types::{Space, H256, U256, U64}; use cfxkey::Error; use primitives::{ - transaction::Action, Eip155Transaction, NativeTransaction, + transaction::{ + eth_transaction::Eip155Transaction, + native_transaction::NativeTransaction, Action, + }, SignedTransaction, Transaction as PrimitiveTransaction, TransactionIndex, TransactionWithSignature, TransactionWithSignatureSerializePart, }; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index b140404938..eeb7c88fe8 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -50,10 +50,12 @@ pub use crate::{ }, storage_key::*, transaction::{ - Action, Eip155Transaction, NativeTransaction, SignedTransaction, - Transaction, TransactionWithSignature, + Action, SignedTransaction, Transaction, TransactionWithSignature, TransactionWithSignatureSerializePart, TxPropagateId, }, transaction_index::TransactionIndex, zero::Zero, }; +pub use transaction::{ + eth_transaction::Eip155Transaction, native_transaction::NativeTransaction, +}; diff --git a/crates/primitives/src/transaction/eth_transaction.rs b/crates/primitives/src/transaction/eth_transaction.rs new file mode 100644 index 0000000000..85a64e856c --- /dev/null +++ b/crates/primitives/src/transaction/eth_transaction.rs @@ -0,0 +1,242 @@ +use crate::{ + transaction::AccessList, Action, SignedTransaction, Transaction, + TransactionWithSignature, TransactionWithSignatureSerializePart, +}; +use bytes::Bytes; +use cfx_types::{AddressWithSpace, H256, U256}; +use rlp::{Encodable, RlpStream}; +use serde_derive::{Deserialize, Serialize}; + +impl Eip155Transaction { + /// Fake sign phantom transactions. + // The signature is part of the hash input. This implementation + // ensures that phantom transactions whose fields are identical + // will have different hashes. + pub fn fake_sign_phantom( + self, from: AddressWithSpace, + ) -> SignedTransaction { + SignedTransaction { + transaction: TransactionWithSignature { + transaction: TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(self), + ), + // we use sender address for `r` and `s` so that phantom + // transactions with matching fields from different senders + // will have different hashes + r: U256::from(from.address.as_ref()), + s: U256::from(from.address.as_ref()), + v: 0, + }, + hash: H256::zero(), + rlp_size: None, + } + .compute_hash(), + sender: from.address, + public: None, + } + } + + /// Fake sign call requests in `eth_call`. + // `fake_sign_phantom` will use zero signature when the sender is the + // zero address, and that will fail basic signature verification. + pub fn fake_sign_rpc(self, from: AddressWithSpace) -> SignedTransaction { + SignedTransaction { + transaction: TransactionWithSignature { + transaction: TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(self), + ), + r: U256::one(), + s: U256::one(), + v: 0, + }, + hash: H256::zero(), + rlp_size: None, + } + .compute_hash(), + sender: from.address, + public: None, + } + } +} + +#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Eip155Transaction { + /// Nonce. + pub nonce: U256, + /// Gas price. + pub gas_price: U256, + /// Gas paid up front for transaction execution. + pub gas: U256, + /// Action, can be either call or contract create. + pub action: Action, + /// Transferred value. + pub value: U256, + /// The chain id of the transaction + pub chain_id: Option, + /// Transaction data. + pub data: Bytes, +} + +impl Encodable for Eip155Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + match self.chain_id { + Some(chain_id) => { + s.begin_list(9); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append(&chain_id); + s.append(&0u8); + s.append(&0u8); + } + None => { + s.begin_list(6); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Eip2930Transaction { + pub chain_id: u32, + pub nonce: U256, + pub gas_price: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub data: Bytes, + pub access_list: AccessList, +} + +impl Encodable for Eip2930Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(8); + s.append(&self.chain_id); + s.append(&self.nonce); + s.append(&self.gas_price); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append_list(&self.access_list); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Eip1559Transaction { + pub chain_id: u32, + pub nonce: U256, + pub max_priority_fee_per_gas: U256, + pub max_fee_per_gas: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub data: Bytes, + pub access_list: AccessList, +} + +impl Encodable for Eip1559Transaction { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(9); + s.append(&self.chain_id); + s.append(&self.nonce); + s.append(&self.max_priority_fee_per_gas); + s.append(&self.max_fee_per_gas); + s.append(&self.gas); + s.append(&self.action); + s.append(&self.value); + s.append(&self.data); + s.append_list(&self.access_list); + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum EthereumTransaction { + Eip155(Eip155Transaction), + Eip1559(Eip1559Transaction), + Eip2930(Eip2930Transaction), +} + +macro_rules! eth_access_common_ref { + ($field:ident, $ty:ty) => { + pub fn $field(&self) -> &$ty { + match self { + EthereumTransaction::Eip155(tx) => &tx.$field, + EthereumTransaction::Eip2930(tx) => &tx.$field, + EthereumTransaction::Eip1559(tx) => &tx.$field, + } + } + }; +} + +impl EthereumTransaction { + eth_access_common_ref!(gas, U256); + + eth_access_common_ref!(data, Bytes); + + eth_access_common_ref!(nonce, U256); + + eth_access_common_ref!(action, Action); + + eth_access_common_ref!(value, U256); + + pub fn gas_price(&self) -> &U256 { + match self { + EthereumTransaction::Eip155(tx) => &tx.gas_price, + EthereumTransaction::Eip1559(tx) => &tx.max_fee_per_gas, + EthereumTransaction::Eip2930(tx) => &tx.gas_price, + } + } + + pub fn chain_id(&self) -> Option { + match self { + EthereumTransaction::Eip155(tx) => tx.chain_id, + EthereumTransaction::Eip1559(tx) => Some(tx.chain_id), + EthereumTransaction::Eip2930(tx) => Some(tx.chain_id), + } + } +} + +/// Replay protection logic for v part of transaction's signature +pub mod eip155_signature { + /// Adds chain id into v + pub fn add_chain_replay_protection(v: u8, chain_id: Option) -> u64 { + v as u64 + + if let Some(n) = chain_id { + 35 + n * 2 + } else { + 27 + } + } + + /// Returns refined v + /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if + /// invalid. + pub fn extract_standard_v(v: u64) -> u8 { + match v { + v if v == 27 => 0, + v if v == 28 => 1, + v if v >= 35 => ((v - 1) % 2) as u8, + _ => 4, + } + } + + pub fn extract_chain_id_from_legacy_v(v: u64) -> Option { + if v >= 35 { + Some((v - 35) / 2 as u64) + } else { + None + } + } +} diff --git a/crates/primitives/src/transaction.rs b/crates/primitives/src/transaction/mod.rs similarity index 71% rename from crates/primitives/src/transaction.rs rename to crates/primitives/src/transaction/mod.rs index 3d81f7b7b9..2d6b026e76 100644 --- a/crates/primitives/src/transaction.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -2,17 +2,23 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ +pub mod eth_transaction; +pub mod native_transaction; + use crate::{bytes::Bytes, hash::keccak}; use cfx_types::{ Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H160, H256, U256, }; +use eth_transaction::{ + eip155_signature, Eip155Transaction, EthereumTransaction, +}; use keylib::{ self, public_to_address, recover, verify_public, Public, Secret, Signature, }; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use native_transaction::NativeTransaction; use rlp::{self, Decodable, DecoderError, Encodable, Rlp, RlpStream}; -use rlp_derive::{RlpDecodable, RlpEncodable}; use serde::{Deserialize, Serialize}; use std::{ error, fmt, @@ -214,268 +220,6 @@ impl Encodable for Action { } } -#[derive( - Default, - Debug, - Clone, - Eq, - PartialEq, - RlpEncodable, - RlpDecodable, - Serialize, - Deserialize, -)] -pub struct NativeTransaction { - /// Nonce. - pub nonce: U256, - /// Gas price. - pub gas_price: U256, - /// Gas paid up front for transaction execution. - pub gas: U256, - /// Action, can be either call or contract create. - pub action: Action, - /// Transferred value. - pub value: U256, - /// Maximum storage increasement in this execution. - pub storage_limit: u64, - /// The epoch height of the transaction. A transaction - /// can only be packed between the epochs of [epoch_height - - /// TRANSACTION_EPOCH_BOUND, epoch_height + TRANSACTION_EPOCH_BOUND] - pub epoch_height: u64, - /// The chain id of the transaction - pub chain_id: u32, - /// Transaction data. - pub data: Bytes, -} - -impl NativeTransaction { - /// Specify the sender; this won't survive the serialize/deserialize - /// process, but can be cloned. - pub fn fake_sign(self, from: AddressWithSpace) -> SignedTransaction { - SignedTransaction { - transaction: TransactionWithSignature { - transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Native(self), - r: U256::one(), - s: U256::one(), - v: 0, - }, - hash: H256::zero(), - rlp_size: None, - } - .compute_hash(), - sender: from.address, - public: None, - } - } -} - -/// Replay protection logic for v part of transaction's signature -pub mod eip155_signature { - /// Adds chain id into v - pub fn add_chain_replay_protection(v: u8, chain_id: Option) -> u64 { - v as u64 - + if let Some(n) = chain_id { - 35 + n * 2 - } else { - 27 - } - } - - /// Returns refined v - /// 0 if `v` would have been 27 under "Electrum" notation, 1 if 28 or 4 if - /// invalid. - pub fn extract_standard_v(v: u64) -> u8 { - match v { - v if v == 27 => 0, - v if v == 28 => 1, - v if v >= 35 => ((v - 1) % 2) as u8, - _ => 4, - } - } - - pub fn extract_chain_id_from_legacy_v(v: u64) -> Option { - if v >= 35 { - Some((v - 35) / 2 as u64) - } else { - None - } - } -} - -impl Eip155Transaction { - /// Fake sign phantom transactions. - // The signature is part of the hash input. This implementation - // ensures that phantom transactions whose fields are identical - // will have different hashes. - pub fn fake_sign_phantom( - self, from: AddressWithSpace, - ) -> SignedTransaction { - SignedTransaction { - transaction: TransactionWithSignature { - transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum( - EthereumTransaction::Eip155(self), - ), - // we use sender address for `r` and `s` so that phantom - // transactions with matching fields from different senders - // will have different hashes - r: U256::from(from.address.as_ref()), - s: U256::from(from.address.as_ref()), - v: 0, - }, - hash: H256::zero(), - rlp_size: None, - } - .compute_hash(), - sender: from.address, - public: None, - } - } - - /// Fake sign call requests in `eth_call`. - // `fake_sign_phantom` will use zero signature when the sender is the - // zero address, and that will fail basic signature verification. - pub fn fake_sign_rpc(self, from: AddressWithSpace) -> SignedTransaction { - SignedTransaction { - transaction: TransactionWithSignature { - transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum( - EthereumTransaction::Eip155(self), - ), - r: U256::one(), - s: U256::one(), - v: 0, - }, - hash: H256::zero(), - rlp_size: None, - } - .compute_hash(), - sender: from.address, - public: None, - } - } -} - -#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct Eip155Transaction { - /// Nonce. - pub nonce: U256, - /// Gas price. - pub gas_price: U256, - /// Gas paid up front for transaction execution. - pub gas: U256, - /// Action, can be either call or contract create. - pub action: Action, - /// Transferred value. - pub value: U256, - /// The chain id of the transaction - pub chain_id: Option, - /// Transaction data. - pub data: Bytes, -} - -impl Encodable for Eip155Transaction { - fn rlp_append(&self, s: &mut RlpStream) { - match self.chain_id { - Some(chain_id) => { - s.begin_list(9); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - s.append(&chain_id); - s.append(&0u8); - s.append(&0u8); - } - None => { - s.begin_list(6); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - } - } - } -} - -// impl Decodable for Eip155Transaction { -// fn decode(rlp: &Rlp) -> Result { -// if !(rlp.at(7)?.is_empty() && rlp.at(8)?.is_empty()) { -// return Err(DecoderError::Custom( -// "The last two items should be empty", -// )); -// } -// Ok(Self { -// nonce: rlp.val_at(0)?, -// gas_price: rlp.val_at(1)?, -// gas: rlp.val_at(2)?, -// action: rlp.val_at(3)?, -// value: rlp.val_at(4)?, -// chain_id: rlp.val_at(5)?, -// data: rlp.val_at(6)?, -// }) -// } -// } - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Eip2930Transaction { - pub chain_id: u32, - pub nonce: U256, - pub gas_price: U256, - pub gas: U256, - pub action: Action, - pub value: U256, - pub data: Bytes, - pub access_list: AccessList, -} - -impl Encodable for Eip2930Transaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(8); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.gas_price); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - s.append_list(&self.access_list); - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct Eip1559Transaction { - pub chain_id: u32, - pub nonce: U256, - pub max_priority_fee_per_gas: U256, - pub max_fee_per_gas: U256, - pub gas: U256, - pub action: Action, - pub value: U256, - pub data: Bytes, - pub access_list: AccessList, -} - -impl Encodable for Eip1559Transaction { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(9); - s.append(&self.chain_id); - s.append(&self.nonce); - s.append(&self.max_priority_fee_per_gas); - s.append(&self.max_fee_per_gas); - s.append(&self.gas); - s.append(&self.action); - s.append(&self.value); - s.append(&self.data); - s.append_list(&self.access_list); - } -} - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AccessListItem { pub address: Address, @@ -501,13 +245,6 @@ impl Decodable for AccessListItem { pub type AccessList = Vec; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum EthereumTransaction { - Eip155(Eip155Transaction), - Eip1559(Eip1559Transaction), - Eip2930(Eip2930Transaction), -} - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Transaction { Native(NativeTransaction), @@ -539,46 +276,6 @@ macro_rules! access_common_ref { }; } -macro_rules! eth_access_common_ref { - ($field:ident, $ty:ty) => { - pub fn $field(&self) -> &$ty { - match self { - EthereumTransaction::Eip155(tx) => &tx.$field, - EthereumTransaction::Eip2930(tx) => &tx.$field, - EthereumTransaction::Eip1559(tx) => &tx.$field, - } - } - }; -} - -impl EthereumTransaction { - eth_access_common_ref!(gas, U256); - - eth_access_common_ref!(data, Bytes); - - eth_access_common_ref!(nonce, U256); - - eth_access_common_ref!(action, Action); - - eth_access_common_ref!(value, U256); - - pub fn gas_price(&self) -> &U256 { - match self { - EthereumTransaction::Eip155(tx) => &tx.gas_price, - EthereumTransaction::Eip1559(tx) => &tx.max_fee_per_gas, - EthereumTransaction::Eip2930(tx) => &tx.gas_price, - } - } - - pub fn chain_id(&self) -> Option { - match self { - EthereumTransaction::Eip155(tx) => tx.chain_id, - EthereumTransaction::Eip1559(tx) => Some(tx.chain_id), - EthereumTransaction::Eip2930(tx) => Some(tx.chain_id), - } - } -} - #[allow(unused)] macro_rules! access_common { ($field:ident, $ty:ident) => { diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs new file mode 100644 index 0000000000..e8a42caf66 --- /dev/null +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -0,0 +1,139 @@ +use crate::{ + transaction::AccessList, Action, SignedTransaction, Transaction, + TransactionWithSignature, TransactionWithSignatureSerializePart, +}; +use bytes::Bytes; +use cfx_types::{AddressWithSpace, H256, U256}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use rlp_derive::{RlpDecodable, RlpEncodable}; +use serde_derive::{Deserialize, Serialize}; + +#[derive( + Default, + Debug, + Clone, + Eq, + PartialEq, + RlpEncodable, + RlpDecodable, + Serialize, + Deserialize, +)] +pub struct NativeTransaction { + /// Nonce. + pub nonce: U256, + /// Gas price. + pub gas_price: U256, + /// Gas paid up front for transaction execution. + pub gas: U256, + /// Action, can be either call or contract create. + pub action: Action, + /// Transferred value. + pub value: U256, + /// Maximum storage increasement in this execution. + pub storage_limit: u64, + /// The epoch height of the transaction. A transaction + /// can only be packed between the epochs of [epoch_height - + /// TRANSACTION_EPOCH_BOUND, epoch_height + TRANSACTION_EPOCH_BOUND] + pub epoch_height: u64, + /// The chain id of the transaction + pub chain_id: u32, + /// Transaction data. + pub data: Bytes, +} + +impl NativeTransaction { + /// Specify the sender; this won't survive the serialize/deserialize + /// process, but can be cloned. + pub fn fake_sign(self, from: AddressWithSpace) -> SignedTransaction { + SignedTransaction { + transaction: TransactionWithSignature { + transaction: TransactionWithSignatureSerializePart { + unsigned: Transaction::Native(self), + r: U256::one(), + s: U256::one(), + v: 0, + }, + hash: H256::zero(), + rlp_size: None, + } + .compute_hash(), + sender: from.address, + public: None, + } + } +} + +#[derive( + Default, + Debug, + Clone, + Eq, + PartialEq, + RlpEncodable, + RlpDecodable, + Serialize, + Deserialize, +)] +pub struct Cip2930Transaction { + pub nonce: U256, + pub gas_price: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub storage_limit: u64, + pub epoch_height: u64, + pub chain_id: u32, + pub data: Bytes, + pub access_list: NativeAccessList, +} + +#[derive( + Default, + Debug, + Clone, + Eq, + PartialEq, + RlpEncodable, + RlpDecodable, + Serialize, + Deserialize, +)] +pub struct Cip1559Transaction { + pub nonce: U256, + pub max_priority_fee_per_gas: U256, + pub max_fee_per_gas: U256, + pub gas: U256, + pub action: Action, + pub value: U256, + pub storage_limit: u64, + pub epoch_height: u64, + pub chain_id: u32, + pub data: Bytes, + pub access_list: NativeAccessList, +} + +#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct NativeAccessList { + inner: AccessList, +} + +impl Encodable for NativeAccessList { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_list(self.inner.as_ref()); + } +} + +impl Decodable for NativeAccessList { + fn decode(rlp: &Rlp) -> Result { + let inner = rlp.list_at(0)?; + Ok(Self { inner }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum TypedNativeTransaction { + Cip155(NativeTransaction), + Cip2930(Cip2930Transaction), + Cip1559(Cip1559Transaction), +} diff --git a/crates/transactiongen/src/lib.rs b/crates/transactiongen/src/lib.rs index 021ea5ba5b..0cecdc27ab 100644 --- a/crates/transactiongen/src/lib.rs +++ b/crates/transactiongen/src/lib.rs @@ -28,7 +28,7 @@ use lazy_static::lazy_static; use metrics::{register_meter_with_group, Meter}; use parking_lot::RwLock; use primitives::{ - transaction::{Action, NativeTransaction}, + transaction::{native_transaction::NativeTransaction, Action}, Account, SignedTransaction, Transaction, }; use rand::prelude::*; diff --git a/crates/util/treap-map/src/tests.rs b/crates/util/treap-map/src/tests.rs index 06bb992a48..814a1dca23 100644 --- a/crates/util/treap-map/src/tests.rs +++ b/crates/util/treap-map/src/tests.rs @@ -7,7 +7,10 @@ use crate::{node::Node, update::ApplyOpOutcome, KeyMngTrait, TreapMapConfig}; use super::{SharedKeyTreapMapConfig, TreapMap}; use cfx_types::{Address, Public, H256, U256, U512}; use cfxkey::Signature; -use primitives::{Action, NativeTransaction, SignedTransaction, Transaction}; +use primitives::{ + transaction::native_transaction::NativeTransaction, Action, + SignedTransaction, Transaction, +}; use rand::{seq::SliceRandom, thread_rng, Rng, RngCore, SeedableRng}; use rand_chacha::ChaChaRng; use rand_xorshift::XorShiftRng; From de04ea865c648d4efc679c8e6be2f710af90b24e Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:57:51 +0800 Subject: [PATCH 009/137] Add typed transactions for core space. --- .../src/transaction_pool/nonce_pool/mod.rs | 8 +-- .../transaction_pool_inner.rs | 12 ++--- crates/cfxcore/core/src/verification.rs | 16 +++--- .../cfxcore/execute-helper/src/estimation.rs | 44 ++++++++-------- .../executor/src/executive/fresh_executive.rs | 6 +-- crates/client/src/rpc/impls/cfx.rs | 19 ++++++- crates/client/src/rpc/impls/common.rs | 2 +- crates/client/src/rpc/types/receipt.rs | 10 ++-- crates/client/src/rpc/types/transaction.rs | 2 +- crates/primitives/src/transaction/mod.rs | 51 +++++++++++++++---- .../src/transaction/native_transaction.rs | 42 ++++++++++++++- 11 files changed, 149 insertions(+), 63 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs index 187cd22f8c..969ea9f3be 100644 --- a/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/nonce_pool/mod.rs @@ -78,8 +78,8 @@ impl TxWithReadyInfo { if let Transaction::Native(ref other) = x.unsigned { // FIXME: Use epoch_bound in spec. It's still a part of // normal config. - if tx.epoch_height - > other.epoch_height.saturating_add( + if *tx.epoch_height() + > other.epoch_height().saturating_add( TRANSACTION_DEFAULT_EPOCH_BOUND.saturating_mul(2), ) { @@ -91,7 +91,7 @@ impl TxWithReadyInfo { // verification anymore and should be dropped. return true; } - tx.epoch_height > other.epoch_height + tx.epoch_height() > other.epoch_height() } else { // Should be unreachable. But I'm not very sure about this. // Return false is safe. @@ -128,7 +128,7 @@ impl TxWithReadyInfo { }; let storage_collateral_requirement = if let Transaction::Native(ref tx) = transaction.unsigned { - U256::from(tx.storage_limit - sponsored_storage) + U256::from(*tx.storage_limit() - sponsored_storage) * *DRIPS_PER_STORAGE_COLLATERAL_UNIT } else { U256::zero() diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 92b5002a3e..4f273795bd 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -1207,12 +1207,12 @@ impl TransactionPoolInner { ); match transaction.unsigned { Transaction::Native(ref utx) => { - need_balance += utx.value.clone(); + need_balance += utx.value().clone(); if sponsored_gas == U256::from(0) { need_balance += estimate_gas_fee; } if sponsored_storage == 0 { - need_balance += U256::from(utx.storage_limit) + need_balance += U256::from(*utx.storage_limit()) * *DRIPS_PER_STORAGE_COLLATERAL_UNIT; } } @@ -1277,7 +1277,7 @@ impl TransactionPoolInner { // Compute sponsored_gas for `transaction` if let Transaction::Native(ref utx) = transaction.unsigned { - if let Action::Call(ref callee) = utx.action { + if let Action::Call(ref callee) = utx.action().clone() { // FIXME: This is a quick fix for performance issue. if callee.is_contract_address() { if let Some(sponsor_info) = @@ -1305,15 +1305,15 @@ impl TransactionPoolInner { && estimated_gas <= sponsor_info.sponsor_balance_for_gas { - sponsored_gas = utx.gas; + sponsored_gas = utx.gas().clone(); } let estimated_collateral = - U256::from(utx.storage_limit) + U256::from(*utx.storage_limit()) * *DRIPS_PER_STORAGE_COLLATERAL_UNIT; if estimated_collateral <= sponsor_info.sponsor_balance_for_collateral + sponsor_info.unused_storage_points() { - sponsored_storage = utx.storage_limit; + sponsored_storage = *utx.storage_limit(); } } } diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 40b7c41eea..f132c80c03 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -22,7 +22,9 @@ use cfx_types::{ use cfx_vm_types::Spec; use primitives::{ block::BlockHeight, - transaction::{native_transaction::NativeTransaction, TransactionError}, + transaction::{ + native_transaction::TypedNativeTransaction, TransactionError, + }, Action, Block, BlockHeader, BlockReceipts, MerkleHash, Receipt, SignedTransaction, Transaction, TransactionWithSignature, }; @@ -516,12 +518,14 @@ impl VerificationConfig { } pub fn check_transaction_epoch_bound( - tx: &NativeTransaction, block_height: u64, transaction_epoch_bound: u64, + tx: &TypedNativeTransaction, block_height: u64, + transaction_epoch_bound: u64, ) -> i8 { - if tx.epoch_height.wrapping_add(transaction_epoch_bound) < block_height + if tx.epoch_height().wrapping_add(transaction_epoch_bound) + < block_height { -1 - } else if tx.epoch_height > block_height + transaction_epoch_bound { + } else if *tx.epoch_height() > block_height + transaction_epoch_bound { 1 } else { 0 @@ -529,7 +533,7 @@ impl VerificationConfig { } fn verify_transaction_epoch_height( - tx: &NativeTransaction, block_height: u64, + tx: &TypedNativeTransaction, block_height: u64, transaction_epoch_bound: u64, mode: &VerifyTxMode, ) -> Result<(), TransactionError> { let result = Self::check_transaction_epoch_bound( @@ -543,7 +547,7 @@ impl VerificationConfig { Ok(()) } else { bail!(TransactionError::EpochHeightOutOfBound { - set: tx.epoch_height, + set: *tx.epoch_height(), block_height, transaction_epoch_bound, }); diff --git a/crates/cfxcore/execute-helper/src/estimation.rs b/crates/cfxcore/execute-helper/src/estimation.rs index f3c56dc853..ec55a1beab 100644 --- a/crates/cfxcore/execute-helper/src/estimation.rs +++ b/crates/cfxcore/execute-helper/src/estimation.rs @@ -16,10 +16,7 @@ use cfx_types::{ address_util::AddressUtil, Address, AddressSpaceUtil, Space, U256, }; use cfx_vm_types::{self as vm, Env, Spec}; -use primitives::{ - transaction::{native_transaction::NativeTransaction, Action}, - SignedTransaction, Transaction, -}; +use primitives::{transaction::Action, SignedTransaction, Transaction}; use std::{ cmp::{max, min}, fmt::Display, @@ -106,26 +103,27 @@ impl<'a> EstimationContext<'a> { fn sponsored_contract_if_eligible_sender( &self, tx: &SignedTransaction, ty: SponsoredType, ) -> DbResult> { - if let Transaction::Native(NativeTransaction { - action: Action::Call(ref to), - .. - }) = tx.unsigned - { - if to.is_contract_address() { - let sponsor = match ty { - SponsoredType::Gas => self.state.sponsor_for_gas(&to)?, - SponsoredType::Collateral => { - self.state.sponsor_for_collateral(&to)? + if let Transaction::Native(ref native_tx) = tx.unsigned { + if let Action::Call(to) = native_tx.action() { + if to.is_contract_address() { + let sponsor = match ty { + SponsoredType::Gas => { + self.state.sponsor_for_gas(&to)? + } + SponsoredType::Collateral => { + self.state.sponsor_for_collateral(&to)? + } + }; + let has_sponsor = sponsor.map_or(false, |x| !x.is_zero()); + + if has_sponsor + && self.state.check_contract_whitelist( + &to, + &tx.sender().address, + )? + { + return Ok(Some(*to)); } - }; - let has_sponsor = sponsor.map_or(false, |x| !x.is_zero()); - - if has_sponsor - && self - .state - .check_contract_whitelist(&to, &tx.sender().address)? - { - return Ok(Some(*to)); } } } diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index bcdc2e3dfc..5978c894cf 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -126,13 +126,13 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let env = self.context.env; - if tx.epoch_height.abs_diff(env.epoch_height) + if tx.epoch_height().abs_diff(env.epoch_height) > env.transaction_epoch_bound { Ok(Err(ExecutionOutcome::NotExecutedToReconsiderPacking( ToRepackError::EpochHeightOutOfBound { block_height: env.epoch_height, - set: tx.epoch_height, + set: *tx.epoch_height(), transaction_epoch_bound: env.transaction_epoch_bound, }, ))) @@ -177,7 +177,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { &tx.transaction.transaction.unsigned, settings.charge_collateral, ) { - U256::from(tx.storage_limit) + U256::from(*tx.storage_limit()) * *DRIPS_PER_STORAGE_COLLATERAL_UNIT } else { U256::zero() diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 540204f38f..cb39e0fc46 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -102,7 +102,10 @@ use cfxcore::{ consensus_parameters::DEFERRED_STATE_EPOCH_COUNT, }; use diem_types::account_address::AccountAddress; -use primitives::transaction::eth_transaction::EthereumTransaction; +use primitives::transaction::{ + eth_transaction::EthereumTransaction, + native_transaction::TypedNativeTransaction, +}; use serde::Serialize; #[derive(Debug)] @@ -1068,7 +1071,19 @@ impl RpcImpl { // set fake data for latency tests match signed_tx.transaction.transaction.unsigned { - Transaction::Native(ref mut unsigned) if tx_data_len > 0 => { + Transaction::Native(TypedNativeTransaction::Cip155( + ref mut unsigned, + )) if tx_data_len > 0 => { + unsigned.data = vec![0; tx_data_len]; + } + Transaction::Native(TypedNativeTransaction::Cip1559( + ref mut unsigned, + )) if tx_data_len > 0 => { + unsigned.data = vec![0; tx_data_len]; + } + Transaction::Native(TypedNativeTransaction::Cip2930( + ref mut unsigned, + )) if tx_data_len > 0 => { unsigned.data = vec![0; tx_data_len]; } Transaction::Ethereum(EthereumTransaction::Eip155( diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index af2c381492..34bc85afe2 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -995,7 +995,7 @@ impl RpcImpl { })?; let required_storage_collateral = if let Transaction::Native(ref tx) = tx.unsigned { - U256::from(tx.storage_limit) + U256::from(*tx.storage_limit()) * *DRIPS_PER_STORAGE_COLLATERAL_UNIT } else { U256::zero() diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index f2b3aab66e..aede1a8169 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -107,24 +107,24 @@ impl Receipt { let (address, action, space) = match transaction.unsigned { Transaction::Native(ref unsigned) => { - if Action::Create == unsigned.action + if Action::Create == *unsigned.action() && outcome_status == TransactionStatus::Success { let (mut created_address, _) = contract_address( CreateContractAddress::FromSenderNonceAndCodeHash, block_number.into(), &transaction.sender, - &unsigned.nonce, - &unsigned.data, + unsigned.nonce(), + unsigned.data(), ); created_address.set_contract_type_bits(); let address = Some(RpcAddress::try_from_h160( created_address, network, )?); - (address, unsigned.action.clone(), Space::Native) + (address, unsigned.action().clone(), Space::Native) } else { - (None, unsigned.action.clone(), Space::Native) + (None, unsigned.action().clone(), Space::Native) } } Transaction::Ethereum(ref unsigned) => { diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index 7fdefc21f8..bab2b4e465 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -109,7 +109,7 @@ impl Transaction { } let (storage_limit, epoch_height) = if let PrimitiveTransaction::Native(ref tx) = t.unsigned { - (tx.storage_limit, tx.epoch_height) + (*tx.storage_limit(), *tx.epoch_height()) } else { (0, 0) }; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 2d6b026e76..c9b8add25c 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -5,7 +5,10 @@ pub mod eth_transaction; pub mod native_transaction; -use crate::{bytes::Bytes, hash::keccak}; +use crate::{ + bytes::Bytes, hash::keccak, + transaction::native_transaction::TypedNativeTransaction, +}; use cfx_types::{ Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H160, H256, U256, @@ -247,16 +250,20 @@ pub type AccessList = Vec; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Transaction { - Native(NativeTransaction), + Native(TypedNativeTransaction), Ethereum(EthereumTransaction), } impl Default for Transaction { - fn default() -> Self { Transaction::Native(Default::default()) } + fn default() -> Self { + Transaction::Native(TypedNativeTransaction::Cip155(Default::default())) + } } impl From for Transaction { - fn from(tx: NativeTransaction) -> Self { Self::Native(tx) } + fn from(tx: NativeTransaction) -> Self { + Self::Native(TypedNativeTransaction::Cip155(tx)) + } } impl From for Transaction { @@ -269,7 +276,7 @@ macro_rules! access_common_ref { ($field:ident, $ty:ty) => { pub fn $field(&self) -> &$ty { match self { - Transaction::Native(tx) => &tx.$field, + Transaction::Native(tx) => &tx.$field(), Transaction::Ethereum(tx) => &tx.$field(), } } @@ -302,21 +309,29 @@ impl Transaction { pub fn chain_id(&self) -> Option { match self { - Transaction::Native(tx) => Some(tx.chain_id), + Transaction::Native(tx) => Some(*tx.chain_id()), Transaction::Ethereum(tx) => tx.chain_id().clone(), } } pub fn storage_limit(&self) -> Option { match self { - Transaction::Native(tx) => Some(tx.storage_limit), + Transaction::Native(tx) => Some(*tx.storage_limit()), Transaction::Ethereum(_tx) => None, } } pub fn nonce_mut(&mut self) -> &mut U256 { match self { - Transaction::Native(tx) => &mut tx.nonce, + Transaction::Native(TypedNativeTransaction::Cip155(tx)) => { + &mut tx.nonce + } + Transaction::Native(TypedNativeTransaction::Cip1559(tx)) => { + &mut tx.nonce + } + Transaction::Native(TypedNativeTransaction::Cip2930(tx)) => { + &mut tx.nonce + } Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { &mut tx.nonce } @@ -346,7 +361,13 @@ impl Transaction { pub fn signature_hash(&self) -> H256 { let mut s = RlpStream::new(); match self { - Transaction::Native(tx) => { + Transaction::Native(TypedNativeTransaction::Cip155(tx)) => { + s.append(tx); + } + Transaction::Native(TypedNativeTransaction::Cip1559(tx)) => { + s.append(tx); + } + Transaction::Native(TypedNativeTransaction::Cip2930(tx)) => { s.append(tx); } Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { @@ -418,7 +439,7 @@ pub struct TransactionWithSignatureSerializePart { impl Encodable for TransactionWithSignatureSerializePart { fn rlp_append(&self, s: &mut RlpStream) { match self.unsigned { - Transaction::Native(ref tx) => { + Transaction::Native(TypedNativeTransaction::Cip155(ref tx)) => { s.begin_list(4); s.append(tx); s.append(&self.v); @@ -456,6 +477,12 @@ impl Encodable for TransactionWithSignatureSerializePart { Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => { todo!() } + Transaction::Native(TypedNativeTransaction::Cip2930(_)) => { + todo!() + } + Transaction::Native(TypedNativeTransaction::Cip1559(_)) => { + todo!() + } } } } @@ -469,7 +496,9 @@ impl Decodable for TransactionWithSignatureSerializePart { let r: U256 = rlp.val_at(2)?; let s: U256 = rlp.val_at(3)?; Ok(TransactionWithSignatureSerializePart { - unsigned: Transaction::Native(unsigned), + unsigned: Transaction::Native( + TypedNativeTransaction::Cip155(unsigned), + ), v, r, s, diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs index e8a42caf66..9557150442 100644 --- a/crates/primitives/src/transaction/native_transaction.rs +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -49,7 +49,9 @@ impl NativeTransaction { SignedTransaction { transaction: TransactionWithSignature { transaction: TransactionWithSignatureSerializePart { - unsigned: Transaction::Native(self), + unsigned: Transaction::Native( + TypedNativeTransaction::Cip155(self), + ), r: U256::one(), s: U256::one(), v: 0, @@ -131,6 +133,44 @@ impl Decodable for NativeAccessList { } } +macro_rules! access_common_ref { + ($field:ident, $ty:ty) => { + pub fn $field(&self) -> &$ty { + match self { + TypedNativeTransaction::Cip155(tx) => &tx.$field, + TypedNativeTransaction::Cip2930(tx) => &tx.$field, + TypedNativeTransaction::Cip1559(tx) => &tx.$field, + } + } + }; +} + +impl TypedNativeTransaction { + access_common_ref!(gas, U256); + + access_common_ref!(data, Bytes); + + access_common_ref!(nonce, U256); + + access_common_ref!(action, Action); + + access_common_ref!(value, U256); + + access_common_ref!(chain_id, u32); + + access_common_ref!(epoch_height, u64); + + access_common_ref!(storage_limit, u64); + + pub fn gas_price(&self) -> &U256 { + match self { + TypedNativeTransaction::Cip155(tx) => &tx.gas_price, + TypedNativeTransaction::Cip1559(tx) => &tx.max_fee_per_gas, + TypedNativeTransaction::Cip2930(tx) => &tx.gas_price, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum TypedNativeTransaction { Cip155(NativeTransaction), From 86f406f19dd3a2f4ba93acdca6e248d10c8e4015 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:55:56 +0800 Subject: [PATCH 010/137] Add encoding/decoding for eth typed transactions. --- crates/primitives/src/transaction/mod.rs | 225 +++++++++++++++++------ 1 file changed, 165 insertions(+), 60 deletions(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index c9b8add25c..9f5771ce7a 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -6,8 +6,12 @@ pub mod eth_transaction; pub mod native_transaction; use crate::{ - bytes::Bytes, hash::keccak, - transaction::native_transaction::TypedNativeTransaction, + bytes::Bytes, + hash::keccak, + transaction::{ + eth_transaction::{Eip1559Transaction, Eip2930Transaction}, + native_transaction::TypedNativeTransaction, + }, }; use cfx_types::{ Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H160, @@ -32,6 +36,13 @@ use unexpected::OutOfBounds; /// Fake address for unsigned transactions. pub const UNSIGNED_SENDER: Address = H160([0xff; 20]); +pub const TYPED_NATIVE_TX_PREFIX: &[u8; 3] = b"cfx"; +pub const TYPED_NATIVE_TX_PREFIX_BYTE: u8 = TYPED_NATIVE_TX_PREFIX[0]; +pub const EIP2930_TYPE: u8 = 0x01; +pub const EIP1559_TYPE: u8 = 0x02; +pub const CIP2930_TYPE: u8 = 0x01; +pub const CIP1559_TYPE: u8 = 0x02; + /// Shorter id for transactions in compact blocks // TODO should be u48 pub type TxShortId = u64; @@ -471,11 +482,36 @@ impl Encodable for TransactionWithSignatureSerializePart { s.append(&self.r); s.append(&self.s); } - Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => { - todo!() + Transaction::Ethereum(EthereumTransaction::Eip2930(ref tx)) => { + s.append_raw(&[EIP2930_TYPE], 0); + s.begin_list(11); + s.append(&tx.chain_id); + s.append(&tx.nonce); + s.append(&tx.gas_price); + s.append(&tx.gas); + s.append(&tx.action); + s.append(&tx.value); + s.append(&tx.data); + s.append_list(&tx.access_list); + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); } - Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => { - todo!() + Transaction::Ethereum(EthereumTransaction::Eip1559(ref tx)) => { + s.append_raw(&[EIP1559_TYPE], 0); + s.begin_list(12); + s.append(&tx.chain_id); + s.append(&tx.nonce); + s.append(&tx.max_priority_fee_per_gas); + s.append(&tx.max_fee_per_gas); + s.append(&tx.gas); + s.append(&tx.action); + s.append(&tx.value); + s.append(&tx.data); + s.append_list(&tx.access_list); + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); } Transaction::Native(TypedNativeTransaction::Cip2930(_)) => { todo!() @@ -489,63 +525,132 @@ impl Encodable for TransactionWithSignatureSerializePart { impl Decodable for TransactionWithSignatureSerializePart { fn decode(rlp: &Rlp) -> Result { - match rlp.item_count()? { - 4 => { - let unsigned: NativeTransaction = rlp.val_at(0)?; - let v: u8 = rlp.val_at(1)?; - let r: U256 = rlp.val_at(2)?; - let s: U256 = rlp.val_at(3)?; - Ok(TransactionWithSignatureSerializePart { - unsigned: Transaction::Native( - TypedNativeTransaction::Cip155(unsigned), - ), - v, - r, - s, - }) + if rlp.as_raw().len() == 0 { + return Err(DecoderError::RlpInvalidLength); + } + if rlp.is_list() { + match rlp.item_count()? { + 4 => { + let unsigned: NativeTransaction = rlp.val_at(0)?; + let v: u8 = rlp.val_at(1)?; + let r: U256 = rlp.val_at(2)?; + let s: U256 = rlp.val_at(3)?; + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Native( + TypedNativeTransaction::Cip155(unsigned), + ), + v, + r, + s, + }) + } + 9 => { + let nonce: U256 = rlp.val_at(0)?; + let gas_price: U256 = rlp.val_at(1)?; + let gas: U256 = rlp.val_at(2)?; + let action: Action = rlp.val_at(3)?; + let value: U256 = rlp.val_at(4)?; + let data: Vec = rlp.val_at(5)?; + let legacy_v: u64 = rlp.val_at(6)?; + let r: U256 = rlp.val_at(7)?; + let s: U256 = rlp.val_at(8)?; + + let v = eip155_signature::extract_standard_v(legacy_v); + let chain_id = + match eip155_signature::extract_chain_id_from_legacy_v( + legacy_v, + ) { + Some(chain_id) if chain_id > (u32::MAX as u64) => { + return Err(DecoderError::Custom( + "Does not support chain_id >= 2^32", + )); + } + chain_id => chain_id.map(|x| x as u32), + }; + + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip155(Eip155Transaction { + nonce, + gas_price, + gas, + action, + value, + chain_id, + data, + }), + ), + v, + r, + s, + }) + } + _ => Err(DecoderError::RlpInvalidLength), } - 9 => { - let nonce: U256 = rlp.val_at(0)?; - let gas_price: U256 = rlp.val_at(1)?; - let gas: U256 = rlp.val_at(2)?; - let action: Action = rlp.val_at(3)?; - let value: U256 = rlp.val_at(4)?; - let data: Vec = rlp.val_at(5)?; - let legacy_v: u64 = rlp.val_at(6)?; - let r: U256 = rlp.val_at(7)?; - let s: U256 = rlp.val_at(8)?; - - let v = eip155_signature::extract_standard_v(legacy_v); - let chain_id = - match eip155_signature::extract_chain_id_from_legacy_v( - legacy_v, - ) { - Some(chain_id) if chain_id > (u32::MAX as u64) => { - return Err(DecoderError::Custom( - "Does not support chain_id >= 2^32", - )); - } - chain_id => chain_id.map(|x| x as u32), + } else { + match rlp.as_raw()[0] { + TYPED_NATIVE_TX_PREFIX_BYTE => { + todo!() + } + EIP2930_TYPE => { + let rlp = Rlp::new(&rlp.as_raw()[1..]); + if rlp.item_count()? != 11 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let tx = Eip2930Transaction { + chain_id: rlp.val_at(0)?, + nonce: rlp.val_at(1)?, + gas_price: rlp.val_at(2)?, + gas: rlp.val_at(3)?, + action: rlp.val_at(4)?, + value: rlp.val_at(5)?, + data: rlp.val_at(6)?, + access_list: rlp.list_at(7)?, }; - - Ok(TransactionWithSignatureSerializePart { - unsigned: Transaction::Ethereum( - EthereumTransaction::Eip155(Eip155Transaction { - nonce, - gas_price, - gas, - action, - value, - chain_id, - data, - }), - ), - v, - r, - s, - }) + let v = rlp.val_at(8)?; + let r = rlp.val_at(9)?; + let s = rlp.val_at(10)?; + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip2930(tx), + ), + v, + r, + s, + }) + } + EIP1559_TYPE => { + let rlp = Rlp::new(&rlp.as_raw()[1..]); + if rlp.item_count()? != 12 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let tx = Eip1559Transaction { + chain_id: rlp.val_at(0)?, + nonce: rlp.val_at(1)?, + max_priority_fee_per_gas: rlp.val_at(2)?, + max_fee_per_gas: rlp.val_at(3)?, + gas: rlp.val_at(4)?, + action: rlp.val_at(5)?, + value: rlp.val_at(6)?, + data: rlp.val_at(7)?, + access_list: rlp.list_at(8)?, + }; + let v = rlp.val_at(9)?; + let r = rlp.val_at(10)?; + let s = rlp.val_at(11)?; + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum( + EthereumTransaction::Eip1559(tx), + ), + v, + r, + s, + }) + } + _ => Err(DecoderError::RlpInvalidLength), } - _ => Err(DecoderError::RlpInvalidLength), } } } From 11b896fb76300dbd8d440544970a0f2efd3f5793 Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 29 Apr 2024 17:58:01 +0800 Subject: [PATCH 011/137] support fourbyte tracer --- .../consensus_inner/consensus_executor.rs | 22 +- .../execute-helper/src/observer/fourbyte.rs | 20 +- .../src/observer/geth_tracer/mod.rs | 511 ++++-------------- .../observer/geth_tracer/tracing_inspector.rs | 394 ++++++++++++++ .../execute-helper/src/observer/mod.rs | 19 +- crates/client/src/rpc/impls/eth/debug.rs | 12 +- 6 files changed, 528 insertions(+), 450 deletions(-) create mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index c6cb420686..5c8dddba29 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -2103,7 +2103,15 @@ impl ConsensusExecutionHandler { Some(t) => match t { GethDebugTracerType::BuiltInTracer(bt) => match bt { GethDebugBuiltInTracerType::FourByteTracer => { - Observer::with_no_tracing() + Observer::geth_tracer( + TracingInspectorConfig::none(), + tx_gas_limit, + Arc::clone(&self.machine), + Some(GethDebugBuiltInTracerType::FourByteTracer), + None, + None, + None, + ) } GethDebugBuiltInTracerType::CallTracer => { let call_config = opts @@ -2115,6 +2123,10 @@ impl ConsensusExecutionHandler { TracingInspectorConfig::from_geth_call_config(&call_config), tx_gas_limit, Arc::clone(&self.machine), + Some(GethDebugBuiltInTracerType::CallTracer), + Some(call_config), + None, + None, ) } GethDebugBuiltInTracerType::PreStateTracer => { @@ -2127,6 +2139,10 @@ impl ConsensusExecutionHandler { TracingInspectorConfig::from_geth_prestate_config(&pre_state_config), tx_gas_limit, Arc::clone(&self.machine), + Some(GethDebugBuiltInTracerType::PreStateTracer), + None, + Some(pre_state_config), + None, ) } GethDebugBuiltInTracerType::NoopTracer => { @@ -2149,6 +2165,10 @@ impl ConsensusExecutionHandler { ), tx_gas_limit, Arc::clone(&self.machine), + None, + None, + None, + Some(opts.config) ) } } diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs index c1b4920ff9..57929b361b 100644 --- a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs +++ b/crates/cfxcore/execute-helper/src/observer/fourbyte.rs @@ -21,13 +21,8 @@ //! } //! ``` -use super::geth_tracer::GethTraceKey; use alloy_primitives::{hex, Selector}; use alloy_rpc_types_trace::geth::{FourByteFrame, GethTrace}; -use cfx_executor::observer::{ - CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, - OpcodeTracer, StorageTracer, -}; use cfx_vm_types::ActionParams; use std::collections::HashMap; @@ -50,10 +45,8 @@ impl FourByteInspector { pub fn drain(self) -> GethTrace { GethTrace::FourByteTracer(FourByteFrame::from(self)) } -} -impl CallTracer for FourByteInspector { - fn record_call(&mut self, params: &ActionParams) { + pub fn record_call(&mut self, params: &ActionParams) { if let Some(input) = ¶ms.data { if input.len() > 4 { let selector = Selector::try_from(&input[..4]) @@ -65,17 +58,6 @@ impl CallTracer for FourByteInspector { } } -impl CheckpointTracer for FourByteInspector {} -impl InternalTransferTracer for FourByteInspector {} -impl StorageTracer for FourByteInspector {} -impl OpcodeTracer for FourByteInspector {} - -impl DrainTrace for FourByteInspector { - fn drain_trace(self, map: &mut typemap::ShareDebugMap) { - map.insert::(self.drain()); - } -} - impl From for FourByteFrame { fn from(value: FourByteInspector) -> Self { Self( diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 5347dc5bb8..34e61063b9 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -4,28 +4,23 @@ mod builder; mod config; mod db_adapter; mod gas; +mod tracing_inspector; mod types; mod utils; pub use arena::CallTraceArena; pub use builder::geth::{self, GethTraceBuilder}; -use cfx_types::{Space, H160}; +use cfx_types::H160; pub use config::{StackSnapshotType, TracingInspectorConfig}; -use arena::PushTraceKind; -use gas::GasInspector; -use types::{ - CallKind, CallTrace, CallTraceNode, CallTraceStep, LogCallOrder, - RecordedMemory, -}; -use utils::{ - convert_h160, convert_h256, convert_u256, gas_used, stack_push_count, -}; +use types::LogCallOrder; +use utils::{convert_h160, convert_h256, convert_u256}; -use alloy_primitives::{Address, Bytes, LogData, U256}; +use super::fourbyte::FourByteInspector; +use alloy_primitives::{Address, Bytes, LogData}; use revm::{ db::InMemoryDB, - interpreter::{Gas, InstructionResult, InterpreterResult, OpCode}, + interpreter::{Gas, InstructionResult, InterpreterResult}, primitives::{ExecutionResult, ResultAndState, State}, }; @@ -41,422 +36,72 @@ use cfx_executor::{ use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; use alloy_rpc_types_trace::geth::{ - CallConfig, CallFrame, DefaultFrame, GethDebugBuiltInTracerType, - GethDefaultTracingOptions, GethTrace, NoopFrame, PreStateConfig, + CallConfig, GethDebugBuiltInTracerType, GethDefaultTracingOptions, + GethTrace, NoopFrame, PreStateConfig, }; +use tracing_inspector::TracingInspector; -use std::{convert, sync::Arc}; - -#[derive(Clone)] -pub struct TracingInspector { - /// Configures what and how the inspector records traces. - config: TracingInspectorConfig, - /// Records all call traces - traces: CallTraceArena, - /// Tracks active calls - trace_stack: Vec, - /// Tracks active steps - step_stack: Vec, - /// Tracks the return value of the last call - last_call_return_data: Option, - /// The gas inspector used to track remaining gas. - gas_inspector: GasInspector, +use std::sync::Arc; + +pub struct GethTracer { + inner: TracingInspector, + // + tracer_type: Option, + // + fourbyte_inspector: FourByteInspector, + // + tx_gas_limit: u64, + // + gas_left: u64, // call depth depth: usize, - // gas stack, used to trace gas_spent in call_result/create_result - gas_stack: Vec, // - tx_gas_limit: u64, + return_data: Bytes, + // + call_config: Option, // - machine: Arc, + prestate_config: Option, + // + opcode_config: Option, } -impl TracingInspector { - /// Returns a new instance for the given config +impl GethTracer { pub fn new( config: TracingInspectorConfig, tx_gas_limit: u64, - machine: Arc, + machine: Arc, tracer_type: Option, + call_config: Option, + prestate_config: Option, + opcode_config: Option, ) -> Self { Self { - config, - traces: Default::default(), - trace_stack: vec![], - step_stack: vec![], - last_call_return_data: None, - gas_inspector: Default::default(), - depth: 0, - gas_stack: vec![], - tx_gas_limit, - machine, - } - } - - /// Resets the inspector to its initial state of [Self::new]. - /// This makes the inspector ready to be used again. - /// - /// Note that this method has no effect on the allocated capacity of the - /// vector. - #[inline] - pub fn fuse(&mut self) { - let Self { - traces, - trace_stack, - step_stack, - last_call_return_data, - gas_inspector, - // kept - config: _, - depth, - gas_stack, + inner: TracingInspector::new(config, machine), + tracer_type, + fourbyte_inspector: FourByteInspector::new(), tx_gas_limit, - machine, - } = self; - traces.clear(); - trace_stack.clear(); - step_stack.clear(); - gas_stack.clear(); - last_call_return_data.take(); - *gas_inspector = Default::default(); - *depth = 0; - *tx_gas_limit = 0; - } - - /// Resets the inspector to it's initial state of [Self::new]. - #[inline] - pub fn fused(mut self) -> Self { - self.fuse(); - self - } - - /// Returns the config of the inspector. - pub const fn config(&self) -> &TracingInspectorConfig { &self.config } - - /// Gets a reference to the recorded call traces. - pub const fn get_traces(&self) -> &CallTraceArena { &self.traces } - - /// Gets a mutable reference to the recorded call traces. - pub fn get_traces_mut(&mut self) -> &mut CallTraceArena { &mut self.traces } - - /// Manually the gas used of the root trace. - /// - /// This is useful if the root trace's gasUsed should mirror the actual gas - /// used by the transaction. - /// - /// This allows setting it manually by consuming the execution result's gas - /// for example. - #[inline] - pub fn set_transaction_gas_used(&mut self, gas_used: u64) { - if let Some(node) = self.traces.arena.first_mut() { - node.trace.gas_used = gas_used; - } - } - - /// Convenience function for [ParityTraceBuilder::set_transaction_gas_used] - /// that consumes the type. - #[inline] - pub fn with_transaction_gas_used(mut self, gas_used: u64) -> Self { - self.set_transaction_gas_used(gas_used); - self - } - - /// Consumes the Inspector and returns a [ParityTraceBuilder]. - // #[inline] - // pub fn into_parity_builder(self) -> ParityTraceBuilder { - // ParityTraceBuilder::new(self.traces.arena, self.spec_id, self.config) - // } - - /// Consumes the Inspector and returns a [GethTraceBuilder]. - #[inline] - pub fn into_geth_builder(self) -> GethTraceBuilder { - GethTraceBuilder::new(self.traces.arena, self.config) - } - - /// Returns true if we're no longer in the context of the root call. - fn is_deep(&self) -> bool { - // the root call will always be the first entry in the trace stack - !self.trace_stack.is_empty() - } - - /// Returns true if this a call to a precompile contract. - /// - /// Returns true if the `to` address is a precompile contract and the value - /// is zero. - #[inline] - fn is_precompile_call(&self, to: &H160, value: U256, space: Space) -> bool { - // TODO: check according block height - let is_precompile = match space { - Space::Native => self.machine.builtins().contains_key(&to), - Space::Ethereum => self.machine.builtins_evm().contains_key(&to), - }; - - if is_precompile { - // only if this is _not_ the root call - return self.is_deep() && value.is_zero(); - } - false - } - - /// Returns the currently active call trace. - /// - /// This will be the last call trace pushed to the stack: the call we - /// entered most recently. - #[track_caller] - #[inline] - fn active_trace(&self) -> Option<&CallTraceNode> { - self.trace_stack.last().map(|idx| &self.traces.arena[*idx]) - } - - /// Returns the last trace [CallTrace] index from the stack. - /// - /// This will be the currently active call trace. - /// - /// # Panics - /// - /// If no [CallTrace] was pushed - #[track_caller] - #[inline] - fn last_trace_idx(&self) -> usize { - self.trace_stack - .last() - .copied() - .expect("can't start step without starting a trace first") - } - - /// _Removes_ the last trace [CallTrace] index from the stack. - /// - /// # Panics - /// - /// If no [CallTrace] was pushed - #[track_caller] - #[inline] - fn pop_trace_idx(&mut self) -> usize { - self.trace_stack - .pop() - .expect("more traces were filled than started") - } - - /// Starts tracking a new trace. - /// - /// Invoked on [Inspector::call]. - #[allow(clippy::too_many_arguments)] - fn start_trace_on_call( - &mut self, address: Address, input_data: Bytes, value: U256, - kind: CallKind, caller: Address, mut gas_limit: u64, - maybe_precompile: Option, tx_gas_limit: u64, depth: usize, - ) { - // This will only be true if the inspector is configured to exclude - // precompiles and the call is to a precompile - let push_kind = if maybe_precompile.unwrap_or(false) { - // We don't want to track precompiles - PushTraceKind::PushOnly - } else { - PushTraceKind::PushAndAttachToParent - }; - - if self.trace_stack.is_empty() { - // this is the root call which should get the original gas limit of - // the transaction, because initialization costs are - // already subtracted from gas_limit For the root call - // this value should use the transaction's gas limit See and - gas_limit = tx_gas_limit; - } - - self.trace_stack.push(self.traces.push_trace( - 0, - push_kind, - CallTrace { - depth, - address, - kind, - data: input_data, - value, - status: InstructionResult::Continue, - caller, - maybe_precompile, - gas_limit, - ..Default::default() - }, - )); - } - - /// Fills the current trace with the outcome of a call. - /// - /// Invoked on [Inspector::call_end]. - /// - /// # Panics - /// - /// This expects an existing trace [Self::start_trace_on_call] - fn fill_trace_on_call_end( - &mut self, result: InterpreterResult, created_address: Option
, - gas_spent: u64, - ) { - let InterpreterResult { - result, - output, - gas: _, - } = result; - - let trace_idx = self.pop_trace_idx(); - let trace = &mut self.traces.arena[trace_idx].trace; - - if trace_idx == 0 { - // this is the root call which should get the gas used of the - // transaction refunds are applied after execution, - // which is when the root call ends - // Conflux have no refund - trace.gas_used = gas_used(gas_spent, 0); - } else { - trace.gas_used = gas_spent; - } - - trace.status = result; - trace.success = trace.status.is_ok(); - trace.output = output.clone(); - - self.last_call_return_data = Some(output); - - if let Some(address) = created_address { - // A new contract was created via CREATE - trace.address = address; + depth: 0, + gas_left: tx_gas_limit, + return_data: Bytes::default(), + call_config, + prestate_config, + opcode_config, } } - /// Starts tracking a step - /// - /// Invoked on [Inspector::step] - /// - /// # Panics - /// - /// This expects an existing [CallTrace], in other words, this panics if not - /// within the context of a call. - fn start_step(&mut self, interp: &dyn InterpreterInfo, depth: u64) { - let trace_idx = self.last_trace_idx(); - let trace = &mut self.traces.arena[trace_idx]; - - self.step_stack.push(StackStep { - trace_idx, - step_idx: trace.trace.steps.len(), - }); - - let memory = self - .config - .record_memory_snapshots - .then(|| RecordedMemory::new(interp.mem().to_vec())) - .unwrap_or_default(); - - let stack = if self.config.record_stack_snapshots.is_full() { - Some( - interp - .stack() - .into_iter() - .map(|v| convert_u256(v)) - .collect(), - ) - } else { - None - }; - - let op = OpCode::new(interp.current_opcode()) - .or_else(|| { - // if the opcode is invalid, we'll use the invalid opcode to - // represent it because this is invoked before - // the opcode is executed, the evm will eventually return a - // `Halt` with invalid/unknown opcode as result - let invalid_opcode = 0xfe; - OpCode::new(invalid_opcode) - }) - .expect("is valid opcode;"); - - trace.trace.steps.push(CallTraceStep { - depth, - pc: interp.program_counter() as usize, - op, - contract: convert_h160(interp.contract_address()), - stack, - push_stack: None, - memory_size: memory.len(), - memory, - gas_remaining: self.gas_inspector.gas_remaining(), - gas_refund_counter: 0, // conflux has no gas refund - - // fields will be populated end of call - gas_cost: 0, - storage_change: None, - status: InstructionResult::Continue, - }); + pub fn is_fourbyte_tracer(&self) -> bool { + self.tracer_type == Some(GethDebugBuiltInTracerType::FourByteTracer) } - /// Fills the current trace with the output of a step. - /// - /// Invoked on [Inspector::step_end]. - fn fill_step_on_step_end(&mut self, interp: &dyn InterpreterInfo) { - let StackStep { - trace_idx, - step_idx, - } = self - .step_stack - .pop() - .expect("can't fill step without starting a step first"); - let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx]; - - if self.config.record_stack_snapshots.is_pushes() { - let num_pushed = stack_push_count(step.op.get()); - let start = interp.stack().len() - num_pushed; - let push_stack = interp.stack()[start..].to_vec(); - step.push_stack = - Some(push_stack.into_iter().map(|v| convert_u256(v)).collect()); - } - - if self.config.record_memory_snapshots { - // resize memory so opcodes that allocated memory is correctly - // displayed - if interp.mem().len() > step.memory.len() { - step.memory.resize(interp.mem().len()); - } - } - if self.config.record_state_diff { - let _op = step.op.get(); - - // TODO setup the storage_change - } - - // The gas cost is the difference between the recorded gas remaining at - // the start of the step the remaining gas here, at the end of - // the step. - // todo(evm-inspector): Figure out why this can overflow. https://github.com/paradigmxyz/evm-inspectors/pull/38 - step.gas_cost = step - .gas_remaining - .saturating_sub(self.gas_inspector.gas_remaining()); - - // TODO set the status - // step.status = interp.instruction_result; - } -} - -pub struct GethTracer { - inner: TracingInspector, - tracer_type: Option, -} - -impl GethTracer { - pub fn new( - config: TracingInspectorConfig, tx_gas_limit: u64, - machine: Arc, - ) -> Self { - Self { - inner: TracingInspector::new(config, tx_gas_limit, machine), - tracer_type: None, - } - } + pub fn gas_used(&self) -> u64 { self.tx_gas_limit - self.gas_left } pub fn drain(self) -> GethTrace { let trace = match self.tracer_type { Some(t) => match t { - GethDebugBuiltInTracerType::FourByteTracer => todo!(), + GethDebugBuiltInTracerType::FourByteTracer => { + self.fourbyte_inspector.drain() + } GethDebugBuiltInTracerType::CallTracer => { - // TODO - let gas_used = 1000000u64; - let opts = CallConfig::default(); + let gas_used = self.gas_used(); + let opts = self.call_config.expect("should have config"); let frame = self .inner .into_geth_builder() @@ -464,9 +109,10 @@ impl GethTracer { GethTrace::CallTracer(frame) } GethDebugBuiltInTracerType::PreStateTracer => { - // TODO replace the empty state with a real state - let gas_used = 1000000u64; - let opts = PreStateConfig::default(); + // TODO replace the empty state and db with a real state + let gas_used = self.gas_used(); + let opts = + self.prestate_config.expect("should have config"); let result = ResultAndState { result: ExecutionResult::Revert { gas_used, @@ -486,14 +132,14 @@ impl GethTracer { GethTrace::NoopTracer(NoopFrame::default()) } GethDebugBuiltInTracerType::MuxTracer => { + // not supported GethTrace::NoopTracer(NoopFrame::default()) - } // not supported + } }, None => { - // TODO - let gas_used = 1000000u64; - let return_value = Bytes::default(); - let opts = GethDefaultTracingOptions::default(); + let gas_used = self.gas_used(); + let return_value = self.return_data; + let opts = self.opcode_config.expect("should have config"); let frame = self.inner.into_geth_builder().geth_traces( gas_used, return_value, @@ -527,7 +173,12 @@ impl StorageTracer for GethTracer {} impl CallTracer for GethTracer { fn record_call(&mut self, params: &ActionParams) { - self.inner.depth += 1; + if self.is_fourbyte_tracer() { + self.fourbyte_inspector.record_call(params); + return; + } + + self.depth += 1; self.inner.gas_stack.push(params.gas.clone()); // determine correct `from` and `to` based on the call scheme @@ -565,18 +216,23 @@ impl CallTracer for GethTracer { from, params.gas.as_u64(), maybe_precompile, - self.inner.tx_gas_limit, - self.inner.depth, + self.tx_gas_limit, + self.depth, ); } fn record_call_result(&mut self, result: &FrameResult) { - self.inner.depth -= 1; + if self.is_fourbyte_tracer() { + return; + } + + self.depth -= 1; let mut gas_spent = self.inner.gas_stack.pop().expect("should have value"); if let Ok(r) = result { gas_spent = gas_spent - r.gas_left; + self.gas_left = r.gas_left.as_u64(); } let instruction_result = to_instruction_result(result); @@ -589,6 +245,7 @@ impl CallTracer for GethTracer { .as_ref() .map(|f| Bytes::from(f.return_data.to_vec())) .unwrap_or_default(); + self.return_data = output.clone(); let outcome = InterpreterResult { result: instruction_result, @@ -601,7 +258,11 @@ impl CallTracer for GethTracer { } fn record_create(&mut self, params: &ActionParams) { - self.inner.depth += 1; + if self.is_fourbyte_tracer() { + return; + } + + self.depth += 1; self.inner.gas_stack.push(params.gas.clone()); let value = if matches!(params.call_type, CallType::DelegateCall) { @@ -624,17 +285,22 @@ impl CallTracer for GethTracer { params.gas.as_u64(), Some(false), params.gas.as_u64(), - self.inner.depth, + self.depth, ); } fn record_create_result(&mut self, result: &FrameResult) { - self.inner.depth -= 1; + if self.is_fourbyte_tracer() { + return; + } + + self.depth -= 1; let mut gas_spent = self.inner.gas_stack.pop().expect("should have value"); if let Ok(r) = result { gas_spent = gas_spent - r.gas_left; + self.gas_left = r.gas_left.as_u64(); } let instruction_result = to_instruction_result(result); @@ -647,6 +313,7 @@ impl CallTracer for GethTracer { .as_ref() .map(|f| Bytes::from(f.return_data.to_vec())) .unwrap_or_default(); + self.return_data = output.clone(); let outcome = InterpreterResult { result: instruction_result, @@ -727,6 +394,10 @@ impl OpcodeTracer for GethTracer { &mut self, _contract: &cfx_types::Address, target: &cfx_types::Address, _value: cfx_types::U256, ) { + if self.is_fourbyte_tracer() { + return; + } + let trace_idx = self.inner.last_trace_idx(); let trace = &mut self.inner.traces.arena[trace_idx].trace; trace.selfdestruct_refund_target = Some(convert_h160(*target as H160)) diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs new file mode 100644 index 0000000000..98b13f6545 --- /dev/null +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -0,0 +1,394 @@ +use super::{ + arena::PushTraceKind, + gas::GasInspector, + types::{ + CallKind, CallTrace, CallTraceNode, CallTraceStep, RecordedMemory, + }, + utils::{convert_h160, convert_u256, gas_used, stack_push_count}, + CallTraceArena, GethTraceBuilder, StackStep, TracingInspectorConfig, +}; +use cfx_types::{Space, H160}; + +use alloy_primitives::{Address, Bytes, U256}; +use revm::interpreter::{InstructionResult, InterpreterResult, OpCode}; + +use cfx_executor::machine::Machine; + +use cfx_vm_types::InterpreterInfo; + +use std::sync::Arc; + +#[derive(Clone)] +pub struct TracingInspector { + /// Configures what and how the inspector records traces. + pub config: TracingInspectorConfig, + /// Records all call traces + pub traces: CallTraceArena, + /// Tracks active calls + trace_stack: Vec, + /// Tracks active steps + step_stack: Vec, + /// Tracks the return value of the last call + last_call_return_data: Option, + /// The gas inspector used to track remaining gas. + pub gas_inspector: GasInspector, + // gas stack, used to trace gas_spent in call_result/create_result + pub gas_stack: Vec, + // + machine: Arc, +} + +impl TracingInspector { + /// Returns a new instance for the given config + pub fn new(config: TracingInspectorConfig, machine: Arc) -> Self { + Self { + config, + traces: Default::default(), + trace_stack: vec![], + step_stack: vec![], + last_call_return_data: None, + gas_inspector: Default::default(), + gas_stack: vec![], + machine, + } + } + + /// Resets the inspector to its initial state of [Self::new]. + /// This makes the inspector ready to be used again. + /// + /// Note that this method has no effect on the allocated capacity of the + /// vector. + #[inline] + pub fn fuse(&mut self) { + let Self { + traces, + trace_stack, + step_stack, + last_call_return_data, + gas_inspector, + // kept + config: _, + gas_stack, + machine: _, + } = self; + traces.clear(); + trace_stack.clear(); + step_stack.clear(); + gas_stack.clear(); + last_call_return_data.take(); + *gas_inspector = Default::default(); + } + + /// Resets the inspector to it's initial state of [Self::new]. + #[inline] + pub fn fused(mut self) -> Self { + self.fuse(); + self + } + + /// Returns the config of the inspector. + pub const fn config(&self) -> &TracingInspectorConfig { &self.config } + + /// Gets a reference to the recorded call traces. + pub const fn get_traces(&self) -> &CallTraceArena { &self.traces } + + /// Gets a mutable reference to the recorded call traces. + pub fn get_traces_mut(&mut self) -> &mut CallTraceArena { &mut self.traces } + + /// Manually the gas used of the root trace. + /// + /// This is useful if the root trace's gasUsed should mirror the actual gas + /// used by the transaction. + /// + /// This allows setting it manually by consuming the execution result's gas + /// for example. + #[inline] + pub fn set_transaction_gas_used(&mut self, gas_used: u64) { + if let Some(node) = self.traces.arena.first_mut() { + node.trace.gas_used = gas_used; + } + } + + /// Convenience function for [ParityTraceBuilder::set_transaction_gas_used] + /// that consumes the type. + #[inline] + pub fn with_transaction_gas_used(mut self, gas_used: u64) -> Self { + self.set_transaction_gas_used(gas_used); + self + } + + /// Consumes the Inspector and returns a [ParityTraceBuilder]. + // #[inline] + // pub fn into_parity_builder(self) -> ParityTraceBuilder { + // ParityTraceBuilder::new(self.traces.arena, self.spec_id, self.config) + // } + + /// Consumes the Inspector and returns a [GethTraceBuilder]. + #[inline] + pub fn into_geth_builder(self) -> GethTraceBuilder { + GethTraceBuilder::new(self.traces.arena, self.config) + } + + /// Returns true if we're no longer in the context of the root call. + fn is_deep(&self) -> bool { + // the root call will always be the first entry in the trace stack + !self.trace_stack.is_empty() + } + + /// Returns true if this a call to a precompile contract. + /// + /// Returns true if the `to` address is a precompile contract and the value + /// is zero. + #[inline] + pub fn is_precompile_call( + &self, to: &H160, value: U256, space: Space, + ) -> bool { + // TODO: check according block height + let is_precompile = match space { + Space::Native => self.machine.builtins().contains_key(&to), + Space::Ethereum => self.machine.builtins_evm().contains_key(&to), + }; + + if is_precompile { + // only if this is _not_ the root call + return self.is_deep() && value.is_zero(); + } + false + } + + /// Returns the currently active call trace. + /// + /// This will be the last call trace pushed to the stack: the call we + /// entered most recently. + #[track_caller] + #[inline] + pub fn active_trace(&self) -> Option<&CallTraceNode> { + self.trace_stack.last().map(|idx| &self.traces.arena[*idx]) + } + + /// Returns the last trace [CallTrace] index from the stack. + /// + /// This will be the currently active call trace. + /// + /// # Panics + /// + /// If no [CallTrace] was pushed + #[track_caller] + #[inline] + pub fn last_trace_idx(&self) -> usize { + self.trace_stack + .last() + .copied() + .expect("can't start step without starting a trace first") + } + + /// _Removes_ the last trace [CallTrace] index from the stack. + /// + /// # Panics + /// + /// If no [CallTrace] was pushed + #[track_caller] + #[inline] + fn pop_trace_idx(&mut self) -> usize { + self.trace_stack + .pop() + .expect("more traces were filled than started") + } + + /// Starts tracking a new trace. + /// + /// Invoked on [Inspector::call]. + #[allow(clippy::too_many_arguments)] + pub fn start_trace_on_call( + &mut self, address: Address, input_data: Bytes, value: U256, + kind: CallKind, caller: Address, mut gas_limit: u64, + maybe_precompile: Option, tx_gas_limit: u64, depth: usize, + ) { + // This will only be true if the inspector is configured to exclude + // precompiles and the call is to a precompile + let push_kind = if maybe_precompile.unwrap_or(false) { + // We don't want to track precompiles + PushTraceKind::PushOnly + } else { + PushTraceKind::PushAndAttachToParent + }; + + if self.trace_stack.is_empty() { + // this is the root call which should get the original gas limit of + // the transaction, because initialization costs are + // already subtracted from gas_limit For the root call + // this value should use the transaction's gas limit See and + gas_limit = tx_gas_limit; + } + + self.trace_stack.push(self.traces.push_trace( + 0, + push_kind, + CallTrace { + depth, + address, + kind, + data: input_data, + value, + status: InstructionResult::Continue, + caller, + maybe_precompile, + gas_limit, + ..Default::default() + }, + )); + } + + /// Fills the current trace with the outcome of a call. + /// + /// Invoked on [Inspector::call_end]. + /// + /// # Panics + /// + /// This expects an existing trace [Self::start_trace_on_call] + pub fn fill_trace_on_call_end( + &mut self, result: InterpreterResult, created_address: Option
, + gas_spent: u64, + ) { + let InterpreterResult { + result, + output, + gas: _, + } = result; + + let trace_idx = self.pop_trace_idx(); + let trace = &mut self.traces.arena[trace_idx].trace; + + if trace_idx == 0 { + // this is the root call which should get the gas used of the + // transaction refunds are applied after execution, + // which is when the root call ends + // Conflux have no refund + trace.gas_used = gas_used(gas_spent, 0); + } else { + trace.gas_used = gas_spent; + } + + trace.status = result; + trace.success = trace.status.is_ok(); + trace.output = output.clone(); + + self.last_call_return_data = Some(output); + + if let Some(address) = created_address { + // A new contract was created via CREATE + trace.address = address; + } + } + + /// Starts tracking a step + /// + /// Invoked on [Inspector::step] + /// + /// # Panics + /// + /// This expects an existing [CallTrace], in other words, this panics if not + /// within the context of a call. + pub fn start_step(&mut self, interp: &dyn InterpreterInfo, depth: u64) { + let trace_idx = self.last_trace_idx(); + let trace = &mut self.traces.arena[trace_idx]; + + self.step_stack.push(StackStep { + trace_idx, + step_idx: trace.trace.steps.len(), + }); + + let memory = self + .config + .record_memory_snapshots + .then(|| RecordedMemory::new(interp.mem().to_vec())) + .unwrap_or_default(); + + let stack = if self.config.record_stack_snapshots.is_full() { + Some( + interp + .stack() + .into_iter() + .map(|v| convert_u256(v)) + .collect(), + ) + } else { + None + }; + + let op = OpCode::new(interp.current_opcode()) + .or_else(|| { + // if the opcode is invalid, we'll use the invalid opcode to + // represent it because this is invoked before + // the opcode is executed, the evm will eventually return a + // `Halt` with invalid/unknown opcode as result + let invalid_opcode = 0xfe; + OpCode::new(invalid_opcode) + }) + .expect("is valid opcode;"); + + trace.trace.steps.push(CallTraceStep { + depth, + pc: interp.program_counter() as usize, + op, + contract: convert_h160(interp.contract_address()), + stack, + push_stack: None, + memory_size: memory.len(), + memory, + gas_remaining: self.gas_inspector.gas_remaining(), + gas_refund_counter: 0, // conflux has no gas refund + + // fields will be populated end of call + gas_cost: 0, + storage_change: None, + status: InstructionResult::Continue, + }); + } + + /// Fills the current trace with the output of a step. + /// + /// Invoked on [Inspector::step_end]. + pub fn fill_step_on_step_end(&mut self, interp: &dyn InterpreterInfo) { + let StackStep { + trace_idx, + step_idx, + } = self + .step_stack + .pop() + .expect("can't fill step without starting a step first"); + let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx]; + + if self.config.record_stack_snapshots.is_pushes() { + let num_pushed = stack_push_count(step.op.get()); + let start = interp.stack().len() - num_pushed; + let push_stack = interp.stack()[start..].to_vec(); + step.push_stack = + Some(push_stack.into_iter().map(|v| convert_u256(v)).collect()); + } + + if self.config.record_memory_snapshots { + // resize memory so opcodes that allocated memory is correctly + // displayed + if interp.mem().len() > step.memory.len() { + step.memory.resize(interp.mem().len()); + } + } + if self.config.record_state_diff { + let _op = step.op.get(); + + // TODO setup the storage_change + } + + // The gas cost is the difference between the recorded gas remaining at + // the start of the step the remaining gas here, at the end of + // the step. + // todo(evm-inspector): Figure out why this can overflow. https://github.com/paradigmxyz/evm-inspectors/pull/38 + step.gas_cost = step + .gas_remaining + .saturating_sub(self.gas_inspector.gas_remaining()); + + // TODO set the status + // step.status = interp.instruction_result; + } +} diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index aa8c5d2591..c9f79a62d9 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -15,6 +15,10 @@ use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; use std::sync::Arc; use self::geth_tracer::{GethTracer, TracingInspectorConfig}; +use alloy_rpc_types_trace::geth::{ + CallConfig, GethDebugBuiltInTracerType, GethDefaultTracingOptions, + PreStateConfig, +}; #[derive(AsTracer, DrainTrace)] pub struct Observer { @@ -50,12 +54,23 @@ impl Observer { pub fn geth_tracer( config: TracingInspectorConfig, tx_gas_limit: u64, - machine: Arc, + machine: Arc, tracer_type: Option, + call_config: Option, + prestate_config: Option, + opcode_config: Option, ) -> Self { Observer { tracer: None, gas_man: None, - geth_tracer: Some(GethTracer::new(config, tx_gas_limit, machine)), + geth_tracer: Some(GethTracer::new( + config, + tx_gas_limit, + machine, + tracer_type, + call_config, + prestate_config, + opcode_config, + )), } } } diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 2eb129c058..dfb1865c2c 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -8,7 +8,7 @@ use alloy_rpc_types_trace::geth::{ }; use cfx_types::H256; use cfxcore::{ConsensusGraph, SharedConsensusGraph}; -use jsonrpc_core::{Error as RpcError, Result as JsonRpcResult}; +use jsonrpc_core::Result as JsonRpcResult; pub struct GethDebugHandler { consensus: SharedConsensusGraph, @@ -41,21 +41,17 @@ impl Debug for GethDebugHandler { if let Some(tracer_type) = &opts.tracer { match tracer_type { BuiltInTracer(builtin_tracer) => match builtin_tracer { - GethDebugBuiltInTracerType::FourByteTracer => { - return Err(RpcError::invalid_params("not supported")) - } + GethDebugBuiltInTracerType::FourByteTracer => (), GethDebugBuiltInTracerType::CallTracer => (), GethDebugBuiltInTracerType::PreStateTracer => (), GethDebugBuiltInTracerType::NoopTracer => { return Ok(GethTrace::NoopTracer(NoopFrame::default())) } GethDebugBuiltInTracerType::MuxTracer => { - return Err(RpcError::invalid_params("not supported")) + return Err(invalid_params_msg("not supported")) } }, - JsTracer(_) => { - return Err(RpcError::invalid_params("not supported")) - } + JsTracer(_) => return Err(invalid_params_msg("not supported")), } } From ea2955c9587b2227ed7b4df7770261e558713648 Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 29 Apr 2024 21:08:28 +0800 Subject: [PATCH 012/137] optimize code --- .../consensus_inner/consensus_executor.rs | 72 ++--------- .../src/observer/geth_tracer/db_adapter.rs | 12 +- .../src/observer/geth_tracer/mod.rs | 122 ++++++++++++------ .../observer/geth_tracer/tracing_inspector.rs | 11 +- .../src/observer/geth_tracer/utils.rs | 12 +- .../execute-helper/src/observer/mod.rs | 23 +--- crates/client/src/rpc/impls/eth/debug.rs | 22 +++- 7 files changed, 136 insertions(+), 138 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 5c8dddba29..067e13ce31 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -62,7 +62,7 @@ use crate::{ use cfx_execute_helper::{ estimation::{EstimateExt, EstimateRequest, EstimationContext}, exec_tracer::TransactionExecTraces, - observer::{geth_tracer::TracingInspectorConfig, Observer}, + observer::Observer, tx_outcome::make_process_tx_outcome, }; use cfx_executor::{ @@ -80,8 +80,8 @@ use cfx_executor::{ use cfx_vm_types::{Env, Spec}; use alloy_rpc_types_trace::geth::{ - GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingOptions, - GethTrace, + GethDebugBuiltInTracerType::*, GethDebugTracerType, + GethDebugTracingOptions, GethTrace, }; lazy_static! { @@ -2101,57 +2101,19 @@ impl ConsensusExecutionHandler { let tx_gas_limit = transaction.gas_limit().as_u64(); match &opts.tracer { Some(t) => match t { - GethDebugTracerType::BuiltInTracer(bt) => match bt { - GethDebugBuiltInTracerType::FourByteTracer => { - Observer::geth_tracer( - TracingInspectorConfig::none(), + GethDebugTracerType::BuiltInTracer(bt) => { + match bt { + FourByteTracer | CallTracer + | PreStateTracer => Observer::geth_tracer( tx_gas_limit, Arc::clone(&self.machine), - Some(GethDebugBuiltInTracerType::FourByteTracer), - None, - None, - None, - ) + opts.clone(), + ), + NoopTracer | MuxTracer => { + Observer::with_no_tracing() + } } - GethDebugBuiltInTracerType::CallTracer => { - let call_config = opts - .tracer_config - .clone() - .into_call_config() - .map_err(|e| format!("{e}"))?; - Observer::geth_tracer( - TracingInspectorConfig::from_geth_call_config(&call_config), - tx_gas_limit, - Arc::clone(&self.machine), - Some(GethDebugBuiltInTracerType::CallTracer), - Some(call_config), - None, - None, - ) - } - GethDebugBuiltInTracerType::PreStateTracer => { - let pre_state_config = opts - .tracer_config - .clone() - .into_pre_state_config() - .map_err(|e| format!("{e}"))?; - Observer::geth_tracer( - TracingInspectorConfig::from_geth_prestate_config(&pre_state_config), - tx_gas_limit, - Arc::clone(&self.machine), - Some(GethDebugBuiltInTracerType::PreStateTracer), - None, - Some(pre_state_config), - None, - ) - } - GethDebugBuiltInTracerType::NoopTracer => { - Observer::with_no_tracing() - } - GethDebugBuiltInTracerType::MuxTracer => { - Observer::with_no_tracing() - } - }, + } GethDebugTracerType::JsTracer(_) => { Observer::with_no_tracing() } @@ -2160,15 +2122,9 @@ impl ConsensusExecutionHandler { // default is opcode tracer { Observer::geth_tracer( - TracingInspectorConfig::from_geth_config( - &opts.config, - ), tx_gas_limit, Arc::clone(&self.machine), - None, - None, - None, - Some(opts.config) + opts.clone(), ) } } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs index 0986b7fcf6..0e5fd05a33 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs @@ -1,4 +1,4 @@ -use super::utils::{convert_h256, convert_u256, to_h160}; +use super::utils::{from_alloy_address, to_alloy_h256, to_alloy_u256}; use cfx_executor::state::State; use cfx_statedb::Error as StateDbError; use cfx_types::{AddressWithSpace, Space}; @@ -56,7 +56,7 @@ impl<'a> DatabaseRef for RevmDbAdapter<'a> { &self, address: Address, ) -> Result, Self::Error> { let space_address = AddressWithSpace { - address: to_h160(address), + address: from_alloy_address(address), space: self.space, }; let exist = self.state.exists(&space_address); @@ -64,14 +64,14 @@ impl<'a> DatabaseRef for RevmDbAdapter<'a> { let balance = self .state .balance(&space_address) - .map(|v| convert_u256(v))?; + .map(|v| to_alloy_u256(v))?; let nonce = self.state.nonce(&space_address).map(|v| v.as_u64())?; let code_hash = self .state .code_hash(&space_address) - .map(|v| convert_h256(v))?; + .map(|v| to_alloy_h256(v))?; // TODO code and code_hash need consider internal_contracts let _code = self.state.code(&space_address).map(|v| { @@ -104,7 +104,7 @@ impl<'a> DatabaseRef for RevmDbAdapter<'a> { &self, address: Address, index: U256, ) -> Result { let space_address = AddressWithSpace { - address: to_h160(address), + address: from_alloy_address(address), space: self.space, }; @@ -112,7 +112,7 @@ impl<'a> DatabaseRef for RevmDbAdapter<'a> { let result = self .state .storage_at(&space_address, &key) - .map(|v| convert_u256(v))?; + .map(|v| to_alloy_u256(v))?; return Ok(result); } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 34e61063b9..4cf3b18fe6 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -14,7 +14,7 @@ use cfx_types::H160; pub use config::{StackSnapshotType, TracingInspectorConfig}; use types::LogCallOrder; -use utils::{convert_h160, convert_h256, convert_u256}; +use utils::{to_alloy_address, to_alloy_h256, to_alloy_u256}; use super::fourbyte::FourByteInspector; use alloy_primitives::{Address, Bytes, LogData}; @@ -36,8 +36,9 @@ use cfx_executor::{ use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; use alloy_rpc_types_trace::geth::{ - CallConfig, GethDebugBuiltInTracerType, GethDefaultTracingOptions, - GethTrace, NoopFrame, PreStateConfig, + CallConfig, GethDebugBuiltInTracerType, GethDebugBuiltInTracerType::*, + GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, + PreStateConfig, }; use tracing_inspector::TracingInspector; @@ -46,8 +47,6 @@ use std::sync::Arc; pub struct GethTracer { inner: TracingInspector, // - tracer_type: Option, - // fourbyte_inspector: FourByteInspector, // tx_gas_limit: u64, @@ -58,61 +57,101 @@ pub struct GethTracer { // return_data: Bytes, // - call_config: Option, - // - prestate_config: Option, - // - opcode_config: Option, + opts: GethDebugTracingOptions, } impl GethTracer { pub fn new( - config: TracingInspectorConfig, tx_gas_limit: u64, - machine: Arc, tracer_type: Option, - call_config: Option, - prestate_config: Option, - opcode_config: Option, + tx_gas_limit: u64, machine: Arc, opts: GethDebugTracingOptions, ) -> Self { + let config = match opts.tracer { + Some(GethDebugTracerType::BuiltInTracer(builtin_tracer)) => { + match builtin_tracer { + FourByteTracer | NoopTracer | MuxTracer => { + TracingInspectorConfig::none() + } + CallTracer => { + let c = opts + .tracer_config + .clone() + .into_call_config() + .expect("should success"); + TracingInspectorConfig::from_geth_call_config(&c) + } + PreStateTracer => { + let c = opts + .tracer_config + .clone() + .into_pre_state_config() + .expect("should success"); + TracingInspectorConfig::from_geth_prestate_config(&c) + } + } + } + Some(GethDebugTracerType::JsTracer(_)) => { + TracingInspectorConfig::none() + } + None => TracingInspectorConfig::from_geth_config(&opts.config), + }; + Self { inner: TracingInspector::new(config, machine), - tracer_type, fourbyte_inspector: FourByteInspector::new(), tx_gas_limit, depth: 0, gas_left: tx_gas_limit, return_data: Bytes::default(), - call_config, - prestate_config, - opcode_config, + opts, + } + } + + fn tracer_type(&self) -> Option { + match self.opts.tracer.clone() { + Some(t) => match t { + GethDebugTracerType::BuiltInTracer(builtin_tracer) => { + Some(builtin_tracer) + } + GethDebugTracerType::JsTracer(_) => { + // not supported + Some(NoopTracer) + } + }, + None => None, } } + fn call_config(&self) -> Option { + self.opts.tracer_config.clone().into_call_config().ok() + } + + fn prestate_config(&self) -> Option { + self.opts.tracer_config.clone().into_pre_state_config().ok() + } + pub fn is_fourbyte_tracer(&self) -> bool { - self.tracer_type == Some(GethDebugBuiltInTracerType::FourByteTracer) + self.tracer_type() == Some(FourByteTracer) } pub fn gas_used(&self) -> u64 { self.tx_gas_limit - self.gas_left } pub fn drain(self) -> GethTrace { - let trace = match self.tracer_type { + let trace = match self.tracer_type() { Some(t) => match t { - GethDebugBuiltInTracerType::FourByteTracer => { - self.fourbyte_inspector.drain() - } - GethDebugBuiltInTracerType::CallTracer => { + FourByteTracer => self.fourbyte_inspector.drain(), + CallTracer => { let gas_used = self.gas_used(); - let opts = self.call_config.expect("should have config"); + let opts = self.call_config().expect("should have config"); let frame = self .inner .into_geth_builder() .geth_call_traces(opts, gas_used); GethTrace::CallTracer(frame) } - GethDebugBuiltInTracerType::PreStateTracer => { + PreStateTracer => { // TODO replace the empty state and db with a real state let gas_used = self.gas_used(); let opts = - self.prestate_config.expect("should have config"); + self.prestate_config().expect("should have config"); let result = ResultAndState { result: ExecutionResult::Revert { gas_used, @@ -128,18 +167,14 @@ impl GethTracer { .unwrap(); GethTrace::PreStateTracer(frame) } - GethDebugBuiltInTracerType::NoopTracer => { - GethTrace::NoopTracer(NoopFrame::default()) - } - GethDebugBuiltInTracerType::MuxTracer => { - // not supported + NoopTracer | MuxTracer => { GethTrace::NoopTracer(NoopFrame::default()) } }, None => { let gas_used = self.gas_used(); let return_value = self.return_data; - let opts = self.opcode_config.expect("should have config"); + let opts = self.opts.config; let frame = self.inner.into_geth_builder().geth_traces( gas_used, return_value, @@ -196,7 +231,7 @@ impl CallTracer for GethTracer { let parent = self.inner.active_trace().unwrap(); parent.trace.value } else { - convert_u256(params.value.value()) + to_alloy_u256(params.value.value()) }; // if calls to precompiles should be excluded, check whether this is a @@ -206,8 +241,8 @@ impl CallTracer for GethTracer { self.inner.is_precompile_call(&to, value, params.space) }); - let to = convert_h160(to); - let from = convert_h160(from); + let to = to_alloy_address(to); + let from = to_alloy_address(from); self.inner.start_trace_on_call( to, params.data.clone().unwrap_or_default().into(), @@ -270,10 +305,10 @@ impl CallTracer for GethTracer { if let Some(parent) = self.inner.active_trace() { parent.trace.value } else { - convert_u256(params.value.value()) + to_alloy_u256(params.value.value()) } } else { - convert_u256(params.value.value()) + to_alloy_u256(params.value.value()) }; self.inner.start_trace_on_call( @@ -281,7 +316,7 @@ impl CallTracer for GethTracer { params.data.clone().unwrap_or_default().into(), value, params.call_type.into(), - convert_h160(params.sender), + to_alloy_address(params.sender), params.gas.as_u64(), Some(false), params.gas.as_u64(), @@ -323,7 +358,7 @@ impl CallTracer for GethTracer { let create_address = if let Ok(FrameReturn { create_address, .. }) = result { - create_address.as_ref().map(|h| convert_h160(*h)) + create_address.as_ref().map(|h| to_alloy_address(*h)) } else { None }; @@ -384,7 +419,7 @@ impl OpcodeTracer for GethTracer { let trace = &mut self.inner.traces.arena[trace_idx]; trace.ordering.push(LogCallOrder::Log(trace.logs.len())); trace.logs.push(LogData::new_unchecked( - topics.iter().map(|f| convert_h256(*f)).collect(), + topics.iter().map(|f| to_alloy_h256(*f)).collect(), Bytes::from(data.to_vec()), )); } @@ -400,7 +435,8 @@ impl OpcodeTracer for GethTracer { let trace_idx = self.inner.last_trace_idx(); let trace = &mut self.inner.traces.arena[trace_idx].trace; - trace.selfdestruct_refund_target = Some(convert_h160(*target as H160)) + trace.selfdestruct_refund_target = + Some(to_alloy_address(*target as H160)) } } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs index 98b13f6545..e97b370682 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -4,7 +4,7 @@ use super::{ types::{ CallKind, CallTrace, CallTraceNode, CallTraceStep, RecordedMemory, }, - utils::{convert_h160, convert_u256, gas_used, stack_push_count}, + utils::{gas_used, stack_push_count, to_alloy_address, to_alloy_u256}, CallTraceArena, GethTraceBuilder, StackStep, TracingInspectorConfig, }; use cfx_types::{Space, H160}; @@ -309,7 +309,7 @@ impl TracingInspector { interp .stack() .into_iter() - .map(|v| convert_u256(v)) + .map(|v| to_alloy_u256(v)) .collect(), ) } else { @@ -331,7 +331,7 @@ impl TracingInspector { depth, pc: interp.program_counter() as usize, op, - contract: convert_h160(interp.contract_address()), + contract: to_alloy_address(interp.contract_address()), stack, push_stack: None, memory_size: memory.len(), @@ -363,8 +363,9 @@ impl TracingInspector { let num_pushed = stack_push_count(step.op.get()); let start = interp.stack().len() - num_pushed; let push_stack = interp.stack()[start..].to_vec(); - step.push_stack = - Some(push_stack.into_iter().map(|v| convert_u256(v)).collect()); + step.push_stack = Some( + push_stack.into_iter().map(|v| to_alloy_u256(v)).collect(), + ); } if self.config.record_memory_snapshots { diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs index 189e15ce04..1779326fb1 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs @@ -116,18 +116,20 @@ pub(crate) const fn stack_push_count(step_op: u8) -> usize { } } -// convert from cfx U256 to Revm U256 -pub fn convert_u256(u: U256) -> RU256 { +// convert from cfx U256 to alloy U256 +pub fn to_alloy_u256(u: U256) -> RU256 { let mut be_bytes: [u8; 32] = [0; 32]; u.to_big_endian(&mut be_bytes); RU256::from_be_bytes(be_bytes) } -pub fn convert_h160(h: H160) -> RAddress { RAddress::from_slice(h.as_bytes()) } +pub fn to_alloy_address(h: H160) -> RAddress { + RAddress::from_slice(h.as_bytes()) +} -pub fn convert_h256(h: H256) -> B256 { B256::from(h.0) } +pub fn to_alloy_h256(h: H256) -> B256 { B256::from(h.0) } -pub fn to_h160(address: RAddress) -> Address { +pub fn from_alloy_address(address: RAddress) -> Address { Address::from_slice(address.as_slice()) } diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index c9f79a62d9..ed9af2cebe 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -14,11 +14,8 @@ use cfx_executor::{ use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; use std::sync::Arc; -use self::geth_tracer::{GethTracer, TracingInspectorConfig}; -use alloy_rpc_types_trace::geth::{ - CallConfig, GethDebugBuiltInTracerType, GethDefaultTracingOptions, - PreStateConfig, -}; +use self::geth_tracer::GethTracer; +use alloy_rpc_types_trace::geth::GethDebugTracingOptions; #[derive(AsTracer, DrainTrace)] pub struct Observer { @@ -53,24 +50,12 @@ impl Observer { } pub fn geth_tracer( - config: TracingInspectorConfig, tx_gas_limit: u64, - machine: Arc, tracer_type: Option, - call_config: Option, - prestate_config: Option, - opcode_config: Option, + tx_gas_limit: u64, machine: Arc, opts: GethDebugTracingOptions, ) -> Self { Observer { tracer: None, gas_man: None, - geth_tracer: Some(GethTracer::new( - config, - tx_gas_limit, - machine, - tracer_type, - call_config, - prestate_config, - opcode_config, - )), + geth_tracer: Some(GethTracer::new(tx_gas_limit, machine, opts)), } } } diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index dfb1865c2c..5bd9754ac0 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -42,8 +42,26 @@ impl Debug for GethDebugHandler { match tracer_type { BuiltInTracer(builtin_tracer) => match builtin_tracer { GethDebugBuiltInTracerType::FourByteTracer => (), - GethDebugBuiltInTracerType::CallTracer => (), - GethDebugBuiltInTracerType::PreStateTracer => (), + GethDebugBuiltInTracerType::CallTracer => { + // pre check config + let _ = opts + .tracer_config + .clone() + .into_call_config() + .map_err(|e| { + invalid_params_msg(&e.to_string()) + })?; + () + } + GethDebugBuiltInTracerType::PreStateTracer => { + // pre check config + let _ = opts + .tracer_config + .clone() + .into_pre_state_config() + .map_err(|e| invalid_params_msg(&e.to_string()))?; + () + } GethDebugBuiltInTracerType::NoopTracer => { return Ok(GethTrace::NoopTracer(NoopFrame::default())) } From 4f4d4b68daf0c967c27a392b7c910dbe2aa2b782 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:07:27 +0800 Subject: [PATCH 013/137] save --- .../cfxcore/core/src/transaction_pool/mod.rs | 1 + crates/client/src/rpc/impls/eth.rs | 1 + crates/primitives/src/block.rs | 2 + crates/primitives/src/transaction/mod.rs | 20 ++++-- .../src/transaction/native_transaction.rs | 25 ++----- tests/evm_space/eip1559_test.py | 65 +++++++++++++++++++ 6 files changed, 88 insertions(+), 26 deletions(-) create mode 100755 tests/evm_space/eip1559_test.py diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index d609ef3d82..7b5799eabd 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -420,6 +420,7 @@ impl TransactionPool { // key after basic verification. match self.data_man.recover_unsigned_tx(&transactions) { Ok(signed_trans) => { + info!("tx: {:?}", signed_trans); let account_cache = self.get_best_state_account_cache(); let mut inner = self.inner.write_with_metric(&INSERT_TXS_ENQUEUE_LOCK); diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 1bd7142970..e17f370bc2 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -676,6 +676,7 @@ impl Eth for EthHandler { "Can not recover pubkey for Ethereum like tx. Conflux eSpace only supports EIP-155 rather than EIP-1559 or other format transactions." )); } + info!("tx: {:?}", tx); let r = self.send_transaction_with_signature(tx)?; Ok(r) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index dce0957ff8..b634e184e9 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -15,6 +15,7 @@ use std::{ hash::Hasher, sync::Arc, }; +use log::info; pub type BlockNumber = u64; pub type BlockHeight = u64; @@ -149,6 +150,7 @@ impl Block { if rlp.as_raw().len() != rlp.payload_info()?.total() { return Err(DecoderError::RlpIsTooBig); } + info!("1 {:?}", rlp.as_raw()); let signed_transactions = rlp.as_list()?; let mut transactions = Vec::with_capacity(signed_transactions.len()); diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 9f5771ce7a..9cc406d889 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -371,27 +371,38 @@ impl Transaction { // signatures. pub fn signature_hash(&self) -> H256 { let mut s = RlpStream::new(); + let mut type_prefix= vec![]; match self { Transaction::Native(TypedNativeTransaction::Cip155(tx)) => { s.append(tx); } Transaction::Native(TypedNativeTransaction::Cip1559(tx)) => { s.append(tx); + type_prefix.extend_from_slice(TYPED_NATIVE_TX_PREFIX); + type_prefix.push(CIP1559_TYPE); } Transaction::Native(TypedNativeTransaction::Cip2930(tx)) => { s.append(tx); + type_prefix.extend_from_slice(TYPED_NATIVE_TX_PREFIX); + type_prefix.push(CIP2930_TYPE); } Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { s.append(tx); } Transaction::Ethereum(EthereumTransaction::Eip1559(tx)) => { s.append(tx); + type_prefix.push(EIP1559_TYPE); } Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { s.append(tx); + type_prefix.push(EIP2930_TYPE); } - } - keccak(s.as_raw()) + }; + let encoded = s.as_raw(); + let mut out = vec![0; type_prefix.len() + encoded.len()]; + out[0..type_prefix.len()].copy_from_slice(&type_prefix); + out[type_prefix.len()..].copy_from_slice(&encoded); + keccak(&out) } pub fn space(&self) -> Space { @@ -692,10 +703,7 @@ impl Decodable for TransactionWithSignature { fn decode(d: &Rlp) -> Result { let hash = keccak(d.as_raw()); let rlp_size = Some(d.as_raw().len()); - // Check item count of TransactionWithSignatureSerializePart - if d.item_count()? != 4 && d.item_count()? != 9 { - return Err(DecoderError::RlpIncorrectListLen); - } + // The item count of TransactionWithSignatureSerializePart is checked in its decoding. let transaction = d.as_val()?; Ok(TransactionWithSignature { transaction, diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs index 9557150442..60953d2e27 100644 --- a/crates/primitives/src/transaction/native_transaction.rs +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -7,6 +7,7 @@ use cfx_types::{AddressWithSpace, H256, U256}; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; use serde_derive::{Deserialize, Serialize}; +use crate::transaction::AccessListItem; #[derive( Default, @@ -87,7 +88,8 @@ pub struct Cip2930Transaction { pub epoch_height: u64, pub chain_id: u32, pub data: Bytes, - pub access_list: NativeAccessList, + // We do not use `AccessList` here because we need `Vec` for rlp derive. + pub access_list: Vec, } #[derive( @@ -112,25 +114,8 @@ pub struct Cip1559Transaction { pub epoch_height: u64, pub chain_id: u32, pub data: Bytes, - pub access_list: NativeAccessList, -} - -#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct NativeAccessList { - inner: AccessList, -} - -impl Encodable for NativeAccessList { - fn rlp_append(&self, s: &mut RlpStream) { - s.append_list(self.inner.as_ref()); - } -} - -impl Decodable for NativeAccessList { - fn decode(rlp: &Rlp) -> Result { - let inner = rlp.list_at(0)?; - Ok(Self { inner }) - } + // We do not use `AccessList` here because we need `Vec` for rlp derive. + pub access_list: Vec, } macro_rules! access_common_ref { diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py new file mode 100755 index 0000000000..bc36493b26 --- /dev/null +++ b/tests/evm_space/eip1559_test.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +import os, sys +sys.path.insert(1, os.path.join(sys.path[0], '..')) + +from test_framework.util import * +from conflux.config import default_config +from base import Web3Base +from test_framework.blocktools import encode_hex_0x +from conflux.address import b32_address_to_hex + + +class Eip1559Test(Web3Base): + def set_test_params(self): + self.num_nodes = 2 + + def run_test(self): + self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] + self.cfxAccount = self.rpc.GENESIS_ADDR + print(f'Using Conflux account {self.cfxAccount}') + # initialize EVM account + self.evmAccount = self.w3.eth.account.privateKeyToAccount(self.DEFAULT_TEST_ACCOUNT_KEY) + print(f'Using EVM account {self.evmAccount.address}') + self.cross_space_transfer(self.evmAccount.address, 1 * 10 ** 18) + assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(1 * 10 ** 18)) + + x = b32_address_to_hex("NET10:TYPE.USER:AAR8JZYBZV0FHZREAV49SYXNZUT8S0JT1ASMXX99XH") + y = b32_address_to_hex('NET10:TYPE.BUILTIN:AAEJUAAAAAAAAAAAAAAAAAAAAAAAAAAAA27GYVFYR7') + ret = self.nodes[0].debug_getTransactionsByEpoch("0x1") + assert_equal(len(ret), 1) + + nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + signed = self.evmAccount.signTransaction({ + "type": "0x2", + "to": self.evmAccount.address, + "value": 1, + "gas": 210000, + 'maxFeePerGas': 1, + 'maxPriorityFeePerGas': 1, + "nonce": nonce, + "chainId": 10, + }) + print(signed) + + return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + self.rpc.generate_block(1) + self.rpc.generate_blocks(20, 1) + receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) + assert_equal(receipt["status"], 1) + # TODO check EIP1559 gas usage + # assert_equal(receipt["gasUsed"], 210000 / 4 * 3) + assert_equal(receipt["txExecErrorMsg"], None) + + tx = self.w3.eth.get_transaction(return_tx_hash) + assert_equal(receipt["status"], 1) + + # Check if another node can decode EIP1559 transactions + ret1 = self.nodes[0].debug_getTransactionsByEpoch(hex(receipt["blockNumber"])) + ret2 = self.nodes[1].debug_getTransactionsByBlock(encode_hex_0x(tx["blockHash"])) + assert_equal(len(ret1), 1) + assert_equal(len(ret2), 1) + assert_equal(ret1[0], ret2[0]) + + +if __name__ == "__main__": + Eip1559Test().main() \ No newline at end of file From 4ff42d7ae101fc972b4796ca420127e7fa5854e3 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 30 Apr 2024 15:24:51 +0800 Subject: [PATCH 014/137] op code --- .../src/observer/geth_tracer/builder/geth.rs | 6 +- .../src/observer/geth_tracer/db_adapter.rs | 127 ---------------- .../src/observer/geth_tracer/gas.rs | 2 +- .../src/observer/geth_tracer/mod.rs | 43 ++---- .../src/observer/geth_tracer/types.rs | 140 +----------------- 5 files changed, 18 insertions(+), 300 deletions(-) delete mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs index 0802c010ce..11ed49fab5 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -1,5 +1,4 @@ //! Geth trace builder - use crate::observer::geth_tracer::{ types::{CallTraceNode, CallTraceStepStackItem}, TracingInspectorConfig, @@ -12,7 +11,7 @@ use alloy_rpc_types_trace::geth::{ }; use revm::{ db::DatabaseRef, - primitives::{AccountInfo, ResultAndState, KECCAK_EMPTY}, + primitives::{AccountInfo, State, KECCAK_EMPTY}, }; use std::collections::{BTreeMap, HashMap, VecDeque}; @@ -234,8 +233,7 @@ impl GethTraceBuilder { /// * `diff_mode` - if prestate is in diff or prestate mode. /// * `db` - The database to fetch state pre-transaction execution. pub fn geth_prestate_traces( - &self, ResultAndState { state, .. }: &ResultAndState, - prestate_config: PreStateConfig, db: DB, + &self, state: State, prestate_config: PreStateConfig, db: DB, ) -> Result { let account_diffs = state.iter().map(|(addr, acc)| (*addr, acc)); diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs deleted file mode 100644 index 0e5fd05a33..0000000000 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/db_adapter.rs +++ /dev/null @@ -1,127 +0,0 @@ -use super::utils::{from_alloy_address, to_alloy_h256, to_alloy_u256}; -use cfx_executor::state::State; -use cfx_statedb::Error as StateDbError; -use cfx_types::{AddressWithSpace, Space}; -use revm::{ - db::{DatabaseRef, EmptyDB}, - primitives::{AccountInfo, Address, Bytecode, Bytes, B256, U256}, -}; -use std::{convert::Infallible, error, fmt}; - -// An adapter impl revm::db::DatabaseRef trait -pub struct RevmDbAdapter<'a> { - state: &'a State, - empty_db: EmptyDB, - space: Space, -} - -impl<'a> RevmDbAdapter<'a> { - fn new(state: &'a State) -> Self { - RevmDbAdapter { - state, - empty_db: EmptyDB::new(), - space: Space::Ethereum, // default is ethereum - } - } -} - -#[derive(Debug)] -pub enum RevmDbError { - Custom(String), -} - -impl error::Error for RevmDbError {} - -impl fmt::Display for RevmDbError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Custom(s) => write!(f, "Custom: {s}"), - } - } -} - -impl From for RevmDbError { - fn from(e: StateDbError) -> Self { RevmDbError::Custom(format!("{e}")) } -} - -impl From for RevmDbError { - fn from(e: Infallible) -> Self { RevmDbError::Custom(format!("{e}")) } -} - -impl<'a> DatabaseRef for RevmDbAdapter<'a> { - type Error = RevmDbError; - - /// Get basic account information. - fn basic_ref( - &self, address: Address, - ) -> Result, Self::Error> { - let space_address = AddressWithSpace { - address: from_alloy_address(address), - space: self.space, - }; - let exist = self.state.exists(&space_address); - if let Ok(true) = exist { - let balance = self - .state - .balance(&space_address) - .map(|v| to_alloy_u256(v))?; - - let nonce = self.state.nonce(&space_address).map(|v| v.as_u64())?; - - let code_hash = self - .state - .code_hash(&space_address) - .map(|v| to_alloy_h256(v))?; - - // TODO code and code_hash need consider internal_contracts - let _code = self.state.code(&space_address).map(|v| { - v.map(|b| Bytecode::new_raw(Bytes::from(b.to_vec()))) - })?; - - let acc_info = AccountInfo { - balance, - nonce, - code_hash, - code: None, - }; - return Ok(Some(acc_info)); - } - - self.empty_db.basic_ref(address).map_err(|e| e.into()) - } - - /// TODO(pana) Get account code by its hash. - fn code_by_hash_ref( - &self, code_hash: B256, - ) -> Result { - self.empty_db - .code_by_hash_ref(code_hash) - .map_err(Into::into) - } - - /// Get storage value of address at index. - fn storage_ref( - &self, address: Address, index: U256, - ) -> Result { - let space_address = AddressWithSpace { - address: from_alloy_address(address), - space: self.space, - }; - - let key = index.to_be_bytes::<32>(); // TODO: check big endian is correct - let result = self - .state - .storage_at(&space_address, &key) - .map(|v| to_alloy_u256(v))?; - - return Ok(result); - } - - /// Get block hash by block number. - /// unused - fn block_hash_ref(&self, number: U256) -> Result { - let res = self.empty_db.block_hash_ref(number)?; - - Ok(res) - } -} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs index 1cbd58f0c6..a46b78c67e 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs @@ -1,5 +1,4 @@ /// Helper [Inspector] that keeps track of gas. -#[allow(dead_code)] #[derive(Clone, Copy, Debug, Default)] pub struct GasInspector { gas_remaining: u64, @@ -9,6 +8,7 @@ pub struct GasInspector { impl GasInspector { pub fn gas_remaining(&self) -> u64 { self.gas_remaining } + #[allow(dead_code)] pub fn last_gas_cost(&self) -> u64 { self.last_gas_cost } pub fn set_gas_remainning(&mut self, remainning: u64) { diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 4cf3b18fe6..f048557719 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -2,7 +2,6 @@ mod arena; mod builder; mod config; -mod db_adapter; mod gas; mod tracing_inspector; mod types; @@ -21,7 +20,7 @@ use alloy_primitives::{Address, Bytes, LogData}; use revm::{ db::InMemoryDB, interpreter::{Gas, InstructionResult, InterpreterResult}, - primitives::{ExecutionResult, ResultAndState, State}, + primitives::State, }; use cfx_executor::{ @@ -149,21 +148,14 @@ impl GethTracer { } PreStateTracer => { // TODO replace the empty state and db with a real state - let gas_used = self.gas_used(); let opts = self.prestate_config().expect("should have config"); - let result = ResultAndState { - result: ExecutionResult::Revert { - gas_used, - output: Bytes::default(), - }, - state: State::default(), - }; + let state = State::default(); let db = InMemoryDB::default(); let frame = self .inner .into_geth_builder() - .geth_prestate_traces(&result, opts, db) + .geth_prestate_traces(state, opts, db) .unwrap(); GethTrace::PreStateTracer(frame) } @@ -451,30 +443,18 @@ pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { Ok(_r) => InstructionResult::Return, // todo check this Err(err) => match err { Error::OutOfGas => InstructionResult::OutOfGas, - Error::BadJumpDestination { destination: _ } => { - InstructionResult::InvalidJump - } - Error::BadInstruction { instruction: _ } => { - InstructionResult::OpcodeNotFound - } - Error::StackUnderflow { - instruction: _, - wanted: _, - on_stack: _, - } => InstructionResult::StackUnderflow, + Error::BadJumpDestination { .. } => InstructionResult::InvalidJump, + Error::BadInstruction { .. } => InstructionResult::OpcodeNotFound, + Error::StackUnderflow { .. } => InstructionResult::StackUnderflow, Error::OutOfStack { .. } => InstructionResult::StackOverflow, Error::SubStackUnderflow { .. } => { InstructionResult::StackUnderflow } - Error::OutOfSubStack { - wanted: _, - limit: _, - } => InstructionResult::StackOverflow, + Error::OutOfSubStack { .. } => InstructionResult::StackOverflow, Error::InvalidSubEntry => InstructionResult::NotActivated, // - Error::NotEnoughBalanceForStorage { - required: _, - got: _, - } => InstructionResult::OutOfFunds, + Error::NotEnoughBalanceForStorage { .. } => { + InstructionResult::OutOfFunds + } Error::ExceedStorageLimit => InstructionResult::OutOfGas, /* treat storage as gas */ Error::BuiltIn(_) => InstructionResult::PrecompileError, Error::InternalContract(_) => InstructionResult::PrecompileError, /* treat internalContract as builtIn */ @@ -486,8 +466,7 @@ pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { Error::OutOfBounds => InstructionResult::OutOfOffset, Error::Reverted => InstructionResult::Revert, Error::InvalidAddress(_) => todo!(), /* when selfdestruct refund */ - // address is invalid will - // emit this error + // address is invalid will emit this error Error::ConflictAddress(_) => InstructionResult::CreateCollision, }, }; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs index 787600416c..ea2396afe6 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs @@ -3,13 +3,9 @@ use crate::observer::geth_tracer::{ config::TraceStyle, utils, utils::convert_memory, }; -use alloy_primitives::{Address, Bytes, LogData, U256, U64}; -use alloy_rpc_types_trace::{ - geth::{CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog}, - parity::{ - Action, ActionType, CallAction, CallOutput, CallType, CreateAction, - CreateOutput, SelfdestructAction, TraceOutput, TransactionTrace, - }, +use alloy_primitives::{Address, Bytes, LogData, U256}; +use alloy_rpc_types_trace::geth::{ + CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog, }; use cfx_vm_types::CallType as CfxCallType; use revm::interpreter::{ @@ -213,64 +209,6 @@ impl CallTraceNode { self.trace.selfdestruct_refund_target.is_some() } - /// Converts this node into a parity `TransactionTrace` - pub fn parity_transaction_trace( - &self, trace_address: Vec, - ) -> TransactionTrace { - let action = self.parity_action(); - let result = if self.trace.is_error() && !self.trace.is_revert() { - // if the trace is a selfdestruct or an error that is not a revert, - // the result is None - None - } else { - Some(self.parity_trace_output()) - }; - let error = self.trace.as_error_msg(TraceStyle::Parity); - TransactionTrace { - action, - error, - result, - trace_address, - subtraces: self.children.len(), - } - } - - /// Returns the `Output` for a parity trace - pub fn parity_trace_output(&self) -> TraceOutput { - match self.kind() { - CallKind::Call - | CallKind::StaticCall - | CallKind::CallCode - | CallKind::DelegateCall => TraceOutput::Call(CallOutput { - gas_used: U64::from(self.trace.gas_used), - output: self.trace.output.clone(), - }), - CallKind::Create | CallKind::Create2 => { - TraceOutput::Create(CreateOutput { - gas_used: U64::from(self.trace.gas_used), - code: self.trace.output.clone(), - address: self.trace.address, - }) - } - } - } - - /// If the trace is a selfdestruct, returns the `Action` for a parity trace. - pub fn parity_selfdestruct_action(&self) -> Option { - if self.is_selfdestruct() { - Some(Action::Selfdestruct(SelfdestructAction { - address: self.trace.address, - refund_address: self - .trace - .selfdestruct_refund_target - .unwrap_or_default(), - balance: self.trace.value, - })) - } else { - None - } - } - /// If the trace is a selfdestruct, returns the `CallFrame` for a geth call /// trace pub fn geth_selfdestruct_call_trace(&self) -> Option { @@ -287,50 +225,6 @@ impl CallTraceNode { } } - /// If the trace is a selfdestruct, returns the `TransactionTrace` for a - /// parity trace. - pub fn parity_selfdestruct_trace( - &self, trace_address: Vec, - ) -> Option { - let trace = self.parity_selfdestruct_action()?; - Some(TransactionTrace { - action: trace, - error: None, - result: None, - trace_address, - subtraces: 0, - }) - } - - /// Returns the `Action` for a parity trace. - /// - /// Caution: This does not include the selfdestruct action, if the trace is - /// a selfdestruct, since those are handled in addition to the call - /// action. - pub fn parity_action(&self) -> Action { - match self.kind() { - CallKind::Call - | CallKind::StaticCall - | CallKind::CallCode - | CallKind::DelegateCall => Action::Call(CallAction { - from: self.trace.caller, - to: self.trace.address, - value: self.trace.value, - gas: U64::from(self.trace.gas_limit), - input: self.trace.data.clone(), - call_type: self.kind().into(), - }), - CallKind::Create | CallKind::Create2 => { - Action::Create(CreateAction { - from: self.trace.caller, - value: self.trace.value, - gas: U64::from(self.trace.gas_limit), - init: self.trace.data.clone(), - }) - } - } - } - /// Converts this call trace into an _empty_ geth [CallFrame] pub fn geth_empty_call_frame(&self, include_logs: bool) -> CallFrame { let mut call_frame = CallFrame { @@ -467,7 +361,7 @@ impl From for CallKind { impl From for CallKind { fn from(ct: CfxCallType) -> Self { match ct { - CfxCallType::None => Self::CallCode, /* TODO(pana) check this is appropriate, or add a None variant to CallKind */ + CfxCallType::None => Self::Create, CfxCallType::Call => Self::Call, CfxCallType::CallCode => Self::CallCode, CfxCallType::DelegateCall => Self::DelegateCall, @@ -476,32 +370,6 @@ impl From for CallKind { } } -impl From for ActionType { - fn from(kind: CallKind) -> Self { - match kind { - CallKind::Call - | CallKind::StaticCall - | CallKind::DelegateCall - | CallKind::CallCode => Self::Call, - CallKind::Create => Self::Create, - CallKind::Create2 => Self::Create, - } - } -} - -impl From for CallType { - fn from(ty: CallKind) -> Self { - match ty { - CallKind::Call => Self::Call, - CallKind::StaticCall => Self::StaticCall, - CallKind::CallCode => Self::CallCode, - CallKind::DelegateCall => Self::DelegateCall, - CallKind::Create => Self::None, - CallKind::Create2 => Self::None, - } - } -} - pub(crate) struct CallTraceStepStackItem<'a> { /// The trace node that contains this step pub(crate) trace_node: &'a CallTraceNode, From 9788cd12c2b99106eb6bf2d9331366fbf8545d30 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 30 Apr 2024 15:27:46 +0800 Subject: [PATCH 015/137] remove unused crate --- Cargo.lock | 175 +- Cargo.toml | 1 - crates/rpc_types_trace/Cargo.toml | 22 - crates/rpc_types_trace/src/block.rs | 47 - crates/rpc_types_trace/src/common.rs | 36 - crates/rpc_types_trace/src/geth/call.rs | 138 - crates/rpc_types_trace/src/geth/four_byte.rs | 42 - crates/rpc_types_trace/src/geth/mod.rs | 627 --- crates/rpc_types_trace/src/geth/mux.rs | 141 - crates/rpc_types_trace/src/geth/noop.rs | 35 - crates/rpc_types_trace/src/geth/pre_state.rs | 384 -- crates/rpc_types_trace/src/lib.rs | 5 - crates/rpc_types_trace/src/parity.rs | 761 --- crates/rpc_types_trace/src/state.rs | 120 - .../test_data/call_tracer/default.json | 21 - .../test_data/call_tracer/legacy.json | 19 - .../test_data/call_tracer/only_top_call.json | 10 - .../test_data/call_tracer/with_log.json | 20 - .../test_data/default/structlogs_01.json | 4112 ----------------- .../test_data/pre_state_tracer/default.json | 20 - .../test_data/pre_state_tracer/diff_mode.json | 41 - .../test_data/pre_state_tracer/legacy.json | 25 - 22 files changed, 17 insertions(+), 6785 deletions(-) delete mode 100644 crates/rpc_types_trace/Cargo.toml delete mode 100644 crates/rpc_types_trace/src/block.rs delete mode 100644 crates/rpc_types_trace/src/common.rs delete mode 100644 crates/rpc_types_trace/src/geth/call.rs delete mode 100644 crates/rpc_types_trace/src/geth/four_byte.rs delete mode 100644 crates/rpc_types_trace/src/geth/mod.rs delete mode 100644 crates/rpc_types_trace/src/geth/mux.rs delete mode 100644 crates/rpc_types_trace/src/geth/noop.rs delete mode 100644 crates/rpc_types_trace/src/geth/pre_state.rs delete mode 100644 crates/rpc_types_trace/src/lib.rs delete mode 100644 crates/rpc_types_trace/src/parity.rs delete mode 100644 crates/rpc_types_trace/src/state.rs delete mode 100644 crates/rpc_types_trace/test_data/call_tracer/default.json delete mode 100644 crates/rpc_types_trace/test_data/call_tracer/legacy.json delete mode 100644 crates/rpc_types_trace/test_data/call_tracer/only_top_call.json delete mode 100644 crates/rpc_types_trace/test_data/call_tracer/with_log.json delete mode 100644 crates/rpc_types_trace/test_data/default/structlogs_01.json delete mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/default.json delete mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json delete mode 100644 crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json diff --git a/Cargo.lock b/Cargo.lock index 90e130a22b..e302611aa3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -935,17 +935,6 @@ dependencies = [ "tokio 1.26.0", ] -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", -] - [[package]] name = "bstr" version = "1.4.0" @@ -1945,18 +1934,6 @@ dependencies = [ "short-hex-str", ] -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "const-hex" version = "1.11.3" @@ -2930,12 +2907,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encoding_rs" version = "0.8.32" @@ -3688,7 +3659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ "aho-corasick", - "bstr 1.4.0", + "bstr", "fnv", "log", "regex", @@ -6697,12 +6668,6 @@ dependencies = [ "regex-syntax 0.6.29", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - [[package]] name = "regex-syntax" version = "0.6.29" @@ -6915,19 +6880,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rpc-types-trace" -version = "2.0.2" -dependencies = [ - "alloy-primitives", - "cfx-bytes", - "cfx-serde", - "cfx-types", - "serde", - "serde_json", - "similar-asserts", -] - [[package]] name = "ruint" version = "1.12.1" @@ -7553,26 +7505,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "similar" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" -dependencies = [ - "bstr 0.2.17", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e041bb827d1bfca18f213411d51b665309f1afb37a04a5d1464530e13779fc0f" -dependencies = [ - "console", - "similar", -] - [[package]] name = "siphasher" version = "0.3.10" @@ -8993,7 +8925,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -9002,13 +8934,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -9017,16 +8949,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -9035,29 +8958,13 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -9066,90 +8973,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" - [[package]] name = "winnow" version = "0.3.6" diff --git a/Cargo.toml b/Cargo.toml index 9e023e7031..82216e0efb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,6 @@ members = [ "crates/util/treap-map", "crates/util/version", "crates/serde", - "crates/rpc_types_trace", ] resolver = "2" diff --git a/crates/rpc_types_trace/Cargo.toml b/crates/rpc_types_trace/Cargo.toml deleted file mode 100644 index df4bccbf4a..0000000000 --- a/crates/rpc_types_trace/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "rpc-types-trace" -edition = "2021" -version.workspace = true -authors.workspace = true -description.workspace = true -documentation.workspace = true -homepage.workspace = true -keywords.workspace = true -repository.workspace = true -license-file.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde.workspace = true -serde_json.workspace = true -alloy-primitives = { workspace = true, features = ["rlp", "serde"] } -cfx-types = { path = "../cfx_types" } -cfx-bytes = { path = "../cfx_bytes" } -cfx-serde = { path = "../serde" } -similar-asserts = "1.5" \ No newline at end of file diff --git a/crates/rpc_types_trace/src/block.rs b/crates/rpc_types_trace/src/block.rs deleted file mode 100644 index 8540eb7949..0000000000 --- a/crates/rpc_types_trace/src/block.rs +++ /dev/null @@ -1,47 +0,0 @@ -use cfx_types::{Address, H256, U256, U64}; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// BlockOverrides is a set of header fields to override. -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase", deny_unknown_fields)] -pub struct BlockOverrides { - /// Overrides the block number. - /// - /// For `eth_callMany` this will be the block number of the first simulated - /// block. Each following block increments its block number by 1 - // Note: geth uses `number`, erigon uses `blockNumber` - #[serde( - default, - skip_serializing_if = "Option::is_none", - alias = "blockNumber" - )] - pub number: Option, - /// Overrides the difficulty of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub difficulty: Option, - /// Overrides the timestamp of the block. - // Note: geth uses `time`, erigon uses `timestamp` - #[serde( - default, - skip_serializing_if = "Option::is_none", - alias = "timestamp" - )] - pub time: Option, - /// Overrides the gas limit of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub gas_limit: Option, - /// Overrides the coinbase address of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub coinbase: Option
, - /// Overrides the prevrandao of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub random: Option, - /// Overrides the basefee of the block. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub base_fee: Option, - /// A dictionary that maps blockNumber to a user-defined hash. It could be - /// queried from the solidity opcode BLOCKHASH. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub block_hash: Option>, -} diff --git a/crates/rpc_types_trace/src/common.rs b/crates/rpc_types_trace/src/common.rs deleted file mode 100644 index 9e0d5fa2a8..0000000000 --- a/crates/rpc_types_trace/src/common.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Types used by tracing backends. - -use cfx_types::H256 as TxHash; -use serde::{Deserialize, Serialize}; - -/// The result of a single transaction trace. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum TraceResult { - /// Untagged success variant - Success { - /// Trace results produced by the tracer - result: Ok, - /// transaction hash - #[serde(skip_serializing_if = "Option::is_none", rename = "txHash")] - tx_hash: Option, - }, - /// Untagged error variant - Error { - /// Trace failure produced by the tracer - error: Err, - /// transaction hash - #[serde(skip_serializing_if = "Option::is_none", rename = "txHash")] - tx_hash: Option, - }, -} - -impl TraceResult { - /// Returns the hash of the transaction that was traced. - pub const fn tx_hash(&self) -> Option { - *match self { - TraceResult::Success { tx_hash, .. } => tx_hash, - TraceResult::Error { tx_hash, .. } => tx_hash, - } - } -} diff --git a/crates/rpc_types_trace/src/geth/call.rs b/crates/rpc_types_trace/src/geth/call.rs deleted file mode 100644 index 5c2e1387ae..0000000000 --- a/crates/rpc_types_trace/src/geth/call.rs +++ /dev/null @@ -1,138 +0,0 @@ -use alloy_primitives::Bytes; -use cfx_serde::from_int_or_hex; -use cfx_types::{Address, H256 as B256, U256}; -use serde::{Deserialize, Serialize}; - -/// The response object for `debug_traceTransaction` with `"tracer": -/// "callTracer"`. -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallFrame { - /// The address of that initiated the call. - pub from: Address, - /// How much gas was left before the call. - #[serde(default, deserialize_with = "from_int_or_hex")] - pub gas: U256, - /// How much gas was used by the call. - #[serde(default, deserialize_with = "from_int_or_hex", rename = "gasUsed")] - pub gas_used: U256, - /// The address of the contract that was called. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub to: Option
, - /// Calldata input. - pub input: Bytes, - /// Output of the call, if any. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub output: Option, - /// Error message, if any. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub error: Option, - /// Why this call reverted, if it reverted. - #[serde( - default, - rename = "revertReason", - skip_serializing_if = "Option::is_none" - )] - pub revert_reason: Option, - /// Recorded child calls. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub calls: Vec, - /// Logs emitted by this call. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub logs: Vec, - /// Value transferred. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub value: Option, - /// The type of the call. - #[serde(rename = "type")] - pub typ: String, -} - -/// Represents a recorded call. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct CallLogFrame { - /// The address of the contract that was called. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub address: Option
, - /// The topics of the log. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub topics: Option>, - /// The data of the log. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub data: Option, -} - -/// The configuration for the call tracer. -#[derive( - Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct CallConfig { - /// When set to true, this will only trace the primary (top-level) call and - /// not any sub-calls. It eliminates the additional processing for each - /// call frame. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub only_top_call: Option, - /// When set to true, this will include the logs emitted by the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub with_log: Option, -} - -impl CallConfig { - /// Sets the only top call flag. - pub const fn only_top_call(mut self) -> Self { - self.only_top_call = Some(true); - self - } - - /// Sets the with log flag. - pub const fn with_log(mut self) -> Self { - self.with_log = Some(true); - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::geth::*; - - // See - const DEFAULT: &str = - include_str!("../../test_data/call_tracer/default.json"); - const LEGACY: &str = - include_str!("../../test_data/call_tracer/legacy.json"); - const ONLY_TOP_CALL: &str = - include_str!("../../test_data/call_tracer/only_top_call.json"); - const WITH_LOG: &str = - include_str!("../../test_data/call_tracer/with_log.json"); - - #[test] - fn test_serialize_call_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.config.disable_storage = Some(false); - opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::CallTracer, - )); - opts.tracing_options.tracer_config = serde_json::to_value(CallConfig { - only_top_call: Some(true), - with_log: Some(true), - }) - .unwrap() - .into(); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"disableStorage":false,"tracer":"callTracer","tracerConfig":{"onlyTopCall":true,"withLog":true}}"# - ); - } - - #[test] - fn test_deserialize_call_trace() { - // let _trace: CallFrame = serde_json::from_str(DEFAULT).unwrap(); - // let _trace: CallFrame = serde_json::from_str(LEGACY).unwrap(); - let _trace: CallFrame = serde_json::from_str(ONLY_TOP_CALL).unwrap(); - let _trace: CallFrame = serde_json::from_str(WITH_LOG).unwrap(); - } -} diff --git a/crates/rpc_types_trace/src/geth/four_byte.rs b/crates/rpc_types_trace/src/geth/four_byte.rs deleted file mode 100644 index d67e2816d9..0000000000 --- a/crates/rpc_types_trace/src/geth/four_byte.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Geth 4byte tracer types. - -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// The 4byte tracer response object. -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct FourByteFrame(pub BTreeMap); - -#[cfg(test)] -mod tests { - use super::*; - use crate::geth::*; - - const DEFAULT: &str = r#"{ - "0x27dc297e-128": 1, - "0x38cc4831-0": 2, - "0x524f3889-96": 1, - "0xadf59f99-288": 1, - "0xc281d19e-0": 1 - }"#; - - #[test] - fn test_serialize_four_byte_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::FourByteTracer, - )); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"tracer":"4byteTracer"}"# - ); - } - - #[test] - fn test_deserialize_four_byte_trace() { - let _trace: FourByteFrame = serde_json::from_str(DEFAULT).unwrap(); - } -} diff --git a/crates/rpc_types_trace/src/geth/mod.rs b/crates/rpc_types_trace/src/geth/mod.rs deleted file mode 100644 index 1986ae6716..0000000000 --- a/crates/rpc_types_trace/src/geth/mod.rs +++ /dev/null @@ -1,627 +0,0 @@ -#![allow(unused)] -use crate::{block::BlockOverrides, state::StateOverride}; -use alloy_primitives::Bytes; -use cfx_types::{H256 as B256, U256}; -use serde::{ - de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize, Serializer, -}; -use std::{collections::BTreeMap, time::Duration}; - -pub use call::*; -use four_byte::FourByteFrame; -use mux::*; -use noop::NoopFrame; -pub use pre_state::*; - -mod call; -mod four_byte; -mod mux; -mod noop; -mod pre_state; - -/// Result type for geth style transaction trace -pub type TraceResult = crate::common::TraceResult; - -/// blockTraceResult represents the results of tracing a single block when an -/// entire chain is being traced. ref -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct BlockTraceResult { - /// Block number corresponding to the trace task - pub block: U256, - /// Block hash corresponding to the trace task - pub hash: B256, - /// Trace results produced by the trace task - pub traces: Vec, -} - -/// Geth Default struct log trace frame -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DefaultFrame { - /// Whether the transaction failed - pub failed: bool, - /// How much gas was used. - pub gas: u64, - /// Output of the transaction - #[serde(serialize_with = "cfx_serde::serialize_hex_string_no_prefix")] - pub return_value: Bytes, - /// Recorded traces of the transaction - pub struct_logs: Vec, -} - -/// Represents a struct log entry in a trace -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct StructLog { - /// program counter - pub pc: u64, - /// opcode to be executed - pub op: String, - /// remaining gas - pub gas: u64, - /// cost for executing op - #[serde(rename = "gasCost")] - pub gas_cost: u64, - /// Current call depth - pub depth: u64, - /// Error message if any - #[serde(default, skip_serializing_if = "Option::is_none")] - pub error: Option, - /// EVM stack - #[serde(default, skip_serializing_if = "Option::is_none")] - pub stack: Option>, - /// Last call's return data. Enabled via enableReturnData - #[serde( - default, - rename = "returnData", - skip_serializing_if = "Option::is_none" - )] - pub return_data: Option, - /// ref - #[serde(default, skip_serializing_if = "Option::is_none")] - pub memory: Option>, - /// Size of memory. - #[serde( - default, - rename = "memSize", - skip_serializing_if = "Option::is_none" - )] - pub memory_size: Option, - /// Storage slots of current contract read from and written to. Only - /// emitted for SLOAD and SSTORE. Disabled via disableStorage - #[serde( - default, - skip_serializing_if = "Option::is_none", - serialize_with = "serialize_string_storage_map_opt" - )] - pub storage: Option>, - /// Refund counter - #[serde( - default, - rename = "refund", - skip_serializing_if = "Option::is_none" - )] - pub refund_counter: Option, -} - -/// Tracing response objects -/// -/// Note: This deserializes untagged, so it's possible that a custom javascript -/// tracer response matches another variant, for example a js tracer that -/// returns `{}` would be deserialized as [GethTrace::NoopTracer] -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum GethTrace { - /// The response for the default struct log tracer - Default(DefaultFrame), - /// The response for call tracer - CallTracer(CallFrame), - /// The response for four byte tracer - FourByteTracer(FourByteFrame), - /// The response for pre-state byte tracer - PreStateTracer(PreStateFrame), - /// An empty json response - NoopTracer(NoopFrame), - /// The response for mux tracer - MuxTracer(MuxFrame), - /// Any other trace response, such as custom javascript response objects - JS(serde_json::Value), -} - -impl From for GethTrace { - fn from(value: DefaultFrame) -> Self { GethTrace::Default(value) } -} - -impl From for GethTrace { - fn from(value: FourByteFrame) -> Self { GethTrace::FourByteTracer(value) } -} - -impl From for GethTrace { - fn from(value: CallFrame) -> Self { GethTrace::CallTracer(value) } -} - -impl From for GethTrace { - fn from(value: PreStateFrame) -> Self { GethTrace::PreStateTracer(value) } -} - -impl From for GethTrace { - fn from(value: NoopFrame) -> Self { GethTrace::NoopTracer(value) } -} - -impl From for GethTrace { - fn from(value: MuxFrame) -> Self { GethTrace::MuxTracer(value) } -} - -/// Available built-in tracers -/// -/// See -#[derive(Debug, Copy, PartialEq, Eq, Clone, Hash, Deserialize, Serialize)] -pub enum GethDebugBuiltInTracerType { - /// The 4byteTracer collects the function selectors of every function - /// executed in the lifetime of a transaction, along with the size of - /// the supplied call data. The result is a [FourByteFrame] where the - /// keys are SELECTOR-CALLDATASIZE and the values are number of - /// occurrences of this key. - #[serde(rename = "4byteTracer")] - FourByteTracer, - /// The callTracer tracks all the call frames executed during a - /// transaction, including depth 0. The result will be a nested list of - /// call frames, resembling how EVM works. They form a tree - /// with the top-level call at root and sub-calls as children of the higher - /// levels. - #[serde(rename = "callTracer")] - CallTracer, - /// The prestate tracer has two modes: prestate and diff. The prestate mode - /// returns the accounts necessary to execute a given transaction. diff - /// mode returns the differences between the transaction's pre and - /// post-state (i.e. what changed because the transaction happened). - /// The prestateTracer defaults to prestate mode. It reexecutes the given - /// transaction and tracks every part of state that is touched. This is - /// similar to the concept of a stateless witness, the difference being - /// this tracer doesn't return any cryptographic proof, rather only the - /// trie leaves. The result is an object. The keys are addresses of - /// accounts. - #[serde(rename = "prestateTracer")] - PreStateTracer, - /// This tracer is noop. It returns an empty object and is only meant for - /// testing the setup. - #[serde(rename = "noopTracer")] - NoopTracer, - /// The mux tracer is a tracer that can run multiple tracers at once. - #[serde(rename = "muxTracer")] - MuxTracer, -} - -/// Available tracers -/// -/// See and -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum GethDebugTracerType { - /// built-in tracer - BuiltInTracer(GethDebugBuiltInTracerType), - /// custom JS tracer - JsTracer(String), -} - -impl From for GethDebugTracerType { - fn from(value: GethDebugBuiltInTracerType) -> Self { - GethDebugTracerType::BuiltInTracer(value) - } -} - -/// Configuration of the tracer -/// -/// This is a simple wrapper around serde_json::Value. -/// with helpers for deserializing tracer configs. -#[derive(Debug, PartialEq, Eq, Clone, Default, Deserialize, Serialize)] -#[serde(transparent)] -pub struct GethDebugTracerConfig(pub serde_json::Value); - -// === impl GethDebugTracerConfig === - -impl GethDebugTracerConfig { - /// Returns if this is a null object - pub fn is_null(&self) -> bool { self.0.is_null() } - - /// Consumes the config and tries to deserialize it into the given type. - pub fn from_value( - self, - ) -> Result { - serde_json::from_value(self.0) - } - - /// Returns the [CallConfig] if it is a call config. - pub fn into_call_config(self) -> Result { - if self.0.is_null() { - return Ok(Default::default()); - } - self.from_value() - } - - /// Returns the raw json value - pub fn into_json(self) -> serde_json::Value { self.0 } - - /// Returns the [PreStateConfig] if it is a prestate config. - pub fn into_pre_state_config( - self, - ) -> Result { - if self.0.is_null() { - return Ok(Default::default()); - } - self.from_value() - } - - /// Returns the [MuxConfig] if it is a mux config. - pub fn into_mux_config(self) -> Result { - if self.0.is_null() { - return Ok(Default::default()); - } - self.from_value() - } -} - -impl From for GethDebugTracerConfig { - fn from(value: serde_json::Value) -> Self { GethDebugTracerConfig(value) } -} - -/// Bindings for additional `debug_traceTransaction` options -/// -/// See -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct GethDebugTracingOptions { - /// The common tracing options - #[serde(default, flatten)] - pub config: GethDefaultTracingOptions, - /// The custom tracer to use. - /// - /// If `None` then the default structlog tracer is used. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tracer: Option, - /// Config specific to given `tracer`. - /// - /// Note default struct logger config are historically embedded in main - /// object. - /// - /// tracerConfig is slated for Geth v1.11.0 - /// See - /// - /// This could be [CallConfig] or [PreStateConfig] depending on the tracer. - #[serde(default, skip_serializing_if = "GethDebugTracerConfig::is_null")] - pub tracer_config: GethDebugTracerConfig, - /// A string of decimal integers that overrides the JavaScript-based - /// tracing calls default timeout of 5 seconds. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub timeout: Option, -} - -impl GethDebugTracingOptions { - /// Sets the tracer to use - pub fn with_tracer(mut self, tracer: GethDebugTracerType) -> Self { - self.tracer = Some(tracer); - self - } - - /// Sets the timeout to use for tracing - pub fn with_timeout(mut self, duration: Duration) -> Self { - self.timeout = Some(format!("{}ms", duration.as_millis())); - self - } - - /// Configures a [CallConfig] - pub fn call_config(mut self, config: CallConfig) -> Self { - self.tracer_config = GethDebugTracerConfig( - serde_json::to_value(config).expect("is serializable"), - ); - self - } - - /// Configures a [PreStateConfig] - pub fn prestate_config(mut self, config: PreStateConfig) -> Self { - self.tracer_config = GethDebugTracerConfig( - serde_json::to_value(config).expect("is serializable"), - ); - self - } -} - -/// Default tracing options for the struct looger. -/// -/// These are all known general purpose tracer options that may or not be -/// supported by a given tracer. For example, the `enableReturnData` option is a -/// noop on regular `debug_trace{Transaction,Block}` calls. -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Default, -)] -#[serde(rename_all = "camelCase")] -pub struct GethDefaultTracingOptions { - /// enable memory capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub enable_memory: Option, - /// Disable memory capture - /// - /// This is the opposite of `enable_memory`. - /// - /// Note: memory capture used to be enabled by default on geth, but has since been flipped and is now disabled by default. - /// However, at the time of writing this, erigon still defaults to enabled - /// and supports the `disableMemory` option. So we keep this option for - /// compatibility, but if it's missing OR `enableMemory` is present - /// `enableMemory` takes precedence. - /// - /// See also - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_memory: Option, - /// disable stack capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_stack: Option, - /// Disable storage capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_storage: Option, - /// Enable return data capture - #[serde(default, skip_serializing_if = "Option::is_none")] - pub enable_return_data: Option, - /// Disable return data capture - /// - /// This is the opposite of `enable_return_data`, and only supported for - /// compatibility reasons. See also `disable_memory`. - /// - /// If `enable_return_data` is present, `enable_return_data` always takes - /// precedence. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub disable_return_data: Option, - /// print output during capture end - #[serde(default, skip_serializing_if = "Option::is_none")] - pub debug: Option, - /// maximum length of output, but zero means unlimited - #[serde(default, skip_serializing_if = "Option::is_none")] - pub limit: Option, -} - -impl GethDefaultTracingOptions { - /// Enables memory capture. - pub const fn enable_memory(self) -> Self { self.with_enable_memory(true) } - - /// Disables memory capture. - pub const fn disable_memory(self) -> Self { self.with_disable_memory(true) } - - /// Disables stack capture. - pub const fn disable_stack(self) -> Self { self.with_disable_stack(true) } - - /// Disables storage capture. - pub const fn disable_storage(self) -> Self { - self.with_disable_storage(true) - } - - /// Enables return data capture. - pub const fn enable_return_data(self) -> Self { - self.with_enable_return_data(true) - } - - /// Disables return data capture. - pub const fn disable_return_data(self) -> Self { - self.with_disable_return_data(true) - } - - /// Enables debug mode. - pub const fn debug(self) -> Self { self.with_debug(true) } - - /// Sets the enable_memory field. - pub const fn with_enable_memory(mut self, enable: bool) -> Self { - self.enable_memory = Some(enable); - self - } - - /// Sets the disable_memory field. - pub const fn with_disable_memory(mut self, disable: bool) -> Self { - self.disable_memory = Some(disable); - self - } - - /// Sets the disable_stack field. - pub const fn with_disable_stack(mut self, disable: bool) -> Self { - self.disable_stack = Some(disable); - self - } - - /// Sets the disable_storage field. - pub const fn with_disable_storage(mut self, disable: bool) -> Self { - self.disable_storage = Some(disable); - self - } - - /// Sets the enable_return_data field. - pub const fn with_enable_return_data(mut self, enable: bool) -> Self { - self.enable_return_data = Some(enable); - self - } - - /// Sets the disable_return_data field. - pub const fn with_disable_return_data(mut self, disable: bool) -> Self { - self.disable_return_data = Some(disable); - self - } - - /// Sets the debug field. - pub const fn with_debug(mut self, debug: bool) -> Self { - self.debug = Some(debug); - self - } - - /// Sets the limit field. - pub const fn with_limit(mut self, limit: u64) -> Self { - self.limit = Some(limit); - self - } - - /// Returns `true` if return data capture is enabled - pub fn is_return_data_enabled(&self) -> bool { - self.enable_return_data - .or_else(|| self.disable_return_data.map(|disable| !disable)) - .unwrap_or(false) - } - - /// Returns `true` if memory capture is enabled - pub fn is_memory_enabled(&self) -> bool { - self.enable_memory - .or_else(|| self.disable_memory.map(|disable| !disable)) - .unwrap_or(false) - } - - /// Returns `true` if stack capture is enabled - pub fn is_stack_enabled(&self) -> bool { - !self.disable_stack.unwrap_or(false) - } - - /// Returns `true` if storage capture is enabled - pub fn is_storage_enabled(&self) -> bool { - !self.disable_storage.unwrap_or(false) - } -} - -/// Bindings for additional `debug_traceCall` options -/// -/// See -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct GethDebugTracingCallOptions { - /// All the options - #[serde(flatten)] - pub tracing_options: GethDebugTracingOptions, - /// The state overrides to apply - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state_overrides: Option, - /// The block overrides to apply - #[serde(default, skip_serializing_if = "Option::is_none")] - pub block_overrides: Option, -} - -/// Serializes a storage map as a list of key-value pairs _without_ 0x-prefix -fn serialize_string_storage_map_opt( - storage: &Option>, s: S, -) -> Result { - match storage { - None => s.serialize_none(), - Some(storage) => { - let mut m = s.serialize_map(Some(storage.len()))?; - for (key, val) in storage.iter() { - let key = format!("{:?}", key); - let val = format!("{:?}", val); - // skip the 0x prefix - m.serialize_entry(&key.as_str()[2..], &val.as_str()[2..])?; - } - m.end() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_tracer_config() { - let s = "{\"tracer\": \"callTracer\"}"; - let opts = serde_json::from_str::(s).unwrap(); - assert_eq!( - opts.tracer, - Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::CallTracer - )) - ); - let _call_config = - opts.tracer_config.clone().into_call_config().unwrap(); - let _prestate_config = - opts.tracer_config.into_pre_state_config().unwrap(); - } - - #[test] - fn test_memory_capture() { - let mut config = GethDefaultTracingOptions::default(); - - // by default false - assert!(!config.is_memory_enabled()); - - config.disable_memory = Some(false); - // disable == false -> enable - assert!(config.is_memory_enabled()); - - config.enable_memory = Some(false); - // enable == false -> disable - assert!(!config.is_memory_enabled()); - } - - #[test] - fn test_return_data_capture() { - let mut config = GethDefaultTracingOptions::default(); - - // by default false - assert!(!config.is_return_data_enabled()); - - config.disable_return_data = Some(false); - // disable == false -> enable - assert!(config.is_return_data_enabled()); - - config.enable_return_data = Some(false); - // enable == false -> disable - assert!(!config.is_return_data_enabled()); - } - - // - #[test] - fn serde_default_frame() { - let input = include_str!("../../test_data/default/structlogs_01.json"); - let _frame: DefaultFrame = serde_json::from_str(input).unwrap(); - } - - #[test] - fn test_serialize_storage_map() { - let s = r#"{ - "pc":3349, - "op":"SLOAD", - "gas":23959, - "gasCost":2100, - "depth":1, - "stack":[], - "memory":[], - "storage":{ - "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a":"0000000000000000000000000000000000000000000000000000000000000000" - } - }"#; - let log: StructLog = serde_json::from_str(s).unwrap(); - let val = serde_json::to_value(&log).unwrap(); - let input = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!(input, val); - } - - #[test] - fn test_trace_result_serde() { - let s = r#"{ - "result": { - "from": "0xccc5499e15fedaaeaba68aeb79b95b20f725bc56", - "gas": "0x186a0", - "gasUsed": "0xdb91", - "to": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "input": "0xa9059cbb000000000000000000000000e3f85a274c1edbea2f2498cf5978f41961cf8b5b0000000000000000000000000000000000000000000000000000000068c8f380", - "value": "0x0", - "type": "CALL" - }, - "txHash": "0x7cc741c553d4098f319c894d9db208999ca49ee1b5c53f6a9992e687cbffb69e" - }"#; - let result: TraceResult = serde_json::from_str(s).unwrap(); - let hash = result.tx_hash().unwrap(); - assert_eq!( - hash, - "7cc741c553d4098f319c894d9db208999ca49ee1b5c53f6a9992e687cbffb69e" - .parse::() - .unwrap() - ); - - let de = serde_json::to_value(&result).unwrap(); - let val = serde_json::from_str::(s).unwrap(); - similar_asserts::assert_eq!(val, de); - } -} diff --git a/crates/rpc_types_trace/src/geth/mux.rs b/crates/rpc_types_trace/src/geth/mux.rs deleted file mode 100644 index 71b8bbfd70..0000000000 --- a/crates/rpc_types_trace/src/geth/mux.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! Geth `muxTracer` types. - -use super::{GethDebugBuiltInTracerType, GethDebugTracerConfig, GethTrace}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// A `muxTracer` config that contains the configuration for running multiple -/// tracers in one go. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MuxConfig( - pub HashMap>, -); - -/// A `muxTracer` frame response that contains the results of multiple tracers -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct MuxFrame(pub HashMap); - -#[cfg(test)] -mod tests { - use super::{super::*, *}; - - const FOUR_BYTE_FRAME: &str = r#"{ - "0x27dc297e-128": 1, - "0x38cc4831-0": 2, - "0x524f3889-96": 1, - "0xadf59f99-288": 1, - "0xc281d19e-0": 1 - }"#; - - const CALL_FRAME_WITH_LOG: &str = - include_str!("../../test_data/call_tracer/with_log.json"); - - const PRESTATE_FRAME: &str = r#"{ - "0x0000000000000000000000000000000000000002": { - "balance": "0x0" - }, - "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { - "balance": "0x2638035a26d133809" - }, - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48734599f7284", - "nonce": 1133 - }, - "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { - "balance": "0x0", - "code": "0x", - "nonce": 1, - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": - "0x000000000000000000000000000000000000000000000000000000000024aea6", - "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": - "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", - "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": - "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", - "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": - "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" - } - } - }"#; - - #[test] - fn test_serialize_mux_tracer_config() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::MuxTracer, - )); - - let call_config = CallConfig { - only_top_call: Some(true), - with_log: Some(true), - }; - let prestate_config = PreStateConfig { - diff_mode: Some(true), - }; - - opts.tracing_options.tracer_config = - serde_json::to_value(MuxConfig(HashMap::from([ - (GethDebugBuiltInTracerType::FourByteTracer, None), - ( - GethDebugBuiltInTracerType::CallTracer, - Some(GethDebugTracerConfig( - serde_json::to_value(call_config).unwrap(), - )), - ), - ( - GethDebugBuiltInTracerType::PreStateTracer, - Some(GethDebugTracerConfig( - serde_json::to_value(prestate_config).unwrap(), - )), - ), - ]))) - .unwrap() - .into(); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"tracer":"muxTracer","tracerConfig":{"4byteTracer":null,"callTracer":{"onlyTopCall":true,"withLog":true},"prestateTracer":{"diffMode":true}}}"#, - ); - } - - #[test] - fn test_deserialize_mux_frame() { - let expected = HashMap::from([ - ( - GethDebugBuiltInTracerType::FourByteTracer, - GethTrace::FourByteTracer( - serde_json::from_str(FOUR_BYTE_FRAME).unwrap(), - ), - ), - ( - GethDebugBuiltInTracerType::CallTracer, - GethTrace::CallTracer( - serde_json::from_str(CALL_FRAME_WITH_LOG).unwrap(), - ), - ), - ( - GethDebugBuiltInTracerType::PreStateTracer, - GethTrace::PreStateTracer( - serde_json::from_str(PRESTATE_FRAME).unwrap(), - ), - ), - ]); - - let raw_frame = serde_json::to_string(&expected).unwrap(); - let trace: MuxFrame = serde_json::from_str(&raw_frame).unwrap(); - - assert_eq!( - trace.0[&GethDebugBuiltInTracerType::FourByteTracer], - expected[&GethDebugBuiltInTracerType::FourByteTracer] - ); - assert_eq!( - trace.0[&GethDebugBuiltInTracerType::CallTracer], - expected[&GethDebugBuiltInTracerType::CallTracer] - ); - assert_eq!( - trace.0[&GethDebugBuiltInTracerType::PreStateTracer], - expected[&GethDebugBuiltInTracerType::PreStateTracer] - ); - } -} diff --git a/crates/rpc_types_trace/src/geth/noop.rs b/crates/rpc_types_trace/src/geth/noop.rs deleted file mode 100644 index 869e32bbab..0000000000 --- a/crates/rpc_types_trace/src/geth/noop.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Noop tracer response. - -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; - -/// An empty frame response that's only an empty json object `{}`. -/// -/// -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct NoopFrame(BTreeMap<(), ()>); - -#[cfg(test)] -mod tests { - use super::{super::*, *}; - - const DEFAULT: &str = r"{}"; - - #[test] - fn test_serialize_noop_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::NoopTracer, - )); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"tracer":"noopTracer"}"# - ); - } - - #[test] - fn test_deserialize_noop_trace() { - let _trace: NoopFrame = serde_json::from_str(DEFAULT).unwrap(); - } -} diff --git a/crates/rpc_types_trace/src/geth/pre_state.rs b/crates/rpc_types_trace/src/geth/pre_state.rs deleted file mode 100644 index 2e37033252..0000000000 --- a/crates/rpc_types_trace/src/geth/pre_state.rs +++ /dev/null @@ -1,384 +0,0 @@ -//! Pre-state Geth tracer types. - -use alloy_primitives::Bytes; -use cfx_serde::num::from_int_or_hex_opt; -use cfx_types::{Address, H256 as B256, U256}; -use serde::{Deserialize, Serialize}; -use std::collections::{btree_map, BTreeMap}; - -/// A tracer that records [AccountState]s. -/// The prestate tracer has two modes: prestate and diff -/// -/// -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum PreStateFrame { - /// The default mode returns the accounts necessary to execute a given - /// transaction. - /// - /// It re-executes the given transaction and tracks every part of state - /// that is touched. - Default(PreStateMode), - /// Diff mode returns the differences between the transaction's pre and - /// post-state (i.e. what changed because the transaction happened). - Diff(DiffMode), -} - -impl PreStateFrame { - /// Returns true if this trace was requested without diffmode. - pub const fn is_default(&self) -> bool { - matches!(self, PreStateFrame::Default(_)) - } - - /// Returns true if this trace was requested with diffmode. - pub const fn is_diff(&self) -> bool { - matches!(self, PreStateFrame::Diff(_)) - } - - /// Returns the account states after the transaction is executed if this - /// trace was requested without diffmode. - pub const fn as_default(&self) -> Option<&PreStateMode> { - match self { - PreStateFrame::Default(mode) => Some(mode), - _ => None, - } - } - - /// Returns the account states before and after the transaction is executed - /// if this trace was requested with diffmode. - pub const fn as_diff(&self) -> Option<&DiffMode> { - match self { - PreStateFrame::Diff(mode) => Some(mode), - _ => None, - } - } -} - -/// Includes all the account states necessary to execute a given transaction. -/// -/// This corresponds to the default mode of the [PreStateConfig]. -/// -/// The [AccountState]'s storage will include all touched slots of an account. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -pub struct PreStateMode(pub BTreeMap); - -/// Represents the account states before and after the transaction is executed. -/// -/// This corresponds to the [DiffMode] of the [PreStateConfig]. -/// -/// This will only contain changed [AccountState]s, created accounts will not be -/// included in the pre state and selfdestructed accounts will not be included -/// in the post state. -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct DiffMode { - /// The account states after the transaction is executed. - pub post: BTreeMap, - /// The account states before the transaction is executed. - pub pre: BTreeMap, -} - -// === impl DiffMode === - -impl DiffMode { - /// The sets of the [DiffMode] should only contain changed [AccountState]s. - /// - /// This will remove all unchanged [AccountState]s from the sets. - /// - /// In other words it removes entries that are equal (unchanged) in both the - /// pre and post sets. - pub fn retain_changed(&mut self) -> &mut Self { - self.pre.retain(|address, pre| { - if let btree_map::Entry::Occupied(entry) = self.post.entry(*address) - { - if entry.get() == pre { - // remove unchanged account state from both sets - entry.remove(); - return false; - } - } - - true - }); - self - } - - /// Removes all zero values from the storage of the [AccountState]s. - pub fn remove_zero_storage_values(&mut self) { - for state in self.pre.values_mut().chain(self.post.values_mut()) { - state.storage.retain(|_, value| *value != B256::zero()); - } - } -} - -/// Helper type for [DiffMode] to represent a specific set -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DiffStateKind { - /// Corresponds to the pre state of the [DiffMode] - Pre, - /// Corresponds to the post state of the [DiffMode] - Post, -} - -impl DiffStateKind { - /// Returns true if this is the pre state of the [DiffMode] - pub const fn is_pre(&self) -> bool { matches!(self, DiffStateKind::Pre) } - - /// Returns true if this is the post state of the [DiffMode] - pub const fn is_post(&self) -> bool { matches!(self, DiffStateKind::Post) } -} - -/// Represents the state of an account -#[derive( - Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, -)] -pub struct AccountState { - /// The optional balance of the account. - #[serde( - default, - deserialize_with = "from_int_or_hex_opt", - skip_serializing_if = "Option::is_none" - )] - pub balance: Option, - /// The optional code of the account. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, - /// The optional nonce of the account. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub nonce: Option, - /// The storage of the account. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub storage: BTreeMap, -} - -impl AccountState { - /// Creates a new `AccountState` with the given account info. - /// - /// If balance is zero, it will be omitted. - /// If nonce is zero, it will be omitted. - /// If code is empty, it will be omitted. - pub fn from_account_info( - nonce: u64, balance: U256, code: Option, - ) -> Self { - Self { - balance: Some(balance), - code: code.filter(|code| !code.is_empty()), - nonce: (nonce != 0).then_some(nonce), - storage: Default::default(), - } - } - - /// Removes balance,nonce or code if they match the given account info. - /// - /// This is useful for comparing pre vs post state and only keep changed - /// values in post state. - pub fn remove_matching_account_info(&mut self, other: &AccountState) { - if self.balance == other.balance { - self.balance = None; - } - if self.nonce == other.nonce { - self.nonce = None; - } - if self.code == other.code { - self.code = None; - } - } -} - -/// Helper type to track the kind of change of an [AccountState]. -#[derive( - Debug, Copy, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize, -)] -pub enum AccountChangeKind { - /// The account was modified. - #[default] - Modify, - /// The account was created. - Create, - /// The account was selfdestructed. - SelfDestruct, -} - -impl AccountChangeKind { - /// Returns true if the account was created - pub const fn is_created(&self) -> bool { - matches!(self, AccountChangeKind::Create) - } - - /// Returns true the account was modified - pub const fn is_modified(&self) -> bool { - matches!(self, AccountChangeKind::Modify) - } - - /// Returns true the account was modified - pub const fn is_selfdestruct(&self) -> bool { - matches!(self, AccountChangeKind::SelfDestruct) - } -} - -/// The config for the prestate tracer. -#[derive( - Debug, Copy, Clone, Default, PartialEq, Eq, Serialize, Deserialize, -)] -#[serde(rename_all = "camelCase")] -pub struct PreStateConfig { - /// If `diffMode` is set to true, the response frame includes all the - /// account and storage diffs for the transaction. If it's missing or - /// set to false it only returns the accounts and storage necessary to - /// execute the transaction. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub diff_mode: Option, -} - -impl PreStateConfig { - /// Returns true if this trace was requested with diffmode. - #[inline] - pub fn is_diff_mode(&self) -> bool { self.diff_mode.unwrap_or_default() } - - /// Is default mode if diff_mode is not set - #[inline] - pub fn is_default_mode(&self) -> bool { !self.is_diff_mode() } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::geth::*; - - // See - const DEFAULT: &str = - include_str!("../../test_data/pre_state_tracer/default.json"); - const LEGACY: &str = - include_str!("../../test_data/pre_state_tracer/legacy.json"); - const DIFF_MODE: &str = - include_str!("../../test_data/pre_state_tracer/diff_mode.json"); - - #[test] - fn test_serialize_pre_state_trace() { - let mut opts = GethDebugTracingCallOptions::default(); - opts.tracing_options.config.disable_storage = Some(false); - opts.tracing_options.tracer = Some(GethDebugTracerType::BuiltInTracer( - GethDebugBuiltInTracerType::PreStateTracer, - )); - opts.tracing_options.tracer_config = - serde_json::to_value(PreStateConfig { - diff_mode: Some(true), - }) - .unwrap() - .into(); - - assert_eq!( - serde_json::to_string(&opts).unwrap(), - r#"{"disableStorage":false,"tracer":"prestateTracer","tracerConfig":{"diffMode":true}}"# - ); - } - - #[test] - fn test_deserialize_pre_state_trace() { - let trace: PreStateFrame = serde_json::from_str(DEFAULT).unwrap(); - match trace { - PreStateFrame::Default(PreStateMode(_)) => {} - _ => unreachable!(), - } - let _trace: PreStateFrame = serde_json::from_str(LEGACY).unwrap(); - let trace: PreStateFrame = serde_json::from_str(DIFF_MODE).unwrap(); - match trace { - PreStateFrame::Diff(DiffMode { - pre: _pre, - post: _post, - }) => {} - _ => unreachable!(), - } - } - - #[test] - fn test_is_diff_mode() { - assert!(PreStateConfig { - diff_mode: Some(true) - } - .is_diff_mode()); - assert!(!PreStateConfig { - diff_mode: Some(false) - } - .is_diff_mode()); - assert!(!PreStateConfig { diff_mode: None }.is_diff_mode()); - } - - #[test] - fn parse_prestate_default_resp() { - let s = r#"{ - "0x0000000000000000000000000000000000000002": { - "balance": "0x0" - }, - "0x008b3b2f992c0e14edaa6e2c662bec549caa8df1": { - "balance": "0x2638035a26d133809" - }, - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48734599f7284", - "nonce": 1133 - }, - "0xc8ba32cab1757528daf49033e3673fae77dcf05d": { - "balance": "0x0", - "code": "0x", - "nonce": 1, - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": - "0x000000000000000000000000000000000000000000000000000000000024aea6", - "0x59fb7853eb21f604d010b94c123acbeae621f09ce15ee5d7616485b1e78a72e9": - "0x00000000000000c42b56a52aedf18667c8ae258a0280a8912641c80c48cd9548", - "0x8d8ebb65ec00cb973d4fe086a607728fd1b9de14aa48208381eed9592f0dee9a": - "0x00000000000000784ae4881e40b1f5ebb4437905fbb8a5914454123b0293b35f", - "0xff896b09014882056009dedb136458f017fcef9a4729467d0d00b4fd413fb1f1": - "0x000000000000000e78ac39cb1c20e9edc753623b153705d0ccc487e31f9d6749" - } - } - }"#; - let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); - assert!(pre_state.is_default()); - } - #[test] - fn parse_prestate_diff_resp() { - let s = r#"{ - "post": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "nonce": 1135 - } - }, - "pre": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48429e177130a", - "nonce": 1134 - } - } - }"#; - let pre_state: PreStateFrame = serde_json::from_str(s).unwrap(); - assert!(pre_state.is_diff()); - } - - #[test] - fn test_retain_changed_accounts() { - let s = r#"{ - "post": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "nonce": 1135 - } - }, - "pre": { - "0x35a9f94af726f07b5162df7e828cc9dc8439e7d0": { - "balance": "0x7a48429e177130a", - "nonce": 1134 - } - } - }"#; - let diff: DiffMode = serde_json::from_str(s).unwrap(); - let mut diff_changed = diff.clone(); - diff_changed.retain_changed(); - // different entries - assert_eq!(diff_changed, diff); - - diff_changed.pre = diff_changed.post.clone(); - diff_changed.retain_changed(); - assert!(diff_changed.post.is_empty()); - assert!(diff_changed.pre.is_empty()); - } -} diff --git a/crates/rpc_types_trace/src/lib.rs b/crates/rpc_types_trace/src/lib.rs deleted file mode 100644 index ba0addf77c..0000000000 --- a/crates/rpc_types_trace/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod block; -pub mod common; -pub mod geth; -pub mod parity; -mod state; diff --git a/crates/rpc_types_trace/src/parity.rs b/crates/rpc_types_trace/src/parity.rs deleted file mode 100644 index c0202e0a33..0000000000 --- a/crates/rpc_types_trace/src/parity.rs +++ /dev/null @@ -1,761 +0,0 @@ -//! Types for trace module. -//! -//! See - -use alloy_primitives::Bytes; -use cfx_types::{Address, H256 as B256, U256, U64}; -use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; -use std::{ - collections::BTreeMap, - ops::{Deref, DerefMut}, -}; - -/// Different Trace diagnostic targets. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum TraceType { - /// Default trace - Trace, - /// Provides a full trace of the VM’s state throughout the execution of the - /// transaction, including for any subcalls. - VmTrace, - /// Provides information detailing all altered portions of the Ethereum - /// state made due to the execution of the transaction. - StateDiff, -} - -/// The Outcome of a traced transaction with optional settings -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TraceResults { - /// Output of the trace - pub output: Bytes, - /// Enabled if [TraceType::StateDiff] is provided - pub state_diff: Option, - /// Enabled if [TraceType::Trace] is provided, otherwise an empty vec - #[serde(default)] - pub trace: Vec, - /// Enabled if [TraceType::VmTrace] is provided - pub vm_trace: Option, -} - -// === impl TraceResults === - -impl TraceResults { - /// Sets the gas used of the root trace. - /// - /// The root trace's gasUsed should mirror the actual gas used by the - /// transaction. - /// - /// This allows setting it manually by consuming the execution result's gas - /// for example. - pub fn set_root_trace_gas_used(&mut self, gas_used: u64) { - if let Some(r) = self.trace.first_mut().and_then(|t| t.result.as_mut()) - { - r.set_gas_used(gas_used) - } - } -} - -/// A `FullTrace` with an additional transaction hash -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TraceResultsWithTransactionHash { - /// The recorded trace. - #[serde(flatten)] - pub full_trace: TraceResults, - /// Hash of the traced transaction. - pub transaction_hash: B256, -} - -/// A changed value -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct ChangedType { - /// Original value - pub from: T, - /// New value - pub to: T, -} - -/// Represents how a value changed. -/// -/// This is used for statediff. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub enum Delta { - /// Existing value didn't change. - #[default] - #[serde(rename = "=")] - Unchanged, - /// New storage value added. - #[serde(rename = "+")] - Added(T), - /// Existing storage value removed. - #[serde(rename = "-")] - Removed(T), - /// Existing storage value changed. - #[serde(rename = "*")] - Changed(ChangedType), -} - -// === impl Delta === - -impl Delta { - /// Creates a new [Delta::Changed] variant - pub const fn changed(from: T, to: T) -> Self { - Self::Changed(ChangedType { from, to }) - } - - /// Returns true if the value is unchanged - pub const fn is_unchanged(&self) -> bool { - matches!(self, Delta::Unchanged) - } - - /// Returns true if the value is added - pub const fn is_added(&self) -> bool { matches!(self, Delta::Added(_)) } - - /// Returns true if the value is removed - pub const fn is_removed(&self) -> bool { matches!(self, Delta::Removed(_)) } - - /// Returns true if the value is changed - pub const fn is_changed(&self) -> bool { matches!(self, Delta::Changed(_)) } -} - -/// The diff of an account after a transaction -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AccountDiff { - /// How the balance changed, if at all - pub balance: Delta, - /// How the code changed, if at all - pub code: Delta, - /// How the nonce changed, if at all - pub nonce: Delta, - /// All touched/changed storage values - pub storage: BTreeMap>, -} - -/// New-type for list of account diffs -#[derive(Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize)] -#[serde(transparent)] -pub struct StateDiff(pub BTreeMap); - -impl Deref for StateDiff { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl DerefMut for StateDiff { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } -} - -/// Represents the various types of actions recorded during tracing -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "type", content = "action")] -pub enum Action { - /// Regular call - Call(CallAction), - /// A CREATE call - Create(CreateAction), - /// Parity style traces never renamed suicide to selfdestruct: - /// - /// For compatibility reasons, this is serialized as `suicide`: - #[serde(rename = "suicide", alias = "selfdestruct")] - Selfdestruct(SelfdestructAction), - /// Rewards if any (pre POS) - Reward(RewardAction), -} - -impl Action { - /// Returns true if this is a call action - pub const fn is_call(&self) -> bool { matches!(self, Action::Call(_)) } - - /// Returns true if this is a create action - pub const fn is_create(&self) -> bool { matches!(self, Action::Create(_)) } - - /// Returns true if this is a selfdestruct action - pub const fn is_selfdestruct(&self) -> bool { - matches!(self, Action::Selfdestruct(_)) - } - - /// Returns true if this is a reward action - pub const fn is_reward(&self) -> bool { matches!(self, Action::Reward(_)) } - - /// Returns what kind of action this is - pub const fn kind(&self) -> ActionType { - match self { - Action::Call(_) => ActionType::Call, - Action::Create(_) => ActionType::Create, - Action::Selfdestruct(_) => ActionType::Selfdestruct, - Action::Reward(_) => ActionType::Reward, - } - } -} - -/// An external action type. -/// -/// Used as enum identifier for [Action] -#[derive(Debug, Copy, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ActionType { - /// Contract call. - Call, - /// Contract creation. - Create, - /// Contract suicide/selfdestruct. - #[serde(rename = "suicide", alias = "selfdestruct")] - Selfdestruct, - /// A block reward. - Reward, -} - -/// Call type. -#[derive( - Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, -)] -#[serde(rename_all = "lowercase")] -pub enum CallType { - /// None - #[default] - None, - /// Call - Call, - /// Call code - CallCode, - /// Delegate call - DelegateCall, - /// Static call - StaticCall, -} - -/// Represents a certain [CallType] of a _call_ or message transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CallAction { - /// Address of the sending account. - pub from: Address, - /// The type of the call. - pub call_type: CallType, - /// The gas available for executing the call. - pub gas: U64, - /// The input data provided to the call. - pub input: Bytes, - /// Address of the destination/target account. - pub to: Address, - /// Value transferred to the destination account. - pub value: U256, -} - -/// Represents a _create_ action, either a `CREATE` operation or a CREATE -/// transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreateAction { - /// The address of the creator. - pub from: Address, - /// The gas available for the creation init code. - pub gas: U64, - /// The init code. - pub init: Bytes, - /// The value with which the new account is endowed. - pub value: U256, -} - -/// What kind of reward. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum RewardType { - /// Block rewards - Block, - /// Reward for uncle block - Uncle, -} - -/// Recorded reward of a block. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RewardAction { - /// Author's address. - pub author: Address, - /// Reward type. - pub reward_type: RewardType, - /// Reward amount. - pub value: U256, -} - -/// Represents a _selfdestruct_ action fka `suicide`. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SelfdestructAction { - /// destroyed/suicided address. - pub address: Address, - /// Balance of the contract just before it was destroyed. - pub balance: U256, - /// destroyed contract heir. - pub refund_address: Address, -} - -/// Outcome of a CALL. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CallOutput { - /// Gas used by the call. - pub gas_used: U64, - /// The output data of the call. - pub output: Bytes, -} - -/// Outcome of a CREATE. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreateOutput { - /// Address of the created contract. - pub address: Address, - /// Contract code. - pub code: Bytes, - /// Gas used by the call. - pub gas_used: U64, -} - -/// Represents the output of a trace. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(untagged)] -pub enum TraceOutput { - /// Output of a regular call transaction. - Call(CallOutput), - /// Output of a CREATE transaction. - Create(CreateOutput), -} - -// === impl TraceOutput === - -impl TraceOutput { - /// Returns the gas used by this trace. - pub const fn gas_used(&self) -> U64 { - match self { - TraceOutput::Call(call) => call.gas_used, - TraceOutput::Create(create) => create.gas_used, - } - } - - /// Sets the gas used by this trace. - pub fn set_gas_used(&mut self, gas_used: u64) { - match self { - TraceOutput::Call(call) => call.gas_used = U64::from(gas_used), - TraceOutput::Create(create) => { - create.gas_used = U64::from(gas_used) - } - } - } -} - -/// A parity style trace of a transaction. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TransactionTrace { - /// Represents what kind of trace this is - #[serde(flatten)] - pub action: Action, - /// The error message if the transaction failed. - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, - /// Output of the trace, can be CALL or CREATE - pub result: Option, - /// How many subtraces this trace has. - pub subtraces: usize, - /// The identifier of this transaction trace in the set. - /// - /// This gives the exact location in the call trace - /// [index in root CALL, index in first CALL, index in second CALL, …]. - pub trace_address: Vec, -} - -/// A wrapper for [TransactionTrace] that includes additional information about -/// the transaction. -#[derive(Clone, Debug, Eq, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct LocalizedTransactionTrace { - /// Trace of the transaction and its result. - #[serde(flatten)] - pub trace: TransactionTrace, - /// Hash of the block, if not pending. - /// - /// Note: this deviates from which always returns a block number - pub block_hash: Option, - /// Block number the transaction is included in, None if pending. - /// - /// Note: this deviates from which always returns a block number - pub block_number: Option, - /// Hash of the transaction - pub transaction_hash: Option, - /// Transaction index within the block, None if pending. - pub transaction_position: Option, -} - -// Implement Serialize manually to ensure consistent ordering of fields to match -// other client's format -impl Serialize for LocalizedTransactionTrace { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - let mut s = - serializer.serialize_struct("LocalizedTransactionTrace", 9)?; - - let TransactionTrace { - action, - error, - result, - subtraces, - trace_address, - } = &self.trace; - - match action { - Action::Call(call_action) => { - s.serialize_field("action", call_action)?; - } - Action::Create(create_action) => { - s.serialize_field("action", create_action)?; - } - Action::Selfdestruct(selfdestruct_action) => { - s.serialize_field("action", selfdestruct_action)?; - } - Action::Reward(reward_action) => { - s.serialize_field("action", reward_action)?; - } - } - if let Some(block_hash) = self.block_hash { - s.serialize_field("blockHash", &block_hash)?; - } - if let Some(block_number) = self.block_number { - s.serialize_field("blockNumber", &block_number)?; - } - - if let Some(error) = error { - s.serialize_field("error", error)?; - } - - match result { - Some(TraceOutput::Call(call)) => { - s.serialize_field("result", call)?; - } - Some(TraceOutput::Create(create)) => { - s.serialize_field("result", create)?; - } - None => {} - } - - s.serialize_field("subtraces", &subtraces)?; - s.serialize_field("traceAddress", &trace_address)?; - - if let Some(transaction_hash) = &self.transaction_hash { - s.serialize_field("transactionHash", transaction_hash)?; - } - if let Some(transaction_position) = &self.transaction_position { - s.serialize_field("transactionPosition", transaction_position)?; - } - - s.serialize_field("type", &action.kind())?; - - s.end() - } -} - -/// A record of a full VM trace for a CALL/CREATE. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmTrace { - /// The code to be executed. - pub code: Bytes, - /// All executed instructions. - pub ops: Vec, -} - -/// A record of a single VM instruction, opcode level. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmInstruction { - /// The gas cost for this instruction. - pub cost: u64, - /// Information concerning the execution of the operation. - pub ex: Option, - /// The program counter. - pub pc: usize, - /// Subordinate trace of the CALL/CREATE if applicable. - pub sub: Option, - /// Stringified opcode. - #[serde(skip_serializing_if = "Option::is_none")] - pub op: Option, - /// Index of the instruction in the set. - #[serde(skip_serializing_if = "Option::is_none")] - pub idx: Option, -} - -/// A record of an executed VM operation. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct VmExecutedOperation { - /// The total gas used. - pub used: u64, - /// The stack item placed, if any. - pub push: Vec, - /// If altered, the memory delta. - pub mem: Option, - /// The altered storage value, if any. - pub store: Option, -} - -/// A diff of some chunk of memory. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MemoryDelta { - /// Offset into memory the change begins. - pub off: usize, - /// The changed data. - pub data: Bytes, -} - -/// A diff of some storage value. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct StorageDelta { - /// Storage key. - pub key: U256, - /// Storage value belonging to the key. - pub val: U256, -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::{json, Value}; - use std::str::FromStr; - - #[test] - fn test_transaction_trace() { - let s = r#"{ - "action": { - "from": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "callType": "call", - "gas": "0x10bfc", - "input": "0xf6cd1e8d0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ec6952892271c8ee13f12e118484e03149281c9600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010480862479000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000160f5f00288e9e1cc8655b327e081566e580a71d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011c37937e080000fffffffffffffffffffffffffffffffffffffffffffffffffee3c86c81f8000000000000000000000000000000000000000000000000000000000000", - "to": "0x160f5f00288e9e1cc8655b327e081566e580a71d", - "value": "0x244b" - }, - "error": "Reverted", - "result": { - "gasUsed": "0x9daf", - "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" - }, - "subtraces": 3, - "traceAddress": [], - "type": "call" - }"#; - let val = serde_json::from_str::(s).unwrap(); - serde_json::to_value(val).unwrap(); - } - - #[test] - fn test_selfdestruct_suicide() { - let input = r#"{ - "action": { - "address": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "refundAddress": "0x66e29f0b6b1b07071f2fde4345d512386cb66f5f", - "balance": "0x244b" - }, - "error": "Reverted", - "result": { - "gasUsed": "0x9daf", - "output": "0x000000000000000000000000000000000000000000000000011c37937e080000" - }, - "subtraces": 3, - "traceAddress": [], - "type": "suicide" - }"#; - let val = serde_json::from_str::(input).unwrap(); - assert!(val.action.is_selfdestruct()); - - let json = serde_json::to_value(val.clone()).unwrap(); - let expect = serde_json::from_str::(input).unwrap(); - similar_asserts::assert_eq!(json, expect); - let s = serde_json::to_string(&val).unwrap(); - let json = serde_json::from_str::(&s).unwrap(); - similar_asserts::assert_eq!(json, expect); - - let input = input.replace("suicide", "selfdestruct"); - let val = serde_json::from_str::(&input).unwrap(); - assert!(val.action.is_selfdestruct()); - } - - #[derive(Debug)] - struct TraceTestCase { - trace: LocalizedTransactionTrace, - expected_json: Value, - } - - #[test] - fn test_serialization_order() { - let test_cases = vec![ - TraceTestCase { - trace: LocalizedTransactionTrace { - trace: TransactionTrace { - action: Action::Call(CallAction { - from: "4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), - call_type: CallType::DelegateCall, - gas: U64::from(3148955), - input: Bytes::from_str("0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000").unwrap(), - to: "99b5fa03a5ea4315725c43346e55a6a6fbd94098".parse::
().unwrap(), - value: U256::from(0), - }), - error: None, - result: Some(TraceOutput::Call(CallOutput { gas_used: U64::from(32364), output: Bytes::new() })), - subtraces: 0, - trace_address: vec![0, 10, 0], - }, - block_hash: Some(B256::zero()), - block_number: Some(18557272), - transaction_hash: Some(B256::from_str("54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), - transaction_position: Some(102), - }, - expected_json: json!({ - "action": { - "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "callType": "delegatecall", - "gas": "0x300c9b", - "input": "0x585a9fd40000000000000000000000000000000000000000000000000000000000000040a47c5ad9a4af285720eae6cc174a9c75c5bbaf973b00f1a0c191327445b6581000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000ce16f69375520ab01377ce7b88f5ba8c48f8d666f61490331372e432315cd97447e3bc452d6c73a6e0536260a88ddab46f85c88d00000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000aab8cf0fbfb038751339cb61161fa11789b41a78f1b7b0e12cf8e467d403590b7a5f26f0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000646616e746f6d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a3078636531364636393337353532306162303133373763653742383866354241384334384638443636360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045553444300000000000000000000000000000000000000000000000000000000", - "to": "0x99b5fa03a5ea4315725c43346e55a6a6fbd94098", - "value": "0x0" - }, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x7e6c", - "output": "0x" - }, - "subtraces": 0, - "traceAddress": [ - 0, - 10, - 0 - ], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "call" - }), - }, - TraceTestCase { - trace: LocalizedTransactionTrace { - trace: TransactionTrace { - action: Action::Create(CreateAction{ - from: "4f4495243837681061c4743b74b3eedf548d56a5".parse::
().unwrap(), - gas: U64::from(3438907), - init: Bytes::from_str("0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap(), - value: U256::from(0), - }), - error: None, - result: Some(TraceOutput::Create(CreateOutput { gas_used: U64::from(183114), address: "7eb6c6c1db08c0b9459a68cfdcedab64f319c138".parse::
().unwrap(), code: Bytes::from_str("0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033").unwrap() })), - subtraces: 0, - trace_address: vec![0, 7, 0, 0], - }, - block_hash: Some(B256::from_str("d5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d").unwrap()), - block_number: Some(18557272), - transaction_hash: Some(B256::from_str("54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071").unwrap()), - transaction_position: Some(102), - }, - expected_json: json!({ - "action": { - "from": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "gas": "0x34793b", - "init": "0x6080604052600160005534801561001557600080fd5b50610324806100256000396000f3fe608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", - "value": "0x0" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "address": "0x7eb6c6c1db08c0b9459a68cfdcedab64f319c138", - "code": "0x608060405234801561001057600080fd5b50600436106100355760003560e01c8062f55d9d1461003a5780631cff79cd1461004f575b600080fd5b61004d6100483660046101da565b610079565b005b61006261005d3660046101fc565b6100bb565b60405161007092919061027f565b60405180910390f35b6002600054141561009d5760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff8116ff5b60006060600260005414156100e35760405163caa30f5560e01b815260040160405180910390fd5b600260005573ffffffffffffffffffffffffffffffffffffffff85163b610136576040517f6f7c43f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8473ffffffffffffffffffffffffffffffffffffffff16848460405161015d9291906102de565b6000604051808303816000865af19150503d806000811461019a576040519150601f19603f3d011682016040523d82523d6000602084013e61019f565b606091505b50600160005590969095509350505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101d557600080fd5b919050565b6000602082840312156101ec57600080fd5b6101f5826101b1565b9392505050565b60008060006040848603121561021157600080fd5b61021a846101b1565b9250602084013567ffffffffffffffff8082111561023757600080fd5b818601915086601f83011261024b57600080fd5b81358181111561025a57600080fd5b87602082850101111561026c57600080fd5b6020830194508093505050509250925092565b821515815260006020604081840152835180604085015260005b818110156102b557858101830151858201606001528201610299565b818111156102c7576000606083870101525b50601f01601f191692909201606001949350505050565b818382376000910190815291905056fea264697066735822122032cb5e746816b7fac95205c068b30da37bd40119a57265be331c162cae74712464736f6c63430008090033", - "gasUsed": "0x2cb4a" - }, - "subtraces": 0, - "traceAddress": [ - 0, - 7, - 0, - 0 - ], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "create" - }), - } - ]; - - for (i, test_case) in test_cases.iter().enumerate() { - let serialized = serde_json::to_string(&test_case.trace).unwrap(); - let actual_json: Value = serde_json::from_str(&serialized).unwrap(); - - assert_eq!( - actual_json, test_case.expected_json, - "Test case {} failed; Trace: {:?}", - i, test_case.trace - ); - } - } - - #[test] - fn test_deserialize_serialize() { - let reference_data = r#"{ - "action": { - "from": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", - "callType": "call", - "gas": "0x4a0d00", - "input": "0x12", - "to": "0x4f4495243837681061c4743b74b3eedf548d56a5", - "value": "0x0" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x17d337", - "output": "0x" - }, - "subtraces": 1, - "traceAddress": [], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "call" -}"#; - - let trace: LocalizedTransactionTrace = - serde_json::from_str(reference_data).unwrap(); - assert!(trace.trace.action.is_call()); - let serialized = serde_json::to_string_pretty(&trace).unwrap(); - similar_asserts::assert_eq!(serialized, reference_data); - } - - #[test] - fn test_deserialize_serialize_selfdestruct() { - let reference_data = r#"{ - "action": { - "address": "0xc77820eef59629fc8d88154977bc8de8a1b2f4ae", - "balance": "0x0", - "refundAddress": "0x4f4495243837681061c4743b74b3eedf548d56a5" - }, - "blockHash": "0xd5ac5043011d4f16dba7841fa760c4659644b78f663b901af4673b679605ed0d", - "blockNumber": 18557272, - "result": { - "gasUsed": "0x17d337", - "output": "0x" - }, - "subtraces": 1, - "traceAddress": [], - "transactionHash": "0x54160ddcdbfaf98a43a43c328ebd44aa99faa765e0daa93e61145b06815a4071", - "transactionPosition": 102, - "type": "suicide" -}"#; - - let trace: LocalizedTransactionTrace = - serde_json::from_str(reference_data).unwrap(); - assert!(trace.trace.action.is_selfdestruct()); - let serialized = serde_json::to_string_pretty(&trace).unwrap(); - similar_asserts::assert_eq!(serialized, reference_data); - } -} diff --git a/crates/rpc_types_trace/src/state.rs b/crates/rpc_types_trace/src/state.rs deleted file mode 100644 index 948993851a..0000000000 --- a/crates/rpc_types_trace/src/state.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! bindings for state overrides in eth_call - -use alloy_primitives::Bytes; -use cfx_types::{Address, H256 as B256, U256, U64}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -/// A set of account overrides -pub type StateOverride = HashMap; - -/// Custom account override used in call -#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase", deny_unknown_fields)] -pub struct AccountOverride { - /// Fake balance to set for the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub balance: Option, - /// Fake nonce to set for the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub nonce: Option, - /// Fake EVM bytecode to inject into the account before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, - /// Fake key-value mapping to override all slots in the account storage - /// before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state: Option>, - /// Fake key-value mapping to override individual slots in the account - /// storage before executing the call. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub state_diff: Option>, -} - -#[cfg(test)] -mod tests { - use super::*; - use std::str::FromStr; - - #[test] - #[should_panic(expected = "invalid type")] - fn test_invalid_json_structure() { - let invalid_json = r#"{ - "0x1234567890123456789012345678901234567890": { - "balance": true - } - }"#; - - let _: StateOverride = serde_json::from_str(invalid_json).unwrap(); - } - - #[test] - fn test_large_values_in_override() { - let large_values_json = r#"{ - "0x1234567890123456789012345678901234567890": { - "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "nonce": "0xffffffffffffffff" - } - }"#; - - let state_override: StateOverride = - serde_json::from_str(large_values_json).unwrap(); - let acc = state_override - .get( - &Address::from_str("1234567890123456789012345678901234567890") - .unwrap(), - ) - .unwrap(); - assert_eq!(acc.balance, Some(U256::MAX)); - assert_eq!(acc.nonce, Some(U64::MAX)); - } - - #[test] - fn test_default_account_override() { - let acc_override = AccountOverride::default(); - assert!(acc_override.balance.is_none()); - assert!(acc_override.nonce.is_none()); - assert!(acc_override.code.is_none()); - assert!(acc_override.state.is_none()); - assert!(acc_override.state_diff.is_none()); - } - - #[test] - fn test_state_override() { - let s = r#"{ - "0x0000000000000000000000000000000000000124": { - "code": "0x6080604052348015600e575f80fd5b50600436106026575f3560e01c80632096525514602a575b5f80fd5b60306044565b604051901515815260200160405180910390f35b5f604e600242605e565b5f0360595750600190565b505f90565b5f82607757634e487b7160e01b5f52601260045260245ffd5b50069056fea2646970667358221220287f77a4262e88659e3fb402138d2ee6a7ff9ba86bae487a95aa28156367d09c64736f6c63430008140033" - } - }"#; - let state_override: StateOverride = serde_json::from_str(s).unwrap(); - let acc = state_override - .get( - &Address::from_str("0000000000000000000000000000000000000124") - .unwrap(), - ) - .unwrap(); - assert!(acc.code.is_some()); - } - #[test] - fn test_state_override_state_diff() { - let s = r#"{ - "0x1b5212AF6b76113afD94cD2B5a78a73B7d7A8222": { - "balance": "0x39726378b58c400000", - "stateDiff": {} - }, - "0xdAC17F958D2ee523a2206206994597C13D831ec7": { - "stateDiff": { - "0xede27e4e7f3676edbf125879f17a896d6507958df3d57bda6219f1880cae8a41": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - } - } - }"#; - let state_override: StateOverride = serde_json::from_str(s).unwrap(); - let acc = state_override - .get( - &Address::from_str("1b5212AF6b76113afD94cD2B5a78a73B7d7A8222") - .unwrap(), - ) - .unwrap(); - assert!(acc.state_diff.is_some()); - } -} diff --git a/crates/rpc_types_trace/test_data/call_tracer/default.json b/crates/rpc_types_trace/test_data/call_tracer/default.json deleted file mode 100644 index 553b2a3979..0000000000 --- a/crates/rpc_types_trace/test_data/call_tracer/default.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "calls": [ - { - "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", - "gas": "0x6d05", - "gasUsed": "0x0", - "input": "0x", - "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", - "type": "CALL", - "value": "0x6f05b59d3b20000" - } - ], - "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb", - "gas": "0x10738", - "gasUsed": "0x9751", - "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", - "output": "0x0000000000000000000000000000000000000000000000000000000000000001", - "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", - "type": "CALL", - "value": "0x0" -} diff --git a/crates/rpc_types_trace/test_data/call_tracer/legacy.json b/crates/rpc_types_trace/test_data/call_tracer/legacy.json deleted file mode 100644 index b89e7ae86c..0000000000 --- a/crates/rpc_types_trace/test_data/call_tracer/legacy.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "calls": [ - { - "from": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", - "input": "0x", - "to": "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", - "type": "CALL", - "value": "0x6f05b59d3b20000" - } - ], - "from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb", - "gas": "0x10738", - "gasUsed": "0x9751", - "input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5", - "output": "0x0000000000000000000000000000000000000000000000000000000000000001", - "to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe", - "type": "CALL", - "value": "0x0" -} diff --git a/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json b/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json deleted file mode 100644 index 327bb42787..0000000000 --- a/crates/rpc_types_trace/test_data/call_tracer/only_top_call.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "from": "0x4f5777744b500616697cb655dcb02ee6cd51deb5", - "gas": "0x2dced", - "gasUsed": "0x1a9e5", - "to": "0x200edd17f30485a8735878661960cd7a9a95733f", - "input": "0xba51a6df0000000000000000000000000000000000000000000000000000000000000000", - "output": "0xba51a6df00000000000000000000000000000000000000000000000000000000", - "value": "0x8ac7230489e80000", - "type": "CALL" -} diff --git a/crates/rpc_types_trace/test_data/call_tracer/with_log.json b/crates/rpc_types_trace/test_data/call_tracer/with_log.json deleted file mode 100644 index 746dcef8d3..0000000000 --- a/crates/rpc_types_trace/test_data/call_tracer/with_log.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "from": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", - "gas": "0x1f36d", - "gasUsed": "0xc6a5", - "to": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd", - "input": "0xa9059cbb000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb0000000000000000000000000000000000000000000000000000000000989680", - "logs": [ - { - "address": "0xf4eced2f682ce333f96f2d8966c613ded8fc95dd", - "topics": [ - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", - "0x000000000000000000000000d1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", - "0x000000000000000000000000dbf03b407c01e7cd3cbea99509d93f8dddc8c6fb" - ], - "data": "0x0000000000000000000000000000000000000000000000000000000000989680" - } - ], - "value": "0x0", - "type": "CALL" -} diff --git a/crates/rpc_types_trace/test_data/default/structlogs_01.json b/crates/rpc_types_trace/test_data/default/structlogs_01.json deleted file mode 100644 index 2025640f81..0000000000 --- a/crates/rpc_types_trace/test_data/default/structlogs_01.json +++ /dev/null @@ -1,4112 +0,0 @@ -{ - "structLogs": [ - { - "pc": 0, - "op": "PUSH1", - "gas": 24595, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [] - }, - { - "pc": 2, - "op": "PUSH1", - "gas": 24592, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x80" - ], - "memory": [] - }, - { - "pc": 4, - "op": "MSTORE", - "gas": 24589, - "gasCost": 12, - "depth": 1, - "stack": [ - "0x80", - "0x40" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000" - ] - }, - { - "pc": 5, - "op": "CALLVALUE", - "gas": 24577, - "gasCost": 2, - "depth": 1, - "stack": [], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6, - "op": "DUP1", - "gas": 24575, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 7, - "op": "ISZERO", - "gas": 24572, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8, - "op": "PUSH2", - "gas": 24569, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x0", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 11, - "op": "JUMPI", - "gas": 24566, - "gasCost": 10, - "depth": 1, - "stack": [ - "0x0", - "0x1", - "0x10" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 16, - "op": "JUMPDEST", - "gas": 24556, - "gasCost": 1, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 17, - "op": "POP", - "gas": 24555, - "gasCost": 2, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 18, - "op": "PUSH1", - "gas": 24553, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 20, - "op": "CALLDATASIZE", - "gas": 24550, - "gasCost": 2, - "depth": 1, - "stack": [ - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 21, - "op": "LT", - "gas": 24548, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x4", - "0x44" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 22, - "op": "PUSH2", - "gas": 24545, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 25, - "op": "JUMPI", - "gas": 24542, - "gasCost": 10, - "depth": 1, - "stack": [ - "0x0", - "0x1fb" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 26, - "op": "PUSH1", - "gas": 24532, - "gasCost": 3, - "depth": 1, - "stack": [], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 28, - "op": "CALLDATALOAD", - "gas": 24529, - "gasCost": 3, - "depth": 1, - "stack": [ - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 29, - "op": "PUSH1", - "gas": 24526, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb46500000000000000000000000000000000000111abe46ff893f3b2fdf1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 31, - "op": "SHR", - "gas": 24523, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb46500000000000000000000000000000000000111abe46ff893f3b2fdf1", - "0xe0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 32, - "op": "DUP1", - "gas": 24520, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 33, - "op": "PUSH4", - "gas": 24517, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 38, - "op": "GT", - "gas": 24514, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465", - "0x6352211e" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 39, - "op": "PUSH2", - "gas": 24511, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 42, - "op": "JUMPI", - "gas": 24508, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0", - "0x11a" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 43, - "op": "DUP1", - "gas": 24498, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 44, - "op": "PUSH4", - "gas": 24495, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 49, - "op": "GT", - "gas": 24492, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465", - "0x95d89b41" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 50, - "op": "PUSH2", - "gas": 24489, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 53, - "op": "JUMPI", - "gas": 24486, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0", - "0xad" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 54, - "op": "DUP1", - "gas": 24476, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 55, - "op": "PUSH4", - "gas": 24473, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 60, - "op": "GT", - "gas": 24470, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465", - "0xdb006a75" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 61, - "op": "PUSH2", - "gas": 24467, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 64, - "op": "JUMPI", - "gas": 24464, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x1", - "0x7c" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 124, - "op": "JUMPDEST", - "gas": 24454, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 125, - "op": "DUP1", - "gas": 24453, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 126, - "op": "PUSH4", - "gas": 24450, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 131, - "op": "EQ", - "gas": 24447, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465", - "0x95d89b41" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 132, - "op": "PUSH2", - "gas": 24444, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 135, - "op": "JUMPI", - "gas": 24441, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x0", - "0x3c7" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 136, - "op": "DUP1", - "gas": 24431, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 137, - "op": "PUSH4", - "gas": 24428, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 142, - "op": "EQ", - "gas": 24425, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0xa22cb465", - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 143, - "op": "PUSH2", - "gas": 24422, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 146, - "op": "JUMPI", - "gas": 24419, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x1", - "0x3cf" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 975, - "op": "JUMPDEST", - "gas": 24409, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 976, - "op": "PUSH2", - "gas": 24408, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 979, - "op": "PUSH2", - "gas": 24405, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 982, - "op": "CALLDATASIZE", - "gas": 24402, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 983, - "op": "PUSH1", - "gas": 24400, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 985, - "op": "PUSH2", - "gas": 24397, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 988, - "op": "JUMP", - "gas": 24394, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x2231" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8753, - "op": "JUMPDEST", - "gas": 24386, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8754, - "op": "PUSH1", - "gas": 24385, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8756, - "op": "DUP1", - "gas": 24382, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8757, - "op": "PUSH1", - "gas": 24379, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8759, - "op": "DUP4", - "gas": 24376, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x40" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8760, - "op": "DUP6", - "gas": 24373, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x40", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8761, - "op": "SUB", - "gas": 24370, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x40", - "0x4", - "0x44" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8762, - "op": "SLT", - "gas": 24367, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x40", - "0x40" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8763, - "op": "ISZERO", - "gas": 24364, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8764, - "op": "PUSH2", - "gas": 24361, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8767, - "op": "JUMPI", - "gas": 24358, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x1", - "0x2243" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8771, - "op": "JUMPDEST", - "gas": 24348, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8772, - "op": "PUSH2", - "gas": 24347, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8775, - "op": "DUP4", - "gas": 24344, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8776, - "op": "PUSH2", - "gas": 24341, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8779, - "op": "JUMP", - "gas": 24338, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x211a" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8474, - "op": "JUMPDEST", - "gas": 24330, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8475, - "op": "DUP1", - "gas": 24329, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8476, - "op": "CALLDATALOAD", - "gas": 24326, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8477, - "op": "PUSH1", - "gas": 24323, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8479, - "op": "PUSH1", - "gas": 24320, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8481, - "op": "PUSH1", - "gas": 24317, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8483, - "op": "SHL", - "gas": 24314, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0xa0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8484, - "op": "SUB", - "gas": 24311, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x10000000000000000000000000000000000000000" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8485, - "op": "DUP2", - "gas": 24308, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xffffffffffffffffffffffffffffffffffffffff" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8486, - "op": "AND", - "gas": 24305, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xffffffffffffffffffffffffffffffffffffffff", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8487, - "op": "DUP2", - "gas": 24302, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8488, - "op": "EQ", - "gas": 24299, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8489, - "op": "PUSH2", - "gas": 24296, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8492, - "op": "JUMPI", - "gas": 24293, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x18b8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6328, - "op": "JUMPDEST", - "gas": 24283, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6329, - "op": "SWAP2", - "gas": 24282, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x224c", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6330, - "op": "SWAP1", - "gas": 24279, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x4", - "0x224c" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6331, - "op": "POP", - "gas": 24276, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x224c", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 6332, - "op": "JUMP", - "gas": 24274, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x224c" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8780, - "op": "JUMPDEST", - "gas": 24266, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8781, - "op": "SWAP2", - "gas": 24265, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x0", - "0x0", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8782, - "op": "POP", - "gas": 24262, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8783, - "op": "PUSH1", - "gas": 24260, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8785, - "op": "DUP4", - "gas": 24257, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x20" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8786, - "op": "ADD", - "gas": 24254, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x20", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8787, - "op": "CALLDATALOAD", - "gas": 24251, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x24" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8788, - "op": "DUP1", - "gas": 24248, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8789, - "op": "ISZERO", - "gas": 24245, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8790, - "op": "ISZERO", - "gas": 24242, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8791, - "op": "DUP2", - "gas": 24239, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8792, - "op": "EQ", - "gas": 24236, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8793, - "op": "PUSH2", - "gas": 24233, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8796, - "op": "JUMPI", - "gas": 24230, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1", - "0x2260" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8800, - "op": "JUMPDEST", - "gas": 24220, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8801, - "op": "DUP1", - "gas": 24219, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8802, - "op": "SWAP2", - "gas": 24216, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8803, - "op": "POP", - "gas": 24213, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8804, - "op": "POP", - "gas": 24211, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8805, - "op": "SWAP3", - "gas": 24209, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x44", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8806, - "op": "POP", - "gas": 24206, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x1", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x44" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8807, - "op": "SWAP3", - "gas": 24204, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x3dd", - "0x1", - "0x4", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8808, - "op": "SWAP1", - "gas": 24201, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x4", - "0x3dd" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8809, - "op": "POP", - "gas": 24198, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x3dd", - "0x4" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 8810, - "op": "JUMP", - "gas": 24196, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x3dd" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 989, - "op": "JUMPDEST", - "gas": 24188, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 990, - "op": "PUSH2", - "gas": 24187, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 993, - "op": "JUMP", - "gas": 24184, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xc94" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3220, - "op": "JUMPDEST", - "gas": 24176, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3221, - "op": "PUSH1", - "gas": 24175, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3223, - "op": "PUSH1", - "gas": 24172, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3225, - "op": "PUSH1", - "gas": 24169, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3227, - "op": "SHL", - "gas": 24166, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0x1", - "0xa0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3228, - "op": "SUB", - "gas": 24163, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0x10000000000000000000000000000000000000000" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3229, - "op": "DUP3", - "gas": 24160, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xffffffffffffffffffffffffffffffffffffffff" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3230, - "op": "AND", - "gas": 24157, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xffffffffffffffffffffffffffffffffffffffff", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3231, - "op": "CALLER", - "gas": 24154, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3232, - "op": "EQ", - "gas": 24152, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3233, - "op": "ISZERO", - "gas": 24149, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3234, - "op": "PUSH2", - "gas": 24146, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3237, - "op": "JUMPI", - "gas": 24143, - "gasCost": 10, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x1", - "0xced" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3309, - "op": "JUMPDEST", - "gas": 24133, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3310, - "op": "CALLER", - "gas": 24132, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3311, - "op": "PUSH1", - "gas": 24130, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3313, - "op": "DUP2", - "gas": 24127, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3314, - "op": "DUP2", - "gas": 24124, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3315, - "op": "MSTORE", - "gas": 24121, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0" - ], - "memory": [ - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3316, - "op": "PUSH1", - "gas": 24118, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3318, - "op": "PUSH1", - "gas": 24115, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x5" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3320, - "op": "SWAP1", - "gas": 24112, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x5", - "0x20" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3321, - "op": "DUP2", - "gas": 24109, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x5" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3322, - "op": "MSTORE", - "gas": 24106, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x5", - "0x20" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3323, - "op": "PUSH1", - "gas": 24103, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3325, - "op": "DUP1", - "gas": 24100, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3326, - "op": "DUP4", - "gas": 24097, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x40" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3327, - "op": "KECCAK256", - "gas": 24094, - "gasCost": 42, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x40", - "0x0" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3328, - "op": "PUSH1", - "gas": 24052, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3330, - "op": "PUSH1", - "gas": 24049, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x1" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3332, - "op": "PUSH1", - "gas": 24046, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x1", - "0x1" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3334, - "op": "SHL", - "gas": 24043, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x1", - "0x1", - "0xa0" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3335, - "op": "SUB", - "gas": 24040, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x1", - "0x10000000000000000000000000000000000000000" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3336, - "op": "DUP8", - "gas": 24037, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0xffffffffffffffffffffffffffffffffffffffff" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3337, - "op": "AND", - "gas": 24034, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0xffffffffffffffffffffffffffffffffffffffff", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3338, - "op": "DUP1", - "gas": 24031, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3339, - "op": "DUP6", - "gas": 24028, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3340, - "op": "MSTORE", - "gas": 24025, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x0" - ], - "memory": [ - "000000000000000000000000a7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3341, - "op": "SWAP1", - "gas": 24022, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3342, - "op": "DUP4", - "gas": 24019, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3343, - "op": "MSTORE", - "gas": 24016, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0x20" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "0000000000000000000000000000000000000000000000000000000000000005", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3344, - "op": "SWAP3", - "gas": 24013, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x0", - "0x20", - "0x40", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3345, - "op": "DUP2", - "gas": 24010, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3346, - "op": "SWAP1", - "gas": 24007, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x0", - "0x40" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3347, - "op": "KECCAK256", - "gas": 24004, - "gasCost": 42, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x40", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3348, - "op": "DUP1", - "gas": 23962, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3349, - "op": "SLOAD", - "gas": 23959, - "gasCost": 2100, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ], - "storage": { - "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a": "0000000000000000000000000000000000000000000000000000000000000000" - } - }, - { - "pc": 3350, - "op": "PUSH1", - "gas": 21859, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3352, - "op": "NOT", - "gas": 21856, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0", - "0xff" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3353, - "op": "AND", - "gas": 21853, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0", - "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3354, - "op": "DUP7", - "gas": 21850, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3355, - "op": "ISZERO", - "gas": 21847, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3356, - "op": "ISZERO", - "gas": 21844, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3357, - "op": "SWAP1", - "gas": 21841, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x0", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3358, - "op": "DUP2", - "gas": 21838, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x1", - "0x0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3359, - "op": "OR", - "gas": 21835, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x1", - "0x0", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3360, - "op": "SWAP1", - "gas": 21832, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x1", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3361, - "op": "SWAP2", - "gas": 21829, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a", - "0x1", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3362, - "op": "SSTORE", - "gas": 21826, - "gasCost": 20000, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x1", - "0x1", - "0x6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ], - "storage": { - "6693dabf5ec7ab1a0d1c5bc58451f85d5e44d504c9ffeb75799bfdb61aa2997a": "0000000000000000000000000000000000000000000000000000000000000001" - } - }, - { - "pc": 3363, - "op": "SWAP1", - "gas": 1826, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x40", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3364, - "op": "MLOAD", - "gas": 1823, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x1", - "0x40" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3365, - "op": "SWAP1", - "gas": 1820, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x1", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3366, - "op": "DUP2", - "gas": 1817, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x80", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080" - ] - }, - { - "pc": 3367, - "op": "MSTORE", - "gas": 1814, - "gasCost": 9, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x80", - "0x1", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000000" - ] - }, - { - "pc": 3368, - "op": "SWAP2", - "gas": 1805, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x20", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3369, - "op": "SWAP3", - "gas": 1802, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x80", - "0x20", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3370, - "op": "SWAP2", - "gas": 1799, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x80", - "0x20", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3371, - "op": "PUSH32", - "gas": 1796, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x20", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3404, - "op": "SWAP2", - "gas": 1793, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x20", - "0x80", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3405, - "op": "ADD", - "gas": 1790, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0x80", - "0x20" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3406, - "op": "PUSH1", - "gas": 1787, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0xa0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3408, - "op": "MLOAD", - "gas": 1784, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0xa0", - "0x40" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3409, - "op": "DUP1", - "gas": 1781, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0xa0", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3410, - "op": "SWAP2", - "gas": 1778, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0xa0", - "0x80", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3411, - "op": "SUB", - "gas": 1775, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0x80", - "0x80", - "0xa0" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3412, - "op": "SWAP1", - "gas": 1772, - "gasCost": 3, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0x80", - "0x20" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3413, - "op": "LOG3", - "gas": 1769, - "gasCost": 1756, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0xa7194f8a5f509ed2c95ade0b4efb6940a45d7a11", - "0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31", - "0x20", - "0x80" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3414, - "op": "POP", - "gas": 13, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8", - "0x1" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3415, - "op": "POP", - "gas": 11, - "gasCost": 2, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b", - "0x111abe46ff893f3b2fdf1f759a8a8" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 3416, - "op": "JUMP", - "gas": 9, - "gasCost": 8, - "depth": 1, - "stack": [ - "0xa22cb465", - "0x27b" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 635, - "op": "JUMPDEST", - "gas": 1, - "gasCost": 1, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - }, - { - "pc": 636, - "op": "STOP", - "gas": 0, - "gasCost": 0, - "depth": 1, - "stack": [ - "0xa22cb465" - ], - "memory": [ - "00000000000000000000000000000000000111abe46ff893f3b2fdf1f759a8a8", - "7d3429278e27616819652c726b56f6b8ffeea2d2c23cf663064312a58b0422d2", - "0000000000000000000000000000000000000000000000000000000000000080", - "0000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000001" - ] - } - ], - "gas": 46107, - "failed": false, - "returnValue": "" -} \ No newline at end of file diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/default.json b/crates/rpc_types_trace/test_data/pre_state_tracer/default.json deleted file mode 100644 index 43e69b11bd..0000000000 --- a/crates/rpc_types_trace/test_data/pre_state_tracer/default.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "0x082d4cdf07f386ffa9258f52a5c49db4ac321ec6": { - "balance": "0xc820f93200f4000", - "nonce": 94 - }, - "0x332b656504f4eabb44c8617a42af37461a34e9dc": { - "balance": "0x11faea4f35e5af80000", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000" - } - }, - "0x52bc44d5378309ee2abf1539bf71de1b7d7be3b5": { - "balance": "0xbf681825be002ac452", - "nonce": 28922 - }, - "0x82effbaaaf28614e55b2ba440fb198e0e5789b0f": { - "balance": "0xb3d0ac5cb94df6f6b0", - "nonce": 1 - } -} diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json b/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json deleted file mode 100644 index 0654d26f54..0000000000 --- a/crates/rpc_types_trace/test_data/pre_state_tracer/diff_mode.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "pre": { - "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { - "balance": "0x0", - "nonce": 22 - }, - "0x1585936b53834b021f68cc13eeefdec2efc8e724": { - "balance": "0x0" - }, - "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { - "balance": "0x4d87094125a369d9bd5", - "nonce": 1, - "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" - } - }, - "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { - "balance": "0x1780d77678137ac1b775", - "nonce": 29072 - } - }, - "post": { - "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { - "balance": "0x6f05b59d3b20000" - }, - "0x1585936b53834b021f68cc13eeefdec2efc8e724": { - "balance": "0x420eed1bd6c00" - }, - "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { - "balance": "0x4d869a3b70062eb9bd5", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b95e" - } - }, - "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { - "balance": "0x1780d7725724a9044b75", - "nonce": 29073 - } - } -} diff --git a/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json b/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json deleted file mode 100644 index dbefb198c4..0000000000 --- a/crates/rpc_types_trace/test_data/pre_state_tracer/legacy.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": { - "balance": "0x0", - "code": "0x", - "nonce": 22, - "storage": {} - }, - "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": { - "balance": "0x4d87094125a369d9bd5", - "code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029", - "nonce": 1, - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb", - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000", - "0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c", - "0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834" - } - }, - "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": { - "balance": "0x1780d77678137ac1b775", - "code": "0x", - "nonce": 29072, - "storage": {} - } -} From b6a1b37e5b263c239e40455aa03429fc4406bc5c Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 30 Apr 2024 18:48:35 +0800 Subject: [PATCH 016/137] misc --- README.md | 10 ++- bins/conflux/src/command/helpers.rs | 2 - .../consensus_inner/consensus_executor.rs | 82 +++++-------------- 3 files changed, 28 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 947ae3f5a8..6f38fb7133 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,12 @@ significant changes to the Conflux protocol, please submit a Unit tests come together with the Rust code. They can be invoked via `cargo test --release --all`. See the [Getting Started](https://doc.confluxnetwork.org/docs/general/run-a-node/) -page for more information. Integration tests are Python test scripts with the -`_test.py` suffix in the `tests` directory. To run these tests, first compile Conflux -in _release_ mode using `cargo build --release`. Then, you can run all -integration tests using the script `tests/test_all.py`. +page for more information. + +Integration tests are Python test scripts with the `_test.py` suffix in the `tests` directory. +To run these tests, first compile Conflux in _release_ mode using `cargo build --release` and +fetch all submodule using `git submodule update --remote --recursive --init`. +Then, you can run all integration tests using the script `tests/test_all.py`. ## Resources diff --git a/bins/conflux/src/command/helpers.rs b/bins/conflux/src/command/helpers.rs index 195d68118a..594c8c239c 100644 --- a/bins/conflux/src/command/helpers.rs +++ b/bins/conflux/src/command/helpers.rs @@ -19,8 +19,6 @@ // See http://www.gnu.org/licenses/ use cfxkey::Password; -#[allow(unused_imports)] -pub use dir::helpers::{replace_home, replace_home_and_local}; use rpassword::read_password; use std::{ fs::File, diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 067e13ce31..ceb3f18f4e 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -1952,31 +1952,28 @@ impl ConsensusExecutionHandler { .expect("blocks exist"); let pivot_block = epoch_blocks.last().expect("Not empty"); - let recover_mpt_during_construct_pivot_state = false; + let state_availability_boundary = + self.data_man.state_availability_boundary.read(); + let state_space = None; + if !state_availability_boundary.check_read_availability( + pivot_block.block_header.height(), + epoch_id, + state_space, + ) { + bail!("state is not ready"); + } + let state_index = self.data_man.get_state_readonly_index(epoch_id); let mut state = State::new(StateDb::new( self.data_man .storage_manager - .get_state_for_next_epoch( - StateIndex::new_for_next_epoch( - pivot_block.block_header.parent_hash(), - &self - .data_man - .get_epoch_execution_commitment( - pivot_block.block_header.parent_hash(), - ) - // Unwrapping is safe because the state exists. - .unwrap() - .state_root_with_aux_info, - pivot_block.block_header.height() - 1, - self.data_man.get_snapshot_epoch_count(), - ), - recover_mpt_during_construct_pivot_state, - ) - .expect("No db error") - // Unwrapping is safe because the state exists. - .expect("State exists"), - )) - .expect("Failed to initialize state"); + .get_state_no_commit( + state_index.unwrap(), + /* try_open = */ true, + state_space, + )? + .ok_or("state deleted")?, + ))?; + drop(state_availability_boundary); let start_block_number = self .data_man @@ -1985,7 +1982,6 @@ impl ConsensusExecutionHandler { .expect("should exist"); self.execute_epoch_tx_to_collect_trace( - *epoch_id, &mut state, &epoch_blocks, start_block_number, @@ -1996,44 +1992,10 @@ impl ConsensusExecutionHandler { } fn execute_epoch_tx_to_collect_trace( - &self, epoch_id: EpochId, state: &mut State, - epoch_blocks: &Vec>, start_block_number: u64, - tx_hash: Option, opts: GethDebugTracingOptions, + &self, state: &mut State, epoch_blocks: &Vec>, + start_block_number: u64, tx_hash: Option, + opts: GethDebugTracingOptions, ) -> DbResult> { - // Prefetch accounts for transactions. - // The return value _prefetch_join_handles is used to join all threads - // before the exit of this function. - let prefetch_join_handles = match self - .execution_state_prefetcher - .as_ref() - { - Some(prefetcher) => { - let mut accounts = vec![]; - for block in epoch_blocks.iter() { - for transaction in block.transactions.iter() { - accounts.push(&transaction.sender); - match transaction.action() { - Action::Call(ref address) => accounts.push(address), - _ => {} - } - } - } - - prefetch_accounts(prefetcher, epoch_id, state, accounts) - } - None => PrefetchTaskHandle { - task_epoch_id: epoch_id, - state, - prefetcher: None, - accounts: vec![], - }, - }; - - prefetch_join_handles.wait_for_task(); - drop(prefetch_join_handles); - - // TODO whether need to revert the state change after the execution - let pivot_block = epoch_blocks.last().expect("Epoch not empty"); let mut block_number = start_block_number; From 33bf6a64942dde1a975505d5e47102e8d3d746b6 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 6 May 2024 11:55:00 +0800 Subject: [PATCH 017/137] CIP-131: Retain Whitelist on Contract Deletion --- .../src/executive/pre_checked_executive.rs | 1 + crates/cfxcore/executor/src/spec.rs | 3 +++ .../executor/src/state/state_object/sponsor.rs | 16 +++++++++------- crates/cfxcore/vm-types/src/spec.rs | 3 +++ crates/client/src/configuration.rs | 5 +++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs index 72bec1490b..20d3a63a6a 100644 --- a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs +++ b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs @@ -365,6 +365,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { state.record_storage_and_whitelist_entries_release( &address.address, &mut substate, + spec.cip131, )?; assert!(state.is_fresh_storage(address)?); diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index a8b5862b9d..5ad0938e34 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -98,6 +98,8 @@ pub struct TransitionsBlockNumber { pub cip118: BlockNumber, /// CIP-119: PUSH0 instruction pub cip119: BlockNumber, + /// CIP-131: Retain Whitelist on Contract Deletion + pub cip131: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -162,6 +164,7 @@ impl CommonParams { spec.cip107 = number >= self.transition_numbers.cip107; spec.cip118 = number >= self.transition_numbers.cip118; spec.cip119 = number >= self.transition_numbers.cip119; + spec.cip131 = number >= self.transition_numbers.cip131; spec } diff --git a/crates/cfxcore/executor/src/state/state_object/sponsor.rs b/crates/cfxcore/executor/src/state/state_object/sponsor.rs index 9a64ce047c..7903265e24 100644 --- a/crates/cfxcore/executor/src/state/state_object/sponsor.rs +++ b/crates/cfxcore/executor/src/state/state_object/sponsor.rs @@ -205,14 +205,16 @@ impl State { } pub fn record_storage_and_whitelist_entries_release( - &mut self, address: &Address, substate: &mut Substate, + &mut self, address: &Address, substate: &mut Substate, cip131: bool, ) -> DbResult<()> { - storage_range_deletion_for_account( - self, - &SPONSOR_WHITELIST_CONTROL_CONTRACT_ADDRESS, - address.as_ref(), - substate, - )?; + if !cip131 { + storage_range_deletion_for_account( + self, + &SPONSOR_WHITELIST_CONTROL_CONTRACT_ADDRESS, + address.as_ref(), + substate, + )?; + } storage_range_deletion_for_account(self, address, &vec![], substate)?; Ok(()) } diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index e995c42bb4..ab07608479 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -158,6 +158,8 @@ pub struct Spec { pub cip118: bool, /// CIP-119: PUSH0 instruction pub cip119: bool, + /// CIP-131: Retain Whitelist on Contract Deletion + pub cip131: bool, pub params_dao_vote_period: u64, } @@ -299,6 +301,7 @@ impl Spec { cip107: false, cip118: false, cip119: false, + cip131: false, } } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 98dcc86797..14ff275047 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -161,6 +161,7 @@ build_config! { (cip112_transition_height, (Option), None) (cip118_transition_number, (Option), None) (cip119_transition_number, (Option), None) + (next_hardfork_transition_number, (Option), None) (referee_bound, (usize), REFEREE_DEFAULT_BOUND) (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD) (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA) @@ -1278,6 +1279,10 @@ impl Configuration { .raw_conf .cip119_transition_number .unwrap_or(default_transition_time); + params.transition_numbers.cip131 = self + .raw_conf + .next_hardfork_transition_number + .unwrap_or(default_transition_time); if self.is_test_or_dev_mode() { params.transition_numbers.cip43b = self.raw_conf.cip43_init_end_number.unwrap_or(u64::MAX); From 5270ae2e42a90ed35c49e4218ac7a3c347aecfe8 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 6 May 2024 12:23:20 +0800 Subject: [PATCH 018/137] Fix python tests --- tests/contract_remove_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/contract_remove_test.py b/tests/contract_remove_test.py index 4c2e9050ac..bcab2ea14e 100644 --- a/tests/contract_remove_test.py +++ b/tests/contract_remove_test.py @@ -36,6 +36,9 @@ def set_test_params(self): self.conf_parameters["dev_snapshot_epoch_count"] = str(SNAPSHOT_EPOCH) self.conf_parameters["anticone_penalty_ratio"] = "10" + # Disable CIP-131 on test + self.conf_parameters["next_hardfork_transition_number"] = 9999999 + def run_test(self): accounts: List[Account] = self.initialize_accounts(2, value = 1000) self.genesis_addr3 = accounts[0].address From a6010a817088520d5f388488efbb4b8969b8f669 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 6 May 2024 12:13:56 +0800 Subject: [PATCH 019/137] CIP-132: Fix Static Context Check for Internal Contracts --- .../internal_contract/components/function.rs | 25 ++++++++++--------- crates/cfxcore/executor/src/spec.rs | 3 +++ crates/cfxcore/vm-types/src/spec.rs | 3 +++ crates/client/src/configuration.rs | 4 +++ 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/crates/cfxcore/executor/src/internal_contract/components/function.rs b/crates/cfxcore/executor/src/internal_contract/components/function.rs index cc1755e8e4..5fde0ab306 100644 --- a/crates/cfxcore/executor/src/internal_contract/components/function.rs +++ b/crates/cfxcore/executor/src/internal_contract/components/function.rs @@ -4,11 +4,9 @@ use cfx_statedb::Result as DbResult; use cfx_types::U256; -use cfx_vm_types::{self as vm, ActionParams, CallType, GasLeft, Spec}; +use cfx_vm_types::{self as vm, ActionParams, CallType, GasLeft}; use solidity_abi::{ABIDecodable, ABIEncodable}; -use crate::stack::CallStackInfo; - use super::{InternalRefContext, InternalTrapResult, IsActive}; use InternalTrapResult::*; @@ -90,7 +88,7 @@ fn preprocessing( sol_fn: &T, input: &[u8], params: &ActionParams, context: &InternalRefContext, ) -> vm::Result<(T::Input, U256)> { - sol_fn.pre_execution_check(params, context.callstack, context.spec)?; + sol_fn.pre_execution_check(params, context)?; let solidity_params = ::abi_decode(&input)?; let cost = sol_fn.upfront_gas_payment(&solidity_params, params, context)?; if cost > params.gas { @@ -108,8 +106,7 @@ pub trait InterfaceTrait { pub trait PreExecCheckTrait: Send + Sync { fn pre_execution_check( - &self, params: &ActionParams, call_stack: &CallStackInfo, - context: &Spec, + &self, params: &ActionParams, context: &InternalRefContext, ) -> vm::Result<()>; } @@ -157,18 +154,22 @@ pub trait PreExecCheckConfTrait: Send + Sync { impl PreExecCheckTrait for T { fn pre_execution_check( - &self, params: &ActionParams, call_stack: &CallStackInfo, spec: &Spec, + &self, params: &ActionParams, context: &InternalRefContext, ) -> vm::Result<()> { if !Self::PAYABLE && !params.value.value().is_zero() { return Err(vm::Error::InternalContract( - "should not transfer balance to Staking contract".into(), + "should not transfer balance to non-payable function".into(), )); } - if Self::HAS_WRITE_OP - && (call_stack.in_reentrancy(spec) - || params.call_type == CallType::StaticCall) - { + let spec = context.spec; + // Check static context before CIP-132 + let mut static_context = context.callstack.in_reentrancy(spec) + || params.call_type == CallType::StaticCall; + // Add the lost constraint after CIP-132 + static_context |= spec.cip132 && context.static_flag; + + if Self::HAS_WRITE_OP && static_context { return Err(vm::Error::MutableCallInStaticContext); } diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 5ad0938e34..cd135f58d7 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -100,6 +100,8 @@ pub struct TransitionsBlockNumber { pub cip119: BlockNumber, /// CIP-131: Retain Whitelist on Contract Deletion pub cip131: BlockNumber, + /// CIP-132: Fix Static Context Check for Internal Contracts + pub cip132: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -165,6 +167,7 @@ impl CommonParams { spec.cip118 = number >= self.transition_numbers.cip118; spec.cip119 = number >= self.transition_numbers.cip119; spec.cip131 = number >= self.transition_numbers.cip131; + spec.cip132 = number >= self.transition_numbers.cip132; spec } diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index ab07608479..e479bf2c9c 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -160,6 +160,8 @@ pub struct Spec { pub cip119: bool, /// CIP-131: Retain Whitelist on Contract Deletion pub cip131: bool, + /// CIP-132: Fix Static Context Check for Internal Contracts + pub cip132: bool, pub params_dao_vote_period: u64, } @@ -302,6 +304,7 @@ impl Spec { cip118: false, cip119: false, cip131: false, + cip132: false, } } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 14ff275047..1ad714edfa 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1283,6 +1283,10 @@ impl Configuration { .raw_conf .next_hardfork_transition_number .unwrap_or(default_transition_time); + params.transition_numbers.cip132 = self + .raw_conf + .next_hardfork_transition_number + .unwrap_or(default_transition_time); if self.is_test_or_dev_mode() { params.transition_numbers.cip43b = self.raw_conf.cip43_init_end_number.unwrap_or(u64::MAX); From 0b257413f06a8049728b077d6a7cfa3b399c24bc Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 6 May 2024 13:26:45 +0800 Subject: [PATCH 020/137] CIP-130: Aligning Gas Limit with Transaction Size --- crates/cfxcore/core/src/verification.rs | 19 +++++++++++++++++++ crates/cfxcore/executor/src/spec.rs | 2 ++ crates/client/src/configuration.rs | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 587b242f3d..746cd78a94 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -644,6 +644,7 @@ impl VerificationConfig { // ****************************************** let cip76 = height >= transitions.cip76; let cip90a = height >= transitions.cip90a; + let cip130 = height >= transitions.cip130; if let Transaction::Native(ref tx) = tx.unsigned { Self::verify_transaction_epoch_height( @@ -659,6 +660,7 @@ impl VerificationConfig { } Self::check_gas_limit(tx, cip76, &mode)?; + Self::check_gas_limit_with_calldata(tx, cip130)?; Ok(()) } @@ -709,6 +711,23 @@ impl VerificationConfig { Ok(()) } + fn check_gas_limit_with_calldata( + tx: &TransactionWithSignature, cip130: bool, + ) -> Result<(), TransactionError> { + if !cip130 { + return Ok(()); + } + let data_length = tx.data().len(); + let min_gas_limit = data_length.saturating_mul(100); + if tx.gas() < &U256::from(min_gas_limit) { + bail!(TransactionError::NotEnoughBaseGas { + required: min_gas_limit.into(), + got: *tx.gas() + }); + } + Ok(()) + } + pub fn check_tx_size( &self, tx: &TransactionWithSignature, ) -> Result<(), TransactionError> { diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index cd135f58d7..81532fa796 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -118,6 +118,8 @@ pub struct TransitionsEpochHeight { pub cip94: BlockHeight, /// CIP-112: Fix Block Headers `custom` Field Serde pub cip112: BlockHeight, + /// CIP-130: Aligning Gas Limit with Transaction Size + pub cip130: BlockHeight, } impl Default for CommonParams { diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 1ad714edfa..7b281b6309 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -162,6 +162,7 @@ build_config! { (cip118_transition_number, (Option), None) (cip119_transition_number, (Option), None) (next_hardfork_transition_number, (Option), None) + (next_hardfork_transition_height, (Option), None) (referee_bound, (usize), REFEREE_DEFAULT_BOUND) (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD) (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA) @@ -1346,6 +1347,10 @@ impl Configuration { .unwrap_or(non_genesis_default_transition_time); params.transition_heights.cip112 = *CIP112_TRANSITION_HEIGHT.get().expect("initialized"); + params.transition_heights.cip130 = self + .raw_conf + .next_hardfork_transition_height + .unwrap_or(default_transition_time); params.params_dao_vote_period = self.raw_conf.params_dao_vote_period; let mut base_block_rewards = BTreeMap::new(); From ad75143c21f1c5b53e53f241586227d5eb733e50 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 6 May 2024 18:11:03 +0800 Subject: [PATCH 021/137] Fix encoding for eip1559 and eip2930 transactions. --- .../sync/message/get_block_txn_response.rs | 32 +++++++++++++++++-- crates/primitives/src/block.rs | 10 ++++-- crates/primitives/src/transaction/mod.rs | 10 +++--- .../src/transaction/native_transaction.rs | 4 +-- tests/evm_space/eip1559_test.py | 18 +++++++++++ 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs b/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs index 175c04e616..82134a702c 100644 --- a/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs +++ b/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs @@ -14,10 +14,10 @@ use crate::{ use cfx_types::H256; use metrics::MeterTimer; use primitives::{Block, TransactionWithSignature}; -use rlp_derive::{RlpDecodable, RlpEncodable}; +use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use std::collections::HashSet; -#[derive(Debug, PartialEq, Default, RlpDecodable, RlpEncodable)] +#[derive(Debug, PartialEq, Default)] pub struct GetBlockTxnResponse { pub request_id: RequestId, pub block_hash: H256, @@ -145,3 +145,31 @@ impl Handleable for GetBlockTxnResponse { Ok(()) } } + +impl Encodable for GetBlockTxnResponse { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3); + s.append(&self.request_id); + s.append(&self.block_hash); + s.begin_list(self.block_txn.len()); + for tx in &self.block_txn { + s.append(&rlp::encode(tx)); + } + } +} + +impl Decodable for GetBlockTxnResponse { + fn decode(rlp: &Rlp) -> Result { + let request_id = rlp.val_at(0)?; + let block_hash = rlp.val_at(1)?; + let mut block_txn = Vec::new(); + for b in rlp.list_at::>(2)? { + block_txn.push(rlp::decode(&b)?); + } + Ok(Self { + request_id, + block_hash, + block_txn, + }) + } +} diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index b634e184e9..b736f441d7 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -6,6 +6,7 @@ use crate::{BlockHeader, SignedTransaction, TransactionWithSignature}; use byteorder::{ByteOrder, LittleEndian}; use cfx_types::{Space, H256, U256}; use keccak_hash::keccak; +use log::info; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use rand::Rng; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -15,7 +16,6 @@ use std::{ hash::Hasher, sync::Arc, }; -use log::info; pub type BlockNumber = u64; pub type BlockHeight = u64; @@ -194,7 +194,7 @@ impl Encodable for Block { stream.begin_list(2).append(&self.block_header); stream.begin_list(self.transactions.len()); for tx in &self.transactions { - stream.append(&tx.transaction); + stream.append(&rlp::encode(&tx.transaction)); } } } @@ -210,7 +210,11 @@ impl Decodable for Block { return Err(DecoderError::RlpIncorrectListLen); } - let transactions = rlp.list_at::(1)?; + let transactions = rlp + .list_at::>(1)? + .into_iter() + .map(|b| rlp::decode(&b)) + .collect::, DecoderError>>()?; let mut signed_transactions = Vec::with_capacity(transactions.len()); for tx in transactions { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 9cc406d889..25e92913c3 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -371,7 +371,7 @@ impl Transaction { // signatures. pub fn signature_hash(&self) -> H256 { let mut s = RlpStream::new(); - let mut type_prefix= vec![]; + let mut type_prefix = vec![]; match self { Transaction::Native(TypedNativeTransaction::Cip155(tx)) => { s.append(tx); @@ -703,7 +703,8 @@ impl Decodable for TransactionWithSignature { fn decode(d: &Rlp) -> Result { let hash = keccak(d.as_raw()); let rlp_size = Some(d.as_raw().len()); - // The item count of TransactionWithSignatureSerializePart is checked in its decoding. + // The item count of TransactionWithSignatureSerializePart is checked in + // its decoding. let transaction = d.as_val()?; Ok(TransactionWithSignature { transaction, @@ -788,7 +789,7 @@ pub struct SignedTransaction { impl Encodable for SignedTransaction { fn rlp_append(&self, s: &mut RlpStream) { s.begin_list(3); - s.append(&self.transaction); + s.append(&rlp::encode(&self.transaction)); s.append(&self.sender); s.append(&self.public); } @@ -796,8 +797,9 @@ impl Encodable for SignedTransaction { impl Decodable for SignedTransaction { fn decode(rlp: &Rlp) -> Result { + let b: Vec = rlp.val_at(0)?; Ok(SignedTransaction { - transaction: rlp.val_at(0)?, + transaction: rlp::decode(&b)?, sender: rlp.val_at(1)?, public: rlp.val_at(2)?, }) diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs index 60953d2e27..b6c358be42 100644 --- a/crates/primitives/src/transaction/native_transaction.rs +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -1,13 +1,11 @@ use crate::{ - transaction::AccessList, Action, SignedTransaction, Transaction, + transaction::AccessListItem, Action, SignedTransaction, Transaction, TransactionWithSignature, TransactionWithSignatureSerializePart, }; use bytes::Bytes; use cfx_types::{AddressWithSpace, H256, U256}; -use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use rlp_derive::{RlpDecodable, RlpEncodable}; use serde_derive::{Deserialize, Serialize}; -use crate::transaction::AccessListItem; #[derive( Default, diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index bc36493b26..5e91230225 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -7,11 +7,28 @@ from base import Web3Base from test_framework.blocktools import encode_hex_0x from conflux.address import b32_address_to_hex +from conflux.rpc import RpcClient +from web3 import Web3 class Eip1559Test(Web3Base): def set_test_params(self): self.num_nodes = 2 + self.conf_parameters["evm_chain_id"] = str(10) + self.conf_parameters["evm_transaction_block_ratio"] = str(1) + self.conf_parameters["executive_trace"] = "true" + + def setup_network(self): + self.add_nodes(self.num_nodes) + self.start_node(0, ["--archive"]) + self.start_node(1, ["--archive"]) + connect_nodes(self.nodes, 0 , 1) + self.rpc = RpcClient(self.nodes[0]) + ip = self.nodes[0].ip + port = self.nodes[0].ethrpcport + self.w3 = Web3(Web3.HTTPProvider(f'http://{ip}:{port}/')) + assert_equal(self.w3.isConnected(), True) + def run_test(self): self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] @@ -54,6 +71,7 @@ def run_test(self): assert_equal(receipt["status"], 1) # Check if another node can decode EIP1559 transactions + sync_blocks(self.nodes) ret1 = self.nodes[0].debug_getTransactionsByEpoch(hex(receipt["blockNumber"])) ret2 = self.nodes[1].debug_getTransactionsByBlock(encode_hex_0x(tx["blockHash"])) assert_equal(len(ret1), 1) From 613eede166d65656b25c1e386087b9c8e606e640 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 6 May 2024 17:37:58 +0800 Subject: [PATCH 022/137] CIP-133: Enhanced Block Hash Query --- .../consensus_inner/consensus_executor.rs | 53 +++++++++-- crates/cfxcore/executor/src/context.rs | 88 +++++++++++++++---- .../internal_contract/contracts/context.rs | 33 ++++++- .../src/internal_contract/impls/context.rs | 32 +++++++ .../src/internal_contract/impls/mod.rs | 1 + .../executor/src/internal_contract/mod.rs | 1 + crates/cfxcore/executor/src/spec.rs | 7 ++ .../src/interpreter/gasometer.rs | 7 +- .../vm-interpreter/src/interpreter/mod.rs | 2 +- crates/cfxcore/vm-types/src/context.rs | 12 ++- crates/cfxcore/vm-types/src/lib.rs | 4 +- crates/cfxcore/vm-types/src/spec.rs | 8 ++ crates/cfxcore/vm-types/src/tests.rs | 11 ++- crates/client/src/rpc/types/eth/block.rs | 16 ++-- .../contracts/ConfluxContext.sol | 2 + tests/blockhash_test.py | 45 ++++++++++ tests/conflux/rpc.py | 10 ++- tests/test_contracts | 2 +- 18 files changed, 288 insertions(+), 46 deletions(-) create mode 100644 crates/cfxcore/executor/src/internal_contract/impls/context.rs create mode 100644 tests/blockhash_test.py diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index d913aff919..1429c0e56d 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -34,8 +34,8 @@ use cfx_types::{ }; use metrics::{register_meter_with_group, Meter, MeterTimer}; use primitives::{ - compute_block_number, receipt::BlockReceipts, Action, Block, - BlockHeaderBuilder, BlockNumber, EpochId, SignedTransaction, + block::BlockHeight, compute_block_number, receipt::BlockReceipts, Action, + Block, BlockHeaderBuilder, BlockNumber, EpochId, SignedTransaction, TransactionIndex, MERKLE_NULL_NODE, }; @@ -69,7 +69,9 @@ use cfx_executor::{ executive::{ ExecutionOutcome, ExecutiveContext, TransactOptions, TransactSettings, }, - internal_contract::initialize_internal_contract_accounts, + internal_contract::{ + block_hash_slot, epoch_hash_slot, initialize_internal_contract_accounts, + }, machine::Machine, state::{ distribute_pos_interest, initialize_cip107, @@ -1240,6 +1242,11 @@ impl ConsensusExecutionHandler { drop(prefetch_join_handles); let pivot_block = epoch_blocks.last().expect("Epoch not empty"); + self.record_epoch_hash( + state, + pivot_block.block_header.height(), + pivot_block.hash(), + )?; let mut epoch_receipts = Vec::with_capacity(epoch_blocks.len()); let mut epoch_staking_events = Vec::new(); @@ -1254,6 +1261,8 @@ impl ConsensusExecutionHandler { for block in epoch_blocks.iter() { self.maybe_update_state(state, block_number)?; + self.record_block_hash(state, block_number, block.hash())?; + let mut cfx_tx_index = 0; let mut tx_exec_error_messages = @@ -1919,14 +1928,16 @@ impl ConsensusExecutionHandler { fn maybe_update_state( &self, state: &mut State, block_number: BlockNumber, ) -> DbResult<()> { - let cip94_start = self.machine.params().transition_numbers.cip94; - let period = self.machine.params().params_dao_vote_period; + let params = self.machine.params(); + let transition_numbers = ¶ms.transition_numbers; + + let cip94_start = transition_numbers.cip94; + let period = params.params_dao_vote_period; // Update/initialize parameters before processing rewards. if block_number >= cip94_start && (block_number - cip94_start) % period == 0 { - let set_pos_staking = - block_number > self.machine.params().transition_numbers.cip105; + let set_pos_staking = block_number > transition_numbers.cip105; initialize_or_update_dao_voted_params(state, set_pos_staking)?; } @@ -1935,11 +1946,37 @@ impl ConsensusExecutionHandler { // integrated with `initialize_or_update_dao_voted_params`, but // that function will update the value after cip107 is enabled // here. - if block_number == self.machine.params().transition_numbers.cip107 { + if block_number == transition_numbers.cip107 { initialize_cip107(state)?; } Ok(()) } + + fn record_block_hash( + &self, state: &mut State, block_number: BlockNumber, hash: H256, + ) -> DbResult<()> { + let params = self.machine.params(); + if block_number >= params.transition_numbers.cip133_b { + state.set_system_storage( + block_hash_slot(block_number).into(), + U256::from_big_endian(&hash.0), + )?; + } + Ok(()) + } + + fn record_epoch_hash( + &self, state: &mut State, epoch_number: BlockHeight, hash: H256, + ) -> DbResult<()> { + let params = self.machine.params(); + if epoch_number >= params.transition_heights.cip133_e { + state.set_system_storage( + epoch_hash_slot(epoch_number).into(), + U256::from_big_endian(&hash.0), + )?; + } + Ok(()) + } } pub struct ConsensusExecutionConfiguration { diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index 425e8d6c92..9c24bca3a1 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -6,8 +6,12 @@ use crate::{ executive::contract_address, executive_observer::TracerTrait, - internal_contract::{suicide as suicide_impl, InternalRefContext}, + internal_contract::{ + block_hash_slot, epoch_hash_slot, suicide as suicide_impl, + InternalRefContext, + }, machine::Machine, + return_if, stack::{CallStackInfo, FrameLocal, RuntimeRes}, state::State, substate::Substate, @@ -17,7 +21,8 @@ use cfx_parameters::staking::{ code_collateral_units, DRIPS_PER_STORAGE_COLLATERAL_UNIT, }; use cfx_types::{ - Address, AddressSpaceUtil, AddressWithSpace, Space, H256, U256, + Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H256, + U256, }; use cfx_vm_types::{ self as vm, ActionParams, ActionValue, CallType, Context as ContextTrait, @@ -26,6 +31,7 @@ use cfx_vm_types::{ }; use primitives::transaction::UNSIGNED_SENDER; use std::sync::Arc; +use vm::BlockHashSource; /// Transaction properties that externalities need to know about. #[derive(Debug)] @@ -106,6 +112,51 @@ impl<'a> Context<'a> { tracer, } } + + fn blockhash_from_env(&self, number: &U256) -> H256 { + if self.space == Space::Ethereum && self.spec.cip98 { + return if U256::from(self.env().epoch_height) == number + 1 { + self.env().last_hash.clone() + } else { + H256::default() + }; + } + + // In Conflux, we only maintain the block hash of the previous block. + // For other block numbers, it always returns zero. + if U256::from(self.env().number) == number + 1 { + self.env().last_hash.clone() + } else { + H256::default() + } + } + + fn blockhash_from_state(&self, number: &U256) -> vm::Result { + return_if!(number > &U256::from(u64::MAX)); + + let number = number.as_u64(); + + let state_res = match self.space { + Space::Native => { + return_if!(number < self.spec.cip133_b); + return_if!(number > self.env.number); + return_if!(number + .checked_add(65536) + .map_or(false, |n| n <= self.env.number)); + self.state.get_system_storage(&block_hash_slot(number))? + } + Space::Ethereum => { + return_if!(number < self.spec.cip133_e); + return_if!(number > self.env.epoch_height); + return_if!(number + .checked_add(65536) + .map_or(false, |n| n <= self.env.epoch_height)); + self.state.get_system_storage(&epoch_hash_slot(number))? + } + }; + + Ok(BigEndianHash::from_uint(&state_res)) + } } impl<'a> ContextTrait for Context<'a> { @@ -165,21 +216,10 @@ impl<'a> ContextTrait for Context<'a> { self.state.balance(&address).map_err(Into::into) } - fn blockhash(&mut self, number: &U256) -> H256 { - if self.space == Space::Ethereum && self.spec.cip98 { - return if U256::from(self.env().epoch_height) == number + 1 { - self.env().last_hash.clone() - } else { - H256::default() - }; - } - - // In Conflux, we only maintain the block hash of the previous block. - // For other block numbers, it always returns zero. - if U256::from(self.env().number) == number + 1 { - self.env().last_hash.clone() - } else { - H256::default() + fn blockhash(&mut self, number: &U256) -> vm::Result { + match self.blockhash_source() { + BlockHashSource::Env => Ok(self.blockhash_from_env(number)), + BlockHashSource::State => self.blockhash_from_state(number), } } @@ -458,6 +498,18 @@ impl<'a> ContextTrait for Context<'a> { fn is_static_or_reentrancy(&self) -> bool { self.static_flag || self.callstack.in_reentrancy(self.spec) } + + fn blockhash_source(&self) -> vm::BlockHashSource { + let from_state = match self.space { + Space::Native => self.env.number >= self.spec.cip133_b, + Space::Ethereum => self.env.epoch_height >= self.spec.cip133_e, + }; + if from_state { + BlockHashSource::State + } else { + BlockHashSource::Env + } + } } impl<'a> Context<'a> { @@ -632,7 +684,7 @@ mod tests { &"0000000000000000000000000000000000000000000000000000000000120000" .parse::() .unwrap(), - ); + ).unwrap(); assert_eq!(hash, H256::zero()); } diff --git a/crates/cfxcore/executor/src/internal_contract/contracts/context.rs b/crates/cfxcore/executor/src/internal_contract/contracts/context.rs index e4a6fe0c8e..3d985962de 100644 --- a/crates/cfxcore/executor/src/internal_contract/contracts/context.rs +++ b/crates/cfxcore/executor/src/internal_contract/contracts/context.rs @@ -3,9 +3,11 @@ // See http://www.gnu.org/licenses/ use cfx_parameters::internal_contract_addresses::CONTEXT_CONTRACT_ADDRESS; -use cfx_types::{Address, U256}; +use cfx_types::{Address, BigEndianHash, U256}; use cfx_vm_interpreter::GasPriceTier; +use crate::{internal_contract::epoch_hash_slot, return_if}; + use super::preludes::*; make_solidity_contract! { @@ -13,7 +15,7 @@ make_solidity_contract! { } fn generate_fn_table() -> SolFnTable { - make_function_table!(EpochNumber, PoSHeight, FinalizedEpoch) + make_function_table!(EpochNumber, PoSHeight, FinalizedEpoch, EpochHash) } group_impl_is_active!( @@ -23,6 +25,8 @@ group_impl_is_active!( FinalizedEpoch ); +group_impl_is_active!(|spec: &Spec| spec.cip133_core, EpochHash); + make_solidity_function! { struct EpochNumber((), "epochNumber()", U256); } @@ -71,6 +75,31 @@ impl SimpleExecutionTrait for FinalizedEpoch { } } +make_solidity_function! { + struct EpochHash(U256, "epochHash(uint256)", H256); +} + +impl_function_type!(EpochHash, "query", gas: |spec: &Spec| spec.sload_gas); + +impl SimpleExecutionTrait for EpochHash { + fn execute_inner( + &self, number: U256, _params: &ActionParams, + context: &mut InternalRefContext, + ) -> vm::Result { + return_if!(number > U256::from(u64::MAX)); + + let number = number.as_u64(); + + return_if!(number < context.spec.cip133_e); + return_if!(number > context.env.epoch_height); + return_if!(number + .checked_add(65536) + .map_or(false, |n| n <= context.env.epoch_height)); + let res = context.state.get_system_storage(&epoch_hash_slot(number))?; + Ok(BigEndianHash::from_uint(&res)) + } +} + #[test] fn test_context_contract_sig() { check_func_signature!(EpochNumber, "f4145a83"); diff --git a/crates/cfxcore/executor/src/internal_contract/impls/context.rs b/crates/cfxcore/executor/src/internal_contract/impls/context.rs new file mode 100644 index 0000000000..a0eba69b28 --- /dev/null +++ b/crates/cfxcore/executor/src/internal_contract/impls/context.rs @@ -0,0 +1,32 @@ +use primitives::{block::BlockHeight, BlockNumber}; +use sha3_macro::keccak; + +const BLOCK_HASHES_START_SLOT: [u8; 32] = { + let mut hash = keccak!("CIP_133_BLOCK_HASHES_START_SLOT"); + hash[30] = 0; + hash[31] = 0; + hash +}; + +const EPOCH_HASHES_START_SLOT: [u8; 32] = { + let mut hash = keccak!("CIP_133_EPOCH_HASHES_START_SLOT"); + hash[30] = 0; + hash[31] = 0; + hash +}; + +pub const fn block_hash_slot(number: BlockNumber) -> [u8; 32] { + let mut answer = BLOCK_HASHES_START_SLOT; + let r = ((number & 0xffff) as u16).to_be_bytes(); + answer[30] = r[0]; + answer[31] = r[1]; + answer +} + +pub const fn epoch_hash_slot(height: BlockHeight) -> [u8; 32] { + let mut answer = EPOCH_HASHES_START_SLOT; + let r = ((height & 0xffff) as u16).to_be_bytes(); + answer[30] = r[0]; + answer[31] = r[1]; + answer +} diff --git a/crates/cfxcore/executor/src/internal_contract/impls/mod.rs b/crates/cfxcore/executor/src/internal_contract/impls/mod.rs index 32817f9d2d..ed7b85acb3 100644 --- a/crates/cfxcore/executor/src/internal_contract/impls/mod.rs +++ b/crates/cfxcore/executor/src/internal_contract/impls/mod.rs @@ -3,6 +3,7 @@ // See http://www.gnu.org/licenses/ pub(super) mod admin; +pub(super) mod context; pub(super) mod cross_space; pub(super) mod params_control; pub(super) mod pos; diff --git a/crates/cfxcore/executor/src/internal_contract/mod.rs b/crates/cfxcore/executor/src/internal_contract/mod.rs index 850d78987a..6e979b19a5 100644 --- a/crates/cfxcore/executor/src/internal_contract/mod.rs +++ b/crates/cfxcore/executor/src/internal_contract/mod.rs @@ -20,6 +20,7 @@ pub use self::{ }, impls::{ admin::suicide, + context::{block_hash_slot, epoch_hash_slot}, cross_space::{evm_map, Resume}, params_control::{ get_settled_param_vote_count, get_settled_pos_staking_for_votes, diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 81532fa796..e5056af799 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -102,6 +102,8 @@ pub struct TransitionsBlockNumber { pub cip131: BlockNumber, /// CIP-132: Fix Static Context Check for Internal Contracts pub cip132: BlockNumber, + /// CIP-133: Enhanced Block Hash Query + pub cip133_b: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -120,6 +122,8 @@ pub struct TransitionsEpochHeight { pub cip112: BlockHeight, /// CIP-130: Aligning Gas Limit with Transaction Size pub cip130: BlockHeight, + /// CIP-133: Enhanced Block Hash Query + pub cip133_e: BlockHeight, } impl Default for CommonParams { @@ -170,6 +174,9 @@ impl CommonParams { spec.cip119 = number >= self.transition_numbers.cip119; spec.cip131 = number >= self.transition_numbers.cip131; spec.cip132 = number >= self.transition_numbers.cip132; + spec.cip133_b = self.transition_numbers.cip133_b; + spec.cip133_e = self.transition_heights.cip133_e; + spec.cip133_core = number >= self.transition_numbers.cip133_b; spec } diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs b/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs index 7a3a9c2be1..d83724f78a 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs @@ -21,6 +21,7 @@ use cfx_types::{Space, U256}; use cfx_vm_types::{self as vm, Spec}; use std::cmp; +use vm::BlockHashSource; use super::{ instructions::{self, Instruction, InstructionInfo}, @@ -298,7 +299,11 @@ impl Gasometer { Request::Gas(gas) } instructions::BLOCKHASH => { - Request::Gas(Gas::from(spec.blockhash_gas)) + let gas = match context.blockhash_source() { + BlockHashSource::Env => spec.blockhash_gas, + BlockHashSource::State => spec.sload_gas, + }; + Request::Gas(Gas::from(gas)) } _ => Request::Gas(default_gas), }; diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index d31d61d028..96bc56b03f 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -1184,7 +1184,7 @@ impl Interpreter { } instructions::BLOCKHASH => { let block_number = self.stack.pop_back(); - let block_hash = context.blockhash(&block_number); + let block_hash = context.blockhash(&block_number)?; self.stack.push(block_hash.into_uint()); } instructions::COINBASE => { diff --git a/crates/cfxcore/vm-types/src/context.rs b/crates/cfxcore/vm-types/src/context.rs index 2d5aa8b6d4..c87e2a259c 100644 --- a/crates/cfxcore/vm-types/src/context.rs +++ b/crates/cfxcore/vm-types/src/context.rs @@ -79,6 +79,14 @@ pub enum CreateContractAddress { FromSenderSaltAndCodeHash(H256), } +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub enum BlockHashSource { + /// Before CIP-133, block hash is read from `Env`, same as the Ethereum + Env, + /// After CIP-133, block hash is read from `State` + State, +} + /// Calculate new contract address. pub fn contract_address( address_scheme: CreateContractAddress, _block_number: u64, @@ -169,7 +177,7 @@ pub trait Context { fn balance(&self, address: &Address) -> Result; /// Returns the hash of one of the 256 most recent complete blocks. - fn blockhash(&mut self, number: &U256) -> H256; + fn blockhash(&mut self, number: &U256) -> Result; /// Creates new contract. /// @@ -262,4 +270,6 @@ pub trait Context { /// Check if running in static context or reentrancy context fn is_static_or_reentrancy(&self) -> bool; + + fn blockhash_source(&self) -> BlockHashSource; } diff --git a/crates/cfxcore/vm-types/src/lib.rs b/crates/cfxcore/vm-types/src/lib.rs index 8a692c0973..fa7e155f1e 100644 --- a/crates/cfxcore/vm-types/src/lib.rs +++ b/crates/cfxcore/vm-types/src/lib.rs @@ -17,8 +17,8 @@ pub use self::{ action_params::{ActionParams, ActionValue, ParamsType}, call_create_type::{CallType, CreateType}, context::{ - contract_address, Context, ContractCreateResult, CreateContractAddress, - MessageCallResult, + contract_address, BlockHashSource, Context, ContractCreateResult, + CreateContractAddress, MessageCallResult, }, env::Env, error::{ diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index e479bf2c9c..7de5fb6f56 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -20,6 +20,7 @@ //! Cost spec and other parameterisations for the EVM. use cfx_types::{address_util::AddressUtil, Address}; +use primitives::{block::BlockHeight, BlockNumber}; /// Definition of the cost spec and other parameterisations for the VM. #[derive(Debug, Clone)] @@ -162,6 +163,10 @@ pub struct Spec { pub cip131: bool, /// CIP-132: Fix Static Context Check for Internal Contracts pub cip132: bool, + /// CIP-133: Enhanced Block Hash Query + pub cip133_b: BlockNumber, + pub cip133_e: BlockHeight, + pub cip133_core: bool, pub params_dao_vote_period: u64, } @@ -305,6 +310,9 @@ impl Spec { cip119: false, cip131: false, cip132: false, + cip133_b: u64::MAX, + cip133_e: u64::MAX, + cip133_core: false, } } diff --git a/crates/cfxcore/vm-types/src/tests.rs b/crates/cfxcore/vm-types/src/tests.rs index a326dfe03d..7ca83f3240 100644 --- a/crates/cfxcore/vm-types/src/tests.rs +++ b/crates/cfxcore/vm-types/src/tests.rs @@ -18,6 +18,8 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ +use crate::BlockHashSource; + use super::{ error::TrapKind, CallType, Context, ContractCreateResult, CreateContractAddress, Env, Error, GasLeft, MessageCallResult, Result, @@ -135,11 +137,12 @@ impl Context for MockContext { Ok(self.balances[address]) } - fn blockhash(&mut self, number: &U256) -> H256 { - self.blockhashes + fn blockhash(&mut self, number: &U256) -> Result { + Ok(self + .blockhashes .get(number) .unwrap_or(&H256::zero()) - .clone() + .clone()) } fn create( @@ -243,4 +246,6 @@ impl Context for MockContext { // } fn space(&self) -> Space { Space::Native } + + fn blockhash_source(&self) -> BlockHashSource { BlockHashSource::Env } } diff --git a/crates/client/src/rpc/types/eth/block.rs b/crates/client/src/rpc/types/eth/block.rs index 090865325a..acf632bf42 100644 --- a/crates/client/src/rpc/types/eth/block.rs +++ b/crates/client/src/rpc/types/eth/block.rs @@ -146,8 +146,7 @@ impl Block { pub fn from_phantom(pb: &PhantomBlock, full: bool) -> Self { let transactions = if full { BlockTransactions::Full( - pb - .transactions + pb.transactions .iter() .enumerate() .map(|(idx, t)| { @@ -155,15 +154,18 @@ impl Block { .outcome_status .in_space(Space::Ethereum); - let contract_address = match Transaction::deployed_contract_address(&**t) { - Some(a) if status == EVM_SPACE_SUCCESS => Some(a), - _ => None, - }; + let contract_address = + match Transaction::deployed_contract_address(&**t) { + Some(a) if status == EVM_SPACE_SUCCESS => { + Some(a) + } + _ => None, + }; Transaction::from_signed( &**t, ( - Some(pb.pivot_header.hash()), // block_hash + Some(pb.pivot_header.hash()), // block_hash Some(pb.pivot_header.height().into()), // block_number Some(idx.into()), // transaction_index ), diff --git a/internal_contract/contracts/ConfluxContext.sol b/internal_contract/contracts/ConfluxContext.sol index 3acd917d14..f59e2245dc 100644 --- a/internal_contract/contracts/ConfluxContext.sol +++ b/internal_contract/contracts/ConfluxContext.sol @@ -17,4 +17,6 @@ contract ConfluxContext { * @return the finalized epoch number */ function finalizedEpochNumber() public view returns (uint256) {} + + function epochHash(uint256) external view returns (bytes32) {} } diff --git a/tests/blockhash_test.py b/tests/blockhash_test.py new file mode 100644 index 0000000000..3eb9c6b2ac --- /dev/null +++ b/tests/blockhash_test.py @@ -0,0 +1,45 @@ +from web3 import Web3 +from web3.contract import ContractFunction, Contract + +from conflux.rpc import RpcClient +from conflux.utils import * +from test_framework.contracts import ConfluxTestFrameworkForContract, ZERO_ADDRESS +from test_framework.util import * +from test_framework.mininode import * + + +class BlockHashFromStateTest(ConfluxTestFrameworkForContract): + def run_test(self): + genesis_hash = self.client.block_by_epoch(0)["hash"] + for _ in range(5): + self.client.generate_block_with_parent(genesis_hash) + + self.wait_for_block(1000) + + test_contract = self.cfx_contract("BlockHash").deploy() + context_contract = self.internal_contract("ConfluxContext"); + for i in range(100, 1001, 100): + assert_equal(test_contract.functions.getBlockHash(i).cfx_call().hex(), self.client.block_by_block_number(i)["hash"][2:]) + assert_equal(context_contract.functions.epochHash(i).cfx_call().hex(), self.client.block_by_epoch(i)["hash"][2:]) + + self.log.info("Generate 65536+ blocks") + for i in range(5000, 66000, 5000): + self.wait_for_block(i) + self.wait_for_block(66000) + + assert_equal(test_contract.functions.getBlockHash(100).cfx_call().hex(), "0" * 64) + assert_equal(context_contract.functions.epochHash(100).cfx_call().hex(), "0" * 64) + + + def wait_for_block(self, block_number, have_not_reach=False): + if have_not_reach: + assert_greater_than_or_equal( + block_number, self.client.epoch_number()) + while self.client.epoch_number() < block_number: + self.client.generate_blocks( + block_number - self.client.epoch_number()) + time.sleep(0.1) + self.log.info(f"block_number: {self.client.epoch_number()}") + +if __name__ == "__main__": + BlockHashFromStateTest().main() diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 84e135d4f0..66efb76676 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -334,12 +334,18 @@ def block_by_hash_with_pivot_assumption(self, block_hash: str, pivot_hash: str, convert_b32_address_field_to_hex(block, "miner") return block - def block_by_epoch(self, epoch: str, include_txs: bool = False) -> dict: + def block_by_epoch(self, epoch: Union[str, int], include_txs: bool = False) -> dict: + if type(epoch) is int: + epoch = hex(epoch) + block = self.node.cfx_getBlockByEpochNumber(epoch, include_txs) convert_b32_address_field_to_hex(block, "miner") return block - def block_by_block_number(self, block_number: str, include_txs: bool = False) -> dict: + def block_by_block_number(self, block_number: Union[str, int], include_txs: bool = False) -> dict: + if type(block_number) is int: + block_number = hex(block_number) + block = self.node.cfx_getBlockByBlockNumber(block_number, include_txs) convert_b32_address_field_to_hex(block, "miner") return block diff --git a/tests/test_contracts b/tests/test_contracts index 8003472fd9..ca89c563bc 160000 --- a/tests/test_contracts +++ b/tests/test_contracts @@ -1 +1 @@ -Subproject commit 8003472fd91db3fcf4469ba5f767c17eaa07dd74 +Subproject commit ca89c563bcbd86289b87a48970bb4b8b09bebd11 From 3dc04db03baf3ae68aaf5452ece0b480c196bb60 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 6 May 2024 21:33:17 +0800 Subject: [PATCH 023/137] Use the same encoding for legacy transactions. --- .../sync/message/get_block_txn_response.rs | 32 +------------- .../cfxcore/core/src/transaction_pool/mod.rs | 1 - crates/client/src/rpc/impls/cfx.rs | 6 ++- crates/client/src/rpc/impls/eth.rs | 8 ++-- crates/client/src/rpc/impls/light.rs | 7 +-- crates/primitives/src/block.rs | 9 +--- crates/primitives/src/transaction/mod.rs | 43 +++++++++++++++---- 7 files changed, 51 insertions(+), 55 deletions(-) diff --git a/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs b/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs index 82134a702c..64f32d2ee2 100644 --- a/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs +++ b/crates/cfxcore/core/src/sync/message/get_block_txn_response.rs @@ -14,10 +14,10 @@ use crate::{ use cfx_types::H256; use metrics::MeterTimer; use primitives::{Block, TransactionWithSignature}; -use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use rlp_derive::{RlpDecodable, RlpEncodable}; use std::collections::HashSet; -#[derive(Debug, PartialEq, Default)] +#[derive(Debug, PartialEq, Default, RlpEncodable, RlpDecodable)] pub struct GetBlockTxnResponse { pub request_id: RequestId, pub block_hash: H256, @@ -145,31 +145,3 @@ impl Handleable for GetBlockTxnResponse { Ok(()) } } - -impl Encodable for GetBlockTxnResponse { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(3); - s.append(&self.request_id); - s.append(&self.block_hash); - s.begin_list(self.block_txn.len()); - for tx in &self.block_txn { - s.append(&rlp::encode(tx)); - } - } -} - -impl Decodable for GetBlockTxnResponse { - fn decode(rlp: &Rlp) -> Result { - let request_id = rlp.val_at(0)?; - let block_hash = rlp.val_at(1)?; - let mut block_txn = Vec::new(); - for b in rlp.list_at::>(2)? { - block_txn.push(rlp::decode(&b)?); - } - Ok(Self { - request_id, - block_hash, - block_txn, - }) - } -} diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 7b5799eabd..d609ef3d82 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -420,7 +420,6 @@ impl TransactionPool { // key after basic verification. match self.data_man.recover_unsigned_tx(&transactions) { Ok(signed_trans) => { - info!("tx: {:?}", signed_trans); let account_cache = self.get_best_state_account_cache(); let mut inner = self.inner.write_with_metric(&INSERT_TXS_ENQUEUE_LOCK); diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index cb39e0fc46..671a2b4435 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -465,8 +465,10 @@ impl RpcImpl { info!("RPC Request: cfx_sendRawTransaction len={:?}", raw.0.len()); debug!("RawTransaction bytes={:?}", raw); - let tx: TransactionWithSignature = - invalid_params_check("raw", Rlp::new(&raw.into_vec()).as_val())?; + let tx: TransactionWithSignature = invalid_params_check( + "raw", + TransactionWithSignature::from_raw(&raw.into_vec()), + )?; if tx.recover_public().is_err() { bail!(invalid_params( diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index e17f370bc2..49a4fd8a72 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -46,7 +46,6 @@ use primitives::{ BlockHashOrEpochNumber, EpochNumber, SignedTransaction, StorageKey, StorageValue, TransactionStatus, TransactionWithSignature, }; -use rlp::Rlp; use rustc_hex::ToHex; use std::{cmp::min, convert::TryInto}; @@ -663,8 +662,10 @@ impl Eth for EthHandler { "RPC Request: eth_sendRawTransaction / eth_submitTransaction raw={:?}", raw, ); - let tx: TransactionWithSignature = - invalid_params_check("raw", Rlp::new(&raw.into_vec()).as_val())?; + let tx: TransactionWithSignature = invalid_params_check( + "raw", + TransactionWithSignature::from_raw(&raw.into_vec()), + )?; if tx.space() != Space::Ethereum { bail!(invalid_params("tx", "Incorrect transaction space")); @@ -676,7 +677,6 @@ impl Eth for EthHandler { "Can not recover pubkey for Ethereum like tx. Conflux eSpace only supports EIP-155 rather than EIP-1559 or other format transactions." )); } - info!("tx: {:?}", tx); let r = self.send_transaction_with_signature(tx)?; Ok(r) diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index be7ab4e50f..1442931165 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -501,9 +501,10 @@ impl RpcImpl { // decode tx so that we have its hash // this way we also avoid spamming peers with invalid txs - let tx: TransactionWithSignature = rlp::decode(&raw.clone()) - .map_err(|e| format!("Failed to decode tx: {:?}", e)) - .map_err(RpcError::invalid_params)?; + let tx: TransactionWithSignature = + TransactionWithSignature::from_raw(&raw.clone()) + .map_err(|e| format!("Failed to decode tx: {:?}", e)) + .map_err(RpcError::invalid_params)?; debug!("Deserialized tx: {:?}", tx); diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index b736f441d7..cf3f15f5a4 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -150,7 +150,6 @@ impl Block { if rlp.as_raw().len() != rlp.payload_info()?.total() { return Err(DecoderError::RlpIsTooBig); } - info!("1 {:?}", rlp.as_raw()); let signed_transactions = rlp.as_list()?; let mut transactions = Vec::with_capacity(signed_transactions.len()); @@ -194,7 +193,7 @@ impl Encodable for Block { stream.begin_list(2).append(&self.block_header); stream.begin_list(self.transactions.len()); for tx in &self.transactions { - stream.append(&rlp::encode(&tx.transaction)); + stream.append(&tx.transaction); } } } @@ -210,11 +209,7 @@ impl Decodable for Block { return Err(DecoderError::RlpIncorrectListLen); } - let transactions = rlp - .list_at::>(1)? - .into_iter() - .map(|b| rlp::decode(&b)) - .collect::, DecoderError>>()?; + let transactions = rlp.list_at::(1)?; let mut signed_transactions = Vec::with_capacity(transactions.len()); for tx in transactions { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 25e92913c3..7c847a44e1 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -700,12 +700,22 @@ impl DerefMut for TransactionWithSignature { } impl Decodable for TransactionWithSignature { - fn decode(d: &Rlp) -> Result { - let hash = keccak(d.as_raw()); - let rlp_size = Some(d.as_raw().len()); + fn decode(tx_rlp: &Rlp) -> Result { + let rlp_size = Some(tx_rlp.as_raw().len()); // The item count of TransactionWithSignatureSerializePart is checked in // its decoding. - let transaction = d.as_val()?; + let hash; + let transaction; + if tx_rlp.is_list() { + hash = keccak(tx_rlp.as_raw()); + // Vanilla tx encoding. + transaction = tx_rlp.as_val()?; + } else { + // Typed tx encoding is wrapped as an RLP string. + let b: Vec = tx_rlp.as_val()?; + hash = keccak(&b); + transaction = rlp::decode(&b)?; + }; Ok(TransactionWithSignature { transaction, hash, @@ -716,7 +726,16 @@ impl Decodable for TransactionWithSignature { impl Encodable for TransactionWithSignature { fn rlp_append(&self, s: &mut RlpStream) { - s.append_internal(&self.transaction); + match &self.transaction.unsigned { + Transaction::Native(TypedNativeTransaction::Cip155(_)) + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => { + s.append_internal(&self.transaction); + } + _ => { + // Typed tx encoding is wrapped as an RLP string. + s.append_internal(&rlp::encode(&self.transaction)); + } + } } } @@ -770,6 +789,14 @@ impl TransactionWithSignature { pub fn rlp_size(&self) -> usize { self.rlp_size.unwrap_or_else(|| self.rlp_bytes().len()) } + + pub fn from_raw(raw: &[u8]) -> Result { + Ok(TransactionWithSignature { + transaction: Rlp::new(raw).as_val()?, + hash: keccak(raw), + rlp_size: Some(raw.len()), + }) + } } impl MallocSizeOf for TransactionWithSignature { @@ -786,10 +813,11 @@ pub struct SignedTransaction { pub public: Option, } +// The default encoder for local storage. impl Encodable for SignedTransaction { fn rlp_append(&self, s: &mut RlpStream) { s.begin_list(3); - s.append(&rlp::encode(&self.transaction)); + s.append(&self.transaction); s.append(&self.sender); s.append(&self.public); } @@ -797,9 +825,8 @@ impl Encodable for SignedTransaction { impl Decodable for SignedTransaction { fn decode(rlp: &Rlp) -> Result { - let b: Vec = rlp.val_at(0)?; Ok(SignedTransaction { - transaction: rlp::decode(&b)?, + transaction: rlp.val_at(0)?, sender: rlp.val_at(1)?, public: rlp.val_at(2)?, }) From 2f5c45e512962dd12d1aa35eb6fbdc6c0b478a7b Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 7 May 2024 11:36:31 +0800 Subject: [PATCH 024/137] Fix test --- crates/client/src/configuration.rs | 8 ++++++++ tests/cip98_test.py | 3 +++ 2 files changed, 11 insertions(+) diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 7b281b6309..f65f6f6ce7 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1288,6 +1288,10 @@ impl Configuration { .raw_conf .next_hardfork_transition_number .unwrap_or(default_transition_time); + params.transition_numbers.cip133_b = self + .raw_conf + .next_hardfork_transition_number + .unwrap_or(default_transition_time); if self.is_test_or_dev_mode() { params.transition_numbers.cip43b = self.raw_conf.cip43_init_end_number.unwrap_or(u64::MAX); @@ -1351,6 +1355,10 @@ impl Configuration { .raw_conf .next_hardfork_transition_height .unwrap_or(default_transition_time); + params.transition_heights.cip133_e = self + .raw_conf + .next_hardfork_transition_height + .unwrap_or(default_transition_time); params.params_dao_vote_period = self.raw_conf.params_dao_vote_period; let mut base_block_rewards = BTreeMap::new(); diff --git a/tests/cip98_test.py b/tests/cip98_test.py index 22bd2d4d68..80c1137de6 100644 --- a/tests/cip98_test.py +++ b/tests/cip98_test.py @@ -18,6 +18,9 @@ def set_test_params(self): self.conf_parameters["evm_chain_id"] = str(EVM_CHAIN_ID) self.conf_parameters["evm_transaction_block_ratio"] = str(1) self.conf_parameters["dao_vote_transition_number"] = "100" + # Disable CIP-133 on test + self.conf_parameters["next_hardfork_transition_number"] = str(9999999) + self.conf_parameters["next_hardfork_transition_height"] = str(9999999) def run_test(self): rpc = self.nodes[0].rpc From fb7827dea7093168ee34e69bc6e56981727408f4 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 7 May 2024 13:38:03 +0800 Subject: [PATCH 025/137] Implement EIP-1559 inside EVM execution and reward distribution. --- .../consensus_inner/consensus_executor.rs | 50 ++++++++++++++++--- crates/cfxcore/core/src/genesis_block.rs | 2 +- .../cfxcore/core/src/transaction_pool/mod.rs | 6 +-- .../transaction_pool_inner.rs | 2 +- crates/cfxcore/core/src/verification.rs | 1 + crates/cfxcore/executor/src/context.rs | 3 +- .../executor/src/executive/executed.rs | 6 +-- .../src/executive/execution_outcome.rs | 12 ++++- .../executor/src/executive/fresh_executive.rs | 13 ++++- crates/cfxcore/executor/src/executive/mod.rs | 48 ++++++++++++------ .../src/executive/pre_checked_executive.rs | 37 +++++++------- .../cfxcore/executor/src/executive/tests.rs | 30 +++++------ .../internal_contract/impls/cross_space.rs | 4 +- crates/cfxcore/executor/src/machine/mod.rs | 11 ++-- crates/cfxcore/executor/src/spec.rs | 11 +++- .../state/state_object/global_statistics.rs | 5 ++ .../vm-interpreter/src/instructions.rs | 3 ++ .../vm-interpreter/src/interpreter/mod.rs | 3 ++ crates/cfxcore/vm-types/src/env.rs | 3 ++ crates/cfxcore/vm-types/src/spec.rs | 6 +++ crates/client/benches/benchmark.rs | 3 +- crates/client/src/configuration.rs | 8 +++ crates/dbs/statedb/src/global_params.rs | 8 ++- crates/primitives/src/block.rs | 1 - crates/primitives/src/lib.rs | 5 +- .../src/transaction/eth_transaction.rs | 37 +++++++++++--- crates/primitives/src/transaction/mod.rs | 44 ++++++++-------- .../src/transaction/native_transaction.rs | 34 +++++++++++-- 28 files changed, 290 insertions(+), 106 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index d913aff919..ee7a695833 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -1088,7 +1088,8 @@ impl ConsensusExecutionHandler { epoch_hash, on_local_pivot, debug_record.as_deref_mut(), - self.machine.spec(current_block_number), + self.machine + .spec(current_block_number, pivot_block_header.height()), ); } @@ -1292,8 +1293,10 @@ impl ConsensusExecutionHandler { transaction_epoch_bound: self .verification_config .transaction_epoch_bound, + // TODO: get the actual base price + base_gas_price: todo_base_price(), }; - let spec = self.machine.spec(env.number); + let spec = self.machine.spec(env.number, env.epoch_height); if !spec.cip43_contract { state.bump_block_number_accumulate_interest(); } @@ -1324,6 +1327,12 @@ impl ConsensusExecutionHandler { ExecutiveContext::new(state, &env, machine, &spec) .transact(transaction, options)?; execution_outcome.log(transaction, &block.hash()); + execution_outcome.burn_by_cip1559( + state, + &transaction, + &todo_base_price(), + ); + let r = make_process_tx_outcome( execution_outcome, &mut env.accumulated_gas_used, @@ -1620,8 +1629,15 @@ impl ConsensusExecutionHandler { debug_assert!( block_receipts.receipts.len() == block.transactions.len() ); - for (idx, tx) in block.transactions.iter().enumerate() { - let fee = block_receipts.receipts[idx].gas_fee; + // TODO: fill base_price. + let base_price = todo_base_price(); + for (tx, receipt) in block + .transactions + .iter() + .zip(block_receipts.receipts.iter()) + { + let fee = receipt.gas_fee; + let info = tx_fee .entry(tx.hash()) .or_insert(TxExecutionInfo(fee, BTreeSet::default())); @@ -1635,7 +1651,22 @@ impl ConsensusExecutionHandler { info.1.insert(block_hash); } if !fee.is_zero() && info.0.is_zero() { - info.0 = fee; + // TODO: a temp implmentation, the 1559 related infomation + // should be put in the `BlockReceipts`. + + // When `base_price = 0`, `effective_gas_price = gas_price` + let effective_gas_price = + tx.effective_gas_price(&base_price); + // When gas_fee is non-zero, gas_price must be non-zero + let gas_charged = if effective_gas_price.is_zero() { + U256::zero() + } else { + fee / effective_gas_price + }; + let miner_fee = + tx.priority_gas_price(&base_price) * gas_charged; + + info.0 = miner_fee; } } } @@ -1834,7 +1865,7 @@ impl ConsensusExecutionHandler { Some(v) => v.start_block_number + epoch_size as u64, None => bail!("cannot obtain the execution context. Database is potentially corrupted!"), }; - let spec = self.machine.spec(start_block_number); + let spec = self.machine.spec(start_block_number, block_height); let transitions = &self.machine.params().transition_heights; invalid_params_check( @@ -1902,8 +1933,9 @@ impl ConsensusExecutionHandler { transaction_epoch_bound: self .verification_config .transaction_epoch_bound, + base_gas_price: U256::zero(), }; - let spec = self.machine.spec(env.number); + let spec = self.machine.spec(env.number, env.epoch_height); let mut ex = EstimationContext::new( &mut state, &env, @@ -1945,3 +1977,7 @@ impl ConsensusExecutionHandler { pub struct ConsensusExecutionConfiguration { pub executive_trace: bool, } + +fn todo_base_price() -> U256 { + U256::zero() +} \ No newline at end of file diff --git a/crates/cfxcore/core/src/genesis_block.rs b/crates/cfxcore/core/src/genesis_block.rs index 4e8ec27031..e5d7c479bd 100644 --- a/crates/cfxcore/core/src/genesis_block.rs +++ b/crates/cfxcore/core/src/genesis_block.rs @@ -478,7 +478,7 @@ fn execute_genesis_transaction( state, &env, machine.as_ref(), - &machine.spec(env.number), + &machine.spec(env.number, env.epoch_height), ) .transact(transaction, options) .unwrap() diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index d609ef3d82..93f6944b20 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -387,7 +387,7 @@ impl TransactionPool { // FIXME: Needs further discussion here, some transactions may be valid // and invalid back and forth does this matters? But for the epoch // height check, it may also become valid and invalid back and forth. - let vm_spec = self.machine.spec(best_block_number); + let vm_spec = self.machine.spec(best_block_number, best_height); let transitions = &self.machine.params().transition_heights; while let Some(tx) = transactions.get(index) { @@ -498,7 +498,7 @@ impl TransactionPool { }; // FIXME: Needs further discussion here, some transactions may be valid // and invalid back and forth does this matters? - let vm_spec = self.machine.spec(best_block_number); + let vm_spec = self.machine.spec(best_block_number, best_height); let transitions = &self.machine.params().transition_heights; while let Some(tx) = signed_transactions.get(index) { @@ -821,7 +821,7 @@ impl TransactionPool { }; // FIXME: Needs further discussion here, some transactions may be valid // and invalid back and forth, does this matters? - let vm_spec = self.machine.spec(best_block_number); + let vm_spec = self.machine.spec(best_block_number, best_height); let transitions = &self.machine.params().transition_heights; while let Some(tx) = recycle_tx_buffer.pop() { diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 4f273795bd..a83a6209ec 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -1066,7 +1066,7 @@ impl TransactionPoolInner { return packed_transactions; } - let spec = machine.spec(best_block_number); + let spec = machine.spec(best_block_number, best_epoch_height); let transitions = &machine.params().transition_heights; let validity = |tx: &SignedTransaction| { diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index cc9a312aee..1d9bdee99b 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -702,6 +702,7 @@ impl VerificationConfig { let tx_intrinsic_gas = gas_required_for( *tx.action() == Action::Create, &tx.data(), + tx.access_list(), &spec, ); if *tx.gas() < (tx_intrinsic_gas as usize).into() { diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index 425e8d6c92..9b39586cdb 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -533,6 +533,7 @@ mod tests { pos_view: None, finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, + base_gas_price: U256::zero(), } } @@ -558,7 +559,7 @@ mod tests { Default::default(), ); let env = get_test_env(); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let callstack = CallStackInfo::new(); let mut setup = Self { diff --git a/crates/cfxcore/executor/src/executive/executed.rs b/crates/cfxcore/executor/src/executive/executed.rs index d627e35550..53951e93ac 100644 --- a/crates/cfxcore/executor/src/executive/executed.rs +++ b/crates/cfxcore/executor/src/executive/executed.rs @@ -74,10 +74,10 @@ impl Executed { tx: &TransactionWithSignature, fee: &U256, cost: CostInfo, ext_result: ExecutedExt, spec: &Spec, ) -> Self { - let gas_charged = if *tx.gas_price() == U256::zero() { + let gas_charged = if cost.gas_price == U256::zero() { U256::zero() } else { - fee / tx.gas_price() + fee / cost.gas_price }; let mut gas_sponsor_paid = cost.gas_sponsored; let mut storage_sponsor_paid = cost.storage_sponsored; @@ -115,7 +115,7 @@ impl Executed { Self { gas_used: *tx.gas(), gas_charged: *tx.gas(), - fee: tx.gas().saturating_mul(*tx.gas_price()), + fee: tx.gas().saturating_mul(cost.gas_price), gas_sponsor_paid, logs: vec![], contracts_created: vec![], diff --git a/crates/cfxcore/executor/src/executive/execution_outcome.rs b/crates/cfxcore/executor/src/executive/execution_outcome.rs index 70ab2469d7..51e6bd3f92 100644 --- a/crates/cfxcore/executor/src/executive/execution_outcome.rs +++ b/crates/cfxcore/executor/src/executive/execution_outcome.rs @@ -1,5 +1,5 @@ use super::executed::{revert_reason_decode, Executed}; -use crate::unwrap_or_return; +use crate::{state::State, unwrap_or_return}; use cfx_types::{Address, H256, U256, U512}; use cfx_vm_types as vm; use primitives::{ @@ -241,4 +241,14 @@ impl ExecutionOutcome { } } } + + pub fn burn_by_cip1559( + &self, state: &mut State, tx: &SignedTransaction, base_gas_price: &U256, + ) { + let executed = unwrap_or_return!(self.try_as_executed()); + let miner_fee = + executed.gas_charged * tx.priority_gas_price(base_gas_price); + let burnt_fee = executed.fee - miner_fee; + state.burn_by_cip1559(burnt_fee); + } } diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 5978c894cf..664db7db29 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -38,6 +38,7 @@ pub(super) struct CostInfo { pub gas_cost: U512, pub storage_cost: U256, pub sender_intended_cost: U512, + pub gas_price: U256, pub gas_sponsored: bool, pub storage_sponsored: bool, @@ -53,6 +54,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let base_gas = gas_required_for( tx.action() == &Action::Create, &tx.data(), + tx.access_list(), context.spec, ); FreshExecutive { @@ -164,11 +166,18 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let settings = self.settings; let sender = tx.sender(); let state = &self.context.state; + let env = self.context.env; let spec = self.context.spec; + let gas_price = if spec.cip1559 { + tx.effective_gas_price(&env.base_gas_price) + } else { + *tx.gas_price() + }; + let sender_balance = U512::from(state.balance(&sender)?); let gas_cost = if settings.charge_gas { - tx.gas().full_mul(*tx.gas_price()) + tx.gas().full_mul(gas_price) } else { 0.into() }; @@ -190,6 +199,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { sender_balance, base_gas: self.base_gas, gas_cost, + gas_price, storage_cost, sender_intended_cost: sender_cost, total_cost: sender_cost, @@ -301,6 +311,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { sender_intended_cost, base_gas: self.base_gas, gas_cost, + gas_price, storage_cost, sender_balance, total_cost, diff --git a/crates/cfxcore/executor/src/executive/mod.rs b/crates/cfxcore/executor/src/executive/mod.rs index 59716c21d1..1d55385d64 100644 --- a/crates/cfxcore/executor/src/executive/mod.rs +++ b/crates/cfxcore/executor/src/executive/mod.rs @@ -15,7 +15,7 @@ use cfx_types::{ U256, }; use cfx_vm_types::{CreateContractAddress, Env, Spec}; -use primitives::SignedTransaction; +use primitives::{AccessList, SignedTransaction}; use fresh_executive::FreshExecutive; use pre_checked_executive::PreCheckedExecutive; @@ -65,20 +65,38 @@ impl<'a> ExecutiveContext<'a> { } } -pub fn gas_required_for(is_create: bool, data: &[u8], spec: &Spec) -> u64 { - data.iter().fold( - (if is_create { - spec.tx_create_gas - } else { - spec.tx_gas - }) as u64, - |g, b| { - g + (match *b { - 0 => spec.tx_data_zero_gas, - _ => spec.tx_data_non_zero_gas, - }) as u64 - }, - ) +pub fn gas_required_for( + is_create: bool, data: &[u8], access_list: Option<&AccessList>, spec: &Spec, +) -> u64 { + let init_gas = (if is_create { + spec.tx_create_gas + } else { + spec.tx_gas + }) as u64; + + let byte_gas = |b: &u8| { + (match *b { + 0 => spec.tx_data_zero_gas, + _ => spec.tx_data_non_zero_gas, + }) as u64 + }; + let data_gas: u64 = data.iter().map(byte_gas).sum(); + + let access_gas: u64 = if let Some(acc) = access_list { + let address_gas = + acc.len() as u64 * spec.access_list_address_gas as u64; + + let storage_key_num = + acc.iter().map(|e| e.storage_keys.len() as u64).sum::(); + let storage_key_gas = + storage_key_num * spec.access_list_storage_key_gas as u64; + + address_gas + storage_key_gas + } else { + 0 + }; + + init_gas + data_gas + access_gas } pub fn contract_address( diff --git a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs index 20d3a63a6a..b4677522df 100644 --- a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs +++ b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs @@ -54,7 +54,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { if self.tx.space() == Space::Native && !self.check_create_address(¶ms)? { - return self.finialize_on_conflict_address(params.address); + return self.finalize_on_conflict_address(params.address); } let result = self.exec_vm(params.clone())?; @@ -71,7 +71,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { // perform suicides self.kill_process()?; - self.finialize_on_executed(result, refund_info) + self.finalize_on_executed(result, refund_info) } } @@ -176,7 +176,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { original_sender: sender.address, storage_owner: sender.address, gas: init_gas, - gas_price: *tx.gas_price(), + gas_price: cost.gas_price, value: ActionValue::Transfer(*tx.value()), code: Some(Arc::new(tx.data().clone())), data: None, @@ -200,7 +200,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { original_sender: sender.address, storage_owner, gas: init_gas, - gas_price: *tx.gas_price(), + gas_price: cost.gas_price, value: ActionValue::Transfer(*tx.value()), code: state.code(&receipient)?, code_hash: state.code_hash(&receipient)?, @@ -465,6 +465,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { fn compute_refunded_gas(&self, result: &ExecutiveResult) -> RefundInfo { let tx = self.tx; + let cost = &self.cost; let gas_left = match result { Ok(ExecutiveReturn { gas_left, .. }) => *gas_left, _ => 0.into(), @@ -479,14 +480,14 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { let gas_charged = tx.gas() - gas_refunded; ( gas_charged, - gas_charged.saturating_mul(*tx.gas_price()), - gas_refunded.saturating_mul(*tx.gas_price()), + gas_charged.saturating_mul(cost.gas_price), + gas_refunded.saturating_mul(cost.gas_price), ) } else { ( gas_used, - gas_used.saturating_mul(*tx.gas_price()), - gas_left.saturating_mul(*tx.gas_price()), + gas_used.saturating_mul(cost.gas_price), + gas_left.saturating_mul(cost.gas_price), ) }; RefundInfo { @@ -553,7 +554,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { )); } - fn finialize_on_conflict_address( + fn finalize_on_conflict_address( self, address: Address, ) -> DbResult { return Ok(ExecutionOutcome::ExecutionErrorBumpNonce( @@ -567,7 +568,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { )); } - fn finialize_on_executed( + fn finalize_on_executed( self, result: ExecutiveResult, refund_info: RefundInfo, ) -> DbResult { let tx = self.tx; @@ -576,14 +577,14 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { let spec = self.context.spec; let tx_substate = self.substate; - match result { + let outcome = match result { Err(vm::Error::StateDbError(e)) => bail!(e.0), - Err(exception) => Ok(ExecutionOutcome::ExecutionErrorBumpNonce( + Err(exception) => ExecutionOutcome::ExecutionErrorBumpNonce( ExecutionError::VmError(exception), Executed::execution_error_fully_charged( tx, cost, ext_result, spec, ), - )), + ), Ok(r) => { let executed = Executed::from_executive_return( &r, @@ -595,16 +596,18 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { ); if r.apply_state { - Ok(ExecutionOutcome::Finished(executed)) + ExecutionOutcome::Finished(executed) } else { // Transaction reverted by vm instruction. - Ok(ExecutionOutcome::ExecutionErrorBumpNonce( + ExecutionOutcome::ExecutionErrorBumpNonce( ExecutionError::VmError(vm::Error::Reverted), executed, - )) + ) } } - } + }; + + Ok(outcome) } } diff --git a/crates/cfxcore/executor/src/executive/tests.rs b/crates/cfxcore/executor/src/executive/tests.rs index 2d1bb0c513..2e933e7f03 100644 --- a/crates/cfxcore/executor/src/executive/tests.rs +++ b/crates/cfxcore/executor/src/executive/tests.rs @@ -117,7 +117,7 @@ fn test_sender_balance() { ); let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let mut substate = Substate::new(); let FinalizationResult { gas_left, .. } = { @@ -213,7 +213,7 @@ fn test_create_contract_out_of_depth() { let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let storage_manager = new_state_manager_for_unit_test(); let mut state = get_state_for_genesis_write(&storage_manager); @@ -268,7 +268,7 @@ fn test_suicide_when_creation() { let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let storage_manager = new_state_manager_for_unit_test(); let mut state = get_state_for_genesis_write(&storage_manager); @@ -359,7 +359,7 @@ fn test_call_to_create() { let env = Env::default(); let machine = make_byzantium_machine(5); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let storage_manager = new_state_manager_for_unit_test(); let mut state = get_state_for_genesis_write(&storage_manager); @@ -426,7 +426,7 @@ fn test_revert() { let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let mut substate = Substate::new(); let storage_manager = new_state_manager_for_unit_test(); @@ -507,7 +507,7 @@ fn test_keccak() { let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let storage_manager = new_state_manager_for_unit_test(); let mut state = get_state_for_genesis_write(&storage_manager); @@ -553,7 +553,7 @@ fn test_not_enough_cash() { let mut env = Env::default(); env.gas_limit = U256::from(100_000); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let storage_manager = new_state_manager_for_unit_test(); let mut state = get_state_for_genesis_write(&storage_manager); @@ -597,7 +597,7 @@ fn test_deposit_withdraw_lock() { let mut state = get_state_for_genesis_write(&storage_manager); let env = Env::default(); let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let mut substate = Substate::new(); state .add_balance( @@ -953,7 +953,7 @@ fn test_commission_privilege_all_whitelisted_across_epochs() { let machine = make_byzantium_machine(0); let mut env = Env::default(); env.gas_limit = U256::MAX; - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let sender = Random.generate().unwrap().address(); let sender_with_space = sender.with_native_space(); @@ -1156,7 +1156,7 @@ fn test_commission_privilege() { let mut env = Env::default(); env.gas_limit = U256::MAX; let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let sender_key = Random.generate().unwrap(); let sender = sender_key.address(); @@ -1551,7 +1551,7 @@ fn test_storage_commission_privilege() { let mut env = Env::default(); env.gas_limit = U256::MAX; let machine = make_byzantium_machine(0); - let spec = machine.spec(env.number); + let spec = machine.spec_for_test(env.number); let sender = Random.generate().unwrap(); let sender_with_space = sender.address().with_native_space(); @@ -2186,7 +2186,7 @@ fn test_push0() { // Test case 1 in EIP-3855 { - let mut spec = machine.spec(env.number); + let mut spec = machine.spec_for_test(env.number); spec.cip119 = true; // code: @@ -2213,7 +2213,7 @@ fn test_push0() { // Test case 2 in EIP-3855 { - let mut spec = machine.spec(env.number); + let mut spec = machine.spec_for_test(env.number); spec.cip119 = true; // code: @@ -2240,7 +2240,7 @@ fn test_push0() { // Test case 2 in EIP-3855 { - let mut spec = machine.spec(env.number); + let mut spec = machine.spec_for_test(env.number); spec.cip119 = true; // code: @@ -2260,7 +2260,7 @@ fn test_push0() { // Before activation of EIP-3855 (CIP119) { - let mut spec = machine.spec(env.number); + let mut spec = machine.spec_for_test(env.number); spec.cip119 = false; // code: diff --git a/crates/cfxcore/executor/src/internal_contract/impls/cross_space.rs b/crates/cfxcore/executor/src/internal_contract/impls/cross_space.rs index 1348f4e74a..267dd41c84 100644 --- a/crates/cfxcore/executor/src/internal_contract/impls/cross_space.rs +++ b/crates/cfxcore/executor/src/internal_contract/impls/cross_space.rs @@ -34,7 +34,7 @@ pub fn create_gas(context: &InternalRefContext, code: &[u8]) -> DbResult { let code_length = code.len(); let transaction_gas = - gas_required_for(/* is_create */ true, code, context.spec) + gas_required_for(/* is_create */ true, code, None, context.spec) + context.spec.tx_gas as u64; let create_gas = U256::from(context.spec.create_gas); @@ -70,7 +70,7 @@ pub fn call_gas( let data_length = data.len(); let transaction_gas = - gas_required_for(/* is_create */ false, data, context.spec) + gas_required_for(/* is_create */ false, data, None, context.spec) + context.spec.tx_gas as u64; let new_account = !context diff --git a/crates/cfxcore/executor/src/machine/mod.rs b/crates/cfxcore/executor/src/machine/mod.rs index 97e596b06c..87999a99b0 100644 --- a/crates/cfxcore/executor/src/machine/mod.rs +++ b/crates/cfxcore/executor/src/machine/mod.rs @@ -15,7 +15,7 @@ use crate::{ }; use cfx_types::{Address, AddressWithSpace, Space, H256}; use cfx_vm_types::Spec; -use primitives::BlockNumber; +use primitives::{block::BlockHeight, BlockNumber}; use std::{collections::BTreeMap, sync::Arc}; pub use vm_factory::VmFactory; @@ -55,14 +55,19 @@ impl Machine { /// Get the general parameters of the chain. pub fn params(&self) -> &CommonParams { &self.params } - pub fn spec(&self, number: BlockNumber) -> Spec { - let mut spec = self.params.spec(number); + pub fn spec(&self, number: BlockNumber, height: BlockHeight) -> Spec { + let mut spec = self.params.spec(number, height); if let Some(ref rules) = self.spec_rules { (rules)(&mut spec, number) } spec } + #[cfg(test)] + pub fn spec_for_test(&self, number: u64) -> Spec { + self.spec(number, number) + } + /// Builtin-contracts for the chain.. pub fn builtins(&self) -> &BTreeMap { &*self.builtins } diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 81532fa796..9e253cff71 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -76,7 +76,6 @@ pub struct TransitionsBlockNumber { pub cip71: BlockNumber, /// CIP-78: Correct `is_sponsored` Fields in Receipt pub cip78a: BlockNumber, - /// CIP-78: Correct `is_sponsored` Fields in Receipt pub cip78b: BlockNumber, /// CIP-90: Introduce a Fully EVM-Compatible Space pub cip90b: BlockNumber, @@ -120,6 +119,7 @@ pub struct TransitionsEpochHeight { pub cip112: BlockHeight, /// CIP-130: Aligning Gas Limit with Transaction Size pub cip130: BlockHeight, + pub cip1559: BlockHeight, } impl Default for CommonParams { @@ -147,7 +147,7 @@ impl Default for CommonParams { } impl CommonParams { - pub fn spec(&self, number: BlockNumber) -> Spec { + pub fn spec(&self, number: BlockNumber, height: BlockHeight) -> Spec { let mut spec = Spec::genesis_spec(); spec.cip43_contract = number >= self.transition_numbers.cip43a; spec.cip43_init = number >= self.transition_numbers.cip43a @@ -170,9 +170,16 @@ impl CommonParams { spec.cip119 = number >= self.transition_numbers.cip119; spec.cip131 = number >= self.transition_numbers.cip131; spec.cip132 = number >= self.transition_numbers.cip132; + + spec.cip1559 = height >= self.transition_heights.cip1559; spec } + #[cfg(test)] + pub fn spec_for_test(&self, number: u64) -> Spec { + self.spec(number, number) + } + /// Return the base reward for a block. /// `past_block_count` may be used for reward decay again in the future. pub fn base_reward_in_ucfx( diff --git a/crates/cfxcore/executor/src/state/state_object/global_statistics.rs b/crates/cfxcore/executor/src/state/state_object/global_statistics.rs index 3805b05c0e..89955624cf 100644 --- a/crates/cfxcore/executor/src/state/state_object/global_statistics.rs +++ b/crates/cfxcore/executor/src/state/state_object/global_statistics.rs @@ -36,6 +36,11 @@ impl State { base_reward } + pub fn burn_by_cip1559(&mut self, by: U256) { + *self.global_stat.val::() += by; + self.sub_total_issued(by); + } + pub fn total_issued_tokens(&self) -> U256 { self.global_stat.get::() } diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index 2fbeac1a7a..a67a647936 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -159,6 +159,8 @@ enum_with_from_u8! { CHAINID = 0x46, #[doc = "get balance of own account"] SELFBALANCE = 0x47, + #[doc = "base fee for EIP-1559 (EIP-3198)"] + BASEFEE = 0x48, #[doc = "remove item from stack"] POP = 0x50, @@ -532,6 +534,7 @@ lazy_static! { arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base)); arr[CHAINID as usize] = Some(InstructionInfo::new("CHAINID", 0, 1, GasPriceTier::Base)); arr[SELFBALANCE as usize] = Some(InstructionInfo::new("SELFBALANCE", 0, 1, GasPriceTier::Low)); + arr[BASEFEE as usize] = Some(InstructionInfo::new("BASEFEE", 0, 1, GasPriceTier::VeryLow)); arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base)); arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow)); arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow)); diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index d31d61d028..799832d67c 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -1182,6 +1182,9 @@ impl Interpreter { instructions::GASPRICE => { self.stack.push(self.params.gas_price.clone()); } + instructions::BASEFEE => { + self.stack.push(context.env().base_gas_price); + } instructions::BLOCKHASH => { let block_number = self.stack.pop_back(); let block_hash = context.blockhash(&block_number); diff --git a/crates/cfxcore/vm-types/src/env.rs b/crates/cfxcore/vm-types/src/env.rs index ab8ec6f4ca..a703059156 100644 --- a/crates/cfxcore/vm-types/src/env.rs +++ b/crates/cfxcore/vm-types/src/env.rs @@ -58,6 +58,9 @@ pub struct Env { /// The transaction_epoch_bound used to verify if a transaction has /// expired. pub transaction_epoch_bound: u64, + /// Base gas price in CIP-1559, equals to 0 if CIP-1559 has not been + /// activated + pub base_gas_price: U256, } #[cfg(test)] diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index e479bf2c9c..6b100bf3cb 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -100,6 +100,8 @@ pub struct Spec { pub retire_gas: usize, /// Price for deploying Eip-1820 contract. pub eip1820_gas: usize, + pub access_list_storage_key_gas: usize, + pub access_list_address_gas: usize, /// Amount of additional gas to pay when SUICIDE credits a non-existant /// account pub suicide_to_new_account_cost: usize, @@ -162,6 +164,7 @@ pub struct Spec { pub cip131: bool, /// CIP-132: Fix Static Context Check for Internal Contracts pub cip132: bool, + pub cip1559: bool, pub params_dao_vote_period: u64, } @@ -276,6 +279,8 @@ impl Spec { suicide_gas: 5000, retire_gas: 5_000_000, eip1820_gas: 1_500_000, + access_list_storage_key_gas: 1900, + access_list_address_gas: 2400, suicide_to_new_account_cost: 25000, sub_gas_cap_divisor: Some(64), no_empty: true, @@ -305,6 +310,7 @@ impl Spec { cip119: false, cip131: false, cip132: false, + cip1559: false, } } diff --git a/crates/client/benches/benchmark.rs b/crates/client/benches/benchmark.rs index c546fed0ba..9a81a10f65 100644 --- a/crates/client/benches/benchmark.rs +++ b/crates/client/benches/benchmark.rs @@ -62,6 +62,7 @@ fn txexe_benchmark(c: &mut Criterion) { pos_view: None, finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, + base_gas_price: U256::zero(), }; let mut group = c.benchmark_group("Execute 1 transaction"); group @@ -87,7 +88,7 @@ fn txexe_benchmark(c: &mut Criterion) { )) .expect("Failed to initialize state"); - let spec = machine.spec(env.number); + let spec = machine.spec(env.number, env.epoch_height); b.iter(|| { state.clear(); diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 7b281b6309..b150096af9 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1288,6 +1288,10 @@ impl Configuration { .raw_conf .next_hardfork_transition_number .unwrap_or(default_transition_time); + params.transition_numbers.cip132 = self + .raw_conf + .next_hardfork_transition_number + .unwrap_or(default_transition_time); if self.is_test_or_dev_mode() { params.transition_numbers.cip43b = self.raw_conf.cip43_init_end_number.unwrap_or(u64::MAX); @@ -1351,6 +1355,10 @@ impl Configuration { .raw_conf .next_hardfork_transition_height .unwrap_or(default_transition_time); + params.transition_heights.cip1559 = self + .raw_conf + .next_hardfork_transition_height + .unwrap_or(default_transition_time); params.params_dao_vote_period = self.raw_conf.params_dao_vote_period; let mut base_block_rewards = BTreeMap::new(); diff --git a/crates/dbs/statedb/src/global_params.rs b/crates/dbs/statedb/src/global_params.rs index 7dcd9031ee..30493fceae 100644 --- a/crates/dbs/statedb/src/global_params.rs +++ b/crates/dbs/statedb/src/global_params.rs @@ -139,4 +139,10 @@ impl GlobalParamKey for PowBaseReward { const KEY: &'static [u8] = b"pow_base_reward"; } -pub const TOTAL_GLOBAL_PARAMS: usize = PowBaseReward::ID + 1; +pub struct TotalBurnt1559; +impl GlobalParamKey for TotalBurnt1559 { + const ID: usize = PowBaseReward::ID + 1; + const KEY: &'static [u8] = b"total_burnt_tokens_by_cip1559"; +} + +pub const TOTAL_GLOBAL_PARAMS: usize = TotalBurnt1559::ID + 1; diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index cf3f15f5a4..dce0957ff8 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -6,7 +6,6 @@ use crate::{BlockHeader, SignedTransaction, TransactionWithSignature}; use byteorder::{ByteOrder, LittleEndian}; use cfx_types::{Space, H256, U256}; use keccak_hash::keccak; -use log::info; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use rand::Rng; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index eeb7c88fe8..edc326da56 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -50,8 +50,9 @@ pub use crate::{ }, storage_key::*, transaction::{ - Action, SignedTransaction, Transaction, TransactionWithSignature, - TransactionWithSignatureSerializePart, TxPropagateId, + AccessList, Action, SignedTransaction, Transaction, + TransactionWithSignature, TransactionWithSignatureSerializePart, + TxPropagateId, }, transaction_index::TransactionIndex, zero::Zero, diff --git a/crates/primitives/src/transaction/eth_transaction.rs b/crates/primitives/src/transaction/eth_transaction.rs index 85a64e856c..b6e079e7e4 100644 --- a/crates/primitives/src/transaction/eth_transaction.rs +++ b/crates/primitives/src/transaction/eth_transaction.rs @@ -167,6 +167,7 @@ pub enum EthereumTransaction { Eip1559(Eip1559Transaction), Eip2930(Eip2930Transaction), } +use EthereumTransaction::*; macro_rules! eth_access_common_ref { ($field:ident, $ty:ty) => { @@ -193,17 +194,41 @@ impl EthereumTransaction { pub fn gas_price(&self) -> &U256 { match self { - EthereumTransaction::Eip155(tx) => &tx.gas_price, - EthereumTransaction::Eip1559(tx) => &tx.max_fee_per_gas, - EthereumTransaction::Eip2930(tx) => &tx.gas_price, + Eip155(tx) => &tx.gas_price, + Eip1559(tx) => &tx.max_fee_per_gas, + Eip2930(tx) => &tx.gas_price, + } + } + + pub fn max_priority_gas_price(&self) -> &U256 { + match self { + Eip155(tx) => &tx.gas_price, + Eip1559(tx) => &tx.max_priority_fee_per_gas, + Eip2930(tx) => &tx.gas_price, } } pub fn chain_id(&self) -> Option { match self { - EthereumTransaction::Eip155(tx) => tx.chain_id, - EthereumTransaction::Eip1559(tx) => Some(tx.chain_id), - EthereumTransaction::Eip2930(tx) => Some(tx.chain_id), + Eip155(tx) => tx.chain_id, + Eip1559(tx) => Some(tx.chain_id), + Eip2930(tx) => Some(tx.chain_id), + } + } + + pub fn nonce_mut(&mut self) -> &mut U256 { + match self { + Eip155(tx) => &mut tx.nonce, + Eip2930(tx) => &mut tx.nonce, + Eip1559(tx) => &mut tx.nonce, + } + } + + pub fn access_list(&self) -> Option<&AccessList> { + match self { + Eip155(_tx) => None, + Eip2930(tx) => Some(&tx.access_list), + Eip1559(tx) => Some(&tx.access_list), } } } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7c847a44e1..7b4047c364 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -287,8 +287,8 @@ macro_rules! access_common_ref { ($field:ident, $ty:ty) => { pub fn $field(&self) -> &$ty { match self { - Transaction::Native(tx) => &tx.$field(), - Transaction::Ethereum(tx) => &tx.$field(), + Transaction::Native(tx) => tx.$field(), + Transaction::Ethereum(tx) => tx.$field(), } } }; @@ -310,6 +310,8 @@ impl Transaction { access_common_ref!(gas_price, U256); + access_common_ref!(max_priority_gas_price, U256); + access_common_ref!(data, Bytes); access_common_ref!(nonce, U256); @@ -334,24 +336,8 @@ impl Transaction { pub fn nonce_mut(&mut self) -> &mut U256 { match self { - Transaction::Native(TypedNativeTransaction::Cip155(tx)) => { - &mut tx.nonce - } - Transaction::Native(TypedNativeTransaction::Cip1559(tx)) => { - &mut tx.nonce - } - Transaction::Native(TypedNativeTransaction::Cip2930(tx)) => { - &mut tx.nonce - } - Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => { - &mut tx.nonce - } - Transaction::Ethereum(EthereumTransaction::Eip1559(tx)) => { - &mut tx.nonce - } - Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => { - &mut tx.nonce - } + Transaction::Native(tx) => tx.nonce_mut(), + Transaction::Ethereum(tx) => tx.nonce_mut(), } } @@ -363,9 +349,27 @@ impl Transaction { Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => Some(2), } } + + pub fn access_list(&self) -> Option<&AccessList> { + match self { + Transaction::Native(tx) => tx.access_list(), + Transaction::Ethereum(tx) => tx.access_list(), + } + } } impl Transaction { + pub fn priority_gas_price(&self, base_price: &U256) -> U256 { + std::cmp::min( + *self.max_priority_gas_price(), + self.gas_price() - base_price, + ) + } + + pub fn effective_gas_price(&self, base_price: &U256) -> U256 { + base_price + self.priority_gas_price(base_price) + } + // This function returns the hash value used in transaction signature. It is // different from transaction hash. The transaction hash also contains // signatures. diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs index b6c358be42..b7ebb7ab81 100644 --- a/crates/primitives/src/transaction/native_transaction.rs +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -7,6 +7,8 @@ use cfx_types::{AddressWithSpace, H256, U256}; use rlp_derive::{RlpDecodable, RlpEncodable}; use serde_derive::{Deserialize, Serialize}; +use super::AccessList; + #[derive( Default, Debug, @@ -147,9 +149,33 @@ impl TypedNativeTransaction { pub fn gas_price(&self) -> &U256 { match self { - TypedNativeTransaction::Cip155(tx) => &tx.gas_price, - TypedNativeTransaction::Cip1559(tx) => &tx.max_fee_per_gas, - TypedNativeTransaction::Cip2930(tx) => &tx.gas_price, + Cip155(tx) => &tx.gas_price, + Cip1559(tx) => &tx.max_fee_per_gas, + Cip2930(tx) => &tx.gas_price, + } + } + + pub fn max_priority_gas_price(&self) -> &U256 { + match self { + Cip155(tx) => &tx.gas_price, + Cip1559(tx) => &tx.max_priority_fee_per_gas, + Cip2930(tx) => &tx.gas_price, + } + } + + pub fn nonce_mut(&mut self) -> &mut U256 { + match self { + Cip155(tx) => &mut tx.nonce, + Cip2930(tx) => &mut tx.nonce, + Cip1559(tx) => &mut tx.nonce, + } + } + + pub fn access_list(&self) -> Option<&AccessList> { + match self { + Cip155(_tx) => None, + Cip2930(tx) => Some(&tx.access_list), + Cip1559(tx) => Some(&tx.access_list), } } } @@ -160,3 +186,5 @@ pub enum TypedNativeTransaction { Cip2930(Cip2930Transaction), Cip1559(Cip1559Transaction), } + +use TypedNativeTransaction::*; From 3c9674810d6af1eed2704accb3d3e5a0f6ee0610 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 7 May 2024 18:48:40 +0800 Subject: [PATCH 026/137] first version that can return opcode log' --- .../consensus_inner/consensus_executor.rs | 33 ++++++++----------- .../src/pos/common/logger/derive/src/lib.rs | 1 + .../core/src/pos/mempool/core_mempool/mod.rs | 1 + .../src/observer/geth_tracer/mod.rs | 8 +++-- .../observer/geth_tracer/tracing_inspector.rs | 1 - .../client/src/rpc/traits/eth_space/debug.rs | 2 +- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index ceb3f18f4e..5f3870ee88 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -652,15 +652,11 @@ impl ConsensusExecutor { } pub fn collect_epoch_geth_trace( - &self, epoch_num: u64, epoch_block_hashes: Vec, - tx_hash: Option, opts: GethDebugTracingOptions, + &self, epoch_block_hashes: Vec, tx_hash: Option, + opts: GethDebugTracingOptions, ) -> RpcResult> { - self.handler.collect_epoch_geth_trace( - epoch_num, - epoch_block_hashes, - tx_hash, - opts, - ) + self.handler + .collect_epoch_geth_trace(epoch_block_hashes, tx_hash, opts) } pub fn stop(&self) { @@ -1934,14 +1930,9 @@ impl ConsensusExecutionHandler { } pub fn collect_epoch_geth_trace( - &self, epoch_num: u64, epoch_block_hashes: Vec, - tx_hash: Option, opts: GethDebugTracingOptions, + &self, epoch_block_hashes: Vec, tx_hash: Option, + opts: GethDebugTracingOptions, ) -> RpcResult> { - let epoch_id = epoch_block_hashes.last().expect("Not empty"); - let _ = epoch_num; - // let epoch_size = epoch_block_hashes.len(); - // let _ = epoch_size; - // Get blocks in this epoch after skip checking let epoch_blocks = self .data_man @@ -1950,19 +1941,23 @@ impl ConsensusExecutionHandler { true, /* update_cache */ ) .expect("blocks exist"); + let pivot_block = epoch_blocks.last().expect("Not empty"); + let last_pivot_block_hash = pivot_block.block_header.parent_hash(); let state_availability_boundary = self.data_man.state_availability_boundary.read(); let state_space = None; if !state_availability_boundary.check_read_availability( - pivot_block.block_header.height(), - epoch_id, + pivot_block.block_header.height() - 1, + last_pivot_block_hash, state_space, ) { bail!("state is not ready"); } - let state_index = self.data_man.get_state_readonly_index(epoch_id); + let state_index = self + .data_man + .get_state_readonly_index(last_pivot_block_hash); let mut state = State::new(StateDb::new( self.data_man .storage_manager @@ -1977,7 +1972,7 @@ impl ConsensusExecutionHandler { let start_block_number = self .data_man - .get_epoch_execution_context(&epoch_id) + .get_epoch_execution_context(&last_pivot_block_hash) .map(|v| v.start_block_number) .expect("should exist"); diff --git a/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs b/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs index a104766db5..d2530b4eb1 100644 --- a/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs +++ b/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs @@ -159,6 +159,7 @@ fn extract_attr(attrs: &[Attribute]) -> Option { for segment in path.segments { // Only handle schema attrs if segment.ident == "schema" { + #[allow(clippy::never_loop)] for meta in &nested { let path = if let NestedMeta::Meta(Meta::Path(path)) = meta { diff --git a/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs b/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs index 6b52e74e78..84d3b80a74 100644 --- a/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs +++ b/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs @@ -12,6 +12,7 @@ mod transaction_store; mod ttl_cache; #[cfg(test)] +#[allow(unused_imports)] pub use self::ttl_cache::TtlCache; pub use self::{ index::TxnPointer, mempool::Mempool as CoreMempool, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index f048557719..7fe67f0e1f 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -205,7 +205,6 @@ impl CallTracer for GethTracer { return; } - self.depth += 1; self.inner.gas_stack.push(params.gas.clone()); // determine correct `from` and `to` based on the call scheme @@ -246,6 +245,8 @@ impl CallTracer for GethTracer { self.tx_gas_limit, self.depth, ); + + self.depth += 1; } fn record_call_result(&mut self, result: &FrameResult) { @@ -289,7 +290,6 @@ impl CallTracer for GethTracer { return; } - self.depth += 1; self.inner.gas_stack.push(params.gas.clone()); let value = if matches!(params.call_type, CallType::DelegateCall) { @@ -314,6 +314,8 @@ impl CallTracer for GethTracer { params.gas.as_u64(), self.depth, ); + + self.depth += 1; } fn record_create_result(&mut self, result: &FrameResult) { @@ -440,7 +442,7 @@ struct StackStep { pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { let result = match frame_result { - Ok(_r) => InstructionResult::Return, // todo check this + Ok(_r) => InstructionResult::Return, Err(err) => match err { Error::OutOfGas => InstructionResult::OutOfGas, Error::BadJumpDestination { .. } => InstructionResult::InvalidJump, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs index e97b370682..8e448afac9 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -220,7 +220,6 @@ impl TracingInspector { // this value should use the transaction's gas limit See and gas_limit = tx_gas_limit; } - self.trace_stack.push(self.traces.push_trace( 0, push_kind, diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index 98f544b0bc..b068bb9f2c 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -14,7 +14,7 @@ pub trait Debug { /// network. It will replay any transaction that may have been executed /// prior to this one before it will finally attempt to execute the /// transaction that corresponds to the given hash. - #[rpc(name = "traceTransaction")] + #[rpc(name = "debug_traceTransaction")] fn debug_trace_transaction( &self, tx_hash: H256, opts: Option, ) -> JsonRpcResult; From 4544a5932b8c49bba08eb0d80bfceb1cc6e67603 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Tue, 7 May 2024 18:51:43 +0800 Subject: [PATCH 027/137] Add encoding for CIP1559 and CIP2930 transactions. --- crates/primitives/src/block.rs | 1 - crates/primitives/src/transaction/mod.rs | 67 ++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index cf3f15f5a4..dce0957ff8 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -6,7 +6,6 @@ use crate::{BlockHeader, SignedTransaction, TransactionWithSignature}; use byteorder::{ByteOrder, LittleEndian}; use cfx_types::{Space, H256, U256}; use keccak_hash::keccak; -use log::info; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use rand::Rng; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 7c847a44e1..18c92bc423 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -524,11 +524,23 @@ impl Encodable for TransactionWithSignatureSerializePart { s.append(&self.r); s.append(&self.s); } - Transaction::Native(TypedNativeTransaction::Cip2930(_)) => { - todo!() + Transaction::Native(TypedNativeTransaction::Cip2930(ref tx)) => { + s.append_raw(TYPED_NATIVE_TX_PREFIX, 0); + s.append_raw(&[CIP2930_TYPE], 0); + s.begin_list(4); + s.append(tx); + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); } - Transaction::Native(TypedNativeTransaction::Cip1559(_)) => { - todo!() + Transaction::Native(TypedNativeTransaction::Cip1559(ref tx)) => { + s.append_raw(TYPED_NATIVE_TX_PREFIX, 0); + s.append_raw(&[CIP1559_TYPE], 0); + s.begin_list(4); + s.append(tx); + s.append(&self.v); + s.append(&self.r); + s.append(&self.s); } } } @@ -601,7 +613,52 @@ impl Decodable for TransactionWithSignatureSerializePart { } else { match rlp.as_raw()[0] { TYPED_NATIVE_TX_PREFIX_BYTE => { - todo!() + if rlp.as_raw().len() <= 4 + || rlp.as_raw()[0..3] != *TYPED_NATIVE_TX_PREFIX + { + return Err(DecoderError::RlpInvalidLength); + } + match rlp.as_raw()[3] { + CIP2930_TYPE => { + let rlp = Rlp::new(&rlp.as_raw()[4..]); + if rlp.item_count()? != 4 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let tx = rlp.val_at(0)?; + let v = rlp.val_at(1)?; + let r = rlp.val_at(2)?; + let s = rlp.val_at(3)?; + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Native( + TypedNativeTransaction::Cip2930(tx), + ), + v, + r, + s, + }) + } + CIP1559_TYPE => { + let rlp = Rlp::new(&rlp.as_raw()[4..]); + if rlp.item_count()? != 4 { + return Err(DecoderError::RlpIncorrectListLen); + } + + let tx = rlp.val_at(0)?; + let v = rlp.val_at(1)?; + let r = rlp.val_at(2)?; + let s = rlp.val_at(3)?; + Ok(TransactionWithSignatureSerializePart { + unsigned: Transaction::Native( + TypedNativeTransaction::Cip1559(tx), + ), + v, + r, + s, + }) + } + _ => Err(DecoderError::RlpInvalidLength), + } } EIP2930_TYPE => { let rlp = Rlp::new(&rlp.as_raw()[1..]); From 28297a784c70ca14d5356fc748b18cbec16394eb Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 8 May 2024 16:29:33 +0800 Subject: [PATCH 028/137] cargo fmt --- .../consensus_inner/consensus_executor.rs | 4 +--- crates/client/src/rpc/types/eth/block.rs | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index ee7a695833..475b5ffa43 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -1978,6 +1978,4 @@ pub struct ConsensusExecutionConfiguration { pub executive_trace: bool, } -fn todo_base_price() -> U256 { - U256::zero() -} \ No newline at end of file +fn todo_base_price() -> U256 { U256::zero() } diff --git a/crates/client/src/rpc/types/eth/block.rs b/crates/client/src/rpc/types/eth/block.rs index 090865325a..acf632bf42 100644 --- a/crates/client/src/rpc/types/eth/block.rs +++ b/crates/client/src/rpc/types/eth/block.rs @@ -146,8 +146,7 @@ impl Block { pub fn from_phantom(pb: &PhantomBlock, full: bool) -> Self { let transactions = if full { BlockTransactions::Full( - pb - .transactions + pb.transactions .iter() .enumerate() .map(|(idx, t)| { @@ -155,15 +154,18 @@ impl Block { .outcome_status .in_space(Space::Ethereum); - let contract_address = match Transaction::deployed_contract_address(&**t) { - Some(a) if status == EVM_SPACE_SUCCESS => Some(a), - _ => None, - }; + let contract_address = + match Transaction::deployed_contract_address(&**t) { + Some(a) if status == EVM_SPACE_SUCCESS => { + Some(a) + } + _ => None, + }; Transaction::from_signed( &**t, ( - Some(pb.pivot_header.hash()), // block_hash + Some(pb.pivot_header.hash()), // block_hash Some(pb.pivot_header.height().into()), // block_number Some(idx.into()), // transaction_index ), From a3c0a5eed9155bce6215cbdcf57de0628fd05e54 Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 8 May 2024 18:04:33 +0800 Subject: [PATCH 029/137] op code --- crates/cfxcore/core/src/consensus/mod.rs | 1 - .../src/observer/geth_tracer/mod.rs | 39 +++++++++---------- .../observer/geth_tracer/tracing_inspector.rs | 5 --- crates/client/src/rpc/impls/eth/debug.rs | 33 +++++++++++++++- .../client/src/rpc/traits/eth_space/debug.rs | 22 ++++++++++- 5 files changed, 71 insertions(+), 29 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index fad1323870..21a239f911 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -1453,7 +1453,6 @@ impl ConsensusGraph { }; self.executor.collect_epoch_geth_trace( - epoch_num, epoch_block_hashes, tx_hash, opts, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 7fe67f0e1f..cf45ba248b 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -48,15 +48,17 @@ pub struct GethTracer { // fourbyte_inspector: FourByteInspector, // - tx_gas_limit: u64, + tx_gas_limit: u64, // tx level gas limit // - gas_left: u64, + gas_left: u64, // update in call_result/create_result // call depth depth: usize, // - return_data: Bytes, + return_data: Bytes, // update in call_result/create_result // opts: GethDebugTracingOptions, + // gas stack, used to trace gas_spent in call_result/create_result + pub gas_stack: Vec, } impl GethTracer { @@ -101,6 +103,7 @@ impl GethTracer { gas_left: tx_gas_limit, return_data: Bytes::default(), opts, + gas_stack: Vec::new(), } } @@ -205,7 +208,8 @@ impl CallTracer for GethTracer { return; } - self.inner.gas_stack.push(params.gas.clone()); + let gas_limit = params.gas.as_u64(); + self.gas_stack.push(gas_limit); // determine correct `from` and `to` based on the call scheme let (from, to) = match params.call_type { @@ -255,11 +259,10 @@ impl CallTracer for GethTracer { } self.depth -= 1; - let mut gas_spent = - self.inner.gas_stack.pop().expect("should have value"); + let mut gas_spent = self.gas_stack.pop().expect("should have value"); if let Ok(r) = result { - gas_spent = gas_spent - r.gas_left; + gas_spent = gas_spent - r.gas_left.as_u64(); self.gas_left = r.gas_left.as_u64(); } @@ -281,8 +284,7 @@ impl CallTracer for GethTracer { gas: Gas::default(), }; - self.inner - .fill_trace_on_call_end(outcome, None, gas_spent.as_u64()); + self.inner.fill_trace_on_call_end(outcome, None, gas_spent); } fn record_create(&mut self, params: &ActionParams) { @@ -290,7 +292,8 @@ impl CallTracer for GethTracer { return; } - self.inner.gas_stack.push(params.gas.clone()); + let gas_limit = params.gas.as_u64(); + self.gas_stack.push(gas_limit); let value = if matches!(params.call_type, CallType::DelegateCall) { // for delegate calls we need to use the value of the top trace @@ -324,11 +327,10 @@ impl CallTracer for GethTracer { } self.depth -= 1; - let mut gas_spent = - self.inner.gas_stack.pop().expect("should have value"); + let mut gas_spent = self.gas_stack.pop().expect("should have value"); if let Ok(r) = result { - gas_spent = gas_spent - r.gas_left; + gas_spent = gas_spent - r.gas_left.as_u64(); self.gas_left = r.gas_left.as_u64(); } @@ -357,11 +359,8 @@ impl CallTracer for GethTracer { None }; - self.inner.fill_trace_on_call_end( - outcome, - create_address, - gas_spent.as_u64(), - ); + self.inner + .fill_trace_on_call_end(outcome, create_address, gas_spent); } } @@ -378,13 +377,13 @@ impl OpcodeTracer for GethTracer { .set_gas_remainning(gas_limit.as_u64()); } - fn step(&mut self, interp: &dyn InterpreterInfo, depth: usize) { + fn step(&mut self, interp: &dyn InterpreterInfo, _depth: usize) { self.inner .gas_inspector .set_gas_remainning(interp.gas_remainning().as_u64()); if self.inner.config.record_steps { - self.inner.start_step(interp, depth as u64); + self.inner.start_step(interp, self.depth as u64); } } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs index 8e448afac9..4291224ae3 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -32,8 +32,6 @@ pub struct TracingInspector { last_call_return_data: Option, /// The gas inspector used to track remaining gas. pub gas_inspector: GasInspector, - // gas stack, used to trace gas_spent in call_result/create_result - pub gas_stack: Vec, // machine: Arc, } @@ -48,7 +46,6 @@ impl TracingInspector { step_stack: vec![], last_call_return_data: None, gas_inspector: Default::default(), - gas_stack: vec![], machine, } } @@ -68,13 +65,11 @@ impl TracingInspector { gas_inspector, // kept config: _, - gas_stack, machine: _, } = self; traces.clear(); trace_stack.clear(); step_stack.clear(); - gas_stack.clear(); last_call_return_data.take(); *gas_inspector = Default::default(); } diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 5bd9754ac0..b6cf2e35fa 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -1,10 +1,13 @@ use crate::rpc::{ - error_codes::invalid_params_msg, traits::eth_space::debug::Debug, + error_codes::invalid_params_msg, + traits::eth_space::debug::Debug, + types::eth::{BlockNumber, CallRequest}, }; use alloy_rpc_types_trace::geth::{ GethDebugBuiltInTracerType, GethDebugTracerType::{BuiltInTracer, JsTracer}, - GethDebugTracingOptions, GethTrace, NoopFrame, + GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, + TraceResult, }; use cfx_types::H256; use cfxcore::{ConsensusGraph, SharedConsensusGraph}; @@ -100,4 +103,30 @@ impl Debug for GethDebugHandler { trace } + + fn debug_trace_block_by_hash( + &self, block: H256, opts: Option, + ) -> JsonRpcResult> { + let _ = block; + let _ = opts; + todo!("not implemented yet"); + } + + fn debug_trace_block_by_number( + &self, block: BlockNumber, opts: Option, + ) -> JsonRpcResult> { + let _ = block; + let _ = opts; + todo!("not implemented yet"); + } + + fn debug_trace_call( + &self, request: CallRequest, block_number: Option, + opts: Option, + ) -> JsonRpcResult { + let _ = request; + let _ = block_number; + let _ = opts; + todo!("not implemented yet"); + } } diff --git a/crates/client/src/rpc/traits/eth_space/debug.rs b/crates/client/src/rpc/traits/eth_space/debug.rs index b068bb9f2c..ce76c24e03 100644 --- a/crates/client/src/rpc/traits/eth_space/debug.rs +++ b/crates/client/src/rpc/traits/eth_space/debug.rs @@ -1,4 +1,8 @@ -use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethTrace}; +use crate::rpc::types::eth::{BlockNumber, CallRequest}; +use alloy_rpc_types_trace::geth::{ + GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, + TraceResult, +}; use cfx_types::H256; use jsonrpc_core::Result as JsonRpcResult; use jsonrpc_derive::rpc; @@ -18,4 +22,20 @@ pub trait Debug { fn debug_trace_transaction( &self, tx_hash: H256, opts: Option, ) -> JsonRpcResult; + + #[rpc(name = "debug_traceBlockByHash")] + fn debug_trace_block_by_hash( + &self, block: H256, opts: Option, + ) -> JsonRpcResult>; + + #[rpc(name = "debug_traceBlockByNumber")] + fn debug_trace_block_by_number( + &self, block: BlockNumber, opts: Option, + ) -> JsonRpcResult>; + + #[rpc(name = "debug_traceCall")] + fn debug_trace_call( + &self, request: CallRequest, block_number: Option, + opts: Option, + ) -> JsonRpcResult; } From e584639671a2f0bbc16e9df2a0ce71399175dde8 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 9 May 2024 17:12:43 +0800 Subject: [PATCH 030/137] the workable version --- .../consensus_inner/consensus_executor.rs | 45 ++-- crates/cfxcore/core/src/consensus/mod.rs | 5 +- .../src/observer/geth_tracer/builder/geth.rs | 7 + .../src/observer/geth_tracer/builder/mod.rs | 2 + .../src/observer/geth_tracer/mod.rs | 12 +- .../src/observer/geth_tracer/types.rs | 9 +- crates/cfxcore/executor/src/context.rs | 2 +- .../executor/src/observer/opcode_tracer.rs | 5 +- crates/client/src/rpc/impls/eth/debug.rs | 76 ++++++- docs/transaction-trace/geth-style-trace.md | 34 +++ tests/evm_space/debug_trace_tx_test.py | 208 ++++++++++++++++++ tests/extra-test-toolkits | 2 +- tests/test_contracts | 2 +- 13 files changed, 364 insertions(+), 45 deletions(-) create mode 100644 docs/transaction-trace/geth-style-trace.md create mode 100644 tests/evm_space/debug_trace_tx_test.py diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 5f3870ee88..7d5d00e47e 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -62,7 +62,7 @@ use crate::{ use cfx_execute_helper::{ estimation::{EstimateExt, EstimateRequest, EstimationContext}, exec_tracer::TransactionExecTraces, - observer::Observer, + observer::{geth_tracer::types::GethTraceWithHash, Observer}, tx_outcome::make_process_tx_outcome, }; use cfx_executor::{ @@ -80,8 +80,7 @@ use cfx_executor::{ use cfx_vm_types::{Env, Spec}; use alloy_rpc_types_trace::geth::{ - GethDebugBuiltInTracerType::*, GethDebugTracerType, - GethDebugTracingOptions, GethTrace, + GethDebugBuiltInTracerType::*, GethDebugTracerType, GethDebugTracingOptions, }; lazy_static! { @@ -654,7 +653,7 @@ impl ConsensusExecutor { pub fn collect_epoch_geth_trace( &self, epoch_block_hashes: Vec, tx_hash: Option, opts: GethDebugTracingOptions, - ) -> RpcResult> { + ) -> RpcResult> { self.handler .collect_epoch_geth_trace(epoch_block_hashes, tx_hash, opts) } @@ -1932,7 +1931,7 @@ impl ConsensusExecutionHandler { pub fn collect_epoch_geth_trace( &self, epoch_block_hashes: Vec, tx_hash: Option, opts: GethDebugTracingOptions, - ) -> RpcResult> { + ) -> RpcResult> { // Get blocks in this epoch after skip checking let epoch_blocks = self .data_man @@ -1943,21 +1942,22 @@ impl ConsensusExecutionHandler { .expect("blocks exist"); let pivot_block = epoch_blocks.last().expect("Not empty"); - let last_pivot_block_hash = pivot_block.block_header.parent_hash(); + let parent_pivot_block_hash = pivot_block.block_header.parent_hash(); + // get the state of the parent pivot block let state_availability_boundary = self.data_man.state_availability_boundary.read(); - let state_space = None; + let state_space = None; // None for both core and espace if !state_availability_boundary.check_read_availability( pivot_block.block_header.height() - 1, - last_pivot_block_hash, + parent_pivot_block_hash, state_space, ) { bail!("state is not ready"); } let state_index = self .data_man - .get_state_readonly_index(last_pivot_block_hash); + .get_state_readonly_index(parent_pivot_block_hash); let mut state = State::new(StateDb::new( self.data_man .storage_manager @@ -1972,7 +1972,7 @@ impl ConsensusExecutionHandler { let start_block_number = self .data_man - .get_epoch_execution_context(&last_pivot_block_hash) + .get_epoch_execution_context(&parent_pivot_block_hash) .map(|v| v.start_block_number) .expect("should exist"); @@ -1986,11 +1986,14 @@ impl ConsensusExecutionHandler { .map_err(|err| err.into()) } + // Execute transactions in the epoch to collect traces. + // The behavior is similar to `process_epoch_transactions`, but it won't + // persist any state or data fn execute_epoch_tx_to_collect_trace( &self, state: &mut State, epoch_blocks: &Vec>, start_block_number: u64, tx_hash: Option, opts: GethDebugTracingOptions, - ) -> DbResult> { + ) -> DbResult> { let pivot_block = epoch_blocks.last().expect("Epoch not empty"); let mut block_number = start_block_number; @@ -2061,14 +2064,14 @@ impl ConsensusExecutionHandler { GethDebugTracerType::BuiltInTracer(bt) => { match bt { FourByteTracer | CallTracer - | PreStateTracer => Observer::geth_tracer( - tx_gas_limit, - Arc::clone(&self.machine), - opts.clone(), - ), - NoopTracer | MuxTracer => { - Observer::with_no_tracing() + | PreStateTracer | NoopTracer => { + Observer::geth_tracer( + tx_gas_limit, + Arc::clone(&self.machine), + opts.clone(), + ) } + MuxTracer => Observer::with_no_tracing(), } } GethDebugTracerType::JsTracer(_) => { @@ -2104,7 +2107,11 @@ impl ConsensusExecutionHandler { ); if need_trace && r.geth_trace.is_some() { - traces.push((transaction.hash(), r.geth_trace.unwrap())); + traces.push(GethTraceWithHash { + trace: r.geth_trace.unwrap(), + tx_hash: transaction.hash(), + space: transaction.space(), + }); } } } diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index 21a239f911..1bb18ca7f0 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -41,11 +41,12 @@ use cfx_execute_helper::{ recover_phantom_traces, ActionType, BlockExecTraces, LocalizedTrace, TraceFilter, TransactionExecTraces, }, + observer::geth_tracer::types::GethTraceWithHash, phantom_tx::build_bloom_and_recover_phantom, }; use cfx_executor::{executive::ExecutionOutcome, state::State}; -use alloy_rpc_types_trace::geth::{GethDebugTracingOptions, GethTrace}; +use alloy_rpc_types_trace::geth::GethDebugTracingOptions; use cfx_internal_common::ChainIdParams; use cfx_parameters::{ consensus::*, @@ -1439,7 +1440,7 @@ impl ConsensusGraph { pub fn collect_epoch_geth_trace( &self, epoch_num: u64, tx_hash: Option, opts: GethDebugTracingOptions, - ) -> RpcResult> { + ) -> RpcResult> { // only allow to call against stated epoch let epoch = EpochNumber::Number(epoch_num); self.validate_stated_epoch(&epoch)?; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs index 11ed49fab5..ad926be7b4 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -86,6 +86,13 @@ impl GethTraceBuilder { let child_trace = &self.nodes[call_child_id]; child_trace.push_steps_on_stack(&mut step_stack); } + + // if we reached the limit, we stop + if let Some(limit) = opts.limit { + if limit > 0 && struct_logs.len() >= limit as usize { + break; + } + } } } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs index ae68f630bb..eeb7dd7634 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs @@ -1 +1,3 @@ pub mod geth; + +pub use geth::GethTraceBuilder; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index cf45ba248b..875d5f1ead 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -4,13 +4,13 @@ mod builder; mod config; mod gas; mod tracing_inspector; -mod types; -mod utils; +pub mod types; +pub mod utils; -pub use arena::CallTraceArena; -pub use builder::geth::{self, GethTraceBuilder}; +use arena::CallTraceArena; +use builder::GethTraceBuilder; use cfx_types::H160; -pub use config::{StackSnapshotType, TracingInspectorConfig}; +use config::TracingInspectorConfig; use types::LogCallOrder; use utils::{to_alloy_address, to_alloy_h256, to_alloy_u256}; @@ -377,7 +377,7 @@ impl OpcodeTracer for GethTracer { .set_gas_remainning(gas_limit.as_u64()); } - fn step(&mut self, interp: &dyn InterpreterInfo, _depth: usize) { + fn step(&mut self, interp: &dyn InterpreterInfo) { self.inner .gas_inspector .set_gas_remainning(interp.gas_remainning().as_u64()); diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs index ea2396afe6..44628d28b8 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs @@ -5,8 +5,9 @@ use crate::observer::geth_tracer::{ }; use alloy_primitives::{Address, Bytes, LogData, U256}; use alloy_rpc_types_trace::geth::{ - CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog, + CallFrame, CallLogFrame, GethDefaultTracingOptions, GethTrace, StructLog, }; +use cfx_types::{Space, H256}; use cfx_vm_types::CallType as CfxCallType; use revm::interpreter::{ opcode, CallContext, CallScheme, CreateScheme, InstructionResult, OpCode, @@ -577,6 +578,12 @@ impl AsRef<[u8]> for RecordedMemory { fn as_ref(&self) -> &[u8] { self.as_bytes() } } +pub struct GethTraceWithHash { + pub trace: GethTrace, + pub tx_hash: H256, + pub space: Space, +} + #[cfg(feature = "serde")] mod opcode_serde { use super::OpCode; diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index e0620f57a6..66c495757f 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -457,7 +457,7 @@ impl<'a> ContextTrait for Context<'a> { // } fn trace_step(&mut self, interpreter: &dyn vm::InterpreterInfo) { - self.tracer.step(interpreter, self.depth); + self.tracer.step(interpreter); } fn trace_step_end(&mut self, interpreter: &dyn vm::InterpreterInfo) { diff --git a/crates/cfxcore/executor/src/observer/opcode_tracer.rs b/crates/cfxcore/executor/src/observer/opcode_tracer.rs index 74139e2b3b..ce7a073ad1 100644 --- a/crates/cfxcore/executor/src/observer/opcode_tracer.rs +++ b/crates/cfxcore/executor/src/observer/opcode_tracer.rs @@ -17,10 +17,7 @@ pub trait OpcodeTracer { /// /// Information about the current execution, including the memory, stack and /// more is available on `interp` (see [Interpreter]). - fn step(&mut self, interp: &dyn InterpreterInfo, depth: usize) { - let _ = interp; - let _ = depth; - } + fn step(&mut self, interp: &dyn InterpreterInfo) { let _ = interp; } /// Called after `step` when the instruction has been executed. fn step_end(&mut self, interp: &dyn InterpreterInfo) { let _ = interp; } diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index b6cf2e35fa..56ab79a2ed 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -1,3 +1,5 @@ +use std::convert::TryInto; + use crate::rpc::{ error_codes::invalid_params_msg, traits::eth_space::debug::Debug, @@ -9,7 +11,8 @@ use alloy_rpc_types_trace::geth::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, TraceResult, }; -use cfx_types::H256; +use cfx_execute_helper::observer::geth_tracer::utils::to_alloy_h256; +use cfx_types::{Space, H256}; use cfxcore::{ConsensusGraph, SharedConsensusGraph}; use jsonrpc_core::Result as JsonRpcResult; @@ -97,27 +100,80 @@ impl Debug for GethDebugHandler { // filter by tx hash let trace = epoch_traces .into_iter() - .find(|(tx_hash, _)| tx_hash == &hash) - .map(|(_, trace)| trace) + .find(|val| val.tx_hash == hash) + .map(|val| val.trace) .ok_or(invalid_params_msg("trace generation failed")); trace } fn debug_trace_block_by_hash( - &self, block: H256, opts: Option, + &self, block_hash: H256, opts: Option, ) -> JsonRpcResult> { - let _ = block; - let _ = opts; - todo!("not implemented yet"); + let opts = opts.unwrap_or_default(); + let epoch_num = self + .consensus_graph() + .get_block_epoch_number_with_pivot_check(&block_hash, false)?; + + let epoch_traces = self + .consensus_graph() + .collect_epoch_geth_trace(epoch_num, None, opts) + .map_err(|e| { + invalid_params_msg(&format!("invalid tx hash: {e}")) + })?; + + let result = epoch_traces + .into_iter() + .filter(|val| val.space == Space::Ethereum) + .map(|val| TraceResult::Success { + result: val.trace, + tx_hash: Some(to_alloy_h256(val.tx_hash)), + }) + .collect(); + Ok(result) } fn debug_trace_block_by_number( &self, block: BlockNumber, opts: Option, ) -> JsonRpcResult> { - let _ = block; - let _ = opts; - todo!("not implemented yet"); + let opts = opts.unwrap_or_default(); + let num = match block { + BlockNumber::Num(block_number) => block_number, + BlockNumber::Latest + | BlockNumber::Safe + | BlockNumber::Finalized => { + let epoch_num = block.try_into().expect("should success"); + self.consensus_graph() + .get_height_from_epoch_number(epoch_num) + .map_err(|msg| invalid_params_msg(&msg))? + } + BlockNumber::Hash { + hash, + require_canonical, + } => self + .consensus_graph() + .get_block_epoch_number_with_pivot_check( + &hash, + require_canonical, + )?, + _ => return Err(invalid_params_msg("not supported")), + }; + let epoch_traces = self + .consensus_graph() + .collect_epoch_geth_trace(num, None, opts) + .map_err(|e| { + invalid_params_msg(&format!("invalid tx hash: {e}")) + })?; + + let result = epoch_traces + .into_iter() + .filter(|val| val.space == Space::Ethereum) + .map(|val| TraceResult::Success { + result: val.trace, + tx_hash: Some(to_alloy_h256(val.tx_hash)), + }) + .collect(); + Ok(result) } fn debug_trace_call( diff --git a/docs/transaction-trace/geth-style-trace.md b/docs/transaction-trace/geth-style-trace.md new file mode 100644 index 0000000000..ae1b2c5b28 --- /dev/null +++ b/docs/transaction-trace/geth-style-trace.md @@ -0,0 +1,34 @@ +# Geth Style Trace + +Ethereum geth client provides [a way to trace transactions](https://geth.ethereum.org/docs/developers/evm-tracing). The trace is useful for debugging and understanding the transaction execution. Geth's trace related RPC methods are under [`debug`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugtracetransaction) namespace, include: + +- `debug_traceTransaction` +- `debug_traceBlock` +- `debug_traceBlockByNumber` +- `debug_traceBlockByHash` +- `debug_traceCall` + +Geth support different kind of traces, include: + +- opcode +- prestateTracer +- 4byteTracer +- noopTracer +- callTracer + +And serveral builtin JS tracers, include: + +- bigram +- evmdis +- unigram +- opcount +- trigram +- unigram + +Geth also support writing custom tracer in [Go and Js](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer). + +debug_traceCall method suport [state override](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#state-overrides). + +## Conflux Implementation + + diff --git a/tests/evm_space/debug_trace_tx_test.py b/tests/evm_space/debug_trace_tx_test.py new file mode 100644 index 0000000000..26dea61e6c --- /dev/null +++ b/tests/evm_space/debug_trace_tx_test.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python3 +# import os, sys +# sys.path.insert(1, os.path.join(sys.path[0], '..')) + +from base import Web3Base +from conflux.config import default_config +from test_framework.util import * +from web3 import Web3 + +toHex = Web3.toHex + +class DebugTraceTxTest(Web3Base): + def set_test_params(self): + super().set_test_params() + self.conf_parameters["public_evm_rpc_apis"] = "\"eth,ethdebug\"" + + def run_test(self): + self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] + self.cfxAccount = self.rpc.GENESIS_ADDR + print(f'Using Conflux account {self.cfxAccount}') + # initialize EVM account + self.evmAccount = self.w3.eth.account.privateKeyToAccount(self.DEFAULT_TEST_ACCOUNT_KEY) + print(f'Using EVM account {self.evmAccount.address}') + self.cross_space_transfer(self.evmAccount.address, 100 * 10 ** 18) + assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(100 * 10 ** 18)) + + # self.common_cfx_transfer_tx_trace() + erc20_addr = self.contract_deploy_tx_trace() + erc20_transfer_hash = self.erc20_transfer_tx_trace(erc20_addr) + self.noop_tracer(erc20_transfer_hash) + self.four_byte_tracer(erc20_transfer_hash) + self.call_tracer(erc20_transfer_hash) + self.check_opcode_trace_with_config(erc20_transfer_hash) + + def trace_tx(self, tx_hash, opts = None): + trace = self.nodes[0].ethrpc.debug_traceTransaction(toHex(tx_hash), opts) + return trace + + def common_cfx_transfer_tx_trace(self): + nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + + signed = self.evmAccount.signTransaction({ + "to": self.evmAccount.address, + "value": 1, + "gasPrice": 1, + "gas": 210000, + "nonce": nonce, + "chainId": self.w3.eth.chainId, + }) + + return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + self.rpc.generate_blocks(20, 1) + trace = self.nodes[0].ethrpc.debug_traceTransaction(toHex(return_tx_hash)) + + assert_equal(trace["failed"], False) + assert_equal(trace["gas"], 21000) + assert_equal(trace["returnValue"], "") + assert_equal(trace["structLogs"], []) + + def contract_deploy_tx_trace(self): + bytecode_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../contracts/erc20_bytecode.dat") + assert(os.path.isfile(bytecode_file)) + bytecode = open(bytecode_file).read() + + nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + signed = self.evmAccount.signTransaction({ + "to": None, + "value": 0, + "gasPrice": 1, + "gas": 10000000, + "nonce": nonce, + "chainId": self.w3.eth.chainId, + "data": bytecode, + }) + + return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + + self.rpc.generate_block(1) + self.rpc.generate_blocks(20, 1) + + receipt = self.w3.eth.get_transaction_receipt(return_tx_hash) + assert_equal(receipt["status"], 1) + + trace = self.nodes[0].ethrpc.debug_traceTransaction(toHex(return_tx_hash)) + assert_equal(trace["failed"], False) + oplog_len = len(trace["structLogs"]) + assert_equal(oplog_len > 0, True) + # print(trace["structLogs"][oplog_len-1]) + assert_equal(trace["structLogs"][oplog_len-1]["op"], "RETURN") + + return receipt["contractAddress"] + + def erc20_transfer_tx_trace(self, erc20_address): + abi = self.load_abi_from_contracts_folder("erc20") + erc20 = self.w3.eth.contract(address=erc20_address, abi=abi) + + # balance = erc20.functions.balanceOf(self.evmAccount.address).call() + target_addr = Web3.toChecksumAddress("0x8b14d287b4150ff22ac73df8be720e933f659abc") + + data = erc20.encodeABI(fn_name="transfer", args=[target_addr, 100]) + + nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + signed = self.evmAccount.signTransaction({ + "to": erc20_address, + "value": 0, + "gasPrice": 1, + "gas": 1000000, + "nonce": nonce, + "chainId": self.w3.eth.chainId, + "data": data, + }) + + tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + + # why this method is not working? + # tx_hash = erc20.functions.transfer(target_addr, 100).transact() + + self.rpc.generate_block(1) + self.rpc.generate_blocks(20, 1) + # receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash) + + trace = self.trace_tx(tx_hash) + + assert_equal(trace["failed"], False) + oplog_len = len(trace["structLogs"]) + assert_equal(oplog_len > 0, True) + assert_equal(trace["structLogs"][oplog_len-1]["op"], "RETURN") + + return tx_hash + + def noop_tracer(self, tx_hash): + noop_trace = self.trace_tx(tx_hash, {"tracer": "noopTracer"}) + assert_equal(noop_trace, {}) + + def four_byte_tracer(self, tx_hash): + four_byte_trace = self.trace_tx(tx_hash, {"tracer": "4byteTracer"}) + assert_equal(four_byte_trace, {'0xa9059cbb-64': 1}) + + def call_tracer(self, tx_hash): + call_trace = self.trace_tx(tx_hash, {"tracer": "callTracer"}) + assert_equal(call_trace["from"], "0xfcad0b19bb29d4674531d6f115237e16afce377c") + assert_equal(call_trace["to"], "0x8bfc6fd9437cf1879fb84aade867b6e81efb5631") + assert_equal(call_trace["type"], 'CALL') + assert_equal(call_trace["value"], "0x0") + assert_equal(call_trace["output"], "0x0000000000000000000000000000000000000000000000000000000000000001") + + def check_opcode_trace_with_config(self, tx_hash): + trace = self.trace_tx(tx_hash, { + "enableMemory": True, + "disableStack": False, + "disableStorage": False, + "enableReturnData": True + }) + + oplog_len = len(trace["structLogs"]) + assert_equal(trace["failed"], False) + assert_equal(oplog_len, 231) + # print(len(trace["structLogs"])) + + # limit parameter test + limited_trace = self.trace_tx(tx_hash, { + "enableMemory": True, + "disableStack": False, + "disableStorage": False, + "enableReturnData": True, + "limit": 10 + }) + assert_equal(len(limited_trace["structLogs"]), 10) + + no_stack_storage_trace = self.trace_tx(tx_hash, { + "enableMemory": True, + "disableStack": True, + "disableStorage": True, + "enableReturnData": True + }) + + disable_all_trace = self.trace_tx(tx_hash, { + "enableMemory": False, + "disableStack": True, + "disableStorage": True, + "enableReturnData": False + }) + + for i, oplog in enumerate(trace["structLogs"]): + oplog = trace["structLogs"][i] + + if "memory" in oplog: + assert_equal("memory" in disable_all_trace["structLogs"][i], False) + + if "returnData" in oplog: + assert_equal("returnData" in disable_all_trace["structLogs"][i], False) + + if "stack" in oplog: + assert_equal("stack" in no_stack_storage_trace["structLogs"][i], False) + + if "storage" in oplog: + assert_equal("storage" in no_stack_storage_trace["structLogs"][i], False) + + + + def load_abi_from_contracts_folder(self, name): + abi_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "contracts", name + "_abi.json") + with open(abi_file, 'r') as abi_file: + abi = json.loads(abi_file.read()) + return abi + +if __name__ == "__main__": + DebugTraceTxTest().main() \ No newline at end of file diff --git a/tests/extra-test-toolkits b/tests/extra-test-toolkits index 043d9c8185..f0a4feafd7 160000 --- a/tests/extra-test-toolkits +++ b/tests/extra-test-toolkits @@ -1 +1 @@ -Subproject commit 043d9c8185fdc62080cbb4f554a5a0a98b08cf1e +Subproject commit f0a4feafd70334a0d34f6e49ba2973715fb66666 diff --git a/tests/test_contracts b/tests/test_contracts index 8003472fd9..d3f0452bda 160000 --- a/tests/test_contracts +++ b/tests/test_contracts @@ -1 +1 @@ -Subproject commit 8003472fd91db3fcf4469ba5f767c17eaa07dd74 +Subproject commit d3f0452bda55154491dc4ff5cf76a56f62472244 From 321eb82c532907929c3f70718057d0d3db844dff Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Thu, 9 May 2024 14:45:36 +0800 Subject: [PATCH 031/137] Add `cip1559_data` in BlockHeader. --- crates/blockgen/src/lib.rs | 2 + crates/cfxcore/core/src/error.rs | 6 +++ crates/cfxcore/core/src/verification.rs | 10 ++++ crates/cfxcore/executor/src/spec.rs | 9 +++- crates/cfxcore/parameters/src/lib.rs | 1 + crates/primitives/src/block_header.rs | 62 +++++++++++++++++++++++-- 6 files changed, 84 insertions(+), 6 deletions(-) diff --git a/crates/blockgen/src/lib.rs b/crates/blockgen/src/lib.rs index a804d366f4..589aa8b80c 100644 --- a/crates/blockgen/src/lib.rs +++ b/crates/blockgen/src/lib.rs @@ -279,6 +279,8 @@ impl BlockGenerator { .with_gas_limit(block_gas_limit) .with_custom(custom) .with_pos_reference(maybe_pos_reference) + // TODO: Set base fee and gas limit + .with_cip1559_data(None) .build(); Block::new(block_header, transactions) diff --git a/crates/cfxcore/core/src/error.rs b/crates/cfxcore/core/src/error.rs index a6471c3222..3e035131a2 100644 --- a/crates/cfxcore/core/src/error.rs +++ b/crates/cfxcore/core/src/error.rs @@ -57,6 +57,10 @@ pub enum BlockError { MissingPosReference, /// Should not have a PoS reference but it's set. UnexpectedPosReference, + /// Should have a base fee but it's not set. + MissingBaseFee, + /// Should not have a base fee but it's set. + UnexpectedBaseFee, /// The PoS reference violates the validity rule (it should extend the PoS /// reference of the parent and referees). InvalidPosReference, @@ -123,6 +127,8 @@ impl fmt::Display for BlockError { } MissingPosReference => "Missing PoS reference".into(), UnexpectedPosReference => "Should not have PoS reference".into(), + MissingBaseFee => "Missing base fee".into(), + UnexpectedBaseFee => "Should not have base fee".into(), InvalidPosReference => "The PoS reference is invalid".into(), }; diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 1d9bdee99b..0b9db83e45 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -366,6 +366,16 @@ impl VerificationConfig { } } + if header.height() >= self.machine.params().transition_heights.cip1559 { + if header.cip1559_data().is_none() { + bail!(BlockError::MissingBaseFee); + } + } else { + if header.cip1559_data().is_some() { + bail!(BlockError::UnexpectedBaseFee); + } + } + // Note that this is just used to rule out deprecated blocks, so the // change of header struct actually happens before the change of // reward is reflected in the state root. The first state root diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index a48f29b56c..adfd3c60cf 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -8,7 +8,8 @@ use cfx_parameters::{ block::{EVM_TRANSACTION_BLOCK_RATIO, EVM_TRANSACTION_GAS_RATIO}, consensus::{ CIP112_HEADER_CUSTOM_FIRST_ELEMENT, - DAO_VOTE_HEADER_CUSTOM_FIRST_ELEMENT, ONE_UCFX_IN_DRIP, + DAO_VOTE_HEADER_CUSTOM_FIRST_ELEMENT, + NEXT_HARDFORK_HEADER_CUSTOM_FIRST_ELEMENT, ONE_UCFX_IN_DRIP, TANZANITE_HEADER_CUSTOM_FIRST_ELEMENT, }, consensus_internal::{ @@ -206,8 +207,12 @@ impl CommonParams { && height < self.transition_heights.cip112 { Some(vec![DAO_VOTE_HEADER_CUSTOM_FIRST_ELEMENT.to_vec()]) - } else if height >= self.transition_heights.cip112 { + } else if height >= self.transition_heights.cip112 + && height < self.transition_heights.cip1559 + { Some(vec![CIP112_HEADER_CUSTOM_FIRST_ELEMENT.to_vec()]) + } else if height >= self.transition_heights.cip1559 { + Some(vec![NEXT_HARDFORK_HEADER_CUSTOM_FIRST_ELEMENT.to_vec()]) } else { None } diff --git a/crates/cfxcore/parameters/src/lib.rs b/crates/cfxcore/parameters/src/lib.rs index 89be79a0ce..b3b5465c93 100644 --- a/crates/cfxcore/parameters/src/lib.rs +++ b/crates/cfxcore/parameters/src/lib.rs @@ -50,6 +50,7 @@ pub mod consensus { pub const TANZANITE_HEADER_CUSTOM_FIRST_ELEMENT: [u8; 1] = [1]; pub const DAO_VOTE_HEADER_CUSTOM_FIRST_ELEMENT: [u8; 1] = [2]; pub const CIP112_HEADER_CUSTOM_FIRST_ELEMENT: [u8; 1] = [3]; + pub const NEXT_HARDFORK_HEADER_CUSTOM_FIRST_ELEMENT: [u8; 1] = [4]; } pub mod consensus_internal { diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index a24970934a..61c30d926d 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -10,6 +10,7 @@ use cfx_types::{Address, Bloom, H256, KECCAK_EMPTY_BLOOM, U256}; use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps}; use once_cell::sync::OnceCell; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; +use rlp_derive::{RlpDecodable, RlpEncodable}; use std::{ mem, ops::{Deref, DerefMut}, @@ -58,6 +59,8 @@ pub struct BlockHeaderRlpPart { nonce: U256, /// Referred PoS block ID. pos_reference: Option, + /// `[core_space_base_fee, espace_base_fee, espace_gas_limit]`. + cip1559_data: Option, } impl PartialEq for BlockHeaderRlpPart { @@ -76,6 +79,8 @@ impl PartialEq for BlockHeaderRlpPart { && self.gas_limit == o.gas_limit && self.referee_hashes == o.referee_hashes && self.custom == o.custom + && self.pos_reference == o.pos_reference + && self.cip1559_data == o.cip1559_data } } @@ -167,6 +172,20 @@ impl BlockHeader { /// Get the PoS reference. pub fn pos_reference(&self) -> &Option { &self.pos_reference } + pub fn cip1559_data(&self) -> &Option { &self.cip1559_data } + + pub fn core_base_fee(&self) -> Option<&U256> { + self.cip1559_data.as_ref().map(|l| &l.core_base_fee) + } + + pub fn espace_base_fee(&self) -> Option<&U256> { + self.cip1559_data.as_ref().map(|l| &l.espace_base_fee) + } + + pub fn espace_gas_limit(&self) -> Option<&U256> { + self.cip1559_data.as_ref().map(|l| &l.espace_gas_limit) + } + /// Set the nonce field of the header. pub fn set_nonce(&mut self, nonce: U256) { self.nonce = nonce; } @@ -212,6 +231,7 @@ impl BlockHeader { let adaptive_n = if self.adaptive { 1 as u8 } else { 0 as u8 }; let list_len = HEADER_LIST_MIN_LEN + self.pos_reference.is_some() as usize + + self.cip1559_data.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -231,6 +251,9 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } + if self.cip1559_data.is_some() { + stream.append(&self.cip1559_data); + } for b in &self.custom { if self.height @@ -249,6 +272,7 @@ impl BlockHeader { let list_len = HEADER_LIST_MIN_LEN + 1 + self.pos_reference.is_some() as usize + + self.cip1559_data.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -269,6 +293,9 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } + if self.cip1559_data.is_some() { + stream.append(&self.cip1559_data); + } for b in &self.custom { if self.height >= *CIP112_TRANSITION_HEIGHT.get().expect("initialized") @@ -286,6 +313,7 @@ impl BlockHeader { let list_len = HEADER_LIST_MIN_LEN + 2 + self.pos_reference.is_some() as usize + + self.cip1559_data.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -309,6 +337,9 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } + if self.cip1559_data.is_some() { + stream.append(&self.cip1559_data); + } for b in &self.custom { if self.height @@ -340,11 +371,14 @@ impl BlockHeader { custom: vec![], nonce: r.val_at(13)?, pos_reference: r.val_at(15).unwrap_or(None), + cip1559_data: r.val_at(16).unwrap_or(None), }; let pow_hash = r.val_at(14)?; - for i in - (15 + rlp_part.pos_reference.is_some() as usize)..r.item_count()? + for i in (15 + + rlp_part.pos_reference.is_some() as usize + + rlp_part.cip1559_data.is_some() as usize) + ..r.item_count()? { if rlp_part.height >= *CIP112_TRANSITION_HEIGHT.get().expect("initialized") @@ -389,6 +423,7 @@ pub struct BlockHeaderBuilder { custom: Vec, nonce: U256, pos_reference: Option, + cip1559_data: Option, } impl BlockHeaderBuilder { @@ -410,6 +445,7 @@ impl BlockHeaderBuilder { custom: Vec::new(), nonce: U256::zero(), pos_reference: None, + cip1559_data: None, } } @@ -505,6 +541,13 @@ impl BlockHeaderBuilder { self } + pub fn with_cip1559_data( + &mut self, cip1559_data: Option, + ) -> &mut Self { + self.cip1559_data = cip1559_data; + self + } + pub fn build(&self) -> BlockHeader { let mut block_header = BlockHeader { rlp_part: BlockHeaderRlpPart { @@ -524,6 +567,7 @@ impl BlockHeaderBuilder { custom: self.custom.clone(), nonce: self.nonce, pos_reference: self.pos_reference, + cip1559_data: self.cip1559_data.clone(), }, hash: None, pow_hash: None, @@ -606,9 +650,12 @@ impl Decodable for BlockHeader { custom: vec![], nonce: r.val_at(13)?, pos_reference: r.val_at(14).unwrap_or(None), + cip1559_data: r.val_at(15).unwrap_or(None), }; - for i in - (14 + rlp_part.pos_reference.is_some() as usize)..r.item_count()? + for i in (14 + + rlp_part.pos_reference.is_some() as usize + + rlp_part.cip1559_data.is_some() as usize) + ..r.item_count()? { if rlp_part.height >= *CIP112_TRANSITION_HEIGHT.get().expect("initialized") @@ -631,6 +678,13 @@ impl Decodable for BlockHeader { } } +#[derive(Clone, Debug, Eq, RlpDecodable, RlpEncodable, PartialEq)] +pub struct CIP1559Data { + core_base_fee: U256, + espace_base_fee: U256, + espace_gas_limit: U256, +} + #[cfg(test)] mod tests { use super::BlockHeaderBuilder; From fab6bd887a708c2a4ba950d7293535ecc8303eb8 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 9 May 2024 18:02:09 +0800 Subject: [PATCH 032/137] add doc about geth trace --- docs/transaction-trace/geth-style-trace.md | 34 ++++++++++++++++++++ docs/transaction-trace/parity-style-trace.md | 3 ++ run/hydra.toml | 7 ++-- tests/test_contracts | 2 +- 4 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 docs/transaction-trace/parity-style-trace.md diff --git a/docs/transaction-trace/geth-style-trace.md b/docs/transaction-trace/geth-style-trace.md index ae1b2c5b28..64335f85c9 100644 --- a/docs/transaction-trace/geth-style-trace.md +++ b/docs/transaction-trace/geth-style-trace.md @@ -31,4 +31,38 @@ debug_traceCall method suport [state override](https://geth.ethereum.org/docs/de ## Conflux Implementation +Conflux eSpace has implemented main features of geth style trace, include: +- `debug_traceTransaction` +- `debug_traceBlockByNumber` +- `debug_traceBlockByHash` +- `debug_traceCall` (Working) + +Currently supported tracers: + +- opcode +- prestateTracer (Working) +- 4byteTracer +- noopTracer +- callTracer + +We will support writing custom tracer with Js in the future. + +To use eSpace trace RPC methods, you need to enable `ethdebug` API module in the config file. + +```toml +public_evm_rpc_apis = "eth,ethdebug" +``` + +### Opcode Tracer + +Currently the opcode trace's structLogs `error` field is not implemented. + +## Resources + +1. [Geth EVM Tracing](https://geth.ethereum.org/docs/developers/evm-tracing) +2. [Geth Debug RPC](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug) +3. [Geth Custom Tracer](https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer) +4. [Geth State Override](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#state-overrides) +5. [Geth Tracer](https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers) +6. [Paradigmxyz's ultimate_evm_tracing_reference](https://github.com/paradigmxyz/ultimate_evm_tracing_reference) \ No newline at end of file diff --git a/docs/transaction-trace/parity-style-trace.md b/docs/transaction-trace/parity-style-trace.md new file mode 100644 index 0000000000..df3f266b2a --- /dev/null +++ b/docs/transaction-trace/parity-style-trace.md @@ -0,0 +1,3 @@ +# Parity Trace Style + +Both Conflux Core Space and eSpace support parity style trace RPC methods, but with some differences, check [this doc](https://doc.confluxnetwork.org/docs/core/build/json-rpc/trace_rpc) for details \ No newline at end of file diff --git a/run/hydra.toml b/run/hydra.toml index 6c88c8c80e..73b841b9a6 100644 --- a/run/hydra.toml +++ b/run/hydra.toml @@ -155,10 +155,13 @@ jsonrpc_local_http_port=12539 # Specify the APIs available through the public JSON-RPC interfaces (HTTP, TCP, WebSocket) # using a comma-delimited list of API names. -# Possible names are: all, safe, cfx, pos, debug, pubsub, test, trace, txpool. + +# Possible Core space names are: all, safe, cfx, pos, debug, pubsub, test, trace, txpool. # `safe` only includes `cfx` and `pubsub`, `txpool`. -# # public_rpc_apis = "safe" + +# Possible eSpace names are: eth, ethpubsub, ethdebug. +# `evm` only includes `eth` and `ethpubsub` # public_evm_rpc_apis = "evm" # --------------- Performance-related Network Parameters ---------------------- diff --git a/tests/test_contracts b/tests/test_contracts index ca89c563bc..d3f0452bda 160000 --- a/tests/test_contracts +++ b/tests/test_contracts @@ -1 +1 @@ -Subproject commit ca89c563bcbd86289b87a48970bb4b8b09bebd11 +Subproject commit d3f0452bda55154491dc4ff5cf76a56f62472244 From 3207971491b8f1f85a8584027208b3274b53c627 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Thu, 9 May 2024 17:44:00 +0800 Subject: [PATCH 033/137] Add CIP136 parameters. --- .../core/src/pos/consensus/executor/src/vm.rs | 7 +- .../core/src/pos/types/src/term_state.rs | 102 ++++++++++-------- .../types/src/term_state/pos_state_config.rs | 73 ++++++++++--- crates/client/src/configuration.rs | 8 ++ 4 files changed, 129 insertions(+), 61 deletions(-) diff --git a/crates/cfxcore/core/src/pos/consensus/executor/src/vm.rs b/crates/cfxcore/core/src/pos/consensus/executor/src/vm.rs index 2904697572..7e09c6601c 100644 --- a/crates/cfxcore/core/src/pos/consensus/executor/src/vm.rs +++ b/crates/cfxcore/core/src/pos/consensus/executor/src/vm.rs @@ -75,17 +75,16 @@ impl PosVM { diem_debug!("get_unlock_events: {}", events.len()); let next_view = state_view.pos_state().current_view() + 1; - let round_per_term = POS_STATE_CONFIG.round_per_term(); + let (term, view_in_term) = POS_STATE_CONFIG.get_term_view(next_view); // TODO(lpl): Simplify. - if next_view % round_per_term == 0 { - let term = next_view / round_per_term; + if view_in_term == 0 { let (validator_verifier, vrf_seed) = state_view.pos_state().get_committee_at(term).map_err(|e| { diem_warn!("get_new_committee error: {:?}", e); VMStatus::Error(StatusCode::CFX_INVALID_TX) })?; - let epoch = next_view / round_per_term + 1; + let epoch = term + 1; let validator_bytes = bcs::to_bytes(&EpochState::new( epoch, validator_verifier, diff --git a/crates/cfxcore/core/src/pos/types/src/term_state.rs b/crates/cfxcore/core/src/pos/types/src/term_state.rs index 427d7db648..49447eaacf 100644 --- a/crates/cfxcore/core/src/pos/types/src/term_state.rs +++ b/crates/cfxcore/core/src/pos/types/src/term_state.rs @@ -260,6 +260,10 @@ pub struct TermData { impl TermData { pub fn start_view(&self) -> u64 { self.start_view } + pub fn get_term(&self) -> u64 { + POS_STATE_CONFIG.get_term_view(self.start_view).0 + } + pub fn node_list(&self) -> &NodeList { &self.node_list } } @@ -289,7 +293,8 @@ impl Eq for ElectingHeap {} impl TermData { fn next_term(&self, node_list: NodeList, seed: Vec) -> Self { TermData { - start_view: self.start_view + POS_STATE_CONFIG.round_per_term(), + start_view: self.start_view + + POS_STATE_CONFIG.round_per_term(self.start_view), seed, node_list, } @@ -403,8 +408,8 @@ impl TermList { } // This double-check should always pass. debug_assert!( - self.term_list[TERM_LIST_LEN].start_view - == new_term.saturating_mul(POS_STATE_CONFIG.round_per_term()) + Some(self.term_list[TERM_LIST_LEN].start_view) + == POS_STATE_CONFIG.get_starting_view_for_term(new_term) ); self.term_list.remove(0); let new_term = self @@ -635,16 +640,14 @@ impl PosState { } }; - if election_tx - .target_term - .checked_mul(POS_STATE_CONFIG.round_per_term()) - .is_none() + let target_view = match POS_STATE_CONFIG + .get_starting_view_for_term(election_tx.target_term) { - return Some(DiscardedVMStatus::ELECTION_TERGET_TERM_NOT_OPEN); - } - - let target_view = - election_tx.target_term * POS_STATE_CONFIG.round_per_term(); + None => { + return Some(DiscardedVMStatus::ELECTION_TERGET_TERM_NOT_OPEN) + } + Some(v) => v, + }; if node.lock_status.available_votes() == 0 { return Some(DiscardedVMStatus::ELECTION_WITHOUT_VOTES); @@ -652,7 +655,8 @@ impl PosState { // Do not check `ELECTION_TERM_END_ROUND` because we are using the // committed state in this simple validation. if target_view - <= self.current_view + POS_STATE_CONFIG.election_term_end_round() + <= self.current_view + + POS_STATE_CONFIG.election_term_end_round(self.current_view) { return Some(DiscardedVMStatus::ELECTION_TERGET_TERM_NOT_OPEN); } @@ -691,13 +695,21 @@ impl PosState { if node.lock_status.available_votes() == 0 { bail!("Election without any votes"); } - let target_view = - election_tx.target_term * POS_STATE_CONFIG.round_per_term(); + let target_view = match POS_STATE_CONFIG + .get_starting_view_for_term(election_tx.target_term) + { + None => { + bail!("target view overflows, election_tx={:?}", election_tx) + } + Some(v) => v, + }; if target_view - > self.current_view + POS_STATE_CONFIG.election_term_start_round() + > self.current_view + + POS_STATE_CONFIG.election_term_start_round(self.current_view) || target_view <= self.current_view - + POS_STATE_CONFIG.election_term_end_round() + + POS_STATE_CONFIG + .election_term_end_round(self.current_view) { bail!( "Target term is not open for election: target={} current={}", @@ -857,25 +869,28 @@ impl PosState { } pub fn final_serving_view(&self, author: &AccountAddress) -> Option { - let mut final_elected_view = None; + let mut final_elected_term = None; for term in self.term_list.term_list.iter().rev() { match &term.node_list { NodeList::Electing(heap) => { if heap.1.contains(author) { - final_elected_view = Some(term.start_view); + final_elected_term = Some(term.get_term()); break; } } NodeList::Elected(map) => { if map.0.contains_key(author) { - final_elected_view = Some(term.start_view); + final_elected_term = Some(term.get_term()); break; } } } } - final_elected_view.map(|v| { - v + TERM_LIST_LEN as u64 * POS_STATE_CONFIG.round_per_term() + 1 + final_elected_term.map(|t| { + POS_STATE_CONFIG + .get_starting_view_for_term(t + TERM_LIST_LEN as u64) + .expect("checked term") + + 1 }) } @@ -1012,27 +1027,30 @@ impl PosState { let (verifier, term_seed) = self.get_committee_at(0)?; // genesis Some(EpochState::new(1, verifier, term_seed.clone())) - } else if self.current_view % POS_STATE_CONFIG.round_per_term() == 0 { - let new_term = - self.current_view / POS_STATE_CONFIG.round_per_term(); - let (verifier, term_seed) = self.get_committee_at(new_term)?; - // generate new epoch for new term. - self.term_list.new_term( - new_term, - self.pivot_decision.block_hash.as_bytes().to_vec(), - ); - // TODO(lpl): If we allow epoch changes within a term, this - // should be updated. - Some(EpochState::new(new_term + 1, verifier, term_seed.clone())) - } else if self.current_view - >= POS_STATE_CONFIG.first_end_election_view() - && self.current_view % POS_STATE_CONFIG.round_per_term() - == POS_STATE_CONFIG.round_per_term() / 2 - { - self.term_list.finalize_election(); - None } else { - None + let (term, view_in_term) = + POS_STATE_CONFIG.get_term_view(self.current_view); + if view_in_term == 0 { + let new_term = term; + let (verifier, term_seed) = self.get_committee_at(new_term)?; + // generate new epoch for new term. + self.term_list.new_term( + new_term, + self.pivot_decision.block_hash.as_bytes().to_vec(), + ); + // TODO(lpl): If we allow epoch changes within a term, this + // should be updated. + Some(EpochState::new(new_term + 1, verifier, term_seed.clone())) + } else if self.current_view + >= POS_STATE_CONFIG.first_end_election_view() + && view_in_term + == POS_STATE_CONFIG.round_per_term(self.current_view) / 2 + { + self.term_list.finalize_election(); + None + } else { + None + } }; if let Some(epoch_state) = &epoch_state { self.epoch_state = epoch_state.clone(); diff --git a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs index 4d7d0fb02d..634a03fa05 100644 --- a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs +++ b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs @@ -8,6 +8,7 @@ use crate::{ use diem_crypto::_once_cell::sync::OnceCell; const CIP99_FORCE_RETIRE_EPOCH_COUNT: u64 = 3; +const CIP136_FORCE_RETIRE_EPOCH_COUNT: u64 = 6; #[derive(Clone, Debug)] pub struct PosStateConfig { @@ -21,14 +22,19 @@ pub struct PosStateConfig { cip99_out_queue_locked_views: u64, cip99_in_queue_locked_views: u64, + cip136_transition_view: u64, + cip136_out_queue_locked_views: u64, + cip136_in_queue_locked_views: u64, + cip136_round_per_term: u64, + nonce_limit_transition_view: u64, max_nonce_per_account: u64, } pub trait PosStateConfigTrait { - fn round_per_term(&self) -> Round; - fn election_term_start_round(&self) -> Round; - fn election_term_end_round(&self) -> Round; + fn round_per_term(&self, view: u64) -> Round; + fn election_term_start_round(&self, view: u64) -> Round; + fn election_term_end_round(&self, view: u64) -> Round; fn first_start_election_view(&self) -> u64; fn first_end_election_view(&self) -> u64; fn term_max_size(&self) -> usize; @@ -39,6 +45,8 @@ pub trait PosStateConfigTrait { fn force_retire_check_epoch_count(&self, view: u64) -> u64; fn max_nonce_per_account(&self, view: u64) -> u64; + fn get_term_view(&self, view: u64) -> (u64, u64); + fn get_starting_view_for_term(&self, term: u64) -> Option; } impl PosStateConfig { @@ -47,7 +55,9 @@ impl PosStateConfig { in_queue_locked_views: u64, out_queue_locked_views: u64, cip99_transition_view: u64, cip99_in_queue_locked_views: u64, cip99_out_queue_locked_views: u64, nonce_limit_transition_view: u64, - max_nonce_per_account: u64, + max_nonce_per_account: u64, cip136_transition_view: u64, + cip136_in_queue_locked_views: u64, cip136_out_queue_locked_views: u64, + cip136_round_per_term: u64, ) -> Self { Self { round_per_term, @@ -58,6 +68,10 @@ impl PosStateConfig { cip99_transition_view, cip99_out_queue_locked_views, cip99_in_queue_locked_views, + cip136_transition_view, + cip136_out_queue_locked_views, + cip136_in_queue_locked_views, + cip136_round_per_term, nonce_limit_transition_view, max_nonce_per_account, } @@ -65,25 +79,34 @@ impl PosStateConfig { } impl PosStateConfigTrait for OnceCell { - fn round_per_term(&self) -> Round { self.get().unwrap().round_per_term } + fn round_per_term(&self, view: u64) -> Round { + let conf = self.get().unwrap(); + if view < conf.cip136_transition_view { + conf.round_per_term + } else { + conf.cip136_round_per_term + } + } /// A term `n` is open for election in the view range /// `(n * ROUND_PER_TERM - ELECTION_TERM_START_ROUND, n * ROUND_PER_TERM - /// ELECTION_TERM_END_ROUND]` - fn election_term_start_round(&self) -> Round { - self.round_per_term() / 2 * 3 + fn election_term_start_round(&self, view: u64) -> Round { + self.round_per_term(view) / 2 * 3 } - fn election_term_end_round(&self) -> Round { self.round_per_term() / 2 } + fn election_term_end_round(&self, view: u64) -> Round { + self.round_per_term(view) / 2 + } fn first_start_election_view(&self) -> u64 { - TERM_LIST_LEN as u64 * self.round_per_term() - - self.election_term_start_round() + TERM_LIST_LEN as u64 * self.round_per_term(0) + - self.election_term_start_round(0) } fn first_end_election_view(&self) -> u64 { - TERM_LIST_LEN as u64 * self.round_per_term() - - self.election_term_end_round() + TERM_LIST_LEN as u64 * self.round_per_term(0) + - self.election_term_end_round(0) } fn term_max_size(&self) -> usize { self.get().unwrap().term_max_size } @@ -94,8 +117,12 @@ impl PosStateConfigTrait for OnceCell { fn in_queue_locked_views(&self, view: u64) -> u64 { let conf = self.get().unwrap(); - if view >= conf.cip99_transition_view { + if view >= conf.cip99_transition_view + && view < conf.cip136_transition_view + { conf.cip99_in_queue_locked_views + } else if view >= conf.cip136_transition_view { + conf.cip136_in_queue_locked_views } else { conf.in_queue_locked_views } @@ -103,8 +130,12 @@ impl PosStateConfigTrait for OnceCell { fn out_queue_locked_views(&self, view: u64) -> u64 { let conf = self.get().unwrap(); - if view >= conf.cip99_transition_view { + if view >= conf.cip99_transition_view + && view < conf.cip136_transition_view + { conf.cip99_out_queue_locked_views + } else if view >= conf.cip136_transition_view { + conf.cip136_out_queue_locked_views } else { conf.out_queue_locked_views } @@ -116,10 +147,14 @@ impl PosStateConfigTrait for OnceCell { fn force_retire_check_epoch_count(&self, view: u64) -> u64 { let conf = self.get().unwrap(); - if view >= conf.cip99_transition_view { + if view >= conf.cip99_transition_view + && view < conf.cip136_transition_view + { // This is set according to the value of `TERM_LIST_LEN`. // Since `TERM_LIST_LEN` is hardcoded, we do not parameterize this. CIP99_FORCE_RETIRE_EPOCH_COUNT + } else if view > conf.cip136_transition_view { + CIP136_FORCE_RETIRE_EPOCH_COUNT } else { 1 } @@ -133,6 +168,10 @@ impl PosStateConfigTrait for OnceCell { u64::MAX } } + + fn get_term_view(&self, view: u64) -> (u64, u64) { todo!() } + + fn get_starting_view_for_term(&self, term: u64) -> Option { todo!() } } pub static POS_STATE_CONFIG: OnceCell = OnceCell::new(); @@ -148,6 +187,10 @@ impl Default for PosStateConfig { cip99_transition_view: u64::MAX, cip99_out_queue_locked_views: IN_QUEUE_LOCKED_VIEWS, cip99_in_queue_locked_views: OUT_QUEUE_LOCKED_VIEWS, + cip136_transition_view: u64::MAX, + cip136_out_queue_locked_views: IN_QUEUE_LOCKED_VIEWS, + cip136_in_queue_locked_views: OUT_QUEUE_LOCKED_VIEWS, + cip136_round_per_term: ROUND_PER_TERM, nonce_limit_transition_view: u64::MAX, max_nonce_per_account: u64::MAX, } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index f65f6f6ce7..2369599c0e 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -348,6 +348,10 @@ build_config! { (pos_cip99_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS) (pos_cip99_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS) (nonce_limit_transition_view, (u64), u64::MAX) + (pos_cip136_transition_view, (u64), u64::MAX) + (pos_cip136_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS) + (pos_cip136_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS) + (pos_cip136_round_per_term, (u64), ROUND_PER_TERM) (dev_pos_private_key_encryption_password, (Option), None) (pos_started_as_voter, (bool), true) @@ -1391,6 +1395,10 @@ impl Configuration { self.raw_conf.pos_cip99_out_queue_locked_views, self.raw_conf.nonce_limit_transition_view, 20_000, // 2 * 10^7 CFX + self.raw_conf.pos_cip136_transition_view, + self.raw_conf.pos_cip136_in_queue_locked_views, + self.raw_conf.pos_cip136_out_queue_locked_views, + self.raw_conf.pos_cip136_round_per_term, ) } } From 26e556e48d5f6f7d41213ea5a45eff2c9c19025e Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Thu, 9 May 2024 19:38:26 +0800 Subject: [PATCH 034/137] CIP-137: Base Fee Sharing in CIP-1559 --- .../consensus_inner/consensus_executor.rs | 102 ++++--- .../execute-helper/src/phantom_tx/mod.rs | 1 + .../cfxcore/execute-helper/src/tx_outcome.rs | 4 +- crates/cfxcore/executor/src/context.rs | 1 + .../executor/src/executive/executed.rs | 17 +- .../src/executive/execution_outcome.rs | 36 ++- .../executor/src/executive/fresh_executive.rs | 32 ++- crates/cfxcore/executor/src/executive/mod.rs | 10 +- .../src/executive/pre_checked_executive.rs | 25 +- .../contracts/params_control.rs | 5 +- .../contracts/system_storage.rs | 2 +- .../internal_contract/impls/params_control.rs | 33 ++- crates/cfxcore/executor/src/spec.rs | 24 +- crates/cfxcore/executor/src/state/mod.rs | 2 +- .../state/state_object/global_statistics.rs | 53 +--- .../executor/src/state/state_object/mod.rs | 3 + .../executor/src/state/state_object/reward.rs | 78 +++++ .../src/state/state_object/staking.rs | 15 +- crates/cfxcore/parameters/src/lib.rs | 3 + crates/cfxcore/vm-types/src/env.rs | 2 + crates/cfxcore/vm-types/src/spec.rs | 2 + crates/client/benches/benchmark.rs | 1 + crates/client/src/configuration.rs | 268 +++++++++--------- crates/client/src/rpc/types/receipt.rs | 1 + crates/dbs/statedb/src/global_params.rs | 8 +- crates/primitives/src/block_header.rs | 4 + crates/primitives/src/receipt.rs | 50 +++- 27 files changed, 471 insertions(+), 311 deletions(-) create mode 100644 crates/cfxcore/executor/src/state/state_object/reward.rs diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs index 89eaaa26ad..1df9eab043 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs @@ -34,8 +34,8 @@ use cfx_types::{ }; use metrics::{register_meter_with_group, Meter, MeterTimer}; use primitives::{ - block::BlockHeight, compute_block_number, receipt::BlockReceipts, Action, - Block, BlockHeaderBuilder, BlockNumber, EpochId, SignedTransaction, + compute_block_number, receipt::BlockReceipts, Action, Block, + BlockHeaderBuilder, BlockNumber, EpochId, SignedTransaction, TransactionIndex, MERKLE_NULL_NODE, }; @@ -74,7 +74,7 @@ use cfx_executor::{ }, machine::Machine, state::{ - distribute_pos_interest, initialize_cip107, + distribute_pos_interest, initialize_cip107, initialize_cip137, initialize_or_update_dao_voted_params, update_pos_status, CleanupMode, State, }, @@ -1201,11 +1201,10 @@ impl ConsensusExecutionHandler { .adjust_upper_bound(&pivot_block.block_header); } - fn process_epoch_transactions( + fn prefetch_storage_for_execution( &self, epoch_id: EpochId, state: &mut State, - epoch_blocks: &Vec>, start_block_number: u64, - on_local_pivot: bool, - ) -> DbResult>> { + epoch_blocks: &Vec>, + ) { // Prefetch accounts for transactions. // The return value _prefetch_join_handles is used to join all threads // before the exit of this function. @@ -1234,6 +1233,7 @@ impl ConsensusExecutionHandler { accounts: vec![], }, }; + // TODO: // Make the state shared ref for vm execution, then remove this drop. // When the state can be made shared, prefetch can happen at the same @@ -1241,13 +1241,19 @@ impl ConsensusExecutionHandler { // for prefetching to finish. prefetch_join_handles.wait_for_task(); drop(prefetch_join_handles); + } + + fn process_epoch_transactions( + &self, epoch_id: EpochId, state: &mut State, + epoch_blocks: &Vec>, start_block_number: u64, + on_local_pivot: bool, + ) -> DbResult>> { + self.prefetch_storage_for_execution(epoch_id, state, epoch_blocks); let pivot_block = epoch_blocks.last().expect("Epoch not empty"); - self.record_epoch_hash( - state, - pivot_block.block_header.height(), - pivot_block.hash(), - )?; + self.before_epoch_execution(state, &*pivot_block)?; + let base_gas_price = todo_base_price(); + let burnt_base_price = state.burnt_base_fee(base_gas_price); let mut epoch_receipts = Vec::with_capacity(epoch_blocks.len()); let mut epoch_staking_events = Vec::new(); @@ -1261,8 +1267,7 @@ impl ConsensusExecutionHandler { let mut evm_tx_index = 0; for block in epoch_blocks.iter() { - self.maybe_update_state(state, block_number)?; - self.record_block_hash(state, block_number, block.hash())?; + self.before_block_execution(state, block_number, &*block)?; let mut cfx_tx_index = 0; @@ -1302,8 +1307,8 @@ impl ConsensusExecutionHandler { transaction_epoch_bound: self .verification_config .transaction_epoch_bound, - // TODO: get the actual base price - base_gas_price: todo_base_price(), + base_gas_price, + burnt_gas_price: burnt_base_price, }; let spec = self.machine.spec(env.number, env.epoch_height); if !spec.cip43_contract { @@ -1336,16 +1341,19 @@ impl ConsensusExecutionHandler { ExecutiveContext::new(state, &env, machine, &spec) .transact(transaction, options)?; execution_outcome.log(transaction, &block.hash()); - execution_outcome.burn_by_cip1559( - state, - &transaction, - &todo_base_price(), - ); + + if let Some(burnt_fee) = execution_outcome + .try_as_executed() + .and_then(|e| e.burnt_fee) + { + state.burn_by_cip1559(burnt_fee); + }; let r = make_process_tx_outcome( execution_outcome, &mut env.accumulated_gas_used, transaction.hash, + &spec, ); if r.receipt.tx_success() { @@ -1639,13 +1647,13 @@ impl ConsensusExecutionHandler { block_receipts.receipts.len() == block.transactions.len() ); // TODO: fill base_price. - let base_price = todo_base_price(); for (tx, receipt) in block .transactions .iter() .zip(block_receipts.receipts.iter()) { - let fee = receipt.gas_fee; + let fee = + receipt.gas_fee - receipt.burnt_gas_fee.unwrap_or_default(); let info = tx_fee .entry(tx.hash()) @@ -1660,22 +1668,7 @@ impl ConsensusExecutionHandler { info.1.insert(block_hash); } if !fee.is_zero() && info.0.is_zero() { - // TODO: a temp implmentation, the 1559 related infomation - // should be put in the `BlockReceipts`. - - // When `base_price = 0`, `effective_gas_price = gas_price` - let effective_gas_price = - tx.effective_gas_price(&base_price); - // When gas_fee is non-zero, gas_price must be non-zero - let gas_charged = if effective_gas_price.is_zero() { - U256::zero() - } else { - fee / effective_gas_price - }; - let miner_fee = - tx.priority_gas_price(&base_price) * gas_charged; - - info.0 = miner_fee; + info.0 = fee; } } } @@ -1943,6 +1936,7 @@ impl ConsensusExecutionHandler { .verification_config .transaction_epoch_bound, base_gas_price: U256::zero(), + burnt_gas_price: U256::zero(), }; let spec = self.machine.spec(env.number, env.epoch_height); let mut ex = EstimationContext::new( @@ -1957,13 +1951,13 @@ impl ConsensusExecutionHandler { Ok(r?) } - fn maybe_update_state( - &self, state: &mut State, block_number: BlockNumber, + fn before_block_execution( + &self, state: &mut State, block_number: BlockNumber, block: &Block, ) -> DbResult<()> { let params = self.machine.params(); let transition_numbers = ¶ms.transition_numbers; - let cip94_start = transition_numbers.cip94; + let cip94_start = transition_numbers.cip94n; let period = params.params_dao_vote_period; // Update/initialize parameters before processing rewards. if block_number >= cip94_start @@ -1981,27 +1975,29 @@ impl ConsensusExecutionHandler { if block_number == transition_numbers.cip107 { initialize_cip107(state)?; } - Ok(()) - } - fn record_block_hash( - &self, state: &mut State, block_number: BlockNumber, hash: H256, - ) -> DbResult<()> { - let params = self.machine.params(); - if block_number >= params.transition_numbers.cip133_b { + if block_number >= transition_numbers.cip133b { state.set_system_storage( block_hash_slot(block_number).into(), - U256::from_big_endian(&hash.0), + U256::from_big_endian(&block.hash().0), )?; } + + if block_number == transition_numbers.cip137 { + initialize_cip137(state); + } Ok(()) } - fn record_epoch_hash( - &self, state: &mut State, epoch_number: BlockHeight, hash: H256, + fn before_epoch_execution( + &self, state: &mut State, pivot_block: &Block, ) -> DbResult<()> { let params = self.machine.params(); - if epoch_number >= params.transition_heights.cip133_e { + + let epoch_number = pivot_block.block_header.height(); + let hash = pivot_block.hash(); + + if epoch_number >= params.transition_heights.cip133e { state.set_system_storage( epoch_hash_slot(epoch_number).into(), U256::from_big_endian(&hash.0), diff --git a/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs b/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs index 4a799326f2..6880911983 100644 --- a/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs +++ b/crates/cfxcore/execute-helper/src/phantom_tx/mod.rs @@ -63,6 +63,7 @@ impl PhantomTransaction { storage_collateralized: vec![], storage_released: vec![], storage_sponsor_paid: false, + burnt_gas_fee: None, } } } diff --git a/crates/cfxcore/execute-helper/src/tx_outcome.rs b/crates/cfxcore/execute-helper/src/tx_outcome.rs index e758d9e3c2..84d771e9a1 100644 --- a/crates/cfxcore/execute-helper/src/tx_outcome.rs +++ b/crates/cfxcore/execute-helper/src/tx_outcome.rs @@ -2,6 +2,7 @@ use cfx_executor::{ executive::ExecutionOutcome, internal_contract::make_staking_events, }; use cfx_types::{H256, U256}; +use cfx_vm_types::Spec; use pow_types::StakingEvent; use primitives::Receipt; @@ -30,6 +31,7 @@ fn tx_traces(outcome: &ExecutionOutcome) -> Vec { pub fn make_process_tx_outcome( outcome: ExecutionOutcome, accumulated_gas_used: &mut U256, tx_hash: H256, + spec: &Spec, ) -> ProcessTxOutcome { // TODO[geth-tracer]: extract your trace result here. let _maybe_geth_trace = outcome @@ -39,7 +41,7 @@ pub fn make_process_tx_outcome( let tx_traces = tx_traces(&outcome); let tx_exec_error_msg = outcome.error_message(); let consider_repacked = outcome.consider_repacked(); - let receipt = outcome.make_receipt(accumulated_gas_used); + let receipt = outcome.make_receipt(accumulated_gas_used, spec); let tx_staking_events = make_staking_events(receipt.logs()); diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index 8a758a5f01..e4a14cb5e7 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -586,6 +586,7 @@ mod tests { finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, base_gas_price: U256::zero(), + burnt_gas_price: U256::zero(), } } diff --git a/crates/cfxcore/executor/src/executive/executed.rs b/crates/cfxcore/executor/src/executive/executed.rs index 53951e93ac..f8657128ea 100644 --- a/crates/cfxcore/executor/src/executive/executed.rs +++ b/crates/cfxcore/executor/src/executive/executed.rs @@ -29,6 +29,9 @@ pub struct Executed { /// Fee that need to be paid by execution of this transaction. pub fee: U256, + /// Fee burnt by CIP-1559 + pub burnt_fee: Option, + /// Gas charged during execution of transaction. pub gas_charged: U256, @@ -85,10 +88,12 @@ impl Executed { gas_sponsor_paid = false; storage_sponsor_paid = false; } + let burnt_fee = spec.cip1559.then_some(*fee); Self { gas_used: *tx.gas(), gas_charged, fee: fee.clone(), + burnt_fee, gas_sponsor_paid, logs: vec![], contracts_created: vec![], @@ -112,10 +117,18 @@ impl Executed { gas_sponsor_paid = false; storage_sponsor_paid = false; } + + let fee = tx.gas().saturating_mul(cost.gas_price); + + let burnt_fee = spec + .cip1559 + .then(|| tx.gas().saturating_mul(cost.burnt_gas_price)); + Self { gas_used: *tx.gas(), gas_charged: *tx.gas(), - fee: tx.gas().saturating_mul(cost.gas_price), + fee, + burnt_fee, gas_sponsor_paid, logs: vec![], contracts_created: vec![], @@ -147,6 +160,7 @@ impl Executed { gas_used, gas_charged, fees_value: fee, + burnt_fees_value: burnt_fee, .. } = refund_info; let mut storage_sponsor_paid = if spec.cip78a { @@ -165,6 +179,7 @@ impl Executed { gas_used, gas_charged, fee, + burnt_fee, gas_sponsor_paid, logs: substate.logs.to_vec(), contracts_created: substate.contracts_created.to_vec(), diff --git a/crates/cfxcore/executor/src/executive/execution_outcome.rs b/crates/cfxcore/executor/src/executive/execution_outcome.rs index 51e6bd3f92..d92432e8a5 100644 --- a/crates/cfxcore/executor/src/executive/execution_outcome.rs +++ b/crates/cfxcore/executor/src/executive/execution_outcome.rs @@ -1,5 +1,5 @@ use super::executed::{revert_reason_decode, Executed}; -use crate::{state::State, unwrap_or_return}; +use crate::unwrap_or_return; use cfx_types::{Address, H256, U256, U512}; use cfx_vm_types as vm; use primitives::{ @@ -14,6 +14,7 @@ pub enum ExecutionOutcome { ExecutionErrorBumpNonce(ExecutionError, Executed), Finished(Executed), } +use vm::Spec; use ExecutionOutcome::*; #[derive(Debug)] @@ -50,6 +51,11 @@ pub enum ToRepackError { /// Returned when a non-sponsored transaction's sender does not exist yet. SenderDoesNotExist, + + NotEnoughBaseFee { + expected: U256, + got: U256, + }, } #[derive(Debug)] @@ -82,7 +88,9 @@ pub enum ExecutionError { impl ExecutionOutcome { #[inline] - pub fn make_receipt(self, accumulated_gas_used: &mut U256) -> Receipt { + pub fn make_receipt( + self, accumulated_gas_used: &mut U256, spec: &Spec, + ) -> Receipt { *accumulated_gas_used += self.gas_used(); let gas_fee = self.gas_fee(); @@ -94,6 +102,8 @@ impl ExecutionOutcome { let storage_collateralized = self.storage_collateralized(); let storage_released = self.storage_released(); + let burnt_fee = self.burnt_fee(spec); + let log_bloom = build_bloom(&transaction_logs); Receipt::new( @@ -106,6 +116,7 @@ impl ExecutionOutcome { storage_sponsor_paid, storage_collateralized, storage_released, + burnt_fee, ) } @@ -177,6 +188,17 @@ impl ExecutionOutcome { executed.storage_released.clone() } + #[inline] + pub fn burnt_fee(&self, spec: &Spec) -> Option { + if let Some(e) = self.try_as_executed() { + e.burnt_fee + } else if spec.cip1559 { + Some(U256::zero()) + } else { + None + } + } + #[inline] pub fn consider_repacked(&self) -> bool { matches!(self, NotExecutedToReconsiderPacking(_)) @@ -241,14 +263,4 @@ impl ExecutionOutcome { } } } - - pub fn burn_by_cip1559( - &self, state: &mut State, tx: &SignedTransaction, base_gas_price: &U256, - ) { - let executed = unwrap_or_return!(self.try_as_executed()); - let miner_fee = - executed.gas_charged * tx.priority_gas_price(base_gas_price); - let burnt_fee = executed.fee - miner_fee; - state.burn_by_cip1559(burnt_fee); - } } diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 664db7db29..1b34aa93eb 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -39,6 +39,7 @@ pub(super) struct CostInfo { pub storage_cost: U256, pub sender_intended_cost: U512, pub gas_price: U256, + pub burnt_gas_price: U256, pub gas_sponsored: bool, pub storage_sponsored: bool, @@ -69,6 +70,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { pub(super) fn check_all( self, ) -> DbResult, ExecutionOutcome>> { + early_return_on_err!(self.check_base_price()); // Validate transaction nonce early_return_on_err!(self.check_nonce()?); @@ -117,6 +119,19 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { }) } + fn check_base_price(&self) -> Result<(), ExecutionOutcome> { + if self.tx.gas_price() < &self.context.env.burnt_gas_price { + Err(ExecutionOutcome::NotExecutedToReconsiderPacking( + ToRepackError::NotEnoughBaseFee { + expected: self.context.env.burnt_gas_price, + got: *self.tx.gas_price(), + }, + )) + } else { + Ok(()) + } + } + fn check_epoch_bound(&self) -> DbResult> { let tx = if let Transaction::Native(ref tx) = self.tx.transaction.transaction.unsigned @@ -169,12 +184,21 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let env = self.context.env; let spec = self.context.spec; - let gas_price = if spec.cip1559 { - tx.effective_gas_price(&env.base_gas_price) - } else { + let gas_price = if !spec.cip1559 { *tx.gas_price() + } else { + // actual_base_gas >= tx gas_price >= burnt_base_price + let actual_base_gas = + U256::min(*tx.gas_price(), env.base_gas_price); + tx.effective_gas_price(&actual_base_gas) }; + let burnt_gas_price = env.burnt_gas_price; + // gas_price >= actual_base_gas >= + // 1. tx gas_price >= burnt_base_price + // 2. base_gas_price >= burnt_gas_price + assert!(gas_price >= burnt_gas_price); + let sender_balance = U512::from(state.balance(&sender)?); let gas_cost = if settings.charge_gas { tx.gas().full_mul(gas_price) @@ -200,6 +224,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { base_gas: self.base_gas, gas_cost, gas_price, + burnt_gas_price, storage_cost, sender_intended_cost: sender_cost, total_cost: sender_cost, @@ -312,6 +337,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { base_gas: self.base_gas, gas_cost, gas_price, + burnt_gas_price, storage_cost, sender_balance, total_cost, diff --git a/crates/cfxcore/executor/src/executive/mod.rs b/crates/cfxcore/executor/src/executive/mod.rs index 1d55385d64..b8250b28b7 100644 --- a/crates/cfxcore/executor/src/executive/mod.rs +++ b/crates/cfxcore/executor/src/executive/mod.rs @@ -56,12 +56,10 @@ impl<'a> ExecutiveContext<'a> { ) -> DbResult { let fresh_exec = FreshExecutive::new(self, tx, options); - let pre_checked_exec = match fresh_exec.check_all()? { - Ok(executive) => executive, - Err(execution_outcome) => return Ok(execution_outcome), - }; - - pre_checked_exec.execute_transaction() + Ok(match fresh_exec.check_all()? { + Ok(executive) => executive.execute_transaction()?, + Err(execution_outcome) => execution_outcome, + }) } } diff --git a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs index b4677522df..63e7f9e75e 100644 --- a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs +++ b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs @@ -466,6 +466,7 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { fn compute_refunded_gas(&self, result: &ExecutiveResult) -> RefundInfo { let tx = self.tx; let cost = &self.cost; + let spec = self.context.spec; let gas_left = match result { Ok(ExecutiveReturn { gas_left, .. }) => *gas_left, _ => 0.into(), @@ -475,25 +476,26 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { // gas_left should be smaller than 1/4 of gas_limit, otherwise // 3/4 of gas_limit is charged. let charge_all = (gas_left + gas_left + gas_left) >= gas_used; - let (gas_charged, fees_value, refund_value) = if charge_all { + let (gas_charged, gas_refunded) = if charge_all { let gas_refunded = tx.gas() >> 2; let gas_charged = tx.gas() - gas_refunded; - ( - gas_charged, - gas_charged.saturating_mul(cost.gas_price), - gas_refunded.saturating_mul(cost.gas_price), - ) + (gas_charged, gas_refunded) } else { - ( - gas_used, - gas_used.saturating_mul(cost.gas_price), - gas_left.saturating_mul(cost.gas_price), - ) + (gas_used, gas_left) }; + + let fees_value = gas_charged.saturating_mul(cost.gas_price); + let burnt_fees_value = spec + .cip1559 + .then(|| gas_charged.saturating_mul(cost.burnt_gas_price)); + + let refund_value = gas_refunded.saturating_mul(cost.gas_price); + RefundInfo { gas_used, gas_charged, fees_value, + burnt_fees_value, refund_value, } } @@ -616,6 +618,7 @@ pub(super) struct RefundInfo { pub gas_charged: U256, pub fees_value: U256, + pub burnt_fees_value: Option, pub refund_value: U256, } diff --git a/crates/cfxcore/executor/src/internal_contract/contracts/params_control.rs b/crates/cfxcore/executor/src/internal_contract/contracts/params_control.rs index f57fc74ff8..2803577ba2 100644 --- a/crates/cfxcore/executor/src/internal_contract/contracts/params_control.rs +++ b/crates/cfxcore/executor/src/internal_contract/contracts/params_control.rs @@ -11,7 +11,7 @@ use cfx_vm_interpreter::GasPriceTier; use super::{super::impls::params_control::*, preludes::*}; make_solidity_contract! { - pub struct ParamsControl(PARAMS_CONTROL_CONTRACT_ADDRESS, generate_fn_table, initialize: |params: &CommonParams| params.transition_numbers.cip94, is_active: |spec: &Spec| spec.cip94); + pub struct ParamsControl(PARAMS_CONTROL_CONTRACT_ADDRESS, generate_fn_table, initialize: |params: &CommonParams| params.transition_numbers.cip94n, is_active: |spec: &Spec| spec.cip94); } fn generate_fn_table() -> SolFnTable { make_function_table!( @@ -137,7 +137,8 @@ fn test_vote_abi_length() { pub const POW_BASE_REWARD_INDEX: u8 = 0; pub const POS_REWARD_INTEREST_RATE_INDEX: u8 = 1; pub const STORAGE_POINT_PROP_INDEX: u8 = 2; -pub const PARAMETER_INDEX_MAX: usize = 3; +pub const BASEFEE_PROP_INDEX: u8 = 3; +pub const PARAMETER_INDEX_MAX: usize = 4; pub const OPTION_UNCHANGE_INDEX: u8 = 0; pub const OPTION_INCREASE_INDEX: u8 = 1; diff --git a/crates/cfxcore/executor/src/internal_contract/contracts/system_storage.rs b/crates/cfxcore/executor/src/internal_contract/contracts/system_storage.rs index 28e72939f0..6fbd94545e 100644 --- a/crates/cfxcore/executor/src/internal_contract/contracts/system_storage.rs +++ b/crates/cfxcore/executor/src/internal_contract/contracts/system_storage.rs @@ -7,7 +7,7 @@ use cfx_parameters::internal_contract_addresses::SYSTEM_STORAGE_ADDRESS; use cfx_types::U256; make_solidity_contract! { - pub struct SystemStorage(SYSTEM_STORAGE_ADDRESS, SolFnTable::default, initialize: |params: &CommonParams| params.transition_numbers.cip94, is_active: |spec: &Spec| spec.cip94); + pub struct SystemStorage(SYSTEM_STORAGE_ADDRESS, SolFnTable::default, initialize: |params: &CommonParams| params.transition_numbers.cip94n, is_active: |spec: &Spec| spec.cip94); } pub fn base_slot(contract: Address) -> U256 { diff --git a/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs b/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs index 3859124515..945126425b 100644 --- a/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs +++ b/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs @@ -333,9 +333,9 @@ impl ParamVoteCount { } pub fn compute_next_params( - &self, old_value: U256, pos_staking_tokens: U256, + &self, old_value: U256, pos_staking_for_votes: U256, ) -> U256 { - if self.should_update(pos_staking_tokens) { + if self.should_update(pos_staking_for_votes) { let answer = self.compute_next_params_inner(old_value); // The return value should be in `[2^8, 2^192]` let min_value = U256::from(256u64); @@ -348,7 +348,7 @@ impl ParamVoteCount { answer } } else { - debug!("params unchanged with pos token {}", pos_staking_tokens); + debug!("params unchanged with pos token {}", pos_staking_for_votes); old_value } } @@ -387,9 +387,9 @@ impl ParamVoteCount { } } - fn should_update(&self, pos_staking_tokens: U256) -> bool { + fn should_update(&self, pos_staking_for_votes: U256) -> bool { (self.decrease + self.increase + self.unchange) - >= pos_staking_tokens * DAO_MIN_VOTE_PERCENTAGE / 100 + >= pos_staking_for_votes * DAO_MIN_VOTE_PERCENTAGE / 100 } } @@ -398,6 +398,7 @@ pub struct AllParamsVoteCount { pub pow_base_reward: ParamVoteCount, pub pos_reward_interest: ParamVoteCount, pub storage_point_prop: ParamVoteCount, + pub base_fee_prop: ParamVoteCount, } /// If the vote counts are not initialized, all counts will be zero, and the @@ -417,10 +418,15 @@ pub fn get_settled_param_vote_count( state, &SETTLED_VOTES_ENTRIES[STORAGE_POINT_PROP_INDEX as usize], )?; + let base_fee_prop = ParamVoteCount::from_state( + state, + &SETTLED_VOTES_ENTRIES[BASEFEE_PROP_INDEX as usize], + )?; Ok(AllParamsVoteCount { pow_base_reward, pos_reward_interest, storage_point_prop, + base_fee_prop, }) } @@ -430,9 +436,7 @@ pub fn get_settled_pos_staking_for_votes(state: &State) -> DbResult { /// Move the next vote counts into settled and reset the counts. /// `set_pos_staking` is for compatibility with the Testnet. -pub fn settle_current_votes( - state: &mut State, set_pos_staking: bool, -) -> DbResult<()> { +pub fn settle_current_votes(state: &mut State, cip105: bool) -> DbResult<()> { // Here using `PARAMETER_INDEX_MAX` without knowing the block_number is okay // because if the new parameters have not been enabled, their votes will // be zero and setting them will be no-op. @@ -450,7 +454,7 @@ pub fn settle_current_votes( )?; } } - if set_pos_staking { + if cip105 { let pos_staking = state.get_system_storage(¤t_pos_staking_for_votes())?; state.set_system_storage( @@ -466,11 +470,14 @@ pub fn settle_current_votes( } pub fn params_index_max(spec: &Spec) -> usize { - if spec.cip107 { - PARAMETER_INDEX_MAX - } else { - PARAMETER_INDEX_MAX - 1 + let mut max = PARAMETER_INDEX_MAX; + if !spec.cip1559 { + max -= 1; + } + if !spec.cip107 { + max -= 1; } + max } /// Solidity variable sequences. diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index f23de5308c..8ab6029746 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -82,7 +82,7 @@ pub struct TransitionsBlockNumber { /// CIP-92: Enable Blake2F Builtin Function pub cip92: BlockNumber, /// CIP-94: On-Chain DAO Vote for Chain Parameters - pub cip94: BlockNumber, + pub cip94n: BlockNumber, /// CIP-97: Clear Staking Lists pub cip97: BlockNumber, /// CIP-98: Fix BLOCKHASH Opcode Bug in eSpace @@ -102,7 +102,8 @@ pub struct TransitionsBlockNumber { /// CIP-132: Fix Static Context Check for Internal Contracts pub cip132: BlockNumber, /// CIP-133: Enhanced Block Hash Query - pub cip133_b: BlockNumber, + pub cip133b: BlockNumber, + pub cip137: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -116,13 +117,13 @@ pub struct TransitionsEpochHeight { /// CIP-90: Introduce a Fully EVM-Compatible Space pub cip90a: BlockHeight, /// CIP-94: On-Chain DAO Vote for Chain Parameters - pub cip94: BlockHeight, + pub cip94h: BlockHeight, /// CIP-112: Fix Block Headers `custom` Field Serde pub cip112: BlockHeight, /// CIP-130: Aligning Gas Limit with Transaction Size pub cip130: BlockHeight, /// CIP-133: Enhanced Block Hash Query - pub cip133_e: BlockHeight, + pub cip133e: BlockHeight, pub cip1559: BlockHeight, } @@ -162,8 +163,8 @@ impl CommonParams { spec.cip90 = number >= self.transition_numbers.cip90b; spec.cip78a = number >= self.transition_numbers.cip78a; spec.cip78b = number >= self.transition_numbers.cip78b; - spec.cip94 = number >= self.transition_numbers.cip94; - spec.cip94_activation_block_number = self.transition_numbers.cip94; + spec.cip94 = number >= self.transition_numbers.cip94n; + spec.cip94_activation_block_number = self.transition_numbers.cip94n; spec.cip97 = number >= self.transition_numbers.cip97; spec.cip98 = number >= self.transition_numbers.cip98; spec.cip105 = number >= self.transition_numbers.cip105; @@ -174,9 +175,10 @@ impl CommonParams { spec.cip119 = number >= self.transition_numbers.cip119; spec.cip131 = number >= self.transition_numbers.cip131; spec.cip132 = number >= self.transition_numbers.cip132; - spec.cip133_b = self.transition_numbers.cip133_b; - spec.cip133_e = self.transition_heights.cip133_e; - spec.cip133_core = number >= self.transition_numbers.cip133_b; + spec.cip133_b = self.transition_numbers.cip133b; + spec.cip133_e = self.transition_heights.cip133e; + spec.cip133_core = number >= self.transition_numbers.cip133b; + spec.cip137 = number >= self.transition_numbers.cip137; spec.cip1559 = height >= self.transition_heights.cip1559; spec } @@ -201,10 +203,10 @@ impl CommonParams { pub fn custom_prefix(&self, height: BlockHeight) -> Option> { if height >= self.transition_heights.cip40 - && height < self.transition_heights.cip94 + && height < self.transition_heights.cip94h { Some(vec![TANZANITE_HEADER_CUSTOM_FIRST_ELEMENT.to_vec()]) - } else if height >= self.transition_heights.cip94 + } else if height >= self.transition_heights.cip94h && height < self.transition_heights.cip112 { Some(vec![DAO_VOTE_HEADER_CUSTOM_FIRST_ELEMENT.to_vec()]) diff --git a/crates/cfxcore/executor/src/state/mod.rs b/crates/cfxcore/executor/src/state/mod.rs index a41a5ed431..8633f714b2 100644 --- a/crates/cfxcore/executor/src/state/mod.rs +++ b/crates/cfxcore/executor/src/state/mod.rs @@ -21,7 +21,7 @@ mod state_object; #[cfg(test)] pub use state_object::get_state_for_genesis_write; pub use state_object::{ - distribute_pos_interest, initialize_cip107, + distribute_pos_interest, initialize_cip107, initialize_cip137, initialize_or_update_dao_voted_params, settle_collateral_for_all, update_pos_status, State, COMMISSION_PRIVILEGE_SPECIAL_KEY, }; diff --git a/crates/cfxcore/executor/src/state/state_object/global_statistics.rs b/crates/cfxcore/executor/src/state/state_object/global_statistics.rs index 89955624cf..a0a06312bc 100644 --- a/crates/cfxcore/executor/src/state/state_object/global_statistics.rs +++ b/crates/cfxcore/executor/src/state/state_object/global_statistics.rs @@ -1,46 +1,11 @@ use super::State; -use cfx_parameters::{ - genesis::{ - genesis_contract_address_four_year, genesis_contract_address_two_year, - }, - staking::INTEREST_RATE_PER_BLOCK_SCALE, +use cfx_parameters::genesis::{ + genesis_contract_address_four_year, genesis_contract_address_two_year, }; use cfx_statedb::{global_params::*, Result as DbResult}; use cfx_types::{Address, AddressSpaceUtil, U256}; impl State { - /// Calculate the secondary reward for the next block number. - pub fn bump_block_number_accumulate_interest(&mut self) { - assert!(self.checkpoints.get_mut().is_empty()); - let interset_rate_per_block = self.global_stat.get::(); - let accumulate_interest_rate = - self.global_stat.val::(); - *accumulate_interest_rate = *accumulate_interest_rate - * (*INTEREST_RATE_PER_BLOCK_SCALE + interset_rate_per_block) - / *INTEREST_RATE_PER_BLOCK_SCALE; - } - - pub fn secondary_reward(&self) -> U256 { - assert!(self.checkpoints.read().is_empty()); - let secondary_reward = *self.global_stat.refr::() - * *self.global_stat.refr::() - / *INTEREST_RATE_PER_BLOCK_SCALE; - // TODO: the interest from tokens other than storage and staking should - // send to public fund. - secondary_reward - } - - pub fn pow_base_reward(&self) -> U256 { - let base_reward = self.global_stat.get::(); - assert!(!base_reward.is_zero()); - base_reward - } - - pub fn burn_by_cip1559(&mut self, by: U256) { - *self.global_stat.val::() += by; - self.sub_total_issued(by); - } - pub fn total_issued_tokens(&self) -> U256 { self.global_stat.get::() } @@ -99,14 +64,6 @@ impl State { self.global_stat.refr::().saturating_sub(v) } - pub fn distributable_pos_interest(&self) -> U256 { - self.global_stat.get::() - } - - pub fn last_distribute_block(&self) -> u64 { - self.global_stat.refr::().as_u64() - } - pub fn total_circulating_tokens(&self) -> DbResult { Ok(self.total_issued_tokens() - self.balance(&Address::zero().with_native_space())? @@ -114,12 +71,6 @@ impl State { - self.balance(&genesis_contract_address_two_year())?) } - pub fn reset_pos_distribute_info(&mut self, current_block_number: u64) { - *self.global_stat.val::() = U256::zero(); - *self.global_stat.val::() = - U256::from(current_block_number); - } - pub fn add_converted_storage_point( &mut self, from_balance: U256, from_collateral: U256, ) { diff --git a/crates/cfxcore/executor/src/state/state_object/mod.rs b/crates/cfxcore/executor/src/state/state_object/mod.rs index e4e9d50da4..aabce5e968 100644 --- a/crates/cfxcore/executor/src/state/state_object/mod.rs +++ b/crates/cfxcore/executor/src/state/state_object/mod.rs @@ -39,12 +39,15 @@ mod staking; /// Implements access functions for the account storage entries of `State`. mod storage_entry; +mod reward; + #[cfg(test)] mod tests; pub use self::{ collateral::{initialize_cip107, settle_collateral_for_all}, pos::{distribute_pos_interest, update_pos_status}, + reward::initialize_cip137, sponsor::COMMISSION_PRIVILEGE_SPECIAL_KEY, staking::initialize_or_update_dao_voted_params, }; diff --git a/crates/cfxcore/executor/src/state/state_object/reward.rs b/crates/cfxcore/executor/src/state/state_object/reward.rs new file mode 100644 index 0000000000..248208ac0f --- /dev/null +++ b/crates/cfxcore/executor/src/state/state_object/reward.rs @@ -0,0 +1,78 @@ +use super::State; +use cfx_parameters::{ + consensus::ONE_CFX_IN_DRIP, consensus_internal::CIP137_BASEFEE_PROP_INIT, +}; +use cfx_types::U256; + +use cfx_parameters::staking::INTEREST_RATE_PER_BLOCK_SCALE; +use cfx_statedb::global_params::*; + +impl State { + /// Calculate the secondary reward for the next block number. + pub fn bump_block_number_accumulate_interest(&mut self) { + assert!(self.checkpoints.get_mut().is_empty()); + let interset_rate_per_block = self.global_stat.get::(); + let accumulate_interest_rate = + self.global_stat.val::(); + *accumulate_interest_rate = *accumulate_interest_rate + * (*INTEREST_RATE_PER_BLOCK_SCALE + interset_rate_per_block) + / *INTEREST_RATE_PER_BLOCK_SCALE; + } + + pub fn secondary_reward(&self) -> U256 { + assert!(self.checkpoints.read().is_empty()); + let secondary_reward = *self.global_stat.refr::() + * *self.global_stat.refr::() + / *INTEREST_RATE_PER_BLOCK_SCALE; + // TODO: the interest from tokens other than storage and staking should + // send to public fund. + secondary_reward + } + + pub fn pow_base_reward(&self) -> U256 { + let base_reward = self.global_stat.get::(); + assert!(!base_reward.is_zero()); + base_reward + } + + pub fn distributable_pos_interest(&self) -> U256 { + self.global_stat.get::() + } + + pub fn last_distribute_block(&self) -> u64 { + self.global_stat.refr::().as_u64() + } + + pub fn reset_pos_distribute_info(&mut self, current_block_number: u64) { + *self.global_stat.val::() = U256::zero(); + *self.global_stat.val::() = + U256::from(current_block_number); + } + + pub fn burn_by_cip1559(&mut self, by: U256) { + *self.global_stat.val::() += by; + self.sub_total_issued(by); + } + + pub fn get_base_fee_prop(&self) -> U256 { + self.global_stat.get::() + } + + pub fn set_base_fee_prop(&mut self, val: U256) { + *self.global_stat.val::() = val; + } + + pub fn burnt_base_fee(&self, base_fee: U256) -> U256 { + if base_fee.is_zero() { + return U256::zero(); + } + let prop = self.get_base_fee_prop(); + base_fee - base_fee * prop / (U256::from(ONE_CFX_IN_DRIP) + prop) + } +} + +/// Initialize CIP-137 for the whole system. +pub fn initialize_cip137(state: &mut State) { + debug!("set base_fee_prop to {}", CIP137_BASEFEE_PROP_INIT); + state.set_base_fee_prop(CIP137_BASEFEE_PROP_INIT.into()); +} diff --git a/crates/cfxcore/executor/src/state/state_object/staking.rs b/crates/cfxcore/executor/src/state/state_object/staking.rs index 0a3a8c375b..9bf2dfa40a 100644 --- a/crates/cfxcore/executor/src/state/state_object/staking.rs +++ b/crates/cfxcore/executor/src/state/state_object/staking.rs @@ -129,7 +129,7 @@ impl State { } pub fn initialize_or_update_dao_voted_params( - state: &mut State, set_pos_staking: bool, + state: &mut State, cip105: bool, ) -> DbResult<()> { let vote_count = get_settled_param_vote_count(state).expect("db error"); debug!( @@ -139,7 +139,7 @@ pub fn initialize_or_update_dao_voted_params( debug!( "before pos interest: {} base_reward:{}", state.global_stat.refr::(), - state.global_stat.refr::() + state.global_stat.refr::(), ); // If pos_staking has not been set before, this will be zero and the @@ -180,13 +180,22 @@ pub fn initialize_or_update_dao_voted_params( ), )?; } + + let old_base_fee_prop = state.get_base_fee_prop(); + if !old_base_fee_prop.is_zero() { + state.set_base_fee_prop( + vote_count + .base_fee_prop + .compute_next_params(old_base_fee_prop, pos_staking_for_votes), + ) + } debug!( "pos interest: {} base_reward: {}", state.global_stat.refr::(), state.global_stat.refr::() ); - settle_current_votes(state, set_pos_staking)?; + settle_current_votes(state, cip105)?; Ok(()) } diff --git a/crates/cfxcore/parameters/src/lib.rs b/crates/cfxcore/parameters/src/lib.rs index 89be79a0ce..786c51cfab 100644 --- a/crates/cfxcore/parameters/src/lib.rs +++ b/crates/cfxcore/parameters/src/lib.rs @@ -116,6 +116,9 @@ pub mod consensus_internal { /// The initial storage point proportion after CIP107 is enabled. pub const CIP107_STORAGE_POINT_PROP_INIT: u64 = ONE_CFX_IN_DRIP; + + /// The initial storage point proportion after CIP1559 is enabled. + pub const CIP137_BASEFEE_PROP_INIT: u64 = ONE_CFX_IN_DRIP; } pub mod rpc { diff --git a/crates/cfxcore/vm-types/src/env.rs b/crates/cfxcore/vm-types/src/env.rs index a703059156..170f65514d 100644 --- a/crates/cfxcore/vm-types/src/env.rs +++ b/crates/cfxcore/vm-types/src/env.rs @@ -61,6 +61,8 @@ pub struct Env { /// Base gas price in CIP-1559, equals to 0 if CIP-1559 has not been /// activated pub base_gas_price: U256, + /// Base gas price to miner according to in CIP-137 + pub burnt_gas_price: U256, } #[cfg(test)] diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index ec5a426869..f34588413b 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -169,6 +169,7 @@ pub struct Spec { pub cip133_b: BlockNumber, pub cip133_e: BlockHeight, pub cip133_core: bool, + pub cip137: bool, pub cip1559: bool, pub params_dao_vote_period: u64, } @@ -318,6 +319,7 @@ impl Spec { cip133_b: u64::MAX, cip133_e: u64::MAX, cip133_core: false, + cip137: false, cip1559: false, } } diff --git a/crates/client/benches/benchmark.rs b/crates/client/benches/benchmark.rs index 9a81a10f65..be4c888e64 100644 --- a/crates/client/benches/benchmark.rs +++ b/crates/client/benches/benchmark.rs @@ -63,6 +63,7 @@ fn txexe_benchmark(c: &mut Criterion) { finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, base_gas_price: U256::zero(), + burnt_gas_price: U256::zero(), }; let mut group = c.benchmark_group("Execute 1 transaction"); group diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index fce7ae3c35..4fb1e50a06 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -412,6 +412,14 @@ build_config! { } } +macro_rules! set_conf { + ($src: expr; $dst: expr => {$($field: tt),* }) => { + { + let number = $src; + $($dst.$field = number;)* + } + }; +} pub struct Configuration { pub raw_conf: RawConfiguration, } @@ -1208,6 +1216,47 @@ impl Configuration { pub fn common_params(&self) -> CommonParams { let mut params = CommonParams::default(); + if self.is_test_or_dev_mode() { + params.early_set_internal_contracts_states = true; + } + + params.chain_id = self.chain_id_params(); + params.anticone_penalty_ratio = self.raw_conf.anticone_penalty_ratio; + params.evm_transaction_block_ratio = + self.raw_conf.evm_transaction_block_ratio; + params.evm_transaction_gas_ratio = + self.raw_conf.evm_transaction_gas_ratio; + + params.params_dao_vote_period = self.raw_conf.params_dao_vote_period; + + self.set_cips(&mut params); + + params + } + + pub fn node_type(&self) -> NodeType { + self.raw_conf.node_type.unwrap_or(NodeType::Full) + } + + pub fn pos_state_config(&self) -> PosStateConfig { + // The current implementation requires the round number to be an even + // number. + assert_eq!(self.raw_conf.pos_round_per_term % 2, 0); + PosStateConfig::new( + self.raw_conf.pos_round_per_term, + self.raw_conf.pos_term_max_size, + self.raw_conf.pos_term_elected_size, + self.raw_conf.pos_in_queue_locked_views, + self.raw_conf.pos_out_queue_locked_views, + self.raw_conf.pos_cip99_transition_view, + self.raw_conf.pos_cip99_in_queue_locked_views, + self.raw_conf.pos_cip99_out_queue_locked_views, + self.raw_conf.nonce_limit_transition_view, + 20_000, // 2 * 10^7 CFX + ) + } + + fn set_cips(&self, params: &mut CommonParams) { let default_transition_time = if let Some(num) = self.raw_conf.default_transition_time { num @@ -1216,6 +1265,7 @@ impl Configuration { } else { u64::MAX }; + // This is to set the default transition time for the CIPs that cannot // be enabled in the genesis. let non_genesis_default_transition_time = @@ -1230,176 +1280,116 @@ impl Configuration { } }; - if self.is_test_or_dev_mode() { - params.early_set_internal_contracts_states = true; - } - - params.chain_id = self.chain_id_params(); - params.anticone_penalty_ratio = self.raw_conf.anticone_penalty_ratio; - params.evm_transaction_block_ratio = - self.raw_conf.evm_transaction_block_ratio; - params.evm_transaction_gas_ratio = - self.raw_conf.evm_transaction_gas_ratio; - + // + // Tanzanite hardfork + // params.transition_heights.cip40 = self.raw_conf.tanzanite_transition_height; - params.transition_numbers.cip43a = self - .raw_conf - .hydra_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip94 = self - .raw_conf - .dao_vote_transition_number - .unwrap_or(non_genesis_default_transition_time); - params.transition_numbers.cip97 = self - .raw_conf - .dao_vote_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip98 = self - .raw_conf - .dao_vote_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip105 = self - .raw_conf - .cip105_transition_number - .or(self.raw_conf.dao_vote_transition_number) - .unwrap_or(default_transition_time); - params.transition_numbers.cip_sigma_fix = self - .raw_conf - .sigma_fix_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip107 = self - .raw_conf - .cip107_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip118 = self - .raw_conf - .cip118_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip119 = self - .raw_conf - .cip119_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip131 = self - .raw_conf - .next_hardfork_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip132 = self - .raw_conf - .next_hardfork_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip132 = self - .raw_conf - .next_hardfork_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip133_b = self - .raw_conf - .next_hardfork_transition_number - .unwrap_or(default_transition_time); - if self.is_test_or_dev_mode() { - params.transition_numbers.cip43b = - self.raw_conf.cip43_init_end_number.unwrap_or(u64::MAX); - } else { - params.transition_numbers.cip43b = self - .raw_conf - .cip43_init_end_number - .unwrap_or(params.transition_numbers.cip43a); - } + let mut base_block_rewards = BTreeMap::new(); + base_block_rewards.insert(0, INITIAL_BASE_MINING_REWARD_IN_UCFX.into()); + base_block_rewards.insert( + params.transition_heights.cip40, + MINING_REWARD_TANZANITE_IN_UCFX.into(), + ); + params.base_block_rewards = base_block_rewards; + + // + // Hydra hardfork (V2.0) + // + set_conf!( + self.raw_conf.hydra_transition_number.unwrap_or(default_transition_time); + params.transition_numbers => { cip43a, cip64, cip71, cip78a, cip92 } + ); + set_conf!( + self.raw_conf.hydra_transition_height.unwrap_or(default_transition_time); + params.transition_heights => { cip76, cip86 } + ); + params.transition_numbers.cip43b = + self.raw_conf.cip43_init_end_number.unwrap_or( + if self.is_test_or_dev_mode() { + u64::MAX + } else { + params.transition_numbers.cip43a + }, + ); params.transition_numbers.cip62 = if self.is_test_or_dev_mode() { 0u64 } else { BN128_ENABLE_NUMBER }; - params.transition_numbers.cip64 = self - .raw_conf - .hydra_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip71 = self - .raw_conf - .hydra_transition_number - .unwrap_or(default_transition_time); - params.transition_numbers.cip78a = self - .raw_conf - .hydra_transition_number - .unwrap_or(default_transition_time); params.transition_numbers.cip78b = self .raw_conf .cip78_patch_transition_number .unwrap_or(params.transition_numbers.cip78a); + params.transition_heights.cip90a = self + .raw_conf + .cip90_transition_height + .or(self.raw_conf.hydra_transition_height) + .unwrap_or(default_transition_time); params.transition_numbers.cip90b = self .raw_conf .cip90_transition_number .or(self.raw_conf.hydra_transition_number) .unwrap_or(default_transition_time); - params.transition_numbers.cip92 = self - .raw_conf - .hydra_transition_number - .unwrap_or(default_transition_time); - params.transition_heights.cip76 = self + // + // DAO vote hardfork (V2.1) + // + set_conf!( + self.raw_conf.dao_vote_transition_number.unwrap_or(default_transition_time); + params.transition_numbers => { cip97, cip98 } + ); + params.transition_numbers.cip94n = self .raw_conf - .hydra_transition_height - .unwrap_or(default_transition_time); - params.transition_heights.cip86 = self + .dao_vote_transition_number + .unwrap_or(non_genesis_default_transition_time); + params.transition_heights.cip94h = self + .raw_conf + .dao_vote_transition_height + .unwrap_or(non_genesis_default_transition_time); + params.transition_numbers.cip105 = self .raw_conf - .hydra_transition_height + .cip105_transition_number + .or(self.raw_conf.dao_vote_transition_number) .unwrap_or(default_transition_time); - params.transition_heights.cip90a = self + + // + // Sigma protocol fix hardfork (V2.2) + // + params.transition_numbers.cip_sigma_fix = self .raw_conf - .cip90_transition_height - .or(self.raw_conf.hydra_transition_height) + .sigma_fix_transition_number .unwrap_or(default_transition_time); - params.transition_heights.cip94 = self + + // + // Burn collateral hardfork (V2.3) + // + params.transition_numbers.cip107 = self .raw_conf - .dao_vote_transition_height - .unwrap_or(non_genesis_default_transition_time); + .cip107_transition_number + .unwrap_or(default_transition_time); params.transition_heights.cip112 = *CIP112_TRANSITION_HEIGHT.get().expect("initialized"); - params.transition_heights.cip130 = self - .raw_conf - .next_hardfork_transition_height - .unwrap_or(default_transition_time); - params.transition_heights.cip133_e = self + params.transition_numbers.cip118 = self .raw_conf - .next_hardfork_transition_height + .cip118_transition_number .unwrap_or(default_transition_time); - params.transition_heights.cip1559 = self + params.transition_numbers.cip119 = self .raw_conf - .next_hardfork_transition_height + .cip119_transition_number .unwrap_or(default_transition_time); - params.params_dao_vote_period = self.raw_conf.params_dao_vote_period; - let mut base_block_rewards = BTreeMap::new(); - base_block_rewards.insert(0, INITIAL_BASE_MINING_REWARD_IN_UCFX.into()); - base_block_rewards.insert( - params.transition_heights.cip40, - MINING_REWARD_TANZANITE_IN_UCFX.into(), + // + // 1559 hardfork (V2.4) + // + set_conf!( + self.raw_conf.next_hardfork_transition_number.unwrap_or(default_transition_time); + params.transition_numbers => { cip131, cip132, cip133b, cip137 } + ); + set_conf!( + self.raw_conf.next_hardfork_transition_height.unwrap_or(default_transition_time); + params.transition_heights => { cip130, cip133e, cip1559 } ); - params.base_block_rewards = base_block_rewards; - - params - } - - pub fn node_type(&self) -> NodeType { - self.raw_conf.node_type.unwrap_or(NodeType::Full) - } - - pub fn pos_state_config(&self) -> PosStateConfig { - // The current implementation requires the round number to be an even - // number. - assert_eq!(self.raw_conf.pos_round_per_term % 2, 0); - PosStateConfig::new( - self.raw_conf.pos_round_per_term, - self.raw_conf.pos_term_max_size, - self.raw_conf.pos_term_elected_size, - self.raw_conf.pos_in_queue_locked_views, - self.raw_conf.pos_out_queue_locked_views, - self.raw_conf.pos_cip99_transition_view, - self.raw_conf.pos_cip99_in_queue_locked_views, - self.raw_conf.pos_cip99_out_queue_locked_views, - self.raw_conf.nonce_limit_transition_view, - 20_000, // 2 * 10^7 CFX - ) } } diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index aede1a8169..5be1316711 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -103,6 +103,7 @@ impl Receipt { storage_collateralized, storage_released, storage_sponsor_paid, + .. } = receipt; let (address, action, space) = match transaction.unsigned { diff --git a/crates/dbs/statedb/src/global_params.rs b/crates/dbs/statedb/src/global_params.rs index 30493fceae..2d39a7718b 100644 --- a/crates/dbs/statedb/src/global_params.rs +++ b/crates/dbs/statedb/src/global_params.rs @@ -145,4 +145,10 @@ impl GlobalParamKey for TotalBurnt1559 { const KEY: &'static [u8] = b"total_burnt_tokens_by_cip1559"; } -pub const TOTAL_GLOBAL_PARAMS: usize = TotalBurnt1559::ID + 1; +pub struct BaseFeeProp; +impl GlobalParamKey for BaseFeeProp { + const ID: usize = TotalBurnt1559::ID + 1; + const KEY: &'static [u8] = b"base_fee_prop"; +} + +pub const TOTAL_GLOBAL_PARAMS: usize = BaseFeeProp::ID + 1; diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index a24970934a..6ffae0137e 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -674,6 +674,7 @@ mod tests { storage_sponsor_paid: false, storage_collateralized: vec![], storage_released: vec![], + burnt_gas_fee: None, }; // 10 blocks with 10 empty receipts each @@ -723,6 +724,7 @@ mod tests { storage_sponsor_paid: false, storage_collateralized: vec![], storage_released: vec![], + burnt_gas_fee: None, }, Receipt { accumulated_gas_used: U256::zero(), @@ -752,6 +754,7 @@ mod tests { storage_sponsor_paid: false, storage_collateralized: vec![], storage_released: vec![], + burnt_gas_fee: None, }, ], block_number: 0, @@ -788,6 +791,7 @@ mod tests { storage_sponsor_paid: false, storage_collateralized: vec![], storage_released: vec![], + burnt_gas_fee: None, }], block_number: 0, secondary_reward: U256::zero(), diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 193579450d..9692542312 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -91,7 +91,7 @@ pub struct SortedStorageChanges { } /// Information describing execution of a transaction. -#[derive(Debug, Clone, PartialEq, Eq, RlpDecodable, RlpEncodable)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Receipt { /// The total gas used (not gas charged) in the block following execution /// of the transaction. @@ -110,6 +110,51 @@ pub struct Receipt { pub storage_sponsor_paid: bool, pub storage_collateralized: Vec, pub storage_released: Vec, + pub burnt_gas_fee: Option, +} + +impl Encodable for Receipt { + fn rlp_append(&self, s: &mut RlpStream) { + let length = if self.burnt_gas_fee.is_none() { 9 } else { 10 }; + s.begin_list(length) + .append(&self.accumulated_gas_used) + .append(&self.gas_fee) + .append(&self.gas_sponsor_paid) + .append(&self.log_bloom) + .append_list(&self.logs) + .append(&self.outcome_status) + .append(&self.storage_sponsor_paid) + .append_list(&self.storage_collateralized) + .append_list(&self.storage_released); + if let Some(burnt_gas_fee) = self.burnt_gas_fee { + s.append(&burnt_gas_fee); + } + } +} + +impl Decodable for Receipt { + fn decode(rlp: &Rlp) -> Result { + let item_count = rlp.item_count()?; + if !matches!(item_count, 9..=10) { + return Err(DecoderError::RlpIncorrectListLen); + } + Ok(Receipt { + accumulated_gas_used: rlp.val_at(0)?, + gas_fee: rlp.val_at(1)?, + gas_sponsor_paid: rlp.val_at(2)?, + log_bloom: rlp.val_at(3)?, + logs: rlp.list_at(4)?, + outcome_status: rlp.val_at(5)?, + storage_sponsor_paid: rlp.val_at(6)?, + storage_collateralized: rlp.list_at(7)?, + storage_released: rlp.list_at(8)?, + burnt_gas_fee: if item_count == 9 { + None + } else { + Some(rlp.val_at(9)?) + }, + }) + } } impl Receipt { @@ -117,7 +162,7 @@ impl Receipt { outcome: TransactionStatus, accumulated_gas_used: U256, gas_fee: U256, gas_sponsor_paid: bool, logs: Vec, log_bloom: Bloom, storage_sponsor_paid: bool, storage_collateralized: Vec, - storage_released: Vec, + storage_released: Vec, burnt_gas_fee: Option, ) -> Self { Self { accumulated_gas_used, @@ -129,6 +174,7 @@ impl Receipt { storage_sponsor_paid, storage_collateralized, storage_released, + burnt_gas_fee, } } From 64b1d2f58a0e336806d70080ce33ef5a91541a6c Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Sat, 11 May 2024 18:35:07 +0800 Subject: [PATCH 035/137] Fix test errors --- .../executor/src/internal_contract/impls/params_control.rs | 2 +- crates/client/src/configuration.rs | 7 +++---- tests/evm_space/eip1559_test.py | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs b/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs index 945126425b..56d226c732 100644 --- a/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs +++ b/crates/cfxcore/executor/src/internal_contract/impls/params_control.rs @@ -513,7 +513,7 @@ mod storage_key { pub fn votes( address: &Address, index: usize, opt_index: usize, ) -> [u8; 32] { - const TOPIC_OFFSET: [usize; 3] = [1, 2, 3]; + const TOPIC_OFFSET: [usize; 4] = [1, 2, 3, 4]; // Position of `votes` let base = U256::from(VOTES_SLOT); diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 126f7a964a..458b8abd3d 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -163,6 +163,7 @@ build_config! { (cip119_transition_number, (Option), None) (next_hardfork_transition_number, (Option), None) (next_hardfork_transition_height, (Option), None) + (cip1559_transition_height, (Option), None) (referee_bound, (usize), REFEREE_DEFAULT_BOUND) (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD) (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA) @@ -1391,10 +1392,8 @@ impl Configuration { params.transition_heights => { cip130, cip133e } ); // TODO: disable 1559 test during dev - params.transition_heights.cip1559 = self - .raw_conf - .next_hardfork_transition_height - .unwrap_or(default_transition_time); + params.transition_heights.cip1559 = + self.raw_conf.cip1559_transition_height.unwrap_or(u64::MAX); } } diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 5e91230225..7caed90a4b 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -68,6 +68,7 @@ def run_test(self): assert_equal(receipt["txExecErrorMsg"], None) tx = self.w3.eth.get_transaction(return_tx_hash) + print(tx) assert_equal(receipt["status"], 1) # Check if another node can decode EIP1559 transactions From a06c4c5b77484bf085e38bf5a995c517a1447eec Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Sat, 11 May 2024 17:42:26 +0800 Subject: [PATCH 036/137] Refactor process epoch transaction --- crates/cfx_types/src/lib.rs | 14 + .../consensus_executor/epoch_execution.rs | 577 +++++++++++++++ .../mod.rs} | 679 +++++------------- crates/cfxcore/core/src/genesis_block.rs | 3 +- .../src/internal_contract/contracts/mod.rs | 27 +- crates/cfxcore/executor/src/state/mod.rs | 3 +- .../executor/src/state/state_object/mod.rs | 1 + .../executor/src/state/state_object/reward.rs | 2 +- .../executor/src/state/state_object/tests.rs | 4 +- 9 files changed, 784 insertions(+), 526 deletions(-) create mode 100644 crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs rename crates/cfxcore/core/src/consensus/consensus_inner/{consensus_executor.rs => consensus_executor/mod.rs} (76%) diff --git a/crates/cfx_types/src/lib.rs b/crates/cfx_types/src/lib.rs index 3a9ae689d8..47c1ca65e6 100644 --- a/crates/cfx_types/src/lib.rs +++ b/crates/cfx_types/src/lib.rs @@ -8,6 +8,8 @@ extern crate rlp_derive; extern crate serde; extern crate serde_derive; +use std::ops::{Index, IndexMut}; + pub use ethereum_types::{ Address, BigEndianHash, Bloom, BloomInput, Public, Secret, Signature, H128, H160, H256, H512, H520, H64, U128, U256, U512, U64, @@ -160,6 +162,18 @@ impl SpaceMap { } } +impl Index for SpaceMap { + type Output = T; + + fn index(&self, space: Space) -> &Self::Output { self.in_space(space) } +} + +impl IndexMut for SpaceMap { + fn index_mut(&mut self, space: Space) -> &mut Self::Output { + self.in_space_mut(space) + } +} + impl serde::Serialize for SpaceMap { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs new file mode 100644 index 0000000000..c234aa9fca --- /dev/null +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs @@ -0,0 +1,577 @@ +use super::ConsensusExecutionHandler; +use std::{convert::From, sync::Arc}; + +use pow_types::StakingEvent; + +use cfx_statedb::{ErrorKind as DbErrorKind, Result as DbResult}; +use cfx_types::{Space, SpaceMap, H256, U256}; +use primitives::{ + receipt::BlockReceipts, Action, Block, BlockNumber, EpochId, Receipt, + SignedTransaction, TransactionIndex, +}; + +use crate::{ + block_data_manager::BlockDataManager, + consensus::consensus_inner::consensus_executor::GOOD_TPS_METER, + state_prefetcher::{prefetch_accounts, PrefetchTaskHandle}, +}; +use cfx_execute_helper::{ + exec_tracer::TransactionExecTraces, + observer::Observer, + tx_outcome::{make_process_tx_outcome, ProcessTxOutcome}, +}; +use cfx_executor::{ + executive::{ExecutiveContext, TransactOptions, TransactSettings}, + internal_contract::{ + block_hash_slot, epoch_hash_slot, initialize_internal_contract_accounts, + }, + state::{ + initialize_cip107, initialize_cip137, + initialize_or_update_dao_voted_params, State, + }, +}; +use cfx_vm_types::Env; + +impl ConsensusExecutionHandler { + pub(super) fn process_epoch_transactions( + &self, epoch_id: EpochId, state: &mut State, + epoch_blocks: &Vec>, start_block_number: u64, + on_local_pivot: bool, + ) -> DbResult>> { + self.prefetch_storage_for_execution(epoch_id, state, epoch_blocks); + + let pivot_block = epoch_blocks.last().expect("Epoch not empty"); + + self.before_epoch_execution(state, &*pivot_block)?; + + let base_gas_price = pivot_block + .block_header + .core_base_fee() + .cloned() + .unwrap_or_default(); + + let burnt_gas_price = state.burnt_gas_price(base_gas_price); + let context = EpochProcessContext { + on_local_pivot, + executive_trace: self.config.executive_trace, + pivot_block, + base_gas_price, + burnt_gas_price, + }; + + let mut epoch_recorder = EpochProcessRecorder::new(); + + let mut block_context = BlockProcessContext::first_block( + &context, + epoch_blocks.first().unwrap(), + start_block_number, + ); + + for (idx, block) in epoch_blocks.iter().enumerate() { + if idx > 0 { + block_context.next_block(block); + } + + self.process_block_transactions( + &block_context, + state, + &mut epoch_recorder, + )?; + } + + if self.pos_verifier.pos_option().is_some() { + debug!( + "put_staking_events: {:?} height={} len={}", + pivot_block.hash(), + pivot_block.block_header.height(), + epoch_recorder.staking_events.len() + ); + self.pos_verifier + .consensus_db() + .put_staking_events( + pivot_block.block_header.height(), + pivot_block.hash(), + epoch_recorder.staking_events, + ) + .map_err(|e| { + cfx_statedb::Error::from(DbErrorKind::PosDatabaseError( + format!("{:?}", e), + )) + })?; + } + + if on_local_pivot { + self.tx_pool.recycle_transactions(epoch_recorder.repack_tx); + } + + debug!("Finish processing tx for epoch"); + Ok(epoch_recorder.receipts) + } + + fn prefetch_storage_for_execution( + &self, epoch_id: EpochId, state: &mut State, + epoch_blocks: &Vec>, + ) { + // Prefetch accounts for transactions. + // The return value _prefetch_join_handles is used to join all threads + // before the exit of this function. + let prefetch_join_handles = match self + .execution_state_prefetcher + .as_ref() + { + Some(prefetcher) => { + let mut accounts = vec![]; + for block in epoch_blocks.iter() { + for transaction in block.transactions.iter() { + accounts.push(&transaction.sender); + match transaction.action() { + Action::Call(ref address) => accounts.push(address), + _ => {} + } + } + } + + prefetch_accounts(prefetcher, epoch_id, state, accounts) + } + None => PrefetchTaskHandle { + task_epoch_id: epoch_id, + state, + prefetcher: None, + accounts: vec![], + }, + }; + + // TODO: + // Make the state shared ref for vm execution, then remove this drop. + // When the state can be made shared, prefetch can happen at the same + // time of the execution, the vm execution do not have to wait + // for prefetching to finish. + prefetch_join_handles.wait_for_task(); + drop(prefetch_join_handles); + } + + fn make_block_env(&self, block_context: &BlockProcessContext) -> Env { + let BlockProcessContext { + epoch_context: + &EpochProcessContext { + pivot_block, + base_gas_price, + burnt_gas_price, + .. + }, + block, + block_number, + last_hash, + } = *block_context; + + let last_block_header = &self.data_man.block_header_by_hash(&last_hash); + + let pos_id = last_block_header + .as_ref() + .and_then(|header| header.pos_reference().as_ref()); + let pos_view_number = + pos_id.and_then(|id| self.pos_verifier.get_pos_view(id)); + let pivot_decision_epoch = pos_id + .and_then(|id| self.pos_verifier.get_pivot_decision(id)) + .and_then(|hash| self.data_man.block_header_by_hash(&hash)) + .map(|header| header.height()); + + let epoch_height = pivot_block.block_header.height(); + let chain_id = self.machine.params().chain_id_map(epoch_height); + Env { + chain_id, + number: block_number, + author: block.block_header.author().clone(), + timestamp: pivot_block.block_header.timestamp(), + difficulty: block.block_header.difficulty().clone(), + accumulated_gas_used: U256::zero(), + last_hash, + gas_limit: U256::from(block.block_header.gas_limit()), + epoch_height, + pos_view: pos_view_number, + finalized_epoch: pivot_decision_epoch, + transaction_epoch_bound: self + .verification_config + .transaction_epoch_bound, + base_gas_price, + burnt_gas_price, + } + } + + fn process_block_transactions( + &self, block_context: &BlockProcessContext, state: &mut State, + epoch_recorder: &mut EpochProcessRecorder, + ) -> DbResult<()> { + let BlockProcessContext { + epoch_context: &EpochProcessContext { on_local_pivot, .. }, + block, + block_number, + .. + } = *block_context; + + debug!( + "process txs in block: hash={:?}, tx count={:?}", + block.hash(), + block.transactions.len() + ); + + // TODO: ideally, this function should not have return value. + // However, the previous implementation read `secondary_reward` in an + // intermediate step. Since we are not sure which steps will influnce + // `secondary_reward`, we must `secondary_reward` at the same point to + // keep the backward compatible. + let secondary_reward = + self.before_block_execution(state, block_number, block)?; + + let mut env = self.make_block_env(block_context); + + let mut block_recorder = + BlockProcessRecorder::new(epoch_recorder.evm_tx_idx); + + for (idx, transaction) in block.transactions.iter().enumerate() { + self.process_transaction( + idx, + transaction, + block_context, + state, + &mut env, + on_local_pivot, + &mut block_recorder, + )?; + } + + block_recorder.finish_block( + &self.data_man, + epoch_recorder, + block_context, + secondary_reward, + ); + + Ok(()) + } + + fn process_transaction( + &self, idx: usize, transaction: &Arc, + block_context: &BlockProcessContext, state: &mut State, env: &mut Env, + on_local_pivot: bool, recorder: &mut BlockProcessRecorder, + ) -> DbResult<()> { + let rpc_index = recorder.tx_idx[transaction.space()]; + + let block = &block_context.block; + + let machine = self.machine.as_ref(); + + let spec = machine.spec(env.number, env.epoch_height); + let observer = if self.config.executive_trace { + Observer::with_tracing() + } else { + Observer::with_no_tracing() + }; + + let options = TransactOptions { + observer, + settings: TransactSettings::all_checks(), + }; + + let execution_outcome = + ExecutiveContext::new(state, env, machine, &spec) + .transact(transaction, options)?; + execution_outcome.log(transaction, &block_context.block.hash()); + + if let Some(burnt_fee) = execution_outcome + .try_as_executed() + .and_then(|e| e.burnt_fee) + { + state.burn_by_cip1559(burnt_fee); + }; + + let r = make_process_tx_outcome( + execution_outcome, + &mut env.accumulated_gas_used, + transaction.hash, + &spec, + ); + + if r.receipt.tx_success() { + GOOD_TPS_METER.mark(1); + } + + let tx_skipped = r.receipt.tx_skipped(); + let phantom_txs = r.phantom_txs.clone(); + + recorder.receive_tx_outcome(r, transaction, block_context); + + if !on_local_pivot || tx_skipped { + return Ok(()); + } + + let hash = transaction.hash(); + + self.data_man.insert_transaction_index( + &hash, + &TransactionIndex { + block_hash: block.hash(), + real_index: idx, + is_phantom: false, + rpc_index: Some(rpc_index), + }, + ); + + // persist tx index for phantom transactions. + // note: in some cases, pivot chain reorgs will result in + // different phantom txs (with different hashes) for the + // same Conflux space tx. we do not remove invalidated + // hashes here, but leave it up to the RPC layer to handle + // this instead. + let evm_chain_id = env.chain_id[&Space::Ethereum]; + let evm_tx_index = &mut recorder.tx_idx[Space::Ethereum]; + + for ptx in phantom_txs { + self.data_man.insert_transaction_index( + &ptx.into_eip155(evm_chain_id).hash(), + &TransactionIndex { + block_hash: block.hash(), + real_index: idx, + is_phantom: true, + rpc_index: Some(*evm_tx_index), + }, + ); + + *evm_tx_index += 1; + } + + Ok(()) + } + + fn before_epoch_execution( + &self, state: &mut State, pivot_block: &Block, + ) -> DbResult<()> { + let params = self.machine.params(); + + let epoch_number = pivot_block.block_header.height(); + let hash = pivot_block.hash(); + + if epoch_number >= params.transition_heights.cip133e { + state.set_system_storage( + epoch_hash_slot(epoch_number).into(), + U256::from_big_endian(&hash.0), + )?; + } + Ok(()) + } + + pub fn before_block_execution( + &self, state: &mut State, block_number: BlockNumber, block: &Block, + ) -> DbResult { + let params = self.machine.params(); + let transition_numbers = ¶ms.transition_numbers; + + let cip94_start = transition_numbers.cip94n; + let period = params.params_dao_vote_period; + // Update/initialize parameters before processing rewards. + if block_number >= cip94_start + && (block_number - cip94_start) % period == 0 + { + let set_pos_staking = block_number > transition_numbers.cip105; + initialize_or_update_dao_voted_params(state, set_pos_staking)?; + } + + // Initialize old_storage_point_prop_ratio in the state. + // The time may not be in the vote period boundary, so this is not + // integrated with `initialize_or_update_dao_voted_params`, but + // that function will update the value after cip107 is enabled + // here. + if block_number == transition_numbers.cip107 { + initialize_cip107(state)?; + } + + if block_number >= transition_numbers.cip133b { + state.set_system_storage( + block_hash_slot(block_number).into(), + U256::from_big_endian(&block.hash().0), + )?; + } + + if block_number == transition_numbers.cip137 { + initialize_cip137(state); + } + + if block_number < transition_numbers.cip43a { + state.bump_block_number_accumulate_interest(); + } + + let secondary_reward = state.secondary_reward(); + + state.inc_distributable_pos_interest(block_number)?; + + initialize_internal_contract_accounts( + state, + self.machine + .internal_contracts() + .initialized_at(block_number), + )?; + + Ok(secondary_reward) + } +} + +struct EpochProcessContext<'a> { + on_local_pivot: bool, + executive_trace: bool, + + pivot_block: &'a Block, + + base_gas_price: U256, + burnt_gas_price: U256, +} + +struct BlockProcessContext<'a, 'b> { + epoch_context: &'b EpochProcessContext<'a>, + block: &'b Block, + block_number: u64, + last_hash: H256, +} + +impl<'a, 'b> BlockProcessContext<'a, 'b> { + fn first_block( + epoch_context: &'b EpochProcessContext<'a>, block: &'b Block, + start_block_number: u64, + ) -> Self { + let EpochProcessContext { pivot_block, .. } = *epoch_context; + let last_hash = *pivot_block.block_header.parent_hash(); + Self { + epoch_context, + block, + block_number: start_block_number, + last_hash, + } + } + + fn next_block(&mut self, block: &'b Block) { + self.last_hash = self.block.hash(); + self.block_number += 1; + self.block = block; + } +} + +#[derive(Default)] +struct EpochProcessRecorder { + receipts: Vec>, + staking_events: Vec, + repack_tx: Vec>, + + evm_tx_idx: usize, +} + +impl EpochProcessRecorder { + fn new() -> Self { Default::default() } +} + +struct BlockProcessRecorder { + receipt: Vec, + tx_error_msg: Vec, + traces: Vec, + repack_tx: Vec>, + staking_events: Vec, + + tx_idx: SpaceMap, +} + +impl BlockProcessRecorder { + fn new(evm_tx_idx: usize) -> BlockProcessRecorder { + let mut tx_idx = SpaceMap::default(); + tx_idx[Space::Ethereum] = evm_tx_idx; + Self { + receipt: vec![], + tx_error_msg: vec![], + traces: vec![], + repack_tx: vec![], + staking_events: vec![], + tx_idx, + } + } + + fn receive_tx_outcome( + &mut self, r: ProcessTxOutcome, tx: &Arc, + block_context: &BlockProcessContext, + ) { + let EpochProcessContext { + on_local_pivot, + executive_trace, + .. + } = *block_context.epoch_context; + + if on_local_pivot && r.consider_repacked { + self.repack_tx.push(tx.clone()) + } + + let not_skipped = !r.receipt.tx_skipped(); + + if executive_trace { + self.traces.push(r.tx_traces.into()); + } + + self.receipt.push(r.receipt); + self.tx_error_msg.push(r.tx_exec_error_msg); + self.staking_events.extend(r.tx_staking_events); + + match tx.space() { + Space::Native => { + self.tx_idx[Space::Native] += 1; + } + Space::Ethereum if not_skipped => { + self.tx_idx[Space::Ethereum] += 1; + } + _ => {} + }; + } + + fn finish_block( + self, data_man: &BlockDataManager, + epoch_recorder: &mut EpochProcessRecorder, + block_context: &BlockProcessContext, secondary_reward: U256, + ) { + let BlockProcessContext { + epoch_context: + &EpochProcessContext { + on_local_pivot, + executive_trace, + pivot_block, + .. + }, + block, + block_number, + .. + } = *block_context; + + let block_receipts = Arc::new(BlockReceipts { + receipts: self.receipt, + // An existing bug makes the block_number is one larger than the + // actual. + block_number: block_number + 1, + secondary_reward, + tx_execution_error_messages: self.tx_error_msg, + }); + + epoch_recorder.receipts.push(block_receipts.clone()); + epoch_recorder.staking_events.extend(self.staking_events); + epoch_recorder.repack_tx.extend(self.repack_tx); + epoch_recorder.evm_tx_idx = self.tx_idx[Space::Ethereum]; + + if executive_trace { + data_man.insert_block_traces( + block.hash(), + self.traces.into(), + pivot_block.hash(), + on_local_pivot, + ); + } + + data_man.insert_block_execution_result( + block.hash(), + pivot_block.hash(), + block_receipts.clone(), + on_local_pivot, + ); + } +} diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs similarity index 76% rename from crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs rename to crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 1df9eab043..20964d775a 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -2,6 +2,8 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ +mod epoch_execution; + use core::convert::TryFrom; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, @@ -23,7 +25,7 @@ use cfx_internal_common::{ debug::*, EpochExecutionCommitment, StateRootWithAuxInfo, }; use cfx_parameters::consensus::*; -use cfx_statedb::{ErrorKind as DbErrorKind, Result as DbResult, StateDb}; +use cfx_statedb::{Result as DbResult, StateDb}; use cfx_storage::{ defaults::DEFAULT_EXECUTION_PREFETCH_THREADS, StateIndex, StorageManagerTrait, @@ -34,9 +36,8 @@ use cfx_types::{ }; use metrics::{register_meter_with_group, Meter, MeterTimer}; use primitives::{ - compute_block_number, receipt::BlockReceipts, Action, Block, - BlockHeaderBuilder, BlockNumber, EpochId, SignedTransaction, - TransactionIndex, MERKLE_NULL_NODE, + compute_block_number, receipt::BlockReceipts, Block, BlockHeader, + BlockHeaderBuilder, SignedTransaction, MERKLE_NULL_NODE, }; use crate::{ @@ -50,33 +51,22 @@ use crate::{ ConsensusGraphInner, }, rpc_errors::{invalid_params_check, Result as RpcResult}, - state_prefetcher::{ - prefetch_accounts, ExecutionStatePrefetcher, PrefetchTaskHandle, - }, + state_prefetcher::ExecutionStatePrefetcher, verification::{ compute_receipts_root, VerificationConfig, VerifyTxLocalMode, VerifyTxMode, }, SharedTransactionPool, }; -use cfx_execute_helper::{ - estimation::{EstimateExt, EstimateRequest, EstimationContext}, - exec_tracer::TransactionExecTraces, - observer::Observer, - tx_outcome::make_process_tx_outcome, +use cfx_execute_helper::estimation::{ + EstimateExt, EstimateRequest, EstimationContext, }; use cfx_executor::{ - executive::{ - ExecutionOutcome, ExecutiveContext, TransactOptions, TransactSettings, - }, - internal_contract::{ - block_hash_slot, epoch_hash_slot, initialize_internal_contract_accounts, - }, + executive::ExecutionOutcome, machine::Machine, state::{ - distribute_pos_interest, initialize_cip107, initialize_cip137, - initialize_or_update_dao_voted_params, update_pos_status, CleanupMode, - State, + distribute_pos_interest, update_pos_status, CleanupMode, State, + StateCommitResult, }, }; use cfx_vm_types::{Env, Spec}; @@ -618,13 +608,14 @@ impl ConsensusExecutor { debug_record: Option<&mut ComputeEpochDebugRecord>, recover_mpt_during_construct_pivot_state: bool, ) { - if !self.consensus_graph_bench_mode { - self.handler.handle_epoch_execution( - task, - debug_record, - recover_mpt_during_construct_pivot_state, - ) + if self.consensus_graph_bench_mode { + return; } + self.handler.handle_epoch_execution( + task, + debug_record, + recover_mpt_during_construct_pivot_state, + ) } pub fn epoch_executed_and_recovered( @@ -909,6 +900,41 @@ impl ConsensusExecutionHandler { .get_epoch_execution_commitment_with_db(epoch_hash) } + fn new_state( + &self, pivot_block: &Block, + recover_mpt_during_construct_pivot_state: bool, + ) -> DbResult { + let state_root_with_aux_info = &self + .data_man + .get_epoch_execution_commitment( + pivot_block.block_header.parent_hash(), + ) + // Unwrapping is safe because the state exists. + .unwrap() + .state_root_with_aux_info; + + let state_index = StateIndex::new_for_next_epoch( + pivot_block.block_header.parent_hash(), + &state_root_with_aux_info, + pivot_block.block_header.height() - 1, + self.data_man.get_snapshot_epoch_count(), + ); + + let storage = self + .data_man + .storage_manager + .get_state_for_next_epoch( + state_index, + recover_mpt_during_construct_pivot_state, + ) + .expect("No db error") + // Unwrapping is safe because the state exists. + .expect("State exists"); + + let state_db = StateDb::new(storage); + State::new(state_db) + } + pub fn epoch_executed_and_recovered( &self, epoch_hash: &H256, epoch_block_hashes: &Vec, on_local_pivot: bool, @@ -963,10 +989,10 @@ impl ConsensusExecutionHandler { // execution is skipped. when `compute_epoch` is called, it is // guaranteed that `epoch_hash` is on the current pivot chain. for (index, hash) in epoch_block_hashes.iter().enumerate() { - self.data_man.insert_hash_by_block_number( - compute_block_number(start_block_number, index as u64), - hash, - ); + let block_number = + compute_block_number(start_block_number, index as u64); + self.data_man + .insert_hash_by_block_number(block_number, hash); } let pivot_block_header = self @@ -974,7 +1000,7 @@ impl ConsensusExecutionHandler { .block_header_by_hash(epoch_hash) .expect("must exists"); - // Check if the state has been computed + // Check if epoch is computed if !force_recompute && debug_record.is_none() && self.epoch_executed_and_recovered( @@ -985,43 +1011,11 @@ impl ConsensusExecutionHandler { pivot_block_header.height(), ) { - if on_local_pivot { - // Unwrap is safe here because it's guaranteed by outer if. - let state_root = &self - .data_man - .get_epoch_execution_commitment(epoch_hash) - .unwrap() - .state_root_with_aux_info; - // When the state have expired, don't inform TransactionPool. - // TransactionPool doesn't require a precise best_executed_state - // when pivot chain oscillates. - if self - .data_man - .state_availability_boundary - .read() - .check_availability(pivot_block_header.height(), epoch_hash) - { - self.tx_pool - .set_best_executed_epoch(StateIndex::new_for_readonly( - epoch_hash, - &state_root, - )) - // FIXME: propogate error. - .expect(&concat!( - file!(), - ":", - line!(), - ":", - column!() - )); - } - } - self.data_man - .state_availability_boundary - .write() - .adjust_upper_bound(pivot_block_header.as_ref()); - debug!("Skip execution in prefix {:?}", epoch_hash); - + self.update_on_skipped_execution( + epoch_hash, + &pivot_block_header, + on_local_pivot, + ); return; } @@ -1041,30 +1035,9 @@ impl ConsensusExecutionHandler { epoch_blocks.len(), ); - let mut state = State::new(StateDb::new( - self.data_man - .storage_manager - .get_state_for_next_epoch( - StateIndex::new_for_next_epoch( - pivot_block.block_header.parent_hash(), - &self - .data_man - .get_epoch_execution_commitment( - pivot_block.block_header.parent_hash(), - ) - // Unwrapping is safe because the state exists. - .unwrap() - .state_root_with_aux_info, - pivot_block.block_header.height() - 1, - self.data_man.get_snapshot_epoch_count(), - ), - recover_mpt_during_construct_pivot_state, - ) - .expect("No db error") - // Unwrapping is safe because the state exists. - .expect("State exists"), - )) - .expect("Failed to initialize state"); + let mut state = self + .new_state(pivot_block, recover_mpt_during_construct_pivot_state) + .expect("Cannot init state"); let epoch_receipts = self .process_epoch_transactions( @@ -1082,6 +1055,9 @@ impl ConsensusExecutionHandler { start_block_number + epoch_receipts.len() as u64 - 1; if let Some(reward_execution_info) = reward_execution_info { + let spec = self + .machine + .spec(current_block_number, pivot_block.block_header.height()); // Calculate the block reward for blocks inside the epoch // All transaction fees are shared among blocks inside one epoch self.process_rewards_and_fees( @@ -1090,11 +1066,86 @@ impl ConsensusExecutionHandler { epoch_hash, on_local_pivot, debug_record.as_deref_mut(), - self.machine - .spec(current_block_number, pivot_block_header.height()), + spec, ); } + self.process_pos_interest( + &mut state, + pivot_block, + current_block_number, + ) + .expect("db error"); + + let commit_result = state + .commit(*epoch_hash, debug_record.as_deref_mut()) + .expect(&concat!(file!(), ":", line!(), ":", column!())); + + if on_local_pivot { + self.notify_txpool(&commit_result, epoch_hash); + }; + + self.data_man.insert_epoch_execution_commitment( + pivot_block.hash(), + commit_result.state_root.clone(), + compute_receipts_root(&epoch_receipts), + BlockHeaderBuilder::compute_block_logs_bloom_hash(&epoch_receipts), + ); + + let epoch_execution_commitment = self + .data_man + .get_epoch_execution_commitment(&epoch_hash) + .unwrap(); + debug!( + "compute_epoch: on_local_pivot={}, epoch={:?} state_root={:?} receipt_root={:?}, logs_bloom_hash={:?}", + on_local_pivot, epoch_hash, commit_result.state_root, epoch_execution_commitment.receipts_root, epoch_execution_commitment.logs_bloom_hash, + ); + self.data_man + .state_availability_boundary + .write() + .adjust_upper_bound(&pivot_block.block_header); + } + + fn update_on_skipped_execution( + &self, epoch_hash: &H256, pivot_block_header: &BlockHeader, + on_local_pivot: bool, + ) { + if on_local_pivot { + // Unwrap is safe here because it's guaranteed by outer if. + let state_root = &self + .data_man + .get_epoch_execution_commitment(epoch_hash) + .unwrap() + .state_root_with_aux_info; + // When the state have expired, don't inform TransactionPool. + // TransactionPool doesn't require a precise best_executed_state + // when pivot chain oscillates. + if self + .data_man + .state_availability_boundary + .read() + .check_availability(pivot_block_header.height(), epoch_hash) + { + self.tx_pool + .set_best_executed_epoch(StateIndex::new_for_readonly( + epoch_hash, + &state_root, + )) + // FIXME: propogate error. + .expect(&concat!(file!(), ":", line!(), ":", column!())); + } + } + self.data_man + .state_availability_boundary + .write() + .adjust_upper_bound(pivot_block_header); + debug!("Skip execution in prefix {:?}", epoch_hash); + } + + fn process_pos_interest( + &self, state: &mut State, pivot_block: &Block, + current_block_number: u64, + ) -> DbResult<()> { // TODO(peilun): Specify if we unlock before or after executing the // transactions. let maybe_parent_pos_ref = self @@ -1121,8 +1172,7 @@ impl ConsensusExecutionHandler { .get_unlock_nodes(current_pos_ref, parent_pos_ref) { debug!("unlock node: {:?} {}", unlock_node_id, votes); - update_pos_status(&mut state, unlock_node_id, votes) - .expect("db error"); + update_pos_status(state, unlock_node_id, votes)?; } if let Some((pos_epoch, reward_event)) = self .pos_verifier @@ -1133,348 +1183,44 @@ impl ConsensusExecutionHandler { debug!("distribute_pos_interest: {:?}", reward_event); let account_rewards: Vec<(H160, H256, U256)> = distribute_pos_interest( - &mut state, + state, reward_event.rewards(), current_block_number, - ) - .expect("db error"); + )?; self.data_man.insert_pos_reward( *pos_epoch, - &PosRewardInfo::new(account_rewards, *epoch_hash), + &PosRewardInfo::new(account_rewards, pivot_block.hash()), ) } } - - // FIXME: We may want to propagate the error up. - let state_root; - if on_local_pivot { - let commit_result = state - .commit(*epoch_hash, debug_record.as_deref_mut()) - .expect(&concat!(file!(), ":", line!(), ":", column!())); - state_root = commit_result.state_root; - let accounts_for_txpool = commit_result.accounts_for_txpool; - { - debug!("Notify epoch[{}]", epoch_hash); - - // TODO: use channel to deliver the message. - let txpool_clone = self.tx_pool.clone(); - std::thread::Builder::new() - .name("txpool_update_state".into()) - .spawn(move || { - txpool_clone - .notify_modified_accounts(accounts_for_txpool); - }) - .expect("can not notify tx pool to start state"); - } - - self.tx_pool - .set_best_executed_epoch(StateIndex::new_for_readonly( - epoch_hash, - &state_root, - )) - .expect(&concat!(file!(), ":", line!(), ":", column!())); - } else { - let commit_result = state - .commit(*epoch_hash, debug_record) - .expect(&concat!(file!(), ":", line!(), ":", column!())); - state_root = commit_result.state_root; - }; - - self.data_man.insert_epoch_execution_commitment( - pivot_block.hash(), - state_root.clone(), - compute_receipts_root(&epoch_receipts), - BlockHeaderBuilder::compute_block_logs_bloom_hash(&epoch_receipts), - ); - - let epoch_execution_commitment = self - .data_man - .get_epoch_execution_commitment(&epoch_hash) - .unwrap(); - debug!( - "compute_epoch: on_local_pivot={}, epoch={:?} state_root={:?} receipt_root={:?}, logs_bloom_hash={:?}", - on_local_pivot, epoch_hash, state_root, epoch_execution_commitment.receipts_root, epoch_execution_commitment.logs_bloom_hash, - ); - self.data_man - .state_availability_boundary - .write() - .adjust_upper_bound(&pivot_block.block_header); + Ok(()) } - fn prefetch_storage_for_execution( - &self, epoch_id: EpochId, state: &mut State, - epoch_blocks: &Vec>, + fn notify_txpool( + &self, commit_result: &StateCommitResult, epoch_hash: &H256, ) { - // Prefetch accounts for transactions. - // The return value _prefetch_join_handles is used to join all threads - // before the exit of this function. - let prefetch_join_handles = match self - .execution_state_prefetcher - .as_ref() - { - Some(prefetcher) => { - let mut accounts = vec![]; - for block in epoch_blocks.iter() { - for transaction in block.transactions.iter() { - accounts.push(&transaction.sender); - match transaction.action() { - Action::Call(ref address) => accounts.push(address), - _ => {} - } - } - } - - prefetch_accounts(prefetcher, epoch_id, state, accounts) - } - None => PrefetchTaskHandle { - task_epoch_id: epoch_id, - state, - prefetcher: None, - accounts: vec![], - }, - }; - - // TODO: - // Make the state shared ref for vm execution, then remove this drop. - // When the state can be made shared, prefetch can happen at the same - // time of the execution, the vm execution do not have to wait - // for prefetching to finish. - prefetch_join_handles.wait_for_task(); - drop(prefetch_join_handles); - } - - fn process_epoch_transactions( - &self, epoch_id: EpochId, state: &mut State, - epoch_blocks: &Vec>, start_block_number: u64, - on_local_pivot: bool, - ) -> DbResult>> { - self.prefetch_storage_for_execution(epoch_id, state, epoch_blocks); - - let pivot_block = epoch_blocks.last().expect("Epoch not empty"); - self.before_epoch_execution(state, &*pivot_block)?; - let base_gas_price = todo_base_price(); - let burnt_base_price = state.burnt_base_fee(base_gas_price); - - let mut epoch_receipts = Vec::with_capacity(epoch_blocks.len()); - let mut epoch_staking_events = Vec::new(); - let mut to_pending = Vec::new(); - let mut block_number = start_block_number; - let mut last_block_hash = - pivot_block.block_header.parent_hash().clone(); - let last_block_header = - &self.data_man.block_header_by_hash(&last_block_hash); - - let mut evm_tx_index = 0; - - for block in epoch_blocks.iter() { - self.before_block_execution(state, block_number, &*block)?; - - let mut cfx_tx_index = 0; - - let mut tx_exec_error_messages = - Vec::with_capacity(block.transactions.len()); - let mut receipts = Vec::new(); - debug!( - "process txs in block: hash={:?}, tx count={:?}", - block.hash(), - block.transactions.len() - ); - - let pos_id = last_block_header - .as_ref() - .and_then(|header| header.pos_reference().as_ref()); - let pos_view_number = - pos_id.and_then(|id| self.pos_verifier.get_pos_view(id)); - let pivot_decision_epoch = pos_id - .and_then(|id| self.pos_verifier.get_pivot_decision(id)) - .and_then(|hash| self.data_man.block_header_by_hash(&hash)) - .map(|header| header.height()); - - let epoch_height = pivot_block.block_header.height(); - let chain_id = self.machine.params().chain_id_map(epoch_height); - let mut env = Env { - chain_id, - number: block_number, - author: block.block_header.author().clone(), - timestamp: pivot_block.block_header.timestamp(), - difficulty: block.block_header.difficulty().clone(), - accumulated_gas_used: U256::zero(), - last_hash: last_block_hash, - gas_limit: U256::from(block.block_header.gas_limit()), - epoch_height, - pos_view: pos_view_number, - finalized_epoch: pivot_decision_epoch, - transaction_epoch_bound: self - .verification_config - .transaction_epoch_bound, - base_gas_price, - burnt_gas_price: burnt_base_price, - }; - let spec = self.machine.spec(env.number, env.epoch_height); - if !spec.cip43_contract { - state.bump_block_number_accumulate_interest(); - } - let machine = self.machine.as_ref(); - - let secondary_reward = state.secondary_reward(); - state.inc_distributable_pos_interest(env.number)?; - initialize_internal_contract_accounts( - state, - self.machine.internal_contracts().initialized_at(env.number), - ); - block_number += 1; - - last_block_hash = block.hash(); - let mut block_traces: Vec = - Default::default(); - for (idx, transaction) in block.transactions.iter().enumerate() { - let observer = if self.config.executive_trace { - Observer::with_tracing() - } else { - Observer::with_no_tracing() - }; - let options = TransactOptions { - observer, - settings: TransactSettings::all_checks(), - }; - let execution_outcome = - ExecutiveContext::new(state, &env, machine, &spec) - .transact(transaction, options)?; - execution_outcome.log(transaction, &block.hash()); - - if let Some(burnt_fee) = execution_outcome - .try_as_executed() - .and_then(|e| e.burnt_fee) - { - state.burn_by_cip1559(burnt_fee); - }; - - let r = make_process_tx_outcome( - execution_outcome, - &mut env.accumulated_gas_used, - transaction.hash, - &spec, - ); - - if r.receipt.tx_success() { - GOOD_TPS_METER.mark(1); - } - - if on_local_pivot && r.consider_repacked { - to_pending.push(transaction.clone()) - } - - let not_skipped = !r.receipt.tx_skipped(); - - if self.config.executive_trace { - block_traces.push(r.tx_traces.into()); - } - - receipts.push(r.receipt); - tx_exec_error_messages.push(r.tx_exec_error_msg); - epoch_staking_events.extend(r.tx_staking_events); - - let get_and_bump = |index: &mut usize| { - let output = *index; - *index += 1; - output - }; - let rpc_index = match transaction.space() { - Space::Native => get_and_bump(&mut cfx_tx_index), - Space::Ethereum if not_skipped => { - get_and_bump(&mut evm_tx_index) - } - _ => usize::MAX, // this will not be used - }; - - if on_local_pivot && not_skipped { - let hash = transaction.hash(); - - self.data_man.insert_transaction_index( - &hash, - &TransactionIndex { - block_hash: block.hash(), - real_index: idx, - is_phantom: false, - rpc_index: Some(rpc_index), - }, - ); - - let evm_chain_id = env.chain_id[&Space::Ethereum]; - - // persist tx index for phantom transactions. - // note: in some cases, pivot chain reorgs will result in - // different phantom txs (with different hashes) for the - // same Conflux space tx. we do not remove invalidated - // hashes here, but leave it up to the RPC layer to handle - // this instead. - for ptx in r.phantom_txs { - self.data_man.insert_transaction_index( - &ptx.into_eip155(evm_chain_id).hash(), - &TransactionIndex { - block_hash: block.hash(), - real_index: idx, - is_phantom: true, - rpc_index: Some(evm_tx_index), - }, - ); - - evm_tx_index += 1; - } - } - } - - if self.config.executive_trace { - self.data_man.insert_block_traces( - block.hash(), - block_traces.into(), - pivot_block.hash(), - on_local_pivot, - ); - } - - let block_receipts = Arc::new(BlockReceipts { - receipts, - block_number, - secondary_reward, - tx_execution_error_messages: tx_exec_error_messages, - }); - self.data_man.insert_block_execution_result( - block.hash(), - pivot_block.hash(), - block_receipts.clone(), - on_local_pivot, - ); - - epoch_receipts.push(block_receipts); - } - if self.pos_verifier.pos_option().is_some() { - debug!( - "put_staking_events: {:?} height={} len={}", - pivot_block.hash(), - pivot_block.block_header.height(), - epoch_staking_events.len() - ); - self.pos_verifier - .consensus_db() - .put_staking_events( - pivot_block.block_header.height(), - pivot_block.hash(), - epoch_staking_events, - ) - .map_err(|e| { - cfx_statedb::Error::from(DbErrorKind::PosDatabaseError( - format!("{:?}", e), - )) - })?; - } + // FIXME: We may want to propagate the error up. - if on_local_pivot { - self.tx_pool.recycle_transactions(to_pending); + let accounts_for_txpool = commit_result.accounts_for_txpool.clone(); + { + debug!("Notify epoch[{}]", epoch_hash); + + // TODO: use channel to deliver the message. + let txpool_clone = self.tx_pool.clone(); + std::thread::Builder::new() + .name("txpool_update_state".into()) + .spawn(move || { + txpool_clone.notify_modified_accounts(accounts_for_txpool); + }) + .expect("can not notify tx pool to start state"); } - debug!("Finish processing tx for epoch"); - Ok(epoch_receipts) + self.tx_pool + .set_best_executed_epoch(StateIndex::new_for_readonly( + epoch_hash, + &commit_result.state_root, + )) + .expect(&concat!(file!(), ":", line!(), ":", column!())); } fn compute_block_base_reward( @@ -1812,29 +1558,7 @@ impl ConsensusExecutionHandler { epoch_blocks.len(), ); let pivot_block = epoch_blocks.last().expect("Not empty"); - let mut state = State::new(StateDb::new( - self.data_man - .storage_manager - .get_state_for_next_epoch( - StateIndex::new_for_next_epoch( - pivot_block.block_header.parent_hash(), - &self - .data_man - .get_epoch_execution_commitment( - pivot_block.block_header.parent_hash(), - ) - // Unwrapping is safe because the state exists. - .unwrap() - .state_root_with_aux_info, - pivot_block.block_header.height() - 1, - self.data_man.get_snapshot_epoch_count(), - ), - false, - ) - .unwrap() - // Unwrapping is safe because the state exists. - .unwrap(), - ))?; + let mut state = self.new_state(&pivot_block, false)?; self.process_epoch_transactions( *pivot_hash, &mut state, @@ -1950,65 +1674,8 @@ impl ConsensusExecutionHandler { trace!("Execution result {:?}", r); Ok(r?) } - - fn before_block_execution( - &self, state: &mut State, block_number: BlockNumber, block: &Block, - ) -> DbResult<()> { - let params = self.machine.params(); - let transition_numbers = ¶ms.transition_numbers; - - let cip94_start = transition_numbers.cip94n; - let period = params.params_dao_vote_period; - // Update/initialize parameters before processing rewards. - if block_number >= cip94_start - && (block_number - cip94_start) % period == 0 - { - let set_pos_staking = block_number > transition_numbers.cip105; - initialize_or_update_dao_voted_params(state, set_pos_staking)?; - } - - // Initialize old_storage_point_prop_ratio in the state. - // The time may not be in the vote period boundary, so this is not - // integrated with `initialize_or_update_dao_voted_params`, but - // that function will update the value after cip107 is enabled - // here. - if block_number == transition_numbers.cip107 { - initialize_cip107(state)?; - } - - if block_number >= transition_numbers.cip133b { - state.set_system_storage( - block_hash_slot(block_number).into(), - U256::from_big_endian(&block.hash().0), - )?; - } - - if block_number == transition_numbers.cip137 { - initialize_cip137(state); - } - Ok(()) - } - - fn before_epoch_execution( - &self, state: &mut State, pivot_block: &Block, - ) -> DbResult<()> { - let params = self.machine.params(); - - let epoch_number = pivot_block.block_header.height(); - let hash = pivot_block.hash(); - - if epoch_number >= params.transition_heights.cip133e { - state.set_system_storage( - epoch_hash_slot(epoch_number).into(), - U256::from_big_endian(&hash.0), - )?; - } - Ok(()) - } } pub struct ConsensusExecutionConfiguration { pub executive_trace: bool, } - -fn todo_base_price() -> U256 { U256::zero() } diff --git a/crates/cfxcore/core/src/genesis_block.rs b/crates/cfxcore/core/src/genesis_block.rs index e5d7c479bd..8998f5bdd4 100644 --- a/crates/cfxcore/core/src/genesis_block.rs +++ b/crates/cfxcore/core/src/genesis_block.rs @@ -117,7 +117,8 @@ pub fn genesis_block( initialize_internal_contract_accounts( &mut state, machine.internal_contracts().initialized_at_genesis(), - ); + ) + .expect("no db error"); trace!("genesis_accounts: {:?}", genesis_accounts); for (addr, balance) in genesis_accounts { state diff --git a/crates/cfxcore/executor/src/internal_contract/contracts/mod.rs b/crates/cfxcore/executor/src/internal_contract/contracts/mod.rs index 86b16f7e5c..78722ee5d3 100644 --- a/crates/cfxcore/executor/src/internal_contract/contracts/mod.rs +++ b/crates/cfxcore/executor/src/internal_contract/contracts/mod.rs @@ -70,20 +70,15 @@ use primitives::storage::STORAGE_LAYOUT_REGULAR_V0; pub fn initialize_internal_contract_accounts( state: &mut State, addresses: &[Address], -) { - || -> DbResult<()> { - { - for address in addresses { - state.new_contract_with_admin( - &address.with_native_space(), - /* No admin; admin = */ &Address::zero(), - /* balance = */ U256::zero(), - Some(STORAGE_LAYOUT_REGULAR_V0), - false, - )?; - } - Ok(()) - } - }() - .expect(&concat!(file!(), ":", line!(), ":", column!())); +) -> DbResult<()> { + for address in addresses { + state.new_contract_with_admin( + &address.with_native_space(), + /* No admin; admin = */ &Address::zero(), + /* balance = */ U256::zero(), + Some(STORAGE_LAYOUT_REGULAR_V0), + false, + )?; + } + Ok(()) } diff --git a/crates/cfxcore/executor/src/state/mod.rs b/crates/cfxcore/executor/src/state/mod.rs index 8633f714b2..d6d012989b 100644 --- a/crates/cfxcore/executor/src/state/mod.rs +++ b/crates/cfxcore/executor/src/state/mod.rs @@ -23,7 +23,8 @@ pub use state_object::get_state_for_genesis_write; pub use state_object::{ distribute_pos_interest, initialize_cip107, initialize_cip137, initialize_or_update_dao_voted_params, settle_collateral_for_all, - update_pos_status, State, COMMISSION_PRIVILEGE_SPECIAL_KEY, + update_pos_status, State, StateCommitResult, + COMMISSION_PRIVILEGE_SPECIAL_KEY, }; use cfx_types::AddressWithSpace; diff --git a/crates/cfxcore/executor/src/state/state_object/mod.rs b/crates/cfxcore/executor/src/state/state_object/mod.rs index aabce5e968..a6b760c774 100644 --- a/crates/cfxcore/executor/src/state/state_object/mod.rs +++ b/crates/cfxcore/executor/src/state/state_object/mod.rs @@ -46,6 +46,7 @@ mod tests; pub use self::{ collateral::{initialize_cip107, settle_collateral_for_all}, + commit::StateCommitResult, pos::{distribute_pos_interest, update_pos_status}, reward::initialize_cip137, sponsor::COMMISSION_PRIVILEGE_SPECIAL_KEY, diff --git a/crates/cfxcore/executor/src/state/state_object/reward.rs b/crates/cfxcore/executor/src/state/state_object/reward.rs index 248208ac0f..625347e46b 100644 --- a/crates/cfxcore/executor/src/state/state_object/reward.rs +++ b/crates/cfxcore/executor/src/state/state_object/reward.rs @@ -62,7 +62,7 @@ impl State { *self.global_stat.val::() = val; } - pub fn burnt_base_fee(&self, base_fee: U256) -> U256 { + pub fn burnt_gas_price(&self, base_fee: U256) -> U256 { if base_fee.is_zero() { return U256::zero(); } diff --git a/crates/cfxcore/executor/src/state/state_object/tests.rs b/crates/cfxcore/executor/src/state/state_object/tests.rs index 133db72d4a..a7e13362be 100644 --- a/crates/cfxcore/executor/src/state/state_object/tests.rs +++ b/crates/cfxcore/executor/src/state/state_object/tests.rs @@ -39,6 +39,7 @@ fn get_state( .expect("Failed to initialize state") } +#[cfg(test)] pub fn get_state_for_genesis_write( storage_manager: &Arc, ) -> State { @@ -49,7 +50,8 @@ pub fn get_state_for_genesis_write( initialize_internal_contract_accounts( &mut state, InternalContractMap::initialize_for_test().as_slice(), - ); + ) + .expect("no db error"); let genesis_epoch_id = EpochId::default(); state .commit(genesis_epoch_id, /* debug_record = */ None) From 339ae11289122e92624fb5c2d3732bba1f1fd07f Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Sat, 11 May 2024 19:08:09 +0800 Subject: [PATCH 037/137] Add test. --- .../types/src/term_state/pos_state_config.rs | 27 +++++++++++++++++-- tests/pos/retire_param_hard_fork_test.py | 15 +++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs index 634a03fa05..5ac9c3335f 100644 --- a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs +++ b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs @@ -169,9 +169,32 @@ impl PosStateConfigTrait for OnceCell { } } - fn get_term_view(&self, view: u64) -> (u64, u64) { todo!() } + fn get_term_view(&self, view: u64) -> (u64, u64) { + let conf = self.get().unwrap(); + if view < conf.cip136_transition_view { + (view / conf.round_per_term, view % conf.round_per_term) + } else { + let transition_term = + conf.cip136_transition_view / conf.round_per_term; + let view_after = view - conf.cip136_transition_view; + ( + transition_term + view_after / conf.cip136_round_per_term, + view_after % conf.cip136_round_per_term, + ) + } + } - fn get_starting_view_for_term(&self, term: u64) -> Option { todo!() } + fn get_starting_view_for_term(&self, term: u64) -> Option { + let conf = self.get().unwrap(); + let transition_term = conf.cip136_transition_view / conf.round_per_term; + if term < transition_term { + Some(term * conf.round_per_term) + } else { + (term - transition_term) + .checked_mul(conf.cip136_round_per_term) + .map(|v| v + conf.cip136_transition_view) + } + } } pub static POS_STATE_CONFIG: OnceCell = OnceCell::new(); diff --git a/tests/pos/retire_param_hard_fork_test.py b/tests/pos/retire_param_hard_fork_test.py index aebe59ef50..689eb18e6f 100755 --- a/tests/pos/retire_param_hard_fork_test.py +++ b/tests/pos/retire_param_hard_fork_test.py @@ -44,6 +44,10 @@ def set_test_params(self): self.conf_parameters["pos_cip99_transition_view"] = 72 self.conf_parameters["pos_cip99_in_queue_locked_views"] = 66 self.conf_parameters["pos_cip99_out_queue_locked_views"] = 6 + self.conf_parameters["pos_cip136_transition_view"] = 144 + self.conf_parameters["pos_cip136_round_per_term"] = 12 + self.conf_parameters["pos_cip136_in_queue_locked_views"] = 132 + self.conf_parameters["pos_cip136_out_queue_locked_views"] = 12 self.rpc_timewait = 6000 def run_test(self): @@ -86,6 +90,17 @@ def wait(): print(new_epoch, old_epoch) assert_greater_than_or_equal(new_epoch - old_epoch, 3) + wait_until(lambda: int(client.pos_status()["latestCommitted"], 0) > self.conf_parameters["pos_cip136_transition_view"], timeout=120) + old_epoch = int(client.pos_status()["epoch"], 0) + check_view = self.conf_parameters["pos_cip136_transition_view"] + self.conf_parameters["pos_cip136_round_per_term"] + wait_until(lambda: int(client.pos_status()["latestCommitted"], 0) > check_view, timeout=120) + status = client.pos_status() + assert_greater_than(check_view + self.conf_parameters["pos_cip136_round_per_term"], int(status["latestCommitted"], 0)) + assert_equal(int(status["epoch"], 0), old_epoch + 1) + wait_until(lambda: int(client.pos_status()["latestCommitted"], 0) >= check_view + 7 * self.conf_parameters["pos_cip136_round_per_term"], timeout=240) + status = client.pos_status() + assert_equal(int(status["epoch"], 0), old_epoch + 7) + if __name__ == '__main__': RetireParamHardforkTest().main() From 922d042cdc2828fd7166154e4343f60a274702ee Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Sat, 11 May 2024 19:46:10 +0800 Subject: [PATCH 038/137] Add test. --- tests/pos/retire_param_hard_fork_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pos/retire_param_hard_fork_test.py b/tests/pos/retire_param_hard_fork_test.py index 689eb18e6f..ffab85c3ea 100755 --- a/tests/pos/retire_param_hard_fork_test.py +++ b/tests/pos/retire_param_hard_fork_test.py @@ -97,9 +97,9 @@ def wait(): status = client.pos_status() assert_greater_than(check_view + self.conf_parameters["pos_cip136_round_per_term"], int(status["latestCommitted"], 0)) assert_equal(int(status["epoch"], 0), old_epoch + 1) - wait_until(lambda: int(client.pos_status()["latestCommitted"], 0) >= check_view + 7 * self.conf_parameters["pos_cip136_round_per_term"], timeout=240) + wait_until(lambda: int(client.pos_status()["latestCommitted"], 0) > check_view + 7 * self.conf_parameters["pos_cip136_round_per_term"], timeout=240) status = client.pos_status() - assert_equal(int(status["epoch"], 0), old_epoch + 7) + assert_equal(int(status["epoch"], 0), old_epoch + 8) if __name__ == '__main__': From 5a7c6df65d07389a296fa4c709638bb5c6620c8e Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Mon, 13 May 2024 14:41:16 +0800 Subject: [PATCH 039/137] Update rocksdb to fix MacOS. --- Cargo.lock | 6 +++--- crates/cfxcore/core/src/pos/storage/schemadb/Cargo.toml | 2 +- crates/dbs/kvdb-rocksdb/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2fa89fc7a..3c5c17f546 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4084,7 +4084,7 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "librocksdb_sys" version = "0.1.0" -source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=a1ce5bd3322a7b732dfb300c2571dc4d99f1edae#a1ce5bd3322a7b732dfb300c2571dc4d99f1edae" +source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=3773afe5b953997188f37c39308105b5deb0faac#3773afe5b953997188f37c39308105b5deb0faac" dependencies = [ "bindgen", "bzip2-sys", @@ -4101,7 +4101,7 @@ dependencies = [ [[package]] name = "libtitan_sys" version = "0.0.1" -source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=a1ce5bd3322a7b732dfb300c2571dc4d99f1edae#a1ce5bd3322a7b732dfb300c2571dc4d99f1edae" +source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=3773afe5b953997188f37c39308105b5deb0faac#3773afe5b953997188f37c39308105b5deb0faac" dependencies = [ "bzip2-sys", "cc", @@ -6055,7 +6055,7 @@ dependencies = [ [[package]] name = "rocksdb" version = "0.3.0" -source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=a1ce5bd3322a7b732dfb300c2571dc4d99f1edae#a1ce5bd3322a7b732dfb300c2571dc4d99f1edae" +source = "git+https://github.com/Conflux-Chain/rust-rocksdb.git?rev=3773afe5b953997188f37c39308105b5deb0faac#3773afe5b953997188f37c39308105b5deb0faac" dependencies = [ "libc", "librocksdb_sys", diff --git a/crates/cfxcore/core/src/pos/storage/schemadb/Cargo.toml b/crates/cfxcore/core/src/pos/storage/schemadb/Cargo.toml index 3cb234662c..ebe14b34f6 100644 --- a/crates/cfxcore/core/src/pos/storage/schemadb/Cargo.toml +++ b/crates/cfxcore/core/src/pos/storage/schemadb/Cargo.toml @@ -18,7 +18,7 @@ diem-metrics = { path = "../../common/metrics" } [dependencies.rocksdb] git = "https://github.com/Conflux-Chain/rust-rocksdb.git" -rev = "a1ce5bd3322a7b732dfb300c2571dc4d99f1edae" +rev = "3773afe5b953997188f37c39308105b5deb0faac" [dev-dependencies] byteorder = "1.4.3" diff --git a/crates/dbs/kvdb-rocksdb/Cargo.toml b/crates/dbs/kvdb-rocksdb/Cargo.toml index affb1e4b74..fb2eb53475 100644 --- a/crates/dbs/kvdb-rocksdb/Cargo.toml +++ b/crates/dbs/kvdb-rocksdb/Cargo.toml @@ -27,4 +27,4 @@ tempdir = "0.3.7" [dependencies.rocksdb] git = "https://github.com/Conflux-Chain/rust-rocksdb.git" -rev = "a1ce5bd3322a7b732dfb300c2571dc4d99f1edae" +rev = "3773afe5b953997188f37c39308105b5deb0faac" From 8c6d8eedb1876e18f92aa7da694c7237ccd0abd6 Mon Sep 17 00:00:00 2001 From: Peilun Li <48905552+peilun-conflux@users.noreply.github.com> Date: Mon, 13 May 2024 14:45:48 +0800 Subject: [PATCH 040/137] Fix unneeded retire period change. --- .../core/src/pos/types/src/term_state/pos_state_config.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs index 5ac9c3335f..3104cac8c2 100644 --- a/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs +++ b/crates/cfxcore/core/src/pos/types/src/term_state/pos_state_config.rs @@ -8,7 +8,6 @@ use crate::{ use diem_crypto::_once_cell::sync::OnceCell; const CIP99_FORCE_RETIRE_EPOCH_COUNT: u64 = 3; -const CIP136_FORCE_RETIRE_EPOCH_COUNT: u64 = 6; #[derive(Clone, Debug)] pub struct PosStateConfig { @@ -147,14 +146,10 @@ impl PosStateConfigTrait for OnceCell { fn force_retire_check_epoch_count(&self, view: u64) -> u64 { let conf = self.get().unwrap(); - if view >= conf.cip99_transition_view - && view < conf.cip136_transition_view - { + if view >= conf.cip99_transition_view { // This is set according to the value of `TERM_LIST_LEN`. // Since `TERM_LIST_LEN` is hardcoded, we do not parameterize this. CIP99_FORCE_RETIRE_EPOCH_COUNT - } else if view > conf.cip136_transition_view { - CIP136_FORCE_RETIRE_EPOCH_COUNT } else { 1 } From c2c27fad45cdd2884ad5fb60ccfee075c11b2111 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 13 May 2024 19:31:23 +0800 Subject: [PATCH 041/137] Implement EIP-1559 block check & Pack transactions --- crates/cfx_types/src/lib.rs | 26 ++- crates/cfxcore/core/src/error.rs | 6 +- .../core/src/sync/synchronization_graph.rs | 10 ++ .../cfxcore/core/src/transaction_pool/mod.rs | 23 ++- .../transaction_pool_inner.rs | 150 +++++++++++++++++- crates/cfxcore/core/src/verification.rs | 134 +++++++++++++--- .../cfxcore/packing-pool/src/packing_batch.rs | 1 + crates/cfxcore/packing-pool/src/pool.rs | 60 +++++++ crates/cfxcore/packing-pool/src/weight.rs | 5 +- crates/cfxcore/parameters/src/lib.rs | 12 +- crates/primitives/src/block_header.rs | 121 ++++++++++---- 11 files changed, 482 insertions(+), 66 deletions(-) diff --git a/crates/cfx_types/src/lib.rs b/crates/cfx_types/src/lib.rs index 47c1ca65e6..a174e96506 100644 --- a/crates/cfx_types/src/lib.rs +++ b/crates/cfx_types/src/lib.rs @@ -8,7 +8,7 @@ extern crate rlp_derive; extern crate serde; extern crate serde_derive; -use std::ops::{Index, IndexMut}; +use std::ops::{Add, Index, IndexMut}; pub use ethereum_types::{ Address, BigEndianHash, Bloom, BloomInput, Public, Secret, Signature, H128, @@ -121,14 +121,14 @@ impl AddressWithSpace { pub fn assert_native(&self) { assert_eq!(self.space, Space::Native) } } -#[derive(Default, Clone)] +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] pub struct SpaceMap { native: T, evm: T, } impl SpaceMap { - pub fn new(native: T, evm: T) -> Self { SpaceMap { native, evm } } + pub const fn new(native: T, evm: T) -> Self { SpaceMap { native, evm } } #[inline] pub fn in_space(&self, space: Space) -> &T { @@ -146,12 +146,30 @@ impl SpaceMap { } } - pub fn map_sum usize>(&self, mut f: F) -> usize { + pub fn zip4( + a: SpaceMap, b: SpaceMap, c: SpaceMap, d: SpaceMap, + ) -> SpaceMap<(T, B, C, D)> { + SpaceMap { + native: (a.native, b.native, c.native, d.native), + evm: (a.evm, b.evm, c.evm, d.evm), + } + } + + pub fn map_sum U, U: Add>( + &self, mut f: F, + ) -> U { f(&self.native) + f(&self.evm) } pub const fn size(&self) -> usize { 2 } + pub fn map_all U>(self, f: F) -> SpaceMap { + SpaceMap { + native: f(self.native), + evm: f(self.evm), + } + } + pub fn apply_all U>( &mut self, mut f: F, ) -> SpaceMap { diff --git a/crates/cfxcore/core/src/error.rs b/crates/cfxcore/core/src/error.rs index 3e035131a2..b08323d06f 100644 --- a/crates/cfxcore/core/src/error.rs +++ b/crates/cfxcore/core/src/error.rs @@ -3,7 +3,7 @@ // See http://www.gnu.org/licenses/ use crate::message::Bytes; -use cfx_types::{Address, H256, U256}; +use cfx_types::{Address, SpaceMap, H256, U256}; use primitives::{filter::FilterError, transaction::TransactionError}; use std::{error, fmt, time::SystemTime}; use unexpected::{Mismatch, OutOfBounds}; @@ -37,6 +37,7 @@ pub enum BlockError { InvalidBlockGasLimit(OutOfBounds), /// Total rlp sizes of transactions in block is out of bound. InvalidBlockSize(OutOfBounds), + InvalidBasePrice(Mismatch>), /// Timestamp header field is invalid. InvalidTimestamp(OutOfBounds), /// Timestamp header field is too far in future. @@ -94,6 +95,9 @@ impl fmt::Display for BlockError { format!("Block has invalid PoW: {}", oob) } InvalidGasLimit(ref oob) => format!("Invalid gas limit: {}", oob), + InvalidBasePrice(ref mis) => { + format!("Invalid base price: {:?}", mis) + } InvalidBlockGasLimit(ref oob) => { format!("Invalid block gas limit: {}", oob) } diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 34861a5fa8..782f31d8ed 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -14,6 +14,7 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; +use cfx_parameters::consensus_internal::ELASTICITY_MULTIPLIER; use futures::executor::block_on; use parking_lot::RwLock; use slab::Slab; @@ -756,6 +757,14 @@ impl SynchronizationGraphInner { ))); } + // Verify by 1559 + let parent_gas_limit = + if epoch == self.machine.params().transition_heights.cip1559 { + parent_gas_limit * ELASTICITY_MULTIPLIER + } else { + parent_gas_limit + }; + // Verify the gas limit is respected let self_gas_limit = *self.arena[index].block_header.gas_limit(); let gas_limit_divisor = self.machine.params().gas_limit_bound_divisor; @@ -1757,6 +1766,7 @@ impl SynchronizationGraph { if need_to_verify { let r = self.verification_config.verify_sync_graph_block_basic( &block, + &inner.arena[me].block_header, self.consensus.best_chain_id(), ); match r { diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 93f6944b20..610e876e78 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -25,7 +25,7 @@ use cfx_executor::{ use cfx_parameters::block::DEFAULT_TARGET_BLOCK_GAS_LIMIT; use cfx_statedb::{Result as StateDbResult, StateDb}; use cfx_storage::{StateIndex, StorageManagerTrait}; -use cfx_types::{AddressWithSpace as Address, AllChainID, Space, H256, U256}; +use cfx_types::{AddressWithSpace as Address, AllChainID, Space, SpaceMap, H256, U256}; use cfx_vm_types::Spec; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{ @@ -731,6 +731,27 @@ impl TransactionPool { ) } + pub fn pack_transactions_1559<'a>( + &self, num_txs: usize, gas_limit: U256, parent_base_price: SpaceMap, + block_size_limit: usize, mut best_epoch_height: u64, + mut best_block_number: u64, + ) -> (Vec>, SpaceMap) { + let mut inner = self.inner.write_with_metric(&PACK_TRANSACTION_LOCK); + best_epoch_height += 1; + // The best block number is not necessary an exact number. + best_block_number += 1; + inner.pack_transactions_1559( + num_txs, + gas_limit, + parent_base_price, + block_size_limit, + best_epoch_height, + best_block_number, + &self.verification_config, + &self.machine, + ) + } + pub fn notify_modified_accounts( &self, accounts_from_execution: Vec, ) { diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index a83a6209ec..1e04cace99 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -7,7 +7,13 @@ use super::{ use crate::verification::{PackingCheckResult, VerificationConfig}; use cfx_executor::machine::Machine; use cfx_packing_pool::{PackingPool, PackingPoolConfig}; -use cfx_parameters::staking::DRIPS_PER_STORAGE_COLLATERAL_UNIT; +use cfx_parameters::{ + consensus_internal::{ + ELASTICITY_MULTIPLIER, INITIAL_1559_CORE_BASE_PRICE, + INITIAL_1559_ETH_BASE_PRICE, + }, + staking::DRIPS_PER_STORAGE_COLLATERAL_UNIT, +}; use cfx_statedb::Result as StateDbResult; use cfx_types::{ @@ -19,7 +25,8 @@ use metrics::{ register_meter_with_group, Counter, CounterUsize, Meter, MeterTimer, }; use primitives::{ - Account, Action, SignedTransaction, Transaction, TransactionWithSignature, + block_header::compute_next_price, Account, Action, SignedTransaction, + Transaction, TransactionWithSignature, }; use rand::SeedableRng; use rand_xorshift::XorShiftRng; @@ -104,10 +111,30 @@ impl DeferredPool { self.packing_pool.apply_all(|x| x.clear()); } + fn estimate_block_gas_limit( + &self, space: Space, gas_target: U256, parent_base_price: U256, + min_base_price: U256, + ) -> (U256, U256) { + let gas_limit = + self.packing_pool.in_space(space).estimate_block_gas_limit( + gas_target, + parent_base_price, + min_base_price, + ); + let price_limit = compute_next_price( + gas_target, + gas_limit, + parent_base_price, + min_base_price, + ); + (gas_limit, price_limit) + } + #[inline] fn packing_sampler<'a, F: Fn(&SignedTransaction) -> PackingCheckResult>( &'a mut self, space: Space, block_gas_limit: U256, - block_size_limit: usize, tx_num_limit: usize, validity: F, + block_size_limit: usize, tx_num_limit: usize, tx_min_price: U256, + validity: F, ) -> (Vec>, U256, usize) { if block_gas_limit.is_zero() || block_size_limit == 0 @@ -145,6 +172,10 @@ impl DeferredPool { .tx_sampler(&mut rng, block_gas_limit.into()) { 'sender: for tx in sender_txs.iter() { + if tx.gas_price() < &tx_min_price { + to_drop_txs.push(tx.clone()); + break 'sender; + } match validity(&*tx) { PackingCheckResult::Pack => {} PackingCheckResult::Pending => { @@ -1084,6 +1115,7 @@ impl TransactionPoolInner { std::cmp::min(block_gas_limit, evm_gas_limit), block_size_limit, num_txs, + U256::zero(), validity, ); packed_transactions.extend_from_slice(&sampled_tx); @@ -1093,6 +1125,7 @@ impl TransactionPoolInner { block_gas_limit - used_gas, block_size_limit - used_size, num_txs - sampled_tx.len(), + U256::zero(), validity, ); packed_transactions.extend_from_slice(&sampled_tx); @@ -1112,6 +1145,117 @@ impl TransactionPoolInner { packed_transactions } + pub fn pack_transactions_1559<'a>( + &mut self, num_txs: usize, gas_limit: U256, + parent_base_price: SpaceMap, block_size_limit: usize, + best_epoch_height: u64, best_block_number: u64, + verification_config: &VerificationConfig, machine: &Machine, + ) -> (Vec>, SpaceMap) { + let mut packed_transactions: Vec> = Vec::new(); + if num_txs == 0 { + return (packed_transactions, parent_base_price); + } + + let mut block_base_price = parent_base_price.clone(); + + let spec = machine.spec(best_block_number, best_epoch_height); + let transitions = &machine.params().transition_heights; + + let validity = |tx: &SignedTransaction| { + verification_config.fast_recheck( + tx, + best_epoch_height, + transitions, + &spec, + ) + }; + + let can_pack_evm = + machine.params().can_pack_evm_transaction(best_epoch_height); + + let (evm_packed_tx_num, evm_used_size) = if can_pack_evm { + let gas_target = gas_limit * 5 / 10 / ELASTICITY_MULTIPLIER; + let parent_base_price = parent_base_price[Space::Ethereum]; + let min_base_price = INITIAL_1559_ETH_BASE_PRICE.into(); + + let (packed_limit, base_price) = + self.deferred_pool.estimate_block_gas_limit( + Space::Ethereum, + gas_target, + parent_base_price, + min_base_price, + ); + let (sampled_tx, used_gas, used_size) = + self.deferred_pool.packing_sampler( + Space::Ethereum, + packed_limit, + block_size_limit, + num_txs, + base_price, + validity, + ); + packed_transactions.extend_from_slice(&sampled_tx); + // Recompute the base price, it should be <= estimated base price, + // since the actual used gas is <= estimated limit + block_base_price[Space::Ethereum] = compute_next_price( + gas_target, + used_gas, + parent_base_price, + min_base_price, + ); + (sampled_tx.len(), used_size) + } else { + (0, 0) + }; + + { + let gas_target = gas_limit * 9 / 10 / ELASTICITY_MULTIPLIER; + let parent_base_price = parent_base_price[Space::Native]; + let min_base_price = INITIAL_1559_CORE_BASE_PRICE.into(); + + let (packed_limit, tx_min_price) = + self.deferred_pool.estimate_block_gas_limit( + Space::Native, + gas_target, + parent_base_price, + min_base_price, + ); + + let (sampled_tx, used_gas, _) = self.deferred_pool.packing_sampler( + Space::Native, + packed_limit, + block_size_limit - evm_used_size, + num_txs - evm_packed_tx_num, + tx_min_price, + validity, + ); + packed_transactions.extend_from_slice(&sampled_tx); + + // Recompute the base price, it should be <= estimated base price, + // since the actual used gas is <= estimated limit + block_base_price[Space::Native] = compute_next_price( + gas_target, + used_gas, + parent_base_price, + min_base_price, + ); + } + + if log::max_level() >= log::Level::Debug { + let mut rlp_s = RlpStream::new(); + for tx in &packed_transactions { + rlp_s.append::(&**tx); + } + debug!( + "After packing packed_transactions: {}, rlp size: {}", + packed_transactions.len(), + rlp_s.out().len(), + ); + } + + (packed_transactions, block_base_price) + } + pub fn notify_modified_accounts( &mut self, accounts_from_execution: Vec, ) { diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 0b9db83e45..1c433daae3 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -11,17 +11,25 @@ use crate::{ use cfx_executor::{ executive::gas_required_for, machine::Machine, spec::TransitionsEpochHeight, }; -use cfx_parameters::block::*; +use cfx_parameters::{ + block::*, + consensus_internal::{ + ELASTICITY_MULTIPLIER, INITIAL_1559_CORE_BASE_PRICE, + INITIAL_1559_ETH_BASE_PRICE, + }, +}; use cfx_storage::{ into_simple_mpt_key, make_simple_mpt, simple_mpt_merkle_root, simple_mpt_proof, SimpleMpt, TrieProof, }; use cfx_types::{ - address_util::AddressUtil, AllChainID, BigEndianHash, Space, H256, U256, + address_util::AddressUtil, AllChainID, BigEndianHash, Space, SpaceMap, + H256, U256, }; use cfx_vm_types::Spec; use primitives::{ block::BlockHeight, + block_header::compute_next_price_tuple, transaction::{ native_transaction::TypedNativeTransaction, TransactionError, }, @@ -367,11 +375,11 @@ impl VerificationConfig { } if header.height() >= self.machine.params().transition_heights.cip1559 { - if header.cip1559_data().is_none() { + if header.base_price().is_none() { bail!(BlockError::MissingBaseFee); } } else { - if header.cip1559_data().is_some() { + if header.base_price().is_some() { bail!(BlockError::UnexpectedBaseFee); } } @@ -462,23 +470,16 @@ impl VerificationConfig { /// should discard this block and all its descendants. #[inline] pub fn verify_sync_graph_block_basic( - &self, block: &Block, chain_id: AllChainID, + &self, block: &Block, parent: &BlockHeader, chain_id: AllChainID, ) -> Result<(), Error> { self.verify_block_integrity(block)?; - let mut block_size = 0; - let mut block_total_gas = U256::zero(); - let block_height = block.block_header.height(); - let evm_space_gas_limit = - if self.machine.params().can_pack_evm_transaction(block_height) { - *block.block_header.gas_limit() - / self.machine.params().evm_transaction_gas_ratio - } else { - U256::zero() - }; - let mut evm_total_gas = U256::zero(); + + let mut total_gas: SpaceMap = SpaceMap::default(); + let mut block_size = 0; let transitions = &self.machine.params().transition_heights; + for t in &block.transactions { self.verify_transaction_common( t, @@ -488,10 +489,7 @@ impl VerificationConfig { VerifyTxMode::Remote, )?; block_size += t.rlp_size(); - block_total_gas += *t.gas_limit(); - if t.space() == Space::Ethereum { - evm_total_gas += *t.gas_limit(); - } + total_gas[t.space()] += *t.gas_limit(); } if block_size > self.max_block_size_in_bytes { @@ -504,6 +502,31 @@ impl VerificationConfig { ))); } + if block_height >= self.machine.params().transition_heights.cip1559 { + self.check_base_fee(block, parent, total_gas)?; + } else { + self.check_hard_gas_limit(block, total_gas)?; + } + + Ok(()) + } + + fn check_hard_gas_limit( + &self, block: &Block, total_gas: SpaceMap, + ) -> Result<(), Error> { + let block_height = block.block_header.height(); + + let evm_space_gas_limit = + if self.machine.params().can_pack_evm_transaction(block_height) { + *block.block_header.gas_limit() + / self.machine.params().evm_transaction_gas_ratio + } else { + U256::zero() + }; + + let evm_total_gas = total_gas[Space::Ethereum]; + let block_total_gas = total_gas.map_sum(|x| *x); + if evm_total_gas > evm_space_gas_limit { return Err(From::from(BlockError::InvalidBlockGasLimit( OutOfBounds { @@ -527,6 +550,77 @@ impl VerificationConfig { Ok(()) } + fn check_base_fee( + &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap, + ) -> Result<(), Error> { + use Space::*; + const INIT_BASE_PRICE: SpaceMap = SpaceMap::new( + INITIAL_1559_CORE_BASE_PRICE, + INITIAL_1559_ETH_BASE_PRICE, + ); + let cip1559_init = self.machine.params().transition_heights.cip1559; + let block_height = block.block_header.height(); + + assert!(block_height >= cip1559_init); + + let core_gas_limit = block.block_header.gas_limit() * 9 / 10; + let espace_gas_limit = + if self.machine.params().can_pack_evm_transaction(block_height) { + block.block_header.gas_limit() * 5 / 10 + } else { + U256::zero() + }; + + if total_gas[Ethereum] > espace_gas_limit { + return Err(From::from(BlockError::InvalidBlockGasLimit( + OutOfBounds { + min: Some(espace_gas_limit), + max: Some(espace_gas_limit), + found: total_gas[Ethereum], + }, + ))); + } + + if total_gas[Native] > core_gas_limit { + return Err(From::from(BlockError::InvalidBlockGasLimit( + OutOfBounds { + min: Some(core_gas_limit), + max: Some(core_gas_limit), + found: total_gas[Native], + }, + ))); + } + + let parent_base_price = if block_height == cip1559_init { + INIT_BASE_PRICE.map_all(U256::from) + } else { + parent.base_price().unwrap() + }; + + let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit); + let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER); + let min_base_price = INIT_BASE_PRICE.map_all(U256::from); + + let expected_base_price = SpaceMap::zip4( + gas_target, + total_gas, + parent_base_price, + min_base_price, + ) + .map_all(compute_next_price_tuple); + + let actual_base_price = block.block_header.base_price().unwrap(); + + if actual_base_price != expected_base_price { + return Err(From::from(BlockError::InvalidBasePrice(Mismatch { + expected: expected_base_price, + found: actual_base_price, + }))); + } + + Ok(()) + } + pub fn check_transaction_epoch_bound( tx: &TypedNativeTransaction, block_height: u64, transaction_epoch_bound: u64, diff --git a/crates/cfxcore/packing-pool/src/packing_batch.rs b/crates/cfxcore/packing-pool/src/packing_batch.rs index dcd90fb9ef..2ef33f53c7 100644 --- a/crates/cfxcore/packing-pool/src/packing_batch.rs +++ b/crates/cfxcore/packing-pool/src/packing_batch.rs @@ -266,6 +266,7 @@ impl PackingBatch { let loss_ratio = config.loss_ratio(sort_key); let weight = PackingPoolWeight { gas_limit: self.total_gas_limit, + min_gas_price: gas_price, weighted_loss_ratio: loss_ratio * self.total_gas_limit, max_loss_ratio: loss_ratio, }; diff --git a/crates/cfxcore/packing-pool/src/pool.rs b/crates/cfxcore/packing-pool/src/pool.rs index b8120ffd62..780b5ac525 100644 --- a/crates/cfxcore/packing-pool/src/pool.rs +++ b/crates/cfxcore/packing-pool/src/pool.rs @@ -12,6 +12,7 @@ use super::{ }; use cfx_types::U256; use malloc_size_of::MallocSizeOf; +use primitives::block_header::estimate_gas_limit; use rand::RngCore; use treap_map::{ ApplyOpOutcome, ConsoliableWeight, Node, SearchDirection, SearchResult, @@ -181,6 +182,40 @@ impl PackingPool { } } + pub fn estimate_block_gas_limit( + &self, block_gas_target: U256, parent_base_price: U256, + min_base_price: U256, + ) -> U256 { + let ret = self.treap_map.search(|left_weight, node| { + let can_sample = |weight| { + can_sample_within_1559( + weight, + block_gas_target, + parent_base_price, + min_base_price, + ) + }; + + if !can_sample(&left_weight) { + return SearchDirection::Left; + } + let right_weight = + PackingPoolWeight::consolidate(left_weight, &node.weight); + if !can_sample(&right_weight) { + return SearchDirection::Stop; + } else { + return SearchDirection::RightOrStop(right_weight); + } + }); + match ret { + Some( + SearchResult::Found { base_weight, .. } + | SearchResult::RightMost(base_weight), + ) => base_weight.gas_limit, + _ => U256::zero(), + } + } + #[cfg(test)] fn assert_consistency(&self) { self.treap_map.assert_consistency(); @@ -243,6 +278,31 @@ fn can_sample(weight: &PackingPoolWeight, gas_limit: U256) -> bool { < weight.weighted_loss_ratio } +fn can_sample_within_1559( + weight: &PackingPoolWeight, gas_target: U256, parent_base_price: U256, + min_base_price: U256, +) -> bool { + if weight.min_gas_price < min_base_price { + return false; + } + + let gas_limit = + estimate_gas_limit(gas_target, weight.min_gas_price, parent_base_price); + + if gas_limit.is_zero() { + return false; + } + + if weight.gas_limit <= gas_limit { + return true; + } + + weight + .max_loss_ratio + .saturating_mul(weight.gas_limit - gas_limit) + < weight.weighted_loss_ratio +} + impl MallocSizeOf for PackingPool where TX: PackingPoolTransaction + MallocSizeOf, diff --git a/crates/cfxcore/packing-pool/src/weight.rs b/crates/cfxcore/packing-pool/src/weight.rs index c86ae62eed..ab90ee6930 100644 --- a/crates/cfxcore/packing-pool/src/weight.rs +++ b/crates/cfxcore/packing-pool/src/weight.rs @@ -5,6 +5,7 @@ use treap_map::ConsoliableWeight; #[derive(Default, Clone, Eq, PartialEq, MallocSizeOf, Debug)] pub struct PackingPoolWeight { pub gas_limit: U256, + pub min_gas_price: U256, pub weighted_loss_ratio: U256, pub max_loss_ratio: U256, } @@ -16,6 +17,7 @@ impl ConsoliableWeight for PackingPoolWeight { fn consolidate(a: &Self, b: &Self) -> Self { Self { gas_limit: a.gas_limit + b.gas_limit, + min_gas_price: U256::min(a.min_gas_price, b.min_gas_price), weighted_loss_ratio: a.weighted_loss_ratio + b.weighted_loss_ratio, max_loss_ratio: U256::max(a.max_loss_ratio, b.max_loss_ratio), } @@ -25,6 +27,7 @@ impl ConsoliableWeight for PackingPoolWeight { self.gas_limit += other.gas_limit; self.weighted_loss_ratio += other.weighted_loss_ratio; self.max_loss_ratio = - U256::max(self.max_loss_ratio, other.max_loss_ratio) + U256::max(self.max_loss_ratio, other.max_loss_ratio); + self.min_gas_price = U256::min(self.min_gas_price, other.min_gas_price); } } diff --git a/crates/cfxcore/parameters/src/lib.rs b/crates/cfxcore/parameters/src/lib.rs index fe58a7a2fc..077a1d25f2 100644 --- a/crates/cfxcore/parameters/src/lib.rs +++ b/crates/cfxcore/parameters/src/lib.rs @@ -54,7 +54,7 @@ pub mod consensus { } pub mod consensus_internal { - use crate::consensus::ONE_CFX_IN_DRIP; + use crate::consensus::{ONE_CFX_IN_DRIP, ONE_GDRIP_IN_DRIP}; /// `REWARD_EPOCH_COUNT` needs to be larger than /// `ANTICONE_PENALTY_UPPER_EPOCH_COUNT`. If we cannot cache receipts of @@ -118,8 +118,16 @@ pub mod consensus_internal { /// The initial storage point proportion after CIP107 is enabled. pub const CIP107_STORAGE_POINT_PROP_INIT: u64 = ONE_CFX_IN_DRIP; - /// The initial storage point proportion after CIP1559 is enabled. + /// The initial base price share proportion after CIP137 is enabled. pub const CIP137_BASEFEE_PROP_INIT: u64 = ONE_CFX_IN_DRIP; + + /// The initial and minimum base price + pub const INITIAL_1559_CORE_BASE_PRICE: u64 = ONE_GDRIP_IN_DRIP; + + pub const INITIAL_1559_ETH_BASE_PRICE: u64 = 20 * ONE_GDRIP_IN_DRIP; + + // Parameter specified in EIP-1559 + pub const ELASTICITY_MULTIPLIER: usize = 2; } pub mod rpc { diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index 3e389a0587..60e906c989 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -6,7 +6,7 @@ use crate::{ block::BlockHeight, bytes::Bytes, hash::keccak, pos::PosBlockId, receipt::BlockReceipts, MERKLE_NULL_NODE, NULL_EPOCH, }; -use cfx_types::{Address, Bloom, H256, KECCAK_EMPTY_BLOOM, U256}; +use cfx_types::{Address, Bloom, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256}; use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps}; use once_cell::sync::OnceCell; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -59,8 +59,8 @@ pub struct BlockHeaderRlpPart { nonce: U256, /// Referred PoS block ID. pos_reference: Option, - /// `[core_space_base_fee, espace_base_fee, espace_gas_limit]`. - cip1559_data: Option, + /// `[core_space_base_price, espace_base_price]`. + base_price: Option, } impl PartialEq for BlockHeaderRlpPart { @@ -80,7 +80,7 @@ impl PartialEq for BlockHeaderRlpPart { && self.referee_hashes == o.referee_hashes && self.custom == o.custom && self.pos_reference == o.pos_reference - && self.cip1559_data == o.cip1559_data + && self.base_price == o.base_price } } @@ -172,18 +172,21 @@ impl BlockHeader { /// Get the PoS reference. pub fn pos_reference(&self) -> &Option { &self.pos_reference } - pub fn cip1559_data(&self) -> &Option { &self.cip1559_data } + pub fn base_price(&self) -> Option> { + self.base_price.map( + |BasePrice { + core_base_price, + espace_base_price, + }| SpaceMap::new(core_base_price, espace_base_price), + ) + } pub fn core_base_fee(&self) -> Option<&U256> { - self.cip1559_data.as_ref().map(|l| &l.core_base_fee) + self.base_price.as_ref().map(|l| &l.core_base_price) } pub fn espace_base_fee(&self) -> Option<&U256> { - self.cip1559_data.as_ref().map(|l| &l.espace_base_fee) - } - - pub fn espace_gas_limit(&self) -> Option<&U256> { - self.cip1559_data.as_ref().map(|l| &l.espace_gas_limit) + self.base_price.as_ref().map(|l| &l.espace_base_price) } /// Set the nonce field of the header. @@ -231,7 +234,7 @@ impl BlockHeader { let adaptive_n = if self.adaptive { 1 as u8 } else { 0 as u8 }; let list_len = HEADER_LIST_MIN_LEN + self.pos_reference.is_some() as usize - + self.cip1559_data.is_some() as usize + + self.base_price.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -251,8 +254,8 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } - if self.cip1559_data.is_some() { - stream.append(&self.cip1559_data); + if self.base_price.is_some() { + stream.append(&self.base_price); } for b in &self.custom { @@ -272,7 +275,7 @@ impl BlockHeader { let list_len = HEADER_LIST_MIN_LEN + 1 + self.pos_reference.is_some() as usize - + self.cip1559_data.is_some() as usize + + self.base_price.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -293,8 +296,8 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } - if self.cip1559_data.is_some() { - stream.append(&self.cip1559_data); + if self.base_price.is_some() { + stream.append(&self.base_price); } for b in &self.custom { if self.height @@ -313,7 +316,7 @@ impl BlockHeader { let list_len = HEADER_LIST_MIN_LEN + 2 + self.pos_reference.is_some() as usize - + self.cip1559_data.is_some() as usize + + self.base_price.is_some() as usize + self.custom.len(); stream .begin_list(list_len) @@ -337,8 +340,8 @@ impl BlockHeader { if self.pos_reference.is_some() { stream.append(&self.pos_reference); } - if self.cip1559_data.is_some() { - stream.append(&self.cip1559_data); + if self.base_price.is_some() { + stream.append(&self.base_price); } for b in &self.custom { @@ -371,13 +374,13 @@ impl BlockHeader { custom: vec![], nonce: r.val_at(13)?, pos_reference: r.val_at(15).unwrap_or(None), - cip1559_data: r.val_at(16).unwrap_or(None), + base_price: r.val_at(16).unwrap_or(None), }; let pow_hash = r.val_at(14)?; for i in (15 + rlp_part.pos_reference.is_some() as usize - + rlp_part.cip1559_data.is_some() as usize) + + rlp_part.base_price.is_some() as usize) ..r.item_count()? { if rlp_part.height @@ -423,7 +426,7 @@ pub struct BlockHeaderBuilder { custom: Vec, nonce: U256, pos_reference: Option, - cip1559_data: Option, + base_price: Option, } impl BlockHeaderBuilder { @@ -445,7 +448,7 @@ impl BlockHeaderBuilder { custom: Vec::new(), nonce: U256::zero(), pos_reference: None, - cip1559_data: None, + base_price: None, } } @@ -542,9 +545,9 @@ impl BlockHeaderBuilder { } pub fn with_cip1559_data( - &mut self, cip1559_data: Option, + &mut self, cip1559_data: Option, ) -> &mut Self { - self.cip1559_data = cip1559_data; + self.base_price = cip1559_data; self } @@ -567,7 +570,7 @@ impl BlockHeaderBuilder { custom: self.custom.clone(), nonce: self.nonce, pos_reference: self.pos_reference, - cip1559_data: self.cip1559_data.clone(), + base_price: self.base_price.clone(), }, hash: None, pow_hash: None, @@ -650,11 +653,11 @@ impl Decodable for BlockHeader { custom: vec![], nonce: r.val_at(13)?, pos_reference: r.val_at(14).unwrap_or(None), - cip1559_data: r.val_at(15).unwrap_or(None), + base_price: r.val_at(15).unwrap_or(None), }; for i in (14 + rlp_part.pos_reference.is_some() as usize - + rlp_part.cip1559_data.is_some() as usize) + + rlp_part.base_price.is_some() as usize) ..r.item_count()? { if rlp_part.height @@ -678,11 +681,61 @@ impl Decodable for BlockHeader { } } -#[derive(Clone, Debug, Eq, RlpDecodable, RlpEncodable, PartialEq)] -pub struct CIP1559Data { - core_base_fee: U256, - espace_base_fee: U256, - espace_gas_limit: U256, +#[derive(Clone, Copy, Debug, Eq, RlpDecodable, RlpEncodable, PartialEq)] +pub struct BasePrice { + pub core_base_price: U256, + pub espace_base_price: U256, +} + +const BASE_PRICE_CHANGE_DENOMINATOR: usize = 8; + +pub fn compute_next_price( + gas_target: U256, gas_actual: U256, last_base_price: U256, + min_base_price: U256, +) -> U256 { + const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; + + let next_base_price = if gas_target.is_zero() || gas_target == gas_actual { + last_base_price + } else if gas_actual > gas_target { + let delta = gas_actual - gas_target; + let mut price_delta = last_base_price * delta / gas_target / DENOM; + if price_delta.is_zero() { + price_delta = U256::one(); + } + last_base_price + price_delta + } else { + let delta = gas_target - gas_actual; + let mut price_delta = last_base_price * delta / gas_target / DENOM; + if price_delta.is_zero() { + price_delta = U256::one(); + } + last_base_price - price_delta + }; + next_base_price.min(min_base_price) +} + +pub fn estimate_gas_limit( + gas_target: U256, current_base_price: U256, last_base_price: U256, +) -> U256 { + const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; + + let upper_base_price = last_base_price + last_base_price / DENOM; + let lower_base_price = last_base_price - last_base_price / DENOM; + + if current_base_price > upper_base_price { + gas_target * 2 + } else if current_base_price < lower_base_price { + U256::zero() + } else { + gas_target * 2 * (current_base_price - lower_base_price) + / (upper_base_price - lower_base_price) + } +} + +/// A helper function for `compute_next_price` which takes a typle as input +pub fn compute_next_price_tuple(x: (U256, U256, U256, U256)) -> U256 { + compute_next_price(x.0, x.1, x.2, x.3) } #[cfg(test)] From 336371db34a73120682226191006ee3aeb46f295 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Tue, 14 May 2024 14:21:45 +0800 Subject: [PATCH 042/137] Compute reward points based on cip136 round. --- .../cfxcore/core/src/consensus/pos_handler.rs | 11 ++++--- .../src/pos/consensus/executor/src/lib.rs | 5 +-- .../pos-ledger-db/src/ledger_store/mod.rs | 6 ++-- .../src/pos/storage/pos-ledger-db/src/lib.rs | 8 +++-- .../src/schema/reward_event/mod.rs | 15 ++++++--- .../pos/storage/storage-interface/src/lib.rs | 7 ++-- .../pos/storage/storage-interface/src/mock.rs | 4 +-- .../types/src/reward_distribution_event.rs | 32 +++++++++++++++---- .../core/src/pos/types/src/term_state.rs | 24 +++++++++----- 9 files changed, 76 insertions(+), 36 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/pos_handler.rs b/crates/cfxcore/core/src/consensus/pos_handler.rs index bba4108795..e7dc5308f1 100644 --- a/crates/cfxcore/core/src/consensus/pos_handler.rs +++ b/crates/cfxcore/core/src/consensus/pos_handler.rs @@ -8,7 +8,7 @@ use diem_crypto::HashValue; use diem_types::{ contract_event::ContractEvent, epoch_state::EpochState, - reward_distribution_event::RewardDistributionEvent, + reward_distribution_event::RewardDistributionEventV2, term_state::{DisputeEvent, UnlockEvent}, validator_config::{ConsensusPrivateKey, ConsensusVRFPrivateKey}, }; @@ -82,7 +82,8 @@ pub trait PosInterface: Send + Sync { &self, start_epoch: u64, end_epoch: u64, ) -> Vec; - fn get_reward_event(&self, epoch: u64) -> Option; + fn get_reward_event(&self, epoch: u64) + -> Option; fn get_epoch_state(&self, block_id: &PosBlockId) -> EpochState; @@ -346,7 +347,7 @@ impl PosHandler { pub fn get_reward_distribution_event( &self, h: &PosBlockId, parent_pos_ref: &PosBlockId, - ) -> Option> { + ) -> Option> { if h == parent_pos_ref { return None; } @@ -610,7 +611,9 @@ impl PosInterface for PosConnection { .collect() } - fn get_reward_event(&self, epoch: u64) -> Option { + fn get_reward_event( + &self, epoch: u64, + ) -> Option { self.pos_storage.get_reward_event(epoch).ok() } diff --git a/crates/cfxcore/core/src/pos/consensus/executor/src/lib.rs b/crates/cfxcore/core/src/pos/consensus/executor/src/lib.rs index a709337739..803b0d4f44 100644 --- a/crates/cfxcore/core/src/pos/consensus/executor/src/lib.rs +++ b/crates/cfxcore/core/src/pos/consensus/executor/src/lib.rs @@ -40,7 +40,7 @@ use diem_types::{ ledger_info::LedgerInfoWithSignatures, on_chain_config, proof::accumulator::InMemoryAccumulator, - reward_distribution_event::{RewardDistributionEvent, VoteCount}, + reward_distribution_event::{RewardDistributionEventV2, VoteCount}, term_state::{ ElectionEvent, PosState, RegisterEvent, RetireEvent, UpdateVotingPowerEvent, @@ -1252,12 +1252,13 @@ impl BlockExecutor for Executor { } } - let reward_event = RewardDistributionEvent { + let reward_event = RewardDistributionEventV2 { candidates: pos_state_to_commit.next_evicted_term(), elected: elected .into_iter() .map(|(k, v)| (H256::from_slice(k.as_ref()), v)) .collect(), + view: pos_state_to_commit.current_view(), }; self.db_with_cache.db.writer.save_reward_event( ledger_info_with_sigs.ledger_info().epoch(), diff --git a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs index cbde0b0292..8cd447a61a 100644 --- a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs +++ b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs @@ -29,7 +29,7 @@ use diem_types::{ TransactionAccumulatorProof, TransactionAccumulatorRangeProof, TransactionInfoWithProof, }, - reward_distribution_event::RewardDistributionEvent, + reward_distribution_event::RewardDistributionEventV2, term_state::PosState, transaction::{TransactionInfo, Version}, }; @@ -501,7 +501,7 @@ impl LedgerStore { } pub fn put_reward_event( - &self, epoch: u64, event: &RewardDistributionEvent, + &self, epoch: u64, event: &RewardDistributionEventV2, ) -> Result<()> { let mut cs = ChangeSet::new(); cs.batch.put::(&epoch, event)?; @@ -510,7 +510,7 @@ impl LedgerStore { pub fn get_reward_event( &self, epoch: u64, - ) -> Result { + ) -> Result { self.db.get::(&epoch)?.ok_or_else(|| { DiemDbError::NotFound(format!("RewardEvent of epoch {}", epoch)) .into() diff --git a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/lib.rs b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/lib.rs index 6dd2433eb3..36a895fbec 100644 --- a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/lib.rs +++ b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/lib.rs @@ -43,7 +43,7 @@ use diem_types::{ AccountStateProof, AccumulatorConsistencyProof, SparseMerkleProof, TransactionListProof, }, - reward_distribution_event::RewardDistributionEvent, + reward_distribution_event::RewardDistributionEventV2, term_state::PosState, transaction::{ Transaction, TransactionInfo, TransactionListWithProof, @@ -995,7 +995,7 @@ impl DbWriter for PosLedgerDB { } fn save_reward_event( - &self, epoch: u64, event: &RewardDistributionEvent, + &self, epoch: u64, event: &RewardDistributionEventV2, ) -> Result<()> { self.ledger_store.put_reward_event(epoch, event) } @@ -1042,7 +1042,9 @@ impl DBReaderForPoW for PosLedgerDB { Ok(ending_blocks) } - fn get_reward_event(&self, epoch: u64) -> Result { + fn get_reward_event( + &self, epoch: u64, + ) -> Result { self.ledger_store.get_reward_event(epoch) } diff --git a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/schema/reward_event/mod.rs b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/schema/reward_event/mod.rs index 14aeb43347..e71eaa1e06 100644 --- a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/schema/reward_event/mod.rs +++ b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/schema/reward_event/mod.rs @@ -20,7 +20,9 @@ use crate::schema::{ensure_slice_len_eq, REWARD_EVENT_CF_NAME}; use anyhow::Result; use byteorder::{BigEndian, ReadBytesExt}; -use diem_types::reward_distribution_event::RewardDistributionEvent; +use diem_types::reward_distribution_event::{ + RewardDistributionEventV1, RewardDistributionEventV2, +}; use schemadb::{ define_schema, schema::{KeyCodec, ValueCodec}, @@ -30,7 +32,7 @@ use std::mem::size_of; define_schema!( RewardEventSchema, u64, /* epoch num */ - RewardDistributionEvent, + RewardDistributionEventV2, REWARD_EVENT_CF_NAME ); @@ -43,12 +45,17 @@ impl KeyCodec for u64 { } } -impl ValueCodec for RewardDistributionEvent { +impl ValueCodec for RewardDistributionEventV2 { fn encode_value(&self) -> Result> { bcs::to_bytes(self).map_err(Into::into) } fn decode_value(data: &[u8]) -> Result { - bcs::from_bytes(data).map_err(Into::into) + bcs::from_bytes(data) + // If `data` cannot be deserialized to `Self`, it may be stored in + // an older version. + .or(bcs::from_bytes::(data) + .map(Into::into)) + .map_err(Into::into) } } diff --git a/crates/cfxcore/core/src/pos/storage/storage-interface/src/lib.rs b/crates/cfxcore/core/src/pos/storage/storage-interface/src/lib.rs index 5ead9c40e0..db8914221b 100644 --- a/crates/cfxcore/core/src/pos/storage/storage-interface/src/lib.rs +++ b/crates/cfxcore/core/src/pos/storage/storage-interface/src/lib.rs @@ -23,7 +23,7 @@ use diem_types::{ proof::{ definition::LeafCount, AccumulatorConsistencyProof, SparseMerkleProof, }, - reward_distribution_event::RewardDistributionEvent, + reward_distribution_event::RewardDistributionEventV2, term_state::PosState, transaction::{ TransactionInfo, TransactionListWithProof, TransactionToCommit, @@ -426,7 +426,7 @@ pub trait DbWriter: Send + Sync { ) -> Result<()>; fn save_reward_event( - &self, epoch: u64, event: &RewardDistributionEvent, + &self, epoch: u64, event: &RewardDistributionEventV2, ) -> Result<()>; fn delete_pos_state_by_block(&self, block_id: &HashValue) -> Result<()>; @@ -529,7 +529,8 @@ pub trait DBReaderForPoW: Send + Sync + DbReader { &self, start_epoch: u64, end_epoch: u64, ) -> Result>; - fn get_reward_event(&self, epoch: u64) -> Result; + fn get_reward_event(&self, epoch: u64) + -> Result; fn get_committed_block_by_hash( &self, block_hash: &HashValue, diff --git a/crates/cfxcore/core/src/pos/storage/storage-interface/src/mock.rs b/crates/cfxcore/core/src/pos/storage/storage-interface/src/mock.rs index 5a84189548..5fc880069e 100644 --- a/crates/cfxcore/core/src/pos/storage/storage-interface/src/mock.rs +++ b/crates/cfxcore/core/src/pos/storage/storage-interface/src/mock.rs @@ -18,7 +18,7 @@ use diem_types::{ epoch_change::EpochChangeProof, ledger_info::LedgerInfoWithSignatures, proof::{AccumulatorConsistencyProof, SparseMerkleProof}, - reward_distribution_event::RewardDistributionEvent, + reward_distribution_event::RewardDistributionEventV2, transaction::{TransactionListWithProof, TransactionWithProof, Version}, }; @@ -139,7 +139,7 @@ impl DBReaderForPoW for MockDbReader { fn get_reward_event( &self, _epoch: u64, - ) -> anyhow::Result { + ) -> anyhow::Result { todo!() } diff --git a/crates/cfxcore/core/src/pos/types/src/reward_distribution_event.rs b/crates/cfxcore/core/src/pos/types/src/reward_distribution_event.rs index 7521d4819f..171ee045ea 100644 --- a/crates/cfxcore/core/src/pos/types/src/reward_distribution_event.rs +++ b/crates/cfxcore/core/src/pos/types/src/reward_distribution_event.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use cfx_types::H256; use crate::term_state::{ - BONUS_VOTE_POINTS, COMMITTEE_POINTS, ELECTION_POINTS, LEADER_POINTS, + bonus_vote_points, leader_points, COMMITTEE_POINTS, ELECTION_POINTS, }; #[derive(Clone, Serialize, Deserialize, Default, Debug, Eq, PartialEq)] @@ -26,28 +26,30 @@ pub struct VoteCount { } impl VoteCount { - pub fn reward_points(&self) -> u64 { + pub fn reward_points(&self, view: u64) -> u64 { if self.vote_count == 0 { return 0; } - self.leader_count as u64 * LEADER_POINTS - + self.included_vote_count as u64 * BONUS_VOTE_POINTS + self.leader_count as u64 * leader_points(view) + + self.included_vote_count * bonus_vote_points(view) + self.total_votes * COMMITTEE_POINTS } } #[derive(Clone, Serialize, Deserialize, Default, Debug, Eq, PartialEq)] -pub struct RewardDistributionEvent { +pub struct RewardDistributionEventV2 { pub candidates: BTreeMap, pub elected: BTreeMap, + pub view: u64, } -impl RewardDistributionEvent { +impl RewardDistributionEventV2 { pub fn rewards(&self) -> impl Iterator { + let view = self.view; let committee_rewards = self .elected .iter() - .map(|(id, vote_count)| (id, vote_count.reward_points())); + .map(move |(id, vote_count)| (id, vote_count.reward_points(view))); let participate_rewards = self .candidates .iter() @@ -55,3 +57,19 @@ impl RewardDistributionEvent { committee_rewards.chain(participate_rewards) } } + +#[derive(Clone, Serialize, Deserialize, Default, Debug, Eq, PartialEq)] +pub struct RewardDistributionEventV1 { + pub candidates: BTreeMap, + pub elected: BTreeMap, +} + +impl From for RewardDistributionEventV2 { + fn from(value: RewardDistributionEventV1) -> Self { + Self { + candidates: value.candidates, + elected: value.elected, + view: 0, + } + } +} diff --git a/crates/cfxcore/core/src/pos/types/src/term_state.rs b/crates/cfxcore/core/src/pos/types/src/term_state.rs index 49447eaacf..7cff8768eb 100644 --- a/crates/cfxcore/core/src/pos/types/src/term_state.rs +++ b/crates/cfxcore/core/src/pos/types/src/term_state.rs @@ -54,8 +54,9 @@ pub const TERM_MAX_SIZE: usize = 10000; pub const TERM_ELECTED_SIZE: usize = 50; mod incentives { - use super::{ - ROUND_PER_TERM, TERM_ELECTED_SIZE, TERM_LIST_LEN, TERM_MAX_SIZE, + use super::{TERM_ELECTED_SIZE, TERM_LIST_LEN, TERM_MAX_SIZE}; + use crate::term_state::pos_state_config::{ + PosStateConfigTrait, POS_STATE_CONFIG, }; const BONUS_VOTE_MAX_SIZE: u64 = 100; @@ -73,12 +74,19 @@ mod incentives { / 100 / (TERM_ELECTED_SIZE as u64) / (TERM_LIST_LEN as u64); - pub const LEADER_POINTS: u64 = - MAX_TERM_POINTS * LEADER_PERCENTAGE / 100 / ROUND_PER_TERM; - pub const BONUS_VOTE_POINTS: u64 = MAX_TERM_POINTS * BONUS_VOTE_PERCENTAGE - / 100 - / ROUND_PER_TERM - / BONUS_VOTE_MAX_SIZE; + + pub fn leader_points(view: u64) -> u64 { + MAX_TERM_POINTS * LEADER_PERCENTAGE + / 100 + / POS_STATE_CONFIG.round_per_term(view) + } + + pub fn bonus_vote_points(view: u64) -> u64 { + MAX_TERM_POINTS * BONUS_VOTE_PERCENTAGE + / 100 + / POS_STATE_CONFIG.round_per_term(view) + / BONUS_VOTE_MAX_SIZE + } } #[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)] From 2d090db9b8d747a304bdafacaf0fe9b15a334d1f Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 14 May 2024 14:44:06 +0800 Subject: [PATCH 043/137] update doc --- docs/transaction-trace/geth-style-trace.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/transaction-trace/geth-style-trace.md b/docs/transaction-trace/geth-style-trace.md index 64335f85c9..eb53c6d8c1 100644 --- a/docs/transaction-trace/geth-style-trace.md +++ b/docs/transaction-trace/geth-style-trace.md @@ -58,6 +58,12 @@ public_evm_rpc_apis = "eth,ethdebug" Currently the opcode trace's structLogs `error` field is not implemented. +## FAQs + +1. Does trace RPC methods support eSpace PhantomTransaction traces? + + Currently, no. We will support it in the future. + ## Resources 1. [Geth EVM Tracing](https://geth.ethereum.org/docs/developers/evm-tracing) From 3a34f15442d77026c24f8bb23c609c4429e71474 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 14 May 2024 15:45:19 +0800 Subject: [PATCH 044/137] Implement block generation & update tx execution for 1559 --- crates/blockgen/src/lib.rs | 67 +++++++--- .../consensus_executor/epoch_execution.rs | 14 +-- .../consensus_inner/consensus_executor/mod.rs | 4 +- .../core/src/sync/synchronization_graph.rs | 11 +- .../cfxcore/core/src/transaction_pool/mod.rs | 115 +++++++++++++----- .../transaction_pool_inner.rs | 37 +++--- crates/cfxcore/core/src/verification.rs | 76 +++++++----- crates/cfxcore/executor/src/context.rs | 4 +- .../executor/src/executive/fresh_executive.rs | 9 +- crates/cfxcore/executor/src/spec.rs | 8 +- .../executor/src/state/state_object/reward.rs | 10 +- .../src/state/state_object/staking.rs | 2 +- crates/cfxcore/packing-pool/src/pool.rs | 21 ++-- crates/cfxcore/packing-pool/src/weight.rs | 7 +- .../vm-interpreter/src/interpreter/mod.rs | 3 +- crates/cfxcore/vm-types/src/env.rs | 6 +- crates/client/benches/benchmark.rs | 4 +- crates/client/src/configuration.rs | 10 +- crates/client/src/rpc/impls/cfx.rs | 1 + crates/primitives/src/block_header.rs | 32 ++--- crates/primitives/src/transaction/mod.rs | 14 ++- tests/evm_space/eip1559_test.py | 6 +- 22 files changed, 297 insertions(+), 164 deletions(-) diff --git a/crates/blockgen/src/lib.rs b/crates/blockgen/src/lib.rs index 589aa8b80c..bdc0e89afc 100644 --- a/crates/blockgen/src/lib.rs +++ b/crates/blockgen/src/lib.rs @@ -8,7 +8,7 @@ use crate::miner::{ work_notify::NotifyWork, }; use cfx_parameters::consensus::GENESIS_GAS_LIMIT; -use cfx_types::{Address, H256, U256}; +use cfx_types::{Address, SpaceMap, H256, U256}; use cfxcore::{ block_parameters::*, consensus::{consensus_inner::StateBlameInfo, pos_handler::PosVerifier}, @@ -202,6 +202,7 @@ impl BlockGenerator { mut blame_info: StateBlameInfo, block_gas_limit: U256, transactions: Vec>, difficulty: u64, adaptive_opt: Option, maybe_pos_reference: Option, + maybe_base_price: Option>, ) -> Block { trace!("{} txs packed", transactions.len()); let consensus_graph = self.consensus_graph(); @@ -279,8 +280,7 @@ impl BlockGenerator { .with_gas_limit(block_gas_limit) .with_custom(custom) .with_pos_reference(maybe_pos_reference) - // TODO: Set base fee and gas limit - .with_cip1559_data(None) + .with_base_price(maybe_base_price) .build(); Block::new(block_header, transactions) @@ -304,14 +304,45 @@ impl BlockGenerator { self.graph.verification_config.max_block_size_in_bytes; let best_info = consensus_graph.best_info(); - let transactions = self.txpool.pack_transactions( - num_txs, - block_gas_limit, - U256::zero(), - block_size_limit, - best_info.best_epoch_number, - best_info.best_block_number, - ); + let parent_block = self + .txpool + .data_man + .block_header_by_hash(&best_info.best_block_hash) + // The parent block must exists. + .expect("Parent block not found"); + + let machine = self.txpool.machine(); + let params = machine.params(); + let cip1559_height = params.transition_heights.cip1559; + let pack_height = best_info.best_epoch_number + 1; + + let (transactions, maybe_base_price) = if pack_height < cip1559_height { + let txs = self.txpool.pack_transactions( + num_txs, + block_gas_limit, + U256::zero(), + block_size_limit, + best_info.best_epoch_number, + best_info.best_block_number, + ); + (txs, None) + } else { + let parent_base_price = if cip1559_height == pack_height { + params.init_base_price() + } else { + parent_block.base_price().unwrap() + }; + + let (txs, base_price) = self.txpool.pack_transactions_1559( + num_txs, + block_gas_limit, + parent_base_price, + block_size_limit, + best_info.best_epoch_number, + best_info.best_block_number, + ); + (txs, Some(base_price)) + }; Ok(self.assemble_new_block_impl( parent_hash, @@ -322,6 +353,7 @@ impl BlockGenerator { difficulty, Some(adaptive), pos_reference.or_else(|| self.get_pos_reference(&parent_hash)), + maybe_base_price, )) } @@ -332,7 +364,7 @@ impl BlockGenerator { ) -> Block { let consensus_graph = self.consensus_graph(); - let (best_info, block_gas_limit, transactions) = + let (best_info, block_gas_limit, transactions, maybe_base_price) = self.txpool.get_best_info_with_packed_transactions( num_txs, block_size_limit, @@ -379,6 +411,7 @@ impl BlockGenerator { 0, None, maybe_pos_reference, + maybe_base_price, ) } @@ -394,7 +427,7 @@ impl BlockGenerator { ) -> Block { let consensus_graph = self.consensus_graph(); - let (best_info, block_gas_limit, transactions) = + let (best_info, block_gas_limit, transactions, maybe_base_price) = self.txpool.get_best_info_with_packed_transactions( num_txs, block_size_limit, @@ -433,6 +466,7 @@ impl BlockGenerator { 0, None, self.get_pos_reference(&best_block_hash), + maybe_base_price, ) } @@ -526,7 +560,7 @@ impl BlockGenerator { ) -> H256 { let consensus_graph = self.consensus_graph(); // get the best block - let (best_info, block_gas_limit, _) = self + let (best_info, block_gas_limit, _, maybe_base_price) = self .txpool .get_best_info_with_packed_transactions(0, 0, Vec::new()); let state_blame_info = consensus_graph @@ -548,6 +582,7 @@ impl BlockGenerator { 0, adaptive, self.get_pos_reference(&best_block_hash), + maybe_base_price, ); self.generate_block_impl(block) @@ -573,6 +608,8 @@ impl BlockGenerator { 0, Some(adaptive), self.get_pos_reference(&parent_hash), + // FIXME[1559]: check later + None, ); if let Some(custom) = maybe_custom { block.block_header.set_custom(custom); @@ -601,6 +638,8 @@ impl BlockGenerator { 0, Some(adaptive), self.get_pos_reference(&parent_hash), + // FIXME[1559] + None, ); block.block_header.set_nonce(nonce); block.block_header.set_timestamp(timestamp); diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs index c234aa9fca..b40f8f9a54 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs @@ -44,13 +44,11 @@ impl ConsensusExecutionHandler { self.before_epoch_execution(state, &*pivot_block)?; - let base_gas_price = pivot_block - .block_header - .core_base_fee() - .cloned() - .unwrap_or_default(); + let base_gas_price = + pivot_block.block_header.base_price().unwrap_or_default(); - let burnt_gas_price = state.burnt_gas_price(base_gas_price); + let burnt_gas_price = + base_gas_price.map_all(|x| state.burnt_gas_price(x)); let context = EpochProcessContext { on_local_pivot, executive_trace: self.config.executive_trace, @@ -421,8 +419,8 @@ struct EpochProcessContext<'a> { pivot_block: &'a Block, - base_gas_price: U256, - burnt_gas_price: U256, + base_gas_price: SpaceMap, + burnt_gas_price: SpaceMap, } struct BlockProcessContext<'a, 'b> { diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 20964d775a..c1d5ca5356 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -1659,8 +1659,8 @@ impl ConsensusExecutionHandler { transaction_epoch_bound: self .verification_config .transaction_epoch_bound, - base_gas_price: U256::zero(), - burnt_gas_price: U256::zero(), + base_gas_price: Default::default(), + burnt_gas_price: Default::default(), }; let spec = self.machine.spec(env.number, env.epoch_height); let mut ex = EstimationContext::new( diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 782f31d8ed..71eecc7f49 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -757,12 +757,11 @@ impl SynchronizationGraphInner { ))); } - // Verify by 1559 - let parent_gas_limit = - if epoch == self.machine.params().transition_heights.cip1559 { - parent_gas_limit * ELASTICITY_MULTIPLIER + let parent_gas_limit = parent_gas_limit + * if epoch == self.machine.params().transition_heights.cip1559 { + ELASTICITY_MULTIPLIER } else { - parent_gas_limit + 1 }; // Verify the gas limit is respected @@ -1774,7 +1773,7 @@ impl SynchronizationGraph { ErrorKind::Block(BlockError::InvalidTransactionsRoot(e)), _, )) => { - warn ! ("BlockTransactionRoot not match! inserted_block={:?} err={:?}", block, e); + warn!("BlockTransactionRoot not match! inserted_block={:?} err={:?}", block, e); // If the transaction root does not match, it might be // caused by receiving wrong // transactions because of conflicting ShortId in diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 610e876e78..2a965b16fb 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -22,10 +22,15 @@ use account_cache::AccountCache; use cfx_executor::{ machine::Machine, spec::TransitionsEpochHeight, state::State, }; -use cfx_parameters::block::DEFAULT_TARGET_BLOCK_GAS_LIMIT; +use cfx_parameters::{ + block::DEFAULT_TARGET_BLOCK_GAS_LIMIT, + consensus_internal::ELASTICITY_MULTIPLIER, +}; use cfx_statedb::{Result as StateDbResult, StateDb}; use cfx_storage::{StateIndex, StorageManagerTrait}; -use cfx_types::{AddressWithSpace as Address, AllChainID, Space, SpaceMap, H256, U256}; +use cfx_types::{ + AddressWithSpace as Address, AllChainID, Space, SpaceMap, H256, U256, +}; use cfx_vm_types::Spec; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{ @@ -732,9 +737,9 @@ impl TransactionPool { } pub fn pack_transactions_1559<'a>( - &self, num_txs: usize, gas_limit: U256, parent_base_price: SpaceMap, - block_size_limit: usize, mut best_epoch_height: u64, - mut best_block_number: u64, + &self, num_txs: usize, block_gas_limit: U256, + parent_base_price: SpaceMap, block_size_limit: usize, + mut best_epoch_height: u64, mut best_block_number: u64, ) -> (Vec>, SpaceMap) { let mut inner = self.inner.write_with_metric(&PACK_TRANSACTION_LOCK); best_epoch_height += 1; @@ -742,7 +747,7 @@ impl TransactionPool { best_block_number += 1; inner.pack_transactions_1559( num_txs, - gas_limit, + block_gas_limit, parent_base_price, block_size_limit, best_epoch_height, @@ -886,7 +891,12 @@ impl TransactionPool { pub fn get_best_info_with_packed_transactions( &self, num_txs: usize, block_size_limit: usize, additional_transactions: Vec>, - ) -> (Arc, U256, Vec>) { + ) -> ( + Arc, + U256, + Vec>, + Option>, + ) { // We do not need to hold the lock because it is fine for us to generate // blocks that are slightly behind the best state. // We do not want to stall the consensus thread. @@ -896,16 +906,25 @@ impl TransactionPool { consensus_best_info_clone ); - let parent_block_gas_limit = self + let params = self.machine.params(); + + let cip1559_height = params.transition_heights.cip1559; + let pack_height = consensus_best_info_clone.best_epoch_number + 1; + + let parent_block = self .data_man .block_header_by_hash(&consensus_best_info_clone.best_block_hash) // The parent block must exists. - .expect(&concat!(file!(), ":", line!(), ":", column!())) - .gas_limit() - .clone(); - - let gas_limit_divisor = self.machine.params().gas_limit_bound_divisor; - let min_gas_limit = self.machine.params().min_gas_limit; + .expect(&concat!(file!(), ":", line!(), ":", column!())); + let parent_block_gas_limit = *parent_block.gas_limit() + * if cip1559_height == pack_height { + ELASTICITY_MULTIPLIER + } else { + 1 + }; + + let gas_limit_divisor = params.gas_limit_bound_divisor; + let min_gas_limit = params.min_gas_limit; assert!(parent_block_gas_limit >= min_gas_limit); let gas_lower = max( parent_block_gas_limit - parent_block_gas_limit / gas_limit_divisor @@ -918,22 +937,53 @@ impl TransactionPool { let target_gas_limit = self.config.target_block_gas_limit.into(); let self_gas_limit = min(max(target_gas_limit, gas_lower), gas_upper); - let evm_gas_limit = if self.machine.params().can_pack_evm_transaction( - consensus_best_info_clone.best_epoch_number + 1, - ) { - self_gas_limit / self.machine.params().evm_transaction_gas_ratio - } else { - U256::zero() - }; - let transactions_from_pool = self.pack_transactions( - num_txs, - self_gas_limit.clone(), - evm_gas_limit, - block_size_limit, - consensus_best_info_clone.best_epoch_number, - consensus_best_info_clone.best_block_number, - ); + let cip1559_height = params.transition_heights.cip1559; + let pack_height = consensus_best_info_clone.best_epoch_number + 1; + + let (transactions_from_pool, maybe_base_price) = + if pack_height < cip1559_height { + let evm_gas_limit = if self + .machine + .params() + .can_pack_evm_transaction(pack_height) + { + self_gas_limit / params.evm_transaction_gas_ratio + } else { + U256::zero() + }; + + let txs = self.pack_transactions( + num_txs, + self_gas_limit.clone(), + evm_gas_limit, + block_size_limit, + consensus_best_info_clone.best_epoch_number, + consensus_best_info_clone.best_block_number, + ); + (txs, None) + } else { + let parent_base_price = if pack_height == cip1559_height { + params.init_base_price() + } else { + parent_block.base_price().unwrap() + }; + + let (tx, base_price) = self.pack_transactions_1559( + num_txs, + self_gas_limit.clone(), + parent_base_price, + block_size_limit, + consensus_best_info_clone.best_epoch_number, + consensus_best_info_clone.best_block_number, + ); + (tx, Some(base_price)) + }; + + if cip1559_height >= pack_height && !additional_transactions.is_empty() + { + eprintln!("The current version cannot handle CIP-1559 with additional transactions. "); + } let transactions = [ additional_transactions.as_slice(), @@ -941,7 +991,12 @@ impl TransactionPool { ] .concat(); - (consensus_best_info_clone, self_gas_limit, transactions) + ( + consensus_best_info_clone, + self_gas_limit, + transactions, + maybe_base_price, + ) } fn best_executed_state( diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 1e04cace99..2e43fd7f86 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -8,10 +8,7 @@ use crate::verification::{PackingCheckResult, VerificationConfig}; use cfx_executor::machine::Machine; use cfx_packing_pool::{PackingPool, PackingPoolConfig}; use cfx_parameters::{ - consensus_internal::{ - ELASTICITY_MULTIPLIER, INITIAL_1559_CORE_BASE_PRICE, - INITIAL_1559_ETH_BASE_PRICE, - }, + consensus_internal::ELASTICITY_MULTIPLIER, staking::DRIPS_PER_STORAGE_COLLATERAL_UNIT, }; @@ -111,23 +108,25 @@ impl DeferredPool { self.packing_pool.apply_all(|x| x.clear()); } - fn estimate_block_gas_limit( + fn estimate_packing_gas_limit( &self, space: Space, gas_target: U256, parent_base_price: U256, min_base_price: U256, ) -> (U256, U256) { - let gas_limit = - self.packing_pool.in_space(space).estimate_block_gas_limit( + let packing_gas_limit = self + .packing_pool + .in_space(space) + .estimate_packing_gas_limit( gas_target, parent_base_price, min_base_price, ); let price_limit = compute_next_price( gas_target, - gas_limit, + packing_gas_limit, parent_base_price, min_base_price, ); - (gas_limit, price_limit) + (packing_gas_limit, price_limit) } #[inline] @@ -1146,7 +1145,7 @@ impl TransactionPoolInner { } pub fn pack_transactions_1559<'a>( - &mut self, num_txs: usize, gas_limit: U256, + &mut self, num_txs: usize, block_gas_limit: U256, parent_base_price: SpaceMap, block_size_limit: usize, best_epoch_height: u64, best_block_number: u64, verification_config: &VerificationConfig, machine: &Machine, @@ -1174,12 +1173,13 @@ impl TransactionPoolInner { machine.params().can_pack_evm_transaction(best_epoch_height); let (evm_packed_tx_num, evm_used_size) = if can_pack_evm { - let gas_target = gas_limit * 5 / 10 / ELASTICITY_MULTIPLIER; + let gas_target = block_gas_limit * 5 / 10 / ELASTICITY_MULTIPLIER; let parent_base_price = parent_base_price[Space::Ethereum]; - let min_base_price = INITIAL_1559_ETH_BASE_PRICE.into(); + let min_base_price = + machine.params().min_base_price()[Space::Ethereum]; - let (packed_limit, base_price) = - self.deferred_pool.estimate_block_gas_limit( + let (packing_gas_limit, base_price) = + self.deferred_pool.estimate_packing_gas_limit( Space::Ethereum, gas_target, parent_base_price, @@ -1188,7 +1188,7 @@ impl TransactionPoolInner { let (sampled_tx, used_gas, used_size) = self.deferred_pool.packing_sampler( Space::Ethereum, - packed_limit, + packing_gas_limit, block_size_limit, num_txs, base_price, @@ -1209,12 +1209,13 @@ impl TransactionPoolInner { }; { - let gas_target = gas_limit * 9 / 10 / ELASTICITY_MULTIPLIER; + let gas_target = block_gas_limit * 9 / 10 / ELASTICITY_MULTIPLIER; let parent_base_price = parent_base_price[Space::Native]; - let min_base_price = INITIAL_1559_CORE_BASE_PRICE.into(); + let min_base_price = + machine.params().min_base_price()[Space::Native]; let (packed_limit, tx_min_price) = - self.deferred_pool.estimate_block_gas_limit( + self.deferred_pool.estimate_packing_gas_limit( Space::Native, gas_target, parent_base_price, diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 1c433daae3..d3cf37f0f5 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -11,13 +11,7 @@ use crate::{ use cfx_executor::{ executive::gas_required_for, machine::Machine, spec::TransitionsEpochHeight, }; -use cfx_parameters::{ - block::*, - consensus_internal::{ - ELASTICITY_MULTIPLIER, INITIAL_1559_CORE_BASE_PRICE, - INITIAL_1559_ETH_BASE_PRICE, - }, -}; +use cfx_parameters::{block::*, consensus_internal::ELASTICITY_MULTIPLIER}; use cfx_storage::{ into_simple_mpt_key, make_simple_mpt, simple_mpt_merkle_root, simple_mpt_proof, SimpleMpt, TrieProof, @@ -554,11 +548,9 @@ impl VerificationConfig { &self, block: &Block, parent: &BlockHeader, total_gas: SpaceMap, ) -> Result<(), Error> { use Space::*; - const INIT_BASE_PRICE: SpaceMap = SpaceMap::new( - INITIAL_1559_CORE_BASE_PRICE, - INITIAL_1559_ETH_BASE_PRICE, - ); - let cip1559_init = self.machine.params().transition_heights.cip1559; + + let params = self.machine.params(); + let cip1559_init = params.transition_heights.cip1559; let block_height = block.block_header.height(); assert!(block_height >= cip1559_init); @@ -592,14 +584,14 @@ impl VerificationConfig { } let parent_base_price = if block_height == cip1559_init { - INIT_BASE_PRICE.map_all(U256::from) + params.init_base_price() } else { parent.base_price().unwrap() }; let gas_limit = SpaceMap::new(core_gas_limit, espace_gas_limit); let gas_target = gas_limit.map_all(|x| x / ELASTICITY_MULTIPLIER); - let min_base_price = INIT_BASE_PRICE.map_all(U256::from); + let min_base_price = params.min_base_price(); let expected_base_price = SpaceMap::zip4( gas_target, @@ -672,10 +664,15 @@ impl VerificationConfig { transitions: &TransitionsEpochHeight, spec: &Spec, ) -> PackingCheckResult { let cip90a = height >= transitions.cip90a; + let cip1559 = height >= transitions.cip1559; let (can_pack, later_pack) = - if let Transaction::Native(ref tx) = tx.unsigned { - Self::fast_recheck_inner(spec, |mode: &VerifyTxMode| { + Self::fast_recheck_inner(spec, |mode: &VerifyTxMode| { + if !Self::check_eip1559_transaction(tx, cip1559, mode) { + return false; + } + + if let Transaction::Native(ref tx) = tx.unsigned { Self::verify_transaction_epoch_height( tx, height, @@ -683,12 +680,11 @@ impl VerificationConfig { mode, ) .is_ok() - }) - } else { - Self::fast_recheck_inner(spec, |mode: &VerifyTxMode| { + } else { Self::check_eip155_transaction(tx, cip90a, mode) - }) - }; + } + }); + match (can_pack, later_pack) { (true, _) => PackingCheckResult::Pack, (false, true) => PackingCheckResult::Pending, @@ -753,6 +749,7 @@ impl VerificationConfig { let cip76 = height >= transitions.cip76; let cip90a = height >= transitions.cip90a; let cip130 = height >= transitions.cip130; + let cip1559 = height >= transitions.cip1559; if let Transaction::Native(ref tx) = tx.unsigned { Self::verify_transaction_epoch_height( @@ -764,7 +761,11 @@ impl VerificationConfig { } if !Self::check_eip155_transaction(tx, cip90a, &mode) { - bail!(TransactionError::InvalidEthereumLike); + bail!(TransactionError::FutureTransactionType); + } + + if !Self::check_eip1559_transaction(tx, cip1559, &mode) { + bail!(TransactionError::FutureTransactionType) } Self::check_gas_limit(tx, cip76, &mode)?; @@ -787,6 +788,21 @@ impl VerificationConfig { } } + fn check_eip1559_transaction( + tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode, + ) -> bool { + if tx.before_1559() { + return true; + } + + use VerifyTxLocalMode::*; + match mode { + VerifyTxMode::Local(Full, _spec) => cip1559, + VerifyTxMode::Local(MaybeLater, _spec) => true, + VerifyTxMode::Remote => cip1559, + } + } + /// Check transaction intrinsic gas. Influenced by CIP-76. fn check_gas_limit( tx: &TransactionWithSignature, cip76: bool, mode: &VerifyTxMode, @@ -859,24 +875,20 @@ pub enum PackingCheckResult { #[derive(Copy, Clone)] pub enum VerifyTxMode<'a> { + /// Check transactions in local mode, may have more constraints Local(VerifyTxLocalMode, &'a Spec), - /* Be strict with yourself: We - * apply more checks in packing - * transactions and local - * execution. */ + /// Check transactions for received blocks in sync graph, may have less + /// constraints Remote, - /* Be lenient to others: We apply less - * check for transaction in sync graph. */ } #[derive(Copy, Clone)] pub enum VerifyTxLocalMode { + /// Apply all checks Full, - // Apply all checks. + /// If a transaction is not valid now, but can become valid in the future, + /// the check sould pass MaybeLater, - /* When inserting transactions to tx pool, if its epoch - * height is too large, it can be accept even if it is not - * regarded as a valid transaction. */ } impl<'a> VerifyTxMode<'a> { diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index e4a14cb5e7..ec1a750ae6 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -585,8 +585,8 @@ mod tests { pos_view: None, finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, - base_gas_price: U256::zero(), - burnt_gas_price: U256::zero(), + base_gas_price: Default::default(), + burnt_gas_price: Default::default(), } } diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 1b34aa93eb..301525795b 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -120,10 +120,11 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { } fn check_base_price(&self) -> Result<(), ExecutionOutcome> { - if self.tx.gas_price() < &self.context.env.burnt_gas_price { + let burnt_gas_price = self.context.env.burnt_gas_price[self.tx.space()]; + if self.tx.gas_price() < &burnt_gas_price { Err(ExecutionOutcome::NotExecutedToReconsiderPacking( ToRepackError::NotEnoughBaseFee { - expected: self.context.env.burnt_gas_price, + expected: burnt_gas_price, got: *self.tx.gas_price(), }, )) @@ -189,11 +190,11 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { } else { // actual_base_gas >= tx gas_price >= burnt_base_price let actual_base_gas = - U256::min(*tx.gas_price(), env.base_gas_price); + U256::min(*tx.gas_price(), env.base_gas_price[tx.space()]); tx.effective_gas_price(&actual_base_gas) }; - let burnt_gas_price = env.burnt_gas_price; + let burnt_gas_price = env.burnt_gas_price[tx.space()]; // gas_price >= actual_base_gas >= // 1. tx gas_price >= burnt_base_price // 2. base_gas_price >= burnt_gas_price diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index b210ca5674..e6f6559ba0 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -17,7 +17,7 @@ use cfx_parameters::{ INITIAL_BASE_MINING_REWARD_IN_UCFX, }, }; -use cfx_types::{AllChainID, Space, U256, U512}; +use cfx_types::{AllChainID, Space, SpaceMap, U256, U512}; use cfx_vm_types::Spec; use primitives::{block::BlockHeight, BlockNumber}; use std::collections::BTreeMap; @@ -54,6 +54,7 @@ pub struct CommonParams { /// transactions pub evm_transaction_gas_ratio: u64, pub params_dao_vote_period: u64, + pub min_base_price: SpaceMap, /// Set the internal contracts to state at the genesis blocks, even if it /// is not activated. @@ -148,6 +149,7 @@ impl Default for CommonParams { early_set_internal_contracts_states: false, transition_numbers: Default::default(), transition_heights: Default::default(), + min_base_price: SpaceMap::new(U256::one(), U256::one()), } } } @@ -242,4 +244,8 @@ impl CommonParams { ), ]) } + + pub fn init_base_price(&self) -> SpaceMap { self.min_base_price } + + pub fn min_base_price(&self) -> SpaceMap { self.min_base_price } } diff --git a/crates/cfxcore/executor/src/state/state_object/reward.rs b/crates/cfxcore/executor/src/state/state_object/reward.rs index 625347e46b..7a2c200079 100644 --- a/crates/cfxcore/executor/src/state/state_object/reward.rs +++ b/crates/cfxcore/executor/src/state/state_object/reward.rs @@ -54,7 +54,7 @@ impl State { self.sub_total_issued(by); } - pub fn get_base_fee_prop(&self) -> U256 { + pub fn get_base_price_prop(&self) -> U256 { self.global_stat.get::() } @@ -62,12 +62,12 @@ impl State { *self.global_stat.val::() = val; } - pub fn burnt_gas_price(&self, base_fee: U256) -> U256 { - if base_fee.is_zero() { + pub fn burnt_gas_price(&self, base_price: U256) -> U256 { + if base_price.is_zero() { return U256::zero(); } - let prop = self.get_base_fee_prop(); - base_fee - base_fee * prop / (U256::from(ONE_CFX_IN_DRIP) + prop) + let prop = self.get_base_price_prop(); + base_price - base_price * prop / (U256::from(ONE_CFX_IN_DRIP) + prop) } } diff --git a/crates/cfxcore/executor/src/state/state_object/staking.rs b/crates/cfxcore/executor/src/state/state_object/staking.rs index 9bf2dfa40a..7d09b8aaea 100644 --- a/crates/cfxcore/executor/src/state/state_object/staking.rs +++ b/crates/cfxcore/executor/src/state/state_object/staking.rs @@ -181,7 +181,7 @@ pub fn initialize_or_update_dao_voted_params( )?; } - let old_base_fee_prop = state.get_base_fee_prop(); + let old_base_fee_prop = state.get_base_price_prop(); if !old_base_fee_prop.is_zero() { state.set_base_fee_prop( vote_count diff --git a/crates/cfxcore/packing-pool/src/pool.rs b/crates/cfxcore/packing-pool/src/pool.rs index 780b5ac525..7cca4e7515 100644 --- a/crates/cfxcore/packing-pool/src/pool.rs +++ b/crates/cfxcore/packing-pool/src/pool.rs @@ -12,7 +12,7 @@ use super::{ }; use cfx_types::U256; use malloc_size_of::MallocSizeOf; -use primitives::block_header::estimate_gas_limit; +use primitives::block_header::estimate_gas_used; use rand::RngCore; use treap_map::{ ApplyOpOutcome, ConsoliableWeight, Node, SearchDirection, SearchResult, @@ -182,15 +182,14 @@ impl PackingPool { } } - pub fn estimate_block_gas_limit( - &self, block_gas_target: U256, parent_base_price: U256, - min_base_price: U256, + pub fn estimate_packing_gas_limit( + &self, gas_target: U256, parent_base_price: U256, min_base_price: U256, ) -> U256 { let ret = self.treap_map.search(|left_weight, node| { let can_sample = |weight| { can_sample_within_1559( weight, - block_gas_target, + gas_target, parent_base_price, min_base_price, ) @@ -204,7 +203,7 @@ impl PackingPool { if !can_sample(&right_weight) { return SearchDirection::Stop; } else { - return SearchDirection::RightOrStop(right_weight); + return SearchDirection::Right(right_weight); } }); match ret { @@ -286,20 +285,20 @@ fn can_sample_within_1559( return false; } - let gas_limit = - estimate_gas_limit(gas_target, weight.min_gas_price, parent_base_price); + let target_gas_used = + estimate_gas_used(gas_target, weight.min_gas_price, parent_base_price); - if gas_limit.is_zero() { + if target_gas_used.is_zero() { return false; } - if weight.gas_limit <= gas_limit { + if weight.gas_limit <= target_gas_used { return true; } weight .max_loss_ratio - .saturating_mul(weight.gas_limit - gas_limit) + .saturating_mul(weight.gas_limit - target_gas_used) < weight.weighted_loss_ratio } diff --git a/crates/cfxcore/packing-pool/src/weight.rs b/crates/cfxcore/packing-pool/src/weight.rs index ab90ee6930..4653f63043 100644 --- a/crates/cfxcore/packing-pool/src/weight.rs +++ b/crates/cfxcore/packing-pool/src/weight.rs @@ -12,7 +12,12 @@ pub struct PackingPoolWeight { impl ConsoliableWeight for PackingPoolWeight { #[inline] - fn empty() -> Self { Self::default() } + fn empty() -> Self { + Self { + min_gas_price: U256::max_value(), + ..Default::default() + } + } fn consolidate(a: &Self, b: &Self) -> Self { Self { diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index 09e693c193..e6d39705c3 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -1183,7 +1183,8 @@ impl Interpreter { self.stack.push(self.params.gas_price.clone()); } instructions::BASEFEE => { - self.stack.push(context.env().base_gas_price); + self.stack + .push(context.env().base_gas_price[context.space()]); } instructions::BLOCKHASH => { let block_number = self.stack.pop_back(); diff --git a/crates/cfxcore/vm-types/src/env.rs b/crates/cfxcore/vm-types/src/env.rs index 170f65514d..8b3a29e084 100644 --- a/crates/cfxcore/vm-types/src/env.rs +++ b/crates/cfxcore/vm-types/src/env.rs @@ -26,7 +26,7 @@ use std::collections::BTreeMap; -use cfx_types::{Address, Space, H256, U256}; +use cfx_types::{Address, Space, SpaceMap, H256, U256}; use primitives::BlockNumber; /// Information concerning the execution environment for a @@ -60,9 +60,9 @@ pub struct Env { pub transaction_epoch_bound: u64, /// Base gas price in CIP-1559, equals to 0 if CIP-1559 has not been /// activated - pub base_gas_price: U256, + pub base_gas_price: SpaceMap, /// Base gas price to miner according to in CIP-137 - pub burnt_gas_price: U256, + pub burnt_gas_price: SpaceMap, } #[cfg(test)] diff --git a/crates/client/benches/benchmark.rs b/crates/client/benches/benchmark.rs index be4c888e64..7b3cddc096 100644 --- a/crates/client/benches/benchmark.rs +++ b/crates/client/benches/benchmark.rs @@ -62,8 +62,8 @@ fn txexe_benchmark(c: &mut Criterion) { pos_view: None, finalized_epoch: None, transaction_epoch_bound: TRANSACTION_DEFAULT_EPOCH_BOUND, - base_gas_price: U256::zero(), - burnt_gas_price: U256::zero(), + base_gas_price: Default::default(), + burnt_gas_price: Default::default(), }; let mut group = c.benchmark_group("Execute 1 transaction"); group diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 458b8abd3d..87211cd280 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -20,7 +20,7 @@ use cfx_storage::{ defaults::DEFAULT_DEBUG_SNAPSHOT_CHECKER_THREADS, storage_dir, ConsensusParam, ProvideExtraSnapshotSyncConfig, StorageConfiguration, }; -use cfx_types::{Address, AllChainID, Space, H256, U256}; +use cfx_types::{Address, AllChainID, Space, SpaceMap, H256, U256}; use cfxcore::{ block_data_manager::{DataManagerConfiguration, DbType}, block_parameters::*, @@ -1221,6 +1221,14 @@ impl Configuration { params.early_set_internal_contracts_states = true; } + if !self.is_test_or_dev_mode() { + params.min_base_price = SpaceMap::new( + INITIAL_1559_CORE_BASE_PRICE, + INITIAL_1559_ETH_BASE_PRICE, + ) + .map_all(U256::from) + } + params.chain_id = self.chain_id_params(); params.anticone_penalty_ratio = self.raw_conf.anticone_penalty_ratio; params.evm_transaction_block_ratio = diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 671a2b4435..489cfb1067 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -965,6 +965,7 @@ impl RpcImpl { "RPC Request: generate_fixed_block({:?}, {:?}, {:?}, {:?}, {:?})", parent_hash, referee, num_txs, difficulty, pos_reference, ); + Ok(self.block_gen.generate_fixed_block( parent_hash, referee, diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index 60e906c989..ef71eb439a 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -6,7 +6,9 @@ use crate::{ block::BlockHeight, bytes::Bytes, hash::keccak, pos::PosBlockId, receipt::BlockReceipts, MERKLE_NULL_NODE, NULL_EPOCH, }; -use cfx_types::{Address, Bloom, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256}; +use cfx_types::{ + Address, Bloom, Space, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256, +}; use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps}; use once_cell::sync::OnceCell; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -181,14 +183,6 @@ impl BlockHeader { ) } - pub fn core_base_fee(&self) -> Option<&U256> { - self.base_price.as_ref().map(|l| &l.core_base_price) - } - - pub fn espace_base_fee(&self) -> Option<&U256> { - self.base_price.as_ref().map(|l| &l.espace_base_price) - } - /// Set the nonce field of the header. pub fn set_nonce(&mut self, nonce: U256) { self.nonce = nonce; } @@ -544,10 +538,13 @@ impl BlockHeaderBuilder { self } - pub fn with_cip1559_data( - &mut self, cip1559_data: Option, + pub fn with_base_price( + &mut self, maybe_base_price: Option>, ) -> &mut Self { - self.base_price = cip1559_data; + self.base_price = maybe_base_price.map(|x| BasePrice { + core_base_price: x[Space::Native], + espace_base_price: x[Space::Ethereum], + }); self } @@ -712,16 +709,19 @@ pub fn compute_next_price( } last_base_price - price_delta }; - next_base_price.min(min_base_price) + next_base_price.max(min_base_price) } -pub fn estimate_gas_limit( +pub fn estimate_gas_used( gas_target: U256, current_base_price: U256, last_base_price: U256, ) -> U256 { + // dbg!(gas_target, current_base_price, last_base_price); const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; - let upper_base_price = last_base_price + last_base_price / DENOM; - let lower_base_price = last_base_price - last_base_price / DENOM; + let delta = U256::max(U256::one(), last_base_price / DENOM); + + let upper_base_price = last_base_price + delta; + let lower_base_price = last_base_price - delta; if current_base_price > upper_base_price { gas_target * 2 diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index bec56f6d4d..08e7d02af0 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -122,8 +122,8 @@ pub enum TransactionError { /// Invalid RLP encoding InvalidRlp(String), ZeroGasPrice, - /// Ethereum-like transaction with invalid storage limit. - InvalidEthereumLike, + /// Transaction types have not been activated + FutureTransactionType, /// Receiver with invalid type bit. InvalidReceiver, /// Transaction nonce exceeds local limit. @@ -189,7 +189,7 @@ impl fmt::Display for TransactionError { format!("Transaction has invalid RLP structure: {}.", err) } ZeroGasPrice => "Zero gas price is not allowed".into(), - InvalidEthereumLike => "Ethereum like transaction should have u64::MAX storage limit".into(), + FutureTransactionType => "Ethereum like transaction should have u64::MAX storage limit".into(), InvalidReceiver => "Sending transaction to invalid address. The first four bits of address must be 0x0, 0x1, or 0x8.".into(), TooLargeNonce => "Transaction nonce is too large.".into(), }; @@ -350,6 +350,14 @@ impl Transaction { } } + pub fn before_1559(&self) -> bool { + matches!( + self, + Transaction::Native(TypedNativeTransaction::Cip155(_)) + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) + ) + } + pub fn access_list(&self) -> Option<&AccessList> { match self { Transaction::Native(tx) => tx.access_list(), diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 7caed90a4b..5aa998cd8d 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -10,13 +10,13 @@ from conflux.rpc import RpcClient from web3 import Web3 - class Eip1559Test(Web3Base): def set_test_params(self): self.num_nodes = 2 self.conf_parameters["evm_chain_id"] = str(10) self.conf_parameters["evm_transaction_block_ratio"] = str(1) self.conf_parameters["executive_trace"] = "true" + self.conf_parameters["cip1559_transition_height"] = str(1) def setup_network(self): self.add_nodes(self.num_nodes) @@ -56,7 +56,7 @@ def run_test(self): "nonce": nonce, "chainId": 10, }) - print(signed) + self.log.info("Signed transaction %s", signed) return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) self.rpc.generate_block(1) @@ -68,7 +68,7 @@ def run_test(self): assert_equal(receipt["txExecErrorMsg"], None) tx = self.w3.eth.get_transaction(return_tx_hash) - print(tx) + self.log.info("Get transaction from node %s", tx) assert_equal(receipt["status"], 1) # Check if another node can decode EIP1559 transactions From 8ecb1e3282e2738d1cbe1cbbea72790746f503d8 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 14 May 2024 16:51:54 +0800 Subject: [PATCH 045/137] Support more test for 1559 implementation --- crates/blockgen/src/lib.rs | 24 ++- .../cfxcore/core/src/transaction_pool/mod.rs | 171 +++++++++++++----- 2 files changed, 149 insertions(+), 46 deletions(-) diff --git a/crates/blockgen/src/lib.rs b/crates/blockgen/src/lib.rs index bdc0e89afc..fcf04860dc 100644 --- a/crates/blockgen/src/lib.rs +++ b/crates/blockgen/src/lib.rs @@ -599,6 +599,15 @@ impl BlockGenerator { &parent_hash, )?; + let maybe_base_price = self + .txpool + .compute_1559_base_price( + &parent_hash, + GENESIS_GAS_LIMIT.into(), + transactions.iter().map(|x| &**x), + ) + .expect("Cannot compute base price"); + let mut block = self.assemble_new_block_impl( parent_hash, referee, @@ -608,8 +617,7 @@ impl BlockGenerator { 0, Some(adaptive), self.get_pos_reference(&parent_hash), - // FIXME[1559]: check later - None, + maybe_base_price, ); if let Some(custom) = maybe_custom { block.block_header.set_custom(custom); @@ -629,6 +637,15 @@ impl BlockGenerator { &parent_hash, )?; + let maybe_base_price = self + .txpool + .compute_1559_base_price( + &parent_hash, + GENESIS_GAS_LIMIT.into(), + transactions.iter().map(|x| &**x), + ) + .expect("Cannot compute base price"); + let mut block = self.assemble_new_block_impl( parent_hash, referee, @@ -638,8 +655,7 @@ impl BlockGenerator { 0, Some(adaptive), self.get_pos_reference(&parent_hash), - // FIXME[1559] - None, + maybe_base_price, ); block.block_header.set_nonce(nonce); block.block_header.set_timestamp(timestamp); diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 2a965b16fb..4b2ea52820 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -39,8 +39,8 @@ use metrics::{ }; use parking_lot::{Mutex, RwLock}; use primitives::{ - block::BlockHeight, Account, SignedTransaction, Transaction, - TransactionWithSignature, + block::BlockHeight, block_header::compute_next_price_tuple, Account, + SignedTransaction, Transaction, TransactionWithSignature, }; use std::{ cmp::{max, min}, @@ -757,6 +757,71 @@ impl TransactionPool { ) } + // A helper function for python test. Not intented to be used in the + // production mode because of its inefficiency + // LINT: this function should not belongs to txpool, since it does not + // access the pools. However, since transaction pool has context for + // computing the base price, it is the most proper position at this + // time. May be fixed in future refactoring. + pub fn compute_1559_base_price<'a, I>( + &self, parent_hash: &H256, block_gas_limit: U256, txs: I, + ) -> Result>, String> + where I: Iterator + 'a { + let parent = self + .data_man + .block_header_by_hash(parent_hash) + .ok_or("Cannot find parent block")?; + let current_height = parent.height() + 1; + + let params = self.machine.params(); + let cip_1559_height = params.transition_heights.cip1559; + if current_height < cip_1559_height { + return Ok(None); + } + + let mut gas_used = SpaceMap::default(); + let mut min_gas_price = + SpaceMap::new(U256::max_value(), U256::max_value()); + for tx in txs { + gas_used[tx.space()] += *tx.gas_limit(); + min_gas_price[tx.space()] = + min_gas_price[tx.space()].min(*tx.gas_limit()); + } + + let core_gas_limit = block_gas_limit * 9 / 10; + let eth_gas_limit = if params.can_pack_evm_transaction(current_height) { + block_gas_limit * 5 / 10 + } else { + U256::zero() + }; + let gas_target = + SpaceMap::new(core_gas_limit, eth_gas_limit).map_all(|x| x / 2); + + let parent_base_price = if current_height == cip_1559_height { + params.init_base_price() + } else { + parent.base_price().unwrap() + }; + + let min_base_price = params.min_base_price(); + + let base_price = SpaceMap::zip4( + gas_target, + gas_used, + parent_base_price, + min_base_price, + ) + .map_all(compute_next_price_tuple); + + for space in [Space::Native, Space::Ethereum] { + if base_price[space] > min_gas_price[space] { + return Err(format!("Not sufficient min price in space {:?}, expected {:?}, actual {:?}", space, base_price[space], min_gas_price[space])); + } + } + + Ok(Some(base_price)) + } + pub fn notify_modified_accounts( &self, accounts_from_execution: Vec, ) { @@ -941,49 +1006,71 @@ impl TransactionPool { let cip1559_height = params.transition_heights.cip1559; let pack_height = consensus_best_info_clone.best_epoch_number + 1; - let (transactions_from_pool, maybe_base_price) = - if pack_height < cip1559_height { - let evm_gas_limit = if self - .machine - .params() - .can_pack_evm_transaction(pack_height) - { - self_gas_limit / params.evm_transaction_gas_ratio - } else { - U256::zero() - }; - - let txs = self.pack_transactions( - num_txs, - self_gas_limit.clone(), - evm_gas_limit, - block_size_limit, - consensus_best_info_clone.best_epoch_number, - consensus_best_info_clone.best_block_number, - ); - (txs, None) + let (transactions_from_pool, maybe_base_price) = if pack_height + < cip1559_height + { + let evm_gas_limit = if self + .machine + .params() + .can_pack_evm_transaction(pack_height) + { + self_gas_limit / params.evm_transaction_gas_ratio } else { - let parent_base_price = if pack_height == cip1559_height { - params.init_base_price() - } else { - parent_block.base_price().unwrap() - }; - - let (tx, base_price) = self.pack_transactions_1559( - num_txs, - self_gas_limit.clone(), - parent_base_price, - block_size_limit, - consensus_best_info_clone.best_epoch_number, - consensus_best_info_clone.best_block_number, - ); - (tx, Some(base_price)) + U256::zero() }; - if cip1559_height >= pack_height && !additional_transactions.is_empty() - { - eprintln!("The current version cannot handle CIP-1559 with additional transactions. "); - } + let txs = self.pack_transactions( + num_txs, + self_gas_limit.clone(), + evm_gas_limit, + block_size_limit, + consensus_best_info_clone.best_epoch_number, + consensus_best_info_clone.best_block_number, + ); + (txs, None) + } else { + let parent_base_price = if pack_height == cip1559_height { + params.init_base_price() + } else { + parent_block.base_price().unwrap() + }; + + let (txs, packing_base_price) = self.pack_transactions_1559( + num_txs, + self_gas_limit.clone(), + parent_base_price, + block_size_limit, + consensus_best_info_clone.best_epoch_number, + consensus_best_info_clone.best_block_number, + ); + + let mut base_price = packing_base_price; + + // May only happens in test mode + if !additional_transactions.is_empty() { + let iter = additional_transactions + .iter() + .chain(txs.iter()) + .map(|x| &**x); + match self.compute_1559_base_price( + &parent_block.hash(), + self_gas_limit, + iter, + ) { + Ok(Some(p)) => { + base_price = p; + } + Ok(None) => { + warn!("Should not happen"); + } + Err(e) => { + error!("Cannot compute base price with additinal transactions: {}", e); + eprintln!("Cannot compute base price with additinal transactions: {}", e); + } + } + } + (txs, Some(base_price)) + }; let transactions = [ additional_transactions.as_slice(), From c3f3ad05577fde1db1b6d1f18458ef5593b0faf6 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 14 May 2024 18:05:54 +0800 Subject: [PATCH 046/137] fix top level call status issue --- .../src/observer/geth_tracer/mod.rs | 16 +++++++++------- .../observer/geth_tracer/tracing_inspector.rs | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs index 875d5f1ead..981eaf1435 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs @@ -54,8 +54,6 @@ pub struct GethTracer { // call depth depth: usize, // - return_data: Bytes, // update in call_result/create_result - // opts: GethDebugTracingOptions, // gas stack, used to trace gas_spent in call_result/create_result pub gas_stack: Vec, @@ -101,7 +99,6 @@ impl GethTracer { tx_gas_limit, depth: 0, gas_left: tx_gas_limit, - return_data: Bytes::default(), opts, gas_stack: Vec::new(), } @@ -168,7 +165,11 @@ impl GethTracer { }, None => { let gas_used = self.gas_used(); - let return_value = self.return_data; + let return_value = self + .inner + .last_call_return_data + .clone() + .unwrap_or_default(); let opts = self.opts.config; let frame = self.inner.into_geth_builder().geth_traces( gas_used, @@ -276,7 +277,6 @@ impl CallTracer for GethTracer { .as_ref() .map(|f| Bytes::from(f.return_data.to_vec())) .unwrap_or_default(); - self.return_data = output.clone(); let outcome = InterpreterResult { result: instruction_result, @@ -344,7 +344,6 @@ impl CallTracer for GethTracer { .as_ref() .map(|f| Bytes::from(f.return_data.to_vec())) .unwrap_or_default(); - self.return_data = output.clone(); let outcome = InterpreterResult { result: instruction_result, @@ -441,7 +440,10 @@ struct StackStep { pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { let result = match frame_result { - Ok(_r) => InstructionResult::Return, + Ok(r) => match r.apply_state { + true => InstructionResult::Return, + false => InstructionResult::Revert, + }, Err(err) => match err { Error::OutOfGas => InstructionResult::OutOfGas, Error::BadJumpDestination { .. } => InstructionResult::InvalidJump, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs index 4291224ae3..cc23b8f7c1 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -29,7 +29,7 @@ pub struct TracingInspector { /// Tracks active steps step_stack: Vec, /// Tracks the return value of the last call - last_call_return_data: Option, + pub last_call_return_data: Option, /// The gas inspector used to track remaining gas. pub gas_inspector: GasInspector, // From dec0ddcb1452b91980a2e0357e164084098ca654 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 14 May 2024 19:40:45 +0800 Subject: [PATCH 047/137] Pass more tests for 1559 --- crates/blockgen/src/lib.rs | 54 +++++++++++++++---- crates/cfxcore/core/src/error.rs | 6 +-- .../cfxcore/core/src/transaction_pool/mod.rs | 11 +++- crates/cfxcore/core/src/verification.rs | 16 +++--- crates/client/src/configuration.rs | 12 ++++- ...ter_fork_finalize_state_after_fork_test.py | 1 + ...filter_fork_finalize_state_in_fork_test.py | 1 + tests/expire_block_test.py | 2 + tests/fork_same_height_hiding_test.py | 1 + .../mpt_snapshot_null_epoch_test.py | 1 + tests/full_node_tests/mpt_snapshot_test.py | 1 + tests/full_node_tests/p2p_era_test.py | 1 + tests/full_node_tests/reboot_test.py | 1 + ..._checkpoint_snapshot_before_stable_test.py | 1 + tests/full_node_tests/sync_checkpoint_test.py | 5 +- tests/invalid_block_sync_test.py | 2 + tests/invalid_message_test.py | 3 ++ tests/light/log_filtering_test.py | 2 + tests/light/rpc_test.py | 2 + tests/log_filtering_test.py | 2 + tests/message_test.py | 2 + tests/pos/hard_fork_test.py | 2 + tests/rpc_test.py | 2 + tests/secondary_reward_test.py | 2 + tests/sync_test.py | 2 + 25 files changed, 109 insertions(+), 26 deletions(-) diff --git a/crates/blockgen/src/lib.rs b/crates/blockgen/src/lib.rs index fcf04860dc..35b27a0616 100644 --- a/crates/blockgen/src/lib.rs +++ b/crates/blockgen/src/lib.rs @@ -7,7 +7,9 @@ use crate::miner::{ stratum::{Options as StratumOption, Stratum}, work_notify::NotifyWork, }; -use cfx_parameters::consensus::GENESIS_GAS_LIMIT; +use cfx_parameters::{ + consensus::GENESIS_GAS_LIMIT, consensus_internal::ELASTICITY_MULTIPLIER, +}; use cfx_types::{Address, SpaceMap, H256, U256}; use cfxcore::{ block_parameters::*, @@ -290,7 +292,7 @@ impl BlockGenerator { /// only pub fn assemble_new_fixed_block( &self, parent_hash: H256, referee: Vec, num_txs: usize, - difficulty: u64, adaptive: bool, block_gas_limit: u64, + difficulty: u64, adaptive: bool, block_gas_target: u64, pos_reference: Option, ) -> Result { let consensus_graph = self.consensus_graph(); @@ -299,7 +301,6 @@ impl BlockGenerator { &parent_hash, )?; - let block_gas_limit = block_gas_limit.into(); let block_size_limit = self.graph.verification_config.max_block_size_in_bytes; let best_info = consensus_graph.best_info(); @@ -316,6 +317,12 @@ impl BlockGenerator { let cip1559_height = params.transition_heights.cip1559; let pack_height = best_info.best_epoch_number + 1; + let block_gas_limit = if pack_height >= cip1559_height { + (block_gas_target * ELASTICITY_MULTIPLIER as u64).into() + } else { + block_gas_target.into() + }; + let (transactions, maybe_base_price) = if pack_height < cip1559_height { let txs = self.txpool.pack_transactions( num_txs, @@ -560,9 +567,26 @@ impl BlockGenerator { ) -> H256 { let consensus_graph = self.consensus_graph(); // get the best block - let (best_info, block_gas_limit, _, maybe_base_price) = self + let (best_info, _, _, _) = self .txpool .get_best_info_with_packed_transactions(0, 0, Vec::new()); + + let parent_hash = best_info.best_block_hash; + let maybe_base_price = self + .txpool + .compute_1559_base_price( + &parent_hash, + (GENESIS_GAS_LIMIT * ELASTICITY_MULTIPLIER as u64).into(), + transactions.iter().map(|x| &**x), + ) + .unwrap(); + let block_gas_limit = GENESIS_GAS_LIMIT + * if maybe_base_price.is_some() { + ELASTICITY_MULTIPLIER as u64 + } else { + 1 + }; + let state_blame_info = consensus_graph .get_blame_and_deferred_state_for_generation( &best_info.best_block_hash, @@ -577,7 +601,7 @@ impl BlockGenerator { best_block_hash, referee, state_blame_info, - block_gas_limit, + block_gas_limit.into(), transactions, 0, adaptive, @@ -603,16 +627,22 @@ impl BlockGenerator { .txpool .compute_1559_base_price( &parent_hash, - GENESIS_GAS_LIMIT.into(), + (GENESIS_GAS_LIMIT * ELASTICITY_MULTIPLIER as u64).into(), transactions.iter().map(|x| &**x), ) .expect("Cannot compute base price"); + let block_gas_limit = if maybe_base_price.is_some() { + GENESIS_GAS_LIMIT * ELASTICITY_MULTIPLIER as u64 + } else { + GENESIS_GAS_LIMIT + }; + let mut block = self.assemble_new_block_impl( parent_hash, referee, state_blame_info, - GENESIS_GAS_LIMIT.into(), + block_gas_limit.into(), transactions, 0, Some(adaptive), @@ -641,16 +671,22 @@ impl BlockGenerator { .txpool .compute_1559_base_price( &parent_hash, - GENESIS_GAS_LIMIT.into(), + (GENESIS_GAS_LIMIT * ELASTICITY_MULTIPLIER as u64).into(), transactions.iter().map(|x| &**x), ) .expect("Cannot compute base price"); + let block_gas_limit = if maybe_base_price.is_some() { + GENESIS_GAS_LIMIT * ELASTICITY_MULTIPLIER as u64 + } else { + GENESIS_GAS_LIMIT + }; + let mut block = self.assemble_new_block_impl( parent_hash, referee, state_blame_info, - GENESIS_GAS_LIMIT.into(), + block_gas_limit.into(), transactions, 0, Some(adaptive), diff --git a/crates/cfxcore/core/src/error.rs b/crates/cfxcore/core/src/error.rs index b08323d06f..c891f36a5d 100644 --- a/crates/cfxcore/core/src/error.rs +++ b/crates/cfxcore/core/src/error.rs @@ -34,7 +34,7 @@ pub enum BlockError { /// Gas limit header field is invalid. InvalidGasLimit(OutOfBounds), /// Total gas limits of transactions in block is out of bound. - InvalidBlockGasLimit(OutOfBounds), + InvalidPackedGasLimit(OutOfBounds), /// Total rlp sizes of transactions in block is out of bound. InvalidBlockSize(OutOfBounds), InvalidBasePrice(Mismatch>), @@ -98,8 +98,8 @@ impl fmt::Display for BlockError { InvalidBasePrice(ref mis) => { format!("Invalid base price: {:?}", mis) } - InvalidBlockGasLimit(ref oob) => { - format!("Invalid block gas limit: {}", oob) + InvalidPackedGasLimit(ref oob) => { + format!("Invalid packed gas limit: {}", oob) } InvalidBlockSize(ref oob) => format!("Invalid block size: {}", oob), InvalidTimestamp(ref oob) => { diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 4b2ea52820..cba95deb02 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -1000,8 +1000,15 @@ impl TransactionPool { + parent_block_gas_limit / gas_limit_divisor - 1; - let target_gas_limit = self.config.target_block_gas_limit.into(); - let self_gas_limit = min(max(target_gas_limit, gas_lower), gas_upper); + let target_gas_limit = self.config.target_block_gas_limit + * if pack_height >= cip1559_height { + ELASTICITY_MULTIPLIER as u64 + } else { + 1 + }; + + let self_gas_limit = + min(max(target_gas_limit.into(), gas_lower), gas_upper); let cip1559_height = params.transition_heights.cip1559; let pack_height = consensus_best_info_clone.best_epoch_number + 1; diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index d3cf37f0f5..e4dffd6202 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -522,9 +522,9 @@ impl VerificationConfig { let block_total_gas = total_gas.map_sum(|x| *x); if evm_total_gas > evm_space_gas_limit { - return Err(From::from(BlockError::InvalidBlockGasLimit( + return Err(From::from(BlockError::InvalidPackedGasLimit( OutOfBounds { - min: Some(evm_space_gas_limit), + min: None, max: Some(evm_space_gas_limit), found: evm_total_gas, }, @@ -532,9 +532,9 @@ impl VerificationConfig { } if block_total_gas > *block.block_header.gas_limit() { - return Err(From::from(BlockError::InvalidBlockGasLimit( + return Err(From::from(BlockError::InvalidPackedGasLimit( OutOfBounds { - min: Some(*block.block_header.gas_limit()), + min: None, max: Some(*block.block_header.gas_limit()), found: block_total_gas, }, @@ -564,9 +564,9 @@ impl VerificationConfig { }; if total_gas[Ethereum] > espace_gas_limit { - return Err(From::from(BlockError::InvalidBlockGasLimit( + return Err(From::from(BlockError::InvalidPackedGasLimit( OutOfBounds { - min: Some(espace_gas_limit), + min: None, max: Some(espace_gas_limit), found: total_gas[Ethereum], }, @@ -574,9 +574,9 @@ impl VerificationConfig { } if total_gas[Native] > core_gas_limit { - return Err(From::from(BlockError::InvalidBlockGasLimit( + return Err(From::from(BlockError::InvalidPackedGasLimit( OutOfBounds { - min: Some(core_gas_limit), + min: None, max: Some(core_gas_limit), found: total_gas[Native], }, diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 87211cd280..164cf2f4f0 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1400,8 +1400,16 @@ impl Configuration { params.transition_heights => { cip130, cip133e } ); // TODO: disable 1559 test during dev - params.transition_heights.cip1559 = - self.raw_conf.cip1559_transition_height.unwrap_or(u64::MAX); + params.transition_heights.cip1559 = self + .raw_conf + .cip1559_transition_height + .unwrap_or(non_genesis_default_transition_time); + + if params.transition_heights.cip1559 + < self.raw_conf.pos_reference_enable_height + { + panic!("1559 can not be activated earlier than pos reference: 1559 (epoch {}), pos (epoch {})", params.transition_heights.cip1559, self.raw_conf.pos_reference_enable_height); + } } } diff --git a/tests/evm_space/filter_fork_finalize_state_after_fork_test.py b/tests/evm_space/filter_fork_finalize_state_after_fork_test.py index e51cdbfe85..b9c432f372 100644 --- a/tests/evm_space/filter_fork_finalize_state_after_fork_test.py +++ b/tests/evm_space/filter_fork_finalize_state_after_fork_test.py @@ -35,6 +35,7 @@ def set_test_params(self): # No auto timeout. self.pos_parameters["round_time_ms"] = 1000000000 self.conf_parameters["pos_reference_enable_height"] = 10 + self.conf_parameters["cip1559_transition_height"] = 10 async def run_async(self): clients = [] diff --git a/tests/evm_space/filter_fork_finalize_state_in_fork_test.py b/tests/evm_space/filter_fork_finalize_state_in_fork_test.py index 98e03d9ee0..708078bbcb 100644 --- a/tests/evm_space/filter_fork_finalize_state_in_fork_test.py +++ b/tests/evm_space/filter_fork_finalize_state_in_fork_test.py @@ -36,6 +36,7 @@ def set_test_params(self): # No auto timeout. self.pos_parameters["round_time_ms"] = 1000000000 self.conf_parameters["pos_reference_enable_height"] = 10 + self.conf_parameters["cip1559_transition_height"] = 10 self.conf_parameters["adaptive_weight_beta"] = "1" async def run_async(self): diff --git a/tests/expire_block_test.py b/tests/expire_block_test.py index 3df08f4b11..b926a9345a 100755 --- a/tests/expire_block_test.py +++ b/tests/expire_block_test.py @@ -16,6 +16,8 @@ def set_test_params(self): self.conf_parameters["era_epoch_count"] = "100" self.conf_parameters["dev_snapshot_epoch_count"] = "50" self.conf_parameters["anticone_penalty_ratio"] = "10" + # Disable 1559 as it uses an incompatible old format. + self.conf_parameters["cip1559_transition_height"] = str(99999999) self.num_nodes = 2 def setup_network(self): diff --git a/tests/fork_same_height_hiding_test.py b/tests/fork_same_height_hiding_test.py index ca7e1d1335..cc061b621e 100755 --- a/tests/fork_same_height_hiding_test.py +++ b/tests/fork_same_height_hiding_test.py @@ -17,6 +17,7 @@ def set_test_params(self): self.num_nodes = 2 # Disable pos reference because pow blocks are generated too fast. self.conf_parameters["pos_reference_enable_height"] = '100000' + self.conf_parameters["cip1559_transition_height"] = '100000' def setup_network(self): self.setup_nodes() diff --git a/tests/full_node_tests/mpt_snapshot_null_epoch_test.py b/tests/full_node_tests/mpt_snapshot_null_epoch_test.py index a8d46e17a3..1984632193 100644 --- a/tests/full_node_tests/mpt_snapshot_null_epoch_test.py +++ b/tests/full_node_tests/mpt_snapshot_null_epoch_test.py @@ -26,6 +26,7 @@ def set_test_params(self): "dev_allow_phase_change_without_peer": "false", # Disable pos reference because pow blocks are generated too fast. "pos_reference_enable_height": "10000", + "cip1559_transition_height": "10000", "node_type": "\"archive\"", "use_isolated_db_for_mpt_table": "true", } diff --git a/tests/full_node_tests/mpt_snapshot_test.py b/tests/full_node_tests/mpt_snapshot_test.py index 27216566e8..a928ce61fb 100644 --- a/tests/full_node_tests/mpt_snapshot_test.py +++ b/tests/full_node_tests/mpt_snapshot_test.py @@ -26,6 +26,7 @@ def set_test_params(self): "dev_allow_phase_change_without_peer": "false", # Disable pos reference because pow blocks are generated too fast. "pos_reference_enable_height": "10000", + "cip1559_transition_height": "10000", "use_isolated_db_for_mpt_table": "false", } diff --git a/tests/full_node_tests/p2p_era_test.py b/tests/full_node_tests/p2p_era_test.py index 0be10f066a..1219e2e3de 100755 --- a/tests/full_node_tests/p2p_era_test.py +++ b/tests/full_node_tests/p2p_era_test.py @@ -30,6 +30,7 @@ def set_test_params(self): # it goes through all the phases to download data as a normal node. self.conf_parameters["dev_allow_phase_change_without_peer"] = "false" self.conf_parameters["pos_reference_enable_height"] = 600 + self.conf_parameters["cip1559_transition_height"] = 600 self.stop_probability = 0.01 self.clean_probability = 0.5 diff --git a/tests/full_node_tests/reboot_test.py b/tests/full_node_tests/reboot_test.py index 165b0defc6..ef1682e49d 100644 --- a/tests/full_node_tests/reboot_test.py +++ b/tests/full_node_tests/reboot_test.py @@ -24,6 +24,7 @@ def set_test_params(self): "dev_allow_phase_change_without_peer": "false", # Disable pos reference because pow blocks are generated too fast. "pos_reference_enable_height": "10000", + "cip1559_transition_height": "10000", "keep_snapshot_before_stable_checkpoint": "false", # force recompute with parent snapshot doesn't exist "force_recompute_height_during_construct_pivot": "1501", diff --git a/tests/full_node_tests/sync_checkpoint_snapshot_before_stable_test.py b/tests/full_node_tests/sync_checkpoint_snapshot_before_stable_test.py index bb804792bd..c45ddb483c 100755 --- a/tests/full_node_tests/sync_checkpoint_snapshot_before_stable_test.py +++ b/tests/full_node_tests/sync_checkpoint_snapshot_before_stable_test.py @@ -26,6 +26,7 @@ def set_test_params(self): "dev_allow_phase_change_without_peer": "false", # Disable pos reference because pow blocks are generated too fast. "pos_reference_enable_height": "10000", + "cip1559_transition_height": "10000", "keep_snapshot_before_stable_checkpoint": "false", } diff --git a/tests/full_node_tests/sync_checkpoint_test.py b/tests/full_node_tests/sync_checkpoint_test.py index 5634360033..9f290bd800 100755 --- a/tests/full_node_tests/sync_checkpoint_test.py +++ b/tests/full_node_tests/sync_checkpoint_test.py @@ -26,7 +26,8 @@ def set_test_params(self): # Make sure checkpoint synchronization is triggered during phase change. "dev_allow_phase_change_without_peer": "false", # Disable pos reference because pow blocks are generated too fast. - "pos_reference_enable_height": "10000" + "pos_reference_enable_height": "10000", + "cip1559_transition_height": "10000", } def setup_network(self): @@ -36,7 +37,7 @@ def setup_network(self): connect_sample_nodes(self.nodes[:-1], self.log, latency_max=1) for i in range(self.num_nodes - 1): self.nodes[i].wait_for_recovery(["NormalSyncPhase"], 10) - + def _generate_txs(self, peer, num): client = RpcClient(self.nodes[peer]) txs = [] diff --git a/tests/invalid_block_sync_test.py b/tests/invalid_block_sync_test.py index a005b5d4f9..705b98bbc1 100755 --- a/tests/invalid_block_sync_test.py +++ b/tests/invalid_block_sync_test.py @@ -69,6 +69,8 @@ class InvalidBodySyncTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 2 self.conf_parameters["dev_allow_phase_change_without_peer"] = "false" + # Disable 1559 because the client submit old format data + self.conf_parameters["cip1559_transition_height"] = str(99999999) def setup_network(self): self.add_nodes(self.num_nodes) diff --git a/tests/invalid_message_test.py b/tests/invalid_message_test.py index cee7212686..bac7272b61 100755 --- a/tests/invalid_message_test.py +++ b/tests/invalid_message_test.py @@ -14,6 +14,9 @@ class InvalidMessageTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 4 + # Disable 1559 for RPC tests temporarily + self.conf_parameters["cip1559_transition_height"] = str(99999999) + def setup_network(self): self.setup_nodes() diff --git a/tests/light/log_filtering_test.py b/tests/light/log_filtering_test.py index 31f646c6ca..d7b03a5ca8 100755 --- a/tests/light/log_filtering_test.py +++ b/tests/light/log_filtering_test.py @@ -28,6 +28,8 @@ class LogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 3 + # Disable 1559 for RPC tests temporarily + self.conf_parameters["cip1559_transition_height"] = str(99999999) def setup_network(self): self.add_nodes(self.num_nodes) diff --git a/tests/light/rpc_test.py b/tests/light/rpc_test.py index 97da74c086..6f2216a345 100755 --- a/tests/light/rpc_test.py +++ b/tests/light/rpc_test.py @@ -37,6 +37,8 @@ def set_test_params(self): self.conf_parameters["timer_chain_beta"] = "20" self.conf_parameters["timer_chain_block_difficulty_ratio"] = "3" self.conf_parameters["block_cache_gc_period_ms"] = "10" + # Disable 1559 for RPC tests temporarily + self.conf_parameters["cip1559_transition_height"] = str(99999999) def deploy_contract(self, data_hex): tx = self.rpc[FULLNODE0].new_contract_tx(receiver="", data_hex=data_hex, storage_limit=2000) diff --git a/tests/log_filtering_test.py b/tests/log_filtering_test.py index 9357eba0e0..54e11f5214 100755 --- a/tests/log_filtering_test.py +++ b/tests/log_filtering_test.py @@ -21,6 +21,8 @@ class LogFilteringTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 + # Disable 1559 because it has hardcore execution result not compatible with 1559 + self.conf_parameters["cip1559_transition_height"] = str(99999999) def setup_network(self): self.setup_nodes() diff --git a/tests/message_test.py b/tests/message_test.py index 8582725358..ef8fd81f36 100755 --- a/tests/message_test.py +++ b/tests/message_test.py @@ -13,6 +13,8 @@ class MessageTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 4 + # Disable 1559 for RPC tests temporarily + self.conf_parameters["cip1559_transition_height"] = str(99999999) def setup_network(self): self.setup_nodes() diff --git a/tests/pos/hard_fork_test.py b/tests/pos/hard_fork_test.py index 1b5e9f6e6d..df906446cc 100755 --- a/tests/pos/hard_fork_test.py +++ b/tests/pos/hard_fork_test.py @@ -43,6 +43,7 @@ def set_test_params(self): self.conf_parameters["hydra_transition_number"] = 300 self.conf_parameters["cip43_init_end_number"] = 500 self.conf_parameters["pos_reference_enable_height"] = 1000 + self.conf_parameters["cip1559_transition_height"] = 1000 self.conf_parameters["era_epoch_count"] = 200 self.conf_parameters["pos_round_per_term"] = 10 self.conf_parameters["pos_term_max_size"] = 100 @@ -51,6 +52,7 @@ def set_test_params(self): self.conf_parameters["sigma_fix_transition_number"] = 1000000 self.conf_parameters["tanzanite_transition_height"] = 100 self.conf_parameters["cip112_transition_height"] = 100 + self.rpc_timewait = 6000 def setup_nodes(self): diff --git a/tests/rpc_test.py b/tests/rpc_test.py index 4b4e6fac2b..61c4b180d8 100755 --- a/tests/rpc_test.py +++ b/tests/rpc_test.py @@ -19,6 +19,8 @@ def set_test_params(self): self.conf_parameters = { "executive_trace": "true", "public_rpc_apis": "\"cfx,debug,test,pubsub,trace\"", + # Disable 1559 for RPC tests temporarily + "cip1559_transition_height": str(99999999), } def setup_network(self): diff --git a/tests/secondary_reward_test.py b/tests/secondary_reward_test.py index 72f19b1d00..4dc0e58aed 100755 --- a/tests/secondary_reward_test.py +++ b/tests/secondary_reward_test.py @@ -16,6 +16,8 @@ def set_test_params(self): self.conf_parameters = {"mining_author": "\"10000000000000000000000000000000000000aa\"", "mining_type": "'disable'" } + # Disable 1559 because it has hardcore execution result not compatible with 1559 + self.conf_parameters["cip1559_transition_height"] = str(99999999) self.gasPrice = 1 def setup_network(self): diff --git a/tests/sync_test.py b/tests/sync_test.py index f0b92281f1..8fcf61a718 100755 --- a/tests/sync_test.py +++ b/tests/sync_test.py @@ -10,6 +10,8 @@ class SyncTest(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 2 + # Disable 1559 because it has hardcore execution result not compatible with 1559 + self.conf_parameters["cip1559_transition_height"] = str(99999999) def setup_network(self): self.add_nodes(self.num_nodes) From 729cd2a774cb227993887c57f09cae3981cef892 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 14 May 2024 21:58:19 +0800 Subject: [PATCH 048/137] Fix --- crates/cfxcore/core/src/transaction_pool/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index cba95deb02..20354f3d2d 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -1010,9 +1010,6 @@ impl TransactionPool { let self_gas_limit = min(max(target_gas_limit.into(), gas_lower), gas_upper); - let cip1559_height = params.transition_heights.cip1559; - let pack_height = consensus_best_info_clone.best_epoch_number + 1; - let (transactions_from_pool, maybe_base_price) = if pack_height < cip1559_height { @@ -1072,7 +1069,6 @@ impl TransactionPool { } Err(e) => { error!("Cannot compute base price with additinal transactions: {}", e); - eprintln!("Cannot compute base price with additinal transactions: {}", e); } } } From f648479fd2eb6f645e2b4f5b801b2fff80a7b1f1 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 13:34:31 +0800 Subject: [PATCH 049/137] Finish espace RPC and check y_parity --- crates/cfx_key/src/error.rs | 3 + .../cfxcore/core/src/transaction_pool/mod.rs | 31 ++++ crates/cfxcore/core/src/verification.rs | 3 +- .../executor/src/state/state_object/reward.rs | 2 + crates/client/src/rpc/impls/eth.rs | 35 +++- crates/client/src/rpc/types/eth/block.rs | 5 +- crates/client/src/rpc/types/eth/receipt.rs | 171 +----------------- .../client/src/rpc/types/eth/transaction.rs | 36 ++-- crates/primitives/src/transaction/mod.rs | 36 +++- 9 files changed, 124 insertions(+), 198 deletions(-) diff --git a/crates/cfx_key/src/error.rs b/crates/cfx_key/src/error.rs index f9bde1d280..427fdf648e 100644 --- a/crates/cfx_key/src/error.rs +++ b/crates/cfx_key/src/error.rs @@ -27,6 +27,8 @@ pub enum Error { InvalidAddress, /// Invalid EC signature InvalidSignature, + /// Invalid y-parity + InvalidYParity, /// Invalid AES message InvalidMessage, /// IO Error @@ -42,6 +44,7 @@ impl fmt::Display for Error { Error::InvalidPublic => "Invalid public".into(), Error::InvalidAddress => "Invalid address".into(), Error::InvalidSignature => "Invalid EC signature".into(), + Error::InvalidYParity => "Invalid y Parity".into(), Error::InvalidMessage => "Invalid AES message".into(), Error::Io(ref err) => format!("I/O error: {}", err), Error::Custom(ref s) => s.clone(), diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index 20354f3d2d..e83ea75733 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -953,6 +953,37 @@ impl TransactionPool { Ok(()) } + // For RPC use only + pub fn get_best_info_with_parent_base_price( + &self, + ) -> (Arc, Option>) { + let consensus_best_info_clone = self.consensus_best_info.lock().clone(); + debug!( + "get_best_info_with_base_price: {:?}", + consensus_best_info_clone + ); + + let params = self.machine.params(); + let parent_block = self + .data_man + .block_header_by_hash(&consensus_best_info_clone.best_block_hash) + // The parent block must exists. + .expect(&concat!(file!(), ":", line!(), ":", column!())); + + let cip1559_height = params.transition_heights.cip1559; + let pack_height = consensus_best_info_clone.best_epoch_number + 1; + + ( + consensus_best_info_clone, + if pack_height <= cip1559_height { + None + } else { + // TODO: should we compute for the current base_price? + Some(parent_block.base_price().unwrap()) + }, + ) + } + pub fn get_best_info_with_packed_transactions( &self, num_txs: usize, block_size_limit: usize, additional_transactions: Vec>, diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index e4dffd6202..331bd46645 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -701,6 +701,7 @@ impl VerificationConfig { mode: VerifyTxMode, ) -> Result<(), TransactionError> { tx.check_low_s()?; + tx.check_y_parity()?; // Disallow unsigned transactions if tx.is_unsigned() { @@ -791,7 +792,7 @@ impl VerificationConfig { fn check_eip1559_transaction( tx: &TransactionWithSignature, cip1559: bool, mode: &VerifyTxMode, ) -> bool { - if tx.before_1559() { + if tx.is_legacy() { return true; } diff --git a/crates/cfxcore/executor/src/state/state_object/reward.rs b/crates/cfxcore/executor/src/state/state_object/reward.rs index 7a2c200079..0e5f0cd3c7 100644 --- a/crates/cfxcore/executor/src/state/state_object/reward.rs +++ b/crates/cfxcore/executor/src/state/state_object/reward.rs @@ -50,6 +50,8 @@ impl State { } pub fn burn_by_cip1559(&mut self, by: U256) { + // This function is called after transaction exeuction. At this time, + // the paid transaction fee has already been in the core space. *self.global_stat.val::() += by; self.sub_total_issued(by); } diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 49a4fd8a72..124e61d7fc 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -238,7 +238,7 @@ impl EthHandler { let transaction_hash = tx.hash(); let transaction_index: U256 = idx.into(); let block_hash = b.pivot_header.hash(); - let block_number: U256 = b.pivot_header.height().into(); + let block_height: U256 = b.pivot_header.height().into(); let logs: Vec<_> = receipt .logs @@ -250,7 +250,7 @@ impl EthHandler { topics: log.topics, data: Bytes(log.data), block_hash, - block_number, + block_number: block_height, transaction_hash, transaction_index, log_index: Some((*prior_log_index + idx).into()), @@ -275,6 +275,18 @@ impl EthHandler { Some(b.errors[idx].clone()) }; + let effective_gas_price = + if let Some(base_price) = b.pivot_header.base_price() { + let base_price = base_price[tx.space()]; + if *tx.gas_price() < base_price { + *tx.gas_price() + } else { + tx.effective_gas_price(&base_price) + } + } else { + *tx.gas_price() + }; + Ok(Receipt { transaction_hash, transaction_index, @@ -284,7 +296,7 @@ impl EthHandler { Action::Create => None, Action::Call(addr) => Some(*addr), }, - block_number, + block_number: block_height, cumulative_gas_used: receipt.accumulated_gas_used, gas_used, contract_address, @@ -294,8 +306,10 @@ impl EthHandler { .outcome_status .in_space(Space::Ethereum) .into(), - effective_gas_price: *tx.gas_price(), + effective_gas_price, tx_exec_error_msg, + type_id: receipt.burnt_gas_fee.is_some().then_some(tx.type_id()), + burnt_gas_fee: receipt.burnt_gas_fee, }) } @@ -377,6 +391,12 @@ impl Eth for EthHandler { fn gas_price(&self) -> jsonrpc_core::Result { info!("RPC Request: eth_gasPrice"); + let (_, maybe_base_price) = + self.tx_pool.get_best_info_with_parent_base_price(); + if let Some(base_price) = maybe_base_price { + return Ok(base_price[Space::Ethereum]); + } + let consensus_gas_price = self .consensus_graph() .gas_price(Space::Ethereum) @@ -389,8 +409,11 @@ impl Eth for EthHandler { fn max_priority_fee_per_gas(&self) -> jsonrpc_core::Result { info!("RPC Request: eth_maxPriorityFeePerGas"); - // TODO: Change this - Ok(U256::from(20000000000u64)) + let (_, maybe_base_price) = + self.tx_pool.get_best_info_with_parent_base_price(); + let answer = maybe_base_price + .map_or(U256::from(20000000000u64), |x| x[Space::Ethereum]); + Ok(answer) } fn accounts(&self) -> jsonrpc_core::Result> { diff --git a/crates/client/src/rpc/types/eth/block.rs b/crates/client/src/rpc/types/eth/block.rs index acf632bf42..a63f7ffe84 100644 --- a/crates/client/src/rpc/types/eth/block.rs +++ b/crates/client/src/rpc/types/eth/block.rs @@ -203,7 +203,10 @@ impl Block { timestamp: pb.pivot_header.timestamp().into(), difficulty: pb.pivot_header.difficulty().into(), total_difficulty: 0.into(), - base_fee_per_gas: None, + base_fee_per_gas: pb + .pivot_header + .base_price() + .map(|x| x[Space::Ethereum]), uncles: vec![], // Note: we allow U256 nonce in Stratum and in the block. // However, most mining clients use U64. Here we truncate diff --git a/crates/client/src/rpc/types/eth/receipt.rs b/crates/client/src/rpc/types/eth/receipt.rs index aaefae0934..df14d19b59 100644 --- a/crates/client/src/rpc/types/eth/receipt.rs +++ b/crates/client/src/rpc/types/eth/receipt.rs @@ -56,171 +56,8 @@ pub struct Receipt { /// is None if tx execution is successful or it can not be offered. /// Error message can not be offered by light client. pub tx_exec_error_msg: Option, + #[serde(rename = "status", skip_serializing_if = "Option::is_none")] + pub type_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub burnt_gas_fee: Option, } - -// impl Receipt { -// fn outcome_to_state_root(outcome: TransactionOutcome) -> Option { -// match outcome { -// TransactionOutcome::Unknown | TransactionOutcome::StatusCode(_) -// => None, TransactionOutcome::StateRoot(root) => Some(root), -// } -// } -// -// fn outcome_to_status_code(outcome: &TransactionOutcome) -> Option { -// match *outcome { -// TransactionOutcome::Unknown | TransactionOutcome::StateRoot(_) => -// None, TransactionOutcome::StatusCode(ref code) => Some((*code as -// u64).into()), } -// } -// } -// -// impl From for Receipt { -// fn from(r: LocalizedReceipt) -> Self { -// Receipt { -// to: r.to.map(Into::into), -// from: Some(r.from), -// transaction_type: r.transaction_type.to_U64_option_id(), -// transaction_hash: Some(r.transaction_hash), -// transaction_index: Some(r.transaction_index.into()), -// block_hash: Some(r.block_hash), -// block_number: Some(r.block_number.into()), -// cumulative_gas_used: r.cumulative_gas_used, -// gas_used: Some(r.gas_used), -// contract_address: r.contract_address.map(Into::into), -// logs: r.logs.into_iter().map(Into::into).collect(), -// status_code: Self::outcome_to_status_code(&r.outcome), -// state_root: Self::outcome_to_state_root(r.outcome), -// logs_bloom: r.log_bloom, -// effective_gas_price: r.effective_gas_price, -// } -// } -// } -// -// impl From for Receipt { -// fn from(r: RichReceipt) -> Self { -// Receipt { -// from: Some(r.from), -// to: r.to.map(Into::into), -// transaction_type: r.transaction_type.to_U64_option_id(), -// transaction_hash: Some(r.transaction_hash), -// transaction_index: Some(r.transaction_index.into()), -// block_hash: None, -// block_number: None, -// cumulative_gas_used: r.cumulative_gas_used, -// gas_used: Some(r.gas_used), -// contract_address: r.contract_address.map(Into::into), -// logs: r.logs.into_iter().map(Into::into).collect(), -// status_code: Self::outcome_to_status_code(&r.outcome), -// state_root: Self::outcome_to_state_root(r.outcome), -// logs_bloom: r.log_bloom, -// effective_gas_price: r.effective_gas_price, -// } -// } -// } -// -// impl From for Receipt { -// fn from(r: TypedReceipt) -> Self { -// let transaction_type = r.tx_type().to_U64_option_id(); -// let legacy_receipt = r.receipt().clone(); -// Receipt { -// from: None, -// to: None, -// transaction_type, -// transaction_hash: None, -// transaction_index: None, -// block_hash: None, -// block_number: None, -// cumulative_gas_used: legacy_receipt.gas_used, -// gas_used: None, -// contract_address: None, -// logs: legacy_receipt.logs.into_iter().map(Into::into).collect(), -// status_code: -// Self::outcome_to_status_code(&legacy_receipt.outcome), -// state_root: Self::outcome_to_state_root(legacy_receipt.outcome), -// logs_bloom: legacy_receipt.log_bloom, effective_gas_price: -// Default::default(), } -// } -// } -// -// #[cfg(test)] -// mod tests { -// use ethereum_types::{Bloom, H256}; -// use serde_json; -// use types::transaction::TypedTxId; -// use v1::types::{Log, Receipt}; -// -// #[test] -// fn receipt_serialization() { -// let s = -// r#"{"type":"0x1","transactionHash":" -// 0x0000000000000000000000000000000000000000000000000000000000000000"," -// transactionIndex":"0x0","blockHash":" -// 0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5","from": -// null,"to":null,"blockNumber":"0x4510c","cumulativeGasUsed":"0x20","gasUsed":" -// 0x10","contractAddress":null,"logs":[{"address":" -// 0x33990122638b9132ca29c723bdf037f1a891a70c","topics":[" -// 0xa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc"," -// 0x4861736852656700000000000000000000000000000000000000000000000000"],"data":" -// 0x","blockHash":" -// 0xed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5"," -// blockNumber":"0x4510c","transactionHash":" -// 0x0000000000000000000000000000000000000000000000000000000000000000"," -// transactionIndex":"0x0","logIndex":"0x1","transactionLogIndex":null,"type":" -// mined","removed":false}],"root":" -// 0x000000000000000000000000000000000000000000000000000000000000000a"," -// logsBloom":" -// 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f" -// ,"status":"0x1","effectiveGasPrice":"0x0"}"#; -// -// let receipt = Receipt { -// from: None, -// to: None, -// transaction_type: TypedTxId::AccessList.to_U64_option_id(), -// transaction_hash: Some(H256::zero()), -// transaction_index: Some(0.into()), -// block_hash: Some( -// -// "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5" -// .parse() -// .unwrap(), -// ), -// block_number: Some(0x4510c.into()), -// cumulative_gas_used: 0x20.into(), -// gas_used: Some(0x10.into()), -// contract_address: None, -// logs: vec![Log { -// address: -// "33990122638b9132ca29c723bdf037f1a891a70c".parse().unwrap(), -// topics: vec![ -// "a6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc" -// .parse() -// .unwrap(), -// -// "4861736852656700000000000000000000000000000000000000000000000000" -// .parse() -// .unwrap(), -// ], -// data: vec![].into(), -// block_hash: Some( -// -// "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5" -// .parse() -// .unwrap(), -// ), -// block_number: Some(0x4510c.into()), -// transaction_hash: Some(H256::zero()), -// transaction_index: Some(0.into()), -// transaction_log_index: None, -// log_index: Some(1.into()), -// removed: false, -// }], -// logs_bloom: Bloom::from_low_u64_be(15), -// state_root: Some(H256::from_low_u64_be(10)), -// status_code: Some(1u64.into()), -// effective_gas_price: Default::default(), -// }; -// -// let serialized = serde_json::to_string(&receipt).unwrap(); -// assert_eq!(serialized, s); -// } -// } diff --git a/crates/client/src/rpc/types/eth/transaction.rs b/crates/client/src/rpc/types/eth/transaction.rs index a9b43c39fd..e83b365f2d 100644 --- a/crates/client/src/rpc/types/eth/transaction.rs +++ b/crates/client/src/rpc/types/eth/transaction.rs @@ -22,7 +22,8 @@ use crate::rpc::types::Bytes; use cfx_types::{H160, H256, H512, U256, U64}; use cfx_vm_types::{contract_address, CreateContractAddress}; use primitives::{ - transaction::eth_transaction::eip155_signature, Action, SignedTransaction, + transaction::eth_transaction::eip155_signature, AccessList, Action, + SignedTransaction, }; use rlp::Encodable; use serde::Serialize; @@ -79,6 +80,16 @@ pub struct Transaction { pub s: U256, // Whether tx is success pub status: Option, + /// Optional access list + #[serde(skip_serializing_if = "Option::is_none")] + pub access_list: Option, + /// miner bribe + #[serde(skip_serializing_if = "Option::is_none")] + pub max_priority_fee_per_gas: Option, + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub y_parity: Option, /* /// Transaction activates at specified block. * pub condition: Option, * /// optional access list @@ -97,21 +108,6 @@ impl Transaction { exec_info: (Option, Option), ) -> Transaction { let signature = t.signature(); - // We only support EIP-155 - // let access_list = match t.as_unsigned() { - // TypedTransaction::AccessList(tx) => { - // Some(tx.access_list.clone().into_iter().map(Into::into). - // collect()) } - // TypedTransaction::EIP1559Transaction(tx) => Some( - // tx.transaction - // .access_list - // .clone() - // .into_iter() - // .map(Into::into) - // .collect(), - // ), - // TypedTransaction::Legacy(_) => None, - // }; Transaction { hash: t.hash(), @@ -126,7 +122,6 @@ impl Transaction { }, value: *t.value(), gas_price: *t.gas_price(), - max_fee_per_gas: None, gas: *t.gas(), input: Bytes::new(t.data().clone()), creates: exec_info.1, @@ -142,6 +137,13 @@ impl Transaction { r: signature.r().into(), s: signature.s().into(), status: exec_info.0, + access_list: t.access_list().cloned(), + max_fee_per_gas: t.after_1559().then_some(*t.gas_price()), + max_priority_fee_per_gas: t + .after_1559() + .then_some(*t.max_priority_gas_price()), + y_parity: t.is_2718().then_some(signature.v()), + transaction_type: Some(t.type_id()), } } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 08e7d02af0..fee53cf247 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -341,16 +341,20 @@ impl Transaction { } } - pub fn type_id(&self) -> Option { + pub fn type_id(&self) -> u8 { match self { - Transaction::Native(_) - | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => None, - Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => Some(1), - Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => Some(2), + Transaction::Native(TypedNativeTransaction::Cip155(_)) + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => 0, + + Transaction::Native(TypedNativeTransaction::Cip2930(_)) + | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => 1, + + Transaction::Native(TypedNativeTransaction::Cip1559(_)) + | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => 2, } } - pub fn before_1559(&self) -> bool { + pub fn is_legacy(&self) -> bool { matches!( self, Transaction::Native(TypedNativeTransaction::Cip155(_)) @@ -358,6 +362,16 @@ impl Transaction { ) } + pub fn is_2718(&self) -> bool { !self.is_legacy() } + + pub fn after_1559(&self) -> bool { + matches!( + self, + Transaction::Native(TypedNativeTransaction::Cip1559(_)) + | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) + ) + } + pub fn access_list(&self) -> Option<&AccessList> { match self { Transaction::Native(tx) => tx.access_list(), @@ -848,6 +862,16 @@ impl TransactionWithSignature { } } + pub fn check_y_parity(&self) -> Result<(), keylib::Error> { + if self.is_2718() && self.v > 1 { + // In Typed transactions (EIP-2718), v means y_parity, which must be + // 0 or 1 + Err(keylib::Error::InvalidYParity) + } else { + Ok(()) + } + } + pub fn hash(&self) -> H256 { self.hash } /// Recovers the public key of the sender. From 68d9b0986516dc77b5486f40df1ed92cba619965 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 16:12:17 +0800 Subject: [PATCH 050/137] Implement stat rpc && fee history --- crates/cfxcore/core/src/consensus/mod.rs | 32 ++++- crates/client/src/rpc/impls/cfx.rs | 12 +- crates/client/src/rpc/impls/common.rs | 84 ++++++++++++- crates/client/src/rpc/impls/eth.rs | 79 +++++++++++- crates/client/src/rpc/impls/light.rs | 116 ++++++++++++++++-- crates/client/src/rpc/traits/cfx_space/cfx.rs | 8 +- crates/client/src/rpc/traits/eth_space/eth.rs | 11 +- crates/client/src/rpc/types.rs | 2 + crates/client/src/rpc/types/eth/receipt.rs | 2 +- crates/client/src/rpc/types/fee_history.rs | 113 +++++++++++++++++ .../client/src/rpc/types/vote_params_info.rs | 1 + tests/evm_space/eip1559_test.py | 7 ++ 12 files changed, 446 insertions(+), 21 deletions(-) create mode 100644 crates/client/src/rpc/types/fee_history.rs diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index c3b035d6bb..638c9b80d6 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -1811,9 +1811,33 @@ impl ConsensusGraph { Ok(Some(bloom)) } + pub fn get_phantom_block_pivot_by_number( + &self, block_num: EpochNumber, pivot_assumption: Option, + include_traces: bool, + ) -> Result, String> { + self.get_phantom_block_by_number_innser( + block_num, + pivot_assumption, + include_traces, + true, + ) + } + pub fn get_phantom_block_by_number( &self, block_num: EpochNumber, pivot_assumption: Option, include_traces: bool, + ) -> Result, String> { + self.get_phantom_block_by_number_innser( + block_num, + pivot_assumption, + include_traces, + false, + ) + } + + fn get_phantom_block_by_number_innser( + &self, block_num: EpochNumber, pivot_assumption: Option, + include_traces: bool, only_pivot: bool, ) -> Result, String> { let hashes = self.get_block_hashes_by_epoch(block_num)?; @@ -1860,7 +1884,13 @@ impl ConsensusGraph { let mut gas_used = U256::from(0); - for b in &blocks { + let iter_blocks = if only_pivot { + &blocks[blocks.len() - 1..] + } else { + &blocks[..] + }; + + for b in iter_blocks { // note: we need the receipts to reconstruct a phantom block. // as a result, we cannot return unexecuted blocks in eth_* RPCs. let exec_info = match self diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 489cfb1067..fbd542a2c1 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -6,8 +6,8 @@ use crate::rpc::{ error_codes::{internal_error_msg, invalid_params_msg}, types::{ call_request::rpc_call_request_network, - errors::check_rpc_address_network, pos::PoSEpochReward, PoSEconomics, - RpcAddress, SponsorInfo, StatOnGasLoad, TokenSupplyInfo, + errors::check_rpc_address_network, pos::PoSEpochReward, FeeHistory, + PoSEconomics, RpcAddress, SponsorInfo, StatOnGasLoad, TokenSupplyInfo, VoteParamsInfo, WrapTransaction, }, }; @@ -19,8 +19,8 @@ use cfx_executor::{ }; use cfx_statedb::{ global_params::{ - AccumulateInterestRate, DistributablePoSInterest, InterestRate, - LastDistributeBlock, PowBaseReward, TotalPosStaking, + AccumulateInterestRate, BaseFeeProp, DistributablePoSInterest, + InterestRate, LastDistributeBlock, PowBaseReward, TotalPosStaking, }, StateDbExt, }; @@ -1574,10 +1574,13 @@ impl RpcImpl { let storage_point_prop = state_db.get_system_storage(&storage_point_prop())?; + + let base_fee_share_prop = state_db.get_global_param::()?; Ok(VoteParamsInfo { pow_base_reward, interest_rate, storage_point_prop, + base_fee_share_prop, }) } @@ -2244,6 +2247,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; + fn fee_history(&self, block_count: usize, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } to self.rpc_impl { diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index 34bc85afe2..6e6cb40300 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -15,7 +15,7 @@ use crate::rpc::{ errors::check_rpc_address_network, pos::PoSEpochReward, AccountPendingInfo, AccountPendingTransactions, Block as RpcBlock, BlockHashOrEpochNumber, Bytes, CheckBalanceAgainstTransactionResponse, - EpochNumber, RpcAddress, Status as RpcStatus, + EpochNumber, FeeHistory, RpcAddress, Status as RpcStatus, Transaction as RpcTransaction, TxPoolPendingNonceRange, TxPoolStatus, TxWithPoolInfo, }, @@ -528,6 +528,88 @@ impl RpcImpl { "num", ) } + + pub fn fee_history( + &self, block_count: usize, newest_block: EpochNumber, + reward_percentiles: Vec, + ) -> RpcResult { + info!( + "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", + block_count, newest_block, reward_percentiles + ); + + if block_count == 0 { + return Ok(FeeHistory::new()); + } + // keep read lock to ensure consistent view + let inner = self.consensus_graph().inner.read(); + + let fetch_block = |height| { + let pivot_hash = inner + .get_pivot_hash_from_epoch_number(height) + .map_err(RpcError::invalid_params)?; + + let maybe_block = self + .data_man + .block_by_hash(&pivot_hash, false /* update_cache */); + if let Some(block) = maybe_block { + // Internal error happens only if the fetch header has + // inconsistent block height + Ok(block) + } else { + Err(RpcError::invalid_params( + "Specified block header does not exist", + )) + } + }; + + let start_height: u64 = self + .consensus_graph() + .get_height_from_epoch_number(newest_block.into()) + .map_err(RpcError::invalid_params)?; + + let mut current_height = start_height; + + let mut fee_history = FeeHistory::new(); + while current_height + >= start_height.saturating_sub(block_count as u64 - 1) + { + let block = fetch_block(current_height)?; + + let transactions = block + .transactions + .iter() + .filter(|tx| tx.space() == Space::Native) + .map(|x| &**x); + + // Internal error happens only if the fetch header has inconsistent + // block height + fee_history + .push_back_block( + Space::Native, + &reward_percentiles, + &block.block_header, + transactions, + ) + .map_err(|_| RpcError::internal_error())?; + + if current_height == 0 { + fee_history.finish(0, None, Space::Native); + return Ok(fee_history); + } else { + current_height -= 1; + } + } + + let block = fetch_block(current_height)?; + fee_history.finish( + current_height + 1, + block.block_header.base_price().as_ref(), + Space::Native, + ); + + Ok(fee_history) + } } // Test RPC implementation diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 124e61d7fc..a0fd4f3089 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -15,7 +15,7 @@ use crate::rpc::{ CallRequest, EthRpcLogFilter, Log, Receipt, SyncInfo, SyncStatus, Transaction, }, - Bytes, Index, MAX_GAS_CALL_REQUEST, + Bytes, FeeHistory, Index, MAX_GAS_CALL_REQUEST, }, }; use cfx_execute_helper::estimation::{ @@ -819,6 +819,83 @@ impl Eth for EthHandler { Ok(estimation.estimated_gas_limit) } + fn fee_history( + &self, block_count: usize, newest_block: BlockNumber, + reward_percentiles: Vec, + ) -> jsonrpc_core::Result { + info!( + "RPC Request: eth_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", + block_count, newest_block, reward_percentiles + ); + + if block_count == 0 { + return Ok(FeeHistory::new()); + } + + // keep read lock to ensure consistent view + let _consensus = self.consensus_graph().inner.read(); + + let fetch_block = |height| { + let maybe_block = self + .consensus_graph() + .get_phantom_block_pivot_by_number( + EpochNumber::Number(height), + None, + false, + ) + .map_err(RpcError::invalid_params)?; + if let Some(block) = maybe_block { + // Internal error happens only if the fetch header has + // inconsistent block height + Ok(block) + } else { + Err(RpcError::invalid_params( + "Specified block header does not exist", + )) + } + }; + + let start_height: u64 = self + .consensus_graph() + .get_height_from_epoch_number(newest_block.try_into()?) + .map_err(RpcError::invalid_params)?; + + let mut current_height = start_height; + + let mut fee_history = FeeHistory::new(); + while current_height + >= start_height.saturating_sub(block_count as u64 - 1) + { + let block = fetch_block(current_height)?; + + // Internal error happens only if the fetch header has inconsistent + // block height + fee_history + .push_back_block( + Space::Ethereum, + &reward_percentiles, + &block.pivot_header, + block.transactions.iter().map(|x| &**x), + ) + .map_err(|_| RpcError::internal_error())?; + + if current_height == 0 { + fee_history.finish(0, None, Space::Ethereum); + return Ok(fee_history); + } else { + current_height -= 1; + } + } + + let block = fetch_block(current_height)?; + fee_history.finish( + current_height + 1, + block.pivot_header.base_price().as_ref(), + Space::Ethereum, + ); + Ok(fee_history) + } + fn transaction_by_hash( &self, hash: H256, ) -> jsonrpc_core::Result> { diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index 1442931165..0ce85e01cb 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -7,12 +7,14 @@ use cfx_types::{ }; use cfxcore::{ block_data_manager::BlockDataManager, + consensus::ConsensusConfig, light_protocol::{ self, query_service::TxInfo, Error as LightError, ErrorKind, }, rpc_errors::{account_result_to_rpc_result, invalid_params_check}, verification::EpochReceiptProof, - ConsensusGraph, LightQueryService, PeerInfo, SharedConsensusGraph, + ConsensusGraph, ConsensusGraphTrait, LightQueryService, PeerInfo, + SharedConsensusGraph, }; use cfxcore_accounts::AccountProvider; use delegate::delegate; @@ -42,12 +44,12 @@ use crate::{ AccountPendingTransactions, BlameInfo, Block as RpcBlock, BlockHashOrEpochNumber, Bytes, CallRequest, CfxRpcLogFilter, CheckBalanceAgainstTransactionResponse, ConsensusGraphStates, - EpochNumber, EstimateGasAndCollateralResponse, Log as RpcLog, - PoSEconomics, Receipt as RpcReceipt, RewardInfo as RpcRewardInfo, - RpcAddress, SendTxRequest, SponsorInfo, StatOnGasLoad, - Status as RpcStatus, StorageCollateralInfo, SyncGraphStates, - TokenSupplyInfo, Transaction as RpcTransaction, VoteParamsInfo, - WrapTransaction, + EpochNumber, EstimateGasAndCollateralResponse, FeeHistory, + Log as RpcLog, PoSEconomics, Receipt as RpcReceipt, + RewardInfo as RpcRewardInfo, RpcAddress, SendTxRequest, + SponsorInfo, StatOnGasLoad, Status as RpcStatus, + StorageCollateralInfo, SyncGraphStates, TokenSupplyInfo, + Transaction as RpcTransaction, VoteParamsInfo, WrapTransaction, }, RpcBoxFuture, RpcResult, }, @@ -1081,6 +1083,105 @@ impl RpcImpl { Box::new(fut.boxed().compat()) } + + fn fee_history( + &self, block_count: usize, newest_block: EpochNumber, + reward_percentiles: Vec, + ) -> RpcBoxFuture { + info!( + "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", + block_count, newest_block, reward_percentiles + ); + + if block_count == 0 { + return Box::new(async { Ok(FeeHistory::new()) }.boxed().compat()); + } + + // clone to avoid lifetime issues due to capturing `self` + let consensus_graph = self.consensus.clone(); + let light = self.light.clone(); + + let fut = async move { + let start_height: u64 = light + .get_height_from_epoch_number(newest_block.into()) + .map_err(|e| e.to_string()) + .map_err(RpcError::invalid_params)?; + + let mut current_height = start_height; + + let mut fee_history = FeeHistory::new(); + + while current_height + >= start_height.saturating_sub(block_count as u64 - 1) + { + let block = fetch_block_for_fee_history( + consensus_graph.clone(), + light.clone(), + current_height, + ) + .await?; + + let transactions = block + .transactions + .iter() + .filter(|tx| tx.space() == Space::Native) + .map(|x| &**x); + // Internal error happens only if the fetch header has + // inconsistent block height + fee_history + .push_back_block( + Space::Native, + &reward_percentiles, + &block.block_header, + transactions, + ) + .map_err(|_| RpcError::internal_error())?; + + if current_height == 0 { + fee_history.finish(0, None, Space::Native); + return Ok(fee_history); + } else { + current_height -= 1; + } + } + + let block = fetch_block_for_fee_history( + consensus_graph.clone(), + light.clone(), + current_height, + ) + .await?; + fee_history.finish( + current_height + 1, + block.block_header.base_price().as_ref(), + Space::Native, + ); + Ok(fee_history) + }; + + Box::new(fut.boxed().compat()) + } +} + +async fn fetch_block_for_fee_history( + consensus_graph: Arc< + dyn ConsensusGraphTrait, + >, + light: Arc, height: u64, +) -> cfxcore::rpc_errors::Result { + let hash = consensus_graph + .as_any() + .downcast_ref::() + .expect("downcast should succeed") + .inner + .read() + .get_pivot_hash_from_epoch_number(height) + .map_err(RpcError::invalid_params)?; + + match light.retrieve_block(hash).await? { + None => Err(RpcError::internal_error().into()), + Some(b) => Ok(b), + } } pub struct CfxHandler { @@ -1132,6 +1233,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; + fn fee_history(&self, block_count: usize, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index dd2da4566d..0bcea5da38 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -7,7 +7,7 @@ use crate::rpc::types::{ AccountPendingTransactions, Block, BlockHashOrEpochNumber, Bytes, CallRequest, CfxFilterChanges, CfxRpcLogFilter, CheckBalanceAgainstTransactionResponse, EpochNumber, - EstimateGasAndCollateralResponse, Log as RpcLog, PoSEconomics, + EstimateGasAndCollateralResponse, FeeHistory, Log as RpcLog, PoSEconomics, Receipt as RpcReceipt, RewardInfo as RpcRewardInfo, RpcAddress, SponsorInfo, Status as RpcStatus, StorageCollateralInfo, TokenSupplyInfo, Transaction, VoteParamsInfo, @@ -197,6 +197,12 @@ pub trait Cfx { &self, request: CallRequest, epoch_number: Option, ) -> JsonRpcResult; + #[rpc(name = "cfx_feeHistory")] + fn fee_history( + &self, block_count: usize, newest_block: EpochNumber, + reward_percentiles: Vec, + ) -> BoxFuture; + /// Check if user balance is enough for the transaction. #[rpc(name = "cfx_checkBalanceAgainstTransaction")] fn check_balance_against_transaction( diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 99e02efabb..929bdac1b8 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -28,7 +28,7 @@ use crate::rpc::types::{ AccountPendingTransactions, Block, BlockNumber, CallRequest, EthRpcLogFilter, FilterChanges, Log, Receipt, SyncStatus, Transaction, }, - Bytes, Index, + Bytes, FeeHistory, Index, }; /// Eth rpc interface. @@ -74,10 +74,11 @@ pub trait Eth { #[rpc(name = "eth_maxPriorityFeePerGas")] fn max_priority_fee_per_gas(&self) -> Result; - // /// Returns transaction fee history. - // #[rpc(name = "eth_feeHistory")] - // fn fee_history(&self, _: U256, _: BlockNumber, _: Option>) - // -> BoxFuture; + #[rpc(name = "eth_feeHistory")] + fn fee_history( + &self, block_count: usize, newest_block: BlockNumber, + reward_percentiles: Vec, + ) -> Result; /// Returns accounts list. #[rpc(name = "eth_accounts")] diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index e9a4871ca9..23b0141556 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -12,6 +12,7 @@ mod consensus_graph_states; mod epoch_number; pub mod errors; pub mod eth; +mod fee_history; mod filter; mod index; mod log; @@ -45,6 +46,7 @@ pub use self::{ }, consensus_graph_states::ConsensusGraphStates, epoch_number::{BlockHashOrEpochNumber, EpochNumber}, + fee_history::FeeHistory, filter::{CfxFilterChanges, CfxFilterLog, CfxRpcLogFilter, RevertTo}, index::Index, log::Log, diff --git a/crates/client/src/rpc/types/eth/receipt.rs b/crates/client/src/rpc/types/eth/receipt.rs index df14d19b59..3b692461e3 100644 --- a/crates/client/src/rpc/types/eth/receipt.rs +++ b/crates/client/src/rpc/types/eth/receipt.rs @@ -56,7 +56,7 @@ pub struct Receipt { /// is None if tx execution is successful or it can not be offered. /// Error message can not be offered by light client. pub tx_exec_error_msg: Option, - #[serde(rename = "status", skip_serializing_if = "Option::is_none")] + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] pub type_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub burnt_gas_fee: Option, diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs new file mode 100644 index 0000000000..a986a2b0ae --- /dev/null +++ b/crates/client/src/rpc/types/fee_history.rs @@ -0,0 +1,113 @@ +use std::collections::VecDeque; + +use cfx_types::{Space, SpaceMap, U256}; +use primitives::{transaction::SignedTransaction, BlockHeader}; + +#[derive(Serialize, Debug, Default)] +pub struct FeeHistory { + /// Oldest Block + oldest_block: U256, + /// An array of block base fees per gas. This includes one block earlier + /// than the oldest block. Zeroes are returned for pre-EIP-1559 blocks. + base_fee_per_gas: VecDeque, + /// In Conflux, 1559 is adjusted by the current block's gas limit of total + /// transactions, instead of parent's gas used + gas_used_ratio: VecDeque, + /// A two-dimensional array of effective priority fees per gas at the + /// requested block percentiles. + reward: VecDeque>, +} + +impl FeeHistory { + pub fn new() -> Self { Default::default() } + + pub fn push_back_block<'a, I>( + &mut self, space: Space, percentiles: &Vec, + pivot_header: &BlockHeader, transactions: I, + ) -> Result<(), String> + where + I: Clone + Iterator, + { + let base_price = if let Some(base_price) = pivot_header.base_price() { + base_price[space] + } else { + self.base_fee_per_gas.push_back(U256::zero()); + self.gas_used_ratio.push_back(0.0); + self.reward.push_back(vec![U256::zero(); percentiles.len()]); + return Ok(()); + }; + + self.base_fee_per_gas.push_back( + pivot_header.base_price().map_or(U256::zero(), |x| x[space]), + ); + + let gas_limit: U256 = match space { + Space::Native => pivot_header.gas_limit() * 9 / 10, + Space::Ethereum => pivot_header.gas_limit() * 5 / 10, + }; + + let gas_used = transactions + .clone() + .map(|x| *x.gas_limit()) + .reduce(|x, y| x + y) + .unwrap_or_default() + / gas_limit; + + let gas_used_ratio = if gas_limit >= U256::from(u128::MAX) + || gas_used >= U256::from(u128::MAX) + { + // Impossible path. + 1.0 + } else { + gas_used.as_u128() as f64 / gas_limit.as_u128() as f64 + }; + + self.gas_used_ratio.push_back(gas_used_ratio); + + let reward = compute_reward(percentiles, transactions, base_price); + self.reward.push_back(reward); + + Ok(()) + } + + pub fn finish( + &mut self, oldest_block: u64, + parent_base_price: Option<&SpaceMap>, space: Space, + ) { + self.oldest_block = oldest_block.into(); + self.base_fee_per_gas + .push_back(parent_base_price.map_or(U256::zero(), |x| x[space])); + } +} + +fn compute_reward<'a, I>( + percentiles: &Vec, transactions: I, base_price: U256, +) -> Vec +where I: Iterator { + let mut rewards: Vec<_> = transactions + .map(|tx| { + if *tx.gas_price() < base_price { + U256::zero() + } else { + tx.effective_gas_price(&base_price) + } + }) + .collect(); + + if rewards.is_empty() { + return vec![U256::zero(); percentiles.len()]; + } + + rewards.sort_unstable(); + let n = rewards.len(); + percentiles + .into_iter() + .map(|per| { + let mut index = *per as usize * n / 100; + if index >= n { + index = n - 1; + } + rewards[index] + }) + .collect() +} diff --git a/crates/client/src/rpc/types/vote_params_info.rs b/crates/client/src/rpc/types/vote_params_info.rs index 7ae5c2d7af..e6703d080d 100644 --- a/crates/client/src/rpc/types/vote_params_info.rs +++ b/crates/client/src/rpc/types/vote_params_info.rs @@ -6,4 +6,5 @@ pub struct VoteParamsInfo { pub(crate) pow_base_reward: U256, pub(crate) interest_rate: U256, pub(crate) storage_point_prop: U256, + pub(crate) base_fee_share_prop: U256, } diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 5aa998cd8d..07dcf5b424 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -62,6 +62,7 @@ def run_test(self): self.rpc.generate_block(1) self.rpc.generate_blocks(20, 1) receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) + print(receipt) assert_equal(receipt["status"], 1) # TODO check EIP1559 gas usage # assert_equal(receipt["gasUsed"], 210000 / 4 * 3) @@ -80,5 +81,11 @@ def run_test(self): assert_equal(ret1[0], ret2[0]) + fee_history = self.nodes[0].eth_feeHistory(5, "latest", [25, 75]) + assert_equal(len(fee_history['base_fee_per_gas']), 6) + assert_equal(len(fee_history['gas_used_ratio']), 5) + assert_equal(len(fee_history['reward']), 5) + + if __name__ == "__main__": Eip1559Test().main() \ No newline at end of file From 134852406d4ac422e3ecf507fd43025b7409b556 Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 15 May 2024 18:11:44 +0800 Subject: [PATCH 051/137] record storage for opcode --- .../src/observer/geth_tracer/builder/geth.rs | 2 +- .../observer/geth_tracer/tracing_inspector.rs | 73 +++++++++++++++---- .../vm-interpreter/src/interpreter/mod.rs | 37 ---------- .../cfxcore/vm-types/src/interpreter_info.rs | 3 - docs/transaction-trace/geth-style-trace.md | 2 + 5 files changed, 60 insertions(+), 57 deletions(-) diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs index ad926be7b4..9f41efe17d 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs @@ -62,7 +62,7 @@ impl GethTraceBuilder { { let mut log = step.convert_to_geth_struct_log(opts); - // Fill in memory and storage depending on the options + // Fill in storage depending on the options if opts.is_storage_enabled() { let contract_storage = storage.entry(step.contract).or_default(); diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs index cc23b8f7c1..27bbe133aa 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs @@ -3,6 +3,7 @@ use super::{ gas::GasInspector, types::{ CallKind, CallTrace, CallTraceNode, CallTraceStep, RecordedMemory, + StorageChange, StorageChangeReason, }, utils::{gas_used, stack_push_count, to_alloy_address, to_alloy_u256}, CallTraceArena, GethTraceBuilder, StackStep, TracingInspectorConfig, @@ -10,7 +11,7 @@ use super::{ use cfx_types::{Space, H160}; use alloy_primitives::{Address, Bytes, U256}; -use revm::interpreter::{InstructionResult, InterpreterResult, OpCode}; +use revm::interpreter::{opcode, InstructionResult, InterpreterResult, OpCode}; use cfx_executor::machine::Machine; @@ -298,17 +299,18 @@ impl TracingInspector { .then(|| RecordedMemory::new(interp.mem().to_vec())) .unwrap_or_default(); - let stack = if self.config.record_stack_snapshots.is_full() { - Some( - interp - .stack() - .into_iter() - .map(|v| to_alloy_u256(v)) - .collect(), - ) - } else { - None - }; + let stack: Option> = + if self.config.record_stack_snapshots.is_full() { + Some( + interp + .stack() + .into_iter() + .map(|v| to_alloy_u256(v)) + .collect(), + ) + } else { + None + }; let op = OpCode::new(interp.current_opcode()) .or_else(|| { @@ -321,6 +323,32 @@ impl TracingInspector { }) .expect("is valid opcode;"); + // if op is SLOAD or SSTORE, we need to record the storage change + let storage_change = match (op.get(), stack.clone()) { + (opcode::SLOAD, Some(s)) if s.len() >= 1 => { + let key = s[s.len() - 1]; + let change = StorageChange { + key, + value: U256::ZERO, + had_value: None, // not used for now + reason: StorageChangeReason::SLOAD, + }; + Some(change) + } + (opcode::SSTORE, Some(s)) if s.len() >= 2 => { + let key = s[s.len() - 1]; + let value = s[s.len() - 2]; + let change = StorageChange { + key, + value, + had_value: None, // not used for now + reason: StorageChangeReason::SSTORE, + }; + Some(change) + } + _ => None, + }; + trace.trace.steps.push(CallTraceStep { depth, pc: interp.program_counter() as usize, @@ -335,7 +363,7 @@ impl TracingInspector { // fields will be populated end of call gas_cost: 0, - storage_change: None, + storage_change, status: InstructionResult::Continue, }); } @@ -369,10 +397,23 @@ impl TracingInspector { step.memory.resize(interp.mem().len()); } } - if self.config.record_state_diff { - let _op = step.op.get(); - // TODO setup the storage_change + if self.config.record_state_diff { + let op = step.op.get(); + + // update value if it's a SLOAD + match (op, step.push_stack.clone(), step.storage_change) { + (opcode::SLOAD, Some(s), Some(change)) if s.len() >= 1 => { + let val = s.last().unwrap(); + step.storage_change = Some(StorageChange { + key: change.key, + value: *val, + had_value: None, // not used for now + reason: StorageChangeReason::SLOAD, + }); + } + _ => {} + } } // The gas cost is the difference between the recorded gas remaining at diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index 1e77071de5..569db703cd 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -176,7 +176,6 @@ pub struct Interpreter { resume_output_range: Option<(U256, U256)>, resume_result: Option>, last_stack_ret_len: usize, - instruction_result: Option>, _type: PhantomData, } @@ -322,7 +321,6 @@ impl Interpreter { last_stack_ret_len: 0, resume_output_range: None, resume_result: None, - instruction_result: None, _type: PhantomData, } } @@ -477,8 +475,6 @@ impl Interpreter { Ok(x) => x, }; - self.instruction_result = Some(result.clone()); - evm_debug!({ self.informant.after_instruction(instruction) }); result @@ -1620,39 +1616,6 @@ impl InterpreterInfo for Interpreter { fn stack(&self) -> Vec { self.stack.content() } fn contract_address(&self) -> Address { self.params.address } - - fn instruction_result(&self) -> Option> { - self.instruction_result.clone().map(|v| match v { - InstructionResult::Ok => InstructionResult::Ok, - InstructionResult::UnusedGas(gas) => { - InstructionResult::UnusedGas(gas.as_u256()) - } - InstructionResult::JumpToPosition(v) => { - InstructionResult::JumpToPosition(v) - } - InstructionResult::JumpToSubroutine(v) => { - InstructionResult::JumpToSubroutine(v) - } - InstructionResult::ReturnFromSubroutine(v) => { - InstructionResult::ReturnFromSubroutine(v) - } - InstructionResult::StopExecutionNeedsReturn { - gas, - init_off, - init_size, - apply, - } => InstructionResult::StopExecutionNeedsReturn { - gas: gas.as_u256(), - init_off, - init_size, - apply, - }, - InstructionResult::StopExecution => { - InstructionResult::StopExecution - } - InstructionResult::Trap(v) => InstructionResult::Trap(v), - }) - } } fn get_and_reset_sign(value: U256) -> (U256, bool) { diff --git a/crates/cfxcore/vm-types/src/interpreter_info.rs b/crates/cfxcore/vm-types/src/interpreter_info.rs index 3fc3adf62b..432b2a2830 100644 --- a/crates/cfxcore/vm-types/src/interpreter_info.rs +++ b/crates/cfxcore/vm-types/src/interpreter_info.rs @@ -1,4 +1,3 @@ -use super::InstructionResult; use cfx_types::{Address, U256}; pub trait InterpreterInfo { @@ -17,6 +16,4 @@ pub trait InterpreterInfo { fn return_stack(&self) -> &Vec; fn contract_address(&self) -> Address; - - fn instruction_result(&self) -> Option>; } diff --git a/docs/transaction-trace/geth-style-trace.md b/docs/transaction-trace/geth-style-trace.md index eb53c6d8c1..a9b579c028 100644 --- a/docs/transaction-trace/geth-style-trace.md +++ b/docs/transaction-trace/geth-style-trace.md @@ -58,6 +58,8 @@ public_evm_rpc_apis = "eth,ethdebug" Currently the opcode trace's structLogs `error` field is not implemented. +Conflux does not have refund mechanism, so the `refund` field is omitted. + ## FAQs 1. Does trace RPC methods support eSpace PhantomTransaction traces? From 46dccc6ec55ee078e193778a5bccdabbd641cd12 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 18:43:31 +0800 Subject: [PATCH 052/137] Implement core space rpc --- crates/client/src/rpc/impls/cfx.rs | 68 ++++++++++++------- crates/client/src/rpc/impls/eth.rs | 5 +- crates/client/src/rpc/impls/light.rs | 7 ++ crates/client/src/rpc/traits/cfx_space/cfx.rs | 5 ++ crates/client/src/rpc/types/block.rs | 11 +++ crates/client/src/rpc/types/eth/receipt.rs | 4 +- .../client/src/rpc/types/eth/transaction.rs | 16 ++--- crates/client/src/rpc/types/receipt.rs | 24 ++++++- crates/client/src/rpc/types/transaction.rs | 30 +++++++- 9 files changed, 126 insertions(+), 44 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index fbd542a2c1..1778efb355 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -19,8 +19,7 @@ use cfx_executor::{ }; use cfx_statedb::{ global_params::{ - AccumulateInterestRate, BaseFeeProp, DistributablePoSInterest, - InterestRate, LastDistributeBlock, PowBaseReward, TotalPosStaking, + AccumulateInterestRate, BaseFeeProp, DistributablePoSInterest, InterestRate, LastDistributeBlock, PowBaseReward, TotalBurnt1559, TotalPosStaking }, StateDbExt, }; @@ -703,6 +702,14 @@ impl RpcImpl { .get_data_manager() .get_executed_state_root(&tx_index.block_hash); + // Acutally, the return value of `block_header_by_hash` + // should not be none. + let maybe_base_price = self + .consensus + .get_data_manager() + .block_header_by_hash(&tx_index.block_hash) + .and_then(|x| x.base_price()); + PackedOrExecuted::Executed(RpcReceipt::new( tx.clone(), receipt, @@ -710,6 +717,7 @@ impl RpcImpl { prior_gas_used, epoch_number, block_number, + maybe_base_price, maybe_state_root, tx_exec_error_msg, *self.sync.network.get_network_type(), @@ -830,6 +838,7 @@ impl RpcImpl { prior_gas_used, Some(exec_info.epoch_number), exec_info.block_receipts.block_number, + exec_info.block.block_header.base_price(), exec_info.maybe_state_root.clone(), tx_exec_error_msg, *self.sync.network.get_network_type(), @@ -1584,6 +1593,15 @@ impl RpcImpl { }) } + pub fn get_fee_burnt(&self,epoch: Option) -> RpcResult{ + let epoch = epoch.unwrap_or(EpochNumber::LatestState).into(); + let state_db = self + .consensus + .get_state_db_by_epoch_number(epoch, "epoch_num")?; + + Ok(state_db.get_global_param::()?) + } + pub fn set_db_crash( &self, crash_probability: f64, crash_exit_code: i32, ) -> RpcResult<()> { @@ -2125,36 +2143,33 @@ impl RpcImpl { let tx_exec_error_msg = &execution_result .block_receipts .tx_execution_error_messages[id]; + let receipt = RpcReceipt::new( + (**tx).clone(), + receipt.clone(), + tx_index, + prior_gas_used, + Some(epoch_number), + execution_result + .block_receipts + .block_number, + b.block_header.base_price(), + maybe_state_root, + if tx_exec_error_msg.is_empty() { + None + } else { + Some(tx_exec_error_msg.clone()) + }, + network, + false, + false, + )?; res.push( WrapTransaction::NativeTransaction( RpcTransaction::from_signed( tx, Some( PackedOrExecuted::Executed( - RpcReceipt::new( - (**tx).clone(), - receipt.clone(), - tx_index, - prior_gas_used, - Some(epoch_number), - execution_result - .block_receipts - .block_number, - maybe_state_root, - if tx_exec_error_msg - .is_empty() - { - None - } else { - Some( - tx_exec_error_msg - .clone(), - ) - }, - network, - false, - false, - )?, + receipt, ), ), network, @@ -2286,6 +2301,7 @@ impl Cfx for CfxHandler { fn get_supply_info(&self, epoch_num: Option) -> JsonRpcResult; fn get_collateral_info(&self, epoch_num: Option) -> JsonRpcResult; fn get_vote_params(&self, epoch_num: Option) -> JsonRpcResult; + fn get_fee_burnt(&self, epoch_num: Option) -> JsonRpcResult; } } } diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index a0fd4f3089..d1d3cecc52 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -308,7 +308,10 @@ impl EthHandler { .into(), effective_gas_price, tx_exec_error_msg, - type_id: receipt.burnt_gas_fee.is_some().then_some(tx.type_id()), + transaction_type: receipt + .burnt_gas_fee + .is_some() + .then_some(tx.type_id()), burnt_gas_fee: receipt.burnt_gas_fee, }) } diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index 0ce85e01cb..a83012663c 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -676,6 +676,7 @@ impl RpcImpl { // clone `self.light` to avoid lifetime issues due to capturing `self` let light = self.light.clone(); + let data_man = self.data_man.clone(); let fut = async move { // TODO: @@ -706,6 +707,10 @@ impl RpcImpl { return Ok(None); } + let maybe_base_price = data_man + .block_header_by_hash(&tx_index.block_hash) + .and_then(|x| x.base_price()); + let receipt = RpcReceipt::new( tx, receipt, @@ -713,6 +718,7 @@ impl RpcImpl { prior_gas_used, maybe_epoch, maybe_block_number.unwrap(), + maybe_base_price, maybe_state_root, // Can not offer error_message from light node. None, @@ -1248,6 +1254,7 @@ impl Cfx for CfxHandler { fn get_collateral_info(&self, epoch_num: Option) -> JsonRpcResult; fn get_vote_params(&self, epoch_num: Option) -> JsonRpcResult; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; + fn get_fee_burnt(&self, epoch: Option) -> JsonRpcResult; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index 0bcea5da38..62cd023723 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -280,6 +280,11 @@ pub trait Cfx { &self, epoch_number: Option, ) -> JsonRpcResult; + #[rpc(name = "cfx_getFeeBurnt")] + fn get_fee_burnt( + &self, epoch_number: Option, + ) -> JsonRpcResult; + #[rpc(name = "cfx_getPoSRewardByEpoch")] fn get_pos_reward_by_epoch( &self, epoch: EpochNumber, diff --git a/crates/client/src/rpc/types/block.rs b/crates/client/src/rpc/types/block.rs index aae0676c72..d8fab80a58 100644 --- a/crates/client/src/rpc/types/block.rs +++ b/crates/client/src/rpc/types/block.rs @@ -113,6 +113,9 @@ pub struct Block { pub gas_limit: U256, /// Gas used pub gas_used: Option, + /// Base fee + #[serde(skip_serializing_if = "Option::is_none")] + pub base_fee_per_gas: Option, /// Timestamp pub timestamp: U256, /// Difficulty @@ -240,6 +243,7 @@ impl Block { prior_gas_used, epoch_number, execution_result.block_receipts.block_number, + b.block_header.base_price(), maybe_state_root, if tx_exec_error_msg.is_empty() { None @@ -304,6 +308,11 @@ impl Block { // fee system gas_used, gas_limit: b.block_header.gas_limit().into(), + base_fee_per_gas: b + .block_header + .base_price() + .map(|x| x[Space::Native]) + .into(), timestamp: b.block_header.timestamp().into(), difficulty: b.block_header.difficulty().clone().into(), pow_quality: b @@ -538,6 +547,7 @@ mod tests { epoch_number: None, block_number: None, gas_limit: U256::default(), + base_fee_per_gas: None, gas_used: None, timestamp: 0.into(), difficulty: U256::default(), @@ -573,6 +583,7 @@ mod tests { transactions_root: KECCAK_EMPTY_LIST_RLP.into(), epoch_number: Some(0.into()), block_number: Some(0.into()), + base_fee_per_gas: None, gas_limit: U256::default(), gas_used: None, timestamp: 0.into(), diff --git a/crates/client/src/rpc/types/eth/receipt.rs b/crates/client/src/rpc/types/eth/receipt.rs index 3b692461e3..61a1453a2f 100644 --- a/crates/client/src/rpc/types/eth/receipt.rs +++ b/crates/client/src/rpc/types/eth/receipt.rs @@ -25,6 +25,8 @@ use cfx_types::{Bloom as H2048, H160, H256, U256, U64}; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct Receipt { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, /// Transaction Hash pub transaction_hash: H256, /// Transaction index @@ -56,8 +58,6 @@ pub struct Receipt { /// is None if tx execution is successful or it can not be offered. /// Error message can not be offered by light client. pub tx_exec_error_msg: Option, - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub type_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub burnt_gas_fee: Option, } diff --git a/crates/client/src/rpc/types/eth/transaction.rs b/crates/client/src/rpc/types/eth/transaction.rs index e83b365f2d..c116a100a8 100644 --- a/crates/client/src/rpc/types/eth/transaction.rs +++ b/crates/client/src/rpc/types/eth/transaction.rs @@ -32,9 +32,9 @@ use serde::Serialize; #[derive(Debug, Default, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { - // /// transaction type - // #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - // pub transaction_type: Option, + /// transaction type + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, /// Hash pub hash: H256, /// Nonce @@ -86,18 +86,10 @@ pub struct Transaction { /// miner bribe #[serde(skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub y_parity: Option, /* /// Transaction activates at specified block. - * pub condition: Option, - * /// optional access list - * #[serde(skip_serializing_if = "Option::is_none")] - * pub access_list: Option, - * /// miner bribe - * #[serde(skip_serializing_if = "Option::is_none")] - * pub max_priority_fee_per_gas: Option, */ + * pub condition: Option, */ } impl Transaction { diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index 5be1316711..de1e439d82 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -4,7 +4,9 @@ use crate::rpc::types::{Log, RpcAddress}; use cfx_addr::Network; -use cfx_types::{address_util::AddressUtil, Bloom, Space, H256, U256, U64}; +use cfx_types::{ + address_util::AddressUtil, Bloom, Space, SpaceMap, H256, U256, U64, +}; use cfx_vm_types::{contract_address, CreateContractAddress}; use primitives::{ receipt::{ @@ -36,6 +38,8 @@ impl StorageChange { #[derive(Debug, Serialize, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Receipt { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, /// Transaction hash. pub transaction_hash: H256, /// Transaction index within the block. @@ -57,6 +61,7 @@ pub struct Receipt { pub accumulated_gas_used: Option, /// The gas fee charged in the execution of the transaction. pub gas_fee: U256, + pub effective_gas_price: U256, /// Address of contract created if the transaction action is create. pub contract_created: Option, /// Array of log objects, which this transaction generated. @@ -82,6 +87,8 @@ pub struct Receipt { /// Transaction space. #[serde(skip_serializing_if = "Option::is_none")] pub space: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub burnt_gas_fee: Option, } impl Receipt { @@ -89,6 +96,7 @@ impl Receipt { transaction: PrimitiveTransaction, receipt: PrimitiveReceipt, transaction_index: TransactionIndex, prior_gas_used: U256, epoch_number: Option, block_number: u64, + maybe_base_price: Option>, maybe_state_root: Option, tx_exec_error_msg: Option, network: Network, include_eth_receipt: bool, include_accumulated_gas_used: bool, @@ -162,7 +170,19 @@ impl Receipt { .map(Into::into) .unwrap_or_default(); + let effective_gas_price = if let Some(base_price) = maybe_base_price { + let base_price = base_price[transaction.space()]; + if *transaction.gas_price() < base_price { + *transaction.gas_price() + } else { + transaction.effective_gas_price(&base_price) + } + } else { + *transaction.gas_price() + }; + Ok(Receipt { + transaction_type: Some(transaction.type_id()), transaction_hash: transaction.hash.into(), index: U64::from( transaction_index @@ -180,6 +200,8 @@ impl Receipt { None }, gas_fee: gas_fee.into(), + burnt_gas_fee: receipt.burnt_gas_fee, + effective_gas_price, from: RpcAddress::try_from_h160(transaction.sender, network)?, to: match &action { Action::Create => None, diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index bab2b4e465..9d350710f0 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -13,8 +13,9 @@ use primitives::{ eth_transaction::Eip155Transaction, native_transaction::NativeTransaction, Action, }, - SignedTransaction, Transaction as PrimitiveTransaction, TransactionIndex, - TransactionWithSignature, TransactionWithSignatureSerializePart, + AccessList, SignedTransaction, Transaction as PrimitiveTransaction, + TransactionIndex, TransactionWithSignature, + TransactionWithSignatureSerializePart, }; #[derive(Debug, Clone, PartialEq, Serialize)] @@ -27,6 +28,8 @@ pub enum WrapTransaction { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Transaction { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub transaction_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub space: Option, pub hash: H256, @@ -44,12 +47,23 @@ pub struct Transaction { pub epoch_height: U256, pub chain_id: Option, pub status: Option, + /// Optional access list + #[serde(skip_serializing_if = "Option::is_none")] + pub access_list: Option, + /// miner bribe + #[serde(skip_serializing_if = "Option::is_none")] + pub max_priority_fee_per_gas: Option, + /// Max fee per gas + #[serde(skip_serializing_if = "Option::is_none")] + pub max_fee_per_gas: Option, /// The standardised V field of the signature. pub v: U256, /// The R field of the signature. pub r: U256, /// The S field of the signature. pub s: U256, + #[serde(skip_serializing_if = "Option::is_none")] + pub y_parity: Option, } pub enum PackedOrExecuted { @@ -79,6 +93,11 @@ impl Transaction { v: Default::default(), r: Default::default(), s: Default::default(), + access_list: Default::default(), + max_priority_fee_per_gas: Default::default(), + max_fee_per_gas: Default::default(), + y_parity: Default::default(), + transaction_type: Default::default(), }) } @@ -139,6 +158,13 @@ impl Transaction { storage_limit: storage_limit.into(), epoch_height: epoch_height.into(), chain_id: t.chain_id().map(|x| U256::from(x as u64)), + access_list: t.access_list().cloned(), + max_fee_per_gas: t.after_1559().then_some(*t.gas_price()), + max_priority_fee_per_gas: t + .after_1559() + .then_some(*t.max_priority_gas_price()), + y_parity: t.is_2718().then_some(t.transaction.v.into()), + transaction_type: Default::default(), v: t.transaction.v.into(), r: t.transaction.r.into(), s: t.transaction.s.into(), From 7cf62b820dfebfba81af8d56fcd8ae0daf6a0e8c Mon Sep 17 00:00:00 2001 From: cuiyourong Date: Wed, 15 May 2024 20:15:27 +0800 Subject: [PATCH 053/137] chore: fix some comments Signed-off-by: cuiyourong --- crates/cfxcore/core/src/consensus/mod.rs | 2 +- .../core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs | 2 +- crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs | 2 +- run/hydra.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index c3b035d6bb..a564c77794 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -985,7 +985,7 @@ impl ConsensusGraph { let inner = self.inner.read(); // NOTE: as batches are processed atomically and only the - // first batch (last few epochs) is likely to fluctuate, is is unlikely + // first batch (last few epochs) is likely to fluctuate, it is unlikely // that releasing the lock between batches would cause inconsistency: // we assume there are no pivot chain reorgs deeper than batch_size. // However, we still add a simple sanity check here: diff --git a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs index 8cd447a61a..8502c7c6b2 100644 --- a/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs +++ b/crates/cfxcore/core/src/pos/storage/pos-ledger-db/src/ledger_store/mod.rs @@ -403,7 +403,7 @@ impl LedgerStore { ) } - /// Write `txn_infos` to `batch`. Assigned `first_version` to the the + /// Write `txn_infos` to `batch`. Assigned `first_version` to the /// version number of the first transaction, and so on. pub fn put_transaction_infos( &self, first_version: u64, txn_infos: &[TransactionInfo], diff --git a/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs b/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs index 8505c7a871..7a85e8733b 100644 --- a/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs +++ b/crates/cfxcore/core/src/pos/types/src/proof/position/mod.rs @@ -496,7 +496,7 @@ fn nodes_to_left_of(node: u64) -> u64 { } /// Given `node`, an index in an in-order traversal of a perfect binary tree, -/// what order would the node be visited in in post-order traversal? +/// what order would the node be visited in post-order traversal? /// For example, consider this tree of in-order nodes. /// /// ```text diff --git a/run/hydra.toml b/run/hydra.toml index 6c88c8c80e..6a9eab4fb5 100644 --- a/run/hydra.toml +++ b/run/hydra.toml @@ -194,7 +194,7 @@ jsonrpc_local_http_port=12539 # # egress_max_throttle = 10 -# Time interval to to garbage-collect not block-graph-ready blocks periodically. +# Time interval to garbage-collect not block-graph-ready blocks periodically. # # expire_block_gc_period_s = 900 From eb25fab3f3781c0110b392a0366692a687de9d66 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 21:59:03 +0800 Subject: [PATCH 054/137] Implement Cancun Opcodes (CIP-141, CIP-142, CIP-143) --- crates/cfxcore/executor/src/context.rs | 26 ++++ crates/cfxcore/executor/src/spec.rs | 9 ++ .../src/state/overlay_account/factory.rs | 2 + .../executor/src/state/overlay_account/mod.rs | 3 + .../src/state/overlay_account/storage.rs | 8 + .../src/state/state_object/storage_entry.rs | 17 +++ crates/cfxcore/vm-interpreter/src/factory.rs | 53 ++++--- .../vm-interpreter/src/instructions.rs | 39 +++-- .../src/interpreter/gasometer.rs | 13 ++ .../vm-interpreter/src/interpreter/mod.rs | 137 ++++++++++++++---- .../src/interpreter/shared_cache.rs | 20 ++- crates/cfxcore/vm-types/src/context.rs | 8 + crates/cfxcore/vm-types/src/spec.rs | 13 ++ crates/cfxcore/vm-types/src/tests.rs | 10 ++ crates/client/src/configuration.rs | 2 +- 15 files changed, 291 insertions(+), 69 deletions(-) diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index ec1a750ae6..08a95c4cc8 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -188,6 +188,32 @@ impl<'a> ContextTrait for Context<'a> { } } + fn transient_storage_at(&self, key: &Vec) -> vm::Result { + let receiver = AddressWithSpace { + address: self.origin.address, + space: self.space, + }; + self.state + .transient_storage_at(&receiver, key) + .map_err(Into::into) + } + + fn transient_set_storage( + &mut self, key: Vec, value: U256, + ) -> vm::Result<()> { + let receiver = AddressWithSpace { + address: self.origin.address, + space: self.space, + }; + if self.is_static_or_reentrancy() { + Err(vm::Error::MutableCallInStaticContext) + } else { + self.state + .transient_set_storage(&receiver, key, value) + .map_err(Into::into) + } + } + fn exists(&self, address: &Address) -> vm::Result { let address = AddressWithSpace { address: *address, diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index e6f6559ba0..83831669a2 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -105,7 +105,12 @@ pub struct TransitionsBlockNumber { pub cip132: BlockNumber, /// CIP-133: Enhanced Block Hash Query pub cip133b: BlockNumber, + /// CIP-137: Base Fee Sharing in CIP-1559 pub cip137: BlockNumber, + /// CIP-141: Disable Subroutine Opcodes + /// CIP-142: Transient Storage Opcodes + /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy + pub cancun_opcodes: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -183,6 +188,10 @@ impl CommonParams { spec.cip133_core = number >= self.transition_numbers.cip133b; spec.cip137 = number >= self.transition_numbers.cip137; spec.cip1559 = height >= self.transition_heights.cip1559; + spec.cancun_opcodes = number >= self.transition_numbers.cancun_opcodes; + if spec.cancun_opcodes { + spec.sload_gas = 800; + } spec } diff --git a/crates/cfxcore/executor/src/state/overlay_account/factory.rs b/crates/cfxcore/executor/src/state/overlay_account/factory.rs index 5a4a0e78c3..415c9bd426 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/factory.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/factory.rs @@ -14,6 +14,7 @@ impl Default for OverlayAccount { sponsor_info: Default::default(), storage_read_cache: Default::default(), storage_write_cache: Default::default(), + transient_storage: Default::default(), storage_layout_change: None, staking_balance: 0.into(), collateral_for_storage: 0.into(), @@ -143,6 +144,7 @@ impl OverlayAccount { pending_db_clear: self.pending_db_clear, storage_write_cache: self.storage_write_cache.clone(), storage_read_cache: self.storage_read_cache.clone(), + transient_storage: self.transient_storage.clone(), storage_layout_change: self.storage_layout_change.clone(), } } diff --git a/crates/cfxcore/executor/src/state/overlay_account/mod.rs b/crates/cfxcore/executor/src/state/overlay_account/mod.rs index ca1ace208a..6c7a0041e8 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/mod.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/mod.rs @@ -134,6 +134,9 @@ pub struct OverlayAccount { /// changed values. storage_write_cache: Arc, StorageValue>>, + /// Transient storage from CIP-142 + transient_storage: Arc, U256>>, + /* --------------- - Special flags - --------------- */ diff --git a/crates/cfxcore/executor/src/state/overlay_account/storage.rs b/crates/cfxcore/executor/src/state/overlay_account/storage.rs index d51703630f..4c1f31d403 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/storage.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/storage.rs @@ -167,6 +167,10 @@ impl OverlayAccount { }) } + pub fn transient_storage_at(&self, key: &[u8]) -> U256 { + self.transient_storage.get(key).cloned().unwrap_or_default() + } + fn get_and_cache_storage( &self, db: &StateDbGeneric, key: &[u8], ) -> DbResult { @@ -185,6 +189,10 @@ impl OverlayAccount { Ok(storage_value) } + pub fn transient_set_storage(&mut self, key: Vec, value: U256) { + Arc::make_mut(&mut self.transient_storage).insert(key, value); + } + pub(super) fn should_have_owner(&self, _key: &[u8]) -> bool { self.address.space == Space::Native && self.address.address != SYSTEM_STORAGE_ADDRESS diff --git a/crates/cfxcore/executor/src/state/state_object/storage_entry.rs b/crates/cfxcore/executor/src/state/state_object/storage_entry.rs index 5adc34f993..5fe9fa8e0a 100644 --- a/crates/cfxcore/executor/src/state/state_object/storage_entry.rs +++ b/crates/cfxcore/executor/src/state/state_object/storage_entry.rs @@ -44,6 +44,14 @@ impl State { acc.storage_at(&self.db, key) } + #[inline] + pub fn transient_storage_at( + &self, address: &AddressWithSpace, key: &[u8], + ) -> DbResult { + let acc = try_loaded!(self.read_account_lock(address)); + Ok(acc.transient_storage_at(key)) + } + #[inline] pub fn storage_entry_at( &self, address: &AddressWithSpace, key: &[u8], @@ -68,6 +76,15 @@ impl State { Ok(()) } + #[inline] + pub fn transient_set_storage( + &mut self, address: &AddressWithSpace, key: Vec, value: U256, + ) -> DbResult<()> { + Ok(self + .write_account_lock(address)? + .transient_set_storage(key, value)) + } + pub fn is_fresh_storage( &self, address: &AddressWithSpace, ) -> DbResult { diff --git a/crates/cfxcore/vm-interpreter/src/factory.rs b/crates/cfxcore/vm-interpreter/src/factory.rs index 68b2cf5c2d..b868448a39 100644 --- a/crates/cfxcore/vm-interpreter/src/factory.rs +++ b/crates/cfxcore/vm-interpreter/src/factory.rs @@ -30,7 +30,8 @@ use std::sync::Arc; #[derive(Clone)] pub struct Factory { evm: VMType, - evm_cache: Arc, + evm_cache: Arc>, + evm_cache_cancun: Arc>, } impl Factory { @@ -39,24 +40,38 @@ impl Factory { pub fn create( &self, params: ActionParams, spec: &Spec, depth: usize, ) -> Box { + use super::interpreter::Interpreter; + // Assert there is only one type. Parity Ethereum is dead and no more + // types will be added. match self.evm { - VMType::Interpreter => { - if Self::can_fit_in_usize(¶ms.gas) { - Box::new(super::interpreter::Interpreter::::new( - params, - self.evm_cache.clone(), - spec, - depth, - )) - } else { - Box::new(super::interpreter::Interpreter::::new( - params, - self.evm_cache.clone(), - spec, - depth, - )) - } - } + VMType::Interpreter => {} + }; + + match (Self::can_fit_in_usize(¶ms.gas), spec.cancun_opcodes) { + (true, true) => Box::new(Interpreter::::new( + params, + self.evm_cache_cancun.clone(), + spec, + depth, + )), + (true, false) => Box::new(Interpreter::::new( + params, + self.evm_cache.clone(), + spec, + depth, + )), + (false, true) => Box::new(Interpreter::::new( + params, + self.evm_cache_cancun.clone(), + spec, + depth, + )), + (false, false) => Box::new(Interpreter::::new( + params, + self.evm_cache.clone(), + spec, + depth, + )), } } @@ -66,6 +81,7 @@ impl Factory { Factory { evm, evm_cache: Arc::new(SharedCache::new(cache_size)), + evm_cache_cancun: Arc::new(SharedCache::new(cache_size)), } } @@ -80,6 +96,7 @@ impl Default for Factory { Factory { evm: VMType::Interpreter, evm_cache: Arc::new(SharedCache::default()), + evm_cache_cancun: Arc::new(SharedCache::default()), } } } diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index a67a647936..f9c400432d 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -186,12 +186,15 @@ enum_with_from_u8! { GAS = 0x5a, #[doc = "set a potential jump destination"] JUMPDEST = 0x5b, - #[doc = "Marks the entry point to a subroutine."] - BEGINSUB = 0x5c, - #[doc = "Returns from a subroutine."] - RETURNSUB = 0x5d, - #[doc = "Jumps to a defined BEGINSUB subroutine."] - JUMPSUB = 0x5e, + #[doc = "Marks the entry point to a subroutine (pre cip-142). load word from transient storage (after cip-142)"] + #[allow(non_camel_case_types)] + BEGINSUB_TLOAD = 0x5c, + #[doc = "Returns from a subroutine (pre cip-142). store word from transient storage (after cip-142)"] + #[allow(non_camel_case_types)] + RETURNSUB_TSTORE = 0x5d, + #[doc = "Jumps to a defined BEGINSUB subroutine (pre cip-143). copy data from one memory range to another (after cip-143)"] + #[allow(non_camel_case_types)] + JUMPSUB_MCOPY = 0x5e, #[doc = "place zero item on stack (EIP-3855/CIP-119)"] PUSH0 = 0x5f, @@ -411,8 +414,14 @@ impl Instruction { } /// Returns the instruction info. - pub fn info(&self) -> &'static InstructionInfo { - INSTRUCTIONS[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.") + pub fn info(&self) -> &InstructionInfo { + let instrs = if !CANCUN { + &*INSTRUCTIONS + } else { + &*INSTRUCTIONS_CANCUN + }; + + instrs[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.") } } @@ -617,9 +626,9 @@ lazy_static! { arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special)); arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special)); arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special)); - arr[BEGINSUB as usize] = Some(InstructionInfo::new("BEGINSUB", 0, 0, GasPriceTier::Base)); - arr[JUMPSUB as usize] = Some(InstructionInfo::new("JUMPSUB", 1, 0, GasPriceTier::High)); - arr[RETURNSUB as usize] = Some(InstructionInfo::new("RETURNSUB", 0, 0, GasPriceTier::Low)); + arr[BEGINSUB_TLOAD as usize] = Some(InstructionInfo::new("BEGINSUB", 0, 0, GasPriceTier::Base)); + arr[JUMPSUB_MCOPY as usize] = Some(InstructionInfo::new("JUMPSUB", 1, 0, GasPriceTier::High)); + arr[RETURNSUB_TSTORE as usize] = Some(InstructionInfo::new("RETURNSUB", 0, 0, GasPriceTier::Low)); arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special)); arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special)); arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special)); @@ -631,6 +640,14 @@ lazy_static! { arr[REVERT as usize] = Some(InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero)); arr }; + + static ref INSTRUCTIONS_CANCUN: [Option; 0x100] = { + let mut arr = *INSTRUCTIONS; + arr[BEGINSUB_TLOAD as usize] = Some(InstructionInfo::new("TLOAD", 1, 1, GasPriceTier::Special)); + arr[JUMPSUB_MCOPY as usize] = Some(InstructionInfo::new("MCOPY", 3, 0, GasPriceTier::Special)); + arr[RETURNSUB_TSTORE as usize] = Some(InstructionInfo::new("TSTORE", 2, 0, GasPriceTier::Special)); + arr + }; } /// Maximal number of topics for log instructions diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs b/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs index d83724f78a..ec8e5cb97a 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/gasometer.rs @@ -207,6 +207,19 @@ impl Gasometer { mem_needed(stack.peek(0), stack.peek(2))?, Gas::from_u256(*stack.peek(2))?, ), + instructions::JUMPSUB_MCOPY if spec.cancun_opcodes => { + Request::GasMemCopy( + default_gas, + mem_needed(stack.peek(0), stack.peek(2))?, + Gas::from_u256(*stack.peek(2))?, + ) + } + instructions::BEGINSUB_TLOAD if spec.cancun_opcodes => { + Request::Gas(Gas::from(spec.tload_gas)) + } + instructions::RETURNSUB_TSTORE if spec.cancun_opcodes => { + Request::Gas(Gas::from(spec.tstore_gas)) + } instructions::EXTCODECOPY => Request::GasMemCopy( spec.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index e6d39705c3..434dbe0de1 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -178,9 +178,9 @@ impl From for InterpreterResult { } /// Interpreter EVM implementation -pub struct Interpreter { +pub struct Interpreter { mem: Vec, - cache: Arc, + cache: Arc>, params: InterpreterParams, reader: CodeReader, return_data: ReturnData, @@ -199,7 +199,9 @@ pub struct Interpreter { _type: PhantomData, } -impl vm::Exec for Interpreter { +impl vm::Exec + for Interpreter +{ fn exec( mut self: Box, context: &mut dyn vm::Context, ) -> vm::ExecTrapResult { @@ -231,7 +233,9 @@ impl vm::Exec for Interpreter { } } -impl vm::ResumeCall for Interpreter { +impl vm::ResumeCall + for Interpreter +{ fn resume_call( mut self: Box, result: MessageCallResult, ) -> Box { @@ -276,7 +280,9 @@ impl vm::ResumeCall for Interpreter { } } -impl vm::ResumeCreate for Interpreter { +impl vm::ResumeCreate + for Interpreter +{ fn resume_create( mut self: Box, result: ContractCreateResult, ) -> Box { @@ -305,12 +311,12 @@ impl vm::ResumeCreate for Interpreter { } } -impl Interpreter { +impl Interpreter { /// Create a new `Interpreter` instance with shared cache. pub fn new( - mut params: ActionParams, cache: Arc, spec: &Spec, + mut params: ActionParams, cache: Arc>, spec: &Spec, depth: usize, - ) -> Interpreter { + ) -> Interpreter { let reader = CodeReader::new( params.code.take().expect("VM always called with code; qed"), ); @@ -410,7 +416,7 @@ impl Interpreter { } }; - let info = instruction.info(); + let info = instruction.info::(); self.last_stack_ret_len = info.ret; if let Err(e) = self.verify_instruction(context, instruction, info) @@ -639,6 +645,7 @@ impl Interpreter { instructions::CALLDATACOPY | instructions::CODECOPY | instructions::RETURNDATACOPY => Some((read(0), read(2))), + instructions::JUMPSUB_MCOPY if CANCUN => Some((read(0), read(2))), instructions::EXTCODECOPY => Some((read(1), read(3))), instructions::CALL | instructions::CALLCODE => { Some((read(5), read(6))) @@ -689,31 +696,58 @@ impl Interpreter { instructions::JUMPDEST => { // ignore } - instructions::BEGINSUB => { - // BEGINSUB should not be executed. If so, returns - // InvalidSubEntry (EIP-2315). - return Err(vm::Error::InvalidSubEntry); - } - instructions::JUMPSUB => { - if self.return_stack.len() >= MAX_SUB_STACK_SIZE { - return Err(vm::Error::OutOfSubStack { - wanted: 1, - limit: MAX_SUB_STACK_SIZE, - }); + instructions::BEGINSUB_TLOAD => { + if !CANCUN { + // BEGINSUB + // BEGINSUB should not be executed. If so, returns + // InvalidSubEntry (EIP-2315). + return Err(vm::Error::InvalidSubEntry); + } else { + // TLOAD + let mut key = vec![0; 32]; + self.stack.pop_back().to_big_endian(key.as_mut()); + let word = context.storage_at(&key)?; + self.stack.push(word); + } + } + instructions::JUMPSUB_MCOPY => { + if !CANCUN { + // JUMPSUB + if self.return_stack.len() >= MAX_SUB_STACK_SIZE { + return Err(vm::Error::OutOfSubStack { + wanted: 1, + limit: MAX_SUB_STACK_SIZE, + }); + } + let sub_destination = self.stack.pop_back(); + return Ok(InstructionResult::JumpToSubroutine( + sub_destination, + )); + } else { + // MCOPY + Self::copy_memory_to_memory(&mut self.mem, &mut self.stack); } - let sub_destination = self.stack.pop_back(); - return Ok(InstructionResult::JumpToSubroutine( - sub_destination, - )); } - instructions::RETURNSUB => { - if let Some(pos) = self.return_stack.pop() { - return Ok(InstructionResult::ReturnFromSubroutine(pos)); + instructions::RETURNSUB_TSTORE => { + if !CANCUN { + // RETURNSUB + if let Some(pos) = self.return_stack.pop() { + return Ok(InstructionResult::ReturnFromSubroutine( + pos, + )); + } else { + return Err(vm::Error::SubStackUnderflow { + wanted: 1, + on_stack: 0, + }); + } } else { - return Err(vm::Error::SubStackUnderflow { - wanted: 1, - on_stack: 0, - }); + // TSTORE + let mut key = vec![0; 32]; + self.stack.pop_back().to_big_endian(key.as_mut()); + let val = self.stack.pop_back(); + + context.transient_set_storage(key, val)?; } } instructions::CREATE | instructions::CREATE2 => { @@ -1551,6 +1585,47 @@ impl Interpreter { let dest_offset = stack.pop_back(); let source_offset = stack.pop_back(); let size = stack.pop_back(); + + Self::copy_data_to_memory_inner( + mem, + source, + dest_offset, + source_offset, + size, + ); + } + + fn copy_memory_to_memory(mem: &mut Vec, stack: &mut dyn Stack) { + let dest_offset = stack.pop_back(); + let source_offset = stack.pop_back(); + let size = stack.pop_back(); + + let mem_size = U256::from(mem.len()); + + if source_offset >= mem_size { + return; + } + let source_offset_usize = source_offset.as_usize(); + let source = if size >= mem_size || source_offset + size >= mem_size { + mem[source_offset_usize..].to_vec() + } else { + let size = size.as_usize(); + mem[source_offset_usize..source_offset_usize + size].to_vec() + }; + + Self::copy_data_to_memory_inner( + mem, + &source, + dest_offset, + U256::zero(), + size, + ); + } + + fn copy_data_to_memory_inner( + mem: &mut Vec, source: &[u8], dest_offset: U256, + source_offset: U256, size: U256, + ) { let source_size = U256::from(source.len()); let output_end = match source_offset > source_size diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/shared_cache.rs b/crates/cfxcore/vm-interpreter/src/interpreter/shared_cache.rs index 4812529fa0..ebdb761b1c 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/shared_cache.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/shared_cache.rs @@ -53,11 +53,11 @@ impl MallocSizeOf for CacheItem { } /// Global cache for EVM interpreter -pub struct SharedCache { +pub struct SharedCache { jump_destinations: Mutex>, } -impl SharedCache { +impl SharedCache { /// Create a jump destinations cache with a maximum size in bytes /// to cache. pub fn new(max_size: usize) -> Self { @@ -101,7 +101,7 @@ impl SharedCache { instructions::JUMPDEST => { jump_dests.insert(position); } - instructions::BEGINSUB => { + instructions::BEGINSUB_TLOAD if !CANCUN => { sub_entrypoints.insert(position); } _ => { @@ -122,7 +122,7 @@ impl SharedCache { } } -impl Default for SharedCache { +impl Default for SharedCache { fn default() -> Self { SharedCache::new(DEFAULT_CACHE_SIZE) } } @@ -141,7 +141,8 @@ fn test_find_jump_destinations() { let code: Vec = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); // when - let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + let cache_item = + SharedCache::::find_jump_and_sub_destinations(&code); // then assert!(cache_item @@ -166,7 +167,8 @@ fn test_find_jump_destinations_not_in_data_segments() { let code: Vec = "600656605B565B6004".from_hex().unwrap(); // when - let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + let cache_item = + SharedCache::::find_jump_and_sub_destinations(&code); // then assert!(cache_item.jump_destination.0.iter().eq(vec![6].into_iter())); @@ -182,7 +184,8 @@ fn test_find_sub_entrypoints() { "6800000000000000000c5e005c60115e5d5c5d".from_hex().unwrap(); // when - let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + let cache_item = + SharedCache::::find_jump_and_sub_destinations(&code); // then assert!(cache_item.jump_destination.0.is_empty()); @@ -206,7 +209,8 @@ fn test_find_jump_and_sub_allowing_unknown_opcodes() { let code: Vec = "5BCC5C".from_hex().unwrap(); // when - let cache_item = SharedCache::find_jump_and_sub_destinations(&code); + let cache_item = + SharedCache::::find_jump_and_sub_destinations(&code); // then assert!(cache_item.jump_destination.0.iter().eq(vec![0].into_iter())); diff --git a/crates/cfxcore/vm-types/src/context.rs b/crates/cfxcore/vm-types/src/context.rs index c87e2a259c..024477be2c 100644 --- a/crates/cfxcore/vm-types/src/context.rs +++ b/crates/cfxcore/vm-types/src/context.rs @@ -163,6 +163,14 @@ pub trait Context { /// Stores a value for given key. fn set_storage(&mut self, key: Vec, value: U256) -> Result<()>; + /// Returns a value for given key. + fn transient_storage_at(&self, key: &Vec) -> Result; + + /// Stores a value for given key. + fn transient_set_storage( + &mut self, key: Vec, value: U256, + ) -> Result<()>; + /// Determine whether an account exists. fn exists(&self, address: &Address) -> Result; diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index f34588413b..690b258774 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -49,6 +49,10 @@ pub struct Spec { pub sstore_reset_gas: usize, /// Gas refund for `SSTORE` clearing (when `storage!=0`, `new==0`) pub sstore_refund_gas: usize, + /// Gas price for `TLOAD` + pub tload_gas: usize, + /// Gas price for `TSTORE` + pub tstore_gas: usize, /// Gas price for `JUMPDEST` opcode pub jumpdest_gas: usize, /// Gas price for `LOG*` @@ -169,8 +173,13 @@ pub struct Spec { pub cip133_b: BlockNumber, pub cip133_e: BlockHeight, pub cip133_core: bool, + /// CIP-137: Base Fee Sharing in CIP-1559 pub cip137: bool, pub cip1559: bool, + /// CIP-141: Disable Subroutine Opcodes + /// CIP-142: Transient Storage Opcodes + /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy + pub cancun_opcodes: bool, pub params_dao_vote_period: u64, } @@ -255,10 +264,13 @@ impl Spec { exp_byte_gas: 50, sha3_gas: 30, sha3_word_gas: 6, + // Become 800 after CIP-142 sload_gas: 200, sstore_set_gas: 20000, sstore_reset_gas: 5000, sstore_refund_gas: 15000, + tload_gas: 100, + tstore_gas: 100, jumpdest_gas: 1, log_gas: 375, log_data_gas: 8, @@ -321,6 +333,7 @@ impl Spec { cip133_core: false, cip137: false, cip1559: false, + cancun_opcodes: false, } } diff --git a/crates/cfxcore/vm-types/src/tests.rs b/crates/cfxcore/vm-types/src/tests.rs index 7ca83f3240..bb18cf835b 100644 --- a/crates/cfxcore/vm-types/src/tests.rs +++ b/crates/cfxcore/vm-types/src/tests.rs @@ -123,6 +123,16 @@ impl Context for MockContext { Ok(()) } + fn transient_storage_at(&self, _key: &Vec) -> Result { + Ok(U256::zero()) + } + + fn transient_set_storage( + &mut self, _key: Vec, _value: U256, + ) -> Result<()> { + Ok(()) + } + fn exists(&self, address: &Address) -> Result { Ok(self.balances.contains_key(address)) } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 75102c5660..5df7f1d826 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1401,7 +1401,7 @@ impl Configuration { // set_conf!( self.raw_conf.next_hardfork_transition_number.unwrap_or(default_transition_time); - params.transition_numbers => { cip131, cip132, cip133b, cip137 } + params.transition_numbers => { cip131, cip132, cip133b, cip137, cancun_opcodes } ); set_conf!( self.raw_conf.next_hardfork_transition_height.unwrap_or(default_transition_time); From 3659438b05a02c5986eb5a7ca8365788887b4b41 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 22:10:51 +0800 Subject: [PATCH 055/137] cargo fmt & fix typo --- crates/cfxcore/core/src/consensus/mod.rs | 6 +++--- crates/client/src/rpc/impls/cfx.rs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index 638c9b80d6..a24314ac6c 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -1815,7 +1815,7 @@ impl ConsensusGraph { &self, block_num: EpochNumber, pivot_assumption: Option, include_traces: bool, ) -> Result, String> { - self.get_phantom_block_by_number_innser( + self.get_phantom_block_by_number_inner( block_num, pivot_assumption, include_traces, @@ -1827,7 +1827,7 @@ impl ConsensusGraph { &self, block_num: EpochNumber, pivot_assumption: Option, include_traces: bool, ) -> Result, String> { - self.get_phantom_block_by_number_innser( + self.get_phantom_block_by_number_inner( block_num, pivot_assumption, include_traces, @@ -1835,7 +1835,7 @@ impl ConsensusGraph { ) } - fn get_phantom_block_by_number_innser( + fn get_phantom_block_by_number_inner( &self, block_num: EpochNumber, pivot_assumption: Option, include_traces: bool, only_pivot: bool, ) -> Result, String> { diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 1778efb355..0b4fe1aab5 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -19,7 +19,9 @@ use cfx_executor::{ }; use cfx_statedb::{ global_params::{ - AccumulateInterestRate, BaseFeeProp, DistributablePoSInterest, InterestRate, LastDistributeBlock, PowBaseReward, TotalBurnt1559, TotalPosStaking + AccumulateInterestRate, BaseFeeProp, DistributablePoSInterest, + InterestRate, LastDistributeBlock, PowBaseReward, TotalBurnt1559, + TotalPosStaking, }, StateDbExt, }; @@ -1593,7 +1595,7 @@ impl RpcImpl { }) } - pub fn get_fee_burnt(&self,epoch: Option) -> RpcResult{ + pub fn get_fee_burnt(&self, epoch: Option) -> RpcResult { let epoch = epoch.unwrap_or(EpochNumber::LatestState).into(); let state_db = self .consensus From a5deb9f0c17d11364a62cfa7455682b3943681ae Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 15 May 2024 22:45:41 +0800 Subject: [PATCH 056/137] Fix tests --- crates/client/src/configuration.rs | 10 +++++++++- tests/cip97_test.py | 1 + tests/contract_bench_test.py | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 5df7f1d826..9e8faf769f 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -164,6 +164,7 @@ build_config! { (next_hardfork_transition_number, (Option), None) (next_hardfork_transition_height, (Option), None) (cip1559_transition_height, (Option), None) + (cancun_opcodes_transition_number, (Option), None) (referee_bound, (usize), REFEREE_DEFAULT_BOUND) (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD) (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA) @@ -1401,7 +1402,7 @@ impl Configuration { // set_conf!( self.raw_conf.next_hardfork_transition_number.unwrap_or(default_transition_time); - params.transition_numbers => { cip131, cip132, cip133b, cip137, cancun_opcodes } + params.transition_numbers => { cip131, cip132, cip133b, cip137 } ); set_conf!( self.raw_conf.next_hardfork_transition_height.unwrap_or(default_transition_time); @@ -1411,7 +1412,14 @@ impl Configuration { params.transition_heights.cip1559 = self .raw_conf .cip1559_transition_height + .or(self.raw_conf.next_hardfork_transition_height) .unwrap_or(non_genesis_default_transition_time); + params.transition_numbers.cancun_opcodes = self + .raw_conf + .cancun_opcodes_transition_number + .or(self.raw_conf.next_hardfork_transition_height) + // Don't enable by default, since the gas is changed + .unwrap_or(default_transition_time); if params.transition_heights.cip1559 < self.raw_conf.pos_reference_enable_height diff --git a/tests/cip97_test.py b/tests/cip97_test.py index 324ec2db90..8d510df4c1 100644 --- a/tests/cip97_test.py +++ b/tests/cip97_test.py @@ -14,6 +14,7 @@ def set_test_params(self): self.num_nodes = 1 self.conf_parameters["dao_vote_transition_number"] = 100 self.conf_parameters["hydra_transition_number"] = 90 + self.conf_parameters["cancun_opcodes_transition_number"] = 99999999 def run_test(self): priv = default_config["GENESIS_PRI_KEY"] diff --git a/tests/contract_bench_test.py b/tests/contract_bench_test.py index 1dc9f00513..db71e62820 100755 --- a/tests/contract_bench_test.py +++ b/tests/contract_bench_test.py @@ -21,6 +21,7 @@ class ContractBenchTest(SmartContractBenchBase): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["execute_genesis"] = "true" + self.conf_parameters["cancun_opcodes_transition_number"] = 99999999 def setup_network(self): self.setup_nodes() From 60a5579b8d80b056ee2a2015ce4f38bb0ef110b6 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Thu, 16 May 2024 18:51:04 +0800 Subject: [PATCH 057/137] CIP-144: Point Evaluation Precompile from EIP-4844 --- Cargo.lock | 40 +++++- crates/cfxcore/executor/Cargo.toml | 4 + .../builtin/ethereum_trusted_setup_points.rs | 58 +++++++++ .../executor/src/builtin/g1_points.bin | Bin 0 -> 196608 bytes .../executor/src/builtin/g2_points.bin | Bin 0 -> 6240 bytes .../src/builtin/kzg_point_evaluations.rs | 117 ++++++++++++++++++ crates/cfxcore/executor/src/builtin/mod.rs | 16 +++ crates/cfxcore/executor/src/machine/mod.rs | 8 ++ crates/cfxcore/executor/src/spec.rs | 3 + crates/cfxcore/vm-types/src/spec.rs | 3 + crates/client/src/configuration.rs | 2 +- 11 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 crates/cfxcore/executor/src/builtin/ethereum_trusted_setup_points.rs create mode 100644 crates/cfxcore/executor/src/builtin/g1_points.bin create mode 100644 crates/cfxcore/executor/src/builtin/g2_points.bin create mode 100644 crates/cfxcore/executor/src/builtin/kzg_point_evaluations.rs diff --git a/Cargo.lock b/Cargo.lock index 3c5c17f546..fc77a99a8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -677,6 +677,19 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "c-kzg" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf100c4cea8f207e883ff91ca886d621d8a166cb04971dfaa9bb8fd99ed95df" +dependencies = [ + "blst", + "cc", + "glob", + "hex 0.4.3", + "libc", +] + [[package]] name = "c_linked_list" version = "1.1.1" @@ -796,6 +809,7 @@ version = "2.0.2" dependencies = [ "bls-signatures", "byteorder", + "c-kzg", "cfx-bytes", "cfx-internal-common", "cfx-math", @@ -807,9 +821,11 @@ dependencies = [ "cfx-vm-tracer-derive", "cfx-vm-types", "cfxkey", + "derive_more", "diem-crypto", "diem-types", "error-chain", + "hex-literal", "impl-tools", "impl-trait-for-tuples 0.2.2", "keccak-hash", @@ -818,6 +834,7 @@ dependencies = [ "malloc_size_of", "malloc_size_of_derive", "num", + "once_cell", "parity-crypto", "parking_lot 0.11.2", "pow-types", @@ -1562,6 +1579,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.9.3" @@ -1978,6 +2001,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2 1.0.71", + "quote 1.0.33", + "rustc_version 0.4.0", + "syn 1.0.109", +] + [[package]] name = "destructure_traitobject" version = "0.2.0" @@ -4718,9 +4754,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index ccd6c694a5..c5581a1c0a 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -48,6 +48,10 @@ pow-types = {path = "../core/src/pos/types/pow-types" } impl-trait-for-tuples = "^0.2" impl-tools = "^0.10" typemap = "0.3" +hex-literal = "0.4.1" +derive_more = "0.99" +c-kzg = { version = "1.0.2", default-features = false} +once_cell = "1.19" [dev-dependencies] cfx-statedb = { path = "../../dbs/statedb", features = ["testonly_code"]} diff --git a/crates/cfxcore/executor/src/builtin/ethereum_trusted_setup_points.rs b/crates/cfxcore/executor/src/builtin/ethereum_trusted_setup_points.rs new file mode 100644 index 0000000000..829e075aa2 --- /dev/null +++ b/crates/cfxcore/executor/src/builtin/ethereum_trusted_setup_points.rs @@ -0,0 +1,58 @@ +// Based on source code from the revm project (https://github.com/bluealloy/revm) under the MIT License. + +use c_kzg::KzgSettings; +use derive_more::{AsMut, AsRef, Deref, DerefMut}; +use once_cell::race::OnceBox; + +pub use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}; + +/// Number of G1 Points. +pub const NUM_G1_POINTS: usize = 4096; + +/// Number of G2 Points. +pub const NUM_G2_POINTS: usize = 65; + +/// A newtype over list of G1 point from kzg trusted setup. +#[derive(Debug, Clone, PartialEq, AsRef, AsMut, Deref, DerefMut)] +#[repr(transparent)] +pub struct G1Points(pub [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS]); + +impl Default for G1Points { + fn default() -> Self { Self([[0; BYTES_PER_G1_POINT]; NUM_G1_POINTS]) } +} + +/// A newtype over list of G2 point from kzg trusted setup. +#[derive(Debug, Clone, Eq, PartialEq, AsRef, AsMut, Deref, DerefMut)] +#[repr(transparent)] +pub struct G2Points(pub [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS]); + +impl Default for G2Points { + fn default() -> Self { Self([[0; BYTES_PER_G2_POINT]; NUM_G2_POINTS]) } +} + +/// Default G1 points. +pub const G1_POINTS: &G1Points = { + const BYTES: &[u8] = include_bytes!("./g1_points.bin"); + assert!(BYTES.len() == core::mem::size_of::()); + unsafe { &*BYTES.as_ptr().cast::() } +}; + +/// Default G2 points. +pub const G2_POINTS: &G2Points = { + const BYTES: &[u8] = include_bytes!("./g2_points.bin"); + assert!(BYTES.len() == core::mem::size_of::()); + unsafe { &*BYTES.as_ptr().cast::() } +}; + +static DEFAULT: OnceBox = OnceBox::new(); + +pub fn default_kzg_settings() -> &'static KzgSettings { + DEFAULT.get_or_init(|| { + let settings = KzgSettings::load_trusted_setup( + G1_POINTS.as_ref(), + G2_POINTS.as_ref(), + ) + .expect("failed to load default trusted setup"); + Box::new(settings) + }) +} diff --git a/crates/cfxcore/executor/src/builtin/g1_points.bin b/crates/cfxcore/executor/src/builtin/g1_points.bin new file mode 100644 index 0000000000000000000000000000000000000000..2ac35953ab86661fcf4ae6738265c45f836f31e5 GIT binary patch literal 196608 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 literal 0 HcmV?d00001 diff --git a/crates/cfxcore/executor/src/builtin/kzg_point_evaluations.rs b/crates/cfxcore/executor/src/builtin/kzg_point_evaluations.rs new file mode 100644 index 0000000000..9a62881e0e --- /dev/null +++ b/crates/cfxcore/executor/src/builtin/kzg_point_evaluations.rs @@ -0,0 +1,117 @@ +// Based on source code from the revm project (https://github.com/bluealloy/revm) under the MIT License. + +use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; + +use hex_literal::hex; +use parity_crypto::digest; +use std::convert::TryInto; + +use super::{ethereum_trusted_setup_points::default_kzg_settings, Error}; + +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; + +/// `U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes() ++ BLS_MODULUS.to_bytes32()` +pub const RETURN_VALUE: &[u8; 64] = &hex!( + "0000000000000000000000000000000000000000000000000000000000001000" + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" +); + +/// Run kzg point evaluation precompile. +/// +/// The Env has the KZGSettings that is needed for evaluation. +/// +/// The input is encoded as follows: +/// | versioned_hash | z | y | commitment | proof | +/// | 32 | 32 | 32 | 48 | 48 | +/// with z and y being padded 32 byte big endian values +pub fn run(input: &[u8]) -> Result<(), Error> { + // Verify input length. + if input.len() != 192 { + return Err(Error("Blob invalid input length")); + } + + // Verify commitment matches versioned_hash + let versioned_hash = &input[..32]; + let commitment = &input[96..144]; + if kzg_to_versioned_hash(commitment) != versioned_hash { + return Err(Error("Blob mismatched version")); + } + + // Verify KZG proof with z and y in big endian format + let commitment = as_bytes48(commitment); + let z = as_bytes32(&input[32..64]); + let y = as_bytes32(&input[64..96]); + let proof = as_bytes48(&input[144..192]); + if !verify_kzg_proof(commitment, z, y, proof, default_kzg_settings()) { + return Err(Error("Blob verify kzg proof failed")); + } + Ok(()) +} + +/// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` +#[inline] +pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] { + let mut hash = [0u8; 32]; + hash.copy_from_slice(&*digest::sha256(commitment)); + hash[0] = VERSIONED_HASH_VERSION_KZG; + hash +} + +#[inline] +pub fn verify_kzg_proof( + commitment: &Bytes48, z: &Bytes32, y: &Bytes32, proof: &Bytes48, + kzg_settings: &KzgSettings, +) -> bool { + KzgProof::verify_kzg_proof(commitment, z, y, proof, kzg_settings) + .unwrap_or(false) +} + +#[inline] +#[track_caller] +pub fn as_array(bytes: &[u8]) -> &[u8; N] { + bytes.try_into().expect("slice with incorrect length") +} + +#[inline] +#[track_caller] +pub fn as_bytes32(bytes: &[u8]) -> &Bytes32 { + // SAFETY: `#[repr(C)] Bytes32([u8; 32])` + unsafe { &*as_array::<32>(bytes).as_ptr().cast() } +} + +#[inline] +#[track_caller] +pub fn as_bytes48(bytes: &[u8]) -> &Bytes48 { + // SAFETY: `#[repr(C)] Bytes48([u8; 48])` + unsafe { &*as_array::<48>(bytes).as_ptr().cast() } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic_test() { + // test data from: https://github.com/ethereum/c-kzg-4844/blob/main/tests/verify_kzg_proof/kzg-mainnet/verify_kzg_proof_case_correct_proof_31ebd010e6098750/data.yaml + + let commitment = hex!("8f59a8d2a1a625a17f3fea0fe5eb8c896db3764f3185481bc22f91b4aaffcca25f26936857bc3a7c2539ea8ec3a952b7").to_vec(); + let mut versioned_hash = digest::sha256(&commitment).to_vec(); + versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; + let z = hex!( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000" + ) + .to_vec(); + let y = hex!( + "1522a4a7f34e1ea350ae07c29c96c7e79655aa926122e95fe69fcbd932ca49e9" + ) + .to_vec(); + let proof = hex!("a62ad71d14c5719385c0686f1871430475bf3a00f0aa3f7b8dd99a9abc2160744faf0070725e00b60ad9a026a15b1a8c").to_vec(); + + let input = [versioned_hash, z, y, commitment, proof].concat(); + run(&input).unwrap(); + + let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + + assert_eq!(RETURN_VALUE[..], expected_output); + } +} diff --git a/crates/cfxcore/executor/src/builtin/mod.rs b/crates/cfxcore/executor/src/builtin/mod.rs index abfe7da623..2b3ba55c2f 100644 --- a/crates/cfxcore/executor/src/builtin/mod.rs +++ b/crates/cfxcore/executor/src/builtin/mod.rs @@ -21,7 +21,9 @@ //! Standard built-in contracts. mod blake2f; +mod ethereum_trusted_setup_points; mod executable; +mod kzg_point_evaluations; pub use executable::BuiltinExec; @@ -277,6 +279,7 @@ pub fn builtin_factory(name: &str) -> Box { "alt_bn128_mul" => Box::new(Bn128MulImpl) as Box, "alt_bn128_pairing" => Box::new(Bn128PairingImpl) as Box, "blake2_f" => Box::new(Blake2FImpl) as Box, + "kzg_point_eval" => Box::new(KzgPointEval) as Box, _ => panic!("invalid builtin name: {}", name), } } @@ -325,6 +328,10 @@ struct Bn128PairingImpl; #[allow(dead_code)] struct Blake2FImpl; +#[derive(Debug)] +#[allow(dead_code)] +struct KzgPointEval; + impl Impl for Identity { fn execute( &self, input: &[u8], output: &mut BytesRef, @@ -771,6 +778,15 @@ impl Impl for Blake2FImpl { } } +impl Impl for KzgPointEval { + fn execute( + &self, input: &[u8], output: &mut BytesRef, + ) -> Result<(), Error> { + kzg_point_evaluations::run(input)?; + output.write(0, &kzg_point_evaluations::RETURN_VALUE[..]); + Ok(()) + } +} #[cfg(test)] mod tests { use super::{ diff --git a/crates/cfxcore/executor/src/machine/mod.rs b/crates/cfxcore/executor/src/machine/mod.rs index 87999a99b0..fd62a80465 100644 --- a/crates/cfxcore/executor/src/machine/mod.rs +++ b/crates/cfxcore/executor/src/machine/mod.rs @@ -173,6 +173,14 @@ fn new_builtin_map( params.transition_numbers.cip92, ), ); + btree.insert( + Address::from(H256::from_low_u64_be(10)), + Builtin::new( + Box::new(Linear::new(50000, 0)), + builtin_factory("kzg_point_eval"), + params.transition_numbers.cip144, + ), + ); btree } diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 83831669a2..b05e99a9bb 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -111,6 +111,8 @@ pub struct TransitionsBlockNumber { /// CIP-142: Transient Storage Opcodes /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy pub cancun_opcodes: BlockNumber, + /// CIP-144: Point Evaluation Precompile from EIP-4844 + pub cip144: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -187,6 +189,7 @@ impl CommonParams { spec.cip133_e = self.transition_heights.cip133e; spec.cip133_core = number >= self.transition_numbers.cip133b; spec.cip137 = number >= self.transition_numbers.cip137; + spec.cip144 = number >= self.transition_numbers.cip144; spec.cip1559 = height >= self.transition_heights.cip1559; spec.cancun_opcodes = number >= self.transition_numbers.cancun_opcodes; if spec.cancun_opcodes { diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index 690b258774..6a454d1ef9 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -180,6 +180,8 @@ pub struct Spec { /// CIP-142: Transient Storage Opcodes /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy pub cancun_opcodes: bool, + /// CIP-144: Point Evaluation Precompile from EIP-4844 + pub cip144: bool, pub params_dao_vote_period: u64, } @@ -334,6 +336,7 @@ impl Spec { cip137: false, cip1559: false, cancun_opcodes: false, + cip144: false, } } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 9e8faf769f..683417a489 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1402,7 +1402,7 @@ impl Configuration { // set_conf!( self.raw_conf.next_hardfork_transition_number.unwrap_or(default_transition_time); - params.transition_numbers => { cip131, cip132, cip133b, cip137 } + params.transition_numbers => { cip131, cip132, cip133b, cip137, cip144 } ); set_conf!( self.raw_conf.next_hardfork_transition_height.unwrap_or(default_transition_time); From 6c24df7549def3f90245ab82045de5ca6369f6a9 Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 17 May 2024 15:57:03 +0800 Subject: [PATCH 058/137] respect newest consensus_executor update --- .../consensus_executor/epoch_execution.rs | 27 ++++-- .../consensus_inner/consensus_executor/mod.rs | 85 +++++++++---------- .../vm-interpreter/src/instructions.rs | 1 - 3 files changed, 60 insertions(+), 53 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs index b40f8f9a54..16ce584964 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs @@ -148,7 +148,9 @@ impl ConsensusExecutionHandler { drop(prefetch_join_handles); } - fn make_block_env(&self, block_context: &BlockProcessContext) -> Env { + pub(super) fn make_block_env( + &self, block_context: &BlockProcessContext, + ) -> Env { let BlockProcessContext { epoch_context: &EpochProcessContext { @@ -413,7 +415,7 @@ impl ConsensusExecutionHandler { } } -struct EpochProcessContext<'a> { +pub(super) struct EpochProcessContext<'a> { on_local_pivot: bool, executive_trace: bool, @@ -423,7 +425,22 @@ struct EpochProcessContext<'a> { burnt_gas_price: SpaceMap, } -struct BlockProcessContext<'a, 'b> { +impl<'a> EpochProcessContext<'a> { + pub(super) fn new( + on_local_pivot: bool, executive_trace: bool, pivot_block: &'a Block, + base_gas_price: SpaceMap, burnt_gas_price: SpaceMap, + ) -> Self { + Self { + on_local_pivot, + executive_trace, + pivot_block, + base_gas_price, + burnt_gas_price, + } + } +} + +pub(super) struct BlockProcessContext<'a, 'b> { epoch_context: &'b EpochProcessContext<'a>, block: &'b Block, block_number: u64, @@ -431,7 +448,7 @@ struct BlockProcessContext<'a, 'b> { } impl<'a, 'b> BlockProcessContext<'a, 'b> { - fn first_block( + pub(super) fn first_block( epoch_context: &'b EpochProcessContext<'a>, block: &'b Block, start_block_number: u64, ) -> Self { @@ -445,7 +462,7 @@ impl<'a, 'b> BlockProcessContext<'a, 'b> { } } - fn next_block(&mut self, block: &'b Block) { + pub(super) fn next_block(&mut self, block: &'b Block) { self.last_hash = self.block.hash(); self.block_number += 1; self.block = block; diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 60676d6f30..1b787d00e1 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -4,6 +4,8 @@ mod epoch_execution; +use epoch_execution::{BlockProcessContext, EpochProcessContext}; + use core::convert::TryFrom; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, @@ -1759,62 +1761,44 @@ impl ConsensusExecutionHandler { ) -> DbResult> { let pivot_block = epoch_blocks.last().expect("Epoch not empty"); - let mut block_number = start_block_number; - let mut last_block_hash = - pivot_block.block_header.parent_hash().clone(); - let last_block_header = - &self.data_man.block_header_by_hash(&last_block_hash); + let base_gas_price = + pivot_block.block_header.base_price().unwrap_or_default(); + let burnt_gas_price = + base_gas_price.map_all(|x| state.burnt_gas_price(x)); + + let context = EpochProcessContext::new( + true, // TODO check this + false, + pivot_block, + base_gas_price, + burnt_gas_price, + ); + + // used for make env + let mut block_context = BlockProcessContext::first_block( + &context, + epoch_blocks.first().unwrap(), + start_block_number, + ); let mut traces = Vec::new(); + let mut block_number = start_block_number; - for block in epoch_blocks.iter() { - // self.maybe_update_state(state, block_number)?; + for (idx, block) in epoch_blocks.iter().enumerate() { + if idx > 0 { + block_context.next_block(block); + } + + let _secondary_reward = + self.before_block_execution(state, block_number, block)?; + + let mut env = self.make_block_env(&block_context); - let pos_id = last_block_header - .as_ref() - .and_then(|header| header.pos_reference().as_ref()); - let pos_view_number = - pos_id.and_then(|id| self.pos_verifier.get_pos_view(id)); - let pivot_decision_epoch = pos_id - .and_then(|id| self.pos_verifier.get_pivot_decision(id)) - .and_then(|hash| self.data_man.block_header_by_hash(&hash)) - .map(|header| header.height()); - - let epoch_height = pivot_block.block_header.height(); - let chain_id = self.machine.params().chain_id_map(epoch_height); - let mut env = Env { - chain_id, - number: block_number, - author: block.block_header.author().clone(), - timestamp: pivot_block.block_header.timestamp(), - difficulty: block.block_header.difficulty().clone(), - accumulated_gas_used: U256::zero(), - last_hash: last_block_hash, - gas_limit: U256::from(block.block_header.gas_limit()), - epoch_height, - pos_view: pos_view_number, - finalized_epoch: pivot_decision_epoch, - transaction_epoch_bound: self - .verification_config - .transaction_epoch_bound, - base_gas_price: Default::default(), - burnt_gas_price: Default::default(), - }; let spec = self.machine.spec(env.number, env.epoch_height); - if !spec.cip43_contract { - state.bump_block_number_accumulate_interest(); - } let machine = self.machine.as_ref(); - state.inc_distributable_pos_interest(env.number)?; - // initialize_internal_contract_accounts( - // state, - // self.machine.internal_contracts().initialized_at(env.number), - // ); block_number += 1; - last_block_hash = block.hash(); - for (_idx, transaction) in block.transactions.iter().enumerate() { let need_trace = match tx_hash { Some(ref hash) => transaction.hash() == *hash, @@ -1865,6 +1849,13 @@ impl ConsensusExecutionHandler { ExecutiveContext::new(state, &env, machine, &spec) .transact(transaction, options)?; + if let Some(burnt_fee) = execution_outcome + .try_as_executed() + .and_then(|e| e.burnt_fee) + { + state.burn_by_cip1559(burnt_fee); + }; + let r = make_process_tx_outcome( execution_outcome, &mut env.accumulated_gas_used, diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index a93ed77351..9354e9b84f 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -162,7 +162,6 @@ enum_with_from_u8! { #[doc = "base fee for EIP-1559 (EIP-3198)"] BASEFEE = 0x48, - // BASEFEE=0x48 // BLOBHASH=0x49 // BLOBBASEFEE=0x4A From 5803df102d053d4cfbd1bfb31248cb95bfd4ca21 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Fri, 17 May 2024 18:46:54 +0800 Subject: [PATCH 059/137] CIP-145: Fix Receipts upon NotEnoughBalance Error --- crates/cfxcore/executor/src/executive/executed.rs | 3 +++ crates/cfxcore/executor/src/spec.rs | 3 +++ crates/cfxcore/vm-types/src/spec.rs | 5 ++++- crates/client/src/configuration.rs | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/cfxcore/executor/src/executive/executed.rs b/crates/cfxcore/executor/src/executive/executed.rs index f8657128ea..fe0b7ed557 100644 --- a/crates/cfxcore/executor/src/executive/executed.rs +++ b/crates/cfxcore/executor/src/executive/executed.rs @@ -117,6 +117,9 @@ impl Executed { gas_sponsor_paid = false; storage_sponsor_paid = false; } + if spec.cip145 { + gas_sponsor_paid = false; + } let fee = tx.gas().saturating_mul(cost.gas_price); diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 83831669a2..c6ece73f77 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -111,6 +111,8 @@ pub struct TransitionsBlockNumber { /// CIP-142: Transient Storage Opcodes /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy pub cancun_opcodes: BlockNumber, + /// CIP-145: Fix Receipts upon `NotEnoughBalance` Error + pub cip145: BlockNumber, } #[derive(Default, Debug, Clone)] @@ -187,6 +189,7 @@ impl CommonParams { spec.cip133_e = self.transition_heights.cip133e; spec.cip133_core = number >= self.transition_numbers.cip133b; spec.cip137 = number >= self.transition_numbers.cip137; + spec.cip145 = number >= self.transition_numbers.cip145; spec.cip1559 = height >= self.transition_heights.cip1559; spec.cancun_opcodes = number >= self.transition_numbers.cancun_opcodes; if spec.cancun_opcodes { diff --git a/crates/cfxcore/vm-types/src/spec.rs b/crates/cfxcore/vm-types/src/spec.rs index 690b258774..da5b9b236a 100644 --- a/crates/cfxcore/vm-types/src/spec.rs +++ b/crates/cfxcore/vm-types/src/spec.rs @@ -152,6 +152,7 @@ pub struct Spec { /// CIP-94: On-chain Parameter DAO Vote pub cip94: bool, pub cip94_activation_block_number: u64, + pub params_dao_vote_period: u64, /// CIP-97: Remove staking list pub cip97: bool, /// CIP-98: Fix espace bug @@ -180,7 +181,8 @@ pub struct Spec { /// CIP-142: Transient Storage Opcodes /// CIP-143: MCOPY (0x5e) Opcode for Efficient Memory Copy pub cancun_opcodes: bool, - pub params_dao_vote_period: u64, + /// CIP-145: Fix Receipts upon `NotEnoughBalance` Error + pub cip145: bool, } /// Wasm cost table @@ -332,6 +334,7 @@ impl Spec { cip133_e: u64::MAX, cip133_core: false, cip137: false, + cip145: false, cip1559: false, cancun_opcodes: false, } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 9e8faf769f..4d9bd2b2d3 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1402,7 +1402,7 @@ impl Configuration { // set_conf!( self.raw_conf.next_hardfork_transition_number.unwrap_or(default_transition_time); - params.transition_numbers => { cip131, cip132, cip133b, cip137 } + params.transition_numbers => { cip131, cip132, cip133b, cip137, cip145 } ); set_conf!( self.raw_conf.next_hardfork_transition_height.unwrap_or(default_transition_time); From 5753824641503c76d6ccab1e25b5578c052e2f52 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Fri, 17 May 2024 17:37:48 +0800 Subject: [PATCH 060/137] Fix issues in the first 1559 dev-net --- crates/cfxcore/core/src/consensus/mod.rs | 13 +- .../executor/src/executive/executed.rs | 13 +- .../executor/src/executive/fresh_executive.rs | 16 ++- .../src/executive/pre_checked_executive.rs | 3 + crates/client/src/rpc/impls/cfx.rs | 3 +- crates/client/src/rpc/impls/eth.rs | 83 ++++++++++--- crates/client/src/rpc/impls/pos.rs | 7 +- crates/client/src/rpc/types/call_request.rs | 112 +++++++++++++++--- .../client/src/rpc/types/eth/call_request.rs | 7 +- crates/client/src/rpc/types/transaction.rs | 2 +- crates/dbs/statedb/src/global_params.rs | 4 + crates/primitives/src/lib.rs | 3 - crates/primitives/src/receipt.rs | 16 ++- .../src/transaction/eth_transaction.rs | 20 ++++ crates/primitives/src/transaction/mod.rs | 21 ++-- .../src/transaction/native_transaction.rs | 20 ++++ tests/evm_space/eip1559_test.py | 23 +++- 17 files changed, 295 insertions(+), 71 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index a2853d742f..127df62494 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -1882,7 +1882,8 @@ impl ConsensusGraph { traces: vec![], }; - let mut gas_used = U256::from(0); + let mut accumulated_gas_used = U256::from(0); + let mut gas_used_offset; let iter_blocks = if only_pivot { &blocks[blocks.len() - 1..] @@ -1891,6 +1892,7 @@ impl ConsensusGraph { }; for b in iter_blocks { + gas_used_offset = accumulated_gas_used; // note: we need the receipts to reconstruct a phantom block. // as a result, we cannot return unexecuted blocks in eth_* RPCs. let exec_info = match self @@ -1962,11 +1964,11 @@ impl ConsensusGraph { return Err("Inconsistent state: zero transaction gas price".into()); } - // FIXME(thegaram): is this correct? - gas_used += receipt.gas_fee / tx.gas_price(); + accumulated_gas_used = + gas_used_offset + receipt.accumulated_gas_used; phantom_block.receipts.push(Receipt { - accumulated_gas_used: gas_used, + accumulated_gas_used, outcome_status: receipt.outcome_status, ..receipt.clone() }); @@ -2013,7 +2015,8 @@ impl ConsensusGraph { )); // note: phantom txs consume no gas - let phantom_receipt = p.into_receipt(gas_used); + let phantom_receipt = + p.into_receipt(accumulated_gas_used); phantom_block .bloom diff --git a/crates/cfxcore/executor/src/executive/executed.rs b/crates/cfxcore/executor/src/executive/executed.rs index f8657128ea..cc8fa82bd7 100644 --- a/crates/cfxcore/executor/src/executive/executed.rs +++ b/crates/cfxcore/executor/src/executive/executed.rs @@ -74,13 +74,13 @@ pub type ExecutedExt = ShareDebugMap; impl Executed { pub(super) fn not_enough_balance_fee_charged( - tx: &TransactionWithSignature, fee: &U256, cost: CostInfo, + tx: &TransactionWithSignature, actual_gas_cost: &U256, cost: CostInfo, ext_result: ExecutedExt, spec: &Spec, ) -> Self { let gas_charged = if cost.gas_price == U256::zero() { U256::zero() } else { - fee / cost.gas_price + actual_gas_cost / cost.gas_price }; let mut gas_sponsor_paid = cost.gas_sponsored; let mut storage_sponsor_paid = cost.storage_sponsored; @@ -88,11 +88,16 @@ impl Executed { gas_sponsor_paid = false; storage_sponsor_paid = false; } - let burnt_fee = spec.cip1559.then_some(*fee); + + let burnt_fee = spec.cip1559.then(|| { + let target_burnt = tx.gas().saturating_mul(cost.burnt_gas_price); + U256::min(*actual_gas_cost, target_burnt) + }); + Self { gas_used: *tx.gas(), gas_charged, - fee: fee.clone(), + fee: *actual_gas_cost, burnt_fee, gas_sponsor_paid, logs: vec![], diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 301525795b..1daac317f4 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -31,18 +31,30 @@ pub struct FreshExecutive<'a, O: ExecutiveObserver> { } pub(super) struct CostInfo { + /// Sender balance pub sender_balance: U512, + /// The intrinsic gas (21000/53000 + tx data gas + access list gas) pub base_gas: u64, + /// Transaction value + gas cost (except the sponsored part) pub total_cost: U512, + /// Gas cost pub gas_cost: U512, + /// Storage collateral cost pub storage_cost: U256, + /// Transaction value + gas cost (except the part that eligible for + /// sponsor) pub sender_intended_cost: U512, + /// Effective gas price pub gas_price: U256, + /// Burnt gas price pub burnt_gas_price: U256, + /// Transaction's gas is sponsored pub gas_sponsored: bool, + /// Transaction's collateral is sponsored pub storage_sponsored: bool, + /// Transaction's gas is in the sponsor whitelist pub storage_sponsor_eligible: bool, } @@ -196,8 +208,8 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let burnt_gas_price = env.burnt_gas_price[tx.space()]; // gas_price >= actual_base_gas >= - // 1. tx gas_price >= burnt_base_price - // 2. base_gas_price >= burnt_gas_price + // either 1. tx gas_price >= burnt_gas_price + // or 2. base_gas_price >= burnt_gas_price assert!(gas_price >= burnt_gas_price); let sender_balance = U512::from(state.balance(&sender)?); diff --git a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs index 63e7f9e75e..fbe993919a 100644 --- a/crates/cfxcore/executor/src/executive/pre_checked_executive.rs +++ b/crates/cfxcore/executor/src/executive/pre_checked_executive.rs @@ -95,6 +95,9 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { .. } = self.cost; + // Here, we only discuss the case `sender_balance < + // sender_intended_cost` The case `sender_intended_cost <= + // sender_balance < sender_cost` has been handled before nonce bumping. let insufficient_sender_balance = sender_balance < sender_intended_cost; let actual_gas_cost: U256 = if insufficient_sender_balance { diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 0b4fe1aab5..5a47acf595 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -1433,7 +1433,8 @@ impl RpcImpl { let estimate_request = EstimateRequest { has_sender: request.from.is_some(), has_gas_limit: request.gas.is_some(), - has_gas_price: request.gas_price.is_some(), + has_gas_price: request.gas_price.is_some() + || request.max_priority_fee_per_gas.is_some(), has_nonce: request.nonce.is_some(), has_storage_limit: request.storage_limit.is_some(), }; diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index d1d3cecc52..e1c020fb03 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -41,8 +41,7 @@ use cfxcore::{ use clap::crate_version; use jsonrpc_core::{Error as RpcError, Result as RpcResult}; use primitives::{ - filter::LogFilter, receipt::EVM_SPACE_SUCCESS, - transaction::eth_transaction::Eip155Transaction, Action, + filter::LogFilter, receipt::EVM_SPACE_SUCCESS, Action, BlockHashOrEpochNumber, EpochNumber, SignedTransaction, StorageKey, StorageValue, TransactionStatus, TransactionWithSignature, }; @@ -80,20 +79,76 @@ impl EthHandler { pub fn sign_call( chain_id: u32, request: CallRequest, ) -> RpcResult { + use primitives::transaction::*; + use EthereumTransaction::*; let max_gas = U256::from(MAX_GAS_CALL_REQUEST); let gas = min(request.gas.unwrap_or(max_gas), max_gas); - let from = request.from.unwrap_or_else(|| Address::zero()); - - Ok(Eip155Transaction { - nonce: request.nonce.unwrap_or_default(), - action: request.to.map_or(Action::Create, |addr| Action::Call(addr)), - gas, - gas_price: request.gas_price.unwrap_or(1.into()), - value: request.value.unwrap_or_default(), - chain_id: Some(chain_id), - data: request.data.unwrap_or_default().into_vec(), - } - .fake_sign_rpc(from.with_evm_space())) + let nonce = request.nonce.unwrap_or_default(); + let action = request.to.map_or(Action::Create, |addr| Action::Call(addr)); + let value = request.value.unwrap_or_default(); + + let default_type_id = if request.max_fee_per_gas.is_some() + || request.max_priority_fee_per_gas.is_some() + { + 2 + } else if request.access_list.is_some() { + 1 + } else { + 0 + }; + let transaction_type = request.transaction_type.unwrap_or(default_type_id); + + let gas_price = request.gas_price.unwrap_or(1.into()); + let max_fee_per_gas = request + .max_fee_per_gas + .or(request.max_priority_fee_per_gas) + .unwrap_or(gas_price); + let max_priority_fee_per_gas = + request.max_priority_fee_per_gas.unwrap_or(U256::zero()); + let access_list = request.access_list.unwrap_or(vec![]); + let data = request.data.unwrap_or_default().into_vec(); + + let transaction = match transaction_type { + 0 => Eip155(Eip155Transaction { + nonce, + gas_price, + gas, + action, + value, + chain_id: Some(chain_id), + data, + }), + 1 => Eip2930(Eip2930Transaction { + chain_id, + nonce, + gas_price, + gas, + action, + value, + data, + access_list, + }), + 2 => Eip1559(Eip1559Transaction { + chain_id, + nonce, + max_priority_fee_per_gas, + max_fee_per_gas, + gas, + action, + value, + data, + access_list, + }), + x => { + return Err( + invalid_params("Unrecognized transaction type", x).into() + ); + } + }; + + let from = request.from.unwrap_or(Address::zero()); + + Ok(transaction.fake_sign_rpc(from.with_evm_space())) } fn block_tx_by_index( diff --git a/crates/client/src/rpc/impls/pos.rs b/crates/client/src/rpc/impls/pos.rs index 53a3ce8d43..d9498373a0 100644 --- a/crates/client/src/rpc/impls/pos.rs +++ b/crates/client/src/rpc/impls/pos.rs @@ -179,17 +179,12 @@ impl PosHandler { call_data.extend_from_slice(&account_addr.abi_encode()); let call_request = CallRequest { - from: None, to: Some(RpcAddress::try_from_h160( POS_REGISTER_CONTRACT_ADDRESS, self.network_type, )?), - gas_price: None, - gas: None, - value: None, data: Some(Bytes(call_data)), - nonce: None, - storage_limit: None, + ..Default::default() }; let epoch = match view { diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index 6678608a6c..b2531a0ab5 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -3,6 +3,7 @@ // See http://www.gnu.org/licenses/ use crate::rpc::{ + error_codes::invalid_params, types::{ address::RpcAddress, errors::{check_rpc_address_network, RcpAddressNetworkInconsistent}, @@ -19,7 +20,7 @@ use primitives::{ transaction::{ native_transaction::NativeTransaction as PrimitiveTransaction, Action, }, - SignedTransaction, Transaction, TransactionWithSignature, + AccessList, SignedTransaction, Transaction, TransactionWithSignature, }; use std::{cmp::min, sync::Arc}; @@ -48,6 +49,12 @@ pub struct CallRequest { pub nonce: Option, /// StorageLimit pub storage_limit: Option, + /// Access list in EIP-2930 + pub access_list: Option, + pub max_fee_per_gas: Option, + pub max_priority_fee_per_gas: Option, + #[serde(rename = "type")] + pub transaction_type: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -145,29 +152,93 @@ impl SendTxRequest { pub fn sign_call( epoch_height: u64, chain_id: u32, request: CallRequest, ) -> RpcResult { + use primitives::transaction::*; + use TypedNativeTransaction::*; + let max_gas = U256::from(MAX_GAS_CALL_REQUEST); let gas = min(request.gas.unwrap_or(max_gas), max_gas); + + let nonce = request.nonce.unwrap_or_default(); + let action = request.to.map_or(Action::Create, |rpc_addr| { + Action::Call(rpc_addr.hex_address) + }); + + let value = request.value.unwrap_or_default(); + let storage_limit = request + .storage_limit + .map(|v| v.as_u64()) + .unwrap_or(std::u64::MAX); + let data = request.data.unwrap_or_default().into_vec(); + + let default_type_id = if request.max_fee_per_gas.is_some() + || request.max_priority_fee_per_gas.is_some() + { + 2 + } else if request.access_list.is_some() { + 1 + } else { + 0 + }; + let transaction_type = request.transaction_type.unwrap_or(default_type_id); + + let gas_price = request.gas_price.unwrap_or(1.into()); + let max_fee_per_gas = request + .max_fee_per_gas + .or(request.max_priority_fee_per_gas) + .unwrap_or(gas_price); + let max_priority_fee_per_gas = + request.max_priority_fee_per_gas.unwrap_or(U256::zero()); + let access_list = request.access_list.unwrap_or(vec![]); + + let transaction = match transaction_type { + 0 => Cip155(NativeTransaction { + nonce, + action, + gas, + gas_price, + value, + storage_limit, + epoch_height, + chain_id, + data, + }), + 1 => Cip2930(Cip2930Transaction { + nonce, + gas_price, + gas, + action, + value, + storage_limit, + epoch_height, + chain_id, + data, + access_list, + }), + 2 => Cip1559(Cip1559Transaction { + nonce, + action, + gas, + value, + max_fee_per_gas, + max_priority_fee_per_gas, + storage_limit, + epoch_height, + chain_id, + data, + access_list, + }), + x => { + return Err( + invalid_params("Unrecognized transaction type", x).into() + ); + } + }; + let from = request .from .map_or_else(|| Address::zero(), |rpc_addr| rpc_addr.hex_address); - Ok(PrimitiveTransaction { - nonce: request.nonce.unwrap_or_default(), - action: request.to.map_or(Action::Create, |rpc_addr| { - Action::Call(rpc_addr.hex_address) - }), - gas, - gas_price: request.gas_price.unwrap_or(1.into()), - value: request.value.unwrap_or_default(), - storage_limit: request - .storage_limit - .map(|v| v.as_u64()) - .unwrap_or(std::u64::MAX), - epoch_height, - chain_id, - data: request.data.unwrap_or_default().into_vec(), - } - .fake_sign(from.with_native_space())) + Ok(transaction.fake_sign_rpc(from.with_native_space())) } pub fn rpc_call_request_network( @@ -224,6 +295,7 @@ mod tests { data: Some(vec![0x12, 0x34, 0x56].into()), storage_limit: Some(U64::from_str("7b").unwrap()), nonce: Some(U256::from(4)), + ..Default::default() }; let s = r#"{ @@ -255,7 +327,8 @@ mod tests { value: Some(U256::from_str("9184e72a").unwrap()), storage_limit: Some(U64::from_str("3344adf").unwrap()), data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex::>().unwrap().into()), - nonce: None + nonce: None, + ..Default::default() }; let s = r#"{ @@ -293,6 +366,7 @@ mod tests { data: None, storage_limit: None, nonce: None, + ..Default::default() }; let s = r#"{"from":"CFX:TYPE.BUILTIN:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJC4EYEY6"}"#; diff --git a/crates/client/src/rpc/types/eth/call_request.rs b/crates/client/src/rpc/types/eth/call_request.rs index 9171f24d8f..459af0a456 100644 --- a/crates/client/src/rpc/types/eth/call_request.rs +++ b/crates/client/src/rpc/types/eth/call_request.rs @@ -20,6 +20,7 @@ use crate::rpc::types::Bytes; use cfx_types::{H160, U256}; +use primitives::AccessList; /// Call request #[derive(Debug, Default, PartialEq, Deserialize)] @@ -30,10 +31,8 @@ pub struct CallRequest { /// To pub to: Option, /// Gas Price - #[serde(skip_serializing_if = "Option::is_none")] pub gas_price: Option, /// Max fee per gas - #[serde(skip_serializing_if = "Option::is_none")] pub max_fee_per_gas: Option, /// Gas pub gas: Option, @@ -44,8 +43,10 @@ pub struct CallRequest { /// Nonce pub nonce: Option, /// Miner bribe - #[serde(skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, + pub access_list: Option, + #[serde(rename = "type")] + pub transaction_type: Option, } // impl Into for CallRequest { diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index 9d350710f0..8ef0f0cde0 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -164,7 +164,7 @@ impl Transaction { .after_1559() .then_some(*t.max_priority_gas_price()), y_parity: t.is_2718().then_some(t.transaction.v.into()), - transaction_type: Default::default(), + transaction_type: Some(t.type_id()), v: t.transaction.v.into(), r: t.transaction.r.into(), s: t.transaction.s.into(), diff --git a/crates/dbs/statedb/src/global_params.rs b/crates/dbs/statedb/src/global_params.rs index 2d39a7718b..7cbd209e9a 100644 --- a/crates/dbs/statedb/src/global_params.rs +++ b/crates/dbs/statedb/src/global_params.rs @@ -42,6 +42,8 @@ macro_rules! for_all_global_param_keys { $f::($($args),*); $f::($($args),*); $f::($($args),*); + $f::($($args),*); + $f::($($args),*); }; ($f:ident::($($args:expr),*)?;) => { $f::($($args),*)?; @@ -56,6 +58,8 @@ macro_rules! for_all_global_param_keys { $f::($($args),*)?; $f::($($args),*)?; $f::($($args),*)?; + $f::($($args),*)?; + $f::($($args),*)?; }; } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index edc326da56..3000d88009 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -57,6 +57,3 @@ pub use crate::{ transaction_index::TransactionIndex, zero::Zero, }; -pub use transaction::{ - eth_transaction::Eip155Transaction, native_transaction::NativeTransaction, -}; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 9692542312..6d771f2ed3 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -91,7 +91,7 @@ pub struct SortedStorageChanges { } /// Information describing execution of a transaction. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Receipt { /// The total gas used (not gas charged) in the block following execution /// of the transaction. @@ -113,6 +113,20 @@ pub struct Receipt { pub burnt_gas_fee: Option, } +#[test] +fn tmp() { + let receipt = Receipt { + accumulated_gas_used: 189000.into(), + gas_fee: 60054.into(), + burnt_gas_fee: Some(30027.into()), + ..Default::default() + }; + dbg!(&receipt); + let x = receipt.rlp_bytes(); + let receipt2: Receipt = Rlp::new(&x).as_val().unwrap(); + assert_eq!(receipt2, receipt) +} + impl Encodable for Receipt { fn rlp_append(&self, s: &mut RlpStream) { let length = if self.burnt_gas_fee.is_none() { 9 } else { 10 }; diff --git a/crates/primitives/src/transaction/eth_transaction.rs b/crates/primitives/src/transaction/eth_transaction.rs index b6e079e7e4..e83c321d56 100644 --- a/crates/primitives/src/transaction/eth_transaction.rs +++ b/crates/primitives/src/transaction/eth_transaction.rs @@ -169,6 +169,26 @@ pub enum EthereumTransaction { } use EthereumTransaction::*; +impl EthereumTransaction { + pub fn fake_sign_rpc(self, from: AddressWithSpace) -> SignedTransaction { + SignedTransaction { + transaction: TransactionWithSignature { + transaction: TransactionWithSignatureSerializePart { + unsigned: Transaction::Ethereum(self), + r: U256::one(), + s: U256::one(), + v: 0, + }, + hash: H256::zero(), + rlp_size: None, + } + .compute_hash(), + sender: from.address, + public: None, + } + } +} + macro_rules! eth_access_common_ref { ($field:ident, $ty:ty) => { pub fn $field(&self) -> &$ty { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index fee53cf247..78957036a1 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -5,26 +5,25 @@ pub mod eth_transaction; pub mod native_transaction; -use crate::{ - bytes::Bytes, - hash::keccak, - transaction::{ - eth_transaction::{Eip1559Transaction, Eip2930Transaction}, - native_transaction::TypedNativeTransaction, - }, +pub use eth_transaction::{ + Eip1559Transaction, Eip155Transaction, Eip2930Transaction, + EthereumTransaction, +}; +pub use native_transaction::{ + Cip1559Transaction, Cip2930Transaction, NativeTransaction, + TypedNativeTransaction, }; + +use crate::{bytes::Bytes, hash::keccak}; use cfx_types::{ Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H160, H256, U256, }; -use eth_transaction::{ - eip155_signature, Eip155Transaction, EthereumTransaction, -}; +use eth_transaction::eip155_signature; use keylib::{ self, public_to_address, recover, verify_public, Public, Secret, Signature, }; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use native_transaction::NativeTransaction; use rlp::{self, Decodable, DecoderError, Encodable, Rlp, RlpStream}; use serde::{Deserialize, Serialize}; use std::{ diff --git a/crates/primitives/src/transaction/native_transaction.rs b/crates/primitives/src/transaction/native_transaction.rs index b7ebb7ab81..35c5edc381 100644 --- a/crates/primitives/src/transaction/native_transaction.rs +++ b/crates/primitives/src/transaction/native_transaction.rs @@ -187,4 +187,24 @@ pub enum TypedNativeTransaction { Cip1559(Cip1559Transaction), } +impl TypedNativeTransaction { + pub fn fake_sign_rpc(self, from: AddressWithSpace) -> SignedTransaction { + SignedTransaction { + transaction: TransactionWithSignature { + transaction: TransactionWithSignatureSerializePart { + unsigned: Transaction::Native(self), + r: U256::one(), + s: U256::one(), + v: 0, + }, + hash: H256::zero(), + rlp_size: None, + } + .compute_hash(), + sender: from.address, + public: None, + } + } +} + use TypedNativeTransaction::*; diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 07dcf5b424..51296024c4 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -62,7 +62,7 @@ def run_test(self): self.rpc.generate_block(1) self.rpc.generate_blocks(20, 1) receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) - print(receipt) + assert_equal(receipt["status"], 1) # TODO check EIP1559 gas usage # assert_equal(receipt["gasUsed"], 210000 / 4 * 3) @@ -86,6 +86,27 @@ def run_test(self): assert_equal(len(fee_history['gas_used_ratio']), 5) assert_equal(len(fee_history['reward']), 5) + assert_greater_than(int(self.nodes[0].cfx_getFeeBurnt(), 0), 0) + + nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + for i in range(1, 10): + signed = self.evmAccount.signTransaction({ + "type": "0x2", + "to": self.evmAccount.address, + "value": 1, + "gas": 21000, + 'maxFeePerGas': 20* (10**9), + 'maxPriorityFeePerGas': 1, + "nonce": i, + "chainId": 10, + }) + return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + self.rpc.generate_block(10) + self.rpc.generate_blocks(20, 1) + receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) + assert_equal(receipt["cumulativeGasUsed"], 21000 * 9) + assert_equal(receipt["gasUsed"], 21000) + if __name__ == "__main__": Eip1559Test().main() \ No newline at end of file From 89c2982dbfb7bd00d9e04a0a2deedf4f320c280c Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Fri, 17 May 2024 19:29:30 +0800 Subject: [PATCH 061/137] Implement max priority fee --- crates/client/src/rpc/impls/cfx.rs | 1 + crates/client/src/rpc/impls/common.rs | 15 +++++++++++++++ crates/client/src/rpc/impls/eth.rs | 19 ++++++++++++++----- crates/client/src/rpc/impls/light.rs | 1 + crates/client/src/rpc/traits/cfx_space/cfx.rs | 4 ++++ crates/client/src/rpc/types/fee_history.rs | 2 ++ tests/evm_space/tx_and_receipt_test.py | 2 +- 7 files changed, 38 insertions(+), 6 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 5a47acf595..ce45fe910b 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -2266,6 +2266,7 @@ impl Cfx for CfxHandler { fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; fn fee_history(&self, block_count: usize, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn max_priority_fee_per_gas(&self) -> BoxFuture; } to self.rpc_impl { diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index 6e6cb40300..35e7fa4510 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -610,6 +610,21 @@ impl RpcImpl { Ok(fee_history) } + + pub fn max_priority_fee_per_gas(&self) -> RpcResult { + info!("RPC Request: max_priority_fee_per_gas",); + + let fee_history = + self.fee_history(300, EpochNumber::LatestState, vec![50])?; + + let total_reward: U256 = fee_history + .reward() + .iter() + .map(|x| x.first().unwrap()) + .fold(U256::zero(), |x, y| x + *y); + + Ok(total_reward / 300) + } } // Test RPC implementation diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index e1c020fb03..05d13a90d1 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -467,11 +467,20 @@ impl Eth for EthHandler { fn max_priority_fee_per_gas(&self) -> jsonrpc_core::Result { info!("RPC Request: eth_maxPriorityFeePerGas"); - let (_, maybe_base_price) = - self.tx_pool.get_best_info_with_parent_base_price(); - let answer = maybe_base_price - .map_or(U256::from(20000000000u64), |x| x[Space::Ethereum]); - Ok(answer) + let evm_ratio = + self.tx_pool.machine().params().evm_transaction_block_ratio + as usize; + + let fee_history = + self.fee_history(300, BlockNumber::Latest, vec![50])?; + + let total_reward: U256 = fee_history + .reward() + .iter() + .map(|x| x.first().unwrap()) + .fold(U256::zero(), |x, y| x + *y); + + Ok(total_reward * evm_ratio / 300) } fn accounts(&self) -> jsonrpc_core::Result> { diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index a83012663c..c252044fc8 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -1255,6 +1255,7 @@ impl Cfx for CfxHandler { fn get_vote_params(&self, epoch_num: Option) -> JsonRpcResult; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; fn get_fee_burnt(&self, epoch: Option) -> JsonRpcResult; + fn max_priority_fee_per_gas(&self) -> BoxFuture; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index 62cd023723..f6f8d2749f 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -40,6 +40,10 @@ pub trait Cfx { #[rpc(name = "cfx_gasPrice")] fn gas_price(&self) -> BoxFuture; + /// Returns current max_priority_fee + #[rpc(name = "cfx_maxPriorityFeePerGas")] + fn max_priority_fee_per_gas(&self) -> BoxFuture; + /// Returns highest epoch number. #[rpc(name = "cfx_epochNumber")] fn epoch_number( diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index a986a2b0ae..e168097be7 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -21,6 +21,8 @@ pub struct FeeHistory { impl FeeHistory { pub fn new() -> Self { Default::default() } + pub fn reward(&self) -> &VecDeque> { &self.reward } + pub fn push_back_block<'a, I>( &mut self, space: Space, percentiles: &Vec, pivot_header: &BlockHeader, transactions: I, diff --git a/tests/evm_space/tx_and_receipt_test.py b/tests/evm_space/tx_and_receipt_test.py index 273442ca77..4a1547e67a 100755 --- a/tests/evm_space/tx_and_receipt_test.py +++ b/tests/evm_space/tx_and_receipt_test.py @@ -41,7 +41,7 @@ def run_test(self): self.rpc.generate_blocks(20, 1) receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) assert_equal(receipt["status"], 1) - assert_equal(receipt["gasUsed"], 210000 / 4 * 3) + assert_equal(receipt["gasUsed"], 21000) assert_equal(receipt["txExecErrorMsg"], None) tx = self.w3.eth.get_transaction(return_tx_hash) From 37741fe6e5238a8a2bc5ad108cc7027f856f78b3 Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 20 May 2024 10:50:21 +0800 Subject: [PATCH 062/137] update submodule test_contracts --- tests/test_contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_contracts b/tests/test_contracts index d3f0452bda..ca89c563bc 160000 --- a/tests/test_contracts +++ b/tests/test_contracts @@ -1 +1 @@ -Subproject commit d3f0452bda55154491dc4ff5cf76a56f62472244 +Subproject commit ca89c563bcbd86289b87a48970bb4b8b09bebd11 From 93c913a2f4b1725446e369817da8ec92209e49bc Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 20 May 2024 12:08:56 +0800 Subject: [PATCH 063/137] Update estimate_gas for CIP-130 --- .../consensus_inner/consensus_executor/mod.rs | 8 +++- .../cfxcore/execute-helper/src/estimation.rs | 38 ++++++++++++++++--- .../src/executive/execution_outcome.rs | 3 ++ crates/client/src/rpc/impls/cfx.rs | 12 ++++++ crates/client/src/rpc/impls/eth.rs | 12 ++++++ 5 files changed, 66 insertions(+), 7 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index c1d5ca5356..4d70ba23a6 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -1644,6 +1644,10 @@ impl ConsensusExecutionHandler { address }; + let base_gas_price = best_block_header.base_price().unwrap_or_default(); + let burnt_gas_price = + base_gas_price.map_all(|x| state.burnt_gas_price(x)); + let env = Env { chain_id: self.machine.params().chain_id_map(block_height), number: start_block_number, @@ -1659,8 +1663,8 @@ impl ConsensusExecutionHandler { transaction_epoch_bound: self .verification_config .transaction_epoch_bound, - base_gas_price: Default::default(), - burnt_gas_price: Default::default(), + base_gas_price, + burnt_gas_price, }; let spec = self.machine.spec(env.number, env.epoch_height); let mut ex = EstimationContext::new( diff --git a/crates/cfxcore/execute-helper/src/estimation.rs b/crates/cfxcore/execute-helper/src/estimation.rs index ec55a1beab..62934c7d93 100644 --- a/crates/cfxcore/execute-helper/src/estimation.rs +++ b/crates/cfxcore/execute-helper/src/estimation.rs @@ -133,6 +133,10 @@ impl<'a> EstimationContext<'a> { pub fn transact_virtual( &mut self, mut tx: SignedTransaction, request: EstimateRequest, ) -> DbResult<(ExecutionOutcome, EstimateExt)> { + if let Some(outcome) = self.check_cip130(&tx, &request) { + return Ok(outcome); + } + self.process_estimate_request(&mut tx, &request)?; let (executed, overwrite_storage_limit) = match self @@ -147,7 +151,9 @@ impl<'a> EstimationContext<'a> { } ExecutionOutcome::ExecutionErrorBumpNonce(_, executed) => { EstimateExt { - estimated_gas_limit: estimated_gas_limit(executed), + estimated_gas_limit: estimated_gas_limit( + executed, &tx, + ), estimated_storage_limit: storage_limit(executed), } } @@ -165,6 +171,25 @@ impl<'a> EstimationContext<'a> { ) } + fn check_cip130( + &self, tx: &SignedTransaction, request: &EstimateRequest, + ) -> Option<(ExecutionOutcome, EstimateExt)> { + let min_gas_limit = U256::from(tx.data().len() * 100); + if !request.has_gas_limit || *tx.gas_limit() >= min_gas_limit { + return None; + } + + let outcome = ExecutionOutcome::NotExecutedDrop( + cfx_executor::executive::TxDropError::NotEnoughGasLimit { + expected: min_gas_limit, + got: *tx.gas_limit(), + }, + ); + let estimation = Default::default(); + + Some((outcome, estimation)) + } + // For the same transaction, the storage limit paid by user and the // storage limit paid by the sponsor are different values. So // this function will @@ -270,7 +295,7 @@ impl<'a> EstimationContext<'a> { ) -> DbResult<(ExecutionOutcome, EstimateExt)> { let estimated_storage_limit = overwrite_storage_limit.unwrap_or(storage_limit(&executed)); - let estimated_gas_limit = estimated_gas_limit(&executed); + let estimated_gas_limit = estimated_gas_limit(&executed, &tx); let estimation = EstimateExt { estimated_storage_limit, estimated_gas_limit, @@ -373,9 +398,12 @@ impl<'a> EstimationContext<'a> { } } -fn estimated_gas_limit(executed: &Executed) -> U256 { - executed.ext_result.get::().unwrap() * 7 / 6 - + executed.base_gas +fn estimated_gas_limit(executed: &Executed, tx: &SignedTransaction) -> U256 { + let cip130_min_gas_limit = U256::from(tx.data().len() * 100); + let estimated = + executed.ext_result.get::().unwrap() * 7 / 6 + + executed.base_gas; + U256::max(estimated, cip130_min_gas_limit) } fn storage_limit(executed: &Executed) -> u64 { diff --git a/crates/cfxcore/executor/src/executive/execution_outcome.rs b/crates/cfxcore/executor/src/executive/execution_outcome.rs index d92432e8a5..2cd341f061 100644 --- a/crates/cfxcore/executor/src/executive/execution_outcome.rs +++ b/crates/cfxcore/executor/src/executive/execution_outcome.rs @@ -67,6 +67,9 @@ pub enum TxDropError { /// Although it can be verified in tx packing, /// by spec doc, it is checked in execution. InvalidRecipientAddress(Address), + + /// Not enough gas limit for large transacton, only for estimation + NotEnoughGasLimit { expected: U256, got: U256 }, } #[derive(Debug, PartialEq)] diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index ce45fe910b..21d689f391 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -1266,6 +1266,12 @@ impl RpcImpl { "Transaction can not be executed".into(), format! {"invalid recipient address {:?}", recipient} )), + ExecutionOutcome::NotExecutedDrop( + TxDropError::NotEnoughGasLimit { expected, got }, + ) => bail!(call_execution_error( + "Transaction can not be executed".into(), + format! {"not enough gas limit with respected to tx size: expected {:?} got {:?}", expected, got} + )), ExecutionOutcome::NotExecutedToReconsiderPacking(e) => { bail!(call_execution_error( "Transaction can not be executed".into(), @@ -1317,6 +1323,12 @@ impl RpcImpl { format! {"{:?}", e} )) } + ExecutionOutcome::NotExecutedDrop( + TxDropError::NotEnoughGasLimit { expected, got }, + ) => bail!(call_execution_error( + "Can not estimate: transaction can not be executed".into(), + format! {"not enough gas limit with respected to tx size: expected {:?} got {:?}", expected, got} + )), ExecutionOutcome::ExecutionErrorBumpNonce( ExecutionError::VmError(VmError::Reverted), executed, diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 05d13a90d1..e9aa6d8815 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -801,6 +801,12 @@ impl Eth for EthHandler { "Transaction can not be executed".into(), format! {"invalid recipient address {:?}", recipient} )), + ExecutionOutcome::NotExecutedDrop( + TxDropError::NotEnoughGasLimit { expected, got }, + ) => bail!(call_execution_error( + "Can not estimate: transaction can not be executed".into(), + format! {"not enough gas limit with respected to tx size: expected {:?} got {:?}", expected, got} + )), ExecutionOutcome::NotExecutedToReconsiderPacking(e) => { bail!(call_execution_error( "Transaction can not be executed".into(), @@ -851,6 +857,12 @@ impl Eth for EthHandler { "Can not estimate: transaction can not be executed".into(), format! {"invalid recipient address {:?}", recipient} )), + ExecutionOutcome::NotExecutedDrop( + TxDropError::NotEnoughGasLimit { expected, got }, + ) => bail!(call_execution_error( + "Can not estimate: transaction can not be executed".into(), + format! {"not enough gas limit with respected to tx size: expected {:?} got {:?}", expected, got} + )), ExecutionOutcome::NotExecutedToReconsiderPacking(e) => { bail!(call_execution_error( "Can not estimate: transaction can not be executed".into(), From 0fef38c74912f5bf6d98447adb7bded6ebcf9a05 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 20 May 2024 14:57:45 +0800 Subject: [PATCH 064/137] Disable BASEFEE before 1559 --- crates/cfxcore/vm-interpreter/src/instructions.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index f9c400432d..9d774db749 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -370,6 +370,9 @@ impl Instruction { if instruction == Some(PUSH0) && !spec.cip119 { instruction = None; } + if instruction == Some(BASEFEE) && !spec.cip1559 { + instruction = None; + } return instruction; } From 017b7567fa55c049fd82ecd08c1914fb048f28ed Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 20 May 2024 16:41:31 +0800 Subject: [PATCH 065/137] Bump toolchain to 1.78 and resolve warnings --- bins/conflux/src/command/helpers.rs | 2 -- crates/blockgen/src/miner/stratum.rs | 2 +- crates/cfx_store/src/account/mod.rs | 2 +- crates/cfx_store/src/json/mod.rs | 2 +- .../cfxcore/core/src/client/chain_notify.rs | 1 + .../common/channel/src/message_queues_test.rs | 1 + .../src/pos/consensus/block_storage/mod.rs | 1 + .../consensus/persistent_liveness_storage.rs | 2 ++ .../core/src/pos/mempool/core_mempool/mod.rs | 2 -- .../transaction_pool_inner.rs | 25 +------------------ .../components/activation.rs | 1 - .../src/internal_contract/components/mod.rs | 5 +--- .../vm-interpreter/src/interpreter/stack.rs | 1 + .../src/impls/delta_mpt/node_ref_map.rs | 7 ------ .../impls/merkle_patricia_trie/mpt_cursor.rs | 1 + crates/dbs/storage/src/utils/tuple.rs | 5 +++- rust-toolchain | 2 +- 17 files changed, 17 insertions(+), 45 deletions(-) diff --git a/bins/conflux/src/command/helpers.rs b/bins/conflux/src/command/helpers.rs index a41a5cef2a..594c8c239c 100644 --- a/bins/conflux/src/command/helpers.rs +++ b/bins/conflux/src/command/helpers.rs @@ -25,8 +25,6 @@ use std::{ io::{self, BufRead, BufReader, Write}, }; -pub use dir::helpers::{replace_home, replace_home_and_local}; - const PASSWORD_STDIN_ERROR: &str = "Unable to ask for password on non-interactive terminal."; diff --git a/crates/blockgen/src/miner/stratum.rs b/crates/blockgen/src/miner/stratum.rs index c4ba98f684..7f348d7c35 100644 --- a/crates/blockgen/src/miner/stratum.rs +++ b/crates/blockgen/src/miner/stratum.rs @@ -100,7 +100,7 @@ impl SubmitPayload { } #[derive(Debug)] -enum PayloadError { +pub enum PayloadError { ArgumentsAmountUnexpected(usize), InvalidNonce(String), InvalidPowHash(String), diff --git a/crates/cfx_store/src/account/mod.rs b/crates/cfx_store/src/account/mod.rs index e6225fd60f..cc718cc6b1 100644 --- a/crates/cfx_store/src/account/mod.rs +++ b/crates/cfx_store/src/account/mod.rs @@ -23,7 +23,7 @@ mod version; pub use self::{ cipher::{Aes128Ctr, Cipher}, crypto::Crypto, - kdf::{Kdf, Pbkdf2, Prf, Scrypt}, + kdf::{Kdf, Pbkdf2, Prf}, safe_account::SafeAccount, version::Version, }; diff --git a/crates/cfx_store/src/json/mod.rs b/crates/cfx_store/src/json/mod.rs index 6e811bb204..196b70a106 100644 --- a/crates/cfx_store/src/json/mod.rs +++ b/crates/cfx_store/src/json/mod.rs @@ -32,7 +32,7 @@ mod version; pub use self::{ bytes::Bytes, cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams}, - crypto::{CipherText, Crypto}, + crypto::Crypto, error::Error, hash::{H128, H160, H256}, id::Uuid, diff --git a/crates/cfxcore/core/src/client/chain_notify.rs b/crates/cfxcore/core/src/client/chain_notify.rs index cd53a4e1cf..136ca103cd 100644 --- a/crates/cfxcore/core/src/client/chain_notify.rs +++ b/crates/cfxcore/core/src/client/chain_notify.rs @@ -22,6 +22,7 @@ impl NewBlocks { pub fn new() -> NewBlocks { NewBlocks {} } } +#[allow(unused)] pub trait ChainNotify: Send + Sync { /// fires when chain has new blocks. fn new_blocks(&self, _new_blocks: NewBlocks) { diff --git a/crates/cfxcore/core/src/pos/common/channel/src/message_queues_test.rs b/crates/cfxcore/core/src/pos/common/channel/src/message_queues_test.rs index de10bbf0e1..514cd5b779 100644 --- a/crates/cfxcore/core/src/pos/common/channel/src/message_queues_test.rs +++ b/crates/cfxcore/core/src/pos/common/channel/src/message_queues_test.rs @@ -17,6 +17,7 @@ struct ProposalMsg { /// This represents a vote message from a validator #[derive(Debug, PartialEq)] +#[allow(dead_code)] struct VoteMsg { msg: String, } diff --git a/crates/cfxcore/core/src/pos/consensus/block_storage/mod.rs b/crates/cfxcore/core/src/pos/consensus/block_storage/mod.rs index 4b9aab2abc..9c81f2afbf 100644 --- a/crates/cfxcore/core/src/pos/consensus/block_storage/mod.rs +++ b/crates/cfxcore/core/src/pos/consensus/block_storage/mod.rs @@ -49,6 +49,7 @@ pub trait BlockReader: Send + Sync { ) -> Option>>; /// Return the certified block with the highest round. + #[allow(dead_code)] fn highest_certified_block(&self) -> Arc; /// Return the quorum certificate with the highest round diff --git a/crates/cfxcore/core/src/pos/consensus/persistent_liveness_storage.rs b/crates/cfxcore/core/src/pos/consensus/persistent_liveness_storage.rs index 9006d31006..c460b1d9d1 100644 --- a/crates/cfxcore/core/src/pos/consensus/persistent_liveness_storage.rs +++ b/crates/cfxcore/core/src/pos/consensus/persistent_liveness_storage.rs @@ -45,6 +45,7 @@ pub trait PersistentLivenessStorage: Send + Sync { fn save_vote(&self, vote: &Vote) -> Result<()>; /// Construct data that can be recovered from ledger + #[allow(dead_code)] fn recover_from_ledger(&self) -> LedgerRecoveryData; /// Construct necessary data to start consensus. @@ -70,6 +71,7 @@ pub trait PersistentLivenessStorage: Send + Sync { unimplemented!() } + #[allow(dead_code)] fn prune_staking_events( &self, _committed_pivot_decision: &PivotBlockDecision, ) -> Result<()> { diff --git a/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs b/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs index 6b52e74e78..5e631eb4ee 100644 --- a/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs +++ b/crates/cfxcore/core/src/pos/mempool/core_mempool/mod.rs @@ -11,8 +11,6 @@ mod transaction; mod transaction_store; mod ttl_cache; -#[cfg(test)] -pub use self::ttl_cache::TtlCache; pub use self::{ index::TxnPointer, mempool::Mempool as CoreMempool, transaction::TimelineState, diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 2e43fd7f86..b84ecc6172 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -30,7 +30,6 @@ use rand_xorshift::XorShiftRng; use rlp::*; use serde::Serialize; use std::{ - cmp::Ordering, collections::{BTreeSet, HashMap}, sync::Arc, time::{SystemTime, UNIX_EPOCH}, @@ -489,29 +488,6 @@ impl DeferredPool { } } -#[derive(DeriveMallocSizeOf, Clone)] -struct PriceOrderedTransaction(Arc); - -impl PartialEq for PriceOrderedTransaction { - fn eq(&self, other: &Self) -> bool { - self.0.gas_price().eq(other.0.gas_price()) - } -} - -impl Eq for PriceOrderedTransaction {} - -impl PartialOrd for PriceOrderedTransaction { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for PriceOrderedTransaction { - fn cmp(&self, other: &Self) -> Ordering { - self.0.gas_price().cmp(other.0.gas_price()) - } -} - #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub enum TransactionStatus { @@ -1011,6 +987,7 @@ impl TransactionPoolInner { .unwrap_or(state_nonce) } + #[allow(dead_code)] fn recalculate_readiness_with_local_info( &mut self, addr: &AddressWithSpace, ) { diff --git a/crates/cfxcore/executor/src/internal_contract/components/activation.rs b/crates/cfxcore/executor/src/internal_contract/components/activation.rs index 3fa7926ea5..236105bc71 100644 --- a/crates/cfxcore/executor/src/internal_contract/components/activation.rs +++ b/crates/cfxcore/executor/src/internal_contract/components/activation.rs @@ -1,5 +1,4 @@ use cfx_vm_types::Spec; -pub use primitives::BlockNumber; pub trait IsActive { fn is_active(&self, spec: &Spec) -> bool; diff --git a/crates/cfxcore/executor/src/internal_contract/components/mod.rs b/crates/cfxcore/executor/src/internal_contract/components/mod.rs index 7bf6679e27..6b37f193be 100644 --- a/crates/cfxcore/executor/src/internal_contract/components/mod.rs +++ b/crates/cfxcore/executor/src/internal_contract/components/mod.rs @@ -14,8 +14,5 @@ pub use contract::{InternalContractTrait, SolFnTable}; pub use contract_map::InternalContractMap; pub use event::SolidityEventTrait; pub use executable::InternalContractExec; -pub use function::{ - ExecutionTrait, InterfaceTrait, SimpleExecutionTrait, - SolidityFunctionTrait, UpfrontPaymentTrait, -}; +pub use function::{InterfaceTrait, SolidityFunctionTrait}; pub use trap_result::InternalTrapResult; diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs index 23a4e87e2f..88915cb84b 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs @@ -39,6 +39,7 @@ pub trait Stack { /// Get number of elements on Stack fn size(&self) -> usize; /// Returns all data on stack. + #[allow(dead_code)] fn peek_top(&self, no_of_elems: usize) -> &[T]; } diff --git a/crates/dbs/storage/src/impls/delta_mpt/node_ref_map.rs b/crates/dbs/storage/src/impls/delta_mpt/node_ref_map.rs index d2f8623f57..52410ca76c 100644 --- a/crates/dbs/storage/src/impls/delta_mpt/node_ref_map.rs +++ b/crates/dbs/storage/src/impls/delta_mpt/node_ref_map.rs @@ -149,13 +149,6 @@ impl NodeRefMapDeltaMpts { pub const DEFAULT_NODE_MAP_SIZE: u32 = 200_000_000; -// Type alias for clarity. -pub trait NodeRefMapTrait { - type StorageAccessKey; - type NodeRef; - type MaybeCacheSlotIndex; -} - use super::{ super::errors::*, cache::algorithm::CacheAlgoDataTrait, node_memory_manager::ActualSlabIndex, row_number::RowNumberUnderlyingType, diff --git a/crates/dbs/storage/src/impls/merkle_patricia_trie/mpt_cursor.rs b/crates/dbs/storage/src/impls/merkle_patricia_trie/mpt_cursor.rs index ebe25a82e5..63bb3b33c4 100644 --- a/crates/dbs/storage/src/impls/merkle_patricia_trie/mpt_cursor.rs +++ b/crates/dbs/storage/src/impls/merkle_patricia_trie/mpt_cursor.rs @@ -1467,6 +1467,7 @@ impl Drop for ReadWritePathNode { } pub trait GetReadMpt { + #[allow(unused)] fn get_merkle_root(&self) -> MerkleHash; fn get_read_mpt(&mut self) -> &mut dyn SnapshotMptTraitRead; diff --git a/crates/dbs/storage/src/utils/tuple.rs b/crates/dbs/storage/src/utils/tuple.rs index 53030ee154..633d00e18e 100644 --- a/crates/dbs/storage/src/utils/tuple.rs +++ b/crates/dbs/storage/src/utils/tuple.rs @@ -37,6 +37,7 @@ pub trait TupleIndexExt: Sized { /// We don't support generics and lifetime yet because it will take some time to /// update the macro implementation. +#[cfg(test)] macro_rules! make_tuple_with_index_ext { ( $tuple_struct_name:ident($($element_type:ty$(: $pub_vis:tt)*),*) ) => { #[derive(Default, Clone)] @@ -281,6 +282,7 @@ mod macros { } } + #[cfg(test)] macro_rules! make_get_index_ext { ( $tuple_type:ty, @@ -318,6 +320,7 @@ mod macros { }; } + #[cfg(test)] macro_rules! make_get_index_ext_all { ( $tuple_struct_name:ident(), @@ -454,7 +457,7 @@ pub mod placeholders { } } -//#[cfg(test)] +#[cfg(test)] mod test { use super::*; diff --git a/rust-toolchain b/rust-toolchain index 5e3a425662..54227249d1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.73.0 +1.78.0 From f60d84ff0e3b0832f560066ad003de6f72f4a7ea Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 20 May 2024 16:54:25 +0800 Subject: [PATCH 066/137] fix: cfx_feeHistory and eth_feeHistory Rpc Param format --- crates/client/src/rpc/impls/cfx.rs | 2 +- crates/client/src/rpc/impls/common.rs | 15 +++++++++------ crates/client/src/rpc/impls/eth.rs | 15 +++++++++------ crates/client/src/rpc/impls/light.rs | 10 +++++----- crates/client/src/rpc/traits/cfx_space/cfx.rs | 4 ++-- crates/client/src/rpc/traits/eth_space/eth.rs | 4 ++-- crates/client/src/rpc/types/fee_history.rs | 8 ++++---- 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 21d689f391..8897d5eec9 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -2277,7 +2277,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; - fn fee_history(&self, block_count: usize, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U256, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; fn max_priority_fee_per_gas(&self) -> BoxFuture; } diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index 35e7fa4510..6bbbfc383e 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -530,15 +530,15 @@ impl RpcImpl { } pub fn fee_history( - &self, block_count: usize, newest_block: EpochNumber, - reward_percentiles: Vec, + &self, block_count: U256, newest_block: EpochNumber, + reward_percentiles: Vec, ) -> RpcResult { info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", block_count, newest_block, reward_percentiles ); - if block_count == 0 { + if block_count == U256::zero() { return Ok(FeeHistory::new()); } // keep read lock to ensure consistent view @@ -572,7 +572,7 @@ impl RpcImpl { let mut fee_history = FeeHistory::new(); while current_height - >= start_height.saturating_sub(block_count as u64 - 1) + >= start_height.saturating_sub(block_count.as_u64() - 1) { let block = fetch_block(current_height)?; @@ -614,8 +614,11 @@ impl RpcImpl { pub fn max_priority_fee_per_gas(&self) -> RpcResult { info!("RPC Request: max_priority_fee_per_gas",); - let fee_history = - self.fee_history(300, EpochNumber::LatestState, vec![50])?; + let fee_history = self.fee_history( + U256::from(300), + EpochNumber::LatestState, + vec![U64::from(50)], + )?; let total_reward: U256 = fee_history .reward() diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index e9aa6d8815..fc79985fb3 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -471,8 +471,11 @@ impl Eth for EthHandler { self.tx_pool.machine().params().evm_transaction_block_ratio as usize; - let fee_history = - self.fee_history(300, BlockNumber::Latest, vec![50])?; + let fee_history = self.fee_history( + U256::from(300), + BlockNumber::Latest, + vec![U64::from(50)], + )?; let total_reward: U256 = fee_history .reward() @@ -899,15 +902,15 @@ impl Eth for EthHandler { } fn fee_history( - &self, block_count: usize, newest_block: BlockNumber, - reward_percentiles: Vec, + &self, block_count: U256, newest_block: BlockNumber, + reward_percentiles: Vec, ) -> jsonrpc_core::Result { info!( "RPC Request: eth_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", block_count, newest_block, reward_percentiles ); - if block_count == 0 { + if block_count == U256::zero() { return Ok(FeeHistory::new()); } @@ -943,7 +946,7 @@ impl Eth for EthHandler { let mut fee_history = FeeHistory::new(); while current_height - >= start_height.saturating_sub(block_count as u64 - 1) + >= start_height.saturating_sub(block_count.as_u64() - 1) { let block = fetch_block(current_height)?; diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index c252044fc8..2eeef528b4 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -1091,15 +1091,15 @@ impl RpcImpl { } fn fee_history( - &self, block_count: usize, newest_block: EpochNumber, - reward_percentiles: Vec, + &self, block_count: U256, newest_block: EpochNumber, + reward_percentiles: Vec, ) -> RpcBoxFuture { info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", block_count, newest_block, reward_percentiles ); - if block_count == 0 { + if block_count == U256::zero() { return Box::new(async { Ok(FeeHistory::new()) }.boxed().compat()); } @@ -1118,7 +1118,7 @@ impl RpcImpl { let mut fee_history = FeeHistory::new(); while current_height - >= start_height.saturating_sub(block_count as u64 - 1) + >= start_height.saturating_sub(block_count.as_u64() - 1) { let block = fetch_block_for_fee_history( consensus_graph.clone(), @@ -1239,7 +1239,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; - fn fee_history(&self, block_count: usize, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U256, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index f6f8d2749f..dc5a24d41f 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -203,8 +203,8 @@ pub trait Cfx { #[rpc(name = "cfx_feeHistory")] fn fee_history( - &self, block_count: usize, newest_block: EpochNumber, - reward_percentiles: Vec, + &self, block_count: U256, newest_block: EpochNumber, + reward_percentiles: Vec, ) -> BoxFuture; /// Check if user balance is enough for the transaction. diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 929bdac1b8..6b9dc86d40 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -76,8 +76,8 @@ pub trait Eth { #[rpc(name = "eth_feeHistory")] fn fee_history( - &self, block_count: usize, newest_block: BlockNumber, - reward_percentiles: Vec, + &self, block_count: U256, newest_block: BlockNumber, + reward_percentiles: Vec, ) -> Result; /// Returns accounts list. diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index e168097be7..dd2614274d 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -1,6 +1,6 @@ use std::collections::VecDeque; -use cfx_types::{Space, SpaceMap, U256}; +use cfx_types::{Space, SpaceMap, U256, U64}; use primitives::{transaction::SignedTransaction, BlockHeader}; #[derive(Serialize, Debug, Default)] @@ -24,7 +24,7 @@ impl FeeHistory { pub fn reward(&self) -> &VecDeque> { &self.reward } pub fn push_back_block<'a, I>( - &mut self, space: Space, percentiles: &Vec, + &mut self, space: Space, percentiles: &Vec, pivot_header: &BlockHeader, transactions: I, ) -> Result<(), String> where @@ -83,7 +83,7 @@ impl FeeHistory { } fn compute_reward<'a, I>( - percentiles: &Vec, transactions: I, base_price: U256, + percentiles: &Vec, transactions: I, base_price: U256, ) -> Vec where I: Iterator { let mut rewards: Vec<_> = transactions @@ -105,7 +105,7 @@ where I: Iterator { percentiles .into_iter() .map(|per| { - let mut index = *per as usize * n / 100; + let mut index = (*per).as_usize() * n / 100; if index >= n { index = n - 1; } From f2408827fbad6b22cb50e238ea4db673dfdd8407 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 20 May 2024 17:38:01 +0800 Subject: [PATCH 067/137] fix: type and y_parity format returned from rpc --- crates/client/src/rpc/impls/eth.rs | 8 +++++--- crates/client/src/rpc/types/call_request.rs | 8 +++++--- crates/client/src/rpc/types/eth/call_request.rs | 2 +- crates/client/src/rpc/types/eth/receipt.rs | 2 +- crates/client/src/rpc/types/eth/transaction.rs | 6 +++--- crates/client/src/rpc/types/receipt.rs | 2 +- crates/client/src/rpc/types/transaction.rs | 4 ++-- crates/primitives/src/transaction/mod.rs | 14 ++++++++++---- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index fc79985fb3..50f1a8495f 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -96,7 +96,9 @@ pub fn sign_call( } else { 0 }; - let transaction_type = request.transaction_type.unwrap_or(default_type_id); + let transaction_type = request + .transaction_type + .unwrap_or(U256::from(default_type_id)); let gas_price = request.gas_price.unwrap_or(1.into()); let max_fee_per_gas = request @@ -108,7 +110,7 @@ pub fn sign_call( let access_list = request.access_list.unwrap_or(vec![]); let data = request.data.unwrap_or_default().into_vec(); - let transaction = match transaction_type { + let transaction = match transaction_type.as_usize() { 0 => Eip155(Eip155Transaction { nonce, gas_price, @@ -366,7 +368,7 @@ impl EthHandler { transaction_type: receipt .burnt_gas_fee .is_some() - .then_some(tx.type_id()), + .then_some(U256::from(tx.type_id())), burnt_gas_fee: receipt.burnt_gas_fee, }) } diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index b2531a0ab5..d47113eb4b 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -54,7 +54,7 @@ pub struct CallRequest { pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, #[serde(rename = "type")] - pub transaction_type: Option, + pub transaction_type: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -179,7 +179,9 @@ pub fn sign_call( } else { 0 }; - let transaction_type = request.transaction_type.unwrap_or(default_type_id); + let transaction_type = request + .transaction_type + .unwrap_or(U256::from(default_type_id)); let gas_price = request.gas_price.unwrap_or(1.into()); let max_fee_per_gas = request @@ -190,7 +192,7 @@ pub fn sign_call( request.max_priority_fee_per_gas.unwrap_or(U256::zero()); let access_list = request.access_list.unwrap_or(vec![]); - let transaction = match transaction_type { + let transaction = match transaction_type.as_usize() { 0 => Cip155(NativeTransaction { nonce, action, diff --git a/crates/client/src/rpc/types/eth/call_request.rs b/crates/client/src/rpc/types/eth/call_request.rs index 459af0a456..23625e00da 100644 --- a/crates/client/src/rpc/types/eth/call_request.rs +++ b/crates/client/src/rpc/types/eth/call_request.rs @@ -46,7 +46,7 @@ pub struct CallRequest { pub max_priority_fee_per_gas: Option, pub access_list: Option, #[serde(rename = "type")] - pub transaction_type: Option, + pub transaction_type: Option, } // impl Into for CallRequest { diff --git a/crates/client/src/rpc/types/eth/receipt.rs b/crates/client/src/rpc/types/eth/receipt.rs index 61a1453a2f..4b4c25b5e1 100644 --- a/crates/client/src/rpc/types/eth/receipt.rs +++ b/crates/client/src/rpc/types/eth/receipt.rs @@ -26,7 +26,7 @@ use cfx_types::{Bloom as H2048, H160, H256, U256, U64}; #[serde(rename_all = "camelCase")] pub struct Receipt { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Transaction Hash pub transaction_hash: H256, /// Transaction index diff --git a/crates/client/src/rpc/types/eth/transaction.rs b/crates/client/src/rpc/types/eth/transaction.rs index c116a100a8..56205fba8e 100644 --- a/crates/client/src/rpc/types/eth/transaction.rs +++ b/crates/client/src/rpc/types/eth/transaction.rs @@ -34,7 +34,7 @@ use serde::Serialize; pub struct Transaction { /// transaction type #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Hash pub hash: H256, /// Nonce @@ -87,7 +87,7 @@ pub struct Transaction { #[serde(skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub y_parity: Option, + pub y_parity: Option, /* /// Transaction activates at specified block. * pub condition: Option, */ } @@ -134,7 +134,7 @@ impl Transaction { max_priority_fee_per_gas: t .after_1559() .then_some(*t.max_priority_gas_price()), - y_parity: t.is_2718().then_some(signature.v()), + y_parity: t.is_2718().then_some(U256::from(signature.v())), transaction_type: Some(t.type_id()), } } diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index de1e439d82..b387f0f0ea 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -39,7 +39,7 @@ impl StorageChange { #[serde(rename_all = "camelCase")] pub struct Receipt { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Transaction hash. pub transaction_hash: H256, /// Transaction index within the block. diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index 8ef0f0cde0..510164389e 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -29,7 +29,7 @@ pub enum WrapTransaction { #[serde(rename_all = "camelCase")] pub struct Transaction { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub space: Option, pub hash: H256, @@ -63,7 +63,7 @@ pub struct Transaction { /// The S field of the signature. pub s: U256, #[serde(skip_serializing_if = "Option::is_none")] - pub y_parity: Option, + pub y_parity: Option, } pub enum PackedOrExecuted { diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 78957036a1..c97a37b451 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -340,16 +340,22 @@ impl Transaction { } } - pub fn type_id(&self) -> u8 { + pub fn type_id(&self) -> U256 { match self { Transaction::Native(TypedNativeTransaction::Cip155(_)) - | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => 0, + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => { + U256::from(0) + } Transaction::Native(TypedNativeTransaction::Cip2930(_)) - | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => 1, + | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => { + U256::from(1) + } Transaction::Native(TypedNativeTransaction::Cip1559(_)) - | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => 2, + | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => { + U256::from(2) + } } } From f12988baedc4c358e3fb5b2bb47880c50faf8378 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 20 May 2024 18:20:02 +0800 Subject: [PATCH 068/137] Fix clippy --- .../src/pos/common/logger/derive/src/lib.rs | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs b/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs index a104766db5..891bc9ff5e 100644 --- a/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs +++ b/crates/cfxcore/core/src/pos/common/logger/derive/src/lib.rs @@ -158,30 +158,37 @@ fn extract_attr(attrs: &[Attribute]) -> Option { { for segment in path.segments { // Only handle schema attrs - if segment.ident == "schema" { - for meta in &nested { - let path = - if let NestedMeta::Meta(Meta::Path(path)) = meta { - path - } else { - panic!("unsupported schema attribute"); - }; - - match path - .segments - .first() - .unwrap() - .ident - .to_string() - .as_ref() - { - "debug" => return Some(ValueType::Debug), - "display" => return Some(ValueType::Display), - "serde" => return Some(ValueType::Serde), - _ => panic!("unsupported schema attribute"), - } - } + if segment.ident != "schema" { + continue; } + + let meta = if let Some(meta) = nested.first() { + meta + } else { + continue; + }; + + let path = if let NestedMeta::Meta(Meta::Path(path)) = meta { + path + } else { + panic!("unsupported schema attribute"); + }; + + let answer = match path + .segments + .first() + .unwrap() + .ident + .to_string() + .as_ref() + { + "debug" => ValueType::Debug, + "display" => ValueType::Display, + "serde" => ValueType::Serde, + _ => panic!("unsupported schema attribute"), + }; + + return Some(answer); } } } From f82e00f8966211267817ef3bf67580ae8765b122 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 20 May 2024 18:37:19 +0800 Subject: [PATCH 069/137] fix: restore type_id signature; use U64 for transaction_type and y_parity --- crates/client/src/rpc/impls/eth.rs | 4 ++-- crates/client/src/rpc/types/call_request.rs | 4 ++-- crates/client/src/rpc/types/eth/call_request.rs | 4 ++-- crates/client/src/rpc/types/eth/receipt.rs | 2 +- crates/client/src/rpc/types/eth/transaction.rs | 8 ++++---- crates/client/src/rpc/types/receipt.rs | 4 ++-- crates/client/src/rpc/types/transaction.rs | 6 +++--- crates/primitives/src/transaction/mod.rs | 14 ++++---------- 8 files changed, 20 insertions(+), 26 deletions(-) diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 50f1a8495f..127def8d02 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -98,7 +98,7 @@ pub fn sign_call( }; let transaction_type = request .transaction_type - .unwrap_or(U256::from(default_type_id)); + .unwrap_or(U64::from(default_type_id)); let gas_price = request.gas_price.unwrap_or(1.into()); let max_fee_per_gas = request @@ -368,7 +368,7 @@ impl EthHandler { transaction_type: receipt .burnt_gas_fee .is_some() - .then_some(U256::from(tx.type_id())), + .then_some(U64::from(tx.type_id())), burnt_gas_fee: receipt.burnt_gas_fee, }) } diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index d47113eb4b..b8f2a0abf0 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -54,7 +54,7 @@ pub struct CallRequest { pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, #[serde(rename = "type")] - pub transaction_type: Option, + pub transaction_type: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -181,7 +181,7 @@ pub fn sign_call( }; let transaction_type = request .transaction_type - .unwrap_or(U256::from(default_type_id)); + .unwrap_or(U64::from(default_type_id)); let gas_price = request.gas_price.unwrap_or(1.into()); let max_fee_per_gas = request diff --git a/crates/client/src/rpc/types/eth/call_request.rs b/crates/client/src/rpc/types/eth/call_request.rs index 23625e00da..ec3a52bf08 100644 --- a/crates/client/src/rpc/types/eth/call_request.rs +++ b/crates/client/src/rpc/types/eth/call_request.rs @@ -19,7 +19,7 @@ // along with OpenEthereum. If not, see . use crate::rpc::types::Bytes; -use cfx_types::{H160, U256}; +use cfx_types::{H160, U256, U64}; use primitives::AccessList; /// Call request @@ -46,7 +46,7 @@ pub struct CallRequest { pub max_priority_fee_per_gas: Option, pub access_list: Option, #[serde(rename = "type")] - pub transaction_type: Option, + pub transaction_type: Option, } // impl Into for CallRequest { diff --git a/crates/client/src/rpc/types/eth/receipt.rs b/crates/client/src/rpc/types/eth/receipt.rs index 4b4c25b5e1..8e800f3215 100644 --- a/crates/client/src/rpc/types/eth/receipt.rs +++ b/crates/client/src/rpc/types/eth/receipt.rs @@ -26,7 +26,7 @@ use cfx_types::{Bloom as H2048, H160, H256, U256, U64}; #[serde(rename_all = "camelCase")] pub struct Receipt { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Transaction Hash pub transaction_hash: H256, /// Transaction index diff --git a/crates/client/src/rpc/types/eth/transaction.rs b/crates/client/src/rpc/types/eth/transaction.rs index 56205fba8e..ed847c9f83 100644 --- a/crates/client/src/rpc/types/eth/transaction.rs +++ b/crates/client/src/rpc/types/eth/transaction.rs @@ -34,7 +34,7 @@ use serde::Serialize; pub struct Transaction { /// transaction type #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Hash pub hash: H256, /// Nonce @@ -87,7 +87,7 @@ pub struct Transaction { #[serde(skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub y_parity: Option, + pub y_parity: Option, /* /// Transaction activates at specified block. * pub condition: Option, */ } @@ -134,8 +134,8 @@ impl Transaction { max_priority_fee_per_gas: t .after_1559() .then_some(*t.max_priority_gas_price()), - y_parity: t.is_2718().then_some(U256::from(signature.v())), - transaction_type: Some(t.type_id()), + y_parity: t.is_2718().then_some(U64::from(signature.v())), + transaction_type: Some(U64::from(t.type_id())), } } diff --git a/crates/client/src/rpc/types/receipt.rs b/crates/client/src/rpc/types/receipt.rs index b387f0f0ea..85fa0e18e3 100644 --- a/crates/client/src/rpc/types/receipt.rs +++ b/crates/client/src/rpc/types/receipt.rs @@ -39,7 +39,7 @@ impl StorageChange { #[serde(rename_all = "camelCase")] pub struct Receipt { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, /// Transaction hash. pub transaction_hash: H256, /// Transaction index within the block. @@ -182,7 +182,7 @@ impl Receipt { }; Ok(Receipt { - transaction_type: Some(transaction.type_id()), + transaction_type: Some(U64::from(transaction.type_id())), transaction_hash: transaction.hash.into(), index: U64::from( transaction_index diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index 510164389e..50933efeb8 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -29,7 +29,7 @@ pub enum WrapTransaction { #[serde(rename_all = "camelCase")] pub struct Transaction { #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub transaction_type: Option, + pub transaction_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub space: Option, pub hash: H256, @@ -63,7 +63,7 @@ pub struct Transaction { /// The S field of the signature. pub s: U256, #[serde(skip_serializing_if = "Option::is_none")] - pub y_parity: Option, + pub y_parity: Option, } pub enum PackedOrExecuted { @@ -164,7 +164,7 @@ impl Transaction { .after_1559() .then_some(*t.max_priority_gas_price()), y_parity: t.is_2718().then_some(t.transaction.v.into()), - transaction_type: Some(t.type_id()), + transaction_type: Some(U64::from(t.type_id())), v: t.transaction.v.into(), r: t.transaction.r.into(), s: t.transaction.s.into(), diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index c97a37b451..78957036a1 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -340,22 +340,16 @@ impl Transaction { } } - pub fn type_id(&self) -> U256 { + pub fn type_id(&self) -> u8 { match self { Transaction::Native(TypedNativeTransaction::Cip155(_)) - | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => { - U256::from(0) - } + | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => 0, Transaction::Native(TypedNativeTransaction::Cip2930(_)) - | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => { - U256::from(1) - } + | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => 1, Transaction::Native(TypedNativeTransaction::Cip1559(_)) - | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => { - U256::from(2) - } + | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => 2, } } From 945b0f6f090a89ec605c52cd6f650fe8f044daab Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 21 May 2024 11:09:23 +0800 Subject: [PATCH 070/137] add changes of 1559 to rpc changelog --- changelogs/JSONRPC.md | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/changelogs/JSONRPC.md b/changelogs/JSONRPC.md index f8221e6863..6d7e0f4920 100644 --- a/changelogs/JSONRPC.md +++ b/changelogs/JSONRPC.md @@ -1,5 +1,87 @@ # JSON-RPC CHANGELOG +## vNext + +This RPC upgrade is primarily to support Conflux 1559 transactions, with the main changes as follows: + +### eSpace + +#### New RPC + +- `eth_maxPriorityFeePerGas`: Returns an estimated `maxPriorityFeePerGas` value based on on-chain data. +- `eth_feeHistory`: Returns an array containing the `baseFeePerGas` values of several consecutive blocks, with the block range specifiable by parameters. + +#### RPC Updates + +Transaction adds fields: + +- `type`: 0 - Legacy tx 1 - 2930 tx 2 - 1559 tx +- 2930 tx adds fields: `accessList`, `yParity` +- 1559 tx adds fields: `accessList`, `yParity`, `maxPriorityFeePerGas`, `maxFeePerGas` + +Receipt adds fields: + +- `type`: 0/1/2 +- `burntGasFee` +- `effectiveGasPrice` + +Block adds fields: + +- `baseFeePerGas` + +`eth_call`, `eth_estimate` Request adds fields: + +- `type` +- `accessList` +- `maxPriorityPerGas` +- `maxFeePerGas` + +#### New `debug` RPC namespace + +This namespace includes some RPCs for debugging: + +- `debug_traceTransaction`: Returns the execution trace of the transaction. +- `debug_traceBlockByHash`: Get the execution trace of the block by block hash. +- `debug_traceBlockByNumber`: Get the execution trace of the block by block number +- `debug_traceCall`:Get the execution trace of one `tx call`. + +### Core Space + +#### New RPC + +- `cfx_maxPriorityFeePerGas`: Returns an estimated `maxPriorityFeePerGas` value based on on-chain data. +- `cfx_feeHistory`: Returns an array containing the `baseFeePerGas` values of several consecutive blocks, with the block range specifiable by parameters. +- `cfx_getFeeBurnt`: Returns the total amount of gas fees burned historically by 1559. + +#### RPC Updates + +Transaction adds fields: + +- `type`: 0 - Legacy tx 1 - 2930 tx 2 - 1559 tx +- 2930 tx adds fields: `accessList`, `yParity` +- 1559 tx adds fields: `accessList`, `yParity`, `maxPriorityFeePerGas`, `maxFeePerGas` + +Receipt adds fields: + +- `type`: 0/1/2 +- `burntGasFee` +- `effectiveGasPrice` + +Block adds fields: + +- `baseFeePerGas` + +`cfx_call`, `cfx_estimate` Request adds fields: + +- `type` +- `accessList` +- `maxPriorityPerGas` +- `maxFeePerGas` + +`cfx_getParamsFromVote` result adds fields: + +- `baseFeeShareProp` + ## v2.3.1 - Return `storagePointProp` in cfx_getParamsFromVote, which is introduced by [CIP-107](https://github.com/Conflux-Chain/CIPs/blob/master/CIPs/cip-107.md#the-voting-of-proportion). From e20ec621a83d6d6c7faacb49ebe9e9a7ff02b782 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 21 May 2024 11:50:09 +0800 Subject: [PATCH 071/137] fix: eip1559 test parameter --- tests/evm_space/eip1559_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 51296024c4..9760dadb17 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -81,7 +81,7 @@ def run_test(self): assert_equal(ret1[0], ret2[0]) - fee_history = self.nodes[0].eth_feeHistory(5, "latest", [25, 75]) + fee_history = self.nodes[0].eth_feeHistory("0x5", "latest", ["0x15", "0x4b"]) assert_equal(len(fee_history['base_fee_per_gas']), 6) assert_equal(len(fee_history['gas_used_ratio']), 5) assert_equal(len(fee_history['reward']), 5) From 0facfabd0e95c974f9625f042e00475309bdc174 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 12:26:38 +0800 Subject: [PATCH 072/137] Fix UB and improper dependencies --- Cargo.lock | 1 - .../src/internal_contract/impls/pos.rs | 18 +++++++++++++----- crates/cfxcore/vm-types/Cargo.toml | 1 - crates/cfxcore/vm-types/src/error.rs | 9 --------- .../cache/algorithm/removable_heap.rs | 8 +++++++- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc77a99a8c..d5408ec214 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,7 +1046,6 @@ dependencies = [ name = "cfx-vm-types" version = "2.0.2" dependencies = [ - "bls-signatures", "cfx-bytes", "cfx-db-errors", "cfx-types", diff --git a/crates/cfxcore/executor/src/internal_contract/impls/pos.rs b/crates/cfxcore/executor/src/internal_contract/impls/pos.rs index 8eaf95210f..4ae0f3f6da 100644 --- a/crates/cfxcore/executor/src/internal_contract/impls/pos.rs +++ b/crates/cfxcore/executor/src/internal_contract/impls/pos.rs @@ -155,11 +155,19 @@ pub fn register( internal_bail!("can not change identifier"); } - let maybe_verified_bls_pubkey = - verify_bls_pubkey(bls_pubkey, bls_proof, !context.spec.cip_sigma_fix)?; - let verified_bls_pubkey = maybe_verified_bls_pubkey.ok_or( - vm::Error::InternalContract("Can not verify bls pubkey".into()), - )?; + let verified_bls_pubkey = match verify_bls_pubkey( + bls_pubkey, + bls_proof, + !context.spec.cip_sigma_fix, + ) { + Err(e) => { + internal_bail!("Crypto decoding error {:?}", e); + } + Ok(None) => { + internal_bail!("Can not verify bls pubkey"); + } + Ok(Some(key)) => key, + }; let mut hasher = Keccak::v256(); hasher.update(verified_bls_pubkey.as_slice()); diff --git a/crates/cfxcore/vm-types/Cargo.toml b/crates/cfxcore/vm-types/Cargo.toml index fc82424730..3d1d4b9c43 100644 --- a/crates/cfxcore/vm-types/Cargo.toml +++ b/crates/cfxcore/vm-types/Cargo.toml @@ -17,7 +17,6 @@ rlp_derive = { git = "https://github.com/Conflux-Chain/conflux-parity-deps.git", serde = { version = "1.0", features = ["rc"] } serde_derive = "1.0" solidity-abi = {path= "../../util/solidity-abi" } -bls-signatures = {git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore"]} [features] testonly_code = [] \ No newline at end of file diff --git a/crates/cfxcore/vm-types/src/error.rs b/crates/cfxcore/vm-types/src/error.rs index d7e7f37989..ba34f73f32 100644 --- a/crates/cfxcore/vm-types/src/error.rs +++ b/crates/cfxcore/vm-types/src/error.rs @@ -21,7 +21,6 @@ //! VM errors module use super::{action_params::ActionParams, ResumeCall, ResumeCreate}; -use bls_signatures::Error as CryptoError; use cfx_db_errors::statedb::{Error as DbError, Result as DbResult}; use cfx_types::{Address, U256}; use solidity_abi::ABIDecodeError; @@ -136,14 +135,6 @@ impl From for Error { fn from(err: DbError) -> Self { Error::StateDbError(PartialEqWrapper(err)) } } -impl From for Error { - fn from(err: CryptoError) -> Self { - Error::InternalContract( - format!("Crypto decoding error {:?}", err).into(), - ) - } -} - impl From for Error { fn from(err: ABIDecodeError) -> Self { Error::InternalContract(format!("ABI decode error: {}", err.0)) diff --git a/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/removable_heap.rs b/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/removable_heap.rs index c61b807fdd..2109978e0d 100644 --- a/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/removable_heap.rs +++ b/crates/dbs/storage/src/impls/delta_mpt/cache/algorithm/removable_heap.rs @@ -631,7 +631,13 @@ impl RemovableHeap { &mut self, pos: PosT, hole: Hole, replaced: *mut ValueType, value_util: &mut ValueUtilT, ) { - ptr::copy_nonoverlapping(self.get_unchecked_mut(pos), replaced, 1); + { + let src = self.get_unchecked_mut(pos) as *mut ValueType; + let dst = replaced; + if src != dst { + ptr::copy_nonoverlapping(src, dst, 1); + } + } if value_util.get_key_for_comparison(self.get_unchecked_mut(pos)) < value_util.get_key_for_comparison(&hole.value) From c79b7ec505aed958c1d024ab8fa57c5ae8d1709c Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 21 May 2024 13:16:50 +0800 Subject: [PATCH 073/137] remove trace logs --- changelogs/JSONRPC.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/changelogs/JSONRPC.md b/changelogs/JSONRPC.md index 6d7e0f4920..faa0628c5b 100644 --- a/changelogs/JSONRPC.md +++ b/changelogs/JSONRPC.md @@ -36,15 +36,6 @@ Block adds fields: - `maxPriorityPerGas` - `maxFeePerGas` -#### New `debug` RPC namespace - -This namespace includes some RPCs for debugging: - -- `debug_traceTransaction`: Returns the execution trace of the transaction. -- `debug_traceBlockByHash`: Get the execution trace of the block by block hash. -- `debug_traceBlockByNumber`: Get the execution trace of the block by block number -- `debug_traceCall`:Get the execution trace of one `tx call`. - ### Core Space #### New RPC From d3d8501c0ceb0abdf404a57381392bbe4b022ff0 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 13:27:14 +0800 Subject: [PATCH 074/137] Fix compile warning --- .cargo/config | 3 --- Cargo.lock | 28 ++---------------------- crates/cfxcore/execute-helper/Cargo.toml | 2 +- crates/cfxcore/executor/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index e62398013b..0000000000 --- a/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[net] -git-fetch-with-cli = true - diff --git a/Cargo.lock b/Cargo.lock index d5408ec214..f2a46ab419 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,7 +800,7 @@ dependencies = [ "serde_derive", "solidity-abi", "strum_macros 0.20.1", - "typemap", + "typemap-ors", ] [[package]] @@ -852,7 +852,7 @@ dependencies = [ "strum_macros 0.20.1", "substrate-bn", "tiny-keccak 2.0.2", - "typemap", + "typemap-ors", ] [[package]] @@ -7499,12 +7499,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - [[package]] name = "transient-hashmap" version = "0.4.1" @@ -7572,15 +7566,6 @@ dependencies = [ "toml", ] -[[package]] -name = "typemap" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" -dependencies = [ - "unsafe-any", -] - [[package]] name = "typemap-ors" version = "1.0.0" @@ -7705,15 +7690,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "unsafe-any" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" -dependencies = [ - "traitobject", -] - [[package]] name = "unsafe-any-ors" version = "1.0.0" diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 2716d7de84..24179686ac 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -48,4 +48,4 @@ strum_macros = "0.20" pow-types = {path = "../core/src/pos/types/pow-types" } # impl-trait-for-tuples = "^0.2" # impl-tools = "^0.10" -typemap = "0.3" \ No newline at end of file +typemap = { package = "typemap-ors", version = "1.0"} \ No newline at end of file diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index c5581a1c0a..096f05d41a 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -47,7 +47,7 @@ diem-types = { path = "../core/src/pos/types" } pow-types = {path = "../core/src/pos/types/pow-types" } impl-trait-for-tuples = "^0.2" impl-tools = "^0.10" -typemap = "0.3" +typemap = { package = "typemap-ors", version = "1.0"} hex-literal = "0.4.1" derive_more = "0.99" c-kzg = { version = "1.0.2", default-features = false} From 87b78eb9beb514ea3b895c695b023c9ca1560fda Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 13:38:24 +0800 Subject: [PATCH 075/137] Go back to 1.77 because unsafe precondition check --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 54227249d1..79e15fd493 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.78.0 +1.77.0 From 5c492e006c3bc2cf1a02e06b3956e296f5aeef9b Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 13:48:04 +0800 Subject: [PATCH 076/137] Fix base price error on gas estimation --- crates/cfxcore/execute-helper/src/estimation.rs | 1 + crates/cfxcore/executor/src/executive/fresh_executive.rs | 4 ++++ crates/cfxcore/executor/src/executive/transact_options.rs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/crates/cfxcore/execute-helper/src/estimation.rs b/crates/cfxcore/execute-helper/src/estimation.rs index 62934c7d93..5036e6bbfc 100644 --- a/crates/cfxcore/execute-helper/src/estimation.rs +++ b/crates/cfxcore/execute-helper/src/estimation.rs @@ -466,6 +466,7 @@ impl EstimateRequest { charge_collateral, charge_gas: self.charge_gas(), check_epoch_bound: false, + check_base_price: self.has_gas_price, } } diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 1daac317f4..5f7fa631e6 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -132,6 +132,10 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { } fn check_base_price(&self) -> Result<(), ExecutionOutcome> { + if !self.settings.check_base_price { + return Ok(()) + } + let burnt_gas_price = self.context.env.burnt_gas_price[self.tx.space()]; if self.tx.gas_price() < &burnt_gas_price { Err(ExecutionOutcome::NotExecutedToReconsiderPacking( diff --git a/crates/cfxcore/executor/src/executive/transact_options.rs b/crates/cfxcore/executor/src/executive/transact_options.rs index 007419248d..0d53b318ad 100644 --- a/crates/cfxcore/executor/src/executive/transact_options.rs +++ b/crates/cfxcore/executor/src/executive/transact_options.rs @@ -19,6 +19,7 @@ impl Default for TransactOptions<()> { pub struct TransactSettings { pub charge_collateral: ChargeCollateral, pub charge_gas: bool, + pub check_base_price: bool, pub check_epoch_bound: bool, } @@ -35,6 +36,7 @@ impl TransactSettings { charge_collateral: ChargeCollateral::Normal, charge_gas: true, check_epoch_bound: true, + check_base_price: true, } } } From 1305c7f2ea451307b89635eb8de2b449ab4b6d91 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 21 May 2024 16:04:38 +0800 Subject: [PATCH 077/137] fix: use U64 for block_count --- crates/client/src/rpc/impls/cfx.rs | 2 +- crates/client/src/rpc/impls/common.rs | 6 +++--- crates/client/src/rpc/impls/eth.rs | 6 +++--- crates/client/src/rpc/impls/light.rs | 6 +++--- crates/client/src/rpc/traits/cfx_space/cfx.rs | 2 +- crates/client/src/rpc/traits/eth_space/eth.rs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index 8897d5eec9..f57fb5e429 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -2277,7 +2277,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; - fn fee_history(&self, block_count: U256, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; fn max_priority_fee_per_gas(&self) -> BoxFuture; } diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index 6bbbfc383e..3431e50089 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -530,7 +530,7 @@ impl RpcImpl { } pub fn fee_history( - &self, block_count: U256, newest_block: EpochNumber, + &self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> RpcResult { info!( @@ -538,7 +538,7 @@ impl RpcImpl { block_count, newest_block, reward_percentiles ); - if block_count == U256::zero() { + if block_count == U64::zero() { return Ok(FeeHistory::new()); } // keep read lock to ensure consistent view @@ -615,7 +615,7 @@ impl RpcImpl { info!("RPC Request: max_priority_fee_per_gas",); let fee_history = self.fee_history( - U256::from(300), + U64::from(300), EpochNumber::LatestState, vec![U64::from(50)], )?; diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 127def8d02..b46ce4dbe4 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -474,7 +474,7 @@ impl Eth for EthHandler { as usize; let fee_history = self.fee_history( - U256::from(300), + U64::from(300), BlockNumber::Latest, vec![U64::from(50)], )?; @@ -904,7 +904,7 @@ impl Eth for EthHandler { } fn fee_history( - &self, block_count: U256, newest_block: BlockNumber, + &self, block_count: U64, newest_block: BlockNumber, reward_percentiles: Vec, ) -> jsonrpc_core::Result { info!( @@ -912,7 +912,7 @@ impl Eth for EthHandler { block_count, newest_block, reward_percentiles ); - if block_count == U256::zero() { + if block_count == U64::zero() { return Ok(FeeHistory::new()); } diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index 2eeef528b4..85032c826d 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -1091,7 +1091,7 @@ impl RpcImpl { } fn fee_history( - &self, block_count: U256, newest_block: EpochNumber, + &self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> RpcBoxFuture { info!( @@ -1099,7 +1099,7 @@ impl RpcImpl { block_count, newest_block, reward_percentiles ); - if block_count == U256::zero() { + if block_count == U64::zero() { return Box::new(async { Ok(FeeHistory::new()) }.boxed().compat()); } @@ -1239,7 +1239,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; - fn fee_history(&self, block_count: U256, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index dc5a24d41f..daff44edff 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -203,7 +203,7 @@ pub trait Cfx { #[rpc(name = "cfx_feeHistory")] fn fee_history( - &self, block_count: U256, newest_block: EpochNumber, + &self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> BoxFuture; diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 6b9dc86d40..80fe7e50a0 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -76,7 +76,7 @@ pub trait Eth { #[rpc(name = "eth_feeHistory")] fn fee_history( - &self, block_count: U256, newest_block: BlockNumber, + &self, block_count: U64, newest_block: BlockNumber, reward_percentiles: Vec, ) -> Result; From 22ab3852a06400e088b9a9aa050f8f366f9398d9 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 16:48:35 +0800 Subject: [PATCH 078/137] cargo fmt --- crates/cfxcore/executor/src/executive/fresh_executive.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index 5f7fa631e6..b6e15a4d1b 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -133,9 +133,9 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { fn check_base_price(&self) -> Result<(), ExecutionOutcome> { if !self.settings.check_base_price { - return Ok(()) + return Ok(()); } - + let burnt_gas_price = self.context.env.burnt_gas_price[self.tx.space()]; if self.tx.gas_price() < &burnt_gas_price { Err(ExecutionOutcome::NotExecutedToReconsiderPacking( From a383bc0fbe49ed650ebdb434f6bca0dc508914c1 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 21 May 2024 16:55:38 +0800 Subject: [PATCH 079/137] fix review feedback --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 2 +- crates/cfxcore/execute-helper/Cargo.toml | 4 ++-- crates/{serde => serde_utils}/Cargo.toml | 2 +- crates/{serde => serde_utils}/src/lib.rs | 0 crates/{serde => serde_utils}/src/num.rs | 0 6 files changed, 14 insertions(+), 14 deletions(-) rename crates/{serde => serde_utils}/Cargo.toml (96%) rename crates/{serde => serde_utils}/src/lib.rs (100%) rename crates/{serde => serde_utils}/src/num.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index be6c616d13..b1497b2dd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1273,16 +1273,6 @@ dependencies = [ "rustc-hex", ] -[[package]] -name = "cfx-serde" -version = "2.0.2" -dependencies = [ - "alloy-primitives", - "cfx-types", - "serde", - "serde_json", -] - [[package]] name = "cfx-statedb" version = "1.0.0" @@ -7289,6 +7279,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde-utils" +version = "2.0.2" +dependencies = [ + "alloy-primitives", + "cfx-types", + "serde", + "serde_json", +] + [[package]] name = "serde-value" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 82216e0efb..6c358c612a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ members = [ "crates/util/throttling", "crates/util/treap-map", "crates/util/version", - "crates/serde", + "crates/serde_utils", ] resolver = "2" diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 36830a6ce3..f24e8c934f 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -53,7 +53,7 @@ pow-types = {path = "../core/src/pos/types/pow-types" } # impl-tools = "^0.10" typemap = { package = "typemap-ors", version = "1.0"} -alloy-primitives.workspace = true +alloy-primitives = { workspace = true } alloy-sol-types = "0.7.1" revm = { version = "8.0", default-features = false, features = ["std"] } -alloy-rpc-types-trace.workspace = true +alloy-rpc-types-trace = { workspace = true } diff --git a/crates/serde/Cargo.toml b/crates/serde_utils/Cargo.toml similarity index 96% rename from crates/serde/Cargo.toml rename to crates/serde_utils/Cargo.toml index 9280259719..2ad147876c 100644 --- a/crates/serde/Cargo.toml +++ b/crates/serde_utils/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cfx-serde" +name = "serde-utils" edition = "2021" version.workspace = true authors.workspace = true diff --git a/crates/serde/src/lib.rs b/crates/serde_utils/src/lib.rs similarity index 100% rename from crates/serde/src/lib.rs rename to crates/serde_utils/src/lib.rs diff --git a/crates/serde/src/num.rs b/crates/serde_utils/src/num.rs similarity index 100% rename from crates/serde/src/num.rs rename to crates/serde_utils/src/num.rs From ff0d92181e8975174de1d22dd6098dab9053a99a Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 21 May 2024 19:45:12 +0800 Subject: [PATCH 080/137] Fix 1559 estimation --- crates/cfx_types/src/lib.rs | 9 ++++++ .../executor/src/executive/fresh_executive.rs | 6 ++-- crates/cfxcore/executor/src/spec.rs | 2 +- crates/client/src/configuration.rs | 29 ++++++++++++++----- tests/evm_space/eip1559_test.py | 4 ++- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/crates/cfx_types/src/lib.rs b/crates/cfx_types/src/lib.rs index a174e96506..f18636caf0 100644 --- a/crates/cfx_types/src/lib.rs +++ b/crates/cfx_types/src/lib.rs @@ -146,6 +146,15 @@ impl SpaceMap { } } + pub fn zip3( + a: SpaceMap, b: SpaceMap, c: SpaceMap, + ) -> SpaceMap<(T, B, C)> { + SpaceMap { + native: (a.native, b.native, c.native), + evm: (a.evm, b.evm, c.evm), + } + } + pub fn zip4( a: SpaceMap, b: SpaceMap, c: SpaceMap, d: SpaceMap, ) -> SpaceMap<(T, B, C, D)> { diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index b6e15a4d1b..a411ed78c2 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -201,7 +201,9 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { let env = self.context.env; let spec = self.context.spec; - let gas_price = if !spec.cip1559 { + let check_base_price = self.settings.check_base_price; + + let gas_price = if !spec.cip1559 || !check_base_price { *tx.gas_price() } else { // actual_base_gas >= tx gas_price >= burnt_base_price @@ -214,7 +216,7 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { // gas_price >= actual_base_gas >= // either 1. tx gas_price >= burnt_gas_price // or 2. base_gas_price >= burnt_gas_price - assert!(gas_price >= burnt_gas_price); + assert!(gas_price >= burnt_gas_price || !check_base_price); let sender_balance = U512::from(state.balance(&sender)?); let gas_cost = if settings.charge_gas { diff --git a/crates/cfxcore/executor/src/spec.rs b/crates/cfxcore/executor/src/spec.rs index 680beef3f2..bafc485edd 100644 --- a/crates/cfxcore/executor/src/spec.rs +++ b/crates/cfxcore/executor/src/spec.rs @@ -158,7 +158,7 @@ impl Default for CommonParams { early_set_internal_contracts_states: false, transition_numbers: Default::default(), transition_heights: Default::default(), - min_base_price: SpaceMap::new(U256::one(), U256::one()), + min_base_price: SpaceMap::default(), } } } diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index fc4a030ded..55c947c225 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -169,6 +169,8 @@ build_config! { (params_dao_vote_period, (u64), DAO_PARAMETER_VOTE_PERIOD) (timer_chain_beta, (u64), TIMER_CHAIN_DEFAULT_BETA) (timer_chain_block_difficulty_ratio, (u64), TIMER_CHAIN_BLOCK_DEFAULT_DIFFICULTY_RATIO) + (min_native_base_price, (Option), None) + (min_eth_base_price, (Option), None) // FIXME: this is part of spec. (transaction_epoch_bound, (u64), TRANSACTION_DEFAULT_EPOCH_BOUND) @@ -1226,13 +1228,26 @@ impl Configuration { params.early_set_internal_contracts_states = true; } - if !self.is_test_or_dev_mode() { - params.min_base_price = SpaceMap::new( - INITIAL_1559_CORE_BASE_PRICE, - INITIAL_1559_ETH_BASE_PRICE, - ) - .map_all(U256::from) - } + let non_test_default = SpaceMap::new( + INITIAL_1559_CORE_BASE_PRICE, + INITIAL_1559_ETH_BASE_PRICE, + ); + let test_default = SpaceMap::new(1u64, 1); + let config = SpaceMap::new( + self.raw_conf.min_native_base_price, + self.raw_conf.min_eth_base_price, + ); + let base_price = SpaceMap::zip3(non_test_default, test_default, config) + .map_all(|(non_test, test, config)| { + if let Some(x) = config { + x + } else if self.is_test_or_dev_mode() { + test + } else { + non_test + } + }); + params.min_base_price = base_price.map_all(U256::from); params.chain_id = self.chain_id_params(); params.anticone_penalty_ratio = self.raw_conf.anticone_penalty_ratio; diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 9760dadb17..c3596722e9 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -17,6 +17,7 @@ def set_test_params(self): self.conf_parameters["evm_transaction_block_ratio"] = str(1) self.conf_parameters["executive_trace"] = "true" self.conf_parameters["cip1559_transition_height"] = str(1) + self.conf_parameters["min_eth_base_price"] = 20 * (10**9) def setup_network(self): self.add_nodes(self.num_nodes) @@ -51,7 +52,7 @@ def run_test(self): "to": self.evmAccount.address, "value": 1, "gas": 210000, - 'maxFeePerGas': 1, + 'maxFeePerGas': 20 * (10**9), 'maxPriorityFeePerGas': 1, "nonce": nonce, "chainId": 10, @@ -107,6 +108,7 @@ def run_test(self): assert_equal(receipt["cumulativeGasUsed"], 21000 * 9) assert_equal(receipt["gasUsed"], 21000) + assert_equal(self.w3.eth.estimate_gas({"to": self.evmAccount.address}), 21000) if __name__ == "__main__": Eip1559Test().main() \ No newline at end of file From 531fa9a10cba648a9a0d0dbc92df37ed86d04e6e Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 21 May 2024 18:34:08 +0800 Subject: [PATCH 081/137] move geth_tracer into a independent crate add license note --- Cargo.lock | 18 +++++ Cargo.toml | 3 +- crates/cfxcore/core/Cargo.toml | 3 +- .../consensus_inner/consensus_executor/mod.rs | 3 +- crates/cfxcore/core/src/consensus/mod.rs | 2 +- crates/cfxcore/execute-helper/Cargo.toml | 1 + .../src/observer/geth_tracer/builder/mod.rs | 3 - .../execute-helper/src/observer/mod.rs | 4 +- .../cfxcore/execute-helper/src/tx_outcome.rs | 2 +- crates/cfxcore/geth-tracer/Cargo.toml | 24 +++++++ crates/cfxcore/geth-tracer/readme.md | 3 + .../geth_tracer => geth-tracer/src}/arena.rs | 29 ++++++++ .../geth_tracer => geth-tracer/src}/config.rs | 28 ++++++++ .../observer => geth-tracer/src}/fourbyte.rs | 0 .../geth_tracer => geth-tracer/src}/gas.rs | 0 .../src/geth_builder.rs} | 31 ++++++++- .../mod.rs => geth-tracer/src/geth_tracer.rs} | 53 +++++---------- crates/cfxcore/geth-tracer/src/lib.rs | 20 ++++++ .../src}/tracing_inspector.rs | 36 +++++++++- .../geth_tracer => geth-tracer/src}/types.rs | 34 ++++++++-- .../geth_tracer => geth-tracer/src}/utils.rs | 68 ++----------------- .../vm-interpreter/src/instructions.rs | 4 +- crates/cfxcore/vm-interpreter/src/lib.rs | 2 +- crates/client/Cargo.toml | 3 +- crates/client/src/rpc/impls/eth/debug.rs | 2 +- crates/serde_utils/Cargo.toml | 4 +- 26 files changed, 255 insertions(+), 125 deletions(-) delete mode 100644 crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs create mode 100644 crates/cfxcore/geth-tracer/Cargo.toml create mode 100644 crates/cfxcore/geth-tracer/readme.md rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/arena.rs (73%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/config.rs (89%) rename crates/cfxcore/{execute-helper/src/observer => geth-tracer/src}/fourbyte.rs (100%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/gas.rs (100%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer/builder/geth.rs => geth-tracer/src/geth_builder.rs} (91%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer/mod.rs => geth-tracer/src/geth_tracer.rs} (96%) create mode 100644 crates/cfxcore/geth-tracer/src/lib.rs rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/tracing_inspector.rs (91%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/types.rs (93%) rename crates/cfxcore/{execute-helper/src/observer/geth_tracer => geth-tracer/src}/utils.rs (66%) diff --git a/Cargo.lock b/Cargo.lock index b1497b2dd8..6da18260b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,6 +1140,7 @@ dependencies = [ "cfx-vm-tracer-derive", "cfx-vm-types", "error-chain", + "geth-tracer", "log", "malloc_size_of", "malloc_size_of_derive", @@ -1466,6 +1467,7 @@ dependencies = [ "fallible-iterator", "fs_extra", "futures 0.3.27", + "geth-tracer", "hashbrown 0.7.2", "heap-map", "hibitset", @@ -1758,6 +1760,7 @@ dependencies = [ "fail", "futures 0.3.27", "futures01", + "geth-tracer", "io", "itertools 0.9.0", "jsonrpc-core", @@ -3576,6 +3579,21 @@ dependencies = [ "libc", ] +[[package]] +name = "geth-tracer" +version = "2.0.2" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-trace", + "alloy-sol-types", + "cfx-executor", + "cfx-types", + "cfx-vm-interpreter", + "cfx-vm-types", + "revm", + "typemap-ors", +] + [[package]] name = "getrandom" version = "0.1.16" diff --git a/Cargo.toml b/Cargo.toml index 6c358c612a..691b46b196 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ members = [ "crates/util/treap-map", "crates/util/version", "crates/serde_utils", + "crates/cfxcore/geth-tracer", ] resolver = "2" @@ -92,4 +93,4 @@ serde = { version = "1.0", features = ["derive", "alloc"] } serde_json = "1.0" alloy-primitives = "0.7.1" -alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "4e22b9e" } \ No newline at end of file +alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "4e22b9e" } diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index 09ac70a66a..c8870c7b46 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -137,7 +137,8 @@ impl-trait-for-tuples = "^0.2" impl-tools = "^0.10" treap-map = {path = "../../util/treap-map" } cfx-packing-pool = { path = "../packing-pool" } -alloy-rpc-types-trace.workspace = true +alloy-rpc-types-trace = { workspace = true } +geth-tracer = { path = "../geth-tracer" } [dev-dependencies] diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 860d30f156..0a6d3b920a 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -62,7 +62,7 @@ use crate::{ }; use cfx_execute_helper::{ estimation::{EstimateExt, EstimateRequest, EstimationContext}, - observer::{geth_tracer::types::GethTraceWithHash, Observer}, + observer::Observer, tx_outcome::make_process_tx_outcome, }; use cfx_executor::{ @@ -76,6 +76,7 @@ use cfx_executor::{ }, }; use cfx_vm_types::{Env, Spec}; +use geth_tracer::GethTraceWithHash; use alloy_rpc_types_trace::geth::{ GethDebugBuiltInTracerType::*, GethDebugTracerType, GethDebugTracingOptions, diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index a1d92f799c..ac059eb733 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -41,10 +41,10 @@ use cfx_execute_helper::{ recover_phantom_traces, ActionType, BlockExecTraces, LocalizedTrace, TraceFilter, TransactionExecTraces, }, - observer::geth_tracer::types::GethTraceWithHash, phantom_tx::build_bloom_and_recover_phantom, }; use cfx_executor::{executive::ExecutionOutcome, state::State}; +use geth_tracer::GethTraceWithHash; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; use cfx_internal_common::ChainIdParams; diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index f24e8c934f..49861b8c54 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -57,3 +57,4 @@ alloy-primitives = { workspace = true } alloy-sol-types = "0.7.1" revm = { version = "8.0", default-features = false, features = ["std"] } alloy-rpc-types-trace = { workspace = true } +geth-tracer = { path = "../geth-tracer" } diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs b/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs deleted file mode 100644 index eeb7dd7634..0000000000 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod geth; - -pub use geth::GethTraceBuilder; diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index ed9af2cebe..4169ef24f5 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -1,7 +1,5 @@ pub mod exec_tracer; -pub mod fourbyte; pub mod gasman; -pub mod geth_tracer; mod utils; use exec_tracer::ExecTracer; @@ -14,8 +12,8 @@ use cfx_executor::{ use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; use std::sync::Arc; -use self::geth_tracer::GethTracer; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; +use geth_tracer::GethTracer; #[derive(AsTracer, DrainTrace)] pub struct Observer { diff --git a/crates/cfxcore/execute-helper/src/tx_outcome.rs b/crates/cfxcore/execute-helper/src/tx_outcome.rs index 5af1ea20ca..5c9ba0c9b1 100644 --- a/crates/cfxcore/execute-helper/src/tx_outcome.rs +++ b/crates/cfxcore/execute-helper/src/tx_outcome.rs @@ -6,8 +6,8 @@ use cfx_vm_types::Spec; use pow_types::StakingEvent; use primitives::Receipt; -use crate::observer::geth_tracer::GethTraceKey; use alloy_rpc_types_trace::geth::GethTrace; +use geth_tracer::GethTraceKey; use super::{ observer::exec_tracer::{ExecTrace, ExecTraceKey}, diff --git a/crates/cfxcore/geth-tracer/Cargo.toml b/crates/cfxcore/geth-tracer/Cargo.toml new file mode 100644 index 0000000000..06c146e495 --- /dev/null +++ b/crates/cfxcore/geth-tracer/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "geth-tracer" +edition = "2021" +version.workspace = true +authors.workspace = true +description.workspace = true +documentation.workspace = true +homepage.workspace = true +keywords.workspace = true +repository.workspace = true +license-file.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +alloy-primitives = { workspace = true } +alloy-sol-types = "0.7.1" +revm = { version = "8.0", default-features = false, features = ["std"] } +alloy-rpc-types-trace = { workspace = true } +cfx-vm-types = { path = "../vm-types" } +cfx-types = { path = "../../cfx_types" } +cfx-executor = { path = "../executor" } +typemap = { package = "typemap-ors", version = "1.0"} +cfx-vm-interpreter = { path = "../vm-interpreter" } diff --git a/crates/cfxcore/geth-tracer/readme.md b/crates/cfxcore/geth-tracer/readme.md new file mode 100644 index 0000000000..a48a15384a --- /dev/null +++ b/crates/cfxcore/geth-tracer/readme.md @@ -0,0 +1,3 @@ +# geth-tracer + +This crate provides a geth style tracer for evm. \ No newline at end of file diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs b/crates/cfxcore/geth-tracer/src/arena.rs similarity index 73% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs rename to crates/cfxcore/geth-tracer/src/arena.rs index 8648f21ea8..a355bbccfe 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/arena.rs +++ b/crates/cfxcore/geth-tracer/src/arena.rs @@ -1,3 +1,32 @@ +// Copyright 2023-2024 Paradigm.xyz +// This file is part of reth. +// Reth is a modular, contributor-friendly and blazing-fast implementation of +// the Ethereum protocol + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: + +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + use super::types::{CallTrace, CallTraceNode, LogCallOrder}; /// An arena of recorded traces. diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs b/crates/cfxcore/geth-tracer/src/config.rs similarity index 89% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs rename to crates/cfxcore/geth-tracer/src/config.rs index 7803ad0425..24a68dcdd5 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/config.rs +++ b/crates/cfxcore/geth-tracer/src/config.rs @@ -1,3 +1,31 @@ +// Copyright 2023-2024 Paradigm.xyz +// This file is part of reth. +// Reth is a modular, contributor-friendly and blazing-fast implementation of +// the Ethereum protocol + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: + +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. use alloy_rpc_types_trace::{ geth::{CallConfig, GethDefaultTracingOptions, PreStateConfig}, parity::TraceType, diff --git a/crates/cfxcore/execute-helper/src/observer/fourbyte.rs b/crates/cfxcore/geth-tracer/src/fourbyte.rs similarity index 100% rename from crates/cfxcore/execute-helper/src/observer/fourbyte.rs rename to crates/cfxcore/geth-tracer/src/fourbyte.rs diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs b/crates/cfxcore/geth-tracer/src/gas.rs similarity index 100% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/gas.rs rename to crates/cfxcore/geth-tracer/src/gas.rs diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs b/crates/cfxcore/geth-tracer/src/geth_builder.rs similarity index 91% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs rename to crates/cfxcore/geth-tracer/src/geth_builder.rs index 9f41efe17d..d07282d560 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/builder/geth.rs +++ b/crates/cfxcore/geth-tracer/src/geth_builder.rs @@ -1,5 +1,32 @@ -//! Geth trace builder -use crate::observer::geth_tracer::{ +// Copyright 2023-2024 Paradigm.xyz +// This file is part of reth. +// Reth is a modular, contributor-friendly and blazing-fast implementation of +// the Ethereum protocol + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: + +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +use crate::{ types::{CallTraceNode, CallTraceStepStackItem}, TracingInspectorConfig, }; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs b/crates/cfxcore/geth-tracer/src/geth_tracer.rs similarity index 96% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs rename to crates/cfxcore/geth-tracer/src/geth_tracer.rs index 981eaf1435..0ad3d46b4e 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/mod.rs +++ b/crates/cfxcore/geth-tracer/src/geth_tracer.rs @@ -1,28 +1,16 @@ -#![allow(unused)] -mod arena; -mod builder; -mod config; -mod gas; -mod tracing_inspector; -pub mod types; -pub mod utils; - -use arena::CallTraceArena; -use builder::GethTraceBuilder; -use cfx_types::H160; -use config::TracingInspectorConfig; - -use types::LogCallOrder; -use utils::{to_alloy_address, to_alloy_h256, to_alloy_u256}; - -use super::fourbyte::FourByteInspector; +use crate::{ + config::TracingInspectorConfig, + fourbyte::FourByteInspector, + tracing_inspector::TracingInspector, + types::LogCallOrder, + utils::{to_alloy_address, to_alloy_h256, to_alloy_u256}, +}; use alloy_primitives::{Address, Bytes, LogData}; -use revm::{ - db::InMemoryDB, - interpreter::{Gas, InstructionResult, InterpreterResult}, - primitives::State, +use alloy_rpc_types_trace::geth::{ + CallConfig, GethDebugBuiltInTracerType, GethDebugBuiltInTracerType::*, + GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, + PreStateConfig, }; - use cfx_executor::{ machine::Machine, observer::{ @@ -31,16 +19,13 @@ use cfx_executor::{ }, stack::{FrameResult, FrameReturn}, }; - +use cfx_types::H160; use cfx_vm_types::{ActionParams, CallType, Error, InterpreterInfo}; - -use alloy_rpc_types_trace::geth::{ - CallConfig, GethDebugBuiltInTracerType, GethDebugBuiltInTracerType::*, - GethDebugTracerType, GethDebugTracingOptions, GethTrace, NoopFrame, - PreStateConfig, +use revm::{ + db::InMemoryDB, + interpreter::{Gas, InstructionResult, InterpreterResult}, + primitives::State, }; -use tracing_inspector::TracingInspector; - use std::sync::Arc; pub struct GethTracer { @@ -432,12 +417,6 @@ impl OpcodeTracer for GethTracer { } } -#[derive(Clone, Copy, Debug)] -struct StackStep { - trace_idx: usize, - step_idx: usize, -} - pub fn to_instruction_result(frame_result: &FrameResult) -> InstructionResult { let result = match frame_result { Ok(r) => match r.apply_state { diff --git a/crates/cfxcore/geth-tracer/src/lib.rs b/crates/cfxcore/geth-tracer/src/lib.rs new file mode 100644 index 0000000000..9ed4e64d32 --- /dev/null +++ b/crates/cfxcore/geth-tracer/src/lib.rs @@ -0,0 +1,20 @@ +#![allow(unused)] +mod arena; +mod config; +mod fourbyte; +mod gas; +mod geth_builder; +mod geth_tracer; +mod tracing_inspector; +mod types; +mod utils; + +use arena::CallTraceArena; +use config::TracingInspectorConfig; +use geth_builder::GethTraceBuilder; + +pub use geth_tracer::{GethTraceKey, GethTracer}; +pub use types::GethTraceWithHash; +pub use utils::{ + from_alloy_address, to_alloy_address, to_alloy_h256, to_alloy_u256, +}; diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs b/crates/cfxcore/geth-tracer/src/tracing_inspector.rs similarity index 91% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs rename to crates/cfxcore/geth-tracer/src/tracing_inspector.rs index 27bbe133aa..278fa77570 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/tracing_inspector.rs +++ b/crates/cfxcore/geth-tracer/src/tracing_inspector.rs @@ -1,3 +1,31 @@ +// Copyright 2023-2024 Paradigm.xyz +// This file is part of reth. +// Reth is a modular, contributor-friendly and blazing-fast implementation of +// the Ethereum protocol + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: + +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. use super::{ arena::PushTraceKind, gas::GasInspector, @@ -6,7 +34,7 @@ use super::{ StorageChange, StorageChangeReason, }, utils::{gas_used, stack_push_count, to_alloy_address, to_alloy_u256}, - CallTraceArena, GethTraceBuilder, StackStep, TracingInspectorConfig, + CallTraceArena, GethTraceBuilder, TracingInspectorConfig, }; use cfx_types::{Space, H160}; @@ -428,3 +456,9 @@ impl TracingInspector { // step.status = interp.instruction_result; } } + +#[derive(Clone, Copy, Debug)] +struct StackStep { + trace_idx: usize, + step_idx: usize, +} diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs b/crates/cfxcore/geth-tracer/src/types.rs similarity index 93% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs rename to crates/cfxcore/geth-tracer/src/types.rs index 44628d28b8..1f1c221c8f 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/types.rs +++ b/crates/cfxcore/geth-tracer/src/types.rs @@ -1,8 +1,34 @@ -//! Types for representing call trace items. +// Copyright 2023-2024 Paradigm.xyz +// This file is part of reth. +// Reth is a modular, contributor-friendly and blazing-fast implementation of +// the Ethereum protocol + +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: + +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. -use crate::observer::geth_tracer::{ - config::TraceStyle, utils, utils::convert_memory, -}; +//! Types for representing call trace items. +use crate::{config::TraceStyle, utils, utils::convert_memory}; use alloy_primitives::{Address, Bytes, LogData, U256}; use alloy_rpc_types_trace::geth::{ CallFrame, CallLogFrame, GethDefaultTracingOptions, GethTrace, StructLog, diff --git a/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs b/crates/cfxcore/geth-tracer/src/utils.rs similarity index 66% rename from crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs rename to crates/cfxcore/geth-tracer/src/utils.rs index 1779326fb1..9d5cf48ca6 100644 --- a/crates/cfxcore/execute-helper/src/observer/geth_tracer/utils.rs +++ b/crates/cfxcore/geth-tracer/src/utils.rs @@ -2,10 +2,8 @@ use alloy_primitives::{hex, B256}; use alloy_sol_types::{ContractError, GenericRevertReason}; use cfx_types::{Address, H160, H256, U256}; -use revm::{ - interpreter::opcode, - primitives::{Address as RAddress, U256 as RU256}, -}; +use cfx_vm_interpreter::instructions::INSTRUCTIONS_CANCUN; +use revm::primitives::{Address as RAddress, U256 as RU256}; /// creates the memory data in 32byte chunks /// see @@ -53,65 +51,9 @@ pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option { /// The value is obvious for most opcodes, but SWAP* and DUP* are a bit weird, /// and we handle those as they are handled in parity vmtraces. /// For reference: -pub(crate) const fn stack_push_count(step_op: u8) -> usize { - match step_op { - opcode::PUSH0..=opcode::PUSH32 => 1, - opcode::SWAP1..=opcode::SWAP16 => { - (step_op - opcode::SWAP1) as usize + 2 - } - opcode::DUP1..=opcode::DUP16 => (step_op - opcode::DUP1) as usize + 2, - opcode::CALLDATALOAD - | opcode::SLOAD - | opcode::MLOAD - | opcode::CALLDATASIZE - | opcode::LT - | opcode::GT - | opcode::DIV - | opcode::SDIV - | opcode::SAR - | opcode::AND - | opcode::EQ - | opcode::CALLVALUE - | opcode::ISZERO - | opcode::ADD - | opcode::EXP - | opcode::CALLER - | opcode::KECCAK256 - | opcode::SUB - | opcode::ADDRESS - | opcode::GAS - | opcode::MUL - | opcode::RETURNDATASIZE - | opcode::NOT - | opcode::SHR - | opcode::SHL - | opcode::EXTCODESIZE - | opcode::SLT - | opcode::OR - | opcode::NUMBER - | opcode::PC - | opcode::TIMESTAMP - | opcode::BALANCE - | opcode::SELFBALANCE - | opcode::MULMOD - | opcode::ADDMOD - | opcode::BASEFEE - | opcode::BLOCKHASH - | opcode::BYTE - | opcode::XOR - | opcode::ORIGIN - | opcode::CODESIZE - | opcode::MOD - | opcode::SIGNEXTEND - | opcode::GASLIMIT - | opcode::DIFFICULTY - | opcode::SGT - | opcode::GASPRICE - | opcode::MSIZE - | opcode::EXTCODEHASH - | opcode::SMOD - | opcode::CHAINID - | opcode::COINBASE => 1, +pub(crate) fn stack_push_count(step_op: u8) -> usize { + match INSTRUCTIONS_CANCUN.get(step_op as usize) { + Some(Some(instruct)) => instruct.ret, _ => 0, } } diff --git a/crates/cfxcore/vm-interpreter/src/instructions.rs b/crates/cfxcore/vm-interpreter/src/instructions.rs index 644e0ed399..b1c6e67829 100644 --- a/crates/cfxcore/vm-interpreter/src/instructions.rs +++ b/crates/cfxcore/vm-interpreter/src/instructions.rs @@ -501,7 +501,7 @@ impl InstructionInfo { lazy_static! { /// Static instruction table. - static ref INSTRUCTIONS: [Option; 0x100] = { + pub static ref INSTRUCTIONS: [Option; 0x100] = { let mut arr = [None; 0x100]; arr[STOP as usize] = Some(InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero)); arr[ADD as usize] = Some(InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow)); @@ -652,7 +652,7 @@ lazy_static! { arr }; - static ref INSTRUCTIONS_CANCUN: [Option; 0x100] = { + pub static ref INSTRUCTIONS_CANCUN: [Option; 0x100] = { let mut arr = *INSTRUCTIONS; arr[BEGINSUB_TLOAD as usize] = Some(InstructionInfo::new("TLOAD", 1, 1, GasPriceTier::Special)); arr[JUMPSUB_MCOPY as usize] = Some(InstructionInfo::new("MCOPY", 3, 0, GasPriceTier::Special)); diff --git a/crates/cfxcore/vm-interpreter/src/lib.rs b/crates/cfxcore/vm-interpreter/src/lib.rs index 97f56c212c..bcc6a8a583 100644 --- a/crates/cfxcore/vm-interpreter/src/lib.rs +++ b/crates/cfxcore/vm-interpreter/src/lib.rs @@ -20,7 +20,7 @@ mod tests; pub use self::{ evm::{CostType, FinalizationResult, Finalize}, factory::Factory, - instructions::GasPriceTier, + instructions::{GasPriceTier, INSTRUCTIONS, INSTRUCTIONS_CANCUN}, interpreter::{Interpreter, InterpreterResult}, vmtype::VMType, }; diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 77a48dbcaa..c8949ce51d 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -100,7 +100,8 @@ static_assertions = "1.1.0" parity-version = {path = "../util/version" } solidity-abi = {path= "../util/solidity-abi" } bls-signatures = {git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore"]} -alloy-rpc-types-trace.workspace = true +alloy-rpc-types-trace = { workspace = true } +geth-tracer = { path = "../cfxcore/geth-tracer" } [dev-dependencies] criterion = "0.3" diff --git a/crates/client/src/rpc/impls/eth/debug.rs b/crates/client/src/rpc/impls/eth/debug.rs index 56ab79a2ed..6725aa2183 100644 --- a/crates/client/src/rpc/impls/eth/debug.rs +++ b/crates/client/src/rpc/impls/eth/debug.rs @@ -11,9 +11,9 @@ use alloy_rpc_types_trace::geth::{ GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, NoopFrame, TraceResult, }; -use cfx_execute_helper::observer::geth_tracer::utils::to_alloy_h256; use cfx_types::{Space, H256}; use cfxcore::{ConsensusGraph, SharedConsensusGraph}; +use geth_tracer::to_alloy_h256; use jsonrpc_core::Result as JsonRpcResult; pub struct GethDebugHandler { diff --git a/crates/serde_utils/Cargo.toml b/crates/serde_utils/Cargo.toml index 2ad147876c..247a95fb07 100644 --- a/crates/serde_utils/Cargo.toml +++ b/crates/serde_utils/Cargo.toml @@ -13,7 +13,7 @@ license-file.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -alloy-primitives.workspace = true -serde.workspace = true +alloy-primitives = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true, features = ["alloc"] } cfx-types = { path = "../cfx_types" } From d396d562eea13c1687aeb07ea9ba16c99235039d Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 22 May 2024 15:26:13 +0800 Subject: [PATCH 082/137] Combine Geth Processing with Transaction Processing --- .../consensus_executor/epoch_execution.rs | 124 +++++++++---- .../consensus_inner/consensus_executor/mod.rs | 175 ++++-------------- 2 files changed, 129 insertions(+), 170 deletions(-) diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs index 16ce584964..16b83aa6a6 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs @@ -1,6 +1,8 @@ use super::ConsensusExecutionHandler; use std::{convert::From, sync::Arc}; +use alloy_rpc_types_trace::geth::GethDebugTracingOptions; +use geth_tracer::{GethTraceWithHash, GethTracer}; use pow_types::StakingEvent; use cfx_statedb::{ErrorKind as DbErrorKind, Result as DbResult}; @@ -32,16 +34,28 @@ use cfx_executor::{ }; use cfx_vm_types::Env; +pub enum VirtualCall<'a> { + GethTrace(GethTask<'a>), +} + +pub struct GethTask<'a> { + pub(super) tx_hash: Option, + pub(super) opts: GethDebugTracingOptions, + pub(super) answer: &'a mut Vec, +} + impl ConsensusExecutionHandler { - pub(super) fn process_epoch_transactions( + pub(super) fn process_epoch_transactions<'a>( &self, epoch_id: EpochId, state: &mut State, epoch_blocks: &Vec>, start_block_number: u64, - on_local_pivot: bool, + on_local_pivot: bool, virtual_call: Option>, ) -> DbResult>> { self.prefetch_storage_for_execution(epoch_id, state, epoch_blocks); let pivot_block = epoch_blocks.last().expect("Epoch not empty"); + let dry_run = virtual_call.is_some(); + self.before_epoch_execution(state, &*pivot_block)?; let base_gas_price = @@ -52,6 +66,8 @@ impl ConsensusExecutionHandler { let context = EpochProcessContext { on_local_pivot, executive_trace: self.config.executive_trace, + dry_run, + virtual_call, pivot_block, base_gas_price, burnt_gas_price, @@ -77,7 +93,11 @@ impl ConsensusExecutionHandler { )?; } - if self.pos_verifier.pos_option().is_some() { + if let Some(VirtualCall::GethTrace(task)) = context.virtual_call { + std::mem::swap(&mut epoch_recorder.geth_traces, task.answer); + } + + if !dry_run && self.pos_verifier.pos_option().is_some() { debug!( "put_staking_events: {:?} height={} len={}", pivot_block.hash(), @@ -98,7 +118,7 @@ impl ConsensusExecutionHandler { })?; } - if on_local_pivot { + if !dry_run && on_local_pivot { self.tx_pool.recycle_transactions(epoch_recorder.repack_tx); } @@ -148,9 +168,7 @@ impl ConsensusExecutionHandler { drop(prefetch_join_handles); } - pub(super) fn make_block_env( - &self, block_context: &BlockProcessContext, - ) -> Env { + fn make_block_env(&self, block_context: &BlockProcessContext) -> Env { let BlockProcessContext { epoch_context: &EpochProcessContext { @@ -258,18 +276,14 @@ impl ConsensusExecutionHandler { let rpc_index = recorder.tx_idx[transaction.space()]; let block = &block_context.block; + let dry_run = block_context.epoch_context.dry_run; let machine = self.machine.as_ref(); let spec = machine.spec(env.number, env.epoch_height); - let observer = if self.config.executive_trace { - Observer::with_tracing() - } else { - Observer::with_no_tracing() - }; let options = TransactOptions { - observer, + observer: self.make_observer(transaction, block_context), settings: TransactSettings::all_checks(), }; @@ -301,7 +315,8 @@ impl ConsensusExecutionHandler { recorder.receive_tx_outcome(r, transaction, block_context); - if !on_local_pivot || tx_skipped { + if !on_local_pivot || tx_skipped || dry_run { + // Skip transaction index persist return Ok(()); } @@ -343,6 +358,44 @@ impl ConsensusExecutionHandler { Ok(()) } + fn make_observer( + &self, transaction: &Arc, + block_context: &BlockProcessContext, + ) -> Observer { + use alloy_rpc_types_trace::geth::{ + GethDebugBuiltInTracerType::*, GethDebugTracerType::BuiltInTracer, + }; + + let mut observer = if self.config.executive_trace { + Observer::with_tracing() + } else { + Observer::with_no_tracing() + }; + + if let Some(VirtualCall::GethTrace(ref task)) = + block_context.epoch_context.virtual_call + { + let need_trace = + task.tx_hash.map_or(true, |hash| transaction.hash() == hash); + let support_tracer = matches!( + task.opts.tracer, + Some(BuiltInTracer( + FourByteTracer | CallTracer | PreStateTracer | NoopTracer + )) | None + ); + let tx_gas_limit = transaction.gas_limit().as_u64(); + + if need_trace && support_tracer { + observer.geth_tracer = Some(GethTracer::new( + tx_gas_limit, + Arc::clone(&self.machine), + task.opts.clone(), + )) + } + } + observer + } + fn before_epoch_execution( &self, state: &mut State, pivot_block: &Block, ) -> DbResult<()> { @@ -415,9 +468,11 @@ impl ConsensusExecutionHandler { } } -pub(super) struct EpochProcessContext<'a> { +struct EpochProcessContext<'a> { on_local_pivot: bool, executive_trace: bool, + virtual_call: Option>, + dry_run: bool, pivot_block: &'a Block, @@ -425,22 +480,7 @@ pub(super) struct EpochProcessContext<'a> { burnt_gas_price: SpaceMap, } -impl<'a> EpochProcessContext<'a> { - pub(super) fn new( - on_local_pivot: bool, executive_trace: bool, pivot_block: &'a Block, - base_gas_price: SpaceMap, burnt_gas_price: SpaceMap, - ) -> Self { - Self { - on_local_pivot, - executive_trace, - pivot_block, - base_gas_price, - burnt_gas_price, - } - } -} - -pub(super) struct BlockProcessContext<'a, 'b> { +struct BlockProcessContext<'a, 'b> { epoch_context: &'b EpochProcessContext<'a>, block: &'b Block, block_number: u64, @@ -448,7 +488,7 @@ pub(super) struct BlockProcessContext<'a, 'b> { } impl<'a, 'b> BlockProcessContext<'a, 'b> { - pub(super) fn first_block( + fn first_block( epoch_context: &'b EpochProcessContext<'a>, block: &'b Block, start_block_number: u64, ) -> Self { @@ -462,7 +502,7 @@ impl<'a, 'b> BlockProcessContext<'a, 'b> { } } - pub(super) fn next_block(&mut self, block: &'b Block) { + fn next_block(&mut self, block: &'b Block) { self.last_hash = self.block.hash(); self.block_number += 1; self.block = block; @@ -474,6 +514,7 @@ struct EpochProcessRecorder { receipts: Vec>, staking_events: Vec, repack_tx: Vec>, + geth_traces: Vec, evm_tx_idx: usize, } @@ -486,6 +527,7 @@ struct BlockProcessRecorder { receipt: Vec, tx_error_msg: Vec, traces: Vec, + geth_traces: Vec, repack_tx: Vec>, staking_events: Vec, @@ -500,6 +542,7 @@ impl BlockProcessRecorder { receipt: vec![], tx_error_msg: vec![], traces: vec![], + geth_traces: vec![], repack_tx: vec![], staking_events: vec![], tx_idx, @@ -530,6 +573,14 @@ impl BlockProcessRecorder { self.tx_error_msg.push(r.tx_exec_error_msg); self.staking_events.extend(r.tx_staking_events); + if let Some(trace) = r.geth_trace { + self.geth_traces.push(GethTraceWithHash { + trace, + tx_hash: tx.hash(), + space: tx.space(), + }); + } + match tx.space() { Space::Native => { self.tx_idx[Space::Native] += 1; @@ -552,6 +603,7 @@ impl BlockProcessRecorder { on_local_pivot, executive_trace, pivot_block, + dry_run, .. }, block, @@ -571,8 +623,14 @@ impl BlockProcessRecorder { epoch_recorder.receipts.push(block_receipts.clone()); epoch_recorder.staking_events.extend(self.staking_events); epoch_recorder.repack_tx.extend(self.repack_tx); + epoch_recorder.geth_traces.extend(self.geth_traces); + epoch_recorder.evm_tx_idx = self.tx_idx[Space::Ethereum]; + if dry_run { + return; + } + if executive_trace { data_man.insert_block_traces( block.hash(), diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 0a6d3b920a..fa2058c594 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -4,8 +4,6 @@ mod epoch_execution; -use epoch_execution::{BlockProcessContext, EpochProcessContext}; - use core::convert::TryFrom; use std::{ collections::{BTreeMap, BTreeSet, HashMap}, @@ -60,15 +58,11 @@ use crate::{ }, SharedTransactionPool, }; -use cfx_execute_helper::{ - estimation::{EstimateExt, EstimateRequest, EstimationContext}, - observer::Observer, - tx_outcome::make_process_tx_outcome, +use cfx_execute_helper::estimation::{ + EstimateExt, EstimateRequest, EstimationContext, }; use cfx_executor::{ - executive::{ - ExecutionOutcome, ExecutiveContext, TransactOptions, TransactSettings, - }, + executive::ExecutionOutcome, machine::Machine, state::{ distribute_pos_interest, update_pos_status, CleanupMode, State, @@ -78,9 +72,9 @@ use cfx_executor::{ use cfx_vm_types::{Env, Spec}; use geth_tracer::GethTraceWithHash; -use alloy_rpc_types_trace::geth::{ - GethDebugBuiltInTracerType::*, GethDebugTracerType, GethDebugTracingOptions, -}; +use alloy_rpc_types_trace::geth::GethDebugTracingOptions; + +use self::epoch_execution::{GethTask, VirtualCall}; lazy_static! { static ref CONSENSIS_EXECUTION_TIMER: Arc = @@ -1065,6 +1059,7 @@ impl ConsensusExecutionHandler { &epoch_blocks, start_block_number, on_local_pivot, + /* virtual_call */ None, ) // TODO: maybe propagate the error all the way up so that the // program may restart by itself. @@ -1584,6 +1579,7 @@ impl ConsensusExecutionHandler { &epoch_blocks, start_block_number, false, + /* virtual_call */ None, ) } @@ -1725,20 +1721,23 @@ impl ConsensusExecutionHandler { ) { bail!("state is not ready"); } + drop(state_availability_boundary); + let state_index = self .data_man .get_state_readonly_index(parent_pivot_block_hash); - let mut state = State::new(StateDb::new( - self.data_man - .storage_manager - .get_state_no_commit( - state_index.unwrap(), - /* try_open = */ true, - state_space, - )? - .ok_or("state deleted")?, - ))?; - drop(state_availability_boundary); + + let storage = self + .data_man + .storage_manager + .get_state_no_commit( + state_index.unwrap(), + /* try_open = */ true, + state_space, + )? + .ok_or("state deleted")?; + let state_db = StateDb::new(storage); + let mut state = State::new(state_db)?; let start_block_number = self .data_man @@ -1756,129 +1755,31 @@ impl ConsensusExecutionHandler { .map_err(|err| err.into()) } - // Execute transactions in the epoch to collect traces. - // The behavior is similar to `process_epoch_transactions`, but it won't - // persist any state or data + /// Execute transactions in the epoch to collect traces. fn execute_epoch_tx_to_collect_trace( &self, state: &mut State, epoch_blocks: &Vec>, start_block_number: u64, tx_hash: Option, opts: GethDebugTracingOptions, ) -> DbResult> { - let pivot_block = epoch_blocks.last().expect("Epoch not empty"); - - let base_gas_price = - pivot_block.block_header.base_price().unwrap_or_default(); - let burnt_gas_price = - base_gas_price.map_all(|x| state.burnt_gas_price(x)); + let epoch_id = epoch_blocks.last().unwrap().hash(); - let context = EpochProcessContext::new( - true, // TODO check this - false, - pivot_block, - base_gas_price, - burnt_gas_price, - ); + let mut answer = vec![]; + let virtual_call = VirtualCall::GethTrace(GethTask { + tx_hash, + opts, + answer: &mut answer, + }); - // used for make env - let mut block_context = BlockProcessContext::first_block( - &context, - epoch_blocks.first().unwrap(), + self.process_epoch_transactions( + epoch_id, + state, + epoch_blocks, start_block_number, - ); - - let mut traces = Vec::new(); - let mut block_number = start_block_number; - - for (idx, block) in epoch_blocks.iter().enumerate() { - if idx > 0 { - block_context.next_block(block); - } - - let _secondary_reward = - self.before_block_execution(state, block_number, block)?; - - let mut env = self.make_block_env(&block_context); - - let spec = self.machine.spec(env.number, env.epoch_height); - let machine = self.machine.as_ref(); - - block_number += 1; - - for (_idx, transaction) in block.transactions.iter().enumerate() { - let need_trace = match tx_hash { - Some(ref hash) => transaction.hash() == *hash, - None => true, // trace all tx - }; - - // choose the observer according to the options - let observer = if need_trace { - let tx_gas_limit = transaction.gas_limit().as_u64(); - match &opts.tracer { - Some(t) => match t { - GethDebugTracerType::BuiltInTracer(bt) => { - match bt { - FourByteTracer | CallTracer - | PreStateTracer | NoopTracer => { - Observer::geth_tracer( - tx_gas_limit, - Arc::clone(&self.machine), - opts.clone(), - ) - } - MuxTracer => Observer::with_no_tracing(), - } - } - GethDebugTracerType::JsTracer(_) => { - Observer::with_no_tracing() - } - }, - None => - // default is opcode tracer - { - Observer::geth_tracer( - tx_gas_limit, - Arc::clone(&self.machine), - opts.clone(), - ) - } - } - } else { - Observer::with_no_tracing() - }; - - let options = TransactOptions { - observer, - settings: TransactSettings::all_checks(), - }; - let execution_outcome = - ExecutiveContext::new(state, &env, machine, &spec) - .transact(transaction, options)?; - - if let Some(burnt_fee) = execution_outcome - .try_as_executed() - .and_then(|e| e.burnt_fee) - { - state.burn_by_cip1559(burnt_fee); - }; - - let r = make_process_tx_outcome( - execution_outcome, - &mut env.accumulated_gas_used, - transaction.hash, - &spec, - ); - - if need_trace && r.geth_trace.is_some() { - traces.push(GethTraceWithHash { - trace: r.geth_trace.unwrap(), - tx_hash: transaction.hash(), - space: transaction.space(), - }); - } - } - } + false, + Some(virtual_call), + )?; - Ok(traces) + Ok(answer) } } From 65d65df7050a53fc3e4cb6a9a3841974e05856df Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Wed, 22 May 2024 16:06:30 +0800 Subject: [PATCH 083/137] chore(dependencies): bump coincurve from 15.0.1 to 19.0.1 --- dev-support/dep_pip3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-support/dep_pip3.sh b/dev-support/dep_pip3.sh index d54b482bfa..40f7f2fe47 100755 --- a/dev-support/dep_pip3.sh +++ b/dev-support/dep_pip3.sh @@ -11,7 +11,7 @@ function install() { install eth-utils install rlp==1.2.0 install py-ecc==5.2.0 -install coincurve==15.0.1 +install coincurve==19.0.1 install pysha3 install trie==1.4.0 install web3==5.31.1 From c43973e56e8b6e16eb12bf92152cab0885201cfe Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 22 May 2024 16:22:58 +0800 Subject: [PATCH 084/137] fix review feedbacks --- Cargo.lock | 1 + crates/cfx_store/src/account/mod.rs | 1 - crates/cfx_store/src/json/mod.rs | 1 - .../consensus_executor/epoch_execution.rs | 12 +++++++++-- crates/cfxcore/execute-helper/Cargo.toml | 2 +- .../execute-helper/src/observer/mod.rs | 7 ++++--- crates/cfxcore/executor/Cargo.toml | 2 +- crates/cfxcore/executor/src/context.rs | 2 +- .../executor/src/observer/opcode_tracer.rs | 2 +- crates/cfxcore/geth-tracer/Cargo.toml | 1 + crates/cfxcore/geth-tracer/src/config.rs | 1 - crates/cfxcore/geth-tracer/src/gas.rs | 1 - crates/cfxcore/geth-tracer/src/geth_tracer.rs | 12 ++++++----- crates/cfxcore/geth-tracer/src/lib.rs | 2 +- .../geth-tracer/src/tracing_inspector.rs | 20 ++++++++++++++++--- crates/cfxcore/geth-tracer/src/types.rs | 8 ++++++++ crates/cfxcore/geth-tracer/src/utils.rs | 16 ++++++++++----- crates/cfxcore/vm-interpreter/Cargo.toml | 2 +- .../vm-interpreter/src/interpreter/mod.rs | 2 +- .../vm-interpreter/src/interpreter/stack.rs | 2 +- crates/cfxcore/vm-interpreter/src/lib.rs | 1 - crates/cfxcore/vm-types/Cargo.toml | 2 +- .../cfxcore/vm-types/src/interpreter_info.rs | 2 +- 23 files changed, 69 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6da18260b3..09fdc8e77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3590,6 +3590,7 @@ dependencies = [ "cfx-types", "cfx-vm-interpreter", "cfx-vm-types", + "primitives", "revm", "typemap-ors", ] diff --git a/crates/cfx_store/src/account/mod.rs b/crates/cfx_store/src/account/mod.rs index 9ed706a586..cc718cc6b1 100644 --- a/crates/cfx_store/src/account/mod.rs +++ b/crates/cfx_store/src/account/mod.rs @@ -20,7 +20,6 @@ mod kdf; mod safe_account; mod version; -#[allow(unused_imports)] pub use self::{ cipher::{Aes128Ctr, Cipher}, crypto::Crypto, diff --git a/crates/cfx_store/src/json/mod.rs b/crates/cfx_store/src/json/mod.rs index 3d577e58ba..196b70a106 100644 --- a/crates/cfx_store/src/json/mod.rs +++ b/crates/cfx_store/src/json/mod.rs @@ -29,7 +29,6 @@ mod vault_file; mod vault_key_file; mod version; -#[allow(unused_imports)] pub use self::{ bytes::Bytes, cipher::{Aes128Ctr, Cipher, CipherSer, CipherSerParams}, diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs index 16b83aa6a6..7bc1ad0a6f 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/epoch_execution.rs @@ -2,7 +2,7 @@ use super::ConsensusExecutionHandler; use std::{convert::From, sync::Arc}; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; -use geth_tracer::{GethTraceWithHash, GethTracer}; +use geth_tracer::{GethTraceWithHash, GethTracer, TxExecContext}; use pow_types::StakingEvent; use cfx_statedb::{ErrorKind as DbErrorKind, Result as DbResult}; @@ -387,7 +387,15 @@ impl ConsensusExecutionHandler { if need_trace && support_tracer { observer.geth_tracer = Some(GethTracer::new( - tx_gas_limit, + TxExecContext { + tx_gas_limit, + block_height: block_context + .epoch_context + .pivot_block + .block_header + .height(), + block_number: block_context.block_number, + }, Arc::clone(&self.machine), task.opts.clone(), )) diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 49861b8c54..b1e52aa643 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-execute-helper" version = "2.0.2" -edition = "2021" +edition = "2018" [dependencies] # substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } diff --git a/crates/cfxcore/execute-helper/src/observer/mod.rs b/crates/cfxcore/execute-helper/src/observer/mod.rs index 4169ef24f5..6b47519777 100644 --- a/crates/cfxcore/execute-helper/src/observer/mod.rs +++ b/crates/cfxcore/execute-helper/src/observer/mod.rs @@ -13,7 +13,7 @@ use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; use std::sync::Arc; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; -use geth_tracer::GethTracer; +use geth_tracer::{GethTracer, TxExecContext}; #[derive(AsTracer, DrainTrace)] pub struct Observer { @@ -48,12 +48,13 @@ impl Observer { } pub fn geth_tracer( - tx_gas_limit: u64, machine: Arc, opts: GethDebugTracingOptions, + tx_exec_context: TxExecContext, machine: Arc, + opts: GethDebugTracingOptions, ) -> Self { Observer { tracer: None, gas_man: None, - geth_tracer: Some(GethTracer::new(tx_gas_limit, machine, opts)), + geth_tracer: Some(GethTracer::new(tx_exec_context, machine, opts)), } } } diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index b4b4d5797c..096f05d41a 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-executor" version = "2.0.2" -edition = "2021" +edition = "2018" [dependencies] substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } diff --git a/crates/cfxcore/executor/src/context.rs b/crates/cfxcore/executor/src/context.rs index 0a47aab251..6a7c60e4cd 100644 --- a/crates/cfxcore/executor/src/context.rs +++ b/crates/cfxcore/executor/src/context.rs @@ -410,7 +410,7 @@ impl<'a> ContextTrait for Context<'a> { return Err(vm::Error::MutableCallInStaticContext); } - self.tracer.log(&self.origin.address, topics.clone(), data); + self.tracer.log(&self.origin.address, &topics, data); let address = self.origin.address.clone(); self.substate.logs.push(LogEntry { diff --git a/crates/cfxcore/executor/src/observer/opcode_tracer.rs b/crates/cfxcore/executor/src/observer/opcode_tracer.rs index ce7a073ad1..5611da5935 100644 --- a/crates/cfxcore/executor/src/observer/opcode_tracer.rs +++ b/crates/cfxcore/executor/src/observer/opcode_tracer.rs @@ -24,7 +24,7 @@ pub trait OpcodeTracer { /// Called when a log is emitted. #[inline] - fn log(&mut self, address: &Address, topics: Vec, data: &[u8]) { + fn log(&mut self, address: &Address, topics: &Vec, data: &[u8]) { let _ = address; let _ = topics; let _ = data; diff --git a/crates/cfxcore/geth-tracer/Cargo.toml b/crates/cfxcore/geth-tracer/Cargo.toml index 06c146e495..46c964cae9 100644 --- a/crates/cfxcore/geth-tracer/Cargo.toml +++ b/crates/cfxcore/geth-tracer/Cargo.toml @@ -22,3 +22,4 @@ cfx-types = { path = "../../cfx_types" } cfx-executor = { path = "../executor" } typemap = { package = "typemap-ors", version = "1.0"} cfx-vm-interpreter = { path = "../vm-interpreter" } +primitives = { path = "../../primitives" } diff --git a/crates/cfxcore/geth-tracer/src/config.rs b/crates/cfxcore/geth-tracer/src/config.rs index 24a68dcdd5..ba3ffcb141 100644 --- a/crates/cfxcore/geth-tracer/src/config.rs +++ b/crates/cfxcore/geth-tracer/src/config.rs @@ -313,7 +313,6 @@ pub(crate) enum TraceStyle { /// Parity style tracer Parity, /// Geth style tracer - #[allow(dead_code)] Geth, } diff --git a/crates/cfxcore/geth-tracer/src/gas.rs b/crates/cfxcore/geth-tracer/src/gas.rs index a46b78c67e..0815a18040 100644 --- a/crates/cfxcore/geth-tracer/src/gas.rs +++ b/crates/cfxcore/geth-tracer/src/gas.rs @@ -8,7 +8,6 @@ pub struct GasInspector { impl GasInspector { pub fn gas_remaining(&self) -> u64 { self.gas_remaining } - #[allow(dead_code)] pub fn last_gas_cost(&self) -> u64 { self.last_gas_cost } pub fn set_gas_remainning(&mut self, remainning: u64) { diff --git a/crates/cfxcore/geth-tracer/src/geth_tracer.rs b/crates/cfxcore/geth-tracer/src/geth_tracer.rs index 0ad3d46b4e..0f2ae40814 100644 --- a/crates/cfxcore/geth-tracer/src/geth_tracer.rs +++ b/crates/cfxcore/geth-tracer/src/geth_tracer.rs @@ -2,7 +2,7 @@ use crate::{ config::TracingInspectorConfig, fourbyte::FourByteInspector, tracing_inspector::TracingInspector, - types::LogCallOrder, + types::{LogCallOrder, TxExecContext}, utils::{to_alloy_address, to_alloy_h256, to_alloy_u256}, }; use alloy_primitives::{Address, Bytes, LogData}; @@ -46,8 +46,10 @@ pub struct GethTracer { impl GethTracer { pub fn new( - tx_gas_limit: u64, machine: Arc, opts: GethDebugTracingOptions, + tx_exec_context: TxExecContext, machine: Arc, + opts: GethDebugTracingOptions, ) -> Self { + let TxExecContext { tx_gas_limit, .. } = tx_exec_context; let config = match opts.tracer { Some(GethDebugTracerType::BuiltInTracer(builtin_tracer)) => { match builtin_tracer { @@ -79,7 +81,7 @@ impl GethTracer { }; Self { - inner: TracingInspector::new(config, machine), + inner: TracingInspector::new(config, machine, tx_exec_context), fourbyte_inspector: FourByteInspector::new(), tx_gas_limit, depth: 0, @@ -388,8 +390,8 @@ impl OpcodeTracer for GethTracer { } fn log( - &mut self, _address: &cfx_types::Address, topics: Vec, - data: &[u8], + &mut self, _address: &cfx_types::Address, + topics: &Vec, data: &[u8], ) { if self.inner.config.record_logs { let trace_idx = self.inner.last_trace_idx(); diff --git a/crates/cfxcore/geth-tracer/src/lib.rs b/crates/cfxcore/geth-tracer/src/lib.rs index 9ed4e64d32..176a85a02f 100644 --- a/crates/cfxcore/geth-tracer/src/lib.rs +++ b/crates/cfxcore/geth-tracer/src/lib.rs @@ -14,7 +14,7 @@ use config::TracingInspectorConfig; use geth_builder::GethTraceBuilder; pub use geth_tracer::{GethTraceKey, GethTracer}; -pub use types::GethTraceWithHash; +pub use types::{GethTraceWithHash, TxExecContext}; pub use utils::{ from_alloy_address, to_alloy_address, to_alloy_h256, to_alloy_u256, }; diff --git a/crates/cfxcore/geth-tracer/src/tracing_inspector.rs b/crates/cfxcore/geth-tracer/src/tracing_inspector.rs index 278fa77570..82a0c00768 100644 --- a/crates/cfxcore/geth-tracer/src/tracing_inspector.rs +++ b/crates/cfxcore/geth-tracer/src/tracing_inspector.rs @@ -17,6 +17,8 @@ // shall be included in all copies or substantial portions // of the Software. +use crate::TxExecContext; + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A @@ -63,11 +65,16 @@ pub struct TracingInspector { pub gas_inspector: GasInspector, // machine: Arc, + + tx_exec_context: TxExecContext, } impl TracingInspector { /// Returns a new instance for the given config - pub fn new(config: TracingInspectorConfig, machine: Arc) -> Self { + pub fn new( + config: TracingInspectorConfig, machine: Arc, + tx_exec_context: TxExecContext, + ) -> Self { Self { config, traces: Default::default(), @@ -76,6 +83,7 @@ impl TracingInspector { last_call_return_data: None, gas_inspector: Default::default(), machine, + tx_exec_context, } } @@ -95,6 +103,7 @@ impl TracingInspector { // kept config: _, machine: _, + .. } = self; traces.clear(); trace_stack.clear(); @@ -333,7 +342,7 @@ impl TracingInspector { interp .stack() .into_iter() - .map(|v| to_alloy_u256(v)) + .map(|v| to_alloy_u256(*v)) .collect(), ) } else { @@ -410,7 +419,12 @@ impl TracingInspector { let step = &mut self.traces.arena[trace_idx].trace.steps[step_idx]; if self.config.record_stack_snapshots.is_pushes() { - let num_pushed = stack_push_count(step.op.get()); + let spec = self.machine.spec( + self.tx_exec_context.block_number, + self.tx_exec_context.block_height, + ); + let num_pushed = + stack_push_count(step.op.get(), spec.cancun_opcodes); let start = interp.stack().len() - num_pushed; let push_stack = interp.stack()[start..].to_vec(); step.push_stack = Some( diff --git a/crates/cfxcore/geth-tracer/src/types.rs b/crates/cfxcore/geth-tracer/src/types.rs index 1f1c221c8f..8a5c5cddad 100644 --- a/crates/cfxcore/geth-tracer/src/types.rs +++ b/crates/cfxcore/geth-tracer/src/types.rs @@ -35,6 +35,7 @@ use alloy_rpc_types_trace::geth::{ }; use cfx_types::{Space, H256}; use cfx_vm_types::CallType as CfxCallType; +use primitives::{block::BlockHeight, BlockNumber}; use revm::interpreter::{ opcode, CallContext, CallScheme, CreateScheme, InstructionResult, OpCode, }; @@ -610,6 +611,13 @@ pub struct GethTraceWithHash { pub space: Space, } +#[derive(Clone)] +pub struct TxExecContext { + pub tx_gas_limit: u64, + pub block_number: BlockNumber, + pub block_height: BlockHeight, +} + #[cfg(feature = "serde")] mod opcode_serde { use super::OpCode; diff --git a/crates/cfxcore/geth-tracer/src/utils.rs b/crates/cfxcore/geth-tracer/src/utils.rs index 9d5cf48ca6..e33455c4d2 100644 --- a/crates/cfxcore/geth-tracer/src/utils.rs +++ b/crates/cfxcore/geth-tracer/src/utils.rs @@ -2,7 +2,7 @@ use alloy_primitives::{hex, B256}; use alloy_sol_types::{ContractError, GenericRevertReason}; use cfx_types::{Address, H160, H256, U256}; -use cfx_vm_interpreter::instructions::INSTRUCTIONS_CANCUN; +use cfx_vm_interpreter::instructions::{INSTRUCTIONS, INSTRUCTIONS_CANCUN}; use revm::primitives::{Address as RAddress, U256 as RU256}; /// creates the memory data in 32byte chunks @@ -51,10 +51,16 @@ pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option { /// The value is obvious for most opcodes, but SWAP* and DUP* are a bit weird, /// and we handle those as they are handled in parity vmtraces. /// For reference: -pub(crate) fn stack_push_count(step_op: u8) -> usize { - match INSTRUCTIONS_CANCUN.get(step_op as usize) { - Some(Some(instruct)) => instruct.ret, - _ => 0, +pub(crate) fn stack_push_count(step_op: u8, cancun_enabled: bool) -> usize { + match cancun_enabled { + true => match INSTRUCTIONS_CANCUN.get(step_op as usize) { + Some(Some(instruct)) => instruct.ret, + _ => 0, + }, + false => match INSTRUCTIONS.get(step_op as usize) { + Some(Some(instruct)) => instruct.ret, + _ => 0, + }, } } diff --git a/crates/cfxcore/vm-interpreter/Cargo.toml b/crates/cfxcore/vm-interpreter/Cargo.toml index a9758436cb..07ab4e4b6b 100644 --- a/crates/cfxcore/vm-interpreter/Cargo.toml +++ b/crates/cfxcore/vm-interpreter/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-interpreter" version = "2.0.2" -edition = "2021" +edition = "2018" [dependencies] bit-set = "0.4" diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index f4a8e91613..55a0ebe044 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -1694,7 +1694,7 @@ impl InterpreterInfo fn return_stack(&self) -> &Vec { &self.return_stack } - fn stack(&self) -> Vec { self.stack.content() } + fn stack(&self) -> &Vec { self.stack.content() } fn contract_address(&self) -> Address { self.params.address } } diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs index c4e703fac1..b554319cc8 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/stack.rs @@ -56,7 +56,7 @@ impl VecStack { } } - pub fn content(&self) -> Vec { self.stack.clone() } + pub fn content(&self) -> &Vec { &self.stack } } impl Stack for VecStack { diff --git a/crates/cfxcore/vm-interpreter/src/lib.rs b/crates/cfxcore/vm-interpreter/src/lib.rs index bcc6a8a583..3937ef9f42 100644 --- a/crates/cfxcore/vm-interpreter/src/lib.rs +++ b/crates/cfxcore/vm-interpreter/src/lib.rs @@ -21,6 +21,5 @@ pub use self::{ evm::{CostType, FinalizationResult, Finalize}, factory::Factory, instructions::{GasPriceTier, INSTRUCTIONS, INSTRUCTIONS_CANCUN}, - interpreter::{Interpreter, InterpreterResult}, vmtype::VMType, }; diff --git a/crates/cfxcore/vm-types/Cargo.toml b/crates/cfxcore/vm-types/Cargo.toml index 942c1e22fc..3d1d4b9c43 100644 --- a/crates/cfxcore/vm-types/Cargo.toml +++ b/crates/cfxcore/vm-types/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-types" version = "2.0.2" -edition = "2021" +edition = "2018" [dependencies] cfx-bytes = { path = "../../cfx_bytes" } diff --git a/crates/cfxcore/vm-types/src/interpreter_info.rs b/crates/cfxcore/vm-types/src/interpreter_info.rs index 432b2a2830..b1c81a295f 100644 --- a/crates/cfxcore/vm-types/src/interpreter_info.rs +++ b/crates/cfxcore/vm-types/src/interpreter_info.rs @@ -11,7 +11,7 @@ pub trait InterpreterInfo { fn mem(&self) -> &Vec; - fn stack(&self) -> Vec; + fn stack(&self) -> &Vec; fn return_stack(&self) -> &Vec; From 34acbf1ce286e9bc2c29026c1540ed21f7561b59 Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 22 May 2024 17:50:56 +0800 Subject: [PATCH 085/137] fix step trace in different level issue --- .../vm-interpreter/src/interpreter/mod.rs | 229 +++++++++--------- 1 file changed, 116 insertions(+), 113 deletions(-) diff --git a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs index 55a0ebe044..81d792fe04 100644 --- a/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs +++ b/crates/cfxcore/vm-interpreter/src/interpreter/mod.rs @@ -368,122 +368,20 @@ impl Interpreter { let result = match self.resume_result.take() { Some(result) => result, None => { - // invoke tracer step hook if self.do_trace { context.trace_step(self); } - let opcode = self.reader.code[self.reader.position]; - let instruction = - Instruction::from_u8_versioned(opcode, context.spec()); - self.reader.position += 1; - - // TODO: make compile-time removable if too much of a - // performance hit. - // self.do_trace = self.do_trace - // && context.trace_next_instruction( - // self.reader.position - 1, - // opcode, - // self.gasometer - // .as_mut() - // .expect(GASOMETER_PROOF) - // .current_gas - // .as_u256(), - // ); - - let instruction = match instruction { - Some(i) => i, - None => { - return InterpreterResult::Done(Err( - vm::Error::BadInstruction { - instruction: opcode, - }, - )); - } - }; + let op_result = self.exec_instruction(context); - let info = instruction.info::(); - self.last_stack_ret_len = info.ret; - if let Err(e) = - self.verify_instruction(context, instruction, info) - { - return InterpreterResult::Done(Err(e)); + if self.do_trace { + context.trace_step_end(self); } - // Calculate gas cost - let requirements = match self - .gasometer - .as_mut() - .expect(GASOMETER_PROOF) - .requirements( - context, - instruction, - info, - &self.stack, - self.mem.size(), - ) { - Ok(t) => t, - Err(e) => return InterpreterResult::Done(Err(e)), - }; - // if self.do_trace { - // context.trace_prepare_execute( - // self.reader.position - 1, - // opcode, - // requirements.gas_cost.as_u256(), - // Self::mem_written(instruction, &self.stack), - // Self::store_written(instruction, &self.stack), - // ); - // } - - if let Err(e) = self - .gasometer - .as_mut() - .expect(GASOMETER_PROOF) - .verify_gas(&requirements.gas_cost) - { - return InterpreterResult::Done(Err(e)); + match op_result { + Ok(result) => result, + Err(e) => return e, } - self.mem.expand(requirements.memory_required_size); - self.gasometer - .as_mut() - .expect(GASOMETER_PROOF) - .current_mem_gas = requirements.memory_total_gas; - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - - requirements.gas_cost; - - evm_debug!({ - self.informant.before_instruction( - self.reader.position, - instruction, - info, - &self - .gasometer - .as_mut() - .expect(GASOMETER_PROOF) - .current_gas, - &self.stack, - ) - }); - - // Execute instruction - let current_gas = - self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; - let result = match self.exec_instruction( - current_gas, - context, - instruction, - requirements.provide_gas, - ) { - Err(x) => { - return InterpreterResult::Done(Err(x)); - } - Ok(x) => x, - }; - - evm_debug!({ self.informant.after_instruction(instruction) }); - - result } }; @@ -509,11 +407,6 @@ impl Interpreter { // ); // } - // invoke trace step_end hook - if self.do_trace { - context.trace_step_end(self); - } - // Advance match result { InstructionResult::JumpToPosition(position) => { @@ -668,6 +561,116 @@ impl Interpreter { } fn exec_instruction( + &mut self, context: &mut dyn vm::Context, + ) -> Result, InterpreterResult> { + let opcode = self.reader.code[self.reader.position]; + let instruction = + Instruction::from_u8_versioned(opcode, context.spec()); + self.reader.position += 1; + + // TODO: make compile-time removable if too much of a + // performance hit. + // self.do_trace = self.do_trace + // && context.trace_next_instruction( + // self.reader.position - 1, + // opcode, + // self.gasometer + // .as_mut() + // .expect(GASOMETER_PROOF) + // .current_gas + // .as_u256(), + // ); + + let instruction = match instruction { + Some(i) => i, + None => { + return Err(InterpreterResult::Done(Err( + vm::Error::BadInstruction { + instruction: opcode, + }, + ))); + } + }; + + let info = instruction.info::(); + self.last_stack_ret_len = info.ret; + if let Err(e) = self.verify_instruction(context, instruction, info) { + return Err(InterpreterResult::Done(Err(e))); + } + + // Calculate gas cost + let requirements = match self + .gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .requirements( + context, + instruction, + info, + &self.stack, + self.mem.size(), + ) { + Ok(t) => t, + Err(e) => return Err(InterpreterResult::Done(Err(e))), + }; + // if self.do_trace { + // context.trace_prepare_execute( + // self.reader.position - 1, + // opcode, + // requirements.gas_cost.as_u256(), + // Self::mem_written(instruction, &self.stack), + // Self::store_written(instruction, &self.stack), + // ); + // } + + if let Err(e) = self + .gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .verify_gas(&requirements.gas_cost) + { + return Err(InterpreterResult::Done(Err(e))); + } + self.mem.expand(requirements.memory_required_size); + self.gasometer + .as_mut() + .expect(GASOMETER_PROOF) + .current_mem_gas = requirements.memory_total_gas; + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + - requirements.gas_cost; + + evm_debug!({ + self.informant.before_instruction( + self.reader.position, + instruction, + info, + &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, + &self.stack, + ) + }); + + // Execute instruction + let current_gas = + self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas; + let result = match self.exec_instruction_inner( + current_gas, + context, + instruction, + requirements.provide_gas, + ) { + Err(x) => { + return Err(InterpreterResult::Done(Err(x))); + } + Ok(x) => x, + }; + + evm_debug!({ self.informant.after_instruction(instruction) }); + + Ok(result) + } + + fn exec_instruction_inner( &mut self, gas: Cost, context: &mut dyn vm::Context, instruction: Instruction, provided: Option, ) -> vm::Result> { From 6e962792f5df6518b57905b9cb5a8e59f87372d2 Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 22 May 2024 17:59:49 +0800 Subject: [PATCH 086/137] fix comment --- crates/cfxcore/executor/src/stack/executable.rs | 8 ++++---- crates/cfxcore/executor/src/stack/frame_start.rs | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/cfxcore/executor/src/stack/executable.rs b/crates/cfxcore/executor/src/stack/executable.rs index 1dea509e34..a1dc3a19d3 100644 --- a/crates/cfxcore/executor/src/stack/executable.rs +++ b/crates/cfxcore/executor/src/stack/executable.rs @@ -1,7 +1,7 @@ use super::{FrameLocal, Resumable}; use crate::{ - builtin::BuiltinExec, context::Context, - internal_contract::InternalContractExec, stack::RuntimeRes, + builtin::BuiltinExec, context::Context, executive_observer::TracerTrait, + internal_contract::InternalContractExec, }; use cfx_statedb::Result as DbResult; use cfx_types::{AddressSpaceUtil, U256}; @@ -46,7 +46,7 @@ use ExecutableOutcome::*; /// contracts, simple transfers, or the execution of EVM bytecode. pub fn make_executable<'a>( frame_local: &FrameLocal<'a>, params: ActionParams, - resources: &mut RuntimeRes<'a>, + tracer: &mut dyn TracerTrait, ) -> Box { let is_create = frame_local.create_address.is_some(); let code_address = params.code_address.with_space(params.space); @@ -76,7 +76,7 @@ pub fn make_executable<'a>( trace!("CallCreate"); // call the initialize_interp hook to log gas_limit - resources.tracer.initialize_interp(params.gas.clone()); + tracer.initialize_interp(params.gas.clone()); let factory = frame_local.machine.vm_factory_ref(); Box::new(factory.create(params, frame_local.spec, frame_local.depth)) diff --git a/crates/cfxcore/executor/src/stack/frame_start.rs b/crates/cfxcore/executor/src/stack/frame_start.rs index 9273f945e1..2541a39a51 100644 --- a/crates/cfxcore/executor/src/stack/frame_start.rs +++ b/crates/cfxcore/executor/src/stack/frame_start.rs @@ -113,7 +113,8 @@ impl<'a> FreshFrame<'a> { )? }; - let executable = make_executable(&frame_local, params, resources); + let executable = + make_executable(&frame_local, params, resources.tracer); run_executable(executable, frame_local, resources) } } From 694f4527f1105974c12af2de85b33d444d8207a2 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 23 May 2024 14:44:33 +0800 Subject: [PATCH 087/137] update feeHistory and accessList.storage_key --- crates/client/src/rpc/impls/cfx.rs | 2 +- crates/client/src/rpc/impls/common.rs | 4 ++-- crates/client/src/rpc/impls/eth.rs | 9 +++------ crates/client/src/rpc/impls/light.rs | 4 ++-- crates/client/src/rpc/traits/cfx_space/cfx.rs | 2 +- crates/client/src/rpc/traits/eth_space/eth.rs | 2 +- crates/client/src/rpc/types/fee_history.rs | 8 ++++---- crates/primitives/src/transaction/mod.rs | 1 + tests/evm_space/eip1559_test.py | 2 +- 9 files changed, 16 insertions(+), 18 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx.rs index f57fb5e429..4e1a0f6649 100644 --- a/crates/client/src/rpc/impls/cfx.rs +++ b/crates/client/src/rpc/impls/cfx.rs @@ -2277,7 +2277,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; - fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; fn max_priority_fee_per_gas(&self) -> BoxFuture; } diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/common.rs index 3431e50089..ae581ba4d1 100644 --- a/crates/client/src/rpc/impls/common.rs +++ b/crates/client/src/rpc/impls/common.rs @@ -531,7 +531,7 @@ impl RpcImpl { pub fn fee_history( &self, block_count: U64, newest_block: EpochNumber, - reward_percentiles: Vec, + reward_percentiles: Vec, ) -> RpcResult { info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", @@ -617,7 +617,7 @@ impl RpcImpl { let fee_history = self.fee_history( U64::from(300), EpochNumber::LatestState, - vec![U64::from(50)], + vec![50f64], )?; let total_reward: U256 = fee_history diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth.rs index 62b44a7b98..567b22991a 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth.rs @@ -476,11 +476,8 @@ impl Eth for EthHandler { self.tx_pool.machine().params().evm_transaction_block_ratio as usize; - let fee_history = self.fee_history( - U64::from(300), - BlockNumber::Latest, - vec![U64::from(50)], - )?; + let fee_history = + self.fee_history(U64::from(300), BlockNumber::Latest, vec![50f64])?; let total_reward: U256 = fee_history .reward() @@ -908,7 +905,7 @@ impl Eth for EthHandler { fn fee_history( &self, block_count: U64, newest_block: BlockNumber, - reward_percentiles: Vec, + reward_percentiles: Vec, ) -> jsonrpc_core::Result { info!( "RPC Request: eth_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/light.rs index 85032c826d..8eed486234 100644 --- a/crates/client/src/rpc/impls/light.rs +++ b/crates/client/src/rpc/impls/light.rs @@ -1092,7 +1092,7 @@ impl RpcImpl { fn fee_history( &self, block_count: U64, newest_block: EpochNumber, - reward_percentiles: Vec, + reward_percentiles: Vec, ) -> RpcBoxFuture { info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", @@ -1239,7 +1239,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; - fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index daff44edff..bb96fd4687 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -204,7 +204,7 @@ pub trait Cfx { #[rpc(name = "cfx_feeHistory")] fn fee_history( &self, block_count: U64, newest_block: EpochNumber, - reward_percentiles: Vec, + reward_percentiles: Vec, ) -> BoxFuture; /// Check if user balance is enough for the transaction. diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 80fe7e50a0..f3b0bbc06c 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -77,7 +77,7 @@ pub trait Eth { #[rpc(name = "eth_feeHistory")] fn fee_history( &self, block_count: U64, newest_block: BlockNumber, - reward_percentiles: Vec, + reward_percentiles: Vec, ) -> Result; /// Returns accounts list. diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index dd2614274d..d706474220 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -1,6 +1,6 @@ use std::collections::VecDeque; -use cfx_types::{Space, SpaceMap, U256, U64}; +use cfx_types::{Space, SpaceMap, U256}; use primitives::{transaction::SignedTransaction, BlockHeader}; #[derive(Serialize, Debug, Default)] @@ -24,7 +24,7 @@ impl FeeHistory { pub fn reward(&self) -> &VecDeque> { &self.reward } pub fn push_back_block<'a, I>( - &mut self, space: Space, percentiles: &Vec, + &mut self, space: Space, percentiles: &Vec, pivot_header: &BlockHeader, transactions: I, ) -> Result<(), String> where @@ -83,7 +83,7 @@ impl FeeHistory { } fn compute_reward<'a, I>( - percentiles: &Vec, transactions: I, base_price: U256, + percentiles: &Vec, transactions: I, base_price: U256, ) -> Vec where I: Iterator { let mut rewards: Vec<_> = transactions @@ -105,7 +105,7 @@ where I: Iterator { percentiles .into_iter() .map(|per| { - let mut index = (*per).as_usize() * n / 100; + let mut index = (*per) as usize * n / 100; if index >= n { index = n - 1; } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 78957036a1..8e5600de1a 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -236,6 +236,7 @@ impl Encodable for Action { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct AccessListItem { pub address: Address, + #[serde(rename = "storageKeys")] pub storage_keys: Vec, } diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index c3596722e9..b3ff0e2abc 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -82,7 +82,7 @@ def run_test(self): assert_equal(ret1[0], ret2[0]) - fee_history = self.nodes[0].eth_feeHistory("0x5", "latest", ["0x15", "0x4b"]) + fee_history = self.nodes[0].eth_feeHistory("0x5", "latest", [21, 75]) assert_equal(len(fee_history['base_fee_per_gas']), 6) assert_equal(len(fee_history['gas_used_ratio']), 5) assert_equal(len(fee_history['reward']), 5) From ae5513e735fe6f997a5a770ec5233d908b203e73 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 23 May 2024 14:56:54 +0800 Subject: [PATCH 088/137] reorg eth rpc files folder --- crates/client/src/rpc/impls.rs | 6 +++--- crates/client/src/rpc/impls/{ => eth}/eth_filter.rs | 0 crates/client/src/rpc/impls/{eth.rs => eth/eth_handler.rs} | 3 --- crates/client/src/rpc/impls/{ => eth}/eth_pubsub.rs | 0 crates/client/src/rpc/impls/eth/mod.rs | 7 +++++++ 5 files changed, 10 insertions(+), 6 deletions(-) rename crates/client/src/rpc/impls/{ => eth}/eth_filter.rs (100%) rename crates/client/src/rpc/impls/{eth.rs => eth/eth_handler.rs} (99%) rename crates/client/src/rpc/impls/{ => eth}/eth_pubsub.rs (100%) create mode 100644 crates/client/src/rpc/impls/eth/mod.rs diff --git a/crates/client/src/rpc/impls.rs b/crates/client/src/rpc/impls.rs index 1ff6421866..3964207205 100644 --- a/crates/client/src/rpc/impls.rs +++ b/crates/client/src/rpc/impls.rs @@ -23,11 +23,11 @@ pub struct RpcImplConfiguration { pub mod cfx; pub mod cfx_filter; pub mod common; -pub mod eth; -pub mod eth_filter; -pub mod eth_pubsub; pub mod light; pub mod pool; pub mod pos; pub mod pubsub; pub mod trace; +pub mod eth; + +pub use eth::{eth_handler::EthHandler, eth_filter, eth_pubsub, debug}; diff --git a/crates/client/src/rpc/impls/eth_filter.rs b/crates/client/src/rpc/impls/eth/eth_filter.rs similarity index 100% rename from crates/client/src/rpc/impls/eth_filter.rs rename to crates/client/src/rpc/impls/eth/eth_filter.rs diff --git a/crates/client/src/rpc/impls/eth.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs similarity index 99% rename from crates/client/src/rpc/impls/eth.rs rename to crates/client/src/rpc/impls/eth/eth_handler.rs index 567b22991a..8f0d643583 100644 --- a/crates/client/src/rpc/impls/eth.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -48,9 +48,6 @@ use primitives::{ use rustc_hex::ToHex; use std::{cmp::min, convert::TryInto}; -mod debug; -pub use debug::GethDebugHandler; - pub struct EthHandler { config: RpcImplConfiguration, consensus: SharedConsensusGraph, diff --git a/crates/client/src/rpc/impls/eth_pubsub.rs b/crates/client/src/rpc/impls/eth/eth_pubsub.rs similarity index 100% rename from crates/client/src/rpc/impls/eth_pubsub.rs rename to crates/client/src/rpc/impls/eth/eth_pubsub.rs diff --git a/crates/client/src/rpc/impls/eth/mod.rs b/crates/client/src/rpc/impls/eth/mod.rs new file mode 100644 index 0000000000..dd1647a27e --- /dev/null +++ b/crates/client/src/rpc/impls/eth/mod.rs @@ -0,0 +1,7 @@ +pub mod eth_handler; +pub mod eth_filter; +pub mod eth_pubsub; +pub mod debug; + +pub use eth_handler::EthHandler; +pub use debug::GethDebugHandler; \ No newline at end of file From 3e0c056b7cac6fa2a2fc01cf6b0ec9b1dcb053a3 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 23 May 2024 15:16:12 +0800 Subject: [PATCH 089/137] move cfx space rpc into one folder --- crates/client/src/rpc/impls.rs | 10 +++------- crates/client/src/rpc/impls/{ => cfx}/cfx_filter.rs | 0 .../src/rpc/impls/{cfx.rs => cfx/cfx_handler.rs} | 0 crates/client/src/rpc/impls/{ => cfx}/common.rs | 0 crates/client/src/rpc/impls/{ => cfx}/light.rs | 0 crates/client/src/rpc/impls/cfx/mod.rs | 8 ++++++++ crates/client/src/rpc/impls/{ => cfx}/pool.rs | 0 crates/client/src/rpc/impls/{ => cfx}/pubsub.rs | 0 crates/client/src/rpc/impls/eth/mod.rs | 6 +++--- 9 files changed, 14 insertions(+), 10 deletions(-) rename crates/client/src/rpc/impls/{ => cfx}/cfx_filter.rs (100%) rename crates/client/src/rpc/impls/{cfx.rs => cfx/cfx_handler.rs} (100%) rename crates/client/src/rpc/impls/{ => cfx}/common.rs (100%) rename crates/client/src/rpc/impls/{ => cfx}/light.rs (100%) create mode 100644 crates/client/src/rpc/impls/cfx/mod.rs rename crates/client/src/rpc/impls/{ => cfx}/pool.rs (100%) rename crates/client/src/rpc/impls/{ => cfx}/pubsub.rs (100%) diff --git a/crates/client/src/rpc/impls.rs b/crates/client/src/rpc/impls.rs index 3964207205..92442ac1b4 100644 --- a/crates/client/src/rpc/impls.rs +++ b/crates/client/src/rpc/impls.rs @@ -21,13 +21,9 @@ pub struct RpcImplConfiguration { } pub mod cfx; -pub mod cfx_filter; -pub mod common; -pub mod light; -pub mod pool; +pub mod eth; pub mod pos; -pub mod pubsub; pub mod trace; -pub mod eth; -pub use eth::{eth_handler::EthHandler, eth_filter, eth_pubsub, debug}; +pub use cfx::{cfx_filter, common, light, pool, pubsub}; +pub use eth::{debug, eth_filter, eth_handler::EthHandler, eth_pubsub}; diff --git a/crates/client/src/rpc/impls/cfx_filter.rs b/crates/client/src/rpc/impls/cfx/cfx_filter.rs similarity index 100% rename from crates/client/src/rpc/impls/cfx_filter.rs rename to crates/client/src/rpc/impls/cfx/cfx_filter.rs diff --git a/crates/client/src/rpc/impls/cfx.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs similarity index 100% rename from crates/client/src/rpc/impls/cfx.rs rename to crates/client/src/rpc/impls/cfx/cfx_handler.rs diff --git a/crates/client/src/rpc/impls/common.rs b/crates/client/src/rpc/impls/cfx/common.rs similarity index 100% rename from crates/client/src/rpc/impls/common.rs rename to crates/client/src/rpc/impls/cfx/common.rs diff --git a/crates/client/src/rpc/impls/light.rs b/crates/client/src/rpc/impls/cfx/light.rs similarity index 100% rename from crates/client/src/rpc/impls/light.rs rename to crates/client/src/rpc/impls/cfx/light.rs diff --git a/crates/client/src/rpc/impls/cfx/mod.rs b/crates/client/src/rpc/impls/cfx/mod.rs new file mode 100644 index 0000000000..b5eeb7e2ab --- /dev/null +++ b/crates/client/src/rpc/impls/cfx/mod.rs @@ -0,0 +1,8 @@ +pub mod cfx_filter; +pub mod cfx_handler; +pub mod common; +pub mod light; +pub mod pool; +pub mod pubsub; + +pub use cfx_handler::{CfxHandler, LocalRpcImpl, RpcImpl, TestRpcImpl}; diff --git a/crates/client/src/rpc/impls/pool.rs b/crates/client/src/rpc/impls/cfx/pool.rs similarity index 100% rename from crates/client/src/rpc/impls/pool.rs rename to crates/client/src/rpc/impls/cfx/pool.rs diff --git a/crates/client/src/rpc/impls/pubsub.rs b/crates/client/src/rpc/impls/cfx/pubsub.rs similarity index 100% rename from crates/client/src/rpc/impls/pubsub.rs rename to crates/client/src/rpc/impls/cfx/pubsub.rs diff --git a/crates/client/src/rpc/impls/eth/mod.rs b/crates/client/src/rpc/impls/eth/mod.rs index dd1647a27e..b0ee412335 100644 --- a/crates/client/src/rpc/impls/eth/mod.rs +++ b/crates/client/src/rpc/impls/eth/mod.rs @@ -1,7 +1,7 @@ -pub mod eth_handler; +pub mod debug; pub mod eth_filter; +pub mod eth_handler; pub mod eth_pubsub; -pub mod debug; +pub use debug::GethDebugHandler; pub use eth_handler::EthHandler; -pub use debug::GethDebugHandler; \ No newline at end of file From 6b340465fcd2099338538100bdbffb7bad2a1310 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 23 May 2024 15:55:39 +0800 Subject: [PATCH 090/137] change rename method --- crates/primitives/src/transaction/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 8e5600de1a..f9f5a06e51 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -234,9 +234,9 @@ impl Encodable for Action { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct AccessListItem { pub address: Address, - #[serde(rename = "storageKeys")] pub storage_keys: Vec, } From 832a4acf075ff58dc8c423997b68441f769dbd6e Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 24 May 2024 15:06:11 +0800 Subject: [PATCH 091/137] change core space accessList item.address to base32 --- crates/client/src/rpc/types/call_request.rs | 31 +++++++++++++++++---- crates/primitives/src/lib.rs | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index b8f2a0abf0..ee34247ba2 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -12,7 +12,7 @@ use crate::rpc::{ RpcResult, }; use cfx_addr::Network; -use cfx_types::{Address, AddressSpaceUtil, U256, U64}; +use cfx_types::{Address, AddressSpaceUtil, H256, U256, U64}; use cfxcore::rpc_errors::invalid_params_check; use cfxcore_accounts::AccountProvider; use cfxkey::Password; @@ -20,9 +20,10 @@ use primitives::{ transaction::{ native_transaction::NativeTransaction as PrimitiveTransaction, Action, }, - AccessList, SignedTransaction, Transaction, TransactionWithSignature, + AccessList, AccessListItem, SignedTransaction, Transaction, + TransactionWithSignature, }; -use std::{cmp::min, sync::Arc}; +use std::{cmp::min, convert::From, sync::Arc}; // use serde_json::de::ParserNumber::U64; @@ -30,6 +31,24 @@ use std::{cmp::min, sync::Arc}; /// not too high that a call_virtual consumes too much resource. pub const MAX_GAS_CALL_REQUEST: u64 = 15_000_000; +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CoreAccessListItem { + pub address: RpcAddress, + pub storage_keys: Vec, +} + +pub type CoreAccessList = Vec; + +fn to_primitive_access_list(list: CoreAccessList) -> AccessList { + list.into_iter() + .map(|item| AccessListItem { + address: item.address.hex_address, + storage_keys: item.storage_keys, + }) + .collect() +} + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CallRequest { @@ -50,7 +69,7 @@ pub struct CallRequest { /// StorageLimit pub storage_limit: Option, /// Access list in EIP-2930 - pub access_list: Option, + pub access_list: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, #[serde(rename = "type")] @@ -214,7 +233,7 @@ pub fn sign_call( epoch_height, chain_id, data, - access_list, + access_list: to_primitive_access_list(access_list), }), 2 => Cip1559(Cip1559Transaction { nonce, @@ -227,7 +246,7 @@ pub fn sign_call( epoch_height, chain_id, data, - access_list, + access_list: to_primitive_access_list(access_list), }), x => { return Err( diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 3000d88009..3738b291d1 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -50,7 +50,7 @@ pub use crate::{ }, storage_key::*, transaction::{ - AccessList, Action, SignedTransaction, Transaction, + AccessList, AccessListItem, Action, SignedTransaction, Transaction, TransactionWithSignature, TransactionWithSignatureSerializePart, TxPropagateId, }, From cfd503a281ebba918788b4075dd8878946df702a Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 24 May 2024 15:09:48 +0800 Subject: [PATCH 092/137] use into trait --- crates/client/src/rpc/types/call_request.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index ee34247ba2..65d3335d2e 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -23,7 +23,7 @@ use primitives::{ AccessList, AccessListItem, SignedTransaction, Transaction, TransactionWithSignature, }; -use std::{cmp::min, convert::From, sync::Arc}; +use std::{cmp::min, convert::Into, sync::Arc}; // use serde_json::de::ParserNumber::U64; @@ -38,15 +38,19 @@ pub struct CoreAccessListItem { pub storage_keys: Vec, } +impl Into for CoreAccessListItem { + fn into(self) -> AccessListItem { + AccessListItem { + address: self.address.hex_address, + storage_keys: self.storage_keys, + } + } +} + pub type CoreAccessList = Vec; fn to_primitive_access_list(list: CoreAccessList) -> AccessList { - list.into_iter() - .map(|item| AccessListItem { - address: item.address.hex_address, - storage_keys: item.storage_keys, - }) - .collect() + list.into_iter().map(|item| item.into()).collect() } #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] From 13b67b26459789a6dd7cba230ab0851ab1132306 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Mon, 27 May 2024 15:02:07 +0800 Subject: [PATCH 093/137] Update version to v2.4.0-alpha. --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- crates/cfxcore/core/Cargo.toml | 2 +- crates/client/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a9b46912f..52909680a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,7 +1428,7 @@ dependencies = [ [[package]] name = "cfxcore" -version = "2.3.5" +version = "2.4.0-alpha" dependencies = [ "alloy-rpc-types-trace", "anyhow", @@ -1728,7 +1728,7 @@ dependencies = [ [[package]] name = "client" -version = "2.3.5" +version = "2.4.0-alpha" dependencies = [ "alloy-rpc-types-trace", "anyhow", @@ -1862,7 +1862,7 @@ dependencies = [ [[package]] name = "conflux" -version = "2.3.5" +version = "2.4.0-alpha" dependencies = [ "app_dirs", "base64ct", @@ -3595,7 +3595,7 @@ dependencies = [ [[package]] name = "geth-tracer" -version = "2.3.5" +version = "2.4.0-alpha" dependencies = [ "alloy-primitives", "alloy-rpc-types-trace", @@ -7311,7 +7311,7 @@ dependencies = [ [[package]] name = "serde-utils" -version = "2.3.5" +version = "2.4.0-alpha" dependencies = [ "alloy-primitives", "cfx-types", diff --git a/Cargo.toml b/Cargo.toml index 64c4e710eb..90ad0df903 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ members = [ resolver = "2" [workspace.package] -version = "2.3.5" +version = "2.4.0-alpha" authors = ["peilun-conflux", "ChenxingLi"] description = "A rust implementation of the Conflux-Protocol" documentation = "https://doc.confluxnetwork.org" diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index 0b8431567c..be7a43fd59 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -3,7 +3,7 @@ description = "Conflux core library" homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfxcore" -version = "2.3.5" +version = "2.4.0-alpha" edition = "2018" [dependencies] diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index bc88c68873..26701ac529 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "client" -version = "2.3.5" +version = "2.4.0-alpha" edition = "2018" [dependencies] From 3fcf87184bbf2c637882df8f500174fd0dcc32d5 Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 27 May 2024 18:08:49 +0800 Subject: [PATCH 094/137] update main crates rust-edition to 2021 --- bins/conflux/Cargo.toml | 2 +- crates/accounts/Cargo.toml | 2 +- crates/blockgen/Cargo.toml | 2 +- crates/cfx_utils/Cargo.toml | 2 +- crates/cfxcore/core/Cargo.toml | 2 +- crates/cfxcore/execute-helper/Cargo.toml | 2 +- crates/cfxcore/executor/Cargo.toml | 2 +- crates/cfxcore/internal_common/Cargo.toml | 2 +- crates/cfxcore/packing-pool/Cargo.toml | 2 +- crates/cfxcore/parameters/Cargo.toml | 2 +- crates/cfxcore/vm-interpreter/Cargo.toml | 2 +- crates/cfxcore/vm-types/Cargo.toml | 2 +- crates/client/Cargo.toml | 2 +- crates/dbs/db-errors/Cargo.toml | 2 +- crates/dbs/db/Cargo.toml | 2 +- crates/dbs/kvdb-rocksdb/Cargo.toml | 2 +- crates/dbs/statedb/Cargo.toml | 2 +- crates/dbs/storage/Cargo.toml | 2 +- crates/network/Cargo.toml | 2 +- crates/primitives/Cargo.toml | 2 +- crates/secret_store/Cargo.toml | 2 +- crates/stratum/Cargo.toml | 2 +- crates/transactiongen/Cargo.toml | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/bins/conflux/Cargo.toml b/bins/conflux/Cargo.toml index ce3ba6da1d..daba982097 100644 --- a/bins/conflux/Cargo.toml +++ b/bins/conflux/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "conflux" build = "build.rs" -edition = "2018" +edition = "2021" version.workspace = true authors.workspace = true description.workspace = true diff --git a/crates/accounts/Cargo.toml b/crates/accounts/Cargo.toml index 484fa2e865..4667293273 100644 --- a/crates/accounts/Cargo.toml +++ b/crates/accounts/Cargo.toml @@ -5,7 +5,7 @@ license = "GPL-3.0" name = "cfxcore-accounts" version = "0.1.0" authors = ["Conflux Foundation"] -edition = "2018" +edition = "2021" [dependencies] cfxkey = { path = "../cfx_key" } diff --git a/crates/blockgen/Cargo.toml b/crates/blockgen/Cargo.toml index dcc8811a44..ff0dab5b3e 100644 --- a/crates/blockgen/Cargo.toml +++ b/crates/blockgen/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "blockgen" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] clap = "2" diff --git a/crates/cfx_utils/Cargo.toml b/crates/cfx_utils/Cargo.toml index 8df8343618..9a25935810 100644 --- a/crates/cfx_utils/Cargo.toml +++ b/crates/cfx_utils/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-utils" version = "0.6.0" -edition = "2018" +edition = "2021" [dependencies] log = "0.4" diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index c8870c7b46..3ed8aeb7ff 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfxcore" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] bit-set = "0.4" diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index b1e52aa643..49861b8c54 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-execute-helper" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] # substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index 096f05d41a..b4b4d5797c 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-executor" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] substrate-bn = { git = "https://github.com/paritytech/bn", default-features = false, rev="63f8c587356a67b33c7396af98e065b66fca5dda" } diff --git a/crates/cfxcore/internal_common/Cargo.toml b/crates/cfxcore/internal_common/Cargo.toml index c5c9ba6186..34d0a56875 100644 --- a/crates/cfxcore/internal_common/Cargo.toml +++ b/crates/cfxcore/internal_common/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-internal-common" version = "1.0.0" -edition = "2018" +edition = "2021" [dependencies] cfx-bytes = { path = "../../cfx_bytes" } diff --git a/crates/cfxcore/packing-pool/Cargo.toml b/crates/cfxcore/packing-pool/Cargo.toml index 812a810b0a..32efa04bf6 100644 --- a/crates/cfxcore/packing-pool/Cargo.toml +++ b/crates/cfxcore/packing-pool/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cfx-packing-pool" version = "0.1.0" -edition = "2018" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/cfxcore/parameters/Cargo.toml b/crates/cfxcore/parameters/Cargo.toml index 69dac972b1..c92815550a 100644 --- a/crates/cfxcore/parameters/Cargo.toml +++ b/crates/cfxcore/parameters/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-parameters" version = "1.0.0" -edition = "2018" +edition = "2021" [dependencies] cfx-types = { path = "../../cfx_types" } diff --git a/crates/cfxcore/vm-interpreter/Cargo.toml b/crates/cfxcore/vm-interpreter/Cargo.toml index 07ab4e4b6b..a9758436cb 100644 --- a/crates/cfxcore/vm-interpreter/Cargo.toml +++ b/crates/cfxcore/vm-interpreter/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-interpreter" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] bit-set = "0.4" diff --git a/crates/cfxcore/vm-types/Cargo.toml b/crates/cfxcore/vm-types/Cargo.toml index 3d1d4b9c43..942c1e22fc 100644 --- a/crates/cfxcore/vm-types/Cargo.toml +++ b/crates/cfxcore/vm-types/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-vm-types" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] cfx-bytes = { path = "../../cfx_bytes" } diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index c8949ce51d..a515724f10 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "client" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] bigdecimal = "0.1.0" diff --git a/crates/dbs/db-errors/Cargo.toml b/crates/dbs/db-errors/Cargo.toml index eb3c3fdf25..8a5be605c1 100644 --- a/crates/dbs/db-errors/Cargo.toml +++ b/crates/dbs/db-errors/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-db-errors" version = "2.0.2" -edition = "2018" +edition = "2021" [dependencies] primitives = { path = "../../primitives" } diff --git a/crates/dbs/db/Cargo.toml b/crates/dbs/db/Cargo.toml index 741428a262..b44d9f2f8c 100644 --- a/crates/dbs/db/Cargo.toml +++ b/crates/dbs/db/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "db" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] log = "0.4" diff --git a/crates/dbs/kvdb-rocksdb/Cargo.toml b/crates/dbs/kvdb-rocksdb/Cargo.toml index fb2eb53475..32f843de8e 100644 --- a/crates/dbs/kvdb-rocksdb/Cargo.toml +++ b/crates/dbs/kvdb-rocksdb/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-common" description = "kvdb implementation backed by rocksDB" license = "GPL-3.0" -edition = "2018" +edition = "2021" [dependencies] cfx-types = { path = "../../cfx_types" } diff --git a/crates/dbs/statedb/Cargo.toml b/crates/dbs/statedb/Cargo.toml index 78e4a8ff48..d5024b8d8f 100644 --- a/crates/dbs/statedb/Cargo.toml +++ b/crates/dbs/statedb/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-statedb" version = "1.0.0" -edition = "2018" +edition = "2021" [dependencies] cfx-internal-common = { path = "../../cfxcore/internal_common" } diff --git a/crates/dbs/storage/Cargo.toml b/crates/dbs/storage/Cargo.toml index d533ff2c01..219092b067 100644 --- a/crates/dbs/storage/Cargo.toml +++ b/crates/dbs/storage/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfx-storage" version = "1.0.0" -edition = "2018" +edition = "2021" [dependencies] cfg-if = "0.1" diff --git a/crates/network/Cargo.toml b/crates/network/Cargo.toml index fcc5910fb7..682b073f01 100644 --- a/crates/network/Cargo.toml +++ b/crates/network/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "network" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] cfx-addr = { path = "../cfx_addr" } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 330dad862d..9cc7a5497f 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "primitives" version = "0.2.0" -edition = "2018" +edition = "2021" [dependencies] byteorder = "1.2.7" diff --git a/crates/secret_store/Cargo.toml b/crates/secret_store/Cargo.toml index e2a510a31b..8e8b1ddf93 100644 --- a/crates/secret_store/Cargo.toml +++ b/crates/secret_store/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "secret-store" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] cfx-types = { path = "../cfx_types" } diff --git a/crates/stratum/Cargo.toml b/crates/stratum/Cargo.toml index dbdf1ab411..b19ebad8ca 100644 --- a/crates/stratum/Cargo.toml +++ b/crates/stratum/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" name = "cfx-stratum" version = "1.12.0" license = "GPL-3.0" -edition = "2018" +edition = "2021" [dependencies] cfx-types = { path = "../cfx_types" } diff --git a/crates/transactiongen/Cargo.toml b/crates/transactiongen/Cargo.toml index 42bc5e8439..f483931279 100644 --- a/crates/transactiongen/Cargo.toml +++ b/crates/transactiongen/Cargo.toml @@ -4,7 +4,7 @@ homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "txgen" version = "0.1.0" -edition = "2018" +edition = "2021" [dependencies] clap = "2" From 76cfec74466cb396d8c208224ba242569f3c1552 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 27 May 2024 19:35:54 +0800 Subject: [PATCH 095/137] Fix block blaming on testnet v2.4.0 --- crates/client/src/configuration.rs | 3 +-- crates/primitives/src/receipt.rs | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index 55c947c225..ff1022459e 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -1432,8 +1432,7 @@ impl Configuration { params.transition_numbers.cancun_opcodes = self .raw_conf .cancun_opcodes_transition_number - .or(self.raw_conf.next_hardfork_transition_height) - // Don't enable by default, since the gas is changed + .or(self.raw_conf.next_hardfork_transition_number) .unwrap_or(default_transition_time); if params.transition_heights.cip1559 diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 6d771f2ed3..54ef026d9c 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -113,20 +113,6 @@ pub struct Receipt { pub burnt_gas_fee: Option, } -#[test] -fn tmp() { - let receipt = Receipt { - accumulated_gas_used: 189000.into(), - gas_fee: 60054.into(), - burnt_gas_fee: Some(30027.into()), - ..Default::default() - }; - dbg!(&receipt); - let x = receipt.rlp_bytes(); - let receipt2: Receipt = Rlp::new(&x).as_val().unwrap(); - assert_eq!(receipt2, receipt) -} - impl Encodable for Receipt { fn rlp_append(&self, s: &mut RlpStream) { let length = if self.burnt_gas_fee.is_none() { 9 } else { 10 }; @@ -244,3 +230,17 @@ fn test_transaction_outcome_rlp() { assert_eq!(rlp::encode(&TransactionStatus::Failure), rlp::encode(&1u8)); assert_eq!(rlp::encode(&TransactionStatus::Skipped), rlp::encode(&2u8)); } + +#[test] +fn test_receipt_rlp_serde() { + let mut receipt = Receipt { + accumulated_gas_used: 189000.into(), + gas_fee: 60054.into(), + burnt_gas_fee: Some(30027.into()), + ..Default::default() + }; + assert_eq!(receipt, Rlp::new(&receipt.rlp_bytes()).as_val().unwrap()); + + receipt.burnt_gas_fee = None; + assert_eq!(receipt, Rlp::new(&receipt.rlp_bytes()).as_val().unwrap()); +} From 16ab81da6eafb42ad8a6fe2ad4be207788497c73 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 28 May 2024 10:04:46 +0800 Subject: [PATCH 096/137] update toolchain to 1.77.2 to mitigate issue CVE-2024-24576 on windows --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 79e15fd493..369f9966f6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.77.0 +1.77.2 From 05331ea4dbe774762a106acd5c0e303a8f9875ae Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 28 May 2024 11:14:17 +0800 Subject: [PATCH 097/137] Fix incorrect parent on sync graph --- crates/cfxcore/core/src/sync/synchronization_graph.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 71eecc7f49..7c538801da 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -1763,9 +1763,14 @@ impl SynchronizationGraph { inner.arena[me].block_ready = true; if need_to_verify { + let parent_hash = inner.arena[me].block_header.parent_hash(); + let parent = self + .data_man + .block_header_by_hash(parent_hash) + .expect("parent not exist"); let r = self.verification_config.verify_sync_graph_block_basic( &block, - &inner.arena[me].block_header, + &*parent, self.consensus.best_chain_id(), ); match r { From 417eda39dbf1e07e9b29fdd776e504f20ef2de8a Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 28 May 2024 11:22:25 +0800 Subject: [PATCH 098/137] Add test --- tests/evm_space/eip1559_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index b3ff0e2abc..4db1624bd9 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -38,8 +38,8 @@ def run_test(self): # initialize EVM account self.evmAccount = self.w3.eth.account.privateKeyToAccount(self.DEFAULT_TEST_ACCOUNT_KEY) print(f'Using EVM account {self.evmAccount.address}') - self.cross_space_transfer(self.evmAccount.address, 1 * 10 ** 18) - assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(1 * 10 ** 18)) + self.cross_space_transfer(self.evmAccount.address, 100 * 10 ** 18) + assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(100 * 10 ** 18)) x = b32_address_to_hex("NET10:TYPE.USER:AAR8JZYBZV0FHZREAV49SYXNZUT8S0JT1ASMXX99XH") y = b32_address_to_hex('NET10:TYPE.BUILTIN:AAEJUAAAAAAAAAAAAAAAAAAAAAAAAAAAA27GYVFYR7') @@ -51,8 +51,8 @@ def run_test(self): "type": "0x2", "to": self.evmAccount.address, "value": 1, - "gas": 210000, - 'maxFeePerGas': 20 * (10**9), + "gas": 30000000, + 'maxFeePerGas': 200 * (10**9), 'maxPriorityFeePerGas': 1, "nonce": nonce, "chainId": 10, From 6189b5464436fc2610f1d7eb6e62d99153bad249 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Tue, 28 May 2024 12:44:53 +0800 Subject: [PATCH 099/137] Check cip1559 block base fee in verify_sync_graph_ready_block. --- .../core/src/sync/synchronization_graph.rs | 34 +++++++++++++++---- crates/cfxcore/core/src/verification.rs | 19 ++++++++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 7c538801da..09dfe6e7a5 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -859,6 +859,17 @@ impl SynchronizationGraphInner { Ok(()) } + fn verify_graph_ready_block( + &self, index: usize, verification_config: &VerificationConfig, + ) -> Result<(), Error> { + let parent = self.arena[self.arena[index].parent].block_header.as_ref(); + let block = self + .data_man + .block_by_hash(&self.arena[index].block_header.hash(), true) + .expect("received"); + verification_config.verify_sync_graph_ready_block(&block, parent) + } + fn process_invalid_blocks(&mut self, invalid_set: &HashSet) { for index in invalid_set { let hash = self.arena[*index].block_header.hash(); @@ -1705,6 +1716,23 @@ impl SynchronizationGraph { index, ); } else if inner.new_to_be_block_graph_ready(index) { + let verify_result = inner + .verify_graph_ready_block(index, &self.verification_config); + if verify_result.is_err() { + warn!( + "Invalid block! inserted_header={:?} err={:?}", + inner.arena[index].block_header.clone(), + verify_result + ); + invalid_set.insert(index); + inner.arena[index].graph_status = BLOCK_INVALID; + inner.set_and_propagate_invalid( + &mut queue, + &mut invalid_set, + index, + ); + continue; + } self.set_graph_ready(inner, index); for child in &inner.arena[index].children { debug_assert!( @@ -1763,14 +1791,8 @@ impl SynchronizationGraph { inner.arena[me].block_ready = true; if need_to_verify { - let parent_hash = inner.arena[me].block_header.parent_hash(); - let parent = self - .data_man - .block_header_by_hash(parent_hash) - .expect("parent not exist"); let r = self.verification_config.verify_sync_graph_block_basic( &block, - &*parent, self.consensus.best_chain_id(), ); match r { diff --git a/crates/cfxcore/core/src/verification.rs b/crates/cfxcore/core/src/verification.rs index 331bd46645..17f8d2adbe 100644 --- a/crates/cfxcore/core/src/verification.rs +++ b/crates/cfxcore/core/src/verification.rs @@ -464,13 +464,12 @@ impl VerificationConfig { /// should discard this block and all its descendants. #[inline] pub fn verify_sync_graph_block_basic( - &self, block: &Block, parent: &BlockHeader, chain_id: AllChainID, + &self, block: &Block, chain_id: AllChainID, ) -> Result<(), Error> { self.verify_block_integrity(block)?; let block_height = block.block_header.height(); - let mut total_gas: SpaceMap = SpaceMap::default(); let mut block_size = 0; let transitions = &self.machine.params().transition_heights; @@ -483,7 +482,6 @@ impl VerificationConfig { VerifyTxMode::Remote, )?; block_size += t.rlp_size(); - total_gas[t.space()] += *t.gas_limit(); } if block_size > self.max_block_size_in_bytes { @@ -495,13 +493,24 @@ impl VerificationConfig { }, ))); } + Ok(()) + } + + pub fn verify_sync_graph_ready_block( + &self, block: &Block, parent: &BlockHeader, + ) -> Result<(), Error> { + let mut total_gas: SpaceMap = SpaceMap::default(); + for t in &block.transactions { + total_gas[t.space()] += *t.gas_limit(); + } - if block_height >= self.machine.params().transition_heights.cip1559 { + if block.block_header.height() + >= self.machine.params().transition_heights.cip1559 + { self.check_base_fee(block, parent, total_gas)?; } else { self.check_hard_gas_limit(block, total_gas)?; } - Ok(()) } From 66833bf59b29ffbabcbdbad6c724fd83f7c06318 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Tue, 28 May 2024 13:30:03 +0800 Subject: [PATCH 100/137] Fix. --- crates/cfxcore/core/src/sync/synchronization_graph.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/cfxcore/core/src/sync/synchronization_graph.rs b/crates/cfxcore/core/src/sync/synchronization_graph.rs index 09dfe6e7a5..f4fa10cfd5 100644 --- a/crates/cfxcore/core/src/sync/synchronization_graph.rs +++ b/crates/cfxcore/core/src/sync/synchronization_graph.rs @@ -862,12 +862,16 @@ impl SynchronizationGraphInner { fn verify_graph_ready_block( &self, index: usize, verification_config: &VerificationConfig, ) -> Result<(), Error> { - let parent = self.arena[self.arena[index].parent].block_header.as_ref(); + let block_header = &self.arena[index].block_header; + let parent = self + .data_man + .block_header_by_hash(block_header.parent_hash()) + .expect("headers will not be deleted"); let block = self .data_man - .block_by_hash(&self.arena[index].block_header.hash(), true) + .block_by_hash(&block_header.hash(), true) .expect("received"); - verification_config.verify_sync_graph_ready_block(&block, parent) + verification_config.verify_sync_graph_ready_block(&block, &parent) } fn process_invalid_blocks(&mut self, invalid_set: &HashSet) { From c45d12cdf89127101d4a705881f8fac994df0691 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Tue, 28 May 2024 14:46:24 +0800 Subject: [PATCH 101/137] Fix log_filtering_test. --- tests/log_filtering_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/log_filtering_test.py b/tests/log_filtering_test.py index 54e11f5214..d092cd4c66 100755 --- a/tests/log_filtering_test.py +++ b/tests/log_filtering_test.py @@ -142,11 +142,11 @@ def run_test(self): parent_hash = self.rpc.block_by_epoch("latest_mined")['hash'] start_nonce = self.rpc.get_nonce(sender) - txs1 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + ii) for ii in range(0, NUM_CALLS)] + txs1 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + ii, gas=1_500_000) for ii in range(0, NUM_CALLS)] block_hash_1 = self.rpc.generate_custom_block(parent_hash = parent_hash, referee = [], txs = txs1) epoch_1 = self.rpc.block_by_hash(block_hash_1)["epochNumber"] - txs2 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + NUM_CALLS + ii) for ii in range(0, NUM_CALLS)] + txs2 = [self.rpc.new_contract_tx(receiver=contractAddr, data_hex=encode_hex_0x(keccak(b"bar()")), sender=sender, priv_key=priv_key, storage_limit=64, nonce = start_nonce + NUM_CALLS + ii, gas=1_500_000) for ii in range(0, NUM_CALLS)] block_hash_2 = self.rpc.generate_custom_block(parent_hash = block_hash_1, referee = [], txs = txs2) epoch_2 = self.rpc.block_by_hash(block_hash_2)["epochNumber"] From 8c393e488faf348d746fdbc7330ebc4792ab6d6b Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Tue, 28 May 2024 14:54:18 +0800 Subject: [PATCH 102/137] Fix ghast_consensus_test. --- crates/cfxcore/core/src/sync/utils.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/cfxcore/core/src/sync/utils.rs b/crates/cfxcore/core/src/sync/utils.rs index 6f0342a69b..7725f47ae0 100644 --- a/crates/cfxcore/core/src/sync/utils.rs +++ b/crates/cfxcore/core/src/sync/utils.rs @@ -42,7 +42,10 @@ use crate::{ verification::VerificationConfig, ConsensusGraph, NodeType, Notifications, TransactionPool, }; -use cfx_executor::machine::{new_machine_with_builtin, VmFactory}; +use cfx_executor::{ + machine::{new_machine_with_builtin, VmFactory}, + spec::CommonParams, +}; pub fn create_simple_block_impl( parent_hash: H256, ref_hashes: Vec, height: u64, nonce: U256, @@ -181,7 +184,9 @@ pub fn initialize_synchronization_graph_with_data_manager( data_man: Arc, beta: u64, h: u64, tcr: u64, tcb: u64, era_epoch_count: u64, pow: Arc, vm: VmFactory, ) -> (Arc, Arc) { - let machine = Arc::new(new_machine_with_builtin(Default::default(), vm)); + let mut params = CommonParams::default(); + params.transition_heights.cip1559 = u64::MAX; + let machine = Arc::new(new_machine_with_builtin(params, vm)); let mut rng = StdRng::from_seed([0u8; 32]); let pos_verifier = Arc::new(PosVerifier::new( None, From f90530573b7e57db49791f69dc0990c5be6d2917 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 29 May 2024 15:19:19 +0800 Subject: [PATCH 103/137] Fix packing issue for 1559 --- .../transaction_pool_inner.rs | 1 - tests/evm_space/eip1559_test.py | 85 +++++++++++++++---- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index b84ecc6172..be176bbc4a 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -1207,7 +1207,6 @@ impl TransactionPoolInner { tx_min_price, validity, ); - packed_transactions.extend_from_slice(&sampled_tx); // Recompute the base price, it should be <= estimated base price, // since the actual used gas is <= estimated limit diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 4db1624bd9..6e52c5fece 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -10,6 +10,7 @@ from conflux.rpc import RpcClient from web3 import Web3 +BASE_PRICE = 20 * (10 ** 9) class Eip1559Test(Web3Base): def set_test_params(self): self.num_nodes = 2 @@ -34,30 +35,45 @@ def setup_network(self): def run_test(self): self.cfxPrivkey = default_config['GENESIS_PRI_KEY'] self.cfxAccount = self.rpc.GENESIS_ADDR - print(f'Using Conflux account {self.cfxAccount}') + self.log.info(f'Using Conflux account {self.cfxAccount}') + # initialize EVM account self.evmAccount = self.w3.eth.account.privateKeyToAccount(self.DEFAULT_TEST_ACCOUNT_KEY) - print(f'Using EVM account {self.evmAccount.address}') + self.log.info(f'Using EVM account {self.evmAccount.address}') self.cross_space_transfer(self.evmAccount.address, 100 * 10 ** 18) assert_equal(self.nodes[0].eth_getBalance(self.evmAccount.address), hex(100 * 10 ** 18)) - x = b32_address_to_hex("NET10:TYPE.USER:AAR8JZYBZV0FHZREAV49SYXNZUT8S0JT1ASMXX99XH") - y = b32_address_to_hex('NET10:TYPE.BUILTIN:AAEJUAAAAAAAAAAAAAAAAAAAAAAAAAAAA27GYVFYR7') + # x = b32_address_to_hex("NET10:TYPE.USER:AAR8JZYBZV0FHZREAV49SYXNZUT8S0JT1ASMXX99XH") + # y = b32_address_to_hex('NET10:TYPE.BUILTIN:AAEJUAAAAAAAAAAAAAAAAAAAAAAAAAAAA27GYVFYR7') ret = self.nodes[0].debug_getTransactionsByEpoch("0x1") assert_equal(len(ret), 1) - nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + self.nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) + + tx, receipt = self.send_large_transaction() + self.check_node_sync(tx, receipt) + + tx, receipt = self.send_large_cheap_transactions() + self.check_node_sync(tx, receipt) + + tx, receipt = self.send_many_transactions_in_one_block() + self.check_node_sync(tx, receipt, tx_num = 10) + + self.check_fee_history() + + + def send_large_transaction(self): signed = self.evmAccount.signTransaction({ "type": "0x2", "to": self.evmAccount.address, "value": 1, "gas": 30000000, - 'maxFeePerGas': 200 * (10**9), + 'maxFeePerGas': 10 * BASE_PRICE, 'maxPriorityFeePerGas': 1, - "nonce": nonce, + "nonce": self.nonce, "chainId": 10, }) - self.log.info("Signed transaction %s", signed) + self.nonce += 1 return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) self.rpc.generate_block(1) @@ -70,18 +86,45 @@ def run_test(self): assert_equal(receipt["txExecErrorMsg"], None) tx = self.w3.eth.get_transaction(return_tx_hash) - self.log.info("Get transaction from node %s", tx) + + return tx, receipt + + def send_large_cheap_transactions(self): + for i in range(0, 5): + signed = self.evmAccount.signTransaction({ + "type": "0x2", + "to": self.evmAccount.address, + "value": 1, + "gas": 7_500_000, + 'maxFeePerGas': BASE_PRICE, + 'maxPriorityFeePerGas': 1, + "nonce": self.nonce + i, + "chainId": 10, + }) + return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + + self.nonce += 5 + + self.rpc.generate_blocks(20, 5) + receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) + assert_equal(receipt["status"], 1) + assert_equal(receipt["txExecErrorMsg"], None) + + tx = self.w3.eth.get_transaction(return_tx_hash) + return tx, receipt + + def check_node_sync(self, tx, receipt, tx_num = 1): # Check if another node can decode EIP1559 transactions sync_blocks(self.nodes) ret1 = self.nodes[0].debug_getTransactionsByEpoch(hex(receipt["blockNumber"])) ret2 = self.nodes[1].debug_getTransactionsByBlock(encode_hex_0x(tx["blockHash"])) - assert_equal(len(ret1), 1) - assert_equal(len(ret2), 1) + assert_equal(len(ret1), tx_num) + assert_equal(len(ret2), tx_num) assert_equal(ret1[0], ret2[0]) - + def check_fee_history(self): fee_history = self.nodes[0].eth_feeHistory("0x5", "latest", [21, 75]) assert_equal(len(fee_history['base_fee_per_gas']), 6) assert_equal(len(fee_history['gas_used_ratio']), 5) @@ -89,26 +132,32 @@ def run_test(self): assert_greater_than(int(self.nodes[0].cfx_getFeeBurnt(), 0), 0) - nonce = self.w3.eth.getTransactionCount(self.evmAccount.address) - for i in range(1, 10): + def send_many_transactions_in_one_block(self): + for i in range(0, 10): signed = self.evmAccount.signTransaction({ "type": "0x2", "to": self.evmAccount.address, "value": 1, "gas": 21000, - 'maxFeePerGas': 20* (10**9), + 'maxFeePerGas': BASE_PRICE, 'maxPriorityFeePerGas': 1, - "nonce": i, + "nonce": self.nonce + i, "chainId": 10, }) return_tx_hash = self.w3.eth.sendRawTransaction(signed["rawTransaction"]) + self.nonce += 10 + self.rpc.generate_block(10) - self.rpc.generate_blocks(20, 1) + self.rpc.generate_blocks(20, 0) receipt = self.w3.eth.waitForTransactionReceipt(return_tx_hash) - assert_equal(receipt["cumulativeGasUsed"], 21000 * 9) + assert_equal(receipt["cumulativeGasUsed"], 21000 * 10) assert_equal(receipt["gasUsed"], 21000) assert_equal(self.w3.eth.estimate_gas({"to": self.evmAccount.address}), 21000) + tx = self.w3.eth.get_transaction(return_tx_hash) + return tx, receipt + + if __name__ == "__main__": Eip1559Test().main() \ No newline at end of file From dfc5f27de85d943d5ac19731b4a904a47b3cfff2 Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 29 May 2024 11:37:01 +0800 Subject: [PATCH 104/137] Fix error in packing pool --- .../transaction_pool_inner.rs | 78 ++++++++++++++++--- crates/cfxcore/packing-pool/src/pool.rs | 19 +++++ crates/primitives/src/block_header.rs | 5 ++ 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index be176bbc4a..28a274e0d2 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -111,7 +111,7 @@ impl DeferredPool { &self, space: Space, gas_target: U256, parent_base_price: U256, min_base_price: U256, ) -> (U256, U256) { - let packing_gas_limit = self + let estimated_gas_limit = self .packing_pool .in_space(space) .estimate_packing_gas_limit( @@ -119,6 +119,7 @@ impl DeferredPool { parent_base_price, min_base_price, ); + let packing_gas_limit = U256::min(gas_target * 2, estimated_gas_limit); let price_limit = compute_next_price( gas_target, packing_gas_limit, @@ -171,7 +172,6 @@ impl DeferredPool { { 'sender: for tx in sender_txs.iter() { if tx.gas_price() < &tx_min_price { - to_drop_txs.push(tx.clone()); break 'sender; } match validity(&*tx) { @@ -1132,6 +1132,11 @@ impl TransactionPoolInner { return (packed_transactions, parent_base_price); } + debug!( + "Packing transaction for 1559, parent base price {:?}", + parent_base_price + ); + let mut block_base_price = parent_base_price.clone(); let spec = machine.spec(best_block_number, best_epoch_height); @@ -1155,32 +1160,59 @@ impl TransactionPoolInner { let min_base_price = machine.params().min_base_price()[Space::Ethereum]; - let (packing_gas_limit, base_price) = + let (packing_gas_limit, tx_min_price) = self.deferred_pool.estimate_packing_gas_limit( Space::Ethereum, gas_target, parent_base_price, min_base_price, ); + debug!( + "Packing plan (espace): gas limit: {:?}, tx min price: {:?}", + packing_gas_limit, tx_min_price + ); let (sampled_tx, used_gas, used_size) = self.deferred_pool.packing_sampler( Space::Ethereum, packing_gas_limit, block_size_limit, num_txs, - base_price, + tx_min_price, validity, ); - packed_transactions.extend_from_slice(&sampled_tx); + // Recompute the base price, it should be <= estimated base price, // since the actual used gas is <= estimated limit - block_base_price[Space::Ethereum] = compute_next_price( + let base_price = compute_next_price( gas_target, used_gas, parent_base_price, min_base_price, ); - (sampled_tx.len(), used_size) + + if base_price <= tx_min_price { + debug!( + "Packing result (espace): gas used: {:?}, base price: {:?}", + used_gas, base_price + ); + block_base_price[Space::Ethereum] = base_price; + packed_transactions.extend_from_slice(&sampled_tx); + + (sampled_tx.len(), used_size) + } else { + // Should be unreachable + warn!( + "Inconsistent packing result (espace): gas used: {:?}, base price: {:?}", + used_gas, base_price + ); + block_base_price[Space::Ethereum] = compute_next_price( + gas_target, + U256::zero(), + parent_base_price, + min_base_price, + ); + (0, 0) + } } else { (0, 0) }; @@ -1191,7 +1223,7 @@ impl TransactionPoolInner { let min_base_price = machine.params().min_base_price()[Space::Native]; - let (packed_limit, tx_min_price) = + let (packing_gas_limit, tx_min_price) = self.deferred_pool.estimate_packing_gas_limit( Space::Native, gas_target, @@ -1199,9 +1231,14 @@ impl TransactionPoolInner { min_base_price, ); + debug!( + "Packing plan (core space): gas limit: {:?}, tx min price: {:?}", + packing_gas_limit, tx_min_price + ); + let (sampled_tx, used_gas, _) = self.deferred_pool.packing_sampler( Space::Native, - packed_limit, + packing_gas_limit, block_size_limit - evm_used_size, num_txs - evm_packed_tx_num, tx_min_price, @@ -1210,12 +1247,33 @@ impl TransactionPoolInner { // Recompute the base price, it should be <= estimated base price, // since the actual used gas is <= estimated limit - block_base_price[Space::Native] = compute_next_price( + let base_price = compute_next_price( gas_target, used_gas, parent_base_price, min_base_price, ); + + if base_price <= tx_min_price { + debug!( + "Packing result (core space): gas used: {:?}, base price: {:?}", + used_gas, base_price + ); + block_base_price[Space::Native] = base_price; + packed_transactions.extend_from_slice(&sampled_tx); + } else { + // Should be unreachable + warn!( + "Inconsistent packing result (core space): gas used: {:?}, base price: {:?}", + used_gas, base_price + ); + block_base_price[Space::Native] = compute_next_price( + gas_target, + U256::zero(), + parent_base_price, + min_base_price, + ); + } } if log::max_level() >= log::Level::Debug { diff --git a/crates/cfxcore/packing-pool/src/pool.rs b/crates/cfxcore/packing-pool/src/pool.rs index 7cca4e7515..5c9e7c6a18 100644 --- a/crates/cfxcore/packing-pool/src/pool.rs +++ b/crates/cfxcore/packing-pool/src/pool.rs @@ -352,6 +352,25 @@ mod pool_tests { } } + #[allow(dead_code)] + fn same_price_txs() -> PackingPool { + let config = PackingPoolConfig::new_for_test(); + let mut pool = PackingPool::new(config); + + static ID: AtomicUsize = AtomicUsize::new(0); + for i in 1000..2000 { + let (_, res) = pool.insert(MockTransaction { + sender: i, + nonce: 0, + gas_price: 20, + gas_limit: 1, + id: ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst), + }); + res.unwrap(); + } + pool + } + #[test] fn test_split_in_middle() { let mut pool = default_pool(5, 10); diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index ef71eb439a..6054884843 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -9,6 +9,7 @@ use crate::{ use cfx_types::{ Address, Bloom, Space, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256, }; +use log::warn; use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps}; use once_cell::sync::OnceCell; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -692,6 +693,10 @@ pub fn compute_next_price( ) -> U256 { const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; + if gas_actual > gas_target * 2 { + warn!("gas target is larger than expected"); + } + let next_base_price = if gas_target.is_zero() || gas_target == gas_actual { last_base_price } else if gas_actual > gas_target { From 83c62995c8ed98755d5f7e2fc0c82152d075015d Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Wed, 29 May 2024 16:52:45 +0800 Subject: [PATCH 105/137] Add more tests --- .../cfxcore/core/src/transaction_pool/mod.rs | 16 +- .../transaction_pool_inner.rs | 209 +++++++++++++++--- 2 files changed, 194 insertions(+), 31 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index e83ea75733..ef3878370f 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -745,15 +745,27 @@ impl TransactionPool { best_epoch_height += 1; // The best block number is not necessary an exact number. best_block_number += 1; + + let spec = self.machine.spec(best_block_number, best_epoch_height); + let transitions = &self.machine.params().transition_heights; + + let validity = |tx: &SignedTransaction| { + self.verification_config.fast_recheck( + tx, + best_epoch_height, + transitions, + &spec, + ) + }; + inner.pack_transactions_1559( num_txs, block_gas_limit, parent_base_price, block_size_limit, best_epoch_height, - best_block_number, - &self.verification_config, &self.machine, + validity, ) } diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 28a274e0d2..628110ae65 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -592,6 +592,9 @@ impl TransactionPoolInner { } } + #[cfg(test)] + pub fn new_for_test() -> Self { Self::new(50_000, 3_000_000, 50, 4) } + pub fn clear(&mut self) { self.deferred_pool.clear(); self.ready_nonces_and_balances.clear(); @@ -793,6 +796,19 @@ impl TransactionPoolInner { pub fn capacity(&self) -> usize { self.capacity } + #[cfg(test)] + fn insert_transaction_for_test( + &mut self, transaction: Arc, sender_nonce: U256, + ) -> InsertResult { + self.insert_transaction_without_readiness_check( + transaction, + false, + true, + (sender_nonce, U256::from(u64::MAX)), + (0.into(), 0), + ) + } + // the new inserting will fail if tx_pool is full (even if `force` is true) fn insert_transaction_without_readiness_check( &mut self, transaction: Arc, packed: bool, @@ -1124,8 +1140,8 @@ impl TransactionPoolInner { pub fn pack_transactions_1559<'a>( &mut self, num_txs: usize, block_gas_limit: U256, parent_base_price: SpaceMap, block_size_limit: usize, - best_epoch_height: u64, best_block_number: u64, - verification_config: &VerificationConfig, machine: &Machine, + best_epoch_height: u64, machine: &Machine, + validity: impl Fn(&SignedTransaction) -> PackingCheckResult, ) -> (Vec>, SpaceMap) { let mut packed_transactions: Vec> = Vec::new(); if num_txs == 0 { @@ -1139,18 +1155,6 @@ impl TransactionPoolInner { let mut block_base_price = parent_base_price.clone(); - let spec = machine.spec(best_block_number, best_epoch_height); - let transitions = &machine.params().transition_heights; - - let validity = |tx: &SignedTransaction| { - verification_config.fast_recheck( - tx, - best_epoch_height, - transitions, - &spec, - ) - }; - let can_pack_evm = machine.params().can_pack_evm_transaction(best_epoch_height); @@ -1178,7 +1182,7 @@ impl TransactionPoolInner { block_size_limit, num_txs, tx_min_price, - validity, + &validity, ); // Recompute the base price, it should be <= estimated base price, @@ -1242,7 +1246,7 @@ impl TransactionPoolInner { block_size_limit - evm_used_size, num_txs - evm_packed_tx_num, tx_min_price, - validity, + &validity, ); // Recompute the base price, it should be <= estimated base price, @@ -1504,40 +1508,66 @@ impl TransactionPoolInner { } #[cfg(test)] -mod test_transaction_pool_inner { - use super::{DeferredPool, InsertResult, TxWithReadyInfo}; - use cfx_types::{Address, AddressSpaceUtil, U256}; +mod tests { + use crate::verification::PackingCheckResult; + + use super::{ + DeferredPool, InsertResult, TransactionPoolInner, TxWithReadyInfo, + }; + use cfx_executor::{ + machine::{new_machine, Machine, VmFactory}, + spec::CommonParams, + }; + use cfx_types::{Address, AddressSpaceUtil, Space, SpaceMap, U256}; + use itertools::Itertools; use keylib::{Generator, KeyPair, Random}; use primitives::{ - transaction::native_transaction::NativeTransaction, Action, - SignedTransaction, Transaction, + block_header::compute_next_price_tuple, + transaction::{ + native_transaction::NativeTransaction, Eip155Transaction, + }, + Action, SignedTransaction, Transaction, }; use std::sync::Arc; fn new_test_tx( - sender: &KeyPair, nonce: usize, gas_price: usize, value: usize, + sender: &KeyPair, nonce: usize, gas_price: usize, gas: usize, + value: usize, space: Space, ) -> Arc { - Arc::new( - Transaction::from(NativeTransaction { + let tx: Transaction = match space { + Space::Native => NativeTransaction { nonce: U256::from(nonce), gas_price: U256::from(gas_price), - gas: U256::from(50000), + gas: U256::from(gas), action: Action::Call(Address::random()), value: U256::from(value), storage_limit: 0, epoch_height: 0, chain_id: 1, data: Vec::new(), - }) - .sign(sender.secret()), - ) + } + .into(), + Space::Ethereum => Eip155Transaction { + nonce: U256::from(nonce), + gas_price: U256::from(gas_price), + gas: U256::from(gas), + action: Action::Call(Address::random()), + value: U256::from(value), + chain_id: Some(1), + data: Vec::new(), + } + .into(), + }; + Arc::new(tx.sign(sender.secret())) } fn new_test_tx_with_read_info( sender: &KeyPair, nonce: usize, gas_price: usize, value: usize, packed: bool, ) -> TxWithReadyInfo { - let transaction = new_test_tx(sender, nonce, gas_price, value); + let gas = 50000; + let transaction = + new_test_tx(sender, nonce, gas_price, gas, value, Space::Native); TxWithReadyInfo::new(transaction, packed, U256::from(0), 0) } @@ -1759,4 +1789,125 @@ mod test_transaction_pool_inner { None ); } + + fn pack_transactions_1559_checked( + pool: &mut TransactionPoolInner, machine: &Machine, + ) { + let parent_base_price = SpaceMap::new(100, 100).map_all(U256::from); + let block_gas_limit = U256::from(6000); + let best_epoch_height = 20; + + let (txs, base_price) = pool.pack_transactions_1559( + usize::MAX, + block_gas_limit, + parent_base_price, + usize::MAX, + best_epoch_height, + machine, + |_| PackingCheckResult::Pack, + ); + + let params = machine.params(); + + let core_gas_limit = block_gas_limit * 9 / 10; + let eth_gas_limit = + if params.can_pack_evm_transaction(best_epoch_height) { + block_gas_limit * 5 / 10 + } else { + U256::zero() + }; + + let gas_target = + SpaceMap::new(core_gas_limit, eth_gas_limit).map_all(|x| x / 2); + + let mut gas_used = SpaceMap::default(); + let mut min_gas_price = + SpaceMap::new(U256::max_value(), U256::max_value()); + + for tx in txs { + gas_used[tx.space()] += *tx.gas_limit(); + min_gas_price[tx.space()] = + min_gas_price[tx.space()].min(*tx.gas_limit()); + } + + let min_base_price = params.min_base_price(); + + let expected_base_price = SpaceMap::zip4( + gas_target, + gas_used, + parent_base_price, + min_base_price, + ) + .map_all(compute_next_price_tuple); + + assert_eq!(expected_base_price, base_price); + assert!(gas_used[Space::Native] <= core_gas_limit); + assert!(gas_used[Space::Ethereum] <= eth_gas_limit); + + for space in [Space::Native, Space::Ethereum] { + assert!(base_price[space] <= min_gas_price[space]); + } + } + + #[test] + fn test_eip1559_transactions() { + let mut pool = TransactionPoolInner::new_for_test(); + + let mut params = CommonParams::default(); + params.min_base_price = SpaceMap::new(100, 200).map_all(U256::from); + + let machine = Arc::new(new_machine(params, VmFactory::default())); + + let test_block_limit = SpaceMap::new(5400, 3000); + + let senders: Vec<_> = (0..20) + .into_iter() + .map(|_| Random.generate().unwrap()) + .collect(); + + let tasks = [1, 2, 3] + .into_iter() + .cartesian_product( + /* gas_price */ [50usize, 95, 100, 105, 150, 1000], + ) + .cartesian_product( + /* gas_limit_percent */ [5usize, 10, 40, 60, 100], + ) + .cartesian_product(/* price_increasing */ [0usize, 1]); + + for (((space_bits, gas_price), gas_limit_percent), price_inc) in tasks { + let tx_gas_limit = + test_block_limit.map_all(|x| x * gas_limit_percent / 100); + + for (idx, sender) in senders.iter().enumerate() { + let gas_price = gas_price + idx * price_inc; + + if space_bits & 0x1 != 0 { + let tx = new_test_tx( + sender, + 0, + gas_price, + tx_gas_limit[Space::Native], + 0, + Space::Native, + ); + pool.insert_transaction_for_test(tx, U256::zero()); + } + + if space_bits & 0x2 != 0 { + let tx = new_test_tx( + sender, + 0, + gas_price * 2, + tx_gas_limit[Space::Ethereum], + 0, + Space::Ethereum, + ); + pool.insert_transaction_for_test(tx, U256::zero()); + } + } + pack_transactions_1559_checked(&mut pool, &machine); + pool.clear(); + } + } } From e916dcf466ff1765f805091e99861f66988a3706 Mon Sep 17 00:00:00 2001 From: dayong Date: Wed, 29 May 2024 17:05:36 +0800 Subject: [PATCH 106/137] add params name for eth rpc methods --- crates/client/src/rpc/traits/eth_space/eth.rs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 80fe7e50a0..10b7022a08 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -90,7 +90,7 @@ pub trait Eth { /// Returns balance of the given account. #[rpc(name = "eth_getBalance")] - fn balance(&self, _: H160, _: Option) -> Result; + fn balance(&self, address: H160, block: Option) -> Result; // /// Returns the account- and storage-values of the specified account // including the Merkle-proof #[rpc(name = "eth_getProof")] @@ -100,97 +100,97 @@ pub trait Eth { /// Returns content of the storage at given address. #[rpc(name = "eth_getStorageAt")] fn storage_at( - &self, _: H160, _: U256, _: Option, + &self, address: H160, storage_slot: U256, block: Option, ) -> jsonrpc_core::Result; /// Returns block with given hash. #[rpc(name = "eth_getBlockByHash")] - fn block_by_hash(&self, _: H256, _: bool) -> Result>; + fn block_by_hash(&self, block_hash: H256, hydrated_transactions: bool) -> Result>; /// Returns block with given number. #[rpc(name = "eth_getBlockByNumber")] - fn block_by_number(&self, _: BlockNumber, _: bool) + fn block_by_number(&self, block: BlockNumber, hydrated_transactions: bool) -> Result>; /// Returns the number of transactions sent from given address at given time /// (block number). #[rpc(name = "eth_getTransactionCount")] fn transaction_count( - &self, _: H160, _: Option, + &self, address: H160, block: Option, ) -> Result; /// Returns the number of transactions in a block with given hash. #[rpc(name = "eth_getBlockTransactionCountByHash")] - fn block_transaction_count_by_hash(&self, _: H256) -> Result>; + fn block_transaction_count_by_hash(&self, block_hash: H256) -> Result>; /// Returns the number of transactions in a block with given block number. #[rpc(name = "eth_getBlockTransactionCountByNumber")] fn block_transaction_count_by_number( - &self, _: BlockNumber, + &self, block: BlockNumber, ) -> Result>; /// Returns the number of uncles in a block with given hash. #[rpc(name = "eth_getUncleCountByBlockHash")] - fn block_uncles_count_by_hash(&self, _: H256) -> Result>; + fn block_uncles_count_by_hash(&self, block_hash: H256) -> Result>; /// Returns the number of uncles in a block with given block number. #[rpc(name = "eth_getUncleCountByBlockNumber")] fn block_uncles_count_by_number( - &self, _: BlockNumber, + &self, block: BlockNumber, ) -> Result>; /// Returns the code at given address at given time (block number). #[rpc(name = "eth_getCode")] - fn code_at(&self, _: H160, _: Option) -> Result; + fn code_at(&self, address: H160, block: Option) -> Result; /// Sends signed transaction, returning its hash. #[rpc(name = "eth_sendRawTransaction")] - fn send_raw_transaction(&self, _: Bytes) -> Result; + fn send_raw_transaction(&self, transaction: Bytes) -> Result; /// @alias of `eth_sendRawTransaction`. #[rpc(name = "eth_submitTransaction")] - fn submit_transaction(&self, _: Bytes) -> Result; + fn submit_transaction(&self, transaction: Bytes) -> Result; /// Call contract, returning the output data. #[rpc(name = "eth_call")] - fn call(&self, _: CallRequest, _: Option) -> Result; + fn call(&self, transaction: CallRequest, block: Option) -> Result; /// Estimate gas needed for execution of given contract. #[rpc(name = "eth_estimateGas")] fn estimate_gas( - &self, _: CallRequest, _: Option, + &self, transaction: CallRequest, block: Option, ) -> Result; /// Get transaction by its hash. #[rpc(name = "eth_getTransactionByHash")] - fn transaction_by_hash(&self, _: H256) -> Result>; + fn transaction_by_hash(&self, transaction_hash: H256) -> Result>; /// Returns transaction at given block hash and index. #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] fn transaction_by_block_hash_and_index( - &self, _: H256, _: Index, + &self, block_hash: H256, transaction_index: Index, ) -> Result>; /// Returns transaction by given block number and index. #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] fn transaction_by_block_number_and_index( - &self, _: BlockNumber, _: Index, + &self, block: BlockNumber, transaction_index: Index, ) -> Result>; /// Returns transaction receipt by transaction hash. #[rpc(name = "eth_getTransactionReceipt")] - fn transaction_receipt(&self, _: H256) -> Result>; + fn transaction_receipt(&self, transaction_hash: H256) -> Result>; /// Returns an uncles at given block and index. #[rpc(name = "eth_getUncleByBlockHashAndIndex")] fn uncle_by_block_hash_and_index( - &self, _: H256, _: Index, + &self, block_hash: H256, _: Index, ) -> Result>; /// Returns an uncles at given block and index. #[rpc(name = "eth_getUncleByBlockNumberAndIndex")] fn uncle_by_block_number_and_index( - &self, _: BlockNumber, _: Index, + &self, block: BlockNumber, _: Index, ) -> Result>; // /// Returns available compilers. @@ -215,7 +215,7 @@ pub trait Eth { /// Returns logs matching given filter object. #[rpc(name = "eth_getLogs")] - fn logs(&self, _: EthRpcLogFilter) -> Result>; + fn logs(&self, filter: EthRpcLogFilter) -> Result>; // /// Returns the hash of the current block, the seedHash, and the boundary // condition to be met. #[rpc(name = "eth_getWork")] @@ -230,11 +230,11 @@ pub trait Eth { fn submit_hashrate(&self, _: U256, _: H256) -> Result; #[rpc(name = "parity_getBlockReceipts")] - fn block_receipts(&self, _: Option) -> Result>; + fn block_receipts(&self, block: Option) -> Result>; #[rpc(name = "eth_getAccountPendingTransactions")] fn account_pending_transactions( - &self, _: H160, maybe_start_nonce: Option, + &self, address: H160, maybe_start_nonce: Option, maybe_limit: Option, ) -> Result; } @@ -244,7 +244,7 @@ pub trait Eth { pub trait EthFilter { /// Returns id of new filter. #[rpc(name = "eth_newFilter")] - fn new_filter(&self, _: EthRpcLogFilter) -> Result; + fn new_filter(&self, filter: EthRpcLogFilter) -> Result; /// Returns id of new block filter. #[rpc(name = "eth_newBlockFilter")] @@ -256,13 +256,13 @@ pub trait EthFilter { /// Returns filter changes since last poll. #[rpc(name = "eth_getFilterChanges")] - fn filter_changes(&self, _: H128) -> Result; + fn filter_changes(&self, identifier: H128) -> Result; /// Returns all logs matching given filter (in a range 'from' - 'to'). #[rpc(name = "eth_getFilterLogs")] - fn filter_logs(&self, _: H128) -> Result>; + fn filter_logs(&self, identifier: H128) -> Result>; /// Uninstalls filter. #[rpc(name = "eth_uninstallFilter")] - fn uninstall_filter(&self, _: H128) -> Result; + fn uninstall_filter(&self, identifier: H128) -> Result; } From e73e199779ff304912256b1ebb4610e50f07868d Mon Sep 17 00:00:00 2001 From: dayong Date: Thu, 30 May 2024 10:48:30 +0800 Subject: [PATCH 107/137] fmt --- crates/client/src/rpc/traits/eth_space/eth.rs | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 10b7022a08..71b224bb75 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -90,7 +90,9 @@ pub trait Eth { /// Returns balance of the given account. #[rpc(name = "eth_getBalance")] - fn balance(&self, address: H160, block: Option) -> Result; + fn balance( + &self, address: H160, block: Option, + ) -> Result; // /// Returns the account- and storage-values of the specified account // including the Merkle-proof #[rpc(name = "eth_getProof")] @@ -105,12 +107,15 @@ pub trait Eth { /// Returns block with given hash. #[rpc(name = "eth_getBlockByHash")] - fn block_by_hash(&self, block_hash: H256, hydrated_transactions: bool) -> Result>; + fn block_by_hash( + &self, block_hash: H256, hydrated_transactions: bool, + ) -> Result>; /// Returns block with given number. #[rpc(name = "eth_getBlockByNumber")] - fn block_by_number(&self, block: BlockNumber, hydrated_transactions: bool) - -> Result>; + fn block_by_number( + &self, block: BlockNumber, hydrated_transactions: bool, + ) -> Result>; /// Returns the number of transactions sent from given address at given time /// (block number). @@ -121,7 +126,9 @@ pub trait Eth { /// Returns the number of transactions in a block with given hash. #[rpc(name = "eth_getBlockTransactionCountByHash")] - fn block_transaction_count_by_hash(&self, block_hash: H256) -> Result>; + fn block_transaction_count_by_hash( + &self, block_hash: H256, + ) -> Result>; /// Returns the number of transactions in a block with given block number. #[rpc(name = "eth_getBlockTransactionCountByNumber")] @@ -131,7 +138,9 @@ pub trait Eth { /// Returns the number of uncles in a block with given hash. #[rpc(name = "eth_getUncleCountByBlockHash")] - fn block_uncles_count_by_hash(&self, block_hash: H256) -> Result>; + fn block_uncles_count_by_hash( + &self, block_hash: H256, + ) -> Result>; /// Returns the number of uncles in a block with given block number. #[rpc(name = "eth_getUncleCountByBlockNumber")] @@ -141,7 +150,9 @@ pub trait Eth { /// Returns the code at given address at given time (block number). #[rpc(name = "eth_getCode")] - fn code_at(&self, address: H160, block: Option) -> Result; + fn code_at( + &self, address: H160, block: Option, + ) -> Result; /// Sends signed transaction, returning its hash. #[rpc(name = "eth_sendRawTransaction")] @@ -153,7 +164,9 @@ pub trait Eth { /// Call contract, returning the output data. #[rpc(name = "eth_call")] - fn call(&self, transaction: CallRequest, block: Option) -> Result; + fn call( + &self, transaction: CallRequest, block: Option, + ) -> Result; /// Estimate gas needed for execution of given contract. #[rpc(name = "eth_estimateGas")] @@ -163,7 +176,9 @@ pub trait Eth { /// Get transaction by its hash. #[rpc(name = "eth_getTransactionByHash")] - fn transaction_by_hash(&self, transaction_hash: H256) -> Result>; + fn transaction_by_hash( + &self, transaction_hash: H256, + ) -> Result>; /// Returns transaction at given block hash and index. #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] @@ -179,7 +194,9 @@ pub trait Eth { /// Returns transaction receipt by transaction hash. #[rpc(name = "eth_getTransactionReceipt")] - fn transaction_receipt(&self, transaction_hash: H256) -> Result>; + fn transaction_receipt( + &self, transaction_hash: H256, + ) -> Result>; /// Returns an uncles at given block and index. #[rpc(name = "eth_getUncleByBlockHashAndIndex")] @@ -230,7 +247,9 @@ pub trait Eth { fn submit_hashrate(&self, _: U256, _: H256) -> Result; #[rpc(name = "parity_getBlockReceipts")] - fn block_receipts(&self, block: Option) -> Result>; + fn block_receipts( + &self, block: Option, + ) -> Result>; #[rpc(name = "eth_getAccountPendingTransactions")] fn account_pending_transactions( From 706558613bbecefe862a9724fff17d6ddd90e93a Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 29 May 2024 17:24:08 +0800 Subject: [PATCH 108/137] use const replace literal small op --- .../cfxcore/core/src/transaction_pool/mod.rs | 5 +- .../client/src/rpc/impls/cfx/cfx_handler.rs | 17 +-- .../client/src/rpc/impls/eth/eth_handler.rs | 25 ++-- crates/client/src/rpc/types/call_request.rs | 27 +++-- .../client/src/rpc/types/eth/call_request.rs | 109 ------------------ crates/primitives/src/transaction/mod.rs | 1 + 6 files changed, 36 insertions(+), 148 deletions(-) diff --git a/crates/cfxcore/core/src/transaction_pool/mod.rs b/crates/cfxcore/core/src/transaction_pool/mod.rs index e83ea75733..bdca618bb7 100644 --- a/crates/cfxcore/core/src/transaction_pool/mod.rs +++ b/crates/cfxcore/core/src/transaction_pool/mod.rs @@ -379,9 +379,6 @@ impl TransactionPool { let mut failure = HashMap::new(); let current_best_info = self.consensus_best_info.lock().clone(); - // filter out invalid transactions. - let mut index = 0; - let (chain_id, best_height, best_block_number) = { ( current_best_info.best_chain_id(), @@ -395,6 +392,8 @@ impl TransactionPool { let vm_spec = self.machine.spec(best_block_number, best_height); let transitions = &self.machine.params().transition_heights; + // filter out invalid transactions. + let mut index = 0; while let Some(tx) = transactions.get(index) { match self.verify_transaction_tx_pool( tx, diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index 4e1a0f6649..1028ad55cf 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -1359,18 +1359,13 @@ impl RpcImpl { }; let storage_collateralized = U64::from(estimation.estimated_storage_limit); - let estimated_gas_limit = estimation.estimated_gas_limit; + let estimated_gas_used = estimation.estimated_gas_limit; let response = EstimateGasAndCollateralResponse { - // We multiply the gas_used for 2 reasons: - // 1. In each EVM call, the gas passed is at most 63/64 of the - // remaining gas, so the gas_limit should be multiplied a factor so - // that the gas passed into the sub-call is sufficient. The 4 / 3 - // factor is sufficient for 18 level of calls. - // 2. In Conflux, we recommend setting the gas_limit to (gas_used * - // 4) / 3, because the extra gas will be refunded up to - // 1/4 of the gas limit. - gas_limit: estimation.estimated_gas_limit, - gas_used: estimated_gas_limit, + gas_limit: estimated_gas_used, /* gas_limit used to be 4/3 of + * gas_used due to inaccuracy, + * currently it's the same as gas + * used as it's more accurate */ + gas_used: estimated_gas_used, storage_collateralized, }; Ok(response) diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index 8f0d643583..f2188f05d5 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -41,8 +41,13 @@ use cfxcore::{ use clap::crate_version; use jsonrpc_core::{Error as RpcError, Result as RpcResult}; use primitives::{ - filter::LogFilter, receipt::EVM_SPACE_SUCCESS, Action, - BlockHashOrEpochNumber, EpochNumber, SignedTransaction, StorageKey, + filter::LogFilter, + receipt::EVM_SPACE_SUCCESS, + transaction::{ + Eip1559Transaction, Eip155Transaction, Eip2930Transaction, + EthereumTransaction::*, EIP1559_TYPE, EIP2930_TYPE, LEGACY_TX_TYPE, + }, + Action, BlockHashOrEpochNumber, EpochNumber, SignedTransaction, StorageKey, StorageValue, TransactionStatus, TransactionWithSignature, }; use rustc_hex::ToHex; @@ -79,8 +84,6 @@ impl EthHandler { pub fn sign_call( chain_id: u32, request: CallRequest, ) -> RpcResult { - use primitives::transaction::*; - use EthereumTransaction::*; let max_gas = U256::from(MAX_GAS_CALL_REQUEST); let gas = min(request.gas.unwrap_or(max_gas), max_gas); let nonce = request.nonce.unwrap_or_default(); @@ -90,11 +93,11 @@ pub fn sign_call( let default_type_id = if request.max_fee_per_gas.is_some() || request.max_priority_fee_per_gas.is_some() { - 2 + EIP1559_TYPE } else if request.access_list.is_some() { - 1 + EIP2930_TYPE } else { - 0 + LEGACY_TX_TYPE }; let transaction_type = request .transaction_type @@ -110,8 +113,8 @@ pub fn sign_call( let access_list = request.access_list.unwrap_or(vec![]); let data = request.data.unwrap_or_default().into_vec(); - let transaction = match transaction_type.as_usize() { - 0 => Eip155(Eip155Transaction { + let transaction = match transaction_type.as_usize() as u8 { + LEGACY_TX_TYPE => Eip155(Eip155Transaction { nonce, gas_price, gas, @@ -120,7 +123,7 @@ pub fn sign_call( chain_id: Some(chain_id), data, }), - 1 => Eip2930(Eip2930Transaction { + EIP2930_TYPE => Eip2930(Eip2930Transaction { chain_id, nonce, gas_price, @@ -130,7 +133,7 @@ pub fn sign_call( data, access_list, }), - 2 => Eip1559(Eip1559Transaction { + EIP1559_TYPE => Eip1559(Eip1559Transaction { chain_id, nonce, max_priority_fee_per_gas, diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index 65d3335d2e..57dcc199a1 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -19,16 +19,18 @@ use cfxkey::Password; use primitives::{ transaction::{ native_transaction::NativeTransaction as PrimitiveTransaction, Action, + Cip1559Transaction, Cip2930Transaction, NativeTransaction, + TypedNativeTransaction::*, CIP1559_TYPE, CIP2930_TYPE, LEGACY_TX_TYPE, }, AccessList, AccessListItem, SignedTransaction, Transaction, TransactionWithSignature, }; use std::{cmp::min, convert::Into, sync::Arc}; -// use serde_json::de::ParserNumber::U64; - -/// The MAX_GAS_CALL_REQUEST is one magnitude higher than block gas limit and -/// not too high that a call_virtual consumes too much resource. +/// The MAX_GAS_CALL_REQUEST is used as max value of cfx_call or cfx_estimate's +/// gas value to prevent call_virtual consumes too much resource. +/// The tx_pool will reject the tx if the gas is larger than half of the block +/// gas limit. which is 30_000_000 before 1559, and 60_000_000 after 1559. pub const MAX_GAS_CALL_REQUEST: u64 = 15_000_000; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -175,9 +177,6 @@ impl SendTxRequest { pub fn sign_call( epoch_height: u64, chain_id: u32, request: CallRequest, ) -> RpcResult { - use primitives::transaction::*; - use TypedNativeTransaction::*; - let max_gas = U256::from(MAX_GAS_CALL_REQUEST); let gas = min(request.gas.unwrap_or(max_gas), max_gas); @@ -196,11 +195,11 @@ pub fn sign_call( let default_type_id = if request.max_fee_per_gas.is_some() || request.max_priority_fee_per_gas.is_some() { - 2 + CIP1559_TYPE } else if request.access_list.is_some() { - 1 + CIP2930_TYPE } else { - 0 + LEGACY_TX_TYPE }; let transaction_type = request .transaction_type @@ -215,8 +214,8 @@ pub fn sign_call( request.max_priority_fee_per_gas.unwrap_or(U256::zero()); let access_list = request.access_list.unwrap_or(vec![]); - let transaction = match transaction_type.as_usize() { - 0 => Cip155(NativeTransaction { + let transaction = match transaction_type.as_usize() as u8 { + LEGACY_TX_TYPE => Cip155(NativeTransaction { nonce, action, gas, @@ -227,7 +226,7 @@ pub fn sign_call( chain_id, data, }), - 1 => Cip2930(Cip2930Transaction { + CIP2930_TYPE => Cip2930(Cip2930Transaction { nonce, gas_price, gas, @@ -239,7 +238,7 @@ pub fn sign_call( data, access_list: to_primitive_access_list(access_list), }), - 2 => Cip1559(Cip1559Transaction { + CIP1559_TYPE => Cip1559(Cip1559Transaction { nonce, action, gas, diff --git a/crates/client/src/rpc/types/eth/call_request.rs b/crates/client/src/rpc/types/eth/call_request.rs index ec3a52bf08..d9d7906cea 100644 --- a/crates/client/src/rpc/types/eth/call_request.rs +++ b/crates/client/src/rpc/types/eth/call_request.rs @@ -48,112 +48,3 @@ pub struct CallRequest { #[serde(rename = "type")] pub transaction_type: Option, } - -// impl Into for CallRequest { -// fn into(self) -> Request { -// Request { -// transaction_type: self.transaction_type, -// from: self.from.map(Into::into), -// to: self.to.map(Into::into), -// gas_price: self.gas_price.map(Into::into), -// max_fee_per_gas: self.max_fee_per_gas, -// gas: self.gas.map(Into::into), -// value: self.value.map(Into::into), -// data: self.data.map(Into::into), -// nonce: self.nonce.map(Into::into), -// access_list: self.access_list.map(Into::into), -// max_priority_fee_per_gas: -// self.max_priority_fee_per_gas.map(Into::into), } -// } -// } -// -// #[cfg(test)] -// mod tests { -// use super::CallRequest; -// use ethereum_types::{H160, U256}; -// use rustc_hex::FromHex; -// use serde_json; -// use std::str::FromStr; -// -// #[test] -// fn call_request_deserialize() { -// let s = r#"{ -// "from":"0x0000000000000000000000000000000000000001", -// "to":"0x0000000000000000000000000000000000000002", -// "gasPrice":"0x1", -// "gas":"0x2", -// "value":"0x3", -// "data":"0x123456", -// "nonce":"0x4" -// }"#; -// let deserialized: CallRequest = serde_json::from_str(s).unwrap(); -// -// assert_eq!( -// deserialized, -// CallRequest { -// transaction_type: Default::default(), -// from: Some(H160::from_low_u64_be(1)), -// to: Some(H160::from_low_u64_be(2)), -// gas_price: Some(U256::from(1)), -// max_fee_per_gas: None, -// gas: Some(U256::from(2)), -// value: Some(U256::from(3)), -// data: Some(vec![0x12, 0x34, 0x56].into()), -// nonce: Some(U256::from(4)), -// access_list: None, -// max_priority_fee_per_gas: None, -// } -// ); -// } -// -// #[test] -// fn call_request_deserialize2() { -// let s = r#"{ -// "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", -// "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", -// "gas": "0x76c0", -// "gasPrice": "0x9184e72a000", -// "value": "0x9184e72a", -// "data": -// "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" -// }"#; -// let deserialized: CallRequest = serde_json::from_str(s).unwrap(); -// -// assert_eq!(deserialized, CallRequest { -// transaction_type: Default::default(), -// from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155"). -// unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567" -// ).unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()), -// max_fee_per_gas: None, -// gas: Some(U256::from_str("76c0").unwrap()), -// value: Some(U256::from_str("9184e72a").unwrap()), -// data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), -// nonce: None, -// access_list: None, -// max_priority_fee_per_gas: None, -// }); -// } -// -// #[test] -// fn call_request_deserialize_empty() { -// let s = r#"{"from":"0x0000000000000000000000000000000000000001"}"#; -// let deserialized: CallRequest = serde_json::from_str(s).unwrap(); -// -// assert_eq!( -// deserialized, -// CallRequest { -// transaction_type: Default::default(), -// from: Some(H160::from_low_u64_be(1)), -// to: None, -// gas_price: None, -// max_fee_per_gas: None, -// gas: None, -// value: None, -// data: None, -// nonce: None, -// access_list: None, -// max_priority_fee_per_gas: None, -// } -// ); -// } -// } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index f9f5a06e51..eb1528ace7 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -37,6 +37,7 @@ pub const UNSIGNED_SENDER: Address = H160([0xff; 20]); pub const TYPED_NATIVE_TX_PREFIX: &[u8; 3] = b"cfx"; pub const TYPED_NATIVE_TX_PREFIX_BYTE: u8 = TYPED_NATIVE_TX_PREFIX[0]; +pub const LEGACY_TX_TYPE: u8 = 0x00; pub const EIP2930_TYPE: u8 = 0x01; pub const EIP1559_TYPE: u8 = 0x02; pub const CIP2930_TYPE: u8 = 0x01; From 74d8d229e075c246a8f54dd7be8851da65961571 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 30 May 2024 15:25:39 +0800 Subject: [PATCH 109/137] add camelCase attribute to FeeHistory fix feeHistory order and used_ratio issue update feehistory tests --- crates/client/src/rpc/impls/cfx/common.rs | 2 +- crates/client/src/rpc/impls/cfx/light.rs | 2 +- .../client/src/rpc/impls/eth/eth_handler.rs | 2 +- crates/client/src/rpc/types/fee_history.rs | 21 ++++++++++--------- tests/evm_space/eip1559_test.py | 4 ++-- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx/common.rs b/crates/client/src/rpc/impls/cfx/common.rs index ae581ba4d1..c09f5a719c 100644 --- a/crates/client/src/rpc/impls/cfx/common.rs +++ b/crates/client/src/rpc/impls/cfx/common.rs @@ -585,7 +585,7 @@ impl RpcImpl { // Internal error happens only if the fetch header has inconsistent // block height fee_history - .push_back_block( + .push_front_block( Space::Native, &reward_percentiles, &block.block_header, diff --git a/crates/client/src/rpc/impls/cfx/light.rs b/crates/client/src/rpc/impls/cfx/light.rs index 8eed486234..8640bde85f 100644 --- a/crates/client/src/rpc/impls/cfx/light.rs +++ b/crates/client/src/rpc/impls/cfx/light.rs @@ -1135,7 +1135,7 @@ impl RpcImpl { // Internal error happens only if the fetch header has // inconsistent block height fee_history - .push_back_block( + .push_front_block( Space::Native, &reward_percentiles, &block.block_header, diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index f2188f05d5..44d7b145a7 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -955,7 +955,7 @@ impl Eth for EthHandler { // Internal error happens only if the fetch header has inconsistent // block height fee_history - .push_back_block( + .push_front_block( Space::Ethereum, &reward_percentiles, &block.pivot_header, diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index d706474220..69c6d0f345 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -4,6 +4,7 @@ use cfx_types::{Space, SpaceMap, U256}; use primitives::{transaction::SignedTransaction, BlockHeader}; #[derive(Serialize, Debug, Default)] +#[serde(rename_all = "camelCase")] pub struct FeeHistory { /// Oldest Block oldest_block: U256, @@ -23,7 +24,7 @@ impl FeeHistory { pub fn reward(&self) -> &VecDeque> { &self.reward } - pub fn push_back_block<'a, I>( + pub fn push_front_block<'a, I>( &mut self, space: Space, percentiles: &Vec, pivot_header: &BlockHeader, transactions: I, ) -> Result<(), String> @@ -33,13 +34,14 @@ impl FeeHistory { let base_price = if let Some(base_price) = pivot_header.base_price() { base_price[space] } else { - self.base_fee_per_gas.push_back(U256::zero()); - self.gas_used_ratio.push_back(0.0); - self.reward.push_back(vec![U256::zero(); percentiles.len()]); + self.base_fee_per_gas.push_front(U256::zero()); + self.gas_used_ratio.push_front(0.0); + self.reward + .push_front(vec![U256::zero(); percentiles.len()]); return Ok(()); }; - self.base_fee_per_gas.push_back( + self.base_fee_per_gas.push_front( pivot_header.base_price().map_or(U256::zero(), |x| x[space]), ); @@ -52,8 +54,7 @@ impl FeeHistory { .clone() .map(|x| *x.gas_limit()) .reduce(|x, y| x + y) - .unwrap_or_default() - / gas_limit; + .unwrap_or_default(); let gas_used_ratio = if gas_limit >= U256::from(u128::MAX) || gas_used >= U256::from(u128::MAX) @@ -64,10 +65,10 @@ impl FeeHistory { gas_used.as_u128() as f64 / gas_limit.as_u128() as f64 }; - self.gas_used_ratio.push_back(gas_used_ratio); + self.gas_used_ratio.push_front(gas_used_ratio); let reward = compute_reward(percentiles, transactions, base_price); - self.reward.push_back(reward); + self.reward.push_front(reward); Ok(()) } @@ -78,7 +79,7 @@ impl FeeHistory { ) { self.oldest_block = oldest_block.into(); self.base_fee_per_gas - .push_back(parent_base_price.map_or(U256::zero(), |x| x[space])); + .push_front(parent_base_price.map_or(U256::zero(), |x| x[space])); } } diff --git a/tests/evm_space/eip1559_test.py b/tests/evm_space/eip1559_test.py index 4db1624bd9..39f65e4d6a 100755 --- a/tests/evm_space/eip1559_test.py +++ b/tests/evm_space/eip1559_test.py @@ -83,8 +83,8 @@ def run_test(self): fee_history = self.nodes[0].eth_feeHistory("0x5", "latest", [21, 75]) - assert_equal(len(fee_history['base_fee_per_gas']), 6) - assert_equal(len(fee_history['gas_used_ratio']), 5) + assert_equal(len(fee_history['baseFeePerGas']), 6) + assert_equal(len(fee_history['gasUsedRatio']), 5) assert_equal(len(fee_history['reward']), 5) assert_greater_than(int(self.nodes[0].cfx_getFeeBurnt(), 0), 0) From ebb682fa6799025bcfcea7ce1264e6bfa0f2fd8e Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 30 May 2024 18:39:40 +0800 Subject: [PATCH 110/137] change toolchain file format --- rust-toolchain | 1 - rust-toolchain.toml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 369f9966f6..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.77.2 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..fb0f94604f --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.77.2" \ No newline at end of file From 4b6642b42c53226c032361921f0e02178ee8d02c Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 31 May 2024 11:12:24 +0800 Subject: [PATCH 111/137] use custom de-hex U64 --- Cargo.lock | 1 + crates/client/Cargo.toml | 1 + .../client/src/rpc/impls/cfx/cfx_handler.rs | 4 +- crates/client/src/rpc/impls/cfx/common.rs | 8 +- crates/client/src/rpc/impls/cfx/light.rs | 7 +- .../client/src/rpc/impls/eth/eth_handler.rs | 13 +-- crates/client/src/rpc/traits/cfx_space/cfx.rs | 4 +- crates/client/src/rpc/traits/eth_space/eth.rs | 3 +- crates/client/src/rpc/types.rs | 2 + crates/client/src/rpc/types/fee_history.rs | 2 +- crates/client/src/rpc/types/variadic_u64.rs | 44 ++++++++++ crates/serde_utils/src/num.rs | 83 ++++++++++++++++++- 12 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 crates/client/src/rpc/types/variadic_u64.rs diff --git a/Cargo.lock b/Cargo.lock index 09fdc8e77f..bf0ead7c0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1797,6 +1797,7 @@ dependencies = [ "rustc-hex", "secret-store", "serde", + "serde-utils", "serde_derive", "serde_json", "serial_test", diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index a515724f10..8d42f0551b 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -102,6 +102,7 @@ solidity-abi = {path= "../util/solidity-abi" } bls-signatures = {git = "https://github.com/Conflux-Chain/bls-signatures.git", rev = "fb52187df92d27c365642cb7e7b2aaf60437cf9c", default-features = false, features = ["multicore"]} alloy-rpc-types-trace = { workspace = true } geth-tracer = { path = "../cfxcore/geth-tracer" } +serde-utils = { path = "../serde_utils" } [dev-dependencies] criterion = "0.3" diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index 1028ad55cf..d49a200936 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -8,7 +8,7 @@ use crate::rpc::{ call_request::rpc_call_request_network, errors::check_rpc_address_network, pos::PoSEpochReward, FeeHistory, PoSEconomics, RpcAddress, SponsorInfo, StatOnGasLoad, TokenSupplyInfo, - VoteParamsInfo, WrapTransaction, + VoteParamsInfo, WrapTransaction, U64 as HexU64, }, }; use blockgen::BlockGenerator; @@ -2272,7 +2272,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; - fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; fn max_priority_fee_per_gas(&self) -> BoxFuture; } diff --git a/crates/client/src/rpc/impls/cfx/common.rs b/crates/client/src/rpc/impls/cfx/common.rs index c09f5a719c..b9ca7168d7 100644 --- a/crates/client/src/rpc/impls/cfx/common.rs +++ b/crates/client/src/rpc/impls/cfx/common.rs @@ -17,7 +17,7 @@ use crate::rpc::{ BlockHashOrEpochNumber, Bytes, CheckBalanceAgainstTransactionResponse, EpochNumber, FeeHistory, RpcAddress, Status as RpcStatus, Transaction as RpcTransaction, TxPoolPendingNonceRange, TxPoolStatus, - TxWithPoolInfo, + TxWithPoolInfo, U64 as HexU64, }, RpcErrorKind, RpcResult, }; @@ -530,7 +530,7 @@ impl RpcImpl { } pub fn fee_history( - &self, block_count: U64, newest_block: EpochNumber, + &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> RpcResult { info!( @@ -538,7 +538,7 @@ impl RpcImpl { block_count, newest_block, reward_percentiles ); - if block_count == U64::zero() { + if block_count.as_u64() == 0 { return Ok(FeeHistory::new()); } // keep read lock to ensure consistent view @@ -615,7 +615,7 @@ impl RpcImpl { info!("RPC Request: max_priority_fee_per_gas",); let fee_history = self.fee_history( - U64::from(300), + HexU64::from(300), EpochNumber::LatestState, vec![50f64], )?; diff --git a/crates/client/src/rpc/impls/cfx/light.rs b/crates/client/src/rpc/impls/cfx/light.rs index 8640bde85f..1fb19b00e8 100644 --- a/crates/client/src/rpc/impls/cfx/light.rs +++ b/crates/client/src/rpc/impls/cfx/light.rs @@ -50,6 +50,7 @@ use crate::{ SponsorInfo, StatOnGasLoad, Status as RpcStatus, StorageCollateralInfo, SyncGraphStates, TokenSupplyInfo, Transaction as RpcTransaction, VoteParamsInfo, WrapTransaction, + U64 as HexU64, }, RpcBoxFuture, RpcResult, }, @@ -1091,7 +1092,7 @@ impl RpcImpl { } fn fee_history( - &self, block_count: U64, newest_block: EpochNumber, + &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> RpcBoxFuture { info!( @@ -1099,7 +1100,7 @@ impl RpcImpl { block_count, newest_block, reward_percentiles ); - if block_count == U64::zero() { + if block_count.as_u64() == 0 { return Box::new(async { Ok(FeeHistory::new()) }.boxed().compat()); } @@ -1239,7 +1240,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; - fn fee_history(&self, block_count: U64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index 44d7b145a7..36dad68811 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -15,7 +15,7 @@ use crate::rpc::{ CallRequest, EthRpcLogFilter, Log, Receipt, SyncInfo, SyncStatus, Transaction, }, - Bytes, FeeHistory, Index, MAX_GAS_CALL_REQUEST, + Bytes, FeeHistory, Index, MAX_GAS_CALL_REQUEST, U64 as HexU64, }, }; use cfx_execute_helper::estimation::{ @@ -476,8 +476,11 @@ impl Eth for EthHandler { self.tx_pool.machine().params().evm_transaction_block_ratio as usize; - let fee_history = - self.fee_history(U64::from(300), BlockNumber::Latest, vec![50f64])?; + let fee_history = self.fee_history( + HexU64::from(300), + BlockNumber::Latest, + vec![50f64], + )?; let total_reward: U256 = fee_history .reward() @@ -904,7 +907,7 @@ impl Eth for EthHandler { } fn fee_history( - &self, block_count: U64, newest_block: BlockNumber, + &self, block_count: HexU64, newest_block: BlockNumber, reward_percentiles: Vec, ) -> jsonrpc_core::Result { info!( @@ -912,7 +915,7 @@ impl Eth for EthHandler { block_count, newest_block, reward_percentiles ); - if block_count == U64::zero() { + if block_count.as_u64() == 0 { return Ok(FeeHistory::new()); } diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index bb96fd4687..95de945952 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -10,7 +10,7 @@ use crate::rpc::types::{ EstimateGasAndCollateralResponse, FeeHistory, Log as RpcLog, PoSEconomics, Receipt as RpcReceipt, RewardInfo as RpcRewardInfo, RpcAddress, SponsorInfo, Status as RpcStatus, StorageCollateralInfo, TokenSupplyInfo, - Transaction, VoteParamsInfo, + Transaction, VoteParamsInfo, U64 as HexU64, }; use cfx_types::{H128, H256, U256, U64}; use jsonrpc_core::{BoxFuture, Result as JsonRpcResult}; @@ -203,7 +203,7 @@ pub trait Cfx { #[rpc(name = "cfx_feeHistory")] fn fee_history( - &self, block_count: U64, newest_block: EpochNumber, + &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, ) -> BoxFuture; diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index f3b0bbc06c..1df91b07d1 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -19,6 +19,7 @@ // along with OpenEthereum. If not, see . //! Eth rpc interface. +use crate::rpc::types::U64 as HexU64; use cfx_types::{H128, H160, H256, U256, U64}; use jsonrpc_core::Result; use jsonrpc_derive::rpc; @@ -76,7 +77,7 @@ pub trait Eth { #[rpc(name = "eth_feeHistory")] fn fee_history( - &self, block_count: U64, newest_block: BlockNumber, + &self, block_count: HexU64, newest_block: BlockNumber, reward_percentiles: Vec, ) -> Result; diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index 23b0141556..9746807d2f 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -32,6 +32,7 @@ mod trace; mod trace_filter; mod transaction; mod tx_pool; +mod variadic_u64; mod vote_params_info; pub use self::{ @@ -70,5 +71,6 @@ pub use self::{ AccountPendingInfo, AccountPendingTransactions, TxPoolPendingNonceRange, TxPoolStatus, TxWithPoolInfo, }, + variadic_u64::U64, vote_params_info::VoteParamsInfo, }; diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index 69c6d0f345..50a68f1344 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -106,7 +106,7 @@ where I: Iterator { percentiles .into_iter() .map(|per| { - let mut index = (*per) as usize * n / 100; + let mut index = ((*per) * (n as f64) / 100f64) as usize; if index >= n { index = n - 1; } diff --git a/crates/client/src/rpc/types/variadic_u64.rs b/crates/client/src/rpc/types/variadic_u64.rs new file mode 100644 index 0000000000..0f9011b21f --- /dev/null +++ b/crates/client/src/rpc/types/variadic_u64.rs @@ -0,0 +1,44 @@ +use serde::{Deserialize, Deserializer}; +use serde_utils::num::deserialize_u64_from_num_or_hex; +use std::fmt; + +// support both hex strings and number deserialization +#[derive(Debug)] +pub struct U64(u64); + +impl fmt::Display for U64 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for U64 { + fn from(value: u64) -> Self { U64(value) } +} + +impl U64 { + pub fn as_u64(&self) -> u64 { self.0 } +} + +impl<'de> Deserialize<'de> for U64 { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + Ok(U64(deserialize_u64_from_num_or_hex(deserializer)?)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deserialize_u64_or_hex() { + let json_data = r#""0x1a""#; + let my_struct: U64 = serde_json::from_str(json_data).unwrap(); + assert_eq!(my_struct.as_u64(), 26); + + let json_data = r#"26"#; + let my_struct: U64 = serde_json::from_str(json_data).unwrap(); + assert_eq!(my_struct.as_u64(), 26); + } +} diff --git a/crates/serde_utils/src/num.rs b/crates/serde_utils/src/num.rs index 10364c356b..d1f4b4d91f 100644 --- a/crates/serde_utils/src/num.rs +++ b/crates/serde_utils/src/num.rs @@ -1,6 +1,7 @@ -use cfx_types::U256; +use cfx_types::{U256, U64}; use core::str::FromStr; use serde::{de, Deserialize, Deserializer}; +use std::fmt; /// An enum that represents either a [serde_json::Number] integer, or a hex /// [U256]. @@ -28,14 +29,16 @@ impl NumberOrHexU256 { /// Deserializes the input into a U256, accepting both 0x-prefixed hex and /// decimal strings with arbitrary precision, defined by serde_json's /// [`Number`](serde_json::Number). -pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result +pub fn from_int_or_hex_to_u256<'de, D>( + deserializer: D, +) -> Result where D: Deserializer<'de> { NumberOrHexU256::deserialize(deserializer)?.try_into_u256() } /// Deserializes the input into an `Option`, using [`from_int_or_hex`] to /// deserialize the inner value. -pub fn from_int_or_hex_opt<'de, D>( +pub fn from_int_or_hex_to_u256_opt<'de, D>( deserializer: D, ) -> Result, D::Error> where D: Deserializer<'de> { @@ -44,3 +47,77 @@ where D: Deserializer<'de> { None => Ok(None), } } + +/// An enum that represents either a [serde_json::Number] integer, or a hex +/// [U64]. +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum NumberOrHexU64 { + /// An integer + Int(serde_json::Number), + /// A hex U64 + Hex(U64), +} + +impl NumberOrHexU64 { + /// Tries to convert this into a [U64]]. + pub fn try_into_u64(self) -> Result { + match self { + NumberOrHexU64::Int(num) => { + U64::from_str(num.to_string().as_str()).map_err(E::custom) + } + NumberOrHexU64::Hex(val) => Ok(val), + } + } +} + +/// Deserializes the input into a U64, accepting both 0x-prefixed hex and +/// decimal strings with arbitrary precision, defined by serde_json's +/// [`Number`](serde_json::Number). +pub fn from_int_or_hex_to_u64<'de, D>( + deserializer: D, +) -> Result +where D: Deserializer<'de> { + NumberOrHexU64::deserialize(deserializer)?.try_into_u64() +} + +/// Deserializes the input into an `Option`, using +/// [`from_int_or_hex_to_u64`] to deserialize the inner value. +pub fn from_int_or_hex_to_u64_opt<'de, D>( + deserializer: D, +) -> Result, D::Error> +where D: Deserializer<'de> { + match Option::::deserialize(deserializer)? { + Some(val) => val.try_into_u64().map(Some), + None => Ok(None), + } +} + +pub fn deserialize_u64_from_num_or_hex<'de, D>( + deserializer: D, +) -> Result +where D: Deserializer<'de> { + struct U64OrHexVisitor; + + impl<'de> serde::de::Visitor<'de> for U64OrHexVisitor { + type Value = u64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .write_str("a u64 integer or a hex string representing a u64") + } + + fn visit_u64(self, value: u64) -> Result { Ok(value) } + + fn visit_str(self, value: &str) -> Result + where E: serde::de::Error { + if let Some(stripped) = value.strip_prefix("0x") { + u64::from_str_radix(stripped, 16).map_err(E::custom) + } else { + Err(E::custom("expected hex string to start with '0x'")) + } + } + } + + deserializer.deserialize_any(U64OrHexVisitor) +} From dc398b4ea1e22862f7b4dbce3c4f18b4a35d0a98 Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 31 May 2024 17:25:32 +0800 Subject: [PATCH 112/137] core transaction.accessList address change to base32 format --- crates/client/src/rpc/types.rs | 4 +- crates/client/src/rpc/types/call_request.rs | 30 ++------------- .../client/src/rpc/types/cfx/access_list.rs | 38 +++++++++++++++++++ .../client/src/rpc/types/{ => cfx}/address.rs | 0 crates/client/src/rpc/types/cfx/mod.rs | 5 +++ crates/client/src/rpc/types/transaction.rs | 17 ++++++--- 6 files changed, 60 insertions(+), 34 deletions(-) create mode 100644 crates/client/src/rpc/types/cfx/access_list.rs rename crates/client/src/rpc/types/{ => cfx}/address.rs (100%) create mode 100644 crates/client/src/rpc/types/cfx/mod.rs diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index 23b0141556..90a6f50d70 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -3,11 +3,11 @@ // See http://www.gnu.org/licenses/ mod account; -pub mod address; mod blame_info; mod block; mod bytes; pub mod call_request; +pub mod cfx; mod consensus_graph_states; mod epoch_number; pub mod errors; @@ -36,7 +36,6 @@ mod vote_params_info; pub use self::{ account::Account, - address::RpcAddress, blame_info::BlameInfo, block::{Block, BlockTransactions, Header}, bytes::Bytes, @@ -44,6 +43,7 @@ pub use self::{ sign_call, CallRequest, CheckBalanceAgainstTransactionResponse, EstimateGasAndCollateralResponse, SendTxRequest, MAX_GAS_CALL_REQUEST, }, + cfx::{address, address::RpcAddress}, consensus_graph_states::ConsensusGraphStates, epoch_number::{BlockHashOrEpochNumber, EpochNumber}, fee_history::FeeHistory, diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/call_request.rs index 65d3335d2e..0efa3a27ba 100644 --- a/crates/client/src/rpc/types/call_request.rs +++ b/crates/client/src/rpc/types/call_request.rs @@ -6,13 +6,14 @@ use crate::rpc::{ error_codes::invalid_params, types::{ address::RpcAddress, + cfx::{to_primitive_access_list, CfxAccessList}, errors::{check_rpc_address_network, RcpAddressNetworkInconsistent}, Bytes, }, RpcResult, }; use cfx_addr::Network; -use cfx_types::{Address, AddressSpaceUtil, H256, U256, U64}; +use cfx_types::{Address, AddressSpaceUtil, U256, U64}; use cfxcore::rpc_errors::invalid_params_check; use cfxcore_accounts::AccountProvider; use cfxkey::Password; @@ -20,8 +21,7 @@ use primitives::{ transaction::{ native_transaction::NativeTransaction as PrimitiveTransaction, Action, }, - AccessList, AccessListItem, SignedTransaction, Transaction, - TransactionWithSignature, + SignedTransaction, Transaction, TransactionWithSignature, }; use std::{cmp::min, convert::Into, sync::Arc}; @@ -31,28 +31,6 @@ use std::{cmp::min, convert::Into, sync::Arc}; /// not too high that a call_virtual consumes too much resource. pub const MAX_GAS_CALL_REQUEST: u64 = 15_000_000; -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CoreAccessListItem { - pub address: RpcAddress, - pub storage_keys: Vec, -} - -impl Into for CoreAccessListItem { - fn into(self) -> AccessListItem { - AccessListItem { - address: self.address.hex_address, - storage_keys: self.storage_keys, - } - } -} - -pub type CoreAccessList = Vec; - -fn to_primitive_access_list(list: CoreAccessList) -> AccessList { - list.into_iter().map(|item| item.into()).collect() -} - #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct CallRequest { @@ -73,7 +51,7 @@ pub struct CallRequest { /// StorageLimit pub storage_limit: Option, /// Access list in EIP-2930 - pub access_list: Option, + pub access_list: Option, pub max_fee_per_gas: Option, pub max_priority_fee_per_gas: Option, #[serde(rename = "type")] diff --git a/crates/client/src/rpc/types/cfx/access_list.rs b/crates/client/src/rpc/types/cfx/access_list.rs new file mode 100644 index 0000000000..1e0295669d --- /dev/null +++ b/crates/client/src/rpc/types/cfx/access_list.rs @@ -0,0 +1,38 @@ +use crate::rpc::types::address::RpcAddress; +use cfx_addr::Network; +use cfx_types::H256; +use primitives::{AccessList, AccessListItem}; +use std::convert::Into; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CfxAccessListItem { + pub address: RpcAddress, + pub storage_keys: Vec, +} + +impl Into for CfxAccessListItem { + fn into(self) -> AccessListItem { + AccessListItem { + address: self.address.hex_address, + storage_keys: self.storage_keys, + } + } +} + +pub type CfxAccessList = Vec; + +pub fn to_primitive_access_list(list: CfxAccessList) -> AccessList { + list.into_iter().map(|item| item.into()).collect() +} + +pub fn from_primitive_access_list( + list: AccessList, network: Network, +) -> CfxAccessList { + list.into_iter() + .map(|item| CfxAccessListItem { + address: RpcAddress::try_from_h160(item.address, network).unwrap(), + storage_keys: item.storage_keys, + }) + .collect() +} diff --git a/crates/client/src/rpc/types/address.rs b/crates/client/src/rpc/types/cfx/address.rs similarity index 100% rename from crates/client/src/rpc/types/address.rs rename to crates/client/src/rpc/types/cfx/address.rs diff --git a/crates/client/src/rpc/types/cfx/mod.rs b/crates/client/src/rpc/types/cfx/mod.rs new file mode 100644 index 0000000000..89d8fb6a46 --- /dev/null +++ b/crates/client/src/rpc/types/cfx/mod.rs @@ -0,0 +1,5 @@ +mod access_list; +pub mod address; + +pub use access_list::*; +pub use address::RpcAddress; diff --git a/crates/client/src/rpc/types/transaction.rs b/crates/client/src/rpc/types/transaction.rs index 50933efeb8..a5795a1aaa 100644 --- a/crates/client/src/rpc/types/transaction.rs +++ b/crates/client/src/rpc/types/transaction.rs @@ -3,7 +3,10 @@ // See http://www.gnu.org/licenses/ use crate::rpc::types::{ - eth::Transaction as ETHTransaction, receipt::Receipt, Bytes, RpcAddress, + cfx::{from_primitive_access_list, CfxAccessList}, + eth::Transaction as ETHTransaction, + receipt::Receipt, + Bytes, RpcAddress, }; use cfx_addr::Network; use cfx_types::{Space, H256, U256, U64}; @@ -13,9 +16,8 @@ use primitives::{ eth_transaction::Eip155Transaction, native_transaction::NativeTransaction, Action, }, - AccessList, SignedTransaction, Transaction as PrimitiveTransaction, - TransactionIndex, TransactionWithSignature, - TransactionWithSignatureSerializePart, + SignedTransaction, Transaction as PrimitiveTransaction, TransactionIndex, + TransactionWithSignature, TransactionWithSignatureSerializePart, }; #[derive(Debug, Clone, PartialEq, Serialize)] @@ -49,7 +51,7 @@ pub struct Transaction { pub status: Option, /// Optional access list #[serde(skip_serializing_if = "Option::is_none")] - pub access_list: Option, + pub access_list: Option, /// miner bribe #[serde(skip_serializing_if = "Option::is_none")] pub max_priority_fee_per_gas: Option, @@ -158,7 +160,10 @@ impl Transaction { storage_limit: storage_limit.into(), epoch_height: epoch_height.into(), chain_id: t.chain_id().map(|x| U256::from(x as u64)), - access_list: t.access_list().cloned(), + access_list: t + .access_list() + .cloned() + .map(|list| from_primitive_access_list(list, network)), max_fee_per_gas: t.after_1559().then_some(*t.gas_price()), max_priority_fee_per_gas: t .after_1559() From e32bd51fa3097998da1696ea12ab702d2fcfbfcb Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Fri, 31 May 2024 19:43:29 +0800 Subject: [PATCH 113/137] Update 1559 gas limit estimation & Fix issues in test --- Cargo.lock | 1 + .../transaction_pool_inner.rs | 13 +- crates/cfxcore/packing-pool/src/pool.rs | 36 +++- crates/primitives/Cargo.toml | 1 + crates/primitives/src/block_header.rs | 68 +----- .../primitives/src/block_header/base_price.rs | 201 ++++++++++++++++++ 6 files changed, 248 insertions(+), 72 deletions(-) create mode 100644 crates/primitives/src/block_header/base_price.rs diff --git a/Cargo.lock b/Cargo.lock index 09fdc8e77f..81ff5a725c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6162,6 +6162,7 @@ dependencies = [ "cfxkey", "criterion", "fixed-hash 0.5.2", + "itertools 0.10.5", "keccak-hash", "lazy_static", "log", diff --git a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs index 628110ae65..612d1c3c60 100644 --- a/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs +++ b/crates/cfxcore/core/src/transaction_pool/transaction_pool_inner.rs @@ -800,13 +800,16 @@ impl TransactionPoolInner { fn insert_transaction_for_test( &mut self, transaction: Arc, sender_nonce: U256, ) -> InsertResult { - self.insert_transaction_without_readiness_check( + let sender = transaction.sender(); + let res = self.insert_transaction_without_readiness_check( transaction, false, true, (sender_nonce, U256::from(u64::MAX)), (0.into(), 0), - ) + ); + self.recalculate_readiness(&sender, sender_nonce, U256::from(u64::MAX)); + res } // the new inserting will fail if tx_pool is full (even if `force` is true) @@ -1793,7 +1796,7 @@ mod tests { fn pack_transactions_1559_checked( pool: &mut TransactionPoolInner, machine: &Machine, ) { - let parent_base_price = SpaceMap::new(100, 100).map_all(U256::from); + let parent_base_price = SpaceMap::new(100, 200).map_all(U256::from); let block_gas_limit = U256::from(6000); let best_epoch_height = 20; @@ -1827,7 +1830,7 @@ mod tests { for tx in txs { gas_used[tx.space()] += *tx.gas_limit(); min_gas_price[tx.space()] = - min_gas_price[tx.space()].min(*tx.gas_limit()); + min_gas_price[tx.space()].min(*tx.gas_price()); } let min_base_price = params.min_base_price(); @@ -1850,7 +1853,7 @@ mod tests { } #[test] - fn test_eip1559_transactions() { + fn test_pack_eip1559_transactions() { let mut pool = TransactionPoolInner::new_for_test(); let mut params = CommonParams::default(); diff --git a/crates/cfxcore/packing-pool/src/pool.rs b/crates/cfxcore/packing-pool/src/pool.rs index 5c9e7c6a18..ac0d0256f0 100644 --- a/crates/cfxcore/packing-pool/src/pool.rs +++ b/crates/cfxcore/packing-pool/src/pool.rs @@ -12,7 +12,9 @@ use super::{ }; use cfx_types::U256; use malloc_size_of::MallocSizeOf; -use primitives::block_header::estimate_gas_used; +use primitives::block_header::{ + compute_next_price, estimate_gas_used_boundary, estimate_max_possible_gas, +}; use rand::RngCore; use treap_map::{ ApplyOpOutcome, ConsoliableWeight, Node, SearchDirection, SearchResult, @@ -210,7 +212,24 @@ impl PackingPool { Some( SearchResult::Found { base_weight, .. } | SearchResult::RightMost(base_weight), - ) => base_weight.gas_limit, + ) => { + let gas_limit = estimate_max_possible_gas( + gas_target, + base_weight.min_gas_price, + parent_base_price, + ); + if cfg!(test) { + // Guarantee the searched result can be packed + let next_price = compute_next_price( + gas_target, + gas_limit, + parent_base_price, + min_base_price, + ); + assert!(base_weight.min_gas_price >= next_price); + } + gas_limit + } _ => U256::zero(), } } @@ -285,20 +304,23 @@ fn can_sample_within_1559( return false; } - let target_gas_used = - estimate_gas_used(gas_target, weight.min_gas_price, parent_base_price); + let max_target_gas_used = estimate_max_possible_gas( + gas_target, + weight.min_gas_price, + parent_base_price, + ); - if target_gas_used.is_zero() { + if max_target_gas_used.is_zero() { return false; } - if weight.gas_limit <= target_gas_used { + if weight.gas_limit <= max_target_gas_used { return true; } weight .max_loss_ratio - .saturating_mul(weight.gas_limit - target_gas_used) + .saturating_mul(weight.gas_limit - max_target_gas_used) < weight.weighted_loss_ratio } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 9cc7a5497f..254d0a6981 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -25,6 +25,7 @@ once_cell = "1.17.1" [dev-dependencies] criterion = "0.3" serde_json = "1.0" +itertools = "0.10" [[bench]] name = "benchmark" diff --git a/crates/primitives/src/block_header.rs b/crates/primitives/src/block_header.rs index 6054884843..357555cc1b 100644 --- a/crates/primitives/src/block_header.rs +++ b/crates/primitives/src/block_header.rs @@ -2,6 +2,12 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ +mod base_price; +pub use base_price::{ + compute_next_price, compute_next_price_tuple, estimate_gas_used_boundary, + estimate_max_possible_gas, +}; + use crate::{ block::BlockHeight, bytes::Bytes, hash::keccak, pos::PosBlockId, receipt::BlockReceipts, MERKLE_NULL_NODE, NULL_EPOCH, @@ -9,7 +15,6 @@ use crate::{ use cfx_types::{ Address, Bloom, Space, SpaceMap, H256, KECCAK_EMPTY_BLOOM, U256, }; -use log::warn; use malloc_size_of::{new_malloc_size_ops, MallocSizeOf, MallocSizeOfOps}; use once_cell::sync::OnceCell; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -25,6 +30,8 @@ const HEADER_LIST_MIN_LEN: usize = 13; /// field. pub static CIP112_TRANSITION_HEIGHT: OnceCell = OnceCell::new(); +const BASE_PRICE_CHANGE_DENOMINATOR: usize = 8; + #[derive(Clone, Debug, Eq)] pub struct BlockHeaderRlpPart { /// Parent hash. @@ -684,65 +691,6 @@ pub struct BasePrice { pub core_base_price: U256, pub espace_base_price: U256, } - -const BASE_PRICE_CHANGE_DENOMINATOR: usize = 8; - -pub fn compute_next_price( - gas_target: U256, gas_actual: U256, last_base_price: U256, - min_base_price: U256, -) -> U256 { - const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; - - if gas_actual > gas_target * 2 { - warn!("gas target is larger than expected"); - } - - let next_base_price = if gas_target.is_zero() || gas_target == gas_actual { - last_base_price - } else if gas_actual > gas_target { - let delta = gas_actual - gas_target; - let mut price_delta = last_base_price * delta / gas_target / DENOM; - if price_delta.is_zero() { - price_delta = U256::one(); - } - last_base_price + price_delta - } else { - let delta = gas_target - gas_actual; - let mut price_delta = last_base_price * delta / gas_target / DENOM; - if price_delta.is_zero() { - price_delta = U256::one(); - } - last_base_price - price_delta - }; - next_base_price.max(min_base_price) -} - -pub fn estimate_gas_used( - gas_target: U256, current_base_price: U256, last_base_price: U256, -) -> U256 { - // dbg!(gas_target, current_base_price, last_base_price); - const DENOM: usize = BASE_PRICE_CHANGE_DENOMINATOR; - - let delta = U256::max(U256::one(), last_base_price / DENOM); - - let upper_base_price = last_base_price + delta; - let lower_base_price = last_base_price - delta; - - if current_base_price > upper_base_price { - gas_target * 2 - } else if current_base_price < lower_base_price { - U256::zero() - } else { - gas_target * 2 * (current_base_price - lower_base_price) - / (upper_base_price - lower_base_price) - } -} - -/// A helper function for `compute_next_price` which takes a typle as input -pub fn compute_next_price_tuple(x: (U256, U256, U256, U256)) -> U256 { - compute_next_price(x.0, x.1, x.2, x.3) -} - #[cfg(test)] mod tests { use super::BlockHeaderBuilder; diff --git a/crates/primitives/src/block_header/base_price.rs b/crates/primitives/src/block_header/base_price.rs new file mode 100644 index 0000000000..02b9550c9b --- /dev/null +++ b/crates/primitives/src/block_header/base_price.rs @@ -0,0 +1,201 @@ +use super::BASE_PRICE_CHANGE_DENOMINATOR as DENOM; +use cfx_types::U256; +use log::warn; + +pub fn compute_next_price( + gas_target: U256, gas_actual: U256, last_base_price: U256, + min_base_price: U256, +) -> U256 { + let gas_actual = if gas_actual > gas_target * 2 { + warn!("gas target is larger than expected"); + gas_target * 2 + } else { + gas_actual + }; + + let next_base_price = if gas_target.is_zero() || gas_target == gas_actual { + last_base_price + } else { + let (gas_delta, delta_sign) = if gas_actual > gas_target { + (gas_actual - gas_target, true) + } else { + (gas_target - gas_actual, false) + }; + + assert!(gas_delta <= gas_target); + + let mut price_delta = gas_delta * last_base_price / gas_target / DENOM; + if price_delta.is_zero() { + price_delta = U256::one() + } + + if delta_sign { + last_base_price + price_delta + } else if !last_base_price.is_zero() { + last_base_price - price_delta + } else { + U256::zero() + } + }; + + next_base_price.max(min_base_price) +} + +pub fn estimate_max_possible_gas( + gas_target: U256, current_base_price: U256, last_base_price: U256, +) -> U256 { + let (_, upper_boundary) = estimate_gas_used_boundary( + gas_target, + current_base_price, + last_base_price, + ); + match upper_boundary { + None => gas_target * 2, + Some(U256([0, 0, 0, 0])) => U256::zero(), + Some(x) => x - 1, + } +} + +// Returns the outside boundary gas usage values that define the range +// +// The first item represents the maximum value that the next_price < +// current_base_price. +// +// The second item represents the minimum value that the +// next_price > current_base_price. +pub fn estimate_gas_used_boundary( + gas_target: U256, current_base_price: U256, last_base_price: U256, +) -> (Option, Option) { + if gas_target.is_zero() { + return (None, Some(1.into())); + } + + if last_base_price.is_zero() { + return if current_base_price == U256::zero() { + (None, Some(gas_target + 1)) + } else if current_base_price == U256::one() { + (Some(gas_target), Some(gas_target * 2 + 1)) + } else { + (Some(gas_target * 2), None) + }; + } + + let max_price_delta = U256::max(U256::one(), last_base_price / DENOM); + let upper_base_price = last_base_price + max_price_delta; + let lower_base_price = last_base_price - max_price_delta; + + if current_base_price > upper_base_price { + (Some(gas_target * 2), None) + } else if current_base_price < lower_base_price { + (None, Some(U256::zero())) + } else if current_base_price == last_base_price { + (Some(gas_target - 1), Some(gas_target + 1)) + } else { + let (price_delta, delta_sign) = if current_base_price > last_base_price + { + (current_base_price - last_base_price, true) + } else { + (last_base_price - current_base_price, false) + }; + + assert!(!price_delta.is_zero()); + + let lower_bound = if price_delta == U256::one() { + U256::zero() + } else { + (price_delta * gas_target * DENOM - 1) / last_base_price + }; + + let upper_bound = + ((price_delta + 1) * gas_target * DENOM + last_base_price - 1) + / last_base_price; + + if delta_sign { + ( + Some(gas_target + lower_bound), + Some(U256::min(gas_target + upper_bound, gas_target * 2 + 1)), + ) + } else { + ( + // Underflow could happen + gas_target.checked_sub(upper_bound), + Some(gas_target - lower_bound), + ) + } + } +} + +/// A helper function for `compute_next_price` which takes a typle as input +pub fn compute_next_price_tuple(x: (U256, U256, U256, U256)) -> U256 { + compute_next_price(x.0, x.1, x.2, x.3) +} + +#[cfg(test)] +mod tests { + use crate::block_header::compute_next_price; + + use super::{estimate_gas_used_boundary, DENOM, U256}; + use itertools::Itertools; + + fn test_boundary(gas_target: usize, last_base_price: usize) { + let max_price_delta = usize::max(1, last_base_price / DENOM); + + let start = last_base_price.saturating_sub(max_price_delta); + let end = last_base_price.saturating_add(max_price_delta); + + let mut next_start = 0usize; + + for price in start..=end { + // dbg!(gas_target, price, last_base_price); + let (lo, up) = estimate_gas_used_boundary( + gas_target.into(), + price.into(), + last_base_price.into(), + ); + let s = lo.map_or(0, |x| x.as_usize() + 1); + let e = up.unwrap().as_usize(); + assert_eq!(s, next_start); + next_start = e; + for gas_used in s..e { + let next_price = compute_next_price( + gas_target.into(), + gas_used.into(), + last_base_price.into(), + U256::zero(), + ) + .as_usize(); + assert_eq!(next_price, price); + } + } + assert_eq!(next_start, gas_target * 2 + 1); + + if start > 0 { + let res = estimate_gas_used_boundary( + gas_target.into(), + (start - 1).into(), + last_base_price.into(), + ); + assert_eq!(res, (None, Some(U256::zero()))) + } + + let res = estimate_gas_used_boundary( + gas_target.into(), + (end + 1).into(), + last_base_price.into(), + ); + assert_eq!(res, (Some((gas_target * 2).into()), None)); + } + + #[test] + fn test_gas_used_estimation() { + let tasks = [1, 2, 3, 4, 5, 8, 10, 991, 1000, 1019, 9949, 10000, 10067] + .into_iter() + .cartesian_product([ + 0, 1, 2, 3, 4, 5, 8, 10, 991, 1000, 1019, 9949, 10000, 10067, + ]); + + for (gas_target, last_base_price) in tasks.into_iter() { + test_boundary(gas_target, last_base_price); + } + } +} From d4f03bc18a9bc1bba28fbc7fe57d725ff1c02acf Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Mon, 3 Jun 2024 11:54:27 +0800 Subject: [PATCH 114/137] Cargo fmt --- crates/cfxcore/packing-pool/src/pool.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/cfxcore/packing-pool/src/pool.rs b/crates/cfxcore/packing-pool/src/pool.rs index ac0d0256f0..8268ad6c3f 100644 --- a/crates/cfxcore/packing-pool/src/pool.rs +++ b/crates/cfxcore/packing-pool/src/pool.rs @@ -12,9 +12,7 @@ use super::{ }; use cfx_types::U256; use malloc_size_of::MallocSizeOf; -use primitives::block_header::{ - compute_next_price, estimate_gas_used_boundary, estimate_max_possible_gas, -}; +use primitives::block_header::{compute_next_price, estimate_max_possible_gas}; use rand::RngCore; use treap_map::{ ApplyOpOutcome, ConsoliableWeight, Node, SearchDirection, SearchResult, From adb84ec9cb005761b84b5644b464301401028148 Mon Sep 17 00:00:00 2001 From: Pana Date: Tue, 4 Jun 2024 09:48:42 +0800 Subject: [PATCH 115/137] fix zero gas_price estimate error issue move core call_request into cfx folder --- crates/client/src/rpc/impls/eth/eth_handler.rs | 6 +++++- crates/client/src/rpc/types.rs | 14 +++++++++----- .../client/src/rpc/types/{ => cfx}/call_request.rs | 0 crates/client/src/rpc/types/cfx/mod.rs | 1 + crates/client/src/rpc/types/eth/call_request.rs | 8 ++++++++ 5 files changed, 23 insertions(+), 6 deletions(-) rename crates/client/src/rpc/types/{ => cfx}/call_request.rs (100%) diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index 36dad68811..73a92198b9 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -187,7 +187,8 @@ fn block_tx_by_index( impl EthHandler { fn exec_transaction( - &self, request: CallRequest, block_number_or_hash: Option, + &self, mut request: CallRequest, + block_number_or_hash: Option, ) -> CfxRpcResult<(ExecutionOutcome, EstimateExt)> { let consensus_graph = self.consensus_graph(); @@ -213,6 +214,9 @@ impl EthHandler { epoch => epoch.try_into()?, }; + // if gas_price is zero, it is considered as not set + request.unset_zero_gas_price(); + let estimate_request = EstimateRequest { has_sender: request.from.is_some(), has_gas_limit: request.gas.is_some(), diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index a5b53b75ee..7f01e6f696 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -6,7 +6,6 @@ mod account; mod blame_info; mod block; mod bytes; -pub mod call_request; pub mod cfx; mod consensus_graph_states; mod epoch_number; @@ -40,11 +39,16 @@ pub use self::{ blame_info::BlameInfo, block::{Block, BlockTransactions, Header}, bytes::Bytes, - call_request::{ - sign_call, CallRequest, CheckBalanceAgainstTransactionResponse, - EstimateGasAndCollateralResponse, SendTxRequest, MAX_GAS_CALL_REQUEST, + cfx::{ + address, + address::RpcAddress, + call_request::{ + self, sign_call, CallRequest, + CheckBalanceAgainstTransactionResponse, + EstimateGasAndCollateralResponse, SendTxRequest, + MAX_GAS_CALL_REQUEST, + }, }, - cfx::{address, address::RpcAddress}, consensus_graph_states::ConsensusGraphStates, epoch_number::{BlockHashOrEpochNumber, EpochNumber}, fee_history::FeeHistory, diff --git a/crates/client/src/rpc/types/call_request.rs b/crates/client/src/rpc/types/cfx/call_request.rs similarity index 100% rename from crates/client/src/rpc/types/call_request.rs rename to crates/client/src/rpc/types/cfx/call_request.rs diff --git a/crates/client/src/rpc/types/cfx/mod.rs b/crates/client/src/rpc/types/cfx/mod.rs index 89d8fb6a46..11b5dd6de4 100644 --- a/crates/client/src/rpc/types/cfx/mod.rs +++ b/crates/client/src/rpc/types/cfx/mod.rs @@ -1,5 +1,6 @@ mod access_list; pub mod address; +pub mod call_request; pub use access_list::*; pub use address::RpcAddress; diff --git a/crates/client/src/rpc/types/eth/call_request.rs b/crates/client/src/rpc/types/eth/call_request.rs index d9d7906cea..3d694209b1 100644 --- a/crates/client/src/rpc/types/eth/call_request.rs +++ b/crates/client/src/rpc/types/eth/call_request.rs @@ -48,3 +48,11 @@ pub struct CallRequest { #[serde(rename = "type")] pub transaction_type: Option, } + +impl CallRequest { + pub fn unset_zero_gas_price(&mut self) { + if self.gas_price == Some(U256::zero()) { + self.gas_price = None; + } + } +} From 235b1d582cc2bb319ce6f07ceb4aed6755634c0b Mon Sep 17 00:00:00 2001 From: Chenxing Li Date: Tue, 4 Jun 2024 17:08:01 +0800 Subject: [PATCH 116/137] Fix incorrect base price in constructing receipt for non-pivot block --- .../client/src/rpc/impls/cfx/cfx_handler.rs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index d49a200936..b2cadc9721 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -47,7 +47,7 @@ use network::{ }; use parking_lot::Mutex; use primitives::{ - filter::LogFilter, receipt::EVM_SPACE_SUCCESS, Account, Block, + filter::LogFilter, receipt::EVM_SPACE_SUCCESS, Account, Block, BlockHeader, BlockReceipts, DepositInfo, SignedTransaction, StorageKey, StorageRoot, StorageValue, Transaction, TransactionIndex, TransactionStatus, TransactionWithSignature, VoteStakeInfo, @@ -115,7 +115,7 @@ pub(crate) struct BlockExecInfo { pub(crate) block: Arc, pub(crate) epoch_number: u64, pub(crate) maybe_state_root: Option, - pub(crate) pivot_hash: H256, + pub(crate) pivot_header: Arc, } pub struct RpcImpl { @@ -790,12 +790,23 @@ impl RpcImpl { bail!("Inconsistent state"); } + let pivot_header = if let Some(x) = self + .consensus + .get_data_manager() + .block_header_by_hash(&pivot_hash) + { + x + } else { + warn!("Cannot find pivot header when get block execution info: pivot hash {:?}", pivot_hash); + return Ok(None); + }; + Ok(Some(BlockExecInfo { block_receipts, block, epoch_number, maybe_state_root, - pivot_hash, + pivot_header, })) } @@ -840,7 +851,7 @@ impl RpcImpl { prior_gas_used, Some(exec_info.epoch_number), exec_info.block_receipts.block_number, - exec_info.block.block_header.base_price(), + exec_info.pivot_header.base_price(), exec_info.maybe_state_root.clone(), tx_exec_error_msg, *self.sync.network.get_network_type(), @@ -900,10 +911,10 @@ impl RpcImpl { }; // pivot chain reorg - if pivot_assumption != exec_info.pivot_hash { + if pivot_assumption != exec_info.pivot_header.hash() { bail!(pivot_assumption_failed( pivot_assumption, - exec_info.pivot_hash + exec_info.pivot_header.hash() )); } From 2bd9b5d806fd99889b837d3d18cb6e72e5bfd407 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 28 May 2024 15:21:46 +0800 Subject: [PATCH 117/137] add test for ignored transaction in non-pivot block by cip-137 --- tests/cip137_test.py | 158 +++++++++++++++++++++++++++++++++++++++++++ tests/conflux/rpc.py | 19 +++++- 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 tests/cip137_test.py diff --git a/tests/cip137_test.py b/tests/cip137_test.py new file mode 100644 index 0000000000..2aaf323f94 --- /dev/null +++ b/tests/cip137_test.py @@ -0,0 +1,158 @@ +from conflux.rpc import RpcClient +from test_framework.util import ( + assert_equal, +) + +from cfx_account import Account as CfxAccount + +from test_framework.test_framework import ConfluxTestFramework + +class CIP137Test(ConfluxTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def setup_network(self): + self.add_nodes(self.num_nodes) + self.start_node(0, ["--archive"]) + self.rpc = RpcClient(self.nodes[0]) + + # We need to ensure that the tx in B block + + # --- --- --- --- --- + # .- | A | <--- | C | <--- | D | <--- | E | <--- | F | <--- ... + # --- | --- --- --- --- --- + # ... <--- | P | <-* . + # --- | --- . + # .- | B | <........................................ + # --- + + # continuously fill transaction in C to F to increase base gas fee for F epoch + # then transaction in B block will fail + def run_test(self): + def base_fee_per_gas(epoch: str = "latest_mined"): + return self.rpc.fee_history(1, epoch)['base_fee_per_gas'][0] + + acct1 = CfxAccount.create() + acct2 = CfxAccount.create() + gas_price_level_1 = 2 + gas_price_level_2 = 10 + burnt_ratio = 0.5 + + assert base_fee_per_gas() < gas_price_level_1 * burnt_ratio + + # send 1000 CFX to each account + self.rpc.send_tx(self.rpc.new_tx(receiver=acct1.address, value=10**21,), True) + self.rpc.send_tx(self.rpc.new_tx(receiver=acct2.address, value=10**21), True) + block_p = self.rpc.block_by_epoch("latest_mined")["hash"] + + acct1_txs = [ + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=0, gas_price=gas_price_level_2), # expected to succeed + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=1, gas_price=gas_price_level_1), # expected to be ignored and can be resend later + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=2, gas_price=gas_price_level_2) # expected to be ignored + ] + + acct2_txs = [ + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=0, gas_price=gas_price_level_2), # expected to succeed + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=1, gas_price=gas_price_level_2), # expected to succeed + self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=2, gas_price=gas_price_level_2) # expected to succeed + ] + + + block_b = self.rpc.generate_custom_block( + parent_hash=block_p, referee=[], txs=[*acct1_txs, *acct2_txs] + ) + + genesis_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) + + # block a, c, d, e + block_a = self.rpc.generate_custom_block( + parent_hash=block_p, + referee=[], + txs=[ + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + gas=15000000, + nonce=(genesis_nonce := genesis_nonce + 1), + ) + for i in range(4) + ], + ) + block_c = self.rpc.generate_custom_block( + parent_hash=block_a, + referee=[], + txs=[ + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + gas=15000000, + nonce=(genesis_nonce := genesis_nonce + 1), + ) + for i in range(4) + ], + ) + block_d = self.rpc.generate_custom_block( + parent_hash=block_c, + referee=[], + txs=[ + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + gas=15000000, + nonce=(genesis_nonce := genesis_nonce + 1), + ) + for i in range(4) + ], + ) + block_e = self.rpc.generate_custom_block( + parent_hash=block_d, + referee=[], + txs=[ + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + gas=15000000, + nonce=(genesis_nonce := genesis_nonce + 1), + ) + for i in range(4) + ], + ) + block_f = self.rpc.generate_custom_block( + parent_hash=block_e, + referee=[block_b], + txs=[ + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + gas=15000000, + nonce=(genesis_nonce := genesis_nonce + 1), + ) + for i in range(4) + ], + ) + assert gas_price_level_2 > base_fee_per_gas() * burnt_ratio + assert gas_price_level_1 < base_fee_per_gas() * burnt_ratio + + # wait for epoch of block f executed + parent_block = block_f + for _ in range(30): + block = self.rpc.generate_custom_block(parent_hash = parent_block, referee = [], txs = []) + parent_block = block + + assert_equal(self.rpc.get_nonce(acct1.address), 1) + assert_equal(self.rpc.get_nonce(acct2.address), 3) + focusing_block = self.rpc.block_by_hash(block_b, True) + assert_equal(focusing_block["transactions"][0]["status"] ,"0x0") + assert_equal(focusing_block["transactions"][1]["status"] , None) + assert_equal(focusing_block["transactions"][1]["blockHash"] , None) + assert_equal(focusing_block["transactions"][2]["status"] , None) + assert_equal(focusing_block["transactions"][2]["blockHash"] , None) + + # as comparison + assert_equal(focusing_block["transactions"][3]["status"] , "0x0") + assert_equal(focusing_block["transactions"][4]["status"] , "0x0") + assert_equal(focusing_block["transactions"][5]["status"] , "0x0") + + self.rpc.generate_blocks(20, 5) + + # transactions shall be sent back to txpool and then get packed + assert_equal(self.rpc.get_nonce(acct1.address), 3) + + +if __name__ == "__main__": + CIP137Test().main() diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 66efb76676..4e76f6b6b7 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -1,6 +1,6 @@ import os import random -from typing import Optional, Union +from typing import Optional, Union, TypedDict from web3 import Web3 import eth_utils @@ -34,6 +34,11 @@ "to": b'', } +class CfxFeeHistoryResponse(TypedDict): + base_fee_per_gas: list[int] + gas_used_ratio: list[float] + reward: list[list[str]] # does not convert it currently + def convert_b32_address_field_to_hex(original_dict: dict, field_name: str): if original_dict is not None and field_name in original_dict and original_dict[field_name] not in [None, "null"]: @@ -580,6 +585,18 @@ def get_transaction_trace(self, tx_hash: str): def filter_trace(self, filter: dict): return self.node.trace_filter(filter) + + def fee_history(self, epoch_count: int, last_epoch: Union[int, str], reward_percentiles: Optional[list[float]]=None) -> CfxFeeHistoryResponse: + if reward_percentiles is None: + reward_percentiles = [50]*epoch_count + if isinstance(last_epoch, int): + last_epoch = hex(last_epoch) + rtn = self.node.cfx_feeHistory(hex(epoch_count), last_epoch, reward_percentiles) + rtn[ + 'base_fee_per_gas' + ] = [ int(v, 16) for v in rtn['base_fee_per_gas'] ] + return rtn + def wait_for_pos_register(self, priv_key=None, stake_value=2_000_000, voting_power=None, legacy=True, should_fail=False): if priv_key is None: From b69754978dd47e50b69fcd0c8797ed19e4c461a1 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 28 May 2024 16:26:27 +0800 Subject: [PATCH 118/137] chore(deps): add cfx-account dependency --- dev-support/dep_pip3.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-support/dep_pip3.sh b/dev-support/dep_pip3.sh index 40f7f2fe47..62559d494d 100755 --- a/dev-support/dep_pip3.sh +++ b/dev-support/dep_pip3.sh @@ -8,6 +8,7 @@ function install() { fi } +install git+https://github.com/conflux-fans/cfx-account.git@v1.1.0-beta.2 # install cfx-account lib and prepare for CIP-1559 tests install eth-utils install rlp==1.2.0 install py-ecc==5.2.0 From 58f9943f86d699080c14b1951e3795a5102bd788 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 28 May 2024 16:33:27 +0800 Subject: [PATCH 119/137] refactor: force rpc client new tx with param name --- tests/conflux/rpc.py | 2 +- tests/tx_consistency_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 4e76f6b6b7..eec1095f4f 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -365,7 +365,7 @@ def get_tx(self, tx_hash: str) -> dict: convert_b32_address_field_to_hex(tx, "contractCreated") return tx - def new_tx(self, sender=None, receiver=None, nonce=None, gas_price=1, gas=21000, value=100, data=b'', sign=True, + def new_tx(self, *, sender=None, receiver=None, nonce=None, gas_price=1, gas=21000, value=100, data=b'', sign=True, priv_key=None, storage_limit=None, epoch_height=None, chain_id=DEFAULT_PY_TEST_CHAIN_ID): if priv_key is None: priv_key = default_config["GENESIS_PRI_KEY"] diff --git a/tests/tx_consistency_test.py b/tests/tx_consistency_test.py index f3eead320b..a5db4ce784 100755 --- a/tests/tx_consistency_test.py +++ b/tests/tx_consistency_test.py @@ -148,7 +148,7 @@ def sample_node_indices(self): # randomly select N nodes to send tx. def send_tx(self, sender: Account, receiver: Account): client = RpcClient(self.nodes[0]) - tx = client.new_tx(sender.address, receiver.address, sender.nonce, value=9000, priv_key=sender.priv_key) + tx = client.new_tx(sender=sender.address, receiver=receiver.address, nonce=sender.nonce, value=9000, priv_key=sender.priv_key) def ensure_send_tx(node, tx): tx_hash = RpcClient(node).send_tx(tx) From 7dfb511e1894c41b7beedd654be6696bef72acfb Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Thu, 30 May 2024 17:23:55 +0800 Subject: [PATCH 120/137] tests: add cip-1559 tests --- tests/cip1559_test.py | 178 +++++++++++++++++++++++ tests/conflux/rpc.py | 70 ++++++++- tests/test_framework/simple_rpc_proxy.py | 3 +- tests/test_framework/test_framework.py | 2 +- tests/test_framework/test_node.py | 2 +- 5 files changed, 244 insertions(+), 11 deletions(-) create mode 100644 tests/cip1559_test.py diff --git a/tests/cip1559_test.py b/tests/cip1559_test.py new file mode 100644 index 0000000000..bc50fcb889 --- /dev/null +++ b/tests/cip1559_test.py @@ -0,0 +1,178 @@ +from conflux.rpc import RpcClient +from test_framework.util import ( + assert_equal, +) +from decimal import Decimal + +from cfx_account import Account as CfxAccount +from cfx_account.signers.local import LocalAccount as CfxLocalAccount + +from test_framework.test_framework import ConfluxTestFramework + +CORE_BLOCK_GAS_TARGET = 270000 +BURNT_RATIO = 0.5 +MIN_NATIVE_BASE_PRICE = 10000 + +class CIP1559Test(ConfluxTestFramework): + def set_test_params(self): + self.num_nodes = 1 + # self.conf_parameters["executive_trace"] = "true" + # self.conf_parameters["cip1559_transition_height"] = str(1) + self.conf_parameters["min_native_base_price"] = MIN_NATIVE_BASE_PRICE + + + def setup_network(self): + self.add_nodes(self.num_nodes) + self.start_node(0, ["--archive"]) + self.rpc = RpcClient(self.nodes[0]) + + # acct should have cfx + def increase_base_fee(self, acct: CfxLocalAccount=None, block_count=10, tx_per_block=4, gas_per_tx=13500000): + if acct is None: + acct = self.init_acct_with_cfx() + starting_nonce = self.rpc.get_nonce(acct.hex_address) + + for block_count in range(block_count): + self.rpc.generate_custom_block( + txs=[ + self.rpc.new_tx( + priv_key=acct.key, + receiver=CfxAccount.create().address, + gas=gas_per_tx, + nonce=starting_nonce + i + block_count * tx_per_block, + gas_price=self.rpc.base_fee_per_gas()*2 # give enough gas price to make the tx valid + ) + for i in range(tx_per_block) + ], + parent_hash=self.rpc.block_by_epoch("latest_mined")["hash"], + referee=[], + ) + + def test_block_base_fee_change(self, acct: CfxLocalAccount, epoch_to_test:int, tx_per_block=4, gas_per_tx=13500000): + starting_epoch = self.rpc.epoch_number() + self.increase_base_fee(acct, epoch_to_test, tx_per_block, gas_per_tx) + expected_base_fee_change_delta_rate = self.get_expected_base_fee_change_delta_rate(tx_per_block * gas_per_tx, 27000000) + + for i in range(starting_epoch+1, self.rpc.epoch_number()): + expected_current_base_fee = self.rpc.base_fee_per_gas(i-1) + int(self.rpc.base_fee_per_gas(i-1) * expected_base_fee_change_delta_rate) + assert_equal(self.rpc.base_fee_per_gas(i), expected_current_base_fee) + + + def get_expected_base_fee_change_delta_rate(self, sum_tx_gas_limit: int, block_target_gas_limit: int = None) -> Decimal: + if block_target_gas_limit is None: + block_target_gas_limit = CORE_BLOCK_GAS_TARGET + BASE_FEE_MAX_CHANGE_DENOMINATOR = 8 + return ((sum_tx_gas_limit-block_target_gas_limit) / Decimal(block_target_gas_limit)) / BASE_FEE_MAX_CHANGE_DENOMINATOR + + # default to 1000 CFX + def init_acct_with_cfx(self, drip: int=10**21) -> CfxLocalAccount: + self.rpc.send_tx( + self.rpc.new_tx( + receiver=(acct:=CfxAccount.create()).address, + value=drip, + gas_price=max(self.rpc.base_fee_per_gas()*2,MIN_NATIVE_BASE_PRICE) # avoid genisis zero gas price + ), + True, + ) + return acct + + def get_gas_charged(self, tx_hash: str) -> int: + gas_limit = int(self.rpc.get_tx(tx_hash)["gas"], 16) + gas_used = int(self.rpc.get_transaction_receipt(tx_hash)["gasUsed"], 16) + return max(int(3/4*gas_limit), gas_used) + + # TODO: currently only assert transaction in pivot block + # the transactions in non-pivot block is different + def assert_expected_effective_gas_fee(self, tx_hash: str): + data = self.rpc.get_tx(tx_hash) + max_fee_per_gas = int(data["maxFeePerGas"], 16) + max_priority_fee_per_gas = int(data["maxPriorityFeePerGas"], 16) + receipt = self.rpc.get_transaction_receipt(tx_hash) + effective_gas_price = int(receipt["effectiveGasPrice"], 16) + transaction_epoch = int(receipt["epochNumber"],16) + base_fee_per_gas = self.rpc.base_fee_per_gas(transaction_epoch) + # computed gas price + priority_fee_per_gas = effective_gas_price - base_fee_per_gas + assert_equal(priority_fee_per_gas, min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas)) + assert_equal(int(receipt["gasFee"], 16), effective_gas_price*self.get_gas_charged(tx_hash)) + + def test_balance_change(self, acct: CfxLocalAccount): + acct_balance = self.rpc.get_balance(acct.address) + h = self.rpc.send_tx( + self.rpc.new_typed_tx( + priv_key=acct.key.hex(), + receiver=CfxAccount.create().address, + max_fee_per_gas=self.rpc.base_fee_per_gas(), + max_priority_fee_per_gas=self.rpc.base_fee_per_gas(), + value=100, + ), + wait_for_receipt=True, + ) + receipt = self.rpc.get_transaction_receipt(h) + acct_new_balance = self.rpc.get_balance(acct.address) + assert_equal(acct_new_balance, acct_balance - int(receipt["gasFee"], 16) - 100) + + def test_max_fee_not_enough_for_current_base_fee(self): + self.increase_base_fee(block_count=10) + initial_base_fee = self.rpc.base_fee_per_gas() + + self.log.info(f"initla base fee: {initial_base_fee}") + + # 112.5% ^ 10 + self.increase_base_fee(block_count=10) + self.log.info(f"increase base fee by 112.5% ^ 10") + self.log.info(f"new base fee: {self.rpc.base_fee_per_gas()}") + assert self.rpc.base_fee_per_gas() > initial_base_fee + self.log.info(f"sending new transaction with max_fee_per_gas: {initial_base_fee}") + # as the transaction's max fee per gas is not enough for current base fee, + # the transaction will become pending until the base fee drops + # we will observe the base fee of the block the transaction is in + h = self.rpc.send_tx( + self.rpc.new_typed_tx( + receiver=CfxAccount.create().address, + max_fee_per_gas=initial_base_fee, + ), + wait_for_receipt=True, + ) + + tx_base_fee = self.rpc.base_fee_per_gas(self.rpc.get_transaction_receipt(h)["epochNumber"]) + self.log.info(f"epoch base fee for transaction accepted: {tx_base_fee}") + assert tx_base_fee <= initial_base_fee + + def test_type_2_tx(self): + + self.assert_expected_effective_gas_fee(self.rpc.send_tx( + self.rpc.new_typed_tx( + receiver=CfxAccount.create().address, + max_fee_per_gas=self.rpc.base_fee_per_gas(), + max_priority_fee_per_gas=self.rpc.base_fee_per_gas(), + ), + wait_for_receipt=True, + )) + self.assert_expected_effective_gas_fee(self.rpc.send_tx( + self.rpc.new_typed_tx( + receiver=CfxAccount.create().address, + max_fee_per_gas=self.rpc.base_fee_per_gas(), + max_priority_fee_per_gas=0, + ), + wait_for_receipt=True, + )) + self.test_balance_change(self.init_acct_with_cfx()) + + def run_test(self): + self.rpc.generate_blocks(5) + + # test fee increasing + self.test_block_base_fee_change(self.init_acct_with_cfx(), 20, 4, 13500000) + self.test_block_base_fee_change(self.init_acct_with_cfx(), 20, 6, 8000000) + self.test_block_base_fee_change(self.init_acct_with_cfx(), 20, 3, 13500000) + # note: as min base fee is provided, we use less epochs + self.test_block_base_fee_change(self.init_acct_with_cfx(), 10, 1, 13500000) + self.test_block_base_fee_change(self.init_acct_with_cfx(), 10, 2, 10000000) + + self.test_type_2_tx() + self.test_max_fee_not_enough_for_current_base_fee() + + +if __name__ == "__main__": + CIP1559Test().main() diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index eec1095f4f..6ab44a9289 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -1,13 +1,15 @@ import os import random -from typing import Optional, Union, TypedDict +from typing import cast, Optional, Union, TypedDict, Any from web3 import Web3 import eth_utils +from cfx_account import Account as CfxAccount +from eth_account.datastructures import SignedTransaction import rlp import json -from .address import hex_to_b32_address, b32_address_to_hex +from .address import hex_to_b32_address, b32_address_to_hex, DEFAULT_PY_TEST_CHAIN_ID from .config import DEFAULT_PY_TEST_CHAIN_ID, default_config from .transactions import CONTRACT_DEFAULT_GAS, Transaction, UnsignedTransaction from .filter import Filter @@ -25,6 +27,7 @@ assert_equal, wait_until, checktx, get_contract_instance ) +from test_framework.test_node import TestNode file_dir = os.path.dirname(os.path.realpath(__file__)) REQUEST_BASE = { @@ -38,6 +41,8 @@ class CfxFeeHistoryResponse(TypedDict): base_fee_per_gas: list[int] gas_used_ratio: list[float] reward: list[list[str]] # does not convert it currently + +# class TransactionReceipt(TypedDict): def convert_b32_address_field_to_hex(original_dict: dict, field_name: str): @@ -46,8 +51,8 @@ def convert_b32_address_field_to_hex(original_dict: dict, field_name: str): class RpcClient: - def __init__(self, node=None, auto_restart=False, log=None): - self.node = node + def __init__(self, node: Optional[TestNode]=None, auto_restart=False, log=None): + self.node: TestNode = node # type: ignore self.auto_restart = auto_restart self.log = log @@ -134,12 +139,13 @@ def generate_block_with_parent(self, parent_hash: str, referee: list = None, num assert_is_hash_string(block_hash) return block_hash - def generate_custom_block(self, parent_hash: str, referee: list, txs: list) -> str: + def generate_custom_block(self, parent_hash: str, referee: list, txs: list[Union[Transaction, SignedTransaction]]) -> str: assert_is_hash_string(parent_hash) for r in referee: assert_is_hash_string(r) + encoded_txs = eth_utils.encode_hex(rlp.encode(txs)) block_hash = self.node.test_generatecustomblock(parent_hash, referee, encoded_txs) @@ -193,6 +199,9 @@ def get_code(self, address: str, epoch: Union[str, dict] = None) -> str: def gas_price(self) -> int: return int(self.node.cfx_gasPrice(), 0) + def base_fee_per_gas(self, epoch: Union[int,str] = "latest_mined"): + return self.fee_history(1, epoch)['base_fee_per_gas'][0] + def get_block_reward_info(self, epoch: str): reward = self.node.cfx_getBlockRewardInfo(epoch) convert_b32_address_field_to_hex(reward, "author") @@ -301,8 +310,12 @@ def send_raw_tx(self, raw_tx: str, wait_for_catchup=True) -> str: def clear_tx_pool(self): self.node.txpool_clear() - def send_tx(self, tx: Transaction, wait_for_receipt=False, wait_for_catchup=True) -> str: - encoded = eth_utils.encode_hex(rlp.encode(tx)) + # a temporary patch for transaction compatibity + def send_tx(self, tx: Union[Transaction, SignedTransaction], wait_for_receipt=False, wait_for_catchup=True) -> str: + if isinstance(tx, SignedTransaction): + encoded = cast(str, tx.rawTransaction.hex()) + else: + encoded = eth_utils.encode_hex(rlp.encode(tx)) tx_hash = self.send_raw_tx(encoded, wait_for_catchup=wait_for_catchup) if wait_for_receipt: @@ -391,6 +404,47 @@ def new_tx(self, *, sender=None, receiver=None, nonce=None, gas_price=1, gas=210 return tx.sign(priv_key) else: return tx + + def new_typed_tx(self, *, type_=2, receiver=None, nonce=None, max_fee_per_gas=None,max_priority_fee_per_gas=0, access_list=[], gas=21000, value=100, data=b'', + priv_key=None, storage_limit=0, epoch_height=None, chain_id=DEFAULT_PY_TEST_CHAIN_ID + ) -> SignedTransaction: + + if priv_key: + acct = CfxAccount.from_key(priv_key, DEFAULT_PY_TEST_CHAIN_ID) + else: + acct = CfxAccount.from_key(default_config["GENESIS_PRI_KEY"], DEFAULT_PY_TEST_CHAIN_ID) + tx = {} + tx["type"] = type_ + tx["gas"] = gas + tx["storageLimit"] = storage_limit + tx["value"] = value + tx["data"] = data + tx["maxPriorityFeePerGas"] = max_priority_fee_per_gas + tx["chainId"] = chain_id + tx["to"] = receiver + + if nonce is None: + nonce = self.get_nonce(acct.hex_address) + tx["nonce"] = nonce + + if access_list != []: + def format_access_list(a_list): + rtn = [] + for item in a_list: + rtn.append({"address": item['address'], "storageKeys": item['storage_keys']}) + + access_list = format_access_list(access_list) + tx["accessList"] = access_list + + if epoch_height is None: + epoch_height = self.epoch_number() + tx["epochHeight"] = epoch_height + + # ensuring transaction can be sent + if max_fee_per_gas is None: + max_fee_per_gas = self.base_fee_per_gas('latest_mined') + 1 + tx["maxFeePerGas"] = max_fee_per_gas + return acct.sign_transaction(tx) def new_contract_tx(self, receiver: Optional[str], data_hex: str = None, sender=None, priv_key=None, nonce=None, gas_price=1, @@ -455,7 +509,7 @@ def disconnect_peer(self, node_id: str, node_op: str = None) -> int: def chain(self) -> list: return self.node.cfx_getChain() - def get_transaction_receipt(self, tx_hash: str) -> dict: + def get_transaction_receipt(self, tx_hash: str) -> dict[str, Any]: assert_is_hash_string(tx_hash) r = self.node.cfx_getTransactionReceipt(tx_hash) if r is None: diff --git a/tests/test_framework/simple_rpc_proxy.py b/tests/test_framework/simple_rpc_proxy.py index 6105f26032..71cd2d7bb1 100644 --- a/tests/test_framework/simple_rpc_proxy.py +++ b/tests/test_framework/simple_rpc_proxy.py @@ -1,4 +1,5 @@ import time +from typing import Any import jsonrpcclient.client from jsonrpcclient.exceptions import ReceivedErrorResponseError @@ -25,7 +26,7 @@ def __init__(self, client, method, timeout, node): self.timeout = timeout self.node = node - def __call__(self, *args, **argsn): + def __call__(self, *args, **argsn) -> Any: if argsn: raise ValueError('json rpc 2 only supports array arguments') from jsonrpcclient.requests import Request diff --git a/tests/test_framework/test_framework.py b/tests/test_framework/test_framework.py index 2abc46690c..b66c0957fa 100644 --- a/tests/test_framework/test_framework.py +++ b/tests/test_framework/test_framework.py @@ -73,7 +73,7 @@ class ConfluxTestFramework: def __init__(self): """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" self.setup_clean_chain = True - self.nodes = [] + self.nodes: list[TestNode] = [] self.network_thread = None self.mocktime = 0 self.rpc_timewait = CONFLUX_RPC_WAIT_TIMEOUT diff --git a/tests/test_framework/test_node.py b/tests/test_framework/test_node.py index 05a240739d..2cbac1ce5b 100644 --- a/tests/test_framework/test_node.py +++ b/tests/test_framework/test_node.py @@ -74,7 +74,7 @@ def __init__(self, index, datadir, rpchost, confluxd, rpc_timeout=None, remote=F self.running = False self.process = None self.rpc_connected = False - self.rpc = None + self.rpc: SimpleRpcProxy = None # type: ignore self.ethrpc = None self.ethrpc_connected = False self.log = logging.getLogger('TestFramework.node%d' % index) From 00e07cb9b77178c105c48c424c5568028a8ebe3c Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Thu, 30 May 2024 18:22:10 +0800 Subject: [PATCH 121/137] refactor: extract base fee manipulation interface --- tests/cip1559_test.py | 28 +++++------------------ tests/test_framework/util.py | 44 +++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/tests/cip1559_test.py b/tests/cip1559_test.py index bc50fcb889..ad62cb257d 100644 --- a/tests/cip1559_test.py +++ b/tests/cip1559_test.py @@ -8,6 +8,7 @@ from cfx_account.signers.local import LocalAccount as CfxLocalAccount from test_framework.test_framework import ConfluxTestFramework +from test_framework.util import generate_blocks_for_base_fee_manipulation CORE_BLOCK_GAS_TARGET = 270000 BURNT_RATIO = 0.5 @@ -16,8 +17,6 @@ class CIP1559Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 - # self.conf_parameters["executive_trace"] = "true" - # self.conf_parameters["cip1559_transition_height"] = str(1) self.conf_parameters["min_native_base_price"] = MIN_NATIVE_BASE_PRICE @@ -27,30 +26,15 @@ def setup_network(self): self.rpc = RpcClient(self.nodes[0]) # acct should have cfx - def increase_base_fee(self, acct: CfxLocalAccount=None, block_count=10, tx_per_block=4, gas_per_tx=13500000): + def change_base_fee(self, acct: CfxLocalAccount=None, block_count=10, tx_per_block=4, gas_per_tx=13500000): if acct is None: acct = self.init_acct_with_cfx() - starting_nonce = self.rpc.get_nonce(acct.hex_address) + generate_blocks_for_base_fee_manipulation(self.rpc, acct, block_count, tx_per_block, gas_per_tx) - for block_count in range(block_count): - self.rpc.generate_custom_block( - txs=[ - self.rpc.new_tx( - priv_key=acct.key, - receiver=CfxAccount.create().address, - gas=gas_per_tx, - nonce=starting_nonce + i + block_count * tx_per_block, - gas_price=self.rpc.base_fee_per_gas()*2 # give enough gas price to make the tx valid - ) - for i in range(tx_per_block) - ], - parent_hash=self.rpc.block_by_epoch("latest_mined")["hash"], - referee=[], - ) def test_block_base_fee_change(self, acct: CfxLocalAccount, epoch_to_test:int, tx_per_block=4, gas_per_tx=13500000): starting_epoch = self.rpc.epoch_number() - self.increase_base_fee(acct, epoch_to_test, tx_per_block, gas_per_tx) + self.change_base_fee(acct, epoch_to_test, tx_per_block, gas_per_tx) expected_base_fee_change_delta_rate = self.get_expected_base_fee_change_delta_rate(tx_per_block * gas_per_tx, 27000000) for i in range(starting_epoch+1, self.rpc.epoch_number()): @@ -113,13 +97,13 @@ def test_balance_change(self, acct: CfxLocalAccount): assert_equal(acct_new_balance, acct_balance - int(receipt["gasFee"], 16) - 100) def test_max_fee_not_enough_for_current_base_fee(self): - self.increase_base_fee(block_count=10) + self.change_base_fee(block_count=10) initial_base_fee = self.rpc.base_fee_per_gas() self.log.info(f"initla base fee: {initial_base_fee}") # 112.5% ^ 10 - self.increase_base_fee(block_count=10) + self.change_base_fee(block_count=10) self.log.info(f"increase base fee by 112.5% ^ 10") self.log.info(f"new base fee: {self.rpc.base_fee_per_gas()}") assert self.rpc.base_fee_per_gas() > initial_base_fee diff --git a/tests/test_framework/util.py b/tests/test_framework/util.py index c8b8aac316..8126c3133b 100644 --- a/tests/test_framework/util.py +++ b/tests/test_framework/util.py @@ -10,12 +10,14 @@ import re from subprocess import CalledProcessError, check_output import time -from typing import Optional, Callable, List, TYPE_CHECKING, cast +from typing import Optional, Callable, List, TYPE_CHECKING, cast, Tuple, Union import socket import threading import jsonrpcclient.exceptions import solcx import web3 +from cfx_account import Account as CfxAccount +from cfx_account.signers.local import LocalAccount as CfxLocalAccount from sys import platform import yaml import shutil @@ -806,3 +808,43 @@ def test_rpc_call_with_block_object(client: "RpcClient", txs: List, rpc_call: Ca assert(expected_result_lambda(result1)) assert_equal(result2, result1) + +# acct should have cfx +# create a chain of blocks with specified transfer tx with specified num and gas +# return the last block's hash and acct nonce +def generate_blocks_for_base_fee_manipulation(rpc: "RpcClient", acct: Union[CfxLocalAccount, str], block_count=10, tx_per_block=4, gas_per_tx=13500000,initial_parent_hash:str = None) -> Tuple[str, int]: + if isinstance(acct, str): + acct = CfxAccount.from_key(acct) + starting_nonce: int = rpc.get_nonce(acct.hex_address) + + if initial_parent_hash is None: + initial_parent_hash = cast(str, rpc.block_by_epoch("latest_mined")["hash"]) + + block_pointer = initial_parent_hash + for block_count in range(block_count): + block_pointer, starting_nonce = generate_single_block_for_base_fee_manipulation(rpc, acct, tx_per_block=tx_per_block, gas_per_tx=gas_per_tx,parent_hash=block_pointer, starting_nonce=starting_nonce) + + return block_pointer, starting_nonce + block_count * tx_per_block + +def generate_single_block_for_base_fee_manipulation(rpc: "RpcClient", acct: CfxLocalAccount, referee:list[str] =[], tx_per_block=4, gas_per_tx=13500000,parent_hash:str = None, starting_nonce: int = None) -> Tuple[str, int]: + if starting_nonce is None: + starting_nonce = cast(int, rpc.get_nonce(acct.hex_address)) + + if parent_hash is None: + parent_hash = cast(str, rpc.block_by_epoch("latest_mined")["hash"]) + + new_block = rpc.generate_custom_block( + txs=[ + rpc.new_tx( + priv_key=acct.key, + receiver=acct.address, + gas=gas_per_tx, + nonce=starting_nonce + i , + gas_price=rpc.base_fee_per_gas()*2 # give enough gas price to make the tx valid + ) + for i in range(tx_per_block) + ], + parent_hash=parent_hash, + referee=referee, + ) + return new_block, starting_nonce + tx_per_block From 7c7d5a8de2a92cac4a00b7fbeea9437780131692 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Fri, 31 May 2024 14:05:21 +0800 Subject: [PATCH 122/137] refactor: cip137 test refactor --- tests/cip137_test.py | 121 +++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 78 deletions(-) diff --git a/tests/cip137_test.py b/tests/cip137_test.py index 2aaf323f94..e4e54296e8 100644 --- a/tests/cip137_test.py +++ b/tests/cip137_test.py @@ -1,15 +1,23 @@ -from conflux.rpc import RpcClient +from typing import Union, Tuple +from conflux.rpc import RpcClient, default_config from test_framework.util import ( assert_equal, ) from cfx_account import Account as CfxAccount +from cfx_account.signers.local import LocalAccount as CfxLocalAccount from test_framework.test_framework import ConfluxTestFramework +from test_framework.util import generate_blocks_for_base_fee_manipulation, generate_single_block_for_base_fee_manipulation + +MIN_NATIVE_BASE_PRICE = 10000 +BURNT_RATIO = 0.5 + class CIP137Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 + self.conf_parameters["min_native_base_price"] = MIN_NATIVE_BASE_PRICE def setup_network(self): self.add_nodes(self.num_nodes) @@ -17,7 +25,7 @@ def setup_network(self): self.rpc = RpcClient(self.nodes[0]) # We need to ensure that the tx in B block - + # B and ending block will be in the same epoch # --- --- --- --- --- # .- | A | <--- | C | <--- | D | <--- | E | <--- | F | <--- ... # --- | --- --- --- --- --- @@ -25,26 +33,47 @@ def setup_network(self): # --- | --- . # .- | B | <........................................ # --- + # ensures txs to be included in B block and the ending block (e.g. F) base gas price is greater than the specified target_minimum_base_fee (not guaranteed to be the first block) + # returns the ending block hash + def construct_non_pivot_block(self, acct: CfxLocalAccount, txs: list, starting_block_hash: str=None, epoch_delta: int=5) -> Tuple[str, str]: + + if epoch_delta <=0: + raise ValueError("epoch_delta must be positive") + + if starting_block_hash is None: + starting_block_hash = self.rpc.block_by_epoch("latest_mined")["hash"] + + # create the non-pivot block + non_pivot_block = self.rpc.generate_custom_block(parent_hash=starting_block_hash, txs=txs, referee=[]) + ending_but_two_block, account_next_nonce = generate_blocks_for_base_fee_manipulation( + self.rpc, acct, epoch_delta, initial_parent_hash=starting_block_hash + ) + ending_block, _ = generate_single_block_for_base_fee_manipulation( + self.rpc, acct, [non_pivot_block], parent_hash=ending_but_two_block, starting_nonce=account_next_nonce + ) + return non_pivot_block, ending_block + + + + # TODO: test in pivot block transaction will not be included if burnt_ratio*base_gas_fee_per_gas < max_fee_per_gas < base_gas_fee_per_gas # continuously fill transaction in C to F to increase base gas fee for F epoch # then transaction in B block will fail def run_test(self): - def base_fee_per_gas(epoch: str = "latest_mined"): - return self.rpc.fee_history(1, epoch)['base_fee_per_gas'][0] acct1 = CfxAccount.create() acct2 = CfxAccount.create() - gas_price_level_1 = 2 - gas_price_level_2 = 10 - burnt_ratio = 0.5 - assert base_fee_per_gas() < gas_price_level_1 * burnt_ratio + # assert self.rpc.base_fee_per_gas() < gas_price_level_1 * burnt_ratio # send 1000 CFX to each account - self.rpc.send_tx(self.rpc.new_tx(receiver=acct1.address, value=10**21,), True) - self.rpc.send_tx(self.rpc.new_tx(receiver=acct2.address, value=10**21), True) + self.rpc.send_tx(self.rpc.new_tx(receiver=acct1.address, value=10**21, gas_price=MIN_NATIVE_BASE_PRICE), True) + self.rpc.send_tx(self.rpc.new_tx(receiver=acct2.address, value=10**21, gas_price=MIN_NATIVE_BASE_PRICE), True) block_p = self.rpc.block_by_epoch("latest_mined")["hash"] + gas_price_level_1 = MIN_NATIVE_BASE_PRICE + gas_price_level_2 = self.rpc.base_fee_per_gas() * 10 + acct1_txs = [ self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=0, gas_price=gas_price_level_2), # expected to succeed self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=1, gas_price=gas_price_level_1), # expected to be ignored and can be resend later @@ -57,76 +86,12 @@ def base_fee_per_gas(epoch: str = "latest_mined"): self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=2, gas_price=gas_price_level_2) # expected to succeed ] - - block_b = self.rpc.generate_custom_block( - parent_hash=block_p, referee=[], txs=[*acct1_txs, *acct2_txs] + block_b, block_f = self.construct_non_pivot_block( + CfxAccount.from_key(default_config["GENESIS_PRI_KEY"]), [*acct1_txs, *acct2_txs], starting_block_hash=block_p, epoch_delta=5 ) - genesis_nonce = self.rpc.get_nonce(self.rpc.GENESIS_ADDR) - - # block a, c, d, e - block_a = self.rpc.generate_custom_block( - parent_hash=block_p, - referee=[], - txs=[ - self.rpc.new_tx( - receiver=self.rpc.rand_addr(), - gas=15000000, - nonce=(genesis_nonce := genesis_nonce + 1), - ) - for i in range(4) - ], - ) - block_c = self.rpc.generate_custom_block( - parent_hash=block_a, - referee=[], - txs=[ - self.rpc.new_tx( - receiver=self.rpc.rand_addr(), - gas=15000000, - nonce=(genesis_nonce := genesis_nonce + 1), - ) - for i in range(4) - ], - ) - block_d = self.rpc.generate_custom_block( - parent_hash=block_c, - referee=[], - txs=[ - self.rpc.new_tx( - receiver=self.rpc.rand_addr(), - gas=15000000, - nonce=(genesis_nonce := genesis_nonce + 1), - ) - for i in range(4) - ], - ) - block_e = self.rpc.generate_custom_block( - parent_hash=block_d, - referee=[], - txs=[ - self.rpc.new_tx( - receiver=self.rpc.rand_addr(), - gas=15000000, - nonce=(genesis_nonce := genesis_nonce + 1), - ) - for i in range(4) - ], - ) - block_f = self.rpc.generate_custom_block( - parent_hash=block_e, - referee=[block_b], - txs=[ - self.rpc.new_tx( - receiver=self.rpc.rand_addr(), - gas=15000000, - nonce=(genesis_nonce := genesis_nonce + 1), - ) - for i in range(4) - ], - ) - assert gas_price_level_2 > base_fee_per_gas() * burnt_ratio - assert gas_price_level_1 < base_fee_per_gas() * burnt_ratio + assert gas_price_level_2 > self.rpc.base_fee_per_gas() * BURNT_RATIO + assert gas_price_level_1 < self.rpc.base_fee_per_gas() * BURNT_RATIO, f"gas_price_level_1 {gas_price_level_1} should be less than {self.rpc.base_fee_per_gas() * BURNT_RATIO}" # wait for epoch of block f executed parent_block = block_f From ccf44b724e4cc9a1d822bf394820e69e8fe48e1a Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:26:57 +0800 Subject: [PATCH 123/137] test: non-pivot block transaction fee setting --- tests/cip137_test.py | 214 ++++++++++++++++++++++++++++++++---------- tests/cip1559_test.py | 2 + tests/conflux/rpc.py | 10 +- 3 files changed, 176 insertions(+), 50 deletions(-) diff --git a/tests/cip137_test.py b/tests/cip137_test.py index e4e54296e8..80dc1c81e2 100644 --- a/tests/cip137_test.py +++ b/tests/cip137_test.py @@ -8,7 +8,10 @@ from cfx_account.signers.local import LocalAccount as CfxLocalAccount from test_framework.test_framework import ConfluxTestFramework -from test_framework.util import generate_blocks_for_base_fee_manipulation, generate_single_block_for_base_fee_manipulation +from test_framework.util import ( + generate_blocks_for_base_fee_manipulation, + generate_single_block_for_base_fee_manipulation, +) MIN_NATIVE_BASE_PRICE = 10000 BURNT_RATIO = 0.5 @@ -26,97 +29,210 @@ def setup_network(self): # We need to ensure that the tx in B block # B and ending block will be in the same epoch - # --- --- --- --- --- - # .- | A | <--- | C | <--- | D | <--- | E | <--- | F | <--- ... - # --- | --- --- --- --- --- - # ... <--- | P | <-* . - # --- | --- . - # .- | B | <........................................ + # --- --- --- --- --- --- + # .- | A | <--- | C | <--- | D | <--- | E | <--- | F | <--- | G | ... + # --- | --- --- --- --- --- --- + # ... <--- | P | <-* . + # --- | --- . + # .- | B | <................................................... # --- # ensures txs to be included in B block and the ending block (e.g. F) base gas price is greater than the specified target_minimum_base_fee (not guaranteed to be the first block) # returns the ending block hash - def construct_non_pivot_block(self, acct: CfxLocalAccount, txs: list, starting_block_hash: str=None, epoch_delta: int=5) -> Tuple[str, str]: - - if epoch_delta <=0: + def construct_non_pivot_block( + self, + acct: CfxLocalAccount, + txs: list, + starting_block_hash: str = None, + epoch_delta: int = 6, # 1.125^6 -> 2.027 which would make the initial tx invalid + ) -> Tuple[str, str]: + + if epoch_delta <= 0: raise ValueError("epoch_delta must be positive") - + if starting_block_hash is None: starting_block_hash = self.rpc.block_by_epoch("latest_mined")["hash"] - + # create the non-pivot block - non_pivot_block = self.rpc.generate_custom_block(parent_hash=starting_block_hash, txs=txs, referee=[]) - ending_but_two_block, account_next_nonce = generate_blocks_for_base_fee_manipulation( - self.rpc, acct, epoch_delta, initial_parent_hash=starting_block_hash + non_pivot_block = self.rpc.generate_custom_block( + parent_hash=starting_block_hash, txs=txs, referee=[] + ) + ending_but_two_block, account_next_nonce = ( + generate_blocks_for_base_fee_manipulation( + self.rpc, acct, epoch_delta-1, initial_parent_hash=starting_block_hash + ) ) ending_block, _ = generate_single_block_for_base_fee_manipulation( - self.rpc, acct, [non_pivot_block], parent_hash=ending_but_two_block, starting_nonce=account_next_nonce + self.rpc, + acct, + [non_pivot_block], + parent_hash=ending_but_two_block, + starting_nonce=account_next_nonce, ) return non_pivot_block, ending_block - - - + + def init_acct_with_cfx(self, drip: int = 10**21) -> CfxLocalAccount: + self.rpc.send_tx( + self.rpc.new_tx( + receiver=(acct := CfxAccount.create()).address, + value=drip, + gas_price=max( + self.rpc.base_fee_per_gas() * 2, MIN_NATIVE_BASE_PRICE + ), # avoid genisis zero gas price + ), + True, + ) + return acct + # TODO: test in pivot block transaction will not be included if burnt_ratio*base_gas_fee_per_gas < max_fee_per_gas < base_gas_fee_per_gas + # low max_fee means the transaction max fee is less than the epoch's base fee * burnt ratio + # def test_transaction_ignored_in_non_pivot_block_if_low_max_fee(self): + + def log_tx_fee_info(self, tx_hash: str): + self.log.info(f'tx hash: {tx_hash}') + data = self.rpc.get_tx(tx_hash) + + max_fee_per_gas = int(data["maxFeePerGas"], 16) + max_priority_fee_per_gas = int(data["maxPriorityFeePerGas"], 16) + receipt = self.rpc.get_transaction_receipt(tx_hash) + # print(receipt) + + # effective_gas_price = int(receipt["effectiveGasPrice"], 16) + # transaction_epoch = int(receipt["epochNumber"],16) + # base_fee_per_gas = self.rpc.base_fee_per_gas(transaction_epoch) + # computed gas price + # priority_fee_per_gas = effective_gas_price - base_fee_per_gas + self.log.info(f'max fee per gas: {max_fee_per_gas}') + self.log.info(f'max priority fee per gas: {max_priority_fee_per_gas}') + self.log.info(f'tx status: {data["status"]}') + if receipt: + self.log.info(f'tx block hash: {receipt["blockHash"]}') + self.log.info(f'tx outcome status: {receipt["outcomeStatus"]}') + else: + self.log.info(f'tx receipt is None: {receipt is None}') + # self.log.info(f'effective gas price: {effective_gas_price}') + # self.log.info(f'base fee per gas: {base_fee_per_gas}') # continuously fill transaction in C to F to increase base gas fee for F epoch # then transaction in B block will fail def run_test(self): - acct1 = CfxAccount.create() - acct2 = CfxAccount.create() - + acct1 = self.init_acct_with_cfx() + acct2 = self.init_acct_with_cfx() + # assert self.rpc.base_fee_per_gas() < gas_price_level_1 * burnt_ratio - # send 1000 CFX to each account - self.rpc.send_tx(self.rpc.new_tx(receiver=acct1.address, value=10**21, gas_price=MIN_NATIVE_BASE_PRICE), True) - self.rpc.send_tx(self.rpc.new_tx(receiver=acct2.address, value=10**21, gas_price=MIN_NATIVE_BASE_PRICE), True) block_p = self.rpc.block_by_epoch("latest_mined")["hash"] - + gas_price_level_1 = MIN_NATIVE_BASE_PRICE + gas_price_level_1_5 = int(MIN_NATIVE_BASE_PRICE * 1.5) gas_price_level_2 = self.rpc.base_fee_per_gas() * 10 - + acct1_txs = [ - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=0, gas_price=gas_price_level_2), # expected to succeed - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=1, gas_price=gas_price_level_1), # expected to be ignored and can be resend later - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct1.key, nonce=2, gas_price=gas_price_level_2) # expected to be ignored + self.rpc.new_typed_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct1.key, + nonce=0, + max_fee_per_gas=gas_price_level_2, + ), # expected to succeed + self.rpc.new_typed_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct1.key, + nonce=1, + max_fee_per_gas=gas_price_level_1_5, + ), # expected to succeed with max fee less than epoch base gas fee + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct1.key, + nonce=2, + gas_price=gas_price_level_1, + ), # expected to be ignored and can be resend later + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct1.key, + nonce=3, + gas_price=gas_price_level_2, + ), # expected to be ignored ] - + acct2_txs = [ - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=0, gas_price=gas_price_level_2), # expected to succeed - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=1, gas_price=gas_price_level_2), # expected to succeed - self.rpc.new_tx(receiver=self.rpc.rand_addr(), priv_key=acct2.key, nonce=2, gas_price=gas_price_level_2) # expected to succeed + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct2.key, + nonce=0, + gas_price=gas_price_level_2, + ), # expected to succeed + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct2.key, + nonce=1, + gas_price=gas_price_level_2, + ), # expected to succeed + self.rpc.new_tx( + receiver=self.rpc.rand_addr(), + priv_key=acct2.key, + nonce=2, + gas_price=gas_price_level_2, + ), # expected to succeed ] - + block_b, block_f = self.construct_non_pivot_block( - CfxAccount.from_key(default_config["GENESIS_PRI_KEY"]), [*acct1_txs, *acct2_txs], starting_block_hash=block_p, epoch_delta=5 + CfxAccount.from_key(default_config["GENESIS_PRI_KEY"]), + [*acct1_txs, *acct2_txs], + starting_block_hash=block_p, + epoch_delta=6, # 1.125^6 -> 2.03 ) + + self.log.info(f"current base fee per gas: {self.rpc.base_fee_per_gas()}") + # we are ensuring the gas price order: + # gas_price_level_1 < current_base_fee * burnt_ratio < gas_price_level_1_5 < current_base_fee < gas_price_level_2 assert gas_price_level_2 > self.rpc.base_fee_per_gas() * BURNT_RATIO - assert gas_price_level_1 < self.rpc.base_fee_per_gas() * BURNT_RATIO, f"gas_price_level_1 {gas_price_level_1} should be less than {self.rpc.base_fee_per_gas() * BURNT_RATIO}" + assert ( + gas_price_level_1 < self.rpc.base_fee_per_gas() * BURNT_RATIO + ), f"gas_price_level_1 {gas_price_level_1} should be less than {self.rpc.base_fee_per_gas() * BURNT_RATIO}" # wait for epoch of block f executed parent_block = block_f for _ in range(30): - block = self.rpc.generate_custom_block(parent_hash = parent_block, referee = [], txs = []) + block = self.rpc.generate_custom_block( + parent_hash=parent_block, referee=[], txs=[] + ) parent_block = block - assert_equal(self.rpc.get_nonce(acct1.address), 1) + assert_equal(self.rpc.get_nonce(acct1.address), 2) assert_equal(self.rpc.get_nonce(acct2.address), 3) focusing_block = self.rpc.block_by_hash(block_b, True) - assert_equal(focusing_block["transactions"][0]["status"] ,"0x0") - assert_equal(focusing_block["transactions"][1]["status"] , None) - assert_equal(focusing_block["transactions"][1]["blockHash"] , None) - assert_equal(focusing_block["transactions"][2]["status"] , None) - assert_equal(focusing_block["transactions"][2]["blockHash"] , None) + epoch = int(focusing_block["epochNumber"],16) + + self.log.info(f"epoch of block b: {epoch}") + self.log.info(f"heigth of block b: {int(focusing_block['height'], 16)}") + self.log.info(f"base_fee_per_gas for epoch {epoch}: {self.rpc.base_fee_per_gas(epoch)}") + self.log.info(f"burnt_fee_per_gas for epoch {epoch}: {self.rpc.base_fee_per_gas(epoch) * 0.5}") + self.log.info(f"least base fee for epoch {epoch}: {self.rpc.base_fee_per_gas(epoch) * BURNT_RATIO}") + self.log.info(f"transactions in block b: {self.rpc.block_by_hash(block_b)['transactions']}") + self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][0]) + self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][1]) + + # print(focusing_block["transactions"]) + assert_equal(focusing_block["transactions"][0]["status"], "0x0") + self.log_tx_fee_info(focusing_block["transactions"][1]["hash"]) + assert_equal(focusing_block["transactions"][1]["status"], "0x0") + # assert_equal(focusing_block["transactions"][1]["blockHash"], None) + assert_equal(focusing_block["transactions"][2]["status"], None) + assert_equal(focusing_block["transactions"][2]["blockHash"], None) + assert_equal(focusing_block["transactions"][3]["status"], None) + assert_equal(focusing_block["transactions"][3]["blockHash"], None) + # as comparison - assert_equal(focusing_block["transactions"][3]["status"] , "0x0") - assert_equal(focusing_block["transactions"][4]["status"] , "0x0") - assert_equal(focusing_block["transactions"][5]["status"] , "0x0") + assert_equal(focusing_block["transactions"][4]["status"], "0x0") + assert_equal(focusing_block["transactions"][5]["status"], "0x0") + assert_equal(focusing_block["transactions"][6]["status"], "0x0") self.rpc.generate_blocks(20, 5) - + # transactions shall be sent back to txpool and then get packed - assert_equal(self.rpc.get_nonce(acct1.address), 3) + assert_equal(self.rpc.get_nonce(acct1.address), 4) if __name__ == "__main__": diff --git a/tests/cip1559_test.py b/tests/cip1559_test.py index ad62cb257d..1472a3d3da 100644 --- a/tests/cip1559_test.py +++ b/tests/cip1559_test.py @@ -96,6 +96,8 @@ def test_balance_change(self, acct: CfxLocalAccount): acct_new_balance = self.rpc.get_balance(acct.address) assert_equal(acct_new_balance, acct_balance - int(receipt["gasFee"], 16) - 100) + # this tests the case for pivot blocks + # as for non-pivot blocks, the tests are in ./cip137_test.py def test_max_fee_not_enough_for_current_base_fee(self): self.change_base_fee(block_count=10) initial_base_fee = self.rpc.base_fee_per_gas() diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 6ab44a9289..3d5bab1cb2 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -145,8 +145,16 @@ def generate_custom_block(self, parent_hash: str, referee: list, txs: list[Union for r in referee: assert_is_hash_string(r) + raw_txs = [] + for tx in txs: + if isinstance(tx, SignedTransaction): + raw_txs.append(tx.rawTransaction) + elif isinstance(tx, Transaction): + raw_txs.append(rlp.encode(tx)) + else: + raise Exception("Unknown transaction type") - encoded_txs = eth_utils.encode_hex(rlp.encode(txs)) + encoded_txs = eth_utils.encode_hex(rlp.encode(raw_txs)) block_hash = self.node.test_generatecustomblock(parent_hash, referee, encoded_txs) assert_is_hash_string(block_hash) From f939b79c80be9a2d2a9bcc7d86c73586db5f35bf Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:13:13 +0800 Subject: [PATCH 124/137] tests: fix unknow tx type rlp encoding --- tests/conflux/rpc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 3d5bab1cb2..3050c48ced 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -152,7 +152,8 @@ def generate_custom_block(self, parent_hash: str, referee: list, txs: list[Union elif isinstance(tx, Transaction): raw_txs.append(rlp.encode(tx)) else: - raise Exception("Unknown transaction type") + raw_txs.append(rlp.encode(tx)) + # raise Exception(f"Unknown transaction type {repr(tx.__class__)}") encoded_txs = eth_utils.encode_hex(rlp.encode(raw_txs)) From 6fb9f8d11d0787883512926b3c0402026cbf4970 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:33:35 +0800 Subject: [PATCH 125/137] test: add param to cip137 test --- tests/cip137_test.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/cip137_test.py b/tests/cip137_test.py index 80dc1c81e2..a82ff69ddf 100644 --- a/tests/cip137_test.py +++ b/tests/cip137_test.py @@ -21,6 +21,8 @@ class CIP137Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["min_native_base_price"] = MIN_NATIVE_BASE_PRICE + self.conf_parameters["next_hardfork_transition_height"] = 1 + self.conf_parameters["next_hardfork_transition_number"] = 1 def setup_network(self): self.add_nodes(self.num_nodes) @@ -94,13 +96,7 @@ def log_tx_fee_info(self, tx_hash: str): max_fee_per_gas = int(data["maxFeePerGas"], 16) max_priority_fee_per_gas = int(data["maxPriorityFeePerGas"], 16) receipt = self.rpc.get_transaction_receipt(tx_hash) - # print(receipt) - - # effective_gas_price = int(receipt["effectiveGasPrice"], 16) - # transaction_epoch = int(receipt["epochNumber"],16) - # base_fee_per_gas = self.rpc.base_fee_per_gas(transaction_epoch) - # computed gas price - # priority_fee_per_gas = effective_gas_price - base_fee_per_gas + self.log.info(f'max fee per gas: {max_fee_per_gas}') self.log.info(f'max priority fee per gas: {max_priority_fee_per_gas}') self.log.info(f'tx status: {data["status"]}') @@ -109,8 +105,6 @@ def log_tx_fee_info(self, tx_hash: str): self.log.info(f'tx outcome status: {receipt["outcomeStatus"]}') else: self.log.info(f'tx receipt is None: {receipt is None}') - # self.log.info(f'effective gas price: {effective_gas_price}') - # self.log.info(f'base fee per gas: {base_fee_per_gas}') # continuously fill transaction in C to F to increase base gas fee for F epoch # then transaction in B block will fail @@ -214,11 +208,9 @@ def run_test(self): self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][0]) self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][1]) - # print(focusing_block["transactions"]) assert_equal(focusing_block["transactions"][0]["status"], "0x0") self.log_tx_fee_info(focusing_block["transactions"][1]["hash"]) assert_equal(focusing_block["transactions"][1]["status"], "0x0") - # assert_equal(focusing_block["transactions"][1]["blockHash"], None) assert_equal(focusing_block["transactions"][2]["status"], None) assert_equal(focusing_block["transactions"][2]["blockHash"], None) assert_equal(focusing_block["transactions"][3]["status"], None) From ad1b5e184f3e62b94055157a01a5af8021c15737 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:55:05 +0800 Subject: [PATCH 126/137] chore: clean code comments --- tests/cip137_test.py | 10 ++-------- tests/conflux/rpc.py | 3 --- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/tests/cip137_test.py b/tests/cip137_test.py index a82ff69ddf..08ec90e0e0 100644 --- a/tests/cip137_test.py +++ b/tests/cip137_test.py @@ -85,10 +85,7 @@ def init_acct_with_cfx(self, drip: int = 10**21) -> CfxLocalAccount: ) return acct - # TODO: test in pivot block transaction will not be included if burnt_ratio*base_gas_fee_per_gas < max_fee_per_gas < base_gas_fee_per_gas - # low max_fee means the transaction max fee is less than the epoch's base fee * burnt ratio - # def test_transaction_ignored_in_non_pivot_block_if_low_max_fee(self): - + def log_tx_fee_info(self, tx_hash: str): self.log.info(f'tx hash: {tx_hash}') data = self.rpc.get_tx(tx_hash) @@ -106,15 +103,12 @@ def log_tx_fee_info(self, tx_hash: str): else: self.log.info(f'tx receipt is None: {receipt is None}') - # continuously fill transaction in C to F to increase base gas fee for F epoch - # then transaction in B block will fail + def run_test(self): acct1 = self.init_acct_with_cfx() acct2 = self.init_acct_with_cfx() - # assert self.rpc.base_fee_per_gas() < gas_price_level_1 * burnt_ratio - block_p = self.rpc.block_by_epoch("latest_mined")["hash"] gas_price_level_1 = MIN_NATIVE_BASE_PRICE diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 3050c48ced..0e82337b66 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -41,8 +41,6 @@ class CfxFeeHistoryResponse(TypedDict): base_fee_per_gas: list[int] gas_used_ratio: list[float] reward: list[list[str]] # does not convert it currently - -# class TransactionReceipt(TypedDict): def convert_b32_address_field_to_hex(original_dict: dict, field_name: str): @@ -153,7 +151,6 @@ def generate_custom_block(self, parent_hash: str, referee: list, txs: list[Union raw_txs.append(rlp.encode(tx)) else: raw_txs.append(rlp.encode(tx)) - # raise Exception(f"Unknown transaction type {repr(tx.__class__)}") encoded_txs = eth_utils.encode_hex(rlp.encode(raw_txs)) From 10399b68f26b1246a0f80b51a9a7041acb9539fe Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:41:02 +0800 Subject: [PATCH 127/137] refactor: check all transaction fee change computation --- tests/cip137_test.py | 33 ++++++++------------------ tests/cip1559_test.py | 30 +++++++---------------- tests/test_framework/util.py | 46 ++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 44 deletions(-) diff --git a/tests/cip137_test.py b/tests/cip137_test.py index 08ec90e0e0..687f5ddec2 100644 --- a/tests/cip137_test.py +++ b/tests/cip137_test.py @@ -1,3 +1,4 @@ +import math from typing import Union, Tuple from conflux.rpc import RpcClient, default_config from test_framework.util import ( @@ -11,6 +12,7 @@ from test_framework.util import ( generate_blocks_for_base_fee_manipulation, generate_single_block_for_base_fee_manipulation, + assert_correct_fee_computation_for_core_tx, ) MIN_NATIVE_BASE_PRICE = 10000 @@ -84,25 +86,11 @@ def init_acct_with_cfx(self, drip: int = 10**21) -> CfxLocalAccount: True, ) return acct - - - def log_tx_fee_info(self, tx_hash: str): - self.log.info(f'tx hash: {tx_hash}') - data = self.rpc.get_tx(tx_hash) - - max_fee_per_gas = int(data["maxFeePerGas"], 16) - max_priority_fee_per_gas = int(data["maxPriorityFeePerGas"], 16) - receipt = self.rpc.get_transaction_receipt(tx_hash) - - self.log.info(f'max fee per gas: {max_fee_per_gas}') - self.log.info(f'max priority fee per gas: {max_priority_fee_per_gas}') - self.log.info(f'tx status: {data["status"]}') - if receipt: - self.log.info(f'tx block hash: {receipt["blockHash"]}') - self.log.info(f'tx outcome status: {receipt["outcomeStatus"]}') - else: - self.log.info(f'tx receipt is None: {receipt is None}') - + + def get_gas_charged(self, tx_hash: str) -> int: + gas_limit = int(self.rpc.get_tx(tx_hash)["gas"], 16) + gas_used = int(self.rpc.get_transaction_receipt(tx_hash)["gasUsed"], 16) + return max(int(3/4*gas_limit), gas_used) def run_test(self): @@ -198,12 +186,8 @@ def run_test(self): self.log.info(f"burnt_fee_per_gas for epoch {epoch}: {self.rpc.base_fee_per_gas(epoch) * 0.5}") self.log.info(f"least base fee for epoch {epoch}: {self.rpc.base_fee_per_gas(epoch) * BURNT_RATIO}") self.log.info(f"transactions in block b: {self.rpc.block_by_hash(block_b)['transactions']}") - - self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][0]) - self.log_tx_fee_info(self.rpc.block_by_hash(block_b)['transactions'][1]) assert_equal(focusing_block["transactions"][0]["status"], "0x0") - self.log_tx_fee_info(focusing_block["transactions"][1]["hash"]) assert_equal(focusing_block["transactions"][1]["status"], "0x0") assert_equal(focusing_block["transactions"][2]["status"], None) assert_equal(focusing_block["transactions"][2]["blockHash"], None) @@ -214,6 +198,9 @@ def run_test(self): assert_equal(focusing_block["transactions"][4]["status"], "0x0") assert_equal(focusing_block["transactions"][5]["status"], "0x0") assert_equal(focusing_block["transactions"][6]["status"], "0x0") + + for tx_hash in self.rpc.block_by_hash(block_b)['transactions']: + assert_correct_fee_computation_for_core_tx(self.rpc, tx_hash, BURNT_RATIO) self.rpc.generate_blocks(20, 5) diff --git a/tests/cip1559_test.py b/tests/cip1559_test.py index 1472a3d3da..bc84e37164 100644 --- a/tests/cip1559_test.py +++ b/tests/cip1559_test.py @@ -8,7 +8,7 @@ from cfx_account.signers.local import LocalAccount as CfxLocalAccount from test_framework.test_framework import ConfluxTestFramework -from test_framework.util import generate_blocks_for_base_fee_manipulation +from test_framework.util import generate_blocks_for_base_fee_manipulation, assert_correct_fee_computation_for_core_tx CORE_BLOCK_GAS_TARGET = 270000 BURNT_RATIO = 0.5 @@ -18,6 +18,8 @@ class CIP1559Test(ConfluxTestFramework): def set_test_params(self): self.num_nodes = 1 self.conf_parameters["min_native_base_price"] = MIN_NATIVE_BASE_PRICE + self.conf_parameters["next_hardfork_transition_height"] = 1 + self.conf_parameters["next_hardfork_transition_number"] = 1 def setup_network(self): @@ -64,22 +66,8 @@ def get_gas_charged(self, tx_hash: str) -> int: gas_limit = int(self.rpc.get_tx(tx_hash)["gas"], 16) gas_used = int(self.rpc.get_transaction_receipt(tx_hash)["gasUsed"], 16) return max(int(3/4*gas_limit), gas_used) - - # TODO: currently only assert transaction in pivot block - # the transactions in non-pivot block is different - def assert_expected_effective_gas_fee(self, tx_hash: str): - data = self.rpc.get_tx(tx_hash) - max_fee_per_gas = int(data["maxFeePerGas"], 16) - max_priority_fee_per_gas = int(data["maxPriorityFeePerGas"], 16) - receipt = self.rpc.get_transaction_receipt(tx_hash) - effective_gas_price = int(receipt["effectiveGasPrice"], 16) - transaction_epoch = int(receipt["epochNumber"],16) - base_fee_per_gas = self.rpc.base_fee_per_gas(transaction_epoch) - # computed gas price - priority_fee_per_gas = effective_gas_price - base_fee_per_gas - assert_equal(priority_fee_per_gas, min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas)) - assert_equal(int(receipt["gasFee"], 16), effective_gas_price*self.get_gas_charged(tx_hash)) - + + def test_balance_change(self, acct: CfxLocalAccount): acct_balance = self.rpc.get_balance(acct.address) h = self.rpc.send_tx( @@ -125,9 +113,9 @@ def test_max_fee_not_enough_for_current_base_fee(self): self.log.info(f"epoch base fee for transaction accepted: {tx_base_fee}") assert tx_base_fee <= initial_base_fee - def test_type_2_tx(self): + def test_type_2_tx_fees(self): - self.assert_expected_effective_gas_fee(self.rpc.send_tx( + assert_correct_fee_computation_for_core_tx(self.rpc, self.rpc.send_tx( self.rpc.new_typed_tx( receiver=CfxAccount.create().address, max_fee_per_gas=self.rpc.base_fee_per_gas(), @@ -135,7 +123,7 @@ def test_type_2_tx(self): ), wait_for_receipt=True, )) - self.assert_expected_effective_gas_fee(self.rpc.send_tx( + assert_correct_fee_computation_for_core_tx(self.rpc, self.rpc.send_tx( self.rpc.new_typed_tx( receiver=CfxAccount.create().address, max_fee_per_gas=self.rpc.base_fee_per_gas(), @@ -156,7 +144,7 @@ def run_test(self): self.test_block_base_fee_change(self.init_acct_with_cfx(), 10, 1, 13500000) self.test_block_base_fee_change(self.init_acct_with_cfx(), 10, 2, 10000000) - self.test_type_2_tx() + self.test_type_2_tx_fees() self.test_max_fee_not_enough_for_current_base_fee() diff --git a/tests/test_framework/util.py b/tests/test_framework/util.py index 8126c3133b..ad45317fd4 100644 --- a/tests/test_framework/util.py +++ b/tests/test_framework/util.py @@ -21,6 +21,7 @@ from sys import platform import yaml import shutil +import math from test_framework.simple_rpc_proxy import SimpleRpcProxy from . import coverage @@ -848,3 +849,48 @@ def generate_single_block_for_base_fee_manipulation(rpc: "RpcClient", acct: CfxL referee=referee, ) return new_block, starting_nonce + tx_per_block + +# for transactions in either pivot/non-pivot block +# checks priority fee is calculated as expeted +def assert_correct_fee_computation_for_core_tx(rpc: "RpcClient", tx_hash: str, burnt_ratio=0.5): + def get_gas_charged(rpc: "RpcClient", tx_hash: str) -> int: + gas_limit = int(rpc.get_tx(tx_hash)["gas"], 16) + gas_used = int(rpc.get_transaction_receipt(tx_hash)["gasUsed"], 16) + return max(int(3/4*gas_limit), gas_used) + + receipt = rpc.get_transaction_receipt(tx_hash) + # The transaction is not executed + if receipt is None: + return + + tx_data = rpc.get_tx(tx_hash) + tx_type = int(tx_data["type"], 16) + if tx_type == 2: + # original tx fields + max_fee_per_gas = int(tx_data["maxFeePerGas"], 16) + max_priority_fee_per_gas = int(tx_data["maxPriorityFeePerGas"], 16) + else: + max_fee_per_gas = int(tx_data["gasPrice"], 16) + max_priority_fee_per_gas = int(tx_data["gasPrice"], 16) + + effective_gas_price = int(receipt["effectiveGasPrice"], 16) + transaction_epoch = int(receipt["epochNumber"],16) + is_in_pivot_block = rpc.block_by_epoch(transaction_epoch)["hash"] == receipt["blockHash"] + base_fee_per_gas = rpc.base_fee_per_gas(transaction_epoch) + burnt_fee_per_gas = math.ceil(base_fee_per_gas * burnt_ratio) + + # check gas fee computation + assert_equal(int(receipt["gasFee"], 16), effective_gas_price*get_gas_charged(rpc, tx_hash)) + # check burnt fee computation + assert_equal(int(receipt["burntGasFee"], 16), burnt_fee_per_gas*get_gas_charged(rpc, tx_hash)) + + # if max_fee_per_gas >= base_fee_per_gas, it shall follow the computation, regardless of transaction in pivot block or not + if max_fee_per_gas >= base_fee_per_gas: + priority_fee_per_gas = effective_gas_price - base_fee_per_gas + # check priority fee computation + assert_equal(priority_fee_per_gas, min(max_priority_fee_per_gas, max_fee_per_gas - base_fee_per_gas)) + else: + # max fee per gas should be greater than burnt fee per gas + assert is_in_pivot_block == False, "Transaction should be in non-pivot block" + assert max_fee_per_gas >= burnt_fee_per_gas + From d6e84806a29f90a068c856b07832e87c435ecc94 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:45:14 +0800 Subject: [PATCH 128/137] test: log info --- tests/test_framework/util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_framework/util.py b/tests/test_framework/util.py index ad45317fd4..760cd5a61f 100644 --- a/tests/test_framework/util.py +++ b/tests/test_framework/util.py @@ -880,6 +880,9 @@ def get_gas_charged(rpc: "RpcClient", tx_hash: str) -> int: burnt_fee_per_gas = math.ceil(base_fee_per_gas * burnt_ratio) # check gas fee computation + print("effective gas price: ", effective_gas_price) + print("gas charged: ", get_gas_charged(rpc, tx_hash)) + print("gas fee", int(receipt["gasFee"], 16)) assert_equal(int(receipt["gasFee"], 16), effective_gas_price*get_gas_charged(rpc, tx_hash)) # check burnt fee computation assert_equal(int(receipt["burntGasFee"], 16), burnt_fee_per_gas*get_gas_charged(rpc, tx_hash)) From 76fc0fd115ef7a7cc83490f313ec430c6a8806a4 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:43:00 +0800 Subject: [PATCH 129/137] fix: feeHistory field name --- tests/conflux/rpc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 0e82337b66..6e86bf0ac5 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -38,8 +38,8 @@ } class CfxFeeHistoryResponse(TypedDict): - base_fee_per_gas: list[int] - gas_used_ratio: list[float] + baseFeePerGas: list[int] + gasUsedRatio: list[float] reward: list[list[str]] # does not convert it currently @@ -206,7 +206,7 @@ def gas_price(self) -> int: return int(self.node.cfx_gasPrice(), 0) def base_fee_per_gas(self, epoch: Union[int,str] = "latest_mined"): - return self.fee_history(1, epoch)['base_fee_per_gas'][0] + return self.fee_history(1, epoch)['baseFeePerGas'][-1] def get_block_reward_info(self, epoch: str): reward = self.node.cfx_getBlockRewardInfo(epoch) @@ -653,8 +653,8 @@ def fee_history(self, epoch_count: int, last_epoch: Union[int, str], reward_perc last_epoch = hex(last_epoch) rtn = self.node.cfx_feeHistory(hex(epoch_count), last_epoch, reward_percentiles) rtn[ - 'base_fee_per_gas' - ] = [ int(v, 16) for v in rtn['base_fee_per_gas'] ] + 'baseFeePerGas' + ] = [ int(v, 16) for v in rtn['baseFeePerGas'] ] return rtn From 882722d190e54bd7e26e6f06eb9d547e7eb88f6f Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:53:44 +0800 Subject: [PATCH 130/137] test: add balance relating tests for 1559 --- tests/cip1559_test.py | 61 ++++++++++++++++++++++++++++++++++++ tests/conflux/rpc.py | 2 ++ tests/test_framework/util.py | 21 ++++++++++--- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/tests/cip1559_test.py b/tests/cip1559_test.py index bc84e37164..1c08166e27 100644 --- a/tests/cip1559_test.py +++ b/tests/cip1559_test.py @@ -3,6 +3,7 @@ assert_equal, ) from decimal import Decimal +from typing import Literal from cfx_account import Account as CfxAccount from cfx_account.signers.local import LocalAccount as CfxLocalAccount @@ -133,6 +134,63 @@ def test_type_2_tx_fees(self): )) self.test_balance_change(self.init_acct_with_cfx()) + def test_balance_not_enough_for_base_fee(self): + # ensuring acct does not have enough balance to pay for base fee + initial_value = 21000*(MIN_NATIVE_BASE_PRICE-1) + acct = self.init_acct_with_cfx(initial_value) + block = self.rpc.generate_custom_block(parent_hash=self.rpc.block_by_epoch("latest_mined")["hash"], referee=[], txs=[ + self.rpc.new_typed_tx(value=0, gas=21000, priv_key=acct.key.hex()) + ]) + self.rpc.generate_blocks(20, 5) + # self. + # h = self.rpc.send_tx( + # self.rpc.new_typed_tx( + # priv_key=acct.key.hex(), + # max_fee_per_gas=self.rpc.base_fee_per_gas(), + # max_priority_fee_per_gas=self.rpc.base_fee_per_gas(), + # value=0, + # ), + # wait_for_receipt=True, + # ) + tx_data = self.rpc.block_by_hash(block, True)["transactions"][0] + tx_receipt = self.rpc.get_transaction_receipt(tx_data["hash"]) + gas_fee = int(tx_receipt["gasFee"],16) + assert_equal(gas_fee, initial_value) + assert_equal(tx_data["status"],"0x1") + # account balance is all consumed + assert_equal(self.rpc.get_balance(acct.address),0) + + # two cases to test based on balance enough for max priority fee per gas + # maxPriorityFeePerGas = maxFeePerGas <- will fail because balance is not enough for effective_gas_price * gas_charged + # maxPriorityFeePerGas = 0 <- succeed + def test_balance_enough_for_base_fee_but_not_for_max_fee_per_gas(self, priority_fee_setting: Literal["MAX", "ZERO"]): + # ensuring acct does not have enough balance to pay for base fee + self.log.info(f"current base fee: {self.rpc.base_fee_per_gas()}") + assert_equal(self.rpc.base_fee_per_gas(), MIN_NATIVE_BASE_PRICE) + # allow extra 1 priority fee + initial_value = 21000*(MIN_NATIVE_BASE_PRICE+1) + acct = self.init_acct_with_cfx(initial_value) + max_fee_per_gas = MIN_NATIVE_BASE_PRICE+2 + max_priority_fee: int + if priority_fee_setting == "MAX": + max_priority_fee = max_fee_per_gas + elif priority_fee_setting == "ZERO": + max_priority_fee = 0 + block = self.rpc.generate_custom_block(parent_hash=self.rpc.block_by_epoch("latest_mined")["hash"], referee=[], txs=[ + self.rpc.new_typed_tx(value=0, gas=21000, priv_key=acct.key.hex(), max_fee_per_gas=max_fee_per_gas, max_priority_fee_per_gas=max_priority_fee) + ]) + self.rpc.generate_blocks(20, 5) + + tx_data = self.rpc.block_by_hash(block, True)["transactions"][0] + assert_correct_fee_computation_for_core_tx(self.rpc, tx_data["hash"], BURNT_RATIO) + + if priority_fee_setting == "MAX": + # extra test to assert gas fee equal to all of the balance + tx_receipt = self.rpc.get_transaction_receipt(tx_data["hash"]) + gas_fee = int(tx_receipt["gasFee"],16) + assert_equal(gas_fee, initial_value) + + def run_test(self): self.rpc.generate_blocks(5) @@ -146,6 +204,9 @@ def run_test(self): self.test_type_2_tx_fees() self.test_max_fee_not_enough_for_current_base_fee() + self.test_balance_not_enough_for_base_fee() + self.test_balance_enough_for_base_fee_but_not_for_max_fee_per_gas("ZERO") + self.test_balance_enough_for_base_fee_but_not_for_max_fee_per_gas("MAX") if __name__ == "__main__": diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 6e86bf0ac5..9eb1592ac2 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -419,6 +419,8 @@ def new_typed_tx(self, *, type_=2, receiver=None, nonce=None, max_fee_per_gas=No acct = CfxAccount.from_key(priv_key, DEFAULT_PY_TEST_CHAIN_ID) else: acct = CfxAccount.from_key(default_config["GENESIS_PRI_KEY"], DEFAULT_PY_TEST_CHAIN_ID) + if receiver is None: + receiver = self.COINBASE_ADDR tx = {} tx["type"] = type_ tx["gas"] = gas diff --git a/tests/test_framework/util.py b/tests/test_framework/util.py index 760cd5a61f..762cbab472 100644 --- a/tests/test_framework/util.py +++ b/tests/test_framework/util.py @@ -878,14 +878,27 @@ def get_gas_charged(rpc: "RpcClient", tx_hash: str) -> int: is_in_pivot_block = rpc.block_by_epoch(transaction_epoch)["hash"] == receipt["blockHash"] base_fee_per_gas = rpc.base_fee_per_gas(transaction_epoch) burnt_fee_per_gas = math.ceil(base_fee_per_gas * burnt_ratio) + gas_fee = int(receipt["gasFee"], 16) + burnt_gas_fee = int(receipt["burntGasFee"], 16) + gas_charged = get_gas_charged(rpc, tx_hash) # check gas fee computation print("effective gas price: ", effective_gas_price) print("gas charged: ", get_gas_charged(rpc, tx_hash)) - print("gas fee", int(receipt["gasFee"], 16)) - assert_equal(int(receipt["gasFee"], 16), effective_gas_price*get_gas_charged(rpc, tx_hash)) - # check burnt fee computation - assert_equal(int(receipt["burntGasFee"], 16), burnt_fee_per_gas*get_gas_charged(rpc, tx_hash)) + print("gas fee", gas_fee) + + # check gas fee and burnt gas fee computation + if receipt["outcomeStatus"] == "0x1": # tx fails becuase of not enough cash + assert "NotEnoughCash" in receipt["txExecErrorMsg"] + # all gas is charged + print(rpc.get_transaction_trace(tx_hash)) + assert_equal(rpc.get_balance(tx_data["from"], receipt["epochNumber"]), 0) + # gas fee less than effective gas price + assert gas_fee < effective_gas_price*gas_charged + else: + assert_equal(gas_fee, effective_gas_price*gas_charged) + # check burnt fee computation + assert_equal(burnt_gas_fee, burnt_fee_per_gas*gas_charged) # if max_fee_per_gas >= base_fee_per_gas, it shall follow the computation, regardless of transaction in pivot block or not if max_fee_per_gas >= base_fee_per_gas: From 7a04491c0e0e4d9ad9773b583b6e72c4526208d9 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:44:45 +0800 Subject: [PATCH 131/137] test: fix fee_history rpc implementation --- tests/conflux/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 9eb1592ac2..9e7e789dec 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -650,7 +650,7 @@ def filter_trace(self, filter: dict): def fee_history(self, epoch_count: int, last_epoch: Union[int, str], reward_percentiles: Optional[list[float]]=None) -> CfxFeeHistoryResponse: if reward_percentiles is None: - reward_percentiles = [50]*epoch_count + reward_percentiles = [50] if isinstance(last_epoch, int): last_epoch = hex(last_epoch) rtn = self.node.cfx_feeHistory(hex(epoch_count), last_epoch, reward_percentiles) From 9140181339f38e88199aa0645358f93bd990c71e Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:22:18 +0800 Subject: [PATCH 132/137] test: comment debug info --- tests/test_framework/util.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_framework/util.py b/tests/test_framework/util.py index 762cbab472..e2ac3329d9 100644 --- a/tests/test_framework/util.py +++ b/tests/test_framework/util.py @@ -883,15 +883,14 @@ def get_gas_charged(rpc: "RpcClient", tx_hash: str) -> int: gas_charged = get_gas_charged(rpc, tx_hash) # check gas fee computation - print("effective gas price: ", effective_gas_price) - print("gas charged: ", get_gas_charged(rpc, tx_hash)) - print("gas fee", gas_fee) + # print("effective gas price: ", effective_gas_price) + # print("gas charged: ", get_gas_charged(rpc, tx_hash)) + # print("gas fee", gas_fee) # check gas fee and burnt gas fee computation if receipt["outcomeStatus"] == "0x1": # tx fails becuase of not enough cash assert "NotEnoughCash" in receipt["txExecErrorMsg"] # all gas is charged - print(rpc.get_transaction_trace(tx_hash)) assert_equal(rpc.get_balance(tx_data["from"], receipt["epochNumber"]), 0) # gas fee less than effective gas price assert gas_fee < effective_gas_price*gas_charged From d3abe82587899907e726c99ca50f2e0b943e44b0 Mon Sep 17 00:00:00 2001 From: darwintree <17946284+darwintree@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:39:03 +0800 Subject: [PATCH 133/137] test: improve base_fee_per_gas test interface implementation --- tests/conflux/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conflux/rpc.py b/tests/conflux/rpc.py index 9e7e789dec..6f66faa6e0 100644 --- a/tests/conflux/rpc.py +++ b/tests/conflux/rpc.py @@ -206,7 +206,7 @@ def gas_price(self) -> int: return int(self.node.cfx_gasPrice(), 0) def base_fee_per_gas(self, epoch: Union[int,str] = "latest_mined"): - return self.fee_history(1, epoch)['baseFeePerGas'][-1] + return int(self.block_by_epoch(epoch).get("baseFeePerGas", "0x0"), 16) def get_block_reward_info(self, epoch: str): reward = self.node.cfx_getBlockRewardInfo(epoch) From 4076aa8063da006b2ae824a27c42659b622dc634 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 6 Jun 2024 16:05:30 +0800 Subject: [PATCH 134/137] fix feeHistory additional block base_fee_per_gas --- crates/client/src/rpc.rs | 3 +- .../client/src/rpc/impls/cfx/cfx_handler.rs | 4 +- crates/client/src/rpc/impls/cfx/common.rs | 35 +++++++++++----- crates/client/src/rpc/impls/cfx/light.rs | 42 +++++++++++-------- .../client/src/rpc/impls/eth/eth_handler.rs | 14 +++++-- crates/client/src/rpc/traits/cfx_space/cfx.rs | 6 +-- crates/client/src/rpc/types.rs | 2 +- .../client/src/rpc/types/cfx/fee_history.rs | 37 ++++++++++++++++ crates/client/src/rpc/types/cfx/mod.rs | 2 + crates/client/src/rpc/types/fee_history.rs | 18 ++++---- 10 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 crates/client/src/rpc/types/cfx/fee_history.rs diff --git a/crates/client/src/rpc.rs b/crates/client/src/rpc.rs index 89ddb3d90a..5d3b0bee85 100644 --- a/crates/client/src/rpc.rs +++ b/crates/client/src/rpc.rs @@ -36,7 +36,8 @@ mod traits; pub mod types; pub use cfxcore::rpc_errors::{ - BoxFuture as RpcBoxFuture, Error as RpcError, ErrorKind as RpcErrorKind, + invalid_params, invalid_params_check, BoxFuture as RpcBoxFuture, + Error as RpcError, ErrorKind as RpcErrorKind, ErrorKind::JsonRpcError as JsonRpcErrorKind, Result as RpcResult, }; diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index d49a200936..41822ae99e 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -6,7 +6,7 @@ use crate::rpc::{ error_codes::{internal_error_msg, invalid_params_msg}, types::{ call_request::rpc_call_request_network, - errors::check_rpc_address_network, pos::PoSEpochReward, FeeHistory, + errors::check_rpc_address_network, pos::PoSEpochReward, CfxFeeHistory, PoSEconomics, RpcAddress, SponsorInfo, StatOnGasLoad, TokenSupplyInfo, VoteParamsInfo, WrapTransaction, U64 as HexU64, }, @@ -2272,7 +2272,7 @@ impl Cfx for CfxHandler { fn account_pending_info(&self, addr: RpcAddress) -> BoxFuture>; fn account_pending_transactions(&self, address: RpcAddress, maybe_start_nonce: Option, maybe_limit: Option) -> BoxFuture; fn get_pos_reward_by_epoch(&self, epoch: EpochNumber) -> JsonRpcResult>; - fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; fn max_priority_fee_per_gas(&self) -> BoxFuture; } diff --git a/crates/client/src/rpc/impls/cfx/common.rs b/crates/client/src/rpc/impls/cfx/common.rs index b9ca7168d7..836794ea27 100644 --- a/crates/client/src/rpc/impls/cfx/common.rs +++ b/crates/client/src/rpc/impls/cfx/common.rs @@ -14,10 +14,10 @@ use crate::rpc::{ types::{ errors::check_rpc_address_network, pos::PoSEpochReward, AccountPendingInfo, AccountPendingTransactions, Block as RpcBlock, - BlockHashOrEpochNumber, Bytes, CheckBalanceAgainstTransactionResponse, - EpochNumber, FeeHistory, RpcAddress, Status as RpcStatus, - Transaction as RpcTransaction, TxPoolPendingNonceRange, TxPoolStatus, - TxWithPoolInfo, U64 as HexU64, + BlockHashOrEpochNumber, Bytes, CfxFeeHistory, + CheckBalanceAgainstTransactionResponse, EpochNumber, FeeHistory, + RpcAddress, Status as RpcStatus, Transaction as RpcTransaction, + TxPoolPendingNonceRange, TxPoolStatus, TxWithPoolInfo, U64 as HexU64, }, RpcErrorKind, RpcResult, }; @@ -529,17 +529,25 @@ impl RpcImpl { ) } + // TODO: cache the history to improve performance pub fn fee_history( &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, - ) -> RpcResult { + ) -> RpcResult { + if newest_block == EpochNumber::LatestMined { + return Err(RpcError::invalid_params( + "newestBlock cannot be 'LatestMined'", + ) + .into()); + } + info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", block_count, newest_block, reward_percentiles ); if block_count.as_u64() == 0 { - return Ok(FeeHistory::new()); + return Ok(CfxFeeHistory::from(FeeHistory::new())); } // keep read lock to ensure consistent view let inner = self.consensus_graph().inner.read(); @@ -594,21 +602,26 @@ impl RpcImpl { .map_err(|_| RpcError::internal_error())?; if current_height == 0 { - fee_history.finish(0, None, Space::Native); - return Ok(fee_history); + break; } else { current_height -= 1; } } - let block = fetch_block(current_height)?; + // Fetch the block after the last block in the history + let block = fetch_block(start_height + 1)?; + let oldest_block = if current_height == 0 { + 0 + } else { + current_height + 1 + }; fee_history.finish( - current_height + 1, + oldest_block, block.block_header.base_price().as_ref(), Space::Native, ); - Ok(fee_history) + Ok(CfxFeeHistory::from(fee_history)) } pub fn max_priority_fee_per_gas(&self) -> RpcResult { diff --git a/crates/client/src/rpc/impls/cfx/light.rs b/crates/client/src/rpc/impls/cfx/light.rs index 1fb19b00e8..ead3360fa1 100644 --- a/crates/client/src/rpc/impls/cfx/light.rs +++ b/crates/client/src/rpc/impls/cfx/light.rs @@ -42,15 +42,15 @@ use crate::{ pos::{Block as PosBlock, PoSEpochReward}, Account as RpcAccount, AccountPendingInfo, AccountPendingTransactions, BlameInfo, Block as RpcBlock, - BlockHashOrEpochNumber, Bytes, CallRequest, CfxRpcLogFilter, - CheckBalanceAgainstTransactionResponse, ConsensusGraphStates, - EpochNumber, EstimateGasAndCollateralResponse, FeeHistory, - Log as RpcLog, PoSEconomics, Receipt as RpcReceipt, - RewardInfo as RpcRewardInfo, RpcAddress, SendTxRequest, - SponsorInfo, StatOnGasLoad, Status as RpcStatus, - StorageCollateralInfo, SyncGraphStates, TokenSupplyInfo, - Transaction as RpcTransaction, VoteParamsInfo, WrapTransaction, - U64 as HexU64, + BlockHashOrEpochNumber, Bytes, CallRequest, CfxFeeHistory, + CfxRpcLogFilter, CheckBalanceAgainstTransactionResponse, + ConsensusGraphStates, EpochNumber, + EstimateGasAndCollateralResponse, FeeHistory, Log as RpcLog, + PoSEconomics, Receipt as RpcReceipt, RewardInfo as RpcRewardInfo, + RpcAddress, SendTxRequest, SponsorInfo, StatOnGasLoad, + Status as RpcStatus, StorageCollateralInfo, SyncGraphStates, + TokenSupplyInfo, Transaction as RpcTransaction, VoteParamsInfo, + WrapTransaction, U64 as HexU64, }, RpcBoxFuture, RpcResult, }, @@ -1094,14 +1094,18 @@ impl RpcImpl { fn fee_history( &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, - ) -> RpcBoxFuture { + ) -> RpcBoxFuture { info!( "RPC Request: cfx_feeHistory: block_count={}, newest_block={:?}, reward_percentiles={:?}", block_count, newest_block, reward_percentiles ); if block_count.as_u64() == 0 { - return Box::new(async { Ok(FeeHistory::new()) }.boxed().compat()); + return Box::new( + async { Ok(CfxFeeHistory::from(FeeHistory::new())) } + .boxed() + .compat(), + ); } // clone to avoid lifetime issues due to capturing `self` @@ -1145,8 +1149,7 @@ impl RpcImpl { .map_err(|_| RpcError::internal_error())?; if current_height == 0 { - fee_history.finish(0, None, Space::Native); - return Ok(fee_history); + break; } else { current_height -= 1; } @@ -1155,15 +1158,20 @@ impl RpcImpl { let block = fetch_block_for_fee_history( consensus_graph.clone(), light.clone(), - current_height, + start_height + 1, ) .await?; + let oldest_block = if current_height == 0 { + 0 + } else { + current_height + 1 + }; fee_history.finish( - current_height + 1, + oldest_block, block.block_header.base_price().as_ref(), Space::Native, ); - Ok(fee_history) + Ok(CfxFeeHistory::from(fee_history)) }; Box::new(fut.boxed().compat()) @@ -1240,7 +1248,7 @@ impl Cfx for CfxHandler { fn transaction_by_hash(&self, hash: H256) -> BoxFuture>; fn transaction_receipt(&self, tx_hash: H256) -> BoxFuture>; fn vote_list(&self, address: RpcAddress, num: Option) -> BoxFuture>; - fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; + fn fee_history(&self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec) -> BoxFuture; } } diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index 36dad68811..ad89d6a581 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -967,16 +967,22 @@ impl Eth for EthHandler { .map_err(|_| RpcError::internal_error())?; if current_height == 0 { - fee_history.finish(0, None, Space::Ethereum); - return Ok(fee_history); + // fee_history.finish(0, None, Space::Ethereum); + // return Ok(fee_history); + break; } else { current_height -= 1; } } - let block = fetch_block(current_height)?; + let block = fetch_block(start_height + 1)?; + let oldest_block = if current_height == 0 { + 0 + } else { + current_height + 1 + }; fee_history.finish( - current_height + 1, + oldest_block, block.pivot_header.base_price().as_ref(), Space::Ethereum, ); diff --git a/crates/client/src/rpc/traits/cfx_space/cfx.rs b/crates/client/src/rpc/traits/cfx_space/cfx.rs index 95de945952..5fe7e9071e 100644 --- a/crates/client/src/rpc/traits/cfx_space/cfx.rs +++ b/crates/client/src/rpc/traits/cfx_space/cfx.rs @@ -5,9 +5,9 @@ use crate::rpc::types::{ pos::PoSEpochReward, Account as RpcAccount, AccountPendingInfo, AccountPendingTransactions, Block, BlockHashOrEpochNumber, Bytes, - CallRequest, CfxFilterChanges, CfxRpcLogFilter, + CallRequest, CfxFeeHistory, CfxFilterChanges, CfxRpcLogFilter, CheckBalanceAgainstTransactionResponse, EpochNumber, - EstimateGasAndCollateralResponse, FeeHistory, Log as RpcLog, PoSEconomics, + EstimateGasAndCollateralResponse, Log as RpcLog, PoSEconomics, Receipt as RpcReceipt, RewardInfo as RpcRewardInfo, RpcAddress, SponsorInfo, Status as RpcStatus, StorageCollateralInfo, TokenSupplyInfo, Transaction, VoteParamsInfo, U64 as HexU64, @@ -205,7 +205,7 @@ pub trait Cfx { fn fee_history( &self, block_count: HexU64, newest_block: EpochNumber, reward_percentiles: Vec, - ) -> BoxFuture; + ) -> BoxFuture; /// Check if user balance is enough for the transaction. #[rpc(name = "cfx_checkBalanceAgainstTransaction")] diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index a5b53b75ee..ef15c2c5b7 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -44,7 +44,7 @@ pub use self::{ sign_call, CallRequest, CheckBalanceAgainstTransactionResponse, EstimateGasAndCollateralResponse, SendTxRequest, MAX_GAS_CALL_REQUEST, }, - cfx::{address, address::RpcAddress}, + cfx::{address, address::RpcAddress, CfxFeeHistory}, consensus_graph_states::ConsensusGraphStates, epoch_number::{BlockHashOrEpochNumber, EpochNumber}, fee_history::FeeHistory, diff --git a/crates/client/src/rpc/types/cfx/fee_history.rs b/crates/client/src/rpc/types/cfx/fee_history.rs new file mode 100644 index 0000000000..aafd3a0280 --- /dev/null +++ b/crates/client/src/rpc/types/cfx/fee_history.rs @@ -0,0 +1,37 @@ +use crate::rpc::types::FeeHistory; +use cfx_types::U256; +use std::collections::VecDeque; + +#[derive(Serialize, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct CfxFeeHistory { + /// Oldest epoch + oldest_epoch: U256, + /// An array of pivot block base fees per gas. This includes one block + /// earlier than the oldest block. Zeroes are returned for pre-EIP-1559 + /// blocks. + base_fee_per_gas: VecDeque, + /// In Conflux, 1559 is adjusted by the current block's gas limit of total + /// transactions, instead of parent's gas used + gas_used_ratio: VecDeque, + /// A two-dimensional array of effective priority fees per gas at the + /// requested block percentiles. + reward: VecDeque>, +} + +impl CfxFeeHistory { + pub fn new(fee_history: FeeHistory) -> Self { fee_history.into() } + + pub fn reward(&self) -> &VecDeque> { &self.reward } +} + +impl From for CfxFeeHistory { + fn from(fee_history: FeeHistory) -> Self { + Self { + oldest_epoch: fee_history.oldest_block, + base_fee_per_gas: fee_history.base_fee_per_gas, + gas_used_ratio: fee_history.gas_used_ratio, + reward: fee_history.reward, + } + } +} diff --git a/crates/client/src/rpc/types/cfx/mod.rs b/crates/client/src/rpc/types/cfx/mod.rs index 89d8fb6a46..40b328acb8 100644 --- a/crates/client/src/rpc/types/cfx/mod.rs +++ b/crates/client/src/rpc/types/cfx/mod.rs @@ -1,5 +1,7 @@ mod access_list; pub mod address; +mod fee_history; pub use access_list::*; pub use address::RpcAddress; +pub use fee_history::*; diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index 50a68f1344..55db223be2 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -7,16 +7,16 @@ use primitives::{transaction::SignedTransaction, BlockHeader}; #[serde(rename_all = "camelCase")] pub struct FeeHistory { /// Oldest Block - oldest_block: U256, + pub oldest_block: U256, /// An array of block base fees per gas. This includes one block earlier /// than the oldest block. Zeroes are returned for pre-EIP-1559 blocks. - base_fee_per_gas: VecDeque, + pub base_fee_per_gas: VecDeque, /// In Conflux, 1559 is adjusted by the current block's gas limit of total /// transactions, instead of parent's gas used - gas_used_ratio: VecDeque, + pub gas_used_ratio: VecDeque, /// A two-dimensional array of effective priority fees per gas at the /// requested block percentiles. - reward: VecDeque>, + pub reward: VecDeque>, } impl FeeHistory { @@ -41,9 +41,7 @@ impl FeeHistory { return Ok(()); }; - self.base_fee_per_gas.push_front( - pivot_header.base_price().map_or(U256::zero(), |x| x[space]), - ); + self.base_fee_per_gas.push_front(base_price); let gas_limit: U256 = match space { Space::Native => pivot_header.gas_limit() * 9 / 10, @@ -74,12 +72,12 @@ impl FeeHistory { } pub fn finish( - &mut self, oldest_block: u64, - parent_base_price: Option<&SpaceMap>, space: Space, + &mut self, oldest_block: u64, last_base_price: Option<&SpaceMap>, + space: Space, ) { self.oldest_block = oldest_block.into(); self.base_fee_per_gas - .push_front(parent_base_price.map_or(U256::zero(), |x| x[space])); + .push_back(last_base_price.map_or(U256::zero(), |x| x[space])); } } From 972cd3ec8e2a7d35b31aee0278620a9a1d9754a0 Mon Sep 17 00:00:00 2001 From: Pana Date: Thu, 6 Jun 2024 16:22:51 +0800 Subject: [PATCH 135/137] remove comment --- crates/client/src/rpc/impls/cfx/common.rs | 4 ++-- crates/client/src/rpc/impls/cfx/light.rs | 4 ++-- .../client/src/rpc/impls/eth/eth_handler.rs | 2 -- crates/client/src/rpc/types.rs | 2 +- .../client/src/rpc/types/cfx/fee_history.rs | 24 +++++++++---------- crates/client/src/rpc/types/cfx/mod.rs | 2 +- crates/client/src/rpc/types/fee_history.rs | 19 +++++++++++---- 7 files changed, 32 insertions(+), 25 deletions(-) diff --git a/crates/client/src/rpc/impls/cfx/common.rs b/crates/client/src/rpc/impls/cfx/common.rs index 836794ea27..c4939cfcf9 100644 --- a/crates/client/src/rpc/impls/cfx/common.rs +++ b/crates/client/src/rpc/impls/cfx/common.rs @@ -547,7 +547,7 @@ impl RpcImpl { ); if block_count.as_u64() == 0 { - return Ok(CfxFeeHistory::from(FeeHistory::new())); + return Ok(FeeHistory::new().to_cfx_fee_history()); } // keep read lock to ensure consistent view let inner = self.consensus_graph().inner.read(); @@ -621,7 +621,7 @@ impl RpcImpl { Space::Native, ); - Ok(CfxFeeHistory::from(fee_history)) + Ok(fee_history.to_cfx_fee_history()) } pub fn max_priority_fee_per_gas(&self) -> RpcResult { diff --git a/crates/client/src/rpc/impls/cfx/light.rs b/crates/client/src/rpc/impls/cfx/light.rs index ead3360fa1..3c3c210b81 100644 --- a/crates/client/src/rpc/impls/cfx/light.rs +++ b/crates/client/src/rpc/impls/cfx/light.rs @@ -1102,7 +1102,7 @@ impl RpcImpl { if block_count.as_u64() == 0 { return Box::new( - async { Ok(CfxFeeHistory::from(FeeHistory::new())) } + async { Ok(FeeHistory::new().to_cfx_fee_history()) } .boxed() .compat(), ); @@ -1171,7 +1171,7 @@ impl RpcImpl { block.block_header.base_price().as_ref(), Space::Native, ); - Ok(CfxFeeHistory::from(fee_history)) + Ok(fee_history.to_cfx_fee_history()) }; Box::new(fut.boxed().compat()) diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index 5d44559b34..79f94460a3 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -971,8 +971,6 @@ impl Eth for EthHandler { .map_err(|_| RpcError::internal_error())?; if current_height == 0 { - // fee_history.finish(0, None, Space::Ethereum); - // return Ok(fee_history); break; } else { current_height -= 1; diff --git a/crates/client/src/rpc/types.rs b/crates/client/src/rpc/types.rs index bb55adfb92..02405f90d2 100644 --- a/crates/client/src/rpc/types.rs +++ b/crates/client/src/rpc/types.rs @@ -48,7 +48,7 @@ pub use self::{ EstimateGasAndCollateralResponse, SendTxRequest, MAX_GAS_CALL_REQUEST, }, - CfxFeeHistory + CfxFeeHistory, }, consensus_graph_states::ConsensusGraphStates, epoch_number::{BlockHashOrEpochNumber, EpochNumber}, diff --git a/crates/client/src/rpc/types/cfx/fee_history.rs b/crates/client/src/rpc/types/cfx/fee_history.rs index aafd3a0280..9a0696597d 100644 --- a/crates/client/src/rpc/types/cfx/fee_history.rs +++ b/crates/client/src/rpc/types/cfx/fee_history.rs @@ -1,4 +1,3 @@ -use crate::rpc::types::FeeHistory; use cfx_types::U256; use std::collections::VecDeque; @@ -20,18 +19,17 @@ pub struct CfxFeeHistory { } impl CfxFeeHistory { - pub fn new(fee_history: FeeHistory) -> Self { fee_history.into() } - - pub fn reward(&self) -> &VecDeque> { &self.reward } -} - -impl From for CfxFeeHistory { - fn from(fee_history: FeeHistory) -> Self { - Self { - oldest_epoch: fee_history.oldest_block, - base_fee_per_gas: fee_history.base_fee_per_gas, - gas_used_ratio: fee_history.gas_used_ratio, - reward: fee_history.reward, + pub fn new( + oldest_epoch: U256, base_fee_per_gas: VecDeque, + gas_used_ratio: VecDeque, reward: VecDeque>, + ) -> Self { + CfxFeeHistory { + oldest_epoch, + base_fee_per_gas, + gas_used_ratio, + reward, } } + + pub fn reward(&self) -> &VecDeque> { &self.reward } } diff --git a/crates/client/src/rpc/types/cfx/mod.rs b/crates/client/src/rpc/types/cfx/mod.rs index 2c9a8a2ccf..4a15fecffb 100644 --- a/crates/client/src/rpc/types/cfx/mod.rs +++ b/crates/client/src/rpc/types/cfx/mod.rs @@ -1,7 +1,7 @@ mod access_list; pub mod address; -mod fee_history; pub mod call_request; +mod fee_history; pub use access_list::*; pub use address::RpcAddress; diff --git a/crates/client/src/rpc/types/fee_history.rs b/crates/client/src/rpc/types/fee_history.rs index 55db223be2..d530701597 100644 --- a/crates/client/src/rpc/types/fee_history.rs +++ b/crates/client/src/rpc/types/fee_history.rs @@ -3,20 +3,22 @@ use std::collections::VecDeque; use cfx_types::{Space, SpaceMap, U256}; use primitives::{transaction::SignedTransaction, BlockHeader}; +use super::CfxFeeHistory; + #[derive(Serialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub struct FeeHistory { /// Oldest Block - pub oldest_block: U256, + oldest_block: U256, /// An array of block base fees per gas. This includes one block earlier /// than the oldest block. Zeroes are returned for pre-EIP-1559 blocks. - pub base_fee_per_gas: VecDeque, + base_fee_per_gas: VecDeque, /// In Conflux, 1559 is adjusted by the current block's gas limit of total /// transactions, instead of parent's gas used - pub gas_used_ratio: VecDeque, + gas_used_ratio: VecDeque, /// A two-dimensional array of effective priority fees per gas at the /// requested block percentiles. - pub reward: VecDeque>, + reward: VecDeque>, } impl FeeHistory { @@ -24,6 +26,15 @@ impl FeeHistory { pub fn reward(&self) -> &VecDeque> { &self.reward } + pub fn to_cfx_fee_history(self) -> CfxFeeHistory { + CfxFeeHistory::new( + self.oldest_block, + self.base_fee_per_gas, + self.gas_used_ratio, + self.reward, + ) + } + pub fn push_front_block<'a, I>( &mut self, space: Space, percentiles: &Vec, pivot_header: &BlockHeader, transactions: I, From 5a934dc3305d04d183b16f7dec27865766456a98 Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Thu, 27 Jun 2024 15:11:40 +0800 Subject: [PATCH 136/137] Update version to v2.4.0. --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- crates/cfxcore/core/Cargo.toml | 2 +- crates/client/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7702dec4b..f89c6e6bf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,7 +1428,7 @@ dependencies = [ [[package]] name = "cfxcore" -version = "2.4.0-alpha" +version = "2.4.0" dependencies = [ "alloy-rpc-types-trace", "anyhow", @@ -1728,7 +1728,7 @@ dependencies = [ [[package]] name = "client" -version = "2.4.0-alpha" +version = "2.4.0" dependencies = [ "alloy-rpc-types-trace", "anyhow", @@ -1863,7 +1863,7 @@ dependencies = [ [[package]] name = "conflux" -version = "2.4.0-alpha" +version = "2.4.0" dependencies = [ "app_dirs", "base64ct", @@ -3596,7 +3596,7 @@ dependencies = [ [[package]] name = "geth-tracer" -version = "2.4.0-alpha" +version = "2.4.0" dependencies = [ "alloy-primitives", "alloy-rpc-types-trace", @@ -7313,7 +7313,7 @@ dependencies = [ [[package]] name = "serde-utils" -version = "2.4.0-alpha" +version = "2.4.0" dependencies = [ "alloy-primitives", "cfx-types", diff --git a/Cargo.toml b/Cargo.toml index 90ad0df903..63f9263857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ members = [ resolver = "2" [workspace.package] -version = "2.4.0-alpha" +version = "2.4.0" authors = ["peilun-conflux", "ChenxingLi"] description = "A rust implementation of the Conflux-Protocol" documentation = "https://doc.confluxnetwork.org" diff --git a/crates/cfxcore/core/Cargo.toml b/crates/cfxcore/core/Cargo.toml index ec0478815c..dd7f1639c3 100644 --- a/crates/cfxcore/core/Cargo.toml +++ b/crates/cfxcore/core/Cargo.toml @@ -3,7 +3,7 @@ description = "Conflux core library" homepage = "https://www.confluxnetwork.org" license = "GPL-3.0" name = "cfxcore" -version = "2.4.0-alpha" +version = "2.4.0" edition = "2021" [dependencies] diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index d67bc021fd..7f2abfb102 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "client" -version = "2.4.0-alpha" +version = "2.4.0" edition = "2021" [dependencies] From b1071d2b7e2b419339d7deaf949f5318d7d8e38b Mon Sep 17 00:00:00 2001 From: Peilun Li Date: Thu, 27 Jun 2024 15:22:12 +0800 Subject: [PATCH 137/137] Set hardfork parameters. --- crates/client/src/configuration.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/client/src/configuration.rs b/crates/client/src/configuration.rs index a9a49d2fe3..8382249b22 100644 --- a/crates/client/src/configuration.rs +++ b/crates/client/src/configuration.rs @@ -161,8 +161,8 @@ build_config! { (cip112_transition_height, (Option), Some(79050000)) (cip118_transition_number, (Option), Some(188900000)) (cip119_transition_number, (Option), Some(188900000)) - (next_hardfork_transition_number, (Option), None) - (next_hardfork_transition_height, (Option), None) + (next_hardfork_transition_number, (Option), Some(247480000)) + (next_hardfork_transition_height, (Option), Some(101900000)) (cip1559_transition_height, (Option), None) (cancun_opcodes_transition_number, (Option), None) (referee_bound, (usize), REFEREE_DEFAULT_BOUND) @@ -355,10 +355,10 @@ build_config! { (pos_fix_cip99_in_queue_locked_views, (u64), 18720) (pos_fix_cip99_out_queue_locked_views, (u64), 1440) (nonce_limit_transition_view, (u64), u64::MAX) - (pos_cip136_transition_view, (u64), u64::MAX) - (pos_cip136_in_queue_locked_views, (u64), IN_QUEUE_LOCKED_VIEWS) - (pos_cip136_out_queue_locked_views, (u64), OUT_QUEUE_LOCKED_VIEWS) - (pos_cip136_round_per_term, (u64), ROUND_PER_TERM) + (pos_cip136_transition_view, (u64), 1684080) + (pos_cip136_in_queue_locked_views, (u64), 18720 * 2) + (pos_cip136_out_queue_locked_views, (u64), 1440 * 2) + (pos_cip136_round_per_term, (u64), ROUND_PER_TERM * 2) (dev_pos_private_key_encryption_password, (Option), None) (pos_started_as_voter, (bool), true)