diff --git a/Cargo.lock b/Cargo.lock index 222fe94c25..eca3268598 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -159,8 +165,8 @@ checksum = "759d98a5db12e9c9d98ef2b92f794ae5c7ded6ec18d21c3fa485c9c65bec237d" dependencies = [ "itertools", "proc-macro-error", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -216,9 +222,9 @@ version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -241,6 +247,15 @@ dependencies = [ "critical-section", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" +dependencies = [ + "critical-section", +] + [[package]] name = "attohttpc" version = "0.16.3" @@ -271,8 +286,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -384,19 +399,19 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -420,6 +435,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" + [[package]] name = "bitvec" version = "0.17.4" @@ -470,6 +491,131 @@ dependencies = [ "generic-array", ] +[[package]] +name = "boa_ast" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "bitflags 2.3.1", + "boa_interner", + "boa_macros", + "indexmap", + "num-bigint", + "rustc-hash", +] + +[[package]] +name = "boa_engine" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "bitflags 2.3.1", + "boa_ast", + "boa_gc", + "boa_icu_provider", + "boa_interner", + "boa_macros", + "boa_parser", + "boa_profiler", + "chrono", + "dashmap", + "fast-float", + "icu_normalizer", + "indexmap", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "num_enum", + "once_cell", + "pollster", + "rand 0.8.5", + "regress", + "rustc-hash", + "ryu-js", + "serde", + "serde_json", + "sptr", + "static_assertions", + "tap", + "thin-vec", + "thiserror", +] + +[[package]] +name = "boa_gc" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "boa_macros", + "boa_profiler", + "thin-vec", +] + +[[package]] +name = "boa_icu_provider" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "icu_collections", + "icu_normalizer", + "icu_properties", + "icu_provider", + "once_cell", + "zerovec", +] + +[[package]] +name = "boa_interner" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "boa_gc", + "boa_macros", + "hashbrown 0.14.0", + "indexmap", + "once_cell", + "phf", + "rustc-hash", + "static_assertions", +] + +[[package]] +name = "boa_macros" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", + "synstructure 0.13.0", +] + +[[package]] +name = "boa_parser" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" +dependencies = [ + "bitflags 2.3.1", + "boa_ast", + "boa_icu_provider", + "boa_interner", + "boa_macros", + "boa_profiler", + "fast-float", + "icu_properties", + "num-bigint", + "num-traits", + "once_cell", + "regress", + "rustc-hash", +] + +[[package]] +name = "boa_profiler" +version = "0.16.0" +source = "git+https://github.com/boa-dev/boa#da4a3315d75680e63ce2a2dba66000ef2e0e50b9" + [[package]] name = "brotli" version = "3.3.4" @@ -624,12 +770,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "serde", "winapi", @@ -698,7 +844,7 @@ version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_lex 0.2.4", "indexmap", "textwrap", @@ -710,7 +856,7 @@ version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex 0.3.2", "is-terminal", @@ -727,8 +873,8 @@ checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck", "proc-macro-error", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -762,10 +908,10 @@ version = "0.1.0" dependencies = [ "convert_case 0.6.0", "parity-scale-codec", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "serde", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -1041,7 +1187,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -1106,7 +1252,7 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ - "quote 1.0.26", + "quote 1.0.28", "syn 1.0.109", ] @@ -1162,8 +1308,8 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "scratch", "syn 1.0.109", ] @@ -1180,8 +1326,8 @@ version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1213,8 +1359,8 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "strsim 0.9.3", "syn 1.0.109", ] @@ -1227,8 +1373,8 @@ checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "strsim 0.10.0", "syn 1.0.109", ] @@ -1240,7 +1386,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", - "quote 1.0.26", + "quote 1.0.28", "syn 1.0.109", ] @@ -1251,7 +1397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" dependencies = [ "darling_core 0.14.3", - "quote 1.0.26", + "quote 1.0.28", "syn 1.0.109", ] @@ -1319,8 +1465,8 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1332,8 +1478,8 @@ checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" dependencies = [ "darling 0.10.2", "derive_builder_core", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1344,8 +1490,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" dependencies = [ "darling 0.10.2", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1356,8 +1502,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "rustc_version", "syn 1.0.109", ] @@ -1477,6 +1623,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", +] + [[package]] name = "dns-lookup" version = "1.0.8" @@ -1562,8 +1719,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1697,8 +1854,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" dependencies = [ "heck", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1709,8 +1866,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -1722,8 +1879,8 @@ checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "rustc_version", "syn 1.0.109", ] @@ -1734,9 +1891,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -1872,12 +2029,12 @@ dependencies = [ "eyre", "hex", "prettyplease", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", "serde", "serde_json", - "syn 2.0.15", + "syn 2.0.18", "toml 0.7.3", "walkdir", ] @@ -1892,10 +2049,10 @@ dependencies = [ "ethers-contract-abigen", "ethers-core", "hex", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "serde_json", - "syn 2.0.15", + "syn 2.0.18", ] [[package]] @@ -1921,7 +2078,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn 2.0.15", + "syn 2.0.18", "tempfile", "thiserror", "tiny-keccak", @@ -2048,6 +2205,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fast-float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" + [[package]] name = "fastrand" version = "1.9.0" @@ -2220,9 +2383,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -2479,6 +2642,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hashers" version = "1.0.1" @@ -2513,7 +2682,7 @@ version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" dependencies = [ - "atomic-polyfill", + "atomic-polyfill 0.1.11", "hash32", "rustc_version", "serde", @@ -2755,6 +2924,88 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "icu_collections" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8302d8dfd6044d3ddb3f807a5ef3d7bbca9a574959c6d6e4dc39aa7012d0d5" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3003f85dccfc0e238ff567693248c59153a46f4e6125ba4020b973cef4d1d335" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", +] + +[[package]] +name = "icu_normalizer" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "652869735c9fb9f5a64ba180ee16f2c848390469c116deef517ecc53f4343598" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_properties" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce0e1aa26851f16c9e04412a5911c86b7f8768dac8f8d4c5f1c568a7e5d7a434" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_provider" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc312a7b6148f7dfe098047ae2494d12d4034f48ade58d4f353000db376e305" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "serde", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2838,8 +3089,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3095,8 +3346,8 @@ checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" dependencies = [ "heck", "proc-macro-crate", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3288,6 +3539,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +[[package]] +name = "litemap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a04a5b2b6f54acba899926491d0a6c59d98012938ca2ab5befb281c034e8f94" + [[package]] name = "lock_api" version = "0.4.9" @@ -3433,8 +3690,8 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3524,8 +3781,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" dependencies = [ "cfg-if", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3545,8 +3802,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3571,7 +3828,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "static_assertions", @@ -3626,6 +3883,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "serde", ] [[package]] @@ -3716,9 +3974,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -3732,9 +3990,13 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +dependencies = [ + "atomic-polyfill 1.0.2", + "critical-section", +] [[package]] name = "oorandom" @@ -3768,8 +4030,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3841,8 +4103,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -3974,6 +4236,48 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "phf" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.0" @@ -3989,9 +4293,9 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -4069,6 +4373,12 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "pollster" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" + [[package]] name = "polyval" version = "0.5.3" @@ -4174,8 +4484,8 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" dependencies = [ - "proc-macro2 1.0.56", - "syn 2.0.15", + "proc-macro2 1.0.60", + "syn 2.0.18", ] [[package]] @@ -4209,8 +4519,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "version_check", ] @@ -4221,8 +4531,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "version_check", ] @@ -4237,9 +4547,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] @@ -4251,7 +4561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" dependencies = [ "bit-set", - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", @@ -4345,11 +4655,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ - "proc-macro2 1.0.56", + "proc-macro2 1.0.60", ] [[package]] @@ -4460,7 +4770,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4491,7 +4801,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4500,7 +4810,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4546,6 +4856,16 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "regress" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a9ecfa0cb04d0b04dddb99b8ccf4f66bc8dfd23df694b398570bd8ae3a50fb" +dependencies = [ + "hashbrown 0.13.2", + "memchr", +] + [[package]] name = "reqwest" version = "0.11.18" @@ -4995,7 +5315,7 @@ dependencies = [ name = "reth-libmdbx" version = "0.1.6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "criterion", "derive_more", @@ -5035,11 +5355,11 @@ version = "0.1.0" dependencies = [ "metrics", "once_cell", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "regex", "serial_test", - "syn 2.0.15", + "syn 2.0.18", "trybuild", ] @@ -5235,11 +5555,16 @@ dependencies = [ name = "reth-revm-inspectors" version = "0.1.0" dependencies = [ + "boa_engine", + "boa_gc", "hashbrown 0.13.2", "reth-primitives", "reth-rpc-types", "revm", "serde", + "serde_json", + "thiserror", + "tokio", ] [[package]] @@ -5272,9 +5597,9 @@ dependencies = [ name = "reth-rlp-derive" version = "0.1.1" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -5512,7 +5837,7 @@ dependencies = [ "aquamarine", "async-trait", "auto_impl", - "bitflags", + "bitflags 1.3.2", "fnv", "futures-util", "parking_lot 0.12.1", @@ -5681,8 +6006,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5740,7 +6065,7 @@ version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno 0.2.8", "io-lifetimes", "libc", @@ -5754,7 +6079,7 @@ version = "0.37.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno 0.3.1", "io-lifetimes", "libc", @@ -5829,6 +6154,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + [[package]] name = "salsa20" version = "0.10.2" @@ -5866,8 +6197,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -5979,7 +6310,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -6037,22 +6368,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -6110,8 +6441,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" dependencies = [ "darling 0.14.3", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -6135,8 +6466,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -6315,6 +6646,12 @@ dependencies = [ "time", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "sketches-ddsketch" version = "0.2.0" @@ -6412,6 +6749,12 @@ dependencies = [ "der 0.7.3", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -6458,8 +6801,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "rustversion", "syn 1.0.109", ] @@ -6542,22 +6885,46 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.15" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", + "unicode-xid 0.2.4", +] + +[[package]] +name = "synstructure" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", + "unicode-xid 0.2.4", +] + [[package]] name = "tap" version = "1.0.1" @@ -6611,8 +6978,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9186daca5c58cb307d09731e0ba06b13fd6c036c90672b9bfc31cecf76cf689" dependencies = [ "cargo_metadata", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "serde", "strum_macros", ] @@ -6626,8 +6993,8 @@ dependencies = [ "darling 0.14.3", "if_chain", "lazy_static", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "subprocess", "syn 1.0.109", "test-fuzz-internal", @@ -6655,6 +7022,12 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +[[package]] +name = "thin-vec" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8" + [[package]] name = "thiserror" version = "1.0.40" @@ -6670,9 +7043,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -6721,6 +7094,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -6771,9 +7154,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -6926,7 +7309,7 @@ checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" dependencies = [ "async-compression", "base64 0.20.0", - "bitflags", + "bitflags 1.3.2", "bytes", "futures-core", "futures-util", @@ -6990,8 +7373,8 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -7076,7 +7459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" dependencies = [ "lazy_static", - "quote 1.0.26", + "quote 1.0.28", "syn 1.0.109", ] @@ -7207,7 +7590,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cassowary", "crossterm", "unicode-segmentation", @@ -7339,8 +7722,8 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", ] @@ -7361,6 +7744,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52df8b7fb78e7910d776fccf2e42ceaf3604d55e8e7eb2dbd183cb1441d8a692" + +[[package]] +name = "utf8_iter" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a8922555b9500e3d865caed19330172cd67cbf82203f1a3311d8c305cc9f33" + [[package]] name = "uuid" version = "0.8.2" @@ -7469,8 +7864,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "wasm-bindgen-shared", ] @@ -7493,7 +7888,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.26", + "quote 1.0.28", "wasm-bindgen-macro-support", ] @@ -7503,8 +7898,8 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", + "proc-macro2 1.0.60", + "quote 1.0.28", "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -7743,6 +8138,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60e49e42bdb1d5dc76f4cd78102f8f0714d32edfa3efb82286eb0f0b1fc0da0f" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -7792,6 +8199,51 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yoke" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1848075a23a28f9773498ee9a0f2cf58fcbad4f8c0ccf84a210ab33c6ae495de" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af46c169923ed7516eef0aa32b56d2651b229f57458ebe46b49ddd6efef5b7a2" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "zerofrom" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df54d76c3251de27615dfcce21e636c172dafb2549cd7fd93e21c66f6ca6bea2" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eae7c1f7d4b8eafce526bc0771449ddc2f250881ae31c50d22c032b5a1c499" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", + "synstructure 0.12.6", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -7807,9 +8259,32 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.26", - "syn 2.0.15", + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 2.0.18", +] + +[[package]] +name = "zerovec" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198f54134cd865f437820aa3b43d0ad518af4e68ee161b444cdd15d8e567c8ea" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486558732d5dde10d0f8cb2936507c1bb21bc539d924c949baf5f36a58e51bac" +dependencies = [ + "proc-macro2 1.0.60", + "quote 1.0.28", + "syn 1.0.109", + "synstructure 0.12.6", ] [[package]] diff --git a/crates/revm/revm-inspectors/Cargo.toml b/crates/revm/revm-inspectors/Cargo.toml index 066d652149..8e17b60f06 100644 --- a/crates/revm/revm-inspectors/Cargo.toml +++ b/crates/revm/revm-inspectors/Cargo.toml @@ -18,3 +18,14 @@ revm = { workspace = true } hashbrown = "0.13" serde = { workspace = true, features = ["derive"] } +thiserror = {version = "1.0", optional = true } +serde_json = { version = "1.0", optional = true } + +# js-tracing-inspector +boa_engine = { git = "https://github.com/boa-dev/boa", optional = true } +boa_gc = { git = "https://github.com/boa-dev/boa", optional = true } +tokio = { version = "1", features = ["sync"], optional = true } + +[features] +default = ["js-tracer"] +js-tracer = ["boa_engine", "boa_gc", "tokio","thiserror", "serde_json"] diff --git a/crates/revm/revm-inspectors/src/tracing/js/bigint.js b/crates/revm/revm-inspectors/src/tracing/js/bigint.js new file mode 100644 index 0000000000..d9a8411b26 --- /dev/null +++ b/crates/revm/revm-inspectors/src/tracing/js/bigint.js @@ -0,0 +1 @@ +var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),LOG_MAX_INT=Math.log(MAX_INT);function Integer(v,radix){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10?parseValue(v):parseBase(v,radix);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i=base?1:0;r[i]=sum-carry*base}while(i0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;ib_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(absb.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);var value=n.value;if(value===0)return false;if(value===1)return true;if(value===2)return this.isEven();return this.mod(n).equals(Integer[0])};SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(25))return true}BigInteger.prototype.isPrime=function(){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs(),nPrev=n.prev();var a=[2,3,5,7,11,13,17,19],b=nPrev,d,t,i,x;while(b.isEven())b=b.divide(2);for(i=0;i-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return(typeof n==="number"||typeof n==="string")&&+Math.abs(n)<=BASE||n instanceof BigInteger&&n.value.length<=1}BigInteger.prototype.shiftLeft=function(n){if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftRight(-n);var result=this;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(n){var remQuo;if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}n=+n;if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=Math.min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var length=range.value.length-1;var result=[],restricted=true;for(var i=length;i>=0;i--){var top=restricted?range.value[i]:BASE;var digit=truncate(Math.random()*top);result.unshift(digit);if(digit=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}else if(c.charCodeAt(0)-87>=absBase){throw new Error(c+" is not a valid digit in base "+base+".")}}}if(2<=base&&base<=36){if(length<=LOG_MAX_INT/Math.log(base)){var result=parseInt(text,base);if(isNaN(result)){throw new Error(c+" is not a valid digit in base "+base+".")}return new SmallInteger(parseInt(text,base))}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i");digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit){var v=digit.value;if(typeof v==="number")v=[v];if(v.length===1&&v[0]<=35){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(v[0])}return"<"+v+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return"0";throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return"0";if(n.isNegative())return new Array(1-n).join("10");return"1"+new Array(+n).join("01")}var minusSign="";if(n.isNegative()&&base.isPositive()){minusSign="-";n=n.abs()}if(base.equals(1)){if(n.isZero())return"0";return minusSign+new Array(+n+1).join(1)}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(stringify(digit))}out.push(stringify(left));return minusSign+out.reverse().join("")}BigInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!==10)return toBase(this,radix);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix){if(radix===undefined)radix=10;if(radix!=10)return toBase(this,radix);return String(this.value)};BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return+this.toString()};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return new SmallInteger(x);throw"Invalid integer: "+v}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=new SmallInteger(i);if(i>0)Integer[-i]=new SmallInteger(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}; bigInt \ No newline at end of file diff --git a/crates/revm/revm-inspectors/src/tracing/js/bindings.rs b/crates/revm/revm-inspectors/src/tracing/js/bindings.rs new file mode 100644 index 0000000000..5e988c955c --- /dev/null +++ b/crates/revm/revm-inspectors/src/tracing/js/bindings.rs @@ -0,0 +1,797 @@ +//! Type bindings for js tracing inspector + +use crate::tracing::{ + js::{ + builtins::{ + address_to_buf, bytes_to_address, bytes_to_hash, from_buf, to_bigint, to_bigint_array, + to_buf, to_buf_value, + }, + JsDbRequest, + }, + types::CallKind, +}; +use boa_engine::{ + native_function::NativeFunction, + object::{builtins::JsArrayBuffer, FunctionObjectBuilder}, + Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsValue, +}; +use boa_gc::{empty_trace, Finalize, Gc, Trace}; +use reth_primitives::{bytes::Bytes, Account, Address, H256, KECCAK_EMPTY, U256}; +use revm::{ + interpreter::{ + opcode::{PUSH0, PUSH32}, + Memory, OpCode, Stack, + }, + primitives::State, +}; +use std::{borrow::Borrow, sync::mpsc::channel}; +use tokio::sync::mpsc; + +/// A macro that creates a native function that returns via [JsValue::from] +macro_rules! js_value_getter { + ($value:ident, $ctx:ident) => { + FunctionObjectBuilder::new( + $ctx, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from($value))), + ) + .length(0) + .build() + }; +} + +/// A macro that creates a native function that returns a captured JsValue +macro_rules! js_value_capture_getter { + ($value:ident, $ctx:ident) => { + FunctionObjectBuilder::new( + $ctx, + NativeFunction::from_copy_closure_with_captures( + move |_this, _args, input, _ctx| Ok(JsValue::from(input.clone())), + $value, + ), + ) + .length(0) + .build() + }; +} + +/// The Log object that is passed to the javascript inspector. +#[derive(Debug)] +pub(crate) struct StepLog { + /// Stack before step execution + pub(crate) stack: StackObj, + /// Opcode to be executed + pub(crate) op: OpObj, + /// All allocated memory in a step + pub(crate) memory: MemoryObj, + /// Program counter before step execution + pub(crate) pc: u64, + /// Remaining gas before step execution + pub(crate) gas_remaining: u64, + /// Gas cost of step execution + pub(crate) cost: u64, + /// Call depth + pub(crate) depth: u64, + /// Gas refund counter before step execution + pub(crate) refund: u64, + /// returns information about the error if one occurred, otherwise returns undefined + pub(crate) error: Option, + /// The contract object available to the js inspector + pub(crate) contract: Contract, +} + +impl StepLog { + /// Converts the contract object into a js object + /// + /// Caution: this expects a global property `bigint` to be present. + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let Self { + stack, + op, + memory, + pc, + gas_remaining: gas, + cost, + depth, + refund, + error, + contract, + } = self; + let obj = JsObject::default(); + + // fields + let op = op.into_js_object(context)?; + let memory = memory.into_js_object(context)?; + let stack = stack.into_js_object(context)?; + let contract = contract.into_js_object(context)?; + + obj.set("op", op, false, context)?; + obj.set("memory", memory, false, context)?; + obj.set("stack", stack, false, context)?; + obj.set("contract", contract, false, context)?; + + // methods + let error = + if let Some(error) = error { JsValue::from(error) } else { JsValue::undefined() }; + let get_error = js_value_capture_getter!(error, context); + let get_pc = js_value_getter!(pc, context); + let get_gas = js_value_getter!(gas, context); + let get_cost = js_value_getter!(cost, context); + let get_refund = js_value_getter!(refund, context); + let get_depth = js_value_getter!(depth, context); + + obj.set("getPc", get_pc, false, context)?; + obj.set("getError", get_error, false, context)?; + obj.set("getGas", get_gas, false, context)?; + obj.set("getCost", get_cost, false, context)?; + obj.set("getDepth", get_depth, false, context)?; + obj.set("getRefund", get_refund, false, context)?; + + Ok(obj) + } +} + +/// Represents the memory object +#[derive(Debug)] +pub(crate) struct MemoryObj(pub(crate) Memory); + +impl MemoryObj { + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let obj = JsObject::default(); + let len = self.0.len(); + // TODO: add into data + let value = to_buf(self.0.data().clone(), context)?; + + let length = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| { + Ok(JsValue::from(len as u64)) + }), + ) + .length(0) + .build(); + + let slice = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + |_this, args, memory, ctx| { + let start = args.get_or_undefined(0).to_number(ctx)?; + let end = args.get_or_undefined(1).to_number(ctx)?; + if end < start || start < 0. { + return Err(JsError::from_native(JsNativeError::typ().with_message( + format!( + "tracer accessed out of bound memory: offset {start}, end {end}" + ), + ))) + } + let start = start as usize; + let end = end as usize; + + let mut mem = memory.take()?; + let slice = mem.drain(start..end).collect::>(); + to_buf_value(slice, ctx) + }, + value.clone(), + ), + ) + .length(2) + .build(); + + let get_uint = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + |_this, args, memory, ctx| { + let offset_f64 = args.get_or_undefined(0).to_number(ctx)?; + + let mut mem = memory.take()?; + let offset = offset_f64 as usize; + if mem.len() < offset+32 || offset_f64 < 0. { + return Err(JsError::from_native( + JsNativeError::typ().with_message(format!("tracer accessed out of bound memory: available {}, offset {}, size 32", mem.len(), offset)) + )); + } + + let slice = mem.drain(offset..offset+32).collect::>(); + to_buf_value(slice, ctx) + }, + value + ), + ) + .length(1) + .build(); + + obj.set("slice", slice, false, context)?; + obj.set("getUint", get_uint, false, context)?; + obj.set("length", length, false, context)?; + Ok(obj) + } +} + +/// Represents the opcode object +#[derive(Debug)] +pub(crate) struct OpObj(pub(crate) OpCode); + +impl OpObj { + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let obj = JsObject::default(); + let value = self.0.u8(); + let is_push = (PUSH0..=PUSH32).contains(&value); + + let to_number = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(value))), + ) + .length(0) + .build(); + + let is_push = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(is_push))), + ) + .length(0) + .build(); + + let to_string = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| { + let s = OpCode::try_from_u8(value).expect("invalid opcode").to_string(); + Ok(JsValue::from(s)) + }), + ) + .length(0) + .build(); + + obj.set("toNumber", to_number, false, context)?; + obj.set("toString", to_string, false, context)?; + obj.set("isPush", is_push, false, context)?; + Ok(obj) + } +} + +/// Represents the stack object +#[derive(Debug, Clone)] +pub(crate) struct StackObj(pub(crate) Stack); + +impl StackObj { + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let obj = JsObject::default(); + let stack = self.0; + let len = stack.len(); + let stack_arr = to_bigint_array(stack.data(), context)?; + let length = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, _ctx| Ok(JsValue::from(len))), + ) + .length(0) + .build(); + + let peek = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, stack_arr, ctx| { + let idx_f64 = args.get_or_undefined(0).to_number(ctx)?; + let idx = idx_f64 as usize; + if len <= idx || idx_f64 < 0. { + return Err(JsError::from_native(JsNativeError::typ().with_message( + format!( + "tracer accessed out of bound stack: size {len}, index {idx_f64}" + ), + ))) + } + stack_arr.get(idx as u64, ctx) + }, + stack_arr, + ), + ) + .length(1) + .build(); + + obj.set("length", length, false, context)?; + obj.set("peek", peek, false, context)?; + Ok(obj) + } +} + +/// Represents the contract object +#[derive(Debug, Clone, Default)] +pub(crate) struct Contract { + pub(crate) caller: Address, + pub(crate) contract: Address, + pub(crate) value: U256, + pub(crate) input: Bytes, +} + +impl Contract { + /// Converts the contract object into a js object + /// + /// Caution: this expects a global property `bigint` to be present. + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let Contract { caller, contract, value, input } = self; + let obj = JsObject::default(); + + let get_caller = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, ctx| { + to_buf_value(caller.as_bytes().to_vec(), ctx) + }), + ) + .length(0) + .build(); + + let get_address = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, ctx| { + to_buf_value(contract.as_bytes().to_vec(), ctx) + }), + ) + .length(0) + .build(); + + let get_value = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure(move |_this, _args, ctx| to_bigint(value, ctx)), + ) + .length(0) + .build(); + + let input = to_buf_value(input.to_vec(), context)?; + let get_input = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, _args, input, _ctx| Ok(input.clone()), + input, + ), + ) + .length(0) + .build(); + + obj.set("getCaller", get_caller, false, context)?; + obj.set("getAddress", get_address, false, context)?; + obj.set("getValue", get_value, false, context)?; + obj.set("getInput", get_input, false, context)?; + + Ok(obj) + } +} + +/// Represents the call frame object for exit functions +pub(crate) struct FrameResult { + pub(crate) gas_used: u64, + pub(crate) output: Bytes, + pub(crate) error: Option, +} + +impl FrameResult { + pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult { + let Self { gas_used, output, error } = self; + let obj = JsObject::default(); + + let output = to_buf_value(output.to_vec(), ctx)?; + let get_output = FunctionObjectBuilder::new( + ctx, + NativeFunction::from_copy_closure_with_captures( + move |_this, _args, output, _ctx| Ok(output.clone()), + output, + ), + ) + .length(0) + .build(); + + let error = error.map(JsValue::from).unwrap_or_default(); + let get_error = js_value_capture_getter!(error, ctx); + let get_gas_used = js_value_getter!(gas_used, ctx); + + obj.set("getGasUsed", get_gas_used, false, ctx)?; + obj.set("getOutput", get_output, false, ctx)?; + obj.set("getError", get_error, false, ctx)?; + + Ok(obj) + } +} + +/// Represents the call frame object for enter functions +pub(crate) struct CallFrame { + pub(crate) contract: Contract, + pub(crate) kind: CallKind, + pub(crate) gas: u64, +} + +impl CallFrame { + pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult { + let CallFrame { contract: Contract { caller, contract, value, input }, kind, gas } = self; + let obj = JsObject::default(); + + let get_from = FunctionObjectBuilder::new( + ctx, + NativeFunction::from_copy_closure(move |_this, _args, ctx| { + to_buf_value(caller.as_bytes().to_vec(), ctx) + }), + ) + .length(0) + .build(); + + let get_to = FunctionObjectBuilder::new( + ctx, + NativeFunction::from_copy_closure(move |_this, _args, ctx| { + to_buf_value(contract.as_bytes().to_vec(), ctx) + }), + ) + .length(0) + .build(); + + let get_value = FunctionObjectBuilder::new( + ctx, + NativeFunction::from_copy_closure(move |_this, _args, ctx| to_bigint(value, ctx)), + ) + .length(0) + .build(); + + let input = to_buf_value(input.to_vec(), ctx)?; + let get_input = FunctionObjectBuilder::new( + ctx, + NativeFunction::from_copy_closure_with_captures( + move |_this, _args, input, _ctx| Ok(input.clone()), + input, + ), + ) + .length(0) + .build(); + + let get_gas = js_value_getter!(gas, ctx); + let ty = kind.to_string(); + let get_type = js_value_capture_getter!(ty, ctx); + + obj.set("getFrom", get_from, false, ctx)?; + obj.set("getTo", get_to, false, ctx)?; + obj.set("getValue", get_value, false, ctx)?; + obj.set("getInput", get_input, false, ctx)?; + obj.set("getGas", get_gas, false, ctx)?; + obj.set("getType", get_type, false, ctx)?; + + Ok(obj) + } +} + +/// The `ctx` object that represents the context in which the transaction is executed. +pub(crate) struct EvmContext { + /// String, one of the two values CALL and CREATE + pub(crate) r#type: String, + /// Sender of the transaction + pub(crate) from: Address, + /// Target of the transaction + pub(crate) to: Option
, + pub(crate) input: Bytes, + /// Gas limit + pub(crate) gas: u64, + /// Number, amount of gas used in executing the transaction (excludes txdata costs) + pub(crate) gas_used: u64, + /// Number, gas price configured in the transaction being executed + pub(crate) gas_price: u64, + /// Number, intrinsic gas for the transaction being executed + pub(crate) intrinsic_gas: u64, + /// big.int Amount to be transferred in wei + pub(crate) value: U256, + /// Number, block number + pub(crate) block: u64, + pub(crate) output: Bytes, + /// Number, block number + pub(crate) time: String, + // TODO more fields + pub(crate) block_hash: Option, + pub(crate) tx_index: Option, + pub(crate) tx_hash: Option, +} + +impl EvmContext { + pub(crate) fn into_js_object(self, ctx: &mut Context<'_>) -> JsResult { + let Self { + r#type, + from, + to, + input, + gas, + gas_used, + gas_price, + intrinsic_gas, + value, + block, + output, + time, + block_hash, + tx_index, + tx_hash, + } = self; + let obj = JsObject::default(); + + // add properties + + obj.set("type", r#type, false, ctx)?; + obj.set("from", address_to_buf(from, ctx)?, false, ctx)?; + if let Some(to) = to { + obj.set("to", address_to_buf(to, ctx)?, false, ctx)?; + } else { + obj.set("to", JsValue::null(), false, ctx)?; + } + + obj.set("input", to_buf(input.to_vec(), ctx)?, false, ctx)?; + obj.set("gas", gas, false, ctx)?; + obj.set("gasUsed", gas_used, false, ctx)?; + obj.set("gasPrice", gas_price, false, ctx)?; + obj.set("intrinsicGas", intrinsic_gas, false, ctx)?; + obj.set("value", to_bigint(value, ctx)?, false, ctx)?; + obj.set("block", block, false, ctx)?; + obj.set("output", to_buf(output.to_vec(), ctx)?, false, ctx)?; + obj.set("time", time, false, ctx)?; + if let Some(block_hash) = block_hash { + obj.set("blockHash", to_buf(block_hash.as_bytes().to_vec(), ctx)?, false, ctx)?; + } + if let Some(tx_index) = tx_index { + obj.set("txIndex", tx_index as u64, false, ctx)?; + } + if let Some(tx_hash) = tx_hash { + obj.set("txHash", to_buf(tx_hash.as_bytes().to_vec(), ctx)?, false, ctx)?; + } + + Ok(obj) + } +} + +/// DB is the object that allows the js inspector to interact with the database. +pub(crate) struct EvmDb { + db: EvmDBInner, +} + +impl EvmDb { + pub(crate) fn new(state: State, to_db: mpsc::Sender) -> Self { + Self { db: EvmDBInner { state, to_db } } + } +} + +impl EvmDb { + pub(crate) fn into_js_object(self, context: &mut Context<'_>) -> JsResult { + let obj = JsObject::default(); + + let db = Gc::new(self.db); + let exists = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, db, ctx| { + let val = args.get_or_undefined(0).clone(); + let db: &EvmDBInner = db.borrow(); + let acc = db.read_basic(val, ctx)?; + let exists = acc.is_some(); + Ok(JsValue::from(exists)) + }, + db.clone(), + ), + ) + .length(1) + .build(); + + let get_balance = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, db, ctx| { + let val = args.get_or_undefined(0).clone(); + let db: &EvmDBInner = db.borrow(); + let acc = db.read_basic(val, ctx)?; + let balance = acc.map(|acc| acc.balance).unwrap_or_default(); + to_bigint(balance, ctx) + }, + db.clone(), + ), + ) + .length(1) + .build(); + + let get_nonce = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, db, ctx| { + let val = args.get_or_undefined(0).clone(); + let db: &EvmDBInner = db.borrow(); + let acc = db.read_basic(val, ctx)?; + let nonce = acc.map(|acc| acc.nonce).unwrap_or_default(); + Ok(JsValue::from(nonce)) + }, + db.clone(), + ), + ) + .length(1) + .build(); + + let get_code = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, db, ctx| { + let val = args.get_or_undefined(0).clone(); + let db: &EvmDBInner = db.borrow(); + Ok(db.read_code(val, ctx)?.into()) + }, + db.clone(), + ), + ) + .length(1) + .build(); + + let get_state = FunctionObjectBuilder::new( + context, + NativeFunction::from_copy_closure_with_captures( + move |_this, args, db, ctx| { + let addr = args.get_or_undefined(0).clone(); + let slot = args.get_or_undefined(1).clone(); + let db: &EvmDBInner = db.borrow(); + Ok(db.read_state(addr, slot, ctx)?.into()) + }, + db, + ), + ) + .length(2) + .build(); + + obj.set("getBalance", get_balance, false, context)?; + obj.set("getNonce", get_nonce, false, context)?; + obj.set("getCode", get_code, false, context)?; + obj.set("getState", get_state, false, context)?; + obj.set("exists", exists, false, context)?; + Ok(obj) + } +} + +#[derive(Clone)] +struct EvmDBInner { + state: State, + to_db: mpsc::Sender, +} + +impl EvmDBInner { + fn read_basic(&self, address: JsValue, ctx: &mut Context<'_>) -> JsResult> { + let buf = from_buf(address, ctx)?; + let address = bytes_to_address(buf); + if let Some(acc) = self.state.get(&address) { + return Ok(Some(Account { + nonce: acc.info.nonce, + balance: acc.info.balance, + bytecode_hash: Some(acc.info.code_hash), + })) + } + let (tx, rx) = channel(); + if self.to_db.try_send(JsDbRequest::Basic { address, resp: tx }).is_err() { + return Err(JsError::from_native( + JsNativeError::error() + .with_message(format!("Failed to read address {address:?} from database",)), + )) + } + + match rx.recv() { + Ok(Ok(maybe_acc)) => Ok(maybe_acc), + _ => Err(JsError::from_native( + JsNativeError::error() + .with_message(format!("Failed to read address {address:?} from database",)), + )), + } + } + + fn read_code(&self, address: JsValue, ctx: &mut Context<'_>) -> JsResult { + let acc = self.read_basic(address, ctx)?; + let code_hash = acc.and_then(|acc| acc.bytecode_hash).unwrap_or(KECCAK_EMPTY); + if code_hash == KECCAK_EMPTY { + return JsArrayBuffer::new(0, ctx) + } + + let (tx, rx) = channel(); + if self.to_db.try_send(JsDbRequest::Code { code_hash, resp: tx }).is_err() { + return Err(JsError::from_native( + JsNativeError::error() + .with_message(format!("Failed to read code hash {code_hash:?} from database",)), + )) + } + + let code = match rx.recv() { + Ok(Ok(code)) => code, + _ => { + return Err(JsError::from_native(JsNativeError::error().with_message(format!( + "Failed to read code hash {code_hash:?} from database", + )))) + } + }; + + to_buf(code.to_vec(), ctx) + } + + fn read_state( + &self, + address: JsValue, + slot: JsValue, + ctx: &mut Context<'_>, + ) -> JsResult { + let buf = from_buf(address, ctx)?; + let address = bytes_to_address(buf); + + let buf = from_buf(slot, ctx)?; + let slot = bytes_to_hash(buf); + + let (tx, rx) = channel(); + if self.to_db.try_send(JsDbRequest::StorageAt { address, index: slot, resp: tx }).is_err() { + return Err(JsError::from_native(JsNativeError::error().with_message(format!( + "Failed to read state for {address:?} at {slot:?} from database", + )))) + } + + let value = match rx.recv() { + Ok(Ok(value)) => value, + _ => { + return Err(JsError::from_native(JsNativeError::error().with_message(format!( + "Failed to read state for {address:?} at {slot:?} from database", + )))) + } + }; + let value: H256 = value.into(); + to_buf(value.as_bytes().to_vec(), ctx) + } +} + +impl Finalize for EvmDBInner {} + +unsafe impl Trace for EvmDBInner { + empty_trace!(); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tracing::js::builtins::BIG_INT_JS; + use boa_engine::{object::builtins::JsArrayBuffer, property::Attribute, Source}; + + #[test] + fn test_contract() { + let mut ctx = Context::default(); + let contract = Contract { + caller: Address::zero(), + contract: Address::zero(), + value: U256::from(1337u64), + input: vec![0x01, 0x02, 0x03].into(), + }; + let big_int = ctx.eval(Source::from_bytes(BIG_INT_JS.as_bytes())).unwrap(); + ctx.register_global_property("bigint", big_int, Attribute::all()).unwrap(); + + let obj = contract.clone().into_js_object(&mut ctx).unwrap(); + let s = r#"({ + call: function(contract) { return contract.getCaller(); }, + value: function(contract) { return contract.getValue(); }, + input: function(contract) { return contract.getInput(); } + })"#; + + let eval_obj = ctx.eval(Source::from_bytes(s.as_bytes())).unwrap(); + + let call = eval_obj.as_object().unwrap().get("call", &mut ctx).unwrap(); + let res = call + .as_callable() + .unwrap() + .call(&JsValue::undefined(), &[obj.clone().into()], &mut ctx) + .unwrap(); + assert!(res.is_object()); + assert!(res.as_object().unwrap().is_array_buffer()); + + let call = eval_obj.as_object().unwrap().get("value", &mut ctx).unwrap(); + let res = call + .as_callable() + .unwrap() + .call(&JsValue::undefined(), &[obj.clone().into()], &mut ctx) + .unwrap(); + assert_eq!( + res.to_string(&mut ctx).unwrap().to_std_string().unwrap(), + contract.value.to_string() + ); + + let call = eval_obj.as_object().unwrap().get("input", &mut ctx).unwrap(); + let res = call + .as_callable() + .unwrap() + .call(&JsValue::undefined(), &[obj.into()], &mut ctx) + .unwrap(); + + let buffer = JsArrayBuffer::from_object(res.as_object().unwrap().clone()).unwrap(); + let input = buffer.take().unwrap(); + assert_eq!(input, contract.input); + } +} diff --git a/crates/revm/revm-inspectors/src/tracing/js/builtins.rs b/crates/revm/revm-inspectors/src/tracing/js/builtins.rs new file mode 100644 index 0000000000..f083ba5aab --- /dev/null +++ b/crates/revm/revm-inspectors/src/tracing/js/builtins.rs @@ -0,0 +1,163 @@ +//! Builtin functions + +use boa_engine::{ + object::builtins::{JsArray, JsArrayBuffer}, + property::Attribute, + Context, JsArgs, JsError, JsNativeError, JsResult, JsString, JsValue, NativeFunction, Source, +}; +use reth_primitives::{hex, Address, H256, U256}; + +/// bigIntegerJS is the minified version of . +pub(crate) const BIG_INT_JS: &str = include_str!("bigint.js"); + +/// Registers all the builtin functions and global bigint property +pub(crate) fn register_builtins(ctx: &mut Context<'_>) -> JsResult<()> { + let big_int = ctx.eval(Source::from_bytes(BIG_INT_JS.as_bytes()))?; + ctx.register_global_property("bigint", big_int, Attribute::all())?; + ctx.register_global_builtin_callable("toHex", 1, NativeFunction::from_fn_ptr(to_hex))?; + + // TODO: register toWord, toAddress toContract toContract2 isPrecompiled slice + + Ok(()) +} + +/// Converts an array, hex string or Uint8Array to a []byte +pub(crate) fn from_buf(val: JsValue, context: &mut Context<'_>) -> JsResult> { + if let Some(obj) = val.as_object().cloned() { + if obj.is_array_buffer() { + let buf = JsArrayBuffer::from_object(obj)?; + return buf.take() + } else if obj.is_string() { + let js_string = obj.borrow().as_string().unwrap(); + return hex_decode_js_string(js_string) + } else if obj.is_array() { + let array = JsArray::from_object(obj)?; + let len = array.length(context)?; + let mut buf = Vec::with_capacity(len as usize); + for i in 0..len { + let val = array.get(i, context)?; + buf.push(val.to_number(context)? as u8); + } + return Ok(buf) + } + } + + Err(JsError::from_native(JsNativeError::typ().with_message("invalid buffer type"))) +} + +/// Create a new array buffer from the address' bytes. +pub(crate) fn address_to_buf(addr: Address, context: &mut Context<'_>) -> JsResult { + to_buf(addr.0.to_vec(), context) +} + +/// Create a new array buffer from byte block. +pub(crate) fn to_buf(bytes: Vec, context: &mut Context<'_>) -> JsResult { + JsArrayBuffer::from_byte_block(bytes, context) +} + +/// Create a new array buffer object from byte block. +pub(crate) fn to_buf_value(bytes: Vec, context: &mut Context<'_>) -> JsResult { + Ok(to_buf(bytes, context)?.into()) +} + +/// Create a new array buffer object from byte block. +pub(crate) fn to_bigint_array(items: &[U256], ctx: &mut Context<'_>) -> JsResult { + let arr = JsArray::new(ctx); + let bigint = ctx.global_object().get("bigint", ctx)?; + if !bigint.is_callable() { + return Err(JsError::from_native( + JsNativeError::typ().with_message("global object bigint is not callable"), + )) + } + let bigint = bigint.as_callable().unwrap(); + + for item in items { + let val = bigint.call(&JsValue::undefined(), &[JsValue::from(item.to_string())], ctx)?; + arr.push(val, ctx)?; + } + Ok(arr) +} + +/// Converts a buffer type to an address. +/// +/// If the buffer is larger than the address size, it will be cropped from the left +pub(crate) fn bytes_to_address(buf: Vec) -> Address { + let mut address = Address::default(); + let mut buf = &buf[..]; + let address_len = address.0.len(); + if buf.len() > address_len { + // crop from left + buf = &buf[buf.len() - address.0.len()..]; + } + let address_slice = &mut address.0[address_len - buf.len()..]; + address_slice.copy_from_slice(buf); + address +} + +/// Converts a buffer type to a hash. +/// +/// If the buffer is larger than the hash size, it will be cropped from the left +pub(crate) fn bytes_to_hash(buf: Vec) -> H256 { + let mut hash = H256::default(); + let mut buf = &buf[..]; + let hash_len = hash.0.len(); + if buf.len() > hash_len { + // crop from left + buf = &buf[buf.len() - hash.0.len()..]; + } + let hash_slice = &mut hash.0[hash_len - buf.len()..]; + hash_slice.copy_from_slice(buf); + hash +} + +/// Converts a U256 to a bigint using the global bigint property +pub(crate) fn to_bigint(value: U256, ctx: &mut Context<'_>) -> JsResult { + let bigint = ctx.global_object().get("bigint", ctx)?; + if !bigint.is_callable() { + return Ok(JsValue::undefined()) + } + bigint.as_callable().unwrap().call( + &JsValue::undefined(), + &[JsValue::from(value.to_string())], + ctx, + ) +} + +/// Converts a buffer type to a hex string +pub(crate) fn to_hex(_: &JsValue, args: &[JsValue], ctx: &mut Context<'_>) -> JsResult { + let val = args.get_or_undefined(0).clone(); + let buf = from_buf(val, ctx)?; + Ok(JsValue::from(hex::encode(buf))) +} + +/// Decodes a hex decoded js-string +fn hex_decode_js_string(js_string: JsString) -> JsResult> { + match js_string.to_std_string() { + Ok(s) => match hex::decode(s.as_str()) { + Ok(data) => Ok(data), + Err(err) => Err(JsError::from_native( + JsNativeError::error().with_message(format!("invalid hex string {s}: {err}",)), + )), + }, + Err(err) => Err(JsError::from_native( + JsNativeError::error() + .with_message(format!("invalid utf8 string {js_string:?}: {err}",)), + )), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use boa_engine::Source; + + #[test] + fn test_install_bigint() { + let mut ctx = Context::default(); + let big_int = ctx.eval(Source::from_bytes(BIG_INT_JS.as_bytes())).unwrap(); + let value = JsValue::from(100); + let result = + big_int.as_callable().unwrap().call(&JsValue::undefined(), &[value], &mut ctx).unwrap(); + assert_eq!(result.to_string(&mut ctx).unwrap().to_std_string().unwrap(), "100"); + } +} diff --git a/crates/revm/revm-inspectors/src/tracing/js/mod.rs b/crates/revm/revm-inspectors/src/tracing/js/mod.rs new file mode 100644 index 0000000000..a80a76cb99 --- /dev/null +++ b/crates/revm/revm-inspectors/src/tracing/js/mod.rs @@ -0,0 +1,549 @@ +//! Javascript inspector + +use crate::tracing::{ + js::{ + bindings::{ + CallFrame, Contract, EvmContext, EvmDb, FrameResult, MemoryObj, OpObj, StackObj, + StepLog, + }, + builtins::register_builtins, + }, + types::CallKind, + utils::get_create_address, +}; +use boa_engine::{Context, JsError, JsObject, JsResult, JsValue, Source}; +use reth_primitives::{bytes::Bytes, Account, Address, H256, U256}; +use revm::{ + interpreter::{ + return_revert, CallInputs, CallScheme, CreateInputs, Gas, InstructionResult, Interpreter, + OpCode, + }, + primitives::{Env, ExecutionResult, Output, ResultAndState, TransactTo, B160, B256}, + Database, EVMData, Inspector, +}; +use tokio::sync::mpsc; + +pub(crate) mod bindings; +pub(crate) mod builtins; + +/// A javascript inspector that will delegate inspector functions to javascript functions +/// +/// See also +#[derive(Debug)] +pub struct JsInspector { + ctx: Context<'static>, + /// The javascript config provided to the inspector. + _config: JsValue, + /// The evaluated object that contains the inspector functions. + obj: JsObject, + + /// The javascript function that will be called when the result is requested. + result_fn: JsObject, + fault_fn: JsObject, + + /// EVM inspector hook functions + enter_fn: Option, + exit_fn: Option, + /// Executed before each instruction is executed. + step_fn: Option, + /// Keeps track of the current call stack. + call_stack: Vec, + /// sender half of a channel to communicate with the database service. + to_db_service: mpsc::Sender, +} + +impl JsInspector { + /// Creates a new inspector from a javascript code snipped that evaluates to an object with the + /// expected fields and a config object. + /// + /// The object must have the following fields: + /// - `result`: a function that will be called when the result is requested. + /// - `fault`: a function that will be called when the transaction fails. + /// + /// Optional functions are invoked during inspection: + /// - `enter`: a function that will be called when the execution enters a new call. + /// - `exit`: a function that will be called when the execution exits a call. + /// - `step`: a function that will be called when the execution steps to the next instruction. + /// + /// This also accepts a sender half of a channel to communicate with the database service so the + /// DB can be queried from inside the inspector. + pub fn new( + code: String, + config: serde_json::Value, + to_db_service: mpsc::Sender, + ) -> Result { + // Instantiate the execution context + let mut ctx = Context::default(); + register_builtins(&mut ctx)?; + + // evaluate the code + let code = format!("({})", code); + let obj = + ctx.eval(Source::from_bytes(code.as_bytes())).map_err(JsInspectorError::EvalCode)?; + + let obj = obj.as_object().cloned().ok_or(JsInspectorError::ExpectedJsObject)?; + + // ensure all the fields are callables, if present + + let result_fn = obj + .get("result", &mut ctx)? + .as_object() + .cloned() + .ok_or(JsInspectorError::ResultFunctionMissing)?; + if !result_fn.is_callable() { + return Err(JsInspectorError::ResultFunctionMissing) + } + + let fault_fn = obj + .get("fault", &mut ctx)? + .as_object() + .cloned() + .ok_or(JsInspectorError::ResultFunctionMissing)?; + if !result_fn.is_callable() { + return Err(JsInspectorError::ResultFunctionMissing) + } + + let enter_fn = obj.get("enter", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); + let exit_fn = obj.get("exit", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); + let step_fn = obj.get("step", &mut ctx)?.as_object().cloned().filter(|o| o.is_callable()); + + let config = + JsValue::from_json(&config, &mut ctx).map_err(JsInspectorError::InvalidJsonConfig)?; + + if let Some(setup_fn) = obj.get("setup", &mut ctx)?.as_object() { + if !setup_fn.is_callable() { + return Err(JsInspectorError::SetupFunctionNotCallable) + } + + // call setup() + setup_fn + .call(&(obj.clone().into()), &[config.clone()], &mut ctx) + .map_err(JsInspectorError::SetupCallFailed)?; + } + + Ok(Self { + ctx, + _config: config, + obj, + result_fn, + fault_fn, + enter_fn, + exit_fn, + step_fn, + call_stack: Default::default(), + to_db_service, + }) + } + + /// Calls the result function and returns the result as [serde_json::Value]. + /// + /// Note: This is supposed to be called after the inspection has finished. + pub fn json_result( + &mut self, + res: ResultAndState, + env: &Env, + ) -> Result { + Ok(self.result(res, env)?.to_json(&mut self.ctx)?) + } + + /// Calls the result function and returns the result. + pub fn result(&mut self, res: ResultAndState, env: &Env) -> Result { + let ResultAndState { result, state } = res; + let db = EvmDb::new(state, self.to_db_service.clone()); + + let gas_used = result.gas_used(); + let mut to = None; + let mut output_bytes = None; + match result { + ExecutionResult::Success { output, .. } => match output { + Output::Call(out) => { + output_bytes = Some(out); + } + Output::Create(out, addr) => { + to = addr; + output_bytes = Some(out); + } + }, + ExecutionResult::Revert { output, .. } => { + output_bytes = Some(output); + } + ExecutionResult::Halt { .. } => {} + }; + + let ctx = EvmContext { + r#type: match env.tx.transact_to { + TransactTo::Call(target) => { + to = Some(target); + "CALL" + } + TransactTo::Create(_) => "CREATE", + } + .to_string(), + from: env.tx.caller, + to, + input: env.tx.data.clone(), + gas: env.tx.gas_limit, + gas_used, + gas_price: env.tx.gas_price.try_into().unwrap_or(u64::MAX), + value: env.tx.value, + block: env.block.number.try_into().unwrap_or(u64::MAX), + output: output_bytes.unwrap_or_default(), + time: env.block.timestamp.to_string(), + // TODO: fill in the following fields + intrinsic_gas: 0, + block_hash: None, + tx_index: None, + tx_hash: None, + }; + let ctx = ctx.into_js_object(&mut self.ctx)?; + let db = db.into_js_object(&mut self.ctx)?; + Ok(self.result_fn.call( + &(self.obj.clone().into()), + &[ctx.into(), db.into()], + &mut self.ctx, + )?) + } + + fn try_fault(&mut self, step: StepLog, db: EvmDb) -> JsResult<()> { + let step = step.into_js_object(&mut self.ctx)?; + let db = db.into_js_object(&mut self.ctx)?; + self.fault_fn.call(&(self.obj.clone().into()), &[step.into(), db.into()], &mut self.ctx)?; + Ok(()) + } + + fn try_step(&mut self, step: StepLog, db: EvmDb) -> JsResult<()> { + if let Some(step_fn) = &self.step_fn { + let step = step.into_js_object(&mut self.ctx)?; + let db = db.into_js_object(&mut self.ctx)?; + step_fn.call(&(self.obj.clone().into()), &[step.into(), db.into()], &mut self.ctx)?; + } + Ok(()) + } + + fn try_enter(&mut self, frame: CallFrame, db: EvmDb) -> JsResult<()> { + if let Some(enter_fn) = &self.enter_fn { + let frame = frame.into_js_object(&mut self.ctx)?; + let db = db.into_js_object(&mut self.ctx)?; + enter_fn.call(&(self.obj.clone().into()), &[frame.into(), db.into()], &mut self.ctx)?; + } + Ok(()) + } + + fn try_exit(&mut self, frame: FrameResult, db: EvmDb) -> JsResult<()> { + if let Some(exit_fn) = &self.exit_fn { + let frame = frame.into_js_object(&mut self.ctx)?; + let db = db.into_js_object(&mut self.ctx)?; + exit_fn.call(&(self.obj.clone().into()), &[frame.into(), db.into()], &mut self.ctx)?; + } + Ok(()) + } + + /// Returns the currently active call + /// + /// Panics: if there's no call yet + #[track_caller] + fn active_call(&self) -> &CallStackItem { + self.call_stack.last().expect("call stack is empty") + } + + /// Pushes a new call to the stack + fn push_call( + &mut self, + address: Address, + data: Bytes, + value: U256, + kind: CallKind, + caller: Address, + gas_limit: u64, + ) -> &CallStackItem { + let call = CallStackItem { + contract: Contract { caller, contract: address, value, input: data }, + kind, + gas_limit, + }; + self.call_stack.push(call); + self.active_call() + } + + fn pop_call(&mut self) { + self.call_stack.pop(); + } +} + +impl Inspector for JsInspector +where + DB: Database, +{ + fn initialize_interp( + &mut self, + _interp: &mut Interpreter, + _data: &mut EVMData<'_, DB>, + _is_static: bool, + ) -> InstructionResult { + InstructionResult::Continue + } + + fn step( + &mut self, + interp: &mut Interpreter, + data: &mut EVMData<'_, DB>, + _is_static: bool, + ) -> InstructionResult { + if self.step_fn.is_none() { + return InstructionResult::Continue + } + + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + + let pc = interp.program_counter(); + let step = StepLog { + stack: StackObj(interp.stack.clone()), + op: OpObj( + OpCode::try_from_u8(interp.contract.bytecode.bytecode()[pc]) + .expect("is valid opcode;"), + ), + memory: MemoryObj(interp.memory.clone()), + pc: pc as u64, + gas_remaining: interp.gas.remaining(), + cost: interp.gas.spend(), + depth: data.journaled_state.depth(), + refund: interp.gas.refunded() as u64, + error: None, + contract: self.active_call().contract.clone(), + }; + + if self.try_step(step, db).is_err() { + return InstructionResult::Revert + } + InstructionResult::Continue + } + + fn log( + &mut self, + _evm_data: &mut EVMData<'_, DB>, + _address: &B160, + _topics: &[B256], + _data: &Bytes, + ) { + } + + fn step_end( + &mut self, + interp: &mut Interpreter, + data: &mut EVMData<'_, DB>, + _is_static: bool, + eval: InstructionResult, + ) -> InstructionResult { + if self.step_fn.is_none() { + return InstructionResult::Continue + } + + if matches!(eval, return_revert!()) { + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + + let pc = interp.program_counter(); + let step = StepLog { + stack: StackObj(interp.stack.clone()), + op: OpObj( + OpCode::try_from_u8(interp.contract.bytecode.bytecode()[pc]) + .expect("is valid opcode;"), + ), + memory: MemoryObj(interp.memory.clone()), + pc: pc as u64, + gas_remaining: interp.gas.remaining(), + cost: interp.gas.spend(), + depth: data.journaled_state.depth(), + refund: interp.gas.refunded() as u64, + error: Some(format!("{:?}", eval)), + contract: self.active_call().contract.clone(), + }; + + let _ = self.try_fault(step, db); + } + + InstructionResult::Continue + } + + fn call( + &mut self, + data: &mut EVMData<'_, DB>, + inputs: &mut CallInputs, + _is_static: bool, + ) -> (InstructionResult, Gas, Bytes) { + // determine correct `from` and `to` based on the call scheme + let (from, to) = match inputs.context.scheme { + CallScheme::DelegateCall | CallScheme::CallCode => { + (inputs.context.address, inputs.context.code_address) + } + _ => (inputs.context.caller, inputs.context.address), + }; + + let value = inputs.transfer.value; + self.push_call( + to, + inputs.input.clone(), + value, + inputs.context.scheme.into(), + from, + inputs.gas_limit, + ); + + if self.enter_fn.is_some() { + let call = self.active_call(); + let frame = CallFrame { + contract: call.contract.clone(), + kind: call.kind, + gas: inputs.gas_limit, + }; + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + if let Err(err) = self.try_enter(frame, db) { + return (InstructionResult::Revert, Gas::new(0), err.to_string().into()) + } + } + + (InstructionResult::Continue, Gas::new(0), Bytes::new()) + } + + fn call_end( + &mut self, + data: &mut EVMData<'_, DB>, + _inputs: &CallInputs, + remaining_gas: Gas, + ret: InstructionResult, + out: Bytes, + _is_static: bool, + ) -> (InstructionResult, Gas, Bytes) { + if self.exit_fn.is_some() { + let frame_result = + FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None }; + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + if let Err(err) = self.try_exit(frame_result, db) { + return (InstructionResult::Revert, Gas::new(0), err.to_string().into()) + } + } + + self.pop_call(); + + (ret, remaining_gas, out) + } + + fn create( + &mut self, + data: &mut EVMData<'_, DB>, + inputs: &mut CreateInputs, + ) -> (InstructionResult, Option, Gas, Bytes) { + let _ = data.journaled_state.load_account(inputs.caller, data.db); + let nonce = data.journaled_state.account(inputs.caller).info.nonce; + let address = get_create_address(inputs, nonce); + self.push_call( + address, + inputs.init_code.clone(), + inputs.value, + inputs.scheme.into(), + inputs.caller, + inputs.gas_limit, + ); + + if self.enter_fn.is_some() { + let call = self.active_call(); + let frame = + CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit }; + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + if let Err(err) = self.try_enter(frame, db) { + return (InstructionResult::Revert, None, Gas::new(0), err.to_string().into()) + } + } + + (InstructionResult::Continue, None, Gas::new(inputs.gas_limit), Bytes::default()) + } + + fn create_end( + &mut self, + data: &mut EVMData<'_, DB>, + _inputs: &CreateInputs, + ret: InstructionResult, + address: Option, + remaining_gas: Gas, + out: Bytes, + ) -> (InstructionResult, Option, Gas, Bytes) { + if self.exit_fn.is_some() { + let frame_result = + FrameResult { gas_used: remaining_gas.spend(), output: out.clone(), error: None }; + let db = EvmDb::new(data.journaled_state.state.clone(), self.to_db_service.clone()); + if let Err(err) = self.try_exit(frame_result, db) { + return (InstructionResult::Revert, None, Gas::new(0), err.to_string().into()) + } + } + + self.pop_call(); + + (ret, address, remaining_gas, out) + } + + fn selfdestruct(&mut self, _contract: B160, _target: B160) { + if self.enter_fn.is_some() { + let call = self.active_call(); + let frame = + CallFrame { contract: call.contract.clone(), kind: call.kind, gas: call.gas_limit }; + let db = EvmDb::new(Default::default(), self.to_db_service.clone()); + let _ = self.try_enter(frame, db); + } + } +} + +/// Request variants to be sent from the inspector to the database +#[derive(Debug, Clone)] +pub enum JsDbRequest { + /// Bindings for [Database::basic] + Basic { + /// The address of the account to be loaded + address: Address, + /// The response channel + resp: std::sync::mpsc::Sender, String>>, + }, + /// Bindings for [Database::code_by_hash] + Code { + /// The code hash of the code to be loaded + code_hash: H256, + /// The response channel + resp: std::sync::mpsc::Sender>, + }, + /// Bindings for [Database::storage] + StorageAt { + /// The address of the account + address: Address, + /// Index of the storage slot + index: H256, + /// The response channel + resp: std::sync::mpsc::Sender>, + }, +} + +/// Represents an active call +#[derive(Debug)] +struct CallStackItem { + contract: Contract, + kind: CallKind, + gas_limit: u64, +} + +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum JsInspectorError { + #[error(transparent)] + JsError(#[from] JsError), + #[error("Failed to eval js code: {0}")] + EvalCode(JsError), + #[error("The evaluated code is not a JS object")] + ExpectedJsObject, + #[error("trace object must expose a function result()")] + ResultFunctionMissing, + #[error("trace object must expose a function fault()")] + FaultFunctionMissing, + #[error("setup object must be a function")] + SetupFunctionNotCallable, + #[error("Failed to call setup(): {0}")] + SetupCallFailed(JsError), + #[error("Invalid JSON config: {0}")] + InvalidJsonConfig(JsError), +} diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index 090ed7f3e1..26504a71d6 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -27,6 +27,9 @@ pub use config::TracingInspectorConfig; pub use fourbyte::FourByteInspector; pub use opcount::OpcodeCountInspector; +#[cfg(feature = "js-tracer")] +pub mod js; + /// An inspector that collects call traces. /// /// This [Inspector] can be hooked into the [EVM](revm::EVM) which then calls the inspector diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index 1698b5500d..1cbb304acd 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -10,6 +10,7 @@ use revm::{ pub type SubState = CacheDB>; /// Wrapper around StateProvider that implements revm database trait +#[derive(Debug, Clone)] pub struct State(pub DB); impl State { diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs index 62f72b2757..05c6e1d9f9 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/mod.rs @@ -100,6 +100,7 @@ pub enum GethTraceFrame { FourByteTracer(FourByteFrame), CallTracer(CallFrame), PreStateTracer(PreStateFrame), + JS(serde_json::Value), } impl From for GethTraceFrame { @@ -241,6 +242,14 @@ impl GethDebugTracerConfig { } } + /// Returns the json config if this config is a JS tracer. + pub fn into_js_config(self) -> Option { + match self { + GethDebugTracerConfig::JsTracer(cfg) => Some(cfg), + _ => None, + } + } + /// Returns the [PreStateConfig] if it is a call config. pub fn into_pre_state_config(self) -> Option { match self { diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index a3116a2655..76911cb6f7 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -1,7 +1,7 @@ use crate::{ eth::{ error::{EthApiError, EthResult}, - revm_utils::{inspect, replay_transactions_until, EvmOverrides}, + revm_utils::{inspect, prepare_call_env, replay_transactions_until, EvmOverrides}, EthTransactions, TransactionSource, }, result::{internal_rpc_err, ToRpcResult}, @@ -10,11 +10,16 @@ use crate::{ use async_trait::async_trait; use jsonrpsee::core::RpcResult; use reth_primitives::{Block, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, H256}; -use reth_provider::{BlockProviderIdExt, HeaderProvider, ReceiptProviderIdExt, StateProviderBox}; +use reth_provider::{ + BlockProviderIdExt, HeaderProvider, ReceiptProviderIdExt, StateProvider, StateProviderBox, +}; use reth_revm::{ database::{State, SubState}, env::tx_env_with_recovered, - tracing::{FourByteInspector, TracingInspector, TracingInspectorConfig}, + tracing::{ + js::{JsDbRequest, JsInspector}, + FourByteInspector, TracingInspector, TracingInspectorConfig, + }, }; use reth_rlp::{Decodable, Encodable}; use reth_rpc_api::DebugApiServer; @@ -30,7 +35,8 @@ use reth_tasks::TaskSpawner; use revm::primitives::Env; use revm_primitives::{db::DatabaseCommit, BlockEnv, CfgEnv}; use std::{future::Future, sync::Arc}; -use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit}; +use tokio::sync::{mpsc, oneshot, AcquireError, OwnedSemaphorePermit}; +use tokio_stream::{wrappers::ReceiverStream, StreamExt}; /// `debug` API implementation. /// @@ -94,6 +100,7 @@ where opts: GethDebugTracingOptions, ) -> EthResult> { // replay all transactions of the block + let this = self.clone(); self.inner.eth_api.with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(transactions.len()); let mut db = SubState::new(State::new(state)); @@ -103,7 +110,8 @@ where let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; let tx = tx_env_with_recovered(&tx); let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; - let (result, state_changes) = trace_transaction(opts.clone(), env, &mut db)?; + let (result, state_changes) = + this.trace_transaction(opts.clone(), env, at, &mut db)?; results.push(TraceResult::Success { result }); if transactions.peek().is_some() { @@ -204,11 +212,11 @@ where // we need to get the state of the parent block because we're essentially replaying the // block the transaction is included in - let state_at = block.parent_hash; + let state_at: BlockId = block.parent_hash.into(); let block_txs = block.body; self.on_blocking_task(|this| async move { - this.inner.eth_api.with_state_at_block(state_at.into(), |state| { + this.inner.eth_api.with_state_at_block(state_at, |state| { // configure env for the target transaction let tx = transaction.into_recovered(); @@ -223,7 +231,7 @@ where )?; let env = Env { cfg, block: block_env, tx: tx_env_with_recovered(&tx) }; - trace_transaction(opts, env, &mut db).map(|(trace, _)| trace) + this.trace_transaction(opts, env, state_at, &mut db).map(|(trace, _)| trace) }) }) .await @@ -302,8 +310,23 @@ where } GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()), }, - GethDebugTracerType::JsTracer(_) => { - Err(EthApiError::Unsupported("javascript tracers are unsupported.")) + GethDebugTracerType::JsTracer(code) => { + let config = tracer_config.and_then(|c| c.into_js_config()).unwrap_or_default(); + + // for JS tracing we need to setup all async work before we can start tracing + // because JSTracer and all JS types are not Send + let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?; + let state = self.inner.eth_api.state_at(at)?; + let mut db = SubState::new(State::new(state)); + let env = prepare_call_env(cfg, block_env, call, &mut db, overrides)?; + + let to_db_service = self.spawn_js_trace_service(at)?; + + let mut inspector = JsInspector::new(code, config, to_db_service)?; + let (res, env) = inspect(db, env, &mut inspector)?; + + let result = inspector.json_result(res, &env)?; + Ok(GethTraceFrame::JS(result)) } } } @@ -321,6 +344,149 @@ where Ok(frame.into()) } + + /// Executes the configured transaction with the environment on the given database. + /// + /// Returns the trace frame and the state that got updated after executing the transaction. + /// + /// Note: this does not apply any state overrides if they're configured in the `opts`. + fn trace_transaction( + &self, + opts: GethDebugTracingOptions, + env: Env, + at: BlockId, + db: &mut SubState>, + ) -> EthResult<(GethTraceFrame, revm_primitives::State)> { + let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts; + + if let Some(tracer) = tracer { + // valid matching config + if let Some(ref config) = tracer_config { + if !config.matches_tracer(&tracer) { + return Err(EthApiError::InvalidTracerConfig) + } + } + + return match tracer { + GethDebugTracerType::BuiltInTracer(tracer) => match tracer { + GethDebugBuiltInTracerType::FourByteTracer => { + let mut inspector = FourByteInspector::default(); + let (res, _) = inspect(db, env, &mut inspector)?; + return Ok((FourByteFrame::from(inspector).into(), res.state)) + } + GethDebugBuiltInTracerType::CallTracer => { + // we validated the config above + let call_config = + tracer_config.and_then(|c| c.into_call_config()).unwrap_or_default(); + + let mut inspector = TracingInspector::new( + TracingInspectorConfig::from_geth_config(&config), + ); + + let (res, _) = inspect(db, env, &mut inspector)?; + + let frame = inspector.into_geth_builder().geth_call_traces(call_config); + + return Ok((frame.into(), res.state)) + } + GethDebugBuiltInTracerType::PreStateTracer => { + Err(EthApiError::Unsupported("prestate tracer is unimplemented yet.")) + } + GethDebugBuiltInTracerType::NoopTracer => { + Ok((NoopFrame::default().into(), Default::default())) + } + }, + GethDebugTracerType::JsTracer(code) => { + let config = tracer_config.and_then(|c| c.into_js_config()).unwrap_or_default(); + + // we spawn the database service that will be used by the JS tracer + // TODO(mattsse) this is not quite accurate when tracing a block inside a + // transaction because the service needs access to the committed state changes + let to_db_service = self.spawn_js_trace_service(at)?; + + let mut inspector = JsInspector::new(code, config, to_db_service)?; + let (res, env) = inspect(db, env, &mut inspector)?; + + let state = res.state.clone(); + let result = inspector.json_result(res, &env)?; + Ok((GethTraceFrame::JS(result), state)) + } + } + } + + // default structlog tracer + let inspector_config = TracingInspectorConfig::from_geth_config(&config); + + let mut inspector = TracingInspector::new(inspector_config); + + let (res, _) = inspect(db, env, &mut inspector)?; + let gas_used = res.result.gas_used(); + + let frame = inspector.into_geth_builder().geth_traces(gas_used, config); + + Ok((frame.into(), res.state)) + } + + /// Spawns [Self::js_trace_db_service_task] on a new task and returns a channel to send requests + /// to it. + /// + /// Note: This blocks until the service is ready to receive requests. + fn spawn_js_trace_service(&self, at: BlockId) -> EthResult> { + let (to_db_service, rx) = mpsc::channel(1); + let (ready_tx, ready_rx) = std::sync::mpsc::channel(); + let this = self.clone(); + self.inner + .task_spawner + .spawn(Box::pin(async move { this.js_trace_db_service_task(at, rx, ready_tx).await })); + // wait for initialization + ready_rx.recv().map_err(|_| { + EthApiError::InternalJsTracerError("js tracer initialization failed".to_string()) + })??; + Ok(to_db_service) + } + + /// A services that handles database requests issued from inside the JavaScript tracing engine. + async fn js_trace_db_service_task( + self, + at: BlockId, + rx: mpsc::Receiver, + on_ready: std::sync::mpsc::Sender>, + ) { + let state = match self.inner.eth_api.state_at(at) { + Ok(state) => { + let _ = on_ready.send(Ok(())); + state + } + Err(err) => { + let _ = on_ready.send(Err(err)); + return + } + }; + + let mut stream = ReceiverStream::new(rx); + while let Some(req) = stream.next().await { + match req { + JsDbRequest::Basic { address, resp } => { + let acc = state.basic_account(address).map_err(|err| err.to_string()); + let _ = resp.send(acc); + } + JsDbRequest::Code { code_hash, resp } => { + let code = state + .bytecode_by_hash(code_hash) + .map(|code| code.map(|c| c.bytecode.clone()).unwrap_or_default()) + .map_err(|err| err.to_string()); + let _ = resp.send(code); + } + JsDbRequest::StorageAt { address, index, resp } => { + let value = state + .storage(address, index) + .map(|val| val.unwrap_or_default()) + .map_err(|err| err.to_string()); + let _ = resp.send(value); + } + } + } + } } #[async_trait] @@ -486,70 +652,3 @@ struct DebugApiInner { /// The type that can spawn tasks which would otherwise block. task_spawner: Box, } - -/// Executes the configured transaction with the environment on the given database. -/// -/// Returns the trace frame and the state that got updated after executing the transaction. -/// -/// Note: this does not apply any state overrides if they're configured in the `opts`. -fn trace_transaction( - opts: GethDebugTracingOptions, - env: Env, - db: &mut SubState>, -) -> EthResult<(GethTraceFrame, revm_primitives::State)> { - let GethDebugTracingOptions { config, tracer, tracer_config, .. } = opts; - - if let Some(tracer) = tracer { - // valid matching config - if let Some(ref config) = tracer_config { - if !config.matches_tracer(&tracer) { - return Err(EthApiError::InvalidTracerConfig) - } - } - - return match tracer { - GethDebugTracerType::BuiltInTracer(tracer) => match tracer { - GethDebugBuiltInTracerType::FourByteTracer => { - let mut inspector = FourByteInspector::default(); - let (res, _) = inspect(db, env, &mut inspector)?; - return Ok((FourByteFrame::from(inspector).into(), res.state)) - } - GethDebugBuiltInTracerType::CallTracer => { - // we validated the config above - let call_config = - tracer_config.and_then(|c| c.into_call_config()).unwrap_or_default(); - - let mut inspector = - TracingInspector::new(TracingInspectorConfig::from_geth_config(&config)); - - let (res, _) = inspect(db, env, &mut inspector)?; - - let frame = inspector.into_geth_builder().geth_call_traces(call_config); - - return Ok((frame.into(), res.state)) - } - GethDebugBuiltInTracerType::PreStateTracer => { - Err(EthApiError::Unsupported("prestate tracer is unimplemented yet.")) - } - GethDebugBuiltInTracerType::NoopTracer => { - Ok((NoopFrame::default().into(), Default::default())) - } - }, - GethDebugTracerType::JsTracer(_) => { - Err(EthApiError::Unsupported("javascript tracers are unsupported.")) - } - } - } - - // default structlog tracer - let inspector_config = TracingInspectorConfig::from_geth_config(&config); - - let mut inspector = TracingInspector::new(inspector_config); - - let (res, _) = inspect(db, env, &mut inspector)?; - let gas_used = res.result.gas_used(); - - let frame = inspector.into_geth_builder().geth_traces(gas_used, config); - - Ok((frame.into(), res.state)) -} diff --git a/crates/rpc/rpc/src/eth/error.rs b/crates/rpc/rpc/src/eth/error.rs index 8517d0025d..625c0e2bd5 100644 --- a/crates/rpc/rpc/src/eth/error.rs +++ b/crates/rpc/rpc/src/eth/error.rs @@ -3,6 +3,7 @@ use crate::result::{internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code}; use jsonrpsee::{core::Error as RpcError, types::ErrorObject}; use reth_primitives::{abi::decode_revert_reason, Address, Bytes, U256}; +use reth_revm::tracing::js::JsInspectorError; use reth_rpc_types::{error::EthRpcErrorCode, BlockError}; use reth_transaction_pool::error::{InvalidPoolTransactionError, PoolError}; use revm::primitives::{EVMError, ExecutionResult, Halt, OutOfGasError}; @@ -57,6 +58,9 @@ pub enum EthApiError { /// Some feature is unsupported #[error("unsupported")] Unsupported(&'static str), + /// General purpose error for invalid params + #[error("{0}")] + InvalidParams(String), /// When tracer config does not match the tracer #[error("invalid tracer config")] InvalidTracerConfig, @@ -69,6 +73,9 @@ pub enum EthApiError { /// Error thrown when a spawned blocking task failed to deliver an anticipated response. #[error("internal eth error")] InternalEthError, + /// Internal Error thrown by the javascript tracer + #[error("{0}")] + InternalJsTracerError(String), } impl From for ErrorObject<'static> { @@ -92,6 +99,8 @@ impl From for ErrorObject<'static> { rpc_error_with_code(EthRpcErrorCode::ResourceNotFound.code(), error.to_string()) } EthApiError::Unsupported(msg) => internal_rpc_err(msg), + EthApiError::InternalJsTracerError(msg) => internal_rpc_err(msg), + EthApiError::InvalidParams(msg) => invalid_params_rpc_err(msg), EthApiError::InvalidRewardPercentile(msg) => internal_rpc_err(msg.to_string()), err @ EthApiError::InternalTracingError => internal_rpc_err(err.to_string()), err @ EthApiError::InternalEthError => internal_rpc_err(err.to_string()), @@ -104,6 +113,16 @@ impl From for RpcError { RpcError::Call(error.into()) } } +impl From for EthApiError { + fn from(error: JsInspectorError) -> Self { + match error { + err @ JsInspectorError::JsError(_) => { + EthApiError::InternalJsTracerError(err.to_string()) + } + err => EthApiError::InvalidParams(err.to_string()), + } + } +} impl From for EthApiError { fn from(error: reth_interfaces::Error) -> Self {