Compare commits

..

41 Commits

Author SHA1 Message Date
Omar Abdulla d04cdf337f Merge remote-tracking branch 'origin/main' into cleanup-execution-logic 2025-07-18 14:47:22 +03:00
Omar Abdulla 11b568a442 Merge remote-tracking branch 'origin/main' into cleanup-execution-logic 2025-07-17 19:46:27 +03:00
Omar Abdulla 74b5e34260 Cleanup execution logic 2025-07-16 18:21:14 +03:00
Omar Abdulla 4c55bba53d Merge branch 'feature/better-input-parser' into omar-temp 2025-07-16 15:34:28 +03:00
Omar Abdulla 222b5d4f86 Merge branch 'bugfix/finding-contract-abi' into omar-temp 2025-07-16 15:34:21 +03:00
Omar Abdulla 762288bf04 Merge branch 'bugfix/kitchensink-gas-limit' into omar-temp 2025-07-16 15:34:04 +03:00
Omar Abdulla cac2220188 Merge remote-tracking branch 'origin/main' into feature/better-input-parser 2025-07-16 15:31:33 +03:00
Omar Abdulla 4bf22f2d2b Merge remote-tracking branch 'origin/main' into bugfix/finding-contract-abi 2025-07-16 15:21:21 +03:00
Omar Abdulla 5c64de7e67 Change kitchensink gas limit assertion 2025-07-15 14:08:55 +03:00
Omar Abdulla fa4bf95091 Add comment on alternative solutions 2025-07-15 13:57:31 +03:00
Omar Abdulla 2537a132e6 Fix tests 2025-07-15 13:54:58 +03:00
Omar Abdulla 20da99784e Add resolution logic for other matterlabs variables 2025-07-14 23:51:59 +03:00
Omar Abdulla 6d7cd67931 Expose APIs for getting the info of a specific block 2025-07-14 23:21:53 +03:00
Omar Abdulla 68bda92465 Add a way to get block info from the node 2025-07-14 23:14:37 +03:00
Omar Abdulla ddd775d703 Add a way to get the block difficulty from the node 2025-07-14 22:53:40 +03:00
Omar Abdulla 02547b62ee Add a way to get the coinbase address 2025-07-14 22:48:45 +03:00
Omar Abdulla 61540741e1 Add support for getting the gas limit from the node 2025-07-14 22:37:50 +03:00
Omar Abdulla fa4bbbb987 Use provider method in tests 2025-07-14 22:33:09 +03:00
Omar Abdulla 8f80b1da8a Get kitchensink provider to use kitchensink network 2025-07-14 22:30:30 +03:00
Omar Abdulla c6d63255ec Merge remote-tracking branch 'origin/bugfix/kitchensink-gas-limit' into feature/better-input-parser 2025-07-14 22:29:34 +03:00
Omar Abdulla a4f5c4c8af Add ability to get the chain_id from node 2025-07-14 22:16:37 +03:00
Omar Abdulla 7d48d1600e Give nodes a standard way to get their alloy provider 2025-07-14 21:59:44 +03:00
Omar Abdulla e7e00a50dd Merge branch 'bugfix/argument-encoding' into feature/better-input-parser 2025-07-14 21:37:09 +03:00
Omar Abdulla 27a0a0de0b Fix doc test 2025-07-14 21:33:57 +03:00
Omar Abdulla 83c20b1be3 Fix tests 2025-07-14 21:30:35 +03:00
Omar Abdulla 331705134a Update the async runtime with syntactic sugar. 2025-07-14 21:13:58 +03:00
Omar Abdulla 075c8235a7 Merge remote-tracking branch 'origin/main' into bugfix/argument-encoding 2025-07-14 20:36:42 +03:00
Omar Abdulla 5f86ade1e0 Implement ABI fix in the compiler trait impl 2025-07-14 20:31:06 +03:00
Omar Abdulla 43064022e8 Merge remote-tracking branch 'origin/main' into bugfix/finding-contract-abi 2025-07-14 20:25:08 +03:00
Omar Abdulla 57bb015fa3 Merge remote-tracking branch 'origin/main' into bugfix/kitchensink-gas-limit 2025-07-14 19:31:34 +03:00
Omar Abdulla 43e0d0e592 Remove reliance on the web3 crate 2025-07-14 18:27:38 +03:00
Omar 332012754d Merge pull request #35 from paritytech/bugfix/fix-kitchensink-no-advance
Fix an issue where kitchensink won't advance
2025-07-14 17:54:58 +03:00
Omar Abdulla eb6c64c17a Merge remote-tracking branch 'origin/main' into bugfix/kitchensink-gas-limit 2025-07-14 13:27:40 +03:00
Omar Abdulla 2373872230 Avoid extra buffer allocation 2025-07-14 00:02:48 +03:00
Omar Abdulla e3723e780a Fix function selector and argument encoding 2025-07-13 19:52:06 +03:00
Omar Abdulla 4d4398f83e Fix the ABI finding logic 2025-07-13 15:59:23 +03:00
Omar Abdulla 76c85f191c fix clippy warning 2025-07-11 17:31:42 +03:00
Omar Abdulla 7664e9735e fix clippy warning 2025-07-11 17:20:36 +03:00
Omar Abdulla 4bab457114 Added --dev to substrate-node arguments.
This commit adds the `--dev` argument to the `substrate-node` to allow
the chain to keep advancing as time goes own. We have found that if this
option is not added then the chain won't advance forward.
2025-07-11 17:18:42 +03:00
Omar Abdulla f6374ad52a fix formatting 2025-07-11 14:49:05 +03:00
Omar Abdulla abba0cee08 Introduce a custom kitchensink network 2025-07-11 11:26:55 +03:00
21 changed files with 741 additions and 1822 deletions
Generated
+72 -79
View File
@@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "alloy" name = "alloy"
version = "1.0.22" version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad4eb51e7845257b70c51b38ef8d842d5e5e93196701fcbd757577971a043c6" checksum = "ae58d888221eecf621595e2096836ce7cfc37be06bfa39d7f64aa6a3ea4c9e5b"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-contract", "alloy-contract",
@@ -102,16 +102,15 @@ dependencies = [
[[package]] [[package]]
name = "alloy-consensus" name = "alloy-consensus"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3b746060277f3d7f9c36903bb39b593a741cb7afcb0044164c28f0e9b673f0" checksum = "ad451f9a70c341d951bca4e811d74dbe1e193897acd17e9dbac1353698cc430b"
dependencies = [ dependencies = [
"alloy-eips", "alloy-eips",
"alloy-primitives", "alloy-primitives",
"alloy-rlp", "alloy-rlp",
"alloy-serde", "alloy-serde",
"alloy-trie", "alloy-trie",
"alloy-tx-macros",
"auto_impl", "auto_impl",
"c-kzg", "c-kzg",
"derive_more 2.0.1", "derive_more 2.0.1",
@@ -127,9 +126,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-consensus-any" name = "alloy-consensus-any"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf98679329fa708fa809ea596db6d974da892b068ad45e48ac1956f582edf946" checksum = "142daffb15d5be1a2b20d2cd540edbcef03037b55d4ff69dc06beb4d06286dba"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-eips", "alloy-eips",
@@ -141,9 +140,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-contract" name = "alloy-contract"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a10e47f5305ea08c37b1772086c1573e9a0a257227143996841172d37d3831bb" checksum = "ebf25443920ecb9728cb087fe4dc04a0b290bd6ac85638c58fe94aba70f1a44e"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-dyn-abi", "alloy-dyn-abi",
@@ -158,7 +157,6 @@ dependencies = [
"alloy-transport", "alloy-transport",
"futures", "futures",
"futures-util", "futures-util",
"serde_json",
"thiserror 2.0.12", "thiserror 2.0.12",
] ]
@@ -229,9 +227,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-eips" name = "alloy-eips"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f562a81278a3ed83290e68361f2d1c75d018ae3b8589a314faf9303883e18ec9" checksum = "3056872f6da48046913e76edb5ddced272861f6032f09461aea1a2497be5ae5d"
dependencies = [ dependencies = [
"alloy-eip2124", "alloy-eip2124",
"alloy-eip2930", "alloy-eip2930",
@@ -249,16 +247,15 @@ dependencies = [
[[package]] [[package]]
name = "alloy-genesis" name = "alloy-genesis"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc41384e9ab8c9b2fb387c52774d9d432656a28edcda1c2d4083e96051524518" checksum = "c98fb40f07997529235cc474de814cd7bd9de561e101716289095696c0e4639d"
dependencies = [ dependencies = [
"alloy-eips", "alloy-eips",
"alloy-primitives", "alloy-primitives",
"alloy-serde", "alloy-serde",
"alloy-trie", "alloy-trie",
"serde", "serde",
"serde_with",
] ]
[[package]] [[package]]
@@ -275,13 +272,12 @@ dependencies = [
[[package]] [[package]]
name = "alloy-json-rpc" name = "alloy-json-rpc"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12c454fcfcd5d26ed3b8cae5933cbee9da5f0b05df19b46d4bd4446d1f082565" checksum = "dc08b31ebf9273839bd9a01f9333cbb7a3abb4e820c312ade349dd18bdc79581"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-sol-types", "alloy-sol-types",
"http",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 2.0.12", "thiserror 2.0.12",
@@ -290,9 +286,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-network" name = "alloy-network"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d6d39eabe5c7b3d8f23ac47b0b683b99faa4359797114636c66e0743103d05" checksum = "ed117b08f0cc190312bf0c38c34cf4f0dabfb4ea8f330071c587cd7160a88cb2"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-consensus-any", "alloy-consensus-any",
@@ -316,9 +312,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-network-primitives" name = "alloy-network-primitives"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3704fa8b7ba9ba3f378d99b3d628c8bc8c2fc431b709947930f154e22a8368b6" checksum = "c7162ff7be8649c0c391f4e248d1273e85c62076703a1f3ec7daf76b283d886d"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-eips", "alloy-eips",
@@ -340,7 +336,7 @@ dependencies = [
"derive_more 2.0.1", "derive_more 2.0.1",
"foldhash", "foldhash",
"hashbrown 0.15.3", "hashbrown 0.15.3",
"indexmap 2.10.0", "indexmap 2.9.0",
"itoa", "itoa",
"k256", "k256",
"keccak-asm", "keccak-asm",
@@ -356,9 +352,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-provider" name = "alloy-provider"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08800e8cbe70c19e2eb7cf3d7ff4b28bdd9b3933f8e1c8136c7d910617ba03bf" checksum = "d84eba1fd8b6fe8b02f2acd5dd7033d0f179e304bd722d11e817db570d1fa6c4"
dependencies = [ dependencies = [
"alloy-chains", "alloy-chains",
"alloy-consensus", "alloy-consensus",
@@ -384,7 +380,6 @@ dependencies = [
"either", "either",
"futures", "futures",
"futures-utils-wasm", "futures-utils-wasm",
"http",
"lru", "lru",
"parking_lot", "parking_lot",
"pin-project", "pin-project",
@@ -400,9 +395,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-pubsub" name = "alloy-pubsub"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae68457a2c2ead6bd7d7acb5bf5f1623324b1962d4f8e7b0250657a3c3ab0a0b" checksum = "8550f7306e0230fc835eb2ff4af0a96362db4b6fc3f25767d161e0ad0ac765bf"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-primitives", "alloy-primitives",
@@ -443,9 +438,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-rpc-client" name = "alloy-rpc-client"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162301b5a57d4d8f000bf30f4dcb82f9f468f3e5e846eeb8598dd39e7886932c" checksum = "518a699422a3eab800f3dac2130d8f2edba8e4fff267b27a9c7dc6a2b0d313ee"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-primitives", "alloy-primitives",
@@ -453,6 +448,7 @@ dependencies = [
"alloy-transport", "alloy-transport",
"alloy-transport-http", "alloy-transport-http",
"alloy-transport-ipc", "alloy-transport-ipc",
"async-stream",
"futures", "futures",
"pin-project", "pin-project",
"reqwest", "reqwest",
@@ -462,15 +458,16 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tower", "tower",
"tracing", "tracing",
"tracing-futures",
"url", "url",
"wasmtimer", "wasmtimer",
] ]
[[package]] [[package]]
name = "alloy-rpc-types" name = "alloy-rpc-types"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cd8ca94ae7e2b32cc3895d9981f3772aab0b4756aa60e9ed0bcfee50f0e1328" checksum = "c000cab4ec26a4b3e29d144e999e1c539c2fa0abed871bf90311eb3466187ca8"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-rpc-types-eth", "alloy-rpc-types-eth",
@@ -481,9 +478,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-rpc-types-any" name = "alloy-rpc-types-any"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076b47e834b367d8618c52dd0a0d6a711ddf66154636df394805300af4923b8a" checksum = "508b2fbe66d952089aa694e53802327798806498cd29ff88c75135770ecaabfc"
dependencies = [ dependencies = [
"alloy-consensus-any", "alloy-consensus-any",
"alloy-rpc-types-eth", "alloy-rpc-types-eth",
@@ -492,9 +489,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-rpc-types-debug" name = "alloy-rpc-types-debug"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94a2a86ad7b7d718c15e79d0779bd255561b6b22968dc5ed2e7c0fbc43bb55fe" checksum = "8c832f2e851801093928dbb4b7bd83cd22270faf76b2e080646b806a285c8757"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"serde", "serde",
@@ -502,9 +499,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-rpc-types-eth" name = "alloy-rpc-types-eth"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c2f847e635ec0be819d06e2ada4bcc4e4204026a83c4bfd78ae8d550e027ae7" checksum = "fcaf7dff0fdd756a714d58014f4f8354a1706ebf9fa2cf73431e0aeec3c9431e"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-consensus-any", "alloy-consensus-any",
@@ -517,15 +514,14 @@ dependencies = [
"itertools 0.14.0", "itertools 0.14.0",
"serde", "serde",
"serde_json", "serde_json",
"serde_with",
"thiserror 2.0.12", "thiserror 2.0.12",
] ]
[[package]] [[package]]
name = "alloy-rpc-types-trace" name = "alloy-rpc-types-trace"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fc58180302a94c934d455eeedb3ecb99cdc93da1dbddcdbbdb79dd6fe618b2a" checksum = "6e3507a04e868dd83219ad3cd6a8c58aefccb64d33f426b3934423a206343e84"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-rpc-types-eth", "alloy-rpc-types-eth",
@@ -537,9 +533,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-serde" name = "alloy-serde"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae699248d02ade9db493bbdae61822277dc14ae0f82a5a4153203b60e34422a6" checksum = "730e8f2edf2fc224cabd1c25d090e1655fa6137b2e409f92e5eec735903f1507"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"serde", "serde",
@@ -548,9 +544,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-signer" name = "alloy-signer"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf7d793c813515e2b627b19a15693960b3ed06670f9f66759396d06ebe5747b" checksum = "6b0d2428445ec13edc711909e023d7779618504c4800be055a5b940025dbafe3"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"async-trait", "async-trait",
@@ -563,9 +559,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-signer-local" name = "alloy-signer-local"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51a424bc5a11df0d898ce0fd15906b88ebe2a6e4f17a514b51bc93946bb756bd" checksum = "e14fe6fedb7fe6e0dfae47fe020684f1d8e063274ef14bca387ddb7a6efa8ec1"
dependencies = [ dependencies = [
"alloy-consensus", "alloy-consensus",
"alloy-network", "alloy-network",
@@ -601,7 +597,7 @@ dependencies = [
"alloy-sol-macro-input", "alloy-sol-macro-input",
"const-hex", "const-hex",
"heck", "heck",
"indexmap 2.10.0", "indexmap 2.9.0",
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -652,9 +648,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-transport" name = "alloy-transport"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f317d20f047b3de4d9728c556e2e9a92c9a507702d2016424cd8be13a74ca5e" checksum = "a712bdfeff42401a7dd9518f72f617574c36226a9b5414537fedc34350b73bf9"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-primitives", "alloy-primitives",
@@ -675,9 +671,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-transport-http" name = "alloy-transport-http"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff084ac7b1f318c87b579d221f11b748341d68b9ddaa4ffca5e62ed2b8cfefb4" checksum = "7ea5a76d7f2572174a382aedf36875bedf60bcc41116c9f031cf08040703a2dc"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-transport", "alloy-transport",
@@ -690,9 +686,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-transport-ipc" name = "alloy-transport-ipc"
version = "1.0.22" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb099cdad8ed2e6a80811cdf9bbf715ebf4e34c981b4a6e2d1f9daacbf8b218" checksum = "606af17a7e064d219746f6d2625676122c79d78bf73dfe746d6db9ecd7dbcb85"
dependencies = [ dependencies = [
"alloy-json-rpc", "alloy-json-rpc",
"alloy-pubsub", "alloy-pubsub",
@@ -710,9 +706,9 @@ dependencies = [
[[package]] [[package]]
name = "alloy-trie" name = "alloy-trie"
version = "0.9.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bada1fc392a33665de0dc50d401a3701b62583c655e3522a323490a5da016962" checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500"
dependencies = [ dependencies = [
"alloy-primitives", "alloy-primitives",
"alloy-rlp", "alloy-rlp",
@@ -724,19 +720,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "alloy-tx-macros"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1154c8187a5ff985c95a8b2daa2fedcf778b17d7668e5e50e556c4ff9c881154"
dependencies = [
"alloy-primitives",
"darling",
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]] [[package]]
name = "android-tzdata" name = "android-tzdata"
version = "0.1.1" version = "0.1.1"
@@ -2417,7 +2400,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http", "http",
"indexmap 2.10.0", "indexmap 2.9.0",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -2859,9 +2842,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.10.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.3", "hashbrown 0.15.3",
@@ -3285,14 +3268,13 @@ dependencies = [
[[package]] [[package]]
name = "nybbles" name = "nybbles"
version = "0.4.1" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675b3a54e5b12af997abc8b6638b0aee51a28caedab70d4967e0d5db3a3f1d06" checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307"
dependencies = [ dependencies = [
"alloy-rlp", "alloy-rlp",
"cfg-if", "const-hex",
"proptest", "proptest",
"ruint",
"serde", "serde",
"smallvec", "smallvec",
] ]
@@ -3980,7 +3962,6 @@ dependencies = [
"alloy", "alloy",
"anyhow", "anyhow",
"clap", "clap",
"indexmap 2.10.0",
"rayon", "rayon",
"revive-dt-compiler", "revive-dt-compiler",
"revive-dt-config", "revive-dt-config",
@@ -4526,7 +4507,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.10.0", "indexmap 2.9.0",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
@@ -5415,7 +5396,7 @@ version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [ dependencies = [
"indexmap 2.10.0", "indexmap 2.9.0",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
@@ -5507,6 +5488,18 @@ dependencies = [
"valuable", "valuable",
] ]
[[package]]
name = "tracing-futures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"futures",
"futures-task",
"pin-project",
"tracing",
]
[[package]] [[package]]
name = "tracing-log" name = "tracing-log"
version = "0.2.0" version = "0.2.0"
+1 -3
View File
@@ -51,7 +51,6 @@ tracing-subscriber = { version = "0.3.19", default-features = false, features =
"json", "json",
"env-filter", "env-filter",
] } ] }
indexmap = { version = "2.10.0", default-features = false }
# revive compiler # revive compiler
revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
@@ -59,7 +58,7 @@ revive-common = { git = "https://github.com/paritytech/revive", rev = "3389865af
revive-differential = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" } revive-differential = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
[workspace.dependencies.alloy] [workspace.dependencies.alloy]
version = "1.0.22" version = "1.0"
default-features = false default-features = false
features = [ features = [
"json-abi", "json-abi",
@@ -73,7 +72,6 @@ features = [
"network", "network",
"serde", "serde",
"rpc-types-eth", "rpc-types-eth",
"genesis",
] ]
[profile.bench] [profile.bench]
-326
View File
@@ -1,326 +0,0 @@
{
"modes": [
"Y >=0.8.9",
"E",
"I"
],
"cases": [
{
"name": "first",
"inputs": [
{
"instance": "WBTC_1",
"method": "#deployer",
"calldata": [
"0x40",
"0x80",
"4",
"0x5742544300000000000000000000000000000000000000000000000000000000",
"14",
"0x5772617070656420425443000000000000000000000000000000000000000000"
],
"expected": [
"WBTC_1.address"
]
},
{
"instance": "WBTC_2",
"method": "#deployer",
"calldata": [
"0x40",
"0x80",
"4",
"0x5742544300000000000000000000000000000000000000000000000000000000",
"14",
"0x5772617070656420425443000000000000000000000000000000000000000000"
],
"expected": [
"WBTC_2.address"
]
},
{
"instance": "Mooniswap",
"method": "#deployer",
"calldata": [
"0x0000000000000000000000000000000000000000000000000000000000000060",
"0x00000000000000000000000000000000000000000000000000000000000000c0",
"0x0000000000000000000000000000000000000000000000000000000000000100",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"WBTC_1.address",
"WBTC_2.address",
"4",
"0x5742544300000000000000000000000000000000000000000000000000000000",
"14",
"0x5772617070656420425443000000000000000000000000000000000000000000"
],
"expected": {
"return_data": [
"Mooniswap.address"
],
"events": [
{
"topics": [
"0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xdeadbeef01000000000000000000000000000000"
],
"values": []
}
],
"exception": false
}
},
{
"instance": "WBTC_1",
"method": "_mint",
"calldata": [
"0xdeadbeef00000000000000000000000000000042",
"1000000000"
],
"expected": {
"return_data": [],
"events": [
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xdeadbeef00000000000000000000000000000042"
],
"values": [
"1000000000"
]
}
],
"exception": false
}
},
{
"instance": "WBTC_2",
"method": "_mint",
"calldata": [
"0xdeadbeef00000000000000000000000000000042",
"1000000000"
],
"expected": {
"return_data": [],
"events": [
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xdeadbeef00000000000000000000000000000042"
],
"values": [
"1000000000"
]
}
],
"exception": false
}
},
{
"instance": "WBTC_1",
"caller": "0xdeadbeef00000000000000000000000000000042",
"method": "approve",
"calldata": [
"Mooniswap.address",
"500000000"
],
"expected": {
"return_data": [
"0x0000000000000000000000000000000000000000000000000000000000000001"
],
"events": [
{
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"500000000"
]
}
],
"exception": false
}
},
{
"instance": "WBTC_2",
"caller": "0xdeadbeef00000000000000000000000000000042",
"method": "approve",
"calldata": [
"Mooniswap.address",
"500000000"
],
"expected": {
"return_data": [
"0x0000000000000000000000000000000000000000000000000000000000000001"
],
"events": [
{
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"500000000"
]
}
],
"exception": false
}
},
{
"instance": "Mooniswap",
"caller": "0xdeadbeef00000000000000000000000000000042",
"method": "deposit",
"calldata": [
"0x0000000000000000000000000000000000000000000000000000000000000040",
"0x00000000000000000000000000000000000000000000000000000000000000a0",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"10000000",
"10000000",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"1000000",
"1000000"
],
"expected": {
"return_data": [
"10000000"
],
"events": [
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"Mooniswap.address"
],
"values": [
"1000"
]
},
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"10000000"
]
},
{
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"490000000"
]
},
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"10000000"
]
},
{
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"490000000"
]
},
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0xdeadbeef00000000000000000000000000000042"
],
"values": [
"10000000"
]
},
{
"topics": [
"0x2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4",
"0xdeadbeef00000000000000000000000000000042"
],
"values": [
"10000000"
]
}
],
"exception": false
}
},
{
"instance": "Mooniswap",
"caller": "0xdeadbeef00000000000000000000000000000042",
"method": "swap",
"calldata": [
"WBTC_1.address",
"WBTC_2.address",
"5000",
"5000",
"0"
]
}
],
"expected": {
"return_data": [
"5000"
],
"events": [
{
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"5000"
]
},
{
"topics": [
"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"0xdeadbeef00000000000000000000000000000042",
"Mooniswap.address"
],
"values": [
"489995000"
]
}
],
"exception": false
}
}
],
"contracts": {
"Mooniswap": "Mooniswap.sol:Mooniswap",
"WBTC_1": "ERC20/ERC20.sol:ERC20",
"WBTC_2": "ERC20/ERC20.sol:ERC20",
"VirtualBalance": "Mooniswap.sol:VirtualBalance",
"Math": "math/Math.sol:Math"
},
"libraries": {
"Mooniswap.sol": {
"VirtualBalance": "VirtualBalance"
},
"math/Math.sol": {
"Math": "Math"
}
},
"group": "Real life"
}
+7 -9
View File
@@ -123,18 +123,16 @@ impl Arguments {
panic!("should have a workdir configured") panic!("should have a workdir configured")
} }
pub fn signer(&self) -> PrivateKeySigner {
self.account
.parse::<PrivateKeySigner>()
.unwrap_or_else(|error| {
panic!("private key '{}' parsing error: {error}", self.account);
})
}
/// Try to parse `self.account` into a [PrivateKeySigner], /// Try to parse `self.account` into a [PrivateKeySigner],
/// panicing on error. /// panicing on error.
pub fn wallet(&self) -> EthereumWallet { pub fn wallet(&self) -> EthereumWallet {
EthereumWallet::new(self.signer()) let signer = self
.account
.parse::<PrivateKeySigner>()
.unwrap_or_else(|error| {
panic!("private key '{}' parsing error: {error}", self.account);
});
EthereumWallet::new(signer)
} }
} }
-1
View File
@@ -23,7 +23,6 @@ revive-dt-report = { workspace = true }
alloy = { workspace = true } alloy = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
indexmap = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-subscriber = { workspace = true } tracing-subscriber = { workspace = true }
rayon = { workspace = true } rayon = { workspace = true }
-73
View File
@@ -1,73 +0,0 @@
use std::{borrow::Cow, collections::HashSet, path::PathBuf};
/// An iterator that finds files of a certain extension in the provided directory. You can think of
/// this a glob pattern similar to: `${path}/**/*.md`
pub struct FilesWithExtensionIterator {
/// The set of allowed extensions that that match the requirement and that should be returned
/// when found.
allowed_extensions: HashSet<Cow<'static, str>>,
/// The set of directories to visit next. This iterator does BFS and so these directories will
/// only be visited if we can't find any files in our state.
directories_to_search: Vec<PathBuf>,
/// The set of files matching the allowed extensions that were found. If there are entries in
/// this vector then they will be returned when the [`Iterator::next`] method is called. If not
/// then we visit one of the next directories to visit.
files_matching_allowed_extensions: Vec<PathBuf>,
}
impl FilesWithExtensionIterator {
pub fn new(root_directory: PathBuf) -> Self {
Self {
allowed_extensions: Default::default(),
directories_to_search: vec![root_directory],
files_matching_allowed_extensions: Default::default(),
}
}
pub fn with_allowed_extension(
mut self,
allowed_extension: impl Into<Cow<'static, str>>,
) -> Self {
self.allowed_extensions.insert(allowed_extension.into());
self
}
}
impl Iterator for FilesWithExtensionIterator {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
if let Some(file_path) = self.files_matching_allowed_extensions.pop() {
return Some(file_path);
};
let directory_to_search = self.directories_to_search.pop()?;
// Read all of the entries in the directory. If we failed to read this dir's entires then we
// elect to just ignore it and look in the next directory, we do that by calling the next
// method again on the iterator, which is an intentional decision that we made here instead
// of panicking.
let Ok(dir_entries) = std::fs::read_dir(directory_to_search) else {
return self.next();
};
for entry in dir_entries.flatten() {
let entry_path = entry.path();
if entry_path.is_dir() {
self.directories_to_search.push(entry_path)
} else if entry_path.is_file()
&& entry_path.extension().is_some_and(|ext| {
self.allowed_extensions
.iter()
.any(|allowed| ext.eq_ignore_ascii_case(allowed.as_ref()))
})
{
self.files_matching_allowed_extensions.push(entry_path)
}
}
self.next()
}
}
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -1,14 +1,13 @@
//! The revive differential testing core library. //! The revive differential testing core library.
//! //!
//! This crate defines the testing configuration and //! This crate defines the testing configuration and
//! provides a helper utility to execute tests. //! provides a helper utilty to execute tests.
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc}; use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
use revive_dt_config::TestingPlatform; use revive_dt_config::TestingPlatform;
use revive_dt_node::{geth, kitchensink::KitchensinkNode}; use revive_dt_node::{geth, kitchensink::KitchensinkNode};
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
pub mod common;
pub mod driver; pub mod driver;
/// One platform can be tested differentially against another. /// One platform can be tested differentially against another.
+8 -31
View File
@@ -1,6 +1,5 @@
use std::{collections::HashMap, sync::LazyLock}; use std::{collections::HashMap, sync::LazyLock};
use alloy::primitives::Address;
use clap::Parser; use clap::Parser;
use rayon::{ThreadPoolBuilder, prelude::*}; use rayon::{ThreadPoolBuilder, prelude::*};
@@ -9,7 +8,7 @@ use revive_dt_core::{
Geth, Kitchensink, Platform, Geth, Kitchensink, Platform,
driver::{Driver, State}, driver::{Driver, State},
}; };
use revive_dt_format::{corpus::Corpus, input::default_caller, metadata::MetadataFile}; use revive_dt_format::{corpus::Corpus, metadata::MetadataFile};
use revive_dt_node::pool::NodePool; use revive_dt_node::pool::NodePool;
use revive_dt_report::reporter::{Report, Span}; use revive_dt_report::reporter::{Report, Span};
use temp_dir::TempDir; use temp_dir::TempDir;
@@ -21,24 +20,12 @@ static TEMP_DIR: LazyLock<TempDir> = LazyLock::new(|| TempDir::new().unwrap());
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let args = init_cli()?; let args = init_cli()?;
let corpora = collect_corpora(&args)?;
let additional_callers = corpora
.values()
.flat_map(|value| value.iter().map(|metadata| &metadata.cases))
.flat_map(|case| case.iter().map(|case| &case.inputs))
.flatten()
.map(|input| input.caller)
.filter(|caller| caller != &default_caller())
.collect::<Vec<_>>();
tracing::debug!(?additional_callers, "Discovered callers");
for (corpus, tests) in collect_corpora(&args)? { for (corpus, tests) in collect_corpora(&args)? {
let span = Span::new(corpus, args.clone())?; let span = Span::new(corpus, args.clone())?;
match &args.compile_only { match &args.compile_only {
Some(platform) => compile_corpus(&args, &tests, platform, span), Some(platform) => compile_corpus(&args, &tests, platform, span),
None => execute_corpus(&args, &tests, &additional_callers, span)?, None => execute_corpus(&args, &tests, span)?,
} }
Report::save()?; Report::save()?;
@@ -96,20 +83,15 @@ fn collect_corpora(args: &Arguments) -> anyhow::Result<HashMap<Corpus, Vec<Metad
Ok(corpora) Ok(corpora)
} }
fn run_driver<L, F>( fn run_driver<L, F>(args: &Arguments, tests: &[MetadataFile], span: Span) -> anyhow::Result<()>
args: &Arguments,
tests: &[MetadataFile],
additional_callers: &[Address],
span: Span,
) -> anyhow::Result<()>
where where
L: Platform, L: Platform,
F: Platform, F: Platform,
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static, L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static, F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
{ {
let leader_nodes = NodePool::<L::Blockchain>::new(args, additional_callers)?; let leader_nodes = NodePool::<L::Blockchain>::new(args)?;
let follower_nodes = NodePool::<F::Blockchain>::new(args, additional_callers)?; let follower_nodes = NodePool::<F::Blockchain>::new(args)?;
tests.par_iter().for_each( tests.par_iter().for_each(
|MetadataFile { |MetadataFile {
@@ -159,18 +141,13 @@ where
Ok(()) Ok(())
} }
fn execute_corpus( fn execute_corpus(args: &Arguments, tests: &[MetadataFile], span: Span) -> anyhow::Result<()> {
args: &Arguments,
tests: &[MetadataFile],
additional_callers: &[Address],
span: Span,
) -> anyhow::Result<()> {
match (&args.leader, &args.follower) { match (&args.leader, &args.follower) {
(TestingPlatform::Geth, TestingPlatform::Kitchensink) => { (TestingPlatform::Geth, TestingPlatform::Kitchensink) => {
run_driver::<Geth, Kitchensink>(args, tests, additional_callers, span)? run_driver::<Geth, Kitchensink>(args, tests, span)?
} }
(TestingPlatform::Geth, TestingPlatform::Geth) => { (TestingPlatform::Geth, TestingPlatform::Geth) => {
run_driver::<Geth, Geth>(args, tests, additional_callers, span)? run_driver::<Geth, Geth>(args, tests, span)?
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
+1 -30
View File
@@ -1,10 +1,6 @@
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{input::Input, mode::Mode};
define_wrapper_type,
input::{Expected, Input},
mode::Mode,
};
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)] #[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
pub struct Case { pub struct Case {
@@ -13,29 +9,4 @@ pub struct Case {
pub modes: Option<Vec<Mode>>, pub modes: Option<Vec<Mode>>,
pub inputs: Vec<Input>, pub inputs: Vec<Input>,
pub group: Option<String>, pub group: Option<String>,
pub expected: Option<Expected>,
}
define_wrapper_type!(
/// A wrapper type for the index of test cases found in metadata file.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
CaseIdx(usize);
);
impl Case {
pub fn inputs_iterator(&self) -> impl Iterator<Item = Input> {
let inputs_len = self.inputs.len();
self.inputs
.clone()
.into_iter()
.enumerate()
.map(move |(idx, mut input)| {
if idx + 1 == inputs_len {
input.expected = self.expected.clone();
input
} else {
input
}
})
}
} }
+112 -243
View File
@@ -7,24 +7,21 @@ use alloy::{
primitives::{Address, Bytes, U256}, primitives::{Address, Bytes, U256},
rpc::types::TransactionRequest, rpc::types::TransactionRequest,
}; };
use alloy_primitives::B256;
use semver::VersionReq; use semver::VersionReq;
use serde::Deserialize; use serde::Deserialize;
use serde_json::Value;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
use crate::metadata::ContractInstance;
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Input { pub struct Input {
#[serde(default = "default_caller")] #[serde(default = "default_caller")]
pub caller: Address, pub caller: Address,
pub comment: Option<String>, pub comment: Option<String>,
#[serde(default = "default_instance")] #[serde(default = "default_instance")]
pub instance: ContractInstance, pub instance: String,
pub method: Method, pub method: Method,
#[serde(default)] pub calldata: Option<Calldata>,
pub calldata: Calldata,
pub expected: Option<Expected>, pub expected: Option<Expected>,
pub value: Option<String>, pub value: Option<String>,
pub storage: Option<HashMap<String, Calldata>>, pub storage: Option<HashMap<String, Calldata>>,
@@ -40,18 +37,10 @@ pub enum Expected {
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct ExpectedOutput { pub struct ExpectedOutput {
pub compiler_version: Option<VersionReq>, compiler_version: Option<VersionReq>,
pub return_data: Option<Calldata>, return_data: Option<Calldata>,
pub events: Option<Vec<Event>>, events: Option<Value>,
#[serde(default)] exception: Option<bool>,
pub exception: bool,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Event {
pub address: Option<Address>,
pub topics: Vec<B256>,
pub values: Calldata,
} }
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
@@ -82,175 +71,109 @@ pub enum Method {
FunctionName(String), FunctionName(String),
} }
impl ExpectedOutput {
pub fn new() -> Self {
Default::default()
}
pub fn with_success(mut self) -> Self {
self.exception = false;
self
}
pub fn with_failure(mut self) -> Self {
self.exception = true;
self
}
pub fn with_calldata(mut self, calldata: Calldata) -> Self {
self.return_data = Some(calldata);
self
}
}
impl Default for Calldata {
fn default() -> Self {
Self::Compound(Default::default())
}
}
impl Calldata {
pub fn find_all_contract_instances(&self, vec: &mut Vec<ContractInstance>) {
if let Calldata::Compound(compound) = self {
for item in compound {
if let Some(instance) = item.strip_suffix(".address") {
vec.push(ContractInstance::new_from(instance))
}
}
}
}
pub fn calldata(
&self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<Vec<u8>> {
let mut buffer = Vec::<u8>::with_capacity(self.size_requirement());
self.calldata_into_slice(&mut buffer, deployed_contracts, chain_state_provider)?;
Ok(buffer)
}
pub fn calldata_into_slice(
&self,
buffer: &mut Vec<u8>,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>,
chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<()> {
match self {
Calldata::Single(string) => {
alloy::hex::decode_to_slice(string, buffer)?;
}
Calldata::Compound(items) => {
for (arg_idx, arg) in items.iter().enumerate() {
match resolve_argument(arg, deployed_contracts, chain_state_provider) {
Ok(resolved) => {
buffer.extend(resolved.to_be_bytes::<32>());
}
Err(error) => {
tracing::error!(arg, arg_idx, ?error, "Failed to resolve argument");
return Err(error);
}
};
}
}
};
Ok(())
}
pub fn size_requirement(&self) -> usize {
match self {
Calldata::Single(single) => (single.len() - 2) / 2,
Calldata::Compound(items) => items.len() * 32,
}
}
}
impl Input { impl Input {
fn instance_to_address( fn instance_to_address(
&self, &self,
instance: &ContractInstance, instance: &str,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<String, Address>,
) -> anyhow::Result<Address> { ) -> anyhow::Result<Address> {
deployed_contracts deployed_contracts
.get(instance) .get(instance)
.map(|(a, _)| *a) .copied()
.ok_or_else(|| anyhow::anyhow!("instance {instance:?} not deployed")) .ok_or_else(|| anyhow::anyhow!("instance {instance} not deployed"))
} }
pub fn encoded_input( pub fn encoded_input(
&self, &self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_abis: &HashMap<String, JsonAbi>,
deployed_contracts: &HashMap<String, Address>,
chain_state_provider: &impl EthereumNode, chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<Bytes> { ) -> anyhow::Result<Bytes> {
match self.method { let Method::FunctionName(ref function_name) = self.method else {
Method::Deployer | Method::Fallback => { return Ok(Bytes::default()); // fallback or deployer — no input
let calldata = self };
.calldata
.calldata(deployed_contracts, chain_state_provider)?;
Ok(calldata.into()) let Some(abi) = deployed_abis.get(&self.instance) else {
} tracing::error!(
Method::FunctionName(ref function_name) => { contract_name = self.instance,
let Some(abi) = deployed_contracts.get(&self.instance).map(|(_, a)| a) else { available_abis = ?deployed_abis.keys().collect::<Vec<_>>(),
tracing::error!( "Attempted to lookup ABI of contract but it wasn't found"
contract_name = self.instance.as_ref(), );
available_abis = ?deployed_contracts.keys().collect::<Vec<_>>(), anyhow::bail!("ABI for instance '{}' not found", &self.instance);
"Attempted to lookup ABI of contract but it wasn't found" };
);
anyhow::bail!("ABI for instance '{}' not found", self.instance.as_ref());
};
tracing::trace!("ABI found for instance: {}", &self.instance.as_ref()); tracing::trace!("ABI found for instance: {}", &self.instance);
// We follow the same logic that's implemented in the matter-labs-tester where they resolve // We follow the same logic that's implemented in the matter-labs-tester where they resolve
// the function name into a function selector and they assume that he function doesn't have // the function name into a function selector and they assume that he function doesn't have
// any existing overloads. // any existing overloads.
// https://github.com/matter-labs/era-compiler-tester/blob/1dfa7d07cba0734ca97e24704f12dd57f6990c2c/compiler_tester/src/test/case/input/mod.rs#L158-L190 // https://github.com/matter-labs/era-compiler-tester/blob/1dfa7d07cba0734ca97e24704f12dd57f6990c2c/compiler_tester/src/test/case/input/mod.rs#L158-L190
let function = abi let function = abi
.functions() .functions()
.find(|function| function.signature().starts_with(function_name)) .find(|function| function.name.starts_with(function_name))
.ok_or_else(|| { .ok_or_else(|| {
anyhow::anyhow!( anyhow::anyhow!(
"Function with name {:?} not found in ABI for the instance {:?}", "Function with name {:?} not found in ABI for the instance {:?}",
function_name, function_name,
&self.instance &self.instance
) )
})?; })?;
tracing::trace!("Functions found for instance: {}", self.instance.as_ref()); tracing::trace!("Functions found for instance: {}", &self.instance);
tracing::trace!( let calldata_args = match &self.calldata {
"Starting encoding ABI's parameters for instance: {}", Some(Calldata::Compound(args)) => args,
self.instance.as_ref() _ => anyhow::bail!("Expected compound calldata for function call"),
); };
// Allocating a vector that we will be using for the calldata. The vector size will be: if calldata_args.len() != function.inputs.len() {
// 4 bytes for the function selector. anyhow::bail!(
// function.inputs.len() * 32 bytes for the arguments (each argument is a U256). "Function expects {} args, but got {}",
// function.inputs.len(),
// We're using indices in the following code in order to avoid the need for us to allocate calldata_args.len()
// a new buffer for each one of the resolved arguments. );
let mut calldata = Vec::<u8>::with_capacity(4 + self.calldata.size_requirement());
calldata.extend(function.selector().0);
self.calldata.calldata_into_slice(
&mut calldata,
deployed_contracts,
chain_state_provider,
)?;
Ok(calldata.into())
}
} }
tracing::trace!(
"Starting encoding ABI's parameters for instance: {}",
&self.instance
);
// Allocating a vector that we will be using for the calldata. The vector size will be:
// 4 bytes for the function selector.
// function.inputs.len() * 32 bytes for the arguments (each argument is a U256).
//
// We're using indices in the following code in order to avoid the need for us to allocate
// a new buffer for each one of the resolved arguments.
let mut calldata = Vec::<u8>::with_capacity(4 + calldata_args.len() * 32);
calldata.extend(function.selector().0);
for (arg_idx, arg) in calldata_args.iter().enumerate() {
match resolve_argument(arg, deployed_contracts, chain_state_provider) {
Ok(resolved) => {
calldata.extend(resolved.to_be_bytes::<32>());
}
Err(error) => {
tracing::error!(arg, arg_idx, ?error, "Failed to resolve argument");
return Err(error);
}
};
}
Ok(calldata.into())
} }
/// Parse this input into a legacy transaction. /// Parse this input into a legacy transaction.
pub fn legacy_transaction( pub fn legacy_transaction(
&self, &self,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, nonce: u64,
deployed_contracts: &HashMap<String, Address>,
deployed_abis: &HashMap<String, JsonAbi>,
chain_state_provider: &impl EthereumNode, chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<TransactionRequest> { ) -> anyhow::Result<TransactionRequest> {
let input_data = self.encoded_input(deployed_contracts, chain_state_provider)?; let input_data =
let transaction_request = TransactionRequest::default(); self.encoded_input(deployed_abis, deployed_contracts, chain_state_provider)?;
let transaction_request = TransactionRequest::default().nonce(nonce);
match self.method { match self.method {
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)), Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
_ => Ok(transaction_request _ => Ok(transaction_request
@@ -258,22 +181,13 @@ impl Input {
.input(input_data.into())), .input(input_data.into())),
} }
} }
pub fn find_all_contract_instances(&self) -> Vec<ContractInstance> {
let mut vec = Vec::new();
vec.push(self.instance.clone());
self.calldata.find_all_contract_instances(&mut vec);
vec
}
} }
pub fn default_instance() -> ContractInstance { fn default_instance() -> String {
ContractInstance::new_from("Test") "Test".to_string()
} }
pub fn default_caller() -> Address { fn default_caller() -> Address {
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap() "90F8bf6A479f320ead074411a4B0e7944Ea8c9C1".parse().unwrap()
} }
@@ -287,14 +201,13 @@ pub fn default_caller() -> Address {
/// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146 /// https://github.com/matter-labs/era-compiler-tester/blob/0ed598a27f6eceee7008deab3ff2311075a2ec69/compiler_tester/src/test/case/input/value.rs#L43-L146
fn resolve_argument( fn resolve_argument(
value: &str, value: &str,
deployed_contracts: &HashMap<ContractInstance, (Address, JsonAbi)>, deployed_contracts: &HashMap<String, Address>,
chain_state_provider: &impl EthereumNode, chain_state_provider: &impl EthereumNode,
) -> anyhow::Result<U256> { ) -> anyhow::Result<U256> {
if let Some(instance) = value.strip_suffix(".address") { if let Some(instance) = value.strip_suffix(".address") {
Ok(U256::from_be_slice( Ok(U256::from_be_slice(
deployed_contracts deployed_contracts
.get(&ContractInstance::new_from(instance)) .get(instance)
.map(|(a, _)| *a)
.ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))? .ok_or_else(|| anyhow::anyhow!("Instance `{}` not found", instance))?
.as_ref(), .as_ref(),
)) ))
@@ -369,19 +282,22 @@ mod tests {
fn trace_transaction( fn trace_transaction(
&self, &self,
_: &alloy::rpc::types::TransactionReceipt, _: alloy::rpc::types::TransactionReceipt,
_: alloy::rpc::types::trace::geth::GethDebugTracingOptions,
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> { ) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
unimplemented!() unimplemented!()
} }
fn state_diff( fn state_diff(
&self, &self,
_: &alloy::rpc::types::TransactionReceipt, _: alloy::rpc::types::TransactionReceipt,
) -> anyhow::Result<alloy::rpc::types::trace::geth::DiffMode> { ) -> anyhow::Result<alloy::rpc::types::trace::geth::DiffMode> {
unimplemented!() unimplemented!()
} }
fn fetch_add_nonce(&self, _: Address) -> anyhow::Result<u64> {
unimplemented!()
}
fn chain_id(&self) -> anyhow::Result<alloy_primitives::ChainId> { fn chain_id(&self) -> anyhow::Result<alloy_primitives::ChainId> {
Ok(0x123) Ok(0x123)
} }
@@ -441,19 +357,19 @@ mod tests {
.0; .0;
let input = Input { let input = Input {
instance: ContractInstance::new_from("Contract"), instance: "Contract".to_string(),
method: Method::FunctionName("store".to_owned()), method: Method::FunctionName("store".to_owned()),
calldata: Calldata::Compound(vec!["42".into()]), calldata: Some(Calldata::Compound(vec!["42".into()])),
..Default::default() ..Default::default()
}; };
let mut contracts = HashMap::new(); let mut deployed_abis = HashMap::new();
contracts.insert( deployed_abis.insert("Contract".to_string(), parsed_abi);
ContractInstance::new_from("Contract"), let deployed_contracts = HashMap::new();
(Address::ZERO, parsed_abi),
);
let encoded = input.encoded_input(&contracts, &DummyEthereumNode).unwrap(); let encoded = input
.encoded_input(&deployed_abis, &deployed_contracts, &DummyEthereumNode)
.unwrap();
assert!(encoded.0.starts_with(&selector)); assert!(encoded.0.starts_with(&selector));
type T = (u64,); type T = (u64,);
@@ -483,68 +399,21 @@ mod tests {
.0; .0;
let input: Input = Input { let input: Input = Input {
instance: ContractInstance::new_from("Contract"), instance: "Contract".to_string(),
method: Method::FunctionName("send".to_owned()), method: Method::FunctionName("send".to_owned()),
calldata: Calldata::Compound(vec![ calldata: Some(Calldata::Compound(vec![
"0x1000000000000000000000000000000000000001".to_string(), "0x1000000000000000000000000000000000000001".to_string(),
]), ])),
..Default::default() ..Default::default()
}; };
let mut contracts = HashMap::new(); let mut abis = HashMap::new();
contracts.insert( abis.insert("Contract".to_string(), parsed_abi);
ContractInstance::new_from("Contract"), let contracts = HashMap::new();
(Address::ZERO, parsed_abi),
);
let encoded = input.encoded_input(&contracts, &DummyEthereumNode).unwrap(); let encoded = input
assert!(encoded.0.starts_with(&selector)); .encoded_input(&abis, &contracts, &DummyEthereumNode)
.unwrap();
type T = (alloy_primitives::Address,);
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
assert_eq!(
decoded.0,
address!("0x1000000000000000000000000000000000000001")
);
}
#[test]
fn test_encoded_input_address_with_signature() {
let raw_abi = r#"[
{
"inputs": [{"name": "recipient", "type": "address"}],
"name": "send",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]"#;
let parsed_abi: JsonAbi = serde_json::from_str(raw_abi).unwrap();
let selector = parsed_abi
.function("send")
.unwrap()
.first()
.unwrap()
.selector()
.0;
let input: Input = Input {
instance: ContractInstance::new_from("Contract"),
method: Method::FunctionName("send(address)".to_owned()),
calldata: Calldata::Compound(vec![
"0x1000000000000000000000000000000000000001".to_string(),
]),
..Default::default()
};
let mut contracts = HashMap::new();
contracts.insert(
ContractInstance::new_from("Contract"),
(Address::ZERO, parsed_abi),
);
let encoded = input.encoded_input(&contracts, &DummyEthereumNode).unwrap();
assert!(encoded.0.starts_with(&selector)); assert!(encoded.0.starts_with(&selector));
type T = (alloy_primitives::Address,); type T = (alloy_primitives::Address,);
-1
View File
@@ -3,6 +3,5 @@
pub mod case; pub mod case;
pub mod corpus; pub mod corpus;
pub mod input; pub mod input;
pub mod macros;
pub mod metadata; pub mod metadata;
pub mod mode; pub mod mode;
-106
View File
@@ -1,106 +0,0 @@
/// Defines wrappers around types.
///
/// For example, the macro invocation seen below:
///
/// ```rust,ignore
/// define_wrapper_type!(CaseId => usize);
/// ```
///
/// Would define a wrapper type that looks like the following:
///
/// ```rust,ignore
/// pub struct CaseId(usize);
/// ```
///
/// And would also implement a number of methods on this type making it easier
/// to use.
///
/// These wrapper types become very useful as they make the code a lot easier
/// to read.
///
/// Take the following as an example:
///
/// ```rust,ignore
/// struct State {
/// contracts: HashMap<usize, HashMap<String, Vec<u8>>>
/// }
/// ```
///
/// In the above code it's hard to understand what the various types refer to or
/// what to expect them to contain.
///
/// With these wrapper types we're able to create code that's self-documenting
/// in that the types tell us what the code is referring to. The above code is
/// transformed into
///
/// ```rust,ignore
/// struct State {
/// contracts: HashMap<CaseId, HashMap<ContractName, ContractByteCode>>
/// }
/// ```
#[macro_export]
macro_rules! define_wrapper_type {
(
$(#[$meta: meta])*
$ident: ident($ty: ty) $(;)?
) => {
$(#[$meta])*
pub struct $ident($ty);
impl $ident {
pub fn new(value: $ty) -> Self {
Self(value)
}
pub fn new_from<T: Into<$ty>>(value: T) -> Self {
Self(value.into())
}
pub fn into_inner(self) -> $ty {
self.0
}
pub fn as_inner(&self) -> &$ty {
&self.0
}
}
impl AsRef<$ty> for $ident {
fn as_ref(&self) -> &$ty {
&self.0
}
}
impl AsMut<$ty> for $ident {
fn as_mut(&mut self) -> &mut $ty {
&mut self.0
}
}
impl std::ops::Deref for $ident {
type Target = $ty;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for $ident {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<$ty> for $ident {
fn from(value: $ty) -> Self {
Self(value)
}
}
impl From<$ident> for $ty {
fn from(value: $ident) -> Self {
value.0
}
}
};
}
+23 -174
View File
@@ -1,17 +1,14 @@
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fmt::Display,
fs::{File, read_to_string}, fs::{File, read_to_string},
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
str::FromStr,
}; };
use serde::{Deserialize, Serialize}; use serde::Deserialize;
use crate::{ use crate::{
case::Case, case::Case,
define_wrapper_type,
mode::{Mode, SolcMode}, mode::{Mode, SolcMode},
}; };
@@ -45,8 +42,7 @@ impl Deref for MetadataFile {
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)] #[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
pub struct Metadata { pub struct Metadata {
pub cases: Vec<Case>, pub cases: Vec<Case>,
pub contracts: Option<BTreeMap<ContractInstance, ContractPathAndIdentifier>>, pub contracts: Option<BTreeMap<String, String>>,
// TODO: Convert into wrapper types for clarity.
pub libraries: Option<BTreeMap<String, BTreeMap<String, String>>>, pub libraries: Option<BTreeMap<String, BTreeMap<String, String>>>,
pub ignore: Option<bool>, pub ignore: Option<bool>,
pub modes: Option<Vec<Mode>>, pub modes: Option<Vec<Mode>>,
@@ -81,35 +77,28 @@ impl Metadata {
.to_path_buf()) .to_path_buf())
} }
/// Returns the contract sources with canonicalized paths for the files /// Extract the contract sources.
pub fn contract_sources( ///
&self, /// Returns a mapping of contract IDs to their source path and contract name.
) -> anyhow::Result<BTreeMap<ContractInstance, ContractPathAndIdentifier>> { pub fn contract_sources(&self) -> anyhow::Result<BTreeMap<String, (PathBuf, String)>> {
let directory = self.directory()?; let directory = self.directory()?;
let mut sources = BTreeMap::new(); let mut sources = BTreeMap::new();
let Some(contracts) = &self.contracts else { let Some(contracts) = &self.contracts else {
return Ok(sources); return Ok(sources);
}; };
for ( for (id, contract) in contracts {
alias, // TODO: broken if a colon is in the dir name..
ContractPathAndIdentifier { let mut parts = contract.split(':');
contract_source_path, let (Some(file_name), Some(contract_name)) = (parts.next(), parts.next()) else {
contract_ident, anyhow::bail!("metadata contains invalid contract: {contract}");
}, };
) in contracts let file = directory.to_path_buf().join(file_name);
{ if !file.is_file() {
let alias = alias.clone(); anyhow::bail!("contract {id} is not a file: {}", file.display());
let absolute_path = directory.join(contract_source_path).canonicalize()?; }
let contract_ident = contract_ident.clone();
sources.insert( sources.insert(id.clone(), (file, contract_name.to_string()));
alias,
ContractPathAndIdentifier {
contract_source_path: absolute_path,
contract_ident,
},
);
} }
Ok(sources) Ok(sources)
@@ -189,16 +178,12 @@ impl Metadata {
match serde_json::from_str::<Self>(&spec) { match serde_json::from_str::<Self>(&spec) {
Ok(mut metadata) => { Ok(mut metadata) => {
metadata.file_path = Some(path.to_path_buf()); metadata.file_path = Some(path.to_path_buf());
metadata.contracts = Some( let name = path
[( .file_name()
ContractInstance::new_from("test"), .expect("this should be the path to a Solidity file")
ContractPathAndIdentifier { .to_str()
contract_source_path: path.to_path_buf(), .expect("the file name should be valid UTF-8k");
contract_ident: ContractIdent::new_from("Test"), metadata.contracts = Some([(String::from("Test"), format!("{name}:Test"))].into());
},
)]
.into(),
);
Some(metadata) Some(metadata)
} }
Err(error) => { Err(error) => {
@@ -211,139 +196,3 @@ impl Metadata {
} }
} }
} }
define_wrapper_type!(
/// Represents a contract instance found a metadata file.
///
/// Typically, this is used as the key to the "contracts" field of metadata files.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
ContractInstance(String);
);
define_wrapper_type!(
/// Represents a contract identifier found a metadata file.
///
/// A contract identifier is the name of the contract in the source code.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
ContractIdent(String);
);
/// Represents an identifier used for contracts.
///
/// The type supports serialization from and into the following string format:
///
/// ```text
/// ${path}:${contract_ident}
/// ```
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct ContractPathAndIdentifier {
/// The path of the contract source code relative to the directory containing the metadata file.
pub contract_source_path: PathBuf,
/// The identifier of the contract.
pub contract_ident: ContractIdent,
}
impl Display for ContractPathAndIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{}",
self.contract_source_path.display(),
self.contract_ident.as_ref()
)
}
}
impl FromStr for ContractPathAndIdentifier {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut splitted_string = s.split(":").peekable();
let mut path = None::<String>;
let mut identifier = None::<String>;
loop {
let Some(next_item) = splitted_string.next() else {
break;
};
if splitted_string.peek().is_some() {
match path {
Some(ref mut path) => {
path.push(':');
path.push_str(next_item);
}
None => path = Some(next_item.to_owned()),
}
} else {
identifier = Some(next_item.to_owned())
}
}
let Some(path) = path else {
anyhow::bail!("Path is not defined");
};
let Some(identifier) = identifier else {
anyhow::bail!("Contract identifier is not defined")
};
Ok(Self {
contract_source_path: PathBuf::from(path),
contract_ident: ContractIdent::new(identifier),
})
}
}
impl TryFrom<String> for ContractPathAndIdentifier {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_str(&value)
}
}
impl From<ContractPathAndIdentifier> for String {
fn from(value: ContractPathAndIdentifier) -> Self {
value.to_string()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn contract_identifier_respects_roundtrip_property() {
// Arrange
let string = "ERC20/ERC20.sol:ERC20";
// Act
let identifier = ContractPathAndIdentifier::from_str(string);
// Assert
let identifier = identifier.expect("Failed to parse");
assert_eq!(
identifier.contract_source_path.display().to_string(),
"ERC20/ERC20.sol"
);
assert_eq!(identifier.contract_ident, "ERC20".to_owned().into());
// Act
let reserialized = identifier.to_string();
// Assert
assert_eq!(string, reserialized);
}
#[test]
fn complex_metadata_file_can_be_deserialized() {
// Arrange
const JSON: &str = include_str!("../../../assets/test_metadata.json");
// Act
let metadata = serde_json::from_str::<Metadata>(JSON);
// Assert
metadata.expect("Failed to deserialize metadata");
}
}
+6 -7
View File
@@ -2,7 +2,7 @@
use alloy::eips::BlockNumberOrTag; use alloy::eips::BlockNumberOrTag;
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256}; use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256};
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace}; use alloy::rpc::types::trace::geth::{DiffMode, GethTrace};
use alloy::rpc::types::{TransactionReceipt, TransactionRequest}; use alloy::rpc::types::{TransactionReceipt, TransactionRequest};
use anyhow::Result; use anyhow::Result;
@@ -15,14 +15,13 @@ pub trait EthereumNode {
fn execute_transaction(&self, transaction: TransactionRequest) -> Result<TransactionReceipt>; fn execute_transaction(&self, transaction: TransactionRequest) -> Result<TransactionReceipt>;
/// Trace the transaction in the [TransactionReceipt] and return a [GethTrace]. /// Trace the transaction in the [TransactionReceipt] and return a [GethTrace].
fn trace_transaction( fn trace_transaction(&self, transaction: TransactionReceipt) -> Result<GethTrace>;
&self,
receipt: &TransactionReceipt,
trace_options: GethDebugTracingOptions,
) -> Result<GethTrace>;
/// Returns the state diff of the transaction hash in the [TransactionReceipt]. /// Returns the state diff of the transaction hash in the [TransactionReceipt].
fn state_diff(&self, receipt: &TransactionReceipt) -> Result<DiffMode>; fn state_diff(&self, transaction: TransactionReceipt) -> Result<DiffMode>;
/// Returns the next available nonce for the given [Address].
fn fetch_add_nonce(&self, address: Address) -> Result<u64>;
/// Returns the ID of the chain that the node is on. /// Returns the ID of the chain that the node is on.
fn chain_id(&self) -> Result<ChainId>; fn chain_id(&self) -> Result<ChainId>;
-108
View File
@@ -1,108 +0,0 @@
use ::core::pin::Pin;
use alloy::{
consensus::SignableTransaction,
network::{Network, TransactionBuilder, TxSigner},
primitives::Address,
providers::{
Provider, SendableTx,
fillers::{GasFiller, TxFiller},
},
signers::{Signature, local::PrivateKeySigner},
transports::TransportResult,
};
#[derive(Clone, Debug)]
pub struct FallbackGasFiller {
inner: GasFiller,
default_gas_limit: u64,
default_max_fee_per_gas: u128,
default_priority_fee: u128,
}
impl FallbackGasFiller {
pub fn new(
default_gas_limit: u64,
default_max_fee_per_gas: u128,
default_priority_fee: u128,
) -> Self {
Self {
inner: GasFiller,
default_gas_limit,
default_max_fee_per_gas,
default_priority_fee,
}
}
}
impl<N> TxFiller<N> for FallbackGasFiller
where
N: Network,
{
type Fillable = Option<<GasFiller as TxFiller<N>>::Fillable>;
fn status(
&self,
tx: &<N as Network>::TransactionRequest,
) -> alloy::providers::fillers::FillerControlFlow {
<GasFiller as TxFiller<N>>::status(&self.inner, tx)
}
fn fill_sync(&self, _: &mut alloy::providers::SendableTx<N>) {}
async fn prepare<P: Provider<N>>(
&self,
provider: &P,
tx: &<N as Network>::TransactionRequest,
) -> TransportResult<Self::Fillable> {
// Try to fetch GasFillers “fillable” (gas_price, base_fee, estimate_gas, …)
// If it errors (i.e. tx would revert under eth_estimateGas), swallow it.
match self.inner.prepare(provider, tx).await {
Ok(fill) => Ok(Some(fill)),
Err(_) => Ok(None),
}
}
async fn fill(
&self,
fillable: Self::Fillable,
mut tx: alloy::providers::SendableTx<N>,
) -> TransportResult<SendableTx<N>> {
if let Some(fill) = fillable {
// our inner GasFiller succeeded — use it
self.inner.fill(fill, tx).await
} else {
if let Some(builder) = tx.as_mut_builder() {
builder.set_gas_limit(self.default_gas_limit);
builder.set_max_fee_per_gas(self.default_max_fee_per_gas);
builder.set_max_priority_fee_per_gas(self.default_priority_fee);
}
Ok(tx)
}
}
}
/// This is a signer that is able to sign transactions for a specific address with another private
/// key.
pub struct AddressSigner {
pub private_key: PrivateKeySigner,
pub address: Address,
}
impl TxSigner<Signature> for AddressSigner {
fn address(&self) -> Address {
self.address
}
fn sign_transaction<'a, 'b, 'c>(
&'a self,
tx: &'b mut dyn SignableTransaction<Signature>,
) -> Pin<Box<dyn Future<Output = Result<Signature, alloy::signers::Error>> + Send + 'c>>
where
'a: 'c,
'b: 'c,
Self: 'c,
{
<PrivateKeySigner as TxSigner<Signature>>::sign_transaction(&self.private_key, tx)
}
}
+51 -64
View File
@@ -1,38 +1,37 @@
//! The go-ethereum node implementation. //! The go-ethereum node implementation.
use std::{ use std::{
collections::HashMap,
fs::{File, OpenOptions, create_dir_all, remove_dir_all}, fs::{File, OpenOptions, create_dir_all, remove_dir_all},
io::{BufRead, BufReader, Read, Write}, io::{BufRead, BufReader, Read, Write},
path::PathBuf, path::PathBuf,
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::atomic::{AtomicU32, Ordering}, sync::{
Mutex,
atomic::{AtomicU32, Ordering},
},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use alloy::{ use alloy::{
eips::BlockNumberOrTag, eips::BlockNumberOrTag,
genesis::Genesis,
network::{Ethereum, EthereumWallet}, network::{Ethereum, EthereumWallet},
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, U256}, primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, U256},
providers::{ providers::{
Provider, ProviderBuilder, Provider, ProviderBuilder,
ext::DebugApi, ext::DebugApi,
fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller}, fillers::{FillProvider, TxFiller},
}, },
rpc::types::{ rpc::types::{
TransactionReceipt, TransactionRequest, TransactionReceipt, TransactionRequest,
trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame}, trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame},
}, },
signers::local::PrivateKeySigner,
}; };
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode}; use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use tracing::Level; use tracing::Level;
use crate::{ use crate::Node;
Node,
common::{AddressSigner, FallbackGasFiller},
};
static NODE_COUNT: AtomicU32 = AtomicU32::new(0); static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
@@ -55,10 +54,7 @@ pub struct Instance {
network_id: u64, network_id: u64,
start_timeout: u64, start_timeout: u64,
wallet: EthereumWallet, wallet: EthereumWallet,
private_key: PrivateKeySigner, nonces: Mutex<HashMap<Address, u64>>,
nonce_manager: CachedNonceManager,
additional_callers: Vec<Address>,
/// This vector stores [`File`] objects that we use for logging which we want to flush when the /// This vector stores [`File`] objects that we use for logging which we want to flush when the
/// node object is dropped. We do not store them in a structured fashion at the moment (in /// node object is dropped. We do not store them in a structured fashion at the moment (in
/// separate fields) as the logic that we need to apply to them is all the same regardless of /// separate fields) as the logic that we need to apply to them is all the same regardless of
@@ -86,24 +82,8 @@ impl Instance {
create_dir_all(&self.base_directory)?; create_dir_all(&self.base_directory)?;
create_dir_all(&self.logs_directory)?; create_dir_all(&self.logs_directory)?;
// Modifying the genesis file so that we get our private key to control the other accounts.
let mut genesis = serde_json::from_str::<Genesis>(&genesis)?;
for additional_caller in self.additional_callers.iter() {
let account = genesis.alloc.entry(*additional_caller).or_default();
account.private_key = Some(self.private_key.to_bytes());
*account = account
.clone()
.with_balance("1000000000000000000".parse().expect("Can't fail"));
}
let genesis_path = self.base_directory.join(Self::GENESIS_JSON_FILE); let genesis_path = self.base_directory.join(Self::GENESIS_JSON_FILE);
serde_json::to_writer(File::create(&genesis_path)?, &genesis)?; File::create(&genesis_path)?.write_all(genesis.as_bytes())?;
for additional_caller in self.additional_callers.iter() {
self.wallet.register_signer(AddressSigner {
private_key: self.private_key.clone(),
address: *additional_caller,
});
}
let mut child = Command::new(&self.geth) let mut child = Command::new(&self.geth)
.arg("init") .arg("init")
@@ -226,19 +206,8 @@ impl Instance {
> + 'static { > + 'static {
let connection_string = self.connection_string(); let connection_string = self.connection_string();
let wallet = self.wallet.clone(); let wallet = self.wallet.clone();
// Note: We would like all providers to make use of the same nonce manager so that we have
// monotonically increasing nonces that are cached. The cached nonce manager uses Arc's in
// its implementation and therefore it means that when we clone it then it still references
// the same state.
let nonce_manager = self.nonce_manager.clone();
Box::pin(async move { Box::pin(async move {
ProviderBuilder::new() ProviderBuilder::new()
.disable_recommended_fillers()
.filler(FallbackGasFiller::new(500_000_000, 500_000_000, 1))
.filler(ChainIdFiller::default())
.filler(NonceFiller::new(nonce_manager))
.wallet(wallet) .wallet(wallet)
.connect(&connection_string) .connect(&connection_string)
.await .await
@@ -255,7 +224,7 @@ impl EthereumNode for Instance {
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> { ) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
let provider = self.provider(); let provider = self.provider();
BlockingExecutor::execute(async move { BlockingExecutor::execute(async move {
let outer_span = tracing::debug_span!("Submitting transaction", ?transaction); let outer_span = tracing::debug_span!("Submitting transaction", ?transaction,);
let _outer_guard = outer_span.enter(); let _outer_guard = outer_span.enter();
let provider = provider.await?; let provider = provider.await?;
@@ -336,28 +305,30 @@ impl EthereumNode for Instance {
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn trace_transaction( fn trace_transaction(
&self, &self,
transaction: &TransactionReceipt, transaction: TransactionReceipt,
trace_options: GethDebugTracingOptions,
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> { ) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
let tx_hash = transaction.transaction_hash;
let provider = self.provider();
BlockingExecutor::execute(async move {
Ok(provider
.await?
.debug_trace_transaction(tx_hash, trace_options)
.await?)
})?
}
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn state_diff(&self, transaction: &TransactionReceipt) -> anyhow::Result<DiffMode> {
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
diff_mode: Some(true), diff_mode: Some(true),
disable_code: None, disable_code: None,
disable_storage: None, disable_storage: None,
}); });
let provider = self.provider();
BlockingExecutor::execute(async move {
Ok(provider
.await?
.debug_trace_transaction(transaction.transaction_hash, trace_options)
.await?)
})?
}
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn state_diff(
&self,
transaction: alloy::rpc::types::TransactionReceipt,
) -> anyhow::Result<DiffMode> {
match self match self
.trace_transaction(transaction, trace_options)? .trace_transaction(transaction)?
.try_into_pre_state_frame()? .try_into_pre_state_frame()?
{ {
PreStateFrame::Diff(diff) => Ok(diff), PreStateFrame::Diff(diff) => Ok(diff),
@@ -365,6 +336,24 @@ impl EthereumNode for Instance {
} }
} }
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn fetch_add_nonce(&self, address: Address) -> anyhow::Result<u64> {
let provider = self.provider();
let onchain_nonce = BlockingExecutor::execute::<anyhow::Result<_>>(async move {
provider
.await?
.get_transaction_count(address)
.await
.map_err(Into::into)
})??;
let mut nonces = self.nonces.lock().unwrap();
let current = nonces.entry(address).or_insert(onchain_nonce);
let value = *current;
*current += 1;
Ok(value)
}
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> { fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> {
let provider = self.provider(); let provider = self.provider();
@@ -448,7 +437,7 @@ impl EthereumNode for Instance {
} }
impl Node for Instance { impl Node for Instance {
fn new(config: &Arguments, additional_callers: &[Address]) -> Self { fn new(config: &Arguments) -> Self {
let geth_directory = config.directory().join(Self::BASE_DIRECTORY); let geth_directory = config.directory().join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = geth_directory.join(id.to_string()); let base_directory = geth_directory.join(id.to_string());
@@ -464,12 +453,10 @@ impl Node for Instance {
network_id: config.network_id, network_id: config.network_id,
start_timeout: config.geth_start_timeout, start_timeout: config.geth_start_timeout,
wallet: config.wallet(), wallet: config.wallet(),
nonces: Mutex::new(HashMap::new()),
// We know that we only need to be storing 2 files so we can specify that when creating // We know that we only need to be storing 2 files so we can specify that when creating
// the vector. It's the stdout and stderr of the geth node. // the vector. It's the stdout and stderr of the geth node.
logs_file_to_flush: Vec::with_capacity(2), logs_file_to_flush: Vec::with_capacity(2),
nonce_manager: Default::default(),
additional_callers: additional_callers.to_vec(),
private_key: config.signer(),
} }
} }
@@ -546,7 +533,7 @@ mod tests {
fn new_node() -> (Instance, TempDir) { fn new_node() -> (Instance, TempDir) {
let (args, temp_dir) = test_config(); let (args, temp_dir) = test_config();
let mut node = Instance::new(&args, &[]); let mut node = Instance::new(&args);
node.init(GENESIS_JSON.to_owned()) node.init(GENESIS_JSON.to_owned())
.expect("Failed to initialize the node") .expect("Failed to initialize the node")
.spawn_process() .spawn_process()
@@ -556,21 +543,21 @@ mod tests {
#[test] #[test]
fn init_works() { fn init_works() {
Instance::new(&test_config().0, &[]) Instance::new(&test_config().0)
.init(GENESIS_JSON.to_string()) .init(GENESIS_JSON.to_string())
.unwrap(); .unwrap();
} }
#[test] #[test]
fn spawn_works() { fn spawn_works() {
Instance::new(&test_config().0, &[]) Instance::new(&test_config().0)
.spawn(GENESIS_JSON.to_string()) .spawn(GENESIS_JSON.to_string())
.unwrap(); .unwrap();
} }
#[test] #[test]
fn version_works() { fn version_works() {
let version = Instance::new(&test_config().0, &[]).version().unwrap(); let version = Instance::new(&test_config().0).version().unwrap();
assert!( assert!(
version.starts_with("geth version"), version.starts_with("geth version"),
"expected version string, got: '{version}'" "expected version string, got: '{version}'"
+55 -52
View File
@@ -1,9 +1,13 @@
use std::{ use std::{
collections::HashMap,
fs::{File, OpenOptions, create_dir_all, remove_dir_all}, fs::{File, OpenOptions, create_dir_all, remove_dir_all},
io::{BufRead, Write}, io::{BufRead, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
sync::atomic::{AtomicU32, Ordering}, sync::{
Mutex,
atomic::{AtomicU32, Ordering},
},
time::Duration, time::Duration,
}; };
@@ -19,7 +23,7 @@ use alloy::{
providers::{ providers::{
Provider, ProviderBuilder, Provider, ProviderBuilder,
ext::DebugApi, ext::DebugApi,
fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller}, fillers::{FillProvider, TxFiller},
}, },
rpc::types::{ rpc::types::{
TransactionReceipt, TransactionReceipt,
@@ -36,7 +40,7 @@ use tracing::Level;
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
use revive_dt_node_interaction::{BlockingExecutor, EthereumNode}; use revive_dt_node_interaction::{BlockingExecutor, EthereumNode};
use crate::{Node, common::FallbackGasFiller}; use crate::Node;
static NODE_COUNT: AtomicU32 = AtomicU32::new(0); static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
@@ -51,7 +55,7 @@ pub struct KitchensinkNode {
logs_directory: PathBuf, logs_directory: PathBuf,
process_substrate: Option<Child>, process_substrate: Option<Child>,
process_proxy: Option<Child>, process_proxy: Option<Child>,
nonce_manager: CachedNonceManager, nonces: Mutex<HashMap<Address, u64>>,
/// This vector stores [`File`] objects that we use for logging which we want to flush when the /// This vector stores [`File`] objects that we use for logging which we want to flush when the
/// node object is dropped. We do not store them in a structured fashion at the moment (in /// node object is dropped. We do not store them in a structured fashion at the moment (in
/// separate fields) as the logic that we need to apply to them is all the same regardless of /// separate fields) as the logic that we need to apply to them is all the same regardless of
@@ -346,24 +350,9 @@ impl KitchensinkNode {
> + 'static { > + 'static {
let connection_string = self.connection_string(); let connection_string = self.connection_string();
let wallet = self.wallet.clone(); let wallet = self.wallet.clone();
// Note: We would like all providers to make use of the same nonce manager so that we have
// monotonically increasing nonces that are cached. The cached nonce manager uses Arc's in
// its implementation and therefore it means that when we clone it then it still references
// the same state.
let nonce_manager = self.nonce_manager.clone();
Box::pin(async move { Box::pin(async move {
ProviderBuilder::new() ProviderBuilder::new()
.disable_recommended_fillers()
.network::<KitchenSinkNetwork>() .network::<KitchenSinkNetwork>()
.filler(FallbackGasFiller::new(
30_000_000,
200_000_000_000,
3_000_000_000,
))
.filler(ChainIdFiller::default())
.filler(NonceFiller::new(nonce_manager))
.wallet(wallet) .wallet(wallet)
.connect(&connection_string) .connect(&connection_string)
.await .await
@@ -395,28 +384,27 @@ impl EthereumNode for KitchensinkNode {
#[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))] #[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))]
fn trace_transaction( fn trace_transaction(
&self, &self,
transaction: &TransactionReceipt, transaction: TransactionReceipt,
trace_options: GethDebugTracingOptions,
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> { ) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
let tx_hash = transaction.transaction_hash;
let provider = self.provider();
BlockingExecutor::execute(async move {
Ok(provider
.await?
.debug_trace_transaction(tx_hash, trace_options)
.await?)
})?
}
#[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))]
fn state_diff(&self, transaction: &TransactionReceipt) -> anyhow::Result<DiffMode> {
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig { let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
diff_mode: Some(true), diff_mode: Some(true),
disable_code: None, disable_code: None,
disable_storage: None, disable_storage: None,
}); });
let provider = self.provider();
BlockingExecutor::execute(async move {
Ok(provider
.await?
.debug_trace_transaction(transaction.transaction_hash, trace_options)
.await?)
})?
}
#[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))]
fn state_diff(&self, transaction: TransactionReceipt) -> anyhow::Result<DiffMode> {
match self match self
.trace_transaction(transaction, trace_options)? .trace_transaction(transaction)?
.try_into_pre_state_frame()? .try_into_pre_state_frame()?
{ {
PreStateFrame::Diff(diff) => Ok(diff), PreStateFrame::Diff(diff) => Ok(diff),
@@ -424,6 +412,24 @@ impl EthereumNode for KitchensinkNode {
} }
} }
#[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))]
fn fetch_add_nonce(&self, address: Address) -> anyhow::Result<u64> {
let provider = self.provider();
let onchain_nonce = BlockingExecutor::execute::<anyhow::Result<_>>(async move {
provider
.await?
.get_transaction_count(address)
.await
.map_err(Into::into)
})??;
let mut nonces = self.nonces.lock().unwrap();
let current = nonces.entry(address).or_insert(onchain_nonce);
let value = *current;
*current += 1;
Ok(value)
}
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))] #[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> { fn chain_id(&self) -> anyhow::Result<alloy::primitives::ChainId> {
let provider = self.provider(); let provider = self.provider();
@@ -507,7 +513,7 @@ impl EthereumNode for KitchensinkNode {
} }
impl Node for KitchensinkNode { impl Node for KitchensinkNode {
fn new(config: &Arguments, _additional_callers: &[Address]) -> Self { fn new(config: &Arguments) -> Self {
let kitchensink_directory = config.directory().join(Self::BASE_DIRECTORY); let kitchensink_directory = config.directory().join(Self::BASE_DIRECTORY);
let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst); let id = NODE_COUNT.fetch_add(1, Ordering::SeqCst);
let base_directory = kitchensink_directory.join(id.to_string()); let base_directory = kitchensink_directory.join(id.to_string());
@@ -523,7 +529,7 @@ impl Node for KitchensinkNode {
logs_directory, logs_directory,
process_substrate: None, process_substrate: None,
process_proxy: None, process_proxy: None,
nonce_manager: Default::default(), nonces: Mutex::new(HashMap::new()),
// We know that we only need to be storing 4 files so we can specify that when creating // We know that we only need to be storing 4 files so we can specify that when creating
// the vector. It's the stdout and stderr of the substrate-node and the eth-rpc. // the vector. It's the stdout and stderr of the substrate-node and the eth-rpc.
logs_file_to_flush: Vec::with_capacity(4), logs_file_to_flush: Vec::with_capacity(4),
@@ -814,12 +820,6 @@ impl TransactionBuilder<KitchenSinkNetwork> for <Ethereum as Network>::Transacti
> { > {
Ok(wallet.sign_request(self).await?) Ok(wallet.sign_request(self).await?)
} }
fn take_nonce(&mut self) -> Option<u64> {
<<Ethereum as Network>::TransactionRequest as TransactionBuilder<Ethereum>>::take_nonce(
self,
)
}
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -1020,7 +1020,7 @@ mod tests {
use alloy::rpc::types::TransactionRequest; use alloy::rpc::types::TransactionRequest;
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{LazyLock, Mutex}; use std::sync::LazyLock;
use temp_dir::TempDir; use temp_dir::TempDir;
use std::fs; use std::fs;
@@ -1060,7 +1060,7 @@ mod tests {
let _guard = NODE_START_MUTEX.lock().unwrap(); let _guard = NODE_START_MUTEX.lock().unwrap();
let (args, temp_dir) = test_config(); let (args, temp_dir) = test_config();
let mut node = KitchensinkNode::new(&args, &[]); let mut node = KitchensinkNode::new(&args);
node.init(GENESIS_JSON) node.init(GENESIS_JSON)
.expect("Failed to initialize the node") .expect("Failed to initialize the node")
.spawn_process() .spawn_process()
@@ -1115,7 +1115,7 @@ mod tests {
} }
"#; "#;
let mut dummy_node = KitchensinkNode::new(&test_config().0, &[]); let mut dummy_node = KitchensinkNode::new(&test_config().0);
// Call `init()` // Call `init()`
dummy_node.init(genesis_content).expect("init failed"); dummy_node.init(genesis_content).expect("init failed");
@@ -1159,7 +1159,7 @@ mod tests {
} }
"#; "#;
let node = KitchensinkNode::new(&test_config().0, &[]); let node = KitchensinkNode::new(&test_config().0);
let result = node let result = node
.extract_balance_from_genesis_file(genesis_json) .extract_balance_from_genesis_file(genesis_json)
@@ -1232,7 +1232,7 @@ mod tests {
fn spawn_works() { fn spawn_works() {
let (config, _temp_dir) = test_config(); let (config, _temp_dir) = test_config();
let mut node = KitchensinkNode::new(&config, &[]); let mut node = KitchensinkNode::new(&config);
node.spawn(GENESIS_JSON.to_string()).unwrap(); node.spawn(GENESIS_JSON.to_string()).unwrap();
} }
@@ -1240,7 +1240,7 @@ mod tests {
fn version_works() { fn version_works() {
let (config, _temp_dir) = test_config(); let (config, _temp_dir) = test_config();
let node = KitchensinkNode::new(&config, &[]); let node = KitchensinkNode::new(&config);
let version = node.version().unwrap(); let version = node.version().unwrap();
assert!( assert!(
@@ -1253,7 +1253,7 @@ mod tests {
fn eth_rpc_version_works() { fn eth_rpc_version_works() {
let (config, _temp_dir) = test_config(); let (config, _temp_dir) = test_config();
let node = KitchensinkNode::new(&config, &[]); let node = KitchensinkNode::new(&config);
let version = node.eth_rpc_version().unwrap(); let version = node.eth_rpc_version().unwrap();
assert!( assert!(
@@ -1296,7 +1296,8 @@ mod tests {
let coinbase = node.block_coinbase(BlockNumberOrTag::Latest); let coinbase = node.block_coinbase(BlockNumberOrTag::Latest);
// Assert // Assert
let _ = coinbase.expect("Failed to get the coinbase"); let coinbase = coinbase.expect("Failed to get the coinbase");
assert_eq!(coinbase, Address::ZERO)
} }
#[test] #[test]
@@ -1308,7 +1309,8 @@ mod tests {
let block_difficulty = node.block_difficulty(BlockNumberOrTag::Latest); let block_difficulty = node.block_difficulty(BlockNumberOrTag::Latest);
// Assert // Assert
let _ = block_difficulty.expect("Failed to get the block difficulty"); let block_difficulty = block_difficulty.expect("Failed to get the block difficulty");
assert_eq!(block_difficulty, U256::ZERO)
} }
#[test] #[test]
@@ -1344,6 +1346,7 @@ mod tests {
let block_number = node.last_block_number(); let block_number = node.last_block_number();
// Assert // Assert
let _ = block_number.expect("Failed to get the block number"); let block_number = block_number.expect("Failed to get the block number");
assert_eq!(block_number, 0)
} }
} }
+1 -3
View File
@@ -1,10 +1,8 @@
//! This crate implements the testing nodes. //! This crate implements the testing nodes.
use alloy::primitives::Address;
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
use revive_dt_node_interaction::EthereumNode; use revive_dt_node_interaction::EthereumNode;
pub mod common;
pub mod geth; pub mod geth;
pub mod kitchensink; pub mod kitchensink;
pub mod pool; pub mod pool;
@@ -15,7 +13,7 @@ pub const GENESIS_JSON: &str = include_str!("../../../genesis.json");
/// An abstract interface for testing nodes. /// An abstract interface for testing nodes.
pub trait Node: EthereumNode { pub trait Node: EthereumNode {
/// Create a new uninitialized instance. /// Create a new uninitialized instance.
fn new(config: &Arguments, additional_callers: &[Address]) -> Self; fn new(config: &Arguments) -> Self;
/// Spawns a node configured according to the genesis json. /// Spawns a node configured according to the genesis json.
/// ///
+4 -12
View File
@@ -6,7 +6,6 @@ use std::{
thread, thread,
}; };
use alloy::primitives::Address;
use anyhow::Context; use anyhow::Context;
use revive_dt_config::Arguments; use revive_dt_config::Arguments;
@@ -24,7 +23,7 @@ where
T: Node + Send + 'static, T: Node + Send + 'static,
{ {
/// Create a new Pool. This will start as many nodes as there are workers in `config`. /// Create a new Pool. This will start as many nodes as there are workers in `config`.
pub fn new(config: &Arguments, additional_callers: &[Address]) -> anyhow::Result<Self> { pub fn new(config: &Arguments) -> anyhow::Result<Self> {
let nodes = config.workers; let nodes = config.workers;
let genesis = read_to_string(&config.genesis_file).context(format!( let genesis = read_to_string(&config.genesis_file).context(format!(
"can not read genesis file: {}", "can not read genesis file: {}",
@@ -35,10 +34,7 @@ where
for _ in 0..nodes { for _ in 0..nodes {
let config = config.clone(); let config = config.clone();
let genesis = genesis.clone(); let genesis = genesis.clone();
let additional_callers = additional_callers.to_vec(); handles.push(thread::spawn(move || spawn_node::<T>(&config, genesis)));
handles.push(thread::spawn(move || {
spawn_node::<T>(&config, genesis, &additional_callers)
}));
} }
let mut nodes = Vec::with_capacity(nodes); let mut nodes = Vec::with_capacity(nodes);
@@ -64,12 +60,8 @@ where
} }
} }
fn spawn_node<T: Node + Send>( fn spawn_node<T: Node + Send>(args: &Arguments, genesis: String) -> anyhow::Result<T> {
args: &Arguments, let mut node = T::new(args);
genesis: String,
additional_callers: &[Address],
) -> anyhow::Result<T> {
let mut node = T::new(args, additional_callers);
tracing::info!("starting node: {}", node.connection_string()); tracing::info!("starting node: {}", node.connection_string());
node.spawn(genesis)?; node.spawn(genesis)?;
Ok(node) Ok(node)
+16 -4
View File
@@ -110,25 +110,37 @@ mod tests {
#[test] #[test]
fn try_get_windows() { fn try_get_windows() {
let version = List::download(List::WINDOWS_URL).unwrap().latest_release; let version = List::download(List::WINDOWS_URL)
.unwrap()
.latest_release
.into();
GHDownloader::windows(version).download().unwrap(); GHDownloader::windows(version).download().unwrap();
} }
#[test] #[test]
fn try_get_macosx() { fn try_get_macosx() {
let version = List::download(List::MACOSX_URL).unwrap().latest_release; let version = List::download(List::MACOSX_URL)
.unwrap()
.latest_release
.into();
GHDownloader::macosx(version).download().unwrap(); GHDownloader::macosx(version).download().unwrap();
} }
#[test] #[test]
fn try_get_linux() { fn try_get_linux() {
let version = List::download(List::LINUX_URL).unwrap().latest_release; let version = List::download(List::LINUX_URL)
.unwrap()
.latest_release
.into();
GHDownloader::linux(version).download().unwrap(); GHDownloader::linux(version).download().unwrap();
} }
#[test] #[test]
fn try_get_wasm() { fn try_get_wasm() {
let version = List::download(List::WASM_URL).unwrap().latest_release; let version = List::download(List::WASM_URL)
.unwrap()
.latest_release
.into();
GHDownloader::wasm(version).download().unwrap(); GHDownloader::wasm(version).download().unwrap();
} }
} }