From 5205041750aaf33877e2d92f6eb3bafd95346f5d Mon Sep 17 00:00:00 2001 From: Eugenio Paluello Date: Fri, 9 Aug 2024 17:35:29 +0200 Subject: [PATCH] feat: generate arbitrary mock tx with signature (#1324) * feat: mock tx with signature * fix: import * fix arbitrary_with_optional_fields * fix arbitrary_with_optional_fields * fix arbitrary_with_optional_fields * fix: lint * fix: set_chain_id * fix: lint * fix chain id value * re fix bug in arbitrary for Transaction + display better error message on failure * fix: set nonce and from * fix: lint * fix: use signer address as from --------- Co-authored-by: Gregory Edison --- Cargo.lock | 276 +++++++++++++++--- Cargo.toml | 4 + .../eth_provider/database/ethereum.rs | 11 +- .../database/types/transaction.rs | 98 ++++--- 4 files changed, 308 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcbba2d90..c5128f888 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,6 +126,23 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "alloy" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba1c79677c9ce51c8d45e20845b05e6fb070ea2c863fba03ad6af2c778474bd" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-core", + "alloy-eips 0.1.4", + "alloy-genesis 0.1.4", + "alloy-provider 0.1.4", + "alloy-rpc-client 0.1.4", + "alloy-rpc-types", + "alloy-serde 0.1.4", + "alloy-transport-http 0.1.4", +] + [[package]] name = "alloy-chains" version = "0.1.23" @@ -172,14 +189,14 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58047cc851e58c26224521d1ecda466e3d746ebca0274cd5427aa660a88c353" +checksum = "04c309895995eaa4bfcc345f5515a39c7df9447798645cc8bf462b6c5bf1dc96" dependencies = [ - "alloy-eips 0.2.0", + "alloy-eips 0.2.1", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.2.0", + "alloy-serde 0.2.1", "c-kzg", "serde", ] @@ -193,15 +210,27 @@ dependencies = [ "alloy-json-abi", "alloy-network 0.1.0", "alloy-primitives", - "alloy-provider", + "alloy-provider 0.1.0", "alloy-rpc-types-eth 0.1.0", "alloy-sol-types", - "alloy-transport", + "alloy-transport 0.1.0", "futures", "futures-util", "thiserror", ] +[[package]] +name = "alloy-core" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529fc6310dc1126c8de51c376cbc59c79c7f662bd742be7dc67055d5421a81b4" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", +] + [[package]] name = "alloy-dyn-abi" version = "0.7.7" @@ -255,13 +284,13 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a3e14fa0d152d00bd8daf605eb74ad397efb0f54bd7155585823dddb4401e" +checksum = "d9431c99a3b3fe606ede4b3d4043bdfbcb780c45b8d8d226c3804e2b75cfbe68" dependencies = [ "alloy-primitives", "alloy-rlp", - "alloy-serde 0.2.0", + "alloy-serde 0.2.1", "c-kzg", "once_cell", "serde", @@ -316,11 +345,25 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.2.0" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a9feec2352c78545d1a37415699817bae8dc41654bd1bfe57d6cdd5433bd" +checksum = "57e2865c4c3bb4cdad3f0d9ec1ab5c0c657ba69a375651bd35e32fb6c180ccc2" dependencies = [ "alloy-primitives", + "alloy-sol-types", "serde", "serde_json", "thiserror", @@ -347,17 +390,38 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.2.0" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3223d71dc78f464b2743418d0be8b5c894313e272105a6206ad5e867d67b3ce2" +checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" dependencies = [ - "alloy-consensus 0.2.0", - "alloy-eips 0.2.0", - "alloy-json-rpc 0.2.0", + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", "alloy-primitives", - "alloy-rpc-types-eth 0.2.0", - "alloy-serde 0.2.0", - "alloy-signer 0.2.0", + "alloy-rpc-types-eth 0.1.4", + "alloy-serde 0.1.4", + "alloy-signer 0.1.4", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e701fc87ef9a3139154b0b4ccb935b565d27ffd9de020fe541bf2dec5ae4ede" +dependencies = [ + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-json-rpc 0.2.1", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth 0.2.1", + "alloy-serde 0.2.1", + "alloy-signer 0.2.1", "alloy-sol-types", "async-trait", "auto_impl", @@ -365,6 +429,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "alloy-network-primitives" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec9d5a0f9170b10988b6774498a022845e13eda94318440d17709d50687f67f9" +dependencies = [ + "alloy-primitives", + "alloy-serde 0.2.1", + "serde", +] + [[package]] name = "alloy-primitives" version = "0.7.7" @@ -403,10 +478,10 @@ dependencies = [ "alloy-json-rpc 0.1.0", "alloy-network 0.1.0", "alloy-primitives", - "alloy-rpc-client", + "alloy-rpc-client 0.1.0", "alloy-rpc-types-eth 0.1.0", "alloy-rpc-types-trace 0.1.0", - "alloy-transport", + "alloy-transport 0.1.0", "async-stream", "async-trait", "auto_impl", @@ -421,6 +496,38 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-provider" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-network 0.1.4", + "alloy-primitives", + "alloy-rpc-client 0.1.4", + "alloy-rpc-types-eth 0.1.4", + "alloy-transport 0.1.4", + "alloy-transport-http 0.1.4", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest 0.12.5", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + [[package]] name = "alloy-rlp" version = "0.3.7" @@ -449,16 +556,37 @@ version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ "alloy-json-rpc 0.1.0", - "alloy-transport", - "alloy-transport-http", + "alloy-transport 0.1.0", + "alloy-transport-http 0.1.0", + "futures", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-transport 0.1.4", + "alloy-transport-http 0.1.4", "futures", "pin-project", + "reqwest 0.12.5", "serde", "serde_json", "tokio", "tokio-stream", "tower", "tracing", + "url", ] [[package]] @@ -571,15 +699,16 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605fa8462732bb8fd0645a9941e12961e079d45ae6a44634c826f8229c187bdf" +checksum = "81e18424d962d7700a882fe423714bd5b9dde74c7a7589d4255ea64068773aef" dependencies = [ - "alloy-consensus 0.2.0", - "alloy-eips 0.2.0", + "alloy-consensus 0.2.1", + "alloy-eips 0.2.1", + "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.2.0", + "alloy-serde 0.2.1", "alloy-sol-types", "itertools 0.13.0", "serde", @@ -664,9 +793,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c5b9057acc02aee1b8aac2b5a0729cb0f73d080082c111313e5d1f92a96630" +checksum = "e33feda6a53e6079895aed1d08dcb98a1377b000d80d16370fbbdb8155d547ef" dependencies = [ "alloy-primitives", "serde", @@ -688,9 +817,23 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.2.0" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve 0.13.8", + "k256 0.13.3", + "thiserror", +] + +[[package]] +name = "alloy-signer" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37f10592696f4ab8b687d5a8ab55e998a14ea0ca5f8eb20ad74a96ad671bb54a" +checksum = "740a25b92e849ed7b0fa013951fe2f64be9af1ad5abe805037b44fb7770c5c47" dependencies = [ "alloy-primitives", "async-trait", @@ -702,14 +845,14 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b537f3e55f30753578f4623d5f66ddad8fa582af3fa6b15bad23dd1b9775228" +checksum = "1b0707d4f63e4356a110b30ef3add8732ab6d181dd7be4607bf79b8777105cee" dependencies = [ - "alloy-consensus 0.2.0", - "alloy-network 0.2.0", + "alloy-consensus 0.2.1", + "alloy-network 0.2.1", "alloy-primitives", - "alloy-signer 0.2.0", + "alloy-signer 0.2.1", "async-trait", "k256 0.13.3", "rand 0.8.5", @@ -802,12 +945,46 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-transport" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +dependencies = [ + "alloy-json-rpc 0.1.4", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + [[package]] name = "alloy-transport-http" version = "0.1.0" source = "git+https://github.com/alloy-rs/alloy?rev=00d81d7#00d81d7882a0bee4720d6d6a1db4c8f164ebb9d0" dependencies = [ - "alloy-transport", + "alloy-transport 0.1.0", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-transport 0.1.4", + "reqwest 0.12.5", + "serde_json", + "tower", + "tracing", "url", ] @@ -6419,6 +6596,22 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.6" @@ -7318,11 +7511,13 @@ dependencies = [ name = "kakarot-rpc" version = "0.6.20" dependencies = [ + "alloy", "alloy-contract", "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", "alloy-rlp", + "alloy-signer 0.2.1", "alloy-signer-local", "alloy-sol-types", "anyhow", @@ -10036,7 +10231,7 @@ dependencies = [ "http-body 0.4.6", "hyper 0.14.30", "hyper-rustls 0.24.2", - "hyper-tls", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -10086,11 +10281,13 @@ dependencies = [ "http-body-util", "hyper 1.4.1", "hyper-rustls 0.27.2", + "hyper-tls 0.6.0", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -10104,6 +10301,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.0", "tower-service", "url", diff --git a/Cargo.toml b/Cargo.toml index 5297bfcdd..4b2375e18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,10 +120,12 @@ http-body-util = { version = "0.1", default-features = false } pin-project-lite = { version = "0.2", default-features = false } # Testing crates +alloy = { version = "0.1.1", features = ["rpc-types"], optional = true } alloy-dyn-abi = { version = "0.7.6", default-features = false } alloy-json-abi = { version = "0.7.6", default-features = false, optional = true } alloy-primitives = { version = "0.7.2", default-features = false, optional = true } alloy-signer-local = { version = "0.2.0", default-features = false, optional = true } +alloy-signer = { version = "0.2.0", default-features = false, optional = true } anyhow = { version = "1", default-features = false, optional = true } arbitrary = { version = "1", features = ["derive"], optional = true } ef-testing = { git = "https://github.com/kkrt-labs/ef-tests", rev = "2f5b44b", default-features = false, features = [ @@ -160,9 +162,11 @@ toml = { version = "0.8", default-features = false } [features] testing = [ + "alloy", "alloy-json-abi", "alloy-primitives", "alloy-signer-local", + "alloy-signer", "anyhow", "dep:arbitrary", "dojo-test-utils", diff --git a/src/providers/eth_provider/database/ethereum.rs b/src/providers/eth_provider/database/ethereum.rs index db8b91fe8..ce4763f38 100644 --- a/src/providers/eth_provider/database/ethereum.rs +++ b/src/providers/eth_provider/database/ethereum.rs @@ -399,17 +399,12 @@ mod tests { let block_transactions = BlockTransactions::Full(transactions.clone()); - let signed_transactions = transactions - .into_iter() - .map(|tx| TransactionSigned::try_from(tx).map_err(|_| EthereumDataFormatError::TransactionConversion)) - .collect::, _>>() - .unwrap(); + let signed_transactions = + transactions.into_iter().map(TransactionSigned::try_from).collect::, _>>().unwrap(); let block = reth_primitives::Block { body: signed_transactions, - header: reth_primitives::Header::try_from(header.clone()) - .map_err(|_| EthereumDataFormatError::Primitive) - .unwrap(), + header: reth_primitives::Header::try_from(header.clone()).unwrap(), withdrawals: Some(Default::default()), ..Default::default() }; diff --git a/src/providers/eth_provider/database/types/transaction.rs b/src/providers/eth_provider/database/types/transaction.rs index c8cf14cca..a1ed5d7b2 100644 --- a/src/providers/eth_provider/database/types/transaction.rs +++ b/src/providers/eth_provider/database/types/transaction.rs @@ -9,9 +9,10 @@ use { RECOVERED_EIP1599_TX_ADDRESS, RECOVERED_EIP2930_TX_ADDRESS, RECOVERED_LEGACY_TX_ADDRESS, TEST_SIG_R, TEST_SIG_S, TEST_SIG_V, }, + alloy_signer::SignerSync, + alloy_signer_local::PrivateKeySigner, arbitrary::Arbitrary, - reth_primitives::TxType, - reth_primitives::{Address, U256}, + reth_primitives::{Address, TransactionSignedNoHash, TxType, U256}, }; /// A full transaction as stored in the database @@ -128,38 +129,67 @@ impl Deref for StoredTransaction { #[cfg(any(test, feature = "arbitrary", feature = "testing"))] impl<'a> StoredTransaction { pub fn arbitrary_with_optional_fields(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let transaction = Transaction::arbitrary(u)?; - - let transaction_type = Into::::into(transaction.transaction_type.unwrap_or_default()) % 3; - - Ok(Self { - tx: Transaction { - block_hash: Some(B256::arbitrary(u)?), - block_number: Some(u64::arbitrary(u)?), - transaction_index: Some(u64::arbitrary(u)?), - gas_price: Some(u128::arbitrary(u)?), - gas: u128::from(u64::arbitrary(u)?), - max_fee_per_gas: if TryInto::::try_into(transaction_type).unwrap() == TxType::Legacy { - None - } else { - Some(u128::arbitrary(u)?) - }, - max_priority_fee_per_gas: if TryInto::::try_into(transaction_type).unwrap() == TxType::Legacy { - None - } else { - Some(u128::arbitrary(u)?) - }, - signature: Some(reth_rpc_types::Signature { - y_parity: Some(reth_rpc_types::Parity(bool::arbitrary(u)?)), - ..reth_rpc_types::Signature::arbitrary(u)? - }), - transaction_type: Some(transaction_type), - chain_id: Some(u64::from(u32::arbitrary(u)?)), - other: Default::default(), - access_list: Some(reth_rpc_types::AccessList::arbitrary(u)?), - ..transaction - }, - }) + let mut primitive_transaction = reth_primitives::Transaction::arbitrary(u)?; + + // Ensure the transaction is not a blob transaction + while primitive_transaction.tx_type() == 3 { + primitive_transaction = reth_primitives::Transaction::arbitrary(u)?; + } + + // Force the chain ID to be set + let chain_id = u32::arbitrary(u)?.into(); + primitive_transaction.set_chain_id(chain_id); + + // Force nonce to be set + let nonce = u64::arbitrary(u)?; + primitive_transaction.set_nonce(nonce); + + // Compute the signing hash + let signing_hash = primitive_transaction.signature_hash(); + + // Sign the transaction with a local wallet + let signer = PrivateKeySigner::random(); + let signature = signer.sign_hash_sync(&signing_hash).map_err(|_| arbitrary::Error::IncorrectFormat)?; + + // Use TransactionSignedNoHash to compute the hash + let y_parity = signature.v().y_parity(); + let hash = TransactionSignedNoHash { + transaction: primitive_transaction.clone(), + signature: reth_primitives::Signature { r: signature.r(), s: signature.s(), odd_y_parity: y_parity }, + } + .hash(); + + // Convert the signature to the RPC format + let is_legacy = primitive_transaction.is_legacy(); + // See docs on `alloy::rpc::types::Signature` for `v` field. + let v: u64 = if is_legacy { 35 + 2 * chain_id + u64::from(y_parity) } else { u64::from(y_parity) }; + let signature = alloy::rpc::types::Signature { + r: signature.r(), + s: signature.s(), + v: U256::from(v), + y_parity: if is_legacy { None } else { Some(signature.v().y_parity().into()) }, + }; + + let transaction = Transaction { + hash, + from: signer.address(), + block_hash: Some(B256::arbitrary(u)?), + block_number: Some(u64::arbitrary(u)?), + transaction_index: Some(u64::arbitrary(u)?), + gas_price: Some(primitive_transaction.effective_gas_price(None)), + gas: u128::from(primitive_transaction.gas_limit()), + max_fee_per_gas: if is_legacy { None } else { Some(primitive_transaction.max_fee_per_gas()) }, + max_priority_fee_per_gas: primitive_transaction.max_priority_fee_per_gas(), + signature: Some(signature), + transaction_type: Some(primitive_transaction.tx_type() as u8), + chain_id: primitive_transaction.chain_id(), + nonce: primitive_transaction.nonce(), + other: Default::default(), + access_list: Some(reth_rpc_types::AccessList::arbitrary(u)?), + ..Default::default() + }; + + Ok(Self { tx: transaction }) } }