From c4d81b9f022ee2e5344f88276b662543e62460cd Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:31:02 +0530 Subject: [PATCH 1/4] chore(deps): alloy 0.7 (#9447) * bump deps * fix: receipts * bump core * fix --- Cargo.lock | 191 ++++++++++++------- Cargo.toml | 66 +++---- crates/anvil/core/src/eth/block.rs | 5 + crates/anvil/core/src/eth/transaction/mod.rs | 28 +-- crates/anvil/src/eth/backend/mem/storage.rs | 32 ++-- crates/cast/tests/cli/main.rs | 1 + crates/cheatcodes/Cargo.toml | 1 + crates/cheatcodes/src/fs.rs | 2 +- crates/common/Cargo.toml | 1 + crates/common/fmt/src/ui.rs | 16 +- crates/common/src/transactions.rs | 3 +- crates/forge/bin/cmd/create.rs | 4 +- crates/script-sequence/Cargo.toml | 1 + crates/script-sequence/src/reader.rs | 2 +- crates/script-sequence/src/sequence.rs | 2 +- crates/script/src/receipts.rs | 2 +- 16 files changed, 212 insertions(+), 145 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f8fa5bb58ef..ea8b8da749b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,14 +86,15 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae09ffd7c29062431dd86061deefe4e3c6f07fa0d674930095f8dcedb0baf02c" +checksum = "3a1ff8439834ab71a4b0ecd1a8ff80b3921c87615f158940c3364f399c732786" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-serde", + "alloy-trie 0.7.4", "auto_impl", "c-kzg", "derive_more", @@ -101,11 +102,25 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-consensus-any" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519a86faaa6729464365a90c04eba68539b6d3a30f426edb4b3dafd78920d42f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-contract" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66430a72d5bf5edead101c8c2f0a24bada5ec9f3cf9909b3e08b6d6899b4803e" +checksum = "cca2b353d8b7f160dc930dfa174557acefece6deab5ecd7e6230d38858579eea" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -173,9 +188,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6aa3961694b30ba53d41006131a2fca3bdab22e4c344e46db2c639e7c2dfdd" +checksum = "8dedb328c2114284f767e075589ca9de8d5e9c8a91333402f4804a584ed71a38" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -191,9 +206,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f7877ded3921d18a0a9556d55bedf84535567198c9edab2aa23106da91855" +checksum = "4841e8dd4e0f53d76b501fd4c6bc21d95d688bc8ebf0ea359fc6c7ab65b48742" dependencies = [ "alloy-primitives", "alloy-serde", @@ -214,9 +229,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3694b7e480728c0b3e228384f223937f14c10caef5a4c766021190fc8f283d35" +checksum = "254f770918f96dc4ec88a15e6e2e243358e1719d66b40ef814428e7697079d25" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -228,15 +243,17 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea94b8ceb5c75d7df0a93ba0acc53b55a22b47b532b600a800a87ef04eb5b0b4" +checksum = "931dd176c6e33355f3dc0170ec69cf5b951f4d73870b276e2c837ab35f9c5136" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", + "alloy-rpc-types-any", "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", @@ -251,9 +268,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9f3e281005943944d15ee8491534a1c7b3cbf7a7de26f8c433b842b93eb5f9" +checksum = "fa6ec0f23be233e851e31c5e4badfedfa9c7bc177bc37f4e03616072cd40a806" dependencies = [ "alloy-consensus", "alloy-eips", @@ -264,9 +281,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9805d126f24be459b958973c0569c73e1aadd27d4535eee82b2b6764aa03616" +checksum = "e3bce85f0f67b2248c2eb42941bb75079ac53648569a668e8bfd7de5a831ec64" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -313,9 +330,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c1f9eede27bf4c13c099e8e64d54efd7ce80ef6ea47478aa75d5d74e2dba3b" +checksum = "5545e2cbf2f8f24c68bb887ba0294fa12a2f816b9e72c4f226cd137b77d0e294" dependencies = [ "alloy-chains", "alloy-consensus", @@ -326,6 +343,7 @@ dependencies = [ "alloy-primitives", "alloy-pubsub", "alloy-rpc-client", + "alloy-rpc-types-debug", "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", @@ -355,9 +373,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f1f34232f77341076541c405482e4ae12f0ee7153d8f9969fc1691201b2247" +checksum = "b633f7731a3df2f4f334001bf80436565113816c5aa5c136c1ded563051e049b" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -396,9 +414,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374dbe0dc3abdc2c964f36b3d3edf9cdb3db29d16bda34aa123f03d810bec1dd" +checksum = "aed9e40c2a73265ebf70f1e48303ee55920282e1ea5971e832873fb2d32cea74" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -422,9 +440,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c74832aa474b670309c20fffc2a869fa141edab7c79ff7963fad0a08de60bae1" +checksum = "42dea20fa715a6f39ec7adc735cfd9567342870737270ac67795d55896527772" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -438,9 +456,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca97963132f78ddfc60e43a017348e6d52eea983925c23652f5b330e8e02291" +checksum = "2750f4f694b27461915b9794df60177198bf733da38dde71aadfbe2946a3c0be" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -448,11 +466,33 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-rpc-types-any" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79d7620e22d6ed7c58451dd303d0501ade5a8bec9dc8daef0fbc48ceffabbae1" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d2d4a265fb1198272cc43d8d418c0423cdfc1aebcd283be9105464874a1dda" +dependencies = [ + "alloy-primitives", + "serde", +] + [[package]] name = "alloy-rpc-types-engine" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56294dce86af23ad6ee8df46cf8b0d292eb5d1ff67dc88a0886051e32b1faf" +checksum = "9fb843daa6feb011475f0db8c499fff5ac62e1e6012fc01d97477ddb3217a83f" dependencies = [ "alloy-consensus", "alloy-eips", @@ -468,11 +508,12 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a477281940d82d29315846c7216db45b15e90bcd52309da9f54bcf7ad94a11" +checksum = "df34b88df4deeac9ecfc80ad7cbb26a33e57437b9db8be5b952792feef6134bc" dependencies = [ "alloy-consensus", + "alloy-consensus-any", "alloy-eips", "alloy-network-primitives", "alloy-primitives", @@ -487,9 +528,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd8b4877ef520c138af702097477cdd19504a8e1e4675ba37e92ba40f2d3c6f" +checksum = "db32f30a55ea4fa9d893127a84eef52fc54d23acb34c1a5a39bfe9bd95fbc149" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -501,9 +542,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4ab49acf90a71f7fb894dc5fd485f1f07a1e348966c714c4d1e0b7478850a8" +checksum = "af1588d8d799095a9bd55d9045b76add042ab725c37316a77da933683754aa4b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -513,9 +554,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dfa4a7ccf15b2492bb68088692481fd6b2604ccbee1d0d6c44c21427ae4df83" +checksum = "43a89fd4cc3f96b3c5c0dd1cebeb63323e4659bbdc837117fa3fd5ac168df7d9" dependencies = [ "alloy-primitives", "serde", @@ -524,9 +565,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e10aec39d60dc27edcac447302c7803d2371946fb737245320a05b78eb2fafd" +checksum = "532010243a96d1f8593c2246ec3971bc52303884fa1e43ca0a776798ba178910" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -540,9 +581,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0109e5b18079aec2a022e4bc9db1d74bcc046f8b66274ffa8b0e4322b44b2b44" +checksum = "fd0bdb5079a35d7559714d9f9690b2ebb462921b9ceea63488bd2bef5744c15a" dependencies = [ "alloy-consensus", "alloy-network", @@ -558,9 +599,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558651eb0d76bcf2224de694481e421112fa2cbc6fe6a413cc76fd67e14cf0d7" +checksum = "794e996552efa65a76b20c088f8a968da514f90f7e44cecc32fc544c8a66fd29" dependencies = [ "alloy-consensus", "alloy-network", @@ -576,9 +617,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29781b6a064b6235de4ec3cc0810f59fe227b8d31258f23a077570fc9525d7a6" +checksum = "416fbc9f19bed61f722181b8f10bd4d89648c254d49f594e1617215f0a30ba46" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -596,9 +637,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8396f6dff60700bc1d215ee03d86ff56de268af96e2bf833a14d0bafcab9882" +checksum = "e8080c0ab2dc729b0cbb183843d08e78d2a1629140c9fc16234d2272abb483bd" dependencies = [ "alloy-consensus", "alloy-network", @@ -615,9 +656,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21267541177607141a5db6fd1abed5a46553b7a6d9363cf3d047721634705905" +checksum = "1862a5a0e883998e65b735c71560a7b9eaca57cab2165eeb80f0b9a01fff3348" dependencies = [ "alloy-consensus", "alloy-network", @@ -705,9 +746,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f99acddb34000d104961897dbb0240298e8b775a7efffb9fda2a1a3efedd65b3" +checksum = "b6f295f4b745fb9e4e663d70bc57aed991288912c7aaaf25767def921050ee43" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -725,9 +766,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc013132e34eeadaa0add7e74164c1503988bfba8bae885b32e0918ba85a8a6" +checksum = "39139015a5ec127d9c895b49b484608e27fe4538544f84cdf5eae0bd36339bc6" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -740,9 +781,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063edc0660e81260653cc6a95777c29d54c2543a668aa5da2359fb450d25a1ba" +checksum = "d9b4f865b13bb8648e93f812b19b74838b9165212a2beb95fc386188c443a5e3" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -761,9 +802,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.6.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd170e600801116d5efe64f74a4fc073dbbb35c807013a7d0a388742aeebba0" +checksum = "6af91e3521b8b3eac26809b1c6f9b86e3ed455dfab812f036836aabdf709b921" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -792,6 +833,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "alloy-trie" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b2e366c0debf0af77766c23694a3f863b02633050e71e096e257ffbd395e50" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arrayvec", + "derive_more", + "nybbles", + "smallvec", + "tracing", +] + [[package]] name = "ammonia" version = "4.0.0" @@ -905,7 +961,7 @@ dependencies = [ "alloy-transport", "alloy-transport-ipc", "alloy-transport-ws", - "alloy-trie", + "alloy-trie 0.6.0", "anvil-core", "anvil-rpc", "anvil-server", @@ -961,7 +1017,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types", "alloy-serde", - "alloy-trie", + "alloy-trie 0.6.0", "bytes", "foundry-common", "foundry-evm", @@ -3546,6 +3602,7 @@ dependencies = [ name = "forge-script-sequence" version = "0.2.0" dependencies = [ + "alloy-network", "alloy-primitives", "alloy-rpc-types", "eyre", @@ -3644,6 +3701,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-genesis", "alloy-json-abi", + "alloy-network", "alloy-primitives", "alloy-provider", "alloy-rlp", @@ -3743,6 +3801,7 @@ dependencies = [ "alloy-eips", "alloy-json-abi", "alloy-json-rpc", + "alloy-network", "alloy-primitives", "alloy-provider", "alloy-pubsub", @@ -4114,9 +4173,9 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f040169c6573e9989d1a26c3dcbe645ef8e4edabbf64af98958552da1073e4e" +checksum = "0c3d9ee7669c2a184b83c05393abfa5c9f24ef99b9abefa627fe45660adee0ba" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -6315,9 +6374,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "op-alloy-consensus" -version = "0.6.8" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce158d886815d419222daa67fcdf949a34f7950653a4498ebeb4963331f70ed" +checksum = "75353c94e7515fac7d3c280bae56bff3375784a05cb44b317260606292ff6ba9" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6331,9 +6390,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.6.8" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060ebeaea8c772e396215f69bb86d231ec8b7f36aca0dd6ce367ceaa9a8c33e6" +checksum = "680a86b63fe4c45fbd5dbf1ac6779409565211c4b234d20af94cf1f79d11f23a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7459,9 +7518,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747291a18ad6726a08dd73f8b6a6b3a844db582ecae2063ccf0a04880c44f482" +checksum = "41bbeb6004cc4ed48d27756f0479011df91a6f5642a3abab9309eda5ce67c4ad" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", diff --git a/Cargo.toml b/Cargo.toml index b07d99cc9f92..a4d8f2724b10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -170,7 +170,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.9.0", default-features = false } foundry-compilers = { version = "0.12.3", default-features = false } -foundry-fork-db = "0.7.0" +foundry-fork-db = "0.8.0" solang-parser = "=0.3.3" solar-ast = { version = "=0.1.0", default-features = false } solar-parse = { version = "=0.1.0", default-features = false } @@ -178,55 +178,55 @@ solar-parse = { version = "=0.1.0", default-features = false } ## revm revm = { version = "18.0.0", default-features = false } revm-primitives = { version = "14.0.0", default-features = false } -revm-inspectors = { version = "0.11.0", features = ["serde"] } +revm-inspectors = { version = "0.12.0", features = ["serde"] } ## ethers ethers-contract-abigen = { version = "2.0.14", default-features = false } ## alloy -alloy-consensus = { version = "0.6.4", default-features = false } -alloy-contract = { version = "0.6.4", default-features = false } -alloy-eips = { version = "0.6.4", default-features = false } -alloy-genesis = { version = "0.6.4", default-features = false } -alloy-json-rpc = { version = "0.6.4", default-features = false } -alloy-network = { version = "0.6.4", default-features = false } -alloy-provider = { version = "0.6.4", default-features = false } -alloy-pubsub = { version = "0.6.4", default-features = false } -alloy-rpc-client = { version = "0.6.4", default-features = false } -alloy-rpc-types = { version = "0.6.4", default-features = true } -alloy-serde = { version = "0.6.4", default-features = false } -alloy-signer = { version = "0.6.4", default-features = false } -alloy-signer-aws = { version = "0.6.4", default-features = false } -alloy-signer-gcp = { version = "0.6.4", default-features = false } -alloy-signer-ledger = { version = "0.6.4", default-features = false } -alloy-signer-local = { version = "0.6.4", default-features = false } -alloy-signer-trezor = { version = "0.6.4", default-features = false } -alloy-transport = { version = "0.6.4", default-features = false } -alloy-transport-http = { version = "0.6.4", default-features = false } -alloy-transport-ipc = { version = "0.6.4", default-features = false } -alloy-transport-ws = { version = "0.6.4", default-features = false } -alloy-node-bindings = { version = "0.6.4", default-features = false } +alloy-consensus = { version = "0.7.0", default-features = false } +alloy-contract = { version = "0.7.0", default-features = false } +alloy-eips = { version = "0.7.0", default-features = false } +alloy-genesis = { version = "0.7.0", default-features = false } +alloy-json-rpc = { version = "0.7.0", default-features = false } +alloy-network = { version = "0.7.0", default-features = false } +alloy-provider = { version = "0.7.0", default-features = false } +alloy-pubsub = { version = "0.7.0", default-features = false } +alloy-rpc-client = { version = "0.7.0", default-features = false } +alloy-rpc-types = { version = "0.7.0", default-features = true } +alloy-serde = { version = "0.7.0", default-features = false } +alloy-signer = { version = "0.7.0", default-features = false } +alloy-signer-aws = { version = "0.7.0", default-features = false } +alloy-signer-gcp = { version = "0.7.0", default-features = false } +alloy-signer-ledger = { version = "0.7.0", default-features = false } +alloy-signer-local = { version = "0.7.0", default-features = false } +alloy-signer-trezor = { version = "0.7.0", default-features = false } +alloy-transport = { version = "0.7.0", default-features = false } +alloy-transport-http = { version = "0.7.0", default-features = false } +alloy-transport-ipc = { version = "0.7.0", default-features = false } +alloy-transport-ws = { version = "0.7.0", default-features = false } +alloy-node-bindings = { version = "0.7.0", default-features = false } ## alloy-core -alloy-dyn-abi = "0.8.11" -alloy-json-abi = "0.8.11" -alloy-primitives = { version = "0.8.11", features = [ +alloy-dyn-abi = "0.8.14" +alloy-json-abi = "0.8.14" +alloy-primitives = { version = "0.8.14", features = [ "getrandom", "rand", "map-foldhash", ] } -alloy-sol-macro-expander = "0.8.11" -alloy-sol-macro-input = "0.8.11" -alloy-sol-types = "0.8.11" -syn-solidity = "0.8.11" +alloy-sol-macro-expander = "0.8.14" +alloy-sol-macro-input = "0.8.14" +alloy-sol-types = "0.8.14" +syn-solidity = "0.8.14" alloy-chains = "0.1" alloy-rlp = "0.3" alloy-trie = "0.6.0" ## op-alloy -op-alloy-rpc-types = "0.6.5" -op-alloy-consensus = "0.6.5" +op-alloy-rpc-types = "0.7.1" +op-alloy-consensus = "0.7.1" ## cli anstream = "0.6" diff --git a/crates/anvil/core/src/eth/block.rs b/crates/anvil/core/src/eth/block.rs index c9f9048b8199..50a9a66b331f 100644 --- a/crates/anvil/core/src/eth/block.rs +++ b/crates/anvil/core/src/eth/block.rs @@ -65,6 +65,7 @@ impl Block { nonce: partial_header.nonce, base_fee_per_gas: partial_header.base_fee, requests_hash: partial_header.requests_hash, + target_blobs_per_block: None, }, transactions, ommers: vec![], @@ -157,6 +158,7 @@ mod tests { parent_beacon_block_root: Default::default(), base_fee_per_gas: None, requests_hash: None, + target_blobs_per_block: None, }; let encoded = alloy_rlp::encode(&header); @@ -198,6 +200,7 @@ mod tests { nonce: B64::ZERO, base_fee_per_gas: None, requests_hash: None, + target_blobs_per_block: None, }; header.encode(&mut data); @@ -231,6 +234,7 @@ mod tests { parent_beacon_block_root: None, base_fee_per_gas: None, requests_hash: None, + target_blobs_per_block: None, }; let header = Header::decode(&mut data.as_slice()).unwrap(); assert_eq!(header, expected); @@ -263,6 +267,7 @@ mod tests { excess_blob_gas: None, parent_beacon_block_root: None, requests_hash: None, + target_blobs_per_block: None, }; assert_eq!(header.hash_slow(), expected_hash); } diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 8de659799a36..4067aa6680fe 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -6,18 +6,18 @@ use alloy_consensus::{ eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar}, TxEip7702, }, - AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, Transaction, TxEip1559, - TxEip2930, TxEnvelope, TxLegacy, TxReceipt, + Receipt, ReceiptEnvelope, ReceiptWithBloom, Signed, Transaction, TxEip1559, TxEip2930, + TxEnvelope, TxLegacy, TxReceipt, }; use alloy_eips::eip2718::{Decodable2718, Eip2718Error, Encodable2718}; -use alloy_network::{AnyRpcTransaction, AnyTxEnvelope}; +use alloy_network::{AnyReceiptEnvelope, AnyRpcTransaction, AnyTransactionReceipt, AnyTxEnvelope}; use alloy_primitives::{ Address, Bloom, Bytes, Log, PrimitiveSignature, TxHash, TxKind, B256, U256, U64, }; use alloy_rlp::{length_of_length, Decodable, Encodable, Header}; use alloy_rpc_types::{ - request::TransactionRequest, trace::otterscan::OtsReceipt, AccessList, AnyTransactionReceipt, - ConversionError, Transaction as RpcTransaction, TransactionReceipt, + request::TransactionRequest, trace::otterscan::OtsReceipt, AccessList, ConversionError, + Transaction as RpcTransaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; use bytes::BufMut; @@ -1109,7 +1109,7 @@ pub struct TransactionInfo { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct DepositReceipt { +pub struct DepositReceipt> { #[serde(flatten)] pub inner: ReceiptWithBloom, #[serde(default, with = "alloy_serde::quantity::opt")] @@ -1136,7 +1136,7 @@ impl DepositReceipt { /// Encodes the receipt data. fn encode_fields(&self, out: &mut dyn BufMut) { self.receipt_rlp_header().encode(out); - self.inner.receipt.status.encode(out); + self.inner.status().encode(out); self.inner.receipt.cumulative_gas_used.encode(out); self.inner.logs_bloom.encode(out); self.inner.receipt.logs.encode(out); @@ -1161,7 +1161,7 @@ impl DepositReceipt { let status = Decodable::decode(b)?; let cumulative_gas_used = Decodable::decode(b)?; let logs_bloom = Decodable::decode(b)?; - let logs = Decodable::decode(b)?; + let logs: Vec = Decodable::decode(b)?; let deposit_nonce = remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; let deposit_nonce_version = remaining(b).then(|| alloy_rlp::Decodable::decode(b)).transpose()?; @@ -1207,7 +1207,7 @@ impl alloy_rlp::Decodable for DepositReceipt { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "type")] -pub enum TypedReceipt { +pub enum TypedReceipt> { #[serde(rename = "0x0", alias = "0x00")] Legacy(ReceiptWithBloom), #[serde(rename = "0x1", alias = "0x01")] @@ -1248,8 +1248,8 @@ impl From> for ReceiptWithBloom { } } -impl From> for OtsReceipt { - fn from(value: TypedReceipt) -> Self { +impl From>> for OtsReceipt { + fn from(value: TypedReceipt>) -> Self { let r#type = match value { TypedReceipt::Legacy(_) => 0x00, TypedReceipt::EIP2930(_) => 0x01, @@ -1258,7 +1258,7 @@ impl From> for OtsReceipt { TypedReceipt::EIP7702(_) => 0x04, TypedReceipt::Deposit(_) => 0x7E, } as u8; - let receipt = ReceiptWithBloom::::from(value); + let receipt = ReceiptWithBloom::>::from(value); let status = receipt.status(); let cumulative_gas_used = receipt.cumulative_gas_used() as u64; let logs = receipt.logs().to_vec(); @@ -1282,7 +1282,7 @@ impl TypedReceipt { } } -impl From> for TypedReceipt { +impl From> for TypedReceipt> { fn from(value: ReceiptEnvelope) -> Self { match value { ReceiptEnvelope::Legacy(r) => Self::Legacy(r), @@ -1439,7 +1439,7 @@ impl Decodable2718 for TypedReceipt { } } -pub type ReceiptResponse = TransactionReceipt>; +pub type ReceiptResponse = TransactionReceipt>>; pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option { let WithOtherFields { diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 056b886277c9..5635a7accbd4 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -555,15 +555,12 @@ impl MinedTransaction { } GethDebugBuiltInTracerType::CallTracer => { return match tracer_config.into_call_config() { - Ok(call_config) => Ok(GethTraceBuilder::new( - self.info.traces.clone(), - TracingInspectorConfig::from_geth_config(&config), - ) - .geth_call_traces( - call_config, - self.receipt.cumulative_gas_used() as u64, - ) - .into()), + Ok(call_config) => Ok(GethTraceBuilder::new(self.info.traces.clone()) + .geth_call_traces( + call_config, + self.receipt.cumulative_gas_used() as u64, + ) + .into()), Err(e) => Err(RpcError::invalid_params(e.to_string()).into()), }; } @@ -579,16 +576,13 @@ impl MinedTransaction { } // default structlog tracer - Ok(GethTraceBuilder::new( - self.info.traces.clone(), - TracingInspectorConfig::from_geth_config(&config), - ) - .geth_traces( - self.receipt.cumulative_gas_used() as u64, - self.info.out.clone().unwrap_or_default(), - opts.config, - ) - .into()) + Ok(GethTraceBuilder::new(self.info.traces.clone()) + .geth_traces( + self.receipt.cumulative_gas_used() as u64, + self.info.out.clone().unwrap_or_default(), + config, + ) + .into()) } } diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index f3d04b09456b..cd833f7c3ed9 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -104,6 +104,7 @@ totalDifficulty [..] blobGasUsed [..] excessBlobGas [..] requestsHash [..] +targetBlobsPerBlock [..] transactions: [ ... ] diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 97b22b16f6e3..a06416160111 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -45,6 +45,7 @@ alloy-signer-local = { workspace = true, features = [ ] } parking_lot.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } +alloy-network.workspace = true alloy-rlp.workspace = true base64.workspace = true diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index e52aaa688d7a..b96a6d4d78bd 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -4,9 +4,9 @@ use super::string::parse; use crate::{Cheatcode, Cheatcodes, CheatcodesExecutor, CheatsCtxt, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_json_abi::ContractObject; +use alloy_network::AnyTransactionReceipt; use alloy_primitives::{hex, map::Entry, Bytes, U256}; use alloy_provider::network::ReceiptResponse; -use alloy_rpc_types::AnyTransactionReceipt; use alloy_sol_types::SolValue; use dialoguer::{Input, Password}; use forge_script_sequence::{BroadcastReader, TransactionWithMetadata}; diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 585e0080cc87..c09e23849ed5 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -43,6 +43,7 @@ alloy-transport-ipc.workspace = true alloy-transport-ws.workspace = true alloy-transport.workspace = true alloy-consensus = { workspace = true, features = ["k256"] } +alloy-network.workspace = true tower.workspace = true diff --git a/crates/common/fmt/src/ui.rs b/crates/common/fmt/src/ui.rs index a82d2cdc3d3c..7ae6adea861a 100644 --- a/crates/common/fmt/src/ui.rs +++ b/crates/common/fmt/src/ui.rs @@ -1,14 +1,15 @@ //! Helper trait and functions to format Ethereum types. use alloy_consensus::{ - AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptWithBloom, Transaction as TxTrait, TxEnvelope, - TxType, + Eip658Value, Receipt, ReceiptWithBloom, Transaction as TxTrait, TxEnvelope, TxType, +}; +use alloy_network::{ + AnyHeader, AnyReceiptEnvelope, AnyRpcBlock, AnyTransactionReceipt, AnyTxEnvelope, + ReceiptResponse, }; -use alloy_network::{AnyHeader, AnyRpcBlock, AnyTxEnvelope, ReceiptResponse}; use alloy_primitives::{hex, Address, Bloom, Bytes, FixedBytes, Uint, I256, U256, U64, U8}; use alloy_rpc_types::{ - AccessListItem, AnyTransactionReceipt, Block, BlockTransactions, Header, Log, Transaction, - TransactionReceipt, + AccessListItem, Block, BlockTransactions, Header, Log, Transaction, TransactionReceipt, }; use alloy_serde::{OtherFields, WithOtherFields}; use serde::Deserialize; @@ -900,6 +901,7 @@ fn pretty_block_basics(block: &Block>) excess_blob_gas, parent_beacon_block_root, requests_hash, + target_blobs_per_block, }, }, uncles: _, @@ -931,7 +933,8 @@ withdrawalsRoot {} totalDifficulty {} blobGasUsed {} excessBlobGas {} -requestsHash {}", +requestsHash {} +targetBlobsPerBlock {}", base_fee_per_gas.pretty(), difficulty.pretty(), extra_data.pretty(), @@ -959,6 +962,7 @@ requestsHash {}", blob_gas_used.pretty(), excess_blob_gas.pretty(), requests_hash.pretty(), + target_blobs_per_block.pretty(), ) } diff --git a/crates/common/src/transactions.rs b/crates/common/src/transactions.rs index a05a46eaed23..b725fc068b17 100644 --- a/crates/common/src/transactions.rs +++ b/crates/common/src/transactions.rs @@ -2,12 +2,13 @@ use alloy_consensus::{Transaction, TxEnvelope}; use alloy_eips::eip7702::SignedAuthorization; +use alloy_network::AnyTransactionReceipt; use alloy_primitives::{Address, TxKind, U256}; use alloy_provider::{ network::{AnyNetwork, ReceiptResponse, TransactionBuilder}, Provider, }; -use alloy_rpc_types::{AnyTransactionReceipt, BlockId, TransactionRequest}; +use alloy_rpc_types::{BlockId, TransactionRequest}; use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::Result; diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 6c2fbb0cfecc..2294d511e9e8 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -2,10 +2,10 @@ use crate::cmd::install; use alloy_chains::Chain; use alloy_dyn_abi::{DynSolValue, JsonAbiExt, Specifier}; use alloy_json_abi::{Constructor, JsonAbi}; -use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder}; +use alloy_network::{AnyNetwork, AnyTransactionReceipt, EthereumWallet, TransactionBuilder}; use alloy_primitives::{hex, Address, Bytes}; use alloy_provider::{PendingTransactionError, Provider, ProviderBuilder}; -use alloy_rpc_types::{AnyTransactionReceipt, TransactionRequest}; +use alloy_rpc_types::TransactionRequest; use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_transport::{Transport, TransportError}; diff --git a/crates/script-sequence/Cargo.toml b/crates/script-sequence/Cargo.toml index 13326e684cac..08fd9a695fde 100644 --- a/crates/script-sequence/Cargo.toml +++ b/crates/script-sequence/Cargo.toml @@ -28,3 +28,4 @@ revm-inspectors.workspace = true alloy-rpc-types.workspace = true alloy-primitives.workspace = true +alloy-network.workspace = true diff --git a/crates/script-sequence/src/reader.rs b/crates/script-sequence/src/reader.rs index c4627dec09ec..de2e9faf1638 100644 --- a/crates/script-sequence/src/reader.rs +++ b/crates/script-sequence/src/reader.rs @@ -1,5 +1,5 @@ use crate::{ScriptSequence, TransactionWithMetadata}; -use alloy_rpc_types::AnyTransactionReceipt; +use alloy_network::AnyTransactionReceipt; use eyre::{bail, Result}; use foundry_common::fs; use revm_inspectors::tracing::types::CallKind; diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 235f28f2c4ff..4b2b434e1179 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -1,6 +1,6 @@ use crate::transaction::TransactionWithMetadata; +use alloy_network::AnyTransactionReceipt; use alloy_primitives::{hex, map::HashMap, TxHash}; -use alloy_rpc_types::AnyTransactionReceipt; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{fs, shell, TransactionMaybeSigned, SELECTOR_LEN}; use foundry_compilers::ArtifactId; diff --git a/crates/script/src/receipts.rs b/crates/script/src/receipts.rs index f073e38e607b..cff893b55688 100644 --- a/crates/script/src/receipts.rs +++ b/crates/script/src/receipts.rs @@ -1,7 +1,7 @@ use alloy_chains::Chain; +use alloy_network::AnyTransactionReceipt; use alloy_primitives::{utils::format_units, TxHash, U256}; use alloy_provider::{PendingTransactionBuilder, PendingTransactionError, Provider, WatchTxError}; -use alloy_rpc_types::AnyTransactionReceipt; use eyre::Result; use foundry_common::{provider::RetryProvider, shell}; use std::time::Duration; From 3e6d3b8b6b96a02df1264294320a840ddc88345b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:55:46 +0200 Subject: [PATCH 2/4] feat: allow any config to be defined inline (#9430) * feat: allow any config to be defined inline * com * rm duplicate * don't update everything * bump * bump --- crates/cheatcodes/src/config.rs | 15 +- crates/cheatcodes/src/inspector.rs | 4 +- crates/chisel/src/executor.rs | 2 +- crates/config/src/inline/mod.rs | 32 +- crates/config/src/lib.rs | 2 +- crates/config/src/utils.rs | 2 +- crates/evm/core/src/opts.rs | 3 +- crates/evm/core/src/utils.rs | 5 +- crates/evm/evm/src/executors/builder.rs | 2 +- crates/evm/evm/src/executors/mod.rs | 40 +- crates/evm/evm/src/executors/trace.rs | 2 +- crates/evm/traces/src/lib.rs | 4 +- crates/forge/bin/cmd/coverage.rs | 3 +- crates/forge/bin/cmd/test/mod.rs | 7 +- crates/forge/src/lib.rs | 117 ------ crates/forge/src/multi_runner.rs | 262 +++++++----- crates/forge/src/result.rs | 42 +- crates/forge/src/runner.rs | 515 +++++++++++++++--------- crates/forge/tests/cli/inline_config.rs | 78 +++- crates/forge/tests/it/config.rs | 4 +- crates/forge/tests/it/spec.rs | 5 +- crates/forge/tests/it/test_helpers.rs | 9 +- crates/script/src/lib.rs | 2 +- 23 files changed, 679 insertions(+), 478 deletions(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 2279e24359a5..e0463dfc4535 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -69,8 +69,8 @@ impl CheatsConfig { running_version: Option, ) -> Self { let mut allowed_paths = vec![config.root.clone()]; - allowed_paths.extend(config.libs.clone()); - allowed_paths.extend(config.allow_paths.clone()); + allowed_paths.extend(config.libs.iter().cloned()); + allowed_paths.extend(config.allow_paths.iter().cloned()); let rpc_endpoints = config.rpc_endpoints.clone().resolved(); trace!(?rpc_endpoints, "using resolved rpc endpoints"); @@ -101,6 +101,17 @@ impl CheatsConfig { } } + /// Returns a new `CheatsConfig` configured with the given `Config` and `EvmOpts`. + pub fn clone_with(&self, config: &Config, evm_opts: EvmOpts) -> Self { + Self::new( + config, + evm_opts, + self.available_artifacts.clone(), + self.running_contract.clone(), + self.running_version.clone(), + ) + } + /// Attempts to canonicalize (see [std::fs::canonicalize]) the path. /// /// Canonicalization fails for non-existing paths, in which case we just normalize the path. diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 447a9e7473ec..ac8058dd9f99 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -439,7 +439,7 @@ pub struct Cheatcodes { /// Scripting based transactions pub broadcastable_transactions: BroadcastableTransactions, - /// Additional, user configurable context this Inspector has access to when inspecting a call + /// Additional, user configurable context this Inspector has access to when inspecting a call. pub config: Arc, /// Test-scoped context holding data that needs to be reset every test run @@ -540,7 +540,7 @@ impl Cheatcodes { /// Returns the configured wallets if available, else creates a new instance. pub fn wallets(&mut self) -> &Wallets { - self.wallets.get_or_insert(Wallets::new(MultiWallet::default(), None)) + self.wallets.get_or_insert_with(|| Wallets::new(MultiWallet::default(), None)) } /// Sets the unlocked wallets. diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 09c00f6ad5b3..71bf18e1ad6a 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -341,7 +341,7 @@ impl SessionSource { ) }) .gas_limit(self.config.evm_opts.gas_limit()) - .spec(self.config.foundry_config.evm_spec_id()) + .spec_id(self.config.foundry_config.evm_spec_id()) .legacy_assertions(self.config.foundry_config.legacy_assertions) .build(env, backend); diff --git a/crates/config/src/inline/mod.rs b/crates/config/src/inline/mod.rs index 30b1c820ec9b..fa67b2426cf0 100644 --- a/crates/config/src/inline/mod.rs +++ b/crates/config/src/inline/mod.rs @@ -4,6 +4,7 @@ use figment::{ value::{Dict, Map, Value}, Figment, Profile, Provider, }; +use foundry_compilers::ProjectCompileOutput; use itertools::Itertools; mod natspec; @@ -53,6 +54,20 @@ impl InlineConfig { Self::default() } + /// Tries to create a new instance by detecting inline configurations from the project compile + /// output. + pub fn new_parsed(output: &ProjectCompileOutput, config: &Config) -> eyre::Result { + let natspecs: Vec = NatSpec::parse(output, &config.root); + let profiles = &config.profiles; + let mut inline = Self::new(); + for natspec in &natspecs { + inline.insert(natspec)?; + // Validate after parsing as TOML. + natspec.validate_profiles(profiles)?; + } + Ok(inline) + } + /// Inserts a new [`NatSpec`] into the [`InlineConfig`]. pub fn insert(&mut self, natspec: &NatSpec) -> Result<(), InlineConfigError> { let map = if let Some(function) = &natspec.function { @@ -92,13 +107,16 @@ impl InlineConfig { Figment::from(base).merge(self.provide(contract, function)) } - /// Returns `true` if a configuration is present at the given contract and function level. - pub fn contains(&self, contract: &str, function: &str) -> bool { - // Order swapped to avoid allocation in `get_function` since order doesn't matter here. - self.get_contract(contract) - .filter(|map| !map.is_empty()) - .or_else(|| self.get_function(contract, function)) - .is_some_and(|map| !map.is_empty()) + /// Returns `true` if a configuration is present at the given contract level. + pub fn contains_contract(&self, contract: &str) -> bool { + self.get_contract(contract).is_some_and(|map| !map.is_empty()) + } + + /// Returns `true` if a configuration is present at the function level. + /// + /// Does not include contract-level configurations. + pub fn contains_function(&self, contract: &str, function: &str) -> bool { + self.get_function(contract, function).is_some_and(|map| !map.is_empty()) } fn get_contract(&self, contract: &str) -> Option<&DataMap> { diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index e1c3fa5ee2d0..f2d25461dfaf 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1125,7 +1125,7 @@ impl Config { /// Returns the [SpecId] derived from the configured [EvmVersion] #[inline] pub fn evm_spec_id(&self) -> SpecId { - evm_spec_id(&self.evm_version, self.alphanet) + evm_spec_id(self.evm_version, self.alphanet) } /// Returns whether the compiler version should be auto-detected diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index e07d7dfbcb09..19f70a939e02 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -259,7 +259,7 @@ impl FromStr for Numeric { /// Returns the [SpecId] derived from [EvmVersion] #[inline] -pub fn evm_spec_id(evm_version: &EvmVersion, alphanet: bool) -> SpecId { +pub fn evm_spec_id(evm_version: EvmVersion, alphanet: bool) -> SpecId { if alphanet { return SpecId::OSAKA; } diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index c5817e483c25..aec0d78a05cf 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -113,9 +113,8 @@ impl EvmOpts { /// And the block that was used to configure the environment. pub async fn fork_evm_env( &self, - fork_url: impl AsRef, + fork_url: &str, ) -> eyre::Result<(revm::primitives::Env, AnyRpcBlock)> { - let fork_url = fork_url.as_ref(); let provider = ProviderBuilder::new(fork_url) .compute_units_per_second(self.get_compute_units_per_second()) .build()?; diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index 2257709e5e91..0841d9340147 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -46,10 +46,7 @@ pub fn apply_chain_and_block_specific_env_changes( return; } - NamedChain::Arbitrum | - NamedChain::ArbitrumGoerli | - NamedChain::ArbitrumNova | - NamedChain::ArbitrumTestnet => { + c if c.is_arbitrum() => { // on arbitrum `block.number` is the L1 block which is included in the // `l1BlockNumber` field if let Some(l1_block_number) = block diff --git a/crates/evm/evm/src/executors/builder.rs b/crates/evm/evm/src/executors/builder.rs index fee3c249ad90..c371a6550b87 100644 --- a/crates/evm/evm/src/executors/builder.rs +++ b/crates/evm/evm/src/executors/builder.rs @@ -52,7 +52,7 @@ impl ExecutorBuilder { /// Sets the EVM spec to use. #[inline] - pub fn spec(mut self, spec: SpecId) -> Self { + pub fn spec_id(mut self, spec: SpecId) -> Self { self.spec_id = spec; self } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 8146cec82d44..8560c3e10f95 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -86,9 +86,7 @@ pub struct Executor { env: EnvWithHandlerCfg, /// The Revm inspector stack. inspector: InspectorStack, - /// The gas limit for calls and deployments. This is different from the gas limit imposed by - /// the passed in environment, as those limits are used by the EVM for certain opcodes like - /// `gaslimit`. + /// The gas limit for calls and deployments. gas_limit: u64, /// Whether `failed()` should be called on the test contract to determine if the test failed. legacy_assertions: bool, @@ -166,6 +164,36 @@ impl Executor { self.env.spec_id() } + /// Sets the EVM spec ID. + pub fn set_spec_id(&mut self, spec_id: SpecId) { + self.env.handler_cfg.spec_id = spec_id; + } + + /// Returns the gas limit for calls and deployments. + /// + /// This is different from the gas limit imposed by the passed in environment, as those limits + /// are used by the EVM for certain opcodes like `gaslimit`. + pub fn gas_limit(&self) -> u64 { + self.gas_limit + } + + /// Sets the gas limit for calls and deployments. + pub fn set_gas_limit(&mut self, gas_limit: u64) { + self.gas_limit = gas_limit; + } + + /// Returns whether `failed()` should be called on the test contract to determine if the test + /// failed. + pub fn legacy_assertions(&self) -> bool { + self.legacy_assertions + } + + /// Sets whether `failed()` should be called on the test contract to determine if the test + /// failed. + pub fn set_legacy_assertions(&mut self, legacy_assertions: bool) { + self.legacy_assertions = legacy_assertions; + } + /// Creates the default CREATE2 Contract Deployer for local tests and scripts. pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> { trace!("deploying local create2 deployer"); @@ -235,12 +263,6 @@ impl Executor { self } - #[inline] - pub fn set_gas_limit(&mut self, gas_limit: u64) -> &mut Self { - self.gas_limit = gas_limit; - self - } - #[inline] pub fn create2_deployer(&self) -> Address { self.inspector().create2_deployer() diff --git a/crates/evm/evm/src/executors/trace.rs b/crates/evm/evm/src/executors/trace.rs index 214e7c28ae16..d9c0d74f6e97 100644 --- a/crates/evm/evm/src/executors/trace.rs +++ b/crates/evm/evm/src/executors/trace.rs @@ -32,7 +32,7 @@ impl TracingExecutor { .alphanet(alphanet) .create2_deployer(create2_deployer) }) - .spec(evm_spec_id(&version.unwrap_or_default(), alphanet)) + .spec_id(evm_spec_id(version.unwrap_or_default(), alphanet)) .build(env, db), } } diff --git a/crates/evm/traces/src/lib.rs b/crates/evm/traces/src/lib.rs index a0fa7e1fca49..f88efbb1b195 100644 --- a/crates/evm/traces/src/lib.rs +++ b/crates/evm/traces/src/lib.rs @@ -349,8 +349,8 @@ impl TraceMode { } } - pub fn with_verbosity(self, verbosiy: u8) -> Self { - if verbosiy >= 3 { + pub fn with_verbosity(self, verbosity: u8) -> Self { + if verbosity >= 3 { std::cmp::max(self, Self::Call) } else { self diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 3a7d43436999..f1c862d81a52 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -11,7 +11,7 @@ use forge::{ }, opts::EvmOpts, utils::IcPcMap, - MultiContractRunnerBuilder, TestOptions, + MultiContractRunnerBuilder, }; use foundry_cli::utils::{LoadConfig, STATIC_FUZZ_SEED}; use foundry_common::{compile::ProjectCompiler, fs}; @@ -233,7 +233,6 @@ impl CoverageArgs { .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_test_options(TestOptions::new(output, config.clone())?) .set_coverage(true) .build(&root, output, env, evm_opts)?; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index e5a058c0d72d..dd6d04a98cde 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -14,7 +14,7 @@ use forge::{ identifier::SignaturesIdentifier, CallTraceDecoderBuilder, InternalTraceMode, TraceKind, }, - MultiContractRunner, MultiContractRunnerBuilder, TestFilter, TestOptions, + MultiContractRunner, MultiContractRunnerBuilder, TestFilter, }; use foundry_cli::{ opts::{CoreBuildArgs, GlobalOpts}, @@ -317,9 +317,6 @@ impl TestArgs { } } - let config = Arc::new(config); - let test_options = TestOptions::new(&output, config.clone())?; - let should_debug = self.debug.is_some(); let should_draw = self.flamegraph || self.flamechart; @@ -346,6 +343,7 @@ impl TestArgs { }; // Prepare the test builder. + let config = Arc::new(config); let runner = MultiContractRunnerBuilder::new(config.clone()) .set_debug(should_debug) .set_decode_internal(decode_internal) @@ -353,7 +351,6 @@ impl TestArgs { .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) - .with_test_options(test_options) .enable_isolation(evm_opts.isolate) .alphanet(evm_opts.alphanet) .build(project_root, &output, env, evm_opts)?; diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 257760c4e94b..ddeada0a69c2 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -7,17 +7,6 @@ extern crate foundry_common; #[macro_use] extern crate tracing; -use alloy_primitives::U256; -use eyre::Result; -use foundry_compilers::ProjectCompileOutput; -use foundry_config::{ - figment::Figment, Config, FuzzConfig, InlineConfig, InvariantConfig, NatSpec, -}; -use proptest::test_runner::{ - FailurePersistence, FileFailurePersistence, RngAlgorithm, TestRng, TestRunner, -}; -use std::sync::Arc; - pub mod coverage; pub mod gas_report; @@ -34,109 +23,3 @@ pub mod result; // TODO: remove pub use foundry_common::traits::TestFilter; pub use foundry_evm::*; - -/// Test configuration. -#[derive(Clone, Debug, Default)] -pub struct TestOptions { - /// The base configuration. - pub config: Arc, - /// Per-test configuration. Merged onto `base_config`. - pub inline: InlineConfig, -} - -impl TestOptions { - /// Tries to create a new instance by detecting inline configurations from the project compile - /// output. - pub fn new(output: &ProjectCompileOutput, base_config: Arc) -> eyre::Result { - let natspecs: Vec = NatSpec::parse(output, &base_config.root); - let profiles = &base_config.profiles; - let mut inline = InlineConfig::new(); - for natspec in &natspecs { - inline.insert(natspec)?; - // Validate after parsing as TOML. - natspec.validate_profiles(profiles)?; - } - Ok(Self { config: base_config, inline }) - } - - /// Creates a new instance without parsing inline configuration. - pub fn new_unparsed(base_config: Arc) -> Self { - Self { config: base_config, inline: InlineConfig::new() } - } - - /// Returns the [`Figment`] for the configuration. - pub fn figment(&self, contract: &str, function: &str) -> Result { - Ok(self.inline.merge(contract, function, &self.config)) - } - - /// Returns a "fuzz" test runner instance. Parameters are used to select tight scoped fuzz - /// configs that apply for a contract-function pair. A fallback configuration is applied - /// if no specific setup is found for a given input. - /// - /// - `contract` is the id of the test contract, expressed as a relative path from the project - /// root. - /// - `function` is the name of the test function declared inside the test contract. - pub fn fuzz_runner(&self, contract: &str, function: &str) -> Result<(FuzzConfig, TestRunner)> { - let config: FuzzConfig = self.figment(contract, function)?.extract_inner("fuzz")?; - let failure_persist_path = config - .failure_persist_dir - .as_ref() - .unwrap() - .join(config.failure_persist_file.as_ref().unwrap()) - .into_os_string() - .into_string() - .unwrap(); - let runner = fuzzer_with_cases( - config.seed, - config.runs, - config.max_test_rejects, - Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))), - ); - Ok((config, runner)) - } - - /// Returns an "invariant" test runner instance. Parameters are used to select tight scoped fuzz - /// configs that apply for a contract-function pair. A fallback configuration is applied - /// if no specific setup is found for a given input. - /// - /// - `contract` is the id of the test contract, expressed as a relative path from the project - /// root. - /// - `function` is the name of the test function declared inside the test contract. - pub fn invariant_runner( - &self, - contract: &str, - function: &str, - ) -> Result<(InvariantConfig, TestRunner)> { - let figment = self.figment(contract, function)?; - let config: InvariantConfig = figment.extract_inner("invariant")?; - let seed: Option = figment.extract_inner("fuzz.seed").ok(); - let runner = fuzzer_with_cases(seed, config.runs, config.max_assume_rejects, None); - Ok((config, runner)) - } -} - -fn fuzzer_with_cases( - seed: Option, - cases: u32, - max_global_rejects: u32, - file_failure_persistence: Option>, -) -> TestRunner { - let config = proptest::test_runner::Config { - failure_persistence: file_failure_persistence, - cases, - max_global_rejects, - // Disable proptest shrink: for fuzz tests we provide single counterexample, - // for invariant tests we shrink outside proptest. - max_shrink_iters: 0, - ..Default::default() - }; - - if let Some(seed) = seed { - trace!(target: "forge::test", %seed, "building deterministic fuzzer"); - let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()); - TestRunner::new_with_rng(config, rng) - } else { - trace!(target: "forge::test", "building stochastic fuzzer"); - TestRunner::new(config) - } -} diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 572c3d4fe984..0372becb208a 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -2,20 +2,18 @@ use crate::{ progress::TestsProgress, result::SuiteResult, runner::LIBRARY_DEPLOYER, ContractRunner, - TestFilter, TestOptions, + TestFilter, }; use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, U256}; use eyre::Result; use foundry_common::{get_contract_name, shell::verbosity, ContractsByArtifact, TestFunctionExt}; -use foundry_compilers::{ - artifacts::Libraries, compilers::Compiler, Artifact, ArtifactId, ProjectCompileOutput, -}; -use foundry_config::Config; +use foundry_compilers::{artifacts::Libraries, Artifact, ArtifactId, ProjectCompileOutput}; +use foundry_config::{Config, InlineConfig}; use foundry_evm::{ backend::Backend, decode::RevertDecoder, - executors::ExecutorBuilder, + executors::{Executor, ExecutorBuilder}, fork::CreateFork, inspectors::CheatsConfig, opts::EvmOpts, @@ -48,38 +46,34 @@ pub struct MultiContractRunner { /// Mapping of contract name to JsonAbi, creation bytecode and library bytecode which /// needs to be deployed & linked against pub contracts: DeployableContracts, - /// The EVM instance used in the test runner - pub evm_opts: EvmOpts, - /// The configured evm - pub env: revm::primitives::Env, - /// The EVM spec - pub evm_spec: SpecId, - /// Revert decoder. Contains all known errors and their selectors. - pub revert_decoder: RevertDecoder, - /// The address which will be used as the `from` field in all EVM calls - pub sender: Option
, - /// The fork to use at launch - pub fork: Option, - /// Project config. - pub config: Arc, - /// Whether to collect coverage info - pub coverage: bool, - /// Whether to collect debug info - pub debug: bool, - /// Whether to enable steps tracking in the tracer. - pub decode_internal: InternalTraceMode, - /// Settings related to fuzz and/or invariant tests - pub test_options: TestOptions, - /// Whether to enable call isolation - pub isolation: bool, - /// Whether to enable Alphanet features. - pub alphanet: bool, /// Known contracts linked with computed library addresses. pub known_contracts: ContractsByArtifact, + /// Revert decoder. Contains all known errors and their selectors. + pub revert_decoder: RevertDecoder, /// Libraries to deploy. pub libs_to_deploy: Vec, /// Library addresses used to link contracts. pub libraries: Libraries, + + /// The fork to use at launch + pub fork: Option, + + /// The base configuration for the test runner. + pub tcfg: TestRunnerConfig, +} + +impl std::ops::Deref for MultiContractRunner { + type Target = TestRunnerConfig; + + fn deref(&self) -> &Self::Target { + &self.tcfg + } +} + +impl std::ops::DerefMut for MultiContractRunner { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.tcfg + } } impl MultiContractRunner { @@ -196,7 +190,7 @@ impl MultiContractRunner { let result = self.run_test_suite( id, contract, - db.clone(), + &db, filter, &tokio_handle, Some(&tests_progress), @@ -219,8 +213,7 @@ impl MultiContractRunner { } else { contracts.par_iter().for_each(|&(id, contract)| { let _guard = tokio_handle.enter(); - let result = - self.run_test_suite(id, contract, db.clone(), filter, &tokio_handle, None); + let result = self.run_test_suite(id, contract, &db, filter, &tokio_handle, None); let _ = tx.send((id.identifier(), result)); }) } @@ -230,7 +223,7 @@ impl MultiContractRunner { &self, artifact_id: &ArtifactId, contract: &TestContract, - db: Backend, + db: &Backend, filter: &dyn TestFilter, tokio_handle: &tokio::runtime::Handle, progress: Option<&TestsProgress>, @@ -238,35 +231,6 @@ impl MultiContractRunner { let identifier = artifact_id.identifier(); let mut span_name = identifier.as_str(); - let cheats_config = CheatsConfig::new( - &self.config, - self.evm_opts.clone(), - Some(self.known_contracts.clone()), - Some(artifact_id.name.clone()), - Some(artifact_id.version.clone()), - ); - - let trace_mode = TraceMode::default() - .with_debug(self.debug) - .with_decode_internal(self.decode_internal) - .with_verbosity(self.evm_opts.verbosity) - .with_state_changes(verbosity() > 4); - - let executor = ExecutorBuilder::new() - .inspectors(|stack| { - stack - .cheatcodes(Arc::new(cheats_config)) - .trace_mode(trace_mode) - .coverage(self.coverage) - .enable_isolation(self.isolation) - .alphanet(self.alphanet) - .create2_deployer(self.evm_opts.create2_deployer) - }) - .spec(self.evm_spec) - .gas_limit(self.evm_opts.gas_limit()) - .legacy_assertions(self.config.legacy_assertions) - .build(self.env.clone(), db); - if !enabled!(tracing::Level::TRACE) { span_name = get_contract_name(&identifier); } @@ -276,20 +240,16 @@ impl MultiContractRunner { debug!("start executing all tests in contract"); - let runner = ContractRunner { - name: &identifier, + let runner = ContractRunner::new( + &identifier, contract, - libs_to_deploy: &self.libs_to_deploy, - executor, - revert_decoder: &self.revert_decoder, - initial_balance: self.evm_opts.initial_balance, - sender: self.sender.unwrap_or_default(), - debug: self.debug, + self.tcfg.executor(self.known_contracts.clone(), artifact_id, db.clone()), progress, tokio_handle, span, - }; - let r = runner.run_tests(filter, &self.test_options, self.known_contracts.clone()); + self, + ); + let r = runner.run_tests(filter); debug!(duration=?r.duration, "executed all tests in contract"); @@ -297,6 +257,116 @@ impl MultiContractRunner { } } +/// Configuration for the test runner. +/// +/// This is modified after instantiation through inline config. +#[derive(Clone)] +pub struct TestRunnerConfig { + /// Project config. + pub config: Arc, + /// Inline configuration. + pub inline_config: Arc, + + /// EVM configuration. + pub evm_opts: EvmOpts, + /// EVM environment. + pub env: revm::primitives::Env, + /// EVM version. + pub spec_id: SpecId, + /// The address which will be used to deploy the initial contracts and send all transactions. + pub sender: Address, + + /// Whether to collect coverage info + pub coverage: bool, + /// Whether to collect debug info + pub debug: bool, + /// Whether to enable steps tracking in the tracer. + pub decode_internal: InternalTraceMode, + /// Whether to enable call isolation. + pub isolation: bool, + /// Whether to enable Alphanet features. + pub alphanet: bool, +} + +impl TestRunnerConfig { + /// Reconfigures all fields using the given `config`. + pub fn reconfigure_with(&mut self, config: Arc) { + debug_assert!(!Arc::ptr_eq(&self.config, &config)); + + // TODO: self.evm_opts + // TODO: self.env + self.spec_id = config.evm_spec_id(); + self.sender = config.sender; + // self.coverage = N/A; + // self.debug = N/A; + // self.decode_internal = N/A; + // self.isolation = N/A; + self.alphanet = config.alphanet; + + self.config = config; + } + + /// Configures the given executor with this configuration. + pub fn configure_executor(&self, executor: &mut Executor) { + // TODO: See above + + let inspector = executor.inspector_mut(); + // inspector.set_env(&self.env); + if let Some(cheatcodes) = inspector.cheatcodes.as_mut() { + cheatcodes.config = + Arc::new(cheatcodes.config.clone_with(&self.config, self.evm_opts.clone())); + } + inspector.tracing(self.trace_mode()); + inspector.collect_coverage(self.coverage); + inspector.enable_isolation(self.isolation); + inspector.alphanet(self.alphanet); + // inspector.set_create2_deployer(self.evm_opts.create2_deployer); + + // executor.env_mut().clone_from(&self.env); + executor.set_spec_id(self.spec_id); + // executor.set_gas_limit(self.evm_opts.gas_limit()); + executor.set_legacy_assertions(self.config.legacy_assertions); + } + + /// Creates a new executor with this configuration. + pub fn executor( + &self, + known_contracts: ContractsByArtifact, + artifact_id: &ArtifactId, + db: Backend, + ) -> Executor { + let cheats_config = Arc::new(CheatsConfig::new( + &self.config, + self.evm_opts.clone(), + Some(known_contracts), + Some(artifact_id.name.clone()), + Some(artifact_id.version.clone()), + )); + ExecutorBuilder::new() + .inspectors(|stack| { + stack + .cheatcodes(cheats_config) + .trace_mode(self.trace_mode()) + .coverage(self.coverage) + .enable_isolation(self.isolation) + .alphanet(self.alphanet) + .create2_deployer(self.evm_opts.create2_deployer) + }) + .spec_id(self.spec_id) + .gas_limit(self.evm_opts.gas_limit()) + .legacy_assertions(self.config.legacy_assertions) + .build(self.env.clone(), db) + } + + fn trace_mode(&self) -> TraceMode { + TraceMode::default() + .with_debug(self.debug) + .with_decode_internal(self.decode_internal) + .with_verbosity(self.evm_opts.verbosity) + .with_state_changes(verbosity() > 4) + } +} + /// Builder used for instantiating the multi-contract runner #[derive(Clone, Debug)] #[must_use = "builders do nothing unless you call `build` on them"] @@ -322,8 +392,6 @@ pub struct MultiContractRunnerBuilder { pub isolation: bool, /// Whether to enable Alphanet features. pub alphanet: bool, - /// Settings related to fuzz and/or invariant tests - pub test_options: Option, } impl MultiContractRunnerBuilder { @@ -337,7 +405,6 @@ impl MultiContractRunnerBuilder { coverage: Default::default(), debug: Default::default(), isolation: Default::default(), - test_options: Default::default(), decode_internal: Default::default(), alphanet: Default::default(), } @@ -363,11 +430,6 @@ impl MultiContractRunnerBuilder { self } - pub fn with_test_options(mut self, test_options: TestOptions) -> Self { - self.test_options = Some(test_options); - self - } - pub fn set_coverage(mut self, enable: bool) -> Self { self.coverage = enable; self @@ -395,10 +457,10 @@ impl MultiContractRunnerBuilder { /// Given an EVM, proceeds to return a runner which is able to execute all tests /// against that evm - pub fn build( + pub fn build( self, root: &Path, - output: &ProjectCompileOutput, + output: &ProjectCompileOutput, env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result { @@ -449,22 +511,28 @@ impl MultiContractRunnerBuilder { Ok(MultiContractRunner { contracts: deployable_contracts, - evm_opts, - env, - evm_spec: self.evm_spec.unwrap_or(SpecId::CANCUN), - sender: self.sender, revert_decoder, - fork: self.fork, - config: self.config, - coverage: self.coverage, - debug: self.debug, - decode_internal: self.decode_internal, - test_options: self.test_options.unwrap_or_default(), - isolation: self.isolation, - alphanet: self.alphanet, known_contracts, libs_to_deploy, libraries, + + fork: self.fork, + + tcfg: TestRunnerConfig { + evm_opts, + env, + spec_id: self.evm_spec.unwrap_or_else(|| self.config.evm_spec_id()), + sender: self.sender.unwrap_or(self.config.sender), + + coverage: self.coverage, + debug: self.debug, + decode_internal: self.decode_internal, + inline_config: Arc::new(InlineConfig::new_parsed(output, &self.config)?), + isolation: self.isolation, + alphanet: self.alphanet, + + config: self.config, + }, }) } } diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 7f02db577c0d..28b52d74c1f0 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -467,12 +467,12 @@ impl fmt::Display for TestResult { impl TestResult { /// Creates a new test result starting from test setup results. - pub fn new(setup: TestSetup) -> Self { + pub fn new(setup: &TestSetup) -> Self { Self { - labeled_addresses: setup.labels, - logs: setup.logs, - traces: setup.traces, - coverage: setup.coverage, + labeled_addresses: setup.labels.clone(), + logs: setup.logs.clone(), + traces: setup.traces.clone(), + coverage: setup.coverage.clone(), ..Default::default() } } @@ -496,27 +496,25 @@ impl TestResult { } /// Returns the skipped result for single test (used in skipped fuzz test too). - pub fn single_skip(mut self, reason: SkipReason) -> Self { + pub fn single_skip(&mut self, reason: SkipReason) { self.status = TestStatus::Skipped; self.reason = reason.0; - self } /// Returns the failed result with reason for single test. - pub fn single_fail(mut self, reason: Option) -> Self { + pub fn single_fail(&mut self, reason: Option) { self.status = TestStatus::Failure; self.reason = reason; - self } /// Returns the result for single test. Merges execution results (logs, labeled addresses, /// traces and coverages) in initial setup results. pub fn single_result( - mut self, + &mut self, success: bool, reason: Option, raw_call_result: RawCallResult, - ) -> Self { + ) { self.kind = TestKind::Unit { gas: raw_call_result.gas_used.wrapping_sub(raw_call_result.stipend) }; @@ -539,13 +537,11 @@ impl TestResult { self.gas_snapshots = cheatcodes.gas_snapshots; self.deprecated_cheatcodes = cheatcodes.deprecated; } - - self } /// Returns the result for a fuzzed test. Merges fuzz execution results (logs, labeled /// addresses, traces and coverages) in initial setup results. - pub fn fuzz_result(mut self, result: FuzzTestResult) -> Self { + pub fn fuzz_result(&mut self, result: FuzzTestResult) { self.kind = TestKind::Fuzz { median_gas: result.median_gas(false), mean_gas: result.mean_gas(false), @@ -572,26 +568,23 @@ impl TestResult { self.gas_report_traces = result.gas_report_traces.into_iter().map(|t| vec![t]).collect(); self.breakpoints = result.breakpoints.unwrap_or_default(); self.deprecated_cheatcodes = result.deprecated_cheatcodes; - - self } /// Returns the skipped result for invariant test. - pub fn invariant_skip(mut self, reason: SkipReason) -> Self { + pub fn invariant_skip(&mut self, reason: SkipReason) { self.kind = TestKind::Invariant { runs: 1, calls: 1, reverts: 1, metrics: HashMap::default() }; self.status = TestStatus::Skipped; self.reason = reason.0; - self } /// Returns the fail result for replayed invariant test. pub fn invariant_replay_fail( - mut self, + &mut self, replayed_entirely: bool, invariant_name: &String, call_sequence: Vec, - ) -> Self { + ) { self.kind = TestKind::Invariant { runs: 1, calls: 1, reverts: 1, metrics: HashMap::default() }; self.status = TestStatus::Failure; @@ -601,22 +594,20 @@ impl TestResult { Some(format!("{invariant_name} persisted failure revert")) }; self.counterexample = Some(CounterExample::Sequence(call_sequence)); - self } /// Returns the fail result for invariant test setup. - pub fn invariant_setup_fail(mut self, e: Report) -> Self { + pub fn invariant_setup_fail(&mut self, e: Report) { self.kind = TestKind::Invariant { runs: 0, calls: 0, reverts: 0, metrics: HashMap::default() }; self.status = TestStatus::Failure; self.reason = Some(format!("failed to set up invariant testing environment: {e}")); - self } /// Returns the invariant test result. #[allow(clippy::too_many_arguments)] pub fn invariant_result( - mut self, + &mut self, gas_report_traces: Vec>, success: bool, reason: Option, @@ -624,7 +615,7 @@ impl TestResult { cases: Vec, reverts: usize, metrics: Map, - ) -> Self { + ) { self.kind = TestKind::Invariant { runs: cases.len(), calls: cases.iter().map(|sequence| sequence.cases().len()).sum(), @@ -638,7 +629,6 @@ impl TestResult { self.reason = reason; self.counterexample = counterexample; self.gas_report_traces = gas_report_traces; - self } /// Returns `true` if this is the result of a fuzz test diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index fc2b89cb0815..0948df6d1b0d 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -2,20 +2,17 @@ use crate::{ fuzz::{invariant::BasicTxDetails, BaseCounterExample}, - multi_runner::{is_matching_test, TestContract}, + multi_runner::{is_matching_test, TestContract, TestRunnerConfig}, progress::{start_fuzz_progress, TestsProgress}, result::{SuiteResult, TestResult, TestSetup}, - TestFilter, TestOptions, + MultiContractRunner, TestFilter, }; use alloy_dyn_abi::DynSolValue; use alloy_json_abi::Function; -use alloy_primitives::{address, map::HashMap, Address, Bytes, U256}; +use alloy_primitives::{address, map::HashMap, Address, U256}; use eyre::Result; -use foundry_common::{ - contracts::{ContractsByAddress, ContractsByArtifact}, - TestFunctionExt, TestFunctionKind, -}; -use foundry_config::{FuzzConfig, InvariantConfig}; +use foundry_common::{contracts::ContractsByAddress, TestFunctionExt, TestFunctionKind}; +use foundry_config::Config; use foundry_evm::{ constants::CALLER, decode::RevertDecoder, @@ -33,9 +30,12 @@ use foundry_evm::{ }, traces::{load_contracts, TraceKind, TraceMode}, }; -use proptest::test_runner::TestRunner; +use proptest::test_runner::{ + FailurePersistence, FileFailurePersistence, RngAlgorithm, TestRng, TestRunner, +}; use rayon::prelude::*; -use std::{borrow::Cow, cmp::min, collections::BTreeMap, time::Instant}; +use std::{borrow::Cow, cmp::min, collections::BTreeMap, sync::Arc, time::Instant}; +use tracing::Span; /// When running tests, we deploy all external libraries present in the project. To avoid additional /// libraries affecting nonces of senders used in tests, we are using separate address to @@ -45,33 +45,56 @@ use std::{borrow::Cow, cmp::min, collections::BTreeMap, time::Instant}; pub const LIBRARY_DEPLOYER: Address = address!("1F95D37F27EA0dEA9C252FC09D5A6eaA97647353"); /// A type that executes all tests of a contract -#[derive(Clone, Debug)] pub struct ContractRunner<'a> { /// The name of the contract. - pub name: &'a str, + name: &'a str, /// The data of the contract. - pub contract: &'a TestContract, - /// The libraries that need to be deployed before the contract. - pub libs_to_deploy: &'a Vec, - /// The executor used by the runner. - pub executor: Executor, - /// Revert decoder. Contains all known errors. - pub revert_decoder: &'a RevertDecoder, - /// The initial balance of the test contract. - pub initial_balance: U256, - /// The address which will be used as the `from` field in all EVM calls. - pub sender: Address, - /// Whether debug traces should be generated. - pub debug: bool, + contract: &'a TestContract, + /// The EVM executor. + executor: Executor, /// Overall test run progress. - pub progress: Option<&'a TestsProgress>, + progress: Option<&'a TestsProgress>, /// The handle to the tokio runtime. - pub tokio_handle: &'a tokio::runtime::Handle, + tokio_handle: &'a tokio::runtime::Handle, /// The span of the contract. - pub span: tracing::Span, + span: tracing::Span, + /// The contract-level configuration. + tcfg: Cow<'a, TestRunnerConfig>, + /// The parent runner. + mcr: &'a MultiContractRunner, +} + +impl<'a> std::ops::Deref for ContractRunner<'a> { + type Target = Cow<'a, TestRunnerConfig>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.tcfg + } } -impl ContractRunner<'_> { +impl<'a> ContractRunner<'a> { + pub fn new( + name: &'a str, + contract: &'a TestContract, + executor: Executor, + progress: Option<&'a TestsProgress>, + tokio_handle: &'a tokio::runtime::Handle, + span: Span, + mcr: &'a MultiContractRunner, + ) -> Self { + Self { + name, + contract, + executor, + progress, + tokio_handle, + span, + tcfg: Cow::Borrowed(&mcr.tcfg), + mcr, + } + } + /// Deploys the test contract inside the runner from the sending account, and optionally runs /// the `setUp` function on the test contract. pub fn setup(&mut self, call_setup: bool) -> TestSetup { @@ -84,6 +107,8 @@ impl ContractRunner<'_> { fn _setup(&mut self, call_setup: bool) -> Result { trace!(call_setup, "setting up"); + self.apply_contract_inline_config()?; + // We max out their balance so that they can deploy and make calls. self.executor.set_balance(self.sender, U256::MAX)?; self.executor.set_balance(CALLER, U256::MAX)?; @@ -95,12 +120,12 @@ impl ContractRunner<'_> { self.executor.set_balance(LIBRARY_DEPLOYER, U256::MAX)?; let mut result = TestSetup::default(); - for code in self.libs_to_deploy.iter() { + for code in self.mcr.libs_to_deploy.iter() { let deploy_result = self.executor.deploy( LIBRARY_DEPLOYER, code.clone(), U256::ZERO, - Some(self.revert_decoder), + Some(&self.mcr.revert_decoder), ); let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?; result.extend(raw, TraceKind::Deployment); @@ -115,14 +140,14 @@ impl ContractRunner<'_> { // Set the contracts initial balance before deployment, so it is available during // construction - self.executor.set_balance(address, self.initial_balance)?; + self.executor.set_balance(address, self.initial_balance())?; // Deploy the test contract let deploy_result = self.executor.deploy( self.sender, self.contract.bytecode.clone(), U256::ZERO, - Some(self.revert_decoder), + Some(&self.mcr.revert_decoder), ); if let Ok(dr) = &deploy_result { debug_assert_eq!(dr.address, address); @@ -135,16 +160,16 @@ impl ContractRunner<'_> { } // Reset `self.sender`s, `CALLER`s and `LIBRARY_DEPLOYER`'s balance to the initial balance. - self.executor.set_balance(self.sender, self.initial_balance)?; - self.executor.set_balance(CALLER, self.initial_balance)?; - self.executor.set_balance(LIBRARY_DEPLOYER, self.initial_balance)?; + self.executor.set_balance(self.sender, self.initial_balance())?; + self.executor.set_balance(CALLER, self.initial_balance())?; + self.executor.set_balance(LIBRARY_DEPLOYER, self.initial_balance())?; self.executor.deploy_create2_deployer()?; // Optionally call the `setUp` function if call_setup { trace!("calling setUp"); - let res = self.executor.setup(None, address, Some(self.revert_decoder)); + let res = self.executor.setup(None, address, Some(&self.mcr.revert_decoder)); let (raw, reason) = RawCallResult::from_evm_result(res)?; result.extend(raw, TraceKind::Setup); result.reason = reason; @@ -155,6 +180,31 @@ impl ContractRunner<'_> { Ok(result) } + fn initial_balance(&self) -> U256 { + self.evm_opts.initial_balance + } + + /// Configures this runner with the inline configuration for the contract. + fn apply_contract_inline_config(&mut self) -> Result<()> { + if self.inline_config.contains_contract(self.name) { + let new_config = Arc::new(self.inline_config(None)?); + self.tcfg.to_mut().reconfigure_with(new_config); + let prev_tracer = self.executor.inspector_mut().tracer.take(); + self.tcfg.configure_executor(&mut self.executor); + // Don't set tracer here. + self.executor.inspector_mut().tracer = prev_tracer; + } + Ok(()) + } + + /// Returns the configuration for a contract or function. + fn inline_config(&self, func: Option<&Function>) -> Result { + let function = func.map(|f| f.name.as_str()).unwrap_or(""); + let config = + self.mcr.inline_config.merge(self.name, function, &self.config).extract::()?; + Ok(config) + } + /// Collect fixtures from test contract. /// /// Fixtures can be defined: @@ -210,12 +260,7 @@ impl ContractRunner<'_> { } /// Runs all tests for a contract whose names match the provided regular expression - pub fn run_tests( - mut self, - filter: &dyn TestFilter, - test_options: &TestOptions, - known_contracts: ContractsByArtifact, - ) -> SuiteResult { + pub fn run_tests(mut self, filter: &dyn TestFilter) -> SuiteResult { let start = Instant::now(); let mut warnings = Vec::new(); @@ -302,16 +347,16 @@ impl ContractRunner<'_> { .functions() .filter(|func| is_matching_test(func, filter)) .collect::>(); - let find_time = find_timer.elapsed(); debug!( "Found {} test functions out of {} in {:?}", functions.len(), self.contract.abi.functions().count(), - find_time, + find_timer.elapsed(), ); - let identified_contracts = has_invariants - .then(|| load_contracts(setup.traces.iter().map(|(_, t)| &t.arena), &known_contracts)); + let identified_contracts = has_invariants.then(|| { + load_contracts(setup.traces.iter().map(|(_, t)| &t.arena), &self.mcr.known_contracts) + }); let test_results = functions .par_iter() .map(|&func| { @@ -335,36 +380,12 @@ impl ContractRunner<'_> { ) .entered(); - let setup = setup.clone(); - let mut res = match kind { - TestFunctionKind::UnitTest { should_fail } => { - self.run_unit_test(func, should_fail, setup) - } - TestFunctionKind::FuzzTest { should_fail } => { - match test_options.fuzz_runner(self.name, &func.name) { - Ok((fuzz_config, runner)) => { - self.run_fuzz_test(func, should_fail, runner, setup, fuzz_config) - } - Err(err) => TestResult::fail(err.to_string()), - } - } - TestFunctionKind::InvariantTest => { - match test_options.invariant_runner(self.name, &func.name) { - Ok((invariant_config, runner)) => self.run_invariant_test( - runner, - setup, - invariant_config, - func, - call_after_invariant, - &known_contracts, - identified_contracts.as_ref().unwrap(), - ), - Err(err) => TestResult::fail(err.to_string()), - } - } - _ => unreachable!(), - }; - + let mut res = FunctionRunner::new(&self, &setup).run( + func, + kind, + call_after_invariant, + identified_contracts.as_ref(), + ); res.duration = start.elapsed(); (sig, res) @@ -374,6 +395,83 @@ impl ContractRunner<'_> { let duration = start.elapsed(); SuiteResult::new(duration, test_results, warnings) } +} + +/// Executes a single test function, returning a [`TestResult`]. +struct FunctionRunner<'a> { + /// The function-level configuration. + tcfg: Cow<'a, TestRunnerConfig>, + /// The EVM executor. + executor: Cow<'a, Executor>, + /// The parent runner. + cr: &'a ContractRunner<'a>, + /// The address of the test contract. + address: Address, + /// The test setup result. + setup: &'a TestSetup, + /// The test result. Returned after running the test. + result: TestResult, +} + +impl<'a> std::ops::Deref for FunctionRunner<'a> { + type Target = Cow<'a, TestRunnerConfig>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.tcfg + } +} + +impl<'a> FunctionRunner<'a> { + fn new(cr: &'a ContractRunner<'a>, setup: &'a TestSetup) -> Self { + Self { + tcfg: match &cr.tcfg { + Cow::Borrowed(tcfg) => Cow::Borrowed(tcfg), + Cow::Owned(tcfg) => Cow::Owned(tcfg.clone()), + }, + executor: Cow::Borrowed(&cr.executor), + cr, + address: setup.address, + setup, + result: TestResult::new(setup), + } + } + + fn revert_decoder(&self) -> &'a RevertDecoder { + &self.cr.mcr.revert_decoder + } + + /// Configures this runner with the inline configuration for the contract. + fn apply_function_inline_config(&mut self, func: &Function) -> Result<()> { + if self.inline_config.contains_function(self.cr.name, &func.name) { + let new_config = Arc::new(self.cr.inline_config(Some(func))?); + self.tcfg.to_mut().reconfigure_with(new_config); + self.tcfg.configure_executor(self.executor.to_mut()); + } + Ok(()) + } + + fn run( + mut self, + func: &Function, + kind: TestFunctionKind, + call_after_invariant: bool, + identified_contracts: Option<&ContractsByAddress>, + ) -> TestResult { + if let Err(e) = self.apply_function_inline_config(func) { + self.result.single_fail(Some(e.to_string())); + return self.result; + } + + match kind { + TestFunctionKind::UnitTest { should_fail } => self.run_unit_test(func, should_fail), + TestFunctionKind::FuzzTest { should_fail } => self.run_fuzz_test(func, should_fail), + TestFunctionKind::InvariantTest => { + self.run_invariant_test(func, call_after_invariant, identified_contracts.unwrap()) + } + _ => unreachable!(), + } + } /// Runs a single unit test. /// @@ -383,80 +481,77 @@ impl ContractRunner<'_> { /// (therefore the unit test call will be made on modified state). /// State modifications of before test txes and unit test function call are discarded after /// test ends, similar to `eth_call`. - pub fn run_unit_test( - &self, - func: &Function, - should_fail: bool, - setup: TestSetup, - ) -> TestResult { + fn run_unit_test(mut self, func: &Function, should_fail: bool) -> TestResult { // Prepare unit test execution. - let (executor, test_result, address) = match self.prepare_test(func, setup) { - Ok(res) => res, - Err(res) => return res, - }; + if self.prepare_test(func).is_err() { + return self.result; + } // Run current unit test. - let (mut raw_call_result, reason) = match executor.call( + let (mut raw_call_result, reason) = match self.executor.call( self.sender, - address, + self.address, func, &[], U256::ZERO, - Some(self.revert_decoder), + Some(self.revert_decoder()), ) { Ok(res) => (res.raw, None), Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)), - Err(EvmError::Skip(reason)) => return test_result.single_skip(reason), - Err(err) => return test_result.single_fail(Some(err.to_string())), + Err(EvmError::Skip(reason)) => { + self.result.single_skip(reason); + return self.result; + } + Err(err) => { + self.result.single_fail(Some(err.to_string())); + return self.result; + } }; - let success = executor.is_raw_call_mut_success(address, &mut raw_call_result, should_fail); - test_result.single_result(success, reason, raw_call_result) + let success = + self.executor.is_raw_call_mut_success(self.address, &mut raw_call_result, should_fail); + self.result.single_result(success, reason, raw_call_result); + self.result } - #[allow(clippy::too_many_arguments)] - pub fn run_invariant_test( - &self, - runner: TestRunner, - setup: TestSetup, - invariant_config: InvariantConfig, + fn run_invariant_test( + mut self, func: &Function, call_after_invariant: bool, - known_contracts: &ContractsByArtifact, identified_contracts: &ContractsByAddress, ) -> TestResult { - let address = setup.address; - let fuzz_fixtures = setup.fuzz_fixtures.clone(); - let mut test_result = TestResult::new(setup); - // First, run the test normally to see if it needs to be skipped. if let Err(EvmError::Skip(reason)) = self.executor.call( self.sender, - address, + self.address, func, &[], U256::ZERO, - Some(self.revert_decoder), + Some(self.revert_decoder()), ) { - return test_result.invariant_skip(reason); + self.result.invariant_skip(reason); + return self.result; }; + let runner = self.invariant_runner(); + let invariant_config = &self.config.invariant; + let mut evm = InvariantExecutor::new( - self.executor.clone(), + self.clone_executor(), runner, invariant_config.clone(), identified_contracts, - known_contracts, + &self.cr.mcr.known_contracts, ); let invariant_contract = InvariantContract { - address, + address: self.address, invariant_function: func, call_after_invariant, - abi: &self.contract.abi, + abi: &self.cr.contract.abi, }; - let failure_dir = invariant_config.clone().failure_dir(self.name); - let failure_file = failure_dir.join(invariant_contract.invariant_function.clone().name); + let failure_dir = invariant_config.clone().failure_dir(self.cr.name); + let failure_file = failure_dir.join(&invariant_contract.invariant_function.name); // Try to replay recorded failure if any. if let Ok(call_sequence) = @@ -474,7 +569,7 @@ impl ContractRunner<'_> { }) .collect::>(); if let Ok((success, replayed_entirely)) = check_sequence( - self.executor.clone(), + self.clone_executor(), &txes, (0..min(txes.len(), invariant_config.depth as usize)).collect(), invariant_contract.address, @@ -492,34 +587,40 @@ impl ContractRunner<'_> { // exit without executing new runs. let _ = replay_run( &invariant_contract, - self.executor.clone(), - known_contracts, + self.clone_executor(), + &self.cr.mcr.known_contracts, identified_contracts.clone(), - &mut test_result.logs, - &mut test_result.traces, - &mut test_result.coverage, - &mut test_result.deprecated_cheatcodes, + &mut self.result.logs, + &mut self.result.traces, + &mut self.result.coverage, + &mut self.result.deprecated_cheatcodes, &txes, ); - return test_result.invariant_replay_fail( + self.result.invariant_replay_fail( replayed_entirely, &invariant_contract.invariant_function.name, call_sequence, - ) + ); + return self.result; } } } let progress = - start_fuzz_progress(self.progress, self.name, &func.name, invariant_config.runs); - let invariant_result = - match evm.invariant_fuzz(invariant_contract.clone(), &fuzz_fixtures, progress.as_ref()) - { - Ok(x) => x, - Err(e) => return test_result.invariant_setup_fail(e), - }; + start_fuzz_progress(self.cr.progress, self.cr.name, &func.name, invariant_config.runs); + let invariant_result = match evm.invariant_fuzz( + invariant_contract.clone(), + &self.setup.fuzz_fixtures, + progress.as_ref(), + ) { + Ok(x) => x, + Err(e) => { + self.result.invariant_setup_fail(e); + return self.result; + } + }; // Merge coverage collected during invariant run with test setup coverage. - test_result.merge_coverages(invariant_result.coverage); + self.result.merge_coverages(invariant_result.coverage); let mut counterexample = None; let success = invariant_result.error.is_none(); @@ -535,13 +636,13 @@ impl ContractRunner<'_> { match replay_error( &case_data, &invariant_contract, - self.executor.clone(), - known_contracts, + self.clone_executor(), + &self.cr.mcr.known_contracts, identified_contracts.clone(), - &mut test_result.logs, - &mut test_result.traces, - &mut test_result.coverage, - &mut test_result.deprecated_cheatcodes, + &mut self.result.logs, + &mut self.result.traces, + &mut self.result.coverage, + &mut self.result.deprecated_cheatcodes, progress.as_ref(), ) { Ok(call_sequence) => { @@ -571,13 +672,13 @@ impl ContractRunner<'_> { _ => { if let Err(err) = replay_run( &invariant_contract, - self.executor.clone(), - known_contracts, + self.clone_executor(), + &self.cr.mcr.known_contracts, identified_contracts.clone(), - &mut test_result.logs, - &mut test_result.traces, - &mut test_result.coverage, - &mut test_result.deprecated_cheatcodes, + &mut self.result.logs, + &mut self.result.traces, + &mut self.result.coverage, + &mut self.result.deprecated_cheatcodes, &invariant_result.last_run_inputs, ) { error!(%err, "Failed to replay last invariant run"); @@ -585,7 +686,7 @@ impl ContractRunner<'_> { } } - test_result.invariant_result( + self.result.invariant_result( invariant_result.gas_report_traces, success, reason, @@ -593,7 +694,8 @@ impl ContractRunner<'_> { invariant_result.cases, invariant_result.reverts, invariant_result.metrics, - ) + ); + self.result } /// Runs a fuzzed test. @@ -605,35 +707,31 @@ impl ContractRunner<'_> { /// (therefore the fuzz test will use the modified state). /// State modifications of before test txes and fuzz test are discarded after test ends, /// similar to `eth_call`. - pub fn run_fuzz_test( - &self, - func: &Function, - should_fail: bool, - runner: TestRunner, - setup: TestSetup, - fuzz_config: FuzzConfig, - ) -> TestResult { - let progress = start_fuzz_progress(self.progress, self.name, &func.name, fuzz_config.runs); - + fn run_fuzz_test(mut self, func: &Function, should_fail: bool) -> TestResult { // Prepare fuzz test execution. - let fuzz_fixtures = setup.fuzz_fixtures.clone(); - let (executor, test_result, address) = match self.prepare_test(func, setup) { - Ok(res) => res, - Err(res) => return res, - }; + if self.prepare_test(func).is_err() { + return self.result; + } + + let runner = self.fuzz_runner(); + let fuzz_config = self.config.fuzz.clone(); + + let progress = + start_fuzz_progress(self.cr.progress, self.cr.name, &func.name, fuzz_config.runs); // Run fuzz test. let fuzzed_executor = - FuzzedExecutor::new(executor.into_owned(), runner, self.sender, fuzz_config); + FuzzedExecutor::new(self.executor.into_owned(), runner, self.tcfg.sender, fuzz_config); let result = fuzzed_executor.fuzz( func, - &fuzz_fixtures, - address, + &self.setup.fuzz_fixtures, + self.address, should_fail, - self.revert_decoder, + &self.cr.mcr.revert_decoder, progress.as_ref(), ); - test_result.fuzz_result(result) + self.result.fuzz_result(result); + self.result } /// Prepares single unit test and fuzz test execution: @@ -645,20 +743,15 @@ impl ContractRunner<'_> { /// /// Unit tests within same contract (or even current test) are valid options for before test tx /// configuration. Test execution stops if any of before test txes fails. - fn prepare_test( - &self, - func: &Function, - setup: TestSetup, - ) -> Result<(Cow<'_, Executor>, TestResult, Address), TestResult> { - let address = setup.address; - let mut executor = Cow::Borrowed(&self.executor); - let mut test_result = TestResult::new(setup); + fn prepare_test(&mut self, func: &Function) -> Result<(), ()> { + let address = self.setup.address; // Apply before test configured functions (if any). - if self.contract.abi.functions().filter(|func| func.name.is_before_test_setup()).count() == + if self.cr.contract.abi.functions().filter(|func| func.name.is_before_test_setup()).count() == 1 { - for calldata in executor + for calldata in self + .executor .call_sol_default( address, &ITest::beforeTestSetupCall { testSelector: func.selector() }, @@ -666,22 +759,84 @@ impl ContractRunner<'_> { .beforeTestCalldata { // Apply before test configured calldata. - match executor.to_mut().transact_raw(self.sender, address, calldata, U256::ZERO) { + match self.executor.to_mut().transact_raw( + self.tcfg.sender, + address, + calldata, + U256::ZERO, + ) { Ok(call_result) => { let reverted = call_result.reverted; // Merge tx result traces in unit test result. - test_result.extend(call_result); + self.result.extend(call_result); // To continue unit test execution the call should not revert. if reverted { - return Err(test_result.single_fail(None)) + self.result.single_fail(None); + return Err(()); } } - Err(_) => return Err(test_result.single_fail(None)), + Err(_) => { + self.result.single_fail(None); + return Err(()); + } } } } - Ok((executor, test_result, address)) + Ok(()) + } + + fn fuzz_runner(&self) -> TestRunner { + let config = &self.config.fuzz; + let failure_persist_path = config + .failure_persist_dir + .as_ref() + .unwrap() + .join(config.failure_persist_file.as_ref().unwrap()) + .into_os_string() + .into_string() + .unwrap(); + fuzzer_with_cases( + config.seed, + config.runs, + config.max_test_rejects, + Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))), + ) + } + + fn invariant_runner(&self) -> TestRunner { + let config = &self.config.invariant; + fuzzer_with_cases(self.config.fuzz.seed, config.runs, config.max_assume_rejects, None) + } + + fn clone_executor(&self) -> Executor { + self.executor.clone().into_owned() + } +} + +fn fuzzer_with_cases( + seed: Option, + cases: u32, + max_global_rejects: u32, + file_failure_persistence: Option>, +) -> TestRunner { + let config = proptest::test_runner::Config { + failure_persistence: file_failure_persistence, + cases, + max_global_rejects, + // Disable proptest shrink: for fuzz tests we provide single counterexample, + // for invariant tests we shrink outside proptest. + max_shrink_iters: 0, + ..Default::default() + }; + + if let Some(seed) = seed { + trace!(target: "forge::test", %seed, "building deterministic fuzzer"); + let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>()); + TestRunner::new_with_rng(config, rng) + } else { + trace!(target: "forge::test", "building stochastic fuzzer"); + TestRunner::new(config) } } diff --git a/crates/forge/tests/cli/inline_config.rs b/crates/forge/tests/cli/inline_config.rs index de585a48ce17..31da29d21ea8 100644 --- a/crates/forge/tests/cli/inline_config.rs +++ b/crates/forge/tests/cli/inline_config.rs @@ -147,14 +147,14 @@ forgetest!(invalid_value, |prj, cmd| { Compiler run successful! Ran 1 test for test/inline.sol:Inline -[FAIL: invalid type: found sequence, expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +[FAIL: invalid type: found sequence, expected u32 for key "default.fuzz.runs" in inline config] setUp() ([GAS]) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/inline.sol:Inline -[FAIL: invalid type: found sequence, expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +[FAIL: invalid type: found sequence, expected u32 for key "default.fuzz.runs" in inline config] setUp() ([GAS]) Encountered a total of 1 failing tests, 0 tests succeeded @@ -179,16 +179,86 @@ forgetest!(invalid_value_2, |prj, cmd| { Compiler run successful! Ran 1 test for test/inline.sol:Inline -[FAIL: invalid type: found string "2", expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +[FAIL: invalid type: found string "2", expected u32 for key "default.fuzz.runs" in inline config] setUp() ([GAS]) Suite result: FAILED. 0 passed; 1 failed; 0 skipped; [ELAPSED] Ran 1 test suite [ELAPSED]: 0 tests passed, 1 failed, 0 skipped (1 total tests) Failing tests: Encountered 1 failing test in test/inline.sol:Inline -[FAIL: invalid type: found string "2", expected u32 for key "default.runs.fuzz" in inline config] test(bool) ([GAS]) +[FAIL: invalid type: found string "2", expected u32 for key "default.fuzz.runs" in inline config] setUp() ([GAS]) Encountered a total of 1 failing tests, 0 tests succeeded "#]]); }); + +forgetest_init!(evm_version, |prj, cmd| { + prj.wipe_contracts(); + prj.add_test( + "inline.sol", + r#" + import {Test} from "forge-std/Test.sol"; + + contract Dummy { + function getBlobBaseFee() public returns (uint256) { + return block.blobbasefee; + } + } + + contract FunctionConfig is Test { + Dummy dummy; + + function setUp() public { + dummy = new Dummy(); + } + + /// forge-config: default.evm_version = "shanghai" + function test_old() public { + vm.expectRevert(); + dummy.getBlobBaseFee(); + } + + function test_new() public { + dummy.getBlobBaseFee(); + } + } + + /// forge-config: default.evm_version = "shanghai" + contract ContractConfig is Test { + Dummy dummy; + + function setUp() public { + dummy = new Dummy(); + } + + function test_old() public { + vm.expectRevert(); + dummy.getBlobBaseFee(); + } + + /// forge-config: default.evm_version = "cancun" + function test_new() public { + dummy.getBlobBaseFee(); + } + } + "#, + ) + .unwrap(); + + cmd.arg("test").arg("--evm-version=cancun").assert_success().stdout_eq(str![[r#" +... +Ran 2 tests for test/inline.sol:FunctionConfig +[PASS] test_new() ([GAS]) +[PASS] test_old() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 2 tests for test/inline.sol:ContractConfig +[PASS] test_new() ([GAS]) +[PASS] test_old() ([GAS]) +Suite result: ok. 2 passed; 0 failed; 0 skipped; [ELAPSED] + +Ran 2 test suites [ELAPSED]: 4 tests passed, 0 failed, 0 skipped (4 total tests) + +"#]]); +}); diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index 9cabd998a01a..655fae4db254 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -31,8 +31,8 @@ impl TestConfig { Self { runner, should_fail: false, filter } } - pub fn evm_spec(mut self, spec: SpecId) -> Self { - self.runner.evm_spec = spec; + pub fn spec_id(mut self, spec: SpecId) -> Self { + self.runner.spec_id = spec; self } diff --git a/crates/forge/tests/it/spec.rs b/crates/forge/tests/it/spec.rs index aed2063a0fba..52e581c33c92 100644 --- a/crates/forge/tests/it/spec.rs +++ b/crates/forge/tests/it/spec.rs @@ -7,8 +7,5 @@ use foundry_test_utils::Filter; #[tokio::test(flavor = "multi_thread")] async fn test_shanghai_compat() { let filter = Filter::new("", "ShanghaiCompat", ".*spec"); - TestConfig::with_filter(TEST_DATA_PARIS.runner(), filter) - .evm_spec(SpecId::SHANGHAI) - .run() - .await; + TestConfig::with_filter(TEST_DATA_PARIS.runner(), filter).spec_id(SpecId::SHANGHAI).run().await; } diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 54985b9b6154..937f582f4e4f 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -2,9 +2,7 @@ use alloy_chains::NamedChain; use alloy_primitives::U256; -use forge::{ - revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, TestOptions, -}; +use forge::{revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder}; use foundry_compilers::{ artifacts::{EvmVersion, Libraries, Settings}, utils::RuntimeOrHandle, @@ -177,9 +175,7 @@ impl ForgeTestData { pub fn base_runner(&self) -> MultiContractRunnerBuilder { init_tracing(); let config = self.config.clone(); - let mut runner = MultiContractRunnerBuilder::new(config.clone()) - .sender(self.config.sender) - .with_test_options(TestOptions::new_unparsed(config)); + let mut runner = MultiContractRunnerBuilder::new(config).sender(self.config.sender); if self.profile.is_paris() { runner = runner.evm_spec(SpecId::MERGE); } @@ -216,7 +212,6 @@ impl ForgeTestData { builder .enable_isolation(opts.isolate) .sender(config.sender) - .with_test_options(TestOptions::new(&self.output, config.clone()).unwrap()) .build(root, &self.output, opts.local_evm_env(), opts) .unwrap() } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index ccf5eab2ad0d..5f5543912fe2 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -618,7 +618,7 @@ impl ScriptConfig { .alphanet(self.evm_opts.alphanet) .create2_deployer(self.evm_opts.create2_deployer) }) - .spec(self.config.evm_spec_id()) + .spec_id(self.config.evm_spec_id()) .gas_limit(self.evm_opts.gas_limit()) .legacy_assertions(self.config.legacy_assertions); From 7d0b0a0371765afa0734197dc01f9f7f4d5d4c1b Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:21:22 +0200 Subject: [PATCH 3/4] chore(deps): bump foundry-compilers 0.12.4 (#9455) --- Cargo.lock | 34 ++++++++++++++++----------------- crates/cheatcodes/src/string.rs | 11 ++--------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea8b8da749b3..87ac75dee06e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3864,9 +3864,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6131af72175c55aa531c4851290d9cf67af7a82b03f20a8a9d28dba81b9fd3" +checksum = "daece74fc0b127e587ac343405283577b4fe9d0195317998c3031778b5f799cd" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3881,7 +3881,6 @@ dependencies = [ "home", "itertools 0.13.0", "md-5", - "once_cell", "path-slash", "rand", "rayon", @@ -3893,7 +3892,7 @@ dependencies = [ "svm-rs", "svm-rs-builds", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.3", "tokio", "tracing", "winnow", @@ -3902,9 +3901,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522c473e7a111b81f0d47f8a65ca1ef0642c8c8d1ca2d1e230338210d16bb02" +checksum = "c0c6b0571a37cc9860103df548330eaceb36e9438cb0264ec63e5db74031fa4f" dependencies = [ "foundry-compilers-artifacts-solc", "foundry-compilers-artifacts-vyper", @@ -3912,9 +3911,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-solc" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cd569a0c38d232244932e71933b54e418cddcc8f0c16fd8af96dd844b27788" +checksum = "278cc3f1f4f628793ae6c0db38b0d3fe4124ab0bc10da081b1eb365766512e6d" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3927,7 +3926,7 @@ dependencies = [ "serde", "serde_json", "serde_repr", - "thiserror 1.0.69", + "thiserror 2.0.3", "tokio", "tracing", "walkdir", @@ -3936,9 +3935,9 @@ dependencies = [ [[package]] name = "foundry-compilers-artifacts-vyper" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b80563ba10981ec4a9667fda9318d02721a81f248ae73303603388580150a35" +checksum = "196b16fcfbbe69a315d64f74dd817ba6843f7749c64124b937118551414b9b90" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3951,15 +3950,14 @@ dependencies = [ [[package]] name = "foundry-compilers-core" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a840a87cb4845d208df3390a12d3724e6d1cc1268a51ac334a01f80f9b6419b" +checksum = "1eb08a74eb12b281038bbf74cbbd76cc3c85546896628d4c7c769c816ab4104b" dependencies = [ "alloy-primitives", "cfg-if", "dunce", "fs_extra", - "once_cell", "path-slash", "regex", "semver 1.0.23", @@ -3967,7 +3965,7 @@ dependencies = [ "serde_json", "svm-rs", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.3", "tokio", "walkdir", ] @@ -7114,7 +7112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.11.0", "proc-macro2", "quote", "syn 2.0.90", @@ -8567,7 +8565,7 @@ dependencies = [ "const-hex", "derive_builder", "dunce", - "itertools 0.13.0", + "itertools 0.11.0", "itoa", "lasso", "match_cfg", @@ -8603,7 +8601,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.6.0", "bumpalo", - "itertools 0.13.0", + "itertools 0.11.0", "memchr", "num-bigint", "num-rational", diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index a4c06eef650b..080d9bc0820f 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -17,7 +17,7 @@ impl Cheatcode for toString_0Call { impl Cheatcode for toString_1Call { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { value } = self; - Ok(hex::encode_prefixed(value).abi_encode()) + Ok(value.to_string().abi_encode()) } } @@ -95,7 +95,6 @@ impl Cheatcode for parseBoolCall { } } -// toLowercase impl Cheatcode for toLowercaseCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; @@ -103,7 +102,6 @@ impl Cheatcode for toLowercaseCall { } } -// toUppercase impl Cheatcode for toUppercaseCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; @@ -111,7 +109,6 @@ impl Cheatcode for toUppercaseCall { } } -// trim impl Cheatcode for trimCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input } = self; @@ -119,7 +116,6 @@ impl Cheatcode for trimCall { } } -// Replace impl Cheatcode for replaceCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, from, to } = self; @@ -127,7 +123,6 @@ impl Cheatcode for replaceCall { } } -// Split impl Cheatcode for splitCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, delimiter } = self; @@ -136,7 +131,6 @@ impl Cheatcode for splitCall { } } -// indexOf impl Cheatcode for indexOfCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { input, key } = self; @@ -144,7 +138,6 @@ impl Cheatcode for indexOfCall { } } -// contains impl Cheatcode for containsCall { fn apply(&self, _state: &mut Cheatcodes) -> Result { let Self { subject, search } = self; @@ -202,7 +195,7 @@ fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option { - if !s.starts_with("0x") && s.chars().all(|c| c.is_ascii_hexdigit()) { + if !s.starts_with("0x") && hex::check_raw(s) { return Some(Err("missing hex prefix (\"0x\") for hex string")); } } From b7a065f79fa63c80ece43e05b5e521ae269b4635 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:56:00 +0200 Subject: [PATCH 4/4] perf(coverage): improve HitMap merging and internal repr (#9456) --- crates/evm/coverage/src/lib.rs | 88 +++++++++++++++++--------------- crates/forge/bin/cmd/coverage.rs | 4 +- crates/forge/src/coverage.rs | 5 +- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 8d5575631f84..345261ad5b2c 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -5,19 +5,20 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#[macro_use] -extern crate foundry_common; - #[macro_use] extern crate tracing; -use alloy_primitives::{map::HashMap, Bytes, B256}; -use eyre::{Context, Result}; +use alloy_primitives::{ + map::{B256HashMap, HashMap}, + Bytes, +}; +use eyre::Result; use foundry_compilers::artifacts::sourcemap::SourceMap; use semver::Version; use std::{ collections::BTreeMap, fmt::Display, + num::NonZeroU32, ops::{Deref, DerefMut, Range}, path::{Path, PathBuf}, sync::Arc, @@ -119,22 +120,21 @@ impl CoverageReport { is_deployed_code: bool, ) -> Result<()> { // Add bytecode level hits - let e = self - .bytecode_hits + self.bytecode_hits .entry(contract_id.clone()) - .or_insert_with(|| HitMap::new(hit_map.bytecode.clone())); - e.merge(hit_map).wrap_err_with(|| format!("{contract_id:?}"))?; + .and_modify(|m| m.merge(hit_map)) + .or_insert_with(|| hit_map.clone()); // Add source level hits if let Some(anchors) = self.anchors.get(contract_id) { let anchors = if is_deployed_code { &anchors.1 } else { &anchors.0 }; for anchor in anchors { - if let Some(&hits) = hit_map.hits.get(&anchor.instruction) { + if let Some(hits) = hit_map.get(anchor.instruction) { self.items .get_mut(&contract_id.version) .and_then(|items| items.get_mut(anchor.item_id)) .expect("Anchor refers to non-existent coverage item") - .hits += hits; + .hits += hits.get(); } } } @@ -160,9 +160,10 @@ impl CoverageReport { /// A collection of [`HitMap`]s. #[derive(Clone, Debug, Default)] -pub struct HitMaps(pub HashMap); +pub struct HitMaps(pub B256HashMap); impl HitMaps { + /// Merges two `Option`. pub fn merge_opt(a: &mut Option, b: Option) { match (a, b) { (_, None) => {} @@ -171,17 +172,14 @@ impl HitMaps { } } + /// Merges two `HitMaps`. pub fn merge(&mut self, other: Self) { - for (code_hash, hit_map) in other.0 { - if let Some(HitMap { hits: extra_hits, .. }) = self.insert(code_hash, hit_map) { - for (pc, hits) in extra_hits { - self.entry(code_hash) - .and_modify(|map| *map.hits.entry(pc).or_default() += hits); - } - } + for (code_hash, other) in other.0 { + self.entry(code_hash).and_modify(|e| e.merge(&other)).or_insert(other); } } + /// Merges two `HitMaps`. pub fn merged(mut self, other: Self) -> Self { self.merge(other); self @@ -189,7 +187,7 @@ impl HitMaps { } impl Deref for HitMaps { - type Target = HashMap; + type Target = B256HashMap; fn deref(&self) -> &Self::Target { &self.0 @@ -207,40 +205,46 @@ impl DerefMut for HitMaps { /// Contains low-level data about hit counters for the instructions in the bytecode of a contract. #[derive(Clone, Debug)] pub struct HitMap { - pub bytecode: Bytes, - pub hits: BTreeMap, + bytecode: Bytes, + hits: HashMap, } impl HitMap { + /// Create a new hitmap with the given bytecode. pub fn new(bytecode: Bytes) -> Self { - Self { bytecode, hits: BTreeMap::new() } + Self { bytecode, hits: Default::default() } + } + + /// Returns the bytecode. + pub fn bytecode(&self) -> &Bytes { + &self.bytecode } - /// Increase the hit counter for the given program counter. + /// Returns the number of hits for the given program counter. + pub fn get(&self, pc: usize) -> Option { + NonZeroU32::new(self.hits.get(&Self::cvt_pc(pc)).copied().unwrap_or(0)) + } + + /// Increase the hit counter by 1 for the given program counter. pub fn hit(&mut self, pc: usize) { - *self.hits.entry(pc).or_default() += 1; + self.hits(pc, 1) + } + + /// Increase the hit counter by `hits` for the given program counter. + pub fn hits(&mut self, pc: usize, hits: u32) { + *self.hits.entry(Self::cvt_pc(pc)).or_default() += hits; } /// Merge another hitmap into this, assuming the bytecode is consistent - pub fn merge(&mut self, other: &Self) -> Result<(), eyre::Report> { - for (pc, hits) in &other.hits { - *self.hits.entry(*pc).or_default() += hits; + pub fn merge(&mut self, other: &Self) { + for (&pc, &hits) in &other.hits { + self.hits(pc as usize, hits); } - Ok(()) } - pub fn consistent_bytecode(&self, hm1: &Self, hm2: &Self) -> bool { - // Consider the bytecodes consistent if they are the same out as far as the - // recorded hits - let len1 = hm1.hits.last_key_value(); - let len2 = hm2.hits.last_key_value(); - if let (Some(len1), Some(len2)) = (len1, len2) { - let len = std::cmp::max(len1.0, len2.0); - let ok = hm1.bytecode.0[..*len] == hm2.bytecode.0[..*len]; - let _ = sh_println!("consistent_bytecode: {}, {}, {}, {}", ok, len1.0, len2.0, len); - return ok; - } - true + #[inline] + fn cvt_pc(pc: usize) -> u32 { + pc.try_into().expect("4GiB bytecode") } } @@ -311,7 +315,7 @@ pub struct CoverageItem { /// The location of the item in the source code. pub loc: SourceLocation, /// The number of times this item was hit. - pub hits: u64, + pub hits: u32, } impl Display for CoverageItem { diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index f1c862d81a52..d995e764df5b 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -250,10 +250,10 @@ impl CoverageArgs { for result in suite.test_results.values() { let Some(hit_maps) = result.coverage.as_ref() else { continue }; for map in hit_maps.0.values() { - if let Some((id, _)) = known_contracts.find_by_deployed_code(&map.bytecode) { + if let Some((id, _)) = known_contracts.find_by_deployed_code(map.bytecode()) { hits.push((id, map, true)); } else if let Some((id, _)) = - known_contracts.find_by_creation_code(&map.bytecode) + known_contracts.find_by_creation_code(map.bytecode()) { hits.push((id, map, false)); } diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 14fc5e7be802..f73b17da532d 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -212,7 +212,7 @@ impl CoverageReporter for BytecodeReporter { let mut line_number_cache = LineNumberCache::new(self.root.clone()); for (contract_id, hits) in &report.bytecode_hits { - let ops = disassemble_bytes(hits.bytecode.to_vec())?; + let ops = disassemble_bytes(hits.bytecode().to_vec())?; let mut formatted = String::new(); let source_elements = @@ -220,8 +220,7 @@ impl CoverageReporter for BytecodeReporter { for (code, source_element) in std::iter::zip(ops.iter(), source_elements) { let hits = hits - .hits - .get(&(code.offset as usize)) + .get(code.offset as usize) .map(|h| format!("[{h:03}]")) .unwrap_or(" ".to_owned()); let source_id = source_element.index();