diff --git a/Cargo.lock b/Cargo.lock index 97e23afde..88bb75fe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -292,7 +301,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -329,7 +338,7 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -372,7 +381,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -399,7 +408,7 @@ dependencies = [ "k256", "serde", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -424,7 +433,7 @@ dependencies = [ "serde", "serde_with", "sha2 0.10.9", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -446,7 +455,7 @@ dependencies = [ "op-alloy-rpc-types-engine", "op-revm", "revm", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -513,7 +522,7 @@ dependencies = [ "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -540,7 +549,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -572,7 +581,7 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "url", ] @@ -591,7 +600,7 @@ dependencies = [ "foldhash 0.2.0", "getrandom 0.3.3", "hashbrown 0.16.0", - "indexmap 2.10.0", + "indexmap 2.12.0", "itoa", "k256", "keccak-asm", @@ -643,7 +652,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -690,7 +699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -807,7 +816,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -821,7 +830,7 @@ dependencies = [ "alloy-serde", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -849,7 +858,7 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -867,7 +876,7 @@ dependencies = [ "aws-sdk-kms", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -885,7 +894,7 @@ dependencies = [ "gcloud-sdk", "k256", "spki", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -905,7 +914,7 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -922,7 +931,7 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -936,7 +945,7 @@ dependencies = [ "alloy-primitives", "alloy-signer", "async-trait", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "turnkey_client", ] @@ -951,7 +960,7 @@ dependencies = [ "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -965,10 +974,10 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.10.0", + "indexmap 2.12.0", "proc-macro-error2", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "syn-solidity", "tiny-keccak 2.0.2", @@ -986,7 +995,7 @@ dependencies = [ "heck", "macro-string", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "serde_json", "syn 2.0.104", "syn-solidity", @@ -1029,7 +1038,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tower", "tracing", @@ -1094,7 +1103,7 @@ checksum = "7ccf423f6de62e8ce1d6c7a11fb7508ae3536d02e0d68aaeb05c8669337d0937" dependencies = [ "darling 0.21.3", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1185,7 +1194,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro-error2", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1298,7 +1307,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1308,7 +1317,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1318,7 +1327,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1330,7 +1339,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint 0.4.6", "num-traits", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1343,7 +1352,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1356,7 +1365,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1445,7 +1454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1496,7 +1505,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0609c78bd572f4edc74310dfb63a01f5609d53fa8b4dd7c4d98aef3b3e8d72d1" dependencies = [ "proc-macro-hack", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1567,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1578,7 +1587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -1616,7 +1625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -2348,7 +2357,7 @@ dependencies = [ "log", "prettyplease", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "regex", "rustc-hash 1.1.0", "shlex", @@ -2367,7 +2376,7 @@ dependencies = [ "clang-sys", "itertools 0.13.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "regex", "rustc-hash 2.1.1", "shlex", @@ -2569,6 +2578,147 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boa_ast" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" +dependencies = [ + "bitflags 2.9.4", + "boa_interner", + "boa_macros", + "boa_string", + "indexmap 2.12.0", + "num-bigint 0.4.6", + "rustc-hash 2.1.1", +] + +[[package]] +name = "boa_engine" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" +dependencies = [ + "aligned-vec", + "arrayvec 0.7.6", + "bitflags 2.9.4", + "boa_ast", + "boa_gc", + "boa_interner", + "boa_macros", + "boa_parser", + "boa_string", + "bytemuck", + "cfg-if", + "cow-utils", + "dashmap", + "dynify", + "fast-float2", + "float16", + "futures-channel", + "futures-concurrency", + "futures-lite", + "hashbrown 0.16.0", + "icu_normalizer", + "indexmap 2.12.0", + "intrusive-collections", + "itertools 0.14.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "num_enum 0.7.4", + "paste", + "portable-atomic", + "rand 0.9.2", + "regress", + "rustc-hash 2.1.1", + "ryu-js", + "serde", + "serde_json", + "small_btree", + "static_assertions", + "tag_ptr", + "tap", + "thin-vec", + "thiserror 2.0.17", + "time", + "xsum", +] + +[[package]] +name = "boa_gc" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" +dependencies = [ + "boa_macros", + "boa_string", + "hashbrown 0.16.0", + "thin-vec", +] + +[[package]] +name = "boa_interner" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" +dependencies = [ + "boa_gc", + "boa_macros", + "hashbrown 0.16.0", + "indexmap 2.12.0", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "static_assertions", +] + +[[package]] +name = "boa_macros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f36418a46544b152632c141b0a0b7a453cd69ca150caeef83aee9e2f4b48b7d" +dependencies = [ + "cfg-if", + "cow-utils", + "proc-macro2 1.0.95", + "quote 1.0.42", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "boa_parser" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" +dependencies = [ + "bitflags 2.9.4", + "boa_ast", + "boa_interner", + "boa_macros", + "fast-float2", + "icu_properties", + "num-bigint 0.4.6", + "num-traits", + "regress", + "rustc-hash 2.1.1", +] + +[[package]] +name = "boa_string" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ce9d7aa5563a2e14eab111e2ae1a06a69a812f6c0c3d843196c9d03fbef440" +dependencies = [ + "fast-float2", + "itoa", + "paste", + "rustc-hash 2.1.1", + "ryu-js", + "static_assertions", +] + [[package]] name = "boojum" version = "0.32.5" @@ -2635,7 +2785,7 @@ dependencies = [ "once_cell", "proc-macro-crate 3.3.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -2653,9 +2803,23 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2 1.0.95", + "quote 1.0.42", + "syn 2.0.104", +] [[package]] name = "byteorder" @@ -2808,7 +2972,7 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -2993,7 +3157,7 @@ checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3155,7 +3319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "unicode-xid 0.2.6", ] @@ -3183,6 +3347,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cordyceps" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688d7fbb8092b8de775ef2536f36c8c31f2bc4006ece2e8d8ad2d17d00ce0a2a" +dependencies = [ + "loom", + "tracing", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -3209,6 +3383,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cow-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79" + [[package]] name = "cpp_demangle" version = "0.4.4" @@ -3492,7 +3672,7 @@ dependencies = [ "itertools 0.14.0", "poseidon2 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "rayon", "seq-macro", "serde", @@ -3517,7 +3697,7 @@ dependencies = [ "itertools 0.14.0", "poseidon2 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "rayon", "seq-macro", "serde", @@ -3534,7 +3714,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3582,7 +3762,7 @@ dependencies = [ "fnv", "ident_case", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "strsim", "syn 2.0.104", ] @@ -3596,7 +3776,7 @@ dependencies = [ "fnv", "ident_case", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "serde", "strsim", "syn 2.0.104", @@ -3609,7 +3789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3620,7 +3800,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3667,12 +3847,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -3682,7 +3862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -3693,7 +3873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3704,7 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3725,11 +3905,17 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "convert_case 0.7.1", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "unicode-xid 0.2.6", ] +[[package]] +name = "diatomic-waker" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c" + [[package]] name = "digest" version = "0.9.0" @@ -3758,7 +3944,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3786,6 +3972,26 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +[[package]] +name = "dynify" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81acb15628a3e22358bf73de5e7e62360b8a777dbcb5fc9ac7dfa9ae73723747" +dependencies = [ + "dynify-macros", +] + +[[package]] +name = "dynify-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" +dependencies = [ + "proc-macro2 1.0.95", + "quote 1.0.42", + "syn 2.0.104", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -3809,7 +4015,7 @@ checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" dependencies = [ "enum-ordinalize", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3883,7 +4089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -3910,6 +4116,26 @@ dependencies = [ "log", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2 1.0.95", + "quote 1.0.42", + "syn 2.0.104", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -4039,7 +4265,7 @@ checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" dependencies = [ "darling 0.20.11", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -4122,6 +4348,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fast-float2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" + [[package]] name = "fastrand" version = "2.3.0" @@ -4307,6 +4539,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.2" @@ -4317,6 +4555,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float16" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bffafbd079d520191c7c2779ae9cf757601266cf4167d3f659ff09617ff8483" +dependencies = [ + "cfg-if", + "rustc_version 0.2.3", +] + [[package]] name = "fnv" version = "1.0.7" @@ -4523,6 +4771,19 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-buffered" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e0e1f38ec07ba4abbde21eed377082f17ccb988be9d988a5adbf4bafc118fd" +dependencies = [ + "cordyceps", + "diatomic-waker", + "futures-core", + "pin-project-lite", + "spin", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -4533,6 +4794,21 @@ dependencies = [ "futures-sink", ] +[[package]] +name = "futures-concurrency" +version = "7.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eb68017df91f2e477ed4bea586c59eaecaa47ed885a770d0444e21e62572cd2" +dependencies = [ + "fixedbitset", + "futures-buffered", + "futures-core", + "futures-lite", + "pin-project 1.1.10", + "slab", + "smallvec", +] + [[package]] name = "futures-core" version = "0.3.31" @@ -4557,6 +4833,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-locks" version = "0.7.1" @@ -4575,7 +4864,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -4653,6 +4942,20 @@ dependencies = [ "url", ] +[[package]] +name = "generator" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4829,7 +5132,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.10.0", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -4895,6 +5198,8 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", "serde", ] @@ -5195,6 +5500,8 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "write16", "zerovec", ] @@ -5310,7 +5617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -5330,7 +5637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", ] [[package]] @@ -5346,13 +5653,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -5367,7 +5675,7 @@ dependencies = [ "crossbeam-utils", "dashmap", "env_logger", - "indexmap 2.10.0", + "indexmap 2.12.0", "itoa", "log", "num-format", @@ -5398,6 +5706,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset 0.9.1", +] + [[package]] name = "io-uring" version = "0.7.8" @@ -5484,7 +5801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -5565,7 +5882,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-rustls", "tokio-util", @@ -5593,7 +5910,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tower", @@ -5618,7 +5935,7 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tower", "url", @@ -5633,7 +5950,7 @@ dependencies = [ "heck", "proc-macro-crate 3.3.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -5656,7 +5973,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -5673,7 +5990,7 @@ dependencies = [ "http 1.3.1", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -5900,6 +6217,19 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber 0.3.20", +] + [[package]] name = "lru" version = "0.12.5" @@ -5965,7 +6295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6018,6 +6348,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metrics" version = "0.24.2" @@ -6035,7 +6374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3dbdd96ed57d565ec744cba02862d707acf373c5772d152abae6ec5c4e24f6c" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "regex", "syn 2.0.104", ] @@ -6115,7 +6454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -6145,7 +6484,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.7.1", "pin-utils", ] @@ -6344,7 +6683,7 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6356,10 +6695,19 @@ checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "nvtx" version = "1.3.0" @@ -6440,7 +6788,7 @@ dependencies = [ "alloy-serde", "derive_more 2.0.1", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -6459,7 +6807,7 @@ dependencies = [ "ethereum_ssz_derive", "op-alloy-consensus", "snap", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -6501,7 +6849,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6533,7 +6881,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -6575,7 +6923,7 @@ dependencies = [ "opentelemetry_sdk", "prost 0.14.1", "reqwest", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", ] @@ -6610,7 +6958,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "rand 0.9.2", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", ] @@ -6705,10 +7053,16 @@ checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -6770,7 +7124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.12", + "thiserror 2.0.17", "ucd-trie", ] @@ -6793,7 +7147,7 @@ dependencies = [ "pest", "pest_meta", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6847,7 +7201,7 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6885,7 +7239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -6896,7 +7250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -6935,7 +7289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.10.0", + "indexmap 2.12.0", "quick-xml 0.38.3", "serde", "time", @@ -7089,7 +7443,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", "version_check", ] @@ -7101,7 +7455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "version_check", ] @@ -7112,7 +7466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", ] [[package]] @@ -7123,7 +7477,7 @@ checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7170,7 +7524,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7251,7 +7605,7 @@ dependencies = [ "anyhow", "itertools 0.10.5", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7264,7 +7618,7 @@ dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7277,7 +7631,7 @@ dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7319,7 +7673,7 @@ dependencies = [ "non_determinism_source 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "poseidon2 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "rayon", "risc_v_simulator 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "seq-macro", @@ -7348,7 +7702,7 @@ dependencies = [ "non_determinism_source 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "poseidon2 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "rayon", "risc_v_simulator 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "seq-macro", @@ -7440,7 +7794,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls", "socket2", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -7461,7 +7815,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -7492,9 +7846,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2 1.0.95", ] @@ -7732,7 +8086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7771,6 +8125,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "regress" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" +dependencies = [ + "hashbrown 0.16.0", + "memchr", +] + [[package]] name = "reqwest" version = "0.12.22" @@ -7917,7 +8281,7 @@ version = "1.9.3" source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea84c5751776ecabdd069646" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -7931,7 +8295,7 @@ dependencies = [ "auto_impl", "reth-execution-types", "reth-primitives-traits", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -7954,7 +8318,7 @@ dependencies = [ "reth-consensus", "reth-execution-errors", "reth-storage-errors", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -7975,7 +8339,7 @@ dependencies = [ "reth-ethereum-primitives", "reth-primitives-traits", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8020,7 +8384,7 @@ dependencies = [ "alloy-rlp", "nybbles", "reth-storage-errors", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8048,7 +8412,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8069,7 +8433,7 @@ dependencies = [ "alloy-rlp", "secp256k1 0.30.0", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", "url", ] @@ -8113,7 +8477,7 @@ dependencies = [ "secp256k1 0.30.0", "serde", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8125,7 +8489,7 @@ dependencies = [ "derive_more 2.0.1", "serde", "strum", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8197,7 +8561,7 @@ dependencies = [ "reth-prune-types", "reth-static-file-types", "revm-database-interface", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -8210,7 +8574,7 @@ dependencies = [ "futures-util", "metrics", "reth-metrics", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "tracing-futures", @@ -8249,7 +8613,7 @@ dependencies = [ "serde", "serde_json", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -8557,7 +8921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5a11a05ee1ce44058fa3d5961d05194fdbe3ad6b40f904af764d81b86450e6b" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -8771,6 +9135,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.3.3" @@ -8945,6 +9318,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "ryu-js" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" + [[package]] name = "same-file" version = "1.0.6" @@ -8998,6 +9377,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -9118,13 +9503,22 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.3", ] [[package]] @@ -9136,6 +9530,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.3" @@ -9272,7 +9672,7 @@ dependencies = [ "rand 0.9.2", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", "url", "uuid", @@ -9331,21 +9731,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.12.0", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -9389,7 +9790,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.12.0", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -9407,7 +9808,7 @@ checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling 0.20.11", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -9417,7 +9818,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.12.0", "itoa", "ryu", "serde", @@ -9445,7 +9846,7 @@ dependencies = [ "machine_without_signed_mul_div 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "proc-macro2 1.0.95", "prover 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", - "quote 1.0.40", + "quote 1.0.42", "reduced_risc_v_log_23_machine 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "reduced_risc_v_machine 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", "risc_v_cycles 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", @@ -9466,7 +9867,7 @@ dependencies = [ "machine_without_signed_mul_div 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "proc-macro2 1.0.95", "prover 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", - "quote 1.0.40", + "quote 1.0.42", "reduced_risc_v_log_23_machine 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "reduced_risc_v_machine 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", "risc_v_cycles 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", @@ -9621,7 +10022,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint 0.4.6", "num-traits", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] @@ -9637,6 +10038,15 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +[[package]] +name = "small_btree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba60d2df92ba73864714808ca68c059734853e6ab722b40e1cf543ebb3a057a" +dependencies = [ + "arrayvec 0.7.6", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -9684,7 +10094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1634024848ad2d76c65a88ddc7b17de8ca323100b3e77d9ee94b103fc5efe65" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -9743,6 +10153,12 @@ dependencies = [ "sha1", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + [[package]] name = "spki" version = "0.7.3" @@ -9848,7 +10264,7 @@ checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -9887,7 +10303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "unicode-ident", ] @@ -9898,7 +10314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "unicode-ident", ] @@ -9910,7 +10326,7 @@ checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" dependencies = [ "paste", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -9930,7 +10346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -9997,6 +10413,12 @@ dependencies = [ "zk_ee 0.1.0 (git+https://github.com/matter-labs/zksync-os?tag=v0.2.4)", ] +[[package]] +name = "tag_ptr" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0e973b34477b7823833469eb0f5a3a60370fef7a453e02d751b59180d0a5a05" + [[package]] name = "tap" version = "1.0.1" @@ -10044,7 +10466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -10065,11 +10487,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -10079,18 +10501,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -10114,13 +10536,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", + "js-sys", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -10129,15 +10554,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -10223,7 +10648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -10316,7 +10741,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.12.0", "toml_datetime", "winnow 0.5.40", ] @@ -10327,7 +10752,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.12.0", "serde", "serde_spanned", "toml_datetime", @@ -10412,7 +10837,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "indexmap 2.10.0", + "indexmap 2.12.0", "pin-project-lite", "slab", "sync_wrapper", @@ -10526,7 +10951,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -10584,7 +11009,7 @@ dependencies = [ "opentelemetry_sdk", "rustversion", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "tracing-core", "tracing-log", @@ -10684,7 +11109,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.12", + "thiserror 2.0.17", "utf-8", ] @@ -10701,7 +11126,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -10717,7 +11142,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "turnkey_api_key_stamper", ] @@ -10828,7 +11253,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad948c1cb799b1a70f836077721a92a35ac177d4daddf4c20a633786d4cf618" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -10904,6 +11329,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -10979,7 +11410,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2 1.0.95", "prover 0.1.0 (git+https://github.com/matter-labs/zksync-airbender?tag=v0.4.3)", - "quote 1.0.40", + "quote 1.0.42", "serde", "serde_json", "syn 2.0.104", @@ -10993,7 +11424,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2 1.0.95", "prover 0.1.0 (git+https://github.com/matter-labs/zksync-airbender.git?tag=v0.5.1)", - "quote 1.0.40", + "quote 1.0.42", "serde", "serde_json", "syn 2.0.104", @@ -11047,7 +11478,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe466916e9bf50cc69aba41affc11970885e9dd2b9e448e20e1fb8fc726132ed" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -11121,7 +11552,7 @@ dependencies = [ "bumpalo", "log", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "wasm-bindgen-shared", ] @@ -11145,7 +11576,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "wasm-bindgen-macro-support", ] @@ -11156,7 +11587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -11297,6 +11728,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -11310,6 +11763,17 @@ dependencies = [ "windows-strings", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link 0.1.3", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.60.0" @@ -11317,7 +11781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -11328,7 +11792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -11344,6 +11808,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link 0.1.3", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -11457,6 +11931,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -11640,6 +12123,12 @@ dependencies = [ "rayon", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + [[package]] name = "writeable" version = "0.6.1" @@ -11659,7 +12148,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper 0.6.0", - "thiserror 2.0.12", + "thiserror 2.0.17", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -11680,6 +12169,12 @@ version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" +[[package]] +name = "xsum" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0637d3a5566a82fa5214bae89087bc8c9fb94cd8e8a3c07feb691bb8d9c632db" + [[package]] name = "yoke" version = "0.8.0" @@ -11699,7 +12194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "synstructure", ] @@ -11720,7 +12215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -11740,7 +12235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", "synstructure", ] @@ -11761,7 +12256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -11794,7 +12289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 2.0.104", ] @@ -12014,7 +12509,7 @@ checksum = "0c5084f440210f86247ff0c75806606eee9effc00382407f78c8b0cdcc742248" dependencies = [ "proc-macro-error", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -12041,7 +12536,7 @@ dependencies = [ "num-integer", "num-traits", "proc-macro2 1.0.95", - "quote 1.0.40", + "quote 1.0.42", "serde", "syn 1.0.109", ] @@ -12075,7 +12570,7 @@ dependencies = [ "blake2 0.10.6", "ruint", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", "zk_ee 0.1.0 (git+https://github.com/matter-labs/zksync-os?tag=v0.1.0-rc1)", "zk_ee 0.1.0 (git+https://github.com/matter-labs/zksync-os?tag=v0.2.4)", "zksync_os_contract_interface", @@ -12102,7 +12597,7 @@ dependencies = [ "semver 1.0.26", "serde", "structdiff", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -12130,7 +12625,7 @@ dependencies = [ "backon", "serde", "structdiff", - "thiserror 2.0.12", + "thiserror 2.0.17", "tracing", "vise", ] @@ -12316,7 +12811,7 @@ dependencies = [ "futures", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "vise", @@ -12372,7 +12867,7 @@ dependencies = [ "serde", "serde_with", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "tracing-subscriber 0.3.20", @@ -12447,7 +12942,7 @@ dependencies = [ "sentry", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "tracing-logfmt", @@ -12534,7 +13029,7 @@ dependencies = [ "serde_json", "serde_yaml", "smart-config", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tracing", "vise", @@ -12573,13 +13068,17 @@ dependencies = [ "anyhow", "async-trait", "basic_system 0.1.0 (git+https://github.com/matter-labs/zksync-os?tag=v0.2.4)", + "boa_engine", + "boa_gc", "dashmap", + "evm_interpreter 0.1.0 (git+https://github.com/matter-labs/zksync-os?tag=v0.2.4)", "futures", "hyper", "jsonrpsee", "ruint", "serde", - "thiserror 2.0.12", + "serde_json", + "thiserror 2.0.17", "tokio", "tokio-stream", "tower", @@ -12705,7 +13204,7 @@ dependencies = [ "serde_yaml", "smart-config", "smart-config-commands", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -12860,7 +13359,7 @@ dependencies = [ "pin-project 1.1.10", "semver 1.0.26", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -12884,7 +13383,7 @@ dependencies = [ "semver 1.0.26", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "zksync_os_contract_interface", "zksync_os_interface", ] diff --git a/Cargo.toml b/Cargo.toml index bee067b94..c511f672a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,6 +96,7 @@ zk_os_forward_system = { package = "forward_system", git = "https://github.com/m zk_ee = { package = "zk_ee", git = "https://github.com/matter-labs/zksync-os", tag = "v0.2.4" } zk_os_basic_system = { package = "basic_system", git = "https://github.com/matter-labs/zksync-os", tag = "v0.2.4" } zk_os_api = { package = "zksync_os_api", git = "https://github.com/matter-labs/zksync-os", tag = "v0.2.4" } +zk_os_evm_interpreter = { package = "evm_interpreter", git = "https://github.com/matter-labs/zksync-os", tag = "v0.2.4" } #execution_utils = { package = "execution_utils", path = "../zksync-airbender/execution_utils" } #full_statement_verifier = { package = "full_statement_verifier", path = "../zksync-airbender/full_statement_verifier" } @@ -164,6 +165,8 @@ url = "2.5.7" num_enum = "0.7.2" metrics = "0.24.2" chrono = { version = "0.4", default-features = false } +boa_engine = { version = "0.21.0" } +boa_gc = { version = "0.21.0" } structdiff = { version = "0.7.3", features = ["debug_diffs"] } tracing-opentelemetry = "0.32.0" diff --git a/integration-tests/test-contracts/src/TracingPrimary.sol b/integration-tests/test-contracts/src/TracingPrimary.sol index 56522f214..892dde598 100644 --- a/integration-tests/test-contracts/src/TracingPrimary.sol +++ b/integration-tests/test-contracts/src/TracingPrimary.sol @@ -5,6 +5,9 @@ import "./TracingSecondary.sol"; contract TracingPrimary { TracingSecondary secondary; + uint256 public lastCalculated; + event CalculationDone(uint256 indexed input, uint256 indexed result); + constructor(address _secondary) { secondary = TracingSecondary(_secondary); } @@ -14,7 +17,12 @@ contract TracingPrimary { } function calculate(uint256 value) public returns (uint) { - return secondary.multiply(value); + uint result = secondary.multiply(value); + lastCalculated = result; + + emit CalculationDone(value, result); + + return result; } function shouldRevert() public view returns (uint) { diff --git a/integration-tests/tests/js_tracer_cross_node.rs b/integration-tests/tests/js_tracer_cross_node.rs new file mode 100644 index 000000000..c329146d4 --- /dev/null +++ b/integration-tests/tests/js_tracer_cross_node.rs @@ -0,0 +1,243 @@ +use alloy::eips::BlockId; +use alloy::network::{EthereumWallet, TransactionBuilder}; +use alloy::primitives::U256; +use alloy::providers::ProviderBuilder; +use alloy::providers::ext::DebugApi; +use alloy::rpc::types::TransactionRequest; +use alloy::rpc::types::trace::geth::{GethDebugTracerType, GethDebugTracingCallOptions, GethTrace}; +use alloy::signers::local::LocalSigner; +use serde_json::Value; +use std::env; +use std::str::FromStr; +use tracing::info; +use zksync_os_integration_tests::contracts::{TracingPrimary, TracingSecondary}; +use zksync_os_integration_tests::dyn_wallet_provider::EthDynProvider; + +const ZKSYNC_URL_ENV: &str = "JS_TRACER_ZKSYNC_RPC_URL"; +const RETH_URL_ENV: &str = "JS_TRACER_RETH_RPC_URL"; +const PRIVATE_KEY_ENV: &str = "JS_TRACER_PRIVATE_KEY"; + +// This test is left here in case we want to do cross-node JS tracer comparisons in the future. +// It is ignored by default since it requires setting up two nodes and providing their RPC URLs +#[ignore] +#[test_log::test(tokio::test)] +async fn compare_js_tracer_outputs_between_nodes() -> anyhow::Result<()> { + let Some(zksync_url) = env::var(ZKSYNC_URL_ENV).ok() else { + info!("skipping cross-node tracer comparison; {ZKSYNC_URL_ENV} is not set"); + return Ok(()); + }; + let Some(reth_url) = env::var(RETH_URL_ENV).ok() else { + info!("skipping cross-node tracer comparison; {RETH_URL_ENV} is not set"); + return Ok(()); + }; + let Some(private_key) = env::var(PRIVATE_KEY_ENV).ok() else { + info!("skipping cross-node tracer comparison; {PRIVATE_KEY_ENV} is not set"); + return Ok(()); + }; + + let wallet = EthereumWallet::new(LocalSigner::from_str(&private_key)?); + let wallet_address = wallet.default_signer().address(); + + let zksync_provider = ProviderBuilder::new() + .wallet(wallet.clone()) + .connect(&zksync_url) + .await?; + let reth_provider = ProviderBuilder::new() + .wallet(wallet.clone()) + .connect(&reth_url) + .await?; + + let zksync_provider = EthDynProvider::new(zksync_provider); + let reth_provider = EthDynProvider::new(reth_provider); + + // Deploy helper contracts on both nodes. + let secondary_init_value = U256::from(7); + let secondary_zksync = + TracingSecondary::deploy(zksync_provider.clone(), secondary_init_value).await?; + let primary_zksync = + TracingPrimary::deploy(zksync_provider.clone(), *secondary_zksync.address()).await?; + + let secondary_reth = + TracingSecondary::deploy(reth_provider.clone(), secondary_init_value).await?; + let primary_reth = + TracingPrimary::deploy(reth_provider.clone(), *secondary_reth.address()).await?; + + // Prepare calls we want to compare. + let calculate_value = U256::from(3); + let mut calc_zksync_request = primary_zksync + .calculate(calculate_value) + .into_transaction_request(); + configure_call_request(&mut calc_zksync_request, wallet_address); + let mut calc_reth_request = primary_reth + .calculate(calculate_value) + .into_transaction_request(); + configure_call_request(&mut calc_reth_request, wallet_address); + + let call_scenarios = vec![("calculate", calc_zksync_request, calc_reth_request)]; + + let tracers = vec![ + ( + "minimal_tracer", + r#" + { + maxSteps: 256, + + setup: function () { + this.steps = []; + }, + + step: function (log, db) { + const rec = { + pc: log.getPC(), + op: log.op.toString(), + gas: log.getGas(), + gasCost: log.getCost(), + depth: log.getDepth(), + error: log.getError ? ("" + log.getError()) : null + }; + this.steps.push(rec); + if (this.steps.length > this.maxSteps) this.steps.shift(); + }, + + fault: function (log, db) { + this.faultLog = { + pc: log.getPC(), + op: log.op.toString(), + gas: log.getGas(), + depth: log.getDepth(), + error: "" + log.getError() + }; + }, + + result: function (ctx, db) { + return { + type: "minimal-steps", + lastSteps: this.steps, + fault: this.faultLog || null + }; + } + } + "#, + ), + ( + "value_transfer_tracer", + r#" { + setup: function () { + this.totalCost = 0; + this.byOp = {}; + }, + + step: function (log, db) { + var op = log.op.toString(); + var cost = log.getCost(); + this.totalCost += cost; + + var e = this.byOp[op]; + if (!e) this.byOp[op] = { count: 1, cost: cost }; + else { e.count += 1; e.cost += cost; } + }, + + result: function () { + var hot = []; + for (var k in this.byOp) { + hot.push({ op: k, count: this.byOp[k].count, cost: this.byOp[k].cost }); + } + hot.sort(function (a, b) { return b.cost - a.cost; }); + + return { + type: "gas-profiler", + totalIntrinsicCostApprox: this.totalCost, + hotOpcodes: hot + }; + } + } + "#, + ), + ( + "opcode_coverage_tracer", + r#" + { + setup: function () { + this.cover = {}; + this.pcs = {}; + }, + + step: function (log, db) { + var op = log.op.toString(); + this.cover[op] = true; + + var pc = log.getPC(); + var m = this.pcs[op]; + if (!m) { m = {}; this.pcs[op] = m; } + m[pc] = true; + }, + + result: function () { + var ops = []; + for (var k in this.cover) { + var pcs = Object.keys(this.pcs[k]).map(function (x) { return Number(x); }); + ops.push({ op: k, uniquePCs: pcs.length }); + } + ops.sort(function (a, b) { return a.op < b.op ? -1 : 1; }); + + return { type: "opcode-coverage", ops: ops }; + } + } + "#, + ), + ]; + + for (scenario_name, zk_request, reth_request) in call_scenarios { + for (tracer_name, tracer_code) in &tracers { + let reth_trace = + trace_with_js(&reth_provider, reth_request.clone(), tracer_code).await?; + let zk_trace = trace_with_js(&zksync_provider, zk_request.clone(), tracer_code).await?; + let mut zk_trace = zk_trace; + let mut reth_trace = reth_trace; + normalize_hex_strings(&mut zk_trace); + normalize_hex_strings(&mut reth_trace); + assert_eq!( + zk_trace, reth_trace, + "JS tracer '{tracer_name}' produced different output for scenario '{scenario_name}'" + ); + } + } + + Ok(()) +} + +fn configure_call_request(req: &mut TransactionRequest, from: alloy::primitives::Address) { + req.set_from(from); + req.max_priority_fee_per_gas = Some(1); + req.max_fee_per_gas = Some(u128::MAX); +} + +async fn trace_with_js( + provider: &EthDynProvider, + request: TransactionRequest, + tracer_code: &str, +) -> anyhow::Result { + let mut opts = GethDebugTracingCallOptions::default(); + + opts.tracing_options.tracer = Some(GethDebugTracerType::JsTracer(tracer_code.to_string())); + let trace = provider + .debug_trace_call(request, BlockId::latest(), opts) + .await?; + match trace { + GethTrace::JS(value) => Ok(value), + other => anyhow::bail!("expected JS trace result, got {other:?}"), + } +} + +fn normalize_hex_strings(value: &mut Value) { + match value { + Value::String(data) => { + if data.starts_with("0x") { + *data = data.to_lowercase(); + } + } + Value::Array(values) => values.iter_mut().for_each(normalize_hex_strings), + Value::Object(map) => map.values_mut().for_each(normalize_hex_strings), + _ => {} + } +} diff --git a/integration-tests/tests/tracing.rs b/integration-tests/tests/tracing.rs index 1d26bbf6f..4c8fad564 100644 --- a/integration-tests/tests/tracing.rs +++ b/integration-tests/tests/tracing.rs @@ -1,10 +1,13 @@ use alloy::eips::BlockId; -use alloy::network::Ethereum; +use alloy::network::{Ethereum, TransactionBuilder}; use alloy::primitives::{Address, Bytes, U256}; use alloy::providers::PendingTransactionBuilder; use alloy::providers::ext::DebugApi; use alloy::rpc::types::TransactionRequest; -use alloy::rpc::types::trace::geth::{CallConfig, CallFrame, GethDebugTracingOptions}; +use alloy::rpc::types::trace::geth::{ + CallConfig, CallFrame, GethDebugTracerType, GethDebugTracingCallOptions, + GethDebugTracingOptions, GethTrace, +}; use alloy::sol_types::{Revert, SolCall, SolError}; use std::collections::HashMap; use zksync_os_integration_tests::Tester; @@ -414,3 +417,224 @@ async fn call_trace_block() -> anyhow::Result<()> { return Ok(()); } } + +#[test_log::test(tokio::test)] +async fn debug_trace_call_js_tracer() -> anyhow::Result<()> { + let tester = Tester::setup().await?; + + let secondary_data = U256::from(7); + let calculate_value = U256::from(3); + let secondary_contract = + TracingSecondary::deploy(tester.l2_provider.clone(), secondary_data).await?; + let primary_contract = + TracingPrimary::deploy(tester.l2_provider.clone(), *secondary_contract.address()).await?; + + let mut call_request = primary_contract + .calculate(calculate_value) + .into_transaction_request(); + + let js_str = r#" + { + data: [], + fault: function(log) {}, + step: function(log) {}, + enter: function (frame) {this.data.push(frame.getTo()); }, + result: function(ctx, db) { return this.data; } + }"#; + + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::JsTracer(js_str.to_string())); + call_request.max_priority_fee_per_gas = Some(1); + call_request.max_fee_per_gas = Some(u128::MAX); + call_request.set_from(tester.l2_wallet.default_signer().address()); + + let trace = tester + .l2_provider + .debug_trace_call(call_request, BlockId::latest(), opts) + .await?; + + let addresses = match trace { + GethTrace::JS(value) => value + .as_array() + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_lowercase())) + .collect::>() + }) + .expect("tracer result missing addresses"), + other => panic!("expected JS trace result, got {other:?}"), + }; + + let expected_primary = format!("{:#x}", primary_contract.address()).to_lowercase(); + let expected_secondary = format!("{:#x}", secondary_contract.address()).to_lowercase(); + + assert!( + addresses.iter().any(|addr| addr == &expected_primary), + "primary contract address not found in tracer output" + ); + assert!( + addresses.iter().any(|addr| addr == &expected_secondary), + "secondary contract address not found in tracer output" + ); + + Ok(()) +} + +#[test_log::test(tokio::test)] +async fn debug_trace_call_js_tracer_with_db() -> anyhow::Result<()> { + let tester = Tester::setup().await?; + + let secondary_data = U256::from(7); + let calculate_value = U256::from(3); + let secondary_contract = + TracingSecondary::deploy(tester.l2_provider.clone(), secondary_data).await?; + let primary_contract = + TracingPrimary::deploy(tester.l2_provider.clone(), *secondary_contract.address()).await?; + + let mut call_request = primary_contract + .calculate(calculate_value) + .into_transaction_request(); + + let js_str = r#" + { + data: [], + write: function (log) { this.data.push([log.address, log.key, log.value]); }, + result: function(ctx, db) { let [address, key, value] = this.data[this.data.length-1]; return [db.getState(address, key), value]; } + }"#; + + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::JsTracer(js_str.to_string())); + call_request.max_priority_fee_per_gas = Some(1); + call_request.max_fee_per_gas = Some(u128::MAX); + call_request.set_from(tester.l2_wallet.default_signer().address()); + + let trace = tester + .l2_provider + .debug_trace_call(call_request, BlockId::latest(), opts) + .await?; + + let values = match trace { + GethTrace::JS(value) => value + .as_array() + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(|s| s.to_lowercase())) + .collect::>() + }) + .expect("tracer result missing addresses"), + other => panic!("expected JS trace result, got {other:?}"), + }; + + assert_eq!(values.len(), 2, "expected exactly two values from tracer"); + assert_eq!( + values[0], values[1], + "db.getState must return the same value as stored as a sanity check" + ); + let res = secondary_data * calculate_value; + assert_eq!( + format!("{res:#x}").to_lowercase(), + format!( + "{:#x}", + u128::from_str_radix(values[0].trim_start_matches("0x"), 16)? + ) + .to_lowercase(), + "stored value must match the expected one" + ); + + Ok(()) +} + +#[test_log::test(tokio::test)] +async fn debug_trace_call_stack() -> anyhow::Result<()> { + let tester = Tester::setup().await?; + + let secondary_data = U256::from(7); + let calculate_value = U256::from(3); + let secondary_contract = + TracingSecondary::deploy(tester.l2_provider.clone(), secondary_data).await?; + let primary_contract = + TracingPrimary::deploy(tester.l2_provider.clone(), *secondary_contract.address()).await?; + + let mut call_request = primary_contract + .calculate(calculate_value) + .into_transaction_request(); + + let js_str = r#" + { + setup: function () { + this.logs = []; + }, + + _bytesToHex: function (bytes) { + var s = "0x"; + for (var i = 0; i < bytes.length; i++) { + var h = bytes[i].toString(16); + if (h.length === 1) h = "0" + h; + s += h; + } + return s; + }, + + step: function (log, db) { + var op = log.op.toString(); + var topicCount = { LOG0:0, LOG1:1, LOG2:2, LOG3:3, LOG4:4 }[op]; + if (topicCount === undefined) return; + + let stackTop = log.stack.peek(0); + + this.logs.push({ + depth: log.getDepth(), + pc: log.getPC(), + data: stackTop.toString(16), + }); + }, + + result: function () { + return { type: "events", logs: this.logs }; + } + }"#; + + let mut opts = GethDebugTracingCallOptions::default(); + opts.tracing_options.tracer = Some(GethDebugTracerType::JsTracer(js_str.to_string())); + call_request.max_priority_fee_per_gas = Some(1); + call_request.max_fee_per_gas = Some(u128::MAX); + call_request.set_from(tester.l2_wallet.default_signer().address()); + + let trace = tester + .l2_provider + .debug_trace_call(call_request, BlockId::latest(), opts) + .await?; + + let val = match trace { + GethTrace::JS(value) => value + .as_object() + .expect("tracer result missing addresses") + .get("logs") + .expect("geth tracer result missing data") + .as_array() + .expect("tracer logs is not an array") + .first() + .expect("tracer logs is empty") + .as_object() + .expect("tracer log entry is not an object") + .get("data") + .expect("tracer log entry missing data") + .as_str() + .expect("tracer log data is not a string") + .to_string(), + other => panic!("expected JS trace result, got {other:?}"), + }; + + let res = secondary_data * calculate_value; + assert_eq!( + format!("{res:#x}").to_lowercase(), + format!( + "{:#x}", + u128::from_str_radix(val.trim_start_matches("0x"), 16)? + ) + .to_lowercase(), + "stored value must match the expected one" + ); + + Ok(()) +} diff --git a/lib/revm_consistency_checker/src/helpers.rs b/lib/revm_consistency_checker/src/helpers.rs index 14dca2fa3..314d63161 100644 --- a/lib/revm_consistency_checker/src/helpers.rs +++ b/lib/revm_consistency_checker/src/helpers.rs @@ -122,7 +122,7 @@ pub fn zk_tx_into_revm_tx( .gas_used_override(Some(gas_used)) .force_fail(!execution_status) .build() - .map_err(|e| anyhow::anyhow!("Failed to build TxEnv: {:?}", e)) + .map_err(|e| anyhow::anyhow!("Failed to build TxEnv: {e:?}")) .unwrap() } diff --git a/lib/rpc/Cargo.toml b/lib/rpc/Cargo.toml index 9f4dc7bf8..75c74e776 100644 --- a/lib/rpc/Cargo.toml +++ b/lib/rpc/Cargo.toml @@ -21,6 +21,7 @@ zk_os_basic_system.workspace = true zksync_os_evm_errors.workspace = true zksync_os_interface.workspace = true +zk_os_evm_interpreter.workspace = true zk_os_api.workspace = true zk_ee.workspace = true @@ -32,6 +33,7 @@ futures.workspace = true jsonrpsee = { workspace = true, default-features = false, features = ["macros", "client"] } ruint.workspace = true serde.workspace = true +serde_json.workspace = true thiserror.workspace = true tokio.workspace = true tokio-stream.workspace = true @@ -40,3 +42,6 @@ vise.workspace = true tower.workspace = true tower-http = { workspace = true, features = ["cors"] } hyper.workspace = true + +boa_engine.workspace = true +boa_gc.workspace = true diff --git a/lib/rpc/src/debug_impl.rs b/lib/rpc/src/debug_impl.rs index 9969128f8..6ea8e1a51 100644 --- a/lib/rpc/src/debug_impl.rs +++ b/lib/rpc/src/debug_impl.rs @@ -5,7 +5,7 @@ use alloy::eips::{BlockId, BlockNumberOrTag}; use alloy::genesis::ChainConfig; use alloy::primitives::{B256, BlockHash, Bytes, TxHash}; use alloy::rpc::types::trace::geth::{ - GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions, + CallConfig, GethDebugBuiltInTracerType, GethDebugTracerType, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult, }; use alloy::rpc::types::{Bundle, StateContext, TransactionRequest}; @@ -27,9 +27,7 @@ impl DebugNamespace { eth_call_handler, } } -} -impl DebugNamespace { fn debug_trace_block_by_id_impl( &self, block_id: BlockId, @@ -40,13 +38,7 @@ impl DebugNamespace { let Some(tracer) = opts.tracer else { return Err(DebugError::UnsupportedDefaultTracer); }; - if tracer != GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer) { - return Err(DebugError::UnsupportedTracer(tracer)); - } - let call_config = opts - .tracer_config - .into_call_config() - .map_err(|_| DebugError::InvalidTracerConfig)?; + let Some(block) = self.storage.get_block_by_id(block_id)? else { return Err(DebugError::BlockNotFound); }; @@ -77,18 +69,45 @@ impl DebugNamespace { txs.push(tx); } let prev_state_view = self.storage.state_view_at(block.number - 1)?; - match sandbox::call_trace(txs, block_context, prev_state_view, call_config) { - Ok(calls) => Ok(calls - .into_iter() - .zip(&block.body.transactions) - .map(|(call, tx_hash)| { - TraceResult::new_success(GethTrace::CallTracer(call), Some(*tx_hash)) - }) - .collect()), - Err(err) => { - tracing::error!(?err, "Failed to trace transaction"); - Err(DebugError::InternalError) + match tracer { + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer) => { + let call_config = opts + .tracer_config + .clone() + .into_call_config() + .map_err(|_| DebugError::InvalidTracerConfig)?; + + match sandbox::call_trace(txs, block_context, prev_state_view, call_config) { + Ok(calls) => Ok(calls + .into_iter() + .zip(&block.body.transactions) + .map(|(call, tx_hash)| { + TraceResult::new_success(GethTrace::CallTracer(call), Some(*tx_hash)) + }) + .collect()), + Err(err) => { + tracing::error!(?err, "Failed to trace transaction"); + Err(DebugError::InternalError) + } + } } + GethDebugTracerType::JsTracer(js) => { + match crate::js_tracer::tracer::trace_block(txs, block_context, prev_state_view, js) + { + Ok(outputs) => Ok(outputs + .into_iter() + .zip(&block.body.transactions) + .map(|(value, tx_hash)| { + TraceResult::new_success(GethTrace::JS(value), Some(*tx_hash)) + }) + .collect()), + Err(err) => { + tracing::error!(?err, "Failed to trace transaction with JS tracer"); + Err(DebugError::InternalError) + } + } + } + other => Err(DebugError::UnsupportedTracer(other)), } } @@ -141,20 +160,37 @@ impl DebugNamespace { let Some(tracer) = tracing_options.tracer else { return Err(DebugError::UnsupportedDefaultTracer); }; - if tracer != GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer) { - return Err(DebugError::UnsupportedTracer(tracer)); + match (tracer, state_overrides, block_overrides) { + ( + GethDebugTracerType::BuiltInTracer(GethDebugBuiltInTracerType::CallTracer), + state_overrides, + block_overrides, + ) => { + let call_config: CallConfig = tracing_options + .tracer_config + .into_call_config() + .map_err(|_| DebugError::InvalidTracerConfig)?; + Ok(self.eth_call_handler.call_trace_impl( + request, + block_id, + call_config, + state_overrides, + block_overrides.map(Box::new), + )?) + } + (GethDebugTracerType::JsTracer(js_cfg), state_overrides, block_overrides) => { + let value = self.eth_call_handler.call_js_tracer_impl( + request, + block_id, + js_cfg, + state_overrides, + block_overrides.map(Box::new), + )?; + + Ok(GethTrace::JS(value)) + } + (other, ..) => Err(DebugError::UnsupportedTracer(other)), } - let call_config = tracing_options - .tracer_config - .into_call_config() - .map_err(|_| DebugError::InvalidTracerConfig)?; - Ok(self.eth_call_handler.call_trace_impl( - request, - block_id, - call_config, - state_overrides, - block_overrides.map(Box::new), - )?) } } diff --git a/lib/rpc/src/eth_call_handler.rs b/lib/rpc/src/eth_call_handler.rs index 64f0e0cc3..849bd3a51 100644 --- a/lib/rpc/src/eth_call_handler.rs +++ b/lib/rpc/src/eth_call_handler.rs @@ -1,5 +1,6 @@ use crate::call_fees::{CallFees, CallFeesError}; use crate::config::RpcConfig; +use crate::js_tracer; use crate::result::RevertError; use crate::rpc_storage::ReadRpcStorage; use crate::sandbox::{call_trace_simulate, execute}; @@ -11,6 +12,7 @@ use alloy::primitives::{Address, B256, Bytes, Signature, TxKind, U256}; use alloy::rpc::types::state::StateOverride; use alloy::rpc::types::trace::geth::{CallConfig, GethTrace}; use alloy::rpc::types::{BlockOverrides, TransactionRequest}; +use serde_json::Value as JsonValue; use tokio::sync::watch; use zk_os_api::helpers::{get_balance, get_nonce}; use zksync_os_interface::types::ExecutionOutput; @@ -22,6 +24,7 @@ use zksync_os_storage_api::ViewState; use zksync_os_storage_api::{ RepositoryError, StateError, state_override_view::OverriddenStateView, }; +use zksync_os_types::ZksyncOsEncode; use zksync_os_types::{ L1_TX_MINIMAL_GAS_LIMIT, L1Envelope, L1PriorityTxType, L1Tx, L1TxType, L2Envelope, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE, UpgradeTxType, ZkEnvelope, ZkTransaction, ZkTxType, @@ -292,6 +295,62 @@ impl EthCallHandler { .map_err(|err| EthCallError::ForwardSubsystemError(anyhow::anyhow!(err))) } + pub fn call_js_tracer_impl( + &self, + request: TransactionRequest, + block: Option, + js_cfg: String, + state_overrides: Option, + block_overrides: Option>, + ) -> Result { + let execution_env = self.prepare_execution_env(request, block, block_overrides)?; + let storage_view = self + .storage + .state_view_at(execution_env.block_context.block_number)?; + + let mut tracer_output = match state_overrides { + Some(overrides) => { + let view = OverriddenStateView::new(storage_view, overrides); + let mut tracer = js_tracer::tracer::JsTracer::new(view.clone(), js_cfg) + .map_err(|e| EthCallError::ForwardSubsystemError(anyhow::anyhow!(e)))?; + + zksync_os_multivm::simulate_tx( + execution_env.transaction.encode(), + execution_env.block_context, + view.clone(), + view, + &mut tracer, + ) + .map_err(|e| EthCallError::ForwardSubsystemError(anyhow::anyhow!(e))) + .and_then(|inner| inner.map_err(EthCallError::InvalidTransaction))?; + + tracer + } + None => { + let mut tracer = js_tracer::tracer::JsTracer::new(storage_view.clone(), js_cfg) + .map_err(|e| EthCallError::ForwardSubsystemError(anyhow::anyhow!(e)))?; + + zksync_os_multivm::simulate_tx( + execution_env.transaction.encode(), + execution_env.block_context, + storage_view.clone(), + storage_view, + &mut tracer, + ) + .map_err(|e| EthCallError::ForwardSubsystemError(anyhow::anyhow!(e))) + .and_then(|inner| inner.map_err(EthCallError::InvalidTransaction))?; + + tracer + } + }; + + if let Some(err) = tracer_output.take_error() { + return Err(EthCallError::CallTracerError(err)); + } + + Ok(tracer_output.results.pop().unwrap_or(JsonValue::Null)) + } + pub fn estimate_gas_impl( &self, request: TransactionRequest, @@ -633,4 +692,8 @@ pub enum EthCallError { Repository(#[from] RepositoryError), #[error(transparent)] State(#[from] StateError), + + /// Error occurred during debug tracing + #[error("Tracer error: {0:?}")] + CallTracerError(anyhow::Error), } diff --git a/lib/rpc/src/js_tracer/host.rs b/lib/rpc/src/js_tracer/host.rs new file mode 100644 index 000000000..cf157046a --- /dev/null +++ b/lib/rpc/src/js_tracer/host.rs @@ -0,0 +1,383 @@ +use crate::js_tracer; +use crate::js_tracer::types::{BalanceOverlay, CodeOverlay, StorageOverlay}; +use crate::js_tracer::utils::{format_hex_u256, parse_address, parse_b256}; +use alloy::primitives::{Address, B256, U256}; +use boa_engine::object::FunctionObjectBuilder; +use boa_engine::{ + Context as BoaContext, Context, JsArgs, JsValue, NativeFunction, Source, js_string, +}; +use boa_gc::{Finalize, Trace}; +use js_tracer::utils::anyhow_error_to_js_error; +use ruint::aliases::B160; +use serde_json::Value as JsonValue; +use std::{cell::RefCell, rc::Rc}; +use zk_ee::common_structs::derive_flat_storage_key; +use zk_os_api::helpers::get_code; +use zksync_os_storage_api::ViewState; + +#[derive(Trace, Finalize)] +struct HostEnvironment { + #[unsafe_ignore_trace] + state_view: RefCell, + #[unsafe_ignore_trace] + storage_overlay: Rc>, + #[unsafe_ignore_trace] + code_overlay: Rc>, + #[unsafe_ignore_trace] + balance_overlay: Rc>, +} + +#[allow(clippy::enum_variant_names)] +enum HostMethod { + GetBalance, + GetNonce, + GetCode, + GetState, + Exists, +} + +impl HostMethod { + fn parse(value: &str) -> Option { + match value { + "getBalance" => Some(Self::GetBalance), + "getNonce" => Some(Self::GetNonce), + "getCode" => Some(Self::GetCode), + "getState" => Some(Self::GetState), + "exists" => Some(Self::Exists), + _ => None, + } + } +} + +pub(crate) fn init_host_env_in_boa_context( + ctx: &mut Context, + tracer_source: &str, + state_view: RefCell, + storage_overlay: Rc>, + code_overlay: Rc>, + balance_overlay: Rc>, +) -> anyhow::Result<()> { + bootstrap_tracer(ctx, tracer_source)?; + + let host_env = HostEnvironment { + state_view, + storage_overlay, + code_overlay, + balance_overlay, + }; + + install_host_bindings(ctx, host_env)?; + install_db_wrapper(ctx)?; + install_step_helpers(ctx)?; + + Ok(()) +} + +// A wrapper around the tracer JS code to bootstrap it in the Boa context. +fn bootstrap_tracer(ctx: &mut BoaContext, tracer_source: &str) -> anyhow::Result<()> { + let bootstrap = format!( + "var tracer=(function(){{\n + tracer={tracer_source};\n + if (typeof tracer === 'object' && tracer) return tracer;\n + if (typeof exports === 'object' && exports) {{\n + var candidate = exports.tracer || exports.default || exports;\n + if (typeof candidate === 'object' && candidate) return candidate;\n + }}\n + return undefined;}})();", + ); + + ctx.eval(Source::from_bytes(bootstrap.as_bytes())) + .map_err(|e| anyhow::anyhow!(format!("JS tracer bootstrap error: {e:?}")))?; + + Ok(()) +} + +fn install_host_bindings( + ctx: &mut BoaContext, + env: HostEnvironment, +) -> anyhow::Result<()> { + let host = FunctionObjectBuilder::new( + ctx.realm(), + NativeFunction::from_copy_closure_with_captures( + |_this, args, env, ctx| { + let method_name = args + .get_or_undefined(0) + .to_string(ctx)? + .to_std_string_escaped(); + + let payload_raw = args + .get_or_undefined(1) + .to_string(ctx)? + .to_std_string_escaped(); + + let payload: JsonValue = serde_json::from_str(&payload_raw) + .map_err(|err| anyhow_error_to_js_error(anyhow::anyhow!(err)))?; + + let Some(method) = HostMethod::parse(&method_name) else { + return Ok(JsValue::from(js_string!("null"))); + }; + + let response = + dispatch_host_call(env, method, &payload).map_err(anyhow_error_to_js_error)?; + + Ok(JsValue::from(js_string!(response))) + }, + env, + ), + ) + .name(js_string!("__hostCall")) + .length(2) + .build(); + + ctx.global_object() + .set(js_string!("__hostCall"), host, false, ctx) + .map_err(|e| anyhow::anyhow!(format!("install __hostCall failed: {e:?}")))?; + + Ok(()) +} + +fn install_db_wrapper(ctx: &mut BoaContext) -> anyhow::Result<()> { + let js_db_wrapper = r#" + var db = { + getBalance: function(a){ return __hostCall("getBalance", JSON.stringify({address: a})); }, + getNonce: function(a){ return __hostCall("getNonce", JSON.stringify({address: a})); }, + getCode: function(a){ return __hostCall("getCode", JSON.stringify({address: a})); }, + getState: function(a,s){ return __hostCall("getState", JSON.stringify({address: a, slot: s})); }, + exists: function(a){ return __hostCall("exists", JSON.stringify({address: a})) === 'true'; } + }; + "#; + + ctx.eval(Source::from_bytes(js_db_wrapper.as_bytes())) + .map_err(|e| anyhow::anyhow!(format!("install db wrapper failed: {e:?}")))?; + + Ok(()) +} + +fn install_step_helpers(ctx: &mut BoaContext) -> anyhow::Result<()> { + let helpers = r#" + function normalizeHex(value){ + if (typeof value !== 'string') { + return '0x'; + } + if (value === '' || value === '0x' || value === '0X') { + return '0x'; + } + if (value.startsWith('0x') || value.startsWith('0X')) { + return '0x' + value.slice(2).toLowerCase(); + } + return '0x' + value.toLowerCase(); + } + + function hexToBytes(value){ + const hex = normalizeHex(value).slice(2); + if (hex.length === 0) { + return new Uint8Array(0); + } + const padded = hex.length % 2 === 0 ? hex : '0' + hex; + const out = new Uint8Array(padded.length / 2); + for (let i = 0; i < padded.length; i += 2) { + out[i >> 1] = parseInt(padded.slice(i, i + 2), 16); + } + return out; + } + "#; + + ctx.eval(Source::from_bytes(helpers.as_bytes())) + .map_err(|e| anyhow::anyhow!(format!("install step helpers failed: {e:?}")))?; + + Ok(()) +} + +fn dispatch_host_call( + env: &HostEnvironment, + method: HostMethod, + payload: &JsonValue, +) -> anyhow::Result { + match method { + HostMethod::GetBalance => host_get_balance(env, payload), + HostMethod::GetNonce => host_get_nonce(env, payload), + HostMethod::GetCode => host_get_code(env, payload), + HostMethod::GetState => host_get_state(env, payload), + HostMethod::Exists => host_exists(env, payload), + } +} + +fn host_get_balance( + env: &HostEnvironment, + payload: &JsonValue, +) -> anyhow::Result { + let addr = payload + .get("address") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + + let Some(address) = parse_address(addr) else { + return Ok("0x0".to_string()); + }; + + let balance_value = resolve_balance(env, address)?; + + Ok(format_hex_u256(balance_value)) +} + +fn host_get_nonce( + env: &HostEnvironment, + payload: &JsonValue, +) -> anyhow::Result { + let addr = payload + .get("address") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + + let Some(address) = parse_address(addr) else { + return Ok("0x0".to_string()); + }; + + let nonce = env + .state_view + .borrow_mut() + .account_nonce(address) + .ok_or(anyhow::anyhow!("Account {address:?} not found in a state"))?; + + Ok(format!("0x{nonce:x}")) +} + +fn host_get_code( + env: &HostEnvironment, + payload: &JsonValue, +) -> anyhow::Result { + let addr = payload + .get("address") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + + let Some(address) = parse_address(addr) else { + return Ok("0x".to_string()); + }; + + if let Some(entry) = env.code_overlay.borrow().get(&address) { + return if let Some(code) = &entry.value { + Ok(format!("0x{}", alloy::primitives::hex::encode(code))) + } else { + Ok("0x".to_string()) + }; + } + + let code = { + let mut state_view = env.state_view.borrow_mut(); + let props = state_view + .get_account(address) + .ok_or(anyhow::anyhow!("Account {address:?} not found in a state"))?; + get_code(&mut *state_view, &props) + }; + + Ok(format!("0x{}", alloy::primitives::hex::encode(code))) +} + +fn host_get_state( + env: &HostEnvironment, + payload: &JsonValue, +) -> anyhow::Result { + let addr = payload + .get("address") + .and_then(|v| v.as_str()) + .ok_or_else(|| anyhow::anyhow!("address is not supplied in getState"))?; + let slot = payload + .get("slot") + .and_then(|v| v.as_str()) + .ok_or_else(|| anyhow::anyhow!("slot is not supplied in getState"))?; + + let (Some(address), Some(key)) = (parse_address(addr), parse_b256(slot)) else { + return Ok("0x0".to_string()); + }; + + if let Some(entry) = env.storage_overlay.borrow().get(&(address, key)) { + return Ok(format!( + "0x{}", + alloy::primitives::hex::encode(entry.value.0) + )); + } + + let flat = derive_flat_storage_key(&B160::from_be_bytes(address.into_array()), &(key.0.into())); + let value = env + .state_view + .borrow_mut() + .read(B256::from(flat.as_u8_array())) + .unwrap_or_default(); + + Ok(format!("0x{}", alloy::primitives::hex::encode(value.0))) +} + +fn host_exists( + env: &HostEnvironment, + payload: &JsonValue, +) -> anyhow::Result { + let addr = payload + .get("address") + .and_then(|v| v.as_str()) + .unwrap_or_default(); + + let Some(address) = parse_address(addr) else { + return Ok("false".to_string()); + }; + + if let Some(entry) = env.code_overlay.borrow().get(&address) { + return Ok(entry.value.is_some().to_string()); + } + + if resolve_balance(env, address)? > U256::ZERO { + return Ok("true".to_string()); + } + + if env + .storage_overlay + .borrow() + .keys() + .any(|(addr_ref, _)| *addr_ref == address) + { + return Ok("true".to_string()); + } + + let exists = env.state_view.borrow_mut().get_account(address).is_some(); + + Ok(exists.to_string()) +} + +fn resolve_balance( + env: &HostEnvironment, + address: Address, +) -> anyhow::Result { + let delta = { + let balance_overlay = env.balance_overlay.borrow(); + balance_overlay + .get(&address) + .map(|entry| entry.value.clone()) + }; + + let mut balance = env + .state_view + .borrow_mut() + .get_account(address) + .map(|props| props.balance) + .unwrap_or_default(); + + if let Some(delta) = delta { + let (with_add, overflow) = balance.overflowing_add(delta.added); + if overflow { + anyhow::bail!("balance overflow when applying balance delta for account {address:?}"); + } + + balance = with_add; + if delta.removed != U256::ZERO { + let (with_sub, overflow) = balance.overflowing_sub(delta.removed); + if overflow { + anyhow::bail!( + "balance underflow when applying balance delta for account {address:?}" + ); + } + balance = with_sub; + } + } + + Ok(balance) +} diff --git a/lib/rpc/src/js_tracer/mod.rs b/lib/rpc/src/js_tracer/mod.rs new file mode 100644 index 000000000..320722448 --- /dev/null +++ b/lib/rpc/src/js_tracer/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod host; +pub mod tracer; +pub(crate) mod types; +pub(crate) mod utils; diff --git a/lib/rpc/src/js_tracer/tracer.rs b/lib/rpc/src/js_tracer/tracer.rs new file mode 100644 index 000000000..987676ef5 --- /dev/null +++ b/lib/rpc/src/js_tracer/tracer.rs @@ -0,0 +1,1034 @@ +use crate::js_tracer::types::SelfdestructEntry; +use crate::js_tracer::{ + host::init_host_env_in_boa_context, + types::{ + BalanceDelta, CreateType, FrameState, OverlayCheckpoint, OverlayEntry, OverlayState, + StepCtx, TracerMethod, TxContext, + }, + utils::{extract_js_source_and_config, gas_used_from_resources, wrap_js_invocation}, +}; +use crate::sandbox::{ERGS_PER_GAS, fmt_error_msg, maybe_revert_reason}; +use alloy::hex::ToHexExt; +use alloy::primitives::{Address, B256, Bytes, U256}; +use boa_engine::{Context as BoaContext, Source}; +use serde_json::Value as JsonValue; +use std::ops::Not; +use std::{cell::RefCell, collections::hash_map::Entry}; +use zksync_os_evm_errors::EvmError; +use zksync_os_interface::tracing::{ + AnyTracer, CallModifier, CallResult, EvmFrameInterface, EvmRequest, EvmResources, + EvmStackInterface, EvmTracer, +}; +use zksync_os_storage_api::ViewState; +use zksync_os_types::{ZkTransaction, ZksyncOsEncode}; + +const MAX_JS_TRACER_PAYLOAD_BYTES: usize = 512 * 1024; + +/// JS tracer implementation +/// Holds a Boa JS runtime and calls user-provided JS tracer methods when the hooks of zksync-os +/// EVM tracer interface are invoked. +/// Since zksync-os interfaces don't provide state access - we use the state before the execution of +/// each transaction and maintain overlays for storage and code modifications done during the tx. +/// +/// Tracer methods supported: +/// - setup(config): called once at the beginning of the transaction with the tracer config +/// - enter(frame): called on entering a new execution frame +/// - exit(result): called on exiting an execution frame +/// - step(log, db): called after each EVM opcode execution step +/// - fault(log, db): called on EVM opcode error +/// - result(ctx, db): called at the end of the transaction to get the final result +/// - write(modification): called on each storage write (extension beyond geth tracer) +/// +/// The JS tracer can use the `db` object to query the state via the following interface: +/// - getBalance(address): returns balance of an address +/// - getNonce(address): returns the nonce as hex string +/// - getCode(address): returns code at address +/// - getState(address, slot): returns storage value at slot +/// - exists(address): returns true if the address exists in the state or overlays +/// +/// Known divergences from geth tracer interface: +/// - `ctx.gasPrice ` is not provided in result() +/// +pub struct JsTracer { + // JS runtime + ctx: BoaContext, + // User-provided tracer config + tracer_config: JsonValue, + + // Overlays for storage and code modifications + storage_overlay: OverlayState<(Address, B256), B256>, + code_overlay: OverlayState>>, + balance_overlay: OverlayState, + selfdestruct_overlay: OverlayState, + + // Depth tracking and per-tx result + current_depth: u64, + pub(crate) results: Vec, + pending_step: Option, + pending_create_type: Option, + + frame_stack: Vec, + last_finished_frame: Option, + tx_failed: bool, + + error: Option, +} + +impl JsTracer { + pub fn new(state_view: impl ViewState + 'static, js_cfg: String) -> anyhow::Result { + if js_cfg.len() > MAX_JS_TRACER_PAYLOAD_BYTES { + return Err(anyhow::anyhow!(format!( + "JS tracer payload exceeds limit of {} bytes", + MAX_JS_TRACER_PAYLOAD_BYTES + ))); + } + + let (tracer_source, tracer_config) = extract_js_source_and_config(js_cfg)?; + + let mut ctx = BoaContext::default(); + + let storage_overlay = OverlayState::<(Address, B256), B256>::new(); + let code_overlay = OverlayState::>>::new(); + let balance_overlay = OverlayState::::new(); + let selfdestruct_overlay = OverlayState::::new(); + + init_host_env_in_boa_context( + &mut ctx, + &tracer_source, + RefCell::new(state_view.clone()), + storage_overlay.handle(), + code_overlay.handle(), + balance_overlay.handle(), + )?; + + Ok(Self { + ctx, + tracer_config, + storage_overlay, + code_overlay, + balance_overlay, + selfdestruct_overlay, + current_depth: 0, + results: Vec::new(), + pending_step: None, + pending_create_type: None, + error: None, + frame_stack: Vec::new(), + last_finished_frame: None, + tx_failed: false, + }) + } + + /// `call_method` invokes a method on the JS tracer object with the given argument. + fn call_method( + &mut self, + method: TracerMethod, + arg: &JsonValue, + with_db: bool, + ) -> anyhow::Result<()> { + if !self.method_exists(method)? { + return Ok(()); + } + + let method_name = method.as_str(); + let mut arg_json = serde_json::to_string(arg).unwrap_or("null".to_string()); + if with_db { + arg_json = format!("{arg_json}, db"); + } + let snippet = wrap_js_invocation(format!("tracer.{method_name}({arg_json});")); + + let _ = self + .ctx + .eval(Source::from_bytes(snippet.as_bytes())) + .map_err(|e| { + anyhow::anyhow!(format!("JS tracer method {method_name} failed: {e:?}")) + })?; + + Ok(()) + } + + fn method_exists(&mut self, method: TracerMethod) -> anyhow::Result { + let method_name = method.as_str(); + Ok(self + .ctx + .eval(Source::from_bytes( + format!( + "(function(){{ return typeof tracer === 'object' && typeof tracer.{method_name} === 'function' }})()" + ) + .as_bytes(), + )) + .map_err(|e| { + anyhow::anyhow!(format!( + "JS tracer method existence check failed: {e:?}" + )) + })? + .to_boolean()) + } + + fn commit_overlays(&self) { + self.storage_overlay.commit(); + self.code_overlay.commit(); + self.balance_overlay.commit(); + self.selfdestruct_overlay.commit(); + } + + fn rollback_overlays(&self) { + self.storage_overlay.rollback(); + self.code_overlay.rollback(); + self.balance_overlay.rollback(); + self.selfdestruct_overlay.rollback(); + } + + fn current_overlay_checkpoint(&self) -> OverlayCheckpoint { + OverlayCheckpoint { + storage: self.storage_overlay.checkpoint(), + code: self.code_overlay.checkpoint(), + balance: self.balance_overlay.checkpoint(), + selfdestruct: self.selfdestruct_overlay.checkpoint(), + } + } + + fn clear_overlay_journals(&self) { + self.storage_overlay.clear_journal(); + self.code_overlay.clear_journal(); + self.balance_overlay.clear_journal(); + self.selfdestruct_overlay.clear_journal(); + } + + fn revert_overlays_to_checkpoint(&self, checkpoint: OverlayCheckpoint) { + self.storage_overlay + .revert_to_checkpoint(checkpoint.storage); + self.code_overlay.revert_to_checkpoint(checkpoint.code); + self.balance_overlay + .revert_to_checkpoint(checkpoint.balance); + self.selfdestruct_overlay + .revert_to_checkpoint(checkpoint.selfdestruct); + } + + fn mark_contract_deployed(&self, address: Address) { + if address == Address::ZERO { + return; + } + + let mut overlay = self.selfdestruct_overlay.borrow_mut(); + match overlay.entry(address) { + Entry::Occupied(mut occupied) => { + let before = occupied.get().clone(); + self.selfdestruct_overlay.record_update(address, before); + occupied.get_mut().value.is_deployed_in_current_tx = true; + } + Entry::Vacant(vacant) => { + self.selfdestruct_overlay.record_insert(address); + vacant.insert(OverlayEntry::new_pending(SelfdestructEntry { + is_deployed_in_current_tx: true, + is_marked_for_selfdestruct: false, + })); + } + } + } + + fn apply_pending_selfdestructs(&mut self) { + let entries: Vec<(Address, bool)> = self + .selfdestruct_overlay + .handle() + .borrow() + .iter() + .map(|(address, entry)| { + ( + *address, + entry.value.is_marked_for_selfdestruct && entry.value.is_deployed_in_current_tx, + ) + }) + .collect(); + + for (address, should_destroy) in entries { + if should_destroy { + let keys: Vec<_> = self + .storage_overlay + .handle() + .borrow() + .keys() + .filter(|(addr, _)| *addr == address) + .cloned() + .collect(); + let mut storage_overlay = self.storage_overlay.borrow_mut(); + for key in keys { + storage_overlay.remove(&key); + } + + self.code_overlay.borrow_mut().remove(&address); + self.balance_overlay.borrow_mut().remove(&address); + } + + self.selfdestruct_overlay.borrow_mut().remove(&address); + } + } + + fn call_enter(&mut self, call_frame: &JsonValue) -> anyhow::Result<()> { + if !self.method_exists(TracerMethod::Enter)? { + return Ok(()); + } + + let raw_frame_input = serde_json::to_string(call_frame).map_err(|e| { + anyhow::anyhow!(format!("JS tracer log input serialization failed: {e:?}")) + })?; + + let method_name = TracerMethod::Enter.as_str(); + let body = format!( + r#" + let raw = {raw_frame_input}; + let frame = {{ + getType() {{ return raw.type; }}, + getFrom() {{ return raw.from; }}, + getTo() {{ return raw.to; }}, + getInput() {{ return hexToBytes(raw.input); }}, + getGas() {{ return raw.gas; }}, + getValue() {{ return raw.value; }}, + }}; + tracer.{method_name}(frame); + "# + ); + + let _ = self + .ctx + .eval(Source::from_bytes(wrap_js_invocation(body).as_bytes())) + .map_err(|e| { + anyhow::anyhow!(format!("JS tracer method {method_name} failed: {e:?}")) + })?; + + Ok(()) + } + + fn call_exit(&mut self, call_frame: &JsonValue) -> anyhow::Result<()> { + if !self.method_exists(TracerMethod::Exit)? { + return Ok(()); + } + + let raw_frame_input = serde_json::to_string(call_frame).map_err(|e| { + anyhow::anyhow!(format!("JS tracer log input serialization failed: {e:?}")) + })?; + + let method_name = TracerMethod::Exit.as_str(); + let body = format!( + r#" + let raw = {raw_frame_input}; + let frame = {{ + getGasUsed() {{ return raw.gasUsed; }}, + getOutput() {{ return raw.output ? hexToBytes(raw.output) : null; }}, + getError() {{ return raw.error; }}, + }}; + tracer.{method_name}(frame); + "# + ); + + let _ = self + .ctx + .eval(Source::from_bytes(wrap_js_invocation(body).as_bytes())) + .map_err(|e| { + anyhow::anyhow!(format!("JS tracer method {method_name} failed: {e:?}")) + })?; + + Ok(()) + } + + fn call_step_or_fault( + &mut self, + method: TracerMethod, + raw_log: &JsonValue, + ) -> anyhow::Result<()> { + if !self.method_exists(method)? { + return Ok(()); + } + + let raw_log_input = serde_json::to_string(raw_log).map_err(|e| { + anyhow::anyhow!(format!("JS tracer log input serialization failed: {e:?}")) + })?; + let method_name = method.as_str(); + + let has_error = raw_log + .as_object() + .and_then(|obj| obj.get("error")) + .unwrap_or(&JsonValue::Null) + .is_null() + .not(); + + let snippet = if has_error { + format!( + r#" + let raw = {raw_log_input}; + let log = {{ + getError() {{ return raw.error; }}, + getDepth() {{ return raw.depth; }}, + }}; + tracer.{method_name}(log, db); + "# + ) + } else { + format!( + r#" + let raw = {raw_log_input}; + let op = {{ + toString() {{ return raw.op.name; }}, + toNumber() {{ return raw.op.code; }}, + isPush() {{ return raw.op.isPush; }}, + }}; + let memory = {{ + __buffer: hexToBytes(raw.memory), + slice(start, stop) {{ + const from = start >>> 0; + const to = stop === undefined ? this.__buffer.length : stop >>> 0; + return this.__buffer.slice(from, to); + }}, + getUint(offset) {{ + const from = offset >>> 0; + const end = from + 32; + const out = new Uint8Array(32); + const available = this.__buffer.slice(from, end); + out.set(available, 0); + return out; + }}, + length() {{ + return this.__buffer.length; + }}, + }}; + let contract = {{ + __input: hexToBytes(raw.contract.input), + getCaller() {{ return raw.contract.caller; }}, + getAddress() {{ return raw.contract.address; }}, + getValue() {{ return raw.contract.value; }}, + getInput() {{ return this.__input.slice(); }}, + }}; + let stack = {{ + __entries: raw.stack, + length() {{ return this.__entries.length; }}, + peek(n) {{ return this.__entries[n]; }}, + }}; + let log = {{ + op, + memory, + contract, + stack, + getPC() {{ return raw.pc; }}, + getGas() {{ return raw.gas; }}, + getCost() {{ return raw.cost }}, + getDepth() {{ return raw.depth; }}, + getRefund() {{ return raw.refund; }}, + getError() {{ return raw.error; }}, + }}; + tracer.{method_name}(log, db); + "# + ) + }; + + let _ = self + .ctx + .eval(Source::from_bytes(wrap_js_invocation(snippet).as_bytes())) + .map_err(|e| { + anyhow::anyhow!(format!("JS tracer method {method_name} failed: {e:?}")) + })?; + + Ok(()) + } + + fn invoke_method(&mut self, method: TracerMethod, arg: &JsonValue) { + if self.error.is_some() { + return; + } + + if let Err(err) = match method { + TracerMethod::Step | TracerMethod::Fault => self.call_step_or_fault(method, arg), + TracerMethod::Setup | TracerMethod::Write => self.call_method(method, arg, false), + TracerMethod::Enter => self.call_enter(arg), + TracerMethod::Exit => self.call_exit(arg), + TracerMethod::Result => self.call_method(method, arg, true), + TracerMethod::StorageRead => Err(anyhow::anyhow!( + "Storage read is not supported by JS tracer" + )), + } { + self.record_error(method, err); + } + } + + fn record_error(&mut self, method: TracerMethod, err: anyhow::Error) { + if self.error.is_none() { + let method_name = method.as_str(); + tracing::debug!( + ?err, + method = method_name, + "JS tracer execution halted due to error" + ); + self.error = Some(err); + } + } + + pub(crate) fn take_error(&mut self) -> Option { + self.error.take() + } + + /// `call_result` is called at the end of the transaction to get the final result from the tracer. + fn call_result(&mut self, ctx: &TxContext) -> anyhow::Result { + let ctx = serde_json::json!({ + "type": ctx.typ, + "from": ctx.from, + "to": ctx.to, + "input": ctx.input, + "gas": ctx.gas, + "value": match ctx.value { + v if v == U256::ZERO => JsonValue::Null, + v => serde_json::to_value(v).unwrap_or(JsonValue::Null), + }, + "gasUsed": ctx.gas_used, + "output": ctx.output, + "error": ctx.error, + }); + + let method_name = TracerMethod::Result.as_str(); + let snippet = wrap_js_invocation(format!( + "return JSON.stringify(tracer.{method_name}({ctx}, db));" + )); + let value = self + .ctx + .eval(Source::from_bytes(snippet.as_bytes())) + .map_err(|e| { + anyhow::anyhow!(format!("JS tracer method {method_name} failed: {e:?}")) + })?; + + let out = value + .to_string(&mut self.ctx) + .map_err(|e| anyhow::anyhow!(format!("JS value to string error: {e:?}")))? + .to_std_string_escaped(); + + Ok(serde_json::from_str::(&out).unwrap_or(JsonValue::Null)) + } + + fn consume_call_type(&mut self, modifier: CallModifier) -> String { + let typ = match modifier { + CallModifier::NoModifier => "CALL".to_string(), + CallModifier::Constructor => match self + .pending_create_type + .take() + .unwrap_or(CreateType::Create) + { + CreateType::Create => "CREATE".to_string(), + CreateType::Create2 => "CREATE2".to_string(), + }, + CallModifier::Delegate | CallModifier::DelegateStatic => "DELEGATECALL".to_string(), + CallModifier::Static => "STATICCALL".to_string(), + CallModifier::EVMCallcode | CallModifier::EVMCallcodeStatic => "CALLCODE".to_string(), + CallModifier::ZKVMSystem | CallModifier::ZKVMSystemStatic => { + panic!("unexpected call type: {modifier:?}") + } + }; + + if self.pending_create_type.is_some() { + self.pending_create_type = None; + } + + typ + } + + fn prepare_log_input( + &mut self, + step_ctx: StepCtx, + frame_state: &impl EvmFrameInterface, + error: Option, + ) -> serde_json::Value { + let gas_after = frame_state.resources().ergs / ERGS_PER_GAS; + let cost = step_ctx.gas_before.saturating_sub(gas_after); + + let memory_bytes = Bytes::copy_from_slice(frame_state.heap()); + let contract_input = Bytes::copy_from_slice(frame_state.calldata()); + let contract_value = format!("{:#x}", frame_state.call_value()); + + let opcode_name = zk_os_evm_interpreter::opcodes::OPCODE_JUMPMAP[step_ctx.opcode as usize] + .unwrap_or("Invalid opcode"); + let is_push = opcode_name.starts_with("PUSH"); + + let stack = frame_state.stack(); + let mut stack_dump = Vec::with_capacity(stack.len()); + for idx in 0..stack.len() { + match stack.peek_n(idx) { + Ok(value) => stack_dump.push(format!("{value:066x}")), + Err(err) => { + tracing::error!(?err, "Failed to read stack entry for JS tracer log"); + break; + } + } + } + serde_json::json!({ + "op": { + "name": opcode_name, + "code": step_ctx.opcode, + "isPush": is_push, + }, + "memory": memory_bytes, + "contract": { + "caller": frame_state.caller(), + "address": frame_state.address(), + "value": contract_value, + "input": contract_input, + }, + "pc": step_ctx.pc, + "gas": step_ctx.gas_before, + "cost": cost, + "stack": stack_dump, + "depth": step_ctx.depth, + "refund": frame_state.refund_counter(), + "error": error, + }) + } + + fn apply_balance_delta( + &mut self, + address: Address, + credit: U256, + debit: U256, + ) -> anyhow::Result<()> { + if credit == U256::ZERO && debit == U256::ZERO { + return Ok(()); + } + + let mut overlay = self.balance_overlay.borrow_mut(); + match overlay.entry(address) { + Entry::Occupied(mut occupied) => { + let before = occupied.get().clone(); + self.balance_overlay.record_update(address, before); + let entry = occupied.get_mut(); + if entry.committed && entry.previous.is_none() { + entry.previous = Some(entry.value.clone()); + } + entry.value.credit(credit)?; + entry.value.debit(debit)?; + entry.committed = false; + + if entry.value.is_empty() && entry.previous.is_none() { + occupied.remove_entry(); + } + } + Entry::Vacant(vacant) => { + let mut delta = BalanceDelta::default(); + delta.credit(credit)?; + delta.debit(debit)?; + if !delta.is_empty() { + self.balance_overlay.record_insert(address); + vacant.insert(OverlayEntry::new_pending(delta)); + } + } + } + + Ok(()) + } +} + +impl AnyTracer for JsTracer { + fn as_evm(&mut self) -> Option<&mut impl EvmTracer> { + Some(self) + } +} + +impl EvmTracer for JsTracer { + fn on_new_execution_frame(&mut self, request: impl EvmRequest) { + let checkpoint = self.current_overlay_checkpoint(); + + let call_value = request.nominal_token_value(); + if call_value != U256::ZERO { + if let Err(err) = self.apply_balance_delta(request.caller(), U256::ZERO, call_value) { + tracing::error!("Caller balance change failed on call enter: {:?}", err); + self.record_error(TracerMethod::Enter, err); + self.revert_overlays_to_checkpoint(checkpoint); + return; + } + + if let Err(err) = self.apply_balance_delta(request.callee(), call_value, U256::ZERO) { + tracing::error!("Callee balance change failed on call enter: {:?}", err); + self.record_error(TracerMethod::Enter, err); + self.revert_overlays_to_checkpoint(checkpoint); + return; + } + } + + self.current_depth += 1; + if self.current_depth == 1 && request.modifier() == CallModifier::Constructor { + self.pending_create_type = Some(CreateType::Create); + } + + let call_type = self.consume_call_type(request.modifier()); + let gas = U256::from(request.resources().ergs / ERGS_PER_GAS); + let input = Bytes::copy_from_slice(request.input()); + let frame_ctx = TxContext { + typ: call_type.clone(), + from: request.caller(), + to: request.callee(), + input: input.clone(), + gas, + value: match request.modifier() { + CallModifier::Static => U256::ZERO, + _ => request.nominal_token_value(), + }, + gas_used: None, + output: None, + error: None, + }; + self.frame_stack.push(FrameState { + ctx: frame_ctx, + checkpoint, + }); + + let obj = serde_json::json!({ + "type": call_type, + "from": request.caller(), + "to": request.callee(), + "input": input.encode_hex(), + "gas": gas, + "value": match request.modifier() { + CallModifier::Static => JsonValue::Null, + _ => serde_json::to_value(request.nominal_token_value()).unwrap_or(JsonValue::Null), + }, + }); + + self.invoke_method(TracerMethod::Enter, &obj); + } + + fn after_execution_frame_completed(&mut self, result: Option<(EvmResources, CallResult)>) { + let (gas_used, output, revert_reason) = match &result { + Some((resources, res)) => match res { + CallResult::Successful { returndata } => ( + gas_used_from_resources(resources.clone()), + Some(Bytes::copy_from_slice(returndata)), + None, + ), + CallResult::Failed { returndata } => ( + gas_used_from_resources(resources.clone()), + Some(Bytes::copy_from_slice(returndata)), + maybe_revert_reason(returndata), + ), + }, + None => (U256::ZERO, None, None), + }; + + let frame_failed = matches!(result, Some((_, CallResult::Failed { .. })) | None); + + if let Some(mut frame_state) = self.frame_stack.pop() { + let ctx = &mut frame_state.ctx; + ctx.gas_used = Some(gas_used); + ctx.output = output.clone(); + ctx.error = revert_reason.clone(); + + if frame_failed { + self.revert_overlays_to_checkpoint(frame_state.checkpoint); + } + + if self.frame_stack.is_empty() && frame_failed { + self.tx_failed = true; + } + + self.last_finished_frame = Some(frame_state.ctx); + } else { + tracing::error!("Execution frame completed but no frame context found"); + } + + if self.current_depth > 0 { + self.current_depth -= 1; + } + + let obj = serde_json::json!({ + "gasUsed": gas_used, + "output": output.map(|o| o.encode_hex()), + "error": revert_reason + }); + self.invoke_method(TracerMethod::Exit, &obj); + } + + /// This method only performs a sanity check that the values in the overlay match the ones + /// from the state. + fn on_storage_read(&mut self, _: bool, address: Address, key: B256, value: B256) { + let storage_key = (address, key); + if let Some(entry) = self + .storage_overlay + .handle() + .borrow() + .get(&storage_key) + .cloned() + && entry.value != value + { + tracing::error!( + address = ?address, + key = ?key, + overlay_value = ?entry.value, + actual_value = ?value, + "Storage overlay/read mismatch" + ); + self.record_error( + TracerMethod::StorageRead, + anyhow::anyhow!("Storage overlay value mismatch on read"), + ); + } + } + + fn on_storage_write(&mut self, _is_transient: bool, address: Address, key: B256, value: B256) { + { + let mut overlay = self.storage_overlay.borrow_mut(); + let storage_key = (address, key); + match overlay.entry(storage_key) { + Entry::Occupied(mut entry) => { + let before = entry.get().clone(); + self.storage_overlay.record_update(storage_key, before); + let slot = entry.get_mut(); + slot.previous = Some(slot.value); + slot.value = value; + slot.committed = false; + } + Entry::Vacant(vacant) => { + self.storage_overlay.record_insert(storage_key); + vacant.insert(OverlayEntry::new_pending(value)); + } + } + } + let obj = serde_json::json!({ + "address": address, + "key": key, + "value": value, + }); + + // this method is an extension beyond geth tracer interface, convenient for state change tracking + self.invoke_method(TracerMethod::Write, &obj); + } + + fn on_bytecode_change( + &mut self, + address: Address, + new_raw_bytecode: Option<&[u8]>, + _new_internal_bytecode_hash: B256, + new_observable_bytecode_length: u32, + ) { + let new_value = new_raw_bytecode.map(|code| { + let len = new_observable_bytecode_length as usize; + let slice = if code.len() >= len { + &code[..len] + } else { + code + }; + slice.to_vec() + }); + + if new_value.is_some() { + self.mark_contract_deployed(address); + } + + let mut overlay = self.code_overlay.borrow_mut(); + match overlay.entry(address) { + Entry::Occupied(mut entry) => { + let before = entry.get().clone(); + self.code_overlay.record_update(address, before); + let record = entry.get_mut(); + if record.committed && record.previous.is_none() { + record.previous = Some(record.value.clone()); + } + record.value = new_value.clone(); + record.committed = false; + } + Entry::Vacant(vacant) => { + self.code_overlay.record_insert(address); + vacant.insert(OverlayEntry::new_pending(new_value)); + } + } + } + + fn on_event(&mut self, _: Address, _: Vec, _: &[u8]) {} + + fn begin_tx(&mut self, _calldata: &[u8]) { + self.tx_failed = false; + self.current_depth = 0; + self.pending_step = None; + self.pending_create_type = None; + self.last_finished_frame = None; + self.frame_stack.clear(); + self.clear_overlay_journals(); + + let config = self.tracer_config.clone(); + self.invoke_method(TracerMethod::Setup, &config); + } + + fn finish_tx(&mut self) { + if self.error.is_some() { + self.rollback_overlays(); + self.clear_overlay_journals(); + self.frame_stack.clear(); + self.tx_failed = false; + self.last_finished_frame = None; + return; + } + + let ctx = match self.last_finished_frame.clone() { + Some(frame) => frame, + None => { + tracing::error!("No finished frame found at transaction end"); + self.record_error( + TracerMethod::Result, + anyhow::anyhow!("No finished frame found at transaction end"), + ); + self.rollback_overlays(); + self.clear_overlay_journals(); + self.frame_stack.clear(); + self.tx_failed = false; + self.last_finished_frame = None; + + return; + } + }; + self.pending_step = None; + + let mut tx_failed = self.tx_failed || ctx.error.is_some(); + + match self.call_result(&ctx) { + Ok(val) => self.results.push(val), + Err(err) => { + tx_failed = true; + self.record_error(TracerMethod::Result, err); + } + } + + if tx_failed { + self.rollback_overlays(); + } else { + self.apply_pending_selfdestructs(); + self.commit_overlays(); + } + + self.clear_overlay_journals(); + self.frame_stack.clear(); + self.tx_failed = false; + self.last_finished_frame = None; + } + + fn before_evm_interpreter_execution_step( + &mut self, + opcode: u8, + frame_state: impl EvmFrameInterface, + ) { + let gas_before = frame_state.resources().ergs / ERGS_PER_GAS; + let pc = frame_state.instruction_pointer() as u64; + + self.pending_step = Some(StepCtx { + opcode, + pc, + gas_before, + depth: self.current_depth, + }); + } + + fn after_evm_interpreter_execution_step( + &mut self, + opcode: u8, + frame_state: impl EvmFrameInterface, + ) { + let pending = self.pending_step.take().unwrap_or_else(|| StepCtx { + opcode, + pc: frame_state.instruction_pointer() as u64, + gas_before: frame_state.resources().ergs / ERGS_PER_GAS, + depth: self.current_depth, + }); + + let log = self.prepare_log_input(pending, &frame_state, None); + self.invoke_method(TracerMethod::Step, &log); + } + + fn on_opcode_error(&mut self, error: &EvmError, frame_state: impl EvmFrameInterface) { + let message = fmt_error_msg(error); + let log = if let Some(pending) = self.pending_step.take() { + self.prepare_log_input(pending, &frame_state, Some(message.clone())) + } else { + tracing::error!("Received opcode error without pending step context"); + serde_json::json!({ + "error": message, + "depth": self.current_depth, + }) + }; + + self.invoke_method(TracerMethod::Fault, &log); + } + + fn on_call_error(&mut self, error: &EvmError) { + self.pending_step = None; + self.tx_failed = true; + let obj = serde_json::json!({ + "error": fmt_error_msg(error), + "depth": self.current_depth, + }); + + self.invoke_method(TracerMethod::Fault, &obj); + } + + fn on_selfdestruct( + &mut self, + beneficiary: Address, + token_value: U256, + frame_state: impl EvmFrameInterface, + ) { + if token_value != U256::ZERO { + if let Err(err) = + self.apply_balance_delta(frame_state.address(), U256::ZERO, token_value) + { + tracing::error!("Selfdestruct balance debit failed: {:?}", err); + self.record_error(TracerMethod::Enter, err); + } + + if let Err(err) = self.apply_balance_delta(beneficiary, token_value, U256::ZERO) { + tracing::error!("Selfdestruct beneficiary credit failed: {:?}", err); + self.record_error(TracerMethod::Enter, err); + } + } + + let address = frame_state.address(); + let mut overlay = self.selfdestruct_overlay.borrow_mut(); + match overlay.entry(address) { + Entry::Occupied(mut entry) => { + let before = entry.get().clone(); + self.selfdestruct_overlay.record_update(address, before); + entry.get_mut().value.is_marked_for_selfdestruct = true; + } + Entry::Vacant(vacant) => { + self.selfdestruct_overlay.record_insert(address); + vacant.insert(OverlayEntry::new_pending(SelfdestructEntry { + is_deployed_in_current_tx: false, + is_marked_for_selfdestruct: true, + })); + } + } + } + + fn on_create_request(&mut self, is_create2: bool) { + self.pending_create_type = Some(if is_create2 { + CreateType::Create2 + } else { + CreateType::Create + }); + } +} + +pub fn trace_block( + txs: Vec, + block_context: zksync_os_interface::types::BlockContext, + state_view: V, + js_tracer_config: String, +) -> anyhow::Result> { + let mut tracer = JsTracer::new(state_view.clone(), js_tracer_config)?; + + let tx_source = zksync_os_interface::traits::TxListSource { + transactions: txs.into_iter().map(|tx| tx.encode()).collect(), + }; + let _ = zksync_os_multivm::run_block( + block_context, + state_view.clone(), + state_view, + tx_source, + zksync_os_interface::traits::NoopTxCallback, + &mut tracer, + )?; + + if let Some(err) = tracer.take_error() { + return Err(err); + } + + Ok(tracer.results) +} diff --git a/lib/rpc/src/js_tracer/types.rs b/lib/rpc/src/js_tracer/types.rs new file mode 100644 index 000000000..dbd4c83d4 --- /dev/null +++ b/lib/rpc/src/js_tracer/types.rs @@ -0,0 +1,252 @@ +use alloy::primitives::{Address, B256, Bytes, U256}; +use std::{ + cell::{RefCell, RefMut}, + collections::HashMap, + hash::Hash, + rc::Rc, +}; + +#[derive(Clone, Copy, Debug)] +pub(crate) enum CreateType { + Create, + Create2, +} + +#[derive(Clone, Copy, Debug)] +pub(crate) enum TracerMethod { + Setup, + Enter, + Exit, + Step, + Fault, + Result, + Write, + StorageRead, +} + +impl TracerMethod { + pub(crate) const fn as_str(self) -> &'static str { + match self { + TracerMethod::Setup => "setup", + TracerMethod::Enter => "enter", + TracerMethod::Exit => "exit", + TracerMethod::Step => "step", + TracerMethod::Fault => "fault", + TracerMethod::Result => "result", + TracerMethod::Write => "write", + TracerMethod::StorageRead => "storage_read", + } + } +} + +#[derive(Debug)] +pub struct OverlayEntry { + pub(crate) value: V, + pub(crate) committed: bool, + pub(crate) previous: Option, +} + +impl Clone for OverlayEntry { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + committed: self.committed, + previous: self.previous.clone(), + } + } +} + +impl OverlayEntry { + pub(crate) fn new_pending(value: V) -> Self { + Self { + value, + committed: false, + previous: None, + } + } +} + +pub type StorageOverlay = HashMap<(Address, B256), OverlayEntry>; +pub type CodeOverlay = HashMap>>>; +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct BalanceDelta { + pub added: U256, + pub removed: U256, +} + +#[derive(Clone, Default, Debug)] +pub struct SelfdestructEntry { + pub is_deployed_in_current_tx: bool, + pub is_marked_for_selfdestruct: bool, +} + +impl BalanceDelta { + pub fn credit(&mut self, amount: U256) -> anyhow::Result<()> { + if amount == U256::ZERO { + return Ok(()); + } + + let (new_total, overflow) = self.added.overflowing_add(amount); + if overflow { + anyhow::bail!("Balance credit overflow"); + } + + self.added = new_total; + + Ok(()) + } + + pub fn debit(&mut self, amount: U256) -> anyhow::Result<()> { + if amount == U256::ZERO { + return Ok(()); + } + + let (new_total, overflow) = self.removed.overflowing_add(amount); + if overflow { + anyhow::bail!("Balance debit overflow"); + } + + self.removed = new_total; + + Ok(()) + } + + pub fn is_empty(&self) -> bool { + self.added == U256::ZERO && self.removed == U256::ZERO + } +} + +pub type BalanceOverlay = HashMap>; + +pub(crate) struct StepCtx { + pub opcode: u8, + pub pc: u64, + pub gas_before: u64, + pub depth: u64, +} + +#[derive(Clone, Debug)] +pub(crate) struct TxContext { + pub typ: String, + pub from: Address, + pub to: Address, + pub input: Bytes, + pub gas: U256, + pub value: U256, + + // the fields below are only filled during when the frame is exited + pub gas_used: Option, + pub output: Option, + pub error: Option, +} + +#[derive(Clone, Copy, Debug, Default)] +pub(crate) struct OverlayCheckpoint { + pub storage: usize, + pub code: usize, + pub balance: usize, + pub selfdestruct: usize, +} + +pub(crate) struct FrameState { + pub ctx: TxContext, + pub checkpoint: OverlayCheckpoint, +} + +enum OverlayAction { + Inserted(K), + Updated(K, OverlayEntry), +} + +pub(crate) struct OverlayState { + handle: Rc>>>, + journal: RefCell>>, +} + +impl OverlayState +where + K: Copy + Eq + Hash, + V: Clone, +{ + pub(crate) fn new() -> Self { + Self { + handle: Rc::new(RefCell::new(HashMap::new())), + journal: RefCell::new(Vec::new()), + } + } + + pub(crate) fn handle(&self) -> Rc>>> { + Rc::clone(&self.handle) + } + + pub(crate) fn borrow_mut(&self) -> RefMut<'_, HashMap>> { + self.handle.borrow_mut() + } + + pub(crate) fn checkpoint(&self) -> usize { + self.journal.borrow().len() + } + + pub(crate) fn record_insert(&self, key: K) { + self.journal.borrow_mut().push(OverlayAction::Inserted(key)); + } + + pub(crate) fn record_update(&self, key: K, entry: OverlayEntry) { + self.journal + .borrow_mut() + .push(OverlayAction::Updated(key, entry)); + } + + pub(crate) fn revert_to_checkpoint(&self, checkpoint: usize) { + let mut overlay = self.handle.borrow_mut(); + let mut journal = self.journal.borrow_mut(); + while journal.len() > checkpoint { + match journal.pop().expect("overlay checkpoint out of bounds") { + OverlayAction::Inserted(key) => { + overlay.remove(&key); + } + OverlayAction::Updated(key, entry) => { + overlay.insert(key, entry); + } + } + } + } + + pub(crate) fn clear_journal(&self) { + self.journal.borrow_mut().clear(); + } + + pub(crate) fn commit(&self) { + Self::commit_map(&mut self.handle.borrow_mut()); + } + + pub(crate) fn rollback(&self) { + Self::rollback_map(&mut self.handle.borrow_mut()); + } + + fn commit_map(map: &mut HashMap>) { + map.retain(|_, entry| { + if !entry.committed { + entry.committed = true; + entry.previous = None; + } + true + }); + } + + fn rollback_map(map: &mut HashMap>) { + map.retain(|_, entry| { + if entry.committed { + return true; + } + + if let Some(prev) = entry.previous.take() { + entry.value = prev; + entry.committed = true; + true + } else { + false + } + }); + } +} diff --git a/lib/rpc/src/js_tracer/utils.rs b/lib/rpc/src/js_tracer/utils.rs new file mode 100644 index 000000000..d999c1f4a --- /dev/null +++ b/lib/rpc/src/js_tracer/utils.rs @@ -0,0 +1,74 @@ +use crate::sandbox::ERGS_PER_GAS; +use alloy::primitives::{Address, B256, U256}; +use boa_engine::{JsError, JsString, JsValue}; +use jsonrpsee::core::JsonValue; +use zksync_os_interface::tracing::EvmResources; + +pub(crate) fn gas_used_from_resources(resources: EvmResources) -> U256 { + U256::from(resources.ergs / ERGS_PER_GAS) +} + +pub(crate) fn extract_js_source_and_config(js_cfg: String) -> anyhow::Result<(String, JsonValue)> { + let (source, config) = if let Ok(cfg) = serde_json::from_str::(&js_cfg) { + match cfg { + JsonValue::Object(map) => ( + map.get("code") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(), + map.get("config") + .map(|v| v.to_owned()) + .unwrap_or(JsonValue::Null), + ), + _ => (String::new(), JsonValue::Null), + } + } else { + // this means the config only contains raw JS code as a string + (js_cfg, JsonValue::Null) + }; + + if source.is_empty() { + return Err(anyhow::anyhow!( + "JS tracer source not provided in 'tracer' field" + )); + } + + Ok((source, config)) +} + +pub(crate) fn parse_address(s: &str) -> Option
{ + let s = s.strip_prefix("0x").unwrap_or(s); + let bytes = alloy::primitives::hex::decode(s).ok()?; + if bytes.len() != 20 { + return None; + } + + Some(Address::from_slice(&bytes)) +} + +pub(crate) fn parse_b256(s: &str) -> Option { + let s = s.strip_prefix("0x").unwrap_or(s); + let bytes = alloy::primitives::hex::decode(s).ok()?; + if bytes.len() != 32 { + return None; + } + + Some(B256::from_slice(&bytes)) +} + +pub(crate) fn format_hex_u256(v: U256) -> String { + if v == U256::ZERO { + return "0x0".to_string(); + } + + format!("0x{v:x}") +} + +pub(crate) fn anyhow_error_to_js_error(e: anyhow::Error) -> JsError { + JsError::from_opaque(JsValue::from(JsString::from(e.to_string()))) +} + +pub(crate) fn wrap_js_invocation(body: impl AsRef) -> String { + let content = body.as_ref().trim_matches('\n'); + format!("(function(){{\n{content}\n}})()") +} diff --git a/lib/rpc/src/lib.rs b/lib/rpc/src/lib.rs index 2fce170f9..83a3baa3a 100644 --- a/lib/rpc/src/lib.rs +++ b/lib/rpc/src/lib.rs @@ -16,6 +16,7 @@ mod result; mod rpc_storage; pub use rpc_storage::{ReadRpcStorage, RpcStorage}; mod debug_impl; +pub mod js_tracer; mod monitoring_middleware; mod net_impl; mod sandbox; diff --git a/lib/rpc/src/sandbox.rs b/lib/rpc/src/sandbox.rs index 3d8ee7551..fdde97b73 100644 --- a/lib/rpc/src/sandbox.rs +++ b/lib/rpc/src/sandbox.rs @@ -419,7 +419,7 @@ impl EvmTracer for CallTracer { } /// Returns a non-empty revert reason if the output is a revert/error. -fn maybe_revert_reason(output: &[u8]) -> Option { +pub(crate) fn maybe_revert_reason(output: &[u8]) -> Option { let reason = match GenericRevertReason::decode(output)? { GenericRevertReason::ContractError(err) => { match err { @@ -440,7 +440,7 @@ fn maybe_revert_reason(output: &[u8]) -> Option { /// Converts [`EvmError`] to a geth-style error message (if possible). /// /// See https://github.com/ethereum/go-ethereum/blob/9ce40d19a8240844be24b9692c639dff45d13d68/core/vm/errors.go#L26-L45 -fn fmt_error_msg(error: &EvmError) -> String { +pub(crate) fn fmt_error_msg(error: &EvmError) -> String { match error { // todo: missing `ErrGasUintOverflow`: likely not propagated during tx decoding EvmError::Revert => "execution reverted".to_string(),