From e6204e56f0e31feeee1da1875dda5e9bc818f5f8 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Fri, 25 Mar 2022 16:59:29 +0100 Subject: [PATCH 01/15] feat: etherscan identifier --- Cargo.lock | 394 ++++++++++++++++---------- Cargo.toml | 15 +- cli/src/cmd/forge/run.rs | 3 + cli/src/cmd/forge/test.rs | 24 +- config/src/lib.rs | 8 + evm/src/executor/opts.rs | 15 +- evm/src/trace/decoder.rs | 72 ++--- evm/src/trace/identifier.rs | 78 ----- evm/src/trace/identifier/etherscan.rs | 97 +++++++ evm/src/trace/identifier/local.rs | 70 +++++ evm/src/trace/identifier/mod.rs | 21 ++ evm/src/trace/mod.rs | 30 +- utils/src/lib.rs | 2 +- 13 files changed, 539 insertions(+), 290 deletions(-) delete mode 100644 evm/src/trace/identifier.rs create mode 100644 evm/src/trace/identifier/etherscan.rs create mode 100644 evm/src/trace/identifier/local.rs create mode 100644 evm/src/trace/identifier/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b7ab2da2be13..7667acc44058 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "once_cell", "version_check", ] @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", "quote", @@ -547,9 +547,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.6" +version = "3.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" dependencies = [ "atty", "bitflags", @@ -575,9 +575,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.4" +version = "3.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -609,7 +609,7 @@ dependencies = [ "bs58", "coins-core", "digest 0.9.0", - "getrandom 0.2.5", + "getrandom 0.2.6", "hmac 0.11.0", "k256", "lazy_static", @@ -626,7 +626,7 @@ checksum = "8f473ea37dfc9d2cb94fdde50c3d41f28c3f384b367573d66386fea38d76d466" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.5", + "getrandom 0.2.6", "hex", "hmac 0.11.0", "pbkdf2 0.8.0", @@ -724,7 +724,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b103d85ca6e209388771bfb7aa6b68a7aeec4afbf6f0a0264bfbf50360e5212e" dependencies = [ - "crossterm 0.23.0", + "crossterm 0.23.2", "strum", "strum_macros", "unicode-width", @@ -808,18 +808,18 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ "libc", ] [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -838,10 +838,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if 1.0.0", "crossbeam-utils", "lazy_static", @@ -851,9 +852,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -893,14 +894,14 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.23.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b75a27dc8d220f1f8521ea69cd55a34d720a200ebb3a624d9aa19193d3b432" +checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" dependencies = [ "bitflags", "crossterm_winapi 0.9.0", "libc", - "mio 0.7.14", + "mio 0.8.2", "parking_lot 0.12.0", "signal-hook", "signal-hook-mio", @@ -975,9 +976,9 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" dependencies = [ "quote", "syn", @@ -1058,6 +1059,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1070,9 +1080,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -1149,9 +1159,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if 1.0.0", ] @@ -1245,9 +1255,8 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ - "ethers-addressbook", + "ethers-addressbook 0.1.0", "ethers-contract", "ethers-core", "ethers-etherscan", @@ -1260,7 +1269,17 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" +dependencies = [ + "ethers-core", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-addressbook" +version = "0.1.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "ethers-core", "once_cell", @@ -1271,7 +1290,6 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1289,14 +1307,13 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "Inflector", "cfg-if 1.0.0", "dunce", "ethers-core", "eyre", - "getrandom 0.2.5", + "getrandom 0.2.6", "hex", "proc-macro2", "quote", @@ -1311,7 +1328,6 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1325,7 +1341,6 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "arrayvec 0.7.2", "bytes", @@ -1351,7 +1366,6 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "ethers-core", "ethers-solc", @@ -1365,7 +1379,6 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "async-trait", "ethers-contract", @@ -1388,7 +1401,6 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "async-trait", "auto_impl", @@ -1420,7 +1432,6 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "async-trait", "coins-bip32", @@ -1443,14 +1454,13 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" -source = "git+https://github.com/gakonst/ethers-rs#23e45e85314083fb3e472c1b6a417c37c905f599" dependencies = [ "colored", "dunce", "ethers-core", "fs_extra", "futures-util", - "getrandom 0.2.5", + "getrandom 0.2.6", "glob", "hex", "home", @@ -1477,14 +1487,36 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9289ed2c0440a6536e65119725cf91fc2c6b5e513bfd2e36e1134d7cca6ca12f" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" dependencies = [ "indenter", "once_cell", ] +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "fake-simd" version = "0.1.2" @@ -1598,7 +1630,7 @@ dependencies = [ "serde_json", "tokio", "tracing", - "tracing-subscriber 0.3.9", + "tracing-subscriber 0.3.10", ] [[package]] @@ -1659,7 +1691,7 @@ dependencies = [ "toml", "tracing", "tracing-error 0.2.0", - "tracing-subscriber 0.3.9", + "tracing-subscriber 0.3.10", "ui", "vergen", "walkdir", @@ -1720,7 +1752,7 @@ dependencies = [ "tokio", "tracing", "tracing-error 0.2.0", - "tracing-subscriber 0.3.9", + "tracing-subscriber 0.3.10", "url", ] @@ -1729,7 +1761,7 @@ name = "foundry-utils" version = "0.2.0" dependencies = [ "ethers", - "ethers-addressbook", + "ethers-addressbook 0.1.0 (git+https://github.com/gakonst/ethers-rs)", "ethers-core", "ethers-etherscan", "ethers-providers", @@ -1742,7 +1774,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tracing-subscriber 0.3.9", + "tracing-subscriber 0.3.10", ] [[package]] @@ -1903,9 +1935,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1932,11 +1964,47 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +[[package]] +name = "git-config" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a552e3de993c389623e23b71a888c8356acfcb6e4232ce8420fba4710a44921b" +dependencies = [ + "bstr", + "dirs 4.0.0", + "git-features", + "memchr", + "nom 7.1.1", + "pwd", + "quick-error 2.0.1", + "unicode-bom", +] + +[[package]] +name = "git-features" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebc06d6b83be53d9c7a00937a6baed4566aad7dba12af11ac830ea72c080af0" +dependencies = [ + "git-hash", + "libc", +] + +[[package]] +name = "git-hash" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106e7c3a08b97285bf0bca7f109e67d286e7fbf34b222faf742443b021de2bb4" +dependencies = [ + "hex", + "quick-error 2.0.1", +] + [[package]] name = "git2" -version = "0.13.25" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" +checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c" dependencies = [ "bitflags", "libc", @@ -1977,9 +2045,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ "bytes", "fnv", @@ -2044,9 +2112,9 @@ dependencies = [ [[package]] name = "hidapi-rusb" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e0b15c35f9c35cbadd0c388197364e8ff3feffd23b69cd45524a014932f5f1" +checksum = "6664e89e7c267621ea65a3453f7883a47a9ead4638d4dc44ccc3b695cc45d3c2" dependencies = [ "cc", "libc", @@ -2118,9 +2186,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.17" +version = "0.14.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" dependencies = [ "bytes", "futures-channel", @@ -2247,9 +2315,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", "hashbrown 0.11.2", @@ -2427,15 +2495,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.119" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "libgit2-sys" -version = "0.12.26+1.3.0" +version = "0.13.2+1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" +checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b" dependencies = [ "cc", "libc", @@ -2469,18 +2537,19 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" dependencies = [ "cfg-if 1.0.0", ] @@ -2583,9 +2652,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8" +checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" dependencies = [ "libc", "log", @@ -2606,9 +2675,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", @@ -2666,20 +2735,19 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] name = "notify" -version = "5.0.0-pre.13" +version = "5.0.0-pre.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245d358380e2352c2d020e8ee62baac09b3420f1f6c012a31326cfced4ad487d" +checksum = "d13c22db70a63592e098fb51735bab36646821e6389a0ba171f3549facdf0b74" dependencies = [ "bitflags", "crossbeam-channel", @@ -2688,7 +2756,7 @@ dependencies = [ "inotify", "kqueue", "libc", - "mio 0.7.14", + "mio 0.8.2", "walkdir", "winapi", ] @@ -2912,9 +2980,9 @@ checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" [[package]] name = "parity-scale-codec" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8483b84fb12de1dc23bf95d26030d16cea56391d136db0db37f749508104e3e6" +checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066" dependencies = [ "arrayvec 0.7.2", "bitvec 1.0.0", @@ -2926,9 +2994,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7259388ceb4c23bc09caef272c9e7a732b3b8f9fbd0b41f0009a91d6548cc1d9" +checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2954,7 +3022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ "lock_api", - "parking_lot_core 0.9.1", + "parking_lot_core 0.9.2", ] [[package]] @@ -2973,9 +3041,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" dependencies = [ "cfg-if 1.0.0", "libc", @@ -3213,9 +3281,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "ppv-lite86" @@ -3231,9 +3299,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c038cb5319b9c704bf9c227c261d275bfec0ad438118a2787ce47944fb228b" +checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" dependencies = [ "ansi_term", "ctor", @@ -3296,9 +3364,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] @@ -3342,6 +3410,16 @@ version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" +[[package]] +name = "pwd" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ca0304857594109dca88140120427c7a65027be6b77d86a5938588e79cb07b" +dependencies = [ + "failure", + "libc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3356,9 +3434,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" dependencies = [ "proc-macro2", ] @@ -3435,7 +3513,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", ] [[package]] @@ -3492,21 +3570,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "redox_syscall", + "thiserror", ] [[package]] @@ -3588,7 +3667,7 @@ dependencies = [ [[package]] name = "revm" version = "1.2.0" -source = "git+https://github.com/bluealloy/revm#211c83ba85518d82a1674eb6e5520cf3efbc617a" +source = "git+https://github.com/bluealloy/revm#06ff5f9b9898467c84d66bd846cc672a261ca10d" dependencies = [ "arrayref", "auto_impl", @@ -3606,7 +3685,7 @@ dependencies = [ [[package]] name = "revm_precompiles" version = "0.4.0" -source = "git+https://github.com/bluealloy/revm#211c83ba85518d82a1674eb6e5520cf3efbc617a" +source = "git+https://github.com/bluealloy/revm#06ff5f9b9898467c84d66bd846cc672a261ca10d" dependencies = [ "bytes", "k256", @@ -4032,12 +4111,13 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", "mio 0.7.14", + "mio 0.8.2", "signal-hook", ] @@ -4074,9 +4154,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" @@ -4133,13 +4213,13 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "string_cache" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" dependencies = [ - "lazy_static", "new_debug_unreachable", - "parking_lot 0.11.2", + "once_cell", + "parking_lot 0.12.0", "phf_shared 0.10.0", "precomputed-hash", ] @@ -4231,12 +4311,24 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.90" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", + "syn", "unicode-xid", ] @@ -4296,7 +4388,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76971977e6121664ec1b960d1313aacfa75642adc93b9d4d53b247bd4cb1747e" dependencies = [ - "dirs", + "dirs 2.0.2", "fnv", "nom 5.1.2", "phf 0.8.0", @@ -4385,7 +4477,7 @@ dependencies = [ "bytes", "libc", "memchr", - "mio 0.8.1", + "mio 0.8.2", "num_cpus", "once_cell", "parking_lot 0.12.0", @@ -4419,9 +4511,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e" dependencies = [ "rustls", "tokio", @@ -4459,16 +4551,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" dependencies = [ "bytes", "futures-core", "futures-sink", - "log", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -4511,9 +4603,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" dependencies = [ "lazy_static", "valuable", @@ -4536,7 +4628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", - "tracing-subscriber 0.3.9", + "tracing-subscriber 0.3.10", ] [[package]] @@ -4573,9 +4665,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +checksum = "b9df98b037d039d03400d9dd06b0f8ce05486b5f25e9a2d7d36196e142ebbc52" dependencies = [ "ansi_term", "lazy_static", @@ -4700,6 +4792,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-bom" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ec69f541d875b783ca40184d655f2927c95f0bffd486faa83cd3ac3529ec32" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -4757,7 +4855,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "serde", ] @@ -4933,9 +5031,9 @@ dependencies = [ [[package]] name = "watchexec" -version = "2.0.0-pre.11" +version = "2.0.0-pre.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3115ed7db83103ab13d6ada8892372bf596eca52cf2ae06571b906fd7dcba4c6" +checksum = "17b7625243bde138cc271f6bc101dc4abe573bfd534cf41771a0ca880517d949" dependencies = [ "async-recursion", "async-stream", @@ -4944,12 +5042,12 @@ dependencies = [ "command-group", "dunce", "futures", - "git2", + "git-config", "globset", "ignore", "libc", "miette", - "nom 7.1.0", + "nom 7.1.1", "notify", "once_cell", "regex", @@ -4991,9 +5089,9 @@ dependencies = [ [[package]] name = "which" -version = "4.2.4" +version = "4.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" dependencies = [ "either", "lazy_static", @@ -5033,9 +5131,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -5046,33 +5144,33 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" [[package]] name = "windows_i686_gnu" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" [[package]] name = "windows_i686_msvc" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" [[package]] name = "windows_x86_64_gnu" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" [[package]] name = "windows_x86_64_msvc" -version = "0.32.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" [[package]] name = "winreg" @@ -5112,12 +5210,12 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.3" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50344758e2f40e3a1fcfc8f6f91aa57b5f8ebd8d27919fe6451f15aaaf9ee608" +checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" diff --git a/Cargo.toml b/Cargo.toml index eb68480e6d0a..8372e620da56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,11 +22,10 @@ panic = "abort" # We end up stripping away these symbols anyway debug = 0 -## Patch ethers-rs with a local checkout then run `cargo update -p ethers` -#[patch."https://github.com/gakonst/ethers-rs"] -#ethers = { path = "../ethers-rs" } -#ethers-core = { path = "../ethers-rs/ethers-core" } -#ethers-providers = { path = "../ethers-rs/ethers-providers" } -#ethers-signers = { path = "../ethers-rs/ethers-signers" } -#ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } -#ethers-solc = { path = "../ethers-rs/ethers-solc" } +[patch."https://github.com/gakonst/ethers-rs"] +ethers = { path = "../ethers-rs" } +ethers-core = { path = "../ethers-rs/ethers-core" } +ethers-providers = { path = "../ethers-rs/ethers-providers" } +ethers-signers = { path = "../ethers-rs/ethers-signers" } +ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } +ethers-solc = { path = "../ethers-rs/ethers-solc" } diff --git a/cli/src/cmd/forge/run.rs b/cli/src/cmd/forge/run.rs index e9930b4a3fef..9aabaaa850a9 100644 --- a/cli/src/cmd/forge/run.rs +++ b/cli/src/cmd/forge/run.rs @@ -144,6 +144,9 @@ impl Cmd for RunArgs { }; // Identify addresses in each trace + // TODO: Could we use the Etherscan identifier here? Main issue: Pulling source code and + // bytecode. Might be better to wait for an interactive debugger where we can do this on + // the fly while retaining access to the database? let local_identifier = LocalTraceIdentifier::new(&known_contracts); let mut decoder = CallTraceDecoder::new_with_labels(result.labeled_addresses.clone()); for (_, trace) in &mut result.traces { diff --git a/cli/src/cmd/forge/test.rs b/cli/src/cmd/forge/test.rs index be16ff41a9ec..90eca74f41f7 100644 --- a/cli/src/cmd/forge/test.rs +++ b/cli/src/cmd/forge/test.rs @@ -16,7 +16,10 @@ use forge::{ decode::decode_console_logs, executor::opts::EvmOpts, gas_report::GasReport, - trace::{identifier::LocalTraceIdentifier, CallTraceDecoder, TraceKind}, + trace::{ + identifier::{EtherscanIdentifier, LocalTraceIdentifier}, + CallTraceDecoder, TraceKind, + }, MultiContractRunner, MultiContractRunnerBuilder, SuiteResult, TestFilter, TestKind, }; use foundry_config::{figment::Figment, Config}; @@ -426,40 +429,52 @@ pub fn custom_run(mut args: TestArgs, include_fuzz_tests: bool) -> eyre::Result< } else { let TestArgs { filter, .. } = args; test( + config, runner, verbosity, filter, args.json, args.allow_failure, include_fuzz_tests, - (args.gas_report, config.gas_reports), + args.gas_report, ) } } /// Runs all the tests fn test( + config: Config, mut runner: MultiContractRunner, verbosity: u8, filter: Filter, json: bool, allow_failure: bool, include_fuzz_tests: bool, - (gas_reporting, gas_reports): (bool, Vec), + gas_reporting: bool, ) -> eyre::Result { if json { let results = runner.test(&filter, None, include_fuzz_tests)?; println!("{}", serde_json::to_string(&results)?); Ok(TestOutcome::new(results, allow_failure)) } else { + // Set up identifiers let local_identifier = LocalTraceIdentifier::new(&runner.known_contracts); + let remote_chain_id = runner.evm_opts.get_remote_chain_id(); + let etherscan_identifier = EtherscanIdentifier::new( + remote_chain_id, + config.etherscan_api_key.unwrap_or_default(), + remote_chain_id.map(|chain_id| Config::foundry_etherscan_cache_dir(chain_id)).flatten(), + ); + + // Set up test reporter channel let (tx, rx) = channel::<(String, SuiteResult)>(); + // Run tests let handle = thread::spawn(move || runner.test(&filter, Some(tx), include_fuzz_tests).unwrap()); let mut results: BTreeMap = BTreeMap::new(); - let mut gas_report = GasReport::new(gas_reports); + let mut gas_report = GasReport::new(config.gas_reports); for (contract_name, suite_result) in rx { let mut tests = suite_result.test_results.clone(); println!(); @@ -492,6 +507,7 @@ fn test( let mut decoded_traces = Vec::new(); for (kind, trace) in &mut result.traces { decoder.identify(trace, &local_identifier); + decoder.identify(trace, ðerscan_identifier); let should_include = match kind { // At verbosity level 3, we only display traces for failed tests diff --git a/config/src/lib.rs b/config/src/lib.rs index 92b5863f9203..00d7cc5090ec 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -134,6 +134,8 @@ pub struct Config { pub verbosity: u8, /// url of the rpc server that should be used for any rpc calls pub eth_rpc_url: Option, + /// etherscan API key + pub etherscan_api_key: Option, /// list of solidity error codes to always silence in the compiler output pub ignored_error_codes: Vec, /// The number of test cases that must execute for each property test @@ -729,6 +731,11 @@ impl Config { Self::foundry_dir().map(|p| p.join("cache")) } + /// Returns the path to foundry's etherscan cache dir `~/.foundry/cache//etherscan` + pub fn foundry_etherscan_cache_dir(chain_id: impl Into) -> Option { + Some(Self::foundry_cache_dir()?.join(chain_id.into().to_string()).join("etherscan")) + } + /// Returns the path to the cache file of the `block` on the `chain` /// `~/.foundry/cache///storage.json` pub fn foundry_block_cache_file(chain_id: impl Into, block: u64) -> Option { @@ -918,6 +925,7 @@ impl Default for Config { block_difficulty: 0, block_gas_limit: None, eth_rpc_url: None, + etherscan_api_key: None, verbosity: 0, remappings: vec![], libraries: vec![], diff --git a/evm/src/executor/opts.rs b/evm/src/executor/opts.rs index 364a73070ed6..6c44af06dacb 100644 --- a/evm/src/executor/opts.rs +++ b/evm/src/executor/opts.rs @@ -1,6 +1,6 @@ use ethers::{ providers::{Middleware, Provider}, - types::{Address, U256}, + types::{Address, Chain, U256}, }; use foundry_utils::RuntimeOrHandle; use revm::{BlockEnv, CfgEnv, SpecId, TxEnv}; @@ -86,23 +86,28 @@ impl EvmOpts { /// - the chain if `fork_url` is set and the endpoints returned its chain id successfully /// - mainnet otherwise pub fn get_chain_id(&self) -> u64 { - use ethers::types::Chain; if let Some(id) = self.env.chain_id { return id } + self.get_remote_chain_id().map_or(Chain::Mainnet as u64, |id| id as u64) + } + + /// Returns the chain ID from the RPC, if any. + pub fn get_remote_chain_id(&self) -> Option { if let Some(ref url) = self.fork_url { if url.contains("mainnet") { tracing::trace!("auto detected mainnet chain from url {}", url); - return Chain::Mainnet as u64 + return Some(Chain::Mainnet) } let provider = Provider::try_from(url.as_str()) .unwrap_or_else(|_| panic!("Failed to establish provider to {}", url)); if let Ok(id) = foundry_utils::RuntimeOrHandle::new().block_on(provider.get_chainid()) { - return id.as_u64() + return Chain::try_from(id.as_u64()).ok() } } - Chain::Mainnet as u64 + + None } } diff --git a/evm/src/trace/decoder.rs b/evm/src/trace/decoder.rs index 8a61279a2aad..0c5fecaffbf1 100644 --- a/evm/src/trace/decoder.rs +++ b/evm/src/trace/decoder.rs @@ -140,45 +140,49 @@ impl CallTraceDecoder { /// /// Unknown contracts are contracts that either lack a label or an ABI. pub fn identify(&mut self, trace: &CallTraceArena, identifier: &impl TraceIdentifier) { - trace.addresses_iter().for_each(|(address, code)| { - // We only try to identify addresses with missing data - if self.labels.contains_key(address) && self.contracts.contains_key(address) { - return - } + let unidentified_addresses = trace + .addresses() + .into_iter() + .filter(|(address, _)| { + !self.labels.contains_key(address) || !self.contracts.contains_key(address) + }) + .collect(); - let (contract, label, abi) = identifier.identify_address(address, code); - if let Some(contract) = contract { - self.contracts.entry(*address).or_insert(contract); - } + identifier.identify_addresses(unidentified_addresses).iter().for_each( + |(address, contract, label, abi)| { + if let Some(contract) = contract { + self.contracts.entry(*address).or_insert(contract.to_string()); + } - if let Some(label) = label { - self.labels.entry(*address).or_insert(label); - } + if let Some(label) = label { + self.labels.entry(*address).or_insert(label.to_string()); + } - if let Some(abi) = abi { - // Store known functions for the address - abi.functions() - .map(|func| (func.short_signature(), func.clone())) - .for_each(|(sig, func)| self.functions.entry(sig).or_default().push(func)); + if let Some(abi) = abi { + // Store known functions for the address + abi.functions() + .map(|func| (func.short_signature(), func.clone())) + .for_each(|(sig, func)| self.functions.entry(sig).or_default().push(func)); - // Flatten events from all ABIs - abi.events() - .map(|event| ((event.signature(), indexed_inputs(event)), event.clone())) - .for_each(|(sig, event)| { - self.events.entry(sig).or_default().push(event); - }); + // Flatten events from all ABIs + abi.events() + .map(|event| ((event.signature(), indexed_inputs(event)), event.clone())) + .for_each(|(sig, event)| { + self.events.entry(sig).or_default().push(event); + }); - // Flatten errors from all ABIs - abi.errors().for_each(|error| { - let entry = self - .errors - .errors - .entry(error.name.clone()) - .or_insert_with(Default::default); - entry.push(error.clone()); - }); - } - }); + // Flatten errors from all ABIs + abi.errors().for_each(|error| { + let entry = self + .errors + .errors + .entry(error.name.clone()) + .or_insert_with(Default::default); + entry.push(error.clone()); + }); + } + }, + ); } pub fn decode(&self, traces: &mut CallTraceArena) { diff --git a/evm/src/trace/identifier.rs b/evm/src/trace/identifier.rs deleted file mode 100644 index ded8629ff766..000000000000 --- a/evm/src/trace/identifier.rs +++ /dev/null @@ -1,78 +0,0 @@ -use ethers::{ - abi::{Abi, Address}, - prelude::ArtifactId, -}; -use std::collections::BTreeMap; - -/// Trace identifiers figure out what ABIs and labels belong to all the addresses of the trace. -pub trait TraceIdentifier { - /// Attempts to identify an address in one or more call traces. - /// - /// The tuple is of the format `(contract, label, abi)`, where `contract` is intended to be of - /// the format `":"`, e.g. `"Foo.json:Foo"`. - fn identify_address( - &self, - address: &Address, - code: Option<&Vec>, - ) -> (Option, Option, Option<&Abi>); -} - -/// The local trace identifier keeps track of addresses that are instances of local contracts. -pub struct LocalTraceIdentifier { - local_contracts: BTreeMap, (String, Abi)>, -} - -impl LocalTraceIdentifier { - pub fn new(known_contracts: &BTreeMap)>) -> Self { - Self { - local_contracts: known_contracts - .iter() - .map(|(id, (abi, runtime_code))| { - (runtime_code.clone(), (id.name.clone(), abi.clone())) - }) - .collect(), - } - } -} - -impl TraceIdentifier for LocalTraceIdentifier { - fn identify_address( - &self, - _: &Address, - code: Option<&Vec>, - ) -> (Option, Option, Option<&Abi>) { - if let Some(code) = code { - if let Some((_, (name, abi))) = self - .local_contracts - .iter() - .find(|(known_code, _)| diff_score(known_code, code) < 0.1) - { - (Some(name.clone()), Some(name.clone()), Some(abi)) - } else { - (None, None, None) - } - } else { - (None, None, None) - } - } -} - -/// Very simple fuzzy matching of contract bytecode. -/// -/// Will fail for small contracts that are essentially all immutable variables. -fn diff_score(a: &[u8], b: &[u8]) -> f64 { - let cutoff_len = usize::min(a.len(), b.len()); - if cutoff_len == 0 { - return 1.0 - } - - let a = &a[..cutoff_len]; - let b = &b[..cutoff_len]; - let mut diff_chars = 0; - for i in 0..cutoff_len { - if a[i] != b[i] { - diff_chars += 1; - } - } - diff_chars as f64 / cutoff_len as f64 -} diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs new file mode 100644 index 000000000000..3e33a05913f3 --- /dev/null +++ b/evm/src/trace/identifier/etherscan.rs @@ -0,0 +1,97 @@ +use super::TraceIdentifier; +use ethers::{ + abi::{Abi, Address}, + etherscan, + types::Chain, +}; +use futures::stream::{self, StreamExt}; +use std::{borrow::Cow, path::PathBuf}; +use tokio::time::{sleep, Duration}; +use tracing::{trace, warn}; + +/// A trace identifier that tries to identify addresses using Etherscan. +pub struct EtherscanIdentifier { + /// The Etherscan client + client: Option, +} + +impl EtherscanIdentifier { + pub fn new( + chain: Option>, + etherscan_api_key: String, + cache_path: Option, + ) -> Self { + if let Some(cache_path) = &cache_path { + if let Err(err) = std::fs::create_dir_all(cache_path.join("sources")) { + warn!("could not create etherscan cache dir: {:?}", err); + } + } + + Self { + client: chain + .map(|chain| { + etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path).ok() + }) + .flatten(), + } + } +} + +impl TraceIdentifier for EtherscanIdentifier { + fn identify_addresses( + &self, + addresses: Vec<(&Address, Option<&Vec>)>, + ) -> Vec<(Address, Option, Option, Option>)> { + if let Some(client) = &self.client { + let stream = stream::iter(addresses.into_iter().map(futures::future::ready)) + .buffered(10) + .filter_map(|(addr, _)| { + let client = client.clone(); + async move { + let mut i = 0; + trace!("requesting etherscan info for contract {addr}"); + loop { + match client.contract_source_code(*addr).await { + Ok(mut metadata) => { + break metadata + .items + .pop() + .map(|item| { + Some(( + *addr, + item.contract_name, + serde_json::from_str(&item.abi).ok()?, + )) + }) + .flatten() + } + Err(etherscan::errors::EtherscanError::RateLimitExceeded) => { + sleep(Duration::from_secs(1)).await; + trace!("rate limit exceeded on attempt {i}"); + i += 1; + if i < 5 { + continue + } else { + warn!("no more retries left for request"); + break None + } + } + Err(err) => { + warn!("could not get etherscan info: {:?}", err); + break None + } + } + } + } + }) + .map(|(addr, label, abi): (Address, String, ethers::abi::Abi)| { + (addr, Some(label.clone()), Some(label.clone()), Some(Cow::Owned(abi.clone()))) + }) + .collect(); + let rt = tokio::runtime::Runtime::new().expect("could not start tokio rt"); + rt.block_on(stream) + } else { + Vec::new() + } + } +} diff --git a/evm/src/trace/identifier/local.rs b/evm/src/trace/identifier/local.rs new file mode 100644 index 000000000000..11fb346178ed --- /dev/null +++ b/evm/src/trace/identifier/local.rs @@ -0,0 +1,70 @@ +use super::TraceIdentifier; +use ethers::{ + abi::{Abi, Address}, + prelude::ArtifactId, +}; +use std::{borrow::Cow, collections::BTreeMap}; + +/// A trace identifier that tries to identify addresses using local contracts. +pub struct LocalTraceIdentifier { + local_contracts: BTreeMap, (String, Abi)>, +} + +impl LocalTraceIdentifier { + pub fn new(known_contracts: &BTreeMap)>) -> Self { + Self { + local_contracts: known_contracts + .iter() + .map(|(id, (abi, runtime_code))| { + (runtime_code.clone(), (id.name.clone(), abi.clone())) + }) + .collect(), + } + } +} + +impl TraceIdentifier for LocalTraceIdentifier { + fn identify_addresses( + &self, + addresses: Vec<(&Address, Option<&Vec>)>, + ) -> Vec<(Address, Option, Option, Option>)> { + addresses + .into_iter() + .filter_map(|(addr, code)| { + code.map(|code| { + self.local_contracts + .iter() + .find(|(known_code, _)| diff_score(known_code, &code) < 0.1) + .map_or((*addr, None, None, None), |(_, (name, abi))| { + ( + *addr, + Some(name.clone()), + Some(name.clone()), + Some(Cow::Borrowed(abi)), + ) + }) + }) + }) + .collect() + } +} + +/// Very simple fuzzy matching of contract bytecode. +/// +/// Will fail for small contracts that are essentially all immutable variables. +fn diff_score(a: &[u8], b: &[u8]) -> f64 { + let cutoff_len = usize::min(a.len(), b.len()); + if cutoff_len == 0 { + return 1.0 + } + + let a = &a[..cutoff_len]; + let b = &b[..cutoff_len]; + let mut diff_chars = 0; + for i in 0..cutoff_len { + if a[i] != b[i] { + diff_chars += 1; + } + } + diff_chars as f64 / cutoff_len as f64 +} diff --git a/evm/src/trace/identifier/mod.rs b/evm/src/trace/identifier/mod.rs new file mode 100644 index 000000000000..abea1e262e67 --- /dev/null +++ b/evm/src/trace/identifier/mod.rs @@ -0,0 +1,21 @@ +mod local; +pub use local::LocalTraceIdentifier; + +mod etherscan; +pub use etherscan::EtherscanIdentifier; + +use ethers::abi::{Abi, Address}; +use std::borrow::Cow; + +/// Trace identifiers figure out what ABIs and labels belong to all the addresses of the trace. +pub trait TraceIdentifier { + // TODO: Update docs + /// Attempts to identify an address in one or more call traces. + /// + /// The tuple is of the format `(contract, label, abi)`, where `contract` is intended to be of + /// the format `":"`, e.g. `"Foo.json:Foo"`. + fn identify_addresses( + &self, + addresses: Vec<(&Address, Option<&Vec>)>, + ) -> Vec<(Address, Option, Option, Option>)>; +} diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index ef5f52e68081..289a059d7ff8 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -14,7 +14,10 @@ use ethers::{ types::U256, }; use serde::{Deserialize, Serialize}; -use std::fmt::{self, Write}; +use std::{ + collections::HashSet, + fmt::{self, Write}, +}; /// An arena of [CallTraceNode]s #[derive(Debug, Clone, Serialize, Deserialize)] @@ -60,20 +63,23 @@ impl CallTraceArena { } } - pub fn addresses_iter(&self) -> impl Iterator>)> { - self.arena.iter().map(|node| { - let code = if node.trace.created() { - if let RawOrDecodedReturnData::Raw(bytes) = &node.trace.output { - Some(bytes) + pub fn addresses(&self) -> HashSet<(&Address, Option<&Vec>)> { + self.arena + .iter() + .map(|node| { + let code = if node.trace.created() { + if let RawOrDecodedReturnData::Raw(bytes) = &node.trace.output { + Some(bytes) + } else { + None + } } else { None - } - } else { - None - }; + }; - (&node.trace.address, code) - }) + (&node.trace.address, code) + }) + .collect() } } diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 705f1f584bd2..f699094a4980 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -881,7 +881,7 @@ fn get_param_type( /// /// Notes: /// * ABI Encoder V2 is not supported yet -/// * Kudos to https://github.com/maxme/abi2solidity for the algorithm +/// * Kudos to [maxme/abi2solidity](https://github.com/maxme/abi2solidity) for the algorithm pub fn abi_to_solidity(contract_abi: &Abi, mut contract_name: &str) -> Result { let functions_iterator = contract_abi.functions(); let events_iterator = contract_abi.events(); From 8ccd5842b71f0ec0d51dee32ca3bf43998265900 Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Tue, 5 Apr 2022 19:07:21 -0700 Subject: [PATCH 02/15] chore: bump ethers for the caching PR https://github.com/gakonst/ethers-rs/pull/1108 --- Cargo.lock | 24 ++++++++++++------------ Cargo.toml | 8 -------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7667acc44058..55515bba8251 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1255,8 +1255,9 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ - "ethers-addressbook 0.1.0", + "ethers-addressbook", "ethers-contract", "ethers-core", "ethers-etherscan", @@ -1266,16 +1267,6 @@ dependencies = [ "ethers-solc", ] -[[package]] -name = "ethers-addressbook" -version = "0.1.0" -dependencies = [ - "ethers-core", - "once_cell", - "serde", - "serde_json", -] - [[package]] name = "ethers-addressbook" version = "0.1.0" @@ -1290,6 +1281,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1307,6 +1299,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "Inflector", "cfg-if 1.0.0", @@ -1328,6 +1321,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1341,6 +1335,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "arrayvec 0.7.2", "bytes", @@ -1366,6 +1361,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "ethers-core", "ethers-solc", @@ -1379,6 +1375,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "async-trait", "ethers-contract", @@ -1401,6 +1398,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "async-trait", "auto_impl", @@ -1432,6 +1430,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "async-trait", "coins-bip32", @@ -1454,6 +1453,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" +source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" dependencies = [ "colored", "dunce", @@ -1761,7 +1761,7 @@ name = "foundry-utils" version = "0.2.0" dependencies = [ "ethers", - "ethers-addressbook 0.1.0 (git+https://github.com/gakonst/ethers-rs)", + "ethers-addressbook", "ethers-core", "ethers-etherscan", "ethers-providers", diff --git a/Cargo.toml b/Cargo.toml index 8372e620da56..bed88c78dcce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,11 +21,3 @@ codegen-units = 1 panic = "abort" # We end up stripping away these symbols anyway debug = 0 - -[patch."https://github.com/gakonst/ethers-rs"] -ethers = { path = "../ethers-rs" } -ethers-core = { path = "../ethers-rs/ethers-core" } -ethers-providers = { path = "../ethers-rs/ethers-providers" } -ethers-signers = { path = "../ethers-rs/ethers-signers" } -ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } -ethers-solc = { path = "../ethers-rs/ethers-solc" } From 05534fa2337617c98c171ef97a601a0ffdae0b2e Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Tue, 5 Apr 2022 19:07:35 -0700 Subject: [PATCH 03/15] feat: add cache ttl to etherscan identifier --- cli/src/cmd/forge/test.rs | 4 ++++ evm/src/trace/identifier/etherscan.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cli/src/cmd/forge/test.rs b/cli/src/cmd/forge/test.rs index 90eca74f41f7..92c8401eb737 100644 --- a/cli/src/cmd/forge/test.rs +++ b/cli/src/cmd/forge/test.rs @@ -460,10 +460,14 @@ fn test( // Set up identifiers let local_identifier = LocalTraceIdentifier::new(&runner.known_contracts); let remote_chain_id = runner.evm_opts.get_remote_chain_id(); + // Do not re-query etherscan for contracts that you've already queried today. + // TODO: Make this configurable. + let cache_ttl = Duration::from_secs(24 * 60 * 60); let etherscan_identifier = EtherscanIdentifier::new( remote_chain_id, config.etherscan_api_key.unwrap_or_default(), remote_chain_id.map(|chain_id| Config::foundry_etherscan_cache_dir(chain_id)).flatten(), + cache_ttl, ); // Set up test reporter channel diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index 3e33a05913f3..6d1dd8300407 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -20,6 +20,7 @@ impl EtherscanIdentifier { chain: Option>, etherscan_api_key: String, cache_path: Option, + ttl: Duration, ) -> Self { if let Some(cache_path) = &cache_path { if let Err(err) = std::fs::create_dir_all(cache_path.join("sources")) { @@ -30,7 +31,8 @@ impl EtherscanIdentifier { Self { client: chain .map(|chain| { - etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path).ok() + etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path, ttl) + .ok() }) .flatten(), } From 96216c690aa8d903a80067dd01cec2af7f8058ce Mon Sep 17 00:00:00 2001 From: Georgios Konstantopoulos Date: Tue, 5 Apr 2022 19:16:26 -0700 Subject: [PATCH 04/15] chore: clippy --- cli/src/cmd/forge/test.rs | 3 ++- evm/src/trace/decoder.rs | 4 ++-- evm/src/trace/identifier/etherscan.rs | 32 ++++++++++----------------- evm/src/trace/identifier/local.rs | 2 +- evm/src/trace/identifier/mod.rs | 1 + 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/cli/src/cmd/forge/test.rs b/cli/src/cmd/forge/test.rs index 92c8401eb737..ca9aa369c1f7 100644 --- a/cli/src/cmd/forge/test.rs +++ b/cli/src/cmd/forge/test.rs @@ -442,6 +442,7 @@ pub fn custom_run(mut args: TestArgs, include_fuzz_tests: bool) -> eyre::Result< } /// Runs all the tests +#[allow(clippy::too_many_arguments)] fn test( config: Config, mut runner: MultiContractRunner, @@ -466,7 +467,7 @@ fn test( let etherscan_identifier = EtherscanIdentifier::new( remote_chain_id, config.etherscan_api_key.unwrap_or_default(), - remote_chain_id.map(|chain_id| Config::foundry_etherscan_cache_dir(chain_id)).flatten(), + remote_chain_id.and_then(Config::foundry_etherscan_cache_dir), cache_ttl, ); diff --git a/evm/src/trace/decoder.rs b/evm/src/trace/decoder.rs index 0c5fecaffbf1..3c7e43dd7776 100644 --- a/evm/src/trace/decoder.rs +++ b/evm/src/trace/decoder.rs @@ -151,11 +151,11 @@ impl CallTraceDecoder { identifier.identify_addresses(unidentified_addresses).iter().for_each( |(address, contract, label, abi)| { if let Some(contract) = contract { - self.contracts.entry(*address).or_insert(contract.to_string()); + self.contracts.entry(*address).or_insert_with(|| contract.to_string()); } if let Some(label) = label { - self.labels.entry(*address).or_insert(label.to_string()); + self.labels.entry(*address).or_insert_with(|| label.to_string()); } if let Some(abi) = abi { diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index 6d1dd8300407..e60f4606bf44 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -29,12 +29,9 @@ impl EtherscanIdentifier { } Self { - client: chain - .map(|chain| { - etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path, ttl) - .ok() - }) - .flatten(), + client: chain.and_then(|chain| { + etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path, ttl).ok() + }), } } } @@ -55,17 +52,13 @@ impl TraceIdentifier for EtherscanIdentifier { loop { match client.contract_source_code(*addr).await { Ok(mut metadata) => { - break metadata - .items - .pop() - .map(|item| { - Some(( - *addr, - item.contract_name, - serde_json::from_str(&item.abi).ok()?, - )) - }) - .flatten() + break metadata.items.pop().and_then(|item| { + Some(( + *addr, + item.contract_name, + serde_json::from_str(&item.abi).ok()?, + )) + }) } Err(etherscan::errors::EtherscanError::RateLimitExceeded) => { sleep(Duration::from_secs(1)).await; @@ -87,11 +80,10 @@ impl TraceIdentifier for EtherscanIdentifier { } }) .map(|(addr, label, abi): (Address, String, ethers::abi::Abi)| { - (addr, Some(label.clone()), Some(label.clone()), Some(Cow::Owned(abi.clone()))) + (addr, Some(label.clone()), Some(label), Some(Cow::Owned(abi))) }) .collect(); - let rt = tokio::runtime::Runtime::new().expect("could not start tokio rt"); - rt.block_on(stream) + foundry_utils::RuntimeOrHandle::new().block_on(stream) } else { Vec::new() } diff --git a/evm/src/trace/identifier/local.rs b/evm/src/trace/identifier/local.rs index 11fb346178ed..8190369708b1 100644 --- a/evm/src/trace/identifier/local.rs +++ b/evm/src/trace/identifier/local.rs @@ -34,7 +34,7 @@ impl TraceIdentifier for LocalTraceIdentifier { code.map(|code| { self.local_contracts .iter() - .find(|(known_code, _)| diff_score(known_code, &code) < 0.1) + .find(|(known_code, _)| diff_score(known_code, code) < 0.1) .map_or((*addr, None, None, None), |(_, (name, abi))| { ( *addr, diff --git a/evm/src/trace/identifier/mod.rs b/evm/src/trace/identifier/mod.rs index abea1e262e67..3ab684e1796b 100644 --- a/evm/src/trace/identifier/mod.rs +++ b/evm/src/trace/identifier/mod.rs @@ -14,6 +14,7 @@ pub trait TraceIdentifier { /// /// The tuple is of the format `(contract, label, abi)`, where `contract` is intended to be of /// the format `":"`, e.g. `"Foo.json:Foo"`. + #[allow(clippy::type_complexity)] fn identify_addresses( &self, addresses: Vec<(&Address, Option<&Vec>)>, From 4d7f9a77069801adb9cd5584c937716e916f619f Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 6 Apr 2022 08:18:06 +0200 Subject: [PATCH 05/15] chore: re-add ethers patch section --- Cargo.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index bed88c78dcce..eb68480e6d0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,12 @@ codegen-units = 1 panic = "abort" # We end up stripping away these symbols anyway debug = 0 + +## Patch ethers-rs with a local checkout then run `cargo update -p ethers` +#[patch."https://github.com/gakonst/ethers-rs"] +#ethers = { path = "../ethers-rs" } +#ethers-core = { path = "../ethers-rs/ethers-core" } +#ethers-providers = { path = "../ethers-rs/ethers-providers" } +#ethers-signers = { path = "../ethers-rs/ethers-signers" } +#ethers-etherscan = { path = "../ethers-rs/ethers-etherscan" } +#ethers-solc = { path = "../ethers-rs/ethers-solc" } From 0dc11c76fa228d7c828d21766fe624616df2cd71 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 6 Apr 2022 08:57:02 +0200 Subject: [PATCH 06/15] build: bump ethers --- Cargo.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 55515bba8251..ca5e3eec5258 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1255,7 +1255,7 @@ dependencies = [ [[package]] name = "ethers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1270,7 +1270,7 @@ dependencies = [ [[package]] name = "ethers-addressbook" version = "0.1.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "ethers-core", "once_cell", @@ -1281,7 +1281,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1299,7 +1299,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "Inflector", "cfg-if 1.0.0", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "ethers-contract-abigen", "ethers-core", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "ethers-core" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "arrayvec 0.7.2", "bytes", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "ethers-etherscan" version = "0.2.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "ethers-core", "ethers-solc", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "async-trait", "ethers-contract", @@ -1398,7 +1398,7 @@ dependencies = [ [[package]] name = "ethers-providers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "async-trait", "auto_impl", @@ -1430,7 +1430,7 @@ dependencies = [ [[package]] name = "ethers-signers" version = "0.6.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "async-trait", "coins-bip32", @@ -1453,7 +1453,7 @@ dependencies = [ [[package]] name = "ethers-solc" version = "0.3.0" -source = "git+https://github.com/gakonst/ethers-rs#c436d19a9c6ccbb8b00b5367aa8bb057d2ad9f0d" +source = "git+https://github.com/gakonst/ethers-rs#d6e5647ab2f87bbdf2d9a2c740c9b436f9d5b880" dependencies = [ "colored", "dunce", From c6125614b45fc325f5d8a49160838a2062943d50 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Wed, 6 Apr 2022 09:13:43 +0200 Subject: [PATCH 07/15] test: fix tests --- cli/tests/it/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/tests/it/config.rs b/cli/tests/it/config.rs index 66758c5f74ed..8b4fe51ff1a2 100644 --- a/cli/tests/it/config.rs +++ b/cli/tests/it/config.rs @@ -68,6 +68,7 @@ forgetest!(can_extract_config_values, |prj: TestProject, mut cmd: TestCommand| { block_difficulty: 10, block_gas_limit: Some(100), eth_rpc_url: Some("localhost".to_string()), + etherscan_api_key: None, verbosity: 4, remappings: vec![Remapping::from_str("ds-test=lib/ds-test/").unwrap().into()], libraries: vec![ From 4e44b3cd8f4ad49533edc6d43ae0cade0938f608 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 08:09:05 +0200 Subject: [PATCH 08/15] fix: trace macros --- evm/src/trace/identifier/etherscan.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index e60f4606bf44..bec081e485a0 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -24,7 +24,7 @@ impl EtherscanIdentifier { ) -> Self { if let Some(cache_path) = &cache_path { if let Err(err) = std::fs::create_dir_all(cache_path.join("sources")) { - warn!("could not create etherscan cache dir: {:?}", err); + warn!(target: "etherscanidentifier", "could not create etherscan cache dir: {:?}", err); } } @@ -48,7 +48,7 @@ impl TraceIdentifier for EtherscanIdentifier { let client = client.clone(); async move { let mut i = 0; - trace!("requesting etherscan info for contract {addr}"); + trace!(target: "etherscanidentifier", "requesting etherscan info for contract {:?}", addr); loop { match client.contract_source_code(*addr).await { Ok(mut metadata) => { @@ -62,17 +62,17 @@ impl TraceIdentifier for EtherscanIdentifier { } Err(etherscan::errors::EtherscanError::RateLimitExceeded) => { sleep(Duration::from_secs(1)).await; - trace!("rate limit exceeded on attempt {i}"); + trace!(target: "etherscanidentifier", "rate limit exceeded on attempt {}", i); i += 1; if i < 5 { continue } else { - warn!("no more retries left for request"); + warn!(target: "etherscanidentifier", "no more retries left for request"); break None } } Err(err) => { - warn!("could not get etherscan info: {:?}", err); + warn!(target: "etherscanidentifier", "could not get etherscan info: {:?}", err); break None } } From 5e42e060f79d4967814916d88fe84740252c6b45 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 7 Apr 2022 11:33:20 +0200 Subject: [PATCH 09/15] bump color eyre and lock tracing-subscriber (#1220) * bump color eyre * lock tracing-subscriber 0.3.9 --- Cargo.lock | 55 +++++++++++++++--------------------------------- cli/Cargo.toml | 4 ++-- evm/Cargo.toml | 2 +- forge/Cargo.toml | 2 +- 4 files changed, 21 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca5e3eec5258..fd736a9b0723 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -682,9 +682,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" +checksum = "8ebf286c900a6d5867aeff75cfee3192857bb7f24b547d4f0df2ed6baa812c90" dependencies = [ "backtrace", "color-spantrace", @@ -692,19 +692,19 @@ dependencies = [ "indenter", "once_cell", "owo-colors", - "tracing-error 0.1.2", + "tracing-error", ] [[package]] name = "color-spantrace" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" dependencies = [ "once_cell", "owo-colors", "tracing-core", - "tracing-error 0.1.2", + "tracing-error", ] [[package]] @@ -1630,7 +1630,7 @@ dependencies = [ "serde_json", "tokio", "tracing", - "tracing-subscriber 0.3.10", + "tracing-subscriber", ] [[package]] @@ -1690,8 +1690,8 @@ dependencies = [ "tokio", "toml", "tracing", - "tracing-error 0.2.0", - "tracing-subscriber 0.3.10", + "tracing-error", + "tracing-subscriber", "ui", "vergen", "walkdir", @@ -1751,8 +1751,8 @@ dependencies = [ "thiserror", "tokio", "tracing", - "tracing-error 0.2.0", - "tracing-subscriber 0.3.10", + "tracing-error", + "tracing-subscriber", "url", ] @@ -1774,7 +1774,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tracing-subscriber 0.3.10", + "tracing-subscriber", ] [[package]] @@ -2974,9 +2974,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "1.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" +checksum = "5e72e30578e0d0993c8ae20823dd9cff2bc5517d2f586a8aef462a581e8a03eb" [[package]] name = "parity-scale-codec" @@ -4611,16 +4611,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-error" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24" -dependencies = [ - "tracing", - "tracing-subscriber 0.2.25", -] - [[package]] name = "tracing-error" version = "0.2.0" @@ -4628,7 +4618,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", - "tracing-subscriber 0.3.10", + "tracing-subscriber", ] [[package]] @@ -4654,20 +4644,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9df98b037d039d03400d9dd06b0f8ce05486b5f25e9a2d7d36196e142ebbc52" +checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" dependencies = [ "ansi_term", "lazy_static", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c676074568e0..4b1cc0edab85 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -30,8 +30,8 @@ ui = { path = "../ui" } dunce = "1.0.2" # ethers = "0.5" ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false } -eyre = "0.6.5" -color-eyre = "0.5" +eyre = "0.6" +color-eyre = "0.6" rustc-hex = "2.1.0" serde_json = "1.0.67" tokio = { version = "1.11.0", features = ["macros"] } diff --git a/evm/Cargo.toml b/evm/Cargo.toml index 6b819cf2e641..fa5828371f7a 100644 --- a/evm/Cargo.toml +++ b/evm/Cargo.toml @@ -19,7 +19,7 @@ thiserror = "1.0.29" # Logging tracing = "0.1.26" -tracing-subscriber = "0.3" +tracing-subscriber = "=0.3.9" tracing-error = "0.2.0" # Threading/futures diff --git a/forge/Cargo.toml b/forge/Cargo.toml index 8a1111c12d8b..c13bcbf8fdf8 100644 --- a/forge/Cargo.toml +++ b/forge/Cargo.toml @@ -19,7 +19,7 @@ glob = "0.3.0" # TODO: Trim down tokio = { version = "1.10.1" } tracing = "0.1.26" -tracing-subscriber = "0.3" +tracing-subscriber = "=0.3.9" proptest = "1.0.0" rayon = "1.5" rlp = "0.5.1" From d69d8d303c610e68467dcc42c1831fbcc94e1e8a Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 12:01:25 +0200 Subject: [PATCH 10/15] feat: pull etherscan api key and eth rpc from env --- config/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/config/src/lib.rs b/config/src/lib.rs index 00d7cc5090ec..fcc2567a1a70 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -808,6 +808,7 @@ impl From for Figment { .merge(Env::prefixed("DAPP_").ignore(&["REMAPPINGS", "LIBRARIES"]).global()) .merge(Env::prefixed("DAPP_TEST_").global()) .merge(DappEnvCompatProvider) + .merge(Env::raw().only(&["ETH_RPC_URL", "ETHERSCAN_API_KEY"])) .merge( Env::prefixed("FOUNDRY_").ignore(&["PROFILE", "REMAPPINGS", "LIBRARIES"]).global(), ) From 67e1a434dd0f5dc0cfa013e591f625bf0422e498 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 12:08:42 +0200 Subject: [PATCH 11/15] refactor: readability in `trace.addresses` --- evm/src/trace/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index 289a059d7ff8..a77e1f68f28c 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -67,15 +67,11 @@ impl CallTraceArena { self.arena .iter() .map(|node| { - let code = if node.trace.created() { + if node.trace.created() { if let RawOrDecodedReturnData::Raw(bytes) = &node.trace.output { - Some(bytes) - } else { - None + return (&node.trace.address, Some(bytes)) } - } else { - None - }; + } (&node.trace.address, code) }) From 77359b7468f9e4a7e278ea72b02230a6280e0e3c Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 12:22:14 +0200 Subject: [PATCH 12/15] refactor: add `AddressIdentity` type --- evm/src/trace/decoder.rs | 63 ++++++++++++++------------- evm/src/trace/identifier/etherscan.rs | 14 +++--- evm/src/trace/identifier/local.rs | 29 ++++++------ evm/src/trace/identifier/mod.rs | 19 ++++++-- evm/src/trace/mod.rs | 3 +- 5 files changed, 67 insertions(+), 61 deletions(-) diff --git a/evm/src/trace/decoder.rs b/evm/src/trace/decoder.rs index 3c7e43dd7776..1e288a4e0351 100644 --- a/evm/src/trace/decoder.rs +++ b/evm/src/trace/decoder.rs @@ -1,5 +1,6 @@ use super::{ - CallTraceArena, RawOrDecodedCall, RawOrDecodedLog, RawOrDecodedReturnData, TraceIdentifier, + identifier::TraceIdentifier, CallTraceArena, RawOrDecodedCall, RawOrDecodedLog, + RawOrDecodedReturnData, }; use crate::abi::{CHEATCODE_ADDRESS, CONSOLE_ABI, HEVM_ABI}; use ethers::{ @@ -148,41 +149,41 @@ impl CallTraceDecoder { }) .collect(); - identifier.identify_addresses(unidentified_addresses).iter().for_each( - |(address, contract, label, abi)| { - if let Some(contract) = contract { - self.contracts.entry(*address).or_insert_with(|| contract.to_string()); - } + identifier.identify_addresses(unidentified_addresses).iter().for_each(|identity| { + let address = identity.address; - if let Some(label) = label { - self.labels.entry(*address).or_insert_with(|| label.to_string()); - } + if let Some(contract) = &identity.contract { + self.contracts.entry(address).or_insert_with(|| contract.to_string()); + } - if let Some(abi) = abi { - // Store known functions for the address - abi.functions() - .map(|func| (func.short_signature(), func.clone())) - .for_each(|(sig, func)| self.functions.entry(sig).or_default().push(func)); + if let Some(label) = &identity.label { + self.labels.entry(address).or_insert_with(|| label.to_string()); + } - // Flatten events from all ABIs - abi.events() - .map(|event| ((event.signature(), indexed_inputs(event)), event.clone())) - .for_each(|(sig, event)| { - self.events.entry(sig).or_default().push(event); - }); + if let Some(abi) = &identity.abi { + // Store known functions for the address + abi.functions() + .map(|func| (func.short_signature(), func.clone())) + .for_each(|(sig, func)| self.functions.entry(sig).or_default().push(func)); - // Flatten errors from all ABIs - abi.errors().for_each(|error| { - let entry = self - .errors - .errors - .entry(error.name.clone()) - .or_insert_with(Default::default); - entry.push(error.clone()); + // Flatten events from all ABIs + abi.events() + .map(|event| ((event.signature(), indexed_inputs(event)), event.clone())) + .for_each(|(sig, event)| { + self.events.entry(sig).or_default().push(event); }); - } - }, - ); + + // Flatten errors from all ABIs + abi.errors().for_each(|error| { + let entry = self + .errors + .errors + .entry(error.name.clone()) + .or_insert_with(Default::default); + entry.push(error.clone()); + }); + } + }); } pub fn decode(&self, traces: &mut CallTraceArena) { diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index bec081e485a0..e71ebf802dbf 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -1,9 +1,5 @@ -use super::TraceIdentifier; -use ethers::{ - abi::{Abi, Address}, - etherscan, - types::Chain, -}; +use super::{AddressIdentity, TraceIdentifier}; +use ethers::{abi::Address, etherscan, types::Chain}; use futures::stream::{self, StreamExt}; use std::{borrow::Cow, path::PathBuf}; use tokio::time::{sleep, Duration}; @@ -40,7 +36,7 @@ impl TraceIdentifier for EtherscanIdentifier { fn identify_addresses( &self, addresses: Vec<(&Address, Option<&Vec>)>, - ) -> Vec<(Address, Option, Option, Option>)> { + ) -> Vec { if let Some(client) = &self.client { let stream = stream::iter(addresses.into_iter().map(futures::future::ready)) .buffered(10) @@ -79,8 +75,8 @@ impl TraceIdentifier for EtherscanIdentifier { } } }) - .map(|(addr, label, abi): (Address, String, ethers::abi::Abi)| { - (addr, Some(label.clone()), Some(label), Some(Cow::Owned(abi))) + .map(|(address, label, abi): (Address, String, ethers::abi::Abi)| { + AddressIdentity { address, label: Some(label.clone()), contract: Some(label), abi: Some(Cow::Owned(abi)) } }) .collect(); foundry_utils::RuntimeOrHandle::new().block_on(stream) diff --git a/evm/src/trace/identifier/local.rs b/evm/src/trace/identifier/local.rs index 8190369708b1..1ed4524d9a0b 100644 --- a/evm/src/trace/identifier/local.rs +++ b/evm/src/trace/identifier/local.rs @@ -1,4 +1,4 @@ -use super::TraceIdentifier; +use super::{AddressIdentity, TraceIdentifier}; use ethers::{ abi::{Abi, Address}, prelude::ArtifactId, @@ -27,22 +27,21 @@ impl TraceIdentifier for LocalTraceIdentifier { fn identify_addresses( &self, addresses: Vec<(&Address, Option<&Vec>)>, - ) -> Vec<(Address, Option, Option, Option>)> { + ) -> Vec { addresses .into_iter() - .filter_map(|(addr, code)| { - code.map(|code| { - self.local_contracts - .iter() - .find(|(known_code, _)| diff_score(known_code, code) < 0.1) - .map_or((*addr, None, None, None), |(_, (name, abi))| { - ( - *addr, - Some(name.clone()), - Some(name.clone()), - Some(Cow::Borrowed(abi)), - ) - }) + .filter_map(|(address, code)| { + let code = code?; + let (_, (name, abi)) = self + .local_contracts + .iter() + .find(|(known_code, _)| diff_score(known_code, code) < 0.1)?; + + Some(AddressIdentity { + address: *address, + contract: Some(name.clone()), + label: Some(name.clone()), + abi: Some(Cow::Borrowed(abi)), }) }) .collect() diff --git a/evm/src/trace/identifier/mod.rs b/evm/src/trace/identifier/mod.rs index 3ab684e1796b..764abf5a7878 100644 --- a/evm/src/trace/identifier/mod.rs +++ b/evm/src/trace/identifier/mod.rs @@ -7,16 +7,27 @@ pub use etherscan::EtherscanIdentifier; use ethers::abi::{Abi, Address}; use std::borrow::Cow; +/// An address identity +pub struct AddressIdentity<'a> { + /// The address this identity belongs to + pub address: Address, + /// The label for the address + pub label: Option, + /// The contract this address represents + /// + /// Note: This may be in the format `":"`. + pub contract: Option, + /// The ABI of the contract at this address + pub abi: Option>, +} + /// Trace identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub trait TraceIdentifier { // TODO: Update docs /// Attempts to identify an address in one or more call traces. - /// - /// The tuple is of the format `(contract, label, abi)`, where `contract` is intended to be of - /// the format `":"`, e.g. `"Foo.json:Foo"`. #[allow(clippy::type_complexity)] fn identify_addresses( &self, addresses: Vec<(&Address, Option<&Vec>)>, - ) -> Vec<(Address, Option, Option, Option>)>; + ) -> Vec; } diff --git a/evm/src/trace/mod.rs b/evm/src/trace/mod.rs index a77e1f68f28c..f44abc1ee790 100644 --- a/evm/src/trace/mod.rs +++ b/evm/src/trace/mod.rs @@ -2,7 +2,6 @@ /// /// Identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub mod identifier; -pub use identifier::TraceIdentifier; mod decoder; pub use decoder::CallTraceDecoder; @@ -73,7 +72,7 @@ impl CallTraceArena { } } - (&node.trace.address, code) + (&node.trace.address, None) }) .collect() } From ae82442c6ca21f464b1e4a38cdf3b48f25c00a25 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 13:59:16 +0200 Subject: [PATCH 13/15] refactor: add a rate limited etherscan stream --- cli/src/cmd/forge/test.rs | 2 +- evm/src/trace/identifier/etherscan.rs | 184 +++++++++++++++++++------- 2 files changed, 138 insertions(+), 48 deletions(-) diff --git a/cli/src/cmd/forge/test.rs b/cli/src/cmd/forge/test.rs index ca9aa369c1f7..5e2542191407 100644 --- a/cli/src/cmd/forge/test.rs +++ b/cli/src/cmd/forge/test.rs @@ -466,7 +466,7 @@ fn test( let cache_ttl = Duration::from_secs(24 * 60 * 60); let etherscan_identifier = EtherscanIdentifier::new( remote_chain_id, - config.etherscan_api_key.unwrap_or_default(), + config.etherscan_api_key, remote_chain_id.and_then(Config::foundry_etherscan_cache_dir), cache_ttl, ); diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index e71ebf802dbf..c57c246d3613 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -1,8 +1,17 @@ use super::{AddressIdentity, TraceIdentifier}; -use ethers::{abi::Address, etherscan, types::Chain}; -use futures::stream::{self, StreamExt}; -use std::{borrow::Cow, path::PathBuf}; -use tokio::time::{sleep, Duration}; +use ethers::{ + abi::{Abi, Address}, + etherscan, + prelude::{contract::ContractMetadata, errors::EtherscanError}, + types::Chain, +}; +use futures::{ + future::Future, + stream::{FuturesUnordered, Stream, StreamExt}, + task::{Context, Poll}, +}; +use std::{borrow::Cow, path::PathBuf, pin::Pin}; +use tokio::time::{Duration, Interval}; use tracing::{trace, warn}; /// A trace identifier that tries to identify addresses using Etherscan. @@ -12,9 +21,12 @@ pub struct EtherscanIdentifier { } impl EtherscanIdentifier { + /// Creates a new Etherscan identifier. + /// + /// The identifier is a noop if either `chain` or `etherscan_api_key` are `None`. pub fn new( chain: Option>, - etherscan_api_key: String, + etherscan_api_key: Option, cache_path: Option, ttl: Duration, ) -> Self { @@ -26,7 +38,9 @@ impl EtherscanIdentifier { Self { client: chain.and_then(|chain| { - etherscan::Client::new_cached(chain.into(), etherscan_api_key, cache_path, ttl).ok() + etherscan_api_key.and_then(|key| { + etherscan::Client::new_cached(chain.into(), key, cache_path, ttl).ok() + }) }), } } @@ -37,51 +51,127 @@ impl TraceIdentifier for EtherscanIdentifier { &self, addresses: Vec<(&Address, Option<&Vec>)>, ) -> Vec { - if let Some(client) = &self.client { - let stream = stream::iter(addresses.into_iter().map(futures::future::ready)) - .buffered(10) - .filter_map(|(addr, _)| { - let client = client.clone(); - async move { - let mut i = 0; - trace!(target: "etherscanidentifier", "requesting etherscan info for contract {:?}", addr); - loop { - match client.contract_source_code(*addr).await { - Ok(mut metadata) => { - break metadata.items.pop().and_then(|item| { - Some(( - *addr, - item.contract_name, - serde_json::from_str(&item.abi).ok()?, - )) - }) - } - Err(etherscan::errors::EtherscanError::RateLimitExceeded) => { - sleep(Duration::from_secs(1)).await; - trace!(target: "etherscanidentifier", "rate limit exceeded on attempt {}", i); - i += 1; - if i < 5 { - continue - } else { - warn!(target: "etherscanidentifier", "no more retries left for request"); - break None - } - } - Err(err) => { - warn!(target: "etherscanidentifier", "could not get etherscan info: {:?}", err); - break None + self.client.as_ref().map_or(Default::default(), |client| { + let mut fetcher = EtherscanFetcher::new(client.clone(), Duration::from_secs(1), 5); + + for (addr, _) in addresses { + fetcher.push(*addr); + } + + let fut = fetcher + .map(|(address, label, abi)| AddressIdentity { + address, + label: Some(label.clone()), + contract: Some(label), + abi: Some(Cow::Owned(abi)), + }) + .collect(); + + foundry_utils::RuntimeOrHandle::new().block_on(fut) + }) + } +} + +type EtherscanFuture = + Pin)>>>; + +/// A rate limit aware Etherscan client. +/// +/// Fetches information about multiple addresses concurrently, while respecting rate limits. +pub struct EtherscanFetcher { + /// The Etherscan client + client: etherscan::Client, + /// The time we wait if we hit the rate limit + timeout: Duration, + /// The interval we are currently waiting for before making a new request + backoff: Option, + /// The maximum amount of requests to send concurrently + concurrency: usize, + /// The addresses we have yet to make requests for + queue: Vec
, + /// The in progress requests + in_progress: FuturesUnordered, +} + +impl EtherscanFetcher { + pub fn new(client: etherscan::Client, timeout: Duration, concurrency: usize) -> Self { + Self { + client, + timeout, + backoff: None, + concurrency, + queue: Vec::new(), + in_progress: FuturesUnordered::new(), + } + } + + pub fn push(&mut self, address: Address) { + self.queue.push(address); + } + + fn queue_next_reqs(&mut self) { + if self.in_progress.len() < self.concurrency { + while let Some(addr) = self.queue.pop() { + let client = self.client.clone(); + trace!(target: "etherscanidentifier", "fetching info for {:?}", addr); + self.in_progress.push(Box::pin(async move { + let res = client.contract_source_code(addr).await; + (addr, res) + })); + + if self.in_progress.len() == self.concurrency { + break + } + } + } + } +} + +impl Stream for EtherscanFetcher { + type Item = (Address, String, Abi); + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let pin = self.get_mut(); + + loop { + if let Some(mut backoff) = pin.backoff.take() { + if backoff.poll_tick(cx).is_pending() { + pin.backoff = Some(backoff); + return Poll::Pending + } + } + + pin.queue_next_reqs(); + + let mut made_progress_this_iter = false; + match pin.in_progress.poll_next_unpin(cx) { + Poll::Pending => {} + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some((addr, res))) => { + made_progress_this_iter = true; + match res { + Ok(mut metadata) => { + if let Some(item) = metadata.items.pop() { + if let Ok(abi) = serde_json::from_str(&item.abi) { + return Poll::Ready(Some((addr, item.contract_name, abi))) } } } + Err(etherscan::errors::EtherscanError::RateLimitExceeded) => { + warn!(target: "etherscanidentifier", "rate limit exceeded on attempt"); + pin.backoff = Some(tokio::time::interval(pin.timeout)); + pin.queue.push(addr); + } + Err(err) => { + warn!(target: "etherscanidentifier", "could not get etherscan info: {:?}", err); + } } - }) - .map(|(address, label, abi): (Address, String, ethers::abi::Abi)| { - AddressIdentity { address, label: Some(label.clone()), contract: Some(label), abi: Some(Cow::Owned(abi)) } - }) - .collect(); - foundry_utils::RuntimeOrHandle::new().block_on(stream) - } else { - Vec::new() + } + } + + if !made_progress_this_iter { + return Poll::Pending + } } } } From d22f4abf1d2473f0da0063143fea930eeb1d80bd Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 15:04:06 +0200 Subject: [PATCH 14/15] test: don't set `ETH_RPC_URL` as its not used --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ffca491f4e3..afbf85ea2b49 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,8 +10,6 @@ jobs: unit: name: unit tests runs-on: ubuntu-latest - env: - ETH_RPC_URL: https://eth-mainnet.alchemyapi.io/v2/Lc7oIGYeL_QvInzI0Wiu_pOZZDEKBrdf steps: - name: Checkout sources uses: actions/checkout@v2 From 793f5b1e82e0a85f79186cc1de980933b61d3567 Mon Sep 17 00:00:00 2001 From: Oliver Nordbjerg Date: Thu, 7 Apr 2022 15:22:44 +0200 Subject: [PATCH 15/15] refactor: smol nit --- evm/src/trace/identifier/etherscan.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/evm/src/trace/identifier/etherscan.rs b/evm/src/trace/identifier/etherscan.rs index c57c246d3613..1b2c8d3ca2fc 100644 --- a/evm/src/trace/identifier/etherscan.rs +++ b/evm/src/trace/identifier/etherscan.rs @@ -110,18 +110,16 @@ impl EtherscanFetcher { } fn queue_next_reqs(&mut self) { - if self.in_progress.len() < self.concurrency { - while let Some(addr) = self.queue.pop() { + while self.in_progress.len() < self.concurrency { + if let Some(addr) = self.queue.pop() { let client = self.client.clone(); trace!(target: "etherscanidentifier", "fetching info for {:?}", addr); self.in_progress.push(Box::pin(async move { let res = client.contract_source_code(addr).await; (addr, res) })); - - if self.in_progress.len() == self.concurrency { - break - } + } else { + break } } }