diff --git a/Cargo.lock b/Cargo.lock index c8e6545369bf6..6c62170818445 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,19 +14,13 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -161,7 +155,7 @@ dependencies = [ "proptest", "serde", "serde_json", - "winnow 0.7.4", + "winnow 0.7.6", ] [[package]] @@ -738,7 +732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" dependencies = [ "serde", - "winnow 0.7.4", + "winnow 0.7.6", ] [[package]] @@ -988,7 +982,7 @@ dependencies = [ "clap_complete", "clap_complete_fig", "ctrlc", - "eyre", + "eyre 1.0.0", "fdlimit", "flate2", "foundry-cli", @@ -1071,9 +1065,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" @@ -1346,9 +1340,9 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", @@ -1799,17 +1793,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -1963,9 +1957,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.5.1" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65268237be94042665b92034f979c42d431d2fd998b49809543afe3e66abad1c" +checksum = "94054366e2ff97b455acdd4fdb03913f717febc57b7bbd1741b2c3b87efae030" dependencies = [ "bon-macros", "rustversion", @@ -1973,9 +1967,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.5.1" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803c95b2ecf650eb10b5f87dda6b9f6a1b758cee53245e2b7b825c9b3803a443" +checksum = "542a990e676ce0a0a895ae54b2d94afd012434f2228a85b186c6bc1a7056cdc6" dependencies = [ "darling", "ident_case", @@ -1998,9 +1992,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "regex-automata 0.4.9", @@ -2125,7 +2119,7 @@ dependencies = [ "comfy-table", "dunce", "evmole", - "eyre", + "eyre 1.0.0", "foundry-block-explorers", "foundry-cli", "foundry-common", @@ -2161,9 +2155,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.18" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "jobserver", "libc", @@ -2200,7 +2194,7 @@ dependencies = [ "alloy-primitives", "clap", "dirs", - "eyre", + "eyre 1.0.0", "forge-fmt", "foundry-cli", "foundry-common", @@ -2290,9 +2284,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", "clap_derive", @@ -2310,9 +2304,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -2369,7 +2363,7 @@ dependencies = [ "nix 0.29.0", "terminfo", "thiserror 2.0.12", - "which 7.0.2", + "which 7.0.3", "windows-sys 0.59.0", ] @@ -2405,6 +2399,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "coins-bip32" version = "0.12.0" @@ -2482,12 +2482,11 @@ dependencies = [ [[package]] name = "color-eyre" version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +source = "git+https://github.com/eyre-rs/eyre#c4ee249f7c51dc6452e8704ae8d117d90d6eeebc" dependencies = [ "backtrace", "color-spantrace", - "eyre", + "eyre 1.0.0", "indenter", "once_cell", "owo-colors", @@ -2496,9 +2495,8 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +version = "0.2.2" +source = "git+https://github.com/eyre-rs/eyre#c4ee249f7c51dc6452e8704ae8d117d90d6eeebc" dependencies = [ "once_cell", "owo-colors", @@ -2754,6 +2752,22 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e9666f4a9a948d4f1dff0c08a4512b0f7c86414b23960104c243c10d79f4c3" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" + [[package]] name = "ctr" version = "0.9.2" @@ -2824,9 +2838,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "dbus" @@ -3061,6 +3075,21 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "dtor" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222ef136a1c687d4aa0395c175f2c4586e379924c352fd02f7870cf7de783c23" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" + [[package]] name = "dunce" version = "1.0.5" @@ -3143,6 +3172,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ena" version = "0.14.3" @@ -3284,7 +3325,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded685d9f07315ff689ba56e7d84e6f1e782db19b531a46c34061a733bba7258" dependencies = [ - "eyre", + "eyre 0.6.12", "hex", ] @@ -3310,6 +3351,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "eyre" +version = "1.0.0" +source = "git+https://github.com/eyre-rs/eyre#c4ee249f7c51dc6452e8704ae8d117d90d6eeebc" +dependencies = [ + "autocfg", + "indenter", + "once_cell", +] + [[package]] name = "faster-hex" version = "0.9.0" @@ -3431,7 +3482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.7", + "miniz_oxide", ] [[package]] @@ -3471,7 +3522,7 @@ dependencies = [ "comfy-table", "dunce", "evm-disassembler", - "eyre", + "eyre 1.0.0", "forge-doc", "forge-fmt", "forge-script", @@ -3534,7 +3585,7 @@ version = "1.0.0" dependencies = [ "alloy-primitives", "derive_more 2.0.1", - "eyre", + "eyre 1.0.0", "forge-fmt", "foundry-common", "foundry-compilers", @@ -3585,7 +3636,7 @@ dependencies = [ "clap", "dialoguer", "dunce", - "eyre", + "eyre 1.0.0", "forge-script-sequence", "forge-verify", "foundry-cheatcodes", @@ -3617,7 +3668,7 @@ version = "1.0.0" dependencies = [ "alloy-network", "alloy-primitives", - "eyre", + "eyre 1.0.0", "foundry-common", "foundry-compilers", "foundry-config", @@ -3633,7 +3684,7 @@ version = "1.0.0" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", - "eyre", + "eyre 1.0.0", "foundry-common", "prettyplease", "proc-macro2", @@ -3653,7 +3704,7 @@ dependencies = [ "async-trait", "ciborium", "clap", - "eyre", + "eyre 1.0.0", "foundry-block-explorers", "foundry-cli", "foundry-common", @@ -3686,9 +3737,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13025e2bf7b3975b34190a7e0fb66b6a5df61e29e54d1da093938073e06ad10" +checksum = "001678abc9895502532c8c4a1a225079c580655fc82a194e78b06dcf99f49b8c" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3721,7 +3772,7 @@ dependencies = [ "base64 0.22.1", "dialoguer", "ecdsa", - "eyre", + "eyre 1.0.0", "forge-script-sequence", "foundry-cheatcodes-spec", "foundry-common", @@ -3774,7 +3825,7 @@ dependencies = [ "clap", "color-eyre", "dotenvy", - "eyre", + "eyre 1.0.0", "forge-fmt", "foundry-common", "foundry-compilers", @@ -3829,7 +3880,7 @@ dependencies = [ "clap", "comfy-table", "dunce", - "eyre", + "eyre 1.0.0", "foundry-block-explorers", "foundry-common-fmt", "foundry-compilers", @@ -3908,7 +3959,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tracing", - "winnow 0.7.4", + "winnow 0.7.6", "yansi", ] @@ -3992,7 +4043,7 @@ dependencies = [ "alloy-primitives", "dirs", "dunce", - "eyre", + "eyre 1.0.0", "figment", "foundry-block-explorers", "foundry-compilers", @@ -4026,7 +4077,7 @@ version = "1.0.0" dependencies = [ "alloy-primitives", "crossterm", - "eyre", + "eyre 1.0.0", "foundry-common", "foundry-compilers", "foundry-evm-core", @@ -4046,7 +4097,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-types", - "eyre", + "eyre 1.0.0", "foundry-cheatcodes", "foundry-common", "foundry-compilers", @@ -4056,6 +4107,7 @@ dependencies = [ "foundry-evm-fuzz", "foundry-evm-traces", "indicatif", + "libafl_bolts", "parking_lot", "proptest", "revm", @@ -4091,7 +4143,7 @@ dependencies = [ "alloy-rpc-types", "alloy-sol-types", "auto_impl", - "eyre", + "eyre 1.0.0", "foundry-cheatcodes-spec", "foundry-common", "foundry-config", @@ -4116,7 +4168,7 @@ name = "foundry-evm-coverage" version = "1.0.0" dependencies = [ "alloy-primitives", - "eyre", + "eyre 1.0.0", "foundry-common", "foundry-compilers", "foundry-evm-core", @@ -4133,7 +4185,7 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "eyre", + "eyre 1.0.0", "foundry-common", "foundry-compilers", "foundry-config", @@ -4158,7 +4210,7 @@ dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-types", - "eyre", + "eyre 1.0.0", "foundry-block-explorers", "foundry-common", "foundry-compilers", @@ -4188,7 +4240,7 @@ dependencies = [ "alloy-primitives", "alloy-provider", "alloy-rpc-types", - "eyre", + "eyre 0.6.12", "futures", "parking_lot", "revm", @@ -4226,7 +4278,7 @@ version = "1.0.0" dependencies = [ "alloy-primitives", "alloy-provider", - "eyre", + "eyre 1.0.0", "fd-lock", "foundry-block-explorers", "foundry-common", @@ -4264,7 +4316,7 @@ dependencies = [ "clap", "derive_builder", "eth-keystore", - "eyre", + "eyre 1.0.0", "foundry-config", "gcloud-sdk", "rpassword", @@ -4282,12 +4334,12 @@ checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "fs4" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c29c30684418547d476f0b48e84f4821639119c483b1eccd566c8cd0cd05f521" +checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" dependencies = [ - "rustix 0.38.44", - "windows-sys 0.52.0", + "rustix 1.0.5", + "windows-sys 0.59.0", ] [[package]] @@ -4497,9 +4549,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gix-actor" @@ -4769,9 +4821,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", @@ -4788,9 +4840,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -4826,6 +4878,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", + "serde", ] [[package]] @@ -4897,6 +4950,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + [[package]] name = "html-escape" version = "0.2.13" @@ -5516,9 +5580,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -5531,9 +5595,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" dependencies = [ "proc-macro2", "quote", @@ -5710,11 +5774,57 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "libafl_bolts" +version = "0.15.2" +source = "git+https://github.com/AFLplusplus/LibAFL#a198b33096c0b79fea83162cbace5876915957bb" +dependencies = [ + "ahash", + "backtrace", + "ctor", + "erased-serde", + "hashbrown 0.14.5", + "hostname", + "libafl_derive", + "libc", + "log", + "mach", + "miniz_oxide", + "nix 0.29.0", + "num_enum", + "once_cell", + "postcard", + "rand_core 0.9.3", + "rustversion", + "serde", + "serial_test", + "static_assertions", + "tuple_list", + "typeid", + "uds", + "uuid 1.16.0", + "wide", + "winapi", + "windows 0.59.0", + "windows-result 0.3.2", + "xxhash-rust", +] + +[[package]] +name = "libafl_derive" +version = "0.15.2" +source = "git+https://github.com/AFLplusplus/LibAFL#a198b33096c0b79fea83162cbace5876915957bb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libdbus-sys" @@ -5773,9 +5883,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -5845,6 +5955,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "macro-string" version = "0.1.4" @@ -5961,6 +6080,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mesc" version = "0.3.0" @@ -6019,18 +6147,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -6106,7 +6225,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] @@ -6120,6 +6239,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", + "memoffset 0.9.1", ] [[package]] @@ -6353,9 +6473,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -6450,9 +6570,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owo-colors" -version = "3.5.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" [[package]] name = "p256" @@ -6802,6 +6922,18 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postcard" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -6972,9 +7104,9 @@ dependencies = [ [[package]] name = "prodash" -version = "29.0.1" +version = "29.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee7ce24c980b976607e2d6ae4aae92827994d23fed71659c3ede3f92528b58b" +checksum = "f04bb108f648884c23b98a0e940ebc2c93c0c3b89f04dbaf7eb8256ce617d1bc" dependencies = [ "log", "parking_lot", @@ -7735,15 +7867,15 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ "aws-lc-rs", "log", @@ -7843,6 +7975,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + [[package]] name = "salsa20" version = "0.10.2" @@ -8843,9 +8984,9 @@ dependencies = [ [[package]] name = "svm-rs" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c86f1c4a401aa4526c0e823af56a4a967a8b445718cd1b0e87b14375e5bace8" +checksum = "039f8b5327f4c4c94384ad8596cc62fb23f58ef5e5d940945757b627fa56d0c2" dependencies = [ "const-hex", "dirs", @@ -8863,9 +9004,9 @@ dependencies = [ [[package]] name = "svm-rs-builds" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9b30390308e3c0f7d146d87e2c798409233fe250d849a02fe57d6a9ae6810f" +checksum = "05723cae9acea48e97af3357b25cf0079277bf2ab54405fd3dd62258caae1a48" dependencies = [ "build_const", "const-hex", @@ -9320,7 +9461,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.4", + "winnow 0.7.6", ] [[package]] @@ -9620,6 +9761,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tuple_list" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141fb9f71ee586d956d7d6e4d5a9ef8e946061188520140f7591b668841d502e" + [[package]] name = "typed-arena" version = "2.0.2" @@ -9644,6 +9791,15 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uds" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661" +dependencies = [ + "libc", +] + [[package]] name = "uint" version = "0.9.5" @@ -10140,16 +10296,25 @@ dependencies = [ [[package]] name = "which" -version = "7.0.2" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2774c861e1f072b3aadc02f8ba886c26ad6321567ecc294c935434cad06f1283" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" dependencies = [ "either", "env_home", - "rustix 0.38.44", + "rustix 1.0.5", "winsafe", ] +[[package]] +name = "wide" +version = "0.7.32" +source = "git+https://github.com/Lokathor/wide?rev=71b5df0b2620da753836fafce5f99076181a49fe#71b5df0b2620da753836fafce5f99076181a49fe" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "1.2.0" @@ -10587,9 +10752,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] @@ -10655,6 +10820,12 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index b7d31f092007d..b2b1cfd6f08f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -268,13 +268,13 @@ chrono = { version = "0.4", default-features = false, features = [ "std", ] } axum = "0.7" -color-eyre = "0.6" +color-eyre = { git = "https://github.com/eyre-rs/eyre" } # https://github.com/eyre-rs/eyre/issues/174 comfy-table = "7" dirs = "6" dunce = "1" evm-disassembler = "0.5" evmole = "0.7" -eyre = "0.6" +eyre = { git = "https://github.com/eyre-rs/eyre" } figment = "0.10" futures = "0.3" hyper = "1.5" diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index cb8bd28986aaa..06cb8f0ab75b6 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1366,7 +1366,7 @@ impl SimpleCast { pub fn to_unit(value: &str, unit: &str) -> Result { let value = DynSolType::coerce_str(&DynSolType::Uint(256), value)? .as_uint() - .wrap_err("Could not convert to uint")? + .context("Could not convert to uint")? .0; let unit = unit.parse().wrap_err("could not parse units")?; Ok(Self::format_unit_as_string(value, unit)) diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs index 13d312b8a079d..fd8dcc3f233b5 100644 --- a/crates/cli/src/utils/mod.rs +++ b/crates/cli/src/utils/mod.rs @@ -137,7 +137,7 @@ pub fn parse_ether_value(value: &str) -> Result { } else { alloy_dyn_abi::DynSolType::coerce_str(&alloy_dyn_abi::DynSolType::Uint(256), value)? .as_uint() - .wrap_err("Could not parse ether value from string")? + .context("Could not parse ether value from string")? .0 }) } diff --git a/crates/common/src/abi.rs b/crates/common/src/abi.rs index fa9f241719fdb..1e33455684e7e 100644 --- a/crates/common/src/abi.rs +++ b/crates/common/src/abi.rs @@ -123,7 +123,7 @@ pub async fn get_func_etherscan( ) -> Result { let client = Client::new(chain, etherscan_api_key)?; let source = find_source(client, contract).await?; - let metadata = source.items.first().wrap_err("etherscan returned empty metadata")?; + let metadata = source.items.first().context("etherscan returned empty metadata")?; let mut abi = metadata.abi()?; let funcs = abi.functions.remove(function_name).unwrap_or_default(); @@ -146,7 +146,7 @@ pub fn find_source( Box::pin(async move { trace!(%address, "find Etherscan source"); let source = client.contract_source_code(address).await?; - let metadata = source.items.first().wrap_err("Etherscan returned no data")?; + let metadata = source.items.first().context("Etherscan returned no data")?; if metadata.proxy == 0 { Ok(source) } else { diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 4a7e459e8fa16..9574cb5a58d81 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -274,7 +274,7 @@ pub struct EnvArgs { impl EvmArgs { /// Ensures that fork url exists and returns its reference. pub fn ensure_fork_url(&self) -> eyre::Result<&String> { - self.fork_url.as_ref().wrap_err("Missing `--fork-url` field.") + self.fork_url.as_ref().context("Missing `--fork-url` field.") } } diff --git a/crates/config/src/fuzz.rs b/crates/config/src/fuzz.rs index 26e1c080cbc8d..019e2299dd3e0 100644 --- a/crates/config/src/fuzz.rs +++ b/crates/config/src/fuzz.rs @@ -22,6 +22,8 @@ pub struct FuzzConfig { pub dictionary: FuzzDictionaryConfig, /// Number of runs to execute and include in the gas report. pub gas_report_samples: u32, + /// Path where stateless fuzz test corpus is stored. + pub corpus_dir: Option, /// Path where fuzz failures are recorded and replayed. pub failure_persist_dir: Option, /// Name of the file to record fuzz failures, defaults to `failures`. @@ -40,6 +42,7 @@ impl Default for FuzzConfig { seed: None, dictionary: FuzzDictionaryConfig::default(), gas_report_samples: 256, + corpus_dir: None, failure_persist_dir: None, failure_persist_file: None, show_logs: false, @@ -52,7 +55,8 @@ impl FuzzConfig { /// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir. pub fn new(cache_dir: PathBuf) -> Self { Self { - failure_persist_dir: Some(cache_dir), + corpus_dir: Some(cache_dir.join("fuzz/corpus")), + failure_persist_dir: Some(cache_dir.join("fuzz")), failure_persist_file: Some("failures".to_string()), ..Default::default() } diff --git a/crates/config/src/invariant.rs b/crates/config/src/invariant.rs index 0c5dbc567d363..23ef5b4fcf238 100644 --- a/crates/config/src/invariant.rs +++ b/crates/config/src/invariant.rs @@ -26,6 +26,8 @@ pub struct InvariantConfig { pub max_assume_rejects: u32, /// Number of runs to execute and include in the gas report. pub gas_report_samples: u32, + /// Path where invariant corpus is stored. + pub corpus_dir: Option, /// Path where invariant failures are recorded and replayed. pub failure_persist_dir: Option, /// Whether to collect and display fuzzed selectors metrics. @@ -47,6 +49,7 @@ impl Default for InvariantConfig { shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, + corpus_dir: None, failure_persist_dir: None, show_metrics: false, timeout: None, @@ -67,7 +70,8 @@ impl InvariantConfig { shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, - failure_persist_dir: Some(cache_dir), + corpus_dir: Some(cache_dir.join("invariant/corpus")), + failure_persist_dir: Some(cache_dir.join("invariant")), show_metrics: false, timeout: None, show_solidity: false, diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 94c445991b887..aa000568be61f 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -1075,7 +1075,9 @@ impl Config { } } }; + remove_test_dir(&self.fuzz.corpus_dir); // TODO maybe force? remove_test_dir(&self.fuzz.failure_persist_dir); + remove_test_dir(&self.invariant.corpus_dir); remove_test_dir(&self.invariant.failure_persist_dir); Ok(()) @@ -1859,7 +1861,7 @@ impl Config { /// | macOS | `$HOME`/Library/Application Support/foundry | /Users/Alice/Library/Application Support/foundry | /// | Windows | `{FOLDERID_RoamingAppData}/foundry` | C:\Users\Alice\AppData\Roaming/foundry | pub fn data_dir() -> eyre::Result { - let path = dirs::data_dir().wrap_err("Failed to find data directory")?.join("foundry"); + let path = dirs::data_dir().context("Failed to find data directory")?.join("foundry"); std::fs::create_dir_all(&path).wrap_err("Failed to create module directory")?; Ok(path) } @@ -2316,8 +2318,8 @@ impl Default for Config { test_failures_file: "cache/test-failures".into(), threads: None, show_progress: false, - fuzz: FuzzConfig::new("cache/fuzz".into()), - invariant: InvariantConfig::new("cache/invariant".into()), + fuzz: FuzzConfig::new("cache".into()), + invariant: InvariantConfig::new("cache".into()), always_use_create_2_factory: false, ffi: false, allow_internal_expect_revert: false, @@ -4430,6 +4432,7 @@ mod tests { runs: 512, depth: 10, failure_persist_dir: Some(PathBuf::from("cache/invariant")), + corpus_dir: Some(PathBuf::from("cache/invariant/corpus"),), ..Default::default() } ); diff --git a/crates/evm/coverage/src/inspector.rs b/crates/evm/coverage/src/inspector.rs index 6a6c50b093c8f..4d1eeb9059988 100644 --- a/crates/evm/coverage/src/inspector.rs +++ b/crates/evm/coverage/src/inspector.rs @@ -5,7 +5,7 @@ use std::ptr::NonNull; /// Inspector implementation for collecting coverage information. #[derive(Clone, Debug)] -pub struct CoverageCollector { +pub struct LineCoverageCollector { // NOTE: `current_map` is always a valid reference into `maps`. // It is accessed only through `get_or_insert_map` which guarantees that it's valid. // Both of these fields are unsafe to access directly outside of `*insert_map`. @@ -16,10 +16,10 @@ pub struct CoverageCollector { } // SAFETY: See comments on `current_map`. -unsafe impl Send for CoverageCollector {} -unsafe impl Sync for CoverageCollector {} +unsafe impl Send for LineCoverageCollector {} +unsafe impl Sync for LineCoverageCollector {} -impl Default for CoverageCollector { +impl Default for LineCoverageCollector { fn default() -> Self { Self { current_map: NonNull::dangling(), @@ -29,7 +29,7 @@ impl Default for CoverageCollector { } } -impl Inspector for CoverageCollector { +impl Inspector for LineCoverageCollector { fn initialize_interp(&mut self, interpreter: &mut Interpreter, _context: &mut EvmContext) { get_or_insert_contract_hash(interpreter); self.insert_map(interpreter); @@ -42,7 +42,7 @@ impl Inspector for CoverageCollector { } } -impl CoverageCollector { +impl LineCoverageCollector { /// Finish collecting coverage information and return the [`HitMaps`]. pub fn finish(self) -> HitMaps { self.maps diff --git a/crates/evm/coverage/src/lib.rs b/crates/evm/coverage/src/lib.rs index 793e0ee56670c..14af71c0a1c5d 100644 --- a/crates/evm/coverage/src/lib.rs +++ b/crates/evm/coverage/src/lib.rs @@ -29,7 +29,7 @@ pub mod analysis; pub mod anchors; mod inspector; -pub use inspector::CoverageCollector; +pub use inspector::LineCoverageCollector; /// A coverage report. /// diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 053a534405f7e..62ba7d592613a 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -51,3 +51,4 @@ thiserror.workspace = true tracing.workspace = true indicatif.workspace = true serde.workspace = true +libafl_bolts = { git = "https://github.com/AFLplusplus/LibAFL", features = ["wide"] } \ No newline at end of file diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index 3e132c733b1a5..b2f6ece7dfbc3 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -181,7 +181,7 @@ impl FuzzedExecutor { traces: last_run_traces, breakpoints: last_run_breakpoints, gas_report_traces: traces.into_iter().map(|a| a.arena).collect(), - coverage: fuzz_result.coverage, + line_coverage: fuzz_result.coverage, deprecated_cheatcodes: fuzz_result.deprecated_cheatcodes, }; @@ -258,7 +258,7 @@ impl FuzzedExecutor { Ok(FuzzOutcome::Case(CaseOutcome { case: FuzzCase { calldata, gas: call.gas_used, stipend: call.stipend }, traces: call.traces, - coverage: call.coverage, + coverage: call.line_coverage, breakpoints, logs: call.logs, deprecated_cheatcodes, diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index 11c38bf43f29b..e08b2d0a0a60f 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -10,7 +10,6 @@ use foundry_config::InvariantConfig; use foundry_evm_core::{ constants::{ CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, MAGIC_ASSUME, - TEST_TIMEOUT, }, precompiles::PRECOMPILES, }; @@ -26,8 +25,9 @@ use foundry_evm_traces::{CallTraceArena, SparsedTraceArena}; use indicatif::ProgressBar; use parking_lot::RwLock; use proptest::{ - strategy::{Strategy, ValueTree}, - test_runner::{TestCaseError, TestRunner}, + prelude::Rng, + strategy::{BoxedStrategy, Strategy, ValueTree}, + test_runner::TestRunner, }; use result::{assert_after_invariant, assert_invariants, can_continue}; use revm::primitives::HashMap; @@ -35,7 +35,9 @@ use shrink::shrink_sequence; use std::{ cell::RefCell, collections::{btree_map::Entry, HashMap as Map}, + path::PathBuf, sync::Arc, + time, }; mod error; @@ -53,6 +55,8 @@ mod shrink; use crate::executors::{EvmError, FuzzTestTimer}; pub use shrink::check_sequence; +use libafl_bolts::simd::{covmap_is_interesting_simd, vector::u8x32, SimdOrReducer}; + sol! { interface IInvariantTest { #[derive(Default)] @@ -130,8 +134,8 @@ pub struct InvariantTestData { pub gas_report_traces: Vec>, // Last call results of the invariant test. pub last_call_results: Option, - // Coverage information collected from all fuzzed calls. - pub coverage: Option, + // Line coverage information collected from all fuzzed calls. + pub line_coverage: Option, // Metrics for each fuzzed selector. pub metrics: Map, @@ -171,7 +175,7 @@ impl InvariantTest { last_run_inputs: vec![], gas_report_traces: vec![], last_call_results, - coverage: None, + line_coverage: None, metrics: Map::default(), branch_runner, }); @@ -203,9 +207,9 @@ impl InvariantTest { self.execution_data.borrow_mut().last_run_inputs.clone_from(inputs); } - /// Merge current collected coverage with the new coverage from last fuzzed call. + /// Merge current collected line coverage with the new coverage from last fuzzed call. pub fn merge_coverage(&self, new_coverage: Option) { - HitMaps::merge_opt(&mut self.execution_data.borrow_mut().coverage, new_coverage); + HitMaps::merge_opt(&mut self.execution_data.borrow_mut().line_coverage, new_coverage); } /// Update metrics for a fuzzed selector, extracted from tx details. @@ -297,6 +301,111 @@ pub struct InvariantExecutor<'a> { project_contracts: &'a ContractsByArtifact, /// Filters contracts to be fuzzed through their artifact identifiers. artifact_filters: ArtifactFilters, + /// History of binned hitcount of edges seen during fuzzing. + history_map: Vec, +} +const COVERAGE_MAP_SIZE: usize = 65536; + +pub struct TxCorpusManager { + pub corpus_dir: Option, + pub in_memory_corpus: Vec>, /* TODO need some sort of corpus management + * (limit memory usage and flush). */ + pub tx_generator: BoxedStrategy, +} + +impl TxCorpusManager { + pub fn new(corpus_dir: PathBuf, tx_generator: BoxedStrategy) -> Self { + // assert!(corpus_dir.is_dir()); + let mut corpus = vec![]; + if let Ok(entries) = std::fs::read_dir(&corpus_dir) { + for entry in entries { + let path = entry.expect("msg").path(); + if path.is_file() && path.extension() == Some(std::ffi::OsStr::new("json")) { + let tx_seq: Vec = + foundry_common::fs::read_json_file(&path).expect("msg"); + corpus.push(tx_seq); + } + } + } + Self { corpus_dir: Some(corpus_dir), in_memory_corpus: corpus, tx_generator } + } + + // pub fn save(&self) -> Result<()> { TODO order or key by timestamp? + // for (i, tx_seq) in self.corpus.iter().enumerate() { + // let path = self.corpus_dir.join(format!("{}.json", i)); + // foundry_common::fs::write_json_file(&path, tx_seq)?; + // } + // Ok(()) + // } + + pub fn insert(&mut self, tx_seq: Vec) { + self.in_memory_corpus.push(tx_seq); + } + + pub fn new_sequence(&self, test_runnner: &mut TestRunner) -> Vec { + let rng = test_runnner.rng(); + + if self.in_memory_corpus.len() > 1 { + let idx1 = rng.gen_range(0..self.in_memory_corpus.len()); + let idx2 = rng.gen_range(0..self.in_memory_corpus.len()); + let one = &self.in_memory_corpus[idx1]; + let two = &self.in_memory_corpus[idx2]; + let mut new_seq = vec![]; + // TODO rounds of mutations on elements? + match rng.gen_range(0..3) { + // TODO expose config and add tests + // splice + 0 => { + let start1 = rng.gen_range(0..one.len()); + let end1 = rng.gen_range(start1..one.len()); + + let start2 = rng.gen_range(0..two.len()); + let end2 = rng.gen_range(start2..two.len()); + + for tx in one.iter().take(end1).skip(start1) { + new_seq.push(tx.clone()); + } + + for tx in two.iter().take(end2).skip(start2) { + new_seq.push(tx.clone()); + } + + if !new_seq.is_empty() { + return new_seq; + } + } + // repeat + 1 => { + let tx = if rng.gen_bool(0.5) { one } else { two }; + new_seq = tx.clone(); + let start = rng.gen_range(0..tx.len()); + let end = rng.gen_range(start..tx.len()); + let item_idx = rng.gen_range(0..tx.len()); + let item = &tx[item_idx]; + for i in start..end { + new_seq[i] = item.clone(); + } + } + // interleave + 2 => { + for (tx1, tx2) in one.iter().zip(two.iter()) { + // chunks? + let tx = if rng.gen_bool(0.5) { tx1.clone() } else { tx2.clone() }; + new_seq.push(tx); + } + return new_seq; + } + // TODO + // 3. Overwrite prefix with new or mutated sequence + // 4. Overwrite suffix with new or mutated sequence + // 5. Select idx to mutate and change its args according to its ABI + _ => { + unreachable!(); + } + } + } + return vec![self.tx_generator.new_tree(test_runnner).unwrap().current()]; + } } impl<'a> InvariantExecutor<'a> { @@ -315,6 +424,7 @@ impl<'a> InvariantExecutor<'a> { setup_contracts, project_contracts, artifact_filters: ArtifactFilters::default(), + history_map: vec![0u8; COVERAGE_MAP_SIZE], } } @@ -328,19 +438,49 @@ impl<'a> InvariantExecutor<'a> { ) -> Result { // Throw an error to abort test run if the invariant function accepts input params if !invariant_contract.invariant_function.inputs.is_empty() { - return Err(eyre!("Invariant test function should have no inputs")) + return Err(eyre!("Invariant test function should have no inputs")); } let (invariant_test, invariant_strategy) = self.prepare_test(&invariant_contract, fuzz_fixtures, deployed_libs)?; + let generator = invariant_strategy.boxed(); + if let Some(corpus_dir) = &self.config.corpus_dir { + if !corpus_dir.is_dir() { + foundry_common::fs::create_dir_all(corpus_dir)?; + } + // TOOD rerun corpus and initialize history map + // let mut corpus = + // TxCorpusManager::new(self.config.corpus_dir.clone().unwrap(), generator.clone()); + + // for tx_seq in corpus.in_memory_corpus.iter() { + // if tx_seq.is_empty() { + // continue; + // } + // let mut current_run = InvariantTestRun::new( + // tx_seq[0].clone(), + // // Before each run, we must reset the backend state. + // self.executor.clone(), + // self.config.depth as usize, + // ); + // for tx in tx_seq[1..].iter() { + + // } + // } + } + let mut corpus = + TxCorpusManager::new(self.config.corpus_dir.clone().unwrap(), generator.clone()); + // Start timer for this invariant test. let timer = FuzzTestTimer::new(self.config.timeout); + let mut runs = 0; - let _ = self.runner.run(&invariant_strategy, |first_input| { + 'stop: while runs < self.config.runs { + let initial_seq = corpus.new_sequence(&mut self.runner); + // println!("initial_seq: {:?}", initial_seq); // Create current invariant run data. let mut current_run = InvariantTestRun::new( - first_input, + initial_seq[0].clone(), // Before each run, we must reset the backend state. self.executor.clone(), self.config.depth as usize, @@ -348,7 +488,7 @@ impl<'a> InvariantExecutor<'a> { // We stop the run immediately if we have reverted, and `fail_on_revert` is set. if self.config.fail_on_revert && invariant_test.reverts() > 0 { - return Err(TestCaseError::fail("call reverted")) + return Err(eyre!("call reverted")) } while current_run.depth < self.config.depth { @@ -358,12 +498,13 @@ impl<'a> InvariantExecutor<'a> { // successful even though it timed out. We *want* // this behavior for now, so that's ok, but // future developers should be aware of this. - return Err(TestCaseError::fail(TEST_TIMEOUT)); + break 'stop; } - let tx = current_run.inputs.last().ok_or_else(|| { - TestCaseError::fail("no input generated to called fuzz target") - })?; + let tx = current_run + .inputs + .last() + .ok_or_else(|| eyre!("no input generated to call fuzzed target."))?; // Execute call from the randomly generated sequence without committing state. // State is committed only if call is not a magic assume. @@ -375,15 +516,47 @@ impl<'a> InvariantExecutor<'a> { tx.call_details.calldata.clone(), U256::ZERO, ) - .map_err(|e| TestCaseError::fail(e.to_string()))?; + .map_err(|e| eyre!(format!("Could not make raw evm call: {e}")))?; let discarded = call_result.result.as_ref() == MAGIC_ASSUME; if self.config.show_metrics { invariant_test.record_metrics(tx, call_result.reverted, discarded); } - // Collect coverage from last fuzzed call. - invariant_test.merge_coverage(call_result.coverage.clone()); + // Collect line coverage from last fuzzed call. + invariant_test.merge_coverage(call_result.line_coverage.clone()); + if let Some(ref mut x) = call_result.edge_coverage { + if !x.is_empty() { + // AFL-style novelty detection https://github.com/AFLplusplus/LibAFL/blob/a198b33096c0b79fea83162cbace5876915957bb/libafl/src/feedbacks/map.rs#L40-L41 + let (new_coverage, _) = covmap_is_interesting_simd::( + &self.history_map, + x.as_slice(), + false, // TODO this metadata could be used in a scheduler + ); + + if !discarded && new_coverage { + // TODO save at very end + if let Some(corpus_dir) = &self.config.corpus_dir { + let timestamp = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .expect("Time went backwards") + .as_secs() + .to_string(); + if let Err(err) = foundry_common::fs::write_json_file( + corpus_dir.join(timestamp).as_path(), + ¤t_run.inputs, + ) { + error!(%err, "Failed to record call sequence"); + } + } + + // This includes reverting txs in the corpus and `can_continue` removes + // them. We want this as it is new coverage + // and may help reach the other branch. + corpus.insert(current_run.inputs.clone()); + } + } + } if discarded { current_run.inputs.pop(); @@ -392,9 +565,7 @@ impl<'a> InvariantExecutor<'a> { invariant_test.set_error(InvariantFuzzError::MaxAssumeRejects( self.config.max_assume_rejects, )); - return Err(TestCaseError::fail( - "reached maximum number of `vm.assume` rejects", - )); + break 'stop; } } else { // Commit executed call result. @@ -446,27 +617,36 @@ impl<'a> InvariantExecutor<'a> { call_result, &state_changeset, ) - .map_err(|e| TestCaseError::fail(e.to_string()))?; + .map_err(|e| eyre!(e.to_string()))?; if !result.can_continue || current_run.depth == self.config.depth - 1 { invariant_test.set_last_run_inputs(¤t_run.inputs); } // If test cannot continue then stop current run and exit test suite. if !result.can_continue { - return Err(TestCaseError::fail("test cannot continue")) + break 'stop; } invariant_test.set_last_call_results(result.call_result); current_run.depth += 1; } - // Generates the next call from the run using the recently updated - // dictionary. - current_run.inputs.push( - invariant_strategy - .new_tree(&mut invariant_test.execution_data.borrow_mut().branch_runner) - .map_err(|_| TestCaseError::Fail("Could not generate case".into()))? - .current(), - ); + // To occasionally intermix new txs + let generate_new = self.runner.rng().gen_ratio(1, 10); + // Initial sequence's length is less than depth + let must_generate = + current_run.depth as usize > initial_seq.len().saturating_sub(1); + + if discarded || must_generate || generate_new { + // Generates the next call from the run using the recently updated dictionary + current_run.inputs.push( + generator + .new_tree(&mut invariant_test.execution_data.borrow_mut().branch_runner) + .map_err(|_| eyre!("Could not generate case"))? + .current(), + ); + } else { + current_run.inputs.push(initial_seq[current_run.depth as usize].clone()); + } } // Call `afterInvariant` only if it is declared and test didn't fail already. @@ -477,7 +657,7 @@ impl<'a> InvariantExecutor<'a> { ¤t_run, &self.config, ) - .map_err(|_| TestCaseError::Fail("Failed to call afterInvariant".into()))?; + .map_err(|_| eyre!("Failed to call afterInvariant"))?; } // End current invariant test run. @@ -488,8 +668,8 @@ impl<'a> InvariantExecutor<'a> { progress.inc(1); } - Ok(()) - }); + runs += 1; + } trace!(?fuzz_fixtures); invariant_test.fuzz_state.log_stats(); @@ -501,7 +681,7 @@ impl<'a> InvariantExecutor<'a> { reverts: result.failures.reverts, last_run_inputs: result.last_run_inputs, gas_report_traces: result.gas_report_traces, - coverage: result.coverage, + line_coverage: result.line_coverage, metrics: result.metrics, }) } @@ -573,7 +753,7 @@ impl<'a> InvariantExecutor<'a> { &mut failures, )?; if let Some(error) = failures.error { - return Err(eyre!(error.revert_reason().unwrap_or_default())) + return Err(eyre!(error.revert_reason().unwrap_or_default())); } Ok(( @@ -676,10 +856,10 @@ impl<'a> InvariantExecutor<'a> { .abi .functions() .find(|func| func.selector().as_slice() == selector.as_slice()) - .wrap_err(format!("{contract} does not have the selector {selector:?}"))?; + .context(format!("{contract} does not have the selector {selector:?}"))?; } - return Ok(artifact.identifier()) + return Ok(artifact.identifier()); } eyre::bail!("{contract} not found in the project. Allowed format: `contract_name` or `contract_path:contract_name`."); } @@ -839,7 +1019,7 @@ impl<'a> InvariantExecutor<'a> { ) -> eyre::Result<()> { // Do not add address in target contracts if no function selected. if selectors.is_empty() { - return Ok(()) + return Ok(()); } let contract = match targeted_contracts.entry(address) { diff --git a/crates/evm/evm/src/executors/invariant/replay.rs b/crates/evm/evm/src/executors/invariant/replay.rs index 89d7aa242a035..f5c42afa5f8e5 100644 --- a/crates/evm/evm/src/executors/invariant/replay.rs +++ b/crates/evm/evm/src/executors/invariant/replay.rs @@ -29,7 +29,7 @@ pub fn replay_run( mut ided_contracts: ContractsByAddress, logs: &mut Vec, traces: &mut Traces, - coverage: &mut Option, + line_coverage: &mut Option, deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>, inputs: &[BasicTxDetails], show_solidity: bool, @@ -52,7 +52,7 @@ pub fn replay_run( logs.extend(call_result.logs); traces.push((TraceKind::Execution, call_result.traces.clone().unwrap())); - HitMaps::merge_opt(coverage, call_result.coverage); + HitMaps::merge_opt(line_coverage, call_result.line_coverage); // Identify newly generated contracts, if they exist. ided_contracts @@ -109,7 +109,7 @@ pub fn replay_error( ided_contracts: ContractsByAddress, logs: &mut Vec, traces: &mut Traces, - coverage: &mut Option, + line_coverage: &mut Option, deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>, progress: Option<&ProgressBar>, show_solidity: bool, @@ -137,7 +137,7 @@ pub fn replay_error( ided_contracts, logs, traces, - coverage, + line_coverage, deprecated_cheatcodes, &calls, show_solidity, diff --git a/crates/evm/evm/src/executors/invariant/result.rs b/crates/evm/evm/src/executors/invariant/result.rs index 8920a1209342a..7b36247d478e5 100644 --- a/crates/evm/evm/src/executors/invariant/result.rs +++ b/crates/evm/evm/src/executors/invariant/result.rs @@ -29,7 +29,7 @@ pub struct InvariantFuzzTestResult { /// Additional traces used for gas report construction. pub gas_report_traces: Vec>, /// The coverage info collected during the invariant test runs. - pub coverage: Option, + pub line_coverage: Option, /// Fuzzed selectors metrics collected during the invariant test runs. pub metrics: HashMap, } diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 5c26a6d6cdc5d..f9ccafdc003a9 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -785,8 +785,10 @@ pub struct RawCallResult { pub labels: AddressHashMap, /// The traces of the call pub traces: Option, - /// The coverage info collected during the call - pub coverage: Option, + /// The line coverage info collected during the call + pub line_coverage: Option, + /// The edge coverage info collected during the call + pub edge_coverage: Option>, /// Scripted transactions generated from this call pub transactions: Option, /// The changeset of the state. @@ -814,7 +816,8 @@ impl Default for RawCallResult { logs: Vec::new(), labels: HashMap::default(), traces: None, - coverage: None, + line_coverage: None, + edge_coverage: None, transactions: None, state_changeset: HashMap::default(), env: EnvWithHandlerCfg::new_with_spec_id(Box::default(), SpecId::LATEST), @@ -945,8 +948,15 @@ fn convert_executed_result( _ => Bytes::new(), }; - let InspectorData { mut logs, labels, traces, coverage, cheatcodes, chisel_state } = - inspector.collect(); + let InspectorData { + mut logs, + labels, + traces, + line_coverage, + edge_coverage, + cheatcodes, + chisel_state, + } = inspector.collect(); if logs.is_empty() { logs = exec_logs; @@ -968,7 +978,8 @@ fn convert_executed_result( logs, labels, traces, - coverage, + line_coverage, + edge_coverage, transactions, state_changeset, env, diff --git a/crates/evm/evm/src/inspectors/mod.rs b/crates/evm/evm/src/inspectors/mod.rs index 41008397a1cbc..f5f198c1f1896 100644 --- a/crates/evm/evm/src/inspectors/mod.rs +++ b/crates/evm/evm/src/inspectors/mod.rs @@ -1,7 +1,7 @@ //! EVM inspectors. pub use foundry_cheatcodes::{self as cheatcodes, Cheatcodes, CheatsConfig}; -pub use foundry_evm_coverage::CoverageCollector; +pub use foundry_evm_coverage::LineCoverageCollector; pub use foundry_evm_fuzz::Fuzzer; pub use foundry_evm_traces::{StackSnapshotType, TracingInspector, TracingInspectorConfig}; diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index e23adfcae0661..622cf4e1aaff9 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -1,5 +1,5 @@ use super::{ - Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Fuzzer, LogCollector, + Cheatcodes, CheatsConfig, ChiselState, Fuzzer, LineCoverageCollector, LogCollector, TracingInspector, }; use alloy_primitives::{map::AddressHashMap, Address, Bytes, Log, TxKind, U256}; @@ -19,6 +19,7 @@ use revm::{ }, EvmContext, Inspector, JournaledState, }; +use revm_inspectors::edge_cov::EdgeCovInspector; use std::{ ops::{Deref, DerefMut}, sync::Arc, @@ -45,8 +46,8 @@ pub struct InspectorStackBuilder { pub trace_mode: TraceMode, /// Whether logs should be collected. pub logs: Option, - /// Whether coverage info should be collected. - pub coverage: Option, + /// Whether line coverage info should be collected. + pub line_coverage: Option, /// Whether to print all opcode traces into the console. Useful for debugging the EVM. pub print: Option, /// The chisel state inspector. @@ -119,10 +120,10 @@ impl InspectorStackBuilder { self } - /// Set whether to collect coverage information. + /// Set whether to collect line coverage information. #[inline] - pub fn coverage(mut self, yes: bool) -> Self { - self.coverage = Some(yes); + pub fn line_coverage(mut self, yes: bool) -> Self { + self.line_coverage = Some(yes); self } @@ -173,7 +174,7 @@ impl InspectorStackBuilder { fuzzer, trace_mode, logs, - coverage, + line_coverage, print, chisel_state, enable_isolation, @@ -199,7 +200,8 @@ impl InspectorStackBuilder { if let Some(chisel_state) = chisel_state { stack.set_chisel(chisel_state); } - stack.collect_coverage(coverage.unwrap_or(false)); + stack.collect_line_coverage(line_coverage.unwrap_or(false)); + stack.collect_edge_coverage(true); stack.collect_logs(logs.unwrap_or(true)); stack.print(print.unwrap_or(false)); stack.tracing(trace_mode); @@ -247,7 +249,8 @@ pub struct InspectorData { pub logs: Vec, pub labels: AddressHashMap, pub traces: Option, - pub coverage: Option, + pub line_coverage: Option, + pub edge_coverage: Option>, pub cheatcodes: Option, pub chisel_state: Option<(Vec, Vec, InstructionResult)>, } @@ -285,7 +288,8 @@ pub struct InspectorStack { #[derive(Default, Clone, Debug)] pub struct InspectorStackInner { pub chisel_state: Option, - pub coverage: Option, + pub line_coverage: Option, + pub edge_coverage: Option, pub fuzzer: Option, pub log_collector: Option, pub printer: Option, @@ -342,7 +346,7 @@ impl InspectorStack { )* }; } - push!(cheatcodes, chisel_state, coverage, fuzzer, log_collector, printer, tracer); + push!(cheatcodes, chisel_state, line_coverage, fuzzer, log_collector, printer, tracer); if self.enable_isolation { enabled.push("isolation"); } @@ -391,10 +395,16 @@ impl InspectorStack { self.chisel_state = Some(ChiselState::new(final_pc)); } - /// Set whether to enable the coverage collector. + /// Set whether to enable the line coverage collector. #[inline] - pub fn collect_coverage(&mut self, yes: bool) { - self.coverage = yes.then(Default::default); + pub fn collect_line_coverage(&mut self, yes: bool) { + self.line_coverage = yes.then(Default::default); + } + + /// Set whether to enable the edge coverage collector. + #[inline] + pub fn collect_edge_coverage(&mut self, yes: bool) { + self.edge_coverage = yes.then(EdgeCovInspector::new); // TODO configurable edge size? } /// Set whether to enable call isolation. @@ -442,7 +452,15 @@ impl InspectorStack { pub fn collect(self) -> InspectorData { let Self { mut cheatcodes, - inner: InspectorStackInner { chisel_state, coverage, log_collector, tracer, .. }, + inner: + InspectorStackInner { + chisel_state, + line_coverage, + edge_coverage, + log_collector, + tracer, + .. + }, } = self; let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| { @@ -470,7 +488,8 @@ impl InspectorStack { .map(|cheatcodes| cheatcodes.labels.clone()) .unwrap_or_default(), traces, - coverage: coverage.map(|coverage| coverage.finish()), + line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()), + edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()), cheatcodes, chisel_state: chisel_state.and_then(|state| state.state), } @@ -650,7 +669,7 @@ impl InspectorStackRefMut<'_> { for (addr, mut acc) in res.state { let Some(acc_mut) = ecx.journaled_state.state.get_mut(&addr) else { ecx.journaled_state.state.insert(addr, acc); - continue + continue; }; // make sure accounts that were warmed earlier do not become cold @@ -665,7 +684,7 @@ impl InspectorStackRefMut<'_> { for (key, val) in acc.storage { let Some(slot_mut) = acc_mut.storage.get_mut(&key) else { acc_mut.storage.insert(key, val); - continue + continue; }; slot_mut.present_value = val.present_value; slot_mut.is_cold &= val.is_cold; @@ -757,7 +776,7 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { ecx: &mut EvmContext<&mut dyn DatabaseExt>, ) { call_inspectors!( - [&mut self.coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], + [&mut self.line_coverage, &mut self.tracer, &mut self.cheatcodes, &mut self.printer], |inspector| inspector.initialize_interp(interpreter, ecx), ); } @@ -767,7 +786,8 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { [ &mut self.fuzzer, &mut self.tracer, - &mut self.coverage, + &mut self.line_coverage, + &mut self.edge_coverage, &mut self.cheatcodes, &mut self.printer, ], @@ -929,7 +949,7 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { call_inspectors!( #[ret] - [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes], + [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes], |inspector| inspector.create(ecx, create).map(Some), ); @@ -989,7 +1009,7 @@ impl Inspector<&mut dyn DatabaseExt> for InspectorStackRefMut<'_> { call_inspectors!( #[ret] - [&mut self.tracer, &mut self.coverage, &mut self.cheatcodes], + [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes], |inspector| inspector.eofcreate(ecx, create).map(Some), ); diff --git a/crates/evm/fuzz/src/invariant/mod.rs b/crates/evm/fuzz/src/invariant/mod.rs index c681512de1e60..18853702cdb7c 100644 --- a/crates/evm/fuzz/src/invariant/mod.rs +++ b/crates/evm/fuzz/src/invariant/mod.rs @@ -2,6 +2,7 @@ use alloy_json_abi::{Function, JsonAbi}; use alloy_primitives::{Address, Bytes, Selector}; use itertools::Either; use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, sync::Arc}; mod call_override; @@ -211,7 +212,7 @@ impl TargetedContract { } /// Details of a transaction generated by invariant strategy for fuzzing a target. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct BasicTxDetails { // Transaction sender address. pub sender: Address, @@ -220,7 +221,7 @@ pub struct BasicTxDetails { } /// Call details of a transaction generated to fuzz invariant target. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct CallDetails { // Address of target contract. pub target: Address, diff --git a/crates/evm/fuzz/src/lib.rs b/crates/evm/fuzz/src/lib.rs index 3486dac0b5172..0afb83e876fd0 100644 --- a/crates/evm/fuzz/src/lib.rs +++ b/crates/evm/fuzz/src/lib.rs @@ -221,8 +221,8 @@ pub struct FuzzTestResult { /// Those traces should not be displayed. pub gas_report_traces: Vec, - /// Raw coverage info - pub coverage: Option, + /// Raw line coverage info + pub line_coverage: Option, /// Breakpoints for debugger. Correspond to the same fuzz case as `traces`. pub breakpoints: Option, diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index 2b656c0be493f..1e7d1c1bba895 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -283,7 +283,7 @@ impl CoverageArgs { let data = outcome.results.iter().flat_map(|(_, suite)| { let mut hits = Vec::new(); for result in suite.test_results.values() { - let Some(hit_maps) = result.coverage.as_ref() else { continue }; + let Some(hit_maps) = result.line_coverage.as_ref() else { continue }; for map in hit_maps.0.values() { if let Some((id, _)) = known_contracts.find_by_deployed_code(map.bytecode()) { hits.push((id, map, true)); @@ -409,7 +409,7 @@ pub struct BytecodeData { /// The source maps are indexed by *instruction counters*, which are the indexes of /// instructions in the bytecode *minus any push bytes*. /// - /// Since our coverage inspector collects hit data using program counters, the anchors + /// Since our line coverage inspector collects hit data using program counters, the anchors /// also need to be based on program counters. ic_pc_map: IcPcMap, } diff --git a/crates/forge/src/cmd/create.rs b/crates/forge/src/cmd/create.rs index 744c38d71d977..8c274896b94dc 100644 --- a/crates/forge/src/cmd/create.rs +++ b/crates/forge/src/cmd/create.rs @@ -10,7 +10,7 @@ use alloy_serde::WithOtherFields; use alloy_signer::Signer; use alloy_transport::TransportError; use clap::{Parser, ValueHint}; -use eyre::{Context, Result}; +use eyre::{Context, ContextCompat, Result}; use forge_verify::{RetryArgs, VerifierArgs, VerifyArgs}; use foundry_cli::{ opts::{BuildOpts, EthereumOpts, EtherscanOpts, TransactionOpts}, diff --git a/crates/forge/src/coverage.rs b/crates/forge/src/coverage.rs index 22c1bbb0701d1..9bcc2eddfddc7 100644 --- a/crates/forge/src/coverage.rs +++ b/crates/forge/src/coverage.rs @@ -4,6 +4,7 @@ use alloy_primitives::map::HashMap; use comfy_table::{modifiers::UTF8_ROUND_CORNERS, Attribute, Cell, Color, Row, Table}; use evm_disassembler::disassemble_bytes; use foundry_common::fs; +pub use foundry_evm::coverage::*; use semver::Version; use std::{ collections::hash_map, @@ -11,8 +12,6 @@ use std::{ path::{Path, PathBuf}, }; -pub use foundry_evm::coverage::*; - /// A coverage reporter. pub trait CoverageReporter { /// Returns `true` if the reporter needs source maps for the final report. @@ -248,7 +247,8 @@ 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()) + .or_else(|_| Err(eyre::eyre!("Failed to disassemble bytecode")))?; let mut formatted = String::new(); let source_elements = diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 03e889c8c5544..f6935e4253915 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -285,8 +285,8 @@ pub struct TestRunnerConfig { /// 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 line coverage info + pub line_coverage: bool, /// Whether to collect debug info pub debug: bool, /// Whether to enable steps tracking in the tracer. @@ -329,7 +329,7 @@ impl TestRunnerConfig { Arc::new(cheatcodes.config.clone_with(&self.config, self.evm_opts.clone())); } inspector.tracing(self.trace_mode()); - inspector.collect_coverage(self.coverage); + inspector.collect_line_coverage(self.line_coverage); inspector.enable_isolation(self.isolation); inspector.odyssey(self.odyssey); // inspector.set_create2_deployer(self.evm_opts.create2_deployer); @@ -358,7 +358,7 @@ impl TestRunnerConfig { stack .cheatcodes(cheats_config) .trace_mode(self.trace_mode()) - .coverage(self.coverage) + .line_coverage(self.line_coverage) .enable_isolation(self.isolation) .odyssey(self.odyssey) .create2_deployer(self.evm_opts.create2_deployer) @@ -393,8 +393,8 @@ pub struct MultiContractRunnerBuilder { pub fork: Option, /// Project config. pub config: Arc, - /// Whether or not to collect coverage info - pub coverage: bool, + /// Whether or not to collect line coverage info + pub line_coverage: bool, /// Whether or not to collect debug info pub debug: bool, /// Whether to enable steps tracking in the tracer. @@ -413,7 +413,7 @@ impl MultiContractRunnerBuilder { initial_balance: Default::default(), evm_spec: Default::default(), fork: Default::default(), - coverage: Default::default(), + line_coverage: Default::default(), debug: Default::default(), isolation: Default::default(), decode_internal: Default::default(), @@ -442,7 +442,7 @@ impl MultiContractRunnerBuilder { } pub fn set_coverage(mut self, enable: bool) -> Self { - self.coverage = enable; + self.line_coverage = enable; self } @@ -535,7 +535,7 @@ impl MultiContractRunnerBuilder { spec_id: self.evm_spec.unwrap_or_else(|| self.config.evm_spec_id()), sender: self.sender.unwrap_or(self.config.sender), - coverage: self.coverage, + line_coverage: self.line_coverage, debug: self.debug, decode_internal: self.decode_internal, inline_config: Arc::new(InlineConfig::new_parsed(output, &self.config)?), diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index f221ad7ebac3d..0b7bfe25db726 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -402,9 +402,9 @@ pub struct TestResult { #[serde(skip)] pub gas_report_traces: Vec>, - /// Raw coverage info + /// Raw line coverage info #[serde(skip)] - pub coverage: Option, + pub line_coverage: Option, /// Labeled addresses pub labeled_addresses: AddressHashMap, @@ -478,7 +478,7 @@ impl TestResult { labeled_addresses: setup.labels.clone(), logs: setup.logs.clone(), traces: setup.traces.clone(), - coverage: setup.coverage.clone(), + line_coverage: setup.coverage.clone(), ..Default::default() } } @@ -495,7 +495,7 @@ impl TestResult { reason: setup.reason, logs: setup.logs, traces: setup.traces, - coverage: setup.coverage, + line_coverage: setup.coverage, labeled_addresses: setup.labels, ..Default::default() } @@ -528,7 +528,7 @@ impl TestResult { self.logs.extend(raw_call_result.logs); self.labeled_addresses.extend(raw_call_result.labels); self.traces.extend(raw_call_result.traces.map(|traces| (TraceKind::Execution, traces))); - self.merge_coverages(raw_call_result.coverage); + self.merge_coverages(raw_call_result.line_coverage); self.status = match success { true => TestStatus::Success, @@ -559,7 +559,7 @@ impl TestResult { self.logs.extend(result.logs); self.labeled_addresses.extend(result.labeled_addresses); self.traces.extend(result.traces.map(|traces| (TraceKind::Execution, traces))); - self.merge_coverages(result.coverage); + self.merge_coverages(result.line_coverage); self.status = if result.skipped { TestStatus::Skipped @@ -652,12 +652,12 @@ impl TestResult { self.logs.extend(call_result.logs); self.labeled_addresses.extend(call_result.labels); self.traces.extend(call_result.traces.map(|traces| (TraceKind::Execution, traces))); - self.merge_coverages(call_result.coverage); + self.merge_coverages(call_result.line_coverage); } /// Merges the given coverage result into `self`. pub fn merge_coverages(&mut self, other_coverage: Option) { - HitMaps::merge_opt(&mut self.coverage, other_coverage); + HitMaps::merge_opt(&mut self.line_coverage, other_coverage); } } @@ -782,6 +782,6 @@ impl TestSetup { self.logs.extend(raw.logs); self.labels.extend(raw.labels); self.traces.extend(raw.traces.map(|traces| (trace_kind, traces))); - HitMaps::merge_opt(&mut self.coverage, raw.coverage); + HitMaps::merge_opt(&mut self.coverage, raw.line_coverage); } } diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index eb8f87221bfed..fcc4b5eee6649 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -652,7 +652,7 @@ impl<'a> FunctionRunner<'a> { identified_contracts.clone(), &mut self.result.logs, &mut self.result.traces, - &mut self.result.coverage, + &mut self.result.line_coverage, &mut self.result.deprecated_cheatcodes, &txes, show_solidity, @@ -682,7 +682,7 @@ impl<'a> FunctionRunner<'a> { } }; // Merge coverage collected during invariant run with test setup coverage. - self.result.merge_coverages(invariant_result.coverage); + self.result.merge_coverages(invariant_result.line_coverage); let mut counterexample = None; let success = invariant_result.error.is_none(); @@ -703,7 +703,7 @@ impl<'a> FunctionRunner<'a> { identified_contracts.clone(), &mut self.result.logs, &mut self.result.traces, - &mut self.result.coverage, + &mut self.result.line_coverage, &mut self.result.deprecated_cheatcodes, progress.as_ref(), show_solidity, @@ -752,7 +752,7 @@ impl<'a> FunctionRunner<'a> { identified_contracts.clone(), &mut self.result.logs, &mut self.result.traces, - &mut self.result.coverage, + &mut self.result.line_coverage, &mut self.result.deprecated_cheatcodes, &invariant_result.last_run_inputs, show_solidity, diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index f845981bda592..ef62a751cc400 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -797,8 +797,8 @@ Compiler run successful! // checks that `clean` removes fuzz and invariant cache dirs forgetest_init!(can_clean_test_cache, |prj, cmd| { prj.update_config(|config| { - config.fuzz = FuzzConfig::new("cache/fuzz".into()); - config.invariant = InvariantConfig::new("cache/invariant".into()); + config.fuzz = FuzzConfig::new("cache".into()); + config.invariant = InvariantConfig::new("cache".into()); }); // default test contract is written in custom out directory let fuzz_cache_dir = prj.root().join("cache/fuzz"); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 5308a2918a6aa..15bc00f419b63 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -85,12 +85,14 @@ forgetest!(can_extract_config_values, |prj, cmd| { seed: Some(U256::from(1000)), failure_persist_dir: Some("test-cache/fuzz".into()), failure_persist_file: Some("failures".to_string()), + corpus_dir: Some("cache/fuzz/corpus".into()), show_logs: false, ..Default::default() }, invariant: InvariantConfig { runs: 256, failure_persist_dir: Some("test-cache/fuzz".into()), + corpus_dir: Some("cache/invariant/corpus".into()), ..Default::default() }, ffi: true, @@ -1082,6 +1084,7 @@ include_push_bytes = true max_fuzz_dictionary_addresses = 15728640 max_fuzz_dictionary_values = 6553600 gas_report_samples = 256 +corpus_dir = "cache/fuzz/corpus" failure_persist_dir = "cache/fuzz" failure_persist_file = "failures" show_logs = false @@ -1099,6 +1102,7 @@ max_fuzz_dictionary_values = 6553600 shrink_run_limit = 5000 max_assume_rejects = 65536 gas_report_samples = 256 +corpus_dir = "cache/invariant/corpus" failure_persist_dir = "cache/invariant" show_metrics = false show_solidity = false @@ -1187,6 +1191,7 @@ exclude = [] "max_fuzz_dictionary_addresses": 15728640, "max_fuzz_dictionary_values": 6553600, "gas_report_samples": 256, + "corpus_dir": "cache/fuzz/corpus", "failure_persist_dir": "cache/fuzz", "failure_persist_file": "failures", "show_logs": false, @@ -1205,6 +1210,7 @@ exclude = [] "shrink_run_limit": 5000, "max_assume_rejects": 65536, "gas_report_samples": 256, + "corpus_dir": "cache/invariant/corpus", "failure_persist_dir": "cache/invariant", "show_metrics": false, "timeout": null, diff --git a/crates/forge/tests/it/invariant.rs b/crates/forge/tests/it/invariant.rs index 43db084d06df3..45b32f4b6c1e2 100644 --- a/crates/forge/tests/it/invariant.rs +++ b/crates/forge/tests/it/invariant.rs @@ -1113,10 +1113,10 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant increment failure] [Sequence] (original: 4, shrunk: 4) - sender=0x00000000000000000000000000000000000018dE addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[1931387396117645594923 [1.931e21]] - sender=0x00000000000000000000000000000000000009d5 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x0000000000000000000000000000000000000105 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x00000000000000000000000000000000000009B2 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[996881781832960761274744263729582347 [9.968e35]] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[996881781832960761274744263729582347 [9.968e35]] invariant_increment() (runs: 0, calls: 0, reverts: 0) Encountered a total of 1 failing tests, 0 tests succeeded @@ -1136,13 +1136,13 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant increment failure] [Sequence] (original: 4, shrunk: 4) - vm.prank(0x00000000000000000000000000000000000018dE); - Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(1931387396117645594923); - vm.prank(0x00000000000000000000000000000000000009d5); + vm.prank([..]); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); - vm.prank(0x0000000000000000000000000000000000000105); + vm.prank([..]); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); - vm.prank(0x00000000000000000000000000000000000009B2); + vm.prank([..]); + Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).increment(); + vm.prank([..]); Counter(0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f).setNumber(996881781832960761274744263729582347); invariant_increment() (runs: 0, calls: 0, reverts: 0) @@ -1162,10 +1162,10 @@ Failing tests: Encountered 1 failing test in test/InvariantSequenceLenTest.t.sol:InvariantSequenceLenTest [FAIL: invariant_increment replay failure] [Sequence] (original: 4, shrunk: 4) - sender=0x00000000000000000000000000000000000018dE addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[1931387396117645594923 [1.931e21]] - sender=0x00000000000000000000000000000000000009d5 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x0000000000000000000000000000000000000105 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] - sender=0x00000000000000000000000000000000000009B2 addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[996881781832960761274744263729582347 [9.968e35]] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=increment() args=[] + sender=[..] addr=[src/Counter.sol:Counter]0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f calldata=setNumber(uint256) args=[996881781832960761274744263729582347 [9.968e35]] invariant_increment() (runs: 1, calls: 1, reverts: 1) Encountered a total of 1 failing tests, 0 tests succeeded diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 89f26382a867a..748589f56871f 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -123,6 +123,7 @@ impl ForgeTestProfile { max_fuzz_dictionary_values: 10_000, }, gas_report_samples: 256, + corpus_dir: Some(tempfile::tempdir().unwrap().into_path()), failure_persist_dir: Some(tempfile::tempdir().unwrap().into_path()), failure_persist_file: Some("testfailure".to_string()), show_logs: false, @@ -143,6 +144,7 @@ impl ForgeTestProfile { shrink_run_limit: 5000, max_assume_rejects: 65536, gas_report_samples: 256, + corpus_dir: Some(tempfile::tempdir().unwrap().into_path()), failure_persist_dir: Some( tempfile::Builder::new() .prefix(&format!("foundry-{self}")) diff --git a/crates/script-sequence/src/sequence.rs b/crates/script-sequence/src/sequence.rs index 4b2b434e11793..f2de32acfc0cb 100644 --- a/crates/script-sequence/src/sequence.rs +++ b/crates/script-sequence/src/sequence.rs @@ -179,7 +179,7 @@ impl ScriptSequence { let mut cache = config.cache_path.to_path_buf(); let mut common = PathBuf::new(); - let target_fname = target.source.file_name().wrap_err("No filename.")?; + let target_fname = target.source.file_name().context("No filename.")?; common.push(target_fname); common.push(chain_id.to_string()); if dry_run { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 3016784d180c7..50c8aaa9f2472 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -363,7 +363,7 @@ impl ScriptArgs { let func = get_func(&self.sig)?; abi.functions() .find(|&abi_func| abi_func.selector() == func.selector()) - .wrap_err(format!("Function `{}` is not implemented in your script.", self.sig))? + .context(format!("Function `{}` is not implemented in your script.", self.sig))? } else { let matching_functions = abi.functions().filter(|func| func.name == self.sig).collect::>(); diff --git a/crates/script/src/multi_sequence.rs b/crates/script/src/multi_sequence.rs index e0fd4d1bc7e68..c054bc573c22b 100644 --- a/crates/script/src/multi_sequence.rs +++ b/crates/script/src/multi_sequence.rs @@ -71,7 +71,7 @@ impl MultiChainSequence { let target_fname = target .source .file_name() - .wrap_err_with(|| format!("No filename for {:?}", target.source))? + .with_context(|| format!("No filename for {:?}", target.source))? .to_string_lossy(); common.push(format!("{target_fname}-latest"));