diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38f96b0fc..dd9880bd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,9 @@ on: env: CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RISC0_VERSION: 0.19.1 + RISC0_TOOLCHAIN_VERSION: test-release-2 concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -18,36 +21,40 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - uses: risc0/risc0/.github/actions/rustup@release-0.18 - - uses: risc0/risc0/.github/actions/sccache@release-0.18 - # - run: cargo install cargo-risczero && cargo risczero install - - run: cargo test --workspace --all-targets --all-features --exclude sgx-ra + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: risc0/risc0/.github/actions/rustup@release-0.19 + - uses: risc0/risc0/.github/actions/sccache@release-0.19 + - uses: risc0/cargo-install@v1 + with: + crate: cargo-binstall + - run: cargo binstall -y --force cargo-risczero@${{ env.RISC0_VERSION }} + - run: cargo risczero install --version $RISC0_TOOLCHAIN_VERSION + - run: cargo test --workspace --all-targets --features taiko + - run: cargo test --workspace --all-targets --features "taiko std" + - run: cargo test --workspace --all-targets --features optimism + clippy: name: clippy runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - uses: risc0/risc0/.github/actions/rustup@release-0.18 - - uses: risc0/risc0/.github/actions/sccache@release-0.18 - - uses: risc0/clippy-action@main - with: - reporter: "github-pr-check" - fail_on_error: true - github_token: ${{ secrets.GITHUB_TOKEN }} - clippy_flags: --workspace --all-targets --all-features -- -Dwarnings + - uses: actions/checkout@v4 + - uses: risc0/risc0/.github/actions/rustup@release-0.19 + - uses: risc0/risc0/.github/actions/sccache@release-0.19 + - uses: risc0/clippy-action@main + with: + reporter: 'github-pr-check' + fail_on_error: true + clippy_flags: --workspace --all-targets --all-features -- -Dwarnings fmt: name: fmt runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v3 - - uses: risc0/risc0/.github/actions/rustup@release-0.18 - - run: cargo fmt --all --check + - uses: actions/checkout@v4 + - uses: risc0/risc0/.github/actions/rustup@release-0.19 + - run: cargo fmt --all --check diff --git a/.vscode/settings.json b/.vscode/settings.json index e3c59b6bd..60926f4b7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { - "rust-analyzer.cargo.features": "all", - "rust-analyzer.linkedProjects": ["./Cargo.toml"] + "rust-analyzer.cargo.features": "all", + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + ] } diff --git a/Cargo.lock b/Cargo.lock index 80b823c3a..90a41de32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,11 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", @@ -46,9 +52,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-dyn-abi" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4e0daba57ddaba12dc9b21f608b843251f3de017f94a431dca4e7f4f72e5ba9" +checksum = "c7265ac54c88a78604cea8444addfa9dfdad08d3098f153484cb4ee66fc202cc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -56,13 +62,14 @@ dependencies = [ "alloy-sol-types", "const-hex", "itoa", + "winnow", ] [[package]] name = "alloy-json-abi" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c9319ad8b2b623c6a3ac15899f8ffb71479224762dbaedc385c16efbb6cfe3" +checksum = "a7c5aecfe87e06da0e760840974c6e3cc19d4247be17a3172825fbbe759c8e60" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -71,9 +78,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0628ec0ba5b98b3370bb6be17b12f23bfce8ee4ad83823325a20546d9b03b78" +checksum = "f4b6fb2b432ff223d513db7f908937f63c252bee0af9b82bfd25b0a5dd1eb0d8" dependencies = [ "alloy-rlp", "bytes", @@ -82,6 +89,8 @@ dependencies = [ "derive_more", "hex-literal", "itoa", + "k256", + "keccak-asm", "proptest", "rand", "ruint", @@ -91,21 +100,19 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ - "alloy-rlp-derive", "arrayvec", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0391754c09fab4eae3404d19d0d297aa1c670c1775ab51d8a5312afeca23157" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", @@ -114,13 +121,14 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a98ad1696a2e17f010ae8e43e9f2a1e930ed176a8e3ff77acfeff6dfb07b42c" +checksum = "8b0b5ab0cb07c21adf9d72e988b34e8200ce648c2bba8d009183bb1c50fb1216" dependencies = [ "const-hex", "dunce", "heck 0.4.1", + "indexmap 2.2.3", "proc-macro-error", "proc-macro2", "quote", @@ -131,18 +139,18 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81c61ccc29e7c58bf16a2f780898852348183f58b127bde03ced6d07ad544787" +checksum = "4dd124ec0a456ec5e9dcca5b6e8b011bc723cc410d4d9a66bf032770feaeef4b" dependencies = [ "winnow", ] [[package]] name = "alloy-sol-types" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d7107bed88e8f09f0ddcc3335622d87bfb6821f3e0c7473329fb1cfad5e015" +checksum = "6c08f62ded7ce03513bfb60ef5cad4fff5d4f67eac6feb4df80426b7b9ffb06e" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -176,9 +184,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -190,43 +198,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "ark-ff" @@ -258,7 +266,7 @@ dependencies = [ "ark-std 0.4.0", "derivative", "digest 0.10.7", - "itertools 0.10.5", + "itertools", "num-bigint 0.4.4", "num-traits", "paste", @@ -360,9 +368,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_cmd" -version = "2.0.12" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467" dependencies = [ "anstyle", "bstr", @@ -375,9 +383,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", @@ -395,15 +403,6 @@ dependencies = [ "rustc_version 0.4.0", ] -[[package]] -name = "atomic-polyfill" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" -dependencies = [ - "critical-section", -] - [[package]] name = "atty" version = "0.2.14" @@ -415,16 +414,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "aurora-engine-modexp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfacad86e9e138fca0670949eb8ed4ffdf73a55bded8887efe0863cd1a3a6f70" +dependencies = [ + "hex 0.4.3", + "num", +] + [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "823b8bb275161044e2ac7a25879cb3e2480cb403e3943022c7c769c599b756aa" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -514,29 +522,6 @@ dependencies = [ "which", ] -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.4.1", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.48", - "which", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -566,9 +551,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" dependencies = [ "serde", ] @@ -618,9 +603,9 @@ dependencies = [ [[package]] name = "bonsai-sdk" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94478e373742b9d1de02e13399633348e5b230dfe6364f65e80056c7df7438c5" +checksum = "441d1092e11977985946b6564251df91d80ae36982128e53be52a32548ad8762" dependencies = [ "reqwest", "serde", @@ -629,12 +614,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.5", "serde", ] @@ -658,9 +643,9 @@ checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" dependencies = [ "bytemuck_derive", ] @@ -693,11 +678,10 @@ dependencies = [ [[package]] name = "c-kzg" -version = "0.1.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac926d808fb72fe09ebf471a091d6d72918876ccf0b4989766093d2d0d24a0ef" +checksum = "b9d8c306be83ec04bf5f73710badd8edf56dea23f2f0d8b7f9fe4644d371c758" dependencies = [ - "bindgen 0.66.1", "blst", "cc", "glob", @@ -717,9 +701,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" dependencies = [ "serde", ] @@ -732,7 +716,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.20", + "semver 1.0.21", "serde", "serde_json", ] @@ -763,9 +747,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -773,14 +757,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -804,9 +788,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -814,21 +798,21 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -838,9 +822,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cmake" @@ -859,9 +843,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const-hex" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" +checksum = "18d59688ad0945eaf6b84cb44fedbe93484c81b48970e98f09db8a22832d7961" dependencies = [ "cfg-if", "cpufeatures", @@ -872,9 +856,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "convert_case" @@ -884,9 +868,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -894,24 +878,33 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -924,22 +917,18 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -949,9 +938,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core", @@ -971,9 +960,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" dependencies = [ "darling_core", "darling_macro", @@ -981,9 +970,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" dependencies = [ "fnv", "ident_case", @@ -995,20 +984,26 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" dependencies = [ "darling_core", "quote", "syn 2.0.48", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" @@ -1022,9 +1017,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -1116,9 +1111,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -1130,21 +1125,21 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elf" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6e7d85896690fe195447717af8eceae0593ac2196fd42fe88c184e904406ce" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" [[package]] name = "elliptic-curve" -version = "0.13.6" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", @@ -1188,9 +1183,9 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", @@ -1218,12 +1213,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1285,9 +1280,9 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.10" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a17f0708692024db9956b31d7a20163607d2745953f5ae8125ab368ba280ad" +checksum = "aab3cef6cc1c9fd7f787043c81ad3052eff2b96a3878ef1526aa446311bdbfc9" dependencies = [ "arrayvec", "bytes", @@ -1312,9 +1307,9 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.10" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6838fa110e57d572336178b7c79e94ff88ef976306852d8cb87d9e5b1fc7c0b5" +checksum = "fb6b15393996e3b8a78ef1332d6483c11d839042c17be58decc92fa8b1c3508a" dependencies = [ "async-trait", "auto_impl", @@ -1420,9 +1415,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1435,9 +1430,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1450,9 +1445,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1460,15 +1455,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1477,15 +1472,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1494,15 +1489,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -1516,9 +1511,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1554,9 +1549,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -1565,9 +1560,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1609,9 +1604,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -1619,7 +1614,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1634,9 +1629,18 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1688,9 +1692,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -1724,18 +1728,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1744,9 +1748,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1773,9 +1777,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1788,7 +1792,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1824,9 +1828,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1853,9 +1857,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1912,12 +1916,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "serde", ] @@ -1938,12 +1942,12 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.3", - "rustix", + "hermit-abi 0.3.6", + "libc", "windows-sys 0.52.0", ] @@ -1956,26 +1960,17 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1996,9 +1991,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa", @@ -2010,13 +2005,23 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2034,18 +2039,42 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libflate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] + +[[package]] +name = "libflate_lz77" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" +dependencies = [ + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", +] [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2060,16 +2089,16 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "libc", - "redox_syscall 0.4.1", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -2104,8 +2133,8 @@ dependencies = [ [[package]] name = "mbedtls" -version = "0.12.1" -source = "git+https://github.com/fortanix/rust-mbedtls.git#51429f1db35d986ddb3f43bbefc078df74be62ff" +version = "0.12.3" +source = "git+https://github.com/fortanix/rust-mbedtls.git#55eac05326dfef3d754af00f35d11413250e0c65" dependencies = [ "bitflags 1.3.2", "byteorder", @@ -2122,7 +2151,7 @@ dependencies = [ [[package]] name = "mbedtls-platform-support" version = "0.1.1" -source = "git+https://github.com/fortanix/rust-mbedtls.git#51429f1db35d986ddb3f43bbefc078df74be62ff" +source = "git+https://github.com/fortanix/rust-mbedtls.git#55eac05326dfef3d754af00f35d11413250e0c65" dependencies = [ "cc", "cfg-if", @@ -2132,10 +2161,10 @@ dependencies = [ [[package]] name = "mbedtls-sys-auto" -version = "2.28.4+mbedtls-2.28.3" -source = "git+https://github.com/fortanix/rust-mbedtls.git#51429f1db35d986ddb3f43bbefc078df74be62ff" +version = "2.28.7" +source = "git+https://github.com/fortanix/rust-mbedtls.git#55eac05326dfef3d754af00f35d11413250e0c65" dependencies = [ - "bindgen 0.65.1", + "bindgen", "cc", "cfg-if", "cmake", @@ -2147,9 +2176,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -2165,18 +2194,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -2259,18 +2288,24 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", @@ -2279,19 +2314,18 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -2312,9 +2346,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -2326,26 +2360,26 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.6", "libc", ] [[package]] name = "num_enum" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.48", @@ -2353,21 +2387,21 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" dependencies = [ - "atomic-polyfill", "critical-section", + "portable-atomic", ] [[package]] @@ -2397,11 +2431,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -2429,9 +2463,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", @@ -2453,9 +2487,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parity-scale-codec" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -2467,11 +2501,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.5" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -2495,7 +2529,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -2523,15 +2557,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" dependencies = [ "memchr", "thiserror", @@ -2550,18 +2584,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -2592,9 +2626,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "portable-atomic" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" [[package]] name = "powerfmt" @@ -2610,13 +2650,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", - "itertools 0.11.0", "predicates-core", ] @@ -2638,9 +2677,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", "syn 2.0.48", @@ -2667,7 +2706,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", ] [[package]] @@ -2696,9 +2753,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2720,19 +2777,19 @@ dependencies = [ [[package]] name = "proptest" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", "bit-vec 0.6.3", - "bitflags 2.4.1", + "bitflags 2.4.2", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", "rusty-fork", "tempfile", "unarray", @@ -2746,11 +2803,11 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "memchr", "unicase", ] @@ -2783,7 +2840,7 @@ dependencies = [ "anyhow", "base64 0.21.7", "base64-serde", - "clap 4.4.7", + "clap 4.5.0", "dirs", "ethers-core", "hex 0.4.3", @@ -2791,7 +2848,7 @@ dependencies = [ "rand", "rand_core", "risc0-zkvm", - "secp256k1", + "secp256k1 0.27.0", "serde", "serde_json", "sgx-ra", @@ -2812,11 +2869,11 @@ dependencies = [ "bincode", "bonsai-sdk", "bytemuck", - "clap 4.4.7", + "clap 4.5.0", "ethers-core", "ethers-providers", "flate2", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "hex 0.4.3", "hyper", "lazy_static", @@ -2880,15 +2937,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2911,13 +2959,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -2932,9 +2980,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -2947,12 +2995,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -2961,15 +3003,15 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "relative-path" -version = "1.9.0" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64 0.21.7", "bytes", @@ -2995,6 +3037,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -3011,7 +3054,7 @@ dependencies = [ [[package]] name = "revm" version = "3.5.0" -source = "git+https://github.com/johntaiko/revm?branch=feat/taiko#327dc05411c59db6520ba3782a4d1cdab992f479" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#0ebc1e0f5e2ab731bd583c4113440d6226dea58c" dependencies = [ "auto_impl", "revm-interpreter", @@ -3023,7 +3066,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.3.0" -source = "git+https://github.com/johntaiko/revm?branch=feat/taiko#327dc05411c59db6520ba3782a4d1cdab992f479" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#0ebc1e0f5e2ab731bd583c4113440d6226dea58c" dependencies = [ "revm-primitives", "serde", @@ -3032,15 +3075,15 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.2.0" -source = "git+https://github.com/johntaiko/revm?branch=feat/taiko#327dc05411c59db6520ba3782a4d1cdab992f479" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#0ebc1e0f5e2ab731bd583c4113440d6226dea58c" dependencies = [ + "aurora-engine-modexp", "c-kzg", "k256", - "num", "once_cell", "revm-primitives", "ripemd", - "secp256k1", + "secp256k1 0.28.2", "sha2", "substrate-bn", ] @@ -3048,16 +3091,15 @@ dependencies = [ [[package]] name = "revm-primitives" version = "1.3.0" -source = "git+https://github.com/johntaiko/revm?branch=feat/taiko#327dc05411c59db6520ba3782a4d1cdab992f479" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#0ebc1e0f5e2ab731bd583c4113440d6226dea58c" dependencies = [ "alloy-primitives", - "alloy-rlp", "auto_impl", - "bitflags 2.4.1", + "bitflags 2.4.2", "bitvec", "c-kzg", "enumn", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "hex 0.4.3", "serde", ] @@ -3089,9 +3131,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -3200,6 +3242,12 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8524b46783b58b00e9b2a4712e837093c975b23cf25bfaf99e1cf69e9011bf6b" +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" + [[package]] name = "rlp" version = "0.5.2" @@ -3324,39 +3372,39 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.20", + "semver 1.0.21", ] [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.5", + "ring 0.17.7", "rustls-webpki", "sct", ] [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.7", ] @@ -3367,7 +3415,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -3391,9 +3439,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -3422,7 +3470,7 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -3430,11 +3478,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3449,7 +3497,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.5", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -3474,7 +3522,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ "rand", - "secp256k1-sys", + "secp256k1-sys 0.8.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +dependencies = [ + "secp256k1-sys 0.9.2", ] [[package]] @@ -3486,6 +3543,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.9.2" @@ -3520,9 +3586,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" dependencies = [ "serde", ] @@ -3550,18 +3616,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -3570,11 +3636,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.2.3", "itoa", "ryu", "serde", @@ -3594,16 +3660,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ "base64 0.21.7", "chrono", "hex 0.4.3", "indexmap 1.9.3", - "indexmap 2.0.2", + "indexmap 2.2.3", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -3611,9 +3678,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ "darling", "proc-macro2", @@ -3661,6 +3728,16 @@ dependencies = [ "keccak", ] +[[package]] +name = "sha3-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3672,9 +3749,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" @@ -3687,9 +3764,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core", @@ -3733,28 +3810,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.4.10" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -3780,9 +3838,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -3806,6 +3864,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "structopt" version = "0.3.26" @@ -3923,9 +3987,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.4.2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b837ef12ab88835251726eb12237655e61ec8dc8a280085d1961cdc3dfd047" +checksum = "63bef2e2c735acbc06874eca3a8506f02a3c4700e6e748afc92cc2e4220e8a03" dependencies = [ "paste", "proc-macro2", @@ -3933,6 +3997,12 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3962,15 +4032,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4012,18 +4081,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -4071,12 +4140,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -4091,10 +4161,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -4124,9 +4195,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.33.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -4136,16 +4207,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -4222,7 +4293,29 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -4359,9 +4452,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -4424,9 +4517,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -4445,9 +4538,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" @@ -4475,9 +4568,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -4556,9 +4649,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4566,9 +4659,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -4581,9 +4674,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -4593,9 +4686,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4603,9 +4696,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -4616,15 +4709,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -4632,9 +4725,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "which" @@ -4681,11 +4774,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -4822,9 +4915,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.17" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -4879,18 +4972,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.15" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ba595b9f2772fbee2312de30eeb80ec773b4cb2f1e8098db024afadda6c06f" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.15" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772666c41fb6dceaf520b564b962d738a8e1a83b41bd48945f50837aed78bb1d" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", @@ -4899,9 +4992,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -4921,15 +5014,18 @@ dependencies = [ name = "zeth-lib" version = "0.1.0" dependencies = [ + "alloy-primitives", "alloy-sol-types", "anyhow", "bincode", + "bytes", "chrono", "ethers-core", "ethers-providers", "flate2", - "hashbrown 0.14.2", + "hashbrown 0.14.3", "hex 0.4.3", + "libflate", "log", "once_cell", "revm", diff --git a/Cargo.toml b/Cargo.toml index e09c1ef0d..542399042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,24 +23,15 @@ lto = true [profile.release.build-override] opt-level = 3 -[patch.crates-io] -# use optimized risc0 circuit -revm = { git = "https://github.com/johntaiko/revm", branch = "feat/taiko" } -revm-primitives = { git = "https://github.com/johntaiko/revm", branch = "feat/taiko" } [workspace.dependencies] -bonsai-sdk = "0.4" +bonsai-sdk = "0.5" hashbrown = { version = "0.14", features = ["inline-more"] } -risc0-build = "0.18" -risc0-zkvm = { version = "0.18", default-features = false } -revm = { version = "3.5", default-features = false, features = [ +risc0-build = "0.19" +risc0-zkvm = { version = "0.19", default-features = false } +revm-primitives = { git = "https://github.com/ceciliaz030/revm.git", branch = "sync-taiko-v3.5", default_features = false } +revm = { git = "https://github.com/ceciliaz030/revm.git", branch = "sync-taiko-v3.5", default-features = false, features = [ "std", "serde", - "taiko", - "optional_no_base_fee", - "optional_balance_check", -] } -revm-primitives = { version = "1.3", default_features = false, features = [ - "taiko", ] } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 82a1058dd..904005f57 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -5,11 +5,17 @@ edition = "2021" [dependencies] anyhow = { version = "1.0", default-features = false } -alloy-sol-types = { version = "0.4", default-features = false, optional = true } +alloy-sol-types = { version = "0.6", default-features = false } +alloy-primitives = { version = "0.6", default-features = false, features = [ + "rlp", + "serde", +] } +bytes = "1.5" ethers-core = { version = "2.0", features = ["optimism"] } hashbrown = { workspace = true } +libflate = "2.0.0" once_cell = { version = "1.18", default-features = false } -revm = { workspace = true } +revm = { workspace = true, default-features = false } ruint = { version = "1.10", default-features = false } serde = { version = "1.0", default-features = false, features = ["alloc"] } thiserror-no-std = "2.0.2" @@ -35,14 +41,13 @@ bincode = "1.3" serde_with = "3.1" [features] -default = ["taiko"] +# default = ["taiko", "std"] std = [] taiko = [ "zeth-primitives/taiko", - "dep:alloy-sol-types", + "revm/taiko", "dep:hex", "dep:rlp", "dep:tracing" ] -pos = [] -server = [] +optimism = ["std", "revm/optimism"] \ No newline at end of file diff --git a/lib/src/block_builder.rs b/lib/src/block_builder.rs deleted file mode 100644 index c090b2d56..000000000 --- a/lib/src/block_builder.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2023 RISC Zero, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::Result; -use revm::{Database, DatabaseCommit}; -use zeth_primitives::{ - block::Header, - transactions::{ethereum::EthereumTxEssence, optimism::OptimismTxEssence, TxEssence}, -}; - -use crate::{ - consts::ChainSpec, - execution::{ethereum::EthTxExecStrategy, optimism::OpTxExecStrategy, TxExecStrategy}, - finalization::{BlockBuildStrategy, BuildFromMemDbStrategy}, - initialization::{DbInitStrategy, MemDbInitStrategy}, - input::Input, - mem_db::MemDb, - preparation::{EthHeaderPrepStrategy, HeaderPrepStrategy}, -}; - -#[derive(Clone, Debug)] -pub struct BlockBuilder<'a, D, E: TxEssence> { - pub(crate) chain_spec: &'a ChainSpec, - pub(crate) input: Input<E>, - pub(crate) db: Option<D>, - pub(crate) header: Option<Header>, -} - -impl<D, E> BlockBuilder<'_, D, E> -where - D: Database + DatabaseCommit, - <D as Database>::Error: core::fmt::Debug, - E: TxEssence, -{ - /// Creates a new block builder. - pub fn new(chain_spec: &ChainSpec, input: Input<E>) -> BlockBuilder<'_, D, E> { - BlockBuilder { - chain_spec, - db: None, - header: None, - input, - } - } - - /// Sets the database. - pub fn with_db(mut self, db: D) -> Self { - self.db = Some(db); - self - } - - /// Initializes the database from the input tries. - pub fn initialize_database<T: DbInitStrategy<E, Database = D>>(self) -> Result<Self> { - T::initialize_database(self) - } - - /// Initializes the header. This must be called before executing transactions. - pub fn prepare_header<T: HeaderPrepStrategy>(self) -> Result<Self> { - T::prepare_header(self) - } - - /// Executes the transactions. - pub fn execute_transactions<T: TxExecStrategy<E>>(self) -> Result<Self> { - T::execute_transactions(self) - } - - /// Builds the block and returns the header. - pub fn build<T: BlockBuildStrategy<E, Database = D>>(self) -> Result<T::Output> { - T::build(self) - } - - /// Returns a reference to the database. - pub fn db(&self) -> Option<&D> { - self.db.as_ref() - } - - /// Returns a mutable reference to the database. - pub fn mut_db(&mut self) -> Option<&mut D> { - self.db.as_mut() - } -} - -pub trait NetworkStrategyBundle { - type Database: Database + DatabaseCommit; - type TxEssence: TxEssence; - - type DbInitStrategy: DbInitStrategy<Self::TxEssence, Database = Self::Database>; - type HeaderPrepStrategy: HeaderPrepStrategy; - type TxExecStrategy: TxExecStrategy<Self::TxEssence>; - type BlockBuildStrategy: BlockBuildStrategy<Self::TxEssence, Database = Self::Database>; -} - -pub struct ConfiguredBlockBuilder<'a, N: NetworkStrategyBundle>( - BlockBuilder<'a, N::Database, N::TxEssence>, -) -where - N::TxEssence: TxEssence; - -impl<N: NetworkStrategyBundle> ConfiguredBlockBuilder<'_, N> -where - <N::Database as Database>::Error: core::fmt::Debug, -{ - pub fn build_from( - chain_spec: &ChainSpec, - input: Input<N::TxEssence>, - ) -> Result<<N::BlockBuildStrategy as BlockBuildStrategy<N::TxEssence>>::Output> { - Self::new(chain_spec, input) - .initialize_database()? - .prepare_header()? - .execute_transactions()? - .build() - } - - /// Creates a new block builder. - pub fn new( - chain_spec: &ChainSpec, - input: Input<N::TxEssence>, - ) -> ConfiguredBlockBuilder<'_, N> { - ConfiguredBlockBuilder(BlockBuilder::new(chain_spec, input)) - } - - /// Sets the database. - pub fn with_db(mut self, db: N::Database) -> Self { - self.0.db = Some(db); - self - } - - /// Initializes the database from the input tries. - pub fn initialize_database(self) -> Result<Self> { - Ok(ConfiguredBlockBuilder( - N::DbInitStrategy::initialize_database(self.0)?, - )) - } - - /// Initializes the header. This must be called before executing transactions. - pub fn prepare_header(self) -> Result<Self> { - Ok(ConfiguredBlockBuilder( - N::HeaderPrepStrategy::prepare_header(self.0)?, - )) - } - - /// Executes the transactions. - pub fn execute_transactions(self) -> Result<Self> { - Ok(ConfiguredBlockBuilder( - N::TxExecStrategy::execute_transactions(self.0)?, - )) - } - - /// Builds the block and returns the header. - pub fn build( - self, - ) -> Result<<N::BlockBuildStrategy as BlockBuildStrategy<N::TxEssence>>::Output> { - N::BlockBuildStrategy::build(self.0) - } - - /// Returns a reference to the database. - pub fn db(&self) -> Option<&N::Database> { - self.0.db.as_ref() - } - - /// Returns a mutable reference to the database. - pub fn mut_db(&mut self) -> Option<&mut N::Database> { - self.0.db.as_mut() - } -} - -pub struct EthereumStrategyBundle {} - -impl NetworkStrategyBundle for EthereumStrategyBundle { - type Database = MemDb; - type TxEssence = EthereumTxEssence; - type DbInitStrategy = MemDbInitStrategy; - type HeaderPrepStrategy = EthHeaderPrepStrategy; - type TxExecStrategy = EthTxExecStrategy; - type BlockBuildStrategy = BuildFromMemDbStrategy; -} - -pub type EthereumBlockBuilder<'a> = ConfiguredBlockBuilder<'a, EthereumStrategyBundle>; - -pub struct OptimismStrategyBundle {} - -impl NetworkStrategyBundle for OptimismStrategyBundle { - type Database = MemDb; - type TxEssence = OptimismTxEssence; - type DbInitStrategy = MemDbInitStrategy; - type HeaderPrepStrategy = EthHeaderPrepStrategy; - type TxExecStrategy = OpTxExecStrategy; - type BlockBuildStrategy = BuildFromMemDbStrategy; -} - -pub type OptimismBlockBuilder<'a> = ConfiguredBlockBuilder<'a, OptimismStrategyBundle>; diff --git a/lib/src/execution/ethereum.rs b/lib/src/builder/execute/ethereum.rs similarity index 78% rename from lib/src/execution/ethereum.rs rename to lib/src/builder/execute/ethereum.rs index 6ca6e71d7..d1a486fc8 100644 --- a/lib/src/execution/ethereum.rs +++ b/lib/src/builder/execute/ethereum.rs @@ -1,4 +1,4 @@ -// Copyright 2023 RISC Zero, Inc. +// Copyright 2024 RISC Zero, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,8 +18,9 @@ use anyhow::{anyhow, bail, Context}; #[cfg(not(target_os = "zkvm"))] use log::debug; use revm::{ + interpreter::Host, primitives::{Account, Address, ResultAndState, SpecId, TransactTo, TxEnv}, - Database, DatabaseCommit, EVM, + Database, DatabaseCommit, Evm, }; use ruint::aliases::U256; use zeth_primitives::{ @@ -32,13 +33,11 @@ use zeth_primitives::{ Bloom, RlpBytes, }; -use crate::{ - block_builder::BlockBuilder, - consts, - consts::{GWEI_TO_WEI, MIN_SPEC_ID}, - execution::TxExecStrategy, - guest_mem_forget, -}; +use super::TxExecStrategy; +use crate::{builder::BlockBuilder, consts, guest_mem_forget}; + +/// Minimum supported protocol version: Paris (Block no. 15537394). +const MIN_SPEC_ID: SpecId = SpecId::MERGE; pub struct EthTxExecStrategy {} @@ -57,11 +56,7 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { // Compute the spec id let spec_id = block_builder.chain_spec.spec_id(header.number); if !SpecId::enabled(spec_id, MIN_SPEC_ID) { - bail!( - "Invalid protocol version: expected >= {:?}, got {:?}", - MIN_SPEC_ID, - spec_id, - ) + bail!("Invalid protocol version: expected >= {MIN_SPEC_ID:?}, got {spec_id:?}"); } #[cfg(not(target_os = "zkvm"))] @@ -69,12 +64,19 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { use chrono::{TimeZone, Utc}; use log::info; let dt = Utc - .timestamp_opt(block_builder.input.timestamp.try_into().unwrap(), 0) + .timestamp_opt( + block_builder + .input + .timestamp + .try_into() + .expect("Timestamp could not fit into i64"), + 0, + ) .unwrap(); info!("Block no. {}", header.number); - info!(" EVM spec ID: {:?}", spec_id); - info!(" Timestamp: {}", dt); + info!(" EVM spec ID: {spec_id:?}"); + info!(" Timestamp: {dt}"); info!(" Transactions: {}", block_builder.input.transactions.len()); info!(" Withdrawals: {}", block_builder.input.withdrawals.len()); info!(" Fee Recipient: {:?}", block_builder.input.beneficiary); @@ -83,23 +85,26 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { info!(" Extra data: {:?}", block_builder.input.extra_data); } - // initialize the EVM - let mut evm = EVM::new(); - - // set the EVM configuration - evm.env.cfg.chain_id = block_builder.chain_spec.chain_id(); - evm.env.cfg.spec_id = spec_id; - - // set the EVM block environment - evm.env.block.number = header.number.try_into().unwrap(); - evm.env.block.coinbase = block_builder.input.beneficiary; - evm.env.block.timestamp = header.timestamp; - evm.env.block.difficulty = U256::ZERO; - evm.env.block.prevrandao = Some(header.mix_hash); - evm.env.block.basefee = header.base_fee_per_gas; - evm.env.block.gas_limit = block_builder.input.gas_limit; - - evm.database(block_builder.db.take().unwrap()); + // initialize the Evm + let mut evm = Evm::builder() + .spec_id(spec_id) + .modify_cfg_env(|cfg_env| { + // set the EVM configuration + cfg_env.chain_id = block_builder.chain_spec.chain_id(); + // cfg_env.optimism = false; + }) + .modify_block_env(|blk_env| { + // set the EVM block environment + blk_env.number = U256::from(header.number); + blk_env.coinbase = block_builder.input.beneficiary; + blk_env.timestamp = header.timestamp; + blk_env.difficulty = U256::ZERO; + blk_env.prevrandao = Some(header.mix_hash); + blk_env.basefee = header.base_fee_per_gas; + blk_env.gas_limit = block_builder.input.gas_limit; + }) + .with_db(block_builder.db.take().expect("No database")) + .build(); // bloom filter over all transaction logs let mut logs_bloom = Bloom::default(); @@ -116,34 +121,34 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { // verify the transaction signature let tx_from = tx .recover_from() - .with_context(|| format!("Error recovering address for transaction {}", tx_no))?; + .with_context(|| format!("Error recovering address for transaction {tx_no}"))?; #[cfg(not(target_os = "zkvm"))] { let tx_hash = tx.hash(); - debug!("Tx no. {} (hash: {})", tx_no, tx_hash); + debug!("Tx no. {tx_no} (hash: {tx_hash})"); debug!(" Type: {}", tx.essence.tx_type()); - debug!(" Fr: {:?}", tx_from); + debug!(" Fr: {tx_from:?}"); debug!(" To: {:?}", tx.essence.to().unwrap_or_default()); } // verify transaction gas let block_available_gas = block_builder.input.gas_limit - cumulative_gas_used; if block_available_gas < tx.essence.gas_limit() { - bail!("Error at transaction {}: gas exceeds block limit", tx_no); + bail!("Error at transaction {tx_no}: gas exceeds block limit"); } // process the transaction - fill_eth_tx_env(&mut evm.env.tx, &tx.essence, tx_from); + fill_eth_tx_env(&mut evm.env().tx, &tx.essence, tx_from); let ResultAndState { result, state } = evm .transact() - .map_err(|evm_err| anyhow!("Error at transaction {}: {:?}", tx_no, evm_err))?; + .map_err(|evm_err| anyhow!("Error at transaction {tx_no}: {evm_err:?}"))?; let gas_used = result.gas_used().try_into().unwrap(); cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).unwrap(); #[cfg(not(target_os = "zkvm"))] - debug!(" Ok: {:?}", result); + debug!(" Ok: {result:?}"); // create the receipt from the EVM result let receipt = Receipt::new( @@ -173,8 +178,7 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { if account.is_touched() { // log account debug!( - " State {:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={}, is_empty={})", - address, + " State {address:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={}, is_empty={})", account.is_selfdestructed(), account.is_loaded_as_not_existing(), account.is_created(), @@ -189,7 +193,7 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { // log state changes for (addr, slot) in &account.storage { if slot.is_changed() { - debug!(" Storage address: {:?}", addr); + debug!(" Storage address: {addr:?}"); debug!(" Before: {:?}", slot.original_value()); debug!(" After: {:?}", slot.present_value()); } @@ -197,11 +201,9 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { } } - evm.db().unwrap().commit(state); + evm.context.evm.db.commit(state); } - let mut db = evm.take_db(); - // process withdrawals unconditionally after any transactions let mut withdrawals_trie = MptNode::default(); for (i, withdrawal) in take(&mut block_builder.input.withdrawals) @@ -209,7 +211,7 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { .enumerate() { // the withdrawal amount is given in Gwei - let amount_wei = GWEI_TO_WEI + let amount_wei = consts::GWEI_TO_WEI .checked_mul(withdrawal.amount.try_into().unwrap()) .unwrap(); @@ -217,10 +219,10 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { { debug!("Withdrawal no. {}", withdrawal.index); debug!(" Recipient: {:?}", withdrawal.address); - debug!(" Value: {}", amount_wei); + debug!(" Value: {amount_wei}"); } // Credit withdrawal amount - increase_account_balance(&mut db, withdrawal.address, amount_wei)?; + increase_account_balance(&mut evm.context.evm.db, withdrawal.address, amount_wei)?; // Add withdrawal to trie withdrawals_trie .insert_rlp(&i.to_rlp(), withdrawal) @@ -233,16 +235,12 @@ impl TxExecStrategy<EthereumTxEssence> for EthTxExecStrategy { header.receipts_root = receipt_trie.hash(); header.logs_bloom = logs_bloom; header.gas_used = cumulative_gas_used; - header.withdrawals_root = if spec_id < SpecId::SHANGHAI { - None - } else { - Some(withdrawals_trie.hash()) - }; + header.withdrawals_root = (spec_id < SpecId::SHANGHAI).then_some(withdrawals_trie.hash()); // Leak memory, save cycles guest_mem_forget([tx_trie, receipt_trie, withdrawals_trie]); // Return block builder with updated database - Ok(block_builder.with_db(db)) + Ok(block_builder.with_db(evm.context.evm.db)) } } @@ -311,13 +309,7 @@ where // Read account from database let mut account: Account = db .basic(address) - .map_err(|db_err| { - anyhow!( - "Error increasing account balance for {}: {:?}", - address, - db_err - ) - })? + .map_err(|db_err| anyhow!("Error increasing account balance for {address}: {db_err:?}"))? .unwrap_or_default() .into(); // Credit withdrawal amount diff --git a/lib/src/execution/mod.rs b/lib/src/builder/execute/mod.rs similarity index 86% rename from lib/src/execution/mod.rs rename to lib/src/builder/execute/mod.rs index f1a88cb84..a02e2d2be 100644 --- a/lib/src/execution/mod.rs +++ b/lib/src/builder/execute/mod.rs @@ -18,10 +18,13 @@ use anyhow::Result; use revm::{Database, DatabaseCommit}; use zeth_primitives::transactions::TxEssence; -use crate::block_builder::BlockBuilder; +use super::BlockBuilder; -pub mod ethereum; -pub mod optimism; +pub(super) mod ethereum; +#[cfg(feature = "optimism")] +pub(super) mod optimism; +#[cfg(feature = "taiko")] +pub(super) mod taiko; pub trait TxExecStrategy<E: TxEssence> { fn execute_transactions<D>(block_builder: BlockBuilder<D, E>) -> Result<BlockBuilder<D, E>> diff --git a/lib/src/builder/execute/optimism.rs b/lib/src/builder/execute/optimism.rs new file mode 100644 index 000000000..ee52e19b2 --- /dev/null +++ b/lib/src/builder/execute/optimism.rs @@ -0,0 +1,269 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use alloc::{format, vec, vec::Vec}; +use core::{fmt::Debug, mem::take, str::FromStr}; + +use anyhow::{anyhow, bail, Context, Result}; +#[cfg(not(target_os = "zkvm"))] +use log::debug; +use revm::{ + interpreter::Host, + optimism, + primitives::{Address, ResultAndState, SpecId, TransactTo, TxEnv}, + Database, DatabaseCommit, Evm, +}; +use ruint::aliases::U256; +use zeth_primitives::{ + receipt::Receipt, + transactions::{ + ethereum::{EthereumTxEssence, TransactionKind}, + optimism::{OptimismTxEssence, TxEssenceOptimismDeposited}, + TxEssence, + }, + trie::MptNode, + Bloom, Bytes, RlpBytes, +}; + +use super::{ethereum, TxExecStrategy}; +use crate::{builder::BlockBuilder, consts, guest_mem_forget}; + +/// Minimum supported protocol version: Bedrock (Block no. 105235063). +const MIN_SPEC_ID: SpecId = SpecId::BEDROCK; + +pub struct OpTxExecStrategy {} + +impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy { + fn execute_transactions<D>( + mut block_builder: BlockBuilder<D, OptimismTxEssence>, + ) -> Result<BlockBuilder<D, OptimismTxEssence>> + where + D: Database + DatabaseCommit, + <D as Database>::Error: Debug, + { + let header = block_builder + .header + .as_mut() + .expect("Header is not initialized"); + // Compute the spec id + let spec_id = block_builder.chain_spec.spec_id(header.number); + if !SpecId::enabled(spec_id, MIN_SPEC_ID) { + bail!("Invalid protocol version: expected >= {MIN_SPEC_ID:?}, got {spec_id:?}") + } + let chain_id = block_builder.chain_spec.chain_id(); + + #[cfg(not(target_os = "zkvm"))] + { + use chrono::{TimeZone, Utc}; + use log::info; + let dt = Utc + .timestamp_opt( + block_builder + .input + .timestamp + .try_into() + .expect("Timestamp could not fit into i64"), + 0, + ) + .unwrap(); + + info!("Block no. {}", header.number); + info!(" EVM spec ID: {spec_id:?}"); + info!(" Timestamp: {dt}"); + info!(" Transactions: {}", block_builder.input.transactions.len()); + info!(" Fee Recipient: {:?}", block_builder.input.beneficiary); + info!(" Gas limit: {}", block_builder.input.gas_limit); + info!(" Base fee per gas: {}", header.base_fee_per_gas); + info!(" Extra data: {:?}", block_builder.input.extra_data); + } + + let mut evm = Evm::builder() + .spec_id(spec_id) + .modify_cfg_env(|cfg_env| { + // set the EVM configuration + cfg_env.chain_id = chain_id; + cfg_env.optimism = true; + }) + .modify_block_env(|blk_env| { + // set the EVM block environment + blk_env.number = U256::from(header.number); + blk_env.coinbase = block_builder.input.beneficiary; + blk_env.timestamp = header.timestamp; + blk_env.difficulty = U256::ZERO; + blk_env.prevrandao = Some(header.mix_hash); + blk_env.basefee = header.base_fee_per_gas; + blk_env.gas_limit = block_builder.input.gas_limit; + }) + .with_db(block_builder.db.take().unwrap()) + .append_handler_register(optimism::optimism_handle_register) + .build(); + + // bloom filter over all transaction logs + let mut logs_bloom = Bloom::default(); + // keep track of the gas used over all transactions + let mut cumulative_gas_used = consts::ZERO; + + // process all the transactions + let mut tx_trie = MptNode::default(); + let mut receipt_trie = MptNode::default(); + for (tx_no, tx) in take(&mut block_builder.input.transactions) + .into_iter() + .enumerate() + { + // verify the transaction signature + let tx_from = tx + .recover_from() + .with_context(|| format!("Error recovering address for transaction {tx_no}"))?; + + #[cfg(not(target_os = "zkvm"))] + { + let tx_hash = tx.hash(); + debug!("Tx no. {tx_no} (hash: {tx_hash})"); + debug!(" Type: {}", tx.essence.tx_type()); + debug!(" Fr: {tx_from:?}"); + debug!(" To: {:?}", tx.essence.to().unwrap_or_default()); + } + + // verify transaction gas + let block_available_gas = block_builder.input.gas_limit - cumulative_gas_used; + if block_available_gas < tx.essence.gas_limit() { + bail!("Error at transaction {tx_no}: gas exceeds block limit"); + } + + match &tx.essence { + OptimismTxEssence::OptimismDeposited(deposit) => { + #[cfg(not(target_os = "zkvm"))] + { + debug!(" Source: {:?}", &deposit.source_hash); + debug!(" Mint: {:?}", &deposit.mint); + debug!(" System Tx: {:?}", deposit.is_system_tx); + } + + // Initialize tx environment + fill_deposit_tx_env(&mut evm.env().tx, deposit, tx_from); + } + OptimismTxEssence::Ethereum(essence) => { + fill_eth_tx_env(&mut evm.env().tx, tx.to_rlp(), essence, tx_from); + } + }; + + // process the transaction + let ResultAndState { result, state } = evm + .transact() + .map_err(|evm_err| anyhow!("Error at transaction {tx_no}: {evm_err:?}"))?; + + let gas_used = result.gas_used().try_into().unwrap(); + cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).unwrap(); + + #[cfg(not(target_os = "zkvm"))] + debug!(" Ok: {result:?}"); + + // create the receipt from the EVM result + let receipt = Receipt::new( + tx.essence.tx_type(), + result.is_success(), + cumulative_gas_used, + result.logs().into_iter().map(|log| log.into()).collect(), + ); + + // update account states + #[cfg(not(target_os = "zkvm"))] + for (address, account) in &state { + if account.is_touched() { + // log account + debug!( + " State {address:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={})", + account.is_selfdestructed(), + account.is_loaded_as_not_existing(), + account.is_created() + ); + // log balance changes + debug!( + " After balance: {} (Nonce: {})", + account.info.balance, account.info.nonce + ); + + // log state changes + for (addr, slot) in &account.storage { + if slot.is_changed() { + debug!(" Storage address: {addr:?}"); + debug!(" Before: {:?}", slot.original_value()); + debug!(" After: {:?}", slot.present_value()); + } + } + } + } + + evm.context.evm.db.commit(state); + + // accumulate logs to the block bloom filter + logs_bloom.accrue_bloom(&receipt.payload.logs_bloom); + + // Add receipt and tx to tries + let trie_key = tx_no.to_rlp(); + tx_trie + .insert_rlp(&trie_key, tx) + .map_err(Into::<anyhow::Error>::into) + .context("failed to insert transaction")?; + receipt_trie + .insert_rlp(&trie_key, receipt) + .map_err(Into::<anyhow::Error>::into) + .context("failed to insert receipt")?; + } + + // Update result header with computed values + header.transactions_root = tx_trie.hash(); + header.receipts_root = receipt_trie.hash(); + header.logs_bloom = logs_bloom; + header.gas_used = cumulative_gas_used; + header.withdrawals_root = None; + + // Leak memory, save cycles + guest_mem_forget([tx_trie, receipt_trie]); + // Return block builder with updated database + Ok(block_builder.with_db(evm.context.evm.db)) + } +} + +fn fill_deposit_tx_env(tx_env: &mut TxEnv, essence: &TxEssenceOptimismDeposited, caller: Address) { + // initialize additional optimism tx fields + tx_env.optimism.source_hash = Some(essence.source_hash); + tx_env.optimism.mint = Some(essence.mint.try_into().unwrap()); + tx_env.optimism.is_system_transaction = Some(essence.is_system_tx); + tx_env.optimism.enveloped_tx = None; // only used for non-deposit txs + + tx_env.caller = caller; // previously overridden to tx.from + tx_env.gas_limit = essence.gas_limit.try_into().unwrap(); + tx_env.gas_price = U256::ZERO; + tx_env.gas_priority_fee = None; + tx_env.transact_to = if let TransactionKind::Call(to_addr) = essence.to { + TransactTo::Call(to_addr) + } else { + TransactTo::create() + }; + tx_env.value = essence.value; + tx_env.data = essence.data.clone(); + tx_env.chain_id = None; + tx_env.nonce = None; + tx_env.access_list.clear(); +} + +fn fill_eth_tx_env(tx_env: &mut TxEnv, tx: Vec<u8>, essence: &EthereumTxEssence, caller: Address) { + // initialize additional optimism tx fields + tx_env.optimism.source_hash = None; + tx_env.optimism.mint = None; + tx_env.optimism.is_system_transaction = Some(false); + tx_env.optimism.enveloped_tx = Some(Bytes::from(tx)); + + ethereum::fill_eth_tx_env(tx_env, essence, caller); +} diff --git a/lib/src/builder/execute/taiko.rs b/lib/src/builder/execute/taiko.rs new file mode 100644 index 000000000..1cfa6df27 --- /dev/null +++ b/lib/src/builder/execute/taiko.rs @@ -0,0 +1,249 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::{fmt::Debug, mem::take, str::from_utf8}; + +use anyhow::{anyhow, bail, Context, Result}; +#[cfg(not(target_os = "zkvm"))] +use log::debug; +use revm::{ + interpreter::Host, + primitives::{Address, ResultAndState, SpecId, TxEnv}, + taiko, Database, DatabaseCommit, Evm, +}; +use ruint::aliases::U256; +use zeth_primitives::{ + receipt::Receipt, + transactions::{ethereum::EthereumTxEssence, TxEssence}, + trie::MptNode, + Bloom, RlpBytes, +}; + +use super::{ethereum, TxExecStrategy}; +use crate::{ + builder::BlockBuilder, + consts::{self, ChainSpec}, + guest_mem_forget, +}; + +/// Minimum supported protocol version: Bedrock (Block no. 105235063). +const MIN_SPEC_ID: SpecId = SpecId::SHANGHAI /*change*/; + +pub struct TkoTxExecStrategy {} + +impl TxExecStrategy<EthereumTxEssence> for TkoTxExecStrategy { + fn execute_transactions<D>( + mut block_builder: BlockBuilder<D, EthereumTxEssence>, + ) -> Result<BlockBuilder<D, EthereumTxEssence>> + where + D: Database + DatabaseCommit, + <D as Database>::Error: Debug, + { + let header = block_builder + .header + .as_mut() + .expect("Header is not initialized"); + // Compute the spec id + let spec_id = block_builder.chain_spec.spec_id(header.number); + if !SpecId::enabled(spec_id, MIN_SPEC_ID) { + bail!("Invalid protocol version: expected >= {MIN_SPEC_ID:?}, got {spec_id:?}") + } + let chain_id = block_builder.chain_spec.chain_id(); + + #[cfg(not(target_os = "zkvm"))] + { + use chrono::{TimeZone, Utc}; + use log::info; + let dt = Utc + .timestamp_opt( + block_builder + .input + .timestamp + .try_into() + .expect("Timestamp could not fit into i64"), + 0, + ) + .unwrap(); + + info!("Block no. {}", header.number); + info!(" EVM spec ID: {spec_id:?}"); + info!(" Timestamp: {dt}"); + info!(" Transactions: {}", block_builder.input.transactions.len()); + info!(" Fee Recipient: {:?}", block_builder.input.beneficiary); + info!(" Gas limit: {}", block_builder.input.gas_limit); + info!(" Base fee per gas: {}", header.base_fee_per_gas); + info!(" Extra data: {:?}", block_builder.input.extra_data); + } + + let mut evm = Evm::builder() + .spec_id(spec_id) + .modify_cfg_env(|cfg_env| { + // set the EVM configuration + cfg_env.chain_id = chain_id; + cfg_env.taiko = true; + }) + .modify_block_env(|blk_env| { + // set the EVM block environment + blk_env.number = U256::from(header.number); + blk_env.coinbase = block_builder.input.beneficiary; + blk_env.timestamp = header.timestamp; + blk_env.difficulty = U256::ZERO; + blk_env.prevrandao = Some(header.mix_hash); + blk_env.basefee = header.base_fee_per_gas; + blk_env.gas_limit = block_builder.input.gas_limit; + }) + .with_db(block_builder.db.take().unwrap()) + .append_handler_register(taiko::handler_register::taiko_handle_register) + .build(); + + // bloom filter over all transaction logs + let mut logs_bloom = Bloom::default(); + // keep track of the gas used over all transactions + let mut cumulative_gas_used = consts::ZERO; + + // process all the transactions + let mut tx_trie = MptNode::default(); + let mut receipt_trie = MptNode::default(); + #[allow(unused_variables)] + let mut actual_tx_no = 0usize; + + for (tx_no, tx) in take(&mut block_builder.input.transactions) + .into_iter() + .enumerate() + { + // anchor transaction must be executed successfully + let is_anchor = tx_no == 0; + // verify the transaction signature + let tx_from = tx + .recover_from() + .with_context(|| anyhow!("Error recovering address for transaction {tx_no}"))?; + + #[cfg(not(target_os = "zkvm"))] + { + let tx_hash = tx.hash(); + debug!("Tx no. {tx_no} (hash: {tx_hash})"); + debug!(" Type: {}", tx.essence.tx_type()); + debug!(" Fr: {tx_from:?}"); + debug!(" To: {:?}", tx.essence.to().unwrap_or_default()); + } + + // verify transaction gas + let block_available_gas = block_builder.input.gas_limit - cumulative_gas_used; + if block_available_gas < tx.essence.gas_limit() { + bail!("Error at transaction {tx_no}: gas exceeds block limit"); + } + + fill_eth_tx_env( + block_builder.chain_spec, + &mut evm.env().tx, + &tx.essence, + tx_from, + is_anchor, + ); + + // process the transaction + let ResultAndState { result, state } = evm + .transact() + .map_err(|evm_err| anyhow!("Error at transaction {tx_no}: {evm_err:?}"))?; + + if is_anchor && !result.is_success() { + bail!( + "Error at transaction {tx_no}: execute anchor failed {result:?}, output {:?}", + result.output().map(|o| from_utf8(o).unwrap_or_default()) + ); + } + + let gas_used = result.gas_used().try_into().unwrap(); + cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).unwrap(); + + #[cfg(not(target_os = "zkvm"))] + debug!(" Ok: {result:?}"); + + // create the receipt from the EVM result + let receipt = Receipt::new( + tx.essence.tx_type(), + result.is_success(), + cumulative_gas_used, + result.logs().into_iter().map(|log| log.into()).collect(), + ); + + // update account states + #[cfg(not(target_os = "zkvm"))] + for (address, account) in &state { + if account.is_touched() { + // log account + debug!( + " State {address:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={})", + account.is_selfdestructed(), + account.is_loaded_as_not_existing(), + account.is_created() + ); + // log balance changes + debug!( + " After balance: {} (Nonce: {})", + account.info.balance, account.info.nonce + ); + + // log state changes + for (addr, slot) in &account.storage { + if slot.is_changed() { + debug!(" Storage address: {addr:?}"); + debug!(" Before: {:?}", slot.original_value()); + debug!(" After: {:?}", slot.present_value()); + } + } + } + } + + actual_tx_no += 1; + + evm.context.evm.db.commit(state); + + // accumulate logs to the block bloom filter + logs_bloom.accrue_bloom(&receipt.payload.logs_bloom); + + // Add receipt and tx to tries + let trie_key = tx_no.to_rlp(); + tx_trie.insert_rlp(&trie_key, tx)?; + receipt_trie.insert_rlp(&trie_key, receipt)?; + } + + // Update result header with computed values + header.transactions_root = tx_trie.hash(); + header.receipts_root = receipt_trie.hash(); + header.logs_bloom = logs_bloom; + header.gas_used = cumulative_gas_used; + header.withdrawals_root = None; + + // Leak memory, save cycles + guest_mem_forget([tx_trie, receipt_trie]); + // Return block builder with updated database + Ok(block_builder.with_db(evm.context.evm.db)) + } +} + +pub fn fill_eth_tx_env( + _l2_chain_spec: &ChainSpec, + tx_env: &mut TxEnv, + essence: &EthereumTxEssence, + caller: Address, + is_anchor: bool, +) { + // claim the anchor + tx_env.taiko.is_anchor = is_anchor; + // set the treasury address + tx_env.taiko.treasury = *crate::taiko::consts::testnet::L2_CONTRACT; + + ethereum::fill_eth_tx_env(tx_env, essence, caller); +} diff --git a/lib/src/finalization.rs b/lib/src/builder/finalize.rs similarity index 56% rename from lib/src/finalization.rs rename to lib/src/builder/finalize.rs index 91972aa30..9cf1c3ddb 100644 --- a/lib/src/finalization.rs +++ b/lib/src/builder/finalize.rs @@ -12,54 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::mem; + use anyhow::Result; -use hashbrown::HashMap; -use revm::primitives::Address; +use revm::{Database, DatabaseCommit}; use zeth_primitives::{ block::Header, keccak::keccak, transactions::TxEssence, trie::{MptNode, StateAccount}, - U256, }; use crate::{ - block_builder::BlockBuilder, + builder::BlockBuilder, guest_mem_forget, mem_db::{AccountState, MemDb}, }; -pub trait BlockBuildStrategy<E: TxEssence> { - type Database; - type Output; - - fn build(block_builder: BlockBuilder<Self::Database, E>) -> Result<Self::Output>; +pub trait BlockFinalizeStrategy<D> +where + D: Database + DatabaseCommit, + <D as Database>::Error: core::fmt::Debug, +{ + fn finalize<E>(block_builder: BlockBuilder<D, E>) -> Result<(Header, MptNode)> + where + E: TxEssence; } -pub struct BuildFromMemDbStrategy {} +pub struct MemDbBlockFinalizeStrategy {} -impl BuildFromMemDbStrategy { - pub fn build_header<E: TxEssence>( - debug_storage_tries: &mut Option<HashMap<Address, MptNode>>, +impl BlockFinalizeStrategy<MemDb> for MemDbBlockFinalizeStrategy { + fn finalize<E: TxEssence>( mut block_builder: BlockBuilder<MemDb, E>, - ) -> Result<Header> { - let db = block_builder.db.as_ref().unwrap(); + ) -> Result<(Header, MptNode)> { + let db = block_builder.db.take().expect("DB not initialized"); // apply state updates - let state_trie = &mut block_builder.input.parent_state_trie; + let mut state_trie = mem::take(&mut block_builder.input.parent_state_trie); for (address, account) in &db.accounts { // if the account has not been touched, it can be ignored if account.state == AccountState::None { - if let Some(map) = debug_storage_tries { - let storage_root = block_builder - .input - .parent_storage - .get(address) - .unwrap() - .0 - .clone(); - map.insert(*address, storage_root); - } continue; } @@ -77,8 +69,11 @@ impl BuildFromMemDbStrategy { let storage_root = { // getting a mutable reference is more efficient than calling remove // every account must have an entry, even newly created accounts - let (storage_trie, _) = - block_builder.input.parent_storage.get_mut(address).unwrap(); + let (storage_trie, _) = block_builder + .input + .parent_storage + .get_mut(address) + .expect("Address not found in storage"); // for cleared accounts always start from the empty trie if account.state == AccountState::StorageCleared { storage_trie.clear(); @@ -87,18 +82,13 @@ impl BuildFromMemDbStrategy { // apply all new storage entries for the current account (address) for (key, value) in state_storage { let storage_trie_index = keccak(key.to_be_bytes::<32>()); - if value == &U256::ZERO { + if value.is_zero() { storage_trie.delete(&storage_trie_index)?; } else { storage_trie.insert_rlp(&storage_trie_index, *value)?; } } - // insert the storage trie for host debugging - if let Some(map) = debug_storage_tries { - map.insert(*address, storage_trie.clone()); - } - storage_trie.hash() }; @@ -112,38 +102,12 @@ impl BuildFromMemDbStrategy { } // update result header with the new state root - let mut header = block_builder - .header - .take() - .expect("Header was not initialized"); + let mut header = block_builder.header.take().expect("Header not initialized"); header.state_root = state_trie.hash(); // Leak memory, save cycles guest_mem_forget(block_builder); - Ok(header) - } -} - -impl<E: TxEssence> BlockBuildStrategy<E> for BuildFromMemDbStrategy { - type Database = MemDb; - type Output = Header; - - #[inline(always)] - fn build(block_builder: BlockBuilder<Self::Database, E>) -> Result<Self::Output> { - BuildFromMemDbStrategy::build_header(&mut None, block_builder) - } -} - -pub struct DebugBuildFromMemDbStrategy {} - -impl<E: TxEssence> BlockBuildStrategy<E> for DebugBuildFromMemDbStrategy { - type Database = MemDb; - type Output = (Header, HashMap<Address, MptNode>); - - fn build(block_builder: BlockBuilder<Self::Database, E>) -> Result<Self::Output> { - let mut storage_trace = Some(Default::default()); - let header = BuildFromMemDbStrategy::build_header(&mut storage_trace, block_builder)?; - Ok((header, storage_trace.unwrap())) + Ok((header, state_trie)) } } diff --git a/lib/src/initialization.rs b/lib/src/builder/initialize.rs similarity index 84% rename from lib/src/initialization.rs rename to lib/src/builder/initialize.rs index 057c5af14..2d8290785 100644 --- a/lib/src/initialization.rs +++ b/lib/src/builder/initialize.rs @@ -16,7 +16,10 @@ use core::mem; use anyhow::{bail, Result}; use hashbrown::HashMap; -use revm::primitives::{AccountInfo, Bytecode, B256}; +use revm::{ + primitives::{AccountInfo, Bytecode, B256}, + Database, DatabaseCommit, +}; use zeth_primitives::{ keccak::{keccak, KECCAK_EMPTY}, transactions::TxEssence, @@ -25,28 +28,28 @@ use zeth_primitives::{ }; use crate::{ - block_builder::BlockBuilder, + builder::BlockBuilder, consts::MAX_BLOCK_HASH_AGE, guest_mem_forget, mem_db::{AccountState, DbAccount, MemDb}, }; -pub trait DbInitStrategy<E: TxEssence> { - type Database; - - fn initialize_database( - block_builder: BlockBuilder<Self::Database, E>, - ) -> Result<BlockBuilder<Self::Database, E>>; +pub trait DbInitStrategy<D> +where + D: Database + DatabaseCommit, + <D as Database>::Error: core::fmt::Debug, +{ + fn initialize_database<E>(block_builder: BlockBuilder<D, E>) -> Result<BlockBuilder<D, E>> + where + E: TxEssence; } pub struct MemDbInitStrategy {} -impl<E: TxEssence> DbInitStrategy<E> for MemDbInitStrategy { - type Database = MemDb; - - fn initialize_database( - mut block_builder: BlockBuilder<Self::Database, E>, - ) -> Result<BlockBuilder<Self::Database, E>> { +impl DbInitStrategy<MemDb> for MemDbInitStrategy { + fn initialize_database<E: TxEssence>( + mut block_builder: BlockBuilder<MemDb, E>, + ) -> Result<BlockBuilder<MemDb, E>> { // Verify state trie root if block_builder.input.parent_state_trie.hash() != block_builder.input.parent_header.state_root @@ -79,8 +82,7 @@ impl<E: TxEssence> DbInitStrategy<E> for MemDbInitStrategy { // Verify storage trie root if storage_trie.hash() != state_account.storage_root { bail!( - "Invalid storage trie for {:?}: expected {}, got {}", - address, + "Invalid storage trie for {address:?}: expected {}, got {}", state_account.storage_root, storage_trie.hash() ); @@ -91,7 +93,10 @@ impl<E: TxEssence> DbInitStrategy<E> for MemDbInitStrategy { let bytecode = if code_hash.0 == KECCAK_EMPTY.0 { Bytecode::new() } else { - let bytes = contracts.get(&code_hash).unwrap().clone(); + let bytes = contracts + .get(&code_hash) + .expect("Contract not found") + .clone(); Bytecode::new_raw(bytes) }; @@ -140,9 +145,8 @@ impl<E: TxEssence> DbInitStrategy<E> for MemDbInitStrategy { || block_builder.input.parent_header.number - current.number >= MAX_BLOCK_HASH_AGE { bail!( - "Invalid chain: {} is not one of the {} most recent blocks", + "Invalid chain: {} is not one of the {MAX_BLOCK_HASH_AGE} most recent blocks", current.number, - MAX_BLOCK_HASH_AGE, ); } block_hashes.insert(current.number, current_hash); diff --git a/lib/src/builder/mod.rs b/lib/src/builder/mod.rs new file mode 100644 index 000000000..e2321a22d --- /dev/null +++ b/lib/src/builder/mod.rs @@ -0,0 +1,163 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use anyhow::Result; +use revm::{Database, DatabaseCommit}; +use zeth_primitives::{ + block::Header, + transactions::{ethereum::EthereumTxEssence, TxEssence}, + trie::MptNode, +}; + +#[cfg(feature = "optimism")] +pub use self::execute::optimism::OpTxExecStrategy; +#[cfg(feature = "optimism")] +use crate::OptimismTxEssence; +#[cfg(feature = "taiko")] +pub use self::execute::taiko::TkoTxExecStrategy; +use crate::{ + builder::{ + execute::{ethereum::EthTxExecStrategy, TxExecStrategy}, + finalize::{BlockFinalizeStrategy, MemDbBlockFinalizeStrategy}, + initialize::{DbInitStrategy, MemDbInitStrategy}, + prepare::{EthHeaderPrepStrategy, HeaderPrepStrategy}, + }, + consts::ChainSpec, + input::Input, + mem_db::MemDb, +}; + +mod execute; +mod finalize; +mod initialize; +pub mod prepare; + +/// A generic builder for building a block. +#[derive(Clone, Debug)] +pub struct BlockBuilder<'a, D, E: TxEssence> { + pub(crate) chain_spec: &'a ChainSpec, + pub(crate) input: Input<E>, + pub(crate) db: Option<D>, + pub(crate) header: Option<Header>, +} + +impl<D, E> BlockBuilder<'_, D, E> +where + D: Database + DatabaseCommit, + <D as Database>::Error: core::fmt::Debug, + E: TxEssence, +{ + /// Creates a new block builder. + pub fn new(chain_spec: &ChainSpec, input: Input<E>) -> BlockBuilder<'_, D, E> { + BlockBuilder { + chain_spec, + db: None, + header: None, + input, + } + } + + /// Sets the database instead of initializing it from the input. + pub fn with_db(mut self, db: D) -> Self { + self.db = Some(db); + self + } + + /// Initializes the database from the input. + pub fn initialize_database<T: DbInitStrategy<D>>(self) -> Result<Self> { + T::initialize_database(self) + } + + /// Initializes the header. This must be called before executing transactions. + pub fn prepare_header<T: HeaderPrepStrategy>(self) -> Result<Self> { + T::prepare_header(self) + } + + /// Executes all input transactions. + pub fn execute_transactions<T: TxExecStrategy<E>>(self) -> Result<Self> { + T::execute_transactions(self) + } + + /// Finalizes the block building and returns the header and the state trie. + pub fn finalize<T: BlockFinalizeStrategy<D>>(self) -> Result<(Header, MptNode)> { + T::finalize(self) + } + + /// Returns a reference to the database. + pub fn db(&self) -> Option<&D> { + self.db.as_ref() + } + + /// Returns a mutable reference to the database. + pub fn mut_db(&mut self) -> Option<&mut D> { + self.db.as_mut() + } +} + +/// A bundle of strategies for building a block using [BlockBuilder]. +pub trait BlockBuilderStrategy { + type TxEssence: TxEssence; + + type DbInitStrategy: DbInitStrategy<MemDb>; + type HeaderPrepStrategy: HeaderPrepStrategy; + type TxExecStrategy: TxExecStrategy<Self::TxEssence>; + type BlockFinalizeStrategy: BlockFinalizeStrategy<MemDb>; + + /// Builds a block from the given input. + fn build_from( + chain_spec: &ChainSpec, + input: Input<Self::TxEssence>, + ) -> Result<(Header, MptNode)> { + BlockBuilder::<MemDb, Self::TxEssence>::new(chain_spec, input) + .initialize_database::<Self::DbInitStrategy>()? + .prepare_header::<Self::HeaderPrepStrategy>()? + .execute_transactions::<Self::TxExecStrategy>()? + .finalize::<Self::BlockFinalizeStrategy>() + } +} + +/// The [BlockBuilderStrategy] for building an Ethereum block. +pub struct EthereumStrategy {} + +impl BlockBuilderStrategy for EthereumStrategy { + type TxEssence = EthereumTxEssence; + type DbInitStrategy = MemDbInitStrategy; + type HeaderPrepStrategy = EthHeaderPrepStrategy; + type TxExecStrategy = EthTxExecStrategy; + type BlockFinalizeStrategy = MemDbBlockFinalizeStrategy; +} + +/// The [BlockBuilderStrategy] for building an Optimism block. +#[cfg(feature = "optimism")] +pub struct OptimismStrategy {} +#[cfg(feature = "optimism")] +impl BlockBuilderStrategy for OptimismStrategy { + type TxEssence = OptimismTxEssence; + type DbInitStrategy = MemDbInitStrategy; + type HeaderPrepStrategy = EthHeaderPrepStrategy; + type TxExecStrategy = OpTxExecStrategy; + type BlockFinalizeStrategy = MemDbBlockFinalizeStrategy; +} + +/// The [BlockBuilderStrategy] for building an Optimism block. +#[cfg(feature = "taiko")] +pub struct TaikoStrategy {} +#[cfg(feature = "taiko")] +impl BlockBuilderStrategy for TaikoStrategy { + type TxEssence = EthereumTxEssence; + type DbInitStrategy = MemDbInitStrategy; + type HeaderPrepStrategy = EthHeaderPrepStrategy; + type TxExecStrategy = TkoTxExecStrategy; + type BlockFinalizeStrategy = MemDbBlockFinalizeStrategy; +} diff --git a/lib/src/preparation.rs b/lib/src/builder/prepare.rs similarity index 90% rename from lib/src/preparation.rs rename to lib/src/builder/prepare.rs index 81c5c4e36..f2613ca07 100644 --- a/lib/src/preparation.rs +++ b/lib/src/builder/prepare.rs @@ -19,7 +19,7 @@ use revm::{Database, DatabaseCommit}; use zeth_primitives::{block::Header, transactions::TxEssence, U256}; use crate::{ - block_builder::BlockBuilder, + builder::BlockBuilder, consts::{Eip1559Constants, GAS_LIMIT_BOUND_DIVISOR, MAX_EXTRA_DATA_BYTES, MIN_GAS_LIMIT, ONE}, }; @@ -27,7 +27,7 @@ pub trait HeaderPrepStrategy { fn prepare_header<D, E>(block_builder: BlockBuilder<D, E>) -> Result<BlockBuilder<D, E>> where D: Database + DatabaseCommit, - <D as Database>::Error: Debug, + <D as Database>::Error: core::fmt::Debug, E: TxEssence; } @@ -49,16 +49,14 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy { let limit = block_builder.input.parent_header.gas_limit / GAS_LIMIT_BOUND_DIVISOR; if diff >= limit { bail!( - "Invalid gas limit: expected {} +- {}, got {}", + "Invalid gas limit: expected {} +- {limit}, got {}", block_builder.input.parent_header.gas_limit, - limit, block_builder.input.gas_limit, ); } if block_builder.input.gas_limit < MIN_GAS_LIMIT { bail!( - "Invalid gas limit: expected >= {}, got {}", - MIN_GAS_LIMIT, + "Invalid gas limit: expected >= {MIN_GAS_LIMIT}, got {}", block_builder.input.gas_limit, ); } @@ -73,11 +71,7 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy { // Validate extra data let extra_data_bytes = block_builder.input.extra_data.len(); if extra_data_bytes > MAX_EXTRA_DATA_BYTES { - bail!( - "Invalid extra data: expected <= {}, got {}", - MAX_EXTRA_DATA_BYTES, - extra_data_bytes, - ) + bail!("Invalid extra data: expected <= {MAX_EXTRA_DATA_BYTES}, got {extra_data_bytes}"); } // Derive header block_builder.header = Some(Header { @@ -107,8 +101,7 @@ impl HeaderPrepStrategy for EthHeaderPrepStrategy { } /// Base fee for next block. [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) spec -#[inline(always)] -pub fn derive_base_fee(parent: &Header, eip_1559_constants: &Eip1559Constants) -> Result<U256> { +fn derive_base_fee(parent: &Header, eip_1559_constants: &Eip1559Constants) -> Result<U256> { let parent_gas_target = parent.gas_limit / eip_1559_constants.elasticity_multiplier; match parent.gas_used.cmp(&parent_gas_target) { diff --git a/lib/src/consts.rs b/lib/src/consts.rs index 5c7d1e3c1..69c49b158 100644 --- a/lib/src/consts.rs +++ b/lib/src/consts.rs @@ -13,6 +13,7 @@ // limitations under the License. //! Constants for the Ethereum protocol. +extern crate alloc; use alloc::{ collections::BTreeMap, @@ -24,7 +25,7 @@ use anyhow::bail; use once_cell::sync::Lazy; use revm::primitives::SpecId; use serde::{Deserialize, Serialize}; -use zeth_primitives::{uint, Address, BlockNumber, ChainId, U256}; +use zeth_primitives::{uint, BlockNumber, ChainId, U256}; /// U256 representation of 0. pub const ZERO: U256 = U256::ZERO; @@ -45,9 +46,6 @@ pub const MAX_BLOCK_HASH_AGE: u64 = 256; /// Multiplier for converting gwei to wei. pub const GWEI_TO_WEI: U256 = uint!(1_000_000_000_U256); -/// Minimum supported protocol version: Paris (Block no. 15537394). -pub const MIN_SPEC_ID: SpecId = SpecId::MERGE; - /// The Ethereum mainnet specification. pub static ETH_MAINNET_CHAIN_SPEC: Lazy<ChainSpec> = Lazy::new(|| { ChainSpec { @@ -65,66 +63,49 @@ pub static ETH_MAINNET_CHAIN_SPEC: Lazy<ChainSpec> = Lazy::new(|| { base_fee_max_decrease_denominator: uint!(8_U256), elasticity_multiplier: uint!(2_U256), }, - l1_contract: None, - l1_signal_service: None, - l2_contract: None, - l2_signal_service: None, } }); +/// The Taiko mainnet specification. #[cfg(feature = "taiko")] -macro_rules! taiko_chain_spec { - ($a:ident, $b:ident ) => { - pub static $a: Lazy<ChainSpec> = Lazy::new(|| { - use zeth_primitives::taiko::$b::*; - ChainSpec { - chain_id: CHAIN_ID, - hard_forks: BTreeMap::from([ - (SpecId::SHANGHAI, ForkCondition::Block(0)), - (SpecId::CANCUN, ForkCondition::TBD), - ]), - eip_1559_constants: Eip1559Constants { - base_fee_change_denominator: uint!(8_U256), - base_fee_max_increase_denominator: uint!(8_U256), - base_fee_max_decrease_denominator: uint!(8_U256), - elasticity_multiplier: uint!(2_U256), - }, - l1_contract: Some(*L1_CONTRACT), - l1_signal_service: Some(*L1_SIGNAL_SERVICE), - l2_contract: Some(*L2_CONTRACT), - l2_signal_service: Some(*L2_SIGNAL_SERVICE), - } - }); - }; -} - -taiko_chain_spec!(TAIKO_TESTNET_CHAIN_SPEC, testnet); -taiko_chain_spec!(TAIKO_INTERNAL_DEVNET_A_CHAIN_SPEC, internal_devnet_a); -taiko_chain_spec!(TAIKO_INTERNAL_DEVNET_B_CHAIN_SPEC, internal_devnet_b); - -pub fn get_taiko_chain_spec(chain: &str) -> ChainSpec { - match chain { - "testnet" => TAIKO_TESTNET_CHAIN_SPEC.clone(), - "internal_devnet_a" => TAIKO_INTERNAL_DEVNET_A_CHAIN_SPEC.clone(), - "internal_devnet_b" => TAIKO_INTERNAL_DEVNET_B_CHAIN_SPEC.clone(), - _ => panic!("unknown chain"), +pub static TKO_MAINNET_CHAIN_SPEC: Lazy<ChainSpec> = Lazy::new(|| { + ChainSpec { + chain_id: 1, + hard_forks: BTreeMap::from([ + (SpecId::SHANGHAI, ForkCondition::Block(0)), + (SpecId::SHANGHAI /* change */, ForkCondition::Block(0)), + // previous versions not supported + (SpecId::CANCUN, ForkCondition::TBD), + ]), + eip_1559_constants: Eip1559Constants { + base_fee_change_denominator: uint!(8_U256), + base_fee_max_increase_denominator: uint!(8_U256), + base_fee_max_decrease_denominator: uint!(8_U256), + elasticity_multiplier: uint!(2_U256), + }, } -} +}); + +#[cfg(feature = "taiko")] +pub use crate::taiko::consts::testnet::*; -/// The optimism mainnet specification. +/// The Optimism mainnet specification. +#[cfg(feature = "optimism")] pub static OP_MAINNET_CHAIN_SPEC: Lazy<ChainSpec> = Lazy::new(|| ChainSpec { chain_id: 10, - hard_forks: BTreeMap::from([(SpecId::MERGE, ForkCondition::Block(0))]), + hard_forks: BTreeMap::from([ + (SpecId::FRONTIER, ForkCondition::Block(0)), + // previous versions not supported + (SpecId::BEDROCK, ForkCondition::Block(105235063)), + // Regolith is activated from day 1 of Bedrock on mainnet + (SpecId::REGOLITH, ForkCondition::Block(105235063)), + ]), eip_1559_constants: Eip1559Constants { base_fee_change_denominator: uint!(50_U256), base_fee_max_increase_denominator: uint!(10_U256), base_fee_max_decrease_denominator: uint!(50_U256), elasticity_multiplier: uint!(6_U256), }, - l1_contract: None, - l1_signal_service: None, - l2_contract: None, - l2_signal_service: None, }); /// The condition at which a fork is activated. @@ -170,13 +151,9 @@ impl Default for Eip1559Constants { /// Specification of a specific chain. #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct ChainSpec { - chain_id: ChainId, - hard_forks: BTreeMap<SpecId, ForkCondition>, - eip_1559_constants: Eip1559Constants, - pub l1_contract: Option<Address>, - pub l1_signal_service: Option<Address>, - pub l2_contract: Option<Address>, - pub l2_signal_service: Option<Address>, + pub chain_id: ChainId, + pub hard_forks: BTreeMap<SpecId, ForkCondition>, + pub eip_1559_constants: Eip1559Constants, } impl ChainSpec { @@ -190,10 +167,6 @@ impl ChainSpec { chain_id, hard_forks: BTreeMap::from([(spec_id, ForkCondition::Block(0))]), eip_1559_constants, - l1_contract: None, - l1_signal_service: None, - l2_contract: None, - l2_signal_service: None, } } /// Returns the network chain ID. @@ -231,6 +204,7 @@ impl FromStr for Network { match s.to_lowercase().as_str() { "ethereum" => Ok(Network::Ethereum), "optimism" => Ok(Network::Optimism), + #[allow(clippy::needless_return)] _ => bail!("Unknown network"), } } diff --git a/lib/src/execution/optimism.rs b/lib/src/execution/optimism.rs deleted file mode 100644 index e164ddafb..000000000 --- a/lib/src/execution/optimism.rs +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright 2023 RISC Zero, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use alloc::{format, vec, vec::Vec}; -use core::{fmt::Debug, mem::take, str::FromStr}; - -use anyhow::{anyhow, bail, Context, Result}; -#[cfg(not(target_os = "zkvm"))] -use log::debug; -use revm::{ - primitives::{Account, Address, ResultAndState, SpecId, TransactTo, TxEnv}, - Database, DatabaseCommit, EVM, -}; -use ruint::aliases::U256; -use zeth_primitives::{ - receipt::Receipt, - transactions::{ - ethereum::{EthereumTxEssence, TransactionKind}, - optimism::{OptimismTxEssence, TxEssenceOptimismDeposited}, - TxEssence, - }, - trie::MptNode, - Bloom, RlpBytes, -}; - -use crate::{ - block_builder::BlockBuilder, - consts, - consts::{GWEI_TO_WEI, MIN_SPEC_ID}, - execution::{ - ethereum::{fill_eth_tx_env, increase_account_balance}, - TxExecStrategy, - }, - guest_mem_forget, -}; - -pub struct OpTxExecStrategy {} - -impl TxExecStrategy<OptimismTxEssence> for OpTxExecStrategy { - fn execute_transactions<D>( - mut block_builder: BlockBuilder<D, OptimismTxEssence>, - ) -> Result<BlockBuilder<D, OptimismTxEssence>> - where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, - { - let header = block_builder - .header - .as_mut() - .expect("Header is not initialized"); - // Compute the spec id - let spec_id = block_builder.chain_spec.spec_id(header.number); - if !SpecId::enabled(spec_id, MIN_SPEC_ID) { - bail!( - "Invalid protocol version: expected >= {:?}, got {:?}", - MIN_SPEC_ID, - spec_id, - ) - } - let chain_id = block_builder.chain_spec.chain_id(); - - #[cfg(not(target_os = "zkvm"))] - { - use chrono::{TimeZone, Utc}; - use log::info; - let dt = Utc - .timestamp_opt(block_builder.input.timestamp.try_into().unwrap(), 0) - .unwrap(); - - info!("Block no. {}", header.number); - info!(" EVM spec ID: {:?}", spec_id); - info!(" Timestamp: {}", dt); - info!(" Transactions: {}", block_builder.input.transactions.len()); - info!(" Withdrawals: {}", block_builder.input.withdrawals.len()); - info!(" Fee Recipient: {:?}", block_builder.input.beneficiary); - info!(" Gas limit: {}", block_builder.input.gas_limit); - info!(" Base fee per gas: {}", header.base_fee_per_gas); - info!(" Extra data: {:?}", block_builder.input.extra_data); - } - - // initialize the EVM - let mut evm = EVM::new(); - - // set the EVM configuration - evm.env.cfg.chain_id = chain_id; - evm.env.cfg.spec_id = spec_id; - - // set the EVM block environment - evm.env.block.number = header.number.try_into().unwrap(); - evm.env.block.coinbase = block_builder.input.beneficiary; - evm.env.block.timestamp = header.timestamp; - evm.env.block.difficulty = U256::ZERO; - evm.env.block.prevrandao = Some(header.mix_hash); - evm.env.block.basefee = header.base_fee_per_gas; - evm.env.block.gas_limit = block_builder.input.gas_limit; - - evm.database(block_builder.db.take().unwrap()); - - // bloom filter over all transaction logs - let mut logs_bloom = Bloom::default(); - // keep track of the gas used over all transactions - let mut cumulative_gas_used = consts::ZERO; - - let l1_info_depositor_address = - Address::from_str("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001").unwrap(); - - let l1_block_attr_pre_deploy = - Address::from_str("0x4200000000000000000000000000000000000015").unwrap(); - - let l1_gas_oracle_pre_deploy = - Address::from_str("0x420000000000000000000000000000000000000F").unwrap(); - - let l1_fee_vault_pre_deploy = - Address::from_str("0x420000000000000000000000000000000000001A").unwrap(); - - let base_fee_vault_pre_deploy = - Address::from_str("0x4200000000000000000000000000000000000019").unwrap(); - - let l1_fee_overhead_decimals = U256::from(10).pow(read_uint( - &mut evm, - vec![0x31u8, 0x3cu8, 0xe5u8, 0x67u8], - Some(chain_id), - header.gas_limit, - l1_gas_oracle_pre_deploy, - )?); - - // process all the transactions - let mut tx_trie = MptNode::default(); - let mut receipt_trie = MptNode::default(); - for (tx_no, tx) in take(&mut block_builder.input.transactions) - .into_iter() - .enumerate() - { - // verify the transaction signature - let tx_from = tx - .recover_from() - .with_context(|| format!("Error recovering address for transaction {}", tx_no))?; - - #[cfg(not(target_os = "zkvm"))] - { - let tx_hash = tx.hash(); - debug!("Tx no. {} (hash: {})", tx_no, tx_hash); - debug!(" Type: {}", tx.essence.tx_type()); - debug!(" Fr: {:?}", tx_from); - debug!(" To: {:?}", tx.essence.to().unwrap_or_default()); - } - - // verify transaction gas - let block_available_gas = block_builder.input.gas_limit - cumulative_gas_used; - if block_available_gas < tx.essence.gas_limit() { - bail!("Error at transaction {}: gas exceeds block limit", tx_no); - } - - let l1_gas_fees = match &tx.essence { - OptimismTxEssence::OptimismDeposited(deposit) => { - // Disable gas fees - evm.env.cfg.disable_base_fee = true; - evm.env.cfg.disable_balance_check = true; - // Irrevocably credit minted amount - let db = evm.db().unwrap(); - increase_account_balance(db, tx_from, deposit.mint)?; - // Retrieve effective nonce - // todo: read this from contract - let effective_nonce = if tx_from == l1_info_depositor_address { - None - } else { - Some(db.basic(tx_from).unwrap().unwrap_or_default().nonce) - }; - // Initialize tx environment - fill_deposit_tx_env(&mut evm.env.tx, deposit, tx_from, effective_nonce); - - U256::ZERO - } - OptimismTxEssence::Ethereum(transaction) => { - // L1 gas fee - // todo: read these values only once after processing the first system tx - let l1_base_fee = read_uint( - &mut evm, - vec![0x5cu8, 0xf2u8, 0x49u8, 0x69u8], - Some(chain_id), - header.gas_limit, - l1_block_attr_pre_deploy, - )?; - let l1_fee_overhead = read_uint( - &mut evm, - vec![0x8bu8, 0x23u8, 0x9fu8, 0x73u8], - Some(chain_id), - header.gas_limit, - l1_block_attr_pre_deploy, - )?; - let l1_fee_scalar = read_uint( - &mut evm, - vec![0x9eu8, 0x8cu8, 0x49u8, 0x66u8], - Some(chain_id), - header.gas_limit, - l1_block_attr_pre_deploy, - )?; - - let tx_data = tx.to_rlp(); - let non_zero = tx_data.iter().filter(|b| *b > &0u8).count() as u128; - let zeroes = tx_data.len() as u128 - non_zero; - let l1_gas = U256::from(16u128 * non_zero + 4u128 * zeroes) + l1_fee_overhead; - let l1_gas_fees = - (l1_base_fee * l1_gas * l1_fee_scalar) / l1_fee_overhead_decimals; - - // Deduct L1 fee from sender - decrease_account_balance(evm.db().unwrap(), tx_from, l1_gas_fees)?; - - // Enable gas fees - evm.env.cfg.disable_base_fee = false; - evm.env.cfg.disable_balance_check = false; - // Initialize tx environment - fill_eth_tx_env(&mut evm.env.tx, transaction, tx_from); - l1_gas_fees - } - }; - - // process the transaction - let ResultAndState { result, state } = evm - .transact() - .map_err(|evm_err| anyhow!("Error at transaction {}: {:?}", tx_no, evm_err))?; - - let gas_used = result.gas_used().try_into().unwrap(); - cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).unwrap(); - - #[cfg(not(target_os = "zkvm"))] - debug!(" Ok: {:?}", result); - - // create the receipt from the EVM result - let receipt = Receipt::new( - tx.essence.tx_type(), - result.is_success(), - cumulative_gas_used, - result.logs().into_iter().map(|log| log.into()).collect(), - ); - - // update account states - #[cfg(not(target_os = "zkvm"))] - for (address, account) in &state { - if account.is_touched() { - // log account - debug!( - " State {:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={})", - address, - account.is_selfdestructed(), - account.is_loaded_as_not_existing(), - account.is_created() - ); - // log balance changes - debug!( - " After balance: {} (Nonce: {})", - account.info.balance, account.info.nonce - ); - - // log state changes - for (addr, slot) in &account.storage { - if slot.is_changed() { - debug!(" Storage address: {:?}", addr); - debug!(" Before: {:?}", slot.original_value()); - debug!(" After: {:?}", slot.present_value()); - } - } - } - } - - let db = evm.db().unwrap(); - - db.commit(state); - - if !matches!(tx.essence, OptimismTxEssence::OptimismDeposited(_)) { - // Credit L2 base fee - increase_account_balance( - db, - base_fee_vault_pre_deploy, - gas_used * header.base_fee_per_gas, - )?; - // Credit L1 gas fee - increase_account_balance(db, l1_fee_vault_pre_deploy, l1_gas_fees)?; - } - - // accumulate logs to the block bloom filter - logs_bloom.accrue_bloom(&receipt.payload.logs_bloom); - - // Add receipt and tx to tries - let trie_key = tx_no.to_rlp(); - tx_trie - .insert_rlp(&trie_key, tx) - .map_err(Into::<anyhow::Error>::into) - .context("failed to insert transaction")?; - receipt_trie - .insert_rlp(&trie_key, receipt) - .map_err(Into::<anyhow::Error>::into) - .context("failed to insert receipt")?; - } - - let mut db = evm.take_db(); - - // process withdrawals unconditionally after any transactions - let mut withdrawals_trie = MptNode::default(); - for (i, withdrawal) in take(&mut block_builder.input.withdrawals) - .into_iter() - .enumerate() - { - // the withdrawal amount is given in Gwei - let amount_wei = GWEI_TO_WEI - .checked_mul(withdrawal.amount.try_into().unwrap()) - .unwrap(); - - #[cfg(not(target_os = "zkvm"))] - { - debug!("Withdrawal no. {}", withdrawal.index); - debug!(" Recipient: {:?}", withdrawal.address); - debug!(" Value: {}", amount_wei); - } - // Read account from database - let withdrawal_address = withdrawal.address; - let mut withdrawal_account: Account = db - .basic(withdrawal.address) - .map_err(|db_err| anyhow!("Error at withdrawal {}: {:?}", i, db_err))? - .unwrap_or_default() - .into(); - // Credit withdrawal amount - withdrawal_account.info.balance = withdrawal_account - .info - .balance - .checked_add(amount_wei) - .unwrap(); - withdrawal_account.mark_touch(); - // Commit changes to database - db.commit([(withdrawal_address, withdrawal_account)].into()); - // Add withdrawal to trie - withdrawals_trie - .insert_rlp(&i.to_rlp(), withdrawal) - .map_err(Into::<anyhow::Error>::into) - .context("failed to insert withdrawal")?; - } - - // Update result header with computed values - header.transactions_root = tx_trie.hash(); - header.receipts_root = receipt_trie.hash(); - header.logs_bloom = logs_bloom; - header.gas_used = cumulative_gas_used; - header.withdrawals_root = if spec_id < SpecId::SHANGHAI { - None - } else { - Some(withdrawals_trie.hash()) - }; - - // Leak memory, save cycles - guest_mem_forget([tx_trie, receipt_trie, withdrawals_trie]); - // Return block builder with updated database - Ok(block_builder.with_db(db)) - } -} - -fn read_uint<D>( - evm: &mut EVM<D>, - abi_call: Vec<u8>, - chain_id: Option<zeth_primitives::ChainId>, - gas_limit: U256, - address: Address, -) -> Result<U256> -where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, -{ - let op_l1_tx = - EthereumTxEssence::Legacy(zeth_primitives::transactions::ethereum::TxEssenceLegacy { - chain_id, - nonce: 0, - gas_price: U256::ZERO, - gas_limit, - to: TransactionKind::Call(address), - value: U256::ZERO, - data: abi_call.into(), - }); - - // disable base fees - evm.env.cfg.disable_base_fee = true; - evm.env.cfg.disable_balance_check = true; - fill_eth_tx_env(&mut evm.env.tx, &op_l1_tx, Default::default()); - - let Ok(ResultAndState { - result: execution_result, - .. - }) = evm.transact() - else { - bail!("Error during execution"); - }; - - let revm::primitives::ExecutionResult::Success { output, .. } = execution_result else { - bail!("Result unsuccessful"); - }; - - let revm::primitives::Output::Call(result_encoded) = output else { - bail!("Unsupported result"); - }; - - // let ethers_core::abi::Token::Uint(uint_result) = - // ethers_core::abi::decode(&[ethers_core::abi::ParamType::Uint(256)], - // &result_encoded)? .pop() - // .unwrap() - // else { - // bail!("Could not decode result"); - // }; - - // Ok(U256::from_limbs(uint_result.0)) - // Todo (Cecilia): fix this - Ok(U256::default()) -} - -fn fill_deposit_tx_env( - tx_env: &mut TxEnv, - tx: &TxEssenceOptimismDeposited, - caller: Address, - deposit_nonce: Option<u64>, -) { - tx_env.caller = caller; // previously overridden to tx.from - tx_env.gas_limit = tx.gas_limit.try_into().unwrap(); - tx_env.gas_price = U256::ZERO; - tx_env.gas_priority_fee = None; - tx_env.transact_to = if let TransactionKind::Call(to_addr) = tx.to { - TransactTo::Call(to_addr) - } else { - TransactTo::create() - }; - tx_env.value = tx.value; - tx_env.data = tx.data.clone(); - tx_env.chain_id = None; - tx_env.nonce = deposit_nonce; - tx_env.access_list.clear(); -} - -pub fn decrease_account_balance<D>( - db: &mut D, - address: Address, - amount_wei: U256, -) -> anyhow::Result<()> -where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, -{ - // Read account from database - let mut account: Account = db - .basic(address) - .map_err(|db_err| { - anyhow!( - "Error decreasing account balance for {}: {:?}", - address, - db_err - ) - })? - .unwrap_or_default() - .into(); - // Credit withdrawal amount - account.info.balance = account.info.balance.checked_sub(amount_wei).unwrap(); - account.mark_touch(); - // Commit changes to database - db.commit([(address, account)].into()); - - Ok(()) -} diff --git a/lib/src/host/mod.rs b/lib/src/host/mod.rs index e8731b962..e4e02ad02 100644 --- a/lib/src/host/mod.rs +++ b/lib/src/host/mod.rs @@ -11,465 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use std::{ - collections::HashSet, - fmt::Debug, - iter::{once, zip}, -}; - -use anyhow::{Context, Result}; -use ethers_core::types::{Bytes, EIP1186ProofResponse, Transaction as EthersTransaction, H256}; -use hashbrown::HashMap; -use log::info; -use revm::Database; -use zeth_primitives::{ - block::Header, - ethers::{from_ethers_h160, from_ethers_h256, from_ethers_u256}, - keccak::keccak, - transactions::{Transaction, TxEssence}, - trie::{MptNode, MptNodeData, MptNodeReference, EMPTY_ROOT}, - withdrawal::Withdrawal, - Address, B256, U256, -}; - -use crate::{ - block_builder::{BlockBuilder, NetworkStrategyBundle}, - consts::ChainSpec, - input::{Input, StorageEntry}, - mem_db::MemDb, -}; +extern crate std; pub mod mpt; +pub mod preflight; pub mod provider; pub mod provider_db; -pub mod taiko; - -use mpt::{orphaned_digests, resolve_digests, shorten_key}; -use provider::{new_provider, BlockQuery}; -use provider_db::ProviderDb; - -#[derive(Clone)] -pub struct Init<E: TxEssence> { - pub db: MemDb, - pub init_block: Header, - pub init_proofs: HashMap<Address, EIP1186ProofResponse>, - pub fini_block: Header, - pub fini_transactions: Vec<Transaction<E>>, - pub fini_withdrawals: Vec<Withdrawal>, - pub fini_proofs: HashMap<Address, EIP1186ProofResponse>, - pub ancestor_headers: Vec<Header>, -} - -pub fn get_initial_data<N: NetworkStrategyBundle>( - chain_spec: ChainSpec, - cache_path: Option<String>, - rpc_url: Option<String>, - block_no: u64, -) -> Result<Init<N::TxEssence>> -where - N::TxEssence: TryFrom<EthersTransaction>, - <N::TxEssence as TryFrom<EthersTransaction>>::Error: Debug, -{ - let mut provider = new_provider(cache_path, rpc_url)?; - - // Fetch the initial block - let init_block = provider.get_partial_block(&BlockQuery { - block_no: block_no - 1, - })?; - - info!( - "Initial block: {:?} ({:?})", - init_block.number.unwrap(), - init_block.hash.unwrap() - ); - - // Fetch the finished block - let fini_block = provider.get_full_block(&BlockQuery { block_no })?; - - info!( - "Final block number: {:?} ({:?})", - fini_block.number.unwrap(), - fini_block.hash.unwrap() - ); - info!("Transaction count: {:?}", fini_block.transactions.len()); - - // Create the provider DB - let provider_db = ProviderDb::new(provider, init_block.number.unwrap().as_u64()); - - // Create input - let input = Input { - beneficiary: fini_block.author.map(from_ethers_h160).unwrap_or_default(), - gas_limit: from_ethers_u256(fini_block.gas_limit), - timestamp: from_ethers_u256(fini_block.timestamp), - extra_data: fini_block.extra_data.0.clone().into(), - mix_hash: from_ethers_h256(fini_block.mix_hash.unwrap()), - transactions: fini_block - .transactions - .clone() - .into_iter() - .map(|tx| tx.try_into().unwrap()) - .collect(), - withdrawals: fini_block - .withdrawals - .clone() - .unwrap_or_default() - .into_iter() - .map(|w| w.try_into().unwrap()) - .collect(), - parent_state_trie: Default::default(), - parent_storage: Default::default(), - contracts: vec![], - parent_header: init_block.clone().try_into()?, - ancestor_headers: vec![], - base_fee_per_gas: Default::default(), - }; - - // Create the block builder, run the transactions and extract the DB - let mut builder = BlockBuilder::new(&chain_spec, input) - .with_db(provider_db) - .prepare_header::<N::HeaderPrepStrategy>()? - .execute_transactions::<N::TxExecStrategy>()?; - let provider_db = builder.mut_db().unwrap(); - - info!("Gathering inclusion proofs ..."); - - // Gather inclusion proofs for the initial and final state - let init_proofs = provider_db.get_initial_proofs()?; - let fini_proofs = provider_db.get_latest_proofs()?; - - // Gather proofs for block history - let ancestor_headers = provider_db.get_ancestor_headers()?; - - info!("Saving provider cache ..."); - - // Save the provider cache - provider_db.get_provider().save()?; - - info!("Provider-backed execution is Done!"); - - let transactions = fini_block - .transactions - .clone() - .into_iter() - .map(|tx| tx.try_into().unwrap()) - .collect(); - let withdrawals = fini_block - .withdrawals - .clone() - .unwrap_or_default() - .into_iter() - .map(|w| w.try_into().unwrap()) - .collect(); - - Ok(Init { - db: provider_db.get_initial_db().clone(), - init_block: init_block.try_into()?, - init_proofs, - fini_block: fini_block.try_into()?, - fini_transactions: transactions, - fini_withdrawals: withdrawals, - fini_proofs, - ancestor_headers, - }) -} - -#[derive(Debug)] -pub enum VerifyError { - BalanceMismatch { - rpc_value: U256, - our_value: U256, - difference: U256, - }, - NonceMismatch { - rpc_value: u64, - our_value: u64, - }, - CodeHashMismatch { - rpc_value: B256, - our_value: B256, - }, - StorageMismatch { - index: U256, - rpc_value: U256, - our_db_value: U256, - our_trie_value: U256, - }, - StorageRootMismatch { - address: Address, - rpc_value: B256, - our_value: B256, - first_delta: Option<String>, - indices: usize, - }, -} - -pub fn verify_state( - mut fini_db: MemDb, - fini_proofs: HashMap<Address, EIP1186ProofResponse>, - mut storage_deltas: HashMap<Address, MptNode>, -) -> Result<HashMap<Address, Vec<VerifyError>>> { - let mut errors = HashMap::new(); - let fini_storage_keys = fini_db.storage_keys(); - - // Construct expected tries from fini proofs - let (nodes_by_pointer, mut storage) = proofs_to_tries(fini_proofs.values().cloned().collect()); - storage - .values_mut() - .for_each(|(n, _)| *n = resolve_digests(n, &nodes_by_pointer)); - storage_deltas - .values_mut() - .for_each(|n| *n = resolve_digests(n, &nodes_by_pointer)); - - for (address, indices) in fini_storage_keys { - let mut address_errors = Vec::new(); - - let account_proof = fini_proofs - .get(&address) - .with_context(|| format!("Proof not found: {}", address))?; - // for deleted accounts, use the default to compare - let account_info = fini_db.basic(address)?.unwrap_or_default(); - - // Account balance - { - let rpc_value = from_ethers_u256(account_proof.balance); - let our_value = account_info.balance; - if rpc_value != our_value { - let difference = rpc_value.abs_diff(our_value); - address_errors.push(VerifyError::BalanceMismatch { - rpc_value, - our_value, - difference, - }) - } - } - - // Nonce - { - let rpc_value = account_proof.nonce.as_u64(); - let our_value = account_info.nonce; - if rpc_value != our_value { - address_errors.push(VerifyError::NonceMismatch { - rpc_value, - our_value, - }) - } - } - - // Code hash - { - let rpc_value = from_ethers_h256(account_proof.code_hash); - let our_value = account_info.code_hash; - if rpc_value != our_value { - address_errors.push(VerifyError::CodeHashMismatch { - rpc_value, - our_value, - }) - } - } - - // Storage root - { - let storage_root_node = storage_deltas.get(&address).cloned().unwrap_or_default(); - let our_value = storage_root_node.hash(); - let rpc_value = from_ethers_h256(account_proof.storage_hash); - if rpc_value != our_value { - let expected = storage - .get(&address) - .unwrap() - .0 - .debug_rlp::<zeth_primitives::U256>(); - let found_pp = storage_root_node.debug_rlp::<zeth_primitives::U256>(); - let first_delta = zip(expected, found_pp) - .find(|(e, f)| e != f) - .map(|(e, f)| format!("Storage trie delta!\nEXPECTED:\t{}FOUND:\t{}", e, f)); - address_errors.push(VerifyError::StorageRootMismatch { - address, - rpc_value, - our_value, - first_delta, - indices: indices.len(), - }); - } - } - - // Storage - { - let storage_trie = storage_deltas.get(&address).cloned().unwrap_or_default(); - for index in indices { - let storage_index = H256::from(index.to_be_bytes()); - let rpc_value = from_ethers_u256( - account_proof - .storage_proof - .iter() - .find(|&storage| storage_index == storage.key) - .expect("Could not find storage proof") - .value, - ); - let our_db_value = fini_db.storage(address, index)?; - let trie_index = keccak(storage_index.as_bytes()); - let our_trie_value = storage_trie.get_rlp(&trie_index)?.unwrap_or_default(); - if rpc_value != our_db_value || our_db_value != our_trie_value { - address_errors.push(VerifyError::StorageMismatch { - index, - rpc_value, - our_db_value, - our_trie_value, - }) - } - } - } - - if !address_errors.is_empty() { - errors.insert(address, address_errors); - } - } - - Ok(errors) -} - -fn proofs_to_tries( - proofs: Vec<EIP1186ProofResponse>, -) -> ( - HashMap<MptNodeReference, MptNode>, - HashMap<Address, StorageEntry>, -) { - // construct the proof tries - let mut nodes_by_reference = HashMap::new(); - let mut storage = HashMap::new(); - for proof in proofs { - // parse the nodes of the account proof - for bytes in &proof.account_proof { - let mpt_node = MptNode::decode(bytes).expect("Failed to decode state proof"); - nodes_by_reference.insert(mpt_node.reference(), mpt_node); - } - - // process the proof for each storage entry - let mut root_node = None; - for storage_proof in &proof.storage_proof { - // parse the nodes of the storage proof and return the root node - root_node = storage_proof - .proof - .iter() - .rev() - .map(|bytes| MptNode::decode(bytes).expect("Failed to decode storage proof")) - .inspect(|node| drop(nodes_by_reference.insert(node.reference(), node.clone()))) - .last(); - // the hash of the root node should match the proof's storage hash - assert_eq!( - root_node.as_ref().map_or(EMPTY_ROOT, |n| n.hash()), - from_ethers_h256(proof.storage_hash) - ); - } - - let root_node = if let Some(root_node) = root_node { - root_node - } else if proof.storage_hash.0 == EMPTY_ROOT.0 || proof.storage_hash.is_zero() { - MptNode::default() - } else { - // if there are no storage proofs but the root is non-empty, create a dummy - // as this is just the digest any tries to update this trie will fail - MptNodeData::Digest(from_ethers_h256(proof.storage_hash)).into() - }; - // collect all storage slots with a proof - let slots = proof - .storage_proof - .into_iter() - .map(|p| zeth_primitives::U256::from_be_bytes(p.key.into())) - .collect(); - - storage.insert(from_ethers_h160(proof.address), (root_node, slots)); - } - (nodes_by_reference, storage) -} - -fn resolve_orphans( - nodes: &Vec<Bytes>, - orphans: &mut HashSet<MptNodeReference>, - nodes_by_reference: &mut HashMap<MptNodeReference, MptNode>, -) { - for node in nodes { - let mpt_node = MptNode::decode(node).expect("Failed to decode state proof"); - for potential_orphan in shorten_key(mpt_node) { - let potential_orphan_hash = potential_orphan.reference(); - if orphans.remove(&potential_orphan_hash) { - nodes_by_reference.insert(potential_orphan_hash, potential_orphan); - } - } - } -} - -impl<E: TxEssence> From<Init<E>> for Input<E> { - fn from(value: Init<E>) -> Input<E> { - // construct the proof tries - let (mut nodes_by_reference, mut storage) = - proofs_to_tries(value.init_proofs.values().cloned().collect()); - // there should be a trie and a list of storage slots for every account - assert_eq!(storage.len(), value.db.accounts_len()); - - // collect the code from each account - let mut contracts = HashMap::new(); - for account in value.db.accounts.values() { - let code = account.info.code.clone().unwrap(); - if !code.is_empty() { - contracts.insert(code.hash_slow(), code.bytecode); - } - } - - // extract the state trie - let state_root = value.init_block.state_root; - let state_trie = nodes_by_reference - .remove(&MptNodeReference::Digest(state_root)) - .expect("State root node not found"); - assert_eq!(state_root, state_trie.hash()); - - // identify orphaned digests, that could lead to issues when deleting nodes - let mut orphans = HashSet::new(); - for root in storage.values().map(|v| &v.0).chain(once(&state_trie)) { - let root = resolve_digests(root, &nodes_by_reference); - orphans.extend(orphaned_digests(&root)); - } - // resolve those orphans using the proofs of the final state - for fini_proof in value.fini_proofs.values() { - resolve_orphans( - &fini_proof.account_proof, - &mut orphans, - &mut nodes_by_reference, - ); - for storage_proof in &fini_proof.storage_proof { - resolve_orphans(&storage_proof.proof, &mut orphans, &mut nodes_by_reference); - } - } - - // resolve the pointers in the state root node and all storage root nodes - let state_trie = resolve_digests(&state_trie, &nodes_by_reference); - storage - .values_mut() - .for_each(|(n, _)| *n = resolve_digests(n, &nodes_by_reference)); - - info!( - "The partial state trie consists of {} nodes", - state_trie.size() - ); - info!( - "The partial storage tries consist of {} nodes", - storage.values().map(|(n, _)| n.size()).sum::<usize>() - ); - - // Create the block builder input - Input { - parent_header: value.init_block, - beneficiary: value.fini_block.beneficiary, - gas_limit: value.fini_block.gas_limit, - timestamp: value.fini_block.timestamp, - extra_data: value.fini_block.extra_data.0.clone().into(), - mix_hash: value.fini_block.mix_hash, - transactions: value.fini_transactions, - withdrawals: value.fini_withdrawals, - parent_state_trie: state_trie, - parent_storage: storage.into_iter().collect(), - contracts: contracts.into_values().collect(), - ancestor_headers: value.ancestor_headers, - base_fee_per_gas: value.fini_block.base_fee_per_gas, - } - } -} +pub mod verify; diff --git a/lib/src/host/mpt.rs b/lib/src/host/mpt.rs index 90ff6b44f..d51c6d57f 100644 --- a/lib/src/host/mpt.rs +++ b/lib/src/host/mpt.rs @@ -12,87 +12,123 @@ // See the License for the specific language governing permissions and // limitations under the License. +use anyhow::{bail, Context, Result}; use hashbrown::HashMap; use zeth_primitives::trie::{to_encoded_path, MptNode, MptNodeData, MptNodeReference}; +/// Parses proof bytes into a vector of MPT nodes. +pub fn parse_proof(proof: &[impl AsRef<[u8]>]) -> Result<Vec<MptNode>> { + Ok(proof + .iter() + .map(MptNode::decode) + .collect::<Result<Vec<_>, _>>()?) +} + +/// Creates a Merkle Patricia trie from an EIP-1186 proof. +/// For inclusion proofs the returned trie contains exactly one leaf with the value. +pub fn mpt_from_proof(proof_nodes: &[MptNode]) -> Result<MptNode> { + let mut next: Option<MptNode> = None; + for (i, node) in proof_nodes.iter().enumerate().rev() { + // there is nothing to replace for the last node + let Some(replacement) = next else { + next = Some(node.clone()); + continue; + }; + + // the next node must have a digest reference + let MptNodeReference::Digest(ref child_ref) = replacement.reference() else { + bail!("node {} in proof is not referenced by hash", i + 1); + }; + // find the child that references the next node + let resolved: MptNode = match node.as_data().clone() { + MptNodeData::Branch(mut children) => { + if let Some(child) = children.iter_mut().flatten().find( + |child| matches!(child.as_data(), MptNodeData::Digest(d) if d == child_ref), + ) { + *child = Box::new(replacement); + } else { + bail!("node {i} does not reference the successor"); + } + MptNodeData::Branch(children).into() + } + MptNodeData::Extension(prefix, child) => { + if !matches!(child.as_data(), MptNodeData::Digest(d) if d == child_ref) { + bail!("node {i} does not reference the successor"); + } + MptNodeData::Extension(prefix, Box::new(replacement)).into() + } + MptNodeData::Null | MptNodeData::Leaf(_, _) | MptNodeData::Digest(_) => { + bail!("node {i} has no children to replace"); + } + }; + + next = Some(resolved); + } + + // the last node in the proof should be the root + Ok(next.unwrap_or_default()) +} + +/// Verifies that the given proof is a valid proof of exclusion for the given key. +pub fn is_not_included(key: &[u8], proof_nodes: &[MptNode]) -> Result<bool> { + let proof_trie = mpt_from_proof(proof_nodes).context("invalid trie")?; + // for valid proofs, the get must not fail + let value = proof_trie.get(key)/* .context("invalid trie") */?; + + Ok(value.is_none()) +} + /// Creates a new MPT trie where all the digests contained in `node_store` are resolved. -pub fn resolve_digests(trie: &MptNode, node_store: &HashMap<MptNodeReference, MptNode>) -> MptNode { - let result: MptNode = match trie.as_data() { - MptNodeData::Null | MptNodeData::Leaf(_, _) => trie.clone(), +pub fn resolve_nodes(root: &MptNode, node_store: &HashMap<MptNodeReference, MptNode>) -> MptNode { + let trie = match root.as_data() { + MptNodeData::Null | MptNodeData::Leaf(_, _) => root.clone(), MptNodeData::Branch(children) => { let children: Vec<_> = children .iter() .map(|child| { child .as_ref() - .map(|node| Box::new(resolve_digests(node, node_store))) + .map(|node| Box::new(resolve_nodes(node, node_store))) }) .collect(); MptNodeData::Branch(children.try_into().unwrap()).into() } - MptNodeData::Extension(prefix, target) => MptNodeData::Extension( - prefix.clone(), - Box::new(resolve_digests(target, node_store)), - ) - .into(), + MptNodeData::Extension(prefix, target) => { + MptNodeData::Extension(prefix.clone(), Box::new(resolve_nodes(target, node_store))) + .into() + } MptNodeData::Digest(digest) => { if let Some(node) = node_store.get(&MptNodeReference::Digest(*digest)) { - resolve_digests(node, node_store) + resolve_nodes(node, node_store) } else { - trie.clone() + root.clone() } } }; - assert_eq!(trie.hash(), result.hash()); - result -} + // the root hash must not change + debug_assert_eq!(root.hash(), trie.hash()); -/// Returns all orphaned digests in the trie. -pub fn orphaned_digests(trie: &MptNode) -> Vec<MptNodeReference> { - let mut result = Vec::new(); - orphaned_digests_internal(trie, &mut result); - result + trie } -fn orphaned_digests_internal(trie: &MptNode, orphans: &mut Vec<MptNodeReference>) { - match trie.as_data() { - MptNodeData::Branch(children) => { - // iterate over all digest children - let mut digests = children.iter().flatten().filter(|node| node.is_digest()); - // if there is exactly one digest child, it is an orphan - if let Some(orphan_digest) = digests.next() { - if digests.next().is_none() { - orphans.push(orphan_digest.reference()); - } - }; - // recurse - children.iter().flatten().for_each(|child| { - orphaned_digests_internal(child, orphans); - }); - } - MptNodeData::Extension(_, target) => { - orphaned_digests_internal(target, orphans); - } - MptNodeData::Null | MptNodeData::Leaf(_, _) | MptNodeData::Digest(_) => {} - } -} - -pub fn shorten_key(node: MptNode) -> Vec<MptNode> { +/// Returns a list of all possible nodes that can be created by shortening the path of the +/// given node. +/// When nodes in an MPT are deleted, leaves or extensions may be extended. To still be +/// able to identify the original nodes, we create all shortened versions of the node. +pub fn shorten_node_path(node: &MptNode) -> Vec<MptNode> { let mut res = Vec::new(); let nibs = node.nibs(); match node.as_data() { - MptNodeData::Null | MptNodeData::Branch(_) | MptNodeData::Digest(_) => { - res.push(node.clone()) - } + MptNodeData::Null | MptNodeData::Branch(_) | MptNodeData::Digest(_) => {} MptNodeData::Leaf(_, value) => { for i in 0..=nibs.len() { res.push(MptNodeData::Leaf(to_encoded_path(&nibs[i..], true), value.clone()).into()) } } - MptNodeData::Extension(_, target) => { + MptNodeData::Extension(_, child) => { for i in 0..=nibs.len() { res.push( - MptNodeData::Extension(to_encoded_path(&nibs[i..], false), target.clone()) + MptNodeData::Extension(to_encoded_path(&nibs[i..], false), child.clone()) .into(), ) } diff --git a/lib/src/host/preflight.rs b/lib/src/host/preflight.rs new file mode 100644 index 000000000..0a7315cec --- /dev/null +++ b/lib/src/host/preflight.rs @@ -0,0 +1,355 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{fmt::Debug, path::PathBuf}; + +use anyhow::{anyhow, Context, Result}; +use ethers_core::types::{ + Block as EthersBlock, EIP1186ProofResponse, Transaction as EthersTransaction, +}; +use hashbrown::{HashMap, HashSet}; +use log::info; +use zeth_primitives::{ + block::Header, + ethers::{from_ethers_h160, from_ethers_h256, from_ethers_u256}, + keccak::keccak, + transactions::{Transaction, TxEssence}, + trie::{MptNode, MptNodeData, MptNodeReference, EMPTY_ROOT}, + withdrawal::Withdrawal, + Address, B256, U256, + +}; +use crate::{ + builder::{BlockBuilder, BlockBuilderStrategy}, + consts::ChainSpec, + host::{mpt::{is_not_included, mpt_from_proof, parse_proof, resolve_nodes, shorten_node_path}, provider::{new_provider, BlockQuery}, provider_db::ProviderDb}, + input::{Input, StorageEntry}, mem_db::MemDb +}; + +/// The initial data required to build a block as returned by the [Preflight]. +#[derive(Debug, Clone)] +pub struct Data<E: TxEssence> { + pub db: MemDb, + pub parent_header: Header, + pub parent_proofs: HashMap<Address, EIP1186ProofResponse>, + pub header: Header, + pub transactions: Vec<Transaction<E>>, + pub withdrawals: Vec<Withdrawal>, + pub proofs: HashMap<Address, EIP1186ProofResponse>, + pub ancestor_headers: Vec<Header>, +} + +pub trait Preflight<E: TxEssence> { + /// Executes the complete block using the input and state from the RPC provider. + /// It returns all the data required to build and validate the block. + fn run_preflight( + chain_spec: ChainSpec, + cache_path: Option<PathBuf>, + rpc_url: Option<String>, + block_no: u64, + ) -> Result<Data<E>>; +} + +#[cfg(not(feature = "taiko"))] +/// Implements the [Preflight] trait for all compatible [BlockBuilderStrategy]s. +impl<N: BlockBuilderStrategy> Preflight<N::TxEssence> for N +where + N::TxEssence: TryFrom<EthersTransaction>, + <N::TxEssence as TryFrom<EthersTransaction>>::Error: Debug, +{ + fn run_preflight( + chain_spec: ChainSpec, + cache_path: Option<PathBuf>, + rpc_url: Option<String>, + block_no: u64, + ) -> Result<Data<N::TxEssence>> { + let mut provider = new_provider(cache_path, rpc_url)?; + + // Fetch the parent block + let parent_block = provider.get_partial_block(&BlockQuery { + block_no: block_no - 1, + })?; + + info!( + "Initial block: {:?} ({:?})", + parent_block.number.unwrap(), + parent_block.hash.unwrap() + ); + let parent_header: Header = parent_block.try_into().context("invalid parent block")?; + + // Fetch the target block + let block = provider.get_full_block(&BlockQuery { block_no })?; + + info!( + "Final block number: {:?} ({:?})", + block.number.unwrap(), + block.hash.unwrap() + ); + info!("Transaction count: {:?}", block.transactions.len()); + + // Create the provider DB + let provider_db = ProviderDb::new(provider, parent_header.number); + + // Create the input data + let input = new_preflight_input(block.clone(), parent_header.clone())?; + let transactions = input.transactions.clone(); + let withdrawals = input.withdrawals.clone(); + + // Create the block builder, run the transactions and extract the DB + let mut builder = BlockBuilder::new(&chain_spec, input) + .with_db(provider_db) + .prepare_header::<N::HeaderPrepStrategy>()? + .execute_transactions::<N::TxExecStrategy>()?; + let provider_db = builder.mut_db().unwrap(); + + info!("Gathering inclusion proofs ..."); + + // Gather inclusion proofs for the initial and final state + let parent_proofs = provider_db.get_initial_proofs()?; + let proofs = provider_db.get_latest_proofs()?; + + // Gather proofs for block history + let ancestor_headers = provider_db.get_ancestor_headers()?; + + info!("Saving provider cache ..."); + + // Save the provider cache + provider_db.get_provider().save()?; + + info!("Provider-backed execution is Done!"); + + Ok(Data { + db: provider_db.get_initial_db().clone(), + parent_header, + parent_proofs, + header: block.try_into().context("invalid block")?, + transactions, + withdrawals, + proofs, + ancestor_headers, + }) + } +} + +/// +pub fn new_preflight_input<E>( + block: EthersBlock<EthersTransaction>, + parent_header: Header, +) -> Result<Input<E>> +where + E: TxEssence + TryFrom<EthersTransaction>, + <E as TryFrom<EthersTransaction>>::Error: Debug, +{ + // convert each transaction + let transactions = block + .transactions + .into_iter() + .enumerate() + .map(|(i, tx)| { + tx.try_into() + .map_err(|err| anyhow!("transaction {i} invalid: {err:?}")) + }) + .collect::<Result<Vec<_>, _>>()?; + // convert each withdrawal + let withdrawals = block + .withdrawals + .unwrap_or_default() + .into_iter() + .enumerate() + .map(|(i, tx)| { + tx.try_into() + .with_context(|| format!("withdrawal {i} invalid")) + }) + .collect::<Result<Vec<_>, _>>()?; + + let input = Input { + beneficiary: from_ethers_h160(block.author.context("author missing")?), + gas_limit: from_ethers_u256(block.gas_limit), + timestamp: from_ethers_u256(block.timestamp), + extra_data: block.extra_data.0.into(), + mix_hash: from_ethers_h256(block.mix_hash.context("mix_hash missing")?), + transactions, + withdrawals, + parent_state_trie: Default::default(), + parent_storage: Default::default(), + contracts: Default::default(), + parent_header, + ancestor_headers: Default::default(), + base_fee_per_gas: from_ethers_u256( + block.base_fee_per_gas.context("base_fee_per_gas missing")?, + ), + }; + Ok(input) +} + +/// Converts the [Data] returned by the [Preflight] into [Input] required by the +/// [BlockBuilder]. +impl<E: TxEssence> TryFrom<Data<E>> for Input<E> { + type Error = anyhow::Error; + + fn try_from(data: Data<E>) -> Result<Input<E>> { + // collect the code from each account + let mut contracts = HashSet::new(); + for account in data.db.accounts.values() { + let code = account.info.code.clone().context("missing code")?; + if !code.is_empty() { + contracts.insert(code.bytecode); + } + } + + // construct the sparse MPTs from the inclusion proofs + let (state_trie, storage) = proofs_to_tries( + data.parent_header.state_root, + data.parent_proofs, + data.proofs, + )?; + + info!( + "The partial state trie consists of {} nodes", + state_trie.size() + ); + info!( + "The partial storage tries consist of {} nodes", + storage.values().map(|(n, _)| n.size()).sum::<usize>() + ); + + // Create the block builder input + let input = Input { + parent_header: data.parent_header, + beneficiary: data.header.beneficiary, + gas_limit: data.header.gas_limit, + timestamp: data.header.timestamp, + extra_data: data.header.extra_data.0.clone().into(), + mix_hash: data.header.mix_hash, + transactions: data.transactions, + withdrawals: data.withdrawals, + parent_state_trie: state_trie, + parent_storage: storage, + contracts: contracts.into_iter().collect(), + ancestor_headers: data.ancestor_headers, + base_fee_per_gas: data.header.base_fee_per_gas, + }; + Ok(input) + } +} + +fn proofs_to_tries( + state_root: B256, + parent_proofs: HashMap<Address, EIP1186ProofResponse>, + proofs: HashMap<Address, EIP1186ProofResponse>, +) -> Result<(MptNode, HashMap<Address, StorageEntry>)> { + // if no addresses are provided, return the trie only consisting of the state root + if parent_proofs.is_empty() { + return Ok((node_from_digest(state_root), HashMap::new())); + } + + let mut storage: HashMap<Address, StorageEntry> = HashMap::with_capacity(parent_proofs.len()); + + let mut state_nodes = HashMap::new(); + let mut state_root_node = MptNode::default(); + for (address, proof) in parent_proofs { + let proof_nodes = + parse_proof(&proof.account_proof).context("invalid account_proof encoding")?; + mpt_from_proof(&proof_nodes).context("invalid account_proof")?; + + // the first node in the proof is the root + if let Some(node) = proof_nodes.first() { + state_root_node = node.clone(); + } + + proof_nodes.into_iter().for_each(|node| { + state_nodes.insert(node.reference(), node); + }); + + let fini_proofs = proofs + .get(&address) + .with_context(|| format!("missing fini_proofs for address {address:#}"))?; + + // assure that addresses can be deleted from the state trie + add_orphaned_leafs(address, &fini_proofs.account_proof, &mut state_nodes)?; + + // if no slots are provided, return the trie only consisting of the storage root + let storage_root = from_ethers_h256(proof.storage_hash); + if proof.storage_proof.is_empty() { + let storage_root_node = node_from_digest(storage_root); + storage.insert(address, (storage_root_node, vec![])); + continue; + } + + let mut storage_nodes = HashMap::new(); + let mut storage_root_node = MptNode::default(); + for storage_proof in &proof.storage_proof { + let proof_nodes = + parse_proof(&storage_proof.proof).context("invalid storage_proof encoding")?; + mpt_from_proof(&proof_nodes).context("invalid storage_proof")?; + + // the first node in the proof is the root + if let Some(node) = proof_nodes.first() { + storage_root_node = node.clone(); + } + + proof_nodes.into_iter().for_each(|node| { + storage_nodes.insert(node.reference(), node); + }); + } + + // assure that slots can be deleted from the storage trie + for storage_proof in &fini_proofs.storage_proof { + add_orphaned_leafs(storage_proof.key, &storage_proof.proof, &mut storage_nodes)?; + } + // create the storage trie, from all the relevant nodes + let storage_trie = resolve_nodes(&storage_root_node, &storage_nodes); + assert_eq!(storage_trie.hash(), storage_root); + + // convert the slots to a vector of U256 + let slots = proof + .storage_proof + .iter() + .map(|p| U256::from_be_bytes(p.key.into())) + .collect(); + storage.insert(address, (storage_trie, slots)); + } + let state_trie = resolve_nodes(&state_root_node, &state_nodes); + assert_eq!(state_trie.hash(), state_root); + + Ok((state_trie, storage)) +} + +/// Adds all the leaf nodes of non-inclusion proofs to the nodes. +fn add_orphaned_leafs( + key: impl AsRef<[u8]>, + proof: &[impl AsRef<[u8]>], + nodes_by_reference: &mut HashMap<MptNodeReference, MptNode>, +) -> Result<()> { + if !proof.is_empty() { + let proof_nodes = parse_proof(proof).context("invalid proof encoding")?; + if is_not_included(&keccak(key), &proof_nodes)? { + // add the leaf node to the nodes + let leaf = proof_nodes.last().expect("No leaf node"); + shorten_node_path(leaf).into_iter().for_each(|node| { + nodes_by_reference.insert(node.reference(), node); + }); + } + } + + Ok(()) +} + +/// Creates a new MPT node from a digest. +fn node_from_digest(digest: B256) -> MptNode { + match digest { + EMPTY_ROOT | B256::ZERO => MptNode::default(), + _ => MptNodeData::Digest(digest).into(), + } +} diff --git a/lib/src/host/provider/cached_rpc_provider.rs b/lib/src/host/provider/cached_rpc_provider.rs index 8a8871183..ba9f04e82 100644 --- a/lib/src/host/provider/cached_rpc_provider.rs +++ b/lib/src/host/provider/cached_rpc_provider.rs @@ -12,15 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::path::PathBuf; + use anyhow::Result; -use ethers_core::types::{Block, Bytes, EIP1186ProofResponse, Transaction, H256, U256}; #[cfg(feature = "taiko")] -use zeth_primitives::taiko::BlockProposed; +use ethers_core::types::Log; +use ethers_core::types::{ + Block, Bytes, EIP1186ProofResponse, Transaction, TransactionReceipt, H256, U256, +}; use super::{ file_provider::FileProvider, rpc_provider::RpcProvider, AccountQuery, BlockQuery, MutProvider, ProofQuery, Provider, StorageQuery, }; +#[cfg(feature = "taiko")] +use crate::host::provider::LogsQuery; pub struct CachedRpcProvider { cache: FileProvider, @@ -28,8 +34,8 @@ pub struct CachedRpcProvider { } impl CachedRpcProvider { - pub fn new(cache_path: String, rpc_url: String) -> Result<Self> { - let cache = match FileProvider::read_from_file(cache_path.clone()) { + pub fn new(cache_path: PathBuf, rpc_url: String) -> Result<Self> { + let cache = match FileProvider::from_file(&cache_path) { Ok(provider) => provider, Err(_) => FileProvider::empty(cache_path), }; @@ -68,6 +74,18 @@ impl Provider for CachedRpcProvider { Ok(out) } + fn get_block_receipts(&mut self, query: &BlockQuery) -> Result<Vec<TransactionReceipt>> { + let cache_out = self.cache.get_block_receipts(query); + if cache_out.is_ok() { + return cache_out; + } + + let out = self.rpc.get_block_receipts(query)?; + self.cache.insert_block_receipts(query.clone(), out.clone()); + + Ok(out) + } + fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse> { let cache_out = self.cache.get_proof(query); if cache_out.is_ok() { @@ -129,34 +147,39 @@ impl Provider for CachedRpcProvider { } #[cfg(feature = "taiko")] - fn get_propose(&mut self, query: &super::ProposeQuery) -> Result<(Transaction, BlockProposed)> { - let cache_out = self.cache.get_propose(query); + fn get_logs(&mut self, query: &LogsQuery) -> Result<Vec<Log>> { + let cache_out = self.cache.get_logs(query); if cache_out.is_ok() { return cache_out; } - let out = self.rpc.get_propose(query)?; - self.cache.insert_propose(query.clone(), out.clone()); + let out = self.rpc.get_logs(query)?; + self.cache.insert_logs(query.clone(), out.clone()); Ok(out) } #[cfg(feature = "taiko")] - fn batch_get_partial_blocks(&mut self, query: &BlockQuery) -> Result<Vec<Block<H256>>> { - let cache_out = self.cache.batch_get_partial_blocks(query); + fn get_transaction(&mut self, query: &super::TxQuery) -> Result<Transaction> { + let cache_out = self.cache.get_transaction(query); if cache_out.is_ok() { return cache_out; } - let out = self.rpc.batch_get_partial_blocks(query)?; - for block in out.iter() { - self.cache.insert_partial_block( - BlockQuery { - block_no: block.number.unwrap().as_u64(), - }, - block.clone(), - ); + // Search cached block for target Tx + if let Some(block_no) = query.block_no { + if let Ok(block) = self.cache.get_full_block(&BlockQuery { block_no }) { + for tx in block.transactions { + if tx.hash == query.tx_hash { + return Ok(tx.clone()); + } + } + } } + + let out = self.rpc.get_transaction(query)?; + self.cache.insert_transaction(query.clone(), out.clone()); + Ok(out) } } diff --git a/lib/src/host/provider/file_provider.rs b/lib/src/host/provider/file_provider.rs index df1ea9fc5..568012610 100644 --- a/lib/src/host/provider/file_provider.rs +++ b/lib/src/host/provider/file_provider.rs @@ -14,32 +14,39 @@ use alloc::vec::Vec; use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, fs::File, io::{Read, Write}, path::{Path, PathBuf}, }; use anyhow::{anyhow, Result}; -use ethers_core::types::{Block, Bytes, EIP1186ProofResponse, Transaction, H256, U256}; +use ethers_core::types::{ + Block, Bytes, EIP1186ProofResponse, Log, Transaction, TransactionReceipt, H256, U256, +}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -#[cfg(feature = "taiko")] -use zeth_primitives::taiko::BlockProposed; +// #[cfg(feature = "taiko")] +// use zeth_primitives::taiko::BlockProposed; use super::{AccountQuery, BlockQuery, MutProvider, ProofQuery, Provider, StorageQuery}; +#[cfg(feature = "taiko")] +use super::{LogsQuery, TxQuery}; #[serde_as] -#[derive(Deserialize, Serialize)] +#[derive(Default, Deserialize, Serialize)] pub struct FileProvider { #[serde(skip)] - file_path: String, + file_path: PathBuf, #[serde(skip)] dirty: bool, #[serde_as(as = "Vec<(_, _)>")] full_blocks: HashMap<BlockQuery, Block<Transaction>>, #[serde_as(as = "Vec<(_, _)>")] - partial_blocks: BTreeMap<BlockQuery, Block<H256>>, + partial_blocks: HashMap<BlockQuery, Block<H256>>, + #[serde(default)] + #[serde_as(as = "Vec<(_, _)>")] + receipts: HashMap<BlockQuery, Vec<TransactionReceipt>>, #[serde_as(as = "Vec<(_, _)>")] proofs: HashMap<ProofQuery, EIP1186ProofResponse>, #[serde_as(as = "Vec<(_, _)>")] @@ -50,40 +57,48 @@ pub struct FileProvider { code: HashMap<AccountQuery, Bytes>, #[serde_as(as = "Vec<(_, _)>")] storage: HashMap<StorageQuery, H256>, + #[cfg(feature = "taiko")] - propose: Option<(Transaction, BlockProposed)>, + #[serde_as(as = "Vec<(_, _)>")] + logs: HashMap<LogsQuery, Vec<Log>>, + #[cfg(feature = "taiko")] + #[serde_as(as = "Vec<(_, _)>")] + transactions: HashMap<TxQuery, Transaction>, } impl FileProvider { - pub fn empty(file_path: String) -> Self { + pub fn empty(file_path: PathBuf) -> Self { FileProvider { file_path, dirty: false, full_blocks: HashMap::new(), - partial_blocks: BTreeMap::new(), + partial_blocks: HashMap::new(), + receipts: HashMap::new(), proofs: HashMap::new(), transaction_count: HashMap::new(), balance: HashMap::new(), code: HashMap::new(), storage: HashMap::new(), #[cfg(feature = "taiko")] - propose: Default::default(), + logs: HashMap::new(), + #[cfg(feature = "taiko")] + transactions: HashMap::new(), } } - pub fn read_from_file(file_path: String) -> Result<Self> { + pub fn from_file(file_path: &PathBuf) -> Result<Self> { let mut buf = vec![]; - let mut decoder = flate2::read::GzDecoder::new(File::open(&file_path)?); + let mut decoder = flate2::read::GzDecoder::new(File::open(file_path)?); decoder.read_to_end(&mut buf)?; let mut out: Self = serde_json::from_slice(&buf[..])?; - out.file_path = file_path; + out.file_path = file_path.clone(); out.dirty = false; Ok(out) } - pub fn save_to_file(&self, file_path: &String) -> Result<()> { + pub fn save_to_file(&self, file_path: &Path) -> Result<()> { if self.dirty { let mut encoder = flate2::write::GzEncoder::new( File::create(file_path)?, @@ -105,66 +120,72 @@ impl Provider for FileProvider { fn get_full_block(&mut self, query: &BlockQuery) -> Result<Block<Transaction>> { match self.full_blocks.get(query) { Some(val) => Ok(val.clone()), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_partial_block(&mut self, query: &BlockQuery) -> Result<Block<H256>> { match self.partial_blocks.get(query) { Some(val) => Ok(val.clone()), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), + } + } + + fn get_block_receipts(&mut self, query: &BlockQuery) -> Result<Vec<TransactionReceipt>> { + match self.receipts.get(query) { + Some(val) => Ok(val.clone()), + None => Err(anyhow!("No data for {query:?}")), } } fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse> { match self.proofs.get(query) { Some(val) => Ok(val.clone()), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_transaction_count(&mut self, query: &AccountQuery) -> Result<U256> { match self.transaction_count.get(query) { Some(val) => Ok(*val), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_balance(&mut self, query: &AccountQuery) -> Result<U256> { match self.balance.get(query) { Some(val) => Ok(*val), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_code(&mut self, query: &AccountQuery) -> Result<Bytes> { match self.code.get(query) { Some(val) => Ok(val.clone()), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_storage(&mut self, query: &StorageQuery) -> Result<H256> { match self.storage.get(query) { Some(val) => Ok(*val), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } #[cfg(feature = "taiko")] - fn get_propose(&mut self, query: &super::ProposeQuery) -> Result<(Transaction, BlockProposed)> { - match self.propose { - Some(ref val) => Ok(val.clone()), - None => Err(anyhow!("No data for {:?}", query)), + fn get_logs(&mut self, query: &LogsQuery) -> Result<Vec<Log>> { + match self.logs.get(query) { + Some(val) => Ok(val.clone()), + None => Err(anyhow!("No data for {query:?}")), } } #[cfg(feature = "taiko")] - fn batch_get_partial_blocks(&mut self, _: &BlockQuery) -> Result<Vec<Block<H256>>> { - if self.partial_blocks.is_empty() { - Err(anyhow!("No data for partial blocks")) - } else { - Ok(self.partial_blocks.values().cloned().collect()) + fn get_transaction(&mut self, query: &TxQuery) -> Result<Transaction> { + match self.transactions.get(query) { + Some(val) => Ok(val.clone()), + None => Err(anyhow!("No data for {query:?}")), } } } @@ -180,6 +201,11 @@ impl MutProvider for FileProvider { self.dirty = true; } + fn insert_block_receipts(&mut self, query: BlockQuery, val: Vec<TransactionReceipt>) { + self.receipts.insert(query, val); + self.dirty = true; + } + fn insert_proof(&mut self, query: ProofQuery, val: EIP1186ProofResponse) { self.proofs.insert(query, val); self.dirty = true; @@ -206,8 +232,14 @@ impl MutProvider for FileProvider { } #[cfg(feature = "taiko")] - fn insert_propose(&mut self, _query: super::ProposeQuery, val: (Transaction, BlockProposed)) { - self.propose = Some(val); + fn insert_logs(&mut self, query: LogsQuery, val: Vec<Log>) { + self.logs.insert(query, val); + self.dirty = true; + } + + #[cfg(feature = "taiko")] + fn insert_transaction(&mut self, query: super::TxQuery, val: Transaction) { + self.transactions.insert(query, val); self.dirty = true; } } @@ -215,7 +247,7 @@ impl MutProvider for FileProvider { #[cfg(feature = "taiko")] pub fn cache_file_path(cache_path: &Path, block_no: u64, is_l1: bool) -> PathBuf { let prefix = if is_l1 { "l1" } else { "l2" }; - let file_name = format!("{}.{}.json.gz", block_no, prefix); + let file_name = format!("{block_no}.{prefix}.json.gz"); cache_path.join(file_name) } diff --git a/lib/src/host/provider/mod.rs b/lib/src/host/provider/mod.rs index 0ffe659f0..ac1307d79 100644 --- a/lib/src/host/provider/mod.rs +++ b/lib/src/host/provider/mod.rs @@ -11,14 +11,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -use std::collections::BTreeSet; - -use anyhow::{anyhow, Result}; -use ethers_core::types::{Block, Bytes, EIP1186ProofResponse, Transaction, H160, H256, U256}; +use std::{collections::BTreeSet, path::PathBuf}; + +use alloy_primitives::Address; +use alloy_sol_types::SolEvent; +use anyhow::{anyhow, Context, Result}; +use ethers_core::types::{ + Block, Bytes, EIP1186ProofResponse, Log, Transaction, TransactionReceipt, H160, H256, U256, +}; use serde::{Deserialize, Serialize}; + #[cfg(feature = "taiko")] -use zeth_primitives::taiko::BlockProposed; +use crate::taiko::BlockProposed; pub mod cached_rpc_provider; pub mod file_provider; @@ -51,10 +55,17 @@ pub struct StorageQuery { #[cfg(feature = "taiko")] #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct ProposeQuery { - pub l1_contract: H160, - pub l1_block_no: u64, - pub l2_block_no: u64, +pub struct LogsQuery { + pub address: H160, + pub from_block: u64, + pub to_block: u64, +} + +#[cfg(feature = "taiko")] +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct TxQuery { + pub tx_hash: H256, + pub block_no: Option<u64>, } pub trait Provider: Send { @@ -62,34 +73,36 @@ pub trait Provider: Send { fn get_full_block(&mut self, query: &BlockQuery) -> Result<Block<Transaction>>; fn get_partial_block(&mut self, query: &BlockQuery) -> Result<Block<H256>>; + fn get_block_receipts(&mut self, query: &BlockQuery) -> Result<Vec<TransactionReceipt>>; fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse>; fn get_transaction_count(&mut self, query: &AccountQuery) -> Result<U256>; fn get_balance(&mut self, query: &AccountQuery) -> Result<U256>; fn get_code(&mut self, query: &AccountQuery) -> Result<Bytes>; fn get_storage(&mut self, query: &StorageQuery) -> Result<H256>; - #[cfg(feature = "taiko")] - fn get_propose(&mut self, query: &ProposeQuery) -> Result<(Transaction, BlockProposed)>; + fn get_logs(&mut self, query: &LogsQuery) -> Result<Vec<Log>>; #[cfg(feature = "taiko")] - /// get 256 blocks one time to reduce the fetch time cost - fn batch_get_partial_blocks(&mut self, query: &BlockQuery) -> Result<Vec<Block<H256>>>; + fn get_transaction(&mut self, query: &TxQuery) -> Result<Transaction>; } pub trait MutProvider: Provider { fn insert_full_block(&mut self, query: BlockQuery, val: Block<Transaction>); fn insert_partial_block(&mut self, query: BlockQuery, val: Block<H256>); + fn insert_block_receipts(&mut self, query: BlockQuery, val: Vec<TransactionReceipt>); fn insert_proof(&mut self, query: ProofQuery, val: EIP1186ProofResponse); fn insert_transaction_count(&mut self, query: AccountQuery, val: U256); fn insert_balance(&mut self, query: AccountQuery, val: U256); fn insert_code(&mut self, query: AccountQuery, val: Bytes); fn insert_storage(&mut self, query: StorageQuery, val: H256); - #[cfg(feature = "taiko")] - fn insert_propose(&mut self, query: ProposeQuery, val: (Transaction, BlockProposed)); + fn insert_logs(&mut self, query: LogsQuery, val: Vec<Log>); + #[cfg(feature = "taiko")] + fn insert_transaction(&mut self, query: TxQuery, val: Transaction); } -pub fn new_file_provider(file_path: String) -> Result<Box<dyn Provider>> { - let provider = file_provider::FileProvider::read_from_file(file_path)?; +pub fn new_file_provider(file_path: PathBuf) -> Result<Box<dyn Provider>> { + let provider = file_provider::FileProvider::from_file(&file_path) + .with_context(|| anyhow!("invalid cache file: {}", file_path.display()))?; Ok(Box::new(provider)) } @@ -100,14 +113,14 @@ pub fn new_rpc_provider(rpc_url: String) -> Result<Box<dyn Provider>> { Ok(Box::new(provider)) } -pub fn new_cached_rpc_provider(cache_path: String, rpc_url: String) -> Result<Box<dyn Provider>> { +pub fn new_cached_rpc_provider(cache_path: PathBuf, rpc_url: String) -> Result<Box<dyn Provider>> { let provider = cached_rpc_provider::CachedRpcProvider::new(cache_path, rpc_url)?; Ok(Box::new(provider)) } pub fn new_provider( - cache_path: Option<String>, + cache_path: Option<PathBuf>, rpc_url: Option<String>, ) -> Result<Box<dyn Provider>> { match (cache_path, rpc_url) { @@ -117,3 +130,81 @@ pub fn new_provider( (None, None) => Err(anyhow!("No cache_path or rpc_url given")), } } + +#[cfg(feature = "taiko")] +impl dyn Provider { + pub fn filter_event_log<E: SolEvent>( + &mut self, + l1_contract: Address, + l1_block_no: u64, + _l2_block_no: u64, + ) -> Result<Vec<(Log, E)>> { + use alloy_sol_types::TopicList; + use zeth_primitives::ethers::from_ethers_h256; + + let logs = self.get_logs(&LogsQuery { + address: l1_contract.into_array().into(), + from_block: l1_block_no, + to_block: l1_block_no, + })?; + let res = logs + .iter() + .filter(|log| log.topics.len() == <<E as SolEvent>::TopicList as TopicList>::COUNT) + .filter(|log| from_ethers_h256(log.topics[0]) == E::SIGNATURE_HASH) + .map(|log| { + let topics = log.topics.iter().map(|topic| from_ethers_h256(*topic)); + let event = E::decode_raw_log(topics, &log.data, false) + .unwrap_or_else(|_| panic!("Decode log failed for l1_block_no {l1_block_no}")); + (log.clone(), event) + }) + .collect::<Vec<_>>(); + + Ok(res) + } + + #[allow(dead_code)] + fn filter_block_proposal( + &mut self, + l1_contract: H160, + l1_block_no: u64, + l2_block_no: u64, + ) -> Result<(Transaction, BlockProposed)> { + use alloy_sol_types::TopicList; + use zeth_primitives::ethers::from_ethers_h256; + + let logs = self.get_logs(&LogsQuery { + address: l1_contract, + from_block: l1_block_no, + to_block: l1_block_no, + })?; + let mut res = logs + .iter() + .filter(|log| { + log.topics.len() == <<BlockProposed as SolEvent>::TopicList as TopicList>::COUNT + }) + .filter(|log| from_ethers_h256(log.topics[0]) == BlockProposed::SIGNATURE_HASH) + .map(|log| { + let topics = log.topics.iter().map(|topic| from_ethers_h256(*topic)); + let block_proposed = BlockProposed::decode_raw_log(topics, &log.data, false) + .unwrap_or_else(|_| panic!("Decode log failed for l1_block_no {l1_block_no}")); + (log.block_number, log.transaction_hash, block_proposed) + }) + .filter(|(_block_no, _tx_hash, event)| { + event.blockId == revm::primitives::U256::from(l2_block_no) + }) + .collect::<Vec<_>>(); + + let (block_no, tx_hash, event) = res + .pop() + .with_context(|| anyhow!("Cannot find BlockProposed event for {l2_block_no}"))?; + + let tx = self + .get_transaction(&TxQuery { + tx_hash: tx_hash.unwrap(), + block_no: block_no.map(|b| b.as_u64()), + }) + .with_context(|| anyhow!("Cannot find BlockProposed Tx {tx_hash:?}"))?; + + Ok((tx, event)) + } +} diff --git a/lib/src/host/provider/rpc_provider.rs b/lib/src/host/provider/rpc_provider.rs index 53480beec..e2cc22724 100644 --- a/lib/src/host/provider/rpc_provider.rs +++ b/lib/src/host/provider/rpc_provider.rs @@ -12,25 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -use anyhow::{anyhow, Context, Result}; +use anyhow::{anyhow, Result}; +#[cfg(feature = "taiko")] +use ethers_core::types::Log; use ethers_core::types::{ - Block, Bytes, EIP1186ProofResponse, Filter, Log, Transaction, H256, U256, + Block, Bytes, EIP1186ProofResponse, Filter, Transaction, TransactionReceipt, H256, U256, }; -use ethers_providers::{Http, Middleware}; +use ethers_providers::{Http, Middleware, RetryClient}; use log::info; -#[cfg(feature = "taiko")] -use zeth_primitives::taiko::BlockProposed; +// #[cfg(feature = "taiko")] +// use zeth_primitives::taiko::BlockProposed; use super::{AccountQuery, BlockQuery, ProofQuery, Provider, StorageQuery}; +#[cfg(feature = "taiko")] +use crate::host::provider::LogsQuery; pub struct RpcProvider { - http_client: ethers_providers::Provider<Http>, + http_client: ethers_providers::Provider<RetryClient<Http>>, tokio_handle: tokio::runtime::Handle, } impl RpcProvider { pub fn new(rpc_url: String) -> Result<Self> { - let http_client = ethers_providers::Provider::<Http>::try_from(&rpc_url)?; + let http_client = + ethers_providers::Provider::<RetryClient<Http>>::new_client(&rpc_url, 3, 500)?; let tokio_handle = tokio::runtime::Handle::current(); Ok(RpcProvider { @@ -46,7 +51,7 @@ impl Provider for RpcProvider { } fn get_full_block(&mut self, query: &BlockQuery) -> Result<Block<Transaction>> { - info!("Querying RPC for full block: {:?}", query); + info!("Querying RPC for full block: {query:?}"); let response = self .tokio_handle @@ -54,12 +59,12 @@ impl Provider for RpcProvider { match response { Some(out) => Ok(out), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } fn get_partial_block(&mut self, query: &BlockQuery) -> Result<Block<H256>> { - info!("Querying RPC for partial block: {:?}", query); + info!("Querying RPC for partial block: {query:?}"); let response = self .tokio_handle @@ -67,12 +72,22 @@ impl Provider for RpcProvider { match response { Some(out) => Ok(out), - None => Err(anyhow!("No data for {:?}", query)), + None => Err(anyhow!("No data for {query:?}")), } } + fn get_block_receipts(&mut self, query: &BlockQuery) -> Result<Vec<TransactionReceipt>> { + info!("Querying RPC for block receipts: {query:?}"); + + let response = self + .tokio_handle + .block_on(async { self.http_client.get_block_receipts(query.block_no).await })?; + + Ok(response) + } + fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse> { - info!("Querying RPC for inclusion proof: {:?}", query); + info!("Querying RPC for inclusion proof: {query:?}"); let out = self.tokio_handle.block_on(async { self.http_client @@ -88,7 +103,7 @@ impl Provider for RpcProvider { } fn get_transaction_count(&mut self, query: &AccountQuery) -> Result<U256> { - info!("Querying RPC for transaction count: {:?}", query); + info!("Querying RPC for transaction count: {query:?}"); let out = self.tokio_handle.block_on(async { self.http_client @@ -100,7 +115,7 @@ impl Provider for RpcProvider { } fn get_balance(&mut self, query: &AccountQuery) -> Result<U256> { - info!("Querying RPC for balance: {:?}", query); + info!("Querying RPC for balance: {query:?}"); let out = self.tokio_handle.block_on(async { self.http_client @@ -112,7 +127,7 @@ impl Provider for RpcProvider { } fn get_code(&mut self, query: &AccountQuery) -> Result<Bytes> { - info!("Querying RPC for code: {:?}", query); + info!("Querying RPC for code: {query:?}"); let out = self.tokio_handle.block_on(async { self.http_client @@ -124,7 +139,7 @@ impl Provider for RpcProvider { } fn get_storage(&mut self, query: &StorageQuery) -> Result<H256> { - info!("Querying RPC for storage: {:?}", query); + info!("Querying RPC for storage: {query:?}"); let out = self.tokio_handle.block_on(async { self.http_client @@ -136,70 +151,32 @@ impl Provider for RpcProvider { } #[cfg(feature = "taiko")] - fn get_propose(&mut self, query: &super::ProposeQuery) -> Result<(Transaction, BlockProposed)> { - use revm::primitives::U256; - info!("Querying RPC for propose: {:?}", query); - let filter = Filter::new() - .address(query.l1_contract) - .from_block(query.l1_block_no) - .to_block(query.l1_block_no); - let logs = self - .tokio_handle - .block_on(async { self.http_client.get_logs(&filter).await })?; - let result = taiko::filter_propose_block_event(&logs, U256::from(query.l2_block_no))?; - let (tx_hash, block_proposed) = - result.ok_or_else(|| anyhow!("No propose block event for {:?}", query))?; - let response = self - .tokio_handle - .block_on(async { self.http_client.get_transaction(tx_hash).await })?; - match response { - Some(out) => Ok((out, block_proposed)), - None => Err(anyhow!("No data for {:?}", query)), - } - } - - #[cfg(feature = "taiko")] - /// get 256 blocks one time to reduce the fetch time cost - fn batch_get_partial_blocks(&mut self, query: &BlockQuery) -> Result<Vec<Block<H256>>> { - info!("Querying RPC for partial blocks: {:?}", query); + fn get_logs(&mut self, query: &LogsQuery) -> Result<Vec<Log>> { + info!("Querying RPC for logs: {query:?}"); let out = self.tokio_handle.block_on(async { - use ethers_core::utils; - let id = utils::serialize(&query.block_no); self.http_client - .request("taiko_getL2ParentHeaders", [id]) + .get_logs( + &Filter::new() + .address(query.address) + .from_block(query.from_block) + .to_block(query.to_block), + ) .await })?; + Ok(out) } -} -#[cfg(feature = "taiko")] -pub mod taiko { - use alloy_sol_types::{SolEvent, TopicList}; - use revm::primitives::U256; - use zeth_primitives::{ethers::from_ethers_h256, taiko::BlockProposed}; - - use super::*; - pub fn filter_propose_block_event( - logs: &[Log], - block_id: U256, - ) -> Result<Option<(H256, BlockProposed)>> { - for log in logs { - if log.topics.len() != <<BlockProposed as SolEvent>::TopicList as TopicList>::COUNT { - continue; - } - if from_ethers_h256(log.topics[0]) != BlockProposed::SIGNATURE_HASH { - continue; - } - let topics = log.topics.iter().map(|topic| from_ethers_h256(*topic)); - let block_proposed = BlockProposed::decode_log(topics, &log.data, false) - .map_err(|e| anyhow!(e.to_string())) - .with_context(|| "decode log failed")?; - if block_proposed.blockId == block_id { - return Ok(log.transaction_hash.map(|h| (h, block_proposed))); - } + #[cfg(feature = "taiko")] + fn get_transaction(&mut self, query: &super::TxQuery) -> Result<Transaction> { + info!("Querying RPC for tx: {query:?}"); + let out = self + .tokio_handle + .block_on(async { self.http_client.get_transaction(query.tx_hash).await })?; + match out { + Some(out) => Ok(out), + None => Err(anyhow!("No data for {query:?}")), } - Ok(None) } } diff --git a/lib/src/host/provider_db.rs b/lib/src/host/provider_db.rs index bc009b3d0..083f89270 100644 --- a/lib/src/host/provider_db.rs +++ b/lib/src/host/provider_db.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +use alloc::vec::Vec; use std::collections::BTreeSet; use ethers_core::types::{EIP1186ProofResponse, H160, H256}; diff --git a/lib/src/host/taiko.rs b/lib/src/host/taiko.rs deleted file mode 100644 index 4deca04e5..000000000 --- a/lib/src/host/taiko.rs +++ /dev/null @@ -1,547 +0,0 @@ -//! Prepare Input for guest -use std::fmt::Debug; - -use anyhow::{anyhow, bail, Context, Result}; -use ethers_core::types::{Block, Transaction as EthersTransaction, H160, H256, U256, U64}; -use serde_json::to_string; -use thiserror::Error as ThisError; -use tracing::info; -use zeth_primitives::{ - block::Header, - ethers::{from_ethers_h160, from_ethers_h256, from_ethers_u256}, - keccak, - taiko::{ - deposits_hash, string_to_bytes32, BlockMetadata, EthDeposit, ProtocolInstance, Transition, - ANCHOR_GAS_LIMIT, *, - }, - transactions::EthereumTransaction, - withdrawal::Withdrawal, - Address, TxHash, B256, -}; - -use super::{ - provider::{new_provider, BlockQuery, ProofQuery, ProposeQuery, Provider}, - provider_db, Init, -}; -use crate::{ - block_builder::{BlockBuilder, NetworkStrategyBundle}, - consts::ChainSpec, - input::Input, - taiko::{utils::rlp_decode_list, Layer}, - EthereumTxEssence, -}; - -#[derive(Debug)] -pub struct TaikoExtra { - pub l1_hash: B256, - pub l1_height: u64, - pub l2_tx_list: Vec<u8>, - pub prover: Address, - pub graffiti: B256, - pub l1_signal_root: B256, - pub l2_signal_root: B256, - pub l2_withdrawals: Vec<Withdrawal>, - pub block_proposed: BlockProposed, - pub l1_next_block: Block<EthersTransaction>, - pub l2_fini_block: Block<EthersTransaction>, -} - -#[allow(clippy::too_many_arguments)] -pub fn get_taiko_initial_data<N: NetworkStrategyBundle<TxEssence = EthereumTxEssence>>( - l1_cache_path: Option<String>, - _l1_chain_spec: ChainSpec, - l1_rpc_url: Option<String>, - prover: Address, - l2_cache_path: Option<String>, - l2_chain_spec: ChainSpec, - l2_rpc_url: Option<String>, - l2_block_no: u64, - graffiti: B256, -) -> Result<(Init<EthereumTxEssence>, TaikoExtra)> { - let (l2_provider, l2_init_block, mut l2_fini_block, l2_signal_root, l2_input) = fetch_data( - "L2", - l2_cache_path, - l2_rpc_url, - l2_block_no, - l2_chain_spec.l2_signal_service.unwrap(), - Layer::L2, - )?; - // Get anchor call parameters - let anchorCall { - l1Hash: anchor_l1_hash, - l1SignalRoot: anchor_l1_signal_root, - l1Height: l1_block_no, - parentGasUsed: l2_parent_gas_used, - } = decode_anchor_call_args(&l2_fini_block.transactions[0].input)?; - - let (mut l1_provider, _l1_init_block, l1_fini_block, l1_signal_root, _l1_input) = fetch_data( - "L1", - l1_cache_path, - l1_rpc_url, - l1_block_no, - l2_chain_spec.l1_signal_service.unwrap(), - Layer::L1, - )?; - - let (propose_tx, block_metadata) = l1_provider.get_propose(&ProposeQuery { - l1_contract: H160::from_slice(l2_chain_spec.l1_contract.unwrap().as_slice()), - l1_block_no: l1_block_no + 1, - l2_block_no, - })?; - - let l1_next_block = l1_provider.get_full_block(&BlockQuery { - block_no: l1_block_no + 1, - })?; - - // save l1 data - l1_provider.save()?; - - let proposeBlockCall { - params: _, - txList: l2_tx_list, - } = decode_propose_block_call_args(&propose_tx.input)?; - - // 1. check l2 parent gas used - if l2_init_block.gas_used != U256::from(l2_parent_gas_used) { - return Err(anyhow!( - "parent gas used mismatch, expect: {}, got: {}", - l2_init_block.gas_used, - l2_parent_gas_used - )); - } - // 2. check l1 signal root - if anchor_l1_signal_root != l1_signal_root { - return Err(anyhow!( - "l1 signal root mismatch, expect: {}, got: {}", - anchor_l1_signal_root, - l1_signal_root - )); - } - // 3. check l1 block hash - if Some(anchor_l1_hash) != l1_fini_block.hash.map(from_ethers_h256) { - return Err(anyhow!( - "l1 block hash mismatch, expect: {}, got: {:?}", - anchor_l1_hash, - l1_fini_block.hash - )); - } - - let extra = TaikoExtra { - l1_hash: anchor_l1_hash, - l1_height: l1_block_no, - l2_tx_list, - prover, - graffiti, - l1_signal_root, - l2_signal_root, - l2_withdrawals: l2_input.withdrawals.clone(), - block_proposed: block_metadata, - l1_next_block, - l2_fini_block: l2_fini_block.clone(), - }; - - // rebuild transaction list by tx_list from l1 contract - rebuild_and_precheck_block(&l2_chain_spec, &mut l2_fini_block, &extra)?; - - // execute transactions and get states - let init = execute_data::<N>( - l2_provider, - l2_chain_spec, - l2_init_block, - l2_input, - l2_fini_block, - )?; - Ok((init, extra)) -} - -// rebuild the block with anchor transaction and txlist from l1 contract, then precheck it -pub fn rebuild_and_precheck_block( - l2_chain_spec: &ChainSpec, - l2_fini: &mut Block<EthersTransaction>, - extra: &TaikoExtra, -) -> Result<()> { - let Some(anchor) = l2_fini.transactions.first().cloned() else { - bail!("no anchor transaction found"); - }; - // - check anchor transaction - precheck_anchor(l2_chain_spec, l2_fini, &anchor) - .map_err(|e| anyhow!(e.to_string())) - .with_context(|| "precheck anchor error")?; - - let mut txs: Vec<EthersTransaction> = vec![]; - // - tx list bytes must be less than MAX_TX_LIST_BYTES - if extra.l2_tx_list.len() <= MAX_TX_LIST_BYTES { - txs = rlp_decode_list(&extra.l2_tx_list).unwrap_or_else(|err| { - tracing::error!("decode tx list error: {}", err); - vec![] - }); - } else { - tracing::error!( - "tx list bytes must be not more than MAX_TX_LIST_BYTES, got: {}", - extra.l2_tx_list.len() - ); - } - // - tx list must be less than MAX_TX_LIST - if txs.len() > MAX_TX_LIST { - tracing::error!( - "tx list must be not more than MAX_TX_LIST, got: {}", - txs.len() - ); - // reset to empty - txs.clear(); - } - // - patch anchor transaction into tx list instead of those from l2 node's - // insert the anchor transaction into the tx list at the first position - txs.insert(0, anchor); - // reset transactions - l2_fini.transactions = txs; - Ok(()) -} - -#[derive(ThisError, Debug)] -pub enum AnchorError { - #[error("anchor transaction type mismatch, expected: {expected}, got: {got}")] - AnchorTypeMisMatch { expected: u8, got: u8 }, - - #[error("anchor transaction from mismatch, expected: {expected}, got: {got:?}")] - AnchorFromMisMatch { - expected: Address, - got: Option<Address>, - }, - - #[error("anchor transaction to mismatch, expected: {expected}, got: {got:?}")] - AnchorToMisMatch { - expected: Address, - got: Option<Address>, - }, - - #[error("anchor transaction value mismatch, expected: {expected}, got: {got:?}")] - AnchorValueMisMatch { expected: U256, got: U256 }, - - #[error("anchor transaction gas limit mismatch, expected: {expected}, got: {got:?}")] - AnchorGasLimitMisMatch { expected: U256, got: U256 }, - - #[error("anchor transaction fee cap mismatch, expected: {expected:?}, got: {got:?}")] - AnchorFeeCapMisMatch { - expected: Option<U256>, - got: Option<U256>, - }, - - #[error("anchor transaction signature mismatch, {msg}")] - AnchorSignatureMismatch { msg: String }, - - #[error("anchor transaction decode error")] - Anyhow(#[from] anyhow::Error), -} - -pub fn precheck_anchor( - l2_chain_spec: &ChainSpec, - l2_fini: &Block<EthersTransaction>, - anchor: &EthersTransaction, -) -> Result<(), AnchorError> { - let tx1559_type = U64::from(0x2); - if anchor.transaction_type != Some(tx1559_type) { - return Err(AnchorError::AnchorTypeMisMatch { - expected: tx1559_type.as_u64() as u8, - got: anchor.transaction_type.unwrap_or_default().as_u64() as u8, - }); - } - let tx: EthereumTransaction = anchor.clone().try_into()?; - // verify transaction - check_anchor_signature(&tx)?; - // verify the transaction signature - let from = from_ethers_h160(anchor.from); - if from != *GOLDEN_TOUCH_ACCOUNT { - return Err(AnchorError::AnchorFromMisMatch { - expected: *GOLDEN_TOUCH_ACCOUNT, - got: Some(from), - }); - } - let Some(to) = anchor.to else { - return Err(AnchorError::AnchorToMisMatch { - expected: l2_chain_spec.l2_contract.unwrap(), - got: None, - }); - }; - let to = from_ethers_h160(to); - if to != l2_chain_spec.l2_contract.unwrap() { - return Err(AnchorError::AnchorFromMisMatch { - expected: l2_chain_spec.l2_contract.unwrap(), - got: Some(to), - }); - } - if anchor.value != U256::zero() { - return Err(AnchorError::AnchorValueMisMatch { - expected: U256::zero(), - got: anchor.value, - }); - } - if anchor.gas != U256::from(ANCHOR_GAS_LIMIT) { - return Err(AnchorError::AnchorGasLimitMisMatch { - expected: U256::from(ANCHOR_GAS_LIMIT), - got: anchor.gas, - }); - } - // anchor's gas price should be the same as the block's - if anchor.max_fee_per_gas != l2_fini.base_fee_per_gas { - return Err(AnchorError::AnchorFeeCapMisMatch { - expected: l2_fini.base_fee_per_gas, - got: anchor.max_fee_per_gas, - }); - } - Ok(()) -} - -pub fn assemble_protocol_instance(extra: &TaikoExtra, header: &Header) -> Result<ProtocolInstance> { - let tx_list_hash = TxHash::from(keccak::keccak(extra.l2_tx_list.as_slice())); - let deposits: Vec<EthDeposit> = extra - .l2_withdrawals - .iter() - .map(|w| EthDeposit { - recipient: w.address, - amount: w.amount as u128, - id: w.index, - }) - .collect(); - let deposits_hash = deposits_hash(&deposits); - let extra_data = string_to_bytes32(&header.extra_data); - // meta.difficulty = meta.blobHash ^ bytes32(block.prevrandao * b.numBlocks * - // block.number); - let block_hash = tx_list_hash; - let block_hash_h256: zeth_primitives::U256 = block_hash.into(); - let prevrando = if cfg!(feature = "pos") { - from_ethers_h256(extra.l1_next_block.mix_hash.unwrap_or_default()).into() - } else { - from_ethers_u256(extra.l1_next_block.difficulty) - }; - let difficulty = block_hash_h256 - ^ (prevrando - * zeth_primitives::U256::from(header.number) - * zeth_primitives::U256::from(extra.l1_next_block.number.unwrap_or_default().as_u64())); - let gas_limit: u64 = header.gas_limit.try_into().unwrap(); - let mut pi = ProtocolInstance { - transition: Transition { - parentHash: header.parent_hash, - blockHash: header.hash(), - signalRoot: extra.l2_signal_root, - graffiti: extra.graffiti, - }, - block_metadata: BlockMetadata { - l1Hash: extra.l1_hash, - difficulty: difficulty.into(), - blobHash: tx_list_hash, - extraData: extra_data.into(), - depositsHash: deposits_hash, - coinbase: header.beneficiary, - id: header.number, - gasLimit: (gas_limit - ANCHOR_GAS_LIMIT) as u32, - timestamp: header.timestamp.try_into().unwrap(), - l1Height: extra.l1_height, - txListByteOffset: 0u32, - txListByteSize: extra.l2_tx_list.len() as u32, - minTier: extra.block_proposed.meta.minTier, - blobUsed: extra.l2_tx_list.is_empty(), - parentMetaHash: extra.block_proposed.meta.parentMetaHash, - }, - prover: extra.prover, - }; - verify(header, &mut pi, extra)?; - Ok(pi) -} - -pub fn verify(header: &Header, pi: &mut ProtocolInstance, extra: &TaikoExtra) -> Result<()> { - use alloy_sol_types::SolValue; - // check the block metadata - if pi.block_metadata.abi_encode() != extra.block_proposed.meta.abi_encode() { - return Err(anyhow!( - "block metadata mismatch, expected: {:?}, got: {:?}", - extra.block_proposed.meta, - pi.block_metadata - )); - } - // println!("Protocol instance Transition: {:?}", pi.transition); - // check the block hash - if Some(header.hash()) != extra.l2_fini_block.hash.map(from_ethers_h256) { - let txs: Vec<EthereumTransaction> = extra - .l2_fini_block - .transactions - .iter() - .filter_map(|tx| tx.clone().try_into().ok()) - .collect(); - return Err(anyhow!( - "block hash mismatch, expected: {}, got: {}", - to_string(&txs).unwrap_or_default(), - to_string(&header.transactions).unwrap_or_default(), - )); - } - - Ok(()) -} - -#[allow(clippy::type_complexity)] -pub fn fetch_data( - annotation: &str, - cache_path: Option<String>, - rpc_url: Option<String>, - block_no: u64, - signal_service: Address, - layer: Layer, -) -> Result<( - Box<dyn Provider>, - Block<H256>, - Block<EthersTransaction>, - B256, - Input<EthereumTxEssence>, -)> { - let mut provider = new_provider(cache_path, rpc_url)?; - - let fini_query = BlockQuery { block_no }; - match layer { - Layer::L1 => {} - Layer::L2 => { - provider.batch_get_partial_blocks(&fini_query)?; - } - } - // Fetch the initial block - let init_block = provider.get_partial_block(&BlockQuery { - block_no: block_no - 1, - })?; - - info!( - "Initial {} block: {:?} ({:?})", - annotation, - init_block.number.unwrap(), - init_block.hash.unwrap() - ); - - // Fetch the finished block - let fini_block = provider.get_full_block(&fini_query)?; - - info!( - "Final {} block number: {:?} ({:?})", - annotation, - fini_block.number.unwrap(), - fini_block.hash.unwrap() - ); - info!("Transaction count: {:?}", fini_block.transactions.len()); - - // Get l2 signal root by signal service - let proof = provider.get_proof(&ProofQuery { - block_no, - address: H160::from_slice(signal_service.as_slice()), - indices: Default::default(), - })?; - let signal_root = from_ethers_h256(proof.storage_hash); - - info!( - "Final {} signal root: {:?} ({:?})", - annotation, - fini_block.number.unwrap(), - signal_root, - ); - - // Create input - let input = Input { - beneficiary: fini_block.author.map(from_ethers_h160).unwrap_or_default(), - gas_limit: from_ethers_u256(fini_block.gas_limit), - timestamp: from_ethers_u256(fini_block.timestamp), - extra_data: fini_block.extra_data.0.clone().into(), - mix_hash: from_ethers_h256(fini_block.mix_hash.unwrap()), - transactions: fini_block - .transactions - .clone() - .into_iter() - .map(|tx| tx.try_into().unwrap()) - .collect(), - withdrawals: fini_block - .withdrawals - .clone() - .unwrap_or_default() - .into_iter() - .map(|w| w.try_into().unwrap()) - .collect(), - parent_state_trie: Default::default(), - parent_storage: Default::default(), - contracts: vec![], - parent_header: init_block.clone().try_into()?, - ancestor_headers: vec![], - base_fee_per_gas: from_ethers_u256(fini_block.base_fee_per_gas.unwrap_or_default()), - }; - - Ok((provider, init_block, fini_block, signal_root, input)) -} - -pub fn execute_data<N: NetworkStrategyBundle<TxEssence = EthereumTxEssence>>( - provider: Box<dyn Provider>, - chain_spec: ChainSpec, - init_block: Block<H256>, - input: Input<EthereumTxEssence>, - fini_block: Block<EthersTransaction>, -) -> Result<Init<EthereumTxEssence>> { - // Create the provider DB - let provider_db = provider_db::ProviderDb::new(provider, init_block.number.unwrap().as_u64()); - // Create the block builder, run the transactions and extract the DB - let mut builder = BlockBuilder::new(&chain_spec, input) - .with_db(provider_db) - .prepare_header::<N::HeaderPrepStrategy>()? - .execute_transactions::<N::TxExecStrategy>()?; - let provider_db = builder.mut_db().unwrap(); - - info!("Gathering inclusion proofs ..."); - - // Gather inclusion proofs for the initial and final state - let init_proofs = provider_db.get_initial_proofs()?; - let fini_proofs = provider_db.get_latest_proofs()?; - - // Gather proofs for block history - let history_headers = provider_db.provider.batch_get_partial_blocks(&BlockQuery { - block_no: fini_block.number.unwrap().as_u64(), - })?; - // ancestors == history - current - parent - let ancestor_headers = if history_headers.len() > 2 { - history_headers - .into_iter() - .rev() - .skip(2) - .map(|header| { - header - .try_into() - .expect("Failed to convert ancestor headers") - }) - .collect() - } else { - vec![] - }; - - info!("Saving provider cache ..."); - - // Save the provider cache - provider_db.get_provider().save()?; - info!("Provider-backed execution is Done!"); - // assemble init - let transactions = fini_block - .transactions - .clone() - .into_iter() - .map(|tx| tx.try_into().unwrap()) - .collect(); - let withdrawals = fini_block - .withdrawals - .clone() - .unwrap_or_default() - .into_iter() - .map(|w| w.try_into().unwrap()) - .collect(); - - let init = Init { - db: provider_db.get_initial_db().clone(), - init_block: init_block.try_into()?, - init_proofs, - fini_block: fini_block.try_into()?, - fini_transactions: transactions, - fini_withdrawals: withdrawals, - fini_proofs, - ancestor_headers, - }; - Ok(init) -} diff --git a/lib/src/host/verify.rs b/lib/src/host/verify.rs new file mode 100644 index 000000000..5d99b499e --- /dev/null +++ b/lib/src/host/verify.rs @@ -0,0 +1,237 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use alloc::vec::Vec; + +use anyhow::{anyhow, bail, Context, Result}; +use ethers_core::types::EIP1186ProofResponse; +use hashbrown::HashMap; +use log::error; +use zeth_primitives::{ + block::Header, + keccak::keccak, + transactions::TxEssence, + trie::{Error as TrieError, MptNode, StateAccount}, + Address, B256, U256, +}; + +use super::{mpt, preflight}; + +#[derive(Debug)] +pub enum VerifyError { + BalanceMismatch { + rpc_value: U256, + our_value: U256, + difference: U256, + }, + NonceMismatch { + rpc_value: u64, + our_value: u64, + }, + CodeHashMismatch { + rpc_value: B256, + our_value: B256, + }, + StorageMismatch { + index: U256, + rpc_value: U256, + our_db_value: U256, + our_trie_value: U256, + }, + StorageRootMismatch { + rpc_value: B256, + our_value: B256, + }, + /// The account is not in the trie, but the RPC response proves that it is included. + MissingAccount, + /// The account cannot be resolved in the trie. + UnresolvedAccount, +} + +/// Verify the block header and state trie. +pub trait Verifier { + fn verify_block(&self, header: &Header, state: &MptNode) -> Result<()>; +} + +/// Verify using the preflight data. +impl<E: TxEssence> Verifier for preflight::Data<E> { + fn verify_block(&self, header: &Header, state: &MptNode) -> Result<()> { + let errors = + verify_state_trie(state, &self.proofs).context("failed to verify state trie")?; + + for (address, address_errors) in &errors { + error!( + "Verify found {:?} error(s) for address {address:?}", + address_errors.len(), + ); + for error in address_errors { + error!(" Error: {error:?}"); + } + } + + let accounts_len = self.proofs.len(); + let errors_len = errors.len(); + if errors_len > 0 { + error!( + "Verify found {errors_len:?} account(s) with error(s) ({}% correct)", + (100.0 * (accounts_len - errors_len) as f64 / accounts_len as f64) + ); + } + + verify_header(header, &self.header) + } +} + +fn verify_header(header: &Header, exp_header: &Header) -> Result<()> { + if header.state_root != exp_header.state_root { + error!( + "State root mismatch {} (expected {})", + header.state_root, exp_header.state_root + ); + } + + if header.transactions_root != exp_header.transactions_root { + error!( + "Transactions root mismatch {} (expected {})", + header.transactions_root, exp_header.transactions_root + ); + } + + if header.receipts_root != exp_header.receipts_root { + error!( + "Receipts root mismatch {} (expected {})", + header.receipts_root, exp_header.receipts_root + ); + } + + if header.base_fee_per_gas != exp_header.base_fee_per_gas { + error!( + "Base fee mismatch {} (expected {})", + header.base_fee_per_gas, exp_header.base_fee_per_gas + ); + } + + if header.withdrawals_root != exp_header.withdrawals_root { + error!( + "Withdrawals root mismatch {:?} (expected {:?})", + header.withdrawals_root, exp_header.withdrawals_root + ); + } + + let found_hash = header.hash(); + let expected_hash = exp_header.hash(); + if found_hash.as_slice() != expected_hash.as_slice() { + error!("Final block hash mismatch {found_hash} (expected {expected_hash})"); + + bail!("Invalid block hash"); + } + + Ok(()) +} + +fn verify_state_trie( + state_trie: &MptNode, + proofs: &HashMap<Address, EIP1186ProofResponse>, +) -> Result<HashMap<Address, Vec<VerifyError>>> { + let mut errors = HashMap::new(); + + for (address, response) in proofs { + let account_proof = &response.account_proof; + let rpc_account: StateAccount = response.clone().into(); + + let key = keccak(address); + let mut address_errors = Vec::new(); + match state_trie.get_rlp::<StateAccount>(&key) { + Ok(account) => match account { + // the account is not in the trie + None => { + // the RPC response should prove that the account is not included + if !account_deleted(&key, account_proof) + .with_context(|| anyhow!("invalid account_proof for {address:#}"))? + { + address_errors.push(VerifyError::MissingAccount); + } + } + Some(account_info) => { + // Account balance + { + let rpc_value = rpc_account.balance; + let our_value = account_info.balance; + if rpc_value != our_value { + let difference = rpc_value.abs_diff(our_value); + address_errors.push(VerifyError::BalanceMismatch { + rpc_value, + our_value, + difference, + }) + } + } + + // Nonce + { + let rpc_value = rpc_account.nonce; + let our_value = account_info.nonce; + if rpc_value != our_value { + address_errors.push(VerifyError::NonceMismatch { + rpc_value, + our_value, + }) + } + } + + // Code hash + { + let rpc_value = rpc_account.code_hash; + let our_value = account_info.code_hash; + if rpc_value != our_value { + address_errors.push(VerifyError::CodeHashMismatch { + rpc_value, + our_value, + }) + } + } + + // Storage root + { + let rpc_value = rpc_account.storage_root; + let our_value = account_info.storage_root; + if rpc_value != our_value { + address_errors.push(VerifyError::StorageRootMismatch { + rpc_value, + our_value, + }); + } + } + } + }, + // the account was pruned from the trie + Err(TrieError::NodeNotResolved(_)) => { + address_errors.push(VerifyError::UnresolvedAccount); + } + Err(err) => { + bail!("Error while fetching account {address:?}: {err:?}"); + } + } + + if !address_errors.is_empty() { + errors.insert(address.to_owned(), address_errors); + } + } + + Ok(errors) +} + +fn account_deleted(key: &[u8], proof: &[impl AsRef<[u8]>]) -> Result<bool> { + let proof_nodes = mpt::parse_proof(proof).context("invalid encoding")?; + mpt::is_not_included(key, &proof_nodes) +} diff --git a/lib/src/input.rs b/lib/src/input.rs index a95a96680..b98f5a606 100644 --- a/lib/src/input.rs +++ b/lib/src/input.rs @@ -24,6 +24,11 @@ use zeth_primitives::{ Address, Bytes, B256, U256, }; +/// Represents the state of an account's storage. +/// The storage trie together with the used storage slots allow us to reconstruct all the +/// required values. +pub type StorageEntry = (MptNode, Vec<U256>); + /// External block input. #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct Input<E: TxEssence> { @@ -55,8 +60,6 @@ pub struct Input<E: TxEssence> { pub base_fee_per_gas: U256, } -pub type StorageEntry = (MptNode, Vec<U256>); - #[cfg(test)] mod tests { use zeth_primitives::transactions::ethereum::EthereumTxEssence; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 224a2c185..32f6c10d3 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -12,29 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. #![feature(path_file_prefix)] -#![cfg_attr(target_os = "zkvm", no_std)] -#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(any(not(feature = "std"), target_os = "zkvm"), no_std)] extern crate alloc; extern crate core; -pub mod block_builder; +pub mod builder; pub mod consts; -pub mod execution; -pub mod finalization; -pub mod initialization; pub mod input; pub mod mem_db; -pub mod preparation; + +#[cfg(feature = "optimism")] +pub mod optimism; #[cfg(feature = "taiko")] pub mod taiko; -#[cfg(feature = "std")] -#[cfg(not(target_os = "zkvm"))] +#[cfg(all(feature = "std", not(target_os = "zkvm")))] pub mod host; -pub use zeth_primitives::transactions::ethereum::EthereumTxEssence; +#[cfg(feature = "optimism")] +mod utils; + +pub use zeth_primitives::transactions::{ethereum::EthereumTxEssence, optimism::OptimismTxEssence}; /// call forget only if running inside the guest pub fn guest_mem_forget<T>(_t: T) { diff --git a/lib/src/optimism/batcher.rs b/lib/src/optimism/batcher.rs new file mode 100644 index 000000000..d1b039fba --- /dev/null +++ b/lib/src/optimism/batcher.rs @@ -0,0 +1,483 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::cmp::Ordering; +use std::collections::{BTreeMap, VecDeque}; + +use anyhow::{bail, ensure, Context, Result}; +use zeth_primitives::{ + batch::{Batch, BatchEssence}, + transactions::{ + ethereum::EthereumTxEssence, + optimism::{OptimismTxEssence, OPTIMISM_DEPOSITED_TX_TYPE}, + Transaction, + }, + BlockHash, BlockNumber, B256, U256, +}; + +use super::{ + batcher_channel::BatcherChannels, batcher_db::BlockInput, config::ChainConfig, deposits, +}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub struct BlockId { + pub hash: B256, + pub number: BlockNumber, +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] +pub struct L2BlockInfo { + pub hash: B256, + pub timestamp: u64, + pub l1_origin: BlockId, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Epoch { + pub number: BlockNumber, + pub hash: B256, + pub timestamp: u64, + pub base_fee_per_gas: U256, + pub deposits: Vec<Transaction<OptimismTxEssence>>, +} + +#[derive(Debug, Clone, Default)] +pub struct State { + pub current_l1_block_number: BlockNumber, + pub current_l1_block_hash: BlockHash, + pub safe_head: L2BlockInfo, + pub epoch: Epoch, + pub op_epoch_queue: VecDeque<Epoch>, + pub next_epoch: Option<Epoch>, +} + +impl State { + pub fn new( + current_l1_block_number: BlockNumber, + current_l1_block_hash: BlockHash, + safe_head: L2BlockInfo, + epoch: Epoch, + ) -> Self { + State { + current_l1_block_number, + current_l1_block_hash, + safe_head, + epoch, + op_epoch_queue: VecDeque::new(), + next_epoch: None, + } + } + + pub fn do_next_epoch(&mut self) -> Result<()> { + self.epoch = self.next_epoch.take().context("no next epoch!")?; + self.deque_next_epoch_if_none()?; + Ok(()) + } + + pub fn push_epoch(&mut self, epoch: Epoch) -> Result<()> { + self.op_epoch_queue.push_back(epoch); + self.deque_next_epoch_if_none()?; + Ok(()) + } + + fn deque_next_epoch_if_none(&mut self) -> Result<()> { + if self.next_epoch.is_none() { + while let Some(next_epoch) = self.op_epoch_queue.pop_front() { + if next_epoch.number == self.epoch.number + 1 { + self.next_epoch = Some(next_epoch); + break; + } + + if next_epoch.number > self.epoch.number { + bail!("Epoch gap!"); + } + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq)] +enum BatchStatus { + Drop, + Accept, + Undecided, + Future, +} + +/// A [Batch] with inclusion information. +pub struct BatchWithInclusion { + pub essence: BatchEssence, + pub inclusion_block_number: BlockNumber, +} + +pub struct Batcher { + /// Multimap of batches, keyed by timestamp + batches: BTreeMap<u64, VecDeque<BatchWithInclusion>>, + batcher_channel: BatcherChannels, + pub state: State, + pub config: ChainConfig, +} + +impl Batcher { + pub fn new( + config: ChainConfig, + op_head: L2BlockInfo, + eth_block: &BlockInput<EthereumTxEssence>, + ) -> Result<Batcher> { + let eth_block_hash = eth_block.block_header.hash(); + + let batcher_channel = BatcherChannels::new(&config); + + let state = State::new( + eth_block.block_header.number, + eth_block_hash, + op_head, + Epoch { + number: eth_block.block_header.number, + hash: eth_block_hash, + timestamp: eth_block.block_header.timestamp.try_into().unwrap(), + base_fee_per_gas: eth_block.block_header.base_fee_per_gas, + deposits: deposits::extract_transactions(&config, eth_block)?, + }, + ); + + Ok(Batcher { + batches: BTreeMap::new(), + batcher_channel, + state, + config, + }) + } + + pub fn process_l1_block(&mut self, eth_block: &BlockInput<EthereumTxEssence>) -> Result<()> { + let eth_block_hash = eth_block.block_header.hash(); + + // Ensure block has correct parent + ensure!( + eth_block.block_header.parent_hash == self.state.current_l1_block_hash, + "Eth block has invalid parent hash" + ); + + // Update the system config. From the spec: + // "Upon traversal of the L1 block, the system configuration copy used by the L1 retrieval + // stage is updated, such that the batch-sender authentication is always accurate to the + // exact L1 block that is read by the stage" + if eth_block.receipts.is_some() { + self.config + .system_config + .update(&self.config.system_config_contract, eth_block) + .context("failed to update system config")?; + } + + // Enqueue epoch + self.state.push_epoch(Epoch { + number: eth_block.block_header.number, + hash: eth_block_hash, + timestamp: eth_block.block_header.timestamp.try_into().unwrap(), + base_fee_per_gas: eth_block.block_header.base_fee_per_gas, + deposits: deposits::extract_transactions(&self.config, eth_block)?, + })?; + + // process all transactions of this block to generate batches + self.batcher_channel + .process_l1_transactions( + self.config.system_config.batch_sender, + eth_block.block_header.number, + ð_block.transactions, + ) + .context("failed to process transactions")?; + + // Read batches + while let Some(batches) = self.batcher_channel.read_batches() { + batches.into_iter().for_each(|batch| { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "received batch: timestamp={}, parent_hash={}, epoch={}", + batch.essence.timestamp, + batch.essence.parent_hash, + batch.essence.epoch_num + ); + self.batches + .entry(batch.essence.timestamp) + .or_default() + .push_back(batch); + }); + } + + self.state.current_l1_block_number = eth_block.block_header.number; + self.state.current_l1_block_hash = eth_block_hash; + + Ok(()) + } + + pub fn read_batch(&mut self) -> Result<Option<Batch>> { + let epoch = &self.state.epoch; + let safe_l2_head = self.state.safe_head; + + ensure!( + safe_l2_head.l1_origin.hash == epoch.hash + || safe_l2_head.l1_origin.number == epoch.number - 1, + "buffered L1 chain epoch does not match safe head origin" + ); + + let mut next_batch = None; + + // Grab the first accepted batch. From the spec: + // "The batches are processed in order of the inclusion on L1: if multiple batches can be + // accept-ed the first is applied. An implementation can defer future batches a later + // derivation step to reduce validation work." + 'outer: while let Some((ts, mut batches)) = self.batches.pop_first() { + // iterate over all batches, in order of inclusion and find the first accepted batch + // retain batches that may be processed in the future, or those we are undecided on + while let Some(batch) = batches.pop_front() { + match self.batch_status(&batch) { + BatchStatus::Accept => { + next_batch = Some(batch); + // if there are still batches left, insert them back into the map + if !batches.is_empty() { + self.batches.insert(ts, batches); + } + break 'outer; + } + BatchStatus::Drop => {} + BatchStatus::Future | BatchStatus::Undecided => { + batches.push_front(batch); + self.batches.insert(ts, batches); + break 'outer; + } + } + } + } + + if let Some(batch) = next_batch { + return Ok(Some(Batch(batch.essence))); + } + + // If there are no accepted batches, attempt to generate the default batch. From the spec: + // "If no batch can be accept-ed, and the stage has completed buffering of all batches + // that can fully be read from the L1 block at height epoch.number + + // sequence_window_size, and the next_epoch is available, then an empty batch can be + // derived." + let current_l1_block = self.state.current_l1_block_number; + let sequence_window_size = self.config.seq_window_size; + let first_of_epoch = epoch.number == safe_l2_head.l1_origin.number + 1; + + if current_l1_block <= epoch.number + sequence_window_size { + return Ok(None); + } + + let Some(next_epoch) = &self.state.next_epoch else { + return Ok(None); + }; + + let next_timestamp = safe_l2_head.timestamp + self.config.blocktime; + let batch_epoch = if next_timestamp < next_epoch.timestamp || first_of_epoch { + // From the spec: + // "If next_timestamp < next_epoch.time: the current L1 origin is repeated, + // to preserve the L2 time invariant." + // "If the batch is the first batch of the epoch, that epoch is used instead + // of advancing the epoch to ensure that there is at least one L2 block per + // epoch." + epoch + } else { + next_epoch + }; + + Ok(Some(Batch::new( + safe_l2_head.hash, + batch_epoch.number, + batch_epoch.hash, + next_timestamp, + ))) + } + + fn batch_status(&self, batch: &BatchWithInclusion) -> BatchStatus { + // Apply the batch status rules. The spec describes a precise order for these checks. + + let epoch = &self.state.epoch; + let next_epoch = &self.state.next_epoch; + let safe_l2_head = self.state.safe_head; + let next_timestamp = safe_l2_head.timestamp + self.config.blocktime; + + // From the spec: + // "batch.timestamp > next_timestamp -> future" + // "batch.timestamp < next_timestamp -> drop" + match batch.essence.timestamp.cmp(&next_timestamp) { + Ordering::Greater => { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Future batch: {} = batch.timestamp > next_timestamp = {}", + &batch.essence.timestamp, + &next_timestamp + ); + return BatchStatus::Future; + } + Ordering::Less => { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Batch too old: {} = batch.timestamp < next_timestamp = {}", + &batch.essence.timestamp, + &next_timestamp + ); + return BatchStatus::Drop; + } + Ordering::Equal => (), + } + + // From the spec: + // "batch.parent_hash != safe_l2_head.hash -> drop" + if batch.essence.parent_hash != safe_l2_head.hash { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Incorrect parent hash: {} != {}", + batch.essence.parent_hash, + safe_l2_head.hash + ); + return BatchStatus::Drop; + } + + // From the spec: + // "batch.epoch_num + sequence_window_size < inclusion_block_number -> drop" + if batch.essence.epoch_num + self.config.seq_window_size < batch.inclusion_block_number { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Batch is not timely: {} + {} < {}", + batch.essence.epoch_num, + self.config.seq_window_size, + batch.inclusion_block_number + ); + return BatchStatus::Drop; + } + + // From the spec: + // "batch.epoch_num < epoch.number -> drop" + if batch.essence.epoch_num < epoch.number { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Batch epoch number is too low: {} < {}", + batch.essence.epoch_num, + epoch.number + ); + return BatchStatus::Drop; + } + + let batch_origin = match batch.essence.epoch_num { + // From the spec: + // "batch.epoch_num == epoch.number: define batch_origin as epoch" + n if n == epoch.number => epoch, + // From the spec: + // "batch.epoch_num == epoch.number+1:" + // " If known, then define batch_origin as next_epoch" + // " If next_epoch is not known -> undecided" + n if n == epoch.number + 1 => match next_epoch { + Some(epoch) => epoch, + None => return BatchStatus::Undecided, + }, + // From the spec: + // "batch.epoch_num > epoch.number+1 -> drop" + _ => { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Batch epoch number is too large: {} > {}", + batch.essence.epoch_num, + epoch.number + 1 + ); + return BatchStatus::Drop; + } + }; + + // From the spec: + // "batch.epoch_hash != batch_origin.hash -> drop" + if batch.essence.epoch_hash != batch_origin.hash { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Epoch hash mismatch: {} != {}", + batch.essence.epoch_hash, + batch_origin.hash + ); + return BatchStatus::Drop; + } + + // From the spec: + // "batch.timestamp < batch_origin.time -> drop" + if batch.essence.timestamp < batch_origin.timestamp { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Batch violates timestamp rule: {} < {}", + batch.essence.timestamp, + batch_origin.timestamp + ); + return BatchStatus::Drop; + } + + // From the spec: + // "batch.timestamp > batch_origin.time + max_sequencer_drift: enforce the L2 timestamp + // drift rule, but with exceptions to preserve above min L2 timestamp invariant:" + if batch.essence.timestamp > batch_origin.timestamp + self.config.max_seq_drift { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "Sequencer drift detected: {} > {} + {}", + batch.essence.timestamp, + batch_origin.timestamp, + self.config.max_seq_drift + ); + + // From the spec: + // "len(batch.transactions) > 0: -> drop" + if !batch.essence.transactions.is_empty() { + #[cfg(not(target_os = "zkvm"))] + log::debug!("Sequencer drift detected for non-empty batch; drop."); + return BatchStatus::Drop; + } + + // From the spec: + // "len(batch.transactions) == 0:" + // epoch.number == batch.epoch_num: this implies the batch does not already + // advance the L1 origin, and must thus be checked against next_epoch." + if epoch.number == batch.essence.epoch_num { + let Some(next_epoch) = next_epoch else { + // From the spec: + // "If next_epoch is not known -> undecided" + #[cfg(not(target_os = "zkvm"))] + log::debug!("Sequencer drift detected, but next epoch is not known; undecided"); + return BatchStatus::Undecided; + }; + + // From the spec: + // "If batch.timestamp >= next_epoch.time -> drop" + if batch.essence.timestamp >= next_epoch.timestamp { + #[cfg(not(target_os = "zkvm"))] + log::debug!("Sequencer drift detected; drop; batch timestamp is too far into the future. {} >= {}", batch.essence.timestamp, next_epoch.timestamp); + return BatchStatus::Drop; + } + } + } + + // From the spec: + // "batch.transactions: drop if the batch.transactions list contains a transaction that is + // invalid or derived by other means exclusively: + // any transaction that is empty (zero length byte string) + // any deposited transactions (identified by the transaction type prefix byte)" + for tx in &batch.essence.transactions { + if matches!(tx.first(), None | Some(&OPTIMISM_DEPOSITED_TX_TYPE)) { + #[cfg(not(target_os = "zkvm"))] + log::debug!("Batch contains empty or invalid transaction"); + return BatchStatus::Drop; + } + } + + BatchStatus::Accept + } +} diff --git a/lib/src/optimism/batcher_channel.rs b/lib/src/optimism/batcher_channel.rs new file mode 100644 index 000000000..7a3718e9b --- /dev/null +++ b/lib/src/optimism/batcher_channel.rs @@ -0,0 +1,579 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + collections::{BTreeMap, VecDeque}, + io::Read, +}; + +use anyhow::{bail, ensure, Context, Result}; +use bytes::Buf; +use libflate::zlib::Decoder; +use zeth_primitives::{ + batch::Batch, + rlp::Decodable, + transactions::{ethereum::EthereumTxEssence, Transaction, TxEssence}, + Address, BlockNumber, +}; + +use super::{batcher::BatchWithInclusion, config::ChainConfig}; +use crate::utils::MultiReader; + +pub const MAX_RLP_BYTES_PER_CHANNEL: u64 = 10_000_000; + +pub struct BatcherChannels { + batch_inbox: Address, + max_channel_bank_size: u64, + channel_timeout: u64, + channels: VecDeque<Channel>, + batches: VecDeque<Vec<BatchWithInclusion>>, +} + +impl BatcherChannels { + pub fn new(config: &ChainConfig) -> Self { + Self { + batch_inbox: config.batch_inbox, + max_channel_bank_size: config.max_channel_bank_size, + channel_timeout: config.channel_timeout, + channels: VecDeque::new(), + batches: VecDeque::new(), + } + } + + /// Processes all batcher transactions in the given block. + /// The given batch_sender must match the potentially updated batcher address loaded + /// from the system config. + pub fn process_l1_transactions( + &mut self, + batch_sender: Address, + block_number: BlockNumber, + transactions: &Vec<Transaction<EthereumTxEssence>>, + ) -> Result<()> { + for tx in transactions { + // From the spec: + // "The receiver must be the configured batcher inbox address." + if tx.essence.to() != Some(self.batch_inbox) { + continue; + } + // From the spec: + // "The sender must match the batcher address loaded from the system config matching + // the L1 block of the data." + if tx.recover_from().context("invalid signature")? != batch_sender { + continue; + } + + #[cfg(not(target_os = "zkvm"))] + log::debug!("received batcher tx: {}", tx.hash()); + + // From the spec: + // "If any one frame fails to parse, the all frames in the transaction are rejected." + let frames = match Frame::process_batcher_transaction(&tx.essence) { + Ok(frames) => frames, + Err(_err) => { + #[cfg(not(target_os = "zkvm"))] + log::warn!("failed to decode all frames; skip entire batcher tx: {_err:#}"); + continue; + } + }; + + // load received frames into the channel bank + for frame in frames { + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "received frame: channel_id={}, frame_number={}, is_last={}", + frame.channel_id, + frame.number, + frame.is_last + ); + + self.add_frame(block_number, frame); + } + + // Remove all timed-out channels at the front of the queue. From the spec: + // "Upon reading, while the first opened channel is timed-out, remove it from the + // channel-bank." + while matches!(self.channels.front(), Some(channel) if block_number > channel.open_l1_block + self.channel_timeout) + { + let _channel = self.channels.pop_front().unwrap(); + #[cfg(not(target_os = "zkvm"))] + log::debug!("timed-out channel: {}", _channel.id); + } + + // read all ready channels from the front of the queue + while matches!(self.channels.front(), Some(channel) if channel.is_ready()) { + let channel = self.channels.pop_front().unwrap(); + #[cfg(not(target_os = "zkvm"))] + log::debug!("received channel: {}", channel.id); + + self.batches.push_back(channel.read_batches(block_number)); + } + } + + Ok(()) + } + + pub fn read_batches(&mut self) -> Option<Vec<BatchWithInclusion>> { + self.batches.pop_front() + } + + /// Adds a frame to the channel bank. Frames that cannot be added are ignored. + fn add_frame(&mut self, block_number: BlockNumber, frame: Frame) { + let channel = self + .channel_index(frame.channel_id) + .and_then(|idx| self.channels.get_mut(idx)); + + match channel { + Some(channel) => { + if block_number > channel.open_l1_block + self.channel_timeout { + // From the spec: + // "New frames for timed-out channels are dropped instead of buffered." + #[cfg(not(target_os = "zkvm"))] + log::warn!("frame's channel is timed out; ignored"); + return; + } + if let Err(_err) = channel.add_frame(frame) { + #[cfg(not(target_os = "zkvm"))] + log::warn!("failed to add frame to channel; ignored: {_err:#}"); + return; + } + } + None => { + // Create new channel. From the spec: + // "When a channel ID referenced by a frame is not already present in the + // Channel Bank, a new channel is opened, tagged with the current L1 + // block, and appended to the channel-queue" + self.channels.push_back(Channel::new(block_number, frame)); + } + } + + // From the spec: + // "After successfully inserting a new frame, the ChannelBank is pruned: channels + // are dropped in FIFO order, until total_size <= MAX_CHANNEL_BANK_SIZE." + self.prune(); + } + + /// Enforces max_channel_bank_size by dropping channels in FIFO order. + fn prune(&mut self) { + let mut total_size = self.total_size(); + while total_size as u64 > self.max_channel_bank_size { + let dropped_channel = self.channels.pop_front().unwrap(); + total_size -= dropped_channel.size; + + #[cfg(not(target_os = "zkvm"))] + log::debug!( + "pruned channel: {} (channel_size: {})", + dropped_channel.id, + dropped_channel.size + ); + } + } + + fn total_size(&self) -> usize { + self.channels.iter().map(|c| c.size).sum() + } + + fn channel_index(&self, channel_id: ChannelId) -> Option<usize> { + self.channels.iter().position(|c| c.id == channel_id) + } +} + +/// A [ChannelId] is a unique identifier for a [Channel]. +type ChannelId = u128; + +/// A [Channel] is a set of batches that are split into at least one, but possibly +/// multiple frames. Frames are allowed to be ingested in any order. +#[derive(Clone, Debug, Default)] +struct Channel { + /// The channel ID. + id: ChannelId, + /// The number of the L1 block that opened this channel. + open_l1_block: u64, + /// The number of the frame that closes this channel. + close_frame_number: Option<u16>, + /// All frames belonging to this channel by their frame number. + frames: BTreeMap<u16, Frame>, + /// The estimated memory size, used to drop the channel if we have too much data. + size: usize, +} + +impl Channel { + const FRAME_OVERHEAD: usize = 200; + + /// Creates a new channel from the given frame. + fn new(open_l1_block: u64, frame: Frame) -> Self { + let mut channel = Self { + id: frame.channel_id, + open_l1_block, + close_frame_number: None, + frames: BTreeMap::new(), + size: 0, + }; + + // cannot fail for an empty channel + channel.add_frame(frame).unwrap(); + + channel + } + + /// Returns true if the channel is closed, i.e. the closing frame has been received. + fn is_closed(&self) -> bool { + self.close_frame_number.is_some() + } + + /// Returns true if the channel is ready to be read. + fn is_ready(&self) -> bool { + // From the spec: + // "A channel is ready if: + // - The channel is closed + // - The channel has a contiguous sequence of frames until the closing frame" + matches!(self.close_frame_number, Some(n) if n as usize == self.frames.len() - 1) + } + + fn add_frame(&mut self, frame: Frame) -> Result<()> { + ensure!( + frame.channel_id == self.id, + "frame channel_id does not match channel id" + ); + if frame.is_last && self.is_closed() { + bail!("channel is already closed"); + } + ensure!( + !self.frames.contains_key(&frame.number), + "duplicate frame number" + ); + if let Some(close_frame_number) = self.close_frame_number { + ensure!( + frame.number < close_frame_number, + "frame number >= close_frame_number" + ); + } + + // From the spec: + // "If a frame is closing any existing higher-numbered frames are removed from the + // channel." + if frame.is_last { + // mark channel as closed + self.close_frame_number = Some(frame.number); + // prune frames with a number higher than the closing frame and update size + self.frames + .split_off(&frame.number) + .values() + .for_each(|pruned| self.size -= Self::FRAME_OVERHEAD + pruned.data.len()); + } + + self.size += Self::FRAME_OVERHEAD + frame.data.len(); + self.frames.insert(frame.number, frame); + + Ok(()) + } + + /// Reads all batches from an ready channel. If there is an invalid batch, the rest of + /// the channel is skipped, but previous batches are returned. + fn read_batches(&self, block_number: BlockNumber) -> Vec<BatchWithInclusion> { + debug_assert!(self.is_ready()); + + let mut batches = Vec::new(); + if let Err(_err) = self.decode_batches(block_number, &mut batches) { + #[cfg(not(target_os = "zkvm"))] + log::warn!("failed to decode all batches; skipping rest of channel: {_err:#}"); + } + + batches + } + + fn decode_batches( + &self, + block_number: BlockNumber, + batches: &mut Vec<BatchWithInclusion>, + ) -> Result<()> { + let decompressed = self + .decompress() + .context("failed to decompress channel data")?; + + let mut channel_data = decompressed.as_slice(); + while !channel_data.is_empty() { + let batch = Batch::decode(&mut channel_data) + .with_context(|| format!("failed to decode batch {}", batches.len()))?; + + batches.push(BatchWithInclusion { + essence: batch.0, + inclusion_block_number: block_number, + }); + } + + Ok(()) + } + + fn decompress(&self) -> Result<Vec<u8>> { + // chain all frames' data together + let data = MultiReader::new(self.frames.values().map(|frame| frame.data.as_slice())); + + // From the spec: + // "When decompressing a channel, we limit the amount of decompressed data to + // MAX_RLP_BYTES_PER_CHANNEL (currently 10,000,000 bytes), in order to avoid "zip-bomb" + // types of attack (where a small compressed input decompresses to a humongous amount + // of data). If the decompressed data exceeds the limit, things proceeds as though the + // channel contained only the first MAX_RLP_BYTES_PER_CHANNEL decompressed bytes." + let mut buf = Vec::new(); + Decoder::new(data)? + .take(MAX_RLP_BYTES_PER_CHANNEL) + .read_to_end(&mut buf)?; + + Ok(buf) + } +} + +/// A [Frame] is a chunk of data belonging to a [Channel]. Batcher transactions carry one +/// or multiple frames. The reason to split a channel into frames is that a channel might +/// too large to include in a single batcher transaction. +#[derive(Debug, Default, Clone)] +struct Frame { + /// The channel ID this frame belongs to. + pub channel_id: ChannelId, + /// The index of this frame within the channel. + pub number: u16, + /// A sequence of bytes belonging to the channel. + pub data: Vec<u8>, + /// Whether this is the last frame of the channel. + pub is_last: bool, +} + +impl Frame { + const HEADER_SIZE: usize = 22; + const MAX_FRAME_DATA_LENGTH: u32 = 1_000_000; + + /// Processes a batcher transaction and returns the list of contained frames. + pub fn process_batcher_transaction(tx_essence: &EthereumTxEssence) -> Result<Vec<Self>> { + let (version, mut rollup_payload) = tx_essence + .data() + .split_first() + .context("empty transaction data")?; + ensure!(version == &0, "invalid transaction version: {version}"); + + let mut frames = Vec::new(); + while !rollup_payload.is_empty() { + let frame = Frame::decode(&mut rollup_payload) + .with_context(|| format!("failed to decode frame {}", frames.len()))?; + frames.push(frame); + } + + Ok(frames) + } + + /// Decodes a [Frame] from the given buffer, advancing the buffer's position. + fn decode(buf: &mut &[u8]) -> Result<Self> { + ensure!(buf.remaining() > Self::HEADER_SIZE, "input too short"); + + let channel_id = buf.get_u128(); + let frame_number = buf.get_u16(); + // From the spec: + // "frame_data_length is the length of frame_data in bytes. It is capped to 1,000,000." + let frame_data_length = buf.get_u32(); + ensure!( + frame_data_length <= Self::MAX_FRAME_DATA_LENGTH, + "frame_data_length too large" + ); + + let frame_data = buf + .get(..frame_data_length as usize) + .context("input too short")?; + buf.advance(frame_data_length as usize); + + // From the spec: + // "is_last is a single byte with a value of 1 if the frame is the last in the channel, + // 0 if there are frames in the channel. Any other value makes the frame invalid." + ensure!(buf.has_remaining(), "input too short"); + let is_last = match buf.get_u8() { + 0 => false, + 1 => true, + _ => bail!("invalid is_last value"), + }; + + Ok(Self { + channel_id, + number: frame_number, + data: frame_data.to_vec(), + is_last, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // test vectors from https://github.com/ethereum-optimism/optimism/blob/711f33b4366f6cd268a265e7ed8ccb37085d86a2/op-node/rollup/derive/channel_test.go + mod channel { + use super::*; + + const CHANNEL_ID: ChannelId = 0xff; + + fn new_channel() -> Channel { + Channel { + id: CHANNEL_ID, + ..Default::default() + } + } + + #[test] + fn frame_validity() { + // wrong channel + { + let frame = Frame { + channel_id: 0xee, + ..Default::default() + }; + + let mut channel = new_channel(); + channel.add_frame(frame).unwrap_err(); + assert_eq!(channel.size, 0); + } + + // double close + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"four".to_vec(), + is_last: true, + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 1, + is_last: true, + ..Default::default() + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 204); + channel.add_frame(frame_b).unwrap_err(); + assert_eq!(channel.size, 204); + } + + // duplicate frame + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"four".to_vec(), + ..Default::default() + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"seven__".to_vec(), + ..Default::default() + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 204); + channel.add_frame(frame_b).unwrap_err(); + assert_eq!(channel.size, 204); + } + + // duplicate closing frame + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"four".to_vec(), + is_last: true, + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"seven__".to_vec(), + is_last: true, + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 204); + channel.add_frame(frame_b).unwrap_err(); + assert_eq!(channel.size, 204); + } + + // frame past closing + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"four".to_vec(), + is_last: true, + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 10, + data: b"seven__".to_vec(), + ..Default::default() + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 204); + channel.add_frame(frame_b).unwrap_err(); + assert_eq!(channel.size, 204); + } + + // prune after close frame + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 10, + data: b"seven__".to_vec(), + is_last: false, + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 2, + data: b"four".to_vec(), + is_last: true, + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 207); + channel.add_frame(frame_b).unwrap(); + assert_eq!(channel.size, 204); + } + + // multiple valid frames + { + let frame_a = Frame { + channel_id: CHANNEL_ID, + number: 1, + data: vec![202, 73, 81, 4, 0, 28, 73, 4, 62], + is_last: true, + }; + let frame_b = Frame { + channel_id: CHANNEL_ID, + number: 0, + data: vec![120, 156, 243, 72, 205, 201, 201, 87, 8, 207, 47], + ..Default::default() + }; + + let mut channel = new_channel(); + channel.add_frame(frame_a).unwrap(); + assert_eq!(channel.size, 209); + assert_eq!(channel.is_ready(), false); + channel.add_frame(frame_b).unwrap(); + assert_eq!(channel.size, 420); + assert_eq!(channel.is_ready(), true); + assert_eq!(channel.decompress().unwrap(), b"Hello World!"); + } + } + } +} diff --git a/lib/src/optimism/batcher_db.rs b/lib/src/optimism/batcher_db.rs new file mode 100644 index 000000000..d88b22977 --- /dev/null +++ b/lib/src/optimism/batcher_db.rs @@ -0,0 +1,179 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +use anyhow::{ensure, Context, Result}; +use serde::{Deserialize, Serialize}; +use zeth_primitives::{ + block::Header, + receipt::Receipt, + transactions::{ + ethereum::EthereumTxEssence, optimism::OptimismTxEssence, Transaction, TxEssence, + }, + trie::MptNode, + RlpBytes, +}; + +use crate::optimism::{config::OPTIMISM_CHAIN_SPEC, deposits, system_config}; + +/// Input for extracting deposits. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct BlockInput<E: TxEssence> { + /// Header of the block. + pub block_header: Header, + /// Transactions of the block. + pub transactions: Vec<Transaction<E>>, + /// Transaction receipts of the block or `None` if not required. + pub receipts: Option<Vec<Receipt>>, +} + +pub trait BatcherDb { + fn validate(&self) -> Result<()>; + fn get_full_op_block(&mut self, block_no: u64) -> Result<BlockInput<OptimismTxEssence>>; + fn get_op_block_header(&mut self, block_no: u64) -> Result<Header>; + fn get_full_eth_block(&mut self, block_no: u64) -> Result<&BlockInput<EthereumTxEssence>>; +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct MemDb { + pub full_op_block: HashMap<u64, BlockInput<OptimismTxEssence>>, + pub op_block_header: HashMap<u64, Header>, + pub full_eth_block: HashMap<u64, BlockInput<EthereumTxEssence>>, + pub eth_block_header: HashMap<u64, Header>, +} + +impl MemDb { + pub fn new() -> Self { + MemDb { + full_op_block: HashMap::new(), + op_block_header: HashMap::new(), + full_eth_block: HashMap::new(), + eth_block_header: HashMap::new(), + } + } +} + +impl Default for MemDb { + fn default() -> Self { + Self::new() + } +} + +impl BatcherDb for MemDb { + fn validate(&self) -> Result<()> { + for (block_no, op_block) in &self.full_op_block { + ensure!( + *block_no == op_block.block_header.number, + "Block number mismatch" + ); + + // Validate tx list + { + let mut tx_trie = MptNode::default(); + for (tx_no, tx) in op_block.transactions.iter().enumerate() { + let trie_key = tx_no.to_rlp(); + tx_trie.insert_rlp(&trie_key, tx)?; + } + ensure!( + tx_trie.hash() == op_block.block_header.transactions_root, + "Invalid op block transaction data!" + ); + } + + // Validate receipts + ensure!( + op_block.receipts.is_none(), + "Op blocks should not contain receipts" + ); + } + + for (block_no, op_block) in &self.op_block_header { + ensure!(*block_no == op_block.number, "Block number mismatch"); + } + + for (block_no, eth_block) in &self.full_eth_block { + ensure!( + *block_no == eth_block.block_header.number, + "Block number mismatch" + ); + + // Validate tx list + { + let mut tx_trie = MptNode::default(); + for (tx_no, tx) in eth_block.transactions.iter().enumerate() { + let trie_key = tx_no.to_rlp(); + tx_trie.insert_rlp(&trie_key, tx)?; + } + ensure!( + tx_trie.hash() == eth_block.block_header.transactions_root, + "Invalid eth block transaction data!" + ); + } + + // Validate receipts + if eth_block.receipts.is_some() { + let mut receipt_trie = MptNode::default(); + for (tx_no, receipt) in eth_block.receipts.as_ref().unwrap().iter().enumerate() { + let trie_key = tx_no.to_rlp(); + receipt_trie.insert_rlp(&trie_key, receipt)?; + } + ensure!( + receipt_trie.hash() == eth_block.block_header.receipts_root, + "Invalid eth block receipt data!" + ); + } else { + let can_contain_deposits = deposits::can_contain( + &OPTIMISM_CHAIN_SPEC.deposit_contract, + ð_block.block_header.logs_bloom, + ); + let can_contain_config = system_config::can_contain( + &OPTIMISM_CHAIN_SPEC.system_config_contract, + ð_block.block_header.logs_bloom, + ); + ensure!( + !can_contain_deposits, + "Eth block has no receipts, but bloom filter indicates it has deposits" + ); + ensure!( + !can_contain_config, + "Eth block has no receipts, but bloom filter indicates it has config updates" + ); + } + } + + Ok(()) + } + + fn get_full_op_block(&mut self, block_no: u64) -> Result<BlockInput<OptimismTxEssence>> { + let op_block = self.full_op_block.remove(&block_no).unwrap(); + + Ok(op_block) + } + + fn get_op_block_header(&mut self, block_no: u64) -> Result<Header> { + let op_block = self + .op_block_header + .remove(&block_no) + .context("not or no longer in db")?; + + Ok(op_block) + } + + fn get_full_eth_block(&mut self, block_no: u64) -> Result<&BlockInput<EthereumTxEssence>> { + let eth_block = self.full_eth_block.get(&block_no).unwrap(); + + Ok(eth_block) + } +} diff --git a/lib/src/optimism/config.rs b/lib/src/optimism/config.rs new file mode 100644 index 000000000..e1f8f57b0 --- /dev/null +++ b/lib/src/optimism/config.rs @@ -0,0 +1,75 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ruint::uint; +use serde::{Deserialize, Serialize}; +use zeth_primitives::{address, Address}; + +use super::system_config::SystemConfig; + +/// A Chain Configuration +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChainConfig { + /// The initial system config value + pub system_config: SystemConfig, + /// The L1 attributes depositor address + pub l1_attributes_depositor: Address, + /// The L1 attributes contract + pub l1_attributes_contract: Address, + /// The L2 address accumulating any transaction priority fee + pub sequencer_fee_vault: Address, + /// The batch inbox address + pub batch_inbox: Address, + /// The deposit contract address + pub deposit_contract: Address, + /// The L1 system config contract + pub system_config_contract: Address, + /// The maximum byte size of all pending channels + pub max_channel_bank_size: u64, + /// The max timeout for a channel (as measured by the frame L1 block number) + pub channel_timeout: u64, + /// Number of L1 blocks in a sequence window + pub seq_window_size: u64, + /// Maximum timestamp drift + pub max_seq_drift: u64, + /// Network blocktime + pub blocktime: u64, +} + +impl ChainConfig { + pub const fn optimism() -> Self { + Self { + system_config: SystemConfig { + batch_sender: address!("6887246668a3b87f54deb3b94ba47a6f63f32985"), + gas_limit: uint!(30_000_000_U256), + l1_fee_overhead: uint!(188_U256), + l1_fee_scalar: uint!(684000_U256), + unsafe_block_signer: address!("AAAA45d9549EDA09E70937013520214382Ffc4A2"), + }, + l1_attributes_depositor: address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001"), + l1_attributes_contract: address!("4200000000000000000000000000000000000015"), + sequencer_fee_vault: address!("4200000000000000000000000000000000000011"), + batch_inbox: address!("ff00000000000000000000000000000000000010"), + deposit_contract: address!("bEb5Fc579115071764c7423A4f12eDde41f106Ed"), + system_config_contract: address!("229047fed2591dbec1eF1118d64F7aF3dB9EB290"), + max_channel_bank_size: 100_000_000, + channel_timeout: 300, + seq_window_size: 3600, + max_seq_drift: 600, + blocktime: 2, + } + } +} + +pub const OPTIMISM_CHAIN_SPEC: ChainConfig = ChainConfig::optimism(); diff --git a/lib/src/optimism/deposits.rs b/lib/src/optimism/deposits.rs new file mode 100644 index 000000000..ecefb1734 --- /dev/null +++ b/lib/src/optimism/deposits.rs @@ -0,0 +1,154 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use alloy_sol_types::{sol_data, SolType}; +use anyhow::{anyhow, ensure, Context}; +use zeth_primitives::{ + fixed_bytes, keccak256, + receipt::Log, + transactions::{ + ethereum::{EthereumTxEssence, TransactionKind}, + optimism::{OptimismTxEssence, TxEssenceOptimismDeposited}, + Transaction, + }, + Address, Bloom, BloomInput, B256, U160, U256, +}; + +use super::{batcher_db::BlockInput, config::ChainConfig}; + +/// Signature of the deposit transaction event, i.e. +/// keccak-256 hash of "TransactionDeposited(address,address,uint256,bytes)" +const TRANSACTION_DEPOSITED_SIGNATURE: B256 = + fixed_bytes!("b3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32"); +/// Version of the deposit transaction event. +const TRANSACTION_DEPOSITED_VERSION: B256 = B256::ZERO; + +/// Extracts deposits from the given block. +pub fn extract_transactions( + config: &ChainConfig, + input: &BlockInput<EthereumTxEssence>, +) -> anyhow::Result<Vec<Transaction<OptimismTxEssence>>> { + let block_hash = input.block_header.hash(); + + // if the bloom filter does not contain the corresponding topics, we have the guarantee + // that there are no deposits in the block + if !can_contain(&config.deposit_contract, &input.block_header.logs_bloom) { + return Ok(vec![]); + } + + let receipts = input.receipts.as_ref().context("receipts missing")?; + + let mut deposits = Vec::new(); + + let mut log_index = 0_usize; + for receipt in receipts { + let receipt = &receipt.payload; + + // skip failed transactions + if !receipt.success { + log_index += receipt.logs.len(); + continue; + } + // we could skip the transaction if the Bloom filter does not contain the deposit log, but + // since hashing is quite expensive on the guest, it seems faster to always check the + // logs + + // parse all the logs for deposit transactions + for log in &receipt.logs { + if log.address == config.deposit_contract + && log.topics[0] == TRANSACTION_DEPOSITED_SIGNATURE + { + deposits.push( + to_deposit_transaction(block_hash, log_index, log) + .context("invalid deposit")?, + ); + } + + log_index += 1; + } + } + + Ok(deposits) +} + +/// Returns whether the given Bloom filter can contain a deposit log. +pub fn can_contain(address: &Address, bloom: &Bloom) -> bool { + let input = BloomInput::Raw(address.as_slice()); + if !bloom.contains_input(input) { + return false; + } + let input = BloomInput::Raw(TRANSACTION_DEPOSITED_SIGNATURE.as_slice()); + if !bloom.contains_input(input) { + return false; + } + true +} + +/// Converts a deposit log into a transaction. +fn to_deposit_transaction( + block_hash: B256, + log_index: usize, + log: &Log, +) -> anyhow::Result<Transaction<OptimismTxEssence>> { + let from = U160::try_from_be_slice(&log.topics[1][12..]) + .context("invalid from")? + .into(); + let to = U160::try_from_be_slice(&log.topics[2][12..]) + .context("invalid to")? + .into(); + + // TODO: it is not 100% defined, what happens if the version is not 0 + // it is assumed that this is an error and must not be ignored + ensure!( + log.topics[3] == TRANSACTION_DEPOSITED_VERSION, + "invalid version" + ); + + // the log data is just an ABI encoded `bytes` type representing the opaque_data + let opaque_data: Vec<u8> = sol_data::Bytes::abi_decode(&log.data, true) + .map_err(|e| anyhow!(e)) + .context("invalid data")?; + + ensure!(opaque_data.len() >= 73, "invalid opaque_data"); + let mint = U256::try_from_be_slice(&opaque_data[0..32]).context("invalid mint")?; + let value = U256::try_from_be_slice(&opaque_data[32..64]).context("invalid value")?; + let gas_limit = U256::try_from_be_slice(&opaque_data[64..72]).context("invalid gas_limit")?; + let is_creation = opaque_data[72] != 0; + let data = opaque_data[73..].to_vec(); + + // compute the source hash + let h = keccak256([block_hash.0, U256::from(log_index).to_be_bytes()].concat()); + let source_hash = keccak256([U256::from(0).to_be_bytes(), h.0].concat()); + + // construct the transaction + let essence = OptimismTxEssence::OptimismDeposited(TxEssenceOptimismDeposited { + source_hash, + from, + to: if is_creation { + TransactionKind::Create + } else { + TransactionKind::Call(to) + }, + mint, + value, + gas_limit, + is_system_tx: false, + data: data.into(), + }); + + Ok(Transaction { + essence, + signature: Default::default(), + }) +} diff --git a/lib/src/optimism/mod.rs b/lib/src/optimism/mod.rs new file mode 100644 index 000000000..370b110a8 --- /dev/null +++ b/lib/src/optimism/mod.rs @@ -0,0 +1,432 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::iter::once; + +use alloy_sol_types::{sol, SolInterface}; +use anyhow::{anyhow, bail, ensure, Context, Result}; +#[cfg(not(target_os = "zkvm"))] +use log::info; +use serde::{Deserialize, Serialize}; +use zeth_primitives::{ + batch::Batch, + keccak::keccak, + transactions::{ + ethereum::TransactionKind, + optimism::{OptimismTxEssence, TxEssenceOptimismDeposited}, + Transaction, TxEssence, + }, + trie::MptNode, + uint, Address, BlockHash, BlockNumber, FixedBytes, RlpBytes, B256, U256, +}; + +use crate::{ + consts::ONE, + optimism::{ + batcher::{Batcher, BlockId, L2BlockInfo}, + batcher_db::BatcherDb, + config::ChainConfig, + }, +}; + +pub mod batcher; +pub mod batcher_channel; +pub mod batcher_db; +pub mod config; +pub mod deposits; +pub mod system_config; + +sol! { + /// The values stored by the L1 Attributes Predeployed Contract. + #[derive(Debug)] + interface OpSystemInfo { + function setL1BlockValues( + /// L1 block attributes. + uint64 number, + uint64 timestamp, + uint256 basefee, + bytes32 hash, + /// Sequence number in the current epoch. + uint64 sequence_number, + /// A versioned hash of the current authorized batcher sender. + bytes32 batcher_hash, + /// The L1 fee overhead to apply to L1 cost computation of transactions. + uint256 l1_fee_overhead, + /// The L1 fee scalar to apply to L1 cost computation of transactions. + uint256 l1_fee_scalar + ); + } +} + +/// Represents the input for the derivation process. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct DeriveInput<D> { + /// Database containing the blocks. + pub db: D, + /// Block number of the L2 head. + pub op_head_block_no: u64, + /// Block count for the operation. + pub op_derive_block_count: u64, +} + +/// Represents the output of the derivation process. +#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Serialize)] +pub struct DeriveOutput { + /// Ethereum tail block. + pub eth_tail: (BlockNumber, BlockHash), + /// Optimism head block. + pub op_head: (BlockNumber, BlockHash), + /// Derived Optimism blocks. + pub derived_op_blocks: Vec<(BlockNumber, BlockHash)>, +} + +/// Implementation of the actual derivation process. +pub struct DeriveMachine<D> { + /// Input for the derivation process. + pub derive_input: DeriveInput<D>, + + op_head_block_hash: BlockHash, + op_block_no: u64, + op_block_seq_no: u64, + op_batcher: Batcher, +} + +impl<D: BatcherDb> DeriveMachine<D> { + /// Creates a new instance of DeriveMachine. + pub fn new(chain_config: &ChainConfig, mut derive_input: DeriveInput<D>) -> Result<Self> { + derive_input.db.validate()?; + + let op_block_no = derive_input.op_head_block_no; + + // read system config from op_head (seq_no/epoch_no..etc) + let op_head = derive_input.db.get_full_op_block(op_block_no)?; + let op_head_block_hash = op_head.block_header.hash(); + + #[cfg(not(target_os = "zkvm"))] + info!( + "Fetched Op head (block no {}) {}", + op_block_no, op_head_block_hash + ); + + // the first transaction in a block MUST be a L1 attributes deposited transaction + let l1_attributes_tx = &op_head + .transactions + .first() + .context("block is empty")? + .essence; + if let Err(err) = validate_l1_attributes_deposited_tx(chain_config, l1_attributes_tx) { + bail!( + "First transaction in block is not a valid L1 attributes deposited transaction: {err}" + ); + } + // decode the L1 attributes deposited transaction + let set_l1_block_values = { + let call = OpSystemInfo::OpSystemInfoCalls::abi_decode(l1_attributes_tx.data(), true) + .map_err(|e| anyhow!(e)) + .context("invalid L1 attributes data")?; + match call { + OpSystemInfo::OpSystemInfoCalls::setL1BlockValues(x) => x, + } + }; + + let op_block_seq_no = set_l1_block_values.sequence_number; + + // check that the correct L1 block is in the database + let eth_block_no = set_l1_block_values.number; + let eth_head = derive_input.db.get_full_eth_block(eth_block_no)?; + ensure!( + eth_head.block_header.hash() == set_l1_block_values.hash, + "Ethereum head block hash mismatch" + ); + #[cfg(not(target_os = "zkvm"))] + info!( + "Fetched Eth head (block no {eth_block_no}) {}", + set_l1_block_values.hash + ); + + let op_batcher = { + // copy the chain config and update the system config + let mut op_chain_config = chain_config.clone(); + op_chain_config.system_config.batch_sender = + Address::from_slice(&set_l1_block_values.batcher_hash.as_slice()[12..]); + op_chain_config.system_config.l1_fee_overhead = set_l1_block_values.l1_fee_overhead; + op_chain_config.system_config.l1_fee_scalar = set_l1_block_values.l1_fee_scalar; + + Batcher::new( + op_chain_config, + L2BlockInfo { + hash: op_head_block_hash, + timestamp: op_head.block_header.timestamp.try_into().unwrap(), + l1_origin: BlockId { + number: set_l1_block_values.number, + hash: set_l1_block_values.hash, + }, + }, + eth_head, + )? + }; + + Ok(DeriveMachine { + derive_input, + op_head_block_hash, + op_block_no, + op_block_seq_no, + op_batcher, + }) + } + + pub fn derive(&mut self) -> Result<DeriveOutput> { + let target_block_no = + self.derive_input.op_head_block_no + self.derive_input.op_derive_block_count; + + let mut derived_op_blocks = Vec::new(); + let mut process_next_eth_block = false; + + while self.op_block_no < target_block_no { + #[cfg(not(target_os = "zkvm"))] + info!( + "op_block_no = {}, eth_block_no = {}", + self.op_block_no, self.op_batcher.state.current_l1_block_number + ); + + // Process next Eth block. We do this on every iteration, except the first iteration. + // (The first iteration is handled by Batcher::new().) + if process_next_eth_block { + let eth_block = self + .derive_input + .db + .get_full_eth_block(self.op_batcher.state.current_l1_block_number + 1) + .context("block not found")?; + + self.op_batcher + .process_l1_block(eth_block) + .context("failed to create batcher transactions")?; + } + process_next_eth_block = true; + + // Process batches + while let Some(op_batch) = self.op_batcher.read_batch()? { + // Process the batch + self.op_block_no += 1; + + #[cfg(not(target_os = "zkvm"))] + info!( + "Read batch for Op block {}: timestamp={}, epoch={}, tx count={}, parent hash={:?}", + self.op_block_no, + op_batch.0.timestamp, + op_batch.0.epoch_num, + op_batch.0.transactions.len(), + op_batch.0.parent_hash, + ); + + // Update sequence number (and fetch deposits if start of new epoch) + let l2_safe_head = &self.op_batcher.state.safe_head; + let deposits = if l2_safe_head.l1_origin.number != op_batch.0.epoch_num { + self.op_block_seq_no = 0; + self.op_batcher.state.do_next_epoch()?; + + self.op_batcher + .state + .epoch + .deposits + .iter() + .map(|tx| tx.to_rlp()) + .collect() + } else { + self.op_block_seq_no += 1; + + Vec::new() + }; + + // obtain verified op block header + let new_op_head = { + // load the next op block header + let new_op_head = self + .derive_input + .db + .get_op_block_header(self.op_block_no) + .context("op block not found")?; + + // Verify that the op block header loaded from the DB matches the payload + // attributes of the batch. + ensure!( + new_op_head.parent_hash == self.op_batcher.state.safe_head.hash, + "Invalid op block parent hash" + ); + ensure!( + new_op_head.beneficiary == self.op_batcher.config.sequencer_fee_vault, + "Invalid op block beneficiary" + ); + ensure!( + new_op_head.gas_limit == self.op_batcher.config.system_config.gas_limit, + "Invalid op block gas limit" + ); + ensure!( + new_op_head.timestamp == U256::from(op_batch.0.timestamp), + "Invalid op block timestamp" + ); + ensure!( + new_op_head.extra_data.is_empty(), + "Invalid op block extra data" + ); + + // verify that the new op head mix hash matches the mix hash of the L1 block + { + let l1_epoch_header = self + .derive_input + .db + .get_full_eth_block(op_batch.0.epoch_num) + .context("eth block not found")? + .block_header + .clone(); + ensure!( + new_op_head.mix_hash == l1_epoch_header.mix_hash, + "Invalid op block mix hash" + ); + } + + // verify that the new op head transactions match the batch transactions + { + // From the spec: + // The first transaction MUST be a L1 attributes deposited transaction, + // followed by an array of zero-or-more user-deposited transactions. + let l1_attributes_tx = self.derive_l1_attributes_deposited_tx(&op_batch); + let derived_transactions = once(l1_attributes_tx.to_rlp()) + .chain(deposits) + .chain(op_batch.0.transactions.iter().map(|tx| tx.to_vec())) + .enumerate(); + + let mut tx_trie = MptNode::default(); + for (tx_no, tx) in derived_transactions { + let trie_key = tx_no.to_rlp(); + tx_trie.insert(&trie_key, tx)?; + } + + ensure!( + tx_trie.hash() == new_op_head.transactions_root, + "Invalid op block transactions" + ); + } + + ensure!( + new_op_head.withdrawals_root.is_none(), + "Invalid op block withdrawals" + ); + + new_op_head + }; + + let new_op_head_hash = new_op_head.hash(); + + #[cfg(not(target_os = "zkvm"))] + info!( + "Derived Op block {} w/ hash {new_op_head_hash}", + new_op_head.number + ); + + self.op_batcher.state.safe_head = L2BlockInfo { + hash: new_op_head_hash, + timestamp: new_op_head.timestamp.try_into().unwrap(), + l1_origin: BlockId { + number: self.op_batcher.state.epoch.number, + hash: self.op_batcher.state.epoch.hash, + }, + }; + + derived_op_blocks.push((new_op_head.number, new_op_head_hash)); + + if self.op_block_no == target_block_no { + break; + } + } + } + + Ok(DeriveOutput { + eth_tail: ( + self.op_batcher.state.current_l1_block_number, + self.op_batcher.state.current_l1_block_hash, + ), + op_head: (self.derive_input.op_head_block_no, self.op_head_block_hash), + derived_op_blocks, + }) + } + + fn derive_l1_attributes_deposited_tx( + &mut self, + op_batch: &Batch, + ) -> Transaction<OptimismTxEssence> { + let batcher_hash = { + let all_zero: FixedBytes<12> = FixedBytes::ZERO; + all_zero.concat_const::<20, 32>(self.op_batcher.config.system_config.batch_sender.0) + }; + + let set_l1_block_values = + OpSystemInfo::OpSystemInfoCalls::setL1BlockValues(OpSystemInfo::setL1BlockValuesCall { + number: self.op_batcher.state.epoch.number, + timestamp: self.op_batcher.state.epoch.timestamp, + basefee: self.op_batcher.state.epoch.base_fee_per_gas, + hash: self.op_batcher.state.epoch.hash, + sequence_number: self.op_block_seq_no, + batcher_hash, + l1_fee_overhead: self.op_batcher.config.system_config.l1_fee_overhead, + l1_fee_scalar: self.op_batcher.config.system_config.l1_fee_scalar, + }); + + let source_hash: B256 = { + let l1_block_hash = op_batch.0.epoch_hash.0; + let seq_number = U256::from(self.op_block_seq_no).to_be_bytes::<32>(); + let source_hash_sequencing = keccak([l1_block_hash, seq_number].concat()); + keccak([ONE.to_be_bytes::<32>(), source_hash_sequencing].concat()).into() + }; + let config = &self.op_batcher.config; + + Transaction { + essence: OptimismTxEssence::OptimismDeposited(TxEssenceOptimismDeposited { + source_hash, + from: config.l1_attributes_depositor, + to: TransactionKind::Call(config.l1_attributes_contract), + mint: Default::default(), + value: Default::default(), + gas_limit: uint!(1_000_000_U256), + is_system_tx: false, + data: set_l1_block_values.abi_encode().into(), + }), + signature: Default::default(), + } + } +} + +fn validate_l1_attributes_deposited_tx(config: &ChainConfig, tx: &OptimismTxEssence) -> Result<()> { + match tx { + OptimismTxEssence::Ethereum(_) => { + bail!("No Optimism deposit transaction"); + } + OptimismTxEssence::OptimismDeposited(op) => { + ensure!( + op.from == config.l1_attributes_depositor, + "Invalid from address" + ); + ensure!( + matches!(op.to, TransactionKind::Call(addr) if addr == config.l1_attributes_contract), + "Invalid to address" + ); + ensure!(op.mint == U256::ZERO, "Invalid mint value"); + ensure!(op.value == U256::ZERO, "Invalid value"); + ensure!(op.gas_limit == uint!(1_000_000_U256), "Invalid gas limit"); + ensure!(!op.is_system_tx, "Invalid is_system_tx value"); + } + } + + Ok(()) +} diff --git a/lib/src/optimism/system_config.rs b/lib/src/optimism/system_config.rs new file mode 100644 index 000000000..93ece36b1 --- /dev/null +++ b/lib/src/optimism/system_config.rs @@ -0,0 +1,146 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use anyhow::{self, bail, ensure, Context, Ok}; +use serde::{Deserialize, Serialize}; +use zeth_primitives::{ + b256, transactions::ethereum::EthereumTxEssence, Address, Bloom, BloomInput, B256, U256, +}; + +use super::batcher_db::BlockInput; + +/// Signature of the deposit transaction event, i.e. +/// keccak-256 hash of "ConfigUpdate(uint256,uint8,bytes)" +const CONFIG_UPDATE_SIGNATURE: B256 = + b256!("1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be"); +/// Version of the deposit transaction event. +const CONFIG_UPDATE_VERSION: B256 = B256::ZERO; + +/// Optimism system config contract values +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemConfig { + /// Batch sender address + pub batch_sender: Address, + /// L2 gas limit + pub gas_limit: U256, + /// Fee overhead + pub l1_fee_overhead: U256, + /// Fee scalar + pub l1_fee_scalar: U256, + /// Sequencer's signer for unsafe blocks + pub unsafe_block_signer: Address, +} + +impl SystemConfig { + /// Updates the system config based on the given input. Returns whether the config was + /// updated. + pub fn update( + &mut self, + system_config_contract: &Address, + input: &BlockInput<EthereumTxEssence>, + ) -> anyhow::Result<bool> { + let mut updated = false; + + // if the bloom filter does not contain the corresponding topics, we have the guarantee + // that there are no config updates in the block + if !can_contain(system_config_contract, &input.block_header.logs_bloom) { + return Ok(updated); + } + + #[cfg(not(target_os = "zkvm"))] + log::info!("Process config"); + + let receipts = input.receipts.as_ref().context("receipts missing")?; + for receipt in receipts { + let receipt = &receipt.payload; + + // skip failed transactions + if !receipt.success { + continue; + } + + for log in &receipt.logs { + // the log event contract address must match the system config contract + // the first log event topic must match the ConfigUpdate signature + if &log.address == system_config_contract + && log.topics[0] == CONFIG_UPDATE_SIGNATURE + { + updated = true; + + // the second topic determines the version + ensure!(log.topics[1] == CONFIG_UPDATE_VERSION, "invalid version"); + + // the third topic determines the type of update + let update_type: u64 = U256::from_be_bytes(log.topics[2].0) + .try_into() + .expect("invalid update type"); + + // TODO: use proper ABI decoding of the data + match update_type { + // type 0: batcherHash overwrite, as bytes32 payload + 0 => { + let addr_bytes = log + .data + .get(76..96) + .context("invalid batch sender address")?; + + self.batch_sender = Address::from_slice(addr_bytes); + } + // type 1: overhead and scalar overwrite, as two packed uint256 entries + 1 => { + let fee_overhead = log.data.get(64..96).context("invalid data")?; + let fee_scalar = log.data.get(96..128).context("invalid data")?; + + self.l1_fee_overhead = U256::try_from_be_slice(fee_overhead) + .context("invalid overhead")?; + self.l1_fee_scalar = + U256::try_from_be_slice(fee_scalar).context("invalid scalar")?; + } + // type 2: gasLimit overwrite, as uint64 payload + 2 => { + let gas_limit = log.data.get(64..96).context("invalid data")?; + + self.gas_limit = + U256::try_from_be_slice(gas_limit).context("invalid gas limit")?; + } + // type 3: unsafeBlockSigner overwrite, as address payload + 3 => { + let addr_bytes = log + .data + .get(76..96) + .context("invalid unsafe block signer address")?; + + self.unsafe_block_signer = Address::from_slice(addr_bytes); + } + _ => { + bail!("invalid update type"); + } + } + } + } + } + + Ok(updated) + } +} + +/// Returns whether the given Bloom filter can contain a config update log. +pub fn can_contain(address: &Address, bloom: &Bloom) -> bool { + let input = BloomInput::Raw(address.as_slice()); + if !bloom.contains_input(input) { + return false; + } + let input = BloomInput::Raw(CONFIG_UPDATE_SIGNATURE.as_slice()); + !bloom.contains_input(input) +} diff --git a/lib/src/taiko/block_builder.rs b/lib/src/taiko/block_builder.rs deleted file mode 100644 index 43fb16430..000000000 --- a/lib/src/taiko/block_builder.rs +++ /dev/null @@ -1,22 +0,0 @@ -use zeth_primitives::transactions::ethereum::EthereumTxEssence; - -use crate::{ - block_builder::{ConfiguredBlockBuilder, NetworkStrategyBundle}, - finalization::BuildFromMemDbStrategy, - initialization::MemDbInitStrategy, - mem_db::MemDb, - taiko::{execute::TaikoTxExecStrategy, prepare::TaikoHeaderPrepStrategy}, -}; - -pub struct TaikoStrategyBundle {} - -impl NetworkStrategyBundle for TaikoStrategyBundle { - type Database = MemDb; - type TxEssence = EthereumTxEssence; - type DbInitStrategy = MemDbInitStrategy; - type HeaderPrepStrategy = TaikoHeaderPrepStrategy; - type TxExecStrategy = TaikoTxExecStrategy; - type BlockBuildStrategy = BuildFromMemDbStrategy; -} - -pub type TaikoBlockBuilder<'a> = ConfiguredBlockBuilder<'a, TaikoStrategyBundle>; diff --git a/primitives/src/taiko/consts.rs b/lib/src/taiko/consts.rs similarity index 59% rename from primitives/src/taiko/consts.rs rename to lib/src/taiko/consts.rs index 1807b8d74..cc43e3cc9 100644 --- a/primitives/src/taiko/consts.rs +++ b/lib/src/taiko/consts.rs @@ -1,7 +1,9 @@ use core::str::FromStr; use alloy_primitives::{uint, Address, U256}; +use anyhow::{anyhow, bail, Result}; use once_cell::sync::Lazy; +use zeth_primitives::transactions::EthereumTransaction; pub static ANCHOR_GAS_LIMIT: u64 = 250_000; pub static MAX_TX_LIST: usize = 79; @@ -12,6 +14,28 @@ pub static GOLDEN_TOUCH_ACCOUNT: Lazy<Address> = Lazy::new(|| { .expect("invalid golden touch account") }); +macro_rules! taiko_contracts { + ($name:ident) => {{ + use crate::taiko::consts::$name::*; + Ok(( + *L1_CONTRACT, + *L2_CONTRACT, + *L1_SIGNAL_SERVICE, + *L2_SIGNAL_SERVICE, + )) + }}; +} + +pub fn get_contracts(name: &str) -> Result<(Address, Address, Address, Address)> { + match name { + "testnet" => taiko_contracts!(testnet), + "internal_devnet_a" => taiko_contracts!(internal_devnet_a), + "internal_devnet_b" => taiko_contracts!(internal_devnet_b), + #[allow(clippy::needless_return)] + _ => bail!("invalid chain name: {name}"), + } +} + pub mod testnet { use super::*; pub static CHAIN_ID: u64 = 167008; @@ -79,3 +103,39 @@ pub mod internal_devnet_b { .expect("invalid l2 signal service") }); } + +static GX1: Lazy<U256> = + Lazy::new(|| uint!(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798_U256)); +static N: Lazy<U256> = + Lazy::new(|| uint!(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_U256)); +static GX1_MUL_PRIVATEKEY: Lazy<U256> = + Lazy::new(|| uint!(0x4341adf5a780b4a87939938fd7a032f6e6664c7da553c121d3b4947429639122_U256)); +static GX2: Lazy<U256> = + Lazy::new(|| uint!(0xc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5_U256)); + +/// check the anchor signature with fixed K value +pub fn check_anchor_signature(anchor: &EthereumTransaction) -> Result<()> { + let sign = &anchor.signature; + if sign.r == *GX1 { + return Ok(()); + } + let msg_hash = anchor.essence.signing_hash(); + let msg_hash: U256 = msg_hash.into(); + if sign.r == *GX2 { + // when r == GX2 require s == 0 if k == 1 + // alias: when r == GX2 require N == msg_hash + *GX1_MUL_PRIVATEKEY + if *N != msg_hash + *GX1_MUL_PRIVATEKEY { + bail!( + "r == GX2, but N != msg_hash + *GX1_MUL_PRIVATEKEY, N: {}, msg_hash: {msg_hash}, *GX1_MUL_PRIVATEKEY: {}", + *N, *GX1_MUL_PRIVATEKEY + ); + } + return Ok(()); + } + Err(anyhow!( + "r != *GX1 && r != GX2, r: {}, *GX1: {}, GX2: {}", + sign.r, + *GX1, + *GX2 + )) +} diff --git a/lib/src/taiko/execute.rs b/lib/src/taiko/execute.rs deleted file mode 100644 index 34a09a6c7..000000000 --- a/lib/src/taiko/execute.rs +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2023 RISC Zero, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use alloc::vec; -use core::{fmt::Debug, mem::take, str::from_utf8}; - -use anyhow::{anyhow, bail, Context}; -#[cfg(all(not(target_os = "zkvm"), not(feature = "server")))] -use log::debug; -use revm::{ - primitives::{Account, Address, ResultAndState, SpecId, TransactTo, TxEnv, U256}, - Database, DatabaseCommit, EVM, -}; -#[cfg(all(not(target_os = "zkvm"), feature = "server"))] -use tracing::debug; -use zeth_primitives::{ - receipt::Receipt, - transactions::{ - ethereum::{EthereumTxEssence, TransactionKind}, - TxEssence, - }, - trie::MptNode, - Bloom, RlpBytes, -}; - -use crate::{ - block_builder::BlockBuilder, - consts, - consts::{ChainSpec, GWEI_TO_WEI, MIN_SPEC_ID}, - execution::TxExecStrategy, - guest_mem_forget, -}; - -pub struct TaikoTxExecStrategy {} - -impl TxExecStrategy<EthereumTxEssence> for TaikoTxExecStrategy { - fn execute_transactions<D>( - mut block_builder: BlockBuilder<D, EthereumTxEssence>, - ) -> anyhow::Result<BlockBuilder<D, EthereumTxEssence>> - where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, - { - let header = block_builder - .header - .as_mut() - .expect("Header is not initialized"); - // Compute the spec id - let spec_id = block_builder.chain_spec.spec_id(header.number); - if !SpecId::enabled(spec_id, MIN_SPEC_ID) { - bail!( - "Invalid protocol version: expected >= {:?}, got {:?}", - MIN_SPEC_ID, - spec_id, - ) - } - - #[cfg(not(target_os = "zkvm"))] - { - use chrono::{TimeZone, Utc}; - use log::info; - let dt = Utc - .timestamp_opt(block_builder.input.timestamp.try_into().unwrap(), 0) - .unwrap(); - - info!("Block no. {}", header.number); - info!(" EVM spec ID: {:?}", spec_id); - info!(" Timestamp: {}", dt); - info!(" Transactions: {}", block_builder.input.transactions.len()); - info!(" Withdrawals: {}", block_builder.input.withdrawals.len()); - info!(" Fee Recipient: {:?}", block_builder.input.beneficiary); - info!(" Gas limit: {}", block_builder.input.gas_limit); - info!(" Base fee per gas: {}", header.base_fee_per_gas); - info!(" Extra data: {:?}", block_builder.input.extra_data); - } - - // initialize the EVM - let mut evm = EVM::new(); - - // set the EVM configuration - evm.env.cfg.chain_id = block_builder.chain_spec.chain_id(); - evm.env.cfg.spec_id = spec_id; - - // set the EVM block environment - evm.env.block.number = header.number.try_into().unwrap(); - evm.env.block.coinbase = block_builder.input.beneficiary; - evm.env.block.timestamp = header.timestamp; - evm.env.block.difficulty = U256::ZERO; - evm.env.block.prevrandao = Some(header.mix_hash); - evm.env.block.basefee = header.base_fee_per_gas; - evm.env.block.gas_limit = block_builder.input.gas_limit; - - evm.database(block_builder.db.take().unwrap()); - - // bloom filter over all transaction logs - let mut logs_bloom = Bloom::default(); - // keep track of the gas used over all transactions - let mut cumulative_gas_used = consts::ZERO; - - // process all the transactions - let mut tx_trie = MptNode::default(); - let mut txs = vec![]; - let mut receipt_trie = MptNode::default(); - let mut actual_tx_no = 0usize; - for (tx_no, tx) in take(&mut block_builder.input.transactions) - .into_iter() - .enumerate() - { - // anchor transaction must be executed successfully - let is_anchor = tx_no == 0; - // verify the transaction signature - let tx_from = match tx.recover_from() { - Ok(tx_from) => tx_from, - Err(err) => { - if is_anchor { - bail!("Error recovering anchor signature: {}", err); - } - #[cfg(not(target_os = "zkvm"))] - debug!( - "Error recovering address for transaction {}, error: {}", - tx_no, err - ); - continue; - } - }; - - #[cfg(not(target_os = "zkvm"))] - { - let tx_hash = tx.hash(); - debug!("Tx no. {} (hash: {})", tx_no, tx_hash); - debug!(" Type: {}", tx.essence.tx_type()); - debug!(" Fr: {:?}", tx_from); - debug!(" To: {:?}", tx.essence.to().unwrap_or_default()); - } - - // verify transaction gas - let block_available_gas = block_builder.input.gas_limit - cumulative_gas_used; - if block_available_gas < tx.essence.gas_limit() { - if is_anchor { - bail!("Error at transaction {}: gas exceeds block limit", tx_no); - } - #[cfg(not(target_os = "zkvm"))] - debug!("Error at transaction {}: gas exceeds block limit", tx_no); - continue; - } - - // process the transaction - fill_eth_tx_env( - block_builder.chain_spec, - &mut evm.env.tx, - &tx.essence, - tx_from, - is_anchor, - ); - let ResultAndState { result, state } = match evm.transact() { - Ok(result) => result, - Err(err) => { - if is_anchor { - bail!("Error at transaction {}: {:?}", tx_no, err); - } - #[cfg(not(target_os = "zkvm"))] - debug!("Error at transaction {}: {:?}", tx_no, err); - continue; - } - }; - - if is_anchor && !result.is_success() { - bail!( - "Error at transaction {}: execute anchor failed {:?}, output {:?}", - tx_no, - result, - result.output().map(|o| from_utf8(o).unwrap_or_default()) - ); - } - - let gas_used = result.gas_used().try_into().unwrap(); - cumulative_gas_used = cumulative_gas_used.checked_add(gas_used).unwrap(); - - #[cfg(not(target_os = "zkvm"))] - debug!(" Ok: {:?}", result); - - // create the receipt from the EVM result - let receipt = Receipt::new( - tx.essence.tx_type(), - result.is_success(), - cumulative_gas_used, - result.logs().into_iter().map(|log| log.into()).collect(), - ); - - // accumulate logs to the block bloom filter - logs_bloom.accrue_bloom(&receipt.payload.logs_bloom); - - // Add receipt and tx to tries - let trie_key = actual_tx_no.to_rlp(); - actual_tx_no += 1; - txs.push(tx.clone()); - match tx_trie.insert_rlp(&trie_key, tx) { - Ok(_) => (), - Err(err) => { - if is_anchor { - bail!("Failed to insert transaction {}: {:?}", actual_tx_no, err); - } - #[cfg(not(target_os = "zkvm"))] - debug!("Failed to insert transaction {}: {:?}", actual_tx_no, err); - } - } - match receipt_trie.insert_rlp(&trie_key, receipt) { - Ok(_) => (), - Err(err) => { - if is_anchor { - bail!("Failed to insert receipt {}: {:?}", actual_tx_no, err); - } - #[cfg(not(target_os = "zkvm"))] - debug!("Failed to insert receipt {}: {:?}", actual_tx_no, err); - } - } - - // update account states - #[cfg(not(target_os = "zkvm"))] - for (address, account) in &state { - if account.is_touched() { - // log account - debug!( - " State {:?} (is_selfdestructed={}, is_loaded_as_not_existing={}, is_created={}, is_empty={})", - address, - account.is_selfdestructed(), - account.is_loaded_as_not_existing(), - account.is_created(), - account.is_empty(), - ); - // log balance changes - debug!( - " After balance: {} (Nonce: {})", - account.info.balance, account.info.nonce - ); - - // log state changes - for (addr, slot) in &account.storage { - if slot.is_changed() { - debug!(" Storage address: {:?}", addr); - debug!(" Before: {:?}", slot.original_value()); - debug!(" After: {:?}", slot.present_value()); - } - } - } - } - - evm.db().unwrap().commit(state); - } - - let mut db = evm.take_db(); - - // process withdrawals unconditionally after any transactions - let mut withdrawals_trie = MptNode::default(); - for (i, withdrawal) in take(&mut block_builder.input.withdrawals) - .into_iter() - .enumerate() - { - // the withdrawal amount is given in Gwei - let amount_wei = GWEI_TO_WEI - .checked_mul(withdrawal.amount.try_into().unwrap()) - .unwrap(); - - #[cfg(not(target_os = "zkvm"))] - { - debug!("Withdrawal no. {}", withdrawal.index); - debug!(" Recipient: {:?}", withdrawal.address); - debug!(" Value: {}", amount_wei); - } - // Credit withdrawal amount - increase_account_balance(&mut db, withdrawal.address, amount_wei)?; - // Add withdrawal to trie - withdrawals_trie - .insert_rlp(&i.to_rlp(), withdrawal) - .map_err(Into::<anyhow::Error>::into) - .with_context(|| "failed to insert withdrawal")?; - } - - // Update result header with computed values - header.transactions_root = tx_trie.hash(); - header.transactions = txs; - header.receipts_root = receipt_trie.hash(); - header.logs_bloom = logs_bloom; - header.gas_used = cumulative_gas_used; - header.withdrawals_root = if spec_id < SpecId::SHANGHAI { - None - } else { - Some(withdrawals_trie.hash()) - }; - - // Leak memory, save cycles - guest_mem_forget([tx_trie, receipt_trie, withdrawals_trie]); - // Return block builder with updated database - Ok(block_builder.with_db(db)) - } -} - -pub fn fill_eth_tx_env( - l2_chain_spec: &ChainSpec, - tx_env: &mut TxEnv, - essence: &EthereumTxEssence, - caller: Address, - is_anchor: bool, -) { - // claim the anchor - tx_env.taiko.is_anchor = is_anchor; - // set the treasury address - tx_env.taiko.treasury = l2_chain_spec.l2_contract.unwrap(); - match essence { - EthereumTxEssence::Legacy(tx) => { - tx_env.caller = caller; - tx_env.gas_limit = tx.gas_limit.try_into().unwrap(); - tx_env.gas_price = tx.gas_price; - tx_env.gas_priority_fee = None; - tx_env.transact_to = if let TransactionKind::Call(to_addr) = tx.to { - TransactTo::Call(to_addr) - } else { - TransactTo::create() - }; - tx_env.value = tx.value; - tx_env.data = tx.data.clone(); - tx_env.chain_id = tx.chain_id; - tx_env.nonce = Some(tx.nonce); - tx_env.access_list.clear(); - } - EthereumTxEssence::Eip2930(tx) => { - tx_env.caller = caller; - tx_env.gas_limit = tx.gas_limit.try_into().unwrap(); - tx_env.gas_price = tx.gas_price; - tx_env.gas_priority_fee = None; - tx_env.transact_to = if let TransactionKind::Call(to_addr) = tx.to { - TransactTo::Call(to_addr) - } else { - TransactTo::create() - }; - tx_env.value = tx.value; - tx_env.data = tx.data.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list = tx.access_list.clone().into(); - } - EthereumTxEssence::Eip1559(tx) => { - tx_env.caller = caller; - tx_env.gas_limit = tx.gas_limit.try_into().unwrap(); - tx_env.gas_price = tx.max_fee_per_gas; - tx_env.gas_priority_fee = Some(tx.max_priority_fee_per_gas); - tx_env.transact_to = if let TransactionKind::Call(to_addr) = tx.to { - TransactTo::Call(to_addr) - } else { - TransactTo::create() - }; - tx_env.value = tx.value; - tx_env.data = tx.data.clone(); - tx_env.chain_id = Some(tx.chain_id); - tx_env.nonce = Some(tx.nonce); - tx_env.access_list = tx.access_list.clone().into(); - } - }; -} - -pub fn increase_account_balance<D>( - db: &mut D, - address: Address, - amount_wei: U256, -) -> anyhow::Result<()> -where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, -{ - // Read account from database - let mut account: Account = db - .basic(address) - .map_err(|db_err| { - anyhow!( - "Error increasing account balance for {}: {:?}", - address, - db_err - ) - })? - .unwrap_or_default() - .into(); - // Credit withdrawal amount - account.info.balance = account.info.balance.checked_add(amount_wei).unwrap(); - account.mark_touch(); - // Commit changes to database - db.commit([(address, account)].into()); - - Ok(()) -} diff --git a/lib/src/taiko/host.rs b/lib/src/taiko/host.rs new file mode 100644 index 000000000..3c38bcf10 --- /dev/null +++ b/lib/src/taiko/host.rs @@ -0,0 +1,258 @@ +use alloc::{string::String, vec::Vec}; +use std::path::PathBuf; + +use alloy_primitives::{Address, B256}; +use anyhow::{bail, ensure, Context, Result}; +use ethers_core::types::Transaction as EthersTransaction; +use log::info; +use rlp::{Decodable, DecoderError, Rlp}; +use zeth_primitives::{ + block::Header, ethers::from_ethers_h256, transactions::ethereum::EthereumTxEssence, +}; + +use super::{provider::TaikoProvider, TaikoSystemInfo}; +use crate::{ + builder::{prepare::EthHeaderPrepStrategy, BlockBuilder, TaikoStrategy, TkoTxExecStrategy}, + consts::ChainSpec, + host::{ + preflight::{new_preflight_input, Data, Preflight}, + provider::{BlockQuery, ProofQuery}, + provider_db::ProviderDb, + }, + input::Input, + taiko::consts::{get_contracts, MAX_TX_LIST, MAX_TX_LIST_BYTES}, +}; + +#[derive(Debug, Clone)] +pub struct HostArgs { + pub l1_cache: Option<PathBuf>, + pub l1_rpc: Option<String>, + pub l2_cache: Option<PathBuf>, + pub l2_rpc: Option<String>, +} + +pub fn init_taiko( + args: HostArgs, + l2_chain_spec: ChainSpec, + testnet: &str, + l2_block_no: u64, + graffiti: B256, + prover: Address, +) -> Result<(Input<EthereumTxEssence>, TaikoSystemInfo)> { + let mut tp = TaikoProvider::new( + args.l1_cache.clone(), + args.l1_rpc.clone(), + args.l2_cache.clone(), + args.l2_rpc.clone(), + )? + .with_prover(prover) + .with_l2_spec(l2_chain_spec.clone()) + .with_contracts(|| get_contracts(testnet)); + + let sys_info = derive_sys_info(&mut tp, l2_block_no, prover, graffiti)?; + tp.save()?; + + let preflight_data = + TaikoStrategy::run_preflight(l2_chain_spec, args.l2_cache, args.l2_rpc, l2_block_no)?; + + // Create the guest input from [Init] + let input: Input<EthereumTxEssence> = preflight_data + .clone() + .try_into() + .context("invalid preflight data")?; + + Ok((input, sys_info)) +} + +pub fn derive_sys_info( + tp: &mut TaikoProvider, + l2_block_no: u64, + prover: Address, + graffiti: B256, +) -> Result<TaikoSystemInfo> { + let l2_block = tp.get_l2_full_block(l2_block_no)?; + let l2_parent_block = tp.get_l2_full_block(l2_block_no - 1)?; + + let (anchor_tx, anchor_call) = tp.get_anchor(&l2_block)?; + + let l1_block_no = anchor_call.l1Height; + let l1_block = tp.get_l1_full_block(l1_block_no)?; + let l1_next_block = tp.get_l1_full_block(l1_block_no + 1)?; + + let (proposal_call, proposal_event) = tp.get_proposal(l1_block_no, l2_block_no)?; + + // 0. check anchor Tx + tp.check_anchor_tx(&anchor_tx, &l2_block)?; + + // 1. check l2 parent gas used + ensure!( + l2_parent_block.gas_used == ethers_core::types::U256::from(anchor_call.parentGasUsed), + "parentGasUsed mismatch" + ); + + // 2. check l1 signal root + let Some(l1_signal_service) = tp.l1_signal_service else { + bail!("l1_signal_service not set"); + }; + + let proof = tp.l1_provider.get_proof(&ProofQuery { + block_no: l1_block_no, + address: l1_signal_service.into_array().into(), + indices: Default::default(), + })?; + + let l1_signal_root = from_ethers_h256(proof.storage_hash); + + ensure!( + l1_signal_root == anchor_call.l1SignalRoot, + "l1SignalRoot mismatch" + ); + + // 3. check l1 block hash + ensure!( + l1_block.hash.unwrap() == ethers_core::types::H256::from(anchor_call.l1Hash.0), + "l1Hash mismatch" + ); + + let proof = tp.l2_provider.get_proof(&ProofQuery { + block_no: l2_block_no, + address: tp + .l2_signal_service + .expect("l2_signal_service not set") + .into_array() + .into(), + indices: Default::default(), + })?; + let l2_signal_root = from_ethers_h256(proof.storage_hash); + + tp.l1_provider.save()?; + tp.l2_provider.save()?; + + let sys_info = TaikoSystemInfo { + l1_hash: anchor_call.l1Hash, + l1_height: anchor_call.l1Height, + l2_tx_list: proposal_call.txList, + prover, + graffiti, + l1_signal_root, + l2_signal_root, + l2_withdrawals: l2_block + .withdrawals + .clone() + .unwrap_or_default() + .into_iter() + .map(|w| w.try_into().unwrap()) + .collect(), + block_proposed: proposal_event, + l1_next_block, + l2_block, + }; + + Ok(sys_info) +} + +impl Preflight<EthereumTxEssence> for TaikoStrategy { + fn run_preflight( + chain_spec: ChainSpec, + cache_path: Option<std::path::PathBuf>, + rpc_url: Option<String>, + block_no: u64, + ) -> Result<Data<EthereumTxEssence>> { + let mut tp = TaikoProvider::new(None, None, cache_path, rpc_url)?; + + // Fetch the parent block + let parent_block = tp.l2_provider.get_partial_block(&BlockQuery { + block_no: block_no - 1, + })?; + + info!( + "Initial block: {:?} ({:?})", + parent_block.number.unwrap(), + parent_block.hash.unwrap() + ); + let parent_header: Header = parent_block.try_into().context("invalid parent block")?; + + // Fetch the target block + let mut block = tp.l2_provider.get_full_block(&BlockQuery { block_no })?; + let (anchor_tx, anchor_call) = tp.get_anchor(&block)?; + let (proposal_call, _) = tp.get_proposal(anchor_call.l1Height, block_no)?; + + let mut l2_tx_list: Vec<EthersTransaction> = rlp_decode_list(&proposal_call.txList)?; + ensure!( + proposal_call.txList.len() <= MAX_TX_LIST_BYTES, + "tx list bytes must be not more than MAX_TX_LIST_BYTES" + ); + ensure!( + l2_tx_list.len() <= MAX_TX_LIST, + "tx list size must be not more than MAX_TX_LISTs" + ); + + // TODO(Cecilia): reset to empty necessary if wrong? + // tracing::log for particular reason instead of uniform error handling? + // txs.clear(); + + info!( + "Inserted anchor {:?} in tx_list decoded from {:?}", + anchor_tx.hash, proposal_call.txList + ); + l2_tx_list.insert(0, anchor_tx); + block.transactions = l2_tx_list; + + info!( + "Final block number: {:?} ({:?})", + block.number.unwrap(), + block.hash.unwrap() + ); + info!("Transaction count: {:?}", block.transactions.len()); + + // Create the provider DB + let provider_db = ProviderDb::new(tp.l2_provider, parent_header.number); + + // Create the input data + let input = new_preflight_input(block.clone(), parent_header.clone())?; + let transactions = input.transactions.clone(); + let withdrawals = input.withdrawals.clone(); + + // Create the block builder, run the transactions and extract the DB + let mut builder = BlockBuilder::new(&chain_spec, input) + .with_db(provider_db) + .prepare_header::<EthHeaderPrepStrategy>()? + .execute_transactions::<TkoTxExecStrategy>()?; + let provider_db = builder.mut_db().unwrap(); + + info!("Gathering inclusion proofs ..."); + + // Gather inclusion proofs for the initial and final state + let parent_proofs = provider_db.get_initial_proofs()?; + let proofs = provider_db.get_latest_proofs()?; + + // Gather proofs for block history + let ancestor_headers = provider_db.get_ancestor_headers()?; + + info!("Saving provider cache ..."); + + // Save the provider cache + provider_db.get_provider().save()?; + + info!("Provider-backed execution is Done!"); + + Ok(Data { + db: provider_db.get_initial_db().clone(), + parent_header, + parent_proofs, + header: block.try_into().context("invalid block")?, + transactions, + withdrawals, + proofs, + ancestor_headers, + }) + } +} + +fn rlp_decode_list<T>(bytes: &[u8]) -> Result<Vec<T>, DecoderError> +where + T: Decodable, +{ + let rlp = Rlp::new(bytes); + rlp.as_list() +} diff --git a/lib/src/taiko/mod.rs b/lib/src/taiko/mod.rs index e7311ae1e..2b12307e6 100644 --- a/lib/src/taiko/mod.rs +++ b/lib/src/taiko/mod.rs @@ -1,10 +1,102 @@ -pub mod block_builder; -#[cfg(not(target_os = "zkvm"))] -pub mod execute; -pub mod prepare; -pub mod utils; - -pub enum Layer { - L1, - L2, +use alloc::vec::Vec; + +use alloy_primitives::{Address, B256}; +use alloy_sol_types::{sol, SolCall}; +use anyhow::{anyhow, Result}; +use ethers_core::types::{Block, Transaction as EthersTransaction}; +use serde::{Deserialize, Serialize}; +use zeth_primitives::withdrawal::Withdrawal; + +pub mod consts; +#[cfg(all(feature = "std", not(target_os = "zkvm")))] +pub mod host; +pub mod protocol_instance; +#[cfg(all(feature = "std", not(target_os = "zkvm")))] +pub mod provider; + +sol! { + function anchor( + bytes32 l1Hash, + bytes32 l1SignalRoot, + uint64 l1Height, + uint32 parentGasUsed + ) + external + {} +} + +#[inline] +pub fn decode_anchor(bytes: &[u8]) -> Result<anchorCall> { + anchorCall::abi_decode(bytes, true).map_err(|e| anyhow!(e)) + // .context("Invalid anchor call") +} + +sol! { + #[derive(Debug, Default, Deserialize, Serialize)] + struct EthDeposit { + address recipient; + uint96 amount; + uint64 id; + } + + #[derive(Debug, Default, Deserialize, Serialize)] + struct BlockMetadata { + bytes32 l1Hash; // slot 1 + bytes32 difficulty; // slot 2 + bytes32 blobHash; //or txListHash (if Blob not yet supported), // slot 3 + bytes32 extraData; // slot 4 + bytes32 depositsHash; // slot 5 + address coinbase; // L2 coinbase, // slot 6 + uint64 id; + uint32 gasLimit; + uint64 timestamp; // slot 7 + uint64 l1Height; + uint24 txListByteOffset; + uint24 txListByteSize; + uint16 minTier; + bool blobUsed; + bytes32 parentMetaHash; // slot 8 + } + + #[derive(Debug)] + struct Transition { + bytes32 parentHash; + bytes32 blockHash; + bytes32 signalRoot; + bytes32 graffiti; + } + + #[derive(Debug, Default, Clone, Deserialize, Serialize)] + event BlockProposed( + uint256 indexed blockId, + address indexed prover, + uint96 livenessBond, + BlockMetadata meta, + EthDeposit[] depositsProcessed + ); + + #[derive(Debug)] + struct TierProof { + uint16 tier; + bytes data; + } + + function proposeBlock(bytes calldata params, bytes calldata txList) {} + + function proveBlock(uint64 blockId, bytes calldata input) {} +} + +#[derive(Debug)] +pub struct TaikoSystemInfo { + pub l1_hash: B256, + pub l1_height: u64, + pub l2_tx_list: Vec<u8>, + pub prover: Address, + pub graffiti: B256, + pub l1_signal_root: B256, + pub l2_signal_root: B256, + pub l2_withdrawals: Vec<Withdrawal>, + pub block_proposed: BlockProposed, + pub l1_next_block: Block<EthersTransaction>, + pub l2_block: Block<EthersTransaction>, } diff --git a/lib/src/taiko/prepare.rs b/lib/src/taiko/prepare.rs deleted file mode 100644 index 551dc6e87..000000000 --- a/lib/src/taiko/prepare.rs +++ /dev/null @@ -1,67 +0,0 @@ -use core::fmt::Debug; - -use anyhow::{bail, Context, Result}; -use revm::{Database, DatabaseCommit}; -use zeth_primitives::{block::Header, taiko::BLOCK_GAS_LIMIT, transactions::TxEssence}; - -use crate::{ - block_builder::BlockBuilder, consts::MAX_EXTRA_DATA_BYTES, preparation::HeaderPrepStrategy, -}; - -pub struct TaikoHeaderPrepStrategy {} - -impl HeaderPrepStrategy for TaikoHeaderPrepStrategy { - fn prepare_header<D, E>(mut block_builder: BlockBuilder<D, E>) -> Result<BlockBuilder<D, E>> - where - D: Database + DatabaseCommit, - <D as Database>::Error: Debug, - E: TxEssence, - { - // Validate gas limit - if block_builder.input.gas_limit != *BLOCK_GAS_LIMIT { - bail!( - "Invalid gas limit: expected == {}, got {}", - *BLOCK_GAS_LIMIT, - block_builder.input.gas_limit, - ); - } - // Validate timestamp - if block_builder.input.timestamp < block_builder.input.parent_header.timestamp { - bail!( - "Invalid timestamp: expected >= {}, got {}", - block_builder.input.parent_header.timestamp, - block_builder.input.timestamp, - ); - } - // Validate extra data - let extra_data_bytes = block_builder.input.extra_data.len(); - if extra_data_bytes > MAX_EXTRA_DATA_BYTES { - bail!( - "Invalid extra data: expected <= {}, got {}", - MAX_EXTRA_DATA_BYTES, - extra_data_bytes, - ) - } - // Derive header - block_builder.header = Some(Header { - // Initialize fields that we can compute from the parent - parent_hash: block_builder.input.parent_header.hash(), - number: block_builder - .input - .parent_header - .number - .checked_add(1) - .with_context(|| "Invalid block number: too large")?, - base_fee_per_gas: block_builder.input.base_fee_per_gas, - // Initialize metadata from input - beneficiary: block_builder.input.beneficiary, - gas_limit: block_builder.input.gas_limit, - timestamp: block_builder.input.timestamp, - mix_hash: block_builder.input.mix_hash, - extra_data: block_builder.input.extra_data.clone(), - // do not fill the remaining fields - ..Default::default() - }); - Ok(block_builder) - } -} diff --git a/lib/src/taiko/protocol_instance.rs b/lib/src/taiko/protocol_instance.rs new file mode 100644 index 000000000..6d9cc3316 --- /dev/null +++ b/lib/src/taiko/protocol_instance.rs @@ -0,0 +1,168 @@ +use alloc::vec::Vec; + +use alloy_primitives::{Address, TxHash, B256}; +use alloy_sol_types::SolValue; +use anyhow::{anyhow, Result}; +use revm::primitives::SpecId; +use zeth_primitives::{ + block::Header, + ethers::{from_ethers_h256, from_ethers_u256}, + keccak::keccak, + transactions::EthereumTransaction, +}; + +use super::{consts::ANCHOR_GAS_LIMIT, BlockMetadata, EthDeposit, TaikoSystemInfo, Transition}; +use crate::consts::TKO_MAINNET_CHAIN_SPEC; + +#[derive(Debug)] +pub struct ProtocolInstance { + pub transition: Transition, + pub block_metadata: BlockMetadata, + pub prover: Address, +} + +impl ProtocolInstance { + pub fn meta_hash(&self) -> B256 { + keccak(self.block_metadata.abi_encode()).into() + } + + // keccak256(abi.encode(tran, newInstance, prover, metaHash)) + pub fn instance_hash(&self, evidence_type: EvidenceType) -> B256 { + match evidence_type { + EvidenceType::Sgx { new_pubkey } => keccak( + ( + self.transition.clone(), + new_pubkey, + self.prover, + self.meta_hash(), + ) + .abi_encode(), + ) + .into(), + EvidenceType::Pse => todo!(), + EvidenceType::Powdr => todo!(), + } + } +} + +#[derive(Debug)] +pub enum EvidenceType { + Sgx { + new_pubkey: Address, // the evidence signature public key + }, + Pse, + Powdr, +} + +// TODO(cecilia): rewrite +pub fn assemble_protocol_instance( + sys: &TaikoSystemInfo, + header: &Header, +) -> Result<ProtocolInstance> { + let tx_list_hash = TxHash::from(keccak(sys.l2_tx_list.as_slice())); + let block_hash: zeth_primitives::U256 = tx_list_hash.into(); + + let deposits = sys + .l2_withdrawals + .iter() + .map(|w| EthDeposit { + recipient: w.address, + amount: w.amount as u128, + id: w.index, + }) + .collect::<Vec<_>>(); + let deposits_hash: B256 = keccak(deposits.abi_encode()).into(); + + let extra_data: B256 = bytes_to_bytes32(&header.extra_data).into(); + + let prevrandao = if TKO_MAINNET_CHAIN_SPEC.spec_id(header.number) >= SpecId::SHANGHAI { + from_ethers_h256(sys.l1_next_block.mix_hash.unwrap_or_default()).into() + } else { + from_ethers_u256(sys.l1_next_block.difficulty) + }; + let difficulty = block_hash + ^ (prevrandao + * zeth_primitives::U256::from(header.number) + * zeth_primitives::U256::from(sys.l1_next_block.number.unwrap_or_default().as_u64())); + + let gas_limit: u64 = header.gas_limit.try_into().unwrap(); + let mut pi = ProtocolInstance { + transition: Transition { + parentHash: header.parent_hash, + blockHash: header.hash(), + signalRoot: sys.l2_signal_root, + graffiti: sys.graffiti, + }, + block_metadata: BlockMetadata { + l1Hash: sys.l1_hash, + difficulty: difficulty.into(), + blobHash: tx_list_hash, + extraData: extra_data, + depositsHash: deposits_hash, + coinbase: header.beneficiary, + id: header.number, + gasLimit: (gas_limit - ANCHOR_GAS_LIMIT) as u32, + timestamp: header.timestamp.try_into().unwrap(), + l1Height: sys.l1_height, + txListByteOffset: 0u32, + txListByteSize: sys.l2_tx_list.len() as u32, + minTier: sys.block_proposed.meta.minTier, + blobUsed: sys.l2_tx_list.is_empty(), + parentMetaHash: sys.block_proposed.meta.parentMetaHash, + }, + prover: sys.prover, + }; + verify(sys, header, &mut pi)?; + Ok(pi) +} + +pub fn verify(sys: &TaikoSystemInfo, header: &Header, pi: &mut ProtocolInstance) -> Result<()> { + // check the block metadata + if pi.block_metadata.abi_encode() != sys.block_proposed.meta.abi_encode() { + return Err(anyhow!( + "block metadata mismatch, expected: {:?}, got: {:?}", + sys.block_proposed.meta, + pi.block_metadata + )); + } + // Check the block hash + if Some(header.hash()) != sys.l2_block.hash.map(from_ethers_h256) { + let _txs: Vec<EthereumTransaction> = sys + .l2_block + .transactions + .iter() + .filter_map(|tx| tx.clone().try_into().ok()) + .collect(); + return Err(anyhow!( + "block hash mismatch, expected: {}, got: {}", + header.hash(), + sys.l2_block.hash.map(from_ethers_h256).unwrap() + )); + } + + Ok(()) +} + +fn bytes_to_bytes32(input: &[u8]) -> [u8; 32] { + let mut bytes = [0u8; 32]; + let len = core::cmp::min(input.len(), 32); + bytes[..len].copy_from_slice(&input[..len]); + bytes +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn bytes_to_bytes32_test() { + let input = ""; + let byte = bytes_to_bytes32(input.as_bytes()); + assert_eq!( + byte, + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 + ] + ); + } +} diff --git a/lib/src/taiko/provider.rs b/lib/src/taiko/provider.rs new file mode 100644 index 000000000..7e3a39603 --- /dev/null +++ b/lib/src/taiko/provider.rs @@ -0,0 +1,204 @@ +use alloc::string::String; +use std::path::PathBuf; + +use alloy_primitives::Address; +use alloy_sol_types::SolCall; +use anyhow::{anyhow, bail, ensure, Context, Result}; +use ethers_core::types::{Block, Transaction, H256, U256, U64}; +use zeth_primitives::{ + ethers::{from_ethers_h160, from_ethers_h256}, + transactions::EthereumTransaction, +}; + +use super::{anchorCall, decode_anchor, proposeBlockCall, BlockProposed}; +use crate::{ + consts::ChainSpec, + host::provider::{new_provider, BlockQuery, ProofQuery, Provider, TxQuery}, + taiko::consts::{check_anchor_signature, ANCHOR_GAS_LIMIT, GOLDEN_TOUCH_ACCOUNT}, +}; + +pub struct TaikoProvider { + pub l1_provider: Box<dyn Provider>, + pub l2_provider: Box<dyn Provider>, + pub l2_spec: Option<ChainSpec>, + pub prover: Option<Address>, + pub l1_contract: Option<Address>, + pub l2_contract: Option<Address>, + pub l1_signal_service: Option<Address>, + pub l2_signal_service: Option<Address>, +} + +impl TaikoProvider { + pub fn new( + l1_cache: Option<PathBuf>, + l1_rpc: Option<String>, + l2_cache: Option<PathBuf>, + l2_rpc: Option<String>, + ) -> Result<Self> { + Ok(Self { + l1_provider: new_provider(l1_cache, l1_rpc)?, + l2_provider: new_provider(l2_cache, l2_rpc)?, + l2_spec: None, + prover: None, + l1_contract: None, + l2_contract: None, + l1_signal_service: None, + l2_signal_service: None, + }) + } + + pub fn with_l2_spec(mut self, spec: ChainSpec) -> Self { + self.l2_spec = Some(spec); + self + } + + pub fn with_prover(mut self, prover: Address) -> Self { + self.prover = Some(prover); + self + } + + pub fn with_contracts( + mut self, + f: impl FnOnce() -> Result<(Address, Address, Address, Address)>, + ) -> Self { + if let Ok((l1_contract, l2_contract, l1_signal_service, l2_signal_service)) = f() { + self.l1_contract = Some(l1_contract); + self.l2_contract = Some(l2_contract); + self.l1_signal_service = Some(l1_signal_service); + self.l2_signal_service = Some(l2_signal_service); + } + self + } + + pub fn save(&mut self) -> Result<()> { + self.l1_provider.save()?; + self.l2_provider.save()?; + Ok(()) + } + + pub fn get_l1_full_block(&mut self, l1_block_no: u64) -> Result<Block<Transaction>> { + self.l1_provider.get_full_block(&BlockQuery { + block_no: l1_block_no, + }) + } + + pub fn get_l2_full_block(&mut self, l2_block_no: u64) -> Result<Block<Transaction>> { + self.l2_provider.get_full_block(&BlockQuery { + block_no: l2_block_no, + }) + } + + pub fn get_anchor(&self, l2_block: &Block<Transaction>) -> Result<(Transaction, anchorCall)> { + let tx = l2_block.transactions[0].clone(); + let call = decode_anchor(tx.input.as_ref())?; + Ok((tx, call)) + } + + pub fn get_proposal( + &mut self, + l1_block_no: u64, + l2_block_no: u64, + ) -> Result<(proposeBlockCall, BlockProposed)> { + let logs = self.l1_provider.filter_event_log::<BlockProposed>( + self.l1_contract.unwrap(), + l1_block_no, + l2_block_no, + )?; + for (log, event) in logs { + if event.blockId == zeth_primitives::U256::from(l2_block_no) { + let tx = self + .l1_provider + .get_transaction(&TxQuery { + tx_hash: log.transaction_hash.unwrap(), + block_no: log.block_number.map(|b| b.as_u64()), + }) + .with_context(|| { + anyhow!( + "Cannot find BlockProposed Tx {:?}", + log.transaction_hash.unwrap() + ) + })?; + let call = proposeBlockCall::abi_decode(&tx.input, false).unwrap(); + // .with_context(|| "failed to decode propose block call")?; + return Ok((call, event)); + } + } + bail!("No BlockProposed event found for block {l2_block_no}"); + } + + pub fn check_anchor_with_blocks<TX1, TX2>( + &mut self, + l1_block: &Block<TX1>, + l2_parent_block: &Block<TX2>, + anchor: anchorCall, + ) -> Result<()> { + // 1. check l2 parent gas used + ensure!( + l2_parent_block.gas_used == U256::from(anchor.parentGasUsed), + "parentGasUsed mismatch" + ); + + // 2. check l1 signal root + let Some(l1_signal_service) = self.l1_signal_service else { + bail!("l1_signal_service not set"); + }; + + let proof = self.l1_provider.get_proof(&ProofQuery { + block_no: l1_block.number.unwrap().as_u64(), + address: l1_signal_service.into_array().into(), + indices: Default::default(), + })?; + let signal_root = from_ethers_h256(proof.storage_hash); + + ensure!(signal_root == anchor.l1SignalRoot, "l1SignalRoot mismatch"); + + // 3. check l1 block hash + ensure!( + l1_block.hash.unwrap() == H256::from(anchor.l1Hash.0), + "l1Hash mismatch" + ); + + Ok(()) + } + + pub fn check_anchor_tx( + &self, + anchor: &Transaction, + l2_block: &Block<Transaction>, + ) -> Result<()> { + let tx1559_type = U64::from(0x2); + ensure!( + anchor.transaction_type == Some(tx1559_type), + "anchor transaction type mismatch" + ); + + let tx: EthereumTransaction = anchor + .clone() + .try_into() + .context(anyhow!("failed to decode anchor transaction: {:?}", anchor))?; + check_anchor_signature(&tx).context(anyhow!("failed to check anchor signature"))?; + + ensure!( + from_ethers_h160(anchor.from) == *GOLDEN_TOUCH_ACCOUNT, + "anchor transaction from mismatch" + ); + ensure!( + from_ethers_h160(anchor.to.unwrap()) == self.l1_contract.unwrap(), + "anchor transaction to mismatch" + ); + ensure!( + anchor.value == U256::from(0), + "anchor transaction value mismatch" + ); + ensure!( + anchor.gas == U256::from(ANCHOR_GAS_LIMIT), + "anchor transaction gas price mismatch" + ); + ensure!( + anchor.max_fee_per_gas == l2_block.base_fee_per_gas, + "anchor transaction gas mismatch" + ); + + Ok(()) + } +} diff --git a/lib/src/taiko/utils.rs b/lib/src/taiko/utils.rs deleted file mode 100644 index e552d39e4..000000000 --- a/lib/src/taiko/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -use alloc::vec::Vec; - -use rlp::{Decodable, DecoderError, Rlp}; - -pub fn rlp_decode_list<T>(bytes: &[u8]) -> Result<Vec<T>, DecoderError> -where - T: Decodable, -{ - let rlp = Rlp::new(bytes); - rlp.as_list() -} diff --git a/lib/src/utils.rs b/lib/src/utils.rs new file mode 100644 index 000000000..db91e675f --- /dev/null +++ b/lib/src/utils.rs @@ -0,0 +1,63 @@ +// Copyright 2023 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{io, io::Read}; + +/// An adaptor that chains multiple readers together. +pub struct MultiReader<I, R> { + readers: I, + current: Option<R>, +} + +impl<I, R> MultiReader<I, R> +where + I: IntoIterator<Item = R>, + R: Read, +{ + /// Creates a new instance of `MultiReader`. + /// + /// This function takes an iterator over readers and returns a `MultiReader`. + pub fn new(readers: I) -> MultiReader<I::IntoIter, R> { + let mut readers = readers.into_iter(); + let current = readers.next(); + MultiReader { readers, current } + } +} + +/// Implementation of the `Read` trait for `MultiReader`. +impl<I, R> Read for MultiReader<I, R> +where + I: Iterator<Item = R>, + R: Read, +{ + /// Reads data from the current reader into a buffer. + /// + /// This function reads as much data as possible from the current reader into the + /// buffer, and switches to the next reader when the current one is exhausted. + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + loop { + match self.current { + Some(ref mut r) => { + let n = r.read(buf)?; + if n > 0 { + return Ok(n); + } + } + None => return Ok(0), + } + self.current = self.readers.next(); + } + } +} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 7a4d3a42c..f84c0f85f 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -4,20 +4,20 @@ version = "0.1.0" edition = "2021" [dependencies] -alloy-primitives = { version = "0.4", default-features = false, features = [ +alloy-primitives = { version = "0.6", default-features = false, features = [ "rlp", "serde", ] } -alloy-sol-types = { version = "0.4", default-features = false, optional = true } -alloy-dyn-abi = { version = "0.4",default-features = false, optional = true } +alloy-sol-types = { version = "0.6", default-features = false, optional = true } +alloy-dyn-abi = { version = "0.6",default-features = false, optional = true } alloy-rlp = { version = "0.3", default-features = false } alloy-rlp-derive = { version = "0.3", default-features = false } anyhow = { version = "1.0", default-features = false } bytes = { version = "1.1", default-features = false } ethers-core = { version = "2.0", optional = true, features = ["optimism"] } -k256 = { version = "=0.13.1", features = ["ecdsa"], default_features = false } +k256 = { version = "^0.13.3", features = ["ecdsa"], default_features = false } -revm-primitives = { workspace = true, optional = true } +revm-primitives = { workspace = true, optional = true, default-features = false } rlp = { version = "0.5.2", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive"] } sha3 = { version = "0.10", default-features = false} @@ -34,11 +34,7 @@ serde_json = { version = "1.0", default-features = false } [features] std = [] +taiko = ["revm-primitives/taiko"] +optimism = ["revm-primitives/optimism"] ethers = ["dep:ethers-core"] revm = ["dep:revm-primitives"] -taiko = [ - "revm-primitives/taiko", - "dep:alloy-sol-types", - "dep:alloy-dyn-abi", - "dep:once_cell", -] diff --git a/primitives/src/batch.rs b/primitives/src/batch.rs new file mode 100644 index 000000000..2e22977dd --- /dev/null +++ b/primitives/src/batch.rs @@ -0,0 +1,116 @@ +// Copyright 2024 RISC Zero, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use alloc::vec::Vec; + +use alloy_primitives::{Bytes, B256}; +use alloy_rlp::{Decodable, Encodable}; +use alloy_rlp_derive::{RlpDecodable, RlpEncodable}; +use serde::{Deserialize, Serialize}; + +/// Bytes for RLP-encoded transactions. +pub type RawTransaction = Bytes; + +/// A batch represents the inputs needed to build Optimism block. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Batch(pub BatchEssence); + +/// Represents the core details of a [Batch], specifically the portion that is derived +/// from the batcher transactions. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RlpEncodable, RlpDecodable)] +pub struct BatchEssence { + /// The block hash of the previous L2 block. + pub parent_hash: B256, + /// The number of the L1 block corresponding to the sequencing epoch of the L2 block. + pub epoch_num: u64, + /// The hash of the L1 block corresponding to the sequencing epoch of the L2 block. + pub epoch_hash: B256, + /// The timestamp of the L2 block. + pub timestamp: u64, + /// An RLP-encoded list of EIP-2718 encoded transactions. + pub transactions: Vec<RawTransaction>, +} + +impl Batch { + pub fn new(parent_hash: B256, epoch_num: u64, epoch_hash: B256, timestamp: u64) -> Self { + Batch(BatchEssence { + parent_hash, + epoch_num, + epoch_hash, + timestamp, + transactions: Vec::new(), + }) + } +} + +impl Encodable for Batch { + #[inline] + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + // wrap the RLP-essence inside a bytes payload + alloy_rlp::Header { + list: false, + payload_length: self.0.length() + 1, + } + .encode(out); + out.put_u8(0x00); + self.0.encode(out); + } + + #[inline] + fn length(&self) -> usize { + let bytes_length = self.0.length() + 1; + alloy_rlp::length_of_length(bytes_length) + bytes_length + } +} + +impl Decodable for Batch { + #[inline] + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> { + let bytes = alloy_rlp::Header::decode_bytes(buf, false)?; + match bytes.split_first() { + Some((0, mut payload)) => Ok(Self(BatchEssence::decode(&mut payload)?)), + Some(_) => Err(alloy_rlp::Error::Custom("invalid version")), + None => Err(alloy_rlp::Error::InputTooShort), + } + } +} + +#[cfg(test)] +mod tests { + use alloc::vec; + + use hex_literal::hex; + use serde_json::json; + + use super::*; + + #[test] + fn rlp_roundtrip() { + let expected = hex!("b85000f84da0dbf6a80fef073de06add9b0d14026d6e5a86c85f6d102c36d3d8e9cf89c2afd3840109d8fea0438335a20d98863a4c0c97999eb2481921ccd28553eac6f913af7c12aec0410884647f5ea9c0"); + let batch: Batch = serde_json::from_value(json!({ + "parent_hash": "0xdbf6a80fef073de06add9b0d14026d6e5a86c85f6d102c36d3d8e9cf89c2afd3", + "epoch_num": 17422590, + "epoch_hash": "0x438335a20d98863a4c0c97999eb2481921ccd28553eac6f913af7c12aec04108", + "timestamp": 1686068905, + "transactions": [] + })) + .unwrap(); + + let encoded = alloy_rlp::encode(&batch); + assert_eq!(encoded.len(), batch.length()); + assert_eq!(encoded, expected); + + let decoded = Batch::decode(&mut &encoded[..]).unwrap(); + assert_eq!(batch, decoded); + } +} diff --git a/primitives/src/block.rs b/primitives/src/block.rs index 68c394617..61a812cbd 100644 --- a/primitives/src/block.rs +++ b/primitives/src/block.rs @@ -107,6 +107,8 @@ impl Header { #[cfg(test)] mod tests { + use alloc::string::ToString; + use serde_json::json; use super::*; diff --git a/primitives/src/ethers.rs b/primitives/src/ethers.rs index a83b189c5..3faa3bb34 100644 --- a/primitives/src/ethers.rs +++ b/primitives/src/ethers.rs @@ -37,21 +37,25 @@ use ethers_core::types::{ transaction::eip2930::{ AccessList as EthersAccessList, AccessListItem as EthersAccessListItem, }, - Block as EthersBlock, Bytes as EthersBytes, Transaction as EthersTransaction, + Block as EthersBlock, Bytes as EthersBytes, EIP1186ProofResponse, + Transaction as EthersTransaction, TransactionReceipt as EthersReceipt, Withdrawal as EthersWithdrawal, H160 as EthersH160, H256 as EthersH256, U256 as EthersU256, + U64, }; use crate::{ access_list::{AccessList, AccessListItem}, block::Header, - signature::TxSignature, + receipt::{Log, Receipt, ReceiptPayload}, transactions::{ ethereum::{ EthereumTxEssence, TransactionKind, TxEssenceEip1559, TxEssenceEip2930, TxEssenceLegacy, }, optimism::{OptimismTxEssence, TxEssenceOptimismDeposited}, + signature::TxSignature, Transaction, TxEssence, }, + trie::StateAccount, withdrawal::Withdrawal, }; @@ -61,6 +65,12 @@ pub fn from_ethers_u256(v: EthersU256) -> U256 { U256::from_limbs(v.0) } +/// Convert an `U256` type to the `EthersU256` type. +#[inline] +pub fn to_ethers_u256(v: U256) -> EthersU256 { + EthersU256(v.into_limbs()) +} + /// Convert an `EthersH160` type to the `Address` type. #[inline] pub fn from_ethers_h160(v: EthersH160) -> Address { @@ -173,13 +183,13 @@ impl TryFrom<EthersTransaction> for EthereumTxEssence { Some(chain_id) => Some( chain_id .try_into() - .map_err(|err| anyhow!("invalid chain_id: {}", err))?, + .map_err(|err| anyhow!("invalid chain_id: {err}"))?, ), }, nonce: tx .nonce .try_into() - .map_err(|err| anyhow!("invalid nonce: {}", err))?, + .map_err(|err| anyhow!("invalid nonce: {err}"))?, gas_price: from_ethers_u256(tx.gas_price.context("gas_price missing")?), gas_limit: from_ethers_u256(tx.gas), to: tx.to.into(), @@ -191,11 +201,11 @@ impl TryFrom<EthersTransaction> for EthereumTxEssence { .chain_id .context("chain_id missing")? .try_into() - .map_err(|err| anyhow!("invalid chain_id: {}", err))?, + .map_err(|err| anyhow!("invalid chain_id: {err}"))?, nonce: tx .nonce .try_into() - .map_err(|err| anyhow!("invalid nonce: {}", err))?, + .map_err(|err| anyhow!("invalid nonce: {err}"))?, gas_price: from_ethers_u256(tx.gas_price.context("gas_price missing")?), gas_limit: from_ethers_u256(tx.gas), to: tx.to.into(), @@ -208,11 +218,11 @@ impl TryFrom<EthersTransaction> for EthereumTxEssence { .chain_id .context("chain_id missing")? .try_into() - .map_err(|err| anyhow!("invalid chain_id: {}", err))?, + .map_err(|err| anyhow!("invalid chain_id: {err}"))?, nonce: tx .nonce .try_into() - .map_err(|err| anyhow!("invalid nonce: {}", err))?, + .map_err(|err| anyhow!("invalid nonce: {err}"))?, max_priority_fee_per_gas: from_ethers_u256( tx.max_priority_fee_per_gas .context("max_priority_fee_per_gas missing")?, @@ -245,9 +255,9 @@ impl TryFrom<EthersTransaction> for OptimismTxEssence { to: tx.to.into(), value: from_ethers_u256(tx.value), data: tx.input.0.into(), - source_hash: from_ethers_h256(tx.source_hash.unwrap_or_default()), - mint: from_ethers_u256(tx.mint.unwrap_or_default()), - is_system_tx: tx.is_system_tx.unwrap_or_default(), + source_hash: from_ethers_h256(tx.source_hash), + mint: from_ethers_u256(tx.mint.context("mint missing")?), + is_system_tx: tx.is_system_tx, }), _ => OptimismTxEssence::Ethereum(tx.try_into()?), }; @@ -268,7 +278,53 @@ impl TryFrom<EthersWithdrawal> for Withdrawal { amount: withdrawal .amount .try_into() - .map_err(|err| anyhow!("invalid amount: {}", err))?, + .map_err(|err| anyhow!("invalid amount: {err}"))?, + }) + } +} + +impl TryFrom<EthersReceipt> for Receipt { + type Error = anyhow::Error; + + fn try_from(receipt: EthersReceipt) -> Result<Self, Self::Error> { + Ok(Receipt { + tx_type: receipt + .transaction_type + .context("transaction_type missing")? + .as_u64() + .try_into() + .context("invalid transaction_type")?, + payload: ReceiptPayload { + success: receipt.status.context("status missing")? == U64::one(), + cumulative_gas_used: from_ethers_u256(receipt.cumulative_gas_used), + logs_bloom: Bloom::from_slice(receipt.logs_bloom.as_bytes()), + logs: receipt + .logs + .into_iter() + .map(|log| { + let address = log.address.0.into(); + let topics = log.topics.into_iter().map(from_ethers_h256).collect(); + let data = log.data.0.into(); + Log { + address, + topics, + data, + } + }) + .collect(), + }, }) } } + +/// Conversion from `EIP1186ProofResponse` to the local [StateAccount]. +impl From<EIP1186ProofResponse> for StateAccount { + fn from(response: EIP1186ProofResponse) -> Self { + StateAccount { + nonce: response.nonce.as_u64(), + balance: from_ethers_u256(response.balance), + storage_root: from_ethers_h256(response.storage_hash), + code_hash: from_ethers_h256(response.code_hash), + } + } +} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 630c95cda..7866603a7 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -22,9 +22,6 @@ pub mod access_list; pub mod block; pub mod keccak; pub mod receipt; -pub mod signature; -#[cfg(feature = "taiko")] -pub mod taiko; pub mod transactions; pub mod trie; pub mod withdrawal; @@ -32,6 +29,7 @@ pub mod withdrawal; #[cfg(feature = "ethers")] pub mod ethers; +pub mod batch; #[cfg(feature = "revm")] pub mod revm; diff --git a/primitives/src/revm.rs b/primitives/src/revm.rs index 18504ea97..2cee7a4e5 100644 --- a/primitives/src/revm.rs +++ b/primitives/src/revm.rs @@ -1,4 +1,4 @@ -// Copyright 2023 RISC Zero, Inc. +// Copyright 2024 RISC Zero, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -66,8 +66,8 @@ impl From<RevmLog> for Log { fn from(log: RevmLog) -> Self { Log { address: log.address, - topics: log.topics, - data: log.data, + topics: log.data.topics().to_vec(), + data: log.data.data, } } } diff --git a/primitives/src/taiko/anchor.rs b/primitives/src/taiko/anchor.rs deleted file mode 100644 index 1b3222d3c..000000000 --- a/primitives/src/taiko/anchor.rs +++ /dev/null @@ -1,61 +0,0 @@ -use alloy_sol_types::{sol, SolCall}; -use anyhow::{anyhow, bail, Context, Result}; -use once_cell::sync::Lazy; - -use super::AbiEncodeError; -use crate::{transactions::EthereumTransaction, uint, U256}; - -static GX1: Lazy<U256> = - Lazy::new(|| uint!(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798_U256)); -static N: Lazy<U256> = - Lazy::new(|| uint!(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141_U256)); -static GX1_MUL_PRIVATEKEY: Lazy<U256> = - Lazy::new(|| uint!(0x4341adf5a780b4a87939938fd7a032f6e6664c7da553c121d3b4947429639122_U256)); -static GX2: Lazy<U256> = - Lazy::new(|| uint!(0xc6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5_U256)); - -sol! { - function anchor( - bytes32 l1Hash, - bytes32 l1SignalRoot, - uint64 l1Height, - uint32 parentGasUsed - ) - external - {} -} - -/// decode anchor arguments from anchor transaction -pub fn decode_anchor_call_args(data: &[u8]) -> Result<anchorCall> { - let anchor_call = anchorCall::abi_decode(data, false) - .map_err(|e| anyhow!(AbiEncodeError::from(e))) - .with_context(|| "failed to decode anchor call")?; - Ok(anchor_call) -} - -/// check the anchor signature with fixed K value -pub fn check_anchor_signature(anchor: &EthereumTransaction) -> Result<()> { - let sign = &anchor.signature; - if sign.r == *GX1 { - return Ok(()); - } - let msg_hash = anchor.essence.signing_hash(); - let msg_hash: U256 = msg_hash.into(); - if sign.r == *GX2 { - // when r == GX2 require s == 0 if k == 1 - // alias: when r == GX2 require N == msg_hash + *GX1_MUL_PRIVATEKEY - if *N != msg_hash + *GX1_MUL_PRIVATEKEY { - bail!( - "r == GX2, but N != msg_hash + *GX1_MUL_PRIVATEKEY, N: {}, msg_hash: {}, *GX1_MUL_PRIVATEKEY: {}", - *N, msg_hash, *GX1_MUL_PRIVATEKEY - ); - } - return Ok(()); - } - Err(anyhow!( - "r != *GX1 && r != GX2, r: {}, *GX1: {}, GX2: {}", - sign.r, - *GX1, - *GX2 - )) -} diff --git a/primitives/src/taiko/mod.rs b/primitives/src/taiko/mod.rs deleted file mode 100644 index 5da19820d..000000000 --- a/primitives/src/taiko/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub mod anchor; -pub mod consts; -pub mod proposal; -pub mod protocol_instance; -pub mod utils; - -pub use anchor::*; -pub use consts::*; -pub use proposal::*; -pub use protocol_instance::*; -use thiserror_no_std::Error as ThisError; -pub use utils::*; - -#[derive(ThisError, Debug)] -#[error(transparent)] -struct AbiEncodeError(#[from] alloy_sol_types::Error); diff --git a/primitives/src/taiko/proposal.rs b/primitives/src/taiko/proposal.rs deleted file mode 100644 index ec01d1e0a..000000000 --- a/primitives/src/taiko/proposal.rs +++ /dev/null @@ -1,19 +0,0 @@ -use alloy_sol_types::{sol, SolCall}; -use anyhow::{anyhow, Context, Result}; - -use super::AbiEncodeError; - -sol! { - function proposeBlock( - bytes calldata params, - bytes calldata txList - ) - {} -} - -pub fn decode_propose_block_call_args(data: &[u8]) -> Result<proposeBlockCall> { - let propose_block_call = proposeBlockCall::abi_decode(data, false) - .map_err(|e| anyhow!(AbiEncodeError::from(e))) - .with_context(|| "failed to decode propose block call")?; - Ok(propose_block_call) -} diff --git a/primitives/src/taiko/protocol_instance.rs b/primitives/src/taiko/protocol_instance.rs deleted file mode 100644 index c09971fbc..000000000 --- a/primitives/src/taiko/protocol_instance.rs +++ /dev/null @@ -1,126 +0,0 @@ -use alloc::vec::Vec; - -use alloy_primitives::{Address, B256}; -use alloy_sol_types::{sol, SolValue}; -use serde::{Deserialize, Serialize}; - -use crate::keccak; - -sol! { - #[derive(Debug, Default, Deserialize, Serialize)] - struct EthDeposit { - address recipient; - uint96 amount; - uint64 id; - } - - #[derive(Debug, Default, Deserialize, Serialize)] - struct BlockMetadata { - bytes32 l1Hash; // slot 1 - bytes32 difficulty; // slot 2 - bytes32 blobHash; //or txListHash (if Blob not yet supported), // slot 3 - bytes32 extraData; // slot 4 - bytes32 depositsHash; // slot 5 - address coinbase; // L2 coinbase, // slot 6 - uint64 id; - uint32 gasLimit; - uint64 timestamp; // slot 7 - uint64 l1Height; - uint24 txListByteOffset; - uint24 txListByteSize; - uint16 minTier; - bool blobUsed; - bytes32 parentMetaHash; // slot 8 - } - - #[derive(Debug)] - struct Transition { - bytes32 parentHash; - bytes32 blockHash; - bytes32 signalRoot; - bytes32 graffiti; - } - - #[derive(Debug, Default, Clone, Deserialize, Serialize)] - event BlockProposed( - uint256 indexed blockId, - address indexed prover, - uint96 livenessBond, - BlockMetadata meta, - EthDeposit[] depositsProcessed - ); - - #[derive(Debug)] - struct TierProof { - uint16 tier; - bytes data; - } - - function proveBlock(uint64 blockId, bytes calldata input) {} -} - -#[derive(Debug)] -pub enum EvidenceType { - Sgx { - new_pubkey: Address, // the evidence signature public key - }, - PseZk, -} - -#[derive(Debug)] -pub struct ProtocolInstance { - pub transition: Transition, - pub block_metadata: BlockMetadata, - pub prover: Address, -} - -impl ProtocolInstance { - pub fn meta_hash(&self) -> B256 { - keccak::keccak(self.block_metadata.abi_encode()).into() - } - - // keccak256(abi.encode(tran, newInstance, prover, metaHash)) - pub fn hash(&self, evidence_type: EvidenceType) -> B256 { - match evidence_type { - EvidenceType::Sgx { new_pubkey } => keccak::keccak( - ( - self.transition.clone(), - new_pubkey, - self.prover, - self.meta_hash(), - ) - .abi_encode(), - ) - .into(), - EvidenceType::PseZk => todo!(), - } - } -} - -pub fn deposits_hash(deposits: &[EthDeposit]) -> B256 { - keccak::keccak(deposits.abi_encode()).into() -} - -#[cfg(test)] -mod tests { - use alloy_sol_types::SolCall; - - use super::*; - #[test] - fn test_prove_block_call() { - let input = "0x10d008bd000000000000000000000000000000000000000000000000000000000000299e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000034057a97bd6f6930af5ca9e7caf48e663588755b690e9de0f82486416960135939559b91a6700c8af9442fe68f4339066d1d7858263c6be97ebcaca787ef70b1a7f8be37f1ab1fe1209f525f7cbced8a86ed49d1813849896c99835628f8eea703b302e31382e302d64657600000000000000000000000000000000000000000000569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000e1e210594771824dad216568b91c9cb4ceed361c000000000000000000000000000000000000000000000000000000000000299e0000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000065a63e6400000000000000000000000000000000000000000000000000000000000b6785000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056220000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000012d5f89f4195325e38f76ac324b08c34ab0c5c9ec430fc00dd967aa44b0bd05c11a7c619d13210437142d7adae4025ee65581228d0a8ed7a0df022634b2f1feadb23b17eaa3a5d3a7cfede2fa7d1653ac512117963c9fbe5f2df6a9dd555041ff20f4e661443b23d0c39ddbbb2725002cd2f7d5edb84d1c1eed9d8c71ddeba300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000059000000000041035896fb7ccbed43b0fd70a82758535f3aa70e317bc173b815f18c416274d39cdd4918013cb12ccffc959700b8ae824b4a421d462c6fa19e28bdc64d6f753d978e0e76c33ce84aadfa19b68163c99dc62a631b00000000000000"; - - let input_data = hex::decode(&input[2..]).unwrap(); - let proveBlockCall { blockId, input } = - proveBlockCall::abi_decode(&input_data, false).unwrap(); - // println!("blockId: {}", blockId); - let (meta, trans, proof) = - <(BlockMetadata, Transition, TierProof)>::abi_decode_params(&input, false).unwrap(); - // println!("meta: {:?}", meta); - let meta_hash: B256 = keccak::keccak(meta.abi_encode()).into(); - // println!("meta_hash: {:?}", meta_hash); - // println!("trans: {:?}", trans); - // println!("proof: {:?}", proof.tier); - // println!("proof: {:?}", hex::encode(proof.data)); - } -} diff --git a/primitives/src/taiko/utils.rs b/primitives/src/taiko/utils.rs deleted file mode 100644 index 32bf28a2a..000000000 --- a/primitives/src/taiko/utils.rs +++ /dev/null @@ -1,25 +0,0 @@ -use core::cmp::min; - -pub fn string_to_bytes32(input: &[u8]) -> [u8; 32] { - let mut bytes = [0u8; 32]; - let len = min(input.len(), 32); - bytes[..len].copy_from_slice(&input[..len]); - bytes -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn string_to_bytes32_test() { - let input = ""; - let byte = string_to_bytes32(input.as_bytes()); - assert_eq!( - byte, - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 - ] - ); - } -} diff --git a/primitives/src/transactions/ethereum.rs b/primitives/src/transactions/ethereum.rs index f4b3982e9..c36654b1a 100644 --- a/primitives/src/transactions/ethereum.rs +++ b/primitives/src/transactions/ethereum.rs @@ -33,7 +33,6 @@ use alloy_primitives::{Address, Bytes, ChainId, TxNumber, B256, U256}; use alloy_rlp::{Encodable, EMPTY_STRING_CODE}; use alloy_rlp_derive::RlpEncodable; use anyhow::{anyhow, Context}; -use bytes::BufMut; use k256::{ ecdsa::{RecoveryId, Signature as K256Signature, VerifyingKey as K256VerifyingKey}, elliptic_curve::sec1::ToEncodedPoint, @@ -42,12 +41,8 @@ use k256::{ use serde::{Deserialize, Serialize}; use thiserror_no_std::Error as ThisError; -use crate::{ - access_list::AccessList, - keccak::keccak, - signature::TxSignature, - transactions::{Transaction, TxEssence}, -}; +use super::signature::TxSignature; +use crate::{access_list::AccessList, keccak::keccak, transactions::TxEssence}; /// Represents a legacy Ethereum transaction as detailed in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). /// @@ -67,8 +62,8 @@ pub struct TxEssenceLegacy { pub gas_price: U256, /// The maximum amount of gas allocated for the transaction's execution. pub gas_limit: U256, - /// The 160-bit address of the intended recipient for a message call. For contract - /// creation transactions, this is null. + /// The 160-bit address of the intended recipient for a message call or + /// [TransactionKind::Create] for contract creation. pub to: TransactionKind, /// The amount, in Wei, to be transferred to the recipient of the message call. pub value: U256, @@ -93,11 +88,12 @@ impl TxEssenceLegacy { /// Encodes the transaction essence into the provided `out` buffer for the purpose of /// signing. /// - /// The method follows the RLP encoding scheme. If a `chain_id` is present, - /// the encoding adheres to the specifications set out in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). + /// According to EIP-155, if `chain_id` is present, `(chain_id, 0, 0)` must be + /// appended to the regular RLP encoding when computing the hash of a transaction for + /// the purposes of signing. pub fn signing_encode(&self, out: &mut dyn alloy_rlp::BufMut) { let mut payload_length = self.payload_length(); - // Append chain ID according to EIP-155 if present + // append chain ID according to EIP-155 if present if let Some(chain_id) = self.chain_id { payload_length += chain_id.length() + 1 + 1; } @@ -126,11 +122,11 @@ impl TxEssenceLegacy { /// including any additional bytes required for the encoding format. pub fn signing_length(&self) -> usize { let mut payload_length = self.payload_length(); - // Append chain ID according to EIP-155 if present + // append chain ID according to EIP-155 if present if let Some(chain_id) = self.chain_id { payload_length += chain_id.length() + 1 + 1; } - alloy_rlp::length_of_length(payload_length) + payload_length + payload_length + alloy_rlp::length_of_length(payload_length) } } @@ -141,6 +137,7 @@ impl Encodable for TxEssenceLegacy { /// /// This method follows the RLP encoding scheme, but intentionally omits the /// `chain_id` to ensure compatibility with legacy transactions. + #[inline] fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { alloy_rlp::Header { list: true, @@ -159,9 +156,10 @@ impl Encodable for TxEssenceLegacy { /// /// This method calculates the total length of the transaction when it is RLP-encoded, /// excluding the `chain_id`. + #[inline] fn length(&self) -> usize { let payload_length = self.payload_length(); - alloy_rlp::length_of_length(payload_length) + payload_length + payload_length + alloy_rlp::length_of_length(payload_length) } } @@ -300,9 +298,8 @@ impl Encodable for TransactionKind { /// Represents the core essence of an Ethereum transaction, specifically the portion that /// gets signed. /// -/// The `TxEssence` enum provides a way to handle different types of Ethereum -/// transactions, from legacy transactions to more recent types introduced by various -/// Ethereum Improvement Proposals (EIPs). +/// The [EthereumTxEssence] enum provides a way to handle different types of Ethereum +/// transactions. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum EthereumTxEssence { /// Represents a legacy Ethereum transaction, which follows the original transaction @@ -318,14 +315,13 @@ pub enum EthereumTxEssence { Eip1559(TxEssenceEip1559), } -// Implement the Encodable trait for the TxEssence enum. -// Ensures that each variant of the `TxEssence` enum can be RLP-encoded. impl Encodable for EthereumTxEssence { /// Encodes the [EthereumTxEssence] enum variant into the provided `out` buffer. /// /// Depending on the variant of the [EthereumTxEssence] enum, this method will /// delegate the encoding process to the appropriate transaction type's encoding /// method. + #[inline] fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { match self { EthereumTxEssence::Legacy(tx) => tx.encode(out), @@ -339,6 +335,7 @@ impl Encodable for EthereumTxEssence { /// Depending on the variant of the [EthereumTxEssence] enum, this method will /// delegate the length computation to the appropriate transaction type's length /// method. + #[inline] fn length(&self) -> usize { match self { EthereumTxEssence::Legacy(tx) => tx.length(), @@ -353,7 +350,7 @@ impl EthereumTxEssence { /// /// This method calculates the Keccak hash of the data that needs to be signed /// for the transaction, ensuring the integrity and authenticity of the transaction. - pub(crate) fn signing_hash(&self) -> B256 { + pub fn signing_hash(&self) -> B256 { keccak(self.signing_data()).into() } @@ -384,11 +381,10 @@ impl EthereumTxEssence { } } - /// Determines whether the y-coordinate of the ECDSA signature's associated public key - /// is odd. + /// Returns the parity of the y-value of the curve point for which `signature.r` is + /// the x-value. This is encoded in the `v` field of the signature. /// - /// This information is derived from the `v` component of the signature and is used - /// during public key recovery. + /// It returns `None` if the parity cannot be determined. fn is_y_odd(&self, signature: &TxSignature) -> Option<bool> { match self { EthereumTxEssence::Legacy(TxEssenceLegacy { chain_id: None, .. }) => { @@ -404,13 +400,7 @@ impl EthereumTxEssence { } /// Converts a given value into a boolean based on its parity. -/// -/// Returns: -/// - `Some(true)` if the value is 1. -/// - `Some(false)` if the value is 0. -/// - `None` otherwise. -#[inline] -pub fn checked_bool(v: u64) -> Option<bool> { +fn checked_bool(v: u64) -> Option<bool> { match v { 0 => Some(false), 1 => Some(true), @@ -419,12 +409,7 @@ pub fn checked_bool(v: u64) -> Option<bool> { } impl TxEssence for EthereumTxEssence { - /// Determines the type of the transaction based on its essence. - /// - /// Returns a byte representing the transaction type: - /// - `0x00` for Legacy transactions. - /// - `0x01` for EIP-2930 transactions. - /// - `0x02` for EIP-1559 transactions. + /// Returns the EIP-2718 transaction type or `0x00` for Legacy transactions. fn tx_type(&self) -> u8 { match self { EthereumTxEssence::Legacy(_) => 0x00, @@ -432,10 +417,7 @@ impl TxEssence for EthereumTxEssence { EthereumTxEssence::Eip1559(_) => 0x02, } } - /// Retrieves the gas limit set for the transaction. - /// - /// The gas limit represents the maximum amount of gas units that the transaction - /// is allowed to consume. It ensures that transactions don't run indefinitely. + /// Returns the gas limit set for the transaction. fn gas_limit(&self) -> U256 { match self { EthereumTxEssence::Legacy(tx) => tx.gas_limit, @@ -443,10 +425,7 @@ impl TxEssence for EthereumTxEssence { EthereumTxEssence::Eip1559(tx) => tx.gas_limit, } } - /// Retrieves the recipient address of the transaction, if available. - /// - /// For contract creation transactions, this method returns `None` as there's no - /// recipient address. + /// Returns the recipient address of the transaction, if available. fn to(&self) -> Option<Address> { match self { EthereumTxEssence::Legacy(tx) => tx.to.into(), @@ -455,10 +434,6 @@ impl TxEssence for EthereumTxEssence { } } /// Recovers the Ethereum address of the sender from the transaction's signature. - /// - /// This method uses the ECDSA recovery mechanism to derive the sender's public key - /// and subsequently their Ethereum address. If the recovery is unsuccessful, an - /// error is returned. fn recover_from(&self, signature: &TxSignature) -> anyhow::Result<Address> { let is_y_odd = self.is_y_odd(signature).context("v invalid")?; let signature = @@ -482,11 +457,7 @@ impl TxEssence for EthereumTxEssence { Ok(Address::from_slice(&hash[12..])) } - /// Computes the length of the RLP-encoded payload in bytes for the transaction - /// essence. - /// - /// This method calculates the length of the transaction data when it is RLP-encoded, - /// which is used for serialization and deserialization in the Ethereum network. + /// Returns the length of the RLP-encoding payload in bytes. fn payload_length(&self) -> usize { match self { EthereumTxEssence::Legacy(tx) => tx.payload_length(), @@ -494,21 +465,13 @@ impl TxEssence for EthereumTxEssence { EthereumTxEssence::Eip1559(tx) => tx._alloy_rlp_payload_length(), } } - - fn encode_with_signature(&self, signature: &TxSignature, out: &mut dyn BufMut) { - // join the essence lists and the signature list into one - rlp_join_lists(self, signature, out); - } - - #[inline] - fn length(transaction: &Transaction<Self>) -> usize { - let payload_length = - transaction.essence.payload_length() + transaction.signature.payload_length(); - let mut length = payload_length + alloy_rlp::length_of_length(payload_length); - if transaction.essence.tx_type() != 0 { - length += 1; + /// Returns a reference to the transaction's call data + fn data(&self) -> &Bytes { + match self { + EthereumTxEssence::Legacy(tx) => &tx.data, + EthereumTxEssence::Eip2930(tx) => &tx.data, + EthereumTxEssence::Eip1559(tx) => &tx.data, } - length } } @@ -516,43 +479,205 @@ impl TxEssence for EthereumTxEssence { #[error(transparent)] struct EcdsaError(#[from] k256::ecdsa::Error); -/// Joins two RLP-encoded lists into a single RLP-encoded list. -/// -/// This function takes two RLP-encoded lists, decodes their headers to ensure they are -/// valid lists, and then combines their payloads into a single RLP-encoded list. The -/// resulting list is written to the provided `out` buffer. -/// -/// # Arguments -/// -/// * `a` - The first RLP-encoded list to be joined. -/// * `b` - The second RLP-encoded list to be joined. -/// * `out` - The buffer where the resulting RLP-encoded list will be written. -/// -/// # Panics -/// -/// This function will panic if either `a` or `b` are not valid RLP-encoded lists. -fn rlp_join_lists(a: impl Encodable, b: impl Encodable, out: &mut dyn alloy_rlp::BufMut) { - let a_buf = alloy_rlp::encode(a); - let header = alloy_rlp::Header::decode(&mut &a_buf[..]).unwrap(); - if !header.list { - panic!("`a` not a list"); +#[cfg(test)] +mod tests { + use alloy_primitives::{address, b256}; + use serde_json::json; + + use super::*; + use crate::transactions::EthereumTransaction; + + #[test] + fn legacy() { + // Tx: 0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060 + let tx = json!({ + "Legacy": { + "nonce": 0, + "gas_price": "0x2d79883d2000", + "gas_limit": "0x5208", + "to": { "Call": "0x5df9b87991262f6ba471f09758cde1c0fc1de734" }, + "value": "0x7a69", + "data": "0x" + } + }); + let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); + + let signature: TxSignature = serde_json::from_value(json!({ + "v": 28, + "r": "0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0", + "s": "0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a" + })) + .unwrap(); + let transaction = EthereumTransaction { essence, signature }; + + // verify that bincode serialization works + let _: EthereumTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + assert_eq!( + transaction.hash(), + b256!("5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("a1e4380a3b1f749673e270229993ee55f35663b4") + ); + } + + #[test] + fn eip155() { + // Tx: 0x4540eb9c46b1654c26353ac3c65e56451f711926982ce1b02f15c50e7459caf7 + let tx = json!({ + "Legacy": { + "nonce": 537760, + "gas_price": "0x03c49bfa04", + "gas_limit": "0x019a28", + "to": { "Call": "0xf0ee707731d1be239f9f482e1b2ea5384c0c426f" }, + "value": "0x06df842eaa9fb800", + "data": "0x", + "chain_id": 1 + } + }); + let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); + + let signature: TxSignature = serde_json::from_value(json!({ + "v": 38, + "r": "0xcadd790a37b78e5613c8cf44dc3002e3d7f06a5325d045963c708efe3f9fdf7a", + "s": "0x1f63adb9a2d5e020c6aa0ff64695e25d7d9a780ed8471abe716d2dc0bf7d4259" + })) + .unwrap(); + let transaction = EthereumTransaction { essence, signature }; + + // verify that bincode serialization works + let _: EthereumTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + assert_eq!( + transaction.hash(), + b256!("4540eb9c46b1654c26353ac3c65e56451f711926982ce1b02f15c50e7459caf7") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("974caa59e49682cda0ad2bbe82983419a2ecc400") + ); } - let a_head_length = header.length(); - let a_payload_length = a_buf.len() - a_head_length; - let b_buf = alloy_rlp::encode(b); - let header = alloy_rlp::Header::decode(&mut &b_buf[..]).unwrap(); - if !header.list { - panic!("`b` not a list"); + #[test] + fn eip2930() { + // Tx: 0xbe4ef1a2244e99b1ef518aec10763b61360be22e3b649dcdf804103719b1faef + let tx = json!({ + "Eip2930": { + "chain_id": 1, + "nonce": 93847, + "gas_price": "0xf46a5a9d8", + "gas_limit": "0x21670", + "to": { "Call": "0xc11ce44147c9f6149fbe54adb0588523c38718d7" }, + "value": "0x10d1471", + "data": "0x050000000002b8809aef26206090eafd7d5688615d48197d1c5ce09be6c30a33be4c861dee44d13f6dd33c2e8c5cad7e2725f88a8f0000000002d67ca5eb0e5fb6", + "access_list": [ + { + "address": "0xd6e64961ba13ba42858ad8a74ed9a9b051a4957d", + "storage_keys": [ + "0x0000000000000000000000000000000000000000000000000000000000000008", + "0x0b4b38935f88a7bddbe6be76893de2a04640a55799d6160729a82349aff1ffae", + "0xc59ee2ee2ba599569b2b1f06989dadbec5ee157c8facfe64f36a3e33c2b9d1bf" + ] + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "storage_keys": [ + "0x7635825e4f8dfeb20367f8742c8aac958a66caa001d982b3a864dcc84167be80", + "0x42555691810bdf8f236c31de88d2cc9407a8ff86cd230ba3b7029254168df92a", + "0x29ece5a5f4f3e7751868475502ab752b5f5fa09010960779bf7204deb72f5dde" + ] + }, + { + "address": "0x4c861dee44d13f6dd33c2e8c5cad7e2725f88a8f", + "storage_keys": [ + "0x000000000000000000000000000000000000000000000000000000000000000c", + "0x0000000000000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000006", + "0x0000000000000000000000000000000000000000000000000000000000000007" + ] + }, + { + "address": "0x90eafd7d5688615d48197d1c5ce09be6c30a33be", + "storage_keys": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x9c04773acff4c5c42718bd0120c72761f458e43068a3961eb935577d1ed4effb", + "0x0000000000000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000004" + ] + } + ] + } + }); + let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); + + let signature: TxSignature = serde_json::from_value(json!({ + "v": 1, + "r": "0xf86aa2dfde99b0d6a41741e96cfcdee0c6271febd63be4056911db19ae347e66", + "s": "0x601deefbc4835cb15aa1af84af6436fc692dea3428d53e7ff3d34a314cefe7fc" + })) + .unwrap(); + let transaction = EthereumTransaction { essence, signature }; + + // verify that bincode serialization works + let _: EthereumTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + assert_eq!( + transaction.hash(), + b256!("be4ef1a2244e99b1ef518aec10763b61360be22e3b649dcdf804103719b1faef") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("79b7a69d90c82e014bf0315e164208119b510fa0") + ); } - let b_head_length = header.length(); - let b_payload_length = b_buf.len() - b_head_length; - alloy_rlp::Header { - list: true, - payload_length: a_payload_length + b_payload_length, + #[test] + fn eip1559() { + // Tx: 0x2bcdc03343ca9c050f8dfd3c87f32db718c762ae889f56762d8d8bdb7c5d69ff + let tx = json!({ + "Eip1559": { + "chain_id": 1, + "nonce": 32, + "max_priority_fee_per_gas": "0x3b9aca00", + "max_fee_per_gas": "0x89d5f3200", + "gas_limit": "0x5b04", + "to": { "Call": "0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43" }, + "value": "0x1dd1f234f68cde2", + "data": "0x", + "access_list": [] + } + }); + let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); + + let signature: TxSignature = serde_json::from_value(json!({ + "v": 0, + "r": "0x2bdf47562da5f2a09f09cce70aed35ec9ac62f5377512b6a04cc427e0fda1f4d", + "s": "0x28f9311b515a5f17aa3ad5ea8bafaecfb0958801f01ca11fd593097b5087121b" + })) + .unwrap(); + let transaction = EthereumTransaction { essence, signature }; + + // verify that bincode serialization works + let _: EthereumTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + assert_eq!( + transaction.hash(), + b256!("2bcdc03343ca9c050f8dfd3c87f32db718c762ae889f56762d8d8bdb7c5d69ff") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("4b9f4114d50e7907bff87728a060ce8d53bf4cf7") + ); } - .encode(out); - out.put_slice(&a_buf[a_head_length..]); // skip the header - out.put_slice(&b_buf[b_head_length..]); // skip the header } diff --git a/primitives/src/transactions/mod.rs b/primitives/src/transactions/mod.rs index 0b05aacb7..04f4baf31 100644 --- a/primitives/src/transactions/mod.rs +++ b/primitives/src/transactions/mod.rs @@ -13,50 +13,49 @@ // limitations under the License. use core::{clone::Clone, option::Option}; -use alloy_primitives::{Address, TxHash}; +use alloy_primitives::{Address, Bytes, TxHash}; use alloy_rlp::Encodable; use serde::{Deserialize, Serialize}; -use crate::{ - keccak::keccak, signature::TxSignature, transactions::ethereum::EthereumTxEssence, U256, +use self::{ + optimism::{OptimismTxEssence, OPTIMISM_DEPOSITED_TX_TYPE}, + signature::TxSignature, }; +use crate::{keccak::keccak, transactions::ethereum::EthereumTxEssence, U256}; pub mod ethereum; pub mod optimism; +pub mod signature; pub type EthereumTransaction = Transaction<EthereumTxEssence>; +pub type OptimismTransaction = Transaction<OptimismTxEssence>; -/// Represents a complete Ethereum transaction, encompassing its core essence and the -/// associated signature. +/// Represents a complete transaction, encompassing its core essence and the associated +/// signature. /// /// The `Transaction` struct encapsulates both the core details of the transaction (the /// essence) and its cryptographic signature. The signature ensures the authenticity and /// integrity of the transaction, confirming it was issued by the rightful sender. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Transaction<E: TxEssence> { - /// The core details of the transaction, which include its type (e.g., legacy, - /// EIP-2930, EIP-1559) and associated data (e.g., recipient address, value, gas - /// details). + /// The core details of the transaction, which includes the data that is signed. pub essence: E, /// The cryptographic signature associated with the transaction, generated by signing /// the transaction essence. pub signature: TxSignature, } +/// Represents the core details of a [Transaction], specifically the portion that gets +/// signed. pub trait TxEssence: Encodable + Clone { - /// Determines the type of the transaction based on its essence. - /// - /// Returns a byte representing the transaction type: - /// - `0x00` for Legacy transactions. - /// - `0x01` for EIP-2930 transactions. - /// - `0x02` for EIP-1559 transactions. + /// Returns the EIP-2718 transaction type or `0x00` for Legacy transactions. fn tx_type(&self) -> u8; - /// Retrieves the gas limit set for the transaction. + /// Returns the gas limit set for the transaction. /// /// The gas limit represents the maximum amount of gas units that the transaction /// is allowed to consume. It ensures that transactions don't run indefinitely. fn gas_limit(&self) -> U256; - /// Retrieves the recipient address of the transaction, if available. + /// Returns the recipient address of the transaction, if available. /// /// For contract creation transactions, this method returns `None` as there's no /// recipient address. @@ -67,26 +66,16 @@ pub trait TxEssence: Encodable + Clone { /// and subsequently their Ethereum address. If the recovery is unsuccessful, an /// error is returned. fn recover_from(&self, signature: &TxSignature) -> anyhow::Result<Address>; - /// Computes the length of the RLP-encoded payload in bytes. + /// Returns the length of the RLP-encoding payload in bytes. /// /// This method calculates the combined length of all the individual fields /// of the transaction when they are RLP-encoded. fn payload_length(&self) -> usize; - /// RLP encodes the transaction essence and signature into the provided `out` buffer. - fn encode_with_signature(&self, signature: &TxSignature, out: &mut dyn alloy_rlp::BufMut); - /// Computes the length of an encompassing RLP-encoded [Transaction] struct in bytes. - /// - /// The computed length includes the lengths of the encoded transaction essence and - /// signature. If the transaction type (as per EIP-2718) is not zero, an - /// additional byte is added to the length. - fn length(transaction: &Transaction<Self>) -> usize; + /// Returns a reference to the transaction's call data + fn data(&self) -> &Bytes; } -/// Provides RLP encoding functionality for the [Transaction] struct. -/// -/// This implementation ensures that the entire transaction, including its essence and -/// signature, can be RLP-encoded. The encoding process also considers the EIP-2718 -/// transaction type. +/// Provides RLP encoding functionality for [Transaction]. impl<E: TxEssence> Encodable for Transaction<E> { /// Encodes the [Transaction] struct into the provided `out` buffer. /// @@ -96,13 +85,20 @@ impl<E: TxEssence> Encodable for Transaction<E> { /// reusing as much of the generated RLP code as possible. #[inline] fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { - // prepend the EIP-2718 transaction type - match self.essence.tx_type() { - 0 => {} - tx_type => out.put_u8(tx_type), + let tx_type = self.essence.tx_type(); + // prepend the EIP-2718 transaction type for non-legacy transactions + if tx_type != 0 { + out.put_u8(tx_type); } - // encode according to essence type - self.essence.encode_with_signature(&self.signature, out); + if tx_type == OPTIMISM_DEPOSITED_TX_TYPE { + // optimism deposited transactions have no signature + self.essence.encode(out); + return; + } + + // join the essence lists and the signature list into one + // this allows to reuse as much of the generated RLP code as possible + rlp_join_lists(&self.essence, &self.signature, out); } /// Computes the length of the RLP-encoded [Transaction] struct in bytes. @@ -112,7 +108,22 @@ impl<E: TxEssence> Encodable for Transaction<E> { /// additional byte is added to the length. #[inline] fn length(&self) -> usize { - <E as TxEssence>::length(self) + let tx_type = self.essence.tx_type(); + let payload_length = self.essence.payload_length() + + if tx_type == OPTIMISM_DEPOSITED_TX_TYPE { + // optimism deposited transactions have no signature + 0 + } else { + self.signature.payload_length() + }; + + let length = payload_length + alloy_rlp::length_of_length(payload_length); + // add the EIP-2718 transaction type for non-legacy transactions + if tx_type != 0 { + length + 1 + } else { + length + } } } @@ -120,6 +131,7 @@ impl<E: TxEssence> Transaction<E> { /// Calculates the Keccak hash of the RLP-encoded transaction. /// /// This hash uniquely identifies the transaction on the Ethereum network. + #[inline] pub fn hash(&self) -> TxHash { keccak(alloy_rlp::encode(self)).into() } @@ -129,258 +141,83 @@ impl<E: TxEssence> Transaction<E> { /// This method uses the ECDSA recovery mechanism to derive the sender's public key /// and subsequently their Ethereum address. If the recovery is unsuccessful, an /// error is returned. + #[inline] pub fn recover_from(&self) -> anyhow::Result<Address> { self.essence.recover_from(&self.signature) } } +/// Joins two RLP-encoded lists into a single RLP-encoded list. +/// +/// This function takes two RLP-encoded lists, decodes their headers to ensure they are +/// valid lists, and then combines their payloads into a single RLP-encoded list. The +/// resulting list is written to the provided `out` buffer. +/// +/// # Panics +/// +/// This function will panic if either `a` or `b` are not valid RLP-encoded lists. +fn rlp_join_lists(a: impl Encodable, b: impl Encodable, out: &mut dyn alloy_rlp::BufMut) { + let a_buf = alloy_rlp::encode(a); + let header = alloy_rlp::Header::decode(&mut &a_buf[..]).unwrap(); + if !header.list { + panic!("`a` not a list"); + } + let a_head_length = header.length(); + let a_payload_length = a_buf.len() - a_head_length; + + let b_buf = alloy_rlp::encode(b); + let header = alloy_rlp::Header::decode(&mut &b_buf[..]).unwrap(); + if !header.list { + panic!("`b` not a list"); + } + let b_head_length = header.length(); + let b_payload_length = b_buf.len() - b_head_length; + + alloy_rlp::Header { + list: true, + payload_length: a_payload_length + b_payload_length, + } + .encode(out); + out.put_slice(&a_buf[a_head_length..]); // skip the header + out.put_slice(&b_buf[b_head_length..]); // skip the header +} + #[cfg(test)] mod tests { use serde_json::json; use super::*; + use crate::transactions::EthereumTransaction; #[test] - fn legacy() { - // Tx: 0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060 - let tx = json!({ - "Legacy": { - "nonce": 0, - "gas_price": "0x2d79883d2000", - "gas_limit": "0x5208", - "to": { "Call": "0x5df9b87991262f6ba471f09758cde1c0fc1de734" }, - "value": "0x7a69", - "data": "0x" - } - }); - let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); - - let signature: TxSignature = serde_json::from_value(json!({ - "v": 28, - "r": "0x88ff6cf0fefd94db46111149ae4bfc179e9b94721fffd821d38d16464b3f71d0", - "s": "0x45e0aff800961cfce805daef7016b9b675c137a6a41a548f7b60a3484c06a33a" - })) - .unwrap(); - let transaction = EthereumTransaction { essence, signature }; - - // verify that bincode serialization works - let _: EthereumTransaction = - bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); - - assert_eq!( - "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060", - transaction.hash().to_string() - ); - let recovered = transaction.recover_from().unwrap(); - assert_eq!( - "0xa1e4380a3b1f749673e270229993ee55f35663b4".to_lowercase(), - recovered.to_string().to_lowercase() - ); - } - - #[test] - fn eip155() { - // Tx: 0x4540eb9c46b1654c26353ac3c65e56451f711926982ce1b02f15c50e7459caf7 + fn rlp_length() { let tx = json!({ - "Legacy": { - "nonce": 537760, - "gas_price": "0x03c49bfa04", - "gas_limit": "0x019a28", - "to": { "Call": "0xf0ee707731d1be239f9f482e1b2ea5384c0c426f" }, - "value": "0x06df842eaa9fb800", - "data": "0x", - "chain_id": 1 - } - }); - let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); - - let signature: TxSignature = serde_json::from_value(json!({ + "essence": { + "Legacy": { + "nonce": 537760, + "gas_price": "0x03c49bfa04", + "gas_limit": "0x019a28", + "to": { "Call": "0xf0ee707731d1be239f9f482e1b2ea5384c0c426f" }, + "value": "0x06df842eaa9fb800", + "data": "0x", + "chain_id": 1 + } + }, + "signature": { "v": 38, "r": "0xcadd790a37b78e5613c8cf44dc3002e3d7f06a5325d045963c708efe3f9fdf7a", "s": "0x1f63adb9a2d5e020c6aa0ff64695e25d7d9a780ed8471abe716d2dc0bf7d4259" - })) - .unwrap(); - let transaction = EthereumTransaction { essence, signature }; - - // verify that bincode serialization works - let _: EthereumTransaction = - bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); - - assert_eq!( - "0x4540eb9c46b1654c26353ac3c65e56451f711926982ce1b02f15c50e7459caf7", - transaction.hash().to_string() - ); - let recovered = transaction.recover_from().unwrap(); - assert_eq!( - "0x974caa59e49682cda0ad2bbe82983419a2ecc400".to_lowercase(), - recovered.to_string().to_lowercase() - ); - } - - #[test] - fn eip2930() { - // Tx: 0xbe4ef1a2244e99b1ef518aec10763b61360be22e3b649dcdf804103719b1faef - let tx = json!({ - "Eip2930": { - "chain_id": 1, - "nonce": 93847, - "gas_price": "0xf46a5a9d8", - "gas_limit": "0x21670", - "to": { "Call": "0xc11ce44147c9f6149fbe54adb0588523c38718d7" }, - "value": "0x10d1471", - "data": "0x050000000002b8809aef26206090eafd7d5688615d48197d1c5ce09be6c30a33be4c861dee44d13f6dd33c2e8c5cad7e2725f88a8f0000000002d67ca5eb0e5fb6", - "access_list": [ - { - "address": "0xd6e64961ba13ba42858ad8a74ed9a9b051a4957d", - "storage_keys": [ - "0x0000000000000000000000000000000000000000000000000000000000000008", - "0x0b4b38935f88a7bddbe6be76893de2a04640a55799d6160729a82349aff1ffae", - "0xc59ee2ee2ba599569b2b1f06989dadbec5ee157c8facfe64f36a3e33c2b9d1bf" - ] - }, - { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - "storage_keys": [ - "0x7635825e4f8dfeb20367f8742c8aac958a66caa001d982b3a864dcc84167be80", - "0x42555691810bdf8f236c31de88d2cc9407a8ff86cd230ba3b7029254168df92a", - "0x29ece5a5f4f3e7751868475502ab752b5f5fa09010960779bf7204deb72f5dde" - ] - }, - { - "address": "0x4c861dee44d13f6dd33c2e8c5cad7e2725f88a8f", - "storage_keys": [ - "0x000000000000000000000000000000000000000000000000000000000000000c", - "0x0000000000000000000000000000000000000000000000000000000000000008", - "0x0000000000000000000000000000000000000000000000000000000000000006", - "0x0000000000000000000000000000000000000000000000000000000000000007" - ] - }, - { - "address": "0x90eafd7d5688615d48197d1c5ce09be6c30a33be", - "storage_keys": [ - "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x9c04773acff4c5c42718bd0120c72761f458e43068a3961eb935577d1ed4effb", - "0x0000000000000000000000000000000000000000000000000000000000000008", - "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0000000000000000000000000000000000000000000000000000000000000004" - ] - } - ] } }); - let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); + let transaction: EthereumTransaction = serde_json::from_value(tx).unwrap(); - let signature: TxSignature = serde_json::from_value(json!({ - "v": 1, - "r": "0xf86aa2dfde99b0d6a41741e96cfcdee0c6271febd63be4056911db19ae347e66", - "s": "0x601deefbc4835cb15aa1af84af6436fc692dea3428d53e7ff3d34a314cefe7fc" - })) - .unwrap(); - let transaction = EthereumTransaction { essence, signature }; + let encoded = alloy_rlp::encode(&transaction.essence); + assert_eq!(encoded.len(), transaction.essence.length()); - // verify that bincode serialization works - let _: EthereumTransaction = - bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); - - assert_eq!( - "0xbe4ef1a2244e99b1ef518aec10763b61360be22e3b649dcdf804103719b1faef", - transaction.hash().to_string() - ); - let recovered = transaction.recover_from().unwrap(); - assert_eq!( - "0x79b7a69d90c82e014bf0315e164208119b510fa0".to_lowercase(), - recovered.to_string().to_lowercase() - ); - } - - #[test] - fn eip1559() { - // Tx: 0x2bcdc03343ca9c050f8dfd3c87f32db718c762ae889f56762d8d8bdb7c5d69ff - let tx = json!({ - "Eip1559": { - "chain_id": 1, - "nonce": 32, - "max_priority_fee_per_gas": "0x3b9aca00", - "max_fee_per_gas": "0x89d5f3200", - "gas_limit": "0x5b04", - "to": { "Call": "0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43" }, - "value": "0x1dd1f234f68cde2", - "data": "0x", - "access_list": [] - } - }); - let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); - - let signature: TxSignature = serde_json::from_value(json!({ - "v": 0, - "r": "0x2bdf47562da5f2a09f09cce70aed35ec9ac62f5377512b6a04cc427e0fda1f4d", - "s": "0x28f9311b515a5f17aa3ad5ea8bafaecfb0958801f01ca11fd593097b5087121b" - })) - .unwrap(); - let transaction = EthereumTransaction { essence, signature }; - - // verify that bincode serialization works - let _: EthereumTransaction = - bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); - - assert_eq!( - "0x2bcdc03343ca9c050f8dfd3c87f32db718c762ae889f56762d8d8bdb7c5d69ff", - transaction.hash().to_string() - ); - let recovered = transaction.recover_from().unwrap(); - assert_eq!( - "0x4b9f4114d50e7907bff87728a060ce8d53bf4cf7".to_lowercase(), - recovered.to_string().to_lowercase() - ); - } - - #[test] - fn rlp() { - // Tx: 0x275631a3549307b2e8c93b18dfcc0fe8aedf0276bb650c28eaa0a8a011d18867 - let tx = json!({ - "Eip1559": { - "chain_id": 1, - "nonce": 267, - "max_priority_fee_per_gas": "0x05f5e100", - "max_fee_per_gas": "0x0cb2bf61c2", - "gas_limit": "0x0278be", - "to": { "Call": "0x00005ea00ac477b1030ce78506496e8c2de24bf5" }, - "value": "0x01351609ff758000", - "data": "0x161ac21f0000000000000000000000007de6f03b8b50b835f706e51a40b3224465802ddc0000000000000000000000000000a26b00c1f0df003000390027140000faa71900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003360c6ebe", - "access_list": [] - } - }); - let essence: EthereumTxEssence = serde_json::from_value(tx).unwrap(); - - let encoded = alloy_rlp::encode(&essence); - assert_eq!(encoded.len(), essence.length()); - assert_eq!( - essence.payload_length() + alloy_rlp::length_of_length(essence.payload_length()), - encoded.len() - ); - - let signature: TxSignature = serde_json::from_value(json!({ - "v": 0, - "r": "0x5fc1441d3469a16715c862240794ef76656c284930e08820b79fd703a98b380a", - "s": "0x37488b0ceef613dc68116ed44b8e63769dbcf039222e25acc1cb9e85e777ade2" - })) - .unwrap(); - - let encoded = alloy_rlp::encode(&signature); - assert_eq!(encoded.len(), signature.length()); - assert_eq!( - signature.payload_length() + alloy_rlp::length_of_length(signature.payload_length()), - encoded.len() - ); - - let transaction = EthereumTransaction { essence, signature }; + let encoded = alloy_rlp::encode(&transaction.signature); + assert_eq!(encoded.len(), transaction.signature.length()); let encoded = alloy_rlp::encode(&transaction); assert_eq!(encoded.len(), transaction.length()); - - assert_eq!( - "0x275631a3549307b2e8c93b18dfcc0fe8aedf0276bb650c28eaa0a8a011d18867", - transaction.hash().to_string() - ); } } diff --git a/primitives/src/transactions/optimism.rs b/primitives/src/transactions/optimism.rs index cd01c46a1..8e2a7de3e 100644 --- a/primitives/src/transactions/optimism.rs +++ b/primitives/src/transactions/optimism.rs @@ -19,59 +19,54 @@ use alloy_rlp_derive::RlpEncodable; use bytes::BufMut; use serde::{Deserialize, Serialize}; -use crate::{ - signature::TxSignature, - transactions::{ - ethereum::{EthereumTxEssence, TransactionKind}, - Transaction, TxEssence, - }, +use super::signature::TxSignature; +use crate::transactions::{ + ethereum::{EthereumTxEssence, TransactionKind}, + TxEssence, }; +/// The EIP-2718 transaction type for an Optimism deposited transaction. +pub const OPTIMISM_DEPOSITED_TX_TYPE: u8 = 0x7E; + +/// Represents an Optimism depositing transaction that is a L2 transaction that was +/// derived from L1 and included in a L2 block. #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, RlpEncodable)] pub struct TxEssenceOptimismDeposited { - /// The source hash which uniquely identifies the origin of the deposit + /// The source hash which uniquely identifies the origin of the deposit. pub source_hash: B256, /// The 160-bit address of the sender. pub from: Address, - /// The 160-bit address of the message call's recipient or, for a contract creation - /// transaction, ∅. + /// The 160-bit address of the intended recipient for a message call or + /// [TransactionKind::Create] for contract creation. pub to: TransactionKind, - /// The ETH value to mint on L2 + /// The ETH value to mint on L2. pub mint: U256, - /// A scalar value equal to the number of Wei to be transferred to the message call's - /// recipient. + /// The amount, in Wei, to be transferred to the recipient of the message call. pub value: U256, - /// A scalar value equal to the maximum amount of gas that should be used in executing - /// this transaction. + /// The maximum amount of gas allocated for the execution of the L2 transaction. pub gas_limit: U256, /// If true, the transaction does not interact with the L2 block gas pool. - /// Note: boolean is disabled (enforced to be false) starting from the Regolith - /// upgrade. pub is_system_tx: bool, - /// An unlimited size byte array specifying the transaction data. + /// The transaction's payload, represented as a variable-length byte array. pub data: Bytes, } -impl TxEssenceOptimismDeposited { - pub fn payload_length(&self) -> usize { - self.source_hash.length() - + self.from.length() - + self.to.length() - + self.mint.length() - + self.value.length() - + self.gas_limit.length() - + self.is_system_tx.length() - + self.data.length() - } -} - +/// Represents the core essence of an Optimism transaction, specifically the portion that +/// gets signed. +/// +/// The [OptimismTxEssence] enum provides a way to handle different types of Optimism +/// transactions. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum OptimismTxEssence { + /// Represents an Ethereum-compatible L2 transaction. Ethereum(EthereumTxEssence), + /// Represents an Optimism depositing transaction. OptimismDeposited(TxEssenceOptimismDeposited), } impl Encodable for OptimismTxEssence { + /// Encodes the [OptimismTxEssence] enum variant into the provided `out` buffer. + #[inline] fn encode(&self, out: &mut dyn BufMut) { match self { OptimismTxEssence::Ethereum(eth) => eth.encode(out), @@ -79,6 +74,8 @@ impl Encodable for OptimismTxEssence { } } + /// Computes the length of the RLP-encoded [OptimismTxEssence] enum variant in bytes. + #[inline] fn length(&self) -> usize { match self { OptimismTxEssence::Ethereum(eth) => eth.length(), @@ -88,60 +85,144 @@ impl Encodable for OptimismTxEssence { } impl TxEssence for OptimismTxEssence { + /// Returns the EIP-2718 transaction type. fn tx_type(&self) -> u8 { match self { OptimismTxEssence::Ethereum(eth) => eth.tx_type(), - OptimismTxEssence::OptimismDeposited(_) => 0x7E, + OptimismTxEssence::OptimismDeposited(_) => OPTIMISM_DEPOSITED_TX_TYPE, } } - + /// Returns the gas limit set for the transaction. fn gas_limit(&self) -> U256 { match self { OptimismTxEssence::Ethereum(eth) => eth.gas_limit(), OptimismTxEssence::OptimismDeposited(op) => op.gas_limit, } } - + /// Returns the recipient address of the transaction, if available. fn to(&self) -> Option<Address> { match self { OptimismTxEssence::Ethereum(eth) => eth.to(), OptimismTxEssence::OptimismDeposited(op) => op.to.into(), } } - + /// Recovers the Ethereum address of the sender from the transaction's signature. fn recover_from(&self, signature: &TxSignature) -> anyhow::Result<Address> { match self { OptimismTxEssence::Ethereum(eth) => eth.recover_from(signature), OptimismTxEssence::OptimismDeposited(op) => Ok(op.from), } } - + /// Returns the length of the RLP-encoding payload in bytes. fn payload_length(&self) -> usize { match self { OptimismTxEssence::Ethereum(eth) => eth.payload_length(), - OptimismTxEssence::OptimismDeposited(op) => op.payload_length(), + OptimismTxEssence::OptimismDeposited(op) => op._alloy_rlp_payload_length(), } } - - fn encode_with_signature(&self, signature: &TxSignature, out: &mut dyn BufMut) { + /// Returns a reference to the transaction's call data + fn data(&self) -> &Bytes { match self { - OptimismTxEssence::Ethereum(eth) => eth.encode_with_signature(signature, out), - OptimismTxEssence::OptimismDeposited(op) => op.encode(out), + OptimismTxEssence::Ethereum(eth) => eth.data(), + OptimismTxEssence::OptimismDeposited(op) => &op.data, } } +} - #[inline] - fn length(transaction: &Transaction<Self>) -> usize { - let payload_length = match &transaction.essence { - OptimismTxEssence::Ethereum(eth) => { - eth.payload_length() + transaction.signature.payload_length() +#[cfg(test)] +mod tests { + use alloc::vec; + + use alloy_primitives::{address, b256}; + use serde_json::json; + + use super::*; + use crate::transactions::OptimismTransaction; + + #[test] + fn ethereum() { + // Tx: 0x9125dcdf2a82f349bbcd8c1201cc601b7b4f98975c76d1f8ee3ce9270334fb8a + let tx = json!({ + "Ethereum": { + "Eip1559": { + "chain_id": 10, + "nonce": 17, + "max_priority_fee_per_gas": "0x3b9cdd02", + "max_fee_per_gas": "0x3b9cdd02", + "gas_limit": "0x01a1a7", + "to": { "Call": "0x7f5c764cbc14f9669b88837ca1490cca17c31607" }, + "value": "0x0", + "data": "0x095ea7b30000000000000000000000004c5d5234f232bd2d76b96aa33f5ae4fcf0e4bfabffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "access_list": [] + } } - OptimismTxEssence::OptimismDeposited(op) => op.payload_length(), + }); + let essence: OptimismTxEssence = serde_json::from_value(tx).unwrap(); + + let signature: TxSignature = serde_json::from_value(json!({ + "v": 0, + "r": "0x044e091fe419b233ddc76c616f60f33f5c68a5d6ea315b0b22afdbe5af66b9e6", + "s": "0x7f00ccbff42777c6c2e5d5e85579a7984783be446cc1b9a7b8e080d167d56fa8" + })) + .unwrap(); + + let transaction = OptimismTransaction { essence, signature }; + + // verify that bincode serialization works + let _: OptimismTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + let encoded = alloy_rlp::encode(&transaction); + assert_eq!(encoded.len(), transaction.length()); + + assert_eq!( + transaction.hash(), + b256!("9125dcdf2a82f349bbcd8c1201cc601b7b4f98975c76d1f8ee3ce9270334fb8a") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("96dd9c6f1fd5b3fbaa70898f09bedff903237d6d") + ); + } + + #[test] + fn optimism_deposited() { + // Tx: 0x2bf9119d4faa19593ca1b3cda4b4ac03c0ced487454a50fbdcd09aebe21210e3 + let tx = json!({ + "OptimismDeposited": { + "source_hash": "0x20b925f36904e1e62099920d902925817c4357e9f674b8b14d13363196139010", + "from": "0x36bde71c97b33cc4729cf772ae268934f7ab70b2", + "to": { "Call": "0x4200000000000000000000000000000000000007" }, + "mint": "0x030d98d59a960000", + "value": "0x030d98d59a960000", + "gas_limit": "0x077d2e", + "is_system_tx": false, + "data": "0xd764ad0b000100000000000000000000000000000000000000000000000000000000af8600000000000000000000000099c9fc46f92e8a1c0dec1b1747d010903e884be10000000000000000000000004200000000000000000000000000000000000010000000000000000000000000000000000000000000000000030d98d59a9600000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000a41635f5fd000000000000000000000000ab12275f2d91f87b301a4f01c9af4e83b3f45baa000000000000000000000000ab12275f2d91f87b301a4f01c9af4e83b3f45baa000000000000000000000000000000000000000000000000030d98d59a9600000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }); + let essence: OptimismTxEssence = serde_json::from_value(tx).unwrap(); + + let transaction = OptimismTransaction { + essence, + signature: TxSignature::default(), }; - let mut length = payload_length + alloy_rlp::length_of_length(payload_length); - if transaction.essence.tx_type() != 0 { - length += 1; - } - length + + // verify that bincode serialization works + let _: OptimismTransaction = + bincode::deserialize(&bincode::serialize(&transaction).unwrap()).unwrap(); + + let encoded = alloy_rlp::encode(&transaction); + assert_eq!(encoded.len(), transaction.length()); + + assert_eq!( + transaction.hash(), + b256!("2bf9119d4faa19593ca1b3cda4b4ac03c0ced487454a50fbdcd09aebe21210e3") + ); + let recovered = transaction.recover_from().unwrap(); + assert_eq!( + recovered, + address!("36bde71c97b33cc4729cf772ae268934f7ab70b2") + ); } } diff --git a/primitives/src/signature.rs b/primitives/src/transactions/signature.rs similarity index 93% rename from primitives/src/signature.rs rename to primitives/src/transactions/signature.rs index 7feb94944..6b110c1ad 100644 --- a/primitives/src/signature.rs +++ b/primitives/src/transactions/signature.rs @@ -21,7 +21,9 @@ use serde::{Deserialize, Serialize}; /// The `TxSignature` struct encapsulates the components of an ECDSA signature: `v`, `r`, /// and `s`. This signature can be used to recover the public key of the signer, ensuring /// the authenticity of the transaction. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, RlpEncodable, RlpMaxEncodedLen)] +#[derive( + Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, RlpEncodable, RlpMaxEncodedLen, +)] pub struct TxSignature { pub v: u64, pub r: U256, diff --git a/raiko-guest/src/one_shot.rs b/raiko-guest/src/one_shot.rs index b8a5234cb..f58ac4673 100644 --- a/raiko-guest/src/one_shot.rs +++ b/raiko-guest/src/one_shot.rs @@ -11,13 +11,17 @@ use base64_serde::base64_serde_type; use secp256k1::KeyPair; use serde::Serialize; use zeth_lib::{ - consts::{get_taiko_chain_spec, ChainSpec, ETH_MAINNET_CHAIN_SPEC}, - host::{taiko::TaikoExtra, Init}, + builder::{BlockBuilderStrategy, TaikoStrategy}, + consts::TKO_MAINNET_CHAIN_SPEC, input::Input, - taiko::block_builder::{TaikoBlockBuilder, TaikoStrategyBundle}, + taiko::{ + host::{init_taiko, HostArgs}, + protocol_instance::{assemble_protocol_instance, EvidenceType}, + TaikoSystemInfo, + }, EthereumTxEssence, }; -use zeth_primitives::{taiko::EvidenceType, Address, B256}; +use zeth_primitives::{Address, B256}; base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD); use crate::{ @@ -87,7 +91,7 @@ pub fn bootstrap(global_opts: GlobalOpts) -> Result<()> { println!("Public key: 0x{}", key_pair.public_key()); let new_instance = public_key_to_address(&key_pair.public_key()); save_attestation_user_report_data(new_instance)?; - println!("Instance address: {}", new_instance); + println!("Instance address: {new_instance}"); let quote = get_sgx_quote()?; let bootstrap_details_file_path = global_opts.config_dir.join(BOOTSTRAP_INFO_FILENAME); save_bootstrap_details(&key_pair, new_instance, quote, &bootstrap_details_file_path)?; @@ -101,13 +105,10 @@ pub fn bootstrap(global_opts: GlobalOpts) -> Result<()> { pub async fn one_shot(global_opts: GlobalOpts, args: OneShotArgs) -> Result<()> { if !is_bootstrapped(&global_opts.secrets_dir) { - bail!("Application was not bootstrapped. Bootstrap it first.") + bail!("Application was not bootstrapped. Bootstrap it first."); } - println!( - "Global options: {:?}, OneShot options: {:?}", - global_opts, args - ); + println!("Global options: {global_opts:?}, OneShot options: {args:?}"); let path_str = args.blocks_data_file.to_string_lossy().to_string(); let block_no = u64::from_str(&String::from( @@ -118,18 +119,17 @@ pub async fn one_shot(global_opts: GlobalOpts, args: OneShotArgs) -> Result<()> .unwrap(), ))?; - println!("Reading input file {} (block no: {})", path_str, block_no); + println!("Reading input file {path_str} (block no: {block_no})"); let privkey_path = global_opts.secrets_dir.join(PRIV_KEY_FILENAME); let prev_privkey = load_private_key(&privkey_path)?; // let (new_privkey, new_pubkey) = generate_new_keypair()?; let new_pubkey = public_key(&prev_privkey); let new_instance = public_key_to_address(&new_pubkey); - let l2_chain_spec = get_taiko_chain_spec(&args.l2_chain.unwrap()); // fs::write(privkey_path, new_privkey.to_bytes())?; let pi_hash = get_data_to_sign( - &l2_chain_spec, + "testnet".to_string(), path_str, args.l1_blocks_data_file.to_string_lossy().to_string(), args.prover, @@ -139,7 +139,7 @@ pub async fn one_shot(global_opts: GlobalOpts, args: OneShotArgs) -> Result<()> ) .await?; - println!("Data to be signed: {}", pi_hash); + println!("Data to be signed: {pi_hash}"); let sig = sign_message(&prev_privkey, pi_hash)?; @@ -153,12 +153,12 @@ pub async fn one_shot(global_opts: GlobalOpts, args: OneShotArgs) -> Result<()> save_attestation_user_report_data(new_instance)?; let quote = get_sgx_quote()?; let data = serde_json::json!({ - "proof": format!("0x{}", proof), + "proof": format!("0x{proof}"), "quote": hex::encode(quote), - "public_key": format!("0x{}", new_pubkey), + "public_key": format!("0x{new_pubkey}"), "instance_address": new_instance.to_string(), }); - println!("{}", data); + println!("{data}"); print_sgx_info() } @@ -169,7 +169,7 @@ fn is_bootstrapped(secrets_dir: &Path) -> bool { } async fn get_data_to_sign( - l2_chain_spec: &ChainSpec, + testnet: String, path_str: String, l1_blocks_path: String, prover: Address, @@ -177,8 +177,8 @@ async fn get_data_to_sign( block_no: u64, new_pubkey: Address, ) -> Result<B256> { - let (init, extra) = parse_to_init( - l2_chain_spec, + let (input, sys_info) = parse_to_init( + testnet, path_str, l1_blocks_path, prover, @@ -186,40 +186,39 @@ async fn get_data_to_sign( graffiti, ) .await?; - let input: Input<zeth_lib::EthereumTxEssence> = init.clone().into(); - let output = TaikoBlockBuilder::build_from(l2_chain_spec, input) + let (header, _mpt_node) = TaikoStrategy::build_from(&TKO_MAINNET_CHAIN_SPEC.clone(), input) .expect("Failed to build the resulting block"); - let pi = zeth_lib::host::taiko::assemble_protocol_instance(&extra, &output)?; - let pi_hash = pi.hash(EvidenceType::Sgx { new_pubkey }); + let pi = assemble_protocol_instance(&sys_info, &header)?; + let pi_hash = pi.instance_hash(EvidenceType::Sgx { new_pubkey }); Ok(pi_hash) } async fn parse_to_init( - l2_chain_spec: &ChainSpec, + testnet: String, blocks_path: String, l1_blocks_path: String, prover: Address, block_no: u64, graffiti: B256, -) -> Result<(Init<zeth_lib::EthereumTxEssence>, TaikoExtra), Error> { - let l2_chain_spec = l2_chain_spec.clone(); - let (init, extra) = tokio::task::spawn_blocking(move || { - zeth_lib::host::taiko::get_taiko_initial_data::<TaikoStrategyBundle>( - Some(l1_blocks_path), - ETH_MAINNET_CHAIN_SPEC.clone(), - None, - prover, - Some(blocks_path), - l2_chain_spec, - None, +) -> Result<(Input<EthereumTxEssence>, TaikoSystemInfo), Error> { + let (input, sys_info) = tokio::task::spawn_blocking(move || { + init_taiko( + HostArgs { + l1_cache: PathBuf::from_str(&l1_blocks_path).ok(), + l1_rpc: None, + l2_cache: PathBuf::from_str(&blocks_path).ok(), + l2_rpc: None, + }, + TKO_MAINNET_CHAIN_SPEC.clone(), + &testnet, block_no, graffiti, + prover, ) - .expect("Could not init") + .expect("Init taiko failed") }) .await?; - - Ok::<(Init<EthereumTxEssence>, TaikoExtra), _>((init, extra)) + Ok((input, sys_info)) } fn save_attestation_user_report_data(pubkey: Address) -> Result<()> { @@ -230,7 +229,7 @@ fn save_attestation_user_report_data(pubkey: Address) -> Result<()> { .open(ATTESTATION_USER_REPORT_DATA_DEVICE_FILE)?; user_report_data_file .write_all(&extended_pubkey) - .map_err(|err| anyhow!("Failed to save user report data: {}", err)) + .map_err(|err| anyhow!("Failed to save user report data: {err}")) } fn print_sgx_info() -> Result<()> { @@ -280,15 +279,15 @@ fn print_sgx_info() -> Result<()> { fn get_sgx_attestation_type() -> Result<String> { let mut attestation_type = String::new(); - if File::open(ATTESTATION_TYPE_DEVICE_FILE) + if !File::open(ATTESTATION_TYPE_DEVICE_FILE) .and_then(|mut file| file.read_to_string(&mut attestation_type)) .is_ok() { - return Ok(attestation_type.trim().to_string()); + bail!( + "Cannot find `{}`; are you running under SGX, with remote attestation enabled?", + ATTESTATION_TYPE_DEVICE_FILE + ); } - bail!( - "Cannot find `{}`; are you running under SGX, with remote attestation enabled?", - ATTESTATION_TYPE_DEVICE_FILE - ); + Ok(attestation_type.trim().to_string()) } diff --git a/raiko-guest/src/signature.rs b/raiko-guest/src/signature.rs index 92bf53922..21084060a 100644 --- a/raiko-guest/src/signature.rs +++ b/raiko-guest/src/signature.rs @@ -5,7 +5,7 @@ use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId}, Error, KeyPair, Message, PublicKey, SecretKey, SECP256K1, }; -use zeth_primitives::{keccak256, signature::TxSignature, Address, B256, U256}; +use zeth_primitives::{keccak256, transactions::signature::TxSignature, Address, B256, U256}; pub fn generate_key() -> KeyPair { KeyPair::new_global(&mut OsRng) @@ -84,7 +84,7 @@ mod tests { let pubkey = public_key(&priv_key); let pub_addr = public_key_to_address(&pubkey); assert_eq!(pub_addr, proof_addr); - println!("Public address: {}", pub_addr); - println!("Proof public address: {}", proof_addr); + println!("Public address: {pub_addr}"); + println!("Proof public address: {proof_addr}"); } } diff --git a/raiko-host/Cargo.toml b/raiko-host/Cargo.toml index 87686bfc5..c3c251d2f 100644 --- a/raiko-host/Cargo.toml +++ b/raiko-host/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" [dependencies] anyhow = "1.0" -alloy-sol-types = { version = "0.4", optional = true } -alloy-primitives = { version = "0.4", default-features = false } +alloy-sol-types = { version = "0.6", optional = true } +alloy-primitives = { version = "0.6", default-features = false } bincode = "1.3.3" bonsai-sdk = { workspace = true } bytemuck = "1.13" @@ -22,7 +22,7 @@ serde = "1.0" serde_with = "3.4.0" tempfile = "3.6" tokio = { version = "1.23", features = ["full"] } -zeth-lib = { path = "../lib", features = ["taiko", "server", "std"] } +zeth-lib = { path = "../lib", features = ["taiko", "std"] } zeth-primitives = { path = "../primitives", features = ["taiko"] } serde_json = "1.0" hyper = { version = "0.14.27", features = ["server"] } diff --git a/raiko-host/src/prover/execution.rs b/raiko-host/src/prover/execution.rs index 55e978e05..91774e864 100644 --- a/raiko-host/src/prover/execution.rs +++ b/raiko-host/src/prover/execution.rs @@ -1,7 +1,5 @@ use std::time::Instant; -use zeth_lib::taiko::block_builder::TaikoStrategyBundle; - use super::{ context::Context, error::Result, @@ -39,7 +37,7 @@ pub async fn execute( let result = async { // 1. load input data into cache path let start = Instant::now(); - let _ = prepare_input::<TaikoStrategyBundle>(ctx, req).await?; + let _ = prepare_input(ctx, req.clone()).await?; let elapsed = Instant::now().duration_since(start).as_millis() as i64; observe_input(elapsed); // 2. run proof diff --git a/raiko-host/src/prover/prepare_input.rs b/raiko-host/src/prover/prepare_input.rs index 0ebca4f55..926477d07 100644 --- a/raiko-host/src/prover/prepare_input.rs +++ b/raiko-host/src/prover/prepare_input.rs @@ -1,31 +1,26 @@ //! Prepare Input for guest -use std::fmt::Debug; use zeth_lib::{ - block_builder::NetworkStrategyBundle, - consts::{get_taiko_chain_spec, ETH_MAINNET_CHAIN_SPEC}, - host::{ - provider::file_provider::cache_file_path, - taiko::{get_taiko_initial_data, TaikoExtra}, - Init, + consts::TKO_MAINNET_CHAIN_SPEC, + input::Input, + taiko::{ + host::{init_taiko, HostArgs}, + TaikoSystemInfo, }, EthereumTxEssence, }; use super::{ context::Context, - error::Result, + error::{Error, Result}, request::{ProofRequest, PseZkRequest, SgxRequest}, }; /// prepare input data for guests -pub async fn prepare_input<N: NetworkStrategyBundle<TxEssence = EthereumTxEssence>>( +pub async fn prepare_input( ctx: &mut Context, - req: &ProofRequest, -) -> Result<(Init<N::TxEssence>, TaikoExtra)> -where - <N::Database as revm::primitives::db::Database>::Error: Debug, -{ + req: ProofRequest, +) -> Result<(Input<EthereumTxEssence>, TaikoSystemInfo)> { match req { ProofRequest::Sgx(SgxRequest { block, @@ -34,33 +29,28 @@ where prover, graffiti, }) => { - let l2_block = *block; - - let l2_spec = get_taiko_chain_spec(&ctx.l2_chain); - let l2_rpc = l2_rpc.to_owned(); - - let l1_spec = ETH_MAINNET_CHAIN_SPEC.clone(); - let l1_rpc = l1_rpc.to_owned(); - let prover = prover.to_owned(); - let graffiti = *graffiti; - // run sync task in blocking mode - let l1_cache_path = ctx.l1_cache_file.as_ref().unwrap().to_owned(); - let l2_cache_path = ctx.l2_cache_file.as_ref().unwrap().to_owned(); + // Todo(Cecilia): should contract address as args, curently hardcode + let l1_cache = ctx.l1_cache_file.clone(); + let l2_cache = ctx.l2_cache_file.clone(); + let testnet = ctx.l2_chain.clone(); tokio::task::spawn_blocking(move || { - get_taiko_initial_data::<N>( - Some(l1_cache_path.into_os_string().into_string().unwrap()), - l1_spec, - Some(l1_rpc), - prover, - Some(l2_cache_path.into_os_string().into_string().unwrap()), - l2_spec, - Some(l2_rpc), - l2_block, + init_taiko( + HostArgs { + l1_cache, + l1_rpc: Some(l1_rpc), + l2_cache, + l2_rpc: Some(l2_rpc), + }, + TKO_MAINNET_CHAIN_SPEC.clone(), + &testnet, + block, graffiti, + prover, ) + .expect("Init taiko failed") }) - .await? - .map_err(Into::into) + .await + .map_err(Into::<Error>::into) } ProofRequest::PseZk(PseZkRequest { .. }) => todo!(), } diff --git a/raiko-host/src/prover/proof/powdr.rs b/raiko-host/src/prover/proof/powdr.rs new file mode 100644 index 000000000..2fd82aae4 --- /dev/null +++ b/raiko-host/src/prover/proof/powdr.rs @@ -0,0 +1,83 @@ +use std::str; + +use serde_json::Value; +use tokio::process::Command; +use tracing::{debug, info}; + +use crate::{ + metrics::inc_sgx_error, + prover::{ + consts::*, + context::Context, + request::{SgxRequest, SgxResponse}, + utils::guest_executable_path, + }, +}; + +pub async fn execute_sgx(ctx: &mut Context, req: &SgxRequest) -> Result<SgxResponse, String> { + let guest_path = guest_executable_path(&ctx.guest_path, SGX_PARENT_DIR); + debug!("Guest path: {:?}", guest_path); + let mut cmd = { + let bin_directory = guest_path + .parent() + .ok_or(String::from("missing sgx executable directory"))?; + let bin = guest_path + .file_name() + .ok_or(String::from("missing sgx executable"))?; + let mut cmd = Command::new("sudo"); + cmd.current_dir(bin_directory) + .arg("gramine-sgx") + .arg(bin) + .arg("one-shot"); + cmd + }; + let output = cmd + .arg("--blocks-data-file") + .arg(ctx.l2_cache_file.as_ref().unwrap()) + .arg("--l1-blocks-data-file") + .arg(ctx.l1_cache_file.as_ref().unwrap()) + .arg("--prover") + .arg(req.prover.to_string()) + .arg("--graffiti") + .arg(req.graffiti.to_string()) + .arg("--sgx-instance-id") + .arg(ctx.sgx_context.instance_id.to_string()) + .arg("--l2-chain") + .arg(&ctx.l2_chain) + .output() + .await + .map_err(|e| e.to_string())?; + info!("Sgx execution stderr: {:?}", str::from_utf8(&output.stderr)); + info!("Sgx execution stdout: {:?}", str::from_utf8(&output.stdout)); + if !output.status.success() { + inc_sgx_error(req.block); + Err(output.status.to_string()) + } else { + parse_sgx_result(output.stdout) + } +} + +fn parse_sgx_result(output: Vec<u8>) -> Result<SgxResponse, String> { + let mut json_value: Option<Value> = None; + let output = String::from_utf8(output).map_err(|e| e.to_string())?; + + for line in output.lines() { + if let Ok(value) = serde_json::from_str::<Value>(line.trim()) { + json_value = Some(value); + break; + } + } + + let extract_field = |field| { + json_value + .as_ref() + .and_then(|json| json.get(field).and_then(|v| v.as_str())) + .unwrap_or("") + .to_string() + }; + + let proof = extract_field("proof"); + let quote = extract_field("quote"); + + Ok(SgxResponse { proof, quote }) +} diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index a6231be78..c3314bd27 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -18,6 +18,7 @@ zeth-lib = { path = "../../lib" } zeth-primitives = { path = "../../primitives" } [dev-dependencies] +diff = "0.1" env_logger = "0.10" log = "0.4" risc0-zkvm = { workspace = true, features = ["prove"] } diff --git a/testing/ef-tests/src/ethers.rs b/testing/ef-tests/src/ethers.rs index 657c7cc05..bffdb68f2 100644 --- a/testing/ef-tests/src/ethers.rs +++ b/testing/ef-tests/src/ethers.rs @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::BTreeSet; + use ethers_core::types::{ - Block, Bloom, Bytes, EIP1186ProofResponse, StorageProof, Transaction, H256, U256, + Block, Bloom, Bytes, EIP1186ProofResponse, StorageProof, Transaction, TransactionReceipt, H256, + U256, }; use zeth_primitives::U256 as LibU256; @@ -23,6 +26,7 @@ use super::*; pub struct TestProvider { pub state: TestState, pub header: Header, + pub post: TestState, } impl Provider for TestProvider { @@ -62,14 +66,28 @@ impl Provider for TestProvider { }) } - fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse, anyhow::Error> { - assert_eq!(query.block_no, self.header.number); + fn get_block_receipts( + &mut self, + _query: &BlockQuery, + ) -> anyhow::Result<Vec<TransactionReceipt>> { + unimplemented!() + } + fn get_proof(&mut self, query: &ProofQuery) -> Result<EIP1186ProofResponse, anyhow::Error> { let indices = query .indices .iter() .map(|idx| LibU256::from_be_bytes(idx.0)); - get_proof(from_ethers_h160(query.address), indices, &self.state) + + match query.block_no { + n if n == self.header.number => { + get_proof(from_ethers_h160(query.address), indices, &self.state) + } + n if n == self.header.number + 1 => { + get_proof(from_ethers_h160(query.address), indices, &self.post) + } + _ => panic!("invalid block number: {}", query.block_no), + } } fn get_transaction_count(&mut self, query: &AccountQuery) -> Result<U256, anyhow::Error> { @@ -125,7 +143,8 @@ impl Provider for TestProvider { } } -fn build_tries(state: &TestState) -> (MptNode, HashMap<Address, MptNode>) { +/// Builds the state trie and storage tries from the test state. +pub fn build_tries(state: &TestState) -> (MptNode, HashMap<Address, MptNode>) { let mut state_trie = MptNode::default(); let mut storage_tries = HashMap::new(); for (address, account) in &state.0 { @@ -168,9 +187,10 @@ fn get_proof( .into_iter() .map(|p| p.into()) .collect(); - let mut storage_proof = vec![]; - for index in indices { - let proof = StorageProof { + let index_set = indices.into_iter().collect::<BTreeSet<_>>(); + let storage_proof = index_set + .into_iter() + .map(|index| StorageProof { key: index.to_be_bytes().into(), proof: mpt_proof(&storage_trie, keccak(index.to_be_bytes::<32>()))? .into_iter() @@ -183,9 +203,8 @@ fn get_proof( .unwrap_or_default() .to_be_bytes() .into(), - }; - storage_proof.push(proof); - } + }) + .collect::<Vec<_>>(); Ok(EIP1186ProofResponse { address: address.into_array().into(), @@ -197,17 +216,3 @@ fn get_proof( storage_proof, }) } - -/// Get EIP-1186 proofs for a set of addresses and storage keys. -pub fn get_state_update_proofs( - provider: &ProviderDb, - storage_keys: HashMap<Address, Vec<LibU256>>, -) -> Result<HashMap<Address, EIP1186ProofResponse>, anyhow::Error> { - let state = provider.into(); - - let mut result = HashMap::new(); - for (address, indices) in storage_keys { - result.insert(address, get_proof(address, indices, &state)?); - } - Ok(result) -} diff --git a/testing/ef-tests/src/lib.rs b/testing/ef-tests/src/lib.rs index b701d93cf..421ae3df0 100644 --- a/testing/ef-tests/src/lib.rs +++ b/testing/ef-tests/src/lib.rs @@ -19,28 +19,26 @@ use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, NoneAsEmptyString}; use zeth_lib::{ - block_builder::BlockBuilder, + builder::{BlockBuilder, BlockBuilderStrategy, EthereumStrategy}, consts::ChainSpec, - execution::ethereum::EthTxExecStrategy, host::{ + preflight::Data, provider::{AccountQuery, BlockQuery, ProofQuery, Provider, StorageQuery}, provider_db::ProviderDb, - Init, }, input::Input, mem_db::{AccountState, DbAccount, MemDb}, - preparation::EthHeaderPrepStrategy, }; use zeth_primitives::{ access_list::{AccessList, AccessListItem}, block::Header, ethers::from_ethers_h160, keccak::keccak, - signature::TxSignature, transactions::{ ethereum::{ EthereumTxEssence, TransactionKind, TxEssenceEip1559, TxEssenceEip2930, TxEssenceLegacy, }, + signature::TxSignature, EthereumTransaction, }, trie::{self, MptNode, MptNodeData, StateAccount}, @@ -48,10 +46,9 @@ use zeth_primitives::{ Address, Bloom, Bytes, RlpBytes, StorageKey, B256, B64, U256, U64, }; -use crate::ethers::{get_state_update_proofs, TestProvider}; +use crate::ethers::TestProvider; pub mod ethers; - pub mod ethtests; pub mod guests { @@ -105,7 +102,7 @@ impl From<DbAccount> for TestAccount { } } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct TestState(pub HashMap<Address, TestAccount>); @@ -114,8 +111,10 @@ impl From<&MemDb> for TestState { TestState( db.accounts .iter() - .filter(|(_, account)| account.state != AccountState::Deleted) - .map(|(addr, account)| (*addr, account.clone().into())) + .filter_map(|(addr, account)| { + (account.state != AccountState::Deleted) + .then_some((*addr, account.clone().into())) + }) .collect(), ) } @@ -287,17 +286,15 @@ fn proof_internal(node: &MptNode, key_nibs: &[u8]) -> Result<Vec<Vec<u8>>, anyho let mut path: Vec<Vec<u8>> = match node.as_data() { MptNodeData::Null | MptNodeData::Leaf(_, _) => vec![], MptNodeData::Branch(children) => { - let mut path = Vec::new(); - for node in children.iter().flatten() { - path.extend(proof_internal(node, &key_nibs[1..])?); + let (i, tail) = key_nibs.split_first().unwrap(); + match &children[*i as usize] { + Some(child) => proof_internal(child, tail)?, + None => vec![], } - path } MptNodeData::Extension(_, child) => { - let ext_nibs = node.nibs(); - let ext_len = ext_nibs.len(); - if key_nibs[..ext_len] == ext_nibs { - proof_internal(child, &key_nibs[ext_len..])? + if let Some(tail) = key_nibs.strip_prefix(node.nibs().as_slice()) { + proof_internal(child, tail)? } else { vec![] } @@ -314,17 +311,19 @@ pub const BIG_STACK_SIZE: usize = 8 * 1024 * 1024; pub fn create_input( chain_spec: &ChainSpec, - state: TestState, parent_header: Header, + parent_state: TestState, header: Header, transactions: Vec<TestTransaction>, withdrawals: Vec<Withdrawal>, + state: TestState, ) -> Input<EthereumTxEssence> { // create the provider DB let provider_db = ProviderDb::new( Box::new(TestProvider { - state, + state: parent_state, header: parent_header.clone(), + post: state, }), parent_header.number, ); @@ -350,29 +349,28 @@ pub fn create_input( }; // create and run the block builder once to create the initial DB - let mut builder = BlockBuilder::new(chain_spec, input) + let mut builder = BlockBuilder::new(&chain_spec, input) .with_db(provider_db) - .prepare_header::<EthHeaderPrepStrategy>() + .prepare_header::<<EthereumStrategy as BlockBuilderStrategy>::HeaderPrepStrategy>() .unwrap() - .execute_transactions::<EthTxExecStrategy>() + .execute_transactions::<<EthereumStrategy as BlockBuilderStrategy>::TxExecStrategy>() .unwrap(); - let provider_db = builder.mut_db().unwrap(); - let init_proofs = provider_db.get_initial_proofs().unwrap(); - let fini_proofs = - get_state_update_proofs(provider_db, provider_db.get_latest_db().storage_keys()).unwrap(); + let parent_proofs = provider_db.get_initial_proofs().unwrap(); + let proofs = provider_db.get_latest_proofs().unwrap(); let ancestor_headers = provider_db.get_ancestor_headers().unwrap(); - Init { + let preflight_data = Data { db: provider_db.get_initial_db().clone(), - init_block: parent_header, - init_proofs, - fini_block: header, - fini_transactions: transactions, - fini_withdrawals: withdrawals, - fini_proofs, + parent_header, + parent_proofs, + header, + transactions, + withdrawals, + proofs, ancestor_headers, - } - .into() + }; + + preflight_data.try_into().unwrap() } diff --git a/testing/ef-tests/testguest/Cargo.lock b/testing/ef-tests/testguest/Cargo.lock index a0e246635..fa658e5ee 100644 --- a/testing/ef-tests/testguest/Cargo.lock +++ b/testing/ef-tests/testguest/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -17,22 +17,29 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.0.3" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -45,9 +52,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "alloy-primitives" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4084879b7257d5b95b9009837c07a1868bd7d60e66418a7764b9b580ae64e0" +checksum = "f4b6fb2b432ff223d513db7f908937f63c252bee0af9b82bfd25b0a5dd1eb0d8" dependencies = [ "alloy-rlp", "bytes", @@ -56,6 +63,8 @@ dependencies = [ "derive_more", "hex-literal", "itoa", + "k256", + "keccak-asm", "proptest", "rand", "ruint", @@ -65,25 +74,23 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" dependencies = [ - "alloy-rlp-derive", "arrayvec", "bytes", - "smol_str", ] [[package]] name = "alloy-rlp-derive" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa5bb468bc7c46e0c5074d418f575262ff79451242e5ac1380121ed4e23c4fd" +checksum = "1a047897373be4bbb0224c1afdabca92648dc57a9c9ef6e7b0be3aff7a859c83" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] @@ -103,9 +110,133 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] [[package]] name = "arrayvec" @@ -115,13 +246,13 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] @@ -132,19 +263,28 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version", + "rustc_version 0.4.0", +] + +[[package]] +name = "aurora-engine-modexp" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfacad86e9e138fca0670949eb8ed4ffdf73a55bded8887efe0863cd1a3a6f70" +dependencies = [ + "hex", + "num", ] [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "823b8bb275161044e2ac7a25879cb3e2480cb403e3943022c7c769c599b756aa" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -155,9 +295,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -182,9 +322,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -192,29 +332,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.4.0", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.28", - "which", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -238,9 +355,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" dependencies = [ "serde", ] @@ -264,7 +381,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -290,9 +407,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -302,46 +419,45 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "0.1.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac926d808fb72fe09ebf471a091d6d72918876ccf0b4989766093d2d0d24a0ef" +checksum = "b9d8c306be83ec04bf5f73710badd8edf56dea23f2f0d8b7f9fe4644d371c758" dependencies = [ - "bindgen", "blst", "cc", "glob", @@ -359,15 +475,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -376,45 +483,35 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "winapi", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", + "windows-targets 0.52.0", ] [[package]] name = "const-hex" -version = "1.6.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +checksum = "18d59688ad0945eaf6b84cb44fedbe93484c81b48970e98f09db8a22832d7961" dependencies = [ "cfg-if", "cpufeatures", "hex", + "proptest", "serde", ] [[package]] name = "const-oid" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "convert_case" @@ -422,26 +519,45 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -476,9 +592,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" dependencies = [ "darling_core", "darling_macro", @@ -486,34 +602,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" dependencies = [ "darling_core", "quote", - "syn 2.0.28", + "syn 2.0.48", ] +[[package]] +name = "dary_heap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7762d17f1241643615821a8455a0b2c3e803784b058693d990b11f2dce25a0ca" + [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" @@ -527,13 +649,25 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.7" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ + "powerfmt", "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -543,10 +677,19 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" @@ -560,32 +703,19 @@ dependencies = [ ] [[package]] -name = "dyn_partial_eq" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07039d197226c4b9a3810c4f165328fb4e715258a4b8584f143d38e9de04301" -dependencies = [ - "dyn_partial_eq_derive", -] - -[[package]] -name = "dyn_partial_eq_derive" -version = "0.1.2" +name = "downcast-rs" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e217c6c1435ebf9b88662354589d339192b8eaf506edd22951e75e045c8e8bd" -dependencies = [ - "quote", - "syn 1.0.109", -] +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -594,19 +724,25 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elf" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -619,20 +755,20 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "enr" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" +checksum = "fe81b5c06ecfdbc71dd845216f225f53b62a10cb8a16c946836a3467f701d05b" dependencies = [ - "base64 0.13.1", + "base64 0.21.7", "bytes", "hex", "k256", @@ -646,13 +782,13 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] @@ -661,34 +797,14 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erased-serde" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da96524cc884f6558f1769b6c46686af2fe8e8b4cd253bd5a3cdba8181b8e070" -dependencies = [ - "serde", -] - [[package]] name = "errno" -version = "0.3.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -741,17 +857,17 @@ dependencies = [ [[package]] name = "ethers-core" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60ca2514feb98918a0a31de7e1983c29f2267ebf61b2dc5d4294f91e5b866623" +checksum = "aab3cef6cc1c9fd7f787043c81ad3052eff2b96a3878ef1526aa446311bdbfc9" dependencies = [ "arrayvec", "bytes", "chrono", + "const-hex", "elliptic-curve", "ethabi", "generic-array", - "hex", "k256", "num_enum", "open-fastrlp", @@ -768,14 +884,15 @@ dependencies = [ [[package]] name = "ethers-providers" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b411b119f1cf0efb69e2190883dee731251882bb21270f893ee9513b3a697c48" +checksum = "fb6b15393996e3b8a78ef1332d6483c11d839042c17be58decc92fa8b1c3508a" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.2", + "base64 0.21.7", "bytes", + "const-hex", "enr", "ethers-core", "futures-channel", @@ -783,9 +900,9 @@ dependencies = [ "futures-timer", "futures-util", "hashers", - "hex", "http", "instant", + "jsonwebtoken", "once_cell", "pin-project", "reqwest", @@ -805,9 +922,20 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] [[package]] name = "ff" @@ -833,9 +961,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -849,9 +977,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -864,9 +992,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -879,9 +1007,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -889,15 +1017,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -906,32 +1034,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -945,9 +1073,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -983,9 +1111,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -994,9 +1122,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -1029,9 +1157,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -1039,7 +1167,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1054,9 +1182,18 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -1080,9 +1217,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -1105,23 +1242,14 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" -dependencies = [ - "windows-sys", + "digest 0.10.7", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -1130,9 +1258,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1147,15 +1275,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1168,7 +1296,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -1177,9 +1305,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -1191,16 +1319,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1220,9 +1348,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1279,12 +1407,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "serde", ] @@ -1298,36 +1426,53 @@ dependencies = [ ] [[package]] -name = "inventory" -version = "0.3.11" +name = "ipnet" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] -name = "ipnet" -version = "2.8.0" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.7", + "pem", + "ring 0.16.20", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "k256" -version = "0.13.1" -source = "git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256/v0.13.1-risczero.1#5fea17d53fbaa0ff72dbe16da3ee2c2d02f2490c" +version = "0.13.3" +source = "git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256/v0.13.3-risczero.0#d4f457a04410397cbb652a67c168b6cd6e9757c4" dependencies = [ "cfg-if", "ecdsa", @@ -1339,61 +1484,79 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] +[[package]] +name = "keccak-asm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8515fff80ed850aea4a1595f2e519c003e2a00a82fe168ebf5269196caf444" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] -name = "lazycell" -version = "1.3.0" +name = "libc" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] -name = "libc" -version = "0.2.148" +name = "libflate" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "9f7d5654ae1795afc7ff76f4365c2c8791b0feb18e8996a96adad8ffd7c3b2bf" +dependencies = [ + "adler32", + "core2", + "crc32fast", + "dary_heap", + "libflate_lz77", +] [[package]] -name = "libloading" -version = "0.7.4" +name = "libflate_lz77" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "be5f52fb8c451576ec6b79d3f4deb327398bc05bbdbd99021a6e77a4c855d524" dependencies = [ - "cfg-if", - "winapi", + "core2", + "hashbrown 0.13.2", + "rle-decode-fast", ] [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1401,15 +1564,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -1417,40 +1580,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", + "windows-sys 0.48.0", ] [[package]] @@ -1469,9 +1616,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -1480,39 +1627,44 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -1533,9 +1685,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -1553,39 +1705,39 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "object" -version = "0.31.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open-fastrlp" @@ -1614,9 +1766,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.6.4" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -1628,11 +1780,11 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.4" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 1.0.109", @@ -1650,15 +1802,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1668,16 +1820,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "pem" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] [[package]] name = "pharos" @@ -1686,34 +1852,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version", + "rustc_version 0.4.0", ] [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1732,26 +1898,22 @@ dependencies = [ ] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "prettyplease" -version = "0.2.12" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" -dependencies = [ - "proc-macro2", - "syn 2.0.28", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -1768,57 +1930,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-crate" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", + "toml_edit 0.20.7", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "proc-macro2", - "quote", - "version_check", + "toml_edit 0.21.1", ] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ "bit-set", - "bitflags 1.3.2", - "byteorder", + "bit-vec", + "bitflags 2.4.2", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.6.29", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -1832,9 +1988,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.32" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1886,55 +2042,49 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.9.3" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.4", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -1956,6 +2106,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-rustls", "tower-service", @@ -1963,15 +2115,14 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.22.6", + "webpki-roots", "winreg", ] [[package]] name = "revm" version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f4ca8ae0345104523b4af1a8a7ea97cfa1865cdb7a7c25d23c1a18d9b48598" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#09531f2d92b80989c79fb9508ef20f8e9cbdd079" dependencies = [ "auto_impl", "revm-interpreter", @@ -1983,8 +2134,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f959cafdf64a7f89b014fa73dc2325001cf654b3d9400260b212d19a2ebe3da0" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#09531f2d92b80989c79fb9508ef20f8e9cbdd079" dependencies = [ "revm-primitives", "serde", @@ -1993,12 +2143,11 @@ dependencies = [ [[package]] name = "revm-precompile" version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d360a88223d85709d2e95d4609eb1e19c649c47e28954bfabae5e92bb37e83e" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#09531f2d92b80989c79fb9508ef20f8e9cbdd079" dependencies = [ + "aurora-engine-modexp", "c-kzg", "k256", - "num", "once_cell", "revm-primitives", "ripemd", @@ -2010,17 +2159,15 @@ dependencies = [ [[package]] name = "revm-primitives" version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51187b852d9e458816a2e19c81f1dd6c924077e1a8fccd16e4f044f865f299d7" +source = "git+https://github.com/ceciliaz030/revm.git?branch=sync-taiko-v3.5#09531f2d92b80989c79fb9508ef20f8e9cbdd079" dependencies = [ "alloy-primitives", - "alloy-rlp", "auto_impl", - "bitflags 2.4.0", + "bitflags 2.4.2", "bitvec", "c-kzg", "enumn", - "hashbrown 0.14.0", + "hashbrown 0.14.3", "hex", "serde", ] @@ -2044,25 +2191,68 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "risc0-binfmt" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923c85a23cb9a9475b8cd4479ad3a06252604a361626e9ae7dc0dc635af22c22" +dependencies = [ + "anyhow", + "elf", + "log", + "risc0-zkp", + "risc0-zkvm-platform", + "serde", +] + +[[package]] +name = "risc0-circuit-recursion" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97547e10e9fdaaab8b64ffb45dc158b31f023b1a68015c6ce9f12fe3e403012a" +dependencies = [ + "anyhow", + "bytemuck", + "log", + "risc0-core", + "risc0-zkp", + "tracing", ] [[package]] name = "risc0-circuit-rv32im" -version = "0.16.1" -source = "git+https://github.com/risc0/risc0.git?branch=release-0.16#cbb6ccab08718506a524af999baa6d1386c51cbb" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a269d01b18cba24ee1a08f68726fc3623e8705ed79d158377d12e9129dcde2e" dependencies = [ "anyhow", "log", @@ -2074,8 +2264,9 @@ dependencies = [ [[package]] name = "risc0-core" -version = "0.16.1" -source = "git+https://github.com/risc0/risc0.git?branch=release-0.16#cbb6ccab08718506a524af999baa6d1386c51cbb" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "477e0bb8d2ec0b7955088b521eb596901e652d0faa2ea73bda0b77e05af5c07d" dependencies = [ "bytemuck", "rand_core", @@ -2083,13 +2274,14 @@ dependencies = [ [[package]] name = "risc0-zkp" -version = "0.16.1" -source = "git+https://github.com/risc0/risc0.git?branch=release-0.16#cbb6ccab08718506a524af999baa6d1386c51cbb" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5abb1a0cf847d3f9aed1e563b76c358107e7ba66dbfab28f7144252c990bd82" dependencies = [ "anyhow", "blake2", "bytemuck", - "digest", + "digest 0.10.7", "hex", "log", "paste", @@ -2098,39 +2290,51 @@ dependencies = [ "risc0-zkvm-platform", "serde", "sha2", - "thiserror", "tracing", ] [[package]] name = "risc0-zkvm" -version = "0.16.1" -source = "git+https://github.com/risc0/risc0.git?branch=release-0.16#cbb6ccab08718506a524af999baa6d1386c51cbb" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf80df202c038efc2199be34fda8114b38bfc5b2b51c60cbbdf1f425b07b384" dependencies = [ "anyhow", "bytemuck", "cfg-if", - "dyn_partial_eq", "getrandom", "hex", - "libm", "log", "num-derive", "num-traits", + "risc0-binfmt", + "risc0-circuit-recursion", "risc0-circuit-rv32im", "risc0-core", "risc0-zkp", "risc0-zkvm-platform", + "rrs-lib", + "semver 1.0.21", "serde", - "thiserror", "tracing", - "typetag", ] [[package]] name = "risc0-zkvm-platform" -version = "0.16.1" -source = "git+https://github.com/risc0/risc0.git?branch=release-0.16#cbb6ccab08718506a524af999baa6d1386c51cbb" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dcd6b66f7a4972001db0acf3f06d99b7851c8d9f0de1f7e0fb4496c66c5cd02" +dependencies = [ + "bytemuck", + "getrandom", + "libm", +] + +[[package]] +name = "rle-decode-fast" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "rlp" @@ -2154,15 +2358,34 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rrs-lib" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4382d3af3a4ebdae7f64ba6edd9114fff92c89808004c4943b393377a25d001" +dependencies = [ + "downcast-rs", + "paste", +] + [[package]] name = "ruint" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +checksum = "608a5726529f2f0ef81b8fde9873c4bb829d6b5b5ca6be4d97345ddf0749c825" dependencies = [ "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", "proptest", "rand", + "rlp", "ruint-macro", "serde", "valuable", @@ -2181,79 +2404,72 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hex" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.21", ] [[package]] name = "rustix" -version = "0.38.17" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.6" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring", - "rustls-webpki 0.101.3", + "ring 0.17.7", + "rustls-webpki", "sct", ] [[package]] name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.2", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "ring", - "untrusted", + "base64 0.21.7", ] [[package]] name = "rustls-webpki" -version = "0.101.3" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -2276,15 +2492,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "cfg-if", "derive_more", @@ -2294,11 +2510,11 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", @@ -2312,12 +2528,12 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.7", + "untrusted 0.9.0", ] [[package]] @@ -2336,27 +2552,45 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.27.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" dependencies = [ "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" dependencies = [ "cc", ] [[package]] name = "semver" -version = "1.0.18" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] [[package]] name = "send_wrapper" @@ -2372,31 +2606,31 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.183" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "itoa", "ryu", "serde", @@ -2416,16 +2650,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.2.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1402f54f9a3b9e2efe71c1cea24e648acce55887983553eeb858cf3115acfd49" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ - "base64 0.21.2", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.0.0", + "indexmap 2.2.3", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -2433,25 +2668,25 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.2.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9197f1ad0e3c173a0222d3c4404fb04c3afe87e962bcb327af73e8301fa203c7" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -2461,7 +2696,7 @@ source = "git+https://github.com/risc0/RustCrypto-hashes?tag=sha2-v0.10.6-riscze dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -2470,15 +2705,19 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] [[package]] -name = "shlex" -version = "1.2.0" +name = "sha3-asm" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "bac61da6b35ad76b195eb4771210f947734321a8d81d7738e1580d953bc7a15e" +dependencies = [ + "cc", + "cfg-if", +] [[package]] name = "signal-hook-registry" @@ -2491,56 +2730,49 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core", ] [[package]] -name = "slab" -version = "0.4.8" +name = "simple_asn1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "autocfg", + "num-bigint", + "num-traits", + "thiserror", + "time", ] [[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "smol_str" -version = "0.2.0" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "serde", + "autocfg", ] [[package]] -name = "socket2" -version = "0.4.9" +name = "smallvec" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2549,11 +2781,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", @@ -2582,15 +2820,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] @@ -2625,15 +2863,42 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -2642,44 +2907,62 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.7.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "test-guest" version = "0.1.0" dependencies = [ - "k256", "risc0-zkvm", "zeth-lib", ] [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "thiserror-impl-no-std" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 1.0.109", +] + +[[package]] +name = "thiserror-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +dependencies = [ + "thiserror-impl-no-std", ] [[package]] @@ -2693,12 +2976,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.25" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", + "powerfmt", "serde", "time-core", "time-macros", @@ -2706,16 +2991,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -2745,9 +3031,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.30.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -2757,20 +3043,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] @@ -2785,9 +3071,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", @@ -2795,14 +3081,14 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots 0.23.1", + "webpki-roots", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2814,17 +3100,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -2837,11 +3145,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2849,20 +3156,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -2879,15 +3186,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.19.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", @@ -2901,38 +3208,19 @@ dependencies = [ "thiserror", "url", "utf-8", - "webpki", ] [[package]] name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "typetag" -version = "0.2.12" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aec6850cc671cd0cfb3ab285465e48a3b927d9de155051c35797446b32f9169f" -dependencies = [ - "erased-serde", - "inventory", - "once_cell", - "serde", - "typetag-impl", -] +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] -name = "typetag-impl" -version = "0.2.12" +name = "ucd-trie" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c49a6815b4f8379c36f06618bc1b80ca77aaf8a3fd4d8549dca6fdb016000f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "uint" @@ -2954,15 +3242,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2985,11 +3273,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -3040,9 +3334,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3050,24 +3344,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -3077,9 +3371,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3087,72 +3381,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" @@ -3177,12 +3437,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", ] [[package]] @@ -3191,82 +3451,149 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.7" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f495880723d0999eb3500a9064d8dbcf836460b24c17df80ea7b5794053aac" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -3280,7 +3607,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version", + "rustc_version 0.4.0", "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", @@ -3297,11 +3624,31 @@ dependencies = [ "tap", ] +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" dependencies = [ "zeroize_derive", ] @@ -3314,29 +3661,31 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.48", ] [[package]] name = "zeth-lib" version = "0.1.0" dependencies = [ + "alloy-primitives", "anyhow", + "bytes", "chrono", "ethers-core", "ethers-providers", "flate2", - "hashbrown 0.14.0", - "hex", + "hashbrown 0.14.3", + "libflate", "log", "once_cell", "revm", - "rlp", "ruint", "serde", "serde_json", "serde_with", "thiserror", + "thiserror-no-std", "tokio", "zeth-primitives", ] @@ -3358,5 +3707,5 @@ dependencies = [ "ruint", "serde", "sha3", - "thiserror", + "thiserror-no-std", ] diff --git a/testing/ef-tests/testguest/Cargo.toml b/testing/ef-tests/testguest/Cargo.toml index eefde440f..05f427530 100644 --- a/testing/ef-tests/testguest/Cargo.toml +++ b/testing/ef-tests/testguest/Cargo.toml @@ -6,12 +6,11 @@ edition = "2021" [workspace] [dependencies] -k256 = { version = "=0.13.1", features = ["std", "ecdsa"], default_features = false } -risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-0.16", default-features = false, features = ['std'] } +risc0-zkvm = { version = "0.19", default-features = false, features = ['std'] } zeth-lib = { path = "../../../lib", default-features = false } [patch.crates-io] # use optimized risc0 circuit crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risczero.0" } -k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.1-risczero.1" } -sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } \ No newline at end of file +k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" } +sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } diff --git a/testing/ef-tests/testguest/src/main.rs b/testing/ef-tests/testguest/src/main.rs index 48956a918..6abd32f6e 100644 --- a/testing/ef-tests/testguest/src/main.rs +++ b/testing/ef-tests/testguest/src/main.rs @@ -15,7 +15,10 @@ #![no_main] use risc0_zkvm::guest::env; -use zeth_lib::{block_builder::EthereumBlockBuilder, consts::ChainSpec}; +use zeth_lib::{ + builder::{BlockBuilderStrategy, EthereumStrategy}, + consts::ChainSpec, +}; risc0_zkvm::guest::entry!(main); @@ -25,10 +28,10 @@ pub fn main() { // Read the input previous block and transaction data let input = env::read(); // Build the resulting block - let output = EthereumBlockBuilder::build_from(&chain_spec, input) + let (header, state) = EthereumStrategy::build_from(&chain_spec, input) .expect("Failed to build the resulting block"); // Output the resulting block's hash to the journal - env::commit(&output.hash()); + env::commit(&header.hash()); // Leak memory, save cycles - core::mem::forget(output); + core::mem::forget((header, state)); } diff --git a/testing/ef-tests/tests/evm.rs b/testing/ef-tests/tests/evm.rs index c06321b2d..7abc2a862 100644 --- a/testing/ef-tests/tests/evm.rs +++ b/testing/ef-tests/tests/evm.rs @@ -17,15 +17,11 @@ use std::path::PathBuf; use rstest::rstest; -use zeth_lib::{ - block_builder::BlockBuilder, execution::ethereum::EthTxExecStrategy, - finalization::BuildFromMemDbStrategy, initialization::MemDbInitStrategy, - preparation::EthHeaderPrepStrategy, -}; -use zeth_primitives::block::Header; +use zeth_lib::builder::{BlockBuilderStrategy, EthereumStrategy}; +use zeth_primitives::{block::Header, trie::StateAccount}; use zeth_testeth::{ + create_input, ethers, ethtests::{read_eth_test, EthTestCase}, - *, }; #[rstest] @@ -41,50 +37,60 @@ fn evm( .try_init(); for EthTestCase { - json, + mut json, genesis, chain_spec, } in read_eth_test(path) { - let mut state = json.pre; - let mut parent_header = genesis; - let mut ancestor_headers = vec![]; - for block in json.blocks { - // skip failing tests for now - if let Some(message) = block.expect_exception { - println!("skipping ({})", message); - break; - } + // only one block supported for now + assert_eq!(json.blocks.len(), 1); + let block = json.blocks.pop().unwrap(); + + // skip failing tests for now + if let Some(message) = block.expect_exception { + println!("skipping ({message})"); + break; + } - let block_header = block.block_header.unwrap(); - let expected_header: Header = block_header.clone().into(); - assert_eq!(&expected_header.hash(), &block_header.hash); + let block_header = block.block_header.unwrap(); + let expected_header: Header = block_header.clone().into(); + assert_eq!(&expected_header.hash(), &block_header.hash); - let input = create_input( - &chain_spec, - state, - parent_header.clone(), - expected_header.clone(), - block.transactions, - block.withdrawals.unwrap_or_default(), - ); - let builder = BlockBuilder::new(&chain_spec, input) - .initialize_database::<MemDbInitStrategy>() - .unwrap() - .prepare_header::<EthHeaderPrepStrategy>() - .unwrap() - .execute_transactions::<EthTxExecStrategy>() - .unwrap(); - // update the state - state = builder.db().unwrap().into(); + // using the empty/default state for the input prepares all accounts for deletion + // this leads to larger input, but can never fail + let post_state = json.post.clone().unwrap_or_default(); - let result_header = builder.build::<BuildFromMemDbStrategy>().unwrap(); - // the headers should match - assert_eq!(result_header, expected_header); + let input = create_input( + &chain_spec, + genesis, + json.pre, + expected_header.clone(), + block.transactions, + block.withdrawals.unwrap_or_default(), + post_state, + ); - // update the headers - ancestor_headers.push(parent_header); - parent_header = block_header.into(); + let (header, state) = EthereumStrategy::build_from(&chain_spec, input).unwrap(); + + if let Some(post) = json.post { + let (exp_state, _) = ethers::build_tries(&post); + + println!("diffing state trie:"); + for diff in diff::slice( + &state.debug_rlp::<StateAccount>(), + &exp_state.debug_rlp::<StateAccount>(), + ) { + match diff { + diff::Result::Left(l) => println!("✗{l}"), + diff::Result::Right(r) => println!("✓{r}"), + diff::Result::Both(l, _) => println!(" {l}"), + } + } + assert_eq!(state.hash(), exp_state.hash()); } + + // the headers should match + assert_eq!(header, expected_header); + assert_eq!(header.hash(), expected_header.hash()); } } diff --git a/testing/ef-tests/tests/executor.rs b/testing/ef-tests/tests/executor.rs index 5ee0587be..f23b9fbe0 100644 --- a/testing/ef-tests/tests/executor.rs +++ b/testing/ef-tests/tests/executor.rs @@ -16,10 +16,7 @@ use std::path::PathBuf; -use risc0_zkvm::{ - serde::{from_slice, to_vec}, - Executor, ExecutorEnv, FileSegmentRef, -}; +use risc0_zkvm::{ExecutorEnv, ExecutorImpl, FileSegmentRef}; use rstest::rstest; use tempfile::tempdir; use zeth_primitives::{block::Header, BlockHash}; @@ -29,7 +26,7 @@ use zeth_testeth::{ guests::TEST_GUEST_ELF, }; -const SEGMENT_LIMIT_PO2: usize = 21; +const SEGMENT_LIMIT_PO2: u32 = 21; #[rstest] fn executor( @@ -55,7 +52,7 @@ fn executor( // skip failing tests for now if let Some(message) = block.expect_exception { - println!("skipping ({})", message); + println!("skipping ({message})"); break; } @@ -65,21 +62,24 @@ fn executor( let input = create_input( &chain_spec, - json.pre, genesis, + json.pre, expected_header.clone(), block.transactions, block.withdrawals.unwrap_or_default(), + json.post.unwrap(), ); let env = ExecutorEnv::builder() .session_limit(None) .segment_limit_po2(SEGMENT_LIMIT_PO2) - .add_input(&to_vec(&chain_spec).unwrap()) - .add_input(&to_vec(&input).unwrap()) + .write(&chain_spec) + .unwrap() + .write(&input) + .unwrap() .build() .unwrap(); - let mut exec = Executor::from_elf(env, TEST_GUEST_ELF).unwrap(); + let mut exec = ExecutorImpl::from_elf(env, TEST_GUEST_ELF).unwrap(); let segment_dir = tempdir().unwrap(); let session = exec @@ -89,8 +89,8 @@ fn executor( .unwrap(); println!("Generated {} segments", session.segments.len()); - let found_hash: BlockHash = from_slice(&session.journal).unwrap(); - println!("Block hash (from executor): {}", found_hash); + let found_hash: BlockHash = session.journal.decode().unwrap(); + println!("Block hash (from executor): {found_hash}"); assert_eq!(found_hash, expected_header.hash()); } } diff --git a/tests/geth/src/lib.rs b/tests/geth/src/lib.rs index 427517dd5..ae2edc597 100644 --- a/tests/geth/src/lib.rs +++ b/tests/geth/src/lib.rs @@ -4,7 +4,6 @@ use std::{ os::raw::c_char, }; -use hex; use serde::Deserialize; extern "C" { fn MptRoot(str: *const c_char) -> *const c_char; @@ -85,8 +84,8 @@ mod tests { let txs = r#" [{"essence":{"Eip1559":{"chain_id":167008,"nonce":23206,"max_priority_fee_per_gas":"0x0","max_fee_per_gas":"0x1","gas_limit":"0x3d090","to":{"Call":"0x1670080000000000000000000000000000010001"},"value":"0x0","data":"0xda69d3db5e05b7df7c06a03d7b463a5374c9e8aeb9f3dc21c152f7309cac258958f6b15dd80f1a3f49e593ac3be25fa60632d332f386880624acbee8edd73679adfc49ec00000000000000000000000000000000000000000000000000000000000ba5a10000000000000000000000000000000000000000000000000000000000e076be","access_list":[]}},"signature":{"v":1,"r":"0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","s":"0x64053f595237fe5f29377f48c113c40576452a7373c385d676a849c804d02144"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":8,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x2a351","to":{"Call":"0xf2f88e7ea95f2c93339484529dc7af9014657cfa"},"value":"0x0","data":"0xd37c353b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000036697066733a2f2f516d547463666873625363536b47453366324c633167517274394241415661464d6958586b353646316a64676d692f000000000000000000000000000000000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":0,"r":"0xee56a183c013a6901e9a309a449186118cd7c5cdd7487d6fa74257a31c1ef534","s":"0x6a5adba439ad3423a8c2a24ff60eb4efa897284dba946ad8ac025dd12063843b"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":7,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x1e4b0","to":"Create","value":"0x0","data":"0x608060405234801561000f575f80fd5b506101438061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632e64cec1146100385780636057361d14610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea26469706673582212207ca8a77a375aff548bc76892f6b2093ea5bec72e34f6638bcd6bc43f620679bc64736f6c63430008160033","access_list":[]}},"signature":{"v":1,"r":"0x8f822b9f11612d0ad65c31c03168f13c3be44615b3d99edfa744e367241e65b0","s":"0x178c11d68167aace32216f8a2163e6087d996b73c73c606e3e682d80cf5b678f"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":3,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x31752","to":{"Call":"0xd2c3cbb943fed0cfc8389b14a3f6df518fd46346"},"value":"0xb1a2bc2ec50000","data":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000b00aa1f0400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b0011e559da84dde3f841e22dc33f3adbf184d84a0000642a99837850543e223c134687f0c2b7e059873047000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0xa114a8306dc24ae55eabdb8693342bb96d38ec5e02bfcf49258d4fe1585142c1","s":"0x1431ab11ed548dd466edc5e759a3bec24f3ae07620f57d858fb77c9b246ce23a"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":49,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x9f96c","to":{"Call":"0x5cbfccd27db8a3981fe9965b0de59d436b2bd8b9"},"value":"0x13c3777bf34ffb","data":"0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000164883164560000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a0000000000000000000000002a99837850543e223c134687f0c2b7e05987304700000000000000000000000000000000000000000000000000000000000001f4fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdbb56fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdff620000000000000000000000000000000000000000000000000013c3777bf34ffb000000000000000000000000000000000000000000000000000000018e3ea11800000000000000000000000000000000000000000000000000139ae99228cb98000000000000000000000000000000000000000000000000000000018bb4e1fb000000000000000000000000a29b384a523729bc9e076013fa035ce9c2544cbc0000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0x689068e5ae1aa38507a8d58a8a7a2c93435bf79de56285b7579f5be542c2d76","s":"0x31eda5de43d522209ac8a01bd73e6c3c1c2e4b7a71df1671e683c8e78aa6f5dd"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":10,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x2a351","to":{"Call":"0x2fcc2e44ff836fbf5c3d7e9c2ebdf3b1509cf4f3"},"value":"0x0","data":"0xd37c353b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000036697066733a2f2f516d62507a4d336645634561365270424c4765694d686e4467734d4c7347447531334263324a6b514d6e77564c482f000000000000000000000000000000000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0x7f1f660b98f4600538e0c59b2eda352bce86a8229e4c32a7e5ff36373b44afb6","s":"0xd74d693dfd28bb0a611d34f5d38c1c5350aaa07a92890d065cf8705f90fd44f"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":3,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x7e0c3","to":{"Call":"0x5cbfccd27db8a3981fe9965b0de59d436b2bd8b9"},"value":"0x66dea16dd4bad73","data":"0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000164883164560000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a000000000000000000000000d69d3e64d71844bbdda51cd7f23ed3631e9fac490000000000000000000000000000000000000000000000000000000000002710fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2766000000000000000000000000000000000000000000000000000000000000d89a0000000000000000000000000000000000000000000000000066dea16dd4bad73000000000000000000000000000000000000000000000046791fc84e07cff5100000000000000000000000000000000000000000000000000669d0a34d4952160000000000000000000000000000000000000000000000464bf6fe6f905302050000000000000000000000003da1db2cfab5f12239ee5ed0c84112a3ac6ed14b0000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":0,"r":"0xeb181f0b30e077e595db6b4141e51e31fc77ad6050fee4b93c714e679db5ed40","s":"0x7e8dadd2fd476a3da2163f553402c97fbacbf621ac35680bca4cde75752f5cd2"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":7,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x1e4b0","to":"Create","value":"0x0","data":"0x608060405234801561000f575f80fd5b506101438061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632e64cec1146100385780636057361d14610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea2646970667358221220175b5ff5925b348b9f880264a1038aba4c999696f4387cf13a5ccdd35467fb6664736f6c63430008160033","access_list":[]}},"signature":{"v":0,"r":"0x92186532daaee1105b32dc5d9f6a08af177fa70a1c82b78f4f300f7883bc05da","s":"0x60446c625c010ec561f7e58ce5c9e5397552c43c47cb21360dda05084d38a44d"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":4,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x1e4a4","to":"Create","value":"0x0","data":"0x608060405234801561000f575f80fd5b506101438061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632e64cec1146100385780636057361d14610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea2646970667358221220af7008041a150a4ff43e9709ce67654cf19205ed004c4b1fe1500399ade56b9b64736f6c63430008170033","access_list":[]}},"signature":{"v":0,"r":"0x258ee024e15f77051480ac80950a73fa01a63ad3177d4ec7c95777799bf63f03","s":"0x40153d47a283de30db685aa6ae382f55415bd22733c115c6dc089fc8a5115bd7"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":5,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0xf34f","to":{"Call":"0xd69d3e64d71844bbdda51cd7f23ed3631e9fac49"},"value":"0x0","data":"0x095ea7b30000000000000000000000005cbfccd27db8a3981fe9965b0de59d436b2bd8b9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","access_list":[]}},"signature":{"v":1,"r":"0x6262be6fbbf8faabf3dade6f0ed9db7c0c640a5c4635e69f917858c9fbb83377","s":"0x4d18809f51c9f0ac05b6d11c7a9fad3cc188737a63e7c27dfc37c9dc2bf1fb33"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":34,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x2cae4","to":{"Call":"0xd2c3cbb943fed0cfc8389b14a3f6df518fd46346"},"value":"0x11c37937e080000","data":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000011c37937e08000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000000000119a5a1d9e00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b0011e559da84dde3f841e22dc33f3adbf184d84a0000642a99837850543e223c134687f0c2b7e059873047000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0x229795aac8a953809780d882cef11783aa775a348e13e13937990c15fbb0c4e","s":"0x1071aba3e0a1a1f91dd2d4aaacd4077c292cfbaf5dbb77e5290b226e9b06708a"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":0,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x18e05","to":{"Call":"0x1670080000000000000000000000000000000001"},"value":"0x26c6036ae68a20","data":"0x33bcd0cc0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000085841072598236162b9a8fff9f254a9e20e9cc2d0000000000000000000000000000000000000000000000000000000000028c60000000000000000000000000000000000000000000000000000000000000426800000000000000000000000085841072598236162b9a8fff9f254a9e20e9cc2d00000000000000000000000085841072598236162b9a8fff9f254a9e20e9cc2d00000000000000000000000085841072598236162b9a8fff9f254a9e20e9cc2d000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000033f10fb258a2000000000000000000000000000000000000000000000000000000000000222e0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0x69c02aca8c3ebb8f3ab9c3ec247538acc3c600d4a80b155a1a97f76124c3e9be","s":"0x71fe7065af839d2e7722430d162b806bec0ff65f3a7d70e39a0b9e8deb163a47"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":9,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x1c71f","to":{"Call":"0x5d04bc3ddd8bcbdf3ff71ab39e62287ca185ae3e"},"value":"0x38d7ea4c68000","data":"0x1249c58b","access_list":[]}},"signature":{"v":0,"r":"0xfb9511460ab049660f2481840ed862642528247687800f9f00fb2b7b73a2c8bf","s":"0x2c38d48b31721f3893228be8705e0b34b5774011a5fdb54f3d3a18560a3955c5"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":3,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0xf34f","to":{"Call":"0xd69d3e64d71844bbdda51cd7f23ed3631e9fac49"},"value":"0x0","data":"0x095ea7b30000000000000000000000005cbfccd27db8a3981fe9965b0de59d436b2bd8b9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff","access_list":[]}},"signature":{"v":1,"r":"0x4c2a32a6a6747c871566ecf4415255fd94ee3c2f648f46b8ea15cb509ffafed8","s":"0x58c53a01ff12fd74f21f00fea903753dd632c0c6a5763ccf680f9e9c8ca242e9"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":18,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0xfc55","to":{"Call":"0x5535b9ff2aa1a63b87921599850cb942f813323b"},"value":"0x0","data":"0xa9059cbb000000000000000000000000eb15859af4dc37738f32e8bb468eb1ffde5bd7a700000000000000000000000000000000000000000000021e19e0c9bab2400000","access_list":[]}},"signature":{"v":1,"r":"0xac3db1534cf8dad72a8cb063c3fea0d1d0f48d27ab0c7f8eb16054e9324395f","s":"0x458c8ac58e9f6d94dc64d59747b56cbfeef3b2db7ba0db8ccadc049d84069337"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":13,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0xd105","to":{"Call":"0xae2c46ddb314b9ba743c6dee4878f151881333d9"},"value":"0x0","data":"0x095ea7b3000000000000000000000000fde807b7c79f69f22622acb73db5b59654e115b60000000000000000000000000000000000000000000000000000000000402a73","access_list":[]}},"signature":{"v":1,"r":"0x907171397fefd07536584acf07fa05e113b7887964aa802761b7c8b161fe1bcd","s":"0x3be9419669fc93e94f68f3cd67edb941b0d6257a63ef860e9bb44eb2311d105"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":2,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x66a97","to":{"Call":"0x1670080000000000000000000000000000000001"},"value":"0x0","data":"0x0138240800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001d764000000000000000000000000d209f6fbdce2462f3063321be3b96b481b44858100000000000000000000000000000000000000000000000000000000000042680000000000000000000000000000000000000000000000000000000000028c60000000000000000000000000d209f6fbdce2462f3063321be3b96b481b448581000000000000000000000000d209f6fbdce2462f3063321be3b96b481b448581000000000000000000000000d209f6fbdce2462f3063321be3b96b481b4485810000000000000000000000000000000000000000000000000011c37937e080000000000000000000000000000000000000000000000000000004cbd15e801ba000000000000000000000000000000000000000000000000000000000000222e0000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000167008000000000000000000000000000001000100000000000000000000000000000000000000000000000000000000000ba59a000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000896f90893b90214f90211a04d338534c32c08dad0c101fe1798d1203edb20b2549468d1a3bb35237f5b76d9a04f2a94632c3c16f5c0f122731eb2beb951b7acf3249414e7a7ccd04597d9c746a0685df84299cc6f9ac64b2f49e2568d02f0c77aad28ee5b664748192e11f007a4a0aee90207fdca1d5e1ea19b61c3ae4b24d7e1aa4b7d283e66029e191e709c864ba0f197a203c989c02cd7f150f0596333d98079e4db2975a53cff67892497c208fda06303c9fce964d57aefb2bc0933477768fd48159deae37fac7138c2a3b8d51cc4a0179b035da2f9fdbb8bcf0ecafefb8d192ad5461aa146c6a690d20f1aa2089cd2a03e7b4458cbef3a6450004d40e0405a5571d030aa726d3a3da927d2a0ad1ba2fca0a4fffc6f869f6fc1bd00e64717df0035bee73e053c8ffa2765fbe2b27e86b3cfa06a1c14b1afbc465cc1553022116dde190b75cb92f8934074d2d046819d96dc00a0ec5386f3963a273e86060b6331ffc6c80a0df3a62e80193e6b9e66fe3262a9a6a066b2ff83824da5d831dda93c61a86c1b7a47492e558812878fcac7eacc1fa2a3a0e956b036b9570e798ddd15d9addf05a9f4e6a59bb3fd390ee86af1ba59632399a07ba682d91356cbb366314dab761dc97f6b53408562220b6233d2d7548d8522c4a0d9193d56e31ed7c50961592f21100febfcad45483fdd936d90ea8d8a2373cd31a0c9c8646daffe4b5fd7034deb172d4395462b3d72496a694c932b2908d1b2fbf780b90214f90211a0696c733cbbb18499e545004214dcb110a35c6acd9ce77461f9ab4f2f30311203a002b91aaa1ce363e25b95e77273990878898c9b100f038b51eafbf1f96baeae89a0984508740e4512154619a4274f22b5cdee7eb6e4faae6ed5741d4b6027390152a06e3344335cd57cda7affb56155f126c60d2fe34b02e9ce8d2be7f1f88ab98a10a0b3254e9ef461f192c9044a65d0e60b304142649ad2057c684f1f26acdae63230a00a65ce7f2398a7a71eb158670acfc4f349e0152a5020d6dc279c4447ef8e9793a001049d0c5ebac806f9189934025ed3820c83fc7ccb4a67387a46fc53d0e9e3b8a0f4e9edeb76a7f2d1e1f70c315cfd2c642c48a4b2e53d6f9fdac1f26d1ad3f60aa03059144b248f828a560a3fec36da447b4f22cc81910db5690d5587844cbd7948a010a11769e51f30f523001bbdcd72370d8e2eea566889ccdd02cde1e3a7beb1ada010c9a764788bf1b6891589350c4f38847832992fe94351fb212d6b33baf0a283a0c4da0adc3b7b82324a20d15760487a79562cbb0f406bc8559e1deab5cee3f260a0e6a50d75e9499e7ba4bf0ed5b45ccee2935088aed62450406228c84c3c317b22a0c206a8a4627daf678da0710496c28dfb2a09e2ed9d10c4d78dcd992a733b8e4aa00e7a51b5c5a86483925750dc1eb0b6b2e597d59ca5e402a4bcd3010fcbb4b0bfa0f565212ee1fdbbc53a29832495f71ac368c06c1b2988d64fd6588dd69ddd020880b90214f90211a0094855f0e01800ab91e234bd4475b26cee6c4b8bafd93be4890bcbe6aa929819a0091bb2ef5ab4862381362ffb0378c9d30e05eae96211f8527e3e255747886d33a0faaf2e875df5fdd723e58919ed9caf447dc6c0248a25f8fd0cb148e04215872ba0db91cc17d08bd087d67a236984c2e12cc9ddea4862a1a21a13f478dc6c80d855a0733fddac882f5a7fce648478daf566988aeefc82a668e4fc1857d98199b01b66a0db33be24d2f3f3df19167f1a08dceb543d47e4530f334eb73bd10fad7e945cfaa0de73de062cab691c79044a024d499c53ef2306afb3955781bb4791d3f95460f0a0f9e7845a7fc516fb1fb3697045addcc638cf084e8ac5810ac70022a4c7c670c7a0a0c2f93c0c5718871c846ca47f449833f131610146ff59e1d943aed70eb79712a04ce6d5d782d3d61cb9c3160ceadcf377384c31a49ed0ac427e26cbf04691586ba0a49205927873adfa81c1dfc05ce22459e3e9ad7c7aa43111d3238412aa70de5fa095fcb28f0fa956ba3236f7cb2bf5f0240ce26671bd2805bd42f9739ccb027814a060d982e4f8d08a0ac23f2f05436d086fa2491a1927162bb2cc02059fcc421090a0612c90d3cd9d84e9baded563e523cef2fbe63fe7713fb666628e6123236e5313a02cbc5e9b1fee2358f2b3a12049778e5c7f8589d1dd8077a98fe549c2969fbdbda0d81256248ec40a800150815c2880135654a428b67665f7d1a2767736e371536f80b901d4f901d1a06e6a5be3912d1e57cd7e62c8cd26bac69f85d985238bdee92dc3d1e76217f021a074e4d0d470f4e14d7039a8bf622f28d71727b857815a26d9116b612e0ad6062580a0dd3b2dd3e45de43392021d9b6711ae51f0ddce380706cc873854eb79fa9702c1a0e6c2420f1bbca66b295de1b50c8098283df205e36b21cbd1fe28bb8f5d478a84a0e08af052acaad51a6ca56e8eab502daaaf1750992c8f75dce02e4064cd6e67c5a07da506423017a0796f02c910edc631dc0efb8a5bcc8f4b71b2a4c7d53ba5afb6a0ef5fdc8453a143c403bb1db38026bb78edfd4e86f4c4af1c0a7d2712c21c573ba05b392046508ce0894d57c3e44579eb71ff03a73d06b7e39894aa0dc39b9cba09a0dfcec8f3f4d10129f4d14843f3c957f2b16d1e262efd72b9babcf997d280527fa070ce02729dbe2f92a5f612e3602f020ed7f7b88c7f288e3112128d53505c6848a04f94d5e5a1a910d400f378e3dadf2cc07c0aa2e637d86b09922a0f8477f8bc5a80a012afb69e9d1de641d4d4f91fe240d7104e594c15a282f51097771908b92153b7a03ef659dcc44ebd4fd09a9d4f98ffe749a41d8b1a4bfead679dcfa6e92c25b2dda0af80c4e08ac9185959a41fe015532674a0b1b030735d6ba8991e59a7b617c53e80b853f851808080a00224e1fdf1ab331b3134156b057cb59d9bc1428229a324ccc7a1261676e7557c80808080808080a0e6e623c65732560b5e311649960f5ae5c9d9c93da90f3f41b98bc3f52a5c50798080808080a1e09e3bf3e72547899f614325047fbbd95aa7d3089f0a4fe8da9293a0b34e5ec901000000000000000000000000000000000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":0,"r":"0xf622a174fc4803fd9c5f3581671487e0e1ccbec5c7128f231252a81364ab772d","s":"0x71cadc95edcfd863e0192cebee1b867212b5662f75ddc3d11e8d2450bb1c5f4"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":2,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0xc989","to":{"Call":"0xd69d3e64d71844bbdda51cd7f23ed3631e9fac49"},"value":"0x0","data":"0x095ea7b3000000000000000000000000fde807b7c79f69f22622acb73db5b59654e115b6000000000000000000000000000000000000000000000000d02ab486cedc0000","access_list":[]}},"signature":{"v":0,"r":"0x374cacab8e656e89b5726c0a62b0184a9922e58b625abfe442333affef9cb5d2","s":"0x3fc455c2876bb3c5c6c33550459e6a5da56a444327577ffea74851bf5554e3da"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":6,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x7e07b","to":{"Call":"0x5cbfccd27db8a3981fe9965b0de59d436b2bd8b9"},"value":"0x221b262dd8000","data":"0xac9650d800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000164883164560000000000000000000000000011e559da84dde3f841e22dc33f3adbf184d84a000000000000000000000000d69d3e64d71844bbdda51cd7f23ed3631e9fac490000000000000000000000000000000000000000000000000000000000002710fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2766000000000000000000000000000000000000000000000000000000000000d89a0000000000000000000000000000000000000000000000000000221b262dd8000000000000000000000000000000000000000000000000000175d77c37d3e32d00000000000000000000000000000000000000000000000000002205671c88644000000000000000000000000000000000000000000000000174e7ed3b949b396000000000000000000000000b56d486406d4c8e7af2da8b79eab680309f7b0030000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000412210e8a00000000000000000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":1,"r":"0xe969a2e7c314fc6a369e72b2ce5bd99340f81072d5ef42cec35cc7510d4f1f06","s":"0x75b5f0a869c54fd473023cc362edb2dc1ae41868a11e4c29efde300fd9ae6eb"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":3,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x1e4b0","to":"Create","value":"0x0","data":"0x608060405234801561000f575f80fd5b506101438061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c80632e64cec1146100385780636057361d14610056575b5f80fd5b610040610072565b60405161004d919061009b565b60405180910390f35b610070600480360381019061006b91906100e2565b61007a565b005b5f8054905090565b805f8190555050565b5f819050919050565b61009581610083565b82525050565b5f6020820190506100ae5f83018461008c565b92915050565b5f80fd5b6100c181610083565b81146100cb575f80fd5b50565b5f813590506100dc816100b8565b92915050565b5f602082840312156100f7576100f66100b4565b5b5f610104848285016100ce565b9150509291505056fea26469706673582212207ca8a77a375aff548bc76892f6b2093ea5bec72e34f6638bcd6bc43f620679bc64736f6c63430008160033","access_list":[]}},"signature":{"v":1,"r":"0x8faabd527d388ef1a925df7b6cda79a8db15fa4841c7b65cefb9feb79bc0c191","s":"0x6da937d631650c8f0583d449c5a7d963a0868dc24eb86f31895111bf6b77fcec"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":1,"max_priority_fee_per_gas":"0x59682f00","max_fee_per_gas":"0x59682f01","gas_limit":"0x47eb9","to":{"Call":"0xd2c3cbb943fed0cfc8389b14a3f6df518fd46346"},"value":"0xb1a2bc2ec500000","data":"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065a970bc00000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000b1a2bc2ec5000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000006a94d74f4300000000000000000000000000000000000000000000000000000000000000132060300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b0011e559da84dde3f841e22dc33f3adbf184d84a0001f4ae2c46ddb314b9ba743c6dee4878f151881333d9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000470de4df82000000000000000000000000000000000000000000000000000000000000000cbd3eb00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b0011e559da84dde3f841e22dc33f3adbf184d84a000bb8ae2c46ddb314b9ba743c6dee4878f151881333d9000000000000000000000000000000000000000000","access_list":[]}},"signature":{"v":0,"r":"0x400e80dc05b0d0500951513bf209257e00c71b28d4e777b9f1dd4dae7f898489","s":"0x7a8fa0a5ebcc6d924a6c1875efdf7242786ac03e1a84101eeb833458608742d7"}},{"essence":{"Eip1559":{"chain_id":167008,"nonce":802,"max_priority_fee_per_gas":"0x186a0","max_fee_per_gas":"0x186a1","gas_limit":"0x55200","to":{"Call":"0xbfcfd6fb89c1fdd0d87c3d24dd8fc1d371203651"},"value":"0x0","data":"0x783cf332000000000000000000000000000000000000000000000000000000395bb93400","access_list":[]}},"signature":{"v":0,"r":"0xcbaea2f53ed121fa6d314add4cb619dd971e0578bec2b1c9d91912d54f7bac7a","s":"0x6c7819f144ff6caba3c31ed383fde2c6f8eed40a3dfbff5183f00fb2095ff208"}}] "#; - let rust_root = rust_mtp_root(&txs).unwrap(); - let go_root = go_mtp_root(&txs).unwrap(); + let rust_root = rust_mtp_root(txs).unwrap(); + let go_root = go_mtp_root(txs).unwrap(); assert_eq!(rust_root.rlps.len(), go_root.rlps.len()); println!("rust_root.rlps[0]: {}", rust_root.rlps[0]);