mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-12 13:31:06 +00:00
Implement the core benchmarking components
This commit is contained in:
Generated
+117
-66
@@ -67,9 +67,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy"
|
name = "alloy"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ad4eb51e7845257b70c51b38ef8d842d5e5e93196701fcbd757577971a043c6"
|
checksum = "7f6cfe35f100bc496007c9a00f90b88bdf565f1421d4c707c9f07e0717e2aaad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-contract",
|
"alloy-contract",
|
||||||
@@ -88,6 +88,7 @@ dependencies = [
|
|||||||
"alloy-transport-http",
|
"alloy-transport-http",
|
||||||
"alloy-transport-ipc",
|
"alloy-transport-ipc",
|
||||||
"alloy-transport-ws",
|
"alloy-transport-ws",
|
||||||
|
"alloy-trie",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -103,9 +104,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-consensus"
|
name = "alloy-consensus"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca3b746060277f3d7f9c36903bb39b593a741cb7afcb0044164c28f0e9b673f0"
|
checksum = "59094911f05dbff1cf5b29046a00ef26452eccc8d47136d50a47c0cf22f00c85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
@@ -122,15 +123,16 @@ dependencies = [
|
|||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"secp256k1 0.30.0",
|
"secp256k1 0.30.0",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-consensus-any"
|
name = "alloy-consensus-any"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf98679329fa708fa809ea596db6d974da892b068ad45e48ac1956f582edf946"
|
checksum = "903cb8f728107ca27c816546f15be38c688df3c381d7bd1a4a9f215effc1ddb4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
@@ -142,9 +144,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-contract"
|
name = "alloy-contract"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a10e47f5305ea08c37b1772086c1573e9a0a257227143996841172d37d3831bb"
|
checksum = "03df5cb3b428ac96b386ad64c11d5c6e87a5505682cf1fbd6f8f773e9eda04f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-dyn-abi",
|
"alloy-dyn-abi",
|
||||||
@@ -230,9 +232,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-eips"
|
name = "alloy-eips"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f562a81278a3ed83290e68361f2d1c75d018ae3b8589a314faf9303883e18ec9"
|
checksum = "ac7f1c9a1ccc7f3e03c36976455751a6166a4f0d2d2c530c3f87dfe7d0cdc836"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-eip2124",
|
"alloy-eip2124",
|
||||||
"alloy-eip2930",
|
"alloy-eip2930",
|
||||||
@@ -245,14 +247,16 @@ dependencies = [
|
|||||||
"derive_more 2.0.1",
|
"derive_more 2.0.1",
|
||||||
"either",
|
"either",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_with",
|
||||||
"sha2 0.10.9",
|
"sha2 0.10.9",
|
||||||
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-genesis"
|
name = "alloy-genesis"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc41384e9ab8c9b2fb387c52774d9d432656a28edcda1c2d4083e96051524518"
|
checksum = "1421f6c9d15e5b86afbfe5865ca84dea3b9f77173a0963c1a2ee4e626320ada9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
@@ -276,9 +280,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-json-rpc"
|
name = "alloy-json-rpc"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12c454fcfcd5d26ed3b8cae5933cbee9da5f0b05df19b46d4bd4446d1f082565"
|
checksum = "65f763621707fa09cece30b73ecc607eb43fd7a72451fe3b46f645b905086926"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-sol-types",
|
"alloy-sol-types",
|
||||||
@@ -291,9 +295,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-network"
|
name = "alloy-network"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42d6d39eabe5c7b3d8f23ac47b0b683b99faa4359797114636c66e0743103d05"
|
checksum = "2f59a869fa4b4c3a7f08b1c8cb79aec61c29febe6e24a24fe0fcfded8a9b5703"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-consensus-any",
|
"alloy-consensus-any",
|
||||||
@@ -317,9 +321,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-network-primitives"
|
name = "alloy-network-primitives"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3704fa8b7ba9ba3f378d99b3d628c8bc8c2fc431b709947930f154e22a8368b6"
|
checksum = "46e9374c667c95c41177602ebe6f6a2edd455193844f011d973d374b65501b38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
@@ -358,9 +362,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-provider"
|
name = "alloy-provider"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08800e8cbe70c19e2eb7cf3d7ff4b28bdd9b3933f8e1c8136c7d910617ba03bf"
|
checksum = "77818b7348bd5486491a5297579dbfe5f706a81f8e1f5976393025f1e22a7c7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-chains",
|
"alloy-chains",
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
@@ -387,7 +391,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-utils-wasm",
|
"futures-utils-wasm",
|
||||||
"http",
|
|
||||||
"lru",
|
"lru",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
@@ -403,13 +406,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-pubsub"
|
name = "alloy-pubsub"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae68457a2c2ead6bd7d7acb5bf5f1623324b1962d4f8e7b0250657a3c3ab0a0b"
|
checksum = "249b45103a66c9ad60ad8176b076106d03a2399a37f0ee7b0e03692e6b354cb9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-rpc",
|
"alloy-json-rpc",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-transport",
|
"alloy-transport",
|
||||||
|
"auto_impl",
|
||||||
"bimap",
|
"bimap",
|
||||||
"futures",
|
"futures",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
@@ -446,9 +450,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-client"
|
name = "alloy-rpc-client"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "162301b5a57d4d8f000bf30f4dcb82f9f468f3e5e846eeb8598dd39e7886932c"
|
checksum = "2430d5623e428dd012c6c2156ae40b7fe638d6fca255e3244e0fba51fa698e93"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-rpc",
|
"alloy-json-rpc",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
@@ -472,11 +476,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-types"
|
name = "alloy-rpc-types"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6cd8ca94ae7e2b32cc3895d9981f3772aab0b4756aa60e9ed0bcfee50f0e1328"
|
checksum = "e9e131624d08a25cfc40557041e7dc42e1182fa1153e7592d120f769a1edce56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
|
"alloy-rpc-types-debug",
|
||||||
"alloy-rpc-types-eth",
|
"alloy-rpc-types-eth",
|
||||||
"alloy-rpc-types-trace",
|
"alloy-rpc-types-trace",
|
||||||
"alloy-serde",
|
"alloy-serde",
|
||||||
@@ -485,9 +490,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-types-any"
|
name = "alloy-rpc-types-any"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "076b47e834b367d8618c52dd0a0d6a711ddf66154636df394805300af4923b8a"
|
checksum = "07429a1099cd17227abcddb91b5e38c960aaeb02a6967467f5bb561fbe716ac6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus-any",
|
"alloy-consensus-any",
|
||||||
"alloy-rpc-types-eth",
|
"alloy-rpc-types-eth",
|
||||||
@@ -496,19 +501,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-types-debug"
|
name = "alloy-rpc-types-debug"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94a2a86ad7b7d718c15e79d0779bd255561b6b22968dc5ed2e7c0fbc43bb55fe"
|
checksum = "aeff305b7d10cc1c888456d023e7bb8a5ea82e9e42b951e37619b88cc1a1486d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
|
"derive_more 2.0.1",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_with",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-types-eth"
|
name = "alloy-rpc-types-eth"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c2f847e635ec0be819d06e2ada4bcc4e4204026a83c4bfd78ae8d550e027ae7"
|
checksum = "db46b0901ee16bbb68d986003c66dcb74a12f9d9b3c44f8e85d51974f2458f0f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-consensus-any",
|
"alloy-consensus-any",
|
||||||
@@ -527,9 +534,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-rpc-types-trace"
|
name = "alloy-rpc-types-trace"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fc58180302a94c934d455eeedb3ecb99cdc93da1dbddcdbbdb79dd6fe618b2a"
|
checksum = "36f10620724bd45f80c79668a8cdbacb6974f860686998abce28f6196ae79444"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-rpc-types-eth",
|
"alloy-rpc-types-eth",
|
||||||
@@ -541,9 +548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-serde"
|
name = "alloy-serde"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae699248d02ade9db493bbdae61822277dc14ae0f82a5a4153203b60e34422a6"
|
checksum = "5413814be7a22fbc81e0f04a2401fcc3eb25e56fd53b04683e8acecc6e1fe01b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -552,9 +559,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-signer"
|
name = "alloy-signer"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cf7d793c813515e2b627b19a15693960b3ed06670f9f66759396d06ebe5747b"
|
checksum = "53410a18a61916e2c073a6519499514e027b01e77eeaf96acd1df7cf96ef6bb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -567,9 +574,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-signer-local"
|
name = "alloy-signer-local"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51a424bc5a11df0d898ce0fd15906b88ebe2a6e4f17a514b51bc93946bb756bd"
|
checksum = "e6006c4cbfa5d08cadec1fcabea6cb56dc585a30a9fce40bcf81e307d6a71c8e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-network",
|
"alloy-network",
|
||||||
@@ -656,12 +663,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-transport"
|
name = "alloy-transport"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f317d20f047b3de4d9728c556e2e9a92c9a507702d2016424cd8be13a74ca5e"
|
checksum = "d94ee404368a3d9910dfe61b203e888c6b0e151a50e147f95da8baff9f9c7763"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-rpc",
|
"alloy-json-rpc",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
|
"auto_impl",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"derive_more 2.0.1",
|
"derive_more 2.0.1",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -679,9 +687,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-transport-http"
|
name = "alloy-transport-http"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff084ac7b1f318c87b579d221f11b748341d68b9ddaa4ffca5e62ed2b8cfefb4"
|
checksum = "a2f8a6338d594f6c6481292215ee8f2fd7b986c80aba23f3f44e761a8658de78"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-rpc",
|
"alloy-json-rpc",
|
||||||
"alloy-transport",
|
"alloy-transport",
|
||||||
@@ -694,9 +702,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-transport-ipc"
|
name = "alloy-transport-ipc"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edb099cdad8ed2e6a80811cdf9bbf715ebf4e34c981b4a6e2d1f9daacbf8b218"
|
checksum = "17a37a8ca18006fa0a58c7489645619ff58cfa073f2b29c4e052c9bd114b123a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-json-rpc",
|
"alloy-json-rpc",
|
||||||
"alloy-pubsub",
|
"alloy-pubsub",
|
||||||
@@ -714,9 +722,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-transport-ws"
|
name = "alloy-transport-ws"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e915e1250dc129ad48d264573ccd08e4716fdda564a772fd217875b8459aff9"
|
checksum = "679b0122b7bca9d4dc5eb2c0549677a3c53153f6e232f23f4b3ba5575f74ebde"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-pubsub",
|
"alloy-pubsub",
|
||||||
"alloy-transport",
|
"alloy-transport",
|
||||||
@@ -748,12 +756,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alloy-tx-macros"
|
name = "alloy-tx-macros"
|
||||||
version = "1.0.22"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1154c8187a5ff985c95a8b2daa2fedcf778b17d7668e5e50e556c4ff9c881154"
|
checksum = "e64c09ec565a90ed8390d82aa08cd3b22e492321b96cb4a3d4f58414683c9e2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"darling",
|
"darling 0.21.3",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
@@ -2010,8 +2018,18 @@ version = "0.20.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
"darling_macro",
|
"darling_macro 0.20.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.21.3",
|
||||||
|
"darling_macro 0.21.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2028,13 +2046,39 @@ dependencies = [
|
|||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.20.11",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.101",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.21.3",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
]
|
]
|
||||||
@@ -4516,7 +4560,6 @@ name = "revive-dt-common"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"alloy-primitives",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"moka",
|
"moka",
|
||||||
@@ -4533,7 +4576,6 @@ name = "revive-dt-compiler"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"alloy-primitives",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"foundry-compilers-artifacts",
|
"foundry-compilers-artifacts",
|
||||||
@@ -4597,8 +4639,6 @@ name = "revive-dt-format"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"alloy-primitives",
|
|
||||||
"alloy-sol-types",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures",
|
"futures",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -4641,6 +4681,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"futures",
|
||||||
"revive-common",
|
"revive-common",
|
||||||
"revive-dt-format",
|
"revive-dt-format",
|
||||||
]
|
]
|
||||||
@@ -4649,7 +4690,7 @@ dependencies = [
|
|||||||
name = "revive-dt-report"
|
name = "revive-dt-report"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy-primitives",
|
"alloy",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
"paste",
|
"paste",
|
||||||
@@ -5115,10 +5156,11 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5132,10 +5174,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_core"
|
||||||
version = "1.0.219"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -5223,7 +5274,7 @@ version = "3.14.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f"
|
checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.20.11",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
|
|||||||
+2
-3
@@ -22,8 +22,6 @@ revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
|
|||||||
revive-dt-report = { version = "0.1.0", path = "crates/report" }
|
revive-dt-report = { version = "0.1.0", path = "crates/report" }
|
||||||
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
|
||||||
|
|
||||||
alloy-primitives = "1.2.1"
|
|
||||||
alloy-sol-types = "1.2.1"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
bson = { version = "2.15.0" }
|
bson = { version = "2.15.0" }
|
||||||
cacache = { version = "13.1.0" }
|
cacache = { version = "13.1.0" }
|
||||||
@@ -75,7 +73,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.37"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"json-abi",
|
"json-abi",
|
||||||
@@ -92,6 +90,7 @@ features = [
|
|||||||
"serde",
|
"serde",
|
||||||
"rpc-types-eth",
|
"rpc-types-eth",
|
||||||
"genesis",
|
"genesis",
|
||||||
|
"sol-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ rust-version.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
alloy-primitives = { workspace = true }
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
moka = { workspace = true, features = ["sync"] }
|
moka = { workspace = true, features = ["sync"] }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use alloy::primitives::U256;
|
||||||
use alloy::signers::local::PrivateKeySigner;
|
use alloy::signers::local::PrivateKeySigner;
|
||||||
use alloy_primitives::U256;
|
use anyhow::{Context, Result, bail};
|
||||||
use anyhow::{Result, bail};
|
|
||||||
|
|
||||||
/// This is a sequential private key allocator. When instantiated, it allocated private keys in
|
/// This is a sequential private key allocator. When instantiated, it allocated private keys in
|
||||||
/// sequentially and in order until the maximum private key specified is reached.
|
/// sequentially and in order until the maximum private key specified is reached.
|
||||||
@@ -10,25 +10,26 @@ pub struct PrivateKeyAllocator {
|
|||||||
next_private_key: U256,
|
next_private_key: U256,
|
||||||
|
|
||||||
/// The highest private key (exclusive) that can be returned by this allocator.
|
/// The highest private key (exclusive) that can be returned by this allocator.
|
||||||
highest_private_key_exclusive: U256,
|
highest_private_key_inclusive: U256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivateKeyAllocator {
|
impl PrivateKeyAllocator {
|
||||||
/// Creates a new instance of the private key allocator.
|
/// Creates a new instance of the private key allocator.
|
||||||
pub fn new(highest_private_key_exclusive: U256) -> Self {
|
pub fn new(highest_private_key_inclusive: U256) -> Self {
|
||||||
Self {
|
Self {
|
||||||
next_private_key: U256::ZERO,
|
next_private_key: U256::ONE,
|
||||||
highest_private_key_exclusive,
|
highest_private_key_inclusive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocates a new private key and errors out if the maximum private key has been reached.
|
/// Allocates a new private key and errors out if the maximum private key has been reached.
|
||||||
pub fn allocate(&mut self) -> Result<PrivateKeySigner> {
|
pub fn allocate(&mut self) -> Result<PrivateKeySigner> {
|
||||||
if self.next_private_key >= self.highest_private_key_exclusive {
|
if self.next_private_key > self.highest_private_key_inclusive {
|
||||||
bail!("Attempted to allocate a private key but failed since all have been allocated");
|
bail!("Attempted to allocate a private key but failed since all have been allocated");
|
||||||
};
|
};
|
||||||
let private_key =
|
let private_key =
|
||||||
PrivateKeySigner::from_slice(self.next_private_key.to_be_bytes::<32>().as_slice())?;
|
PrivateKeySigner::from_slice(self.next_private_key.to_be_bytes::<32>().as_slice())
|
||||||
|
.context("Failed to convert the private key digits into a private key")?;
|
||||||
self.next_private_key += U256::ONE;
|
self.next_private_key += U256::ONE;
|
||||||
Ok(private_key)
|
Ok(private_key)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ revive-dt-solc-binaries = { workspace = true }
|
|||||||
revive-common = { workspace = true }
|
revive-common = { workspace = true }
|
||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
alloy-primitives = { workspace = true }
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
dashmap = { workspace = true }
|
dashmap = { workspace = true }
|
||||||
foundry-compilers-artifacts = { workspace = true }
|
foundry-compilers-artifacts = { workspace = true }
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use alloy::json_abi::JsonAbi;
|
use alloy::json_abi::JsonAbi;
|
||||||
use alloy_primitives::Address;
|
use alloy::primitives::Address;
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
+43
-17
@@ -28,7 +28,11 @@ use temp_dir::TempDir;
|
|||||||
#[command(name = "retester")]
|
#[command(name = "retester")]
|
||||||
pub enum Context {
|
pub enum Context {
|
||||||
/// Executes tests in the MatterLabs format differentially on multiple targets concurrently.
|
/// Executes tests in the MatterLabs format differentially on multiple targets concurrently.
|
||||||
ExecuteTests(Box<TestExecutionContext>),
|
Test(Box<TestExecutionContext>),
|
||||||
|
|
||||||
|
/// Executes differential benchmarks on various platforms.
|
||||||
|
Benchmark(Box<BenchmarkingContext>),
|
||||||
|
|
||||||
/// Exports the JSON schema of the MatterLabs test format used by the tool.
|
/// Exports the JSON schema of the MatterLabs test format used by the tool.
|
||||||
ExportJsonSchema,
|
ExportJsonSchema,
|
||||||
}
|
}
|
||||||
@@ -46,7 +50,8 @@ impl Context {
|
|||||||
impl AsRef<WorkingDirectoryConfiguration> for Context {
|
impl AsRef<WorkingDirectoryConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &WorkingDirectoryConfiguration {
|
fn as_ref(&self) -> &WorkingDirectoryConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +60,8 @@ impl AsRef<WorkingDirectoryConfiguration> for Context {
|
|||||||
impl AsRef<CorpusConfiguration> for Context {
|
impl AsRef<CorpusConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &CorpusConfiguration {
|
fn as_ref(&self) -> &CorpusConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +70,8 @@ impl AsRef<CorpusConfiguration> for Context {
|
|||||||
impl AsRef<SolcConfiguration> for Context {
|
impl AsRef<SolcConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &SolcConfiguration {
|
fn as_ref(&self) -> &SolcConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +80,8 @@ impl AsRef<SolcConfiguration> for Context {
|
|||||||
impl AsRef<ResolcConfiguration> for Context {
|
impl AsRef<ResolcConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &ResolcConfiguration {
|
fn as_ref(&self) -> &ResolcConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +90,8 @@ impl AsRef<ResolcConfiguration> for Context {
|
|||||||
impl AsRef<GethConfiguration> for Context {
|
impl AsRef<GethConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &GethConfiguration {
|
fn as_ref(&self) -> &GethConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +100,8 @@ impl AsRef<GethConfiguration> for Context {
|
|||||||
impl AsRef<KurtosisConfiguration> for Context {
|
impl AsRef<KurtosisConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &KurtosisConfiguration {
|
fn as_ref(&self) -> &KurtosisConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +110,8 @@ impl AsRef<KurtosisConfiguration> for Context {
|
|||||||
impl AsRef<KitchensinkConfiguration> for Context {
|
impl AsRef<KitchensinkConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &KitchensinkConfiguration {
|
fn as_ref(&self) -> &KitchensinkConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +120,8 @@ impl AsRef<KitchensinkConfiguration> for Context {
|
|||||||
impl AsRef<ReviveDevNodeConfiguration> for Context {
|
impl AsRef<ReviveDevNodeConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &ReviveDevNodeConfiguration {
|
fn as_ref(&self) -> &ReviveDevNodeConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +130,8 @@ impl AsRef<ReviveDevNodeConfiguration> for Context {
|
|||||||
impl AsRef<EthRpcConfiguration> for Context {
|
impl AsRef<EthRpcConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &EthRpcConfiguration {
|
fn as_ref(&self) -> &EthRpcConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +140,11 @@ impl AsRef<EthRpcConfiguration> for Context {
|
|||||||
impl AsRef<GenesisConfiguration> for Context {
|
impl AsRef<GenesisConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &GenesisConfiguration {
|
fn as_ref(&self) -> &GenesisConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(..) => {
|
||||||
|
static GENESIS: LazyLock<GenesisConfiguration> = LazyLock::new(Default::default);
|
||||||
|
&GENESIS
|
||||||
|
}
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +153,8 @@ impl AsRef<GenesisConfiguration> for Context {
|
|||||||
impl AsRef<WalletConfiguration> for Context {
|
impl AsRef<WalletConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &WalletConfiguration {
|
fn as_ref(&self) -> &WalletConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +163,8 @@ impl AsRef<WalletConfiguration> for Context {
|
|||||||
impl AsRef<ConcurrencyConfiguration> for Context {
|
impl AsRef<ConcurrencyConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &ConcurrencyConfiguration {
|
fn as_ref(&self) -> &ConcurrencyConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +173,8 @@ impl AsRef<ConcurrencyConfiguration> for Context {
|
|||||||
impl AsRef<CompilationConfiguration> for Context {
|
impl AsRef<CompilationConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &CompilationConfiguration {
|
fn as_ref(&self) -> &CompilationConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +183,8 @@ impl AsRef<CompilationConfiguration> for Context {
|
|||||||
impl AsRef<ReportConfiguration> for Context {
|
impl AsRef<ReportConfiguration> for Context {
|
||||||
fn as_ref(&self) -> &ReportConfiguration {
|
fn as_ref(&self) -> &ReportConfiguration {
|
||||||
match self {
|
match self {
|
||||||
Self::ExecuteTests(context) => context.as_ref().as_ref(),
|
Self::Test(context) => context.as_ref().as_ref(),
|
||||||
|
Self::Benchmark(context) => context.as_ref().as_ref(),
|
||||||
Self::ExportJsonSchema => unreachable!(),
|
Self::ExportJsonSchema => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,6 +289,11 @@ pub struct BenchmarkingContext {
|
|||||||
)]
|
)]
|
||||||
pub platforms: Vec<PlatformIdentifier>,
|
pub platforms: Vec<PlatformIdentifier>,
|
||||||
|
|
||||||
|
/// The default repetition count for any workload specified but that doesn't contain a repeat
|
||||||
|
/// step.
|
||||||
|
#[arg(short = 'r', long = "default-repetition-count", default_value_t = 1000)]
|
||||||
|
pub default_repetition_count: usize,
|
||||||
|
|
||||||
/// Configuration parameters for the corpus files to use.
|
/// Configuration parameters for the corpus files to use.
|
||||||
#[clap(flatten, next_help_heading = "Corpus Configuration")]
|
#[clap(flatten, next_help_heading = "Corpus Configuration")]
|
||||||
pub corpus_configuration: CorpusConfiguration,
|
pub corpus_configuration: CorpusConfiguration,
|
||||||
@@ -495,7 +521,7 @@ impl AsRef<ReportConfiguration> for BenchmarkingContext {
|
|||||||
#[derive(Clone, Debug, Parser, Serialize)]
|
#[derive(Clone, Debug, Parser, Serialize)]
|
||||||
pub struct CorpusConfiguration {
|
pub struct CorpusConfiguration {
|
||||||
/// A list of test corpus JSON files to be tested.
|
/// A list of test corpus JSON files to be tested.
|
||||||
#[arg(long = "corpus", short)]
|
#[arg(short = 'c', long = "corpus")]
|
||||||
pub paths: Vec<PathBuf>,
|
pub paths: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,7 +653,7 @@ pub struct EthRpcConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A set of configuration parameters for the genesis.
|
/// A set of configuration parameters for the genesis.
|
||||||
#[derive(Clone, Debug, Parser, Serialize)]
|
#[derive(Clone, Debug, Default, Parser, Serialize)]
|
||||||
pub struct GenesisConfiguration {
|
pub struct GenesisConfiguration {
|
||||||
/// Specifies the path of the genesis file to use for the nodes that are started.
|
/// Specifies the path of the genesis file to use for the nodes that are started.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -0,0 +1,758 @@
|
|||||||
|
use std::{collections::HashMap, ops::ControlFlow, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use alloy::{
|
||||||
|
hex,
|
||||||
|
json_abi::JsonAbi,
|
||||||
|
network::{Ethereum, TransactionBuilder},
|
||||||
|
primitives::{Address, TxHash, U256},
|
||||||
|
rpc::types::{
|
||||||
|
TransactionReceipt, TransactionRequest,
|
||||||
|
trace::geth::{
|
||||||
|
CallFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig, GethDebugTracerType,
|
||||||
|
GethDebugTracingOptions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use anyhow::{Context as _, Result, bail};
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
use revive_dt_common::{
|
||||||
|
futures::{PollingWaitBehavior, poll},
|
||||||
|
types::PrivateKeyAllocator,
|
||||||
|
};
|
||||||
|
use revive_dt_format::{
|
||||||
|
metadata::{ContractInstance, ContractPathAndIdent},
|
||||||
|
steps::{
|
||||||
|
AllocateAccountStep, BalanceAssertionStep, Calldata, EtherValue, FunctionCallStep, Method,
|
||||||
|
RepeatStep, Step, StepAddress, StepIdx, StepPath, StorageEmptyAssertionStep,
|
||||||
|
},
|
||||||
|
traits::{ResolutionContext, ResolverApi},
|
||||||
|
};
|
||||||
|
use tokio::sync::{Mutex, mpsc::UnboundedSender};
|
||||||
|
use tracing::{Instrument, Span, debug, error, field::display, info, info_span, instrument, trace};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
differential_benchmarks::{ExecutionState, WatcherEvent},
|
||||||
|
helpers::{CachedCompiler, TestDefinition, TestPlatformInformation},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The differential tests driver for a single platform.
|
||||||
|
pub struct Driver<'a, I> {
|
||||||
|
/// The information of the platform that this driver is for.
|
||||||
|
platform_information: &'a TestPlatformInformation<'a>,
|
||||||
|
|
||||||
|
/// The resolver of the platform.
|
||||||
|
resolver: Arc<dyn ResolverApi + 'a>,
|
||||||
|
|
||||||
|
/// The definition of the test that the driver is instructed to execute.
|
||||||
|
test_definition: &'a TestDefinition<'a>,
|
||||||
|
|
||||||
|
/// The private key allocator used by this driver and other drivers when account allocations are
|
||||||
|
/// needed.
|
||||||
|
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
||||||
|
|
||||||
|
/// The execution state associated with the platform.
|
||||||
|
execution_state: ExecutionState,
|
||||||
|
|
||||||
|
/// The send side of the watcher's unbounded channel associated with this driver.
|
||||||
|
watcher_tx: UnboundedSender<WatcherEvent>,
|
||||||
|
|
||||||
|
/// The number of steps that were executed on the driver.
|
||||||
|
steps_executed: usize,
|
||||||
|
|
||||||
|
/// This is the queue of steps that are to be executed by the driver for this test case. Each
|
||||||
|
/// time `execute_step` is called one of the steps is executed.
|
||||||
|
steps_iterator: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I> Driver<'a, I>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = (StepPath, Step)>,
|
||||||
|
{
|
||||||
|
// region:Constructors & Initialization
|
||||||
|
pub async fn new(
|
||||||
|
platform_information: &'a TestPlatformInformation<'a>,
|
||||||
|
test_definition: &'a TestDefinition<'a>,
|
||||||
|
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
||||||
|
cached_compiler: &CachedCompiler<'a>,
|
||||||
|
watcher_tx: UnboundedSender<WatcherEvent>,
|
||||||
|
steps: I,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let mut this = Driver {
|
||||||
|
platform_information,
|
||||||
|
resolver: platform_information
|
||||||
|
.node
|
||||||
|
.resolver()
|
||||||
|
.await
|
||||||
|
.context("Failed to create resolver")?,
|
||||||
|
test_definition,
|
||||||
|
private_key_allocator,
|
||||||
|
execution_state: ExecutionState::empty(),
|
||||||
|
steps_executed: 0,
|
||||||
|
steps_iterator: steps,
|
||||||
|
watcher_tx,
|
||||||
|
};
|
||||||
|
this.init_execution_state(cached_compiler)
|
||||||
|
.await
|
||||||
|
.context("Failed to initialize the execution state of the platform")?;
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_execution_state(&mut self, cached_compiler: &CachedCompiler<'a>) -> Result<()> {
|
||||||
|
let compiler_output = cached_compiler
|
||||||
|
.compile_contracts(
|
||||||
|
self.test_definition.metadata,
|
||||||
|
self.test_definition.metadata_file_path,
|
||||||
|
self.test_definition.mode.clone(),
|
||||||
|
None,
|
||||||
|
self.platform_information.compiler.as_ref(),
|
||||||
|
self.platform_information.platform,
|
||||||
|
&self.platform_information.reporter,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
"Pre-linking compilation failed"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.context("Failed to produce the pre-linking compiled contracts")?;
|
||||||
|
|
||||||
|
let mut deployed_libraries = None::<HashMap<_, _>>;
|
||||||
|
let mut contract_sources = self
|
||||||
|
.test_definition
|
||||||
|
.metadata
|
||||||
|
.contract_sources()
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
"Failed to retrieve contract sources from metadata"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.context("Failed to get the contract instances from the metadata file")?;
|
||||||
|
for library_instance in self
|
||||||
|
.test_definition
|
||||||
|
.metadata
|
||||||
|
.libraries
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.flat_map(|(_, map)| map.values())
|
||||||
|
{
|
||||||
|
debug!(%library_instance, "Deploying Library Instance");
|
||||||
|
|
||||||
|
let ContractPathAndIdent {
|
||||||
|
contract_source_path: library_source_path,
|
||||||
|
contract_ident: library_ident,
|
||||||
|
} = contract_sources
|
||||||
|
.remove(library_instance)
|
||||||
|
.context("Failed to get the contract sources of the contract instance")?;
|
||||||
|
|
||||||
|
let (code, abi) = compiler_output
|
||||||
|
.contracts
|
||||||
|
.get(&library_source_path)
|
||||||
|
.and_then(|contracts| contracts.get(library_ident.as_str()))
|
||||||
|
.context("Failed to get the code and abi for the instance")?;
|
||||||
|
|
||||||
|
let code = alloy::hex::decode(code)?;
|
||||||
|
|
||||||
|
// Getting the deployer address from the cases themselves. This is to ensure
|
||||||
|
// that we're doing the deployments from different accounts and therefore we're
|
||||||
|
// not slowed down by the nonce.
|
||||||
|
let deployer_address = self
|
||||||
|
.test_definition
|
||||||
|
.case
|
||||||
|
.steps
|
||||||
|
.iter()
|
||||||
|
.filter_map(|step| match step {
|
||||||
|
Step::FunctionCall(input) => input.caller.as_address().copied(),
|
||||||
|
Step::BalanceAssertion(..) => None,
|
||||||
|
Step::StorageEmptyAssertion(..) => None,
|
||||||
|
Step::Repeat(..) => None,
|
||||||
|
Step::AllocateAccount(..) => None,
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.unwrap_or(FunctionCallStep::default_caller_address());
|
||||||
|
let tx = TransactionBuilder::<Ethereum>::with_deploy_code(
|
||||||
|
TransactionRequest::default().from(deployer_address),
|
||||||
|
code,
|
||||||
|
);
|
||||||
|
let receipt = self.execute_transaction(tx).await.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
%library_instance,
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
"Failed to deploy the library"
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
?library_instance,
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
"Deployed library"
|
||||||
|
);
|
||||||
|
|
||||||
|
let library_address = receipt
|
||||||
|
.contract_address
|
||||||
|
.expect("Failed to deploy the library");
|
||||||
|
|
||||||
|
deployed_libraries.get_or_insert_default().insert(
|
||||||
|
library_instance.clone(),
|
||||||
|
(library_ident.clone(), library_address, abi.clone()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let compiler_output = cached_compiler
|
||||||
|
.compile_contracts(
|
||||||
|
self.test_definition.metadata,
|
||||||
|
self.test_definition.metadata_file_path,
|
||||||
|
self.test_definition.mode.clone(),
|
||||||
|
deployed_libraries.as_ref(),
|
||||||
|
self.platform_information.compiler.as_ref(),
|
||||||
|
self.platform_information.platform,
|
||||||
|
&self.platform_information.reporter,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
"Post-linking compilation failed"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.context("Failed to compile the post-link contracts")?;
|
||||||
|
|
||||||
|
self.execution_state = ExecutionState::new(
|
||||||
|
compiler_output.contracts,
|
||||||
|
deployed_libraries.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
// endregion:Constructors & Initialization
|
||||||
|
|
||||||
|
// region:Step Handling
|
||||||
|
pub async fn execute_all(mut self) -> Result<usize> {
|
||||||
|
while let Some(result) = self.execute_next_step().await {
|
||||||
|
result?
|
||||||
|
}
|
||||||
|
Ok(self.steps_executed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute_next_step(&mut self) -> Option<Result<()>> {
|
||||||
|
let (step_path, step) = self.steps_iterator.next()?;
|
||||||
|
info!(%step_path, "Executing Step");
|
||||||
|
Some(
|
||||||
|
self.execute_step(&step_path, &step)
|
||||||
|
.await
|
||||||
|
.inspect(|_| info!(%step_path, "Step execution succeeded"))
|
||||||
|
.inspect_err(|err| error!(%step_path, ?err, "Step execution failed")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
%step_path,
|
||||||
|
),
|
||||||
|
err(Debug),
|
||||||
|
)]
|
||||||
|
async fn execute_step(&mut self, step_path: &StepPath, step: &Step) -> Result<()> {
|
||||||
|
let steps_executed = match step {
|
||||||
|
Step::FunctionCall(step) => self
|
||||||
|
.execute_function_call(step_path, step.as_ref())
|
||||||
|
.await
|
||||||
|
.context("Function call step Failed"),
|
||||||
|
Step::Repeat(step) => self
|
||||||
|
.execute_repeat_step(step_path, step.as_ref())
|
||||||
|
.await
|
||||||
|
.context("Repetition Step Failed"),
|
||||||
|
Step::AllocateAccount(step) => self
|
||||||
|
.execute_account_allocation(step_path, step.as_ref())
|
||||||
|
.await
|
||||||
|
.context("Account Allocation Step Failed"),
|
||||||
|
// The following steps are disabled in the benchmarking driver.
|
||||||
|
Step::BalanceAssertion(..) | Step::StorageEmptyAssertion(..) => Ok(0),
|
||||||
|
}?;
|
||||||
|
self.steps_executed += steps_executed;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all)]
|
||||||
|
pub async fn execute_function_call(
|
||||||
|
&mut self,
|
||||||
|
_: &StepPath,
|
||||||
|
step: &FunctionCallStep,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let deployment_receipts = self
|
||||||
|
.handle_function_call_contract_deployment(step)
|
||||||
|
.await
|
||||||
|
.context("Failed to deploy contracts for the function call step")?;
|
||||||
|
let execution_receipt = self
|
||||||
|
.handle_function_call_execution(step, deployment_receipts)
|
||||||
|
.await
|
||||||
|
.context("Failed to handle the function call execution")?;
|
||||||
|
let tracing_result = self
|
||||||
|
.handle_function_call_call_frame_tracing(execution_receipt.transaction_hash)
|
||||||
|
.await
|
||||||
|
.context("Failed to handle the function call call frame tracing")?;
|
||||||
|
self.handle_function_call_variable_assignment(step, &tracing_result)
|
||||||
|
.await
|
||||||
|
.context("Failed to handle function call variable assignment")?;
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_function_call_contract_deployment(
|
||||||
|
&mut self,
|
||||||
|
step: &FunctionCallStep,
|
||||||
|
) -> Result<HashMap<ContractInstance, TransactionReceipt>> {
|
||||||
|
trace!("Handling Function Call Contract Deployment");
|
||||||
|
|
||||||
|
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
|
||||||
|
for instance in step.find_all_contract_instances().into_iter() {
|
||||||
|
if !self
|
||||||
|
.execution_state
|
||||||
|
.deployed_contracts
|
||||||
|
.contains_key(&instance)
|
||||||
|
{
|
||||||
|
instances_we_must_deploy.entry(instance).or_insert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Method::Deployer = step.method {
|
||||||
|
instances_we_must_deploy.swap_remove(&step.instance);
|
||||||
|
instances_we_must_deploy.insert(step.instance.clone(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut receipts = HashMap::new();
|
||||||
|
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
|
||||||
|
let calldata = deploy_with_constructor_arguments.then_some(&step.calldata);
|
||||||
|
let value = deploy_with_constructor_arguments
|
||||||
|
.then_some(step.value)
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let caller = {
|
||||||
|
let context = self.default_resolution_context();
|
||||||
|
step.caller
|
||||||
|
.resolve_address(self.resolver.as_ref(), context)
|
||||||
|
.await?
|
||||||
|
};
|
||||||
|
if let (_, _, Some(receipt)) = self
|
||||||
|
.get_or_deploy_contract_instance(&instance, caller, calldata, value)
|
||||||
|
.await
|
||||||
|
.context("Failed to get or deploy contract instance during input execution")?
|
||||||
|
{
|
||||||
|
receipts.insert(instance.clone(), receipt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
deployed_contracts = receipts.len(),
|
||||||
|
"Handled function call contract deployment"
|
||||||
|
);
|
||||||
|
Ok(receipts)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_function_call_execution(
|
||||||
|
&mut self,
|
||||||
|
step: &FunctionCallStep,
|
||||||
|
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
|
||||||
|
) -> Result<TransactionReceipt> {
|
||||||
|
trace!("Handling the function call execution");
|
||||||
|
match step.method {
|
||||||
|
// This step was already executed when `handle_step` was called. We just need to
|
||||||
|
// lookup the transaction receipt in this case and continue on.
|
||||||
|
Method::Deployer => deployment_receipts
|
||||||
|
.remove(&step.instance)
|
||||||
|
.context("Failed to find deployment receipt for constructor call"),
|
||||||
|
Method::Fallback | Method::FunctionName(_) => {
|
||||||
|
trace!("Creating the transaction");
|
||||||
|
let tx = step
|
||||||
|
.as_transaction(self.resolver.as_ref(), self.default_resolution_context())
|
||||||
|
.await?;
|
||||||
|
trace!("Created the transaction");
|
||||||
|
trace!("Calling the execute transaction when handling the function call execution");
|
||||||
|
self.execute_transaction(tx).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_function_call_call_frame_tracing(
|
||||||
|
&mut self,
|
||||||
|
tx_hash: TxHash,
|
||||||
|
) -> Result<CallFrame> {
|
||||||
|
self.platform_information
|
||||||
|
.node
|
||||||
|
.trace_transaction(
|
||||||
|
tx_hash,
|
||||||
|
GethDebugTracingOptions {
|
||||||
|
tracer: Some(GethDebugTracerType::BuiltInTracer(
|
||||||
|
GethDebugBuiltInTracerType::CallTracer,
|
||||||
|
)),
|
||||||
|
tracer_config: GethDebugTracerConfig(serde_json::json! {{
|
||||||
|
"onlyTopCall": true,
|
||||||
|
"withLog": false,
|
||||||
|
"withStorage": false,
|
||||||
|
"withMemory": false,
|
||||||
|
"withStack": false,
|
||||||
|
"withReturnData": true
|
||||||
|
}}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|trace| {
|
||||||
|
trace
|
||||||
|
.try_into_call_frame()
|
||||||
|
.expect("Impossible - we requested a callframe trace so we must get it back")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_function_call_variable_assignment(
|
||||||
|
&mut self,
|
||||||
|
step: &FunctionCallStep,
|
||||||
|
tracing_result: &CallFrame,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(ref assignments) = step.variable_assignments else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handling the return data variable assignments.
|
||||||
|
for (variable_name, output_word) in assignments.return_data.iter().zip(
|
||||||
|
tracing_result
|
||||||
|
.output
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_vec()
|
||||||
|
.chunks(32),
|
||||||
|
) {
|
||||||
|
let value = U256::from_be_slice(output_word);
|
||||||
|
self.execution_state
|
||||||
|
.variables
|
||||||
|
.insert(variable_name.clone(), value);
|
||||||
|
tracing::info!(
|
||||||
|
variable_name,
|
||||||
|
variable_value = hex::encode(value.to_be_bytes::<32>()),
|
||||||
|
"Assigned variable"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all)]
|
||||||
|
pub async fn execute_balance_assertion(
|
||||||
|
&mut self,
|
||||||
|
_: &StepPath,
|
||||||
|
_: &BalanceAssertionStep,
|
||||||
|
) -> anyhow::Result<usize> {
|
||||||
|
// Kept empty intentionally for the benchmark driver.
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all, err(Debug))]
|
||||||
|
async fn execute_storage_empty_assertion_step(
|
||||||
|
&mut self,
|
||||||
|
_: &StepPath,
|
||||||
|
_: &StorageEmptyAssertionStep,
|
||||||
|
) -> Result<usize> {
|
||||||
|
// Kept empty intentionally for the benchmark driver.
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all, err(Debug))]
|
||||||
|
async fn execute_repeat_step(
|
||||||
|
&mut self,
|
||||||
|
step_path: &StepPath,
|
||||||
|
step: &RepeatStep,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let tasks = (0..step.repeat)
|
||||||
|
.map(|_| Driver {
|
||||||
|
platform_information: self.platform_information,
|
||||||
|
resolver: self.resolver.clone(),
|
||||||
|
test_definition: self.test_definition,
|
||||||
|
private_key_allocator: self.private_key_allocator.clone(),
|
||||||
|
execution_state: self.execution_state.clone(),
|
||||||
|
steps_executed: 0,
|
||||||
|
steps_iterator: {
|
||||||
|
let steps: Vec<(StepPath, Step)> = step
|
||||||
|
.steps
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(step_idx, step)| {
|
||||||
|
let step_idx = StepIdx::new(step_idx);
|
||||||
|
let step_path = step_path.append(step_idx);
|
||||||
|
(step_path, step)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
steps.into_iter()
|
||||||
|
},
|
||||||
|
watcher_tx: self.watcher_tx.clone(),
|
||||||
|
})
|
||||||
|
.map(|driver| driver.execute_all())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// TODO: Determine how we want to know the `ignore_block_before` and if it's through the
|
||||||
|
// receipt and how this would impact the architecture and the possibility of us not waiting
|
||||||
|
// for receipts in the future.
|
||||||
|
self.watcher_tx
|
||||||
|
.send(WatcherEvent::RepetitionStartEvent {
|
||||||
|
ignore_block_before: 0,
|
||||||
|
})
|
||||||
|
.context("Failed to send message on the watcher's tx")?;
|
||||||
|
|
||||||
|
let res = futures::future::try_join_all(tasks)
|
||||||
|
.await
|
||||||
|
.context("Repetition execution failed")?;
|
||||||
|
Ok(res.into_iter().sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all, err(Debug))]
|
||||||
|
pub async fn execute_account_allocation(
|
||||||
|
&mut self,
|
||||||
|
_: &StepPath,
|
||||||
|
step: &AllocateAccountStep,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let Some(variable_name) = step.variable_name.strip_prefix("$VARIABLE:") else {
|
||||||
|
bail!("Account allocation must start with $VARIABLE:");
|
||||||
|
};
|
||||||
|
|
||||||
|
let private_key = self
|
||||||
|
.private_key_allocator
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.allocate()
|
||||||
|
.context("Account allocation through the private key allocator failed")?;
|
||||||
|
let account = private_key.address();
|
||||||
|
let variable = U256::from_be_slice(account.0.as_slice());
|
||||||
|
|
||||||
|
self.execution_state
|
||||||
|
.variables
|
||||||
|
.insert(variable_name.to_string(), variable);
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
// endregion:Step Handling
|
||||||
|
|
||||||
|
// region:Contract Deployment
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
%contract_instance,
|
||||||
|
%deployer
|
||||||
|
),
|
||||||
|
err(Debug),
|
||||||
|
)]
|
||||||
|
async fn get_or_deploy_contract_instance(
|
||||||
|
&mut self,
|
||||||
|
contract_instance: &ContractInstance,
|
||||||
|
deployer: Address,
|
||||||
|
calldata: Option<&Calldata>,
|
||||||
|
value: Option<EtherValue>,
|
||||||
|
) -> Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
|
||||||
|
if let Some((_, address, abi)) = self
|
||||||
|
.execution_state
|
||||||
|
.deployed_contracts
|
||||||
|
.get(contract_instance)
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
|
||||||
|
%address,
|
||||||
|
"Contract instance already deployed."
|
||||||
|
);
|
||||||
|
Ok((*address, abi.clone(), None))
|
||||||
|
} else {
|
||||||
|
info!("Contract instance requires deployment.");
|
||||||
|
let (address, abi, receipt) = self
|
||||||
|
.deploy_contract(contract_instance, deployer, calldata, value)
|
||||||
|
.await
|
||||||
|
.context("Failed to deploy contract")?;
|
||||||
|
info!(
|
||||||
|
%address,
|
||||||
|
"Contract instance has been deployed."
|
||||||
|
);
|
||||||
|
Ok((address, abi, Some(receipt)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(
|
||||||
|
platform_identifier = %self.platform_information.platform.platform_identifier(),
|
||||||
|
%contract_instance,
|
||||||
|
%deployer
|
||||||
|
),
|
||||||
|
err(Debug),
|
||||||
|
)]
|
||||||
|
async fn deploy_contract(
|
||||||
|
&mut self,
|
||||||
|
contract_instance: &ContractInstance,
|
||||||
|
deployer: Address,
|
||||||
|
calldata: Option<&Calldata>,
|
||||||
|
value: Option<EtherValue>,
|
||||||
|
) -> Result<(Address, JsonAbi, TransactionReceipt)> {
|
||||||
|
let Some(ContractPathAndIdent {
|
||||||
|
contract_source_path,
|
||||||
|
contract_ident,
|
||||||
|
}) = self
|
||||||
|
.test_definition
|
||||||
|
.metadata
|
||||||
|
.contract_sources()?
|
||||||
|
.remove(contract_instance)
|
||||||
|
else {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Contract source not found for instance {:?}",
|
||||||
|
contract_instance
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some((code, abi)) = self
|
||||||
|
.execution_state
|
||||||
|
.compiled_contracts
|
||||||
|
.get(&contract_source_path)
|
||||||
|
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
anyhow::bail!(
|
||||||
|
"Failed to find information for contract {:?}",
|
||||||
|
contract_instance
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut code = match alloy::hex::decode(&code) {
|
||||||
|
Ok(code) => code,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(
|
||||||
|
?error,
|
||||||
|
contract_source_path = contract_source_path.display().to_string(),
|
||||||
|
contract_ident = contract_ident.as_ref(),
|
||||||
|
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
|
||||||
|
);
|
||||||
|
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(calldata) = calldata {
|
||||||
|
let calldata = calldata
|
||||||
|
.calldata(self.resolver.as_ref(), self.default_resolution_context())
|
||||||
|
.await?;
|
||||||
|
code.extend(calldata);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx = {
|
||||||
|
let tx = TransactionRequest::default().from(deployer);
|
||||||
|
let tx = match value {
|
||||||
|
Some(ref value) => tx.value(value.into_inner()),
|
||||||
|
_ => tx,
|
||||||
|
};
|
||||||
|
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
||||||
|
};
|
||||||
|
|
||||||
|
let receipt = match self.execute_transaction(tx).await {
|
||||||
|
Ok(receipt) => receipt,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!(?error, "Contract deployment transaction failed.");
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(address) = receipt.contract_address else {
|
||||||
|
anyhow::bail!("Contract deployment didn't return an address");
|
||||||
|
};
|
||||||
|
tracing::info!(
|
||||||
|
instance_name = ?contract_instance,
|
||||||
|
instance_address = ?address,
|
||||||
|
"Deployed contract"
|
||||||
|
);
|
||||||
|
self.platform_information
|
||||||
|
.reporter
|
||||||
|
.report_contract_deployed_event(contract_instance.clone(), address)?;
|
||||||
|
|
||||||
|
self.execution_state.deployed_contracts.insert(
|
||||||
|
contract_instance.clone(),
|
||||||
|
(contract_ident, address, abi.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((address, abi, receipt))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all)]
|
||||||
|
async fn step_address_auto_deployment(
|
||||||
|
&mut self,
|
||||||
|
step_address: &StepAddress,
|
||||||
|
) -> Result<Address> {
|
||||||
|
match step_address {
|
||||||
|
StepAddress::Address(address) => Ok(*address),
|
||||||
|
StepAddress::ResolvableAddress(resolvable) => {
|
||||||
|
let Some(instance) = resolvable
|
||||||
|
.strip_suffix(".address")
|
||||||
|
.map(ContractInstance::new)
|
||||||
|
else {
|
||||||
|
bail!("Not an address variable");
|
||||||
|
};
|
||||||
|
|
||||||
|
self.get_or_deploy_contract_instance(
|
||||||
|
&instance,
|
||||||
|
FunctionCallStep::default_caller_address(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|v| v.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:Contract Deployment
|
||||||
|
|
||||||
|
// region:Resolution & Resolver
|
||||||
|
fn default_resolution_context(&self) -> ResolutionContext<'_> {
|
||||||
|
ResolutionContext::default()
|
||||||
|
.with_deployed_contracts(&self.execution_state.deployed_contracts)
|
||||||
|
.with_variables(&self.execution_state.variables)
|
||||||
|
}
|
||||||
|
// endregion:Resolution & Resolver
|
||||||
|
|
||||||
|
// region:Transaction Execution
|
||||||
|
/// Executes the transaction on the driver's node with some custom waiting logic for the receipt
|
||||||
|
#[instrument(level = "info", skip_all, fields(transaction_hash = tracing::field::Empty))]
|
||||||
|
async fn execute_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: TransactionRequest,
|
||||||
|
) -> anyhow::Result<TransactionReceipt> {
|
||||||
|
trace!("Submitting transaction");
|
||||||
|
let node = self.platform_information.node;
|
||||||
|
let transaction_hash = node
|
||||||
|
.submit_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.context("Failed to submit transaction")?;
|
||||||
|
Span::current().record("transaction_hash", display(transaction_hash));
|
||||||
|
|
||||||
|
info!("Submitted transaction");
|
||||||
|
|
||||||
|
self.watcher_tx
|
||||||
|
.send(WatcherEvent::SubmittedTransaction { transaction_hash })
|
||||||
|
.context("Failed to send the transaction hash to the watcher")?;
|
||||||
|
|
||||||
|
info!("Starting to poll for transaction receipt");
|
||||||
|
poll(
|
||||||
|
Duration::from_secs(10 * 60),
|
||||||
|
PollingWaitBehavior::Constant(Duration::from_secs(1)),
|
||||||
|
|| {
|
||||||
|
async move {
|
||||||
|
match node.get_receipt(transaction_hash).await {
|
||||||
|
Ok(receipt) => Ok(ControlFlow::Break(receipt)),
|
||||||
|
Err(_) => Ok(ControlFlow::Continue(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.instrument(info_span!("Polling for receipt"))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
// endregion:Transaction Execution
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
//! The main entry point for differential benchmarking.
|
||||||
|
|
||||||
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use futures::{FutureExt, StreamExt};
|
||||||
|
use revive_dt_common::types::PrivateKeyAllocator;
|
||||||
|
use revive_dt_core::Platform;
|
||||||
|
use revive_dt_format::steps::{Step, StepIdx, StepPath};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::{error, info, info_span, instrument, warn};
|
||||||
|
|
||||||
|
use revive_dt_config::{BenchmarkingContext, Context};
|
||||||
|
use revive_dt_report::Reporter;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
differential_benchmarks::{Driver, Watcher, WatcherEvent},
|
||||||
|
helpers::{CachedCompiler, NodePool, collect_metadata_files, create_test_definitions_stream},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles the differential testing executing it according to the information defined in the
|
||||||
|
/// context
|
||||||
|
#[instrument(level = "info", err(Debug), skip_all)]
|
||||||
|
pub async fn handle_differential_benchmarks(
|
||||||
|
mut context: BenchmarkingContext,
|
||||||
|
reporter: Reporter,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
// A bit of a hack but we need to override the number of nodes specified through the CLI since
|
||||||
|
// benchmarks can only be run on a single node. Perhaps in the future we'd have a cleaner way to
|
||||||
|
// do this. But, for the time being, we need to override the cli arguments.
|
||||||
|
if context.concurrency_configuration.number_of_nodes != 1 {
|
||||||
|
warn!(
|
||||||
|
specified_number_of_nodes = context.concurrency_configuration.number_of_nodes,
|
||||||
|
updated_number_of_nodes = 1,
|
||||||
|
"Invalid number of nodes specified through the CLI. Benchmarks can only be run on a single node. Updated the arguments."
|
||||||
|
);
|
||||||
|
context.concurrency_configuration.number_of_nodes = 1;
|
||||||
|
};
|
||||||
|
let full_context = Context::Benchmark(Box::new(context.clone()));
|
||||||
|
|
||||||
|
// Discover all of the metadata files that are defined in the context.
|
||||||
|
let metadata_files = collect_metadata_files(&context)
|
||||||
|
.context("Failed to collect metadata files for differential testing")?;
|
||||||
|
info!(len = metadata_files.len(), "Discovered metadata files");
|
||||||
|
|
||||||
|
// Discover the list of platforms that the tests should run on based on the context.
|
||||||
|
let platforms = context
|
||||||
|
.platforms
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(Into::<&dyn Platform>::into)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Starting the nodes of the various platforms specified in the context. Note that we use the
|
||||||
|
// node pool since it contains all of the code needed to spawn nodes from A to Z and therefore
|
||||||
|
// it's the preferred way for us to start nodes even when we're starting just a single node. The
|
||||||
|
// added overhead from it is quite small (performance wise) since it's involved only when we're
|
||||||
|
// creating the test definitions, but it might have other maintenance overhead as it obscures
|
||||||
|
// the fact that only a single node is spawned.
|
||||||
|
let platforms_and_nodes = {
|
||||||
|
let mut map = BTreeMap::new();
|
||||||
|
|
||||||
|
for platform in platforms.iter() {
|
||||||
|
let platform_identifier = platform.platform_identifier();
|
||||||
|
|
||||||
|
let node_pool = NodePool::new(full_context.clone(), *platform)
|
||||||
|
.await
|
||||||
|
.inspect_err(|err| {
|
||||||
|
error!(
|
||||||
|
?err,
|
||||||
|
%platform_identifier,
|
||||||
|
"Failed to initialize the node pool for the platform."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.context("Failed to initialize the node pool")?;
|
||||||
|
|
||||||
|
map.insert(platform_identifier, (*platform, node_pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
};
|
||||||
|
info!("Spawned the platform nodes");
|
||||||
|
|
||||||
|
// Preparing test definitions for the execution.
|
||||||
|
let test_definitions = create_test_definitions_stream(
|
||||||
|
&full_context,
|
||||||
|
metadata_files.iter(),
|
||||||
|
&platforms_and_nodes,
|
||||||
|
reporter.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await;
|
||||||
|
info!(len = test_definitions.len(), "Created test definitions");
|
||||||
|
|
||||||
|
// Creating the objects that will be shared between the various runs. The cached compiler is the
|
||||||
|
// only one at the current moment of time that's safe to share between runs.
|
||||||
|
let cached_compiler = CachedCompiler::new(
|
||||||
|
context
|
||||||
|
.working_directory
|
||||||
|
.as_path()
|
||||||
|
.join("compilation_cache"),
|
||||||
|
context
|
||||||
|
.compilation_configuration
|
||||||
|
.invalidate_compilation_cache,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(Arc::new)
|
||||||
|
.context("Failed to initialize cached compiler")?;
|
||||||
|
|
||||||
|
// Note: we do not want to run all of the workloads concurrently on all platforms. Rather, we'd
|
||||||
|
// like to run all of the workloads for one platform, and then the next sequentially as we'd
|
||||||
|
// like for the effect of concurrency to be minimized when we're doing the benchmarking.
|
||||||
|
for platform in platforms.iter() {
|
||||||
|
let platform_identifier = platform.platform_identifier();
|
||||||
|
|
||||||
|
let span = info_span!("Benchmarking for the platform", %platform_identifier);
|
||||||
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
for test_definition in test_definitions.iter() {
|
||||||
|
let platform_information = &test_definition.platforms[&platform_identifier];
|
||||||
|
|
||||||
|
let span = info_span!(
|
||||||
|
"Executing workload",
|
||||||
|
metadata_file_path = %test_definition.metadata_file_path.display(),
|
||||||
|
case_idx = %test_definition.case_idx,
|
||||||
|
mode = %test_definition.mode,
|
||||||
|
);
|
||||||
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
// Initializing all of the components requires to execute this particular workload.
|
||||||
|
let private_key_allocator = Arc::new(Mutex::new(PrivateKeyAllocator::new(
|
||||||
|
context.wallet_configuration.highest_private_key_exclusive(),
|
||||||
|
)));
|
||||||
|
let (watcher, watcher_tx) = Watcher::new(
|
||||||
|
platform_identifier,
|
||||||
|
platform_information
|
||||||
|
.node
|
||||||
|
.subscribe_to_full_blocks_information()
|
||||||
|
.await
|
||||||
|
.context("Failed to subscribe to full blocks information from the node")?,
|
||||||
|
);
|
||||||
|
let driver = Driver::new(
|
||||||
|
platform_information,
|
||||||
|
test_definition,
|
||||||
|
private_key_allocator,
|
||||||
|
cached_compiler.as_ref(),
|
||||||
|
watcher_tx.clone(),
|
||||||
|
test_definition
|
||||||
|
.case
|
||||||
|
.steps_iterator_for_benchmarks(context.default_repetition_count)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(step_idx, step)| -> (StepPath, Step) {
|
||||||
|
(StepPath::new(vec![StepIdx::new(step_idx)]), step)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Failed to create the benchmarks driver")?;
|
||||||
|
|
||||||
|
futures::future::try_join(
|
||||||
|
watcher.run(),
|
||||||
|
driver.execute_all().inspect(|_| {
|
||||||
|
info!("All transactions submitted - driver completed execution");
|
||||||
|
watcher_tx
|
||||||
|
.send(WatcherEvent::AllTransactionsSubmitted)
|
||||||
|
.unwrap()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Failed to run the driver and executor")
|
||||||
|
.inspect(|(_, steps_executed)| info!(steps_executed, "Workload Execution Succeeded"))
|
||||||
|
.inspect_err(|err| error!(?err, "Workload Execution Failed"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use alloy::{
|
||||||
|
json_abi::JsonAbi,
|
||||||
|
primitives::{Address, U256},
|
||||||
|
};
|
||||||
|
|
||||||
|
use revive_dt_format::metadata::{ContractIdent, ContractInstance};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// The state associated with the test execution of one of the workloads.
|
||||||
|
pub struct ExecutionState {
|
||||||
|
/// The compiled contracts, these contracts have been compiled and have had the libraries linked
|
||||||
|
/// against them and therefore they're ready to be deployed on-demand.
|
||||||
|
pub compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||||
|
|
||||||
|
/// A map of all of the deployed contracts and information about them.
|
||||||
|
pub deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||||
|
|
||||||
|
/// This map stores the variables used for each one of the cases contained in the metadata file.
|
||||||
|
pub variables: HashMap<String, U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutionState {
|
||||||
|
pub fn new(
|
||||||
|
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
||||||
|
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
compiled_contracts,
|
||||||
|
deployed_contracts,
|
||||||
|
variables: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
compiled_contracts: Default::default(),
|
||||||
|
deployed_contracts: Default::default(),
|
||||||
|
variables: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
mod driver;
|
||||||
|
mod entry_point;
|
||||||
|
mod execution_state;
|
||||||
|
mod watcher;
|
||||||
|
|
||||||
|
pub use driver::*;
|
||||||
|
pub use entry_point::*;
|
||||||
|
pub use execution_state::*;
|
||||||
|
pub use watcher::*;
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
use std::{collections::HashSet, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
|
use alloy::primitives::{BlockNumber, TxHash};
|
||||||
|
use anyhow::Result;
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
|
use revive_dt_node_interaction::MinedBlockInformation;
|
||||||
|
use tokio::sync::{
|
||||||
|
RwLock,
|
||||||
|
mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
|
||||||
|
};
|
||||||
|
use tracing::{info, instrument};
|
||||||
|
|
||||||
|
/// This struct defines the watcher used in the benchmarks. A watcher is only valid for 1 workload
|
||||||
|
/// and MUST NOT be re-used between workloads since it holds important internal state for a given
|
||||||
|
/// workload and is not designed for reuse.
|
||||||
|
pub struct Watcher {
|
||||||
|
/// The identifier of the platform that this watcher is for.
|
||||||
|
platform_identifier: PlatformIdentifier,
|
||||||
|
|
||||||
|
/// The receive side of the channel that all of the drivers and various other parts of the code
|
||||||
|
/// send events to the watcher on.
|
||||||
|
rx: UnboundedReceiver<WatcherEvent>,
|
||||||
|
|
||||||
|
/// This is a stream of the blocks that were mined by the node. This is for a single platform
|
||||||
|
/// and a single node from that platform.
|
||||||
|
blocks_stream: Pin<Box<dyn Stream<Item = MinedBlockInformation>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Watcher {
|
||||||
|
pub fn new(
|
||||||
|
platform_identifier: PlatformIdentifier,
|
||||||
|
blocks_stream: Pin<Box<dyn Stream<Item = MinedBlockInformation>>>,
|
||||||
|
) -> (Self, UnboundedSender<WatcherEvent>) {
|
||||||
|
let (tx, rx) = unbounded_channel::<WatcherEvent>();
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
platform_identifier,
|
||||||
|
rx,
|
||||||
|
blocks_stream,
|
||||||
|
},
|
||||||
|
tx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all)]
|
||||||
|
pub async fn run(mut self) -> Result<()> {
|
||||||
|
// The first event that the watcher receives must be a `RepetitionStartEvent` that informs
|
||||||
|
// the watcher of the last block number that it should ignore and what the block number is
|
||||||
|
// for the first important block that it should look for.
|
||||||
|
let ignore_block_before = loop {
|
||||||
|
let Some(WatcherEvent::RepetitionStartEvent {
|
||||||
|
ignore_block_before,
|
||||||
|
}) = self.rx.recv().await
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
break ignore_block_before;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the set of the transaction hashes that the watcher should be looking for and
|
||||||
|
// watch for them in the blocks. The watcher will keep watching for blocks until it sees
|
||||||
|
// that all of the transactions that it was watching for has been seen in the mined blocks.
|
||||||
|
let watch_for_transaction_hashes = Arc::new(RwLock::new(HashSet::<TxHash>::new()));
|
||||||
|
|
||||||
|
// A boolean that keeps track of whether all of the transactions were submitted or if more
|
||||||
|
// txs are expected to come through the receive side of the channel. We do not want to rely
|
||||||
|
// on the channel closing alone for the watcher to know that all of the transactions were
|
||||||
|
// submitted and for there to be an explicit event sent by the core orchestrator that
|
||||||
|
// informs the watcher that no further transactions are to be expected and that it can
|
||||||
|
// safely ignore the channel.
|
||||||
|
let all_transactions_submitted = Arc::new(RwLock::new(false));
|
||||||
|
|
||||||
|
let watcher_event_watching_task = {
|
||||||
|
let watch_for_transaction_hashes = watch_for_transaction_hashes.clone();
|
||||||
|
let all_transactions_submitted = all_transactions_submitted.clone();
|
||||||
|
async move {
|
||||||
|
while let Some(watcher_event) = self.rx.recv().await {
|
||||||
|
match watcher_event {
|
||||||
|
// Subsequent repetition starts are ignored since certain workloads can
|
||||||
|
// contain nested repetitions and therefore there's no use in doing any
|
||||||
|
// action if the repetitions are nested.
|
||||||
|
WatcherEvent::RepetitionStartEvent { .. } => {}
|
||||||
|
WatcherEvent::SubmittedTransaction { transaction_hash } => {
|
||||||
|
watch_for_transaction_hashes
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.insert(transaction_hash);
|
||||||
|
}
|
||||||
|
WatcherEvent::AllTransactionsSubmitted => {
|
||||||
|
*all_transactions_submitted.write().await = true;
|
||||||
|
self.rx.close();
|
||||||
|
info!("Watcher's Events Watching Task Finished");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let block_information_watching_task = {
|
||||||
|
let watch_for_transaction_hashes = watch_for_transaction_hashes.clone();
|
||||||
|
let all_transactions_submitted = all_transactions_submitted.clone();
|
||||||
|
let mut blocks_information_stream = self.blocks_stream;
|
||||||
|
async move {
|
||||||
|
let mut mined_blocks_information = Vec::new();
|
||||||
|
|
||||||
|
while let Some(block) = blocks_information_stream.next().await {
|
||||||
|
// If the block number is equal to or less than the last block before the
|
||||||
|
// repetition then we ignore it and continue on to the next block.
|
||||||
|
if block.block_number <= ignore_block_before {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *all_transactions_submitted.read().await
|
||||||
|
&& watch_for_transaction_hashes.read().await.is_empty()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
remaining_transactions = watch_for_transaction_hashes.read().await.len(),
|
||||||
|
block_tx_count = block.transaction_hashes.len(),
|
||||||
|
"Observed a block"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove all of the transaction hashes observed in this block from the txs we
|
||||||
|
// are currently watching for.
|
||||||
|
let mut watch_for_transaction_hashes =
|
||||||
|
watch_for_transaction_hashes.write().await;
|
||||||
|
for tx_hash in block.transaction_hashes.iter() {
|
||||||
|
watch_for_transaction_hashes.remove(tx_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
mined_blocks_information.push(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Watcher's Block Watching Task Finished");
|
||||||
|
mined_blocks_information
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, mined_blocks_information) =
|
||||||
|
futures::future::join(watcher_event_watching_task, block_information_watching_task)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// region:TEMPORARY
|
||||||
|
{
|
||||||
|
// TODO: The following core is TEMPORARY and will be removed once we have proper
|
||||||
|
// reporting in place and then it can be removed. This serves as as way of doing some
|
||||||
|
// very simple reporting for the time being.
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
let mut stdout = std::io::stdout().lock();
|
||||||
|
writeln!(
|
||||||
|
stdout,
|
||||||
|
"Watcher information for {}",
|
||||||
|
self.platform_identifier
|
||||||
|
)?;
|
||||||
|
writeln!(
|
||||||
|
stdout,
|
||||||
|
"block_number,block_timestamp,mined_gas,block_gas_limit,tx_count"
|
||||||
|
)?;
|
||||||
|
for block in mined_blocks_information {
|
||||||
|
writeln!(
|
||||||
|
stdout,
|
||||||
|
"{},{},{},{},{}",
|
||||||
|
block.block_number,
|
||||||
|
block.block_timestamp,
|
||||||
|
block.mined_gas,
|
||||||
|
block.block_gas_limit,
|
||||||
|
block.transaction_hashes.len()
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion:TEMPORARY
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum WatcherEvent {
|
||||||
|
/// Informs the watcher that it should begin watching for the blocks mined by the platforms.
|
||||||
|
/// Before the watcher receives this event it will not be watching for the mined blocks. The
|
||||||
|
/// reason behind this is that we do not want the initialization transactions (e.g., contract
|
||||||
|
/// deployments) to be included in the overall TPS and GPS measurements since these blocks will
|
||||||
|
/// most likely only contain a single transaction since they're just being used for
|
||||||
|
/// initialization.
|
||||||
|
RepetitionStartEvent {
|
||||||
|
/// This is the block number of the last block seen before the repetition started. This is
|
||||||
|
/// used to instruct the watcher to ignore all block prior to this block when it starts
|
||||||
|
/// streaming the blocks.
|
||||||
|
ignore_block_before: BlockNumber,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Informs the watcher that a transaction was submitted and that the watcher should watch for a
|
||||||
|
/// transaction with this hash in the blocks that it watches.
|
||||||
|
SubmittedTransaction {
|
||||||
|
/// The hash of the submitted transaction.
|
||||||
|
transaction_hash: TxHash,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Informs the watcher that all of the transactions of this benchmark have been submitted and
|
||||||
|
/// that it can expect to receive no further transaction hashes and not even watch the channel
|
||||||
|
/// any longer.
|
||||||
|
AllTransactionsSubmitted,
|
||||||
|
}
|
||||||
@@ -40,14 +40,14 @@ use crate::{
|
|||||||
|
|
||||||
type StepsIterator = std::vec::IntoIter<(StepPath, Step)>;
|
type StepsIterator = std::vec::IntoIter<(StepPath, Step)>;
|
||||||
|
|
||||||
pub struct DifferentialTestsDriver<'a, I> {
|
pub struct Driver<'a, I> {
|
||||||
/// The drivers for the various platforms that we're executing the tests on.
|
/// The drivers for the various platforms that we're executing the tests on.
|
||||||
platform_drivers: BTreeMap<PlatformIdentifier, DifferentialTestsPlatformDriver<'a, I>>,
|
platform_drivers: BTreeMap<PlatformIdentifier, PlatformDriver<'a, I>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I> DifferentialTestsDriver<'a, I> where I: Iterator<Item = (StepPath, Step)> {}
|
impl<'a, I> Driver<'a, I> where I: Iterator<Item = (StepPath, Step)> {}
|
||||||
|
|
||||||
impl<'a> DifferentialTestsDriver<'a, StepsIterator> {
|
impl<'a> Driver<'a, StepsIterator> {
|
||||||
// region:Constructors
|
// region:Constructors
|
||||||
pub async fn new_root(
|
pub async fn new_root(
|
||||||
test_definition: &'a TestDefinition<'a>,
|
test_definition: &'a TestDefinition<'a>,
|
||||||
@@ -85,7 +85,7 @@ impl<'a> DifferentialTestsDriver<'a, StepsIterator> {
|
|||||||
test_definition: &'a TestDefinition<'a>,
|
test_definition: &'a TestDefinition<'a>,
|
||||||
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
||||||
cached_compiler: &CachedCompiler<'a>,
|
cached_compiler: &CachedCompiler<'a>,
|
||||||
) -> Result<DifferentialTestsPlatformDriver<'a, StepsIterator>> {
|
) -> Result<PlatformDriver<'a, StepsIterator>> {
|
||||||
let steps: Vec<(StepPath, Step)> = test_definition
|
let steps: Vec<(StepPath, Step)> = test_definition
|
||||||
.case
|
.case
|
||||||
.steps_iterator()
|
.steps_iterator()
|
||||||
@@ -96,7 +96,7 @@ impl<'a> DifferentialTestsDriver<'a, StepsIterator> {
|
|||||||
.collect();
|
.collect();
|
||||||
let steps_iterator: StepsIterator = steps.into_iter();
|
let steps_iterator: StepsIterator = steps.into_iter();
|
||||||
|
|
||||||
DifferentialTestsPlatformDriver::new(
|
PlatformDriver::new(
|
||||||
information,
|
information,
|
||||||
test_definition,
|
test_definition,
|
||||||
private_key_allocator,
|
private_key_allocator,
|
||||||
@@ -125,7 +125,7 @@ impl<'a> DifferentialTestsDriver<'a, StepsIterator> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The differential tests driver for a single platform.
|
/// The differential tests driver for a single platform.
|
||||||
pub struct DifferentialTestsPlatformDriver<'a, I> {
|
pub struct PlatformDriver<'a, I> {
|
||||||
/// The information of the platform that this driver is for.
|
/// The information of the platform that this driver is for.
|
||||||
platform_information: &'a TestPlatformInformation<'a>,
|
platform_information: &'a TestPlatformInformation<'a>,
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ pub struct DifferentialTestsPlatformDriver<'a, I> {
|
|||||||
steps_iterator: I,
|
steps_iterator: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I> DifferentialTestsPlatformDriver<'a, I>
|
impl<'a, I> PlatformDriver<'a, I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = (StepPath, Step)>,
|
I: Iterator<Item = (StepPath, Step)>,
|
||||||
{
|
{
|
||||||
@@ -164,7 +164,7 @@ where
|
|||||||
Self::init_execution_state(platform_information, test_definition, cached_compiler)
|
Self::init_execution_state(platform_information, test_definition, cached_compiler)
|
||||||
.await
|
.await
|
||||||
.context("Failed to initialize the execution state of the platform")?;
|
.context("Failed to initialize the execution state of the platform")?;
|
||||||
Ok(DifferentialTestsPlatformDriver {
|
Ok(PlatformDriver {
|
||||||
platform_information,
|
platform_information,
|
||||||
test_definition,
|
test_definition,
|
||||||
private_key_allocator,
|
private_key_allocator,
|
||||||
@@ -330,7 +330,14 @@ where
|
|||||||
)]
|
)]
|
||||||
pub async fn execute_next_step(&mut self) -> Option<Result<()>> {
|
pub async fn execute_next_step(&mut self) -> Option<Result<()>> {
|
||||||
let (step_path, step) = self.steps_iterator.next()?;
|
let (step_path, step) = self.steps_iterator.next()?;
|
||||||
Some(self.execute_step(&step_path, &step).await)
|
|
||||||
|
info!(%step_path, "Executing Step");
|
||||||
|
Some(
|
||||||
|
self.execute_step(&step_path, &step)
|
||||||
|
.await
|
||||||
|
.inspect(|_| info!(%step_path, "Step execution succeeded"))
|
||||||
|
.inspect_err(|err| error!(%step_path, ?err, "Step execution failed")),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(
|
||||||
@@ -455,7 +462,7 @@ where
|
|||||||
Method::Fallback | Method::FunctionName(_) => {
|
Method::Fallback | Method::FunctionName(_) => {
|
||||||
let resolver = self.platform_information.node.resolver().await?;
|
let resolver = self.platform_information.node.resolver().await?;
|
||||||
let tx = match step
|
let tx = match step
|
||||||
.legacy_transaction(resolver.as_ref(), self.default_resolution_context())
|
.as_transaction(resolver.as_ref(), self.default_resolution_context())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(tx) => tx,
|
Ok(tx) => tx,
|
||||||
@@ -803,7 +810,7 @@ where
|
|||||||
step: &RepeatStep,
|
step: &RepeatStep,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
let tasks = (0..step.repeat)
|
let tasks = (0..step.repeat)
|
||||||
.map(|_| DifferentialTestsPlatformDriver {
|
.map(|_| PlatformDriver {
|
||||||
platform_information: self.platform_information,
|
platform_information: self.platform_information,
|
||||||
test_definition: self.test_definition,
|
test_definition: self.test_definition,
|
||||||
private_key_allocator: self.private_key_allocator.clone(),
|
private_key_allocator: self.private_key_allocator.clone(),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use revive_dt_config::{Context, TestExecutionContext};
|
|||||||
use revive_dt_report::{Reporter, ReporterEvent, TestCaseStatus};
|
use revive_dt_report::{Reporter, ReporterEvent, TestCaseStatus};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
differential_tests::DifferentialTestsDriver,
|
differential_tests::Driver,
|
||||||
helpers::{CachedCompiler, NodePool, collect_metadata_files, create_test_definitions_stream},
|
helpers::{CachedCompiler, NodePool, collect_metadata_files, create_test_definitions_stream},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ pub async fn handle_differential_tests(
|
|||||||
for platform in platforms.iter() {
|
for platform in platforms.iter() {
|
||||||
let platform_identifier = platform.platform_identifier();
|
let platform_identifier = platform.platform_identifier();
|
||||||
|
|
||||||
let context = Context::ExecuteTests(Box::new(context.clone()));
|
let context = Context::Test(Box::new(context.clone()));
|
||||||
let node_pool = NodePool::new(context, *platform)
|
let node_pool = NodePool::new(context, *platform)
|
||||||
.await
|
.await
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
@@ -71,7 +71,7 @@ pub async fn handle_differential_tests(
|
|||||||
info!("Spawned the platform nodes");
|
info!("Spawned the platform nodes");
|
||||||
|
|
||||||
// Preparing test definitions.
|
// Preparing test definitions.
|
||||||
let full_context = Context::ExecuteTests(Box::new(context.clone()));
|
let full_context = Context::Test(Box::new(context.clone()));
|
||||||
let test_definitions = create_test_definitions_stream(
|
let test_definitions = create_test_definitions_stream(
|
||||||
&full_context,
|
&full_context,
|
||||||
metadata_files.iter(),
|
metadata_files.iter(),
|
||||||
@@ -112,23 +112,20 @@ pub async fn handle_differential_tests(
|
|||||||
mode = %mode
|
mode = %mode
|
||||||
);
|
);
|
||||||
async move {
|
async move {
|
||||||
let driver = match DifferentialTestsDriver::new_root(
|
let driver =
|
||||||
test_definition,
|
match Driver::new_root(test_definition, private_key_allocator, &cached_compiler)
|
||||||
private_key_allocator,
|
.await
|
||||||
&cached_compiler,
|
{
|
||||||
)
|
Ok(driver) => driver,
|
||||||
.await
|
Err(error) => {
|
||||||
{
|
test_definition
|
||||||
Ok(driver) => driver,
|
.reporter
|
||||||
Err(error) => {
|
.report_test_failed_event(format!("{error:#}"))
|
||||||
test_definition
|
.expect("Can't fail");
|
||||||
.reporter
|
error!("Test Case Failed");
|
||||||
.report_test_failed_event(format!("{error:#}"))
|
return;
|
||||||
.expect("Can't fail");
|
}
|
||||||
error!("Test Case Failed");
|
};
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!("Created the driver for the test case");
|
info!("Created the driver for the test case");
|
||||||
|
|
||||||
match driver.execute_all().await {
|
match driver.execute_all().await {
|
||||||
@@ -149,6 +146,7 @@ pub async fn handle_differential_tests(
|
|||||||
.instrument(span)
|
.instrument(span)
|
||||||
}))
|
}))
|
||||||
.inspect(|_| {
|
.inspect(|_| {
|
||||||
|
info!("Finished executing all test cases");
|
||||||
reporter_clone
|
reporter_clone
|
||||||
.report_completion_event()
|
.report_completion_event()
|
||||||
.expect("Can't fail")
|
.expect("Can't fail")
|
||||||
|
|||||||
@@ -1,900 +0,0 @@
|
|||||||
//! The test driver handles the compilation and execution of the test cases.
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use alloy::consensus::EMPTY_ROOT_HASH;
|
|
||||||
use alloy::hex;
|
|
||||||
use alloy::json_abi::JsonAbi;
|
|
||||||
use alloy::network::{Ethereum, TransactionBuilder};
|
|
||||||
use alloy::primitives::{TxHash, U256};
|
|
||||||
use alloy::rpc::types::TransactionReceipt;
|
|
||||||
use alloy::rpc::types::trace::geth::{
|
|
||||||
CallFrame, GethDebugBuiltInTracerType, GethDebugTracerConfig, GethDebugTracerType,
|
|
||||||
GethDebugTracingOptions, GethTrace, PreStateConfig,
|
|
||||||
};
|
|
||||||
use alloy::{
|
|
||||||
primitives::Address,
|
|
||||||
rpc::types::{TransactionRequest, trace::geth::DiffMode},
|
|
||||||
};
|
|
||||||
use anyhow::{Context as _, bail};
|
|
||||||
use futures::{TryStreamExt, future::try_join_all};
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use revive_dt_common::types::{PlatformIdentifier, PrivateKeyAllocator};
|
|
||||||
use revive_dt_format::traits::{ResolutionContext, ResolverApi};
|
|
||||||
use revive_dt_report::ExecutionSpecificReporter;
|
|
||||||
use semver::Version;
|
|
||||||
|
|
||||||
use revive_dt_format::case::Case;
|
|
||||||
use revive_dt_format::metadata::{ContractIdent, ContractInstance, ContractPathAndIdent};
|
|
||||||
use revive_dt_format::steps::{
|
|
||||||
BalanceAssertionStep, Calldata, EtherValue, Expected, ExpectedOutput, FunctionCallStep, Method,
|
|
||||||
StepIdx, StepPath, StorageEmptyAssertionStep,
|
|
||||||
};
|
|
||||||
use revive_dt_format::{metadata::Metadata, steps::Step};
|
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
|
||||||
use tokio::sync::Mutex;
|
|
||||||
use tokio::try_join;
|
|
||||||
use tracing::{Instrument, info, info_span, instrument};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct CaseState {
|
|
||||||
/// A map of all of the compiled contracts for the given metadata file.
|
|
||||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
|
||||||
|
|
||||||
/// This map stores the contracts deployments for this case.
|
|
||||||
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
|
||||||
|
|
||||||
/// This map stores the variables used for each one of the cases contained in the metadata
|
|
||||||
/// file.
|
|
||||||
variables: HashMap<String, U256>,
|
|
||||||
|
|
||||||
/// Stores the version used for the current case.
|
|
||||||
compiler_version: Version,
|
|
||||||
|
|
||||||
/// The execution reporter.
|
|
||||||
execution_reporter: ExecutionSpecificReporter,
|
|
||||||
|
|
||||||
/// The private key allocator used for this case state. This is an Arc Mutex to allow for the
|
|
||||||
/// state to be cloned and for all of the clones to refer to the same allocator.
|
|
||||||
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CaseState {
|
|
||||||
pub fn new(
|
|
||||||
compiler_version: Version,
|
|
||||||
compiled_contracts: HashMap<PathBuf, HashMap<String, (String, JsonAbi)>>,
|
|
||||||
deployed_contracts: HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>,
|
|
||||||
execution_reporter: ExecutionSpecificReporter,
|
|
||||||
private_key_allocator: Arc<Mutex<PrivateKeyAllocator>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
compiled_contracts,
|
|
||||||
deployed_contracts,
|
|
||||||
variables: Default::default(),
|
|
||||||
compiler_version,
|
|
||||||
execution_reporter,
|
|
||||||
private_key_allocator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_step(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
step: &Step,
|
|
||||||
step_path: &StepPath,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<StepOutput> {
|
|
||||||
match step {
|
|
||||||
Step::FunctionCall(input) => {
|
|
||||||
let (receipt, geth_trace, diff_mode) = self
|
|
||||||
.handle_input(metadata, input, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to handle function call step")?;
|
|
||||||
Ok(StepOutput::FunctionCall(receipt, geth_trace, diff_mode))
|
|
||||||
}
|
|
||||||
Step::BalanceAssertion(balance_assertion) => {
|
|
||||||
self.handle_balance_assertion(metadata, balance_assertion, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to handle balance assertion step")?;
|
|
||||||
Ok(StepOutput::BalanceAssertion)
|
|
||||||
}
|
|
||||||
Step::StorageEmptyAssertion(storage_empty) => {
|
|
||||||
self.handle_storage_empty(metadata, storage_empty, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to handle storage empty assertion step")?;
|
|
||||||
Ok(StepOutput::StorageEmptyAssertion)
|
|
||||||
}
|
|
||||||
Step::Repeat(repetition_step) => {
|
|
||||||
self.handle_repeat(
|
|
||||||
metadata,
|
|
||||||
repetition_step.repeat,
|
|
||||||
&repetition_step.steps,
|
|
||||||
step_path,
|
|
||||||
node,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.context("Failed to handle the repetition step")?;
|
|
||||||
Ok(StepOutput::Repetition)
|
|
||||||
}
|
|
||||||
Step::AllocateAccount(account_allocation) => {
|
|
||||||
self.handle_account_allocation(account_allocation.variable_name.as_str())
|
|
||||||
.await
|
|
||||||
.context("Failed to allocate account")?;
|
|
||||||
Ok(StepOutput::AccountAllocation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.inspect(|_| info!("Step Succeeded"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Handling Input", skip_all)]
|
|
||||||
pub async fn handle_input(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
input: &FunctionCallStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
|
|
||||||
let deployment_receipts = self
|
|
||||||
.handle_input_contract_deployment(metadata, input, node)
|
|
||||||
.await
|
|
||||||
.context("Failed during contract deployment phase of input handling")?;
|
|
||||||
let execution_receipt = self
|
|
||||||
.handle_input_execution(input, deployment_receipts, node)
|
|
||||||
.await
|
|
||||||
.context("Failed during transaction execution phase of input handling")?;
|
|
||||||
let tracing_result = self
|
|
||||||
.handle_input_call_frame_tracing(execution_receipt.transaction_hash, node)
|
|
||||||
.await
|
|
||||||
.context("Failed during callframe tracing phase of input handling")?;
|
|
||||||
self.handle_input_variable_assignment(input, &tracing_result)
|
|
||||||
.context("Failed to assign variables from callframe output")?;
|
|
||||||
let (_, (geth_trace, diff_mode)) = try_join!(
|
|
||||||
self.handle_input_expectations(
|
|
||||||
input,
|
|
||||||
&execution_receipt,
|
|
||||||
resolver.as_ref(),
|
|
||||||
&tracing_result
|
|
||||||
),
|
|
||||||
self.handle_input_diff(execution_receipt.transaction_hash, node)
|
|
||||||
)
|
|
||||||
.context("Failed while evaluating expectations and diffs in parallel")?;
|
|
||||||
Ok((execution_receipt, geth_trace, diff_mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Handling Balance Assertion", skip_all)]
|
|
||||||
pub async fn handle_balance_assertion(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
balance_assertion: &BalanceAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
self.handle_balance_assertion_contract_deployment(metadata, balance_assertion, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to deploy contract for balance assertion")?;
|
|
||||||
self.handle_balance_assertion_execution(balance_assertion, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to execute balance assertion")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Handling Storage Assertion", skip_all)]
|
|
||||||
pub async fn handle_storage_empty(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
storage_empty: &StorageEmptyAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
self.handle_storage_empty_assertion_contract_deployment(metadata, storage_empty, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to deploy contract for storage empty assertion")?;
|
|
||||||
self.handle_storage_empty_assertion_execution(storage_empty, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to execute storage empty assertion")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Handling Repetition", skip_all)]
|
|
||||||
pub async fn handle_repeat(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
repetitions: usize,
|
|
||||||
steps: &[Step],
|
|
||||||
step_path: &StepPath,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let tasks = (0..repetitions).map(|_| {
|
|
||||||
let mut state = self.clone();
|
|
||||||
async move {
|
|
||||||
for (step_idx, step) in steps.iter().enumerate() {
|
|
||||||
let step_path = step_path.append(step_idx);
|
|
||||||
state.handle_step(metadata, step, &step_path, node).await?;
|
|
||||||
}
|
|
||||||
Ok::<(), anyhow::Error>(())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try_join_all(tasks).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Handling Account Allocation", skip_all)]
|
|
||||||
pub async fn handle_account_allocation(&mut self, variable_name: &str) -> anyhow::Result<()> {
|
|
||||||
let Some(variable_name) = variable_name.strip_prefix("$VARIABLE:") else {
|
|
||||||
bail!("Account allocation must start with $VARIABLE:");
|
|
||||||
};
|
|
||||||
|
|
||||||
let private_key = self.private_key_allocator.lock().await.allocate()?;
|
|
||||||
let account = private_key.address();
|
|
||||||
let variable = U256::from_be_slice(account.0.as_slice());
|
|
||||||
|
|
||||||
self.variables.insert(variable_name.to_string(), variable);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles the contract deployment for a given input performing it if it needs to be performed.
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_contract_deployment(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
input: &FunctionCallStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<HashMap<ContractInstance, TransactionReceipt>> {
|
|
||||||
let mut instances_we_must_deploy = IndexMap::<ContractInstance, bool>::new();
|
|
||||||
for instance in input.find_all_contract_instances().into_iter() {
|
|
||||||
if !self.deployed_contracts.contains_key(&instance) {
|
|
||||||
instances_we_must_deploy.entry(instance).or_insert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Method::Deployer = input.method {
|
|
||||||
instances_we_must_deploy.swap_remove(&input.instance);
|
|
||||||
instances_we_must_deploy.insert(input.instance.clone(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut receipts = HashMap::new();
|
|
||||||
for (instance, deploy_with_constructor_arguments) in instances_we_must_deploy.into_iter() {
|
|
||||||
let calldata = deploy_with_constructor_arguments.then_some(&input.calldata);
|
|
||||||
let value = deploy_with_constructor_arguments
|
|
||||||
.then_some(input.value)
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
let caller = {
|
|
||||||
let context = self.default_resolution_context();
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
input
|
|
||||||
.caller
|
|
||||||
.resolve_address(resolver.as_ref(), context)
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
if let (_, _, Some(receipt)) = self
|
|
||||||
.get_or_deploy_contract_instance(&instance, metadata, caller, calldata, value, node)
|
|
||||||
.await
|
|
||||||
.context("Failed to get or deploy contract instance during input execution")?
|
|
||||||
{
|
|
||||||
receipts.insert(instance.clone(), receipt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(receipts)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles the execution of the input in terms of the calls that need to be made.
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_execution(
|
|
||||||
&mut self,
|
|
||||||
input: &FunctionCallStep,
|
|
||||||
mut deployment_receipts: HashMap<ContractInstance, TransactionReceipt>,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<TransactionReceipt> {
|
|
||||||
match input.method {
|
|
||||||
// This input was already executed when `handle_input` was called. We just need to
|
|
||||||
// lookup the transaction receipt in this case and continue on.
|
|
||||||
Method::Deployer => deployment_receipts
|
|
||||||
.remove(&input.instance)
|
|
||||||
.context("Failed to find deployment receipt for constructor call"),
|
|
||||||
Method::Fallback | Method::FunctionName(_) => {
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
let tx = match input
|
|
||||||
.legacy_transaction(resolver.as_ref(), self.default_resolution_context())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(tx) => tx,
|
|
||||||
Err(err) => {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match node.execute_transaction(tx).await {
|
|
||||||
Ok(receipt) => Ok(receipt),
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_call_frame_tracing(
|
|
||||||
&self,
|
|
||||||
tx_hash: TxHash,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<CallFrame> {
|
|
||||||
node.trace_transaction(
|
|
||||||
tx_hash,
|
|
||||||
GethDebugTracingOptions {
|
|
||||||
tracer: Some(GethDebugTracerType::BuiltInTracer(
|
|
||||||
GethDebugBuiltInTracerType::CallTracer,
|
|
||||||
)),
|
|
||||||
tracer_config: GethDebugTracerConfig(serde_json::json! {{
|
|
||||||
"onlyTopCall": true,
|
|
||||||
"withLog": false,
|
|
||||||
"withStorage": false,
|
|
||||||
"withMemory": false,
|
|
||||||
"withStack": false,
|
|
||||||
"withReturnData": true
|
|
||||||
}}),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map(|trace| {
|
|
||||||
trace
|
|
||||||
.try_into_call_frame()
|
|
||||||
.expect("Impossible - we requested a callframe trace so we must get it back")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
fn handle_input_variable_assignment(
|
|
||||||
&mut self,
|
|
||||||
input: &FunctionCallStep,
|
|
||||||
tracing_result: &CallFrame,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let Some(ref assignments) = input.variable_assignments else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handling the return data variable assignments.
|
|
||||||
for (variable_name, output_word) in assignments.return_data.iter().zip(
|
|
||||||
tracing_result
|
|
||||||
.output
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_vec()
|
|
||||||
.chunks(32),
|
|
||||||
) {
|
|
||||||
let value = U256::from_be_slice(output_word);
|
|
||||||
self.variables.insert(variable_name.clone(), value);
|
|
||||||
tracing::info!(
|
|
||||||
variable_name,
|
|
||||||
variable_value = hex::encode(value.to_be_bytes::<32>()),
|
|
||||||
"Assigned variable"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_expectations(
|
|
||||||
&self,
|
|
||||||
input: &FunctionCallStep,
|
|
||||||
execution_receipt: &TransactionReceipt,
|
|
||||||
resolver: &(impl ResolverApi + ?Sized),
|
|
||||||
tracing_result: &CallFrame,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
// Resolving the `input.expected` into a series of expectations that we can then assert on.
|
|
||||||
let mut expectations = match input {
|
|
||||||
FunctionCallStep {
|
|
||||||
expected: Some(Expected::Calldata(calldata)),
|
|
||||||
..
|
|
||||||
} => vec![ExpectedOutput::new().with_calldata(calldata.clone())],
|
|
||||||
FunctionCallStep {
|
|
||||||
expected: Some(Expected::Expected(expected)),
|
|
||||||
..
|
|
||||||
} => vec![expected.clone()],
|
|
||||||
FunctionCallStep {
|
|
||||||
expected: Some(Expected::ExpectedMany(expected)),
|
|
||||||
..
|
|
||||||
} => expected.clone(),
|
|
||||||
FunctionCallStep { expected: None, .. } => vec![ExpectedOutput::new().with_success()],
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is a bit of a special case and we have to support it separately on it's own. If it's
|
|
||||||
// a call to the deployer method, then the tests will assert that it "returns" the address
|
|
||||||
// of the contract. Deployments do not return the address of the contract but the runtime
|
|
||||||
// code of the contracts. Therefore, this assertion would always fail. So, we replace it
|
|
||||||
// with an assertion of "check if it succeeded"
|
|
||||||
if let Method::Deployer = &input.method {
|
|
||||||
for expectation in expectations.iter_mut() {
|
|
||||||
expectation.return_data = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
futures::stream::iter(expectations.into_iter().map(Ok))
|
|
||||||
.try_for_each_concurrent(None, |expectation| async move {
|
|
||||||
self.handle_input_expectation_item(
|
|
||||||
execution_receipt,
|
|
||||||
resolver,
|
|
||||||
expectation,
|
|
||||||
tracing_result,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_expectation_item(
|
|
||||||
&self,
|
|
||||||
execution_receipt: &TransactionReceipt,
|
|
||||||
resolver: &(impl ResolverApi + ?Sized),
|
|
||||||
expectation: ExpectedOutput,
|
|
||||||
tracing_result: &CallFrame,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if let Some(ref version_requirement) = expectation.compiler_version {
|
|
||||||
if !version_requirement.matches(&self.compiler_version) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolution_context = self
|
|
||||||
.default_resolution_context()
|
|
||||||
.with_block_number(execution_receipt.block_number.as_ref())
|
|
||||||
.with_transaction_hash(&execution_receipt.transaction_hash);
|
|
||||||
|
|
||||||
// Handling the receipt state assertion.
|
|
||||||
let expected = !expectation.exception;
|
|
||||||
let actual = execution_receipt.status();
|
|
||||||
if actual != expected {
|
|
||||||
tracing::error!(
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
?execution_receipt,
|
|
||||||
?tracing_result,
|
|
||||||
"Transaction status assertion failed"
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
|
||||||
"Transaction status assertion failed - Expected {expected} but got {actual}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling the calldata assertion
|
|
||||||
if let Some(ref expected_calldata) = expectation.return_data {
|
|
||||||
let expected = expected_calldata;
|
|
||||||
let actual = &tracing_result.output.as_ref().unwrap_or_default();
|
|
||||||
if !expected
|
|
||||||
.is_equivalent(actual, resolver, resolution_context)
|
|
||||||
.await
|
|
||||||
.context("Failed to resolve calldata equivalence for return data assertion")?
|
|
||||||
{
|
|
||||||
tracing::error!(
|
|
||||||
?execution_receipt,
|
|
||||||
?expected,
|
|
||||||
%actual,
|
|
||||||
"Calldata assertion failed"
|
|
||||||
);
|
|
||||||
anyhow::bail!("Calldata assertion failed - Expected {expected:?} but got {actual}",);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling the events assertion
|
|
||||||
if let Some(ref expected_events) = expectation.events {
|
|
||||||
// Handling the events length assertion.
|
|
||||||
let expected = expected_events.len();
|
|
||||||
let actual = execution_receipt.logs().len();
|
|
||||||
if actual != expected {
|
|
||||||
tracing::error!(expected, actual, "Event count assertion failed",);
|
|
||||||
anyhow::bail!(
|
|
||||||
"Event count assertion failed - Expected {expected} but got {actual}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling the events assertion.
|
|
||||||
for (event_idx, (expected_event, actual_event)) in expected_events
|
|
||||||
.iter()
|
|
||||||
.zip(execution_receipt.logs())
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
// Handling the emitter assertion.
|
|
||||||
if let Some(ref expected_address) = expected_event.address {
|
|
||||||
let expected = expected_address
|
|
||||||
.resolve_address(resolver, resolution_context)
|
|
||||||
.await?;
|
|
||||||
let actual = actual_event.address();
|
|
||||||
if actual != expected {
|
|
||||||
tracing::error!(
|
|
||||||
event_idx,
|
|
||||||
%expected,
|
|
||||||
%actual,
|
|
||||||
"Event emitter assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
|
||||||
"Event emitter assertion failed - Expected {expected} but got {actual}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling the topics assertion.
|
|
||||||
for (expected, actual) in expected_event
|
|
||||||
.topics
|
|
||||||
.as_slice()
|
|
||||||
.iter()
|
|
||||||
.zip(actual_event.topics())
|
|
||||||
{
|
|
||||||
let expected = Calldata::new_compound([expected]);
|
|
||||||
if !expected
|
|
||||||
.is_equivalent(&actual.0, resolver, resolution_context)
|
|
||||||
.await
|
|
||||||
.context("Failed to resolve event topic equivalence")?
|
|
||||||
{
|
|
||||||
tracing::error!(
|
|
||||||
event_idx,
|
|
||||||
?execution_receipt,
|
|
||||||
?expected,
|
|
||||||
?actual,
|
|
||||||
"Event topics assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
|
||||||
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling the values assertion.
|
|
||||||
let expected = &expected_event.values;
|
|
||||||
let actual = &actual_event.data().data;
|
|
||||||
if !expected
|
|
||||||
.is_equivalent(&actual.0, resolver, resolution_context)
|
|
||||||
.await
|
|
||||||
.context("Failed to resolve event value equivalence")?
|
|
||||||
{
|
|
||||||
tracing::error!(
|
|
||||||
event_idx,
|
|
||||||
?execution_receipt,
|
|
||||||
?expected,
|
|
||||||
?actual,
|
|
||||||
"Event value assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
|
||||||
"Event value assertion failed - Expected {expected:?} but got {actual:?}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
async fn handle_input_diff(
|
|
||||||
&self,
|
|
||||||
tx_hash: TxHash,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<(GethTrace, DiffMode)> {
|
|
||||||
let trace_options = GethDebugTracingOptions::prestate_tracer(PreStateConfig {
|
|
||||||
diff_mode: Some(true),
|
|
||||||
disable_code: None,
|
|
||||||
disable_storage: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let trace = node
|
|
||||||
.trace_transaction(tx_hash, trace_options)
|
|
||||||
.await
|
|
||||||
.context("Failed to obtain geth prestate tracer output")?;
|
|
||||||
let diff = node
|
|
||||||
.state_diff(tx_hash)
|
|
||||||
.await
|
|
||||||
.context("Failed to obtain state diff for transaction")?;
|
|
||||||
|
|
||||||
Ok((trace, diff))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
pub async fn handle_balance_assertion_contract_deployment(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
balance_assertion: &BalanceAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let Some(address) = balance_assertion.address.as_resolvable_address() else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let Some(instance) = address.strip_suffix(".address").map(ContractInstance::new) else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
self.get_or_deploy_contract_instance(
|
|
||||||
&instance,
|
|
||||||
metadata,
|
|
||||||
FunctionCallStep::default_caller_address(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
node,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
pub async fn handle_balance_assertion_execution(
|
|
||||||
&mut self,
|
|
||||||
BalanceAssertionStep {
|
|
||||||
address,
|
|
||||||
expected_balance: amount,
|
|
||||||
..
|
|
||||||
}: &BalanceAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
let address = address
|
|
||||||
.resolve_address(resolver.as_ref(), self.default_resolution_context())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let balance = node.balance_of(address).await?;
|
|
||||||
|
|
||||||
let expected = *amount;
|
|
||||||
let actual = balance;
|
|
||||||
if expected != actual {
|
|
||||||
tracing::error!(%expected, %actual, %address, "Balance assertion failed");
|
|
||||||
anyhow::bail!(
|
|
||||||
"Balance assertion failed - Expected {} but got {} for {} resolved to {}",
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
address,
|
|
||||||
address,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
pub async fn handle_storage_empty_assertion_contract_deployment(
|
|
||||||
&mut self,
|
|
||||||
metadata: &Metadata,
|
|
||||||
storage_empty_assertion: &StorageEmptyAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let Some(address) = storage_empty_assertion.address.as_resolvable_address() else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let Some(instance) = address.strip_suffix(".address").map(ContractInstance::new) else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
|
|
||||||
self.get_or_deploy_contract_instance(
|
|
||||||
&instance,
|
|
||||||
metadata,
|
|
||||||
FunctionCallStep::default_caller_address(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
node,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
|
||||||
pub async fn handle_storage_empty_assertion_execution(
|
|
||||||
&mut self,
|
|
||||||
StorageEmptyAssertionStep {
|
|
||||||
address,
|
|
||||||
is_storage_empty,
|
|
||||||
..
|
|
||||||
}: &StorageEmptyAssertionStep,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
let address = address
|
|
||||||
.resolve_address(resolver.as_ref(), self.default_resolution_context())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let storage = node.latest_state_proof(address, Default::default()).await?;
|
|
||||||
let is_empty = storage.storage_hash == EMPTY_ROOT_HASH;
|
|
||||||
|
|
||||||
let expected = is_storage_empty;
|
|
||||||
let actual = is_empty;
|
|
||||||
|
|
||||||
if *expected != actual {
|
|
||||||
tracing::error!(%expected, %actual, %address, "Storage Empty Assertion failed");
|
|
||||||
anyhow::bail!(
|
|
||||||
"Storage Empty Assertion failed - Expected {} but got {} for {} resolved to {}",
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
address,
|
|
||||||
address,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the information of a deployed contract or library from the state. If it's found to not
|
|
||||||
/// be deployed then it will be deployed.
|
|
||||||
///
|
|
||||||
/// If a [`CaseIdx`] is not specified then this contact instance address will be stored in the
|
|
||||||
/// cross-case deployed contracts address mapping.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub async fn get_or_deploy_contract_instance(
|
|
||||||
&mut self,
|
|
||||||
contract_instance: &ContractInstance,
|
|
||||||
metadata: &Metadata,
|
|
||||||
deployer: Address,
|
|
||||||
calldata: Option<&Calldata>,
|
|
||||||
value: Option<EtherValue>,
|
|
||||||
node: &dyn EthereumNode,
|
|
||||||
) -> anyhow::Result<(Address, JsonAbi, Option<TransactionReceipt>)> {
|
|
||||||
if let Some((_, address, abi)) = self.deployed_contracts.get(contract_instance) {
|
|
||||||
return Ok((*address, abi.clone(), None));
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(ContractPathAndIdent {
|
|
||||||
contract_source_path,
|
|
||||||
contract_ident,
|
|
||||||
}) = metadata.contract_sources()?.remove(contract_instance)
|
|
||||||
else {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Contract source not found for instance {:?}",
|
|
||||||
contract_instance
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some((code, abi)) = self
|
|
||||||
.compiled_contracts
|
|
||||||
.get(&contract_source_path)
|
|
||||||
.and_then(|source_file_contracts| source_file_contracts.get(contract_ident.as_ref()))
|
|
||||||
.cloned()
|
|
||||||
else {
|
|
||||||
anyhow::bail!(
|
|
||||||
"Failed to find information for contract {:?}",
|
|
||||||
contract_instance
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut code = match alloy::hex::decode(&code) {
|
|
||||||
Ok(code) => code,
|
|
||||||
Err(error) => {
|
|
||||||
tracing::error!(
|
|
||||||
?error,
|
|
||||||
contract_source_path = contract_source_path.display().to_string(),
|
|
||||||
contract_ident = contract_ident.as_ref(),
|
|
||||||
"Failed to hex-decode byte code - This could possibly mean that the bytecode requires linking"
|
|
||||||
);
|
|
||||||
anyhow::bail!("Failed to hex-decode the byte code {}", error)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(calldata) = calldata {
|
|
||||||
let resolver = node.resolver().await?;
|
|
||||||
let calldata = calldata
|
|
||||||
.calldata(resolver.as_ref(), self.default_resolution_context())
|
|
||||||
.await?;
|
|
||||||
code.extend(calldata);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tx = {
|
|
||||||
let tx = TransactionRequest::default().from(deployer);
|
|
||||||
let tx = match value {
|
|
||||||
Some(ref value) => tx.value(value.into_inner()),
|
|
||||||
_ => tx,
|
|
||||||
};
|
|
||||||
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
|
||||||
};
|
|
||||||
|
|
||||||
let receipt = match node.execute_transaction(tx).await {
|
|
||||||
Ok(receipt) => receipt,
|
|
||||||
Err(error) => {
|
|
||||||
tracing::error!(?error, "Contract deployment transaction failed.");
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(address) = receipt.contract_address else {
|
|
||||||
anyhow::bail!("Contract deployment didn't return an address");
|
|
||||||
};
|
|
||||||
tracing::info!(
|
|
||||||
instance_name = ?contract_instance,
|
|
||||||
instance_address = ?address,
|
|
||||||
"Deployed contract"
|
|
||||||
);
|
|
||||||
self.execution_reporter
|
|
||||||
.report_contract_deployed_event(contract_instance.clone(), address)?;
|
|
||||||
|
|
||||||
self.deployed_contracts.insert(
|
|
||||||
contract_instance.clone(),
|
|
||||||
(contract_ident, address, abi.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((address, abi, Some(receipt)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_resolution_context(&self) -> ResolutionContext<'_> {
|
|
||||||
ResolutionContext::default()
|
|
||||||
.with_deployed_contracts(&self.deployed_contracts)
|
|
||||||
.with_variables(&self.variables)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CaseDriver<'a> {
|
|
||||||
metadata: &'a Metadata,
|
|
||||||
case: &'a Case,
|
|
||||||
platform_state: Vec<(&'a dyn EthereumNode, PlatformIdentifier, CaseState)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> CaseDriver<'a> {
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn new(
|
|
||||||
metadata: &'a Metadata,
|
|
||||||
case: &'a Case,
|
|
||||||
platform_state: Vec<(&'a dyn EthereumNode, PlatformIdentifier, CaseState)>,
|
|
||||||
) -> CaseDriver<'a> {
|
|
||||||
Self {
|
|
||||||
metadata,
|
|
||||||
case,
|
|
||||||
platform_state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", name = "Executing Case", skip_all)]
|
|
||||||
pub async fn execute(&mut self) -> anyhow::Result<usize> {
|
|
||||||
let mut steps_executed = 0;
|
|
||||||
for (step_idx, step) in self
|
|
||||||
.case
|
|
||||||
.steps_iterator()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, v)| (StepIdx::new(idx), v))
|
|
||||||
{
|
|
||||||
let metadata = self.metadata;
|
|
||||||
let step_futures =
|
|
||||||
self.platform_state
|
|
||||||
.iter_mut()
|
|
||||||
.map(|(node, platform_id, case_state)| {
|
|
||||||
let platform_id = *platform_id;
|
|
||||||
let node_ref = *node;
|
|
||||||
let step = step.clone();
|
|
||||||
let span = info_span!(
|
|
||||||
"Handling Step",
|
|
||||||
%step_idx,
|
|
||||||
platform = %platform_id,
|
|
||||||
);
|
|
||||||
async move {
|
|
||||||
let step_path = StepPath::from_iterator([step_idx]);
|
|
||||||
case_state
|
|
||||||
.handle_step(metadata, &step, &step_path, node_ref)
|
|
||||||
.await
|
|
||||||
.map_err(|e| (platform_id, e))
|
|
||||||
}
|
|
||||||
.instrument(span)
|
|
||||||
});
|
|
||||||
|
|
||||||
match try_join_all(step_futures).await {
|
|
||||||
Ok(_outputs) => {
|
|
||||||
steps_executed += 1;
|
|
||||||
}
|
|
||||||
Err((platform_id, error)) => {
|
|
||||||
tracing::error!(
|
|
||||||
%step_idx,
|
|
||||||
platform = %platform_id,
|
|
||||||
?error,
|
|
||||||
"Step failed on platform",
|
|
||||||
);
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(steps_executed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
pub enum StepOutput {
|
|
||||||
FunctionCall(TransactionReceipt, GethTrace, DiffMode),
|
|
||||||
BalanceAssertion,
|
|
||||||
StorageEmptyAssertion,
|
|
||||||
Repetition,
|
|
||||||
AccountAllocation,
|
|
||||||
}
|
|
||||||
@@ -19,8 +19,6 @@ use revive_dt_node::{
|
|||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
pub mod driver;
|
|
||||||
|
|
||||||
/// A trait that describes the interface for the platforms that are supported by the tool.
|
/// A trait that describes the interface for the platforms that are supported by the tool.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub trait Platform {
|
pub trait Platform {
|
||||||
|
|||||||
+23
-2
@@ -1,3 +1,4 @@
|
|||||||
|
mod differential_benchmarks;
|
||||||
mod differential_tests;
|
mod differential_tests;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
@@ -11,7 +12,10 @@ use revive_dt_config::Context;
|
|||||||
use revive_dt_core::Platform;
|
use revive_dt_core::Platform;
|
||||||
use revive_dt_format::metadata::Metadata;
|
use revive_dt_format::metadata::Metadata;
|
||||||
|
|
||||||
use crate::differential_tests::handle_differential_tests;
|
use crate::{
|
||||||
|
differential_benchmarks::handle_differential_benchmarks,
|
||||||
|
differential_tests::handle_differential_tests,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let (writer, _guard) = tracing_appender::non_blocking::NonBlockingBuilder::default()
|
let (writer, _guard) = tracing_appender::non_blocking::NonBlockingBuilder::default()
|
||||||
@@ -37,7 +41,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let (reporter, report_aggregator_task) = ReportAggregator::new(context.clone()).into_task();
|
let (reporter, report_aggregator_task) = ReportAggregator::new(context.clone()).into_task();
|
||||||
|
|
||||||
match context {
|
match context {
|
||||||
Context::ExecuteTests(context) => tokio::runtime::Builder::new_multi_thread()
|
Context::Test(context) => tokio::runtime::Builder::new_multi_thread()
|
||||||
.worker_threads(context.concurrency_configuration.number_of_threads)
|
.worker_threads(context.concurrency_configuration.number_of_threads)
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
@@ -49,6 +53,23 @@ fn main() -> anyhow::Result<()> {
|
|||||||
futures::future::try_join(differential_tests_handling_task, report_aggregator_task)
|
futures::future::try_join(differential_tests_handling_task, report_aggregator_task)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
Context::Benchmark(context) => tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.worker_threads(context.concurrency_configuration.number_of_threads)
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.expect("Failed building the Runtime")
|
||||||
|
.block_on(async move {
|
||||||
|
let differential_benchmarks_handling_task =
|
||||||
|
handle_differential_benchmarks(*context, reporter);
|
||||||
|
|
||||||
|
futures::future::try_join(
|
||||||
|
differential_benchmarks_handling_task,
|
||||||
|
report_aggregator_task,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
Context::ExportJsonSchema => {
|
Context::ExportJsonSchema => {
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ revive-dt-common = { workspace = true }
|
|||||||
revive-common = { workspace = true }
|
revive-common = { workspace = true }
|
||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
alloy-primitives = { workspace = true }
|
|
||||||
alloy-sol-types = { workspace = true }
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::{collections::HashMap, fmt::Display, str::FromStr};
|
use std::{collections::HashMap, fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
use alloy::primitives::{FixedBytes, utils::parse_units};
|
||||||
use alloy::{
|
use alloy::{
|
||||||
eips::BlockNumberOrTag,
|
eips::BlockNumberOrTag,
|
||||||
json_abi::Function,
|
json_abi::Function,
|
||||||
@@ -7,7 +8,6 @@ use alloy::{
|
|||||||
primitives::{Address, Bytes, U256},
|
primitives::{Address, Bytes, U256},
|
||||||
rpc::types::TransactionRequest,
|
rpc::types::TransactionRequest,
|
||||||
};
|
};
|
||||||
use alloy_primitives::{FixedBytes, utils::parse_units};
|
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, stream};
|
use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, stream};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
@@ -537,7 +537,7 @@ impl FunctionCallStep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse this input into a legacy transaction.
|
/// Parse this input into a legacy transaction.
|
||||||
pub async fn legacy_transaction(
|
pub async fn as_transaction(
|
||||||
&self,
|
&self,
|
||||||
resolver: &(impl ResolverApi + ?Sized),
|
resolver: &(impl ResolverApi + ?Sized),
|
||||||
context: ResolutionContext<'_>,
|
context: ResolutionContext<'_>,
|
||||||
@@ -959,9 +959,9 @@ impl<'de> Deserialize<'de> for EtherValue {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
use alloy::primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address};
|
||||||
|
use alloy::sol_types::SolValue;
|
||||||
use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi};
|
use alloy::{eips::BlockNumberOrTag, json_abi::JsonAbi};
|
||||||
use alloy_primitives::{BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, address};
|
|
||||||
use alloy_sol_types::SolValue;
|
|
||||||
use std::{collections::HashMap, pin::Pin};
|
use std::{collections::HashMap, pin::Pin};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -1115,7 +1115,7 @@ mod tests {
|
|||||||
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
||||||
assert!(encoded.0.starts_with(&selector));
|
assert!(encoded.0.starts_with(&selector));
|
||||||
|
|
||||||
type T = (alloy_primitives::Address,);
|
type T = (alloy::primitives::Address,);
|
||||||
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
decoded.0,
|
decoded.0,
|
||||||
@@ -1162,7 +1162,7 @@ mod tests {
|
|||||||
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
let encoded = input.encoded_input(&resolver, context).await.unwrap();
|
||||||
assert!(encoded.0.starts_with(&selector));
|
assert!(encoded.0.starts_with(&selector));
|
||||||
|
|
||||||
type T = (alloy_primitives::Address,);
|
type T = (alloy::primitives::Address,);
|
||||||
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
let decoded: T = T::abi_decode(&encoded.0[4..]).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
decoded.0,
|
decoded.0,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use std::pin::Pin;
|
|||||||
|
|
||||||
use alloy::eips::BlockNumberOrTag;
|
use alloy::eips::BlockNumberOrTag;
|
||||||
use alloy::json_abi::JsonAbi;
|
use alloy::json_abi::JsonAbi;
|
||||||
|
use alloy::primitives::TxHash;
|
||||||
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256};
|
use alloy::primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, U256};
|
||||||
use alloy_primitives::TxHash;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::metadata::{ContractIdent, ContractInstance};
|
use crate::metadata::{ContractIdent, ContractInstance};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ revive-dt-format = { workspace = true }
|
|||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
futures = { workspace = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use alloy::primitives::{Address, StorageKey, TxHash, U256};
|
use alloy::primitives::{Address, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256};
|
||||||
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
|
use alloy::rpc::types::trace::geth::{DiffMode, GethDebugTracingOptions, GethTrace};
|
||||||
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest};
|
use alloy::rpc::types::{EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use futures::Stream;
|
||||||
use revive_common::EVMVersion;
|
use revive_common::EVMVersion;
|
||||||
use revive_dt_format::traits::ResolverApi;
|
use revive_dt_format::traits::ResolverApi;
|
||||||
|
|
||||||
@@ -22,6 +23,16 @@ pub trait EthereumNode {
|
|||||||
/// Returns the nodes connection string.
|
/// Returns the nodes connection string.
|
||||||
fn connection_string(&self) -> &str;
|
fn connection_string(&self) -> &str;
|
||||||
|
|
||||||
|
fn submit_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: TransactionRequest,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<TxHash>> + '_>>;
|
||||||
|
|
||||||
|
fn get_receipt(
|
||||||
|
&self,
|
||||||
|
tx_hash: TxHash,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<TransactionReceipt>> + '_>>;
|
||||||
|
|
||||||
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
|
/// Execute the [TransactionRequest] and return a [TransactionReceipt].
|
||||||
fn execute_transaction(
|
fn execute_transaction(
|
||||||
&self,
|
&self,
|
||||||
@@ -53,4 +64,32 @@ pub trait EthereumNode {
|
|||||||
|
|
||||||
/// Returns the EVM version of the node.
|
/// Returns the EVM version of the node.
|
||||||
fn evm_version(&self) -> EVMVersion;
|
fn evm_version(&self) -> EVMVersion;
|
||||||
|
|
||||||
|
/// Returns a stream of the blocks that were mined by the node.
|
||||||
|
fn subscribe_to_full_blocks_information(
|
||||||
|
&self,
|
||||||
|
) -> Pin<
|
||||||
|
Box<
|
||||||
|
dyn Future<Output = anyhow::Result<Pin<Box<dyn Stream<Item = MinedBlockInformation>>>>>
|
||||||
|
+ '_,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct MinedBlockInformation {
|
||||||
|
/// The block number.
|
||||||
|
pub block_number: BlockNumber,
|
||||||
|
|
||||||
|
/// The block timestamp.
|
||||||
|
pub block_timestamp: BlockTimestamp,
|
||||||
|
|
||||||
|
/// The amount of gas mined in the block.
|
||||||
|
pub mined_gas: u128,
|
||||||
|
|
||||||
|
/// The gas limit of the block.
|
||||||
|
pub block_gas_limit: u128,
|
||||||
|
|
||||||
|
/// The hashes of the transactions that were mined as part of the block.
|
||||||
|
pub transaction_hashes: Vec<TxHash>,
|
||||||
}
|
}
|
||||||
|
|||||||
+143
-37
@@ -20,18 +20,25 @@ use alloy::{
|
|||||||
network::{Ethereum, EthereumWallet, NetworkWallet},
|
network::{Ethereum, EthereumWallet, NetworkWallet},
|
||||||
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256},
|
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256},
|
||||||
providers::{
|
providers::{
|
||||||
Provider, ProviderBuilder,
|
Identity, Provider, ProviderBuilder, RootProvider,
|
||||||
ext::DebugApi,
|
ext::DebugApi,
|
||||||
fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller},
|
fillers::{
|
||||||
|
CachedNonceManager, ChainIdFiller, FillProvider, JoinFill, NonceFiller, TxFiller,
|
||||||
|
WalletFiller,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rpc::types::{
|
rpc::types::{
|
||||||
EIP1186AccountProofResponse, TransactionRequest,
|
EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest,
|
||||||
trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame},
|
trace::geth::{
|
||||||
|
DiffMode, GethDebugTracingOptions, GethTrace, PreStateConfig, PreStateFrame,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
use revive_common::EVMVersion;
|
use revive_common::EVMVersion;
|
||||||
use tracing::{Instrument, instrument};
|
use tokio::sync::OnceCell;
|
||||||
|
use tracing::{Instrument, error, instrument};
|
||||||
|
|
||||||
use revive_dt_common::{
|
use revive_dt_common::{
|
||||||
fs::clear_directory,
|
fs::clear_directory,
|
||||||
@@ -39,7 +46,7 @@ use revive_dt_common::{
|
|||||||
};
|
};
|
||||||
use revive_dt_config::*;
|
use revive_dt_config::*;
|
||||||
use revive_dt_format::traits::ResolverApi;
|
use revive_dt_format::traits::ResolverApi;
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::{EthereumNode, MinedBlockInformation};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Node,
|
Node,
|
||||||
@@ -71,6 +78,18 @@ pub struct GethNode {
|
|||||||
wallet: Arc<EthereumWallet>,
|
wallet: Arc<EthereumWallet>,
|
||||||
nonce_manager: CachedNonceManager,
|
nonce_manager: CachedNonceManager,
|
||||||
chain_id_filler: ChainIdFiller,
|
chain_id_filler: ChainIdFiller,
|
||||||
|
provider: OnceCell<
|
||||||
|
FillProvider<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
|
NonceFiller,
|
||||||
|
>,
|
||||||
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GethNode {
|
impl GethNode {
|
||||||
@@ -121,6 +140,7 @@ impl GethNode {
|
|||||||
wallet: wallet.clone(),
|
wallet: wallet.clone(),
|
||||||
chain_id_filler: Default::default(),
|
chain_id_filler: Default::default(),
|
||||||
nonce_manager: Default::default(),
|
nonce_manager: Default::default(),
|
||||||
|
provider: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +255,7 @@ impl GethNode {
|
|||||||
match process {
|
match process {
|
||||||
Ok(process) => self.handle = Some(process),
|
Ok(process) => self.handle = Some(process),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::error!(?err, "Failed to start geth, shutting down gracefully");
|
error!(?err, "Failed to start geth, shutting down gracefully");
|
||||||
self.shutdown()
|
self.shutdown()
|
||||||
.context("Failed to gracefully shutdown after geth start error")?;
|
.context("Failed to gracefully shutdown after geth start error")?;
|
||||||
return Err(err);
|
return Err(err);
|
||||||
@@ -247,21 +267,27 @@ impl GethNode {
|
|||||||
|
|
||||||
async fn provider(
|
async fn provider(
|
||||||
&self,
|
&self,
|
||||||
) -> anyhow::Result<FillProvider<impl TxFiller<Ethereum>, impl Provider<Ethereum>, Ethereum>>
|
) -> anyhow::Result<
|
||||||
{
|
FillProvider<impl TxFiller<Ethereum>, impl Provider<Ethereum> + Clone, Ethereum>,
|
||||||
ProviderBuilder::new()
|
> {
|
||||||
.disable_recommended_fillers()
|
self.provider
|
||||||
.filler(FallbackGasFiller::new(
|
.get_or_try_init(|| async move {
|
||||||
25_000_000,
|
ProviderBuilder::new()
|
||||||
1_000_000_000,
|
.disable_recommended_fillers()
|
||||||
1_000_000_000,
|
.filler(FallbackGasFiller::new(
|
||||||
))
|
25_000_000,
|
||||||
.filler(self.chain_id_filler.clone())
|
1_000_000_000,
|
||||||
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
1_000_000_000,
|
||||||
.wallet(self.wallet.clone())
|
))
|
||||||
.connect(&self.connection_string)
|
.filler(self.chain_id_filler.clone())
|
||||||
|
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
||||||
|
.wallet(self.wallet.clone())
|
||||||
|
.connect(&self.connection_string)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +304,50 @@ impl EthereumNode for GethNode {
|
|||||||
&self.connection_string
|
&self.connection_string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(geth_node_id = self.id, connection_string = self.connection_string),
|
||||||
|
err,
|
||||||
|
)]
|
||||||
|
fn submit_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: TransactionRequest,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TxHash>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self
|
||||||
|
.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the provider for transaction submission")?;
|
||||||
|
let pending_transaction = provider
|
||||||
|
.send_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.context("Failed to submit the transaction through the provider")?;
|
||||||
|
Ok(*pending_transaction.tx_hash())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(geth_node_id = self.id, connection_string = self.connection_string),
|
||||||
|
err,
|
||||||
|
)]
|
||||||
|
fn get_receipt(
|
||||||
|
&self,
|
||||||
|
tx_hash: TxHash,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create provider for getting the receipt")?
|
||||||
|
.get_transaction_receipt(tx_hash)
|
||||||
|
.await
|
||||||
|
.context("Failed to get the receipt of the transaction")?
|
||||||
|
.context("Failed to get the receipt of the transaction")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(
|
||||||
level = "info",
|
level = "info",
|
||||||
skip_all,
|
skip_all,
|
||||||
@@ -287,8 +357,7 @@ impl EthereumNode for GethNode {
|
|||||||
fn execute_transaction(
|
fn execute_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: TransactionRequest,
|
transaction: TransactionRequest,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::TransactionReceipt>> + '_>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let provider = self
|
let provider = self
|
||||||
.provider()
|
.provider()
|
||||||
@@ -296,12 +365,12 @@ impl EthereumNode for GethNode {
|
|||||||
.context("Failed to create provider for transaction submission")?;
|
.context("Failed to create provider for transaction submission")?;
|
||||||
|
|
||||||
let pending_transaction = provider
|
let pending_transaction = provider
|
||||||
.send_transaction(transaction)
|
.send_transaction(transaction)
|
||||||
.await
|
.await
|
||||||
.inspect_err(
|
.inspect_err(
|
||||||
|err| tracing::error!(%err, "Encountered an error when submitting the transaction"),
|
|err| error!(%err, "Encountered an error when submitting the transaction"),
|
||||||
)
|
)
|
||||||
.context("Failed to submit transaction to geth node")?;
|
.context("Failed to submit transaction to geth node")?;
|
||||||
let transaction_hash = *pending_transaction.tx_hash();
|
let transaction_hash = *pending_transaction.tx_hash();
|
||||||
|
|
||||||
// The following is a fix for the "transaction indexing is in progress" error that we used
|
// The following is a fix for the "transaction indexing is in progress" error that we used
|
||||||
@@ -321,7 +390,6 @@ impl EthereumNode for GethNode {
|
|||||||
// allowed for 60 seconds of waiting with a 1 second delay in polling, we need to allow for
|
// allowed for 60 seconds of waiting with a 1 second delay in polling, we need to allow for
|
||||||
// a larger wait time. Therefore, in here we allow for 5 minutes of waiting with exponential
|
// a larger wait time. Therefore, in here we allow for 5 minutes of waiting with exponential
|
||||||
// backoff each time we attempt to get the receipt and find that it's not available.
|
// backoff each time we attempt to get the receipt and find that it's not available.
|
||||||
let provider = Arc::new(provider);
|
|
||||||
poll(
|
poll(
|
||||||
Self::RECEIPT_POLLING_DURATION,
|
Self::RECEIPT_POLLING_DURATION,
|
||||||
PollingWaitBehavior::Constant(Duration::from_millis(200)),
|
PollingWaitBehavior::Constant(Duration::from_millis(200)),
|
||||||
@@ -355,14 +423,12 @@ impl EthereumNode for GethNode {
|
|||||||
&self,
|
&self,
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
trace_options: GethDebugTracingOptions,
|
trace_options: GethDebugTracingOptions,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::trace::geth::GethTrace>> + '_>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let provider = Arc::new(
|
let provider = self
|
||||||
self.provider()
|
.provider()
|
||||||
.await
|
.await
|
||||||
.context("Failed to create provider for tracing")?,
|
.context("Failed to create provider for tracing")?;
|
||||||
);
|
|
||||||
poll(
|
poll(
|
||||||
Self::TRACE_POLLING_DURATION,
|
Self::TRACE_POLLING_DURATION,
|
||||||
PollingWaitBehavior::Constant(Duration::from_millis(200)),
|
PollingWaitBehavior::Constant(Duration::from_millis(200)),
|
||||||
@@ -460,6 +526,46 @@ impl EthereumNode for GethNode {
|
|||||||
fn evm_version(&self) -> EVMVersion {
|
fn evm_version(&self) -> EVMVersion {
|
||||||
EVMVersion::Cancun
|
EVMVersion::Cancun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn subscribe_to_full_blocks_information(
|
||||||
|
&self,
|
||||||
|
) -> Pin<
|
||||||
|
Box<
|
||||||
|
dyn Future<Output = anyhow::Result<Pin<Box<dyn Stream<Item = MinedBlockInformation>>>>>
|
||||||
|
+ '_,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self
|
||||||
|
.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the provider for block subscription")?;
|
||||||
|
let block_subscription = provider.subscribe_full_blocks();
|
||||||
|
let block_stream = block_subscription
|
||||||
|
.into_stream()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the block stream")?;
|
||||||
|
|
||||||
|
let mined_block_information_stream = block_stream.filter_map(|block| async {
|
||||||
|
let block = block.ok()?;
|
||||||
|
Some(MinedBlockInformation {
|
||||||
|
block_number: block.number(),
|
||||||
|
block_timestamp: block.header.timestamp,
|
||||||
|
mined_gas: block.header.gas_used as _,
|
||||||
|
block_gas_limit: block.header.gas_limit as _,
|
||||||
|
transaction_hashes: block
|
||||||
|
.transactions
|
||||||
|
.into_hashes()
|
||||||
|
.as_hashes()
|
||||||
|
.expect("Must be hashes")
|
||||||
|
.to_vec(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Box::pin(mined_block_information_stream)
|
||||||
|
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
pub struct GethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
||||||
|
|||||||
@@ -24,29 +24,36 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
eips::{BlockId, BlockNumberOrTag},
|
eips::BlockNumberOrTag,
|
||||||
genesis::{Genesis, GenesisAccount},
|
genesis::{Genesis, GenesisAccount},
|
||||||
network::{Ethereum, EthereumWallet, NetworkWallet},
|
network::{Ethereum, EthereumWallet, NetworkWallet},
|
||||||
primitives::{
|
primitives::{
|
||||||
Address, BlockHash, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256, address,
|
Address, BlockHash, BlockNumber, BlockTimestamp, StorageKey, TxHash, U256, address,
|
||||||
},
|
},
|
||||||
providers::{
|
providers::{
|
||||||
Provider, ProviderBuilder,
|
Identity, Provider, ProviderBuilder, RootProvider,
|
||||||
ext::DebugApi,
|
ext::DebugApi,
|
||||||
fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller},
|
fillers::{
|
||||||
|
CachedNonceManager, ChainIdFiller, FillProvider, JoinFill, NonceFiller, TxFiller,
|
||||||
|
WalletFiller,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rpc::{
|
rpc::{
|
||||||
client::{BuiltInConnectionString, ClientBuilder, RpcClient},
|
client::{BuiltInConnectionString, ClientBuilder, RpcClient},
|
||||||
types::{
|
types::{
|
||||||
EIP1186AccountProofResponse, TransactionRequest,
|
EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest,
|
||||||
trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame},
|
trace::geth::{
|
||||||
|
DiffMode, GethDebugTracingOptions, GethTrace, PreStateConfig, PreStateFrame,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
use revive_common::EVMVersion;
|
use revive_common::EVMVersion;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
use tracing::{Instrument, info, instrument};
|
use tracing::{Instrument, info, instrument};
|
||||||
|
|
||||||
use revive_dt_common::{
|
use revive_dt_common::{
|
||||||
@@ -55,7 +62,7 @@ use revive_dt_common::{
|
|||||||
};
|
};
|
||||||
use revive_dt_config::*;
|
use revive_dt_config::*;
|
||||||
use revive_dt_format::traits::ResolverApi;
|
use revive_dt_format::traits::ResolverApi;
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::{EthereumNode, MinedBlockInformation};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Node,
|
Node,
|
||||||
@@ -101,7 +108,31 @@ pub struct LighthouseGethNode {
|
|||||||
/* Provider Related Fields */
|
/* Provider Related Fields */
|
||||||
wallet: Arc<EthereumWallet>,
|
wallet: Arc<EthereumWallet>,
|
||||||
nonce_manager: CachedNonceManager,
|
nonce_manager: CachedNonceManager,
|
||||||
chain_id_filler: ChainIdFiller,
|
|
||||||
|
persistent_http_provider: OnceCell<
|
||||||
|
FillProvider<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
|
NonceFiller,
|
||||||
|
>,
|
||||||
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
persistent_ws_provider: OnceCell<
|
||||||
|
FillProvider<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
|
NonceFiller,
|
||||||
|
>,
|
||||||
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LighthouseGethNode {
|
impl LighthouseGethNode {
|
||||||
@@ -170,7 +201,8 @@ impl LighthouseGethNode {
|
|||||||
/* Provider Related Fields */
|
/* Provider Related Fields */
|
||||||
wallet: wallet.clone(),
|
wallet: wallet.clone(),
|
||||||
nonce_manager: Default::default(),
|
nonce_manager: Default::default(),
|
||||||
chain_id_filler: Default::default(),
|
persistent_http_provider: OnceCell::const_new(),
|
||||||
|
persistent_ws_provider: OnceCell::const_new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +237,8 @@ impl LighthouseGethNode {
|
|||||||
execution_layer_extra_parameters: vec![
|
execution_layer_extra_parameters: vec![
|
||||||
"--nodiscover".to_string(),
|
"--nodiscover".to_string(),
|
||||||
"--cache=4096".to_string(),
|
"--cache=4096".to_string(),
|
||||||
|
"--txlookuplimit=0".to_string(),
|
||||||
|
"--gcmode=archive".to_string(),
|
||||||
"--txpool.globalslots=100000".to_string(),
|
"--txpool.globalslots=100000".to_string(),
|
||||||
"--txpool.globalqueue=100000".to_string(),
|
"--txpool.globalqueue=100000".to_string(),
|
||||||
"--txpool.accountslots=128".to_string(),
|
"--txpool.accountslots=128".to_string(),
|
||||||
@@ -216,6 +250,7 @@ impl LighthouseGethNode {
|
|||||||
"--ws.port=8546".to_string(),
|
"--ws.port=8546".to_string(),
|
||||||
"--ws.api=eth,net,web3,txpool,engine".to_string(),
|
"--ws.api=eth,net,web3,txpool,engine".to_string(),
|
||||||
"--ws.origins=*".to_string(),
|
"--ws.origins=*".to_string(),
|
||||||
|
"--verbosity=4".to_string(),
|
||||||
],
|
],
|
||||||
consensus_layer_extra_parameters: vec![
|
consensus_layer_extra_parameters: vec![
|
||||||
"--disable-deposit-contract-sync".to_string(),
|
"--disable-deposit-contract-sync".to_string(),
|
||||||
@@ -351,17 +386,34 @@ impl LighthouseGethNode {
|
|||||||
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
||||||
err(Debug),
|
err(Debug),
|
||||||
)]
|
)]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn ws_provider(
|
async fn ws_provider(
|
||||||
&self,
|
&self,
|
||||||
) -> anyhow::Result<FillProvider<impl TxFiller<Ethereum>, impl Provider<Ethereum>, Ethereum>>
|
) -> anyhow::Result<
|
||||||
{
|
FillProvider<
|
||||||
let client = ClientBuilder::default()
|
JoinFill<
|
||||||
.connect_with(BuiltInConnectionString::Ws(
|
JoinFill<
|
||||||
self.ws_connection_string.as_str().parse().unwrap(),
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
None,
|
NonceFiller,
|
||||||
))
|
>,
|
||||||
.await?;
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
Ok(self.provider(client))
|
>,
|
||||||
|
RootProvider,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
self.persistent_ws_provider
|
||||||
|
.get_or_try_init(|| async move {
|
||||||
|
info!("Initializing the WS provider of the lighthouse node");
|
||||||
|
let client = ClientBuilder::default()
|
||||||
|
.connect_with(BuiltInConnectionString::Ws(
|
||||||
|
self.ws_connection_string.as_str().parse().unwrap(),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
Ok(self.provider(client))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(
|
||||||
@@ -370,22 +422,46 @@ impl LighthouseGethNode {
|
|||||||
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
||||||
err(Debug),
|
err(Debug),
|
||||||
)]
|
)]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
async fn http_provider(
|
async fn http_provider(
|
||||||
&self,
|
&self,
|
||||||
) -> anyhow::Result<FillProvider<impl TxFiller<Ethereum>, impl Provider<Ethereum>, Ethereum>>
|
) -> anyhow::Result<
|
||||||
{
|
FillProvider<
|
||||||
let client = ClientBuilder::default()
|
JoinFill<
|
||||||
.connect_with(BuiltInConnectionString::Http(
|
JoinFill<
|
||||||
self.http_connection_string.as_str().parse().unwrap(),
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
))
|
NonceFiller,
|
||||||
.await?;
|
>,
|
||||||
Ok(self.provider(client))
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
self.persistent_http_provider
|
||||||
|
.get_or_try_init(|| async move {
|
||||||
|
info!("Initializing the HTTP provider of the lighthouse node");
|
||||||
|
let client = ClientBuilder::default()
|
||||||
|
.connect_with(BuiltInConnectionString::Http(
|
||||||
|
self.http_connection_string.as_str().parse().unwrap(),
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
Ok(self.provider(client))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn provider(
|
fn provider(
|
||||||
&self,
|
&self,
|
||||||
rpc_client: RpcClient,
|
rpc_client: RpcClient,
|
||||||
) -> FillProvider<impl TxFiller<Ethereum>, impl Provider<Ethereum>, Ethereum> {
|
) -> FillProvider<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>, NonceFiller>,
|
||||||
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider,
|
||||||
|
> {
|
||||||
ProviderBuilder::new()
|
ProviderBuilder::new()
|
||||||
.disable_recommended_fillers()
|
.disable_recommended_fillers()
|
||||||
.filler(FallbackGasFiller::new(
|
.filler(FallbackGasFiller::new(
|
||||||
@@ -393,7 +469,7 @@ impl LighthouseGethNode {
|
|||||||
1_000_000_000,
|
1_000_000_000,
|
||||||
1_000_000_000,
|
1_000_000_000,
|
||||||
))
|
))
|
||||||
.filler(self.chain_id_filler.clone())
|
.filler(ChainIdFiller::new(Some(420420420)))
|
||||||
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
||||||
.wallet(self.wallet.clone())
|
.wallet(self.wallet.clone())
|
||||||
.connect_client(rpc_client)
|
.connect_client(rpc_client)
|
||||||
@@ -407,60 +483,67 @@ impl LighthouseGethNode {
|
|||||||
err(Debug),
|
err(Debug),
|
||||||
)]
|
)]
|
||||||
async fn fund_all_accounts(&self) -> anyhow::Result<()> {
|
async fn fund_all_accounts(&self) -> anyhow::Result<()> {
|
||||||
let mut providers =
|
let provider = self
|
||||||
futures::future::try_join_all((0..100).map(|_| self.ws_provider()).collect::<Vec<_>>())
|
.ws_provider()
|
||||||
.await
|
.await
|
||||||
.context("Failed to create the providers")?
|
.context("Failed to create the WS provider")?;
|
||||||
.into_iter()
|
let mut full_block_subscriber = provider
|
||||||
.map(Arc::new)
|
.subscribe_full_blocks()
|
||||||
.collect::<Vec<_>>();
|
.into_stream()
|
||||||
|
.await
|
||||||
|
.context("Full block subscriber")?;
|
||||||
|
|
||||||
let mut tx_hashes = futures::future::try_join_all(
|
let mut tx_hashes = futures::future::try_join_all(
|
||||||
NetworkWallet::<Ethereum>::signer_addresses(self.wallet.as_ref())
|
NetworkWallet::<Ethereum>::signer_addresses(self.wallet.as_ref())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(nonce, address)| {
|
.map(|(nonce, address)| {
|
||||||
let provider = providers[nonce % 100].clone();
|
let provider = provider.clone();
|
||||||
async move {
|
async move {
|
||||||
let transaction = TransactionRequest::default()
|
let mut transaction = TransactionRequest::default()
|
||||||
.from(self.prefunded_account_address)
|
.from(self.prefunded_account_address)
|
||||||
.to(address)
|
.to(address)
|
||||||
.nonce(nonce as _)
|
.nonce(nonce as _)
|
||||||
|
.gas_limit(25_000_000)
|
||||||
|
.max_fee_per_gas(1_000_000_000)
|
||||||
|
.max_priority_fee_per_gas(1_000_000_000)
|
||||||
.value(INITIAL_BALANCE.try_into().unwrap());
|
.value(INITIAL_BALANCE.try_into().unwrap());
|
||||||
|
transaction.chain_id = Some(420420420);
|
||||||
provider
|
provider
|
||||||
.send_transaction(transaction)
|
.send_transaction(transaction)
|
||||||
.await
|
.await
|
||||||
.map(|tx| *tx.tx_hash())
|
.map(|tx| *tx.tx_hash())
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("Failed to submit all transactions")?
|
.context("Failed to submit all transactions")?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
let provider = providers.pop().unwrap();
|
while let Some(block) = full_block_subscriber.next().await {
|
||||||
let mut block_number = 0 as BlockNumber;
|
let Ok(block) = block else {
|
||||||
while !tx_hashes.is_empty() {
|
|
||||||
let Ok(Some(block)) = provider
|
|
||||||
.get_block(BlockId::Number(BlockNumberOrTag::Number(block_number)))
|
|
||||||
.await
|
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let block_number = block.number();
|
||||||
|
let block_timestamp = block.header.timestamp;
|
||||||
|
let block_transaction_count = block.transactions.len();
|
||||||
|
|
||||||
for hash in block.transactions.into_hashes().as_hashes().unwrap() {
|
for hash in block.transactions.into_hashes().as_hashes().unwrap() {
|
||||||
tx_hashes.remove(hash);
|
tx_hashes.remove(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
block.number = block_number,
|
block.number = block_number,
|
||||||
block.timestamp = block.header.timestamp,
|
block.timestamp = block_timestamp,
|
||||||
|
block.transaction_count = block_transaction_count,
|
||||||
remaining_transactions = tx_hashes.len(),
|
remaining_transactions = tx_hashes.len(),
|
||||||
"Discovered new block in funding accounts"
|
"Discovered new block when funding accounts"
|
||||||
);
|
);
|
||||||
|
|
||||||
block_number += 1
|
if tx_hashes.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -468,11 +551,12 @@ impl LighthouseGethNode {
|
|||||||
|
|
||||||
fn internal_execute_transaction<'a>(
|
fn internal_execute_transaction<'a>(
|
||||||
transaction: TransactionRequest,
|
transaction: TransactionRequest,
|
||||||
provider: Arc<
|
provider: FillProvider<
|
||||||
FillProvider<impl TxFiller<Ethereum> + 'a, impl Provider<Ethereum> + 'a, Ethereum>,
|
impl TxFiller<Ethereum> + 'a,
|
||||||
|
impl Provider<Ethereum> + Clone + 'a,
|
||||||
|
Ethereum,
|
||||||
>,
|
>,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::TransactionReceipt>> + 'a>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + 'a>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let pending_transaction = provider
|
let pending_transaction = provider
|
||||||
.send_transaction(transaction)
|
.send_transaction(transaction)
|
||||||
@@ -552,17 +636,60 @@ impl EthereumNode for LighthouseGethNode {
|
|||||||
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
||||||
err,
|
err,
|
||||||
)]
|
)]
|
||||||
fn execute_transaction(
|
fn submit_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: TransactionRequest,
|
transaction: TransactionRequest,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::TransactionReceipt>> + '_>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TxHash>> + '_>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let provider = self
|
let provider = self
|
||||||
.ws_provider()
|
.ws_provider()
|
||||||
.await
|
.await
|
||||||
.map(Arc::new)
|
.context("Failed to create the provider for transaction submission")?;
|
||||||
.context("Failed to create provider for transaction submission")?;
|
tracing::trace!("Submit transaction, provider created");
|
||||||
|
let pending_transaction = provider
|
||||||
|
.send_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.context("Failed to submit the transaction through the provider")?;
|
||||||
|
tracing::trace!("Submitted transaction");
|
||||||
|
Ok(*pending_transaction.tx_hash())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
||||||
|
)]
|
||||||
|
fn get_receipt(
|
||||||
|
&self,
|
||||||
|
tx_hash: TxHash,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.ws_provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create provider for getting the receipt")?
|
||||||
|
.get_transaction_receipt(tx_hash)
|
||||||
|
.await
|
||||||
|
.context("Failed to get the receipt of the transaction")?
|
||||||
|
.context("Failed to get the receipt of the transaction")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(
|
||||||
|
level = "info",
|
||||||
|
skip_all,
|
||||||
|
fields(lighthouse_node_id = self.id, connection_string = self.ws_connection_string),
|
||||||
|
err,
|
||||||
|
)]
|
||||||
|
fn execute_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: TransactionRequest,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self
|
||||||
|
.ws_provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create provider for transaction execution")?;
|
||||||
Self::internal_execute_transaction(transaction, provider).await
|
Self::internal_execute_transaction(transaction, provider).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -572,8 +699,7 @@ impl EthereumNode for LighthouseGethNode {
|
|||||||
&self,
|
&self,
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
trace_options: GethDebugTracingOptions,
|
trace_options: GethDebugTracingOptions,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::trace::geth::GethTrace>> + '_>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let provider = Arc::new(
|
let provider = Arc::new(
|
||||||
self.http_provider()
|
self.http_provider()
|
||||||
@@ -663,7 +789,7 @@ impl EthereumNode for LighthouseGethNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))]
|
#[instrument(level = "info", skip_all, fields(lighthouse_node_id = self.id))]
|
||||||
fn resolver(
|
fn resolver(
|
||||||
&self,
|
&self,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<Arc<dyn ResolverApi + '_>>> + '_>> {
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<Arc<dyn ResolverApi + '_>>> + '_>> {
|
||||||
@@ -677,6 +803,43 @@ impl EthereumNode for LighthouseGethNode {
|
|||||||
fn evm_version(&self) -> EVMVersion {
|
fn evm_version(&self) -> EVMVersion {
|
||||||
EVMVersion::Cancun
|
EVMVersion::Cancun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn subscribe_to_full_blocks_information(
|
||||||
|
&self,
|
||||||
|
) -> Pin<
|
||||||
|
Box<
|
||||||
|
dyn Future<Output = anyhow::Result<Pin<Box<dyn Stream<Item = MinedBlockInformation>>>>>
|
||||||
|
+ '_,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self.ws_provider().await?;
|
||||||
|
let block_subscription = provider.subscribe_full_blocks().channel_size(1024);
|
||||||
|
let block_stream = block_subscription
|
||||||
|
.into_stream()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the block stream")?;
|
||||||
|
|
||||||
|
let mined_block_information_stream = block_stream.filter_map(|block| async {
|
||||||
|
let block = block.ok()?;
|
||||||
|
Some(MinedBlockInformation {
|
||||||
|
block_number: block.number(),
|
||||||
|
block_timestamp: block.header.timestamp,
|
||||||
|
mined_gas: block.header.gas_used as _,
|
||||||
|
block_gas_limit: block.header.gas_limit as _,
|
||||||
|
transaction_hashes: block
|
||||||
|
.transactions
|
||||||
|
.into_hashes()
|
||||||
|
.as_hashes()
|
||||||
|
.expect("Must be hashes")
|
||||||
|
.to_vec(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Box::pin(mined_block_information_stream)
|
||||||
|
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LighthouseGethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
pub struct LighthouseGethNodeResolver<F: TxFiller<Ethereum>, P: Provider<Ethereum>> {
|
||||||
|
|||||||
+125
-22
@@ -23,17 +23,23 @@ use alloy::{
|
|||||||
TxHash, U256,
|
TxHash, U256,
|
||||||
},
|
},
|
||||||
providers::{
|
providers::{
|
||||||
Provider, ProviderBuilder,
|
Identity, Provider, ProviderBuilder, RootProvider,
|
||||||
ext::DebugApi,
|
ext::DebugApi,
|
||||||
fillers::{CachedNonceManager, ChainIdFiller, FillProvider, NonceFiller, TxFiller},
|
fillers::{
|
||||||
|
CachedNonceManager, ChainIdFiller, FillProvider, JoinFill, NonceFiller, TxFiller,
|
||||||
|
WalletFiller,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rpc::types::{
|
rpc::types::{
|
||||||
EIP1186AccountProofResponse, TransactionReceipt,
|
EIP1186AccountProofResponse, TransactionReceipt, TransactionRequest,
|
||||||
eth::{Block, Header, Transaction},
|
eth::{Block, Header, Transaction},
|
||||||
trace::geth::{DiffMode, GethDebugTracingOptions, PreStateConfig, PreStateFrame},
|
trace::geth::{
|
||||||
|
DiffMode, GethDebugTracingOptions, GethTrace, PreStateConfig, PreStateFrame,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
use futures::{Stream, StreamExt};
|
||||||
use revive_common::EVMVersion;
|
use revive_common::EVMVersion;
|
||||||
use revive_dt_common::fs::clear_directory;
|
use revive_dt_common::fs::clear_directory;
|
||||||
use revive_dt_format::traits::ResolverApi;
|
use revive_dt_format::traits::ResolverApi;
|
||||||
@@ -43,7 +49,8 @@ use sp_core::crypto::Ss58Codec;
|
|||||||
use sp_runtime::AccountId32;
|
use sp_runtime::AccountId32;
|
||||||
|
|
||||||
use revive_dt_config::*;
|
use revive_dt_config::*;
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::{EthereumNode, MinedBlockInformation};
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -59,6 +66,7 @@ static NODE_COUNT: AtomicU32 = AtomicU32::new(0);
|
|||||||
/// or the revive-dev-node which is done by changing the path and some of the other arguments passed
|
/// or the revive-dev-node which is done by changing the path and some of the other arguments passed
|
||||||
/// to the command.
|
/// to the command.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
||||||
pub struct SubstrateNode {
|
pub struct SubstrateNode {
|
||||||
id: u32,
|
id: u32,
|
||||||
node_binary: PathBuf,
|
node_binary: PathBuf,
|
||||||
@@ -72,6 +80,20 @@ pub struct SubstrateNode {
|
|||||||
wallet: Arc<EthereumWallet>,
|
wallet: Arc<EthereumWallet>,
|
||||||
nonce_manager: CachedNonceManager,
|
nonce_manager: CachedNonceManager,
|
||||||
chain_id_filler: ChainIdFiller,
|
chain_id_filler: ChainIdFiller,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
provider: OnceCell<
|
||||||
|
FillProvider<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<
|
||||||
|
JoinFill<JoinFill<Identity, FallbackGasFiller>, ChainIdFiller>,
|
||||||
|
NonceFiller,
|
||||||
|
>,
|
||||||
|
WalletFiller<Arc<EthereumWallet>>,
|
||||||
|
>,
|
||||||
|
RootProvider<ReviveNetwork>,
|
||||||
|
ReviveNetwork,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubstrateNode {
|
impl SubstrateNode {
|
||||||
@@ -123,6 +145,7 @@ impl SubstrateNode {
|
|||||||
wallet: wallet.clone(),
|
wallet: wallet.clone(),
|
||||||
chain_id_filler: Default::default(),
|
chain_id_filler: Default::default(),
|
||||||
nonce_manager: Default::default(),
|
nonce_manager: Default::default(),
|
||||||
|
provider: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,22 +359,31 @@ impl SubstrateNode {
|
|||||||
async fn provider(
|
async fn provider(
|
||||||
&self,
|
&self,
|
||||||
) -> anyhow::Result<
|
) -> anyhow::Result<
|
||||||
FillProvider<impl TxFiller<ReviveNetwork>, impl Provider<ReviveNetwork>, ReviveNetwork>,
|
FillProvider<
|
||||||
|
impl TxFiller<ReviveNetwork>,
|
||||||
|
impl Provider<ReviveNetwork> + Clone,
|
||||||
|
ReviveNetwork,
|
||||||
|
>,
|
||||||
> {
|
> {
|
||||||
ProviderBuilder::new()
|
self.provider
|
||||||
.disable_recommended_fillers()
|
.get_or_try_init(|| async move {
|
||||||
.network::<ReviveNetwork>()
|
ProviderBuilder::new()
|
||||||
.filler(FallbackGasFiller::new(
|
.disable_recommended_fillers()
|
||||||
25_000_000,
|
.network::<ReviveNetwork>()
|
||||||
1_000_000_000,
|
.filler(FallbackGasFiller::new(
|
||||||
1_000_000_000,
|
25_000_000,
|
||||||
))
|
1_000_000_000,
|
||||||
.filler(self.chain_id_filler.clone())
|
1_000_000_000,
|
||||||
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
))
|
||||||
.wallet(self.wallet.clone())
|
.filler(self.chain_id_filler.clone())
|
||||||
.connect(&self.rpc_url)
|
.filler(NonceFiller::new(self.nonce_manager.clone()))
|
||||||
|
.wallet(self.wallet.clone())
|
||||||
|
.connect(&self.rpc_url)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(Into::into)
|
.cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,9 +400,41 @@ impl EthereumNode for SubstrateNode {
|
|||||||
&self.rpc_url
|
&self.rpc_url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn submit_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: TransactionRequest,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TxHash>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self
|
||||||
|
.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the provider for transaction submission")?;
|
||||||
|
let pending_transaction = provider
|
||||||
|
.send_transaction(transaction)
|
||||||
|
.await
|
||||||
|
.context("Failed to submit the transaction through the provider")?;
|
||||||
|
Ok(*pending_transaction.tx_hash())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_receipt(
|
||||||
|
&self,
|
||||||
|
tx_hash: TxHash,
|
||||||
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
self.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create provider for getting the receipt")?
|
||||||
|
.get_transaction_receipt(tx_hash)
|
||||||
|
.await
|
||||||
|
.context("Failed to get the receipt of the transaction")?
|
||||||
|
.context("Failed to get the receipt of the transaction")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_transaction(
|
fn execute_transaction(
|
||||||
&self,
|
&self,
|
||||||
transaction: alloy::rpc::types::TransactionRequest,
|
transaction: TransactionRequest,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<TransactionReceipt>> + '_>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let receipt = self
|
let receipt = self
|
||||||
@@ -391,8 +455,7 @@ impl EthereumNode for SubstrateNode {
|
|||||||
&self,
|
&self,
|
||||||
tx_hash: TxHash,
|
tx_hash: TxHash,
|
||||||
trace_options: GethDebugTracingOptions,
|
trace_options: GethDebugTracingOptions,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<alloy::rpc::types::trace::geth::GethTrace>> + '_>>
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<GethTrace>> + '_>> {
|
||||||
{
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.provider()
|
self.provider()
|
||||||
.await
|
.await
|
||||||
@@ -467,6 +530,46 @@ impl EthereumNode for SubstrateNode {
|
|||||||
fn evm_version(&self) -> EVMVersion {
|
fn evm_version(&self) -> EVMVersion {
|
||||||
EVMVersion::Cancun
|
EVMVersion::Cancun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn subscribe_to_full_blocks_information(
|
||||||
|
&self,
|
||||||
|
) -> Pin<
|
||||||
|
Box<
|
||||||
|
dyn Future<Output = anyhow::Result<Pin<Box<dyn Stream<Item = MinedBlockInformation>>>>>
|
||||||
|
+ '_,
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
Box::pin(async move {
|
||||||
|
let provider = self
|
||||||
|
.provider()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the provider for block subscription")?;
|
||||||
|
let block_subscription = provider.subscribe_full_blocks();
|
||||||
|
let block_stream = block_subscription
|
||||||
|
.into_stream()
|
||||||
|
.await
|
||||||
|
.context("Failed to create the block stream")?;
|
||||||
|
|
||||||
|
let mined_block_information_stream = block_stream.filter_map(|block| async {
|
||||||
|
let block = block.ok()?;
|
||||||
|
Some(MinedBlockInformation {
|
||||||
|
block_number: block.number(),
|
||||||
|
block_timestamp: block.header.timestamp,
|
||||||
|
mined_gas: block.header.gas_used as _,
|
||||||
|
block_gas_limit: block.header.gas_limit,
|
||||||
|
transaction_hashes: block
|
||||||
|
.transactions
|
||||||
|
.into_hashes()
|
||||||
|
.as_hashes()
|
||||||
|
.expect("Must be hashes")
|
||||||
|
.to_vec(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Box::pin(mined_block_information_stream)
|
||||||
|
as Pin<Box<dyn Stream<Item = MinedBlockInformation>>>)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubstrateNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> {
|
pub struct SubstrateNodeResolver<F: TxFiller<ReviveNetwork>, P: Provider<ReviveNetwork>> {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ revive-dt-config = { workspace = true }
|
|||||||
revive-dt-format = { workspace = true }
|
revive-dt-format = { workspace = true }
|
||||||
revive-dt-compiler = { workspace = true }
|
revive-dt-compiler = { workspace = true }
|
||||||
|
|
||||||
alloy-primitives = { workspace = true }
|
alloy = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
indexmap = { workspace = true, features = ["serde"] }
|
indexmap = { workspace = true, features = ["serde"] }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use std::{
|
|||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
use alloy_primitives::Address;
|
use alloy::primitives::Address;
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use revive_dt_common::types::PlatformIdentifier;
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
@@ -106,7 +106,10 @@ impl ReportAggregator {
|
|||||||
RunnerEvent::ContractDeployed(event) => {
|
RunnerEvent::ContractDeployed(event) => {
|
||||||
self.handle_contract_deployed_event(*event);
|
self.handle_contract_deployed_event(*event);
|
||||||
}
|
}
|
||||||
RunnerEvent::Completion(event) => self.handle_completion(*event),
|
RunnerEvent::Completion(event) => {
|
||||||
|
self.handle_completion(*event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("Report aggregation completed");
|
debug!("Report aggregation completed");
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
|
use std::{collections::BTreeMap, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use alloy_primitives::Address;
|
use alloy::primitives::Address;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use revive_dt_common::types::PlatformIdentifier;
|
use revive_dt_common::types::PlatformIdentifier;
|
||||||
|
|||||||
+1
-2
@@ -89,8 +89,7 @@ echo "This may take a while..."
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Run the tool
|
# Run the tool
|
||||||
RUST_LOG="info,alloy_pubsub::service=error" cargo run --release -- execute-tests \
|
RUST_LOG="info,alloy_pubsub::service=error" cargo run --release -- test \
|
||||||
--platform geth-evm-solc \
|
|
||||||
--platform revive-dev-node-polkavm-resolc \
|
--platform revive-dev-node-polkavm-resolc \
|
||||||
--corpus "$CORPUS_FILE" \
|
--corpus "$CORPUS_FILE" \
|
||||||
--working-directory "$WORKDIR" \
|
--working-directory "$WORKDIR" \
|
||||||
|
|||||||
Reference in New Issue
Block a user