diff --git a/.github/labeler.yml b/.github/labeler.yml index cc9748609..72647b77b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -23,12 +23,17 @@ - changed-files: - any-glob-to-any-file: zero_bin/** -# Add 'specs' label to any changes within 'docs' folder. +# Add 'specs' label to any changes within 'docs' or `book` folder. 'specs': - changed-files: - - any-glob-to-any-file: docs/** + - any-glob-to-any-file: ['docs/**', 'book/**'] # Add 'crate: common' label to any changes within 'common' folder. 'crate: common': - changed-files: - any-glob-to-any-file: common/** + +# Add 'ci' label to any changes within '.github' folder. +'ci': + - changed-files: + - any-glob-to-any-file: .github/** diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 45c47716e..aef229b3f 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -3,9 +3,12 @@ name: zkEVM mdbook on: push: branches: [develop, main] + pull_request: + branches: + - "**" jobs: - deploy: + build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -20,15 +23,37 @@ jobs: command: install args: mdbook - - name: Install mdbook-katex and mdbook-bib + - name: Install preprocessors uses: actions-rs/cargo@v1 with: command: install - args: mdbook-katex mdbook-bib + args: mdbook-katex mdbook-bib mdbook-mermaid + + - name: Initialize mermaid preprocessor + run: mdbook-mermaid install book - name: Build book run: mdbook build book + - name: Upload built book + uses: actions/upload-artifact@v3 + with: + name: built-mdbook + path: ./book/book + + deploy: + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop') + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + + - name: Download built book + uses: actions/download-artifact@v3 + with: + name: built-mdbook + path: ./book/book + - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43c0c1254..1f34dd53f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: name: Test mpt_trie runs-on: ubuntu-latest timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -47,7 +47,12 @@ jobs: name: Test trace_decoder runs-on: zero-ci timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + env: + RUST_LOG: info + CARGO_INCREMENTAL: 1 + RUST_BACKTRACE: 1 + + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -60,18 +65,17 @@ jobs: with: cache-on-failure: true - - name: Test in trace_decoder subdirectory + - name: build # build separately so test logs are actually nice + run: cargo build --tests --manifest-path trace_decoder/Cargo.toml + + - name: test run: cargo test --release --manifest-path trace_decoder/Cargo.toml -- --nocapture - env: - RUST_LOG: info - CARGO_INCREMENTAL: 1 - RUST_BACKTRACE: 1 test_proof_gen: name: Test proof_gen runs-on: ubuntu-latest timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -96,7 +100,7 @@ jobs: name: Test evm_arithmetization runs-on: ubuntu-latest timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -121,7 +125,7 @@ jobs: name: Test zero_bin runs-on: ubuntu-latest timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -132,7 +136,7 @@ jobs: - name: Set up rust cache uses: Swatinem/rust-cache@v2 with: - cache-on-failure: true + cache-on-failure: true - name: Test in zero_bin subdirectory run: | @@ -154,7 +158,7 @@ jobs: name: Test zk_evm_proc_macro runs-on: ubuntu-latest timeout-minutes: 30 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -218,7 +222,7 @@ jobs: name: Rustdoc, Formatting and Clippy runs-on: ubuntu-latest timeout-minutes: 10 - if: "! contains(toJSON(github.event.commits.*.message), '[skip-ci]')" + if: ${{ ! contains(toJSON(github.event.commits.*.message), '[skip-ci]') }} steps: - name: Checkout sources uses: actions/checkout@v4 @@ -237,10 +241,10 @@ jobs: run: cargo fmt --all --check - name: Run cargo clippy - run: cargo clippy --all-features --all-targets -- -D warnings -A incomplete-features + run: cargo clippy --all-targets -- -D warnings -A incomplete-features - name: Run cargo clippy (with `cdk_erigon` flag) - run: cargo clippy --all-features --all-targets --features cdk_erigon -- -D warnings -A incomplete-features + run: cargo clippy --all-targets --no-default-features --features cdk_erigon -- -D warnings -A incomplete-features - name: Rustdoc run: cargo doc --all diff --git a/.gitignore b/.gitignore index 667ccfc9c..f035f7002 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,5 @@ *.iml .idea/ .vscode +/**/*.ignoreme **/output.log - diff --git a/Cargo.lock b/Cargo.lock index dcd859ede..1791c16ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aes" @@ -58,14 +58,15 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eadd758805fe353ea8a520da4531efb9739382312c354d3e90b7a16353b0315" +checksum = "c37d89f69cb43901949ba29307ada8b9e3b170f94057ad4c04d6fd169d24d65f" dependencies = [ "alloy-consensus", "alloy-core", "alloy-eips", "alloy-json-rpc", + "alloy-network", "alloy-provider", "alloy-rpc-client", "alloy-rpc-types", @@ -75,9 +76,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07629a5d0645d29f68d2fb6f4d0cf15c89ec0965be915f303967180929743f" +checksum = "2b4f201b0ac8f81315fbdc55269965a8ddadbc04ab47fa65a1a468f9a40f7a5f" dependencies = [ "num_enum", "strum", @@ -95,9 +96,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7198a527b4c4762cb88d54bcaeb0428f4298b72552c9c8ec4af614b4a4990c59" +checksum = "1468e3128e07c7afe4ff13c17e8170c330d12c322f8924b8bf6986a27e0aad3d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -109,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6dbb79f4e3285cc87f50c0d4be9a3a812643623b2e3558d425b41cbd795ceb" +checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -141,9 +142,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159eab0e4e15b88571f55673af37314f4b8f17630dc1b393c3d70f2128a1d494" +checksum = "0c35df7b972b06f1b2f4e8b7a53328522fa788054a9d3e556faf2411c5a51d5a" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -151,6 +152,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "c-kzg", + "derive_more", "once_cell", "serde", "sha2", @@ -158,9 +160,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7733446dd531f8eb877331fea02f6c40bdbb47444a17dc3464bf75319cc073a" +checksum = "8866562186d237f1dfeaf989ef941a24764f764bf5c33311e37ead3519c6a429" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -172,9 +174,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b80851d1697fc4fa2827998e3ee010a3d1fc59c7d25e87070840169fcf465832" +checksum = "abe714e233f9eaf410de95a9af6bcd05d3a7f8c8de7a0817221e95a6b642a080" dependencies = [ "alloy-consensus", "alloy-eips", @@ -193,10 +195,11 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76a2336889f3d0624b18213239d27f4f34eb476eb35bef22f6a8cc24e0c0078" +checksum = "8c5a38117974c5776a45e140226745a0b664f79736aa900995d8e4121558e064" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-serde", "serde", @@ -204,9 +207,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a767e59c86900dd7c3ce3ecef04f3ace5ac9631ee150beb8b7d22f7fa3bbb2d7" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" dependencies = [ "alloy-rlp", "bytes", @@ -226,9 +229,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d2a195caa6707f5ce13905794865765afc6d9ea92c3a56e3a973c168d703bc" +checksum = "c65633d6ef83c3626913c004eaf166a6dd50406f724772ea8567135efd6dc5d3" dependencies = [ "alloy-chains", "alloy-consensus", @@ -245,7 +248,7 @@ dependencies = [ "async-stream", "async-trait", "auto_impl", - "dashmap 6.0.1", + "dashmap 6.1.0", "futures", "futures-utils-wasm", "lru", @@ -278,14 +281,14 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "alloy-rpc-client" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed31cdba2b23d71c555505b06674f8e7459496abfd7f4875d268434ef5a99ee6" +checksum = "d5fc328bb5d440599ba1b5aa44c0b9ab0625fbc3a403bb5ee94ed4a01ba23e07" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -297,16 +300,16 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-rpc-types" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d758f65aa648491c6358335c578de45cd7de6fdf2877c3cef61f2c9bebea21" +checksum = "8f8ff679f94c497a8383f2cd09e2a099266e5f3d5e574bc82b4b379865707dbb" dependencies = [ "alloy-rpc-types-eth", "alloy-rpc-types-trace", @@ -316,9 +319,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ba05d6ee4db0d89113294a614137940f79abfc2c40a9a3bee2995660358776" +checksum = "9a59b1d7c86e0a653e7f3d29954f6de5a2878d8cfd1f010ff93be5c2c48cd3b1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -335,9 +338,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2af822ed58f2b6dd7cfccf88bf69f42c9a8cbf4663316227646a8a3e5a591f" +checksum = "c54375e5a34ec5a2cf607f9ce98c0ece30dc76ad623afeb25d3953a8d7d30f20" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -349,9 +352,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd260ede54f0b53761fdd04133acc10ae70427f66a69aa9590529bbd066cd58" +checksum = "51db8a6428a2159e01b7a43ec7aac801edd0c4db1d4de06f310c288940f16fd3" dependencies = [ "alloy-primitives", "serde", @@ -360,9 +363,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5193ee6b370b89db154d7dc40c6a8e6ce11213865baaf2b418a9f2006be762" +checksum = "bebc1760c13592b7ba3fcd964abba546b8d6a9f10d15e8d92a8263731be33f36" dependencies = [ "alloy-primitives", "async-trait", @@ -374,56 +377,56 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183bcfc0f3291d9c41a3774172ee582fb2ce6eb6569085471d8f225de7bb86fc" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c4d842beb7a6686d04125603bc57614d5ed78bf95e4753274db3db4ba95214" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" dependencies = [ "alloy-sol-macro-input", "const-hex", - "heck 0.5.0", + "heck", "indexmap", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1306e8d3c9e6e6ecf7a39ffaf7291e73a5f655a2defd366ee92c2efebcdf7fee" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" dependencies = [ "const-hex", "dunce", - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "syn-solidity", ] [[package]] name = "alloy-sol-types" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "577e262966e92112edbd15b1b2c0947cc434d6e8311df96d3329793fe8047da9" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -432,9 +435,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "454220c714857cf68af87d788d1f0638ad8766268b94f6a49fed96cbc2ab382c" +checksum = "fd5dc4e902f1860d54952446d246ac05386311ad61030a2b906ae865416d36e0" dependencies = [ "alloy-json-rpc", "base64", @@ -444,22 +447,22 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.5.1", "tracing", "url", ] [[package]] name = "alloy-transport-http" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377f2353d7fea03a2dba6b9ffbb7d610402c040dd5700d1fae8b9ec2673eed9b" +checksum = "1742b94bb814f1ca6b322a6f9dd38a0252ff45a3119e40e888fb7029afa500ce" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.5.1", "tracing", "url", ] @@ -569,9 +572,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" dependencies = [ "backtrace", ] @@ -730,7 +733,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "synstructure", ] @@ -742,7 +745,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -754,7 +757,7 @@ dependencies = [ "assert2-macros", "diff", "is-terminal", - "yansi", + "yansi 1.0.1", ] [[package]] @@ -766,7 +769,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -783,9 +786,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", @@ -853,7 +856,7 @@ dependencies = [ "futures-lite 2.3.0", "parking", "polling 3.7.3", - "rustix 0.38.35", + "rustix 0.38.37", "slab", "tracing", "windows-sys 0.59.0", @@ -910,7 +913,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -921,13 +924,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -953,7 +956,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -990,7 +993,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -1033,17 +1036,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -1237,9 +1240,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.15" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" dependencies = [ "shlex", ] @@ -1289,9 +1292,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -1299,9 +1302,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -1315,10 +1318,10 @@ version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1413,12 +1416,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "cookie-factory" version = "0.3.3" @@ -1427,9 +1424,9 @@ checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" [[package]] name = "copyvec" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540a4ee3e6ea3547a6b492c5e76a4b9086e6b41178061e03ca82fc385912b1f" +checksum = "8aba112395a3627b61476b950f4c015964642a1d32a44112ad97c4a781dab81f" [[package]] name = "core-foundation" @@ -1449,9 +1446,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1613,7 +1610,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1624,7 +1621,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1642,9 +1639,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1695,7 +1692,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1720,46 +1717,54 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "derive_builder_macro" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" dependencies = [ "derive_builder_core", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "derive_more" -version = "0.99.18" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.76", + "syn 2.0.77", + "unicode-xid", ] [[package]] @@ -1827,7 +1832,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -1901,14 +1906,14 @@ checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "enum-as-inner" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2075,6 +2080,7 @@ dependencies = [ "static_assertions", "thiserror", "tiny-keccak", + "zk_evm_common", "zk_evm_proc_macro", ] @@ -2281,7 +2287,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -2346,21 +2352,21 @@ dependencies = [ [[package]] name = "getset" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +checksum = "f636605b743120a8d32ed92fc27b6cde1a769f8f936c065151eb66f88ded513c" dependencies = [ - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "glob" @@ -2424,12 +2430,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2562,9 +2562,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -2575,7 +2575,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.7", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -2647,9 +2647,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -2696,9 +2696,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" [[package]] name = "is-terminal" @@ -2794,9 +2794,9 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422fbc7ff2f2f5bdffeb07718e5a5324dca72b0c9293d50df4026652385e3314" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -2859,7 +2859,6 @@ dependencies = [ "axum", "cargo_metadata", "clap", - "dotenvy", "evm_arithmetization", "futures", "ops", @@ -2876,6 +2875,7 @@ dependencies = [ "tracing-subscriber", "vergen", "zero_bin_common", + "zk_evm_common", ] [[package]] @@ -2929,7 +2929,7 @@ checksum = "cb26336e6dc7cc76e7927d2c9e7e3bb376d7af65a6f56a0b16c47d18a9b1abc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3005,11 +3005,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -3198,7 +3198,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3221,9 +3221,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -3272,7 +3272,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3302,9 +3302,9 @@ dependencies = [ "paladin-core", "proof_gen", "serde", - "trace_decoder", "tracing", "zero_bin_common", + "zk_evm_common", ] [[package]] @@ -3381,7 +3381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af25dcb10b7c0ce99abee8694e2e79e4787d7f778b9339dc5a50ba6fc45e5cc9" dependencies = [ "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3412,9 +3412,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -3472,9 +3472,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" dependencies = [ "memchr", "thiserror", @@ -3483,9 +3483,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" dependencies = [ "pest", "pest_generator", @@ -3493,22 +3493,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "pest_meta" -version = "2.7.11" +version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" dependencies = [ "once_cell", "pest", @@ -3532,7 +3532,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -3679,9 +3679,9 @@ source = "git+https://github.com/0xPolygonZero/plonky2.git?rev=dc77c77f2b06500e1 [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -3692,15 +3692,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] @@ -3731,7 +3731,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.35", + "rustix 0.38.37", "tracing", "windows-sys 0.59.0", ] @@ -3764,6 +3764,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi 0.5.1", +] + [[package]] name = "pretty_env_logger" version = "0.5.0" @@ -3807,27 +3817,25 @@ dependencies = [ ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.109", - "version_check", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ + "proc-macro-error-attr2", "proc-macro2", "quote", - "version_check", + "syn 2.0.77", ] [[package]] @@ -3849,6 +3857,7 @@ dependencies = [ "paste", "plonky2", "serde", + "zk_evm_common", ] [[package]] @@ -3893,6 +3902,7 @@ dependencies = [ "trace_decoder", "tracing", "zero_bin_common", + "zk_evm_common", ] [[package]] @@ -3997,9 +4007,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ "bitflags 2.6.0", ] @@ -4158,6 +4168,7 @@ name = "rpc" version = "0.1.0" dependencies = [ "alloy", + "alloy-compat", "anyhow", "cargo_metadata", "clap", @@ -4168,17 +4179,19 @@ dependencies = [ "itertools 0.13.0", "mpt_trie", "primitive-types 0.12.2", + "proof_gen", "prover", "serde", "serde_json", "tokio", - "tower", + "tower 0.4.13", "trace_decoder", "tracing", "tracing-subscriber", "url", "vergen", "zero_bin_common", + "zk_evm_common", ] [[package]] @@ -4266,9 +4279,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags 2.6.0", "errno", @@ -4279,9 +4292,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ "once_cell", "ring", @@ -4335,9 +4348,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.7" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -4388,11 +4401,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4478,9 +4491,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] @@ -4496,20 +4509,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -4572,9 +4585,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d79b758b7cb2085612b11a235055e485605a5103faccdd633f35bd7aee69dd" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", "cfg-if", @@ -4734,11 +4747,11 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4760,9 +4773,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -4771,14 +4784,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284c41c2919303438fcf8dede4036fd1e82d4fc0fbb2b279bd2a1442c909ca92" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4804,7 +4817,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4834,7 +4847,7 @@ dependencies = [ "cfg-if", "fastrand 2.1.1", "once_cell", - "rustix 0.38.35", + "rustix 0.38.37", "windows-sys 0.59.0", ] @@ -4864,7 +4877,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -4955,9 +4968,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -4990,7 +5003,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5019,9 +5032,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -5031,9 +5044,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -5092,6 +5105,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -5118,6 +5145,7 @@ dependencies = [ "camino", "ciborium", "ciborium-io", + "clap", "copyvec", "criterion", "either", @@ -5135,6 +5163,7 @@ dependencies = [ "nunny", "plonky2", "plonky2_maybe_rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions", "pretty_env_logger", "prover", "rlp", @@ -5170,7 +5199,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5240,9 +5269,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "u4" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068a8b5939889cb4b24fd99201f2c7bc142a25986ae0eb903cb05537484192d5" +checksum = "390884f06a6f4eee34a2082a3eb17844cf3605a69f033f79bf0e6f460279112c" dependencies = [ "const-default", "serde", @@ -5280,9 +5309,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" @@ -5293,6 +5322,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + [[package]] name = "unroll" version = "0.1.5" @@ -5390,6 +5425,7 @@ dependencies = [ "tracing-subscriber", "vergen", "zero_bin_common", + "zk_evm_common", ] [[package]] @@ -5460,7 +5496,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -5494,7 +5530,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5797,6 +5833,12 @@ dependencies = [ "time", ] +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "yansi" version = "1.0.1" @@ -5813,6 +5855,7 @@ dependencies = [ "cargo_metadata", "clap", "directories", + "dotenvy", "evm_arithmetization", "futures", "lru", @@ -5821,11 +5864,13 @@ dependencies = [ "proof_gen", "serde", "serde_json", + "serde_path_to_error", "thiserror", "tokio", - "trace_decoder", "tracing", + "tracing-subscriber", "vergen", + "zk_evm_common", ] [[package]] @@ -5846,7 +5891,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5866,7 +5911,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", ] [[package]] @@ -5885,6 +5930,6 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.76", + "syn 2.0.77", "trybuild", ] diff --git a/Cargo.toml b/Cargo.toml index 008257556..9628a345c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,19 +111,19 @@ vergen = { version = "9.0.0", features = ["build", "rustc"] } winnow = "0.6.13" # local dependencies -evm_arithmetization = { path = "evm_arithmetization", version = "0.4.0" } +evm_arithmetization = { path = "evm_arithmetization", version = "0.4.0", default-features = false } mpt_trie = { path = "mpt_trie", version = "0.4.1" } -proof_gen = { path = "proof_gen", version = "0.4.0" } +proof_gen = { path = "proof_gen", version = "0.4.0", default-features = false } smt_trie = { path = "smt_trie", version = "0.1.1" } -trace_decoder = { path = "trace_decoder", version = "0.6.0" } +trace_decoder = { path = "trace_decoder", version = "0.6.0", default-features = false } zk_evm_common = { path = "common", version = "0.1.0" } zk_evm_proc_macro = { path = "proc_macro", version = "0.1.0" } # zero-bin related dependencies -ops = { path = "zero_bin/ops" } -prover = { path = "zero_bin/prover" } -rpc = { path = "zero_bin/rpc" } -zero_bin_common = { path = "zero_bin/common" } +ops = { path = "zero_bin/ops", default-features = false } +prover = { path = "zero_bin/prover", default-features = false } +rpc = { path = "zero_bin/rpc", default-features = false } +zero_bin_common = { path = "zero_bin/common", default-features = false } # plonky2-related dependencies plonky2 = { git = "https://github.com/0xPolygonZero/plonky2.git", rev = "dc77c77f2b06500e16ad4d7f1c2b057903602eed" } diff --git a/README.md b/README.md index a4d8326e4..b7bd2208e 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,10 @@ EVM block proofs. Below is a simplified view of the dependency graph, including the proving system backends and the application layer defined within [zero-bin](https://github.com/0xPolygonZero/zero-bin). ```mermaid +%%{init: {'theme':'dark'}}%% flowchart LR subgraph ps [proving systems] A1{{plonky2}} diff --git a/book/.gitignore b/book/.gitignore index 7585238ef..2b1e9364a 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1 +1,6 @@ book + +# Mermaid initialization files +# Obtained with `mdbook-mermaid install book`. +mermaid-init.js +mermaid.min.js diff --git a/book/book.toml b/book/book.toml index 33cbda21c..eb3dcc30c 100644 --- a/book/book.toml +++ b/book/book.toml @@ -20,7 +20,11 @@ create-missing = true [preprocessor.bib] bibliography = "bibliography.bib" +[preprocessor.mermaid] +command = "mdbook-mermaid" + [output.html] +additional-js = ["mermaid.min.js", "mermaid-init.js"] [output.html.print] # Disable page break diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index c2ee742bc..bdd613969 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -1,10 +1,10 @@ # Summary -[Introduction](intro.md) +[Introduction](../../README.md) - [STARK framework](framework/intro.md) - - [Cost model](framework/cost_model.md) - [Field](framework/field.md) + - [Cost model](framework/cost_model.md) - [Cross-Table Lookups](framework/ctls.md) - [Range-Checks](framework/range_check.md) - [Tables](tables/intro.md) diff --git a/book/src/intro.md b/book/src/intro.md deleted file mode 100644 index e10b99d01..000000000 --- a/book/src/intro.md +++ /dev/null @@ -1 +0,0 @@ -# Introduction diff --git a/book/src/mpt/intro.md b/book/src/mpt/intro.md index 1bb416531..25018bb84 100644 --- a/book/src/mpt/intro.md +++ b/book/src/mpt/intro.md @@ -3,7 +3,7 @@ The *EVM World state* is a representation of the different accounts at a particular time, as well as the last processed transactions together with their receipts. The world state is represented using *Merkle -Patricia Tries* (MPTs) [@@yellowpaper App. D], and there are three +Patricia Tries* (MPTs, see [Yellowpaper App. D](@@yellowpaper)), and there are three different tries: the state trie, the transaction trie and the receipt trie. @@ -17,10 +17,10 @@ inserting new nodes or deleting existing nodes. An MPT is composed of five different nodes: branch, extension, leaf, empty and digest nodes. Branch and leaf nodes might contain a payload whose format depends on the particular trie. The nodes are encoded, -primarily using RLP encoding and Hex-prefix encoding (see @@yellowpaper -App. B and C, respectively). The resulting encoding is then hashed, -following a strategy similar to that of normal Merkle trees, to generate -the trie hashes. +primarily using RLP encoding and Hex-prefix encoding (see [Yellowpaper +App. B and C](@@yellowpaper), respectively). The resulting encoding is +then hashed, following a strategy similar to that of normal Merkle trees, +to generate the trie hashes. Insertion and deletion is performed in the same way as other MPTs implementations. The only difference is for inserting extension nodes diff --git a/book/src/tables/intro.md b/book/src/tables/intro.md index afb1b4c41..8e6ac1d29 100644 --- a/book/src/tables/intro.md +++ b/book/src/tables/intro.md @@ -1 +1,42 @@ # Tables + +Our EVM statements are decomposed into several STARK tables, each corresponding to some coprocessor capable of handling specific operations, +orchestrated by a [Central Processing Unit](./cpu.md). + +Each coprocessor execution can be proven independently and concurrently, and a global check (via our [cross-table lookups](../framework/ctls.md)) +enforces consistency of values being shared across multiple coprocessors. + +Below is a depiction of the current decomposition of EVM statements execution[^1]: + +```mermaid +%%{init: {'theme':'dark'}}%% +flowchart TB + A[Arithmetic] + BP[BytePacking] + C[CPU] + + subgraph KK [ Keccak Hash ] + direction LR + K[Keccak] + KS[KeccakSponge] + K --- KS + end + + L[Logic] + M[Memory] + + subgraph MM [ zk-continuations ] + MB[MemBefore] + MA[MemAfter] + MB --- MA + end + + C --- A + C --- BP + C --- KK + C --- L + C --- M + M --- MM +``` + +[^1]: This diagram is simplified, and does not represent *all* interactions between co-processors. \ No newline at end of file diff --git a/common/src/lib.rs b/common/src/lib.rs index 9f33bfaf1..0ff0d47d5 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,4 +1,4 @@ -use ethereum_types::H256; +use ethereum_types::{H256, U256}; /// The hash value of an account empty EVM code. /// 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 @@ -14,6 +14,45 @@ pub const EMPTY_TRIE_HASH: H256 = H256([ 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33, ]); +#[macro_export] +/// A convenience macro to check the feature flags activating chain specific +/// behaviors. Only one of these flags may be activated at a time. +macro_rules! check_chain_features { + () => { + #[cfg(any( + all(feature = "cdk_erigon", feature = "polygon_pos"), + all(feature = "cdk_erigon", feature = "eth_mainnet"), + all(feature = "polygon_pos", feature = "eth_mainnet"), + not(any( + feature = "cdk_erigon", + feature = "eth_mainnet", + feature = "polygon_pos" + )) + ))] + compile_error!("One and only one of the feature chains `cdk_erigon`, `polygon_pos` or `eth_mainnet` must be selected"); + }; +} + +/// Converts an amount in `ETH` to `wei` units. +pub fn eth_to_wei(eth: U256) -> U256 { + // 1 ether = 10^18 wei. + eth * U256::from(10).pow(18.into()) +} + +/// Converts an amount in `gwei` to `wei` units. +/// This also works for converting `ETH` to `gwei`. +pub fn gwei_to_wei(eth: U256) -> U256 { + // 1 ether = 10^9 gwei = 10^18 wei. + eth * U256::from(10).pow(9.into()) +} + +#[test] +fn test_eth_conversion() { + assert_eq!( + eth_to_wei(U256::one()), + gwei_to_wei(gwei_to_wei(U256::one())) + ); +} #[test] fn test_empty_code_hash() { assert_eq!(EMPTY_CODE_HASH, keccak_hash::keccak([])); diff --git a/evm_arithmetization/Cargo.toml b/evm_arithmetization/Cargo.toml index 96d435bc8..ffdb8d2f5 100644 --- a/evm_arithmetization/Cargo.toml +++ b/evm_arithmetization/Cargo.toml @@ -24,15 +24,15 @@ hex-literal = { workspace = true } itertools = { workspace = true } keccak-hash = { workspace = true } log = { workspace = true } -plonky2_maybe_rayon = { workspace = true } +plonky2_maybe_rayon = { workspace = true, features = ["parallel"] } num = { workspace = true } num-bigint = { workspace = true } once_cell = { workspace = true } pest = { workspace = true } pest_derive = { workspace = true } -plonky2 = { workspace = true } +plonky2 = { workspace = true, features = ["parallel"] } plonky2_util = { workspace = true } -starky = { workspace = true } +starky = { workspace = true, features = ["parallel"] } rand = { workspace = true } rand_chacha = { workspace = true } rlp = { workspace = true } @@ -49,6 +49,7 @@ serde-big-array = { workspace = true } # Local dependencies mpt_trie = { workspace = true } smt_trie = { workspace = true, optional = true } +zk_evm_common = { workspace = true } zk_evm_proc_macro = { workspace = true } [dev-dependencies] @@ -57,15 +58,11 @@ hex = { workspace = true } ripemd = { workspace = true } [features] -default = ["parallel"] +default = ["eth_mainnet"] asmtools = ["hex"] -parallel = [ - "plonky2/parallel", - "plonky2_maybe_rayon/parallel", - "starky/parallel", -] polygon_pos = [] cdk_erigon = ["smt_trie"] +eth_mainnet = [] [[bin]] name = "assemble" diff --git a/evm_arithmetization/benches/fibonacci_25m_gas.rs b/evm_arithmetization/benches/fibonacci_25m_gas.rs index 28c9a2333..f6d5fc39f 100644 --- a/evm_arithmetization/benches/fibonacci_25m_gas.rs +++ b/evm_arithmetization/benches/fibonacci_25m_gas.rs @@ -26,6 +26,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; type F = GoldilocksField; @@ -50,7 +52,7 @@ fn criterion_benchmark(c: &mut Criterion) { simulate_execution::(inputs).unwrap(); } -fn prepare_setup() -> anyhow::Result { +fn prepare_setup() -> anyhow::Result> { let sender = hex!("8943545177806ED17B9F23F0a21ee5948eCaa776"); let to = hex!("159271B89fea49aF29DFaf8b4eCE7D042D5d6f07"); @@ -181,6 +183,7 @@ fn prepare_setup() -> anyhow::Result { checkpoint_state_trie_root: H256(hex!( "fe07ff6d1ab215df17884b89112ccf2373597285a56c5902150313ad1a53ee57" )), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], ger_data: None, block_metadata, txn_number_before: 0.into(), diff --git a/evm_arithmetization/src/cpu/kernel/aggregator.rs b/evm_arithmetization/src/cpu/kernel/aggregator.rs index 27054ac37..74a5f17cb 100644 --- a/evm_arithmetization/src/cpu/kernel/aggregator.rs +++ b/evm_arithmetization/src/cpu/kernel/aggregator.rs @@ -9,10 +9,15 @@ use super::assembler::{assemble, Kernel}; use crate::cpu::kernel::constants::evm_constants; use crate::cpu::kernel::parser::parse; -pub const NUMBER_KERNEL_FILES: usize = if cfg!(feature = "cdk_erigon") { - 159 +pub const NUMBER_KERNEL_FILES: usize = if cfg!(feature = "eth_mainnet") { + 157 +} else if cfg!(feature = "cdk_erigon") { + 155 +} else if cfg!(feature = "polygon_pos") { + 154 } else { - 158 + // unreachable + 0 }; pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ @@ -49,6 +54,7 @@ pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ include_str!("asm/core/log.asm"), include_str!("asm/core/selfdestruct_list.asm"), include_str!("asm/core/touched_addresses.asm"), + #[cfg(feature = "eth_mainnet")] include_str!("asm/core/withdrawals.asm"), include_str!("asm/core/precompiles/main.asm"), include_str!("asm/core/precompiles/ecrec.asm"), @@ -60,8 +66,9 @@ pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ include_str!("asm/core/precompiles/bn_mul.asm"), include_str!("asm/core/precompiles/snarkv.asm"), include_str!("asm/core/precompiles/blake2_f.asm"), + #[cfg(feature = "eth_mainnet")] include_str!("asm/core/precompiles/kzg_peval.asm"), - include_str!("asm/curve/bls381/util.asm"), + // include_str!("asm/curve/bls381/util.asm"), include_str!("asm/curve/bn254/curve_arithmetic/constants.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_add.asm"), include_str!("asm/curve/bn254/curve_arithmetic/curve_mul.asm"), @@ -168,6 +175,7 @@ pub static KERNEL_FILES: [&str; NUMBER_KERNEL_FILES] = [ include_str!("asm/transactions/type_0.asm"), include_str!("asm/transactions/type_1.asm"), include_str!("asm/transactions/type_2.asm"), + #[cfg(feature = "eth_mainnet")] include_str!("asm/transactions/type_3.asm"), include_str!("asm/util/assertions.asm"), include_str!("asm/util/basic_macros.asm"), @@ -186,6 +194,10 @@ pub(crate) fn combined_kernel_from_files(files: [&str; N]) -> Ke let mut active_features = HashSet::new(); if cfg!(feature = "cdk_erigon") { active_features.insert("cdk_erigon"); + } else if cfg!(feature = "polygon_pos") { + active_features.insert("polygon_pos"); + } else { + active_features.insert("eth_mainnet"); } let parsed_files = files diff --git a/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm b/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm index 36f491fb5..17ade9e86 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/beacon_roots.asm @@ -3,16 +3,11 @@ /// /// *NOTE*: This will panic if one of the provided timestamps is zero. +/// Pre-stack: (empty) +/// Post-stack: (empty) global set_beacon_root: - #[cfg(feature = cdk_erigon)] - { - PUSH pre_block_execution - } - #[cfg(not(feature = cdk_erigon))] - { - PUSH txn_loop - } - + // stack: (empty) + PUSH txn_loop %timestamp // stack: timestamp, retdest PUSH @HISTORY_BUFFER_LENGTH diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm b/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm index a2a2742ec..925921097 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/exception.asm @@ -327,8 +327,16 @@ min_stack_len_for_opcode: BYTES 0 // 0x46, CHAINID BYTES 0 // 0x47, SELFBALANCE BYTES 0 // 0x48, BASEFEE - BYTES 1 // 0x49, BLOBHASH - BYTES 0 // 0x4a, BLOBBASEFEE + #[cfg(feature = eth_mainnet)] + { + BYTES 1 // 0x49, BLOBHASH + BYTES 0 // 0x4a, BLOBBASEFEE + } + #[cfg(not(feature = eth_mainnet))] + { + BYTES 0 // 0x49, BLOBHASH is only active on Ethereum mainnet + BYTES 0 // 0x4a, BLOBBASEFEE is only active on Ethereum mainnet + } %rep 5 // 0x4b-0x4f, invalid BYTES 0 %endrep diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/precompiles/main.asm b/evm_arithmetization/src/cpu/kernel/asm/core/precompiles/main.asm index 0a1883491..1f1aa5af5 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/precompiles/main.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/precompiles/main.asm @@ -18,8 +18,16 @@ global handle_precompiles: DUP1 %eq_const(@BN_ADD) %jumpi(precompile_bn_add) DUP1 %eq_const(@BN_MUL) %jumpi(precompile_bn_mul) DUP1 %eq_const(@SNARKV) %jumpi(precompile_snarkv) - DUP1 %eq_const(@BLAKE2_F) %jumpi(precompile_blake2_f) - %eq_const(@KZG_PEVAL) %jumpi(precompile_kzg_peval) + #[cfg(feature = eth_mainnet)] + { + DUP1 %eq_const(@BLAKE2_F) %jumpi(precompile_blake2_f) + %eq_const(@KZG_PEVAL) %jumpi(precompile_kzg_peval) + } + #[cfg(not(feature = eth_mainnet))] + { + %eq_const(@BLAKE2_F) %jumpi(precompile_blake2_f) + } + // TODO: Add support of EIP-7712 for Polygon Pos, https://github.com/0xPolygonZero/zk_evm/issues/265 // stack: retdest JUMP diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm b/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm index 87c01dc7f..7677654e2 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/syscall.asm @@ -69,8 +69,16 @@ global syscall_jumptable: JUMPTABLE sys_chainid JUMPTABLE sys_selfbalance JUMPTABLE sys_basefee - JUMPTABLE sys_blobhash - JUMPTABLE sys_blobbasefee + #[cfg(feature = eth_mainnet)] + { + JUMPTABLE sys_blobhash + JUMPTABLE sys_blobbasefee + } + #[cfg(not(feature = eth_mainnet))] + { + JUMPTABLE panic // BLOBHASH is only active on Ethereum mainnet + JUMPTABLE panic // BLOBBASEFEE is only active on Ethereum mainnet + } %rep 5 JUMPTABLE panic // 0x4b-0x4f are invalid opcodes %endrep diff --git a/evm_arithmetization/src/cpu/kernel/asm/core/util.asm b/evm_arithmetization/src/cpu/kernel/asm/core/util.asm index 053c6159c..713b2274f 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/core/util.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/core/util.asm @@ -37,8 +37,20 @@ %macro is_precompile // stack: addr - DUP1 %ge_const(@ECREC) SWAP1 %le_const(@KZG_PEVAL) - // stack: addr>=1, addr<=10 + DUP1 %ge_const(@ECREC) + SWAP1 + // stack: addr, addr>=1 + #[cfg(feature = eth_mainnet)] + { + %le_const(@KZG_PEVAL) + // stack: addr>=1, addr<=10 + } + // TODO: Update after support of EIP-7712 for Polygon Pos, https://github.com/0xPolygonZero/zk_evm/issues/265 + #[cfg(not(feature = eth_mainnet))] + { + %le_const(@BLAKE2_F) + // stack: addr>=1, addr<=9 + } MUL // Cheaper than AND %endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/main.asm b/evm_arithmetization/src/cpu/kernel/asm/main.asm index 58e969ee2..6f20938af 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/main.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/main.asm @@ -127,16 +127,33 @@ global start_txns: %mload_global_metadata(@GLOBAL_METADATA_BLOCK_GAS_USED_BEFORE) // stack: init_gas_used, txn_counter, num_nibbles, txn_nb - // If txn_idx == 0, update the beacon_root and exit roots. - %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) - ISZERO - %jumpi(set_beacon_root) + #[cfg(feature = eth_mainnet)] + { + // If txn_idx == 0, update the beacon_root for Ethereum mainnet. + %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) + ISZERO + %jumpi(set_beacon_root) + } + #[cfg(feature = cdk_erigon)] + { + // If txn_idx == 0, perform pre-state execution for CDK erigon. + %mload_global_metadata(@GLOBAL_METADATA_TXN_NUMBER_BEFORE) + ISZERO + %jumpi(pre_block_execution) + } // stack: init_gas_used, txn_counter, num_nibbles, txn_nb global txn_loop: // If the prover has no more txns for us to process, halt. PROVER_INPUT(end_of_txns) - %jumpi(execute_withdrawals) + #[cfg(feature = eth_mainnet)] + { + %jumpi(execute_withdrawals) + } + #[cfg(not(feature = eth_mainnet))] + { + %jumpi(perform_final_checks) + } // Call route_txn. When we return, we will process the txn receipt. PUSH txn_loop_after @@ -161,9 +178,12 @@ global txn_loop_after: // stack: new_cum_gas, txn_counter, num_nibbles, new_txn_number %jump(txn_loop) -global execute_withdrawals: - // stack: cum_gas, txn_counter, num_nibbles, txn_nb - %withdrawals +#[cfg(feature = eth_mainnet)] +{ + global execute_withdrawals: + // stack: cum_gas, txn_counter, num_nibbles, txn_nb + %withdrawals +} global perform_final_checks: // stack: cum_gas, txn_counter, num_nibbles, txn_nb @@ -255,5 +275,8 @@ global check_final_state_trie: PUSH 0 %mstore_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) PUSH 0 %mstore_txn_field(@TXN_FIELD_TO) - %reset_blob_versioned_hashes + #[cfg(feature = eth_mainnet)] + { + %reset_blob_versioned_hashes + } %endmacro diff --git a/evm_arithmetization/src/cpu/kernel/asm/memory/metadata.asm b/evm_arithmetization/src/cpu/kernel/asm/memory/metadata.asm index 1747d6692..5e94794f6 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/memory/metadata.asm @@ -277,47 +277,51 @@ global sys_basefee: SWAP1 EXIT_KERNEL -global sys_blobhash: - // stack: kexit_info, index - %charge_gas_const(@GAS_HASH_OPCODE) - // stack: kexit_info, index - %blobhash - // stack: blobhash, kexit_info - SWAP1 - EXIT_KERNEL - -%macro blobhash - // stack: kexit_info, index - SWAP1 - // stack: index, kexit_info - %mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN) - DUP2 - LT ISZERO // == GE - // stack: index >= len, index, kexit_info - %jumpi(%%index_too_big) - PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES - %build_kernel_address - // stack: read_addr, kexit_info - MLOAD_GENERAL - %jump(%%end) -%%index_too_big: - // The index is larger than the list, just push 0. - // stack: index, kexit_info - POP - PUSH 0 - // stack: 0, kexit_info -%%end: - // stack: blobhash, kexit_info -%endmacro - -global sys_blobbasefee: - // stack: kexit_info - %charge_gas_const(@GAS_BASE) - // stack: kexit_info - PROVER_INPUT(blobbasefee) - // stack: blobbasefee, kexit_info - SWAP1 - EXIT_KERNEL +/// Blob-related macros are only available for Ethereum mainnet. +#[cfg(feature = eth_mainnet)] +{ + global sys_blobhash: + // stack: kexit_info, index + %charge_gas_const(@GAS_HASH_OPCODE) + // stack: kexit_info, index + %blobhash + // stack: blobhash, kexit_info + SWAP1 + EXIT_KERNEL + + %macro blobhash + // stack: kexit_info, index + SWAP1 + // stack: index, kexit_info + %mload_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN) + DUP2 + LT ISZERO // == GE + // stack: index >= len, index, kexit_info + %jumpi(%%index_too_big) + PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES + %build_kernel_address + // stack: read_addr, kexit_info + MLOAD_GENERAL + %jump(%%end) + %%index_too_big: + // The index is larger than the list, just push 0. + // stack: index, kexit_info + POP + PUSH 0 + // stack: 0, kexit_info + %%end: + // stack: blobhash, kexit_info + %endmacro + + global sys_blobbasefee: + // stack: kexit_info + %charge_gas_const(@GAS_BASE) + // stack: kexit_info + PROVER_INPUT(blobbasefee) + // stack: blobbasefee, kexit_info + SWAP1 + EXIT_KERNEL +} global sys_blockhash: // stack: kexit_info, block_number diff --git a/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm index 0621db533..6ee92ca2b 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/transactions/common_decoding.asm @@ -127,66 +127,6 @@ %%after: %endmacro -%macro decode_and_store_max_fee_per_blob_gas - // stack: rlp_addr - %decode_rlp_scalar - %stack (rlp_addr, max_fee_per_blob_gas) -> (max_fee_per_blob_gas, rlp_addr) - %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_BLOB_GAS) - // stack: rlp_addr -%endmacro - -%macro decode_and_store_blob_versioned_hashes - // stack: rlp_addr - %decode_rlp_list_len - %stack (rlp_addr, len) -> (len, len, rlp_addr, %%after) - - // EIP-4844: Blob transactions should have at least 1 versioned hash - %assert_nonzero(invalid_txn_2) - - // stack: len, rlp_addr, %%after - %jump(decode_and_store_blob_versioned_hashes) -%%after: -%endmacro - -// The blob versioned hashes are just a list of hashes. -global decode_and_store_blob_versioned_hashes: - // stack: len, rlp_addr - // Store the list length - DUP1 %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN) - - // stack: len, rlp_addr - DUP2 ADD - // stack: end_rlp_addr, rlp_addr - // stack: end_rlp_addr, rlp_addr - PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES // initial address to write to - SWAP2 -decode_and_store_blob_versioned_hashes_loop: - // stack: rlp_addr, end_rlp_addr, store_addr - DUP2 DUP2 EQ %jumpi(decode_and_store_blob_versioned_hashes_finish) - // stack: rlp_addr, end_rlp_addr, store_addr - %decode_rlp_scalar // blob_versioned_hashes[i] - // stack: rlp_addr, hash, end_rlp_addr, store_addr - - // EIP-4844: Versioned hashes should have `VERSIONED_HASH_VERSION_KZG` as MSB - DUP2 - %shr_const(248) - // stack: MSB, hash, end_rlp_addr, store_addr - %eq_const(1) - // stack: hash_is_valid?, rlp_addr, hash, end_rlp_addr, store_addr - %assert_nonzero(invalid_txn_3) - - // stack: rlp_addr, hash, end_rlp_addr, store_addr - SWAP3 DUP1 SWAP2 - // stack: hash, store_addr, store_addr, end_rlp_addr, rlp_addr - MSTORE_GENERAL - // stack: store_addr, end_rlp_addr, rlp_addr - %increment SWAP2 - // stack: rlp_addr, end_rlp_addr, store_addr' - %jump(decode_and_store_blob_versioned_hashes_loop) -decode_and_store_blob_versioned_hashes_finish: - %stack (rlp_addr, end_rlp_addr, store_addr, retdest) -> (retdest, rlp_addr) - JUMP - %macro decode_and_store_y_parity // stack: rlp_addr %decode_rlp_scalar @@ -303,3 +243,67 @@ sload_with_addr: // stack: value, retdest SWAP1 JUMP + +/// Type-3 transactions specific decoding helper macros. +#[cfg(feature = eth_mainnet)] +{ + %macro decode_and_store_max_fee_per_blob_gas + // stack: rlp_addr + %decode_rlp_scalar + %stack (rlp_addr, max_fee_per_blob_gas) -> (max_fee_per_blob_gas, rlp_addr) + %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_BLOB_GAS) + // stack: rlp_addr + %endmacro + + %macro decode_and_store_blob_versioned_hashes + // stack: rlp_addr + %decode_rlp_list_len + %stack (rlp_addr, len) -> (len, len, rlp_addr, %%after) + + // EIP-4844: Blob transactions should have at least 1 versioned hash + %assert_nonzero(invalid_txn_2) + + // stack: len, rlp_addr, %%after + %jump(decode_and_store_blob_versioned_hashes) + %%after: + %endmacro + + // The blob versioned hashes are just a list of hashes. + global decode_and_store_blob_versioned_hashes: + // stack: len, rlp_addr + // Store the list length + DUP1 %mstore_global_metadata(@GLOBAL_METADATA_BLOB_VERSIONED_HASHES_LEN) + + // stack: len, rlp_addr + DUP2 ADD + // stack: end_rlp_addr, rlp_addr + // stack: end_rlp_addr, rlp_addr + PUSH @SEGMENT_TXN_BLOB_VERSIONED_HASHES // initial address to write to + SWAP2 + decode_and_store_blob_versioned_hashes_loop: + // stack: rlp_addr, end_rlp_addr, store_addr + DUP2 DUP2 EQ %jumpi(decode_and_store_blob_versioned_hashes_finish) + // stack: rlp_addr, end_rlp_addr, store_addr + %decode_rlp_scalar // blob_versioned_hashes[i] + // stack: rlp_addr, hash, end_rlp_addr, store_addr + + // EIP-4844: Versioned hashes should have `VERSIONED_HASH_VERSION_KZG` as MSB + DUP2 + %shr_const(248) + // stack: MSB, hash, end_rlp_addr, store_addr + %eq_const(1) + // stack: hash_is_valid?, rlp_addr, hash, end_rlp_addr, store_addr + %assert_nonzero(invalid_txn_3) + + // stack: rlp_addr, hash, end_rlp_addr, store_addr + SWAP3 DUP1 SWAP2 + // stack: hash, store_addr, store_addr, end_rlp_addr, rlp_addr + MSTORE_GENERAL + // stack: store_addr, end_rlp_addr, rlp_addr + %increment SWAP2 + // stack: rlp_addr, end_rlp_addr, store_addr' + %jump(decode_and_store_blob_versioned_hashes_loop) + decode_and_store_blob_versioned_hashes_finish: + %stack (rlp_addr, end_rlp_addr, store_addr, retdest) -> (retdest, rlp_addr) + JUMP +} \ No newline at end of file diff --git a/evm_arithmetization/src/cpu/kernel/asm/transactions/router.asm b/evm_arithmetization/src/cpu/kernel/asm/transactions/router.asm index e7c7f88ee..eaef7efd7 100644 --- a/evm_arithmetization/src/cpu/kernel/asm/transactions/router.asm +++ b/evm_arithmetization/src/cpu/kernel/asm/transactions/router.asm @@ -33,14 +33,18 @@ read_txn_from_memory: %jumpi(process_type_2_txn) // stack: rlp_start_addr, retdest - DUP1 - MLOAD_GENERAL - %eq_const(3) - // stack: first_byte == 3, rlp_start_addr, retdest - %jumpi(process_type_3_txn) - // stack: rlp_start_addr, retdest + // Only Ethereum mainnet supports Blob-transactions. + #[cfg(feature = eth_mainnet)] + { + DUP1 + MLOAD_GENERAL + %eq_const(3) + // stack: first_byte == 3, rlp_start_addr, retdest + %jumpi(process_type_3_txn) + // stack: rlp_start_addr, retdest + } - // At this point, since it's not a type 1, 2 or 3 transaction, + // At this point, since it's not a typed transaction, // it must be a legacy (aka type 0) transaction. %jump(process_type_0_txn) diff --git a/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs b/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs index 0abc84e70..5ed5972c1 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/exc_bitfields.rs @@ -28,6 +28,7 @@ const fn u256_from_set_index_ranges(ranges: &[RangeInclusive U256(res_limbs) } +#[cfg(feature = "eth_mainnet")] pub(crate) const STACK_LENGTH_INCREASING_OPCODES_USER: U256 = u256_from_set_index_ranges(&[ 0x30..=0x30, // ADDRESS 0x32..=0x34, // ORIGIN, CALLER, CALLVALUE @@ -42,6 +43,20 @@ pub(crate) const STACK_LENGTH_INCREASING_OPCODES_USER: U256 = u256_from_set_inde 0x5f..=0x8f, // PUSH*, DUP* ]); +#[cfg(not(feature = "eth_mainnet"))] +pub(crate) const STACK_LENGTH_INCREASING_OPCODES_USER: U256 = u256_from_set_index_ranges(&[ + 0x30..=0x30, // ADDRESS + 0x32..=0x34, // ORIGIN, CALLER, CALLVALUE + 0x36..=0x36, // CALLDATASIZE + 0x38..=0x38, // CODESIZE + 0x3a..=0x3a, // GASPRICE + 0x3d..=0x3d, // RETURNDATASIZE + 0x41..=0x48, /* COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT, CHAINID, SELFBALANCE, + * BASEFEE */ + 0x58..=0x5a, // PC, MSIZE, GAS + 0x5f..=0x8f, // PUSH*, DUP* +]); + pub(crate) const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges(&[ 0x0c..=0x0f, 0x1e..=0x1f, diff --git a/evm_arithmetization/src/cpu/kernel/constants/mod.rs b/evm_arithmetization/src/cpu/kernel/constants/mod.rs index 853e5500c..348c5b2c6 100644 --- a/evm_arithmetization/src/cpu/kernel/constants/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/constants/mod.rs @@ -18,6 +18,13 @@ pub(crate) mod journal_entry; pub(crate) mod trie_type; pub(crate) mod txn_fields; +/// A named constant. +/// Prefer this over `(name, value)` tuples. +pub struct Named<'a, T> { + pub name: &'a str, + pub value: T, +} + /// Constants that are accessible to our kernel assembly code. pub(crate) fn evm_constants() -> HashMap { let mut c = HashMap::new(); @@ -71,8 +78,8 @@ pub(crate) fn evm_constants() -> HashMap { U256::from_big_endian(&cancun_constants::BEACON_ROOTS_CONTRACT_STATE_KEY.1), ); c.insert( - cancun_constants::HISTORY_BUFFER_LENGTH.0.into(), - cancun_constants::HISTORY_BUFFER_LENGTH.1.into(), + cancun_constants::HISTORY_BUFFER_LENGTH.name.into(), + cancun_constants::HISTORY_BUFFER_LENGTH.value, ); c.insert( @@ -424,7 +431,10 @@ pub mod cancun_constants { // Beacon constants /////////////////// - pub const HISTORY_BUFFER_LENGTH: (&str, u64) = ("HISTORY_BUFFER_LENGTH", 8191); + pub const HISTORY_BUFFER_LENGTH: Named = Named { + name: "HISTORY_BUFFER_LENGTH", + value: U256([8191, 0, 0, 0]), + }; pub const BEACON_ROOTS_CONTRACT_ADDRESS: Address = H160(hex!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02")); diff --git a/evm_arithmetization/src/cpu/kernel/interpreter.rs b/evm_arithmetization/src/cpu/kernel/interpreter.rs index 33f8edfd3..10af8d495 100644 --- a/evm_arithmetization/src/cpu/kernel/interpreter.rs +++ b/evm_arithmetization/src/cpu/kernel/interpreter.rs @@ -156,7 +156,7 @@ impl Interpreter { pub(crate) fn new_with_generation_inputs( initial_offset: usize, initial_stack: Vec, - inputs: &GenerationInputs, + inputs: &GenerationInputs, max_cpu_len_log: Option, ) -> Self { debug_inputs(inputs); @@ -218,7 +218,7 @@ impl Interpreter { } /// Initializes the interpreter state given `GenerationInputs`. - pub(crate) fn initialize_interpreter_state(&mut self, inputs: &GenerationInputs) { + pub(crate) fn initialize_interpreter_state(&mut self, inputs: &GenerationInputs) { // Initialize registers. let registers_before = RegistersState::new(); self.generation_state.registers = RegistersState { @@ -273,10 +273,6 @@ impl Interpreter { // Set `GlobalMetadata` values. let metadata = &inputs.block_metadata; - #[cfg(feature = "cdk_erigon")] - let burn_addr = inputs - .burn_addr - .map_or_else(U256::max_value, |addr| U256::from_big_endian(&addr.0)); let global_metadata_to_set = [ ( GlobalMetadata::BlockBeneficiary, @@ -297,14 +293,17 @@ impl Interpreter { h2u(inputs.block_hashes.cur_hash), ), (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockBlobGasUsed, metadata.block_blob_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockExcessBlobGas, metadata.block_excess_blob_gas, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::ParentBeaconBlockRoot, h2u(metadata.parent_beacon_block_root), @@ -343,7 +342,12 @@ impl Interpreter { (GlobalMetadata::KernelHash, h2u(KERNEL.code_hash)), (GlobalMetadata::KernelLen, KERNEL.code.len().into()), #[cfg(feature = "cdk_erigon")] - (GlobalMetadata::BurnAddr, burn_addr), + ( + GlobalMetadata::BurnAddr, + inputs + .burn_addr + .map_or_else(U256::max_value, |addr| U256::from_big_endian(&addr.0)), + ), ]; self.set_global_metadata_multi_fields(&global_metadata_to_set); diff --git a/evm_arithmetization/src/cpu/kernel/parser.rs b/evm_arithmetization/src/cpu/kernel/parser.rs index 4cbef3c81..be80ae5d4 100644 --- a/evm_arithmetization/src/cpu/kernel/parser.rs +++ b/evm_arithmetization/src/cpu/kernel/parser.rs @@ -91,7 +91,9 @@ fn parse_conditional_block(item: Pair, active_features: &HashSet<&str>) -> features_string: &str, group_rule: FeatureGroupRule, ) -> bool { - let features = features_string.split(","); + let features = features_string + .split(&[',', ' ']) // allows for both `foo,bar` and `foo, bar` in ASM + .filter(|s| !s.is_empty()); match group_rule { FeatureGroupRule::Not => { diff --git a/evm_arithmetization/src/cpu/kernel/tests/add11.rs b/evm_arithmetization/src/cpu/kernel/tests/add11.rs index 9e44c8bfa..fe36b40e3 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/add11.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/add11.rs @@ -9,6 +9,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, Node, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; @@ -174,7 +176,7 @@ fn test_add11_yml() { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -184,6 +186,7 @@ fn test_add11_yml() { contract_code: contract_code.clone(), block_metadata, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: gas_used, @@ -360,6 +363,7 @@ fn test_add11_yml_with_exception() { contract_code: contract_code.clone(), block_metadata, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: txn_gas_limit.into(), diff --git a/evm_arithmetization/src/cpu/kernel/tests/bls381.rs b/evm_arithmetization/src/cpu/kernel/tests/bls381.rs index 40a28ac5b..f61229796 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/bls381.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/bls381.rs @@ -2,40 +2,13 @@ use anyhow::Result; use ethereum_types::U256; use hex_literal::hex; use plonky2::field::goldilocks_field::GoldilocksField as F; -use rand::Rng; -use super::{run_interpreter_with_memory, InterpreterMemoryInitialization}; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::cancun_constants::POINT_EVALUATION_PRECOMPILE_RETURN_VALUE; use crate::cpu::kernel::constants::cancun_constants::KZG_VERSIONED_HASH; use crate::cpu::kernel::interpreter::Interpreter; -use crate::extension_tower::{Fp2, Stack, BLS381}; -use crate::memory::segments::Segment::KernelGeneral; use crate::util::sha2; -#[test] -fn test_bls_fp2_mul() -> Result<()> { - let mut rng = rand::thread_rng(); - let x: Fp2 = rng.gen::>(); - let y: Fp2 = rng.gen::>(); - - let mut stack = x.to_stack().to_vec(); - stack.extend(y.to_stack().to_vec()); - stack.push(U256::from(0xdeadbeefu32)); - let setup = InterpreterMemoryInitialization { - label: "mul_fp381_2".to_string(), - stack, - segment: KernelGeneral, - memory: vec![], - }; - let interpreter = run_interpreter_with_memory::(setup).unwrap(); - let stack: Vec = interpreter.stack().iter().rev().cloned().collect(); - let output = Fp2::::from_stack(&stack); - - assert_eq!(output, x * y); - Ok(()) -} - /// A KZG point evaluation precompile payload consists in: /// - a G1 compressed point commitment (48 bytes) /// - a Scalar element z (32 bytes) diff --git a/evm_arithmetization/src/cpu/kernel/tests/init_exc_stop.rs b/evm_arithmetization/src/cpu/kernel/tests/init_exc_stop.rs index 57ad232cb..be88021de 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/init_exc_stop.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/init_exc_stop.rs @@ -4,6 +4,8 @@ use ethereum_types::U256; use keccak_hash::{keccak, H256}; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField as F; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use crate::cpu::kernel::{aggregator::KERNEL, interpreter::Interpreter}; use crate::generation::{ @@ -76,7 +78,7 @@ fn test_init_exc_stop() { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![], burn_addr: None, withdrawals: vec![], @@ -89,6 +91,7 @@ fn test_init_exc_stop() { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/src/cpu/kernel/tests/mod.rs b/evm_arithmetization/src/cpu/kernel/tests/mod.rs index 53f65e9f8..39810148b 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/mod.rs @@ -1,10 +1,13 @@ mod account_code; +#[cfg(feature = "eth_mainnet")] mod add11; mod balance; mod bignum; mod blake2_f; +#[cfg(feature = "eth_mainnet")] mod blobhash; mod block_hash; +#[cfg(feature = "eth_mainnet")] mod bls381; mod bn254; mod core; diff --git a/evm_arithmetization/src/cpu/kernel/tests/transaction_parsing/mod.rs b/evm_arithmetization/src/cpu/kernel/tests/transaction_parsing/mod.rs index c2ee22f4f..1f70a3a1e 100644 --- a/evm_arithmetization/src/cpu/kernel/tests/transaction_parsing/mod.rs +++ b/evm_arithmetization/src/cpu/kernel/tests/transaction_parsing/mod.rs @@ -9,6 +9,7 @@ use crate::{ mod parse_type_0_txn; mod parse_type_1_txn; mod parse_type_2_txn; +#[cfg(feature = "eth_mainnet")] mod parse_type_3_txn; pub(crate) fn prepare_interpreter_for_txn_parsing( diff --git a/evm_arithmetization/src/fixed_recursive_verifier.rs b/evm_arithmetization/src/fixed_recursive_verifier.rs index 8746d705a..459e33c93 100644 --- a/evm_arithmetization/src/fixed_recursive_verifier.rs +++ b/evm_arithmetization/src/fixed_recursive_verifier.rs @@ -46,7 +46,7 @@ use crate::proof::{ PublicValuesTarget, RegistersDataTarget, TrieRoots, TrieRootsTarget, DEFAULT_CAP_LEN, TARGET_HASH_SIZE, }; -use crate::prover::{check_abort_signal, prove}; +use crate::prover::{check_abort_signal, features_check, prove}; use crate::recursive_verifier::{ add_common_recursion_gates, add_virtual_final_public_values_public_input, add_virtual_public_values_public_input, get_memory_extra_looking_sum_circuit, @@ -54,8 +54,6 @@ use crate::recursive_verifier::{ PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, }; use crate::util::h256_limbs; -#[cfg(feature = "cdk_erigon")] -use crate::util::u256_limbs; use crate::verifier::initial_memory_merkle_cap; /// The recursion threshold. We end a chain of recursive proofs once we reach @@ -71,7 +69,7 @@ where { pub is_dummy: bool, pub proof_with_pis: ProofWithPublicInputs, - pub public_values: PublicValues, + pub public_values: PublicValues, } /// Contains all recursive circuits used in the system. For each STARK and each @@ -1044,18 +1042,21 @@ where ); // Connect the burn address targets. - BurnAddrTarget::conditional_assert_eq( - &mut builder, - is_not_dummy, - lhs_pv.burn_addr, - rhs_pv.burn_addr.clone(), - ); - BurnAddrTarget::conditional_assert_eq( - &mut builder, - is_not_dummy, - public_values.burn_addr.clone(), - rhs_pv.burn_addr, - ); + #[cfg(feature = "cdk_erigon")] + { + BurnAddrTarget::conditional_assert_eq( + &mut builder, + is_not_dummy, + lhs_pv.burn_addr, + rhs_pv.burn_addr.clone(), + ); + BurnAddrTarget::conditional_assert_eq( + &mut builder, + is_not_dummy, + public_values.burn_addr.clone(), + rhs_pv.burn_addr, + ); + } BlockMetadataTarget::conditional_assert_eq( &mut builder, @@ -1194,16 +1195,19 @@ where ); // Connect the burn address targets. - BurnAddrTarget::connect( - &mut builder, - lhs_pv.burn_addr.clone(), - rhs_pv.burn_addr.clone(), - ); - BurnAddrTarget::connect( - &mut builder, - public_values.burn_addr.clone(), - rhs_pv.burn_addr.clone(), - ); + #[cfg(feature = "cdk_erigon")] + { + BurnAddrTarget::connect( + &mut builder, + lhs_pv.burn_addr.clone(), + rhs_pv.burn_addr.clone(), + ); + BurnAddrTarget::connect( + &mut builder, + public_values.burn_addr.clone(), + rhs_pv.burn_addr.clone(), + ); + } Self::connect_extra_public_values( &mut builder, @@ -1343,9 +1347,6 @@ where let parent_block_proof = builder.add_virtual_proof_with_pis(&expected_common_data); let agg_root_proof = builder.add_virtual_proof_with_pis(&agg.circuit.common); - // Connect block hashes - Self::connect_block_hashes(&mut builder, &parent_block_proof, &agg_root_proof); - let parent_pv = PublicValuesTarget::from_public_inputs(&parent_block_proof.public_inputs); let agg_pv = PublicValuesTarget::from_public_inputs(&agg_root_proof.public_inputs); @@ -1378,16 +1379,19 @@ where ); // Connect the burn address targets. - BurnAddrTarget::connect( - &mut builder, - parent_pv.burn_addr.clone(), - agg_pv.burn_addr.clone(), - ); - BurnAddrTarget::connect( - &mut builder, - public_values.burn_addr.clone(), - agg_pv.burn_addr.clone(), - ); + #[cfg(feature = "cdk_erigon")] + { + BurnAddrTarget::connect( + &mut builder, + parent_pv.burn_addr.clone(), + agg_pv.burn_addr.clone(), + ); + BurnAddrTarget::connect( + &mut builder, + public_values.burn_addr.clone(), + agg_pv.burn_addr.clone(), + ); + } // Make connections between block proofs, and check initial and final block // values. @@ -1548,7 +1552,7 @@ where // This also enforces that the initial state trie root that will be stored in // these `FinalPublicValues` actually matches the known checkpoint state trie // root. - final_pv.connect_parent(&mut builder, &parent_pv); + final_pv.connect_parent::(&mut builder, &parent_pv); let block_verifier_data = builder.constant_verifier_data(&block.circuit.verifier_only); @@ -1665,13 +1669,11 @@ where /// Connect the 256 block hashes between two blocks fn connect_block_hashes( builder: &mut CircuitBuilder, - lhs: &ProofWithPublicInputsTarget, - rhs: &ProofWithPublicInputsTarget, + lhs_public_values: &PublicValuesTarget, + rhs_public_values: &PublicValuesTarget, ) { - let lhs_public_values = PublicValuesTarget::from_public_inputs(&lhs.public_inputs); - let rhs_public_values = PublicValuesTarget::from_public_inputs(&rhs.public_inputs); for i in 0..255 { - for j in 0..8 { + for j in 0..TARGET_HASH_SIZE { builder.connect( lhs_public_values.block_hashes.prev_hashes[8 * (i + 1) + j], rhs_public_values.block_hashes.prev_hashes[8 * i + j], @@ -1680,7 +1682,7 @@ where } let expected_hash = lhs_public_values.block_hashes.cur_hash; let prev_block_hash = &rhs_public_values.block_hashes.prev_hashes[255 * 8..256 * 8]; - for i in 0..expected_hash.len() { + for i in 0..TARGET_HASH_SIZE { builder.connect(expected_hash[i], prev_block_hash[i]); } } @@ -1729,6 +1731,9 @@ where // Check that the checkpoint block has the predetermined state trie root in // `ExtraBlockData`. Self::connect_checkpoint_block(builder, rhs, has_not_parent_block); + + // Connect block hashes + Self::connect_block_hashes(builder, lhs, rhs); } fn connect_checkpoint_block( @@ -1748,6 +1753,18 @@ where constr = builder.mul(has_not_parent_block, constr); builder.assert_zero(constr); } + + let consolidated_hash = builder + .hash_n_to_hash_no_pad::(x.block_hashes.prev_hashes.to_vec()) + .elements; + + for i in 0..NUM_HASH_OUT_ELTS { + builder.conditional_assert_eq( + has_not_parent_block, + x.extra_block_data.checkpoint_consolidated_hash[i], + consolidated_hash[i], + ) + } } fn connect_final_block_values_to_intermediary( @@ -1811,14 +1828,13 @@ where &self, all_stark: &AllStark, config: &StarkConfig, - generation_inputs: TrimmedGenerationInputs, + generation_inputs: TrimmedGenerationInputs, segment_data: &mut GenerationSegmentData, timing: &mut TimingTree, abort_signal: Option>, ) -> anyhow::Result> { - if generation_inputs.burn_addr.is_some() && !cfg!(feature = "cdk_erigon") { - log::warn!("The burn address in the GenerationInputs will be ignored, as the `cdk_erigon` feature is not activated.") - } + features_check(&generation_inputs); + let all_proof = prove::( all_stark, config, @@ -1887,11 +1903,13 @@ where &self, all_stark: &AllStark, config: &StarkConfig, - generation_inputs: GenerationInputs, + generation_inputs: GenerationInputs, max_cpu_len_log: usize, timing: &mut TimingTree, abort_signal: Option>, ) -> anyhow::Result>> { + features_check(&generation_inputs.clone().trim()); + let segment_iterator = SegmentDataIterator::::new(&generation_inputs, Some(max_cpu_len_log)); @@ -1977,7 +1995,7 @@ where all_proof: AllProof, table_circuits: &[(RecursiveCircuitsForTableSize, u8); NUM_TABLES], abort_signal: Option>, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut root_inputs = PartialWitness::new(); for table in 0..NUM_TABLES { @@ -2098,6 +2116,9 @@ where checkpoint_state_trie_root: lhs_public_values .extra_block_data .checkpoint_state_trie_root, + checkpoint_consolidated_hash: lhs_public_values + .extra_block_data + .checkpoint_consolidated_hash, txn_number_before: lhs_public_values.extra_block_data.txn_number_before, txn_number_after: real_public_values.extra_block_data.txn_number_after, gas_used_before: lhs_public_values.extra_block_data.gas_used_before, @@ -2167,11 +2188,11 @@ where &self, lhs_is_agg: bool, lhs_proof: &ProofWithPublicInputs, - lhs_public_values: PublicValues, + lhs_public_values: PublicValues, rhs_is_agg: bool, rhs_proof: &ProofWithPublicInputs, - rhs_public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { + rhs_public_values: PublicValues, + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut txn_inputs = PartialWitness::new(); Self::set_dummy_if_necessary( @@ -2282,8 +2303,8 @@ where &self, opt_parent_block_proof: Option<&ProofWithPublicInputs>, agg_root_proof: &ProofWithPublicInputs, - public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { + public_values: PublicValues, + ) -> anyhow::Result<(ProofWithPublicInputs, PublicValues)> { let mut block_inputs = PartialWitness::new(); block_inputs.set_bool_target( @@ -2347,7 +2368,7 @@ where { let burn_addr_keys = TrieRootsTarget::SIZE * 2..TrieRootsTarget::SIZE * 2 + burn_addr_offset; - for (key, &value) in burn_addr_keys.zip_eq(&u256_limbs( + for (key, &value) in burn_addr_keys.zip_eq(&crate::util::u256_limbs( public_values .burn_addr .expect("We should have a burn addr when cdk_erigon is activated"), @@ -2379,14 +2400,14 @@ where + TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE + BlockHashesTarget::SIZE - - 8; - + - 16; for i in 0..public_values.block_hashes.prev_hashes.len() - 1 { let targets = h256_limbs::(public_values.block_hashes.prev_hashes[i]); for j in 0..8 { nonzero_pis.insert(block_hashes_keys.start + 8 * (i + 1) + j, targets[j]); } } + let block_hashes_current_start = burn_addr_offset + TrieRootsTarget::SIZE * 2 + BlockMetadataTarget::SIZE @@ -2466,11 +2487,15 @@ where /// This method outputs a tuple of [`ProofWithPublicInputs`] and /// associated [`FinalPublicValues`]. Only the proof with public inputs is /// necessary for a verifier to assert correctness of the computation. + #[allow(clippy::type_complexity)] pub fn prove_block_wrapper( &self, block_proof: &ProofWithPublicInputs, - public_values: PublicValues, - ) -> anyhow::Result<(ProofWithPublicInputs, FinalPublicValues)> { + public_values: PublicValues, + ) -> anyhow::Result<( + ProofWithPublicInputs, + FinalPublicValues, + )> { let mut block_wrapper_inputs = PartialWitness::new(); block_wrapper_inputs diff --git a/evm_arithmetization/src/generation/mod.rs b/evm_arithmetization/src/generation/mod.rs index 315f85265..9c7625d2b 100644 --- a/evm_arithmetization/src/generation/mod.rs +++ b/evm_arithmetization/src/generation/mod.rs @@ -1,14 +1,13 @@ use std::collections::HashMap; use anyhow::anyhow; -use ethereum_types::H160; use ethereum_types::{Address, BigEndianHash, H256, U256}; use keccak_hash::keccak; use log::log_enabled; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; -use plonky2::hash::hash_types::RichField; +use plonky2::hash::hash_types::{RichField, NUM_HASH_OUT_ELTS}; use plonky2::timed; use plonky2::util::timing::TimingTree; use segments::GenerationSegmentData; @@ -54,7 +53,8 @@ pub type MemBeforeValues = Vec<(MemoryAddress, U256)>; /// Inputs needed for trace generation. #[derive(Clone, Debug, Deserialize, Serialize, Default)] -pub struct GenerationInputs { +#[serde(bound = "")] +pub struct GenerationInputs { /// The index of the transaction being proven within its block. pub txn_number_before: U256, /// The cumulative gas used through the execution of all transactions prior @@ -72,7 +72,7 @@ pub struct GenerationInputs { /// `None`, then the base fee is directly burnt. /// /// Note: this is only used when feature `cdk_erigon` is activated. - pub burn_addr: Option, + pub burn_addr: Option
, /// Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is /// added to `addr`'s balance. See EIP-4895. pub withdrawals: Vec<(Address, U256)>, @@ -87,6 +87,9 @@ pub struct GenerationInputs { /// without requiring proofs for blocks past this checkpoint. pub checkpoint_state_trie_root: H256, + /// Consolidated previous block hashes, at the checkpoint block. + pub checkpoint_consolidated_hash: [F; NUM_HASH_OUT_ELTS], + /// Mapping between smart contract code hashes and the contract byte code. /// All account smart contracts that are invoked will have an entry present. pub contract_code: HashMap>, @@ -108,7 +111,8 @@ pub struct GenerationInputs { /// A lighter version of [`GenerationInputs`], which have been trimmed /// post pre-initialization processing. #[derive(Clone, Debug, Deserialize, Serialize, Default)] -pub struct TrimmedGenerationInputs { +#[serde(bound = "")] +pub struct TrimmedGenerationInputs { pub trimmed_tries: TrimmedTrieInputs, /// The index of the first transaction in this payload being proven within /// its block. @@ -135,6 +139,9 @@ pub struct TrimmedGenerationInputs { /// without requiring proofs for blocks past this checkpoint. pub checkpoint_state_trie_root: H256, + /// Consolidated previous block hashes, at the checkpoint block. + pub checkpoint_consolidated_hash: [F; NUM_HASH_OUT_ELTS], + /// Mapping between smart contract code hashes and the contract byte code. /// All account smart contracts that are invoked will have an entry present. pub contract_code: HashMap>, @@ -144,7 +151,7 @@ pub struct TrimmedGenerationInputs { /// Address where the burnt fees are stored. Only used if the `cfg_erigon` /// feature is activated. - pub burn_addr: Option, + pub burn_addr: Option
, /// The hash of the current block, and a list of the 256 previous block /// hashes. @@ -194,11 +201,11 @@ impl TrieInputs { } } } -impl GenerationInputs { +impl GenerationInputs { /// Outputs a trimmed version of the `GenerationInputs`, that do not contain /// the fields that have already been processed during pre-initialization, /// namely: the input tries, the signed transaction, and the withdrawals. - pub(crate) fn trim(&self) -> TrimmedGenerationInputs { + pub(crate) fn trim(&self) -> TrimmedGenerationInputs { let txn_hashes = self .signed_txns .iter() @@ -218,6 +225,7 @@ impl GenerationInputs { }, trie_roots_after: self.trie_roots_after.clone(), checkpoint_state_trie_root: self.checkpoint_state_trie_root, + checkpoint_consolidated_hash: self.checkpoint_consolidated_hash, contract_code: self.contract_code.clone(), burn_addr: self.burn_addr, block_metadata: self.block_metadata.clone(), @@ -228,16 +236,12 @@ impl GenerationInputs { fn apply_metadata_and_tries_memops, const D: usize>( state: &mut GenerationState, - inputs: &TrimmedGenerationInputs, + inputs: &TrimmedGenerationInputs, registers_before: &RegistersData, registers_after: &RegistersData, ) { let metadata = &inputs.block_metadata; let trie_roots_after = &inputs.trie_roots_after; - #[cfg(feature = "cdk_erigon")] - let burn_addr = inputs - .burn_addr - .map_or_else(U256::max_value, |addr| U256::from_big_endian(&addr.0)); let fields = [ ( GlobalMetadata::BlockBeneficiary, @@ -258,14 +262,17 @@ fn apply_metadata_and_tries_memops, const D: usize> h2u(inputs.block_hashes.cur_hash), ), (GlobalMetadata::BlockGasUsed, metadata.block_gas_used), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockBlobGasUsed, metadata.block_blob_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockExcessBlobGas, metadata.block_excess_blob_gas, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::ParentBeaconBlockRoot, h2u(metadata.parent_beacon_block_root), @@ -304,7 +311,12 @@ fn apply_metadata_and_tries_memops, const D: usize> (GlobalMetadata::KernelHash, h2u(KERNEL.code_hash)), (GlobalMetadata::KernelLen, KERNEL.code.len().into()), #[cfg(feature = "cdk_erigon")] - (GlobalMetadata::BurnAddr, burn_addr), + ( + GlobalMetadata::BurnAddr, + inputs + .burn_addr + .map_or_else(U256::max_value, |addr| U256::from_big_endian(&addr.0)), + ), ]; let channel = MemoryChannel::GeneralPurpose(0); @@ -386,7 +398,7 @@ fn apply_metadata_and_tries_memops, const D: usize> state.traces.memory_ops.extend(ops); } -pub(crate) fn debug_inputs(inputs: &GenerationInputs) { +pub(crate) fn debug_inputs(inputs: &GenerationInputs) { log::debug!("Input signed_txns: {:?}", &inputs.signed_txns); log::debug!("Input state_trie: {:?}", &inputs.tries.state_trie); log::debug!( @@ -441,10 +453,11 @@ fn get_all_memory_address_and_values(memory_before: &MemoryState) -> Vec<(Memory res } -type TablesWithPVsAndFinalMem = ([Vec>; NUM_TABLES], PublicValues); +type TablesWithPVsAndFinalMem = ([Vec>; NUM_TABLES], PublicValues); + pub fn generate_traces, const D: usize>( all_stark: &AllStark, - inputs: &TrimmedGenerationInputs, + inputs: &TrimmedGenerationInputs, config: &StarkConfig, segment_data: &mut GenerationSegmentData, timing: &mut TimingTree, @@ -503,6 +516,7 @@ pub fn generate_traces, const D: usize>( let extra_block_data = ExtraBlockData { checkpoint_state_trie_root: inputs.checkpoint_state_trie_root, + checkpoint_consolidated_hash: inputs.checkpoint_consolidated_hash, txn_number_before: inputs.txn_number_before, txn_number_after, gas_used_before: inputs.gas_used_before, diff --git a/evm_arithmetization/src/generation/segments.rs b/evm_arithmetization/src/generation/segments.rs index 88b4ae0dc..7f123aa10 100644 --- a/evm_arithmetization/src/generation/segments.rs +++ b/evm_arithmetization/src/generation/segments.rs @@ -92,7 +92,7 @@ pub type SegmentRunResult = Option SegmentDataIterator { - pub fn new(inputs: &GenerationInputs, max_cpu_len_log: Option) -> Self { + pub fn new(inputs: &GenerationInputs, max_cpu_len_log: Option) -> Self { debug_inputs(inputs); let interpreter = Interpreter::::new_with_generation_inputs( @@ -170,7 +170,7 @@ impl SegmentDataIterator { } impl Iterator for SegmentDataIterator { - type Item = AllData; + type Item = AllData; fn next(&mut self) -> Option { let run = self.generate_next_segment(self.partial_next_data.clone()); diff --git a/evm_arithmetization/src/generation/state.rs b/evm_arithmetization/src/generation/state.rs index 144634691..b094c15f9 100644 --- a/evm_arithmetization/src/generation/state.rs +++ b/evm_arithmetization/src/generation/state.rs @@ -336,7 +336,7 @@ pub(crate) trait State { #[derive(Debug, Default)] pub struct GenerationState { - pub(crate) inputs: TrimmedGenerationInputs, + pub(crate) inputs: TrimmedGenerationInputs, pub(crate) registers: RegistersState, pub(crate) memory: MemoryState, pub(crate) traces: Traces, @@ -421,7 +421,10 @@ impl GenerationState { trie_roots_ptrs } - pub(crate) fn new(inputs: &GenerationInputs, kernel_code: &[u8]) -> Result { + pub(crate) fn new( + inputs: &GenerationInputs, + kernel_code: &[u8], + ) -> Result { let rlp_prover_inputs = all_rlp_prover_inputs_reversed(&inputs.signed_txns); let withdrawal_prover_inputs = all_withdrawals_prover_inputs_reversed(&inputs.withdrawals); let ger_prover_inputs = all_ger_prover_inputs(inputs.ger_data); @@ -459,7 +462,7 @@ impl GenerationState { } pub(crate) fn new_with_segment_data( - trimmed_inputs: &TrimmedGenerationInputs, + trimmed_inputs: &TrimmedGenerationInputs, segment_data: &GenerationSegmentData, ) -> Result { let mut state = Self { diff --git a/evm_arithmetization/src/get_challenges.rs b/evm_arithmetization/src/get_challenges.rs index bb37e01f0..5f0df7cfa 100644 --- a/evm_arithmetization/src/get_challenges.rs +++ b/evm_arithmetization/src/get_challenges.rs @@ -65,13 +65,16 @@ fn observe_block_metadata< challenger.observe_element(basefee.0); challenger.observe_element(basefee.1); challenger.observe_element(u256_to_u32(block_metadata.block_gas_used)?); - let blob_gas_used = u256_to_u64(block_metadata.block_blob_gas_used)?; - challenger.observe_element(blob_gas_used.0); - challenger.observe_element(blob_gas_used.1); - let excess_blob_gas = u256_to_u64(block_metadata.block_excess_blob_gas)?; - challenger.observe_element(excess_blob_gas.0); - challenger.observe_element(excess_blob_gas.1); - challenger.observe_elements(&h256_limbs::(block_metadata.parent_beacon_block_root)); + #[cfg(feature = "eth_mainnet")] + { + let blob_gas_used = u256_to_u64(block_metadata.block_blob_gas_used)?; + challenger.observe_element(blob_gas_used.0); + challenger.observe_element(blob_gas_used.1); + let excess_blob_gas = u256_to_u64(block_metadata.block_excess_blob_gas)?; + challenger.observe_element(excess_blob_gas.0); + challenger.observe_element(excess_blob_gas.1); + challenger.observe_elements(&h256_limbs::(block_metadata.parent_beacon_block_root)); + } for i in 0..8 { challenger.observe_elements(&u256_limbs(block_metadata.block_bloom[i])); } @@ -98,9 +101,12 @@ fn observe_block_metadata_target< challenger.observe_element(block_metadata.block_chain_id); challenger.observe_elements(&block_metadata.block_base_fee); challenger.observe_element(block_metadata.block_gas_used); - challenger.observe_elements(&block_metadata.block_blob_gas_used); - challenger.observe_elements(&block_metadata.block_excess_blob_gas); - challenger.observe_elements(&block_metadata.parent_beacon_block_root); + #[cfg(feature = "eth_mainnet")] + { + challenger.observe_elements(&block_metadata.block_blob_gas_used); + challenger.observe_elements(&block_metadata.block_excess_blob_gas); + challenger.observe_elements(&block_metadata.parent_beacon_block_root); + } challenger.observe_elements(&block_metadata.block_bloom); } @@ -110,9 +116,10 @@ fn observe_extra_block_data< const D: usize, >( challenger: &mut Challenger, - extra_data: &ExtraBlockData, + extra_data: &ExtraBlockData, ) -> Result<(), ProgramError> { challenger.observe_elements(&h256_limbs(extra_data.checkpoint_state_trie_root)); + challenger.observe_elements(&extra_data.checkpoint_consolidated_hash); challenger.observe_element(u256_to_u32(extra_data.txn_number_before)?); challenger.observe_element(u256_to_u32(extra_data.txn_number_after)?); challenger.observe_element(u256_to_u32(extra_data.gas_used_before)?); @@ -132,6 +139,7 @@ fn observe_extra_block_data_target< C::Hasher: AlgebraicHasher, { challenger.observe_elements(&extra_data.checkpoint_state_trie_root); + challenger.observe_elements(&extra_data.checkpoint_consolidated_hash); challenger.observe_element(extra_data.txn_number_before); challenger.observe_element(extra_data.txn_number_after); challenger.observe_element(extra_data.gas_used_before); @@ -199,7 +207,7 @@ pub(crate) fn observe_public_values< const D: usize, >( challenger: &mut Challenger, - public_values: &PublicValues, + public_values: &PublicValues, ) -> Result<(), ProgramError> { observe_trie_roots::(challenger, &public_values.trie_roots_before); observe_trie_roots::(challenger, &public_values.trie_roots_after); diff --git a/evm_arithmetization/src/lib.rs b/evm_arithmetization/src/lib.rs index 7e456bc63..ca2cf2bd7 100644 --- a/evm_arithmetization/src/lib.rs +++ b/evm_arithmetization/src/lib.rs @@ -183,6 +183,8 @@ #![allow(clippy::field_reassign_with_default)] #![feature(let_chains)] +zk_evm_common::check_chain_features!(); + // Individual STARK processing units pub mod arithmetic; pub mod byte_packing; @@ -232,4 +234,4 @@ pub use starky::config::StarkConfig; /// Returned type from a `SegmentDataIterator`, needed to prove all segments in /// a transaction batch. -pub type AllData = Result<(TrimmedGenerationInputs, GenerationSegmentData), SegmentError>; +pub type AllData = Result<(TrimmedGenerationInputs, GenerationSegmentData), SegmentError>; diff --git a/evm_arithmetization/src/proof.rs b/evm_arithmetization/src/proof.rs index 27f71f8c1..d4a04deff 100644 --- a/evm_arithmetization/src/proof.rs +++ b/evm_arithmetization/src/proof.rs @@ -1,9 +1,12 @@ +use std::marker::PhantomData; + use ethereum_types::{Address, H256, U256}; +use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField, NUM_HASH_OUT_ELTS}; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::config::{GenericConfig, GenericHashOut, Hasher}; use plonky2::util::serialization::{Buffer, IoResult, Read, Write}; use serde::{Deserialize, Serialize}; use starky::config::StarkConfig; @@ -11,7 +14,7 @@ use starky::lookup::GrandProductChallengeSet; use starky::proof::{MultiProof, StarkProofChallenges}; use crate::all_stark::NUM_TABLES; -use crate::util::{get_h160, get_h256, get_u256, h2u}; +use crate::util::{get_h160, get_h256, get_u256, h256_limbs, h2u}; use crate::witness::state::RegistersState; /// The default cap height used for our zkEVM STARK proofs. @@ -27,7 +30,7 @@ pub struct AllProof, C: GenericConfig, co /// their cross-table lookup challenges. pub multi_proof: MultiProof, /// Public memory values used for the recursive proofs. - pub public_values: PublicValues, + pub public_values: PublicValues, } impl, C: GenericConfig, const D: usize> AllProof { @@ -47,7 +50,8 @@ pub(crate) struct AllProofChallenges, const D: usiz /// Memory values which are public. #[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct PublicValues { +#[serde(bound = "")] +pub struct PublicValues { /// Trie hashes before the execution of the local state transition pub trie_roots_before: TrieRoots, /// Trie hashes after the execution of the local state transition. @@ -60,7 +64,7 @@ pub struct PublicValues { /// 256 previous block hashes and current block's hash. pub block_hashes: BlockHashes, /// Extra block data that is specific to the current proof. - pub extra_block_data: ExtraBlockData, + pub extra_block_data: ExtraBlockData, /// Registers to initialize the current proof. pub registers_before: RegistersData, /// Registers at the end of the current proof. @@ -70,12 +74,12 @@ pub struct PublicValues { pub mem_after: MemCap, } -impl PublicValues { +impl PublicValues { /// Extracts public values from the given public inputs of a proof. /// Public values are always the first public inputs added to the circuit, /// so we can start extracting at index 0. /// `len_mem_cap` is the length of the `MemBefore` and `MemAfter` caps. - pub fn from_public_inputs(pis: &[F]) -> Self { + pub fn from_public_inputs(pis: &[F]) -> Self { assert!(pis.len() >= PublicValuesTarget::SIZE); let mut offset = 0; @@ -130,37 +134,64 @@ impl PublicValues { /// Memory values which are public once a final block proof is generated. #[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct FinalPublicValues { +#[serde(bound = "")] +pub struct FinalPublicValues> { + /// The chain id of this chian. + pub chain_id: U256, /// State trie root before the execution of this global state transition. - pub state_trie_root_before: H256, + pub checkpoint_state_trie_root: H256, /// State trie root after the execution of this global state transition. - pub state_trie_root_after: H256, + pub new_state_trie_root: H256, + /// A compact view of the block hashes before the previous checkpoint. + pub checkpoint_consolidated_hash: [F; NUM_HASH_OUT_ELTS], + /// A compact view of the previous block hashes, for connection past + /// checkpoints. + pub new_consolidated_hash: [F; NUM_HASH_OUT_ELTS], + + _phantom: PhantomData, } -impl FinalPublicValues { +impl> FinalPublicValues { /// Extracts final public values from the given public inputs of a proof. /// Public values are always the first public inputs added to the circuit, /// so we can start extracting at index 0. - pub fn from_public_inputs(pis: &[F]) -> Self { + pub fn from_public_inputs(pis: &[F]) -> Self { assert!(FinalPublicValuesTarget::SIZE <= pis.len()); - let mut offset = 0; - let state_trie_root_before = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); + let chain_id = pis[0].to_noncanonical_u64().into(); + let mut offset = 1; + let checkpoint_state_trie_root = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); + offset += TARGET_HASH_SIZE; + let new_state_trie_root = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); offset += TARGET_HASH_SIZE; - let state_trie_root_after = get_h256(&pis[offset..offset + TARGET_HASH_SIZE]); + let checkpoint_consolidated_hash = + pis[offset..offset + NUM_HASH_OUT_ELTS].try_into().unwrap(); + offset += NUM_HASH_OUT_ELTS; + let new_consolidated_hash = pis[offset..offset + NUM_HASH_OUT_ELTS].try_into().unwrap(); Self { - state_trie_root_before, - state_trie_root_after, + chain_id, + checkpoint_state_trie_root, + new_state_trie_root, + checkpoint_consolidated_hash, + new_consolidated_hash, + _phantom: PhantomData, } } } -impl From for FinalPublicValues { - fn from(value: PublicValues) -> Self { +impl, F: RichField> From> for FinalPublicValues { + fn from(value: PublicValues) -> Self { + let mut hash_payload = value.block_hashes.prev_hashes[1..].to_vec(); + hash_payload.push(value.block_hashes.cur_hash); + Self { - state_trie_root_before: value.trie_roots_before.state_root, - state_trie_root_after: value.trie_roots_after.state_root, + chain_id: value.block_metadata.block_chain_id, + checkpoint_state_trie_root: value.trie_roots_before.state_root, + new_state_trie_root: value.trie_roots_after.state_root, + checkpoint_consolidated_hash: value.extra_block_data.checkpoint_consolidated_hash, + new_consolidated_hash: consolidate_hashes::(&hash_payload), + _phantom: PhantomData, } } } @@ -170,57 +201,95 @@ impl From for FinalPublicValues { /// order. #[derive(Eq, PartialEq, Debug)] pub struct FinalPublicValuesTarget { + /// The chain id of this chian. + pub chain_id: Target, /// State trie root before the execution of this global state transition. - pub state_trie_root_before: [Target; TARGET_HASH_SIZE], + pub checkpoint_state_trie_root: [Target; TARGET_HASH_SIZE], /// State trie root after the execution of this global state transition. - pub state_trie_root_after: [Target; TARGET_HASH_SIZE], + pub new_state_trie_root: [Target; TARGET_HASH_SIZE], + /// A compact view of the block hashes before the previous checkpoint. + pub checkpoint_consolidated_hash: [Target; NUM_HASH_OUT_ELTS], + /// A compact view of the previous block hashes, for connection past + /// checkpoints. + pub new_consolidated_hash: [Target; NUM_HASH_OUT_ELTS], } impl FinalPublicValuesTarget { - pub(crate) const SIZE: usize = TARGET_HASH_SIZE * 2; + pub(crate) const SIZE: usize = 1 + TARGET_HASH_SIZE * 2 + NUM_HASH_OUT_ELTS * 2; /// Serializes public value targets. pub(crate) fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { - buffer.write_target_array(&self.state_trie_root_before)?; - buffer.write_target_array(&self.state_trie_root_after)?; + buffer.write_target(self.chain_id)?; + buffer.write_target_array(&self.checkpoint_state_trie_root)?; + buffer.write_target_array(&self.new_state_trie_root)?; + buffer.write_target_array(&self.checkpoint_consolidated_hash)?; + buffer.write_target_array(&self.new_consolidated_hash)?; Ok(()) } /// Deserializes public value targets. pub(crate) fn from_buffer(buffer: &mut Buffer) -> IoResult { - let state_trie_root_before = buffer.read_target_array()?; - let state_trie_root_after = buffer.read_target_array()?; + let chain_id = buffer.read_target()?; + let checkpoint_state_trie_root = buffer.read_target_array()?; + let new_state_trie_root = buffer.read_target_array()?; + let checkpoint_consolidated_hash = buffer.read_target_array()?; + let new_consolidated_hash = buffer.read_target_array()?; Ok(Self { - state_trie_root_before, - state_trie_root_after, + chain_id, + checkpoint_state_trie_root, + new_state_trie_root, + checkpoint_consolidated_hash, + new_consolidated_hash, }) } /// Connects these `FinalPublicValuesTarget` with their corresponding /// counterpart in a full parent `PublicValuesTarget`. - pub(crate) fn connect_parent, const D: usize>( + pub(crate) fn connect_parent( &self, builder: &mut CircuitBuilder, - pv1: &PublicValuesTarget, - ) { - for i in 0..8 { + pv: &PublicValuesTarget, + ) where + F: RichField + Extendable, + C: GenericConfig + 'static, + { + builder.connect(self.chain_id, pv.block_metadata.block_chain_id); + + for i in 0..TARGET_HASH_SIZE { builder.connect( - self.state_trie_root_before[i], - pv1.trie_roots_before.state_root[i], + self.checkpoint_state_trie_root[i], + pv.trie_roots_before.state_root[i], ); builder.connect( - self.state_trie_root_after[i], - pv1.trie_roots_after.state_root[i], + self.new_state_trie_root[i], + pv.trie_roots_after.state_root[i], ); // We only use `FinalPublicValues` at the final block proof wrapping stage, // where we should enforce consistency with the known checkpoint. builder.connect( - self.state_trie_root_before[i], - pv1.extra_block_data.checkpoint_state_trie_root[i], + self.checkpoint_state_trie_root[i], + pv.extra_block_data.checkpoint_state_trie_root[i], ); } + + for i in 0..NUM_HASH_OUT_ELTS { + builder.connect( + self.checkpoint_consolidated_hash[i], + pv.extra_block_data.checkpoint_consolidated_hash[i], + ); + } + + let mut hash_payload = pv.block_hashes.prev_hashes[TARGET_HASH_SIZE..].to_vec(); + hash_payload.extend_from_slice(&pv.block_hashes.cur_hash); + let consolidated_hash = builder + .hash_n_to_hash_no_pad::(hash_payload) + .elements; + + for i in 0..NUM_HASH_OUT_ELTS { + builder.connect(self.new_consolidated_hash[i], consolidated_hash[i]); + } } } @@ -294,6 +363,19 @@ impl BlockHashes { } } +/// Generates the consolidated hash for a sequence of block hashes. +/// +/// It will pack 256 contiguous block hashes and hash them out. +pub fn consolidate_hashes, F: RichField>(hashes: &[H256]) -> [F; NUM_HASH_OUT_ELTS] { + debug_assert!(hashes.len() == 256); + + let payload = hashes.iter().flat_map(|&h| h256_limbs(h)).collect_vec(); + H::hash_no_pad(&payload) + .to_vec() + .try_into() + .expect("Digests have fixed size.") +} + /// Metadata contained in a block header. Those are identical between /// all state transition proofs within the same block. #[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] @@ -368,10 +450,13 @@ impl BlockMetadata { /// Additional block data that are specific to the local transaction being /// proven, unlike `BlockMetadata`. -#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)] -pub struct ExtraBlockData { +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +#[serde(bound = "")] +pub struct ExtraBlockData { /// The state trie digest of the checkpoint block. pub checkpoint_state_trie_root: H256, + /// The consolidated previous block hashes, at the checkpoint block. + pub checkpoint_consolidated_hash: [F; NUM_HASH_OUT_ELTS], /// The transaction count prior execution of the local state transition, /// starting at 0 for the initial transaction of a block. pub txn_number_before: U256, @@ -386,18 +471,42 @@ pub struct ExtraBlockData { pub gas_used_after: U256, } -impl ExtraBlockData { - pub fn from_public_inputs(pis: &[F]) -> Self { +impl Default for ExtraBlockData { + fn default() -> Self { + Self { + checkpoint_state_trie_root: H256::default(), + checkpoint_consolidated_hash: EMPTY_CONSOLIDATED_BLOCKHASH.map(F::from_canonical_u64), + txn_number_before: U256::default(), + txn_number_after: U256::default(), + gas_used_before: U256::default(), + gas_used_after: U256::default(), + } + } +} + +/// Consolidated hash for the Genesis block, where all previous block hashes +/// default to 0. +pub const EMPTY_CONSOLIDATED_BLOCKHASH: [u64; NUM_HASH_OUT_ELTS] = [ + 5498946765822202150, + 10724662260254836878, + 9161393967331872654, + 5704373722058976135, +]; + +impl ExtraBlockData { + pub fn from_public_inputs(pis: &[F]) -> Self { assert!(pis.len() == ExtraBlockDataTarget::SIZE); let checkpoint_state_trie_root = get_h256(&pis[0..8]); - let txn_number_before = pis[8].to_canonical_u64().into(); - let txn_number_after = pis[9].to_canonical_u64().into(); - let gas_used_before = pis[10].to_canonical_u64().into(); - let gas_used_after = pis[11].to_canonical_u64().into(); + let checkpoint_consolidated_hash = pis[8..12].try_into().unwrap(); + let txn_number_before = pis[12].to_canonical_u64().into(); + let txn_number_after = pis[13].to_canonical_u64().into(); + let gas_used_before = pis[14].to_canonical_u64().into(); + let gas_used_after = pis[15].to_canonical_u64().into(); Self { checkpoint_state_trie_root, + checkpoint_consolidated_hash, txn_number_before, txn_number_after, gas_used_before, @@ -574,12 +683,14 @@ impl PublicValuesTarget { let ExtraBlockDataTarget { checkpoint_state_trie_root, + checkpoint_consolidated_hash, txn_number_before, txn_number_after, gas_used_before, gas_used_after, } = self.extra_block_data; buffer.write_target_array(&checkpoint_state_trie_root)?; + buffer.write_target_array(&checkpoint_consolidated_hash)?; buffer.write_target(txn_number_before)?; buffer.write_target(txn_number_after)?; buffer.write_target(gas_used_before)?; @@ -661,6 +772,7 @@ impl PublicValuesTarget { let extra_block_data = ExtraBlockDataTarget { checkpoint_state_trie_root: buffer.read_target_array()?, + checkpoint_consolidated_hash: buffer.read_target_array()?, txn_number_before: buffer.read_target()?, txn_number_after: buffer.read_target()?, gas_used_before: buffer.read_target()?, @@ -968,59 +1080,45 @@ impl BurnAddrTarget { } } + #[cfg(feature = "cdk_erigon")] /// Connects the burn address in `ba0` to the burn address in `ba1`. - /// This is a no-op if `cdk_erigon` feature is not activated. - /// - /// This will panic if the `cdk_erigon` is activated and not both - /// `BurnAddrTarget`s are `BurnAddr` variants. pub(crate) fn connect, const D: usize>( builder: &mut CircuitBuilder, ba0: Self, ba1: Self, ) { - // There only are targets to connect if there is a burn address, i.e. when the - // `cdk_erigon` feature is active. - if cfg!(feature = "cdk_erigon") == true { - // If the `cdk_erigon` feature is activated, both `ba0` and `ba1` should be of - // type `BurnAddr`. - match (ba0, ba1) { - (BurnAddrTarget::BurnAddr(a0), BurnAddrTarget::BurnAddr(a1)) => { - for i in 0..BurnAddrTarget::get_size() { - builder.connect(a0[i], a1[i]); - } + match (ba0, ba1) { + (BurnAddrTarget::BurnAddr(a0), BurnAddrTarget::BurnAddr(a1)) => { + for i in 0..BurnAddrTarget::get_size() { + builder.connect(a0[i], a1[i]); } - _ => panic!("We should have already set an address (or U256::MAX) before."), } + _ => panic!("We should have already set an address (or U256::MAX) before."), } } + #[cfg(feature = "cdk_erigon")] /// If `condition`, asserts that `ba0 == ba1`. - /// This is a no-op if `cdk_erigon` feature is not activated. - /// - /// This will panic if the `cdk_erigon` is activated and not both - /// `BurnAddrTarget` are `BurnAddr` variants. pub(crate) fn conditional_assert_eq, const D: usize>( builder: &mut CircuitBuilder, condition: BoolTarget, ba0: Self, ba1: Self, ) { - if cfg!(feature = "cdk_erigon") { - match (ba0, ba1) { - ( - BurnAddrTarget::BurnAddr(addr_targets_0), - BurnAddrTarget::BurnAddr(addr_targets_1), - ) => { - for i in 0..BurnAddrTarget::get_size() { - builder.conditional_assert_eq( - condition.target, - addr_targets_0[i], - addr_targets_1[i], - ) - } + match (ba0, ba1) { + ( + BurnAddrTarget::BurnAddr(addr_targets_0), + BurnAddrTarget::BurnAddr(addr_targets_1), + ) => { + for i in 0..BurnAddrTarget::get_size() { + builder.conditional_assert_eq( + condition.target, + addr_targets_0[i], + addr_targets_1[i], + ) } - _ => panic!("There should be an address set in cdk_erigon."), } + _ => panic!("There should be an address set in cdk_erigon."), } } } @@ -1243,7 +1341,7 @@ pub struct BlockHashesTarget { /// hash, i.e. `prev_hashes[0..8]`, is the oldest, and the rightmost, /// i.e. `prev_hashes[255 * 7..255 * 8]` is the hash of the parent block. pub(crate) prev_hashes: [Target; 2048], - // `Target` for the hash of the current block. + // `Target`s for the hash of the current block. pub(crate) cur_hash: [Target; 8], } @@ -1316,6 +1414,9 @@ impl BlockHashesTarget { pub struct ExtraBlockDataTarget { /// `Target`s for the state trie digest of the checkpoint block. pub checkpoint_state_trie_root: [Target; 8], + /// `Target`s for the consolidated previous block hashes, at the checkpoint + /// block. + pub checkpoint_consolidated_hash: [Target; NUM_HASH_OUT_ELTS], /// `Target` for the transaction count prior execution of the local state /// transition, starting at 0 for the initial trnasaction of a block. pub txn_number_before: Target, @@ -1333,19 +1434,21 @@ pub struct ExtraBlockDataTarget { impl ExtraBlockDataTarget { /// Number of `Target`s required for the extra block data. - pub(crate) const SIZE: usize = 12; + pub(crate) const SIZE: usize = 16; /// Extracts the extra block data `Target`s from the public input `Target`s. /// The provided `pis` should start with the extra vblock data. pub(crate) fn from_public_inputs(pis: &[Target]) -> Self { let checkpoint_state_trie_root = pis[0..8].try_into().unwrap(); - let txn_number_before = pis[8]; - let txn_number_after = pis[9]; - let gas_used_before = pis[10]; - let gas_used_after = pis[11]; + let checkpoint_consolidated_hash = pis[8..12].try_into().unwrap(); + let txn_number_before = pis[12]; + let txn_number_after = pis[13]; + let gas_used_before = pis[14]; + let gas_used_after = pis[15]; Self { checkpoint_state_trie_root, + checkpoint_consolidated_hash, txn_number_before, txn_number_after, gas_used_before, @@ -1369,6 +1472,13 @@ impl ExtraBlockDataTarget { ed1.checkpoint_state_trie_root[i], ) }), + checkpoint_consolidated_hash: core::array::from_fn(|i| { + builder.select( + condition, + ed0.checkpoint_consolidated_hash[i], + ed1.checkpoint_consolidated_hash[i], + ) + }), txn_number_before: builder.select( condition, ed0.txn_number_before, @@ -1393,6 +1503,12 @@ impl ExtraBlockDataTarget { ed1.checkpoint_state_trie_root[i], ); } + for i in 0..NUM_HASH_OUT_ELTS { + builder.connect( + ed0.checkpoint_consolidated_hash[i], + ed1.checkpoint_consolidated_hash[i], + ); + } builder.connect(ed0.txn_number_before, ed1.txn_number_before); builder.connect(ed0.txn_number_after, ed1.txn_number_after); builder.connect(ed0.gas_used_before, ed1.gas_used_before); @@ -1413,6 +1529,13 @@ impl ExtraBlockDataTarget { ed1.checkpoint_state_trie_root[i], ); } + for i in 0..NUM_HASH_OUT_ELTS { + builder.conditional_assert_eq( + condition.target, + ed0.checkpoint_consolidated_hash[i], + ed1.checkpoint_consolidated_hash[i], + ); + } builder.conditional_assert_eq( condition.target, ed0.txn_number_before, diff --git a/evm_arithmetization/src/prover.rs b/evm_arithmetization/src/prover.rs index bd6769607..0cd825e87 100644 --- a/evm_arithmetization/src/prover.rs +++ b/evm_arithmetization/src/prover.rs @@ -31,7 +31,7 @@ use crate::proof::{AllProof, MemCap, PublicValues, DEFAULT_CAP_LEN}; pub fn prove( all_stark: &AllStark, config: &StarkConfig, - inputs: TrimmedGenerationInputs, + inputs: TrimmedGenerationInputs, segment_data: &mut GenerationSegmentData, timing: &mut TimingTree, abort_signal: Option>, @@ -40,9 +40,8 @@ where F: RichField + Extendable, C: GenericConfig, { - if inputs.burn_addr.is_some() && !cfg!(feature = "cdk_erigon") { - log::warn!("The burn address in the GenerationInputs will be ignored, as the `cdk_erigon` feature is not activated.") - } + features_check(&inputs); + // Sanity check on the provided config assert_eq!(DEFAULT_CAP_LEN, 1 << config.fri_config.cap_height); @@ -73,7 +72,7 @@ pub(crate) fn prove_with_traces( all_stark: &AllStark, config: &StarkConfig, trace_poly_values: [Vec>; NUM_TABLES], - public_values: &mut PublicValues, + public_values: &mut PublicValues, timing: &mut TimingTree, abort_signal: Option>, ) -> Result> @@ -473,6 +472,20 @@ pub fn check_abort_signal(abort_signal: Option>) -> Result<()> { Ok(()) } +/// Sanity checks on the consistency between this proof payload and the feature +/// flags being used. +pub(crate) fn features_check(inputs: &TrimmedGenerationInputs) { + if !cfg!(feature = "eth_mainnet") { + assert!(inputs.block_metadata.parent_beacon_block_root.is_zero()); + assert!(inputs.block_metadata.block_blob_gas_used.is_zero()); + assert!(inputs.block_metadata.block_excess_blob_gas.is_zero()); + } + + if !cfg!(feature = "cdk_erigon") { + assert!(inputs.burn_addr.is_none()); + } +} + /// A utility module designed to test witness generation externally. pub mod testing { use super::*; @@ -487,10 +500,9 @@ pub mod testing { /// Simulates the zkEVM CPU execution. /// It does not generate any trace or proof of correct state transition. - pub fn simulate_execution(inputs: GenerationInputs) -> Result<()> { - if inputs.burn_addr.is_some() && !cfg!(feature = "cdk_erigon") { - log::warn!("The burn address in the GenerationInputs will be ignored, as the `cdk_erigon` feature is not activated.") - } + pub fn simulate_execution(inputs: GenerationInputs) -> Result<()> { + features_check(&inputs.clone().trim()); + let initial_stack = vec![]; let initial_offset = KERNEL.global_labels["init"]; let mut interpreter: Interpreter = @@ -508,7 +520,7 @@ pub mod testing { pub fn prove_all_segments( all_stark: &AllStark, config: &StarkConfig, - inputs: GenerationInputs, + inputs: GenerationInputs, max_cpu_len_log: usize, timing: &mut TimingTree, abort_signal: Option>, @@ -539,12 +551,14 @@ pub mod testing { } pub fn simulate_execution_all_segments( - inputs: GenerationInputs, + inputs: GenerationInputs, max_cpu_len_log: usize, ) -> Result<()> where F: RichField, { + features_check(&inputs.clone().trim()); + for segment in SegmentDataIterator::::new(&inputs, Some(max_cpu_len_log)) { if let Err(e) = segment { return Err(anyhow::format_err!(e)); diff --git a/evm_arithmetization/src/recursive_verifier.rs b/evm_arithmetization/src/recursive_verifier.rs index a15c86b2d..417bf468d 100644 --- a/evm_arithmetization/src/recursive_verifier.rs +++ b/evm_arithmetization/src/recursive_verifier.rs @@ -14,7 +14,7 @@ use plonky2::iop::target::Target; use plonky2::iop::witness::{PartialWitness, Witness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; -use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::util::serialization::{ Buffer, GateSerializer, IoResult, Read, WitnessGeneratorSerializer, Write, @@ -380,10 +380,12 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, ), ]; - // This contains the `block_beneficiary`, `block_random`, `block_base_fee`, - // `block_blob_gas_used`, `block_excess_blob_gas`, `parent_beacon_block_root` - // as well as `cur_hash`. - let block_fields_arrays: [(GlobalMetadata, &[Target]); 7] = [ + // This contains the `block_beneficiary`, `block_random`, `block_base_fee`, and + // `cur_hash`, as well as the additional `block_blob_gas_used`, + // `block_excess_blob_gas`, `parent_beacon_block_root` when compiling with + // `eth_mainnet` feature flag. + const LENGTH: usize = if cfg!(feature = "eth_mainnet") { 7 } else { 4 }; + let block_fields_arrays: [(GlobalMetadata, &[Target]); LENGTH] = [ ( GlobalMetadata::BlockBeneficiary, &public_values.block_metadata.block_beneficiary, @@ -396,14 +398,17 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, GlobalMetadata::BlockBaseFee, &public_values.block_metadata.block_base_fee, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockBlobGasUsed, &public_values.block_metadata.block_blob_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockExcessBlobGas, &public_values.block_metadata.block_excess_blob_gas, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::ParentBeaconBlockRoot, &public_values.block_metadata.parent_beacon_block_root, @@ -634,12 +639,18 @@ pub(crate) fn add_virtual_final_public_values_public_input< >( builder: &mut CircuitBuilder, ) -> FinalPublicValuesTarget { - let state_trie_root_before = builder.add_virtual_public_input_arr(); - let state_trie_root_after = builder.add_virtual_public_input_arr(); + let chain_id = builder.add_virtual_public_input(); + let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); + let new_state_trie_root = builder.add_virtual_public_input_arr(); + let checkpoint_consolidated_hash = builder.add_virtual_public_input_arr(); + let new_consolidated_hash = builder.add_virtual_public_input_arr(); FinalPublicValuesTarget { - state_trie_root_before, - state_trie_root_after, + chain_id, + checkpoint_state_trie_root, + new_state_trie_root, + checkpoint_consolidated_hash, + new_consolidated_hash, } } @@ -761,6 +772,7 @@ pub(crate) fn add_virtual_extra_block_data_public_input< builder: &mut CircuitBuilder, ) -> ExtraBlockDataTarget { let checkpoint_state_trie_root = builder.add_virtual_public_input_arr(); + let checkpoint_consolidated_hash = builder.add_virtual_public_input_arr(); let txn_number_before = builder.add_virtual_public_input(); let txn_number_after = builder.add_virtual_public_input(); let gas_used_before = builder.add_virtual_public_input(); @@ -768,6 +780,7 @@ pub(crate) fn add_virtual_extra_block_data_public_input< ExtraBlockDataTarget { checkpoint_state_trie_root, + checkpoint_consolidated_hash, txn_number_before, txn_number_after, gas_used_before, @@ -798,7 +811,7 @@ pub(crate) fn add_virtual_registers_data_public_input< } } -pub(crate) fn debug_public_values(public_values: &PublicValues) { +pub(crate) fn debug_public_values(public_values: &PublicValues) { log::debug!("Public Values:"); log::debug!( " Trie Roots Before: {:?}", @@ -813,7 +826,7 @@ pub(crate) fn debug_public_values(public_values: &PublicValues) { pub fn set_public_value_targets( witness: &mut W, public_values_target: &PublicValuesTarget, - public_values: &PublicValues, + public_values: &PublicValues, ) -> Result<(), ProgramError> where F: RichField + Extendable, @@ -879,49 +892,59 @@ where Ok(()) } -pub fn set_final_public_value_targets( +pub fn set_final_public_value_targets( witness: &mut W, public_values_target: &FinalPublicValuesTarget, - public_values: &FinalPublicValues, + public_values: &FinalPublicValues, ) -> Result<(), ProgramError> where F: RichField + Extendable, + H: Hasher, W: Witness, { + witness.set_target( + public_values_target.chain_id, + F::from_canonical_u64(public_values.chain_id.low_u64()), + ); + for (i, limb) in public_values - .state_trie_root_before + .checkpoint_state_trie_root .into_uint() .0 .into_iter() .enumerate() { witness.set_target( - public_values_target.state_trie_root_before[2 * i], + public_values_target.checkpoint_state_trie_root[2 * i], F::from_canonical_u32(limb as u32), ); witness.set_target( - public_values_target.state_trie_root_before[2 * i + 1], + public_values_target.checkpoint_state_trie_root[2 * i + 1], F::from_canonical_u32((limb >> 32) as u32), ); } for (i, limb) in public_values - .state_trie_root_after + .new_state_trie_root .into_uint() .0 .into_iter() .enumerate() { witness.set_target( - public_values_target.state_trie_root_after[2 * i], + public_values_target.new_state_trie_root[2 * i], F::from_canonical_u32(limb as u32), ); witness.set_target( - public_values_target.state_trie_root_after[2 * i + 1], + public_values_target.new_state_trie_root[2 * i + 1], F::from_canonical_u32((limb >> 32) as u32), ); } + for (i, limb) in public_values.new_consolidated_hash.iter().enumerate() { + witness.set_target(public_values_target.new_consolidated_hash[i], *limb); + } + Ok(()) } @@ -1046,31 +1069,34 @@ where block_metadata_target.block_gas_used, u256_to_u32(block_metadata.block_gas_used)?, ); - // BlobGasUsed fits in 2 limbs - let blob_gas_used = u256_to_u64(block_metadata.block_blob_gas_used)?; - witness.set_target( - block_metadata_target.block_blob_gas_used[0], - blob_gas_used.0, - ); - witness.set_target( - block_metadata_target.block_blob_gas_used[1], - blob_gas_used.1, - ); - // ExcessBlobGas fits in 2 limbs - let excess_blob_gas = u256_to_u64(block_metadata.block_excess_blob_gas)?; - witness.set_target( - block_metadata_target.block_excess_blob_gas[0], - excess_blob_gas.0, - ); - witness.set_target( - block_metadata_target.block_excess_blob_gas[1], - excess_blob_gas.1, - ); + #[cfg(feature = "eth_mainnet")] + { + // BlobGasUsed fits in 2 limbs + let blob_gas_used = u256_to_u64(block_metadata.block_blob_gas_used)?; + witness.set_target( + block_metadata_target.block_blob_gas_used[0], + blob_gas_used.0, + ); + witness.set_target( + block_metadata_target.block_blob_gas_used[1], + blob_gas_used.1, + ); + // ExcessBlobGas fits in 2 limbs + let excess_blob_gas = u256_to_u64(block_metadata.block_excess_blob_gas)?; + witness.set_target( + block_metadata_target.block_excess_blob_gas[0], + excess_blob_gas.0, + ); + witness.set_target( + block_metadata_target.block_excess_blob_gas[1], + excess_blob_gas.1, + ); - witness.set_target_arr( - &block_metadata_target.parent_beacon_block_root, - &h256_limbs(block_metadata.parent_beacon_block_root), - ); + witness.set_target_arr( + &block_metadata_target.parent_beacon_block_root, + &h256_limbs(block_metadata.parent_beacon_block_root), + ); + } let mut block_bloom_limbs = [F::ZERO; 64]; for (i, limbs) in block_bloom_limbs.chunks_exact_mut(8).enumerate() { @@ -1103,7 +1129,7 @@ pub(crate) fn set_block_hashes_target( pub(crate) fn set_extra_public_values_target( witness: &mut W, ed_target: &ExtraBlockDataTarget, - ed: &ExtraBlockData, + ed: &ExtraBlockData, ) -> Result<(), ProgramError> where F: RichField + Extendable, @@ -1113,6 +1139,10 @@ where &ed_target.checkpoint_state_trie_root, &h256_limbs::(ed.checkpoint_state_trie_root), ); + witness.set_target_arr( + &ed_target.checkpoint_consolidated_hash, + &ed.checkpoint_consolidated_hash, + ); witness.set_target( ed_target.txn_number_before, u256_to_u32(ed.txn_number_before)?, diff --git a/evm_arithmetization/src/testing_utils.rs b/evm_arithmetization/src/testing_utils.rs index 4152da5b6..7018e06be 100644 --- a/evm_arithmetization/src/testing_utils.rs +++ b/evm_arithmetization/src/testing_utils.rs @@ -64,8 +64,8 @@ pub fn update_beacon_roots_account_storage( timestamp: U256, parent_root: H256, ) -> anyhow::Result<()> { - let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH.1; - let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH.1; + let timestamp_idx = timestamp % HISTORY_BUFFER_LENGTH.value; + let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH.value; insert_storage(storage_trie, timestamp_idx, timestamp)?; insert_storage(storage_trie, root_idx, h2u(parent_root)) @@ -161,9 +161,3 @@ pub fn scalable_contract_from_storage(storage_trie: &HashedPartialTrie) -> Accou ..Default::default() } } - -/// Converts an amount in `ETH` to `wei` units. -pub fn eth_to_wei(eth: U256) -> U256 { - // 1 ether = 10^18 wei. - eth * U256::from(10).pow(18.into()) -} diff --git a/evm_arithmetization/src/verifier.rs b/evm_arithmetization/src/verifier.rs index 52c2c21eb..9ee5750f8 100644 --- a/evm_arithmetization/src/verifier.rs +++ b/evm_arithmetization/src/verifier.rs @@ -94,7 +94,7 @@ fn verify_initial_memory< C: GenericConfig, const D: usize, >( - public_values: &PublicValues, + public_values: &PublicValues, config: &StarkConfig, ) -> Result<()> { for (hash1, hash2) in initial_memory_merkle_cap::( @@ -271,7 +271,7 @@ fn verify_proof, C: GenericConfig, const /// - block metadata writes, /// - trie roots writes. pub(crate) fn get_memory_extra_looking_sum( - public_values: &PublicValues, + public_values: &PublicValues, challenge: GrandProductChallenge, ) -> F where @@ -320,6 +320,7 @@ where GlobalMetadata::BlockBaseFee, public_values.block_metadata.block_base_fee, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::ParentBeaconBlockRoot, h2u(public_values.block_metadata.parent_beacon_block_root), @@ -332,10 +333,12 @@ where GlobalMetadata::BlockGasUsed, public_values.block_metadata.block_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockBlobGasUsed, public_values.block_metadata.block_blob_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockExcessBlobGas, public_values.block_metadata.block_excess_blob_gas, @@ -494,7 +497,7 @@ pub(crate) mod debug_utils { /// Output all the extra memory rows that don't appear in the CPU trace but /// are necessary to correctly check the MemoryStark CTL. pub(crate) fn get_memory_extra_looking_values( - public_values: &PublicValues, + public_values: &PublicValues, ) -> Vec> where F: RichField + Extendable, @@ -505,6 +508,13 @@ pub(crate) mod debug_utils { GlobalMetadata::BlockBeneficiary, U256::from_big_endian(&public_values.block_metadata.block_beneficiary.0), ), + #[cfg(feature = "cdk_erigon")] + ( + GlobalMetadata::BurnAddr, + public_values + .burn_addr + .expect("There should be an address set in cdk_erigon."), + ), ( GlobalMetadata::BlockTimestamp, public_values.block_metadata.block_timestamp, @@ -541,14 +551,17 @@ pub(crate) mod debug_utils { GlobalMetadata::BlockGasUsed, public_values.block_metadata.block_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockBlobGasUsed, public_values.block_metadata.block_blob_gas_used, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::BlockExcessBlobGas, public_values.block_metadata.block_excess_blob_gas, ), + #[cfg(feature = "eth_mainnet")] ( GlobalMetadata::ParentBeaconBlockRoot, h2u(public_values.block_metadata.parent_beacon_block_root), diff --git a/evm_arithmetization/src/witness/transition.rs b/evm_arithmetization/src/witness/transition.rs index 1bcbeb567..6263977f4 100644 --- a/evm_arithmetization/src/witness/transition.rs +++ b/evm_arithmetization/src/witness/transition.rs @@ -120,7 +120,9 @@ pub(crate) fn decode(registers: RegistersState, opcode: u8) -> Result Ok(Operation::Syscall(opcode, 0, true)), // CHAINID (0x47, _) => Ok(Operation::Syscall(opcode, 0, true)), // SELFBALANCE (0x48, _) => Ok(Operation::Syscall(opcode, 0, true)), // BASEFEE + #[cfg(feature = "eth_mainnet")] (0x49, _) => Ok(Operation::Syscall(opcode, 1, false)), // BLOBHASH + #[cfg(feature = "eth_mainnet")] (0x4a, _) => Ok(Operation::Syscall(opcode, 0, true)), // BLOBBASEFEE (0x50, _) => Ok(Operation::Pop), (0x51, _) => Ok(Operation::Syscall(opcode, 1, false)), // MLOAD diff --git a/evm_arithmetization/src/witness/util.rs b/evm_arithmetization/src/witness/util.rs index 5769f6600..5bd103096 100644 --- a/evm_arithmetization/src/witness/util.rs +++ b/evm_arithmetization/src/witness/util.rs @@ -1,5 +1,4 @@ use ethereum_types::U256; -use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use super::memory::DUMMY_MEMOP; @@ -23,7 +22,7 @@ fn to_byte_checked(n: U256) -> u8 { res } -fn to_bits_le(n: u8) -> [F; 8] { +fn to_bits_le(n: u8) -> [F; 8] { let mut res = [F::ZERO; 8]; for (i, bit) in res.iter_mut().enumerate() { *bit = F::from_bool(n & (1 << i) != 0); @@ -62,7 +61,11 @@ pub(crate) fn current_context_peek( .get_with_init(MemoryAddress::new(context, segment, virt)) } -pub(crate) fn fill_channel_with_value(row: &mut CpuColumnsView, n: usize, val: U256) { +pub(crate) fn fill_channel_with_value( + row: &mut CpuColumnsView, + n: usize, + val: U256, +) { let channel = &mut row.mem_channels[n]; let val_limbs: [u64; 4] = val.0; for (i, limb) in val_limbs.into_iter().enumerate() { diff --git a/evm_arithmetization/tests/add11_yml.rs b/evm_arithmetization/tests/add11_yml.rs index e892d2772..5b8290229 100644 --- a/evm_arithmetization/tests/add11_yml.rs +++ b/evm_arithmetization/tests/add11_yml.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::collections::HashMap; use std::str::FromStr; @@ -21,6 +21,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -28,7 +30,7 @@ type F = GoldilocksField; const D: usize = 2; type C = KeccakGoldilocksConfig; -fn get_generation_inputs() -> GenerationInputs { +fn get_generation_inputs() -> GenerationInputs { let beneficiary = hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"); let sender = hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); let to = hex!("095e7baea6a6c7c4c2dfeb977efac326af552d87"); @@ -180,7 +182,7 @@ fn get_generation_inputs() -> GenerationInputs { receipts_root: receipts_trie.hash(), }; - GenerationInputs { + GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -190,6 +192,7 @@ fn get_generation_inputs() -> GenerationInputs { contract_code, block_metadata, checkpoint_state_trie_root: state_trie_before.hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], txn_number_before: 0.into(), gas_used_before: 0.into(), gas_used_after: 0xa868u64.into(), diff --git a/evm_arithmetization/tests/erc20.rs b/evm_arithmetization/tests/erc20.rs index cd16de80f..2c1a56829 100644 --- a/evm_arithmetization/tests/erc20.rs +++ b/evm_arithmetization/tests/erc20.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::str::FromStr; use std::time::Duration; @@ -19,6 +19,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -176,7 +178,7 @@ fn test_erc20() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -185,6 +187,7 @@ fn test_erc20() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/erc721.rs b/evm_arithmetization/tests/erc721.rs index 851560430..170a19df4 100644 --- a/evm_arithmetization/tests/erc721.rs +++ b/evm_arithmetization/tests/erc721.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::str::FromStr; use std::time::Duration; @@ -20,6 +20,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -180,7 +182,7 @@ fn test_erc721() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -189,6 +191,7 @@ fn test_erc721() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/global_exit_root.rs b/evm_arithmetization/tests/global_exit_root.rs index ba9ac1c02..a41d79e6f 100644 --- a/evm_arithmetization/tests/global_exit_root.rs +++ b/evm_arithmetization/tests/global_exit_root.rs @@ -8,10 +8,8 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::testing::prove_all_segments; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, ger_account_nibbles, - ger_contract_from_storage, init_logger, preinitialized_state_and_storage_tries, - scalable_account_nibbles, scalable_contract_from_storage, update_beacon_roots_account_storage, - update_ger_account_storage, update_scalable_account_storage, + ger_account_nibbles, ger_contract_from_storage, init_logger, scalable_account_nibbles, + scalable_contract_from_storage, update_ger_account_storage, update_scalable_account_storage, ADDRESS_SCALABLE_L2_ADDRESS_HASHED, GLOBAL_EXIT_ROOT_ACCOUNT, GLOBAL_EXIT_ROOT_ADDRESS_HASHED, }; use evm_arithmetization::verifier::testing::verify_all_proofs; @@ -19,6 +17,8 @@ use evm_arithmetization::{AllStark, Node, StarkConfig}; use keccak_hash::keccak; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -40,13 +40,13 @@ fn test_global_exit_root() -> anyhow::Result<()> { ..BlockMetadata::default() }; - let (mut state_trie_before, mut storage_tries) = preinitialized_state_and_storage_tries()?; + let mut state_trie_before = HashedPartialTrie::from(Node::Empty); + let mut storage_tries = vec![]; state_trie_before.insert( ger_account_nibbles(), rlp::encode(&GLOBAL_EXIT_ROOT_ACCOUNT).to_vec(), )?; - let mut beacon_roots_account_storage = storage_tries[0].1.clone(); let mut ger_account_storage = HashedPartialTrie::from(Node::Empty); let mut scalable_account_storage = HashedPartialTrie::from(Node::Empty); @@ -66,11 +66,6 @@ fn test_global_exit_root() -> anyhow::Result<()> { let state_trie_after = { let mut trie = HashedPartialTrie::from(Node::Empty); - update_beacon_roots_account_storage( - &mut beacon_roots_account_storage, - block_metadata.block_timestamp, - block_metadata.parent_beacon_block_root, - )?; update_ger_account_storage(&mut ger_account_storage, ger_data)?; update_scalable_account_storage( &mut scalable_account_storage, @@ -78,15 +73,9 @@ fn test_global_exit_root() -> anyhow::Result<()> { state_trie_before.hash(), )?; - let beacon_roots_account = - beacon_roots_contract_from_storage(&beacon_roots_account_storage); let ger_account = ger_contract_from_storage(&ger_account_storage); let scalable_account = scalable_contract_from_storage(&scalable_account_storage); - trie.insert( - beacon_roots_account_nibbles(), - rlp::encode(&beacon_roots_account).to_vec(), - )?; trie.insert(ger_account_nibbles(), rlp::encode(&ger_account).to_vec())?; trie.insert( scalable_account_nibbles(), @@ -102,7 +91,7 @@ fn test_global_exit_root() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![], burn_addr: None, withdrawals: vec![], @@ -116,6 +105,7 @@ fn test_global_exit_root() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/log_opcode.rs b/evm_arithmetization/tests/log_opcode.rs index b08ace71c..44453ae22 100644 --- a/evm_arithmetization/tests/log_opcode.rs +++ b/evm_arithmetization/tests/log_opcode.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::collections::HashMap; use std::str::FromStr; @@ -24,6 +24,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; @@ -246,7 +248,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { false => None, }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr, withdrawals: vec![], @@ -255,6 +257,7 @@ fn test_log_opcodes() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/selfdestruct.rs b/evm_arithmetization/tests/selfdestruct.rs index 0669486bc..7d8ecba5b 100644 --- a/evm_arithmetization/tests/selfdestruct.rs +++ b/evm_arithmetization/tests/selfdestruct.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::str::FromStr; use std::time::Duration; @@ -9,7 +9,7 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::testing::prove_all_segments; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, eth_to_wei, init_logger, + beacon_roots_account_nibbles, beacon_roots_contract_from_storage, init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, }; use evm_arithmetization::verifier::testing::verify_all_proofs; @@ -19,8 +19,11 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use zk_evm_common::eth_to_wei; type F = GoldilocksField; const D: usize = 2; @@ -150,7 +153,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -159,6 +162,7 @@ fn test_selfdestruct() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/simple_transfer.rs b/evm_arithmetization/tests/simple_transfer.rs index 37d14bf05..7dbf91399 100644 --- a/evm_arithmetization/tests/simple_transfer.rs +++ b/evm_arithmetization/tests/simple_transfer.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::collections::HashMap; use std::str::FromStr; @@ -10,7 +10,7 @@ use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; use evm_arithmetization::proof::{BlockHashes, BlockMetadata, TrieRoots}; use evm_arithmetization::prover::testing::prove_all_segments; use evm_arithmetization::testing_utils::{ - beacon_roots_account_nibbles, beacon_roots_contract_from_storage, eth_to_wei, init_logger, + beacon_roots_account_nibbles, beacon_roots_contract_from_storage, init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, }; use evm_arithmetization::verifier::testing::verify_all_proofs; @@ -20,8 +20,11 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::KeccakGoldilocksConfig; use plonky2::util::timing::TimingTree; +use zk_evm_common::eth_to_wei; type F = GoldilocksField; const D: usize = 2; @@ -142,7 +145,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![txn.to_vec()], burn_addr: None, withdrawals: vec![], @@ -151,6 +154,7 @@ fn test_simple_transfer() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/evm_arithmetization/tests/two_to_one_block.rs b/evm_arithmetization/tests/two_to_one_block.rs index 6c107c54d..a70c43c8c 100644 --- a/evm_arithmetization/tests/two_to_one_block.rs +++ b/evm_arithmetization/tests/two_to_one_block.rs @@ -1,11 +1,13 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use ethereum_types::{Address, BigEndianHash, H256}; use evm_arithmetization::fixed_recursive_verifier::{ extract_block_final_public_values, extract_two_to_one_block_hash, }; use evm_arithmetization::generation::{GenerationInputs, TrieInputs}; -use evm_arithmetization::proof::{BlockMetadata, FinalPublicValues, PublicValues, TrieRoots}; +use evm_arithmetization::proof::{ + BlockMetadata, FinalPublicValues, PublicValues, TrieRoots, EMPTY_CONSOLIDATED_BLOCKHASH, +}; use evm_arithmetization::testing_utils::{ beacon_roots_account_nibbles, beacon_roots_contract_from_storage, init_logger, preinitialized_state_and_storage_tries, update_beacon_roots_account_storage, @@ -14,6 +16,7 @@ use evm_arithmetization::{AllRecursiveCircuits, AllStark, Node, StarkConfig}; use hex_literal::hex; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; use plonky2::hash::poseidon::PoseidonHash; use plonky2::plonk::config::{Hasher, PoseidonGoldilocksConfig}; use plonky2::plonk::proof::ProofWithPublicInputs; @@ -25,7 +28,7 @@ type C = PoseidonGoldilocksConfig; /// Get `GenerationInputs` for a dummy payload, where the block has the given /// timestamp. -fn dummy_payload(timestamp: u64, is_first_payload: bool) -> anyhow::Result { +fn dummy_payload(timestamp: u64, is_first_payload: bool) -> anyhow::Result> { let beneficiary = hex!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); let block_metadata = BlockMetadata { @@ -89,6 +92,7 @@ fn dummy_payload(timestamp: u64, is_first_payload: bool) -> anyhow::Result anyhow::Result<()> { let all_stark = AllStark::::default(); let config = StarkConfig::standard_fast_config(); - let circuit_ranges = if cfg!(feature = "cdk_erigon") { - vec![ - 16..17_usize, - 8..9, - 14..15, - 9..10, - 8..9, - 7..8, - 17..18, - 17..18, - 7..8, - 4..5, - ] - } else { - vec![ - 16..17_usize, + let all_circuits = AllRecursiveCircuits::::new( + &all_stark, + &[ + 16..17, 8..9, - 14..15, + 12..13, 9..10, 8..9, - 7..8, + 6..7, 17..18, 17..18, 7..8, - ] - }; - - let all_circuits = AllRecursiveCircuits::::new( - &all_stark, - &circuit_ranges.try_into().unwrap(), + ], &config, ); diff --git a/evm_arithmetization/tests/withdrawals.rs b/evm_arithmetization/tests/withdrawals.rs index 9839c6646..fcdd73f49 100644 --- a/evm_arithmetization/tests/withdrawals.rs +++ b/evm_arithmetization/tests/withdrawals.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "cdk_erigon"))] +#![cfg(feature = "eth_mainnet")] use std::collections::HashMap; use std::time::Duration; @@ -18,6 +18,8 @@ use keccak_hash::keccak; use mpt_trie::nibbles::Nibbles; use mpt_trie::partial_trie::{HashedPartialTrie, PartialTrie}; use plonky2::field::goldilocks_field::GoldilocksField; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::util::timing::TimingTree; use rand::random; @@ -81,7 +83,7 @@ fn test_withdrawals() -> anyhow::Result<()> { receipts_root: receipts_trie.hash(), }; - let inputs = GenerationInputs { + let inputs = GenerationInputs:: { signed_txns: vec![], burn_addr: None, withdrawals, @@ -95,6 +97,7 @@ fn test_withdrawals() -> anyhow::Result<()> { trie_roots_after, contract_code, checkpoint_state_trie_root: HashedPartialTrie::from(Node::Empty).hash(), + checkpoint_consolidated_hash: [F::ZERO; NUM_HASH_OUT_ELTS], block_metadata, txn_number_before: 0.into(), gas_used_before: 0.into(), diff --git a/proof_gen/Cargo.toml b/proof_gen/Cargo.toml index 304862ece..1e2f595b9 100644 --- a/proof_gen/Cargo.toml +++ b/proof_gen/Cargo.toml @@ -18,10 +18,19 @@ hashbrown = { workspace = true } # Local dependencies evm_arithmetization = { workspace = true } +zk_evm_common = { workspace = true } [features] -default = [] -cdk_erigon = ["evm_arithmetization/cdk_erigon"] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet" +] +cdk_erigon = [ + "evm_arithmetization/cdk_erigon" +] +polygon_pos = [ + "evm_arithmetization/polygon_pos" +] [lints] workspace = true diff --git a/proof_gen/src/lib.rs b/proof_gen/src/lib.rs index c96491af4..f1e48a109 100644 --- a/proof_gen/src/lib.rs +++ b/proof_gen/src/lib.rs @@ -134,6 +134,8 @@ //! assert!(verifier_state.verify(block_proof.intern).is_ok()); //! ``` +zk_evm_common::check_chain_features!(); + pub(crate) mod constants; pub mod proof_gen; pub mod proof_types; diff --git a/proof_gen/src/proof_gen.rs b/proof_gen/src/proof_gen.rs index 2e2e39ae2..e69d8de3c 100644 --- a/proof_gen/src/proof_gen.rs +++ b/proof_gen/src/proof_gen.rs @@ -50,7 +50,7 @@ impl From for ProofGenError { /// Generates a transaction proof from some IR data. pub fn generate_segment_proof( p_state: &ProverState, - gen_inputs: TrimmedGenerationInputs, + gen_inputs: TrimmedGenerationInputs, segment_data: &mut GenerationSegmentData, abort_signal: Option>, ) -> ProofGenResult { diff --git a/proof_gen/src/proof_types.rs b/proof_gen/src/proof_types.rs index 20be552d9..a0a1db43c 100644 --- a/proof_gen/src/proof_types.rs +++ b/proof_gen/src/proof_types.rs @@ -9,14 +9,14 @@ use evm_arithmetization::{ use plonky2::plonk::config::Hasher as _; use serde::{Deserialize, Serialize}; -use crate::types::{Hash, Hasher, PlonkyProofIntern}; +use crate::types::{Field, Hash, Hasher, PlonkyProofIntern}; /// A transaction proof along with its public values, for proper connection with /// contiguous proofs. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GeneratedSegmentProof { /// Public values of this transaction proof. - pub p_vals: PublicValues, + pub p_vals: PublicValues, /// Underlying plonky2 proof. pub intern: PlonkyProofIntern, } @@ -29,7 +29,7 @@ pub struct GeneratedSegmentProof { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GeneratedSegmentAggProof { /// Public values of this aggregation proof. - pub p_vals: PublicValues, + pub p_vals: PublicValues, /// Underlying plonky2 proof. pub intern: PlonkyProofIntern, } @@ -42,7 +42,7 @@ pub struct GeneratedSegmentAggProof { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GeneratedTxnAggProof { /// Public values of this transaction aggregation proof. - pub p_vals: PublicValues, + pub p_vals: PublicValues, /// Underlying plonky2 proof. pub intern: PlonkyProofIntern, } @@ -94,7 +94,7 @@ pub enum BatchAggregatableProof { } impl SegmentAggregatableProof { - pub(crate) fn public_values(&self) -> PublicValues { + pub(crate) fn public_values(&self) -> PublicValues { match self { SegmentAggregatableProof::Seg(info) => info.p_vals.clone(), SegmentAggregatableProof::Agg(info) => info.p_vals.clone(), @@ -117,7 +117,7 @@ impl SegmentAggregatableProof { } impl BatchAggregatableProof { - pub(crate) fn public_values(&self) -> PublicValues { + pub(crate) fn public_values(&self) -> PublicValues { match self { BatchAggregatableProof::Segment(info) => info.p_vals.clone(), BatchAggregatableProof::Txn(info) => info.p_vals.clone(), diff --git a/proof_gen/src/types.rs b/proof_gen/src/types.rs index bf1b4185c..5e6c47c2d 100644 --- a/proof_gen/src/types.rs +++ b/proof_gen/src/types.rs @@ -8,6 +8,7 @@ use plonky2::{ }; /// The base field on which statements are being proven. +// TODO(Robin): https://github.com/0xPolygonZero/zk_evm/issues/531 pub type Field = GoldilocksField; /// The recursive circuit configuration to be used to shrink and aggregate /// proofs. diff --git a/trace_decoder/Cargo.toml b/trace_decoder/Cargo.toml index 78e0aa3ef..262185c7a 100644 --- a/trace_decoder/Cargo.toml +++ b/trace_decoder/Cargo.toml @@ -10,7 +10,9 @@ homepage.workspace = true keywords.workspace = true [dependencies] -anyhow.workspace = true +alloy = { workspace = true } +alloy-compat = "0.1.0" +anyhow = { workspace = true } bitflags = { workspace = true } bitvec = { workspace = true } bytes = { workspace = true } @@ -20,23 +22,25 @@ copyvec = "0.2.0" either = { workspace = true } enum-as-inner = { workspace = true } ethereum-types = { workspace = true } -evm_arithmetization = { workspace = true } hex = { workspace = true } hex-literal = { workspace = true } -itertools.workspace = true +itertools = { workspace = true } keccak-hash = { workspace = true } log = { workspace = true } -mpt_trie = { workspace = true } nunny = { workspace = true, features = ["serde"] } plonky2 = { workspace = true } rlp = { workspace = true } serde = { workspace = true } -smt_trie = { workspace = true } stackstack = "0.3.0" strum = { version = "0.26.3", features = ["derive"] } thiserror = { workspace = true } u4 = { workspace = true } winnow = { workspace = true } + +# Local dependencies +evm_arithmetization = { workspace = true } +mpt_trie = { workspace = true } +smt_trie = { workspace = true } zk_evm_common = { workspace = true } [dev-dependencies] @@ -44,23 +48,44 @@ alloy = { workspace = true } alloy-compat = "0.1.0" assert2 = "0.3.15" camino = "1.1.9" +clap = { workspace = true } criterion = { workspace = true } glob = "0.3.1" libtest-mimic = "0.7.3" plonky2_maybe_rayon = { workspace = true } +pretty_assertions = "1.4.0" pretty_env_logger = { workspace = true } prover = { workspace = true } serde_json = { workspace = true } serde_path_to_error = { workspace = true } + +[features] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "prover/eth_mainnet", +] +cdk_erigon = [ + "evm_arithmetization/cdk_erigon", + "prover/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "prover/polygon_pos", +] + [[bench]] name = "block_processing" harness = false +required-features = ["eth_mainnet"] [[test]] name = "consistent-with-header" harness = false +required-features = ["eth_mainnet"] [[test]] name = "simulate-execution" harness = false +required-features = ["eth_mainnet"] diff --git a/trace_decoder/benches/block_processing.rs b/trace_decoder/benches/block_processing.rs index c6e9716e3..6f3319d94 100644 --- a/trace_decoder/benches/block_processing.rs +++ b/trace_decoder/benches/block_processing.rs @@ -33,7 +33,7 @@ fn criterion_benchmark(c: &mut Criterion) { block_trace, other_data, }| { - trace_decoder::entrypoint(block_trace, other_data, batch_size, false).unwrap() + trace_decoder::entrypoint(block_trace, other_data, batch_size).unwrap() }, BatchSize::LargeInput, ) diff --git a/trace_decoder/src/core.rs b/trace_decoder/src/core.rs new file mode 100644 index 000000000..64c0dc2ee --- /dev/null +++ b/trace_decoder/src/core.rs @@ -0,0 +1,641 @@ +use std::{ + cmp, + collections::{BTreeMap, BTreeSet, HashMap}, + mem, +}; + +use alloy::primitives::address; +use alloy_compat::Compat as _; +use anyhow::{anyhow, bail, ensure, Context as _}; +use ethereum_types::{Address, U256}; +use evm_arithmetization::{ + generation::{mpt::AccountRlp, TrieInputs}, + proof::TrieRoots, + testing_utils::{BEACON_ROOTS_CONTRACT_ADDRESS, HISTORY_BUFFER_LENGTH}, + GenerationInputs, +}; +use itertools::Itertools as _; +use keccak_hash::H256; +use mpt_trie::partial_trie::PartialTrie as _; +use nunny::NonEmpty; +use zk_evm_common::gwei_to_wei; + +use crate::{ + typed_mpt::{ReceiptTrie, StateMpt, StateTrie, StorageTrie, TransactionTrie, TrieKey}, + BlockLevelData, BlockTrace, BlockTraceTriePreImages, CombinedPreImages, ContractCodeUsage, + Field, OtherBlockData, SeparateStorageTriesPreImage, SeparateTriePreImage, + SeparateTriePreImages, TxnInfo, TxnMeta, TxnTrace, +}; + +/// TODO(0xaatif): document this after https://github.com/0xPolygonZero/zk_evm/issues/275 +pub fn entrypoint( + trace: BlockTrace, + other: OtherBlockData, + batch_size_hint: usize, +) -> anyhow::Result>> { + ensure!(batch_size_hint != 0); + + let BlockTrace { + trie_pre_images, + code_db, + txn_info, + } = trace; + let (state, storage, mut code) = start(trie_pre_images)?; + code.extend(code_db); + + let OtherBlockData { + b_data: + BlockLevelData { + b_meta, + b_hashes, + mut withdrawals, + }, + checkpoint_state_trie_root, + checkpoint_consolidated_hash, + burn_addr, + } = other; + + for (_, amt) in &mut withdrawals { + *amt = gwei_to_wei(*amt) + } + + let batches = middle( + state, + storage, + batch(txn_info, batch_size_hint), + &mut code, + b_meta.block_timestamp, + b_meta.parent_beacon_block_root, + withdrawals, + )?; + + let mut running_gas_used = 0; + Ok(batches + .into_iter() + .map( + |Batch { + first_txn_ix, + gas_used, + contract_code, + byte_code, + before: + IntraBlockTries { + state, + storage, + transaction, + receipt, + }, + after, + withdrawals, + }| GenerationInputs:: { + txn_number_before: first_txn_ix.into(), + gas_used_before: running_gas_used.into(), + gas_used_after: { + running_gas_used += gas_used; + running_gas_used.into() + }, + signed_txns: byte_code.into_iter().map(Into::into).collect(), + withdrawals, + ger_data: None, + tries: TrieInputs { + state_trie: state.into(), + transactions_trie: transaction.into(), + receipts_trie: receipt.into(), + storage_tries: storage.into_iter().map(|(k, v)| (k, v.into())).collect(), + }, + trie_roots_after: after, + checkpoint_state_trie_root, + checkpoint_consolidated_hash, + contract_code: contract_code + .into_iter() + .map(|it| (keccak_hash::keccak(&it), it)) + .collect(), + block_metadata: b_meta.clone(), + block_hashes: b_hashes.clone(), + burn_addr, + }, + ) + .collect()) +} + +/// The user has either provided us with a [`serde`]-ed +/// [`HashedPartialTrie`](mpt_trie::partial_trie::HashedPartialTrie), +/// or a [`wire`](crate::wire)-encoded representation of one. +/// +/// Turn either of those into our [`typed_mpt`](crate::typed_mpt) +/// representations. +fn start( + pre_images: BlockTraceTriePreImages, +) -> anyhow::Result<(StateMpt, BTreeMap, Hash2Code)> { + Ok(match pre_images { + // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/401 + // refactor our convoluted input types + BlockTraceTriePreImages::Separate(SeparateTriePreImages { + state: SeparateTriePreImage::Direct(state), + storage: SeparateStorageTriesPreImage::MultipleTries(storage), + }) => { + let state = state.items().try_fold( + StateMpt::default(), + |mut acc, (nibbles, hash_or_val)| { + let path = TrieKey::from_nibbles(nibbles); + match hash_or_val { + mpt_trie::trie_ops::ValOrHash::Val(bytes) => { + #[expect(deprecated)] // this is MPT specific + acc.insert_by_hashed_address( + path.into_hash() + .context("invalid path length in direct state trie")?, + rlp::decode(&bytes) + .context("invalid AccountRlp in direct state trie")?, + )?; + } + mpt_trie::trie_ops::ValOrHash::Hash(h) => { + acc.insert_hash_by_key(path, h)?; + } + }; + anyhow::Ok(acc) + }, + )?; + let storage = storage + .into_iter() + .map(|(k, SeparateTriePreImage::Direct(v))| { + v.items() + .try_fold(StorageTrie::default(), |mut acc, (nibbles, hash_or_val)| { + let path = TrieKey::from_nibbles(nibbles); + match hash_or_val { + mpt_trie::trie_ops::ValOrHash::Val(value) => { + acc.insert(path, value)?; + } + mpt_trie::trie_ops::ValOrHash::Hash(h) => { + acc.insert_hash(path, h)?; + } + }; + anyhow::Ok(acc) + }) + .map(|v| (k, v)) + }) + .collect::>()?; + (state, storage, Hash2Code::new()) + } + BlockTraceTriePreImages::Combined(CombinedPreImages { compact }) => { + let instructions = crate::wire::parse(&compact) + .context("couldn't parse instructions from binary format")?; + let crate::type1::Frontend { + state, + storage, + code, + } = crate::type1::frontend(instructions)?; + (state, storage, code.into_iter().map(Into::into).collect()) + } + }) +} + +/// Break `txns` into batches of length `batch_size_hint`, prioritising creating +/// at least two batches. +/// +/// [`None`] represents a dummy transaction that should not increment the +/// transaction index. +fn batch(txns: Vec, batch_size_hint: usize) -> Vec>> { + let hint = cmp::max(batch_size_hint, 1); + let mut txns = txns.into_iter().map(Some).collect::>(); + let n_batches = txns.iter().chunks(hint).into_iter().count(); + match (txns.len(), n_batches) { + // enough + (_, 2..) => txns + .into_iter() + .chunks(hint) + .into_iter() + .map(FromIterator::from_iter) + .collect(), + // not enough batches at `hint`, but enough real transactions, + // so just split them in half + (2.., ..2) => { + let second = txns.split_off(txns.len() / 2); + vec![txns, second] + } + // add padding + (0 | 1, _) => txns + .into_iter() + .pad_using(2, |_ix| None) + .map(|it| vec![it]) + .collect(), + } +} + +#[test] +fn test_batch() { + #[track_caller] + fn do_test(n: usize, hint: usize, exp: impl IntoIterator) { + itertools::assert_equal( + exp, + batch(vec![TxnInfo::default(); n], hint) + .iter() + .map(Vec::len), + ) + } + + do_test(0, 0, [1, 1]); // pad2 + do_test(1, 0, [1, 1]); // pad1 + do_test(2, 0, [1, 1]); // exact + do_test(3, 0, [1, 1, 1]); + do_test(3, 1, [1, 1, 1]); + do_test(3, 2, [2, 1]); // leftover after hint + do_test(3, 3, [1, 2]); // big hint +} + +#[derive(Debug)] +struct Batch { + pub first_txn_ix: usize, + pub gas_used: u64, + /// See [`GenerationInputs::contract_code`]. + pub contract_code: BTreeSet>, + /// For each transaction in batch, in order. + pub byte_code: Vec>>, + + pub before: IntraBlockTries, + pub after: TrieRoots, + + /// Empty for all but the final batch + pub withdrawals: Vec<(Address, U256)>, +} + +/// [`evm_arithmetization::generation::TrieInputs`], +/// generic over state trie representation. +#[derive(Debug)] +struct IntraBlockTries { + pub state: StateTrieT, + pub storage: BTreeMap, + pub transaction: TransactionTrie, + pub receipt: ReceiptTrie, +} + +/// Does the main work mentioned in the [module documentation](super). +fn middle( + // state at the beginning of the block + mut state_trie: StateTrieT, + // storage at the beginning of the block + mut storage_tries: BTreeMap, + // None represents a dummy transaction that should not increment the transaction index + // all batches SHOULD not be empty + batches: Vec>>, + code: &mut Hash2Code, + block_timestamp: U256, + parent_beacon_block_root: H256, + // added to final batch + mut withdrawals: Vec<(Address, U256)>, +) -> anyhow::Result>> { + // Initialise the storage tries. + for (haddr, acct) in state_trie.iter() { + let storage = storage_tries.entry(haddr).or_insert({ + let mut it = StorageTrie::default(); + it.insert_hash(TrieKey::default(), acct.storage_root) + .expect("empty trie insert cannot fail"); + it + }); + ensure!( + storage.root() == acct.storage_root, + "inconsistent initial storage for hashed address {haddr:x}" + ) + } + + // These are the per-block tries. + let mut transaction_trie = TransactionTrie::new(); + let mut receipt_trie = ReceiptTrie::new(); + + let mut out = vec![]; + + let mut txn_ix = 0; // incremented for non-dummy transactions + let mut loop_ix = 0; // always incremented + let loop_len = batches.iter().flatten().count(); + for batch in batches { + let batch_first_txn_ix = txn_ix; // GOTCHA: if there are no transactions in this batch + let mut batch_gas_used = 0; + let mut batch_byte_code = vec![]; + let mut batch_contract_code = BTreeSet::from([vec![]]); // always include empty code + + let mut before = IntraBlockTries { + state: state_trie.clone(), + transaction: transaction_trie.clone(), + receipt: receipt_trie.clone(), + storage: storage_tries.clone(), + }; + + // We want to perform mask the TrieInputs above, + // but won't know the bounds until after the loop below, + // so store that information here. + let mut storage_masks = BTreeMap::<_, BTreeSet>::new(); + let mut state_mask = BTreeSet::new(); + + if txn_ix == 0 { + do_beacon_hook( + block_timestamp, + &mut storage_tries, + &mut storage_masks, + parent_beacon_block_root, + &mut state_mask, + &mut state_trie, + )?; + } + + for txn in batch { + let do_increment_txn_ix = txn.is_some(); + let TxnInfo { + traces, + meta: + TxnMeta { + byte_code, + new_receipt_trie_node_byte, + gas_used: txn_gas_used, + }, + } = txn.unwrap_or_default(); + + if let Ok(nonempty) = nunny::Vec::new(byte_code) { + batch_byte_code.push(nonempty.clone()); + transaction_trie.insert(txn_ix, nonempty.into())?; + receipt_trie.insert( + txn_ix, + map_receipt_bytes(new_receipt_trie_node_byte.clone())?, + )?; + } + + batch_gas_used += txn_gas_used; + + for ( + addr, + just_access, + TxnTrace { + balance, + nonce, + storage_read, + storage_written, + code_usage, + self_destructed, + }, + ) in traces + .into_iter() + .map(|(addr, trc)| (addr, trc == TxnTrace::default(), trc)) + { + let (_, _, receipt) = evm_arithmetization::generation::mpt::decode_receipt( + &map_receipt_bytes(new_receipt_trie_node_byte.clone())?, + ) + .map_err(|e| anyhow!("{e:?}")) + .context("couldn't decode receipt")?; + + let (mut acct, born) = state_trie + .get_by_address(addr) + .map(|acct| (acct, false)) + .unwrap_or((AccountRlp::default(), true)); + + if born || just_access { + state_trie + .clone() + .insert_by_address(addr, acct) + .context(format!( + "couldn't reach state of {} address {addr:x}", + match born { + true => "created", + false => "accessed", + } + ))?; + } + + let do_writes = !just_access + && match born { + // if txn failed, don't commit changes to trie + true => receipt.status, + false => true, + }; + + let storage_mask = storage_masks.entry(addr).or_default(); + + storage_mask.extend( + storage_written + .keys() + .chain(&storage_read) + .map(|it| TrieKey::from_hash(keccak_hash::keccak(it))), + ); + + if do_writes { + acct.balance = balance.unwrap_or(acct.balance); + acct.nonce = nonce.unwrap_or(acct.nonce); + acct.code_hash = code_usage + .map(|it| match it { + ContractCodeUsage::Read(hash) => { + batch_contract_code.insert(code.get(hash)?); + anyhow::Ok(hash) + } + ContractCodeUsage::Write(bytes) => { + code.insert(bytes.clone()); + let hash = keccak_hash::keccak(&bytes); + batch_contract_code.insert(bytes); + Ok(hash) + } + }) + .transpose()? + .unwrap_or(acct.code_hash); + + if !storage_written.is_empty() { + let storage = match born { + true => storage_tries.entry(keccak_hash::keccak(addr)).or_default(), + false => storage_tries + .get_mut(&keccak_hash::keccak(addr)) + .context(format!("missing storage trie for address {addr:x}"))?, + }; + + for (k, v) in storage_written { + let slot = TrieKey::from_hash(keccak_hash::keccak(k)); + match v.is_zero() { + // this is actually a delete + true => storage_mask.extend(storage.reporting_remove(slot)?), + false => { + storage.insert(slot, rlp::encode(&v).to_vec())?; + } + } + } + acct.storage_root = storage.root(); + } + + state_trie.insert_by_address(addr, acct)?; + } + + if self_destructed { + storage_tries.remove(&keccak_hash::keccak(addr)); + state_mask.extend(state_trie.reporting_remove(addr)?) + } + + let precompiled_addresses = address!("0000000000000000000000000000000000000001") + ..address!("000000000000000000000000000000000000000a"); + + if !precompiled_addresses.contains(&addr.compat()) { + // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/pull/613 + // masking like this SHOULD be a space-saving optimization, + // BUT if it's omitted, we actually get state root mismatches + state_mask.insert(TrieKey::from_address(addr)); + } // else we don't even need to include them, + // because nodes will only emit a precompiled address if + // the transaction calling them reverted. + } + + if do_increment_txn_ix { + txn_ix += 1; + } + loop_ix += 1; + } // txn in batch + + out.push(Batch { + first_txn_ix: batch_first_txn_ix, + gas_used: batch_gas_used, + contract_code: batch_contract_code, + byte_code: batch_byte_code, + withdrawals: match loop_ix == loop_len { + true => { + for (addr, amt) in &withdrawals { + state_mask.insert(TrieKey::from_address(*addr)); + let mut acct = state_trie + .get_by_address(*addr) + .context("missing address for withdrawal")?; + acct.balance += *amt; + state_trie + .insert_by_address(*addr, acct) + // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 + // Add an entry API + .expect("insert must succeed with the same key as a successful `get`"); + } + mem::take(&mut withdrawals) + } + false => vec![], + }, + before: { + before.state.mask(state_mask)?; + before.receipt.mask(batch_first_txn_ix..txn_ix)?; + before.transaction.mask(batch_first_txn_ix..txn_ix)?; + + let keep = storage_masks + .keys() + .map(keccak_hash::keccak) + .collect::>(); + before.storage.retain(|haddr, _| keep.contains(haddr)); + + for (addr, mask) in storage_masks { + if let Some(it) = before.storage.get_mut(&keccak_hash::keccak(addr)) { + it.mask(mask)? + } // else must have self-destructed + } + before + }, + after: TrieRoots { + state_root: state_trie.root(), + transactions_root: transaction_trie.root(), + receipts_root: receipt_trie.root(), + }, + }); + } // batch in batches + + Ok(out) +} + +/// Updates the storage of the beacon block root contract, +/// according to +/// +/// This is cancun-specific, and runs at the start of the block, +/// before any transactions (as per the EIP). +fn do_beacon_hook( + block_timestamp: U256, + storage: &mut BTreeMap, + trim_storage: &mut BTreeMap>, + parent_beacon_block_root: H256, + trim_state: &mut BTreeSet, + state_trie: &mut StateTrieT, +) -> anyhow::Result<()> { + let history_timestamp = block_timestamp % HISTORY_BUFFER_LENGTH.value; + let history_timestamp_next = history_timestamp + HISTORY_BUFFER_LENGTH.value; + let beacon_storage = storage + .get_mut(&keccak_hash::keccak(BEACON_ROOTS_CONTRACT_ADDRESS)) + .context("missing beacon contract storage trie")?; + let beacon_trim = trim_storage + .entry(BEACON_ROOTS_CONTRACT_ADDRESS) + .or_default(); + for (ix, u) in [ + (history_timestamp, block_timestamp), + ( + history_timestamp_next, + U256::from_big_endian(parent_beacon_block_root.as_bytes()), + ), + ] { + let mut h = [0; 32]; + ix.to_big_endian(&mut h); + let slot = TrieKey::from_hash(keccak_hash::keccak(H256::from_slice(&h))); + beacon_trim.insert(slot); + + match u.is_zero() { + true => beacon_trim.extend(beacon_storage.reporting_remove(slot)?), + false => { + beacon_storage.insert(slot, alloy::rlp::encode(u.compat()))?; + beacon_trim.insert(slot); + } + } + } + trim_state.insert(TrieKey::from_address(BEACON_ROOTS_CONTRACT_ADDRESS)); + let mut beacon_acct = state_trie + .get_by_address(BEACON_ROOTS_CONTRACT_ADDRESS) + .context("missing beacon contract address")?; + beacon_acct.storage_root = beacon_storage.root(); + state_trie + .insert_by_address(BEACON_ROOTS_CONTRACT_ADDRESS, beacon_acct) + // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 + // Add an entry API + .expect("insert must succeed with the same key as a successful `get`"); + Ok(()) +} + +fn map_receipt_bytes(bytes: Vec) -> anyhow::Result> { + match rlp::decode::(&bytes) { + Ok(_) => Ok(bytes), + Err(_) => { + rlp::decode(&bytes).context("couldn't decode receipt as a legacy receipt or raw bytes") + } + } +} + +/// Code hash mappings that we have constructed from parsing the block +/// trace. +/// If there are any txns that create contracts, then they will also +/// get added here as we process the deltas. +struct Hash2Code { + /// Key must always be [`hash`] of value. + inner: HashMap>, +} + +impl Hash2Code { + pub fn new() -> Self { + let mut this = Self { + inner: HashMap::new(), + }; + this.insert(vec![]); + this + } + pub fn get(&mut self, hash: H256) -> anyhow::Result> { + match self.inner.get(&hash) { + Some(code) => Ok(code.clone()), + None => bail!("no code for hash {}", hash), + } + } + pub fn insert(&mut self, code: Vec) { + self.inner.insert(keccak_hash::keccak(&code), code); + } +} + +impl Extend> for Hash2Code { + fn extend>>(&mut self, iter: II) { + for it in iter { + self.insert(it) + } + } +} + +impl FromIterator> for Hash2Code { + fn from_iter>>(iter: II) -> Self { + let mut this = Self::new(); + this.extend(iter); + this + } +} diff --git a/trace_decoder/src/decoding.rs b/trace_decoder/src/decoding.rs deleted file mode 100644 index b0f909374..000000000 --- a/trace_decoder/src/decoding.rs +++ /dev/null @@ -1,710 +0,0 @@ -use std::{cmp::min, collections::HashMap, ops::Range}; - -use anyhow::{anyhow, Context as _}; -use ethereum_types::H160; -use ethereum_types::{Address, BigEndianHash, H256, U256, U512}; -use evm_arithmetization::{ - generation::{ - mpt::{decode_receipt, AccountRlp}, - GenerationInputs, TrieInputs, - }, - proof::{BlockMetadata, ExtraBlockData, TrieRoots}, - testing_utils::{ - BEACON_ROOTS_CONTRACT_ADDRESS, BEACON_ROOTS_CONTRACT_ADDRESS_HASHED, HISTORY_BUFFER_LENGTH, - }, -}; -use mpt_trie::{ - nibbles::Nibbles, - partial_trie::{HashedPartialTrie, PartialTrie as _}, - special_query::path_for_query, - trie_ops::TrieOpError, - utils::{IntoTrieKey as _, TriePath}, -}; - -use crate::{ - hash, - processed_block_trace::{ - NodesUsedByTxnBatch, ProcessedBlockTrace, ProcessedTxnBatchInfo, StateWrite, TxnMetaState, - }, - typed_mpt::{ReceiptTrie, StateTrie, StorageTrie, TransactionTrie, TrieKey}, - OtherBlockData, PartialTriePreImages, TryIntoExt as TryIntoBounds, -}; - -/// The current state of all tries as we process txn deltas. These are mutated -/// after every txn we process in the trace. -#[derive(Clone, Debug, Default)] -struct PartialTrieState { - state: StateTrieT, - storage: HashMap, - txn: TransactionTrie, - receipt: ReceiptTrie, -} - -/// Additional information discovered during delta application. -#[derive(Debug, Default)] -struct TrieDeltaApplicationOutput { - // During delta application, if a delete occurs, we may have to make sure additional nodes - // that are not accessed by the txn remain unhashed. - additional_state_trie_paths_to_not_hash: Vec, - additional_storage_trie_paths_to_not_hash: HashMap>, -} - -pub fn into_txn_proof_gen_ir( - ProcessedBlockTrace { - tries: PartialTriePreImages { state, storage }, - txn_info, - withdrawals, - }: ProcessedBlockTrace, - other_data: OtherBlockData, - use_burn_addr: bool, - batch_size: usize, -) -> anyhow::Result> { - let mut curr_block_tries = PartialTrieState { - state: state.clone(), - storage: storage.iter().map(|(k, v)| (*k, v.clone())).collect(), - ..Default::default() - }; - - let mut extra_data = ExtraBlockData { - checkpoint_state_trie_root: other_data.checkpoint_state_trie_root, - txn_number_before: U256::zero(), - txn_number_after: U256::zero(), - gas_used_before: U256::zero(), - gas_used_after: U256::zero(), - }; - - let num_txs = txn_info - .iter() - .map(|tx_info| tx_info.meta.len()) - .sum::(); - - let mut txn_gen_inputs = txn_info - .into_iter() - .enumerate() - .map(|(txn_idx, txn_info)| { - let txn_range = - min(txn_idx * batch_size, num_txs)..min(txn_idx * batch_size + batch_size, num_txs); - let is_initial_payload = txn_range.start == 0; - - process_txn_info( - txn_range.clone(), - is_initial_payload, - txn_info, - &mut curr_block_tries, - &mut extra_data, - &other_data, - use_burn_addr, - ) - .context(format!( - "at transaction range {}..{}", - txn_range.start, txn_range.end - )) - }) - .collect::>>() - .context(format!( - "at block num {} with chain id {}", - other_data.b_data.b_meta.block_number, other_data.b_data.b_meta.block_chain_id - ))?; - - if !withdrawals.is_empty() { - add_withdrawals_to_txns(&mut txn_gen_inputs, &mut curr_block_tries, withdrawals)?; - } - - Ok(txn_gen_inputs) -} - -/// Cancun HF specific: At the start of a block, prior txn execution, we -/// need to update the storage of the beacon block root contract. -// See . -fn update_beacon_block_root_contract_storage( - trie_state: &mut PartialTrieState, - delta_out: &mut TrieDeltaApplicationOutput, - nodes_used: &mut NodesUsedByTxnBatch, - block_data: &BlockMetadata, -) -> anyhow::Result<()> { - const HISTORY_BUFFER_LENGTH_MOD: U256 = U256([HISTORY_BUFFER_LENGTH.1, 0, 0, 0]); - - let timestamp_idx = block_data.block_timestamp % HISTORY_BUFFER_LENGTH_MOD; - let timestamp = rlp::encode(&block_data.block_timestamp).to_vec(); - - let root_idx = timestamp_idx + HISTORY_BUFFER_LENGTH_MOD; - let calldata = rlp::encode(&U256::from_big_endian( - &block_data.parent_beacon_block_root.0, - )) - .to_vec(); - - let storage_trie = trie_state - .storage - .get_mut(&BEACON_ROOTS_CONTRACT_ADDRESS_HASHED) - .context(format!( - "missing account storage trie for address {:x}", - BEACON_ROOTS_CONTRACT_ADDRESS - ))?; - - let slots_nibbles = nodes_used - .storage_accesses - .entry(BEACON_ROOTS_CONTRACT_ADDRESS_HASHED) - .or_default(); - - for (ix, val) in [(timestamp_idx, timestamp), (root_idx, calldata)] { - // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 - // document this - let slot = TrieKey::from_nibbles(Nibbles::from_h256_be(hash( - Nibbles::from_h256_be(H256::from_uint(&ix)).bytes_be(), - ))); - - slots_nibbles.push(slot); - - // If we are writing a zero, then we actually need to perform a delete. - match val == ZERO_STORAGE_SLOT_VAL_RLPED { - false => { - storage_trie.insert(slot, val.clone()).context(format!( - "at slot {:?} with value {}", - slot, - U512::from_big_endian(val.as_slice()) - ))?; - - delta_out - .additional_storage_trie_paths_to_not_hash - .entry(BEACON_ROOTS_CONTRACT_ADDRESS_HASHED) - .or_default() - .push(slot); - } - true => { - if let Ok(Some(remaining_slot_key)) = - delete_node_and_report_remaining_key_if_branch_collapsed( - storage_trie.as_mut_hashed_partial_trie_unchecked(), - &slot, - ) - { - delta_out - .additional_storage_trie_paths_to_not_hash - .entry(BEACON_ROOTS_CONTRACT_ADDRESS_HASHED) - .or_default() - .push(remaining_slot_key); - } - } - } - } - - delta_out - .additional_state_trie_paths_to_not_hash - .push(TrieKey::from_hash(BEACON_ROOTS_CONTRACT_ADDRESS_HASHED)); - let mut account = trie_state - .state - .get_by_address(BEACON_ROOTS_CONTRACT_ADDRESS) - .context(format!( - "missing account storage trie for address {:x}", - BEACON_ROOTS_CONTRACT_ADDRESS - ))?; - - account.storage_root = storage_trie.root(); - - trie_state - .state - .insert_by_address(BEACON_ROOTS_CONTRACT_ADDRESS, account) - // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 - // Add an entry API - .expect("insert must succeed with the same key as a successful `get`"); - - Ok(()) -} - -fn update_txn_and_receipt_tries( - trie_state: &mut PartialTrieState, - meta: &TxnMetaState, - txn_idx: usize, -) -> anyhow::Result<()> { - if let Some(bytes) = &meta.txn_bytes { - trie_state.txn.insert(txn_idx, bytes.clone())?; - trie_state - .receipt - .insert(txn_idx, meta.receipt_node_bytes.clone())?; - } // else it's just a dummy - Ok(()) -} - -/// If the account does not have a storage trie or does but is not -/// accessed by any txns, then we still need to manually create an entry for -/// them. -fn init_any_needed_empty_storage_tries<'a>( - storage_tries: &mut HashMap, - accounts_with_storage: impl Iterator, - accts_with_unaccessed_storage: &HashMap, -) { - for h_addr in accounts_with_storage { - if !storage_tries.contains_key(h_addr) { - let trie = accts_with_unaccessed_storage - .get(h_addr) - .map(|s_root| { - let mut it = StorageTrie::default(); - it.insert_hash(TrieKey::default(), *s_root) - .expect("empty trie insert cannot fail"); - it - }) - .unwrap_or_default(); - - storage_tries.insert(*h_addr, trie); - }; - } -} - -fn create_minimal_partial_tries_needed_by_txn( - curr_block_tries: &PartialTrieState>, - nodes_used_by_txn: &NodesUsedByTxnBatch, - txn_range: Range, - delta_application_out: TrieDeltaApplicationOutput, -) -> anyhow::Result { - let mut state_trie = curr_block_tries.state.clone(); - state_trie.trim_to( - nodes_used_by_txn - .state_accesses - .iter() - .map(|it| TrieKey::from_address(*it)) - .chain(delta_application_out.additional_state_trie_paths_to_not_hash), - )?; - - let txn_keys = txn_range.map(TrieKey::from_txn_ix); - - let transactions_trie = create_trie_subset_wrapped( - curr_block_tries.txn.as_hashed_partial_trie(), - txn_keys.clone(), - TrieType::Txn, - )?; - - let receipts_trie = create_trie_subset_wrapped( - curr_block_tries.receipt.as_hashed_partial_trie(), - txn_keys, - TrieType::Receipt, - )?; - - let storage_tries = create_minimal_storage_partial_tries( - &curr_block_tries.storage, - &nodes_used_by_txn.storage_accesses, - &delta_application_out.additional_storage_trie_paths_to_not_hash, - )?; - - Ok(TrieInputs { - state_trie: state_trie.try_into()?, - transactions_trie, - receipts_trie, - storage_tries, - }) -} - -fn apply_deltas_to_trie_state( - trie_state: &mut PartialTrieState, - deltas: &NodesUsedByTxnBatch, - meta: &[TxnMetaState], -) -> anyhow::Result { - let mut out = TrieDeltaApplicationOutput::default(); - - for (hashed_acc_addr, storage_writes) in deltas.storage_writes.iter() { - let storage_trie = trie_state - .storage - .get_mut(hashed_acc_addr) - .context(format!( - "missing account storage trie {:x}", - hashed_acc_addr - ))?; - - for (key, val) in storage_writes { - let slot = TrieKey::from_hash(hash(key.into_nibbles().bytes_be())); - // If we are writing a zero, then we actually need to perform a delete. - match val == &ZERO_STORAGE_SLOT_VAL_RLPED { - false => { - storage_trie.insert(slot, val.clone()).context(format!( - "at slot {:?} with value {}", - slot, - U512::from_big_endian(val.as_slice()) - ))?; - } - true => { - if let Some(remaining_slot_key) = - delete_node_and_report_remaining_key_if_branch_collapsed( - storage_trie.as_mut_hashed_partial_trie_unchecked(), - &slot, - )? - { - out.additional_storage_trie_paths_to_not_hash - .entry(*hashed_acc_addr) - .or_default() - .push(remaining_slot_key); - } - } - }; - } - } - - for (addr, state_write) in &deltas.state_writes { - // If the account was created, then it will not exist in the trie yet. - let is_created = !trie_state.state.contains_address(*addr); - let mut account = trie_state.state.get_by_address(*addr).unwrap_or_default(); - - state_write.apply_writes_to_state_node(&mut account, &hash(addr), &trie_state.storage)?; - - trie_state.state.insert_by_address(*addr, account)?; - - if is_created { - // If the account did not exist prior this transaction, we - // need to make sure the transaction didn't revert. - - // We will check the status of the last receipt that attempted to create the - // account in this batch. - let last_creation_receipt = &meta - .iter() - .rev() - .find(|tx| tx.created_accounts.contains(addr)) - .expect("We should have found a matching transaction") - .receipt_node_bytes; - - let (_, _, receipt) = decode_receipt(last_creation_receipt) - .map_err(|_| anyhow!("couldn't RLP-decode receipt node bytes"))?; - - if !receipt.status { - // The transaction failed, hence any created account should be removed. - if let Some(remaining_account_key) = trie_state.state.reporting_remove(*addr)? { - out.additional_state_trie_paths_to_not_hash - .push(remaining_account_key); - trie_state.storage.remove(&hash(addr)); - continue; - } - } - } - } - - // Remove any accounts that self-destructed. - for addr in deltas.self_destructed_accounts.iter() { - trie_state.storage.remove(&hash(addr)); - - if let Some(remaining_account_key) = trie_state.state.reporting_remove(*addr)? { - out.additional_state_trie_paths_to_not_hash - .push(remaining_account_key); - } - } - - Ok(out) -} - -fn get_trie_trace(trie: &HashedPartialTrie, k: &Nibbles) -> TriePath { - path_for_query(trie, *k, true).collect() -} - -/// If a branch collapse occurred after a delete, then we must ensure that -/// the other single child that remains also is not hashed when passed into -/// plonky2. Returns the key to the remaining child if a collapse occurred. -pub fn delete_node_and_report_remaining_key_if_branch_collapsed( - trie: &mut HashedPartialTrie, - key: &TrieKey, -) -> Result, TrieOpError> { - let key = key.into_nibbles(); - let old_trace = get_trie_trace(trie, &key); - trie.delete(key)?; - let new_trace = get_trie_trace(trie, &key); - Ok( - node_deletion_resulted_in_a_branch_collapse(&old_trace, &new_trace) - .map(TrieKey::from_nibbles), - ) -} - -/// Comparing the path of the deleted key before and after the deletion, -/// determine if the deletion resulted in a branch collapsing into a leaf or -/// extension node, and return the path to the remaining child if this -/// occurred. -fn node_deletion_resulted_in_a_branch_collapse( - old_path: &TriePath, - new_path: &TriePath, -) -> Option { - // Collapse requires at least 2 nodes. - if old_path.0.len() < 2 { - return None; - } - - // If the node path length decreased after the delete, then a collapse occurred. - // As an aside, note that while it's true that the branch could have collapsed - // into an extension node with multiple nodes below it, the query logic will - // always stop at most one node after the keys diverge, which guarantees that - // the new trie path will always be shorter if a collapse occurred. - let branch_collapse_occurred = old_path.0.len() > new_path.0.len(); - - // Now we need to determine the key of the only remaining node after the - // collapse. - branch_collapse_occurred.then(|| new_path.iter().into_key()) -} - -/// The withdrawals are always in the final ir payload. -fn add_withdrawals_to_txns( - txn_ir: &mut [GenerationInputs], - final_trie_state: &mut PartialTrieState< - impl StateTrie + Clone + TryIntoBounds, - >, - mut withdrawals: Vec<(Address, U256)>, -) -> anyhow::Result<()> { - // Scale withdrawals amounts. - for (_addr, amt) in withdrawals.iter_mut() { - *amt = eth_to_gwei(*amt) - } - - let withdrawals_with_hashed_addrs_iter = || { - withdrawals - .iter() - .map(|(addr, v)| (*addr, hash(addr.as_bytes()), *v)) - }; - - let last_inputs = txn_ir - .last_mut() - .expect("We cannot have an empty list of payloads."); - - if last_inputs.signed_txns.is_empty() { - let mut state_trie = final_trie_state.state.clone(); - state_trie.trim_to( - // This is a dummy payload, hence it does not contain yet - // state accesses to the withdrawal addresses. - withdrawals - .iter() - .map(|(addr, _)| *addr) - .chain(match last_inputs.txn_number_before == 0.into() { - // We need to include the beacon roots contract as this payload is at the - // start of the block execution. - true => Some(BEACON_ROOTS_CONTRACT_ADDRESS), - false => None, - }) - .map(TrieKey::from_address), - )?; - last_inputs.tries.state_trie = state_trie.try_into()?; - } - - update_trie_state_from_withdrawals( - withdrawals_with_hashed_addrs_iter(), - &mut final_trie_state.state, - )?; - - last_inputs.withdrawals = withdrawals; - last_inputs.trie_roots_after.state_root = final_trie_state.state.clone().try_into()?.hash(); - - Ok(()) -} - -/// Withdrawals update balances in the account trie, so we need to update -/// our local trie state. -fn update_trie_state_from_withdrawals<'a>( - withdrawals: impl IntoIterator + 'a, - state: &mut impl StateTrie, -) -> anyhow::Result<()> { - for (addr, h_addr, amt) in withdrawals { - let mut acc_data = state.get_by_address(addr).context(format!( - "No account present at {addr:x} (hashed: {h_addr:x}) to withdraw {amt} Gwei from!" - ))?; - - acc_data.balance += amt; - - state - .insert_by_address(addr, acc_data) - // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 - // Add an entry API - .expect("insert must succeed with the same key as a successful `get`"); - } - - Ok(()) -} - -/// Processes a single transaction in the trace. -fn process_txn_info( - txn_range: Range, - is_initial_payload: bool, - txn_info: ProcessedTxnBatchInfo, - curr_block_tries: &mut PartialTrieState< - impl StateTrie + Clone + TryIntoBounds, - >, - extra_data: &mut ExtraBlockData, - other_data: &OtherBlockData, - use_burn_target: bool, -) -> anyhow::Result { - log::trace!( - "Generating proof IR for txn {} through {}...", - txn_range.start, - txn_range.end - 1 - ); - - init_any_needed_empty_storage_tries( - &mut curr_block_tries.storage, - txn_info.nodes_used_by_txn.storage_accesses.keys(), - &txn_info.nodes_used_by_txn.accts_with_unaccessed_storage, - ); - - // For each non-dummy txn, we increment `txn_number_after` and - // update `gas_used_after` accordingly. - extra_data.txn_number_after += txn_info.meta.len().into(); - extra_data.gas_used_after += txn_info.meta.iter().map(|i| i.gas_used).sum::().into(); - - // Because we need to run delta application before creating the minimal - // sub-tries (we need to detect if deletes collapsed any branches), we need to - // do this clone every iteration. - let tries_at_start_of_txn = curr_block_tries.clone(); - - for (i, meta) in txn_info.meta.iter().enumerate() { - update_txn_and_receipt_tries( - curr_block_tries, - meta, - extra_data.txn_number_before.as_usize() + i, - )?; - } - - let mut delta_out = apply_deltas_to_trie_state( - curr_block_tries, - &txn_info.nodes_used_by_txn, - &txn_info.meta, - )?; - - let nodes_used_by_txn = if is_initial_payload { - let mut nodes_used = txn_info.nodes_used_by_txn; - update_beacon_block_root_contract_storage( - curr_block_tries, - &mut delta_out, - &mut nodes_used, - &other_data.b_data.b_meta, - )?; - - nodes_used - } else { - txn_info.nodes_used_by_txn - }; - - let tries = create_minimal_partial_tries_needed_by_txn( - &tries_at_start_of_txn, - &nodes_used_by_txn, - txn_range, - delta_out, - )?; - - let burn_addr = match use_burn_target { - // TODO: https://github.com/0xPolygonZero/zk_evm/issues/565 - // Retrieve the actual burn address from `cdk-erigon`. - true => Some(H160::zero()), - false => None, - }; - let gen_inputs = GenerationInputs { - txn_number_before: extra_data.txn_number_before, - burn_addr, - gas_used_before: extra_data.gas_used_before, - gas_used_after: extra_data.gas_used_after, - signed_txns: txn_info - .meta - .iter() - .filter_map(|t| t.txn_bytes.clone()) - .collect::>(), - withdrawals: Vec::default(), /* Only ever set in a dummy txn at the end of - * the block (see `[add_withdrawals_to_txns]` - * for more info). */ - tries, - trie_roots_after: TrieRoots { - state_root: curr_block_tries.state.clone().try_into()?.hash(), - transactions_root: curr_block_tries.txn.root(), - receipts_root: curr_block_tries.receipt.root(), - }, - checkpoint_state_trie_root: extra_data.checkpoint_state_trie_root, - contract_code: txn_info - .contract_code_accessed - .into_iter() - .map(|code| (hash(&code), code)) - .collect(), - block_metadata: other_data.b_data.b_meta.clone(), - block_hashes: other_data.b_data.b_hashes.clone(), - ger_data: None, - }; - - // After processing a transaction, we update the remaining accumulators - // for the next transaction. - extra_data.txn_number_before = extra_data.txn_number_after; - extra_data.gas_used_before = extra_data.gas_used_after; - - Ok(gen_inputs) -} - -impl StateWrite { - fn apply_writes_to_state_node( - &self, - state_node: &mut AccountRlp, - h_addr: &H256, - acc_storage_tries: &HashMap, - ) -> anyhow::Result<()> { - let storage_root_hash_change = match self.storage_trie_change { - false => None, - true => { - let storage_trie = acc_storage_tries - .get(h_addr) - .context(format!("missing account storage trie {:x}", h_addr))?; - - Some(storage_trie.root()) - } - }; - - state_node.balance = self.balance.unwrap_or(state_node.balance); - state_node.nonce = self.nonce.unwrap_or(state_node.nonce); - state_node.storage_root = storage_root_hash_change.unwrap_or(state_node.storage_root); - state_node.code_hash = self.code_hash.unwrap_or(state_node.code_hash); - - Ok(()) - } -} - -// TODO!!!: We really need to be appending the empty storage tries to the base -// trie somewhere else! This is a big hack! -fn create_minimal_storage_partial_tries<'a>( - storage_tries: &HashMap, - accesses_per_account: impl IntoIterator)>, - additional_storage_trie_paths_to_not_hash: &HashMap>, -) -> anyhow::Result> { - accesses_per_account - .into_iter() - .map(|(h_addr, mem_accesses)| { - // Guaranteed to exist due to calling `init_any_needed_empty_storage_tries` - // earlier on. - let base_storage_trie = &storage_tries[h_addr]; - - let storage_slots_to_not_hash = mem_accesses.iter().cloned().chain( - additional_storage_trie_paths_to_not_hash - .get(h_addr) - .into_iter() - .flat_map(|slots| slots.iter().cloned()), - ); - - let partial_storage_trie = create_trie_subset_wrapped( - base_storage_trie.as_hashed_partial_trie(), - storage_slots_to_not_hash, - TrieType::Storage, - )?; - - Ok((*h_addr, partial_storage_trie)) - }) - .collect() -} - -fn create_trie_subset_wrapped( - trie: &HashedPartialTrie, - accesses: impl IntoIterator, - trie_type: TrieType, -) -> anyhow::Result { - mpt_trie::trie_subsets::create_trie_subset( - trie, - accesses.into_iter().map(TrieKey::into_nibbles), - ) - .context(format!("missing keys when creating {}", trie_type)) -} - -fn eth_to_gwei(eth: U256) -> U256 { - // 1 ether = 10^9 gwei. - eth * U256::from(10).pow(9.into()) -} - -// This is just `rlp(0)`. -const ZERO_STORAGE_SLOT_VAL_RLPED: [u8; 1] = [128]; - -/// Aid for error context. -#[derive(Debug, strum::Display)] -#[allow(missing_docs)] -enum TrieType { - Storage, - Receipt, - Txn, -} diff --git a/trace_decoder/src/interface.rs b/trace_decoder/src/interface.rs new file mode 100644 index 000000000..901fef89a --- /dev/null +++ b/trace_decoder/src/interface.rs @@ -0,0 +1,193 @@ +//! Public types for this crate. +//! +//! These are all in one place because they're about to be heavily refactored in [#401](https://github.com/0xPolygonZero/zk_evm/issues/401). + +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +use ethereum_types::{Address, U256}; +use evm_arithmetization::proof::{BlockHashes, BlockMetadata}; +use keccak_hash::H256; +use mpt_trie::partial_trie::HashedPartialTrie; +use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; +use serde::{Deserialize, Serialize}; + +use crate::Field; + +/// Core payload needed to generate proof for a block. +/// Additional data retrievable from the blockchain node (using standard ETH RPC +/// API) may be needed for proof generation. +/// +/// The trie preimages are the hashed partial tries at the +/// start of the block. A [TxnInfo] contains all the transaction data +/// necessary to generate an IR. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BlockTrace { + /// The state and storage trie pre-images (i.e. the tries before + /// the execution of the current block) in multiple possible formats. + pub trie_pre_images: BlockTraceTriePreImages, + + /// A collection of contract code. + /// This will be accessed by its hash internally. + #[serde(default)] + pub code_db: BTreeSet>, + + /// Traces and other info per transaction. The index of the transaction + /// within the block corresponds to the slot in this vec. + pub txn_info: Vec, +} + +/// Minimal hashed out tries needed by all txns in the block. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum BlockTraceTriePreImages { + /// The trie pre-image with separate state/storage tries. + Separate(SeparateTriePreImages), + /// The trie pre-image with combined state/storage tries. + Combined(CombinedPreImages), +} + +/// State/Storage trie pre-images that are separate. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SeparateTriePreImages { + /// State trie. + pub state: SeparateTriePreImage, + /// Storage trie. + pub storage: SeparateStorageTriesPreImage, +} + +/// A trie pre-image where state & storage are separate. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SeparateTriePreImage { + /// Storage or state trie format that can be processed as is, as it + /// corresponds to the internal format. + Direct(HashedPartialTrie), +} + +/// A trie pre-image where both state & storage are combined into one payload. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct CombinedPreImages { + /// Compact combined state and storage tries. + #[serde(with = "crate::hex")] + pub compact: Vec, +} + +/// A trie pre-image where state and storage are separate. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SeparateStorageTriesPreImage { + /// Each storage trie is sent over in a hashmap with the hashed account + /// address as a key. + MultipleTries(HashMap), +} + +/// Info specific to txns in the block. +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub struct TxnInfo { + /// Trace data for the txn. This is used by the protocol to: + /// - Mutate it's own trie state between txns to arrive at the correct trie + /// state for the start of each txn. + /// - Create minimal partial tries needed for proof gen based on what state + /// the txn accesses. (eg. What trie nodes are accessed). + pub traces: BTreeMap, + + /// Data that is specific to the txn as a whole. + pub meta: TxnMeta, +} + +/// Structure holding metadata for one transaction. +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub struct TxnMeta { + /// Txn byte code. This is also the raw RLP bytestring inserted into the txn + /// trie by this txn. Note that the key is not included and this is only + /// the rlped value of the node! + #[serde(with = "crate::hex")] + pub byte_code: Vec, + + /// Rlped bytes of the new receipt value inserted into the receipt trie by + /// this txn. Note that the key is not included and this is only the rlped + /// value of the node! + #[serde(with = "crate::hex")] + pub new_receipt_trie_node_byte: Vec, + + /// Gas used by this txn (Note: not cumulative gas used). + pub gas_used: u64, +} + +/// A "trace" specific to an account for a txn. +/// +/// Specifically, since we can not execute the txn before proof generation, we +/// rely on a separate EVM to run the txn and supply this data for us. +#[derive(Clone, Debug, Deserialize, Serialize, Default, PartialEq)] +pub struct TxnTrace { + /// If the balance changed, then the new balance will appear here. Will be + /// `None` if no change. + #[serde(skip_serializing_if = "Option::is_none")] + pub balance: Option, + + /// If the nonce changed, then the new nonce will appear here. Will be + /// `None` if no change. + #[serde(skip_serializing_if = "Option::is_none")] + pub nonce: Option, + + /// hash([Address]) of storages read by the transaction. + #[serde(default, skip_serializing_if = "BTreeSet::is_empty")] + pub storage_read: BTreeSet, + + /// hash([Address]) of storages written by the transaction, + /// with their new value. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub storage_written: BTreeMap, + + /// Contract code that this account has accessed or created + #[serde(skip_serializing_if = "Option::is_none")] + pub code_usage: Option, + + /// True if the account got self-destructed at the end of this txn. + #[serde(default, skip_serializing_if = "is_false")] + pub self_destructed: bool, +} + +fn is_false(b: &bool) -> bool { + !b +} + +/// Contract code access type. Used by txn traces. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum ContractCodeUsage { + /// Contract was read. + Read(H256), + + /// Contract was created (and these are the bytes). Note that this new + /// contract code will not appear in the [`BlockTrace`] map. + Write(#[serde(with = "crate::hex")] Vec), +} + +/// Other data that is needed for proof gen. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct OtherBlockData { + /// Data that is specific to the block. + pub b_data: BlockLevelData, + /// State trie root hash at the checkpoint. + pub checkpoint_state_trie_root: H256, + /// Consolidated block hashes at the checkpoint. + pub checkpoint_consolidated_hash: [Field; NUM_HASH_OUT_ELTS], + /// Address where the burnt fees are stored. + /// + /// Only used if the `cfg_erigon` feature is activated. + pub burn_addr: Option
, +} + +/// Data that is specific to a block and is constant for all txns in a given +/// block. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct BlockLevelData { + /// All block data excluding block hashes and withdrawals. + pub b_meta: BlockMetadata, + /// Block hashes: the previous 256 block hashes and the current block hash. + pub b_hashes: BlockHashes, + /// Block withdrawal addresses and values. + pub withdrawals: Vec<(Address, U256)>, +} diff --git a/trace_decoder/src/lib.rs b/trace_decoder/src/lib.rs index 2202d58c5..fba82d8f7 100644 --- a/trace_decoder/src/lib.rs +++ b/trace_decoder/src/lib.rs @@ -1,92 +1,64 @@ -//!
-//! This library is undergoing major refactoring as part of (#275)(https://github.com/0xPolygonZero/zk_evm/issues/275). -//! Consider all TODOs to be tracked under that issue. -//!
+//! An _Ethereum Node_ executes _transactions_ in _blocks_. //! -//! Your neighborhood zk-ready [ethereum](https://github.com/0xPolygonZero/erigon) -//! [node](https://github.com/0xPolygonHermez/cdk-erigon/) emits binary "witnesses"[^1]. +//! Execution mutates two key data structures: +//! - [The state trie](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/#state-trie). +//! - [The storage tries](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/#storage-trie). //! -//! But [`plonky2`], your prover, wants [`GenerationInputs`]. +//! Ethereum nodes expose information about the transactions over RPC, e.g: +//! - [The specific changes to the storage tries](TxnTrace::storage_written). +//! - [Changes to account balance in the state trie](TxnTrace::balance). //! -//! This library helps you get there. +//! The state execution correctness is then asserted by the zkEVM prover in +//! [`evm_arithmetization`], relying on `starky` and [`plonky2`]. //! -//! [^1]: A witness is an attestation of the state of the world, which can be -//! proven by a prover. +//! **Prover perfomance is a high priority.** //! -//! # Non-Goals -//! - Performance - this won't be the bottleneck in any proving system. -//! - Robustness - malicious or malformed input may crash this library. +//! The aformentioned trie structures may have subtries _hashed out_. +//! That is, any node (and its children!) may be replaced by its hash, +//! while maintaining provability of its contents: //! -//! TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 -//! refactor all the docs below +//! ```text +//! A A +//! / \ / \ +//! B C -> H C +//! / \ \ \ +//! D E F F +//! ``` +//! (where `H` is the hash of the `D/B\E` subtrie). //! -//! It might not be obvious why we need traces for each txn in order to generate -//! proofs. While it's true that we could just run all the txns of a block in an -//! EVM to generate the traces ourselves, there are a few major downsides: -//! - The client is likely a full node and already has to run the txns in an EVM -//! anyways. -//! - We want this protocol to be as agnostic as possible to the underlying -//! chain that we're generating proofs for, and running our own EVM would -//! likely cause us to loose this genericness. +//! The principle concern of this module is to step through the transactions, +//! and reproduce the _intermediate tries_, +//! while hashing out all possible subtries to minimise prover load +//! (since prover performance is sensitive to the size of the trie). +//! The prover can therefore prove each batch of transactions independently. //! -//! While it's also true that we run our own zk-EVM (plonky2) to generate -//! proofs, it's critical that we are able to generate txn proofs in parallel. -//! Since generating proofs with plonky2 is very slow, this would force us to -//! sequentialize the entire proof generation process. So in the end, it's ideal -//! if we can get this information sent to us instead. -//! -//! This library generates an Intermediary Representation (IR) of -//! a block's transactions, given a [BlockTrace] and some additional -//! data represented by [OtherBlockData]. -//! -//! It first preprocesses the [BlockTrace] to provide transaction, -//! withdrawals and tries data that can be directly used to generate an IR. -//! For each transaction, this library extracts the -//! necessary data from the processed transaction information to -//! return the IR. -//! -//! The IR is used to generate root proofs, then aggregation proofs and finally -//! block proofs. Because aggregation proofs require at least two entries, we -//! pad the vector of IRs thanks to additional dummy payload intermediary -//! representations whenever necessary. -//! -//! ### [Withdrawals](https://ethereum.org/staking/withdrawals) and Padding -//! -//! Withdrawals are all proven together in a dummy payload. A dummy payload -//! corresponds to the IR of a proof with no transaction. They must, however, be -//! proven last. The padding is therefore carried out as follows: If there are -//! no transactions in the block, we add two dummy transactions. The withdrawals -//! -- if any -- are added to the second dummy transaction. If there is only one -//! transaction in the block, we add one dummy transaction. If -//! there are withdrawals, the dummy transaction is at the end. Otherwise, it is -//! added at the start. If there are two or more transactions: -//! - if there are no withdrawals, no dummy transactions are added -//! - if there are withdrawals, one dummy transaction is added at the end, with -//! all the withdrawals in it. +//! # Non-goals +//! - Performance - this will never be the bottleneck in any proving stack. +//! - Robustness - this library depends on other libraries that are not robust, +//! so may panic at any time. #![deny(rustdoc::broken_intra_doc_links)] #![warn(missing_debug_implementations)] #![warn(missing_docs)] -/// The broad overview is as follows: +zk_evm_common::check_chain_features!(); + +/// Over RPC, ethereum nodes expose their tries as a series of binary +/// [`wire::Instruction`]s in a node-dependant format. /// -/// 1. Ethereum nodes emit a bunch of binary [`wire::Instruction`]s, which are -/// parsed in [`wire`]. -/// 2. They are passed to one of two "frontends", depending on the node +/// These are parsed into the relevant trie depending on the node: /// - [`type2`], which contains an [`smt_trie`]. /// - [`type1`], which contains an [`mpt_trie`]. -/// 3. The frontend ([`type1::Frontend`] or [`type2::Frontend`]) is passed to -/// the "backend", which lowers to [`evm_arithmetization::GenerationInputs`]. /// -/// Deviations from the specification are signalled with `BUG(spec)` in the -/// code. +/// After getting the tries, +/// we can continue to do the main work of "executing" the transactions. const _DEVELOPER_DOCS: () = (); -/// Defines the main functions used to generate the IR. -mod decoding; -/// Defines functions that processes a [BlockTrace] so that it is easier to turn -/// the block transactions into IRs. -mod processed_block_trace; +mod interface; + +pub use interface::*; +use plonky2::field::goldilocks_field::GoldilocksField; + mod type1; // TODO(0xaatif): https://github.com/0xPolygonZero/zk_evm/issues/275 // add backend/prod support for type 2 @@ -96,364 +68,13 @@ mod type2; mod typed_mpt; mod wire; -use std::collections::{BTreeMap, BTreeSet, HashMap}; - -use ethereum_types::{Address, U256}; -use evm_arithmetization::proof::{BlockHashes, BlockMetadata}; -use evm_arithmetization::GenerationInputs; -use keccak_hash::keccak as hash; -use keccak_hash::H256; -use mpt_trie::partial_trie::{HashedPartialTrie, OnOrphanedHashNode}; -use processed_block_trace::ProcessedTxnBatchInfo; -use serde::{Deserialize, Serialize}; -use typed_mpt::{StateMpt, StateTrie as _, StorageTrie, TrieKey}; - -/// Core payload needed to generate proof for a block. -/// Additional data retrievable from the blockchain node (using standard ETH RPC -/// API) may be needed for proof generation. -/// -/// The trie preimages are the hashed partial tries at the -/// start of the block. A [TxnInfo] contains all the transaction data -/// necessary to generate an IR. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BlockTrace { - /// The state and storage trie pre-images (i.e. the tries before - /// the execution of the current block) in multiple possible formats. - pub trie_pre_images: BlockTraceTriePreImages, - - /// A collection of contract code. - /// This will be accessed by its hash internally. - #[serde(default)] - pub code_db: BTreeSet>, - - /// Traces and other info per transaction. The index of the transaction - /// within the block corresponds to the slot in this vec. - pub txn_info: Vec, -} - -/// Minimal hashed out tries needed by all txns in the block. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum BlockTraceTriePreImages { - /// The trie pre-image with separate state/storage tries. - Separate(SeparateTriePreImages), - /// The trie pre-image with combined state/storage tries. - Combined(CombinedPreImages), -} - -/// State/Storage trie pre-images that are separate. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct SeparateTriePreImages { - /// State trie. - pub state: SeparateTriePreImage, - /// Storage trie. - pub storage: SeparateStorageTriesPreImage, -} - -/// A trie pre-image where state & storage are separate. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SeparateTriePreImage { - /// Storage or state trie format that can be processed as is, as it - /// corresponds to the internal format. - Direct(HashedPartialTrie), -} - -/// A trie pre-image where both state & storage are combined into one payload. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct CombinedPreImages { - /// Compact combined state and storage tries. - #[serde(with = "crate::hex")] - pub compact: Vec, -} - -/// A trie pre-image where state and storage are separate. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum SeparateStorageTriesPreImage { - /// Each storage trie is sent over in a hashmap with the hashed account - /// address as a key. - MultipleTries(HashMap), -} - -/// Info specific to txns in the block. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct TxnInfo { - /// Trace data for the txn. This is used by the protocol to: - /// - Mutate it's own trie state between txns to arrive at the correct trie - /// state for the start of each txn. - /// - Create minimal partial tries needed for proof gen based on what state - /// the txn accesses. (eg. What trie nodes are accessed). - pub traces: BTreeMap, - - /// Data that is specific to the txn as a whole. - pub meta: TxnMeta, -} - -/// Structure holding metadata for one transaction. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct TxnMeta { - /// Txn byte code. This is also the raw RLP bytestring inserted into the txn - /// trie by this txn. Note that the key is not included and this is only - /// the rlped value of the node! - #[serde(with = "crate::hex")] - pub byte_code: Vec, - - /// Rlped bytes of the new receipt value inserted into the receipt trie by - /// this txn. Note that the key is not included and this is only the rlped - /// value of the node! - #[serde(with = "crate::hex")] - pub new_receipt_trie_node_byte: Vec, - - /// Gas used by this txn (Note: not cumulative gas used). - pub gas_used: u64, -} - -/// A "trace" specific to an account for a txn. -/// -/// Specifically, since we can not execute the txn before proof generation, we -/// rely on a separate EVM to run the txn and supply this data for us. -#[derive(Clone, Debug, Deserialize, Serialize, Default)] -pub struct TxnTrace { - /// If the balance changed, then the new balance will appear here. Will be - /// `None` if no change. - #[serde(skip_serializing_if = "Option::is_none")] - pub balance: Option, - - /// If the nonce changed, then the new nonce will appear here. Will be - /// `None` if no change. - #[serde(skip_serializing_if = "Option::is_none")] - pub nonce: Option, - - /// [hash](hash)([Address]) of storages read by the - /// transaction. - #[serde(default, skip_serializing_if = "BTreeSet::is_empty")] - pub storage_read: BTreeSet, +pub use core::entrypoint; - /// [hash](hash)([Address]) of storages written by the - /// transaction, with their new value. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub storage_written: BTreeMap, +mod core; - /// Contract code that this account has accessed or created - #[serde(skip_serializing_if = "Option::is_none")] - pub code_usage: Option, - - /// True if the account got self-destructed at the end of this txn. - #[serde(default, skip_serializing_if = "is_false")] - pub self_destructed: bool, -} - -fn is_false(b: &bool) -> bool { - !b -} - -/// Contract code access type. Used by txn traces. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum ContractCodeUsage { - /// Contract was read. - Read(H256), - - /// Contract was created (and these are the bytes). Note that this new - /// contract code will not appear in the [`BlockTrace`] map. - Write(#[serde(with = "crate::hex")] Vec), -} - -/// Other data that is needed for proof gen. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct OtherBlockData { - /// Data that is specific to the block. - pub b_data: BlockLevelData, - /// State trie root hash at the checkpoint. - pub checkpoint_state_trie_root: H256, -} - -/// Data that is specific to a block and is constant for all txns in a given -/// block. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct BlockLevelData { - /// All block data excluding block hashes and withdrawals. - pub b_meta: BlockMetadata, - /// Block hashes: the previous 256 block hashes and the current block hash. - pub b_hashes: BlockHashes, - /// Block withdrawal addresses and values. - pub withdrawals: Vec<(Address, U256)>, -} - -/// TODO(0xaatif): -/// document this once we have the API finalized -pub fn entrypoint( - trace: BlockTrace, - other: OtherBlockData, - mut batch_size: usize, - use_burn_addr: bool, -) -> anyhow::Result> { - use anyhow::Context as _; - use mpt_trie::partial_trie::PartialTrie as _; - - use crate::processed_block_trace::{ - Hash2Code, ProcessedBlockTrace, ProcessedBlockTracePreImages, - }; - use crate::PartialTriePreImages; - use crate::{ - BlockTraceTriePreImages, CombinedPreImages, SeparateStorageTriesPreImage, - SeparateTriePreImage, SeparateTriePreImages, - }; - - let BlockTrace { - trie_pre_images, - code_db, - txn_info, - } = trace; - - let pre_images = match trie_pre_images { - BlockTraceTriePreImages::Separate(SeparateTriePreImages { - state: SeparateTriePreImage::Direct(state), - storage: SeparateStorageTriesPreImage::MultipleTries(storage), - }) => ProcessedBlockTracePreImages { - tries: PartialTriePreImages { - state: state.items().try_fold( - StateMpt::new(OnOrphanedHashNode::Reject), - |mut acc, (nibbles, hash_or_val)| { - let path = TrieKey::from_nibbles(nibbles); - match hash_or_val { - mpt_trie::trie_ops::ValOrHash::Val(bytes) => { - #[expect(deprecated)] // this is MPT specific - acc.insert_by_hashed_address( - path.into_hash() - .context("invalid path length in direct state trie")?, - rlp::decode(&bytes) - .context("invalid AccountRlp in direct state trie")?, - )?; - } - mpt_trie::trie_ops::ValOrHash::Hash(h) => { - acc.insert_hash_by_key(path, h)?; - } - }; - anyhow::Ok(acc) - }, - )?, - storage: storage - .into_iter() - .map(|(k, SeparateTriePreImage::Direct(v))| { - v.items() - .try_fold( - StorageTrie::new(OnOrphanedHashNode::Reject), - |mut acc, (nibbles, hash_or_val)| { - let path = TrieKey::from_nibbles(nibbles); - match hash_or_val { - mpt_trie::trie_ops::ValOrHash::Val(value) => { - acc.insert(path, value)?; - } - mpt_trie::trie_ops::ValOrHash::Hash(h) => { - acc.insert_hash(path, h)?; - } - }; - anyhow::Ok(acc) - }, - ) - .map(|v| (k, v)) - }) - .collect::>()?, - }, - extra_code_hash_mappings: None, - }, - BlockTraceTriePreImages::Combined(CombinedPreImages { compact }) => { - let instructions = - wire::parse(&compact).context("couldn't parse instructions from binary format")?; - let type1::Frontend { - state, - code, - storage, - } = type1::frontend(instructions)?; - ProcessedBlockTracePreImages { - tries: PartialTriePreImages { - state, - storage: storage.into_iter().collect(), - }, - extra_code_hash_mappings: match code.is_empty() { - true => None, - false => Some( - code.into_iter() - .map(|it| (crate::hash(&it), it.into_vec())) - .collect(), - ), - }, - } - } - }; - - let all_accounts_in_pre_images = pre_images.tries.state.iter().collect::>(); - - // Note we discard any user-provided hashes. - let mut hash2code = code_db - .into_iter() - .chain( - pre_images - .extra_code_hash_mappings - .unwrap_or_default() - .into_values(), - ) - .collect::(); - - // Make sure the batch size is smaller than the total number of transactions, - // or we would need to generate dummy proofs for the aggregation layers. - if batch_size > txn_info.len() { - batch_size = txn_info.len() / 2 + 1; - } - - let last_tx_idx = txn_info.len().saturating_sub(1) / batch_size; - - let mut txn_info = txn_info - .chunks(batch_size) - .enumerate() - .map(|(i, t)| { - let extra_state_accesses = if last_tx_idx == i { - // If this is the last transaction, we mark the withdrawal addresses - // as accessed in the state trie. - other - .b_data - .withdrawals - .iter() - .map(|(addr, _)| *addr) - .collect::>() - } else { - Vec::new() - }; - - TxnInfo::into_processed_txn_info( - t, - &pre_images.tries, - &all_accounts_in_pre_images, - &extra_state_accesses, - &mut hash2code, - ) - }) - .collect::, _>>()?; - - while txn_info.len() < 2 { - txn_info.push(ProcessedTxnBatchInfo::default()); - } - - decoding::into_txn_proof_gen_ir( - ProcessedBlockTrace { - tries: pre_images.tries, - txn_info, - withdrawals: other.b_data.withdrawals.clone(), - }, - other, - use_burn_addr, - batch_size, - ) -} - -#[derive(Debug, Default)] -struct PartialTriePreImages { - pub state: StateMpt, - pub storage: HashMap, -} +/// The base field on which statements are being proven. +// TODO(Robin): https://github.com/0xPolygonZero/zk_evm/issues/531 +pub type Field = GoldilocksField; /// Like `#[serde(with = "hex")`, but tolerates and emits leading `0x` prefixes mod hex { @@ -481,23 +102,6 @@ mod hex { } } -trait TryIntoExt { - type Error: std::error::Error + Send + Sync + 'static; - fn try_into(self) -> Result; -} - -impl TryIntoExt for ThisT -where - ThisT: TryInto, - E: std::error::Error + Send + Sync + 'static, -{ - type Error = ThisT::Error; - - fn try_into(self) -> Result { - TryInto::try_into(self) - } -} - #[cfg(test)] #[derive(serde::Deserialize)] struct Case { diff --git a/trace_decoder/src/processed_block_trace.rs b/trace_decoder/src/processed_block_trace.rs deleted file mode 100644 index f2e4fcb5e..000000000 --- a/trace_decoder/src/processed_block_trace.rs +++ /dev/null @@ -1,298 +0,0 @@ -use std::collections::{BTreeSet, HashMap, HashSet}; - -use anyhow::{bail, Context as _}; -use ethereum_types::{Address, H256, U256}; -use evm_arithmetization::generation::mpt::{AccountRlp, LegacyReceiptRlp}; -use itertools::Itertools; -use zk_evm_common::EMPTY_TRIE_HASH; - -use crate::typed_mpt::{StateTrie as _, TrieKey}; -use crate::PartialTriePreImages; -use crate::{hash, TxnTrace}; -use crate::{ContractCodeUsage, TxnInfo}; - -const FIRST_PRECOMPILE_ADDRESS: U256 = U256([1, 0, 0, 0]); -const LAST_PRECOMPILE_ADDRESS: U256 = U256([10, 0, 0, 0]); - -/// A processed block trace, ready to be used to generate prover input payloads. -#[derive(Debug)] -pub(crate) struct ProcessedBlockTrace { - pub tries: PartialTriePreImages, - pub txn_info: Vec, - pub withdrawals: Vec<(Address, U256)>, -} - -#[derive(Debug)] -pub(crate) struct ProcessedBlockTracePreImages { - pub tries: PartialTriePreImages, - pub extra_code_hash_mappings: Option>>, -} - -/// A processed transaction batch, containing all information necessary to -/// reproduce the state transition incurred by its set of transactions. -#[derive(Debug, Default)] -pub(crate) struct ProcessedTxnBatchInfo { - pub nodes_used_by_txn: NodesUsedByTxnBatch, - pub contract_code_accessed: HashSet>, - pub meta: Vec, -} - -/// Code hash mappings that we have constructed from parsing the block -/// trace. -/// If there are any txns that create contracts, then they will also -/// get added here as we process the deltas. -pub(crate) struct Hash2Code { - /// Key must always be [`hash`] of value. - inner: HashMap>, -} - -impl Hash2Code { - pub fn new() -> Self { - Self { - inner: HashMap::new(), - } - } - fn get(&mut self, hash: H256) -> anyhow::Result> { - match self.inner.get(&hash) { - Some(code) => Ok(code.clone()), - None => bail!("no code for hash {}", hash), - } - } - fn insert(&mut self, code: Vec) { - self.inner.insert(hash(&code), code); - } -} - -impl FromIterator> for Hash2Code { - fn from_iter>>(iter: II) -> Self { - let mut this = Self::new(); - for code in iter { - this.insert(code) - } - this - } -} - -impl TxnInfo { - pub(crate) fn into_processed_txn_info( - tx_infos: &[Self], - tries: &PartialTriePreImages, - all_accounts_in_pre_image: &[(H256, AccountRlp)], - extra_state_accesses: &[Address], - hash2code: &mut Hash2Code, - ) -> anyhow::Result { - let mut nodes_used_by_txn = NodesUsedByTxnBatch::default(); - let mut contract_code_accessed = HashSet::from([vec![]]); // we always "access" empty code - let mut meta = Vec::with_capacity(tx_infos.len()); - - let all_accounts: BTreeSet = - all_accounts_in_pre_image.iter().map(|(h, _)| *h).collect(); - - for txn in tx_infos { - let mut created_accounts = BTreeSet::new(); - - for ( - addr, - TxnTrace { - balance, - nonce, - storage_read, - storage_written, - code_usage, - self_destructed, - }, - ) in &txn.traces - { - // record storage changes - let storage_written = storage_written.clone(); - - let storage_read_keys = storage_read.clone().into_iter(); - - let storage_written_keys = storage_written.keys(); - let storage_access_keys = storage_read_keys.chain(storage_written_keys.copied()); - - if let Some(storage) = nodes_used_by_txn.storage_accesses.get_mut(&hash(addr)) { - storage.extend( - storage_access_keys - .map(|H256(bytes)| TrieKey::from_hash(hash(bytes))) - .collect_vec(), - ) - } else { - nodes_used_by_txn.storage_accesses.insert( - hash(addr), - storage_access_keys - .map(|H256(bytes)| TrieKey::from_hash(hash(bytes))) - .collect(), - ); - }; - - // record state changes - let state_write = StateWrite { - balance: *balance, - nonce: *nonce, - storage_trie_change: !storage_written.is_empty(), - code_hash: code_usage.as_ref().map(|it| match it { - ContractCodeUsage::Read(hash) => *hash, - ContractCodeUsage::Write(bytes) => hash(bytes), - }), - }; - - if state_write != StateWrite::default() { - // a write occurred - - // Account creations are flagged to handle reverts. - if !all_accounts.contains(&hash(addr)) { - created_accounts.insert(*addr); - } - - // Some edge case may see a contract creation followed by a `SELFDESTRUCT`, with - // then a follow-up transaction within the same batch updating the state of the - // account. If that happens, we should not delete the account after processing - // this batch. - nodes_used_by_txn.self_destructed_accounts.remove(addr); - - if let Some(existing_state_write) = nodes_used_by_txn.state_writes.get_mut(addr) - { - // The entry already exists, so we update only the relevant fields. - if state_write.balance.is_some() { - existing_state_write.balance = state_write.balance; - } - if state_write.nonce.is_some() { - existing_state_write.nonce = state_write.nonce; - } - if state_write.storage_trie_change { - existing_state_write.storage_trie_change = - state_write.storage_trie_change; - } - if state_write.code_hash.is_some() { - existing_state_write.code_hash = state_write.code_hash; - } - } else { - nodes_used_by_txn.state_writes.insert(*addr, state_write); - } - } - - for (k, v) in storage_written.into_iter() { - if let Some(storage) = nodes_used_by_txn.storage_writes.get_mut(&hash(addr)) { - storage.insert(TrieKey::from_hash(k), rlp::encode(&v).to_vec()); - } else { - nodes_used_by_txn.storage_writes.insert( - hash(addr), - HashMap::from_iter([(TrieKey::from_hash(k), rlp::encode(&v).to_vec())]), - ); - } - } - - let is_precompile = (FIRST_PRECOMPILE_ADDRESS..LAST_PRECOMPILE_ADDRESS) - .contains(&U256::from_big_endian(&addr.0)); - - // Trie witnesses will only include accessed precompile accounts as hash - // nodes if the transaction calling them reverted. If this is the case, we - // shouldn't include them in this transaction's `state_accesses` to allow the - // decoder to build a minimal state trie without hitting any hash node. - if !is_precompile || tries.state.get_by_address(*addr).is_some() { - nodes_used_by_txn.state_accesses.insert(*addr); - } - - match code_usage { - Some(ContractCodeUsage::Read(hash)) => { - contract_code_accessed.insert(hash2code.get(*hash)?); - } - Some(ContractCodeUsage::Write(code)) => { - contract_code_accessed.insert(code.clone()); - hash2code.insert(code.to_vec()); - } - None => {} - } - - if *self_destructed { - nodes_used_by_txn.self_destructed_accounts.insert(*addr); - } - } - - for &addr in extra_state_accesses { - nodes_used_by_txn.state_accesses.insert(addr); - } - - let accounts_with_storage_accesses = nodes_used_by_txn - .storage_accesses - .iter() - .filter(|(_, slots)| !slots.is_empty()) - .map(|(addr, _)| *addr) - .collect::>(); - - let all_accounts_with_non_empty_storage = all_accounts_in_pre_image - .iter() - .filter(|(_, data)| data.storage_root != EMPTY_TRIE_HASH); - - let accounts_with_storage_but_no_storage_accesses = all_accounts_with_non_empty_storage - .filter(|&(addr, _data)| !accounts_with_storage_accesses.contains(addr)) - .map(|(addr, data)| (*addr, data.storage_root)); - - nodes_used_by_txn - .accts_with_unaccessed_storage - .extend(accounts_with_storage_but_no_storage_accesses); - - meta.push(TxnMetaState { - txn_bytes: match txn.meta.byte_code.is_empty() { - false => Some(txn.meta.byte_code.clone()), - true => None, - }, - receipt_node_bytes: check_receipt_bytes( - txn.meta.new_receipt_trie_node_byte.clone(), - )?, - gas_used: txn.meta.gas_used, - created_accounts, - }); - } - - Ok(ProcessedTxnBatchInfo { - nodes_used_by_txn, - contract_code_accessed, - meta, - }) - } -} - -fn check_receipt_bytes(bytes: Vec) -> anyhow::Result> { - match rlp::decode::(&bytes) { - Ok(_) => Ok(bytes), - Err(_) => { - rlp::decode(&bytes).context("couldn't decode receipt as a legacy receipt or raw bytes") - } - } -} - -/// A collection of all the state and storage accesses performed by a batch of -/// transaction. -/// -/// Note that "*_accesses" fields include writes. -#[derive(Debug, Default)] -pub(crate) struct NodesUsedByTxnBatch { - pub state_accesses: HashSet
, - pub state_writes: HashMap, - - pub storage_accesses: HashMap>, - pub storage_writes: HashMap>>, - - /// Hashed address -> storage root. - pub accts_with_unaccessed_storage: HashMap, - pub self_destructed_accounts: HashSet
, -} - -#[derive(Debug, Default, PartialEq)] -pub(crate) struct StateWrite { - pub balance: Option, - pub nonce: Option, - pub storage_trie_change: bool, - pub code_hash: Option, -} - -#[derive(Debug, Default)] -pub(crate) struct TxnMetaState { - /// [`None`] if this is a dummy transaction inserted for padding. - pub txn_bytes: Option>, - pub receipt_node_bytes: Vec, - pub gas_used: u64, - pub created_accounts: BTreeSet
, -} diff --git a/trace_decoder/src/type1.rs b/trace_decoder/src/type1.rs index 019a75c95..aeea0dbb6 100644 --- a/trace_decoder/src/type1.rs +++ b/trace_decoder/src/type1.rs @@ -97,11 +97,11 @@ fn visit( match code { Some(Either::Left(Hash { raw_hash })) => raw_hash.into(), Some(Either::Right(Code { code })) => { - let hash = crate::hash(&code); + let hash = keccak_hash::keccak(&code); frontend.code.insert(code); hash } - None => crate::hash([]), + None => keccak_hash::keccak([]), } }, }; diff --git a/trace_decoder/src/typed_mpt.rs b/trace_decoder/src/typed_mpt.rs index 5a49966a6..28a03aa33 100644 --- a/trace_decoder/src/typed_mpt.rs +++ b/trace_decoder/src/typed_mpt.rs @@ -9,12 +9,9 @@ use evm_arithmetization::generation::mpt::AccountRlp; use mpt_trie::partial_trie::{HashedPartialTrie, Node, OnOrphanedHashNode, PartialTrie as _}; use u4::{AsNibbles, U4}; -/// Map where keys are [up to 64 nibbles](TrieKey), -/// and values are [`rlp::Encodable`]/[`rlp::Decodable`]. -/// /// See . /// -/// Portions of the trie may be deferred: see [`Self::insert_hash`]. +/// Portions of the trie may be _hashed out_: see [`Self::insert_hash`]. #[derive(Debug, Clone, PartialEq, Eq)] struct TypedMpt { inner: HashedPartialTrie, @@ -31,6 +28,8 @@ impl TypedMpt { } } /// Insert a node which represents an out-of-band sub-trie. + /// + /// See [module documentation](super) for more. fn insert_hash(&mut self, key: TrieKey, hash: H256) -> anyhow::Result<()> { self.inner.insert(key.into_nibbles(), hash)?; Ok(()) @@ -186,6 +185,9 @@ pub struct TransactionTrie { } impl TransactionTrie { + pub fn new() -> Self { + Self::default() + } pub fn insert(&mut self, txn_ix: usize, val: Vec) -> anyhow::Result>> { let prev = self .untyped @@ -201,6 +203,22 @@ impl TransactionTrie { pub const fn as_hashed_partial_trie(&self) -> &mpt_trie::partial_trie::HashedPartialTrie { &self.untyped } + /// _Hash out_ parts of the trie that aren't in `txn_ixs`. + pub fn mask(&mut self, txn_ixs: impl IntoIterator) -> anyhow::Result<()> { + self.untyped = mpt_trie::trie_subsets::create_trie_subset( + &self.untyped, + txn_ixs + .into_iter() + .map(|it| TrieKey::from_txn_ix(it).into_nibbles()), + )?; + Ok(()) + } +} + +impl From for HashedPartialTrie { + fn from(value: TransactionTrie) -> Self { + value.untyped + } } /// Per-block, `txn_ix -> [u8]`. @@ -212,6 +230,9 @@ pub struct ReceiptTrie { } impl ReceiptTrie { + pub fn new() -> Self { + Self::default() + } pub fn insert(&mut self, txn_ix: usize, val: Vec) -> anyhow::Result>> { let prev = self .untyped @@ -227,6 +248,37 @@ impl ReceiptTrie { pub const fn as_hashed_partial_trie(&self) -> &mpt_trie::partial_trie::HashedPartialTrie { &self.untyped } + /// _Hash out_ parts of the trie that aren't in `txn_ixs`. + pub fn mask(&mut self, txn_ixs: impl IntoIterator) -> anyhow::Result<()> { + self.untyped = mpt_trie::trie_subsets::create_trie_subset( + &self.untyped, + txn_ixs + .into_iter() + .map(|it| TrieKey::from_txn_ix(it).into_nibbles()), + )?; + Ok(()) + } +} + +impl From for HashedPartialTrie { + fn from(value: ReceiptTrie) -> Self { + value.untyped + } +} + +pub trait StateTrie { + fn insert_by_address( + &mut self, + address: Address, + account: AccountRlp, + ) -> anyhow::Result>; + fn insert_hash_by_key(&mut self, key: TrieKey, hash: H256) -> anyhow::Result<()>; + fn get_by_address(&self, address: Address) -> Option; + fn reporting_remove(&mut self, address: Address) -> anyhow::Result>; + /// _Hash out_ parts of the trie that aren't in `txn_ixs`. + fn mask(&mut self, address: impl IntoIterator) -> anyhow::Result<()>; + fn iter(&self) -> impl Iterator + '_; + fn root(&self) -> H256; } /// Global, [`Address`] `->` [`AccountRlp`]. @@ -259,12 +311,9 @@ impl StateMpt { .iter() .map(|(key, rlp)| (key.into_hash().expect("key is always H256"), rlp)) } - pub const fn as_hashed_partial_trie(&self) -> &mpt_trie::partial_trie::HashedPartialTrie { + pub fn as_hashed_partial_trie(&self) -> &mpt_trie::partial_trie::HashedPartialTrie { self.typed.as_hashed_partial_trie() } - pub fn root(&self) -> H256 { - self.typed.root() - } } impl StateTrie for StateMpt { @@ -274,9 +323,9 @@ impl StateTrie for StateMpt { account: AccountRlp, ) -> anyhow::Result> { #[expect(deprecated)] - self.insert_by_hashed_address(crate::hash(address), account) + self.insert_by_hashed_address(keccak_hash::keccak(address), account) } - /// Insert a deferred part of the trie + /// Insert an _hashed out_ part of the trie fn insert_hash_by_key(&mut self, key: TrieKey, hash: H256) -> anyhow::Result<()> { self.typed.insert_hash(key, hash) } @@ -287,19 +336,12 @@ impl StateTrie for StateMpt { /// Delete the account at `address`, returning any remaining branch on /// collapse fn reporting_remove(&mut self, address: Address) -> anyhow::Result> { - Ok( - crate::decoding::delete_node_and_report_remaining_key_if_branch_collapsed( - self.typed.as_mut_hashed_partial_trie_unchecked(), - &TrieKey::from_address(address), - )?, + delete_node_and_report_remaining_key_if_branch_collapsed( + self.typed.as_mut_hashed_partial_trie_unchecked(), + TrieKey::from_address(address), ) } - fn contains_address(&self, address: Address) -> bool { - self.typed - .as_hashed_partial_trie() - .contains(TrieKey::from_address(address).into_nibbles()) - } - fn trim_to(&mut self, addresses: impl IntoIterator) -> anyhow::Result<()> { + fn mask(&mut self, addresses: impl IntoIterator) -> anyhow::Result<()> { let inner = mpt_trie::trie_subsets::create_trie_subset( self.typed.as_hashed_partial_trie(), addresses.into_iter().map(TrieKey::into_nibbles), @@ -310,6 +352,14 @@ impl StateTrie for StateMpt { }; Ok(()) } + fn iter(&self) -> impl Iterator + '_ { + self.typed + .iter() + .map(|(key, rlp)| (key.into_hash().expect("key is always H256"), rlp)) + } + fn root(&self) -> H256 { + self.typed.root() + } } impl From for HashedPartialTrie { @@ -323,20 +373,7 @@ impl From for HashedPartialTrie { pub struct StateSmt { address2state: BTreeMap, - deferred: BTreeMap, -} - -pub trait StateTrie { - fn insert_by_address( - &mut self, - address: Address, - account: AccountRlp, - ) -> anyhow::Result>; - fn insert_hash_by_key(&mut self, key: TrieKey, hash: H256) -> anyhow::Result<()>; - fn get_by_address(&self, address: Address) -> Option; - fn reporting_remove(&mut self, address: Address) -> anyhow::Result>; - fn contains_address(&self, address: Address) -> bool; - fn trim_to(&mut self, address: impl IntoIterator) -> anyhow::Result<()>; + hashed_out: BTreeMap, } impl StateTrie for StateSmt { @@ -348,7 +385,7 @@ impl StateTrie for StateSmt { Ok(self.address2state.insert(address, account)) } fn insert_hash_by_key(&mut self, key: TrieKey, hash: H256) -> anyhow::Result<()> { - self.deferred.insert(key, hash); + self.hashed_out.insert(key, hash); Ok(()) } fn get_by_address(&self, address: Address) -> Option { @@ -358,13 +395,18 @@ impl StateTrie for StateSmt { self.address2state.remove(&address); Ok(None) } - fn contains_address(&self, address: Address) -> bool { - self.address2state.contains_key(&address) - } - fn trim_to(&mut self, address: impl IntoIterator) -> anyhow::Result<()> { + fn mask(&mut self, address: impl IntoIterator) -> anyhow::Result<()> { let _ = address; Ok(()) } + fn iter(&self) -> impl Iterator + '_ { + self.address2state + .iter() + .map(|(addr, acct)| (keccak_hash::keccak(addr), *acct)) + } + fn root(&self) -> H256 { + todo!() + } } /// Global, per-account. @@ -395,8 +437,69 @@ impl StorageTrie { pub const fn as_hashed_partial_trie(&self) -> &HashedPartialTrie { &self.untyped } - + pub fn reporting_remove(&mut self, key: TrieKey) -> anyhow::Result> { + delete_node_and_report_remaining_key_if_branch_collapsed(&mut self.untyped, key) + } pub fn as_mut_hashed_partial_trie_unchecked(&mut self) -> &mut HashedPartialTrie { &mut self.untyped } + /// _Hash out_ the parts of the trie that aren't in `paths`. + pub fn mask(&mut self, paths: impl IntoIterator) -> anyhow::Result<()> { + self.untyped = mpt_trie::trie_subsets::create_trie_subset( + &self.untyped, + paths.into_iter().map(TrieKey::into_nibbles), + )?; + Ok(()) + } +} + +impl From for HashedPartialTrie { + fn from(value: StorageTrie) -> Self { + value.untyped + } +} + +/// If a branch collapse occurred after a delete, then we must ensure that +/// the other single child that remains also is not hashed when passed into +/// plonky2. Returns the key to the remaining child if a collapse occurred. +fn delete_node_and_report_remaining_key_if_branch_collapsed( + trie: &mut HashedPartialTrie, + key: TrieKey, +) -> anyhow::Result> { + let old_trace = get_trie_trace(trie, key); + trie.delete(key.into_nibbles())?; + let new_trace = get_trie_trace(trie, key); + Ok( + node_deletion_resulted_in_a_branch_collapse(&old_trace, &new_trace) + .map(TrieKey::from_nibbles), + ) +} + +fn get_trie_trace(trie: &HashedPartialTrie, k: TrieKey) -> mpt_trie::utils::TriePath { + mpt_trie::special_query::path_for_query(trie, k.into_nibbles(), true).collect() +} + +/// Comparing the path of the deleted key before and after the deletion, +/// determine if the deletion resulted in a branch collapsing into a leaf or +/// extension node, and return the path to the remaining child if this +/// occurred. +fn node_deletion_resulted_in_a_branch_collapse( + old_path: &mpt_trie::utils::TriePath, + new_path: &mpt_trie::utils::TriePath, +) -> Option { + // Collapse requires at least 2 nodes. + if old_path.0.len() < 2 { + return None; + } + + // If the node path length decreased after the delete, then a collapse occurred. + // As an aside, note that while it's true that the branch could have collapsed + // into an extension node with multiple nodes below it, the query logic will + // always stop at most one node after the keys diverge, which guarantees that + // the new trie path will always be shorter if a collapse occurred. + let branch_collapse_occurred = old_path.0.len() > new_path.0.len(); + + // Now we need to determine the key of the only remaining node after the + // collapse. + branch_collapse_occurred.then(|| mpt_trie::utils::IntoTrieKey::into_key(new_path.iter())) } diff --git a/trace_decoder/src/wire.rs b/trace_decoder/src/wire.rs index 355d30827..6f56f1e44 100644 --- a/trace_decoder/src/wire.rs +++ b/trace_decoder/src/wire.rs @@ -4,7 +4,7 @@ //! //! Fortunately, their opcodes don't conflict, so we can have a single //! [`Instruction`] type, with shared parsing logic in this module, and bail on -//! unsupported instructions later on in the frontend. +//! unsupported instructions later on. //! //! This is fine because we don't care about failing fast when parsing. @@ -68,7 +68,7 @@ pub enum Instruction { AccountLeaf { key: NonEmpty>, nonce: Option, - /// BUG(spec): see decode site [`account_leaf`]. + /// BUG(spec): see parse site [`account_leaf`]. balance: Option, has_code: bool, has_storage: bool, diff --git a/trace_decoder/tests/cases/b19807080_main.json b/trace_decoder/tests/cases/b19807080_main.json index 3a09e82d0..ddd67d1e0 100644 --- a/trace_decoder/tests/cases/b19807080_main.json +++ b/trace_decoder/tests/cases/b19807080_main.json @@ -1010,7 +1010,13 @@ ] ] }, - "checkpoint_state_trie_root": "0xbbd66174555d27c88e285ff4797de401470d8d2486d15513ab36e491e864bca2" + "checkpoint_state_trie_root": "0xbbd66174555d27c88e285ff4797de401470d8d2486d15513ab36e491e864bca2", + "checkpoint_consolidated_hash": [ + 7715853179812774584, + 12908177954576181071, + 11068935829000177885, + 16446535885506885907 + ] } } ] \ No newline at end of file diff --git a/trace_decoder/tests/cases/b19840104_main.json b/trace_decoder/tests/cases/b19840104_main.json index 6537bc456..6c788e0bf 100644 --- a/trace_decoder/tests/cases/b19840104_main.json +++ b/trace_decoder/tests/cases/b19840104_main.json @@ -1,366 +1,374 @@ -[{ - "block_trace": { - "trie_pre_images": { - "combined": { - "compact": "0x0103e13c370f7ce6f9de81dd4068775d7c39d1e19051063dea7b4f0dc570ab06868103940aa0f44732cfe96e03cc53d65cc93839531689de2370a6bc12eb515a75f6ea03cce25a8e8a0b65fddd641e8856e8baec7cd1a012b7470e92aea3d4f6fd02c1c80386d98f1b6d13f0446b302efc1197100dc2d2d10c9e7db46ec2a34d55b2f0bc9f0345a634828040159ea3390d453145c5e97f3406575308f3803fc63944db067cbf03cb91ecd916f16b6227a60215cab43c4b9a409b19cb9af242375517af5841ac5d0329a14ef57d4d2282d4f7479ea6d37f728b45af8ac42a621a953369de0b54535503c4c441ce0b6d427b5577cd15b9cd890ff95d1434d101165be1623b9cd1ef67b6035ffdb8ef07117acdf909af168d381bf8e503dba36547fe5895671e9d495add1c03324e1dcc50950c11240379740d5b383f879719bb1d477d04592d65471d973e8203f355ecc89a4b76dfdd7fa0f0318293854c502c03a74c0c4ce388fcd79835e0bd03bb455a53b6153bf3fb7e0fc6db20d7ef3661942e9e957af3b9120f91f755984103be0773cd56d7c9df1c8062a9c35b731b1359b4b9ef8c31a5c74c7286d96fcac103f7dde717fe02b995df87659a9cf25cc95f994360e2b8cba31db4183c14c9670203c669e29de38188ac1e9b8662b0d995e31e500883d08affadd4eaf71cb7ae43b603dbe9990a2e3a1b78a7e86924142e73d32e6a5fe8ff15f9d923df57f5f5667c02036a42738b24621de70cf0a5975e4e0de90c279602ed12d99c46e9951927348d1f03476ce5c4306a2f9875011e1b52bfad9af7f9eaa53a0c09725b641957f6e5abd1033a6eee3a707b54e089427a7df5f4d780cc9bf727b8527a2670b3e558fc580ebd037c900a244c50a273971a28d09b3e5448a2d3b92248efb447d5e914c83885d94c03cbb3c52e38138f069c39c31753b3a1e333713637c57dc2f38dade06d65833c4e032e9e786d65c8c00c3053a66aafa6d738b6029bd9b44ca911fadbb1b22bbccd3803f23a75947392a97153c9fabd3ebe5d109892d7ae8a4d3b64458d60fb5b0cdef0034c0924eddf164fd85e99748a48284ca73c4b2dc83edb9d3072551b3a6be0f94703bcf4e068ce6001b29bd880451c3388ef1c144cd79c727000046ef06b8b6beaa403b615536da0f1386f5dd58645a0955875bf7e4c00c8bf8397054268b30a5ca536038f67227b47f06ade1eb106d7b50bd823a327fc6d04556d011b98e4e695a6578e0328a17cd0c678af3871f0a5d3dfe41ff24967fbb7f11726dbf53707b5c27a5961036ce3286ec2d725cfeb234d4a29386ac1b057ea5109e381b59f5816430e82386c03a2d833f5221678f65bb919bab928e1ac2a21fb16c768c802385743c1f20f33bd0394ef837d0a9420521c9b61374d3433c534a22365b1ef1d34c305b1d4d508b17803245d8e881b569e0f2859d441687596d69a1f13172b721b798abdaa13cd61acf10331b17ef9b060f53595553fba5298848848045635c1ddd4867fb8834193531efb03b254caa5808fc83395b676f33b88eb741b88fbd627dcfbe735a4924fb3a7ef47032c53e4633f67dd8f1cd3b97857e2fa1888a08113bd4ab0ddd9f3f0667b75f31d03ebfe5a0154be1c4918681bb0b84185583e2381f1c034dbfeed740abe5a18a73e03e0414af533edb23b377e58da622436a7e1bc72da8005d38e7403e9c796fc8d7d0354fdd1dcf7c140089c99e10173a00bde1620974e76caa85b89c61386854d2e1e03a4fe5ae7bb248ad34f6d6c3cc546f3945bcabb9ea6832e5b2c126fb2948d37b0038436e7414d7bd51873cf6d66cd7100548bc241391940c4596cb8fd8318c9eb43033ef1123618d733594d9bde1b472f2695835b273426ef0e82d2901eced8db7402036499aa00f5cc368850094bfc120b9b888f16d1b2882fd29bae671a5c415cb83c03d27584f834991d48dd9d39eaf146fd21299f67af62a7f0f8c56f316b58c812f503c22ffb33a73ace056ec28dccf8f19920c8075a47151913e13bb76de334454350035bef61b2be3a91a5d66d92b259cb46bbe3a8a5866efac22f59f73f70174791b503c892127d3f6cceb7c41af2f17aa67450342f091879adb9acf3a7b5a8c1035ddd03556d3ec12e5343a2ef72db2e7ef381104279b2476ce4ae4a56b660b50347595203835ea527241d3ff19f15c1d172c75245fc94e1e2680fe5d2551b6225417ecdbb03fffa2540a1573b228956fa86248745aed31ae6e2c983714f32fd9dd7fc58c36a05581e0329328c87be9836183768908f30171daf16714c1f39a360b617bd23df400c0a4629546746ae0805581e03934cc196d443b1d2c5588f9fe55fa70f673923ac0eef128d9203835d500c0147c7a8357a0fa80005581e038bc60869ca403f3d41ddf8575cfa5be7785783dfb7dccc395b1ad999b00c01470d35cd2acec40005581e03d4abd7a5e810b20841e6aaf7fdba78949602e371cc5ff9aac8f74876200c054702c0246afc0c000458613373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff0155000358c5239b1dd310f0537a237bc27563369523c442d801ec8818db3671c74674540365826e2e7ab17befd18111567901f4d1c040051467d927e308ce4b248822a0cb03b1c225aa33368624bda2174d75e18cf62636a5b76c4c6e1b2e418d4f5b2a355f03f9f0c35958ba855fe3b67c2d3e281098a811faf015ff970c2ac1ed7519b7a9f90340d8abd4aea2178e472a6fb5d9a54ab7d9da7b4cec41de15657ae7d7ee47ee9203b4b2017ac478ec2fecab17810a12138dd142aed069ddb547bc6ca75f30d6edbe0306ea5daf16f46213f499aabb9937cce13f5fef58bb32f6e0cb3e9a06a0b51e5e035f11d8c8b2a04750588c9cb0a75e46b7e6e954dd62adfd624acf8da2643082d903a0eed321da78a0ebdd5fe9921d733518413a2cb187ca6c1105f7abd1cb2fbb1b0311b7c5e1e609f177f216cde5180864e5b646e33cccdacb581e3f588664f2aa29037d0990dcadece1c27990f6006dfb1b06731a2e38f30a794da8bad6323491555b03fe92a1e51e25e90d509a1d38bc6f2a9fe43e4f72047a2a24b870c8d30fc64b6703bcde60f65dd3d2ca3a2f2f4b0fea9276db48ca7062d6b0b6aaa3a0510d812fc003df6748c404401faeb51fffb775298fa91f0dd2b737b0f8595ddd8053103833c50367ba887e07b40051afda724bac2d9295be9135f1fabf12fcb95ef8762bbc5e54005820035b0fcf3bd800db1425c3b5cea82894f4bcd6c6e3f3681233ecaa3a21a22a3044663cc98f03a6b7d063b82dec0e83c94ca4a3eb6519dad637b47c2321d17ec61548a784c95703fbf5999fcabba0e2ed169e657630905ddaa06e1ff8ded70eec12494da396b04f039ebc1b67747b6a172ee99df6e8debb1b98d1bb2af2fd4d7d9233c6f86fb8bb9a03262f082f2f808c68b5102bd0ca743333881bcdf56c5b0caf594c71733ed0c34700582003cddb82bd7d6f65fe1dc37f3f335825dafeac204521b498c3035fca9d24671044663d7ae3034ccdf84f270e9fb07171b769d1737ca3cb47be3d33be2cd32aab896647a32da0031f8901f34af3b4a8bac1e2b5d52650022a64650558a0c04c5fb4599c094dd08f03f52e9a5bf4207d9cd90239f7199782d58a55fceb3895fa2cb4cc9542d2a5872303e3af39c59d84c6dd4bbb43417edf1e939070570d233bb5a1b7198c4d67b1567100581f02b50381a466ad9b3f5753ae9d99dc1961955a9450f39a54fe5ed20f23703944663cfcbf00581f02d7283d573d576507b2896b999a1774e22aadc14d202294c19a0f513a12fb44663ca90700581f022d34ba1eaa86355c866834b1f968ae22db024991e5f42a9a675196c9459d44663de78b02198410035714c53a00c63f7abf88010b7d8641a6f7c834c8c8b6ab07d1304165dd133e2603fb951dd6719d0846becbb4d55276d92a7a77f6687eba589ff57bdfdb799fe4ce0328f4625c2a4b7929cbf0ea4f0b0e3459896a55de82446c8997a5856856abffd90391340802d5194304e8a2f4323c84da7647cf985a3c6d2a1ef795dbd8a53817800219bfff03576e9c67f4913b6dec689d445a1d0cd79a90fb1c9a2f0c87a2ba84d25171980603d924cd2dd4d5362c29bcbb0fc4f61e17558e556aea838c8d09ab96901ba11fe3033517c66a20bd6abd4b1ee2e5cd45298563b00fd979ad371af28dfe95b868322a033e0ce59b58426d4eb6806b518e0a18ab4d6ebeda65951a616eb9c9c917b983520219ffff03387f24a9b1c6be19fad5f890ca5c502479f798fa7a51ebdf492ec7752bb784ba03244183a300b82435242d6e0b450a90dd10e49846e00e2cda1481f2cd6893f04b03356f09c737f039614e52e2e0c37fb7a70313ace29b2154d9114db788f0ae541a03ad4ee9196fcc075e9a4dc9fd2bb8c75338dab17003f968d73480b3db389bf9d7031d739c09b9667fbd3f6ab1ef093a08e2c5366e1d6acda970be8d1c3fddedc2a2031afcc8c217d91826795f263a1f09fe4f6130bbd58deca5fd7c33077375fa1cf5032a8efece59d3d07314f21a48b9d4e15cb7c42c0780b4da74b5713427a941530403f0804f578891545342627171f5a9f2262c76551c8dff1d87e1d4f600e0dba569034e67010819e15c986e4fc147d9d699936155d18e88644fabd15bd556521a9b7203b6fd210aee5e046b5053ad7132b8c705eaeeee963dd7c5efe7209d356667b6e803b5c9b2a4290b6130b89889f84edc7688e2d8893cf6671a82834411fe9bf6f27003ec93db40b8df894e80edcf0202cd39373099967eb1e75f285e6cc998e7b5b26803692dcb502f53043296639d68228f69c17e83663caf8ab3cae97b1674befdb8e103d5c209b220a62d28e6f24c89d7d6a37de20b3fe53afa4e39f71b5759f4d3e4c4032a92f5ace6d6fe31425c6282bbda8e84e2db0345cf5662433266e1bf343a12b403e12845abeacbb0beb485495bef7d65bf834364233fece312a49b576ff3c340bc03e6a66e06863ede15f9f2aa3ade52cec2ca242e3f806c40699a40d9284bd88b94037d6889baaf1cc4d81c4acd0d0e884c685ffb3a2d29cae5b0727b2ecd3e8d8dd2035982174780f06ddfdd84d88987dcbdc69ae1279de27976eb62d2e5abcfa1c18b03e13a3a9a9b2880112b3171f114eed30dcbc533ff7d56980c8898a82d1f4482c7030e65acda5fde29d1cc76bbd3ffc17d8acbbde66d3e68b6a5d9dabe5204525aa103c5a998297fe5c2766bf6dcaa34f57328a1961bbb6140e34f22155c4904bcdcb203f53af6a0b865c4f515acc6d3c93000e2cddc6f8e8d961c2672a7629d1907994303a7385e81588f6c99b53022f433eb8c00efc96fa67eb6331327cac4b1a07d7585037f98a226ecdb56c0d8651302d8b22d2acff8bc0eecd3e3213753028839c0d92903cf753d039db5e738d1ee967356555d047215a4bb82aab79373c359ff70a4478b0379a542cab81cfd9d454470df1e42547fa517c5d3120e5bd3730d98934b64e62603a05dfba906e040db50a0afa79a425201f74146354d88c2754c5821eb719b5190037b2a32ea2c5f26a8552a7872f3ce840aa447e7d9c0efb0b03c99d131161b810903f7b2d22dd98c0f407dc59ac30c13260cec6e6565f54b9d9b09c4bf1bff9829e50371a35ec266c88aed1b2897e3f36a14cbb329816629742399d38ccf05c541a55700582003c8baf22843559d0eb809346dcfbcc9cae4248ce96c61ee9416d0767869af1044663d262f0395451063870cbebd1a1c414c042a67a269e123a33eefbe12795ca4533cbee4210377696199292b63a284658963a209f97c039eabfad945fd77a3e3d3a2431baf2f03c62ede3a485a67f19ceee383b691bd1579d726b72f36bd25966b245d2658c99f0362f50e33577871564dd39157ff97bca77d58296e10eaf771487a741d7c032be10364f74730c91f576090ca0ba7b1e956e4003c70aa7dd5bfc978ce3110ff013e5b00581f0238787dd0e1f5d8a1615fa968dea23acfd3d692e6fc565b123f7caf145eff5820fd7d812e8f33936363ce5cc165cab273cc02af6aee8da3d42370f4d343c6f1fd00581f031906aedc23fbbf5f6acc171555cde2fc54aafcf7c2b796b6c9407434c47058201dd3ed11c47ea3f75e3c5ca68e5ab9e0bbb7ecda2318917c262c27a9af24af1900581f03c2e6f82dfc83b58e2df5c82f2a4b3a3d5a9f68fbfa24b96adf46c00deeb044663cb0330206021928000219ffff0219ffff03db912c435e75072736adad652ecfc4673017ec25347326549992227d294d856b0309e3878fa9a66b2f5cc86044daab843416ab5977bcf6dc06b760060f09ec7ca2037c1fe9f58ca2408965a47d402563a15f2fcba83a4a6eb0df1b030da50ba63a4b0219ffff05581e03a92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e4200701186103efc4a95eba2b0e8ca1e7050522d539fcaee456a9baaed13b74846ce6e9f5284f05581e0387d430445038d44dfe5f67a6a29f0e61137f74004f19eb39df5f6507100c134622a41c687260035a6395100cee14b8619641123268781a56f8d9fad4ec4d364c15d99ab81fd8650219d595035d3b51473708235f94b7786ec3d9e1141cffde44d1b3c2f8f50735bc89406bdc0219ffff03bb6dce5781ee1b12b6667ebff1c6a7e00b27f644760beec12fb305c5eca6748703c2659bef399996280795da7d7c6cc8380980b8ed448477279fce86cb1439815903e45da9fe37e969afed21a06e65aab4b8219e0be940cf6c2d96a3312dde77c4ba03cbeb42d91012dfa7755211b9ebebc4e6481daa53edeaceb78a26478589a5171c0355e8cbfb54708ecacf62e4d8e82d3ac23e130e81a0a767b06f854fc5a76a803203d82d3c30bfa72b176d2285d259d03f43e6dccdc50cc03dff946f850414c15e6103b84ba827a5ef576ce6db5eeb236b0b05db5fa57d18448dfa5685297e76ae072103494925526b1d70068072e28e02df4d43941cfb627cfba3d4a148a8e857bee15e03cd19d837ba2dea15c076f66e441a92c62e7c353901568a45ba5f71e66e88d278038ba8c6e5027ea75be5cc7cb96300e7201167a07c4c58fb06c272593502d8b9dd0219ffff0301b8e895dbf6e8f0d41d8a44ae0ad8221319b0b3232425ee213ed801d9eb9ddb03c15636c31d015e3f4a306e5ca113823cf9bcf4422ae44ce6f9833e1a4c92a87b03e4b068e19c0ce1a9cde31af1072d04cdbe5f101a86ab598ee61a8ccb7a5160e3035131433214d21d38997d1bd4d2eaf4a69ee15f096d86344b920532b298824fc303152393ebcf209df305d93fcdb3ee0d6c9c28dd5fed398bdaf44f0e32a1253e5b03c28ccc74e893b02e05d6d5447b79d210a80b3799c658e3b2cae91e64f250d52d03b663e80603c7f9bb86ac127aa3726183321274f5675178b609500bd338fe6689034a4d70349a6c431a3edaefc60a1dfa6f6151ad5d23e0028b924ea72676af2798033122fd4a0193f4e18ca6f08a697c0af1c8141e2659da5c6bf0f87f5ed86ed8a70219ffff0372acd27b85056450bebd86a5a917527908636a4c781fa8535f6801d52643cbe60313561846f55baa646d462f32f65378782aeb0a36ec4396cb1dd95ff2e32a73cb0219ffff03ab23c03eb82757b5599c6b1b720f61171157020454f78fb6fba98f50b444103603d21ed651d18455a8a402320838db0c0ee34b4fd81fa20fdd9fe9bc10a3f6756f03f215f60497fcb591efea4098f8e6d1af4260aa34dd0143154ccca2ff7a41371103390f008988bcb2194db38af1ae0c5b450fc67aa63847c3333631236ff160ddca0392da5bcac8b88fa85dd08f6c0a2c54c8049826621e9de17655a5ac61c896422e03361866a4d5b1c4ae521a022b8d0bf3f956cce9f4558293865b4e2e09b679f5fc03c1204c89e187aa54d5bc785652301c858d3b8a3a271e3b29970f5e35b03f03b003e5d334dae613ef68f86ecc885179c64542cbe18c5b4aa3bd929eb6d2bf5d9e430219ffff03001cc050182fb06ae5d5cb21738d6a93d6acec33ffbada6bb24116265a0d418203eec4298676ca64e441552ff83659d2575ad601c6fc53e98344ea7882a0fe1c56039d6b1ff6c754633d01894c26d7ec56ad635490dfb291b08544cfd1b5dcb0cec3039a6d2abc2c326ca7b11f03d687495f40353542ae9b674bb6c59bcc6956657d8b03fb6d16aa3f68791b386410f1c544051aef6b48a87517f2c2f60be6c45290bc0b0364b38a393150f0aeb5df8ae532cc8017202732b532046e192a385a1df530a1db03cbb43ac36287287ae24670a0c4214587495798add9fbd6124993f2c6629d4236035826a6a848d733844dd5987c128e28517f21bc7c06fd3742853a2d94c29cca9203f49b340fc1b72e277463376094dbbb5ab76de329e2984dffaa0324bff1cf9c6b03c61f7518e360642bc12298499dea86e091bad02e3082654be4ab16ade8be481303f3cc663d1119c0e755090ff50e657071c1b7aaf76f5049f8f68efd2fb3e2a76c034f1fc53876be73fe021b3da55b1b841bb1e1d6ca99a8687da7557638ec4021cc03152f541cf3ee990146f63fea1d9b53abaf06a58b00a2ab68fce1c0f5b0c489120387f624a5927e67b0a59632a5850f1c011737bfa8f3a51c937ea14a3988e066b20323c3238b822b325fe11ec12c14e024ddf5710d8802e5c85d1ce7c47fb02a52b303fc2eab2d511cbc973f0716322261116d8b81f804ad73d6d380caf1c42989ecea03067c67cd91a2747c892fd1605d2a38c8b7409376e9684323b7694d359eabf9ff03097bfa2378565be66df31046bbd59bd5d4c2c14299553cc58d7ffb2ecf27699603ddbec2457d43fcf01730e63a612c370b56bb911de1147bd597396fc1a116484e0390b76a69782f11b0cbea81d1398b41e2927e5edb66688c6c728806bce71839d103a1d7ebab554c080cb2fb2cb17667e3058fb3537ed64855620f7305c9c45973e5039c2e37751faff09ddd740b02db8f0ee80d92547b41e50ef9a3d2113bdd2f18be0330ae1b86a3326ef4be28d5c2470d8808df51f32e5399ba760da826f46905959f0362cad7ebe4f2c9f7e283ebe4f1ca1dc9e8e7c8bedc52ae4bde537a19beea1f1d03d51b9c0f3ccf30ef1a3754573521f05d4981b3c7a0eb635ddc81684ab75fdc9b03766ce30501987085efd915e492dc68eb6fb728a13070605c762c2dd8fc65c9e503d2be7794bc7b7ac3eff3e2bf8fca6721b9d7304e19263153d566066109a5fab505581e0329e4cb84257f3a85c60d49d690d86c942f7d8521e57336a24bd99719500846f84ac406100005581d0231e95b8419eb826136a480dd3e995ed8b16c389a83abf73d262522a80c18434a049d41dbc0f641387da905581d021defeb7d022491798adb92c6a0e0bbd3e028c60b710678fcfc65bdc804030219804003f0a45890bea7520a3246b1d46241f5163e20df1f78ed7ae79c9d2c9c8491ce5003c33fc0d0e68fbd9b43795a1d49f372864646e80a64b2ae991aafe78cf1a6b8bc05581e031342fc8a455f0bb4cf1cf5c8d538e0fc58be64fa5515ece1f52ab69a9007011bffffffffffffffff05581e0377c6fb8536ebf253b0a73a57f2d3dd389ca68dd09a55ca7c9402700640040105581e038548cf6f40bc75e6b2075230a9b50dab25ae567bbff331319892bb76200c0847207fdc31f0253705581e03e43b5623140b81b5788ab27a9b607435c29e998d62d7041c02aa5497b00c01461e55d5526400035cc05631b99585c07c74357703c6536ee955b7fc282ec27b997ee3cfc7db376305581e033e96d0303c74a5f280436b54c6cb65c305ca1432bce3657a886fa885600c0147c9ac605151e00005581e037248f8cba499143afad86bc4846e956fc50ef032d10256e551b3342c900c0346a2c1a8265fb805581e03448abfbdc1f8a978d1266c4e652cfe22c375ac38e7fec717c83e4464700c03472cde779ad41d6c036657955b7ec60ccd679d744e3007ebed64a1c193b1f5e6764e5fd5098f5621a4039be7f2f51b6926d91717387c7d931554000322f401f33bae238c59d16d1bc25005581e0304099b4cfbfbef7c9241d66211f3db31de111d250cb83214371a03052007011bffffffffffffffff02197f550306084be24dad54cc280b3bf8ec4aaabe90ef39d4a1d3de01361e7978f2333e6203d070f1709a1cee0cb0f2dc904d4a97ba0bb0899e7c44573daf0c4481dd0acdef037256332e427d6fbea337f43cf2cdfff5aff4243af95cea9f189e5215b4da468003a8eba973b3479f7730e2da45137f7025dabe1729a91367de311f71e2a554b9d2032388595cefb05ecc623b4644216fa13da46bf66e0af30c3b6e61e980e47f2f4103a78c06eacefde35ab475592d485471fd7c9e2ed061a66683db9e0f938c6eeea103943cf313af4d005afba941eaf012b10bfeba6c27bab6a5b500e7a463beb4ce8603cba2737f18889d6a5f490126166eccf6eeedf56294b6983e211caddd1e958bde03e70c0538d263e7153e98080dd8651340c6a16c61ccac8e72faf62b3ce0035e6c032c7f02e20d33992e4a756861c214ea04fd1fa5b7d7b77054043d909012ce7ca6033ddebcaaabe1a9f3fc9f1433bd43c275272753ecffc73f4dc4d147106d060bac03501228901ee23e51b629d809fe86ca91b857ebc264348fa6711d52ec799e49790380856eaf2c16d41b7c5851c8b8be074e2796ef9fc5a51af35abf82e7064d8ce70219ffff0373457bb1c2d5a37570f05f98d0da6ab88337ebb1dc5e69d8905424c0d25f3ff5035ceaa6bbae7d847a6b71ed0fd91751f23a221217cae0cca560eca0ed46641fe203baefa85ed25f86ec38ffeb1dce60e649268ec9253f9ea2e937177cac4a32c02103deb8799b4ebd27039932274a35b805633be4c4f9e316424c0da29cfeee1413500319e279bdc8e991271380be21083446fef85aaae12b0ee658bd0a4472344497c80379866774e2f225b8895e3de8a034c7cda76505fe5a0bbf4cee28bcf03a50889103f207db476fd76b340723f6806bf05778064c1b8c03ea762913042a6deaa83bf00333ab1293f95b00e0f5ae9eb29d2b2914f25a9e927ff18e077fabb6ed05166bc00219ffff030f435593b37c6e7e90456965f90baccca95ec22f18726dba77f969586563319a0374f3bf4b37dfba97d3bb836784a582d5387f1825e37bae15cef044e4a400ee10034224592b2898cf17dc3412815aa5d8306aa68fd1897203f32a00fde6614829db03b141e7b53041cedc0726c3eb62ccf7ef8594064490e280a085412683871b3de0039ad73dd089132dd4797a0dbd3bd6fe1b098c46f06c517ef1e3797ef8c51dd21003c0ccc062063d494691863a58cffb0498c3ecf32b97fe6bafbfa4f91a3aadbd3403ca29993c9ab4769f55b1668e25e86366c58315e90c52fbfbbdaeb966f0d8213a035444b46af6e3c94f584c4dc0c7cee1e3e9750071fa4f648b452633be27cee3d103dd0ef771e03aa7643e5ebc78ad7ba2ba6e3029f1f54503f575b2c1db3a50484e032fb5287c04e5b0440901877a095a4a433cb704c875ab4faf5d92b890f83a368d0342182d0d0b8b8f6888219bb5441e71ed5247061f247b3e5beb05256fd437446703057fea1a0b7e2ad1b0dac40108d98219a77820023ae54f6aa1ccd599dbbfaf90030bcd00ac7b07c9a993662985753fd20a5ad28aac1cad7cafef8550f76d905bf003fdc6ae80d23be595b425ec6bf90d701c20aa537c992ae517519c1c9132cd35b00219ffff0300b3538138548d8e7cdfe3ec6a0649346cab821a2c12759a2d5c735ca1e2476b0397f9be41801a5e5cfb6d5aa5fdffc062a433b8403c000683a3eff412dceb69d303ae5583703a0d55910f9e00556ed1e99f16b07e48d8063abec2fc81036899731f03468233cd70fdd2f93ca606aacf821aa5b529aa0b97ad5fe3e01834e23fd2f26f0380dbd6361c810ebf3dd1e1d37a2708bacdbfa655f6f65184e4c77d428d18dc65030fa56fdff3c568c91864f230f49dcd3071208ac2c6f2e9f436777e5f8a3600060394e5b49d3a8c2f88390491996babb83300f840ef28c6dca64d89405e3a39151d03a8be77041b3cc39fd155bb8c1634cbc4f642485cc1bf74edd32f8097e2b9d1d00219ffff038ee69e0c947fa4b09f8d38dde182267b4c4a16ea0242c041369619c16b2f69500392065172c22a81711d3803081e2c66a408e0727400ad2d83ef13c570633d5fea03ec289b0aa52aff14cdb4d22bb62e330c7e4ca2aa611ebae50902c190e9d0c99703b5288a3d3514fb46b592e754af10a92ec8a7f724ce0c27d8406956ac2b8f5b8b032f969988b3bb0cc7bbc443189f47d5900025e8f77ac5500ca95baf449e774c7103c4154482e9e26c9bc32112b211d4717775216f694bfddfc8b269ab56a46205d80219ffff03b86aaf4643a9522fbbae67b172edc7c77393325dc9c31c9a7abd8d3994c847c20389ee3ccc7222da174ba639ecac039602c3e717952af7296b84ed11b7e6f2309e037361d147efae615395c158f88bca05f3945005fc43a8196e1bbc97e710e13cd10342525c1620a81a4b104fc85e3694cb32589489ce2ee5b01cd9e6ab14a5cebace0343e3b06cddcce5e0f06baed99ecd0a20775a5ff0e74cc5a76fc9c61010739764038b6c262adfe371830f62289777185cf8f52217c9f95282c67923a377eaaf86ed035dda29994853c06889e9e46917a2a0f79400940167b9aef146ab347b55d664e703559ca936f787bfb77440659de1c59ad30eeb28eebc23817b843ae74f761b283e036bb1e58c1e30f46e3e5a17c627b36840ce672a1b4576340c39a5ab5614686d62036d4da0172d96c64cf89c5e12666fff5bae46b070b11ff34d32f8cd1e16c31de903b51b58d1619a46dcfc5d4137a3842bbb299e2fc846ce6f87bbb6b89c2b0b9bdf03d913f2df7f405954f7f3ddec0f6b6fb418ebc3eb0748d9195474e98518ae658a034f042a8a0a48b51cab19b6ff5b512e2e54ff0bdfb317f28c999791cb204411d203e5d907601a73120bae4431e4d05d07deb1eb429c219bd2219dcb26e71fd9affe03cb44b7d70efc4351355d73d14d0022521f1b259212653ad83e8baa6cec3c23d3031dc993b12dc23a0d67244bf463405d99e8000baa1fbd040708ceeebc4fdbad8203d0d7e8ea93789f0857b17f2698c99009e8d53b35eaa1e292ffc9714d45adc00d0395d70f0441aafcca4c87ba0a8e565313ea2d2b242fb3cad8d04d6fb8483fadbf0355534b75d3c88e148d52a6870f57d19ba0ac801caa76e08476b90b48d7245a4003baa59f2afa6444c81244b86b7c9dd17b6edca4ac623a93762aa2db74658760e6035b375faf8e6b683046c729754d73b5625199502802051d1b437d400cdcb0cf3603600009c54247532fa59301cf9015172f85b4dc83cc238f70df51f73262af9457039fa254060036f7f90b710bc3b17f2d866d5db4c06b175506c911246f6d0b50f5036d5b2af4df1e2f292bf9b5cb4e13e1ceb528bcd8ac00fdb041d777a456ae4d1b03e7f4e78e600c3126f4691665f1a867963f504bf7e5f272f7e269369d693885370390d6c43e3dede80b4c6759b42c5d330666f3fd1146f164889637c446b1673469038dd25b7adccd1a9cc378ec8a4c419b593e30f6c853535e3f3c3a7675d9f5948a03b6c688039c4174b06ad78c703fba69d3145fde491a9ea057e0ca4689ce6f083b03b5591bb5ba5bd4c45ac3827151cc5af7614e1bfed7809196971af8362c19490403c07c7fc3f92e6ebff32d0fce3961aefd7ee2e0c5090158ee8578d094fdc5b2e203e91b66637cde3244ee7a4f3c0c6f2d41e5d3d51f2fb161fde3b3e46a7be5d2e403ca6f9f545f835eb201a5cb3a64e0c6a3d382ec7bf5bd18d1ee4128de0d5c8fb303d2ff40753b8f0eec2e458c6c092129ecdf4da77d7b95645ca1ef6a47af7135db0342225c8b5558e35ea88dec62a89b5dddca727ca654cd826c19ea4bacc6fbeb6703b0de12445e9e9b542fa6214e604fc40e069fb71044af40d50d4b741d3a3791eb037d577156ee5867430b360d6a12c062c7557e0fe2cfdc2c23c24675676109820603e4abd4b3b402bf6ca99131af823b1f1e3dc564cd30eb6b83aeb4e6295678639c033c1f38bb537c6eb904d7f5068a749092053dfb9fd8445dd54829d6f8c96024650380fecf187fecb501dd15f7452fb9a5b4030af29bbb400caab89d9fd1f6b32d0603bdedc66f85c076c34ddaa82d7f153bf1940a7a05e35f70ee5078634ff0d9529f038c72a5acd831a0cdfd5fa323c3d53bd217dd034aa9adea20652ed7fd52458f230337e3badcb7453ce9eda24b15ef374242c727263e5f5d002190cd5723f3fa6c180380ffb85a7a5817f873341fb95fadb78e688522800706a26f5721c9cc29cd9a2005581e03eb820fd1bffc460d328d2689d7ffe13c4443877bebf25bc3502cd345900c02472427ac4344f80003a31f886c62f676187a889179625c86a313cef7b2e41693325ff5e023fba049f203508d78823e3c33ea99a6bf0627dce396125c96e0f94f2752a9ff05880c38b1dc03e886b0f9abe462c549e7b057256e86d99e81713aa80dcddbafc8f00a0c3fb71605581e0371534472d82ce1ddcefa8a9a081057fa05952073b437c3327c830078f00402031f8b67be329f6419c9282095843235301b6b3475e42bc9e3262b646aba8072060605581e03f53b77d7a78435e2976275d2001631a331bf18d7cd453410c7e13373f007011bffffffffffffffff031d93f60f105899172f7255c030301c3af4564edd4a48577dbdc448aec7ddb0ac0605581e03ae96e94082f9c665a9a9b7f6b303fa097b51f1de5b42723c5fb44dad7007011bffffffffffffffff05581e0339df67ea70629138640dd9da3fc27fd679261a5ed2042474aa5feaa5400c084712cd0bc426c00005581e03cbf8b6bbea8c53f00e0fb58e1ca24db29869fdfa1a0b1ca084233002100c02471b5fd6500abfbf021973b80219ffff03e7428ac36fde822f7b83a928647fe466714ccf84913a6c5aaab7ac82d2b59c7b03d5f0df5e6a2783e918a037a9ee7fa2685115ba141d07037d7b5d46e2054dd5e7039f79a9182ba3b1d9aae33023b9070a638797d05e7f4a44729aa17849d5223a24035fdcd59a931ec1e91064d844c649073f657e4203d36eacb2351521e9e4954ac603af08685cc91bca4266afab1357e6e9f2f219f11bdd5e1261f3d98327009541ab03f8593ca6ef3232bf440abe37ff28edeba31191f213e5d4b2c3b5e1439decbc4b037730aa436fa2aacac76aec54eb6182c0ca79133e33d8131d4554eefe4d75ef1403916342879007e01d97b956f624ce44c84248f89bab906ad9c5b606db31c8588a0372587fa7fb363fd94c739e3143d15247587726f61c87b0d21223c25021bba3ed039481046e71013bc6b2a66cd833a256a6b6463c9fe29691a062792221433402d80356ec3acc0a875b7d4d1c3d8fad6fd9368ea37b473ec6f1e19b78bfe9688e599f0362eca503aebc0ffd7ebf4011dd3b5a9c0d801d14f9ffccdc1d19bfcfca7200c8039a28638fa0c05db0d14a30bc9ad3c7098402739c7e17b7da6b480c83e70b3734030aaa978382ea98c6ad771b344bb123f56ee7d5411df29e368eb8c0b4f38423d103059c72e2ba055b1657fd968572d1f163a911c55b7e10b7098e5394e265cbd79a0219ffff03edc2850f10888801ac80c00884047f5f1f9544044abfa9ab59823ad741a2400403f51b244fbd3f59b9a76bb6bd4e26ce99a5fb4a0356949d052bf8e1a61212e8c8035085eab9c1ea2fd2a560bb213b6d65267407f6db09f61e2dbaf4e4a7008279ee03540d382e5593c6ae6206d3279c3eadb38dca924833afdc4ca438a9937b4c27520337f218ee8dd919d2e018d8c42eb15735b7d2bbe0a74839a890c46788319fbcfd033ac886856797396af4c01108aa53ea9372d271d2b9acbc1330f74a8677e4c2030219ffff032299a13f697369a2b8c8cd261698d2c8787129b582643aa4a927107a7660802803bc910df7c9217db39175a9572de2d6e7b40223794d53ba7c243144bc16d2847a034c997b100bd7d9daeb847ef6a709d188e83d39df005cb3680a1b86ff656d718e035f53bbfea1cbd32e1cb555de9e02c8670a100a58dc33bd879f6ae31c83623c530379a28106c797704a0effe2026865167c4da61f6f36350cecacdbb4a552d223e203e92c82c1edb4bf407b8d4c88e76983f82c06025827a8448d940953d3e344ab9803415a787536042dcce97a33582d622b3c234fc3830a69c4c4a69f6cb5b747ffe303204e8dca3471264a5d4a52f7fc903b258b615b152237ea048a368e60801c8ee803379d91c811e24844e170e2a6b7eeb60f0bc974dd7770e1658ec9093fb561912a03478c6781a8b905bd91ba3277ad73fd8f441063ad8b4ceb471630d8f3ed66721303031a7750b7920766c5a83f2f2536cebac67ed4ac864e67e19b983b125711b4530219ffff037bf29d067ad70cfe53cfaca684a37229afe02b269a966eed4ed1468e28150b7403493e70d29d51289a7d856c5f354a06e786c281d154b3400e808102badc2388850302ff93a3d24cbb140307b0eef2cd8f54c657b10eafb610840855e336ff7d506603f4fe654f6a1f9d54fa6ded013deceb6bd263f60aa743472d43d0847671421b6603b79fdfee4d00a18b9b2bf952e6e3bf6e8c58143ff6bd625b85910f82c989e9a0037d6bab9c0422fcb453d57c21d8d125f8111f44e0e77e5b85292d811e9b1b49bd0308f33ccd920808d75225fdcc533b87d62202c3c57695853bb328da416ccef01303564a099ace8ddd129f8fa6ffb1424cf467b5c8d3e42ff0a86006db8c76ebf2fc0219ffff039f6c2732d7b95617cb8197e7e4002cc002c64b144b5845e9daecc07fd3339b5003bd2bcf581ad903637e4b18193b7f9be573ac1cc2937acab93bf51d6e988a42e50386494bbfb0aa70413b277552286c6f29bdefcd7272de4e0157a9b0941dfae682035b5b82d8a2aab2799f37814d9c86295de5931fcfcfdd704f8aec87c1a138b7950306f1364261d9ebf275893b908531ea5b3a477fab09954a136e867b35f1d2f0d303a488dc121ff72c6a93a87347962f56baa5d7ca73fe9d843b61bea8ad6abac63a033743dbd1d0c596f2a960a19a9828f1804b0f7b7bb2ea5e0cde398adb8ea475a603338a53de165699f747dbca896213f929be1829790e8375571f32bea3b9dcf80b037a23d8955e02626c2d9fd140ff89ecb66065fa37307d13a2e091a88d50be0da503fce5604a841794dd825a8230ac71ac0b39e20d50e06a76c5e01054fe236e7b9c039c401fe781b9c7c3a3c5404c3c4534e7efbf6814bf59ffbade4f69191cb801da033ddbe85c6065e70c8607c2b56a55c611d921fc7254fa6c20e3b0cd1d34e3e6f003cd236c04aae3f2c1d68481eb9bc358740d4bafb2ae08006ccfcd011d6afed38f031bbb02dd05d10628864a88112243e6894c305e535f73e727a4d52f403e143ca103819e66518a3856d1d846aa1d5dc35dc477febcf8bc0907ceecbf8cf79aa27fae03d2cf80a073783fae139933f1118b3464f00a14500d42dde47448e74100c6e77203de3c2a255b9b8bf74ee7b19feea80661889633fd0276b9628aec54d3d205b5ea03a7e6f134ee27e789afd55d643954b0eb2eebe8c7a371156db10b438eea837175039491a0c87ccc44b8836fab4b9759c5ccbf54c64f9a79aaa5b17d8c998631aa59036c9a03b3ecf60fef21257cdf8c1e29218b5803c7af18ed5144c1d117df3472f203933976cd2b0d1b8aa5f90a0f05897ea5cfef9719e26b44c045a45ba9c31f008a031d040db431c1fb7f7f1d03948d9460864f252e74802469844850a06bcbb9427e034cca4db733293a24d9ac26f272ba20d508028a6bd06f11de91ef40863e7e69b70303f8de524306c156a570cc25d8338c395c1d3f250c6f99895dcc60cdd2f682e3035cadbcd3d4e5e0653750a7b95e0b23a8d99528089bad926e1eefd78e42f6f5f3035e38338aecc7ac3cde429b7e3ca376145c5f47b227675c83aeddfa37730e7bbe0396af2dd1fcea069b5d7ec3c83208498da143d91ddca8b76a026484ed0a6c675f03a00b51ba6358fe3421a49355bce187b96bc2ee92bb024717c9308964ba93b1d60353f300e70b2a63b7c0d61d497bde74e0fd2dd87ba270bb896db24248e9f18752032e32d8311e84f7669859ab7142d248057171786d538b72b3b224f99390f5487303d3586623ba49d4a46a0d22a44c5d21975812461e0b98198bf602f91fc600bd1703ffe1b3a1a75a68ea426847a428f6abc359c7fc3f2254410c0b18bfdfdb2df636031b290c8af2c6b40f4413944c55645e780c6bd37db2397a369694b03519eb1b5f0374470a5751c368eff94a85f83d58ca0deb060f16bbcc115cd0f3bc5ca1c1c79103d0117d2b9071d72802a9a798fc10d91517b8e153afd593a7766ee3c6fe5a50aa038a461051b15d2b75df6c9895928735cc2614bad3c8fa997cea767b7f245491da031147cc7e1da5fc3970ff386d6e178e39e2e3f256ed9c5234312439ccc09bb7770381a1aa551415e356b298808eff24d7cc13f0e4a4e042531b704d0978dd348d9e033d8d47d0b3ab8f53caa5462ec26e18ffd435793ce80d454d3a0de3a66b8f4dd3037cc67dd0f9e0b3c1dec99b0a939a542f806924dd609dc79b15e879e374c2ddd603db1edd88da31b9173b3a01a4bcb70736f1fb7d3d7c4ff994859c9853af8d13ad03368a3deceddbf642196ed53eccf27173a264a7676dd248d202620423db656dba03dcaddf110217ee48bfb8eeff146af24219a51ac93ca67f522482e2a3564b460103304bf37f15689fcfc3757c0cb8a9c73e888edf17c9ae9b058175ae200c1a49ae0351b04acdc59d6d2676cb376513e6ef4e0e457fb7667841912288d0e20a2a80b303cf603aa4de6a946d150a191044c72b182176fb4021c6b2d18a383855a8f0234e031b5526d099906496a3a33d60306caefad770cd03a0881fe26d7f1a0de66b43ac035e59a8277f34b835258b96b4508bec0ef3922515e4142f0490770b3b2175569f03293dce157508c58129da9fdebc65200c1c1d983a8107e0f00d93b0cfe25af3e005581e03e61bc331fa2bcfdfbcbaaffe43953dd29f5dae150f901483658d263be00c044707b05cbe758c7d05581e03e98f02ec287a7bc81664c3bd3547c9c43fc30dd780deb035cc572ed0200c044683c5a2e4fd4005581e0301b4c5ac518015a0bce436357c6ffae80bb8291afc971ebe35544f24400c024501aa2450d005581e03fe83da1ccaef9d9bd78ce53543b6368d6df3947d410bf2d34eb594c300040205581e03ec48afe9bed2ccb5532d8a32fc0cbb2e1ba61c7556de5f51e397c4be200c01463d1ec5ffb1f0032df39c0062882ba634fb24c89e7f9871ecef10a6e6a6e79a336f6ed463d5c94203576d247b864a9c9c193245be5262a86aceeb51240a250d7b4354c293d83a678d05581e0397fc5d38badeb2417fc4af69d36e4965c61f33bb84f646b7d58a9da68007011bffffffffffffffff033fb0fa6f5661fde6378c4db73cf7f1243b922e4b55a3435735c010e477f1c24405581e037a436184a5fc4ce62ad4b2daaf8a45e35f2947e33aeb11416da640405004060219e48e03026455994a7764223a128fa757f29d61c41ac8e28bbab1f879c66e232fddb035036c4372246d91297c5eca3d8d1d450153f9522df23de517957182e29d8416b60c039c41360797c1aa7097a0a9af04c1f817bb5c454d0f772e8ebe2fd352ca7e0d8003ba5c25d3ffc47ad414f2443245ed620a381ce9d9f0d1ee70505a1db59120de1403c0b160d239ce0c71812917d0798c6964edb234491c2f32eb7574c8d7a16270d1034424e96e0a1731300a2559039dea0424a1e764dd8d7d15d03aeaad0ad4f3c4510219ffff036d14a8da010d275acfb59254cdcb9eae8cf5fe7b6ab4a71a2a36194c74b9a3bc03731ac71e9d419e6d1673e09527d61c4f7b9d634dec4cf33946a8ea03f67fa57e03a916837a533d8f341b1bb7fb0e55bcc6b3331f628dde4d08d5ac4d72b8da516703c20aaaece2b73a3a9adc8e7a8806aca1e659dcab0526d1437690766ff0b4452d03d21f43153c769cbbc6c78b5dcf8b9a5d5f139ab0d0f24fd215618da92a34226a03d152c22b71f6c3039c0923a13d58ba41db780be6dbee23526315a0a7cf0431f103c90bb142fa9e97c9d16aab891708825684b56324f90029d149c20ad2d236a30703401ae904b5b28a574e99197b56de5407bb0eef555f36f65acfe9f1819fa6a2dd03966249ae6fb9ba9de1c890a1de3ecf7da07fa01d7602346d1d2a3b0f829cbb750379694c8599c0f3eb1711dfed98e2e4ff393e83f1c8132f830c1298d0a76c182603355149d946dfa2ad5f816899256dbb60ca00eecff995689c2bbd7370215729a80219ffff03fa4bc73fd4b3e5ef71f2079c8c7d0367d6c5e067eaed4b34656b1926863401bc03aef7db107436b3c50134ad9bdba4ab448c0b3cf02a7e2b8c0c88fbdb4b0c2cb50219ffff035193acd8f798b1956536dcf7fe595d15a5dd7454a103659708015d2297bdbcef03c5619d369433bf9fcfb86dbc288cde781cab61a13890d23d8e35269f21df280603173e6adb834c681b932bab9925c8017b62ee314a1a96d5785219e76ed6e6fff903e34b9c0599f8c2ef3b96a881ffdcaec6e123973792fd3b5afa5ef14a69a289880219ffff036d601f7967997e14f0b8ddec3cd67d8c4758d5849ca7356bce8860758ee9c848036b190a0ac12310f42571c2af0c442bac657e635a7e06b05aa0139febf8aef0a003c8825581763941270cb3e17f04c73fd4484788da4848a24a6b4ac5c87f1e31840219ffff0219ffff" - } +[ + { + "block_trace": { + "trie_pre_images": { + "combined": { + "compact": "0x0103e13c370f7ce6f9de81dd4068775d7c39d1e19051063dea7b4f0dc570ab06868103940aa0f44732cfe96e03cc53d65cc93839531689de2370a6bc12eb515a75f6ea03cce25a8e8a0b65fddd641e8856e8baec7cd1a012b7470e92aea3d4f6fd02c1c80386d98f1b6d13f0446b302efc1197100dc2d2d10c9e7db46ec2a34d55b2f0bc9f0345a634828040159ea3390d453145c5e97f3406575308f3803fc63944db067cbf03cb91ecd916f16b6227a60215cab43c4b9a409b19cb9af242375517af5841ac5d0329a14ef57d4d2282d4f7479ea6d37f728b45af8ac42a621a953369de0b54535503c4c441ce0b6d427b5577cd15b9cd890ff95d1434d101165be1623b9cd1ef67b6035ffdb8ef07117acdf909af168d381bf8e503dba36547fe5895671e9d495add1c03324e1dcc50950c11240379740d5b383f879719bb1d477d04592d65471d973e8203f355ecc89a4b76dfdd7fa0f0318293854c502c03a74c0c4ce388fcd79835e0bd03bb455a53b6153bf3fb7e0fc6db20d7ef3661942e9e957af3b9120f91f755984103be0773cd56d7c9df1c8062a9c35b731b1359b4b9ef8c31a5c74c7286d96fcac103f7dde717fe02b995df87659a9cf25cc95f994360e2b8cba31db4183c14c9670203c669e29de38188ac1e9b8662b0d995e31e500883d08affadd4eaf71cb7ae43b603dbe9990a2e3a1b78a7e86924142e73d32e6a5fe8ff15f9d923df57f5f5667c02036a42738b24621de70cf0a5975e4e0de90c279602ed12d99c46e9951927348d1f03476ce5c4306a2f9875011e1b52bfad9af7f9eaa53a0c09725b641957f6e5abd1033a6eee3a707b54e089427a7df5f4d780cc9bf727b8527a2670b3e558fc580ebd037c900a244c50a273971a28d09b3e5448a2d3b92248efb447d5e914c83885d94c03cbb3c52e38138f069c39c31753b3a1e333713637c57dc2f38dade06d65833c4e032e9e786d65c8c00c3053a66aafa6d738b6029bd9b44ca911fadbb1b22bbccd3803f23a75947392a97153c9fabd3ebe5d109892d7ae8a4d3b64458d60fb5b0cdef0034c0924eddf164fd85e99748a48284ca73c4b2dc83edb9d3072551b3a6be0f94703bcf4e068ce6001b29bd880451c3388ef1c144cd79c727000046ef06b8b6beaa403b615536da0f1386f5dd58645a0955875bf7e4c00c8bf8397054268b30a5ca536038f67227b47f06ade1eb106d7b50bd823a327fc6d04556d011b98e4e695a6578e0328a17cd0c678af3871f0a5d3dfe41ff24967fbb7f11726dbf53707b5c27a5961036ce3286ec2d725cfeb234d4a29386ac1b057ea5109e381b59f5816430e82386c03a2d833f5221678f65bb919bab928e1ac2a21fb16c768c802385743c1f20f33bd0394ef837d0a9420521c9b61374d3433c534a22365b1ef1d34c305b1d4d508b17803245d8e881b569e0f2859d441687596d69a1f13172b721b798abdaa13cd61acf10331b17ef9b060f53595553fba5298848848045635c1ddd4867fb8834193531efb03b254caa5808fc83395b676f33b88eb741b88fbd627dcfbe735a4924fb3a7ef47032c53e4633f67dd8f1cd3b97857e2fa1888a08113bd4ab0ddd9f3f0667b75f31d03ebfe5a0154be1c4918681bb0b84185583e2381f1c034dbfeed740abe5a18a73e03e0414af533edb23b377e58da622436a7e1bc72da8005d38e7403e9c796fc8d7d0354fdd1dcf7c140089c99e10173a00bde1620974e76caa85b89c61386854d2e1e03a4fe5ae7bb248ad34f6d6c3cc546f3945bcabb9ea6832e5b2c126fb2948d37b0038436e7414d7bd51873cf6d66cd7100548bc241391940c4596cb8fd8318c9eb43033ef1123618d733594d9bde1b472f2695835b273426ef0e82d2901eced8db7402036499aa00f5cc368850094bfc120b9b888f16d1b2882fd29bae671a5c415cb83c03d27584f834991d48dd9d39eaf146fd21299f67af62a7f0f8c56f316b58c812f503c22ffb33a73ace056ec28dccf8f19920c8075a47151913e13bb76de334454350035bef61b2be3a91a5d66d92b259cb46bbe3a8a5866efac22f59f73f70174791b503c892127d3f6cceb7c41af2f17aa67450342f091879adb9acf3a7b5a8c1035ddd03556d3ec12e5343a2ef72db2e7ef381104279b2476ce4ae4a56b660b50347595203835ea527241d3ff19f15c1d172c75245fc94e1e2680fe5d2551b6225417ecdbb03fffa2540a1573b228956fa86248745aed31ae6e2c983714f32fd9dd7fc58c36a05581e0329328c87be9836183768908f30171daf16714c1f39a360b617bd23df400c0a4629546746ae0805581e03934cc196d443b1d2c5588f9fe55fa70f673923ac0eef128d9203835d500c0147c7a8357a0fa80005581e038bc60869ca403f3d41ddf8575cfa5be7785783dfb7dccc395b1ad999b00c01470d35cd2acec40005581e03d4abd7a5e810b20841e6aaf7fdba78949602e371cc5ff9aac8f74876200c054702c0246afc0c000458613373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff0155000358c5239b1dd310f0537a237bc27563369523c442d801ec8818db3671c74674540365826e2e7ab17befd18111567901f4d1c040051467d927e308ce4b248822a0cb03b1c225aa33368624bda2174d75e18cf62636a5b76c4c6e1b2e418d4f5b2a355f03f9f0c35958ba855fe3b67c2d3e281098a811faf015ff970c2ac1ed7519b7a9f90340d8abd4aea2178e472a6fb5d9a54ab7d9da7b4cec41de15657ae7d7ee47ee9203b4b2017ac478ec2fecab17810a12138dd142aed069ddb547bc6ca75f30d6edbe0306ea5daf16f46213f499aabb9937cce13f5fef58bb32f6e0cb3e9a06a0b51e5e035f11d8c8b2a04750588c9cb0a75e46b7e6e954dd62adfd624acf8da2643082d903a0eed321da78a0ebdd5fe9921d733518413a2cb187ca6c1105f7abd1cb2fbb1b0311b7c5e1e609f177f216cde5180864e5b646e33cccdacb581e3f588664f2aa29037d0990dcadece1c27990f6006dfb1b06731a2e38f30a794da8bad6323491555b03fe92a1e51e25e90d509a1d38bc6f2a9fe43e4f72047a2a24b870c8d30fc64b6703bcde60f65dd3d2ca3a2f2f4b0fea9276db48ca7062d6b0b6aaa3a0510d812fc003df6748c404401faeb51fffb775298fa91f0dd2b737b0f8595ddd8053103833c50367ba887e07b40051afda724bac2d9295be9135f1fabf12fcb95ef8762bbc5e54005820035b0fcf3bd800db1425c3b5cea82894f4bcd6c6e3f3681233ecaa3a21a22a3044663cc98f03a6b7d063b82dec0e83c94ca4a3eb6519dad637b47c2321d17ec61548a784c95703fbf5999fcabba0e2ed169e657630905ddaa06e1ff8ded70eec12494da396b04f039ebc1b67747b6a172ee99df6e8debb1b98d1bb2af2fd4d7d9233c6f86fb8bb9a03262f082f2f808c68b5102bd0ca743333881bcdf56c5b0caf594c71733ed0c34700582003cddb82bd7d6f65fe1dc37f3f335825dafeac204521b498c3035fca9d24671044663d7ae3034ccdf84f270e9fb07171b769d1737ca3cb47be3d33be2cd32aab896647a32da0031f8901f34af3b4a8bac1e2b5d52650022a64650558a0c04c5fb4599c094dd08f03f52e9a5bf4207d9cd90239f7199782d58a55fceb3895fa2cb4cc9542d2a5872303e3af39c59d84c6dd4bbb43417edf1e939070570d233bb5a1b7198c4d67b1567100581f02b50381a466ad9b3f5753ae9d99dc1961955a9450f39a54fe5ed20f23703944663cfcbf00581f02d7283d573d576507b2896b999a1774e22aadc14d202294c19a0f513a12fb44663ca90700581f022d34ba1eaa86355c866834b1f968ae22db024991e5f42a9a675196c9459d44663de78b02198410035714c53a00c63f7abf88010b7d8641a6f7c834c8c8b6ab07d1304165dd133e2603fb951dd6719d0846becbb4d55276d92a7a77f6687eba589ff57bdfdb799fe4ce0328f4625c2a4b7929cbf0ea4f0b0e3459896a55de82446c8997a5856856abffd90391340802d5194304e8a2f4323c84da7647cf985a3c6d2a1ef795dbd8a53817800219bfff03576e9c67f4913b6dec689d445a1d0cd79a90fb1c9a2f0c87a2ba84d25171980603d924cd2dd4d5362c29bcbb0fc4f61e17558e556aea838c8d09ab96901ba11fe3033517c66a20bd6abd4b1ee2e5cd45298563b00fd979ad371af28dfe95b868322a033e0ce59b58426d4eb6806b518e0a18ab4d6ebeda65951a616eb9c9c917b983520219ffff03387f24a9b1c6be19fad5f890ca5c502479f798fa7a51ebdf492ec7752bb784ba03244183a300b82435242d6e0b450a90dd10e49846e00e2cda1481f2cd6893f04b03356f09c737f039614e52e2e0c37fb7a70313ace29b2154d9114db788f0ae541a03ad4ee9196fcc075e9a4dc9fd2bb8c75338dab17003f968d73480b3db389bf9d7031d739c09b9667fbd3f6ab1ef093a08e2c5366e1d6acda970be8d1c3fddedc2a2031afcc8c217d91826795f263a1f09fe4f6130bbd58deca5fd7c33077375fa1cf5032a8efece59d3d07314f21a48b9d4e15cb7c42c0780b4da74b5713427a941530403f0804f578891545342627171f5a9f2262c76551c8dff1d87e1d4f600e0dba569034e67010819e15c986e4fc147d9d699936155d18e88644fabd15bd556521a9b7203b6fd210aee5e046b5053ad7132b8c705eaeeee963dd7c5efe7209d356667b6e803b5c9b2a4290b6130b89889f84edc7688e2d8893cf6671a82834411fe9bf6f27003ec93db40b8df894e80edcf0202cd39373099967eb1e75f285e6cc998e7b5b26803692dcb502f53043296639d68228f69c17e83663caf8ab3cae97b1674befdb8e103d5c209b220a62d28e6f24c89d7d6a37de20b3fe53afa4e39f71b5759f4d3e4c4032a92f5ace6d6fe31425c6282bbda8e84e2db0345cf5662433266e1bf343a12b403e12845abeacbb0beb485495bef7d65bf834364233fece312a49b576ff3c340bc03e6a66e06863ede15f9f2aa3ade52cec2ca242e3f806c40699a40d9284bd88b94037d6889baaf1cc4d81c4acd0d0e884c685ffb3a2d29cae5b0727b2ecd3e8d8dd2035982174780f06ddfdd84d88987dcbdc69ae1279de27976eb62d2e5abcfa1c18b03e13a3a9a9b2880112b3171f114eed30dcbc533ff7d56980c8898a82d1f4482c7030e65acda5fde29d1cc76bbd3ffc17d8acbbde66d3e68b6a5d9dabe5204525aa103c5a998297fe5c2766bf6dcaa34f57328a1961bbb6140e34f22155c4904bcdcb203f53af6a0b865c4f515acc6d3c93000e2cddc6f8e8d961c2672a7629d1907994303a7385e81588f6c99b53022f433eb8c00efc96fa67eb6331327cac4b1a07d7585037f98a226ecdb56c0d8651302d8b22d2acff8bc0eecd3e3213753028839c0d92903cf753d039db5e738d1ee967356555d047215a4bb82aab79373c359ff70a4478b0379a542cab81cfd9d454470df1e42547fa517c5d3120e5bd3730d98934b64e62603a05dfba906e040db50a0afa79a425201f74146354d88c2754c5821eb719b5190037b2a32ea2c5f26a8552a7872f3ce840aa447e7d9c0efb0b03c99d131161b810903f7b2d22dd98c0f407dc59ac30c13260cec6e6565f54b9d9b09c4bf1bff9829e50371a35ec266c88aed1b2897e3f36a14cbb329816629742399d38ccf05c541a55700582003c8baf22843559d0eb809346dcfbcc9cae4248ce96c61ee9416d0767869af1044663d262f0395451063870cbebd1a1c414c042a67a269e123a33eefbe12795ca4533cbee4210377696199292b63a284658963a209f97c039eabfad945fd77a3e3d3a2431baf2f03c62ede3a485a67f19ceee383b691bd1579d726b72f36bd25966b245d2658c99f0362f50e33577871564dd39157ff97bca77d58296e10eaf771487a741d7c032be10364f74730c91f576090ca0ba7b1e956e4003c70aa7dd5bfc978ce3110ff013e5b00581f0238787dd0e1f5d8a1615fa968dea23acfd3d692e6fc565b123f7caf145eff5820fd7d812e8f33936363ce5cc165cab273cc02af6aee8da3d42370f4d343c6f1fd00581f031906aedc23fbbf5f6acc171555cde2fc54aafcf7c2b796b6c9407434c47058201dd3ed11c47ea3f75e3c5ca68e5ab9e0bbb7ecda2318917c262c27a9af24af1900581f03c2e6f82dfc83b58e2df5c82f2a4b3a3d5a9f68fbfa24b96adf46c00deeb044663cb0330206021928000219ffff0219ffff03db912c435e75072736adad652ecfc4673017ec25347326549992227d294d856b0309e3878fa9a66b2f5cc86044daab843416ab5977bcf6dc06b760060f09ec7ca2037c1fe9f58ca2408965a47d402563a15f2fcba83a4a6eb0df1b030da50ba63a4b0219ffff05581e03a92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e4200701186103efc4a95eba2b0e8ca1e7050522d539fcaee456a9baaed13b74846ce6e9f5284f05581e0387d430445038d44dfe5f67a6a29f0e61137f74004f19eb39df5f6507100c134622a41c687260035a6395100cee14b8619641123268781a56f8d9fad4ec4d364c15d99ab81fd8650219d595035d3b51473708235f94b7786ec3d9e1141cffde44d1b3c2f8f50735bc89406bdc0219ffff03bb6dce5781ee1b12b6667ebff1c6a7e00b27f644760beec12fb305c5eca6748703c2659bef399996280795da7d7c6cc8380980b8ed448477279fce86cb1439815903e45da9fe37e969afed21a06e65aab4b8219e0be940cf6c2d96a3312dde77c4ba03cbeb42d91012dfa7755211b9ebebc4e6481daa53edeaceb78a26478589a5171c0355e8cbfb54708ecacf62e4d8e82d3ac23e130e81a0a767b06f854fc5a76a803203d82d3c30bfa72b176d2285d259d03f43e6dccdc50cc03dff946f850414c15e6103b84ba827a5ef576ce6db5eeb236b0b05db5fa57d18448dfa5685297e76ae072103494925526b1d70068072e28e02df4d43941cfb627cfba3d4a148a8e857bee15e03cd19d837ba2dea15c076f66e441a92c62e7c353901568a45ba5f71e66e88d278038ba8c6e5027ea75be5cc7cb96300e7201167a07c4c58fb06c272593502d8b9dd0219ffff0301b8e895dbf6e8f0d41d8a44ae0ad8221319b0b3232425ee213ed801d9eb9ddb03c15636c31d015e3f4a306e5ca113823cf9bcf4422ae44ce6f9833e1a4c92a87b03e4b068e19c0ce1a9cde31af1072d04cdbe5f101a86ab598ee61a8ccb7a5160e3035131433214d21d38997d1bd4d2eaf4a69ee15f096d86344b920532b298824fc303152393ebcf209df305d93fcdb3ee0d6c9c28dd5fed398bdaf44f0e32a1253e5b03c28ccc74e893b02e05d6d5447b79d210a80b3799c658e3b2cae91e64f250d52d03b663e80603c7f9bb86ac127aa3726183321274f5675178b609500bd338fe6689034a4d70349a6c431a3edaefc60a1dfa6f6151ad5d23e0028b924ea72676af2798033122fd4a0193f4e18ca6f08a697c0af1c8141e2659da5c6bf0f87f5ed86ed8a70219ffff0372acd27b85056450bebd86a5a917527908636a4c781fa8535f6801d52643cbe60313561846f55baa646d462f32f65378782aeb0a36ec4396cb1dd95ff2e32a73cb0219ffff03ab23c03eb82757b5599c6b1b720f61171157020454f78fb6fba98f50b444103603d21ed651d18455a8a402320838db0c0ee34b4fd81fa20fdd9fe9bc10a3f6756f03f215f60497fcb591efea4098f8e6d1af4260aa34dd0143154ccca2ff7a41371103390f008988bcb2194db38af1ae0c5b450fc67aa63847c3333631236ff160ddca0392da5bcac8b88fa85dd08f6c0a2c54c8049826621e9de17655a5ac61c896422e03361866a4d5b1c4ae521a022b8d0bf3f956cce9f4558293865b4e2e09b679f5fc03c1204c89e187aa54d5bc785652301c858d3b8a3a271e3b29970f5e35b03f03b003e5d334dae613ef68f86ecc885179c64542cbe18c5b4aa3bd929eb6d2bf5d9e430219ffff03001cc050182fb06ae5d5cb21738d6a93d6acec33ffbada6bb24116265a0d418203eec4298676ca64e441552ff83659d2575ad601c6fc53e98344ea7882a0fe1c56039d6b1ff6c754633d01894c26d7ec56ad635490dfb291b08544cfd1b5dcb0cec3039a6d2abc2c326ca7b11f03d687495f40353542ae9b674bb6c59bcc6956657d8b03fb6d16aa3f68791b386410f1c544051aef6b48a87517f2c2f60be6c45290bc0b0364b38a393150f0aeb5df8ae532cc8017202732b532046e192a385a1df530a1db03cbb43ac36287287ae24670a0c4214587495798add9fbd6124993f2c6629d4236035826a6a848d733844dd5987c128e28517f21bc7c06fd3742853a2d94c29cca9203f49b340fc1b72e277463376094dbbb5ab76de329e2984dffaa0324bff1cf9c6b03c61f7518e360642bc12298499dea86e091bad02e3082654be4ab16ade8be481303f3cc663d1119c0e755090ff50e657071c1b7aaf76f5049f8f68efd2fb3e2a76c034f1fc53876be73fe021b3da55b1b841bb1e1d6ca99a8687da7557638ec4021cc03152f541cf3ee990146f63fea1d9b53abaf06a58b00a2ab68fce1c0f5b0c489120387f624a5927e67b0a59632a5850f1c011737bfa8f3a51c937ea14a3988e066b20323c3238b822b325fe11ec12c14e024ddf5710d8802e5c85d1ce7c47fb02a52b303fc2eab2d511cbc973f0716322261116d8b81f804ad73d6d380caf1c42989ecea03067c67cd91a2747c892fd1605d2a38c8b7409376e9684323b7694d359eabf9ff03097bfa2378565be66df31046bbd59bd5d4c2c14299553cc58d7ffb2ecf27699603ddbec2457d43fcf01730e63a612c370b56bb911de1147bd597396fc1a116484e0390b76a69782f11b0cbea81d1398b41e2927e5edb66688c6c728806bce71839d103a1d7ebab554c080cb2fb2cb17667e3058fb3537ed64855620f7305c9c45973e5039c2e37751faff09ddd740b02db8f0ee80d92547b41e50ef9a3d2113bdd2f18be0330ae1b86a3326ef4be28d5c2470d8808df51f32e5399ba760da826f46905959f0362cad7ebe4f2c9f7e283ebe4f1ca1dc9e8e7c8bedc52ae4bde537a19beea1f1d03d51b9c0f3ccf30ef1a3754573521f05d4981b3c7a0eb635ddc81684ab75fdc9b03766ce30501987085efd915e492dc68eb6fb728a13070605c762c2dd8fc65c9e503d2be7794bc7b7ac3eff3e2bf8fca6721b9d7304e19263153d566066109a5fab505581e0329e4cb84257f3a85c60d49d690d86c942f7d8521e57336a24bd99719500846f84ac406100005581d0231e95b8419eb826136a480dd3e995ed8b16c389a83abf73d262522a80c18434a049d41dbc0f641387da905581d021defeb7d022491798adb92c6a0e0bbd3e028c60b710678fcfc65bdc804030219804003f0a45890bea7520a3246b1d46241f5163e20df1f78ed7ae79c9d2c9c8491ce5003c33fc0d0e68fbd9b43795a1d49f372864646e80a64b2ae991aafe78cf1a6b8bc05581e031342fc8a455f0bb4cf1cf5c8d538e0fc58be64fa5515ece1f52ab69a9007011bffffffffffffffff05581e0377c6fb8536ebf253b0a73a57f2d3dd389ca68dd09a55ca7c9402700640040105581e038548cf6f40bc75e6b2075230a9b50dab25ae567bbff331319892bb76200c0847207fdc31f0253705581e03e43b5623140b81b5788ab27a9b607435c29e998d62d7041c02aa5497b00c01461e55d5526400035cc05631b99585c07c74357703c6536ee955b7fc282ec27b997ee3cfc7db376305581e033e96d0303c74a5f280436b54c6cb65c305ca1432bce3657a886fa885600c0147c9ac605151e00005581e037248f8cba499143afad86bc4846e956fc50ef032d10256e551b3342c900c0346a2c1a8265fb805581e03448abfbdc1f8a978d1266c4e652cfe22c375ac38e7fec717c83e4464700c03472cde779ad41d6c036657955b7ec60ccd679d744e3007ebed64a1c193b1f5e6764e5fd5098f5621a4039be7f2f51b6926d91717387c7d931554000322f401f33bae238c59d16d1bc25005581e0304099b4cfbfbef7c9241d66211f3db31de111d250cb83214371a03052007011bffffffffffffffff02197f550306084be24dad54cc280b3bf8ec4aaabe90ef39d4a1d3de01361e7978f2333e6203d070f1709a1cee0cb0f2dc904d4a97ba0bb0899e7c44573daf0c4481dd0acdef037256332e427d6fbea337f43cf2cdfff5aff4243af95cea9f189e5215b4da468003a8eba973b3479f7730e2da45137f7025dabe1729a91367de311f71e2a554b9d2032388595cefb05ecc623b4644216fa13da46bf66e0af30c3b6e61e980e47f2f4103a78c06eacefde35ab475592d485471fd7c9e2ed061a66683db9e0f938c6eeea103943cf313af4d005afba941eaf012b10bfeba6c27bab6a5b500e7a463beb4ce8603cba2737f18889d6a5f490126166eccf6eeedf56294b6983e211caddd1e958bde03e70c0538d263e7153e98080dd8651340c6a16c61ccac8e72faf62b3ce0035e6c032c7f02e20d33992e4a756861c214ea04fd1fa5b7d7b77054043d909012ce7ca6033ddebcaaabe1a9f3fc9f1433bd43c275272753ecffc73f4dc4d147106d060bac03501228901ee23e51b629d809fe86ca91b857ebc264348fa6711d52ec799e49790380856eaf2c16d41b7c5851c8b8be074e2796ef9fc5a51af35abf82e7064d8ce70219ffff0373457bb1c2d5a37570f05f98d0da6ab88337ebb1dc5e69d8905424c0d25f3ff5035ceaa6bbae7d847a6b71ed0fd91751f23a221217cae0cca560eca0ed46641fe203baefa85ed25f86ec38ffeb1dce60e649268ec9253f9ea2e937177cac4a32c02103deb8799b4ebd27039932274a35b805633be4c4f9e316424c0da29cfeee1413500319e279bdc8e991271380be21083446fef85aaae12b0ee658bd0a4472344497c80379866774e2f225b8895e3de8a034c7cda76505fe5a0bbf4cee28bcf03a50889103f207db476fd76b340723f6806bf05778064c1b8c03ea762913042a6deaa83bf00333ab1293f95b00e0f5ae9eb29d2b2914f25a9e927ff18e077fabb6ed05166bc00219ffff030f435593b37c6e7e90456965f90baccca95ec22f18726dba77f969586563319a0374f3bf4b37dfba97d3bb836784a582d5387f1825e37bae15cef044e4a400ee10034224592b2898cf17dc3412815aa5d8306aa68fd1897203f32a00fde6614829db03b141e7b53041cedc0726c3eb62ccf7ef8594064490e280a085412683871b3de0039ad73dd089132dd4797a0dbd3bd6fe1b098c46f06c517ef1e3797ef8c51dd21003c0ccc062063d494691863a58cffb0498c3ecf32b97fe6bafbfa4f91a3aadbd3403ca29993c9ab4769f55b1668e25e86366c58315e90c52fbfbbdaeb966f0d8213a035444b46af6e3c94f584c4dc0c7cee1e3e9750071fa4f648b452633be27cee3d103dd0ef771e03aa7643e5ebc78ad7ba2ba6e3029f1f54503f575b2c1db3a50484e032fb5287c04e5b0440901877a095a4a433cb704c875ab4faf5d92b890f83a368d0342182d0d0b8b8f6888219bb5441e71ed5247061f247b3e5beb05256fd437446703057fea1a0b7e2ad1b0dac40108d98219a77820023ae54f6aa1ccd599dbbfaf90030bcd00ac7b07c9a993662985753fd20a5ad28aac1cad7cafef8550f76d905bf003fdc6ae80d23be595b425ec6bf90d701c20aa537c992ae517519c1c9132cd35b00219ffff0300b3538138548d8e7cdfe3ec6a0649346cab821a2c12759a2d5c735ca1e2476b0397f9be41801a5e5cfb6d5aa5fdffc062a433b8403c000683a3eff412dceb69d303ae5583703a0d55910f9e00556ed1e99f16b07e48d8063abec2fc81036899731f03468233cd70fdd2f93ca606aacf821aa5b529aa0b97ad5fe3e01834e23fd2f26f0380dbd6361c810ebf3dd1e1d37a2708bacdbfa655f6f65184e4c77d428d18dc65030fa56fdff3c568c91864f230f49dcd3071208ac2c6f2e9f436777e5f8a3600060394e5b49d3a8c2f88390491996babb83300f840ef28c6dca64d89405e3a39151d03a8be77041b3cc39fd155bb8c1634cbc4f642485cc1bf74edd32f8097e2b9d1d00219ffff038ee69e0c947fa4b09f8d38dde182267b4c4a16ea0242c041369619c16b2f69500392065172c22a81711d3803081e2c66a408e0727400ad2d83ef13c570633d5fea03ec289b0aa52aff14cdb4d22bb62e330c7e4ca2aa611ebae50902c190e9d0c99703b5288a3d3514fb46b592e754af10a92ec8a7f724ce0c27d8406956ac2b8f5b8b032f969988b3bb0cc7bbc443189f47d5900025e8f77ac5500ca95baf449e774c7103c4154482e9e26c9bc32112b211d4717775216f694bfddfc8b269ab56a46205d80219ffff03b86aaf4643a9522fbbae67b172edc7c77393325dc9c31c9a7abd8d3994c847c20389ee3ccc7222da174ba639ecac039602c3e717952af7296b84ed11b7e6f2309e037361d147efae615395c158f88bca05f3945005fc43a8196e1bbc97e710e13cd10342525c1620a81a4b104fc85e3694cb32589489ce2ee5b01cd9e6ab14a5cebace0343e3b06cddcce5e0f06baed99ecd0a20775a5ff0e74cc5a76fc9c61010739764038b6c262adfe371830f62289777185cf8f52217c9f95282c67923a377eaaf86ed035dda29994853c06889e9e46917a2a0f79400940167b9aef146ab347b55d664e703559ca936f787bfb77440659de1c59ad30eeb28eebc23817b843ae74f761b283e036bb1e58c1e30f46e3e5a17c627b36840ce672a1b4576340c39a5ab5614686d62036d4da0172d96c64cf89c5e12666fff5bae46b070b11ff34d32f8cd1e16c31de903b51b58d1619a46dcfc5d4137a3842bbb299e2fc846ce6f87bbb6b89c2b0b9bdf03d913f2df7f405954f7f3ddec0f6b6fb418ebc3eb0748d9195474e98518ae658a034f042a8a0a48b51cab19b6ff5b512e2e54ff0bdfb317f28c999791cb204411d203e5d907601a73120bae4431e4d05d07deb1eb429c219bd2219dcb26e71fd9affe03cb44b7d70efc4351355d73d14d0022521f1b259212653ad83e8baa6cec3c23d3031dc993b12dc23a0d67244bf463405d99e8000baa1fbd040708ceeebc4fdbad8203d0d7e8ea93789f0857b17f2698c99009e8d53b35eaa1e292ffc9714d45adc00d0395d70f0441aafcca4c87ba0a8e565313ea2d2b242fb3cad8d04d6fb8483fadbf0355534b75d3c88e148d52a6870f57d19ba0ac801caa76e08476b90b48d7245a4003baa59f2afa6444c81244b86b7c9dd17b6edca4ac623a93762aa2db74658760e6035b375faf8e6b683046c729754d73b5625199502802051d1b437d400cdcb0cf3603600009c54247532fa59301cf9015172f85b4dc83cc238f70df51f73262af9457039fa254060036f7f90b710bc3b17f2d866d5db4c06b175506c911246f6d0b50f5036d5b2af4df1e2f292bf9b5cb4e13e1ceb528bcd8ac00fdb041d777a456ae4d1b03e7f4e78e600c3126f4691665f1a867963f504bf7e5f272f7e269369d693885370390d6c43e3dede80b4c6759b42c5d330666f3fd1146f164889637c446b1673469038dd25b7adccd1a9cc378ec8a4c419b593e30f6c853535e3f3c3a7675d9f5948a03b6c688039c4174b06ad78c703fba69d3145fde491a9ea057e0ca4689ce6f083b03b5591bb5ba5bd4c45ac3827151cc5af7614e1bfed7809196971af8362c19490403c07c7fc3f92e6ebff32d0fce3961aefd7ee2e0c5090158ee8578d094fdc5b2e203e91b66637cde3244ee7a4f3c0c6f2d41e5d3d51f2fb161fde3b3e46a7be5d2e403ca6f9f545f835eb201a5cb3a64e0c6a3d382ec7bf5bd18d1ee4128de0d5c8fb303d2ff40753b8f0eec2e458c6c092129ecdf4da77d7b95645ca1ef6a47af7135db0342225c8b5558e35ea88dec62a89b5dddca727ca654cd826c19ea4bacc6fbeb6703b0de12445e9e9b542fa6214e604fc40e069fb71044af40d50d4b741d3a3791eb037d577156ee5867430b360d6a12c062c7557e0fe2cfdc2c23c24675676109820603e4abd4b3b402bf6ca99131af823b1f1e3dc564cd30eb6b83aeb4e6295678639c033c1f38bb537c6eb904d7f5068a749092053dfb9fd8445dd54829d6f8c96024650380fecf187fecb501dd15f7452fb9a5b4030af29bbb400caab89d9fd1f6b32d0603bdedc66f85c076c34ddaa82d7f153bf1940a7a05e35f70ee5078634ff0d9529f038c72a5acd831a0cdfd5fa323c3d53bd217dd034aa9adea20652ed7fd52458f230337e3badcb7453ce9eda24b15ef374242c727263e5f5d002190cd5723f3fa6c180380ffb85a7a5817f873341fb95fadb78e688522800706a26f5721c9cc29cd9a2005581e03eb820fd1bffc460d328d2689d7ffe13c4443877bebf25bc3502cd345900c02472427ac4344f80003a31f886c62f676187a889179625c86a313cef7b2e41693325ff5e023fba049f203508d78823e3c33ea99a6bf0627dce396125c96e0f94f2752a9ff05880c38b1dc03e886b0f9abe462c549e7b057256e86d99e81713aa80dcddbafc8f00a0c3fb71605581e0371534472d82ce1ddcefa8a9a081057fa05952073b437c3327c830078f00402031f8b67be329f6419c9282095843235301b6b3475e42bc9e3262b646aba8072060605581e03f53b77d7a78435e2976275d2001631a331bf18d7cd453410c7e13373f007011bffffffffffffffff031d93f60f105899172f7255c030301c3af4564edd4a48577dbdc448aec7ddb0ac0605581e03ae96e94082f9c665a9a9b7f6b303fa097b51f1de5b42723c5fb44dad7007011bffffffffffffffff05581e0339df67ea70629138640dd9da3fc27fd679261a5ed2042474aa5feaa5400c084712cd0bc426c00005581e03cbf8b6bbea8c53f00e0fb58e1ca24db29869fdfa1a0b1ca084233002100c02471b5fd6500abfbf021973b80219ffff03e7428ac36fde822f7b83a928647fe466714ccf84913a6c5aaab7ac82d2b59c7b03d5f0df5e6a2783e918a037a9ee7fa2685115ba141d07037d7b5d46e2054dd5e7039f79a9182ba3b1d9aae33023b9070a638797d05e7f4a44729aa17849d5223a24035fdcd59a931ec1e91064d844c649073f657e4203d36eacb2351521e9e4954ac603af08685cc91bca4266afab1357e6e9f2f219f11bdd5e1261f3d98327009541ab03f8593ca6ef3232bf440abe37ff28edeba31191f213e5d4b2c3b5e1439decbc4b037730aa436fa2aacac76aec54eb6182c0ca79133e33d8131d4554eefe4d75ef1403916342879007e01d97b956f624ce44c84248f89bab906ad9c5b606db31c8588a0372587fa7fb363fd94c739e3143d15247587726f61c87b0d21223c25021bba3ed039481046e71013bc6b2a66cd833a256a6b6463c9fe29691a062792221433402d80356ec3acc0a875b7d4d1c3d8fad6fd9368ea37b473ec6f1e19b78bfe9688e599f0362eca503aebc0ffd7ebf4011dd3b5a9c0d801d14f9ffccdc1d19bfcfca7200c8039a28638fa0c05db0d14a30bc9ad3c7098402739c7e17b7da6b480c83e70b3734030aaa978382ea98c6ad771b344bb123f56ee7d5411df29e368eb8c0b4f38423d103059c72e2ba055b1657fd968572d1f163a911c55b7e10b7098e5394e265cbd79a0219ffff03edc2850f10888801ac80c00884047f5f1f9544044abfa9ab59823ad741a2400403f51b244fbd3f59b9a76bb6bd4e26ce99a5fb4a0356949d052bf8e1a61212e8c8035085eab9c1ea2fd2a560bb213b6d65267407f6db09f61e2dbaf4e4a7008279ee03540d382e5593c6ae6206d3279c3eadb38dca924833afdc4ca438a9937b4c27520337f218ee8dd919d2e018d8c42eb15735b7d2bbe0a74839a890c46788319fbcfd033ac886856797396af4c01108aa53ea9372d271d2b9acbc1330f74a8677e4c2030219ffff032299a13f697369a2b8c8cd261698d2c8787129b582643aa4a927107a7660802803bc910df7c9217db39175a9572de2d6e7b40223794d53ba7c243144bc16d2847a034c997b100bd7d9daeb847ef6a709d188e83d39df005cb3680a1b86ff656d718e035f53bbfea1cbd32e1cb555de9e02c8670a100a58dc33bd879f6ae31c83623c530379a28106c797704a0effe2026865167c4da61f6f36350cecacdbb4a552d223e203e92c82c1edb4bf407b8d4c88e76983f82c06025827a8448d940953d3e344ab9803415a787536042dcce97a33582d622b3c234fc3830a69c4c4a69f6cb5b747ffe303204e8dca3471264a5d4a52f7fc903b258b615b152237ea048a368e60801c8ee803379d91c811e24844e170e2a6b7eeb60f0bc974dd7770e1658ec9093fb561912a03478c6781a8b905bd91ba3277ad73fd8f441063ad8b4ceb471630d8f3ed66721303031a7750b7920766c5a83f2f2536cebac67ed4ac864e67e19b983b125711b4530219ffff037bf29d067ad70cfe53cfaca684a37229afe02b269a966eed4ed1468e28150b7403493e70d29d51289a7d856c5f354a06e786c281d154b3400e808102badc2388850302ff93a3d24cbb140307b0eef2cd8f54c657b10eafb610840855e336ff7d506603f4fe654f6a1f9d54fa6ded013deceb6bd263f60aa743472d43d0847671421b6603b79fdfee4d00a18b9b2bf952e6e3bf6e8c58143ff6bd625b85910f82c989e9a0037d6bab9c0422fcb453d57c21d8d125f8111f44e0e77e5b85292d811e9b1b49bd0308f33ccd920808d75225fdcc533b87d62202c3c57695853bb328da416ccef01303564a099ace8ddd129f8fa6ffb1424cf467b5c8d3e42ff0a86006db8c76ebf2fc0219ffff039f6c2732d7b95617cb8197e7e4002cc002c64b144b5845e9daecc07fd3339b5003bd2bcf581ad903637e4b18193b7f9be573ac1cc2937acab93bf51d6e988a42e50386494bbfb0aa70413b277552286c6f29bdefcd7272de4e0157a9b0941dfae682035b5b82d8a2aab2799f37814d9c86295de5931fcfcfdd704f8aec87c1a138b7950306f1364261d9ebf275893b908531ea5b3a477fab09954a136e867b35f1d2f0d303a488dc121ff72c6a93a87347962f56baa5d7ca73fe9d843b61bea8ad6abac63a033743dbd1d0c596f2a960a19a9828f1804b0f7b7bb2ea5e0cde398adb8ea475a603338a53de165699f747dbca896213f929be1829790e8375571f32bea3b9dcf80b037a23d8955e02626c2d9fd140ff89ecb66065fa37307d13a2e091a88d50be0da503fce5604a841794dd825a8230ac71ac0b39e20d50e06a76c5e01054fe236e7b9c039c401fe781b9c7c3a3c5404c3c4534e7efbf6814bf59ffbade4f69191cb801da033ddbe85c6065e70c8607c2b56a55c611d921fc7254fa6c20e3b0cd1d34e3e6f003cd236c04aae3f2c1d68481eb9bc358740d4bafb2ae08006ccfcd011d6afed38f031bbb02dd05d10628864a88112243e6894c305e535f73e727a4d52f403e143ca103819e66518a3856d1d846aa1d5dc35dc477febcf8bc0907ceecbf8cf79aa27fae03d2cf80a073783fae139933f1118b3464f00a14500d42dde47448e74100c6e77203de3c2a255b9b8bf74ee7b19feea80661889633fd0276b9628aec54d3d205b5ea03a7e6f134ee27e789afd55d643954b0eb2eebe8c7a371156db10b438eea837175039491a0c87ccc44b8836fab4b9759c5ccbf54c64f9a79aaa5b17d8c998631aa59036c9a03b3ecf60fef21257cdf8c1e29218b5803c7af18ed5144c1d117df3472f203933976cd2b0d1b8aa5f90a0f05897ea5cfef9719e26b44c045a45ba9c31f008a031d040db431c1fb7f7f1d03948d9460864f252e74802469844850a06bcbb9427e034cca4db733293a24d9ac26f272ba20d508028a6bd06f11de91ef40863e7e69b70303f8de524306c156a570cc25d8338c395c1d3f250c6f99895dcc60cdd2f682e3035cadbcd3d4e5e0653750a7b95e0b23a8d99528089bad926e1eefd78e42f6f5f3035e38338aecc7ac3cde429b7e3ca376145c5f47b227675c83aeddfa37730e7bbe0396af2dd1fcea069b5d7ec3c83208498da143d91ddca8b76a026484ed0a6c675f03a00b51ba6358fe3421a49355bce187b96bc2ee92bb024717c9308964ba93b1d60353f300e70b2a63b7c0d61d497bde74e0fd2dd87ba270bb896db24248e9f18752032e32d8311e84f7669859ab7142d248057171786d538b72b3b224f99390f5487303d3586623ba49d4a46a0d22a44c5d21975812461e0b98198bf602f91fc600bd1703ffe1b3a1a75a68ea426847a428f6abc359c7fc3f2254410c0b18bfdfdb2df636031b290c8af2c6b40f4413944c55645e780c6bd37db2397a369694b03519eb1b5f0374470a5751c368eff94a85f83d58ca0deb060f16bbcc115cd0f3bc5ca1c1c79103d0117d2b9071d72802a9a798fc10d91517b8e153afd593a7766ee3c6fe5a50aa038a461051b15d2b75df6c9895928735cc2614bad3c8fa997cea767b7f245491da031147cc7e1da5fc3970ff386d6e178e39e2e3f256ed9c5234312439ccc09bb7770381a1aa551415e356b298808eff24d7cc13f0e4a4e042531b704d0978dd348d9e033d8d47d0b3ab8f53caa5462ec26e18ffd435793ce80d454d3a0de3a66b8f4dd3037cc67dd0f9e0b3c1dec99b0a939a542f806924dd609dc79b15e879e374c2ddd603db1edd88da31b9173b3a01a4bcb70736f1fb7d3d7c4ff994859c9853af8d13ad03368a3deceddbf642196ed53eccf27173a264a7676dd248d202620423db656dba03dcaddf110217ee48bfb8eeff146af24219a51ac93ca67f522482e2a3564b460103304bf37f15689fcfc3757c0cb8a9c73e888edf17c9ae9b058175ae200c1a49ae0351b04acdc59d6d2676cb376513e6ef4e0e457fb7667841912288d0e20a2a80b303cf603aa4de6a946d150a191044c72b182176fb4021c6b2d18a383855a8f0234e031b5526d099906496a3a33d60306caefad770cd03a0881fe26d7f1a0de66b43ac035e59a8277f34b835258b96b4508bec0ef3922515e4142f0490770b3b2175569f03293dce157508c58129da9fdebc65200c1c1d983a8107e0f00d93b0cfe25af3e005581e03e61bc331fa2bcfdfbcbaaffe43953dd29f5dae150f901483658d263be00c044707b05cbe758c7d05581e03e98f02ec287a7bc81664c3bd3547c9c43fc30dd780deb035cc572ed0200c044683c5a2e4fd4005581e0301b4c5ac518015a0bce436357c6ffae80bb8291afc971ebe35544f24400c024501aa2450d005581e03fe83da1ccaef9d9bd78ce53543b6368d6df3947d410bf2d34eb594c300040205581e03ec48afe9bed2ccb5532d8a32fc0cbb2e1ba61c7556de5f51e397c4be200c01463d1ec5ffb1f0032df39c0062882ba634fb24c89e7f9871ecef10a6e6a6e79a336f6ed463d5c94203576d247b864a9c9c193245be5262a86aceeb51240a250d7b4354c293d83a678d05581e0397fc5d38badeb2417fc4af69d36e4965c61f33bb84f646b7d58a9da68007011bffffffffffffffff033fb0fa6f5661fde6378c4db73cf7f1243b922e4b55a3435735c010e477f1c24405581e037a436184a5fc4ce62ad4b2daaf8a45e35f2947e33aeb11416da640405004060219e48e03026455994a7764223a128fa757f29d61c41ac8e28bbab1f879c66e232fddb035036c4372246d91297c5eca3d8d1d450153f9522df23de517957182e29d8416b60c039c41360797c1aa7097a0a9af04c1f817bb5c454d0f772e8ebe2fd352ca7e0d8003ba5c25d3ffc47ad414f2443245ed620a381ce9d9f0d1ee70505a1db59120de1403c0b160d239ce0c71812917d0798c6964edb234491c2f32eb7574c8d7a16270d1034424e96e0a1731300a2559039dea0424a1e764dd8d7d15d03aeaad0ad4f3c4510219ffff036d14a8da010d275acfb59254cdcb9eae8cf5fe7b6ab4a71a2a36194c74b9a3bc03731ac71e9d419e6d1673e09527d61c4f7b9d634dec4cf33946a8ea03f67fa57e03a916837a533d8f341b1bb7fb0e55bcc6b3331f628dde4d08d5ac4d72b8da516703c20aaaece2b73a3a9adc8e7a8806aca1e659dcab0526d1437690766ff0b4452d03d21f43153c769cbbc6c78b5dcf8b9a5d5f139ab0d0f24fd215618da92a34226a03d152c22b71f6c3039c0923a13d58ba41db780be6dbee23526315a0a7cf0431f103c90bb142fa9e97c9d16aab891708825684b56324f90029d149c20ad2d236a30703401ae904b5b28a574e99197b56de5407bb0eef555f36f65acfe9f1819fa6a2dd03966249ae6fb9ba9de1c890a1de3ecf7da07fa01d7602346d1d2a3b0f829cbb750379694c8599c0f3eb1711dfed98e2e4ff393e83f1c8132f830c1298d0a76c182603355149d946dfa2ad5f816899256dbb60ca00eecff995689c2bbd7370215729a80219ffff03fa4bc73fd4b3e5ef71f2079c8c7d0367d6c5e067eaed4b34656b1926863401bc03aef7db107436b3c50134ad9bdba4ab448c0b3cf02a7e2b8c0c88fbdb4b0c2cb50219ffff035193acd8f798b1956536dcf7fe595d15a5dd7454a103659708015d2297bdbcef03c5619d369433bf9fcfb86dbc288cde781cab61a13890d23d8e35269f21df280603173e6adb834c681b932bab9925c8017b62ee314a1a96d5785219e76ed6e6fff903e34b9c0599f8c2ef3b96a881ffdcaec6e123973792fd3b5afa5ef14a69a289880219ffff036d601f7967997e14f0b8ddec3cd67d8c4758d5849ca7356bce8860758ee9c848036b190a0ac12310f42571c2af0c442bac657e635a7e06b05aa0139febf8aef0a003c8825581763941270cb3e17f04c73fd4484788da4848a24a6b4ac5c87f1e31840219ffff0219ffff" + } + }, + "txn_info": [] }, - "txn_info": [] - }, - "other_data": { - "b_data": { - "b_meta": { - "block_beneficiary": "0x0000000000000000000000000000000000000000", - "block_timestamp": "0x663e28fb", - "block_number": "0x12ebc68", - "block_difficulty": "0x0", - "block_random": "0x2e5e4c5b459345002e2597c5b8b556f0fb51fd9c6c45f7892eb98cb4de973795", - "block_gaslimit": "0x1c9c380", - "block_chain_id": "0x1", - "block_base_fee": "0x1aafac7f2", - "block_gas_used": "0x0", - "block_blob_gas_used": "0x0", - "block_excess_blob_gas": "0x60000", - "parent_beacon_block_root": "0x5ab8b5a8f53fb6e010ee24d46334a9ce1051a5ba1691b6383ce8b2a03c10f158", - "block_bloom": [ - "0x0", - "0x0", - "0x0", - "0x0", - "0x0", - "0x0", - "0x0", - "0x0" + "other_data": { + "b_data": { + "b_meta": { + "block_beneficiary": "0x0000000000000000000000000000000000000000", + "block_timestamp": "0x663e28fb", + "block_number": "0x12ebc68", + "block_difficulty": "0x0", + "block_random": "0x2e5e4c5b459345002e2597c5b8b556f0fb51fd9c6c45f7892eb98cb4de973795", + "block_gaslimit": "0x1c9c380", + "block_chain_id": "0x1", + "block_base_fee": "0x1aafac7f2", + "block_gas_used": "0x0", + "block_blob_gas_used": "0x0", + "block_excess_blob_gas": "0x60000", + "parent_beacon_block_root": "0x5ab8b5a8f53fb6e010ee24d46334a9ce1051a5ba1691b6383ce8b2a03c10f158", + "block_bloom": [ + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0", + "0x0" + ] + }, + "b_hashes": { + "prev_hashes": [ + "0x55c186b1bbde8e25b506461db3948d0ee3420042b5fe5a7410af110c1de0554c", + "0xaeed1603c512f372a2edadeb3d4cbd206835a22b25cd9d39c3e144804b26c16f", + "0x8dca61b2f49865cc3effd9e925230c787baf87e30919bd49b738ea4f9dd036b2", + "0x784d858125b4d78a6e0dba2b7ff74bbdc3d6cdc5ef978d08ab3c7ce504229394", + "0x5bb4ba2e0ac463c24628bdd92b74e7a47e0502c8c96089de64f98fd35521dfcd", + "0xcadf72a2755406bb8059cd8dba4c72f97994e51e5ea12b8133bf7f525a428169", + "0xec80579f0f3ffb6658e493e6f1483bb0806356edc411dcaaec825724a3e85e55", + "0xcda45eab952474a2adda67ba2b623716d391bae8d5b2fd81941dd84eeafe83bf", + "0x43155a9903b32ed536bff6788430cd522a275e94c405f619da62c9f41b018a56", + "0x1dda2c3bd2b5216008e68146df9d8cc1caece12dcb2ff13f2d66c16ec1f56aee", + "0x5bcd9b6bfa39e238cef925d01df0d26cd670e0ba714825862f9cb850284774d4", + "0x3b3c0b38b57668e82873490e17ca1ef17b6dad0601c34f2972d7fc8b8d79d22e", + "0x61b483cd38bcf09fd561812bba39b3e0575e93cba262fb454dee0a36e26ab024", + "0x5ff6964818c36655c7482419013438b32b7bf43dc1d0bd80afefc563849d4cd6", + "0x9d06161a501ffd95aa500f5df003b02fdab30b429c3c6de48b7cd32a367fc9c3", + "0x5e1b8c54af7fdaee9c27c67ed4990b609922c54e5fdfadfe928183304ff8cd45", + "0xafd7f313fb970bde5669de90b24b56ff010a3485ffa4229746d6d8000f892f5c", + "0xd10b7f3f17f6a0b00f9947380b0f509e595d3c52059e7bd52a2294fcd5003d4e", + "0x708d167c7dc11778cb2016d12caadb0c38c4c9f173f499c71f4030b87a7b7b28", + "0x29ba6bbb96afc0058f9ab3fe3f22a70a94a91adba5a671798fac8569abf7ba17", + "0x7ed2fa00a64618717df2375d8eeca6eb00b26a69772891d29d53bcc4eb97d7c0", + "0xa2031bdafa03772d8e8504c380ca08269e89761519083027c64bd27108650bc3", + "0x4cbb35640dae5ac34a0aaf0e22f68266ae98b4edeb0906781e24cfc0d2c1228e", + "0x58f159fbfea30bd5848f382e8cefe0df41c355f2000f0f67ed1c38ed41844b24", + "0xe09a822d4bf4cfb20b9fc45e996b452668920a22f397cd02d5ee1f13cae4d641", + "0x9e6ab278fcace59150124fe6472e7bd931674e6370f240abcb38fde46d01cc22", + "0x1b69a6375b4c78ffa61e023a2c3ffb8f21e59f567ab40c647e5248d2763ddcae", + "0x5d2d637fec24b5fa7b72709c699b0605f20c4f77d02bf7e7450a2f845054e472", + "0xa1086afa9e6aff4921a7f3744b7eb28c3441f4e4e359b3a25e7007c65c640531", + "0x8f2cb0870c743235582ca9cae7a5948af500bc1b374f93df3a574e32fbc38f3f", + "0x1a7094e313b8c766f54be0f5da29c29e43b05d6ae42ba517ac1b487bc5c96b29", + "0xcc819e929bede7df0a415f55c1ab062970f815326af426de9f5fbfb992e7e437", + "0x2f051da3ca994b22fb131a27d68bcb2c9726154744a10d46298f40140f6a56fe", + "0x6c853ce7a1da5258cd093e6cd033391a68d0a459038ce37a67d9d17cb88036c1", + "0xc8b0a654e358d7ea93973e2de403d258fdf63da30d6d4034c6827d63fb0edc94", + "0x079cd237218aeaf9a0806360b659ca28762c81f635a3cc501004bb285da58d45", + "0xce1a58a6ed09e666cb0079d48bed5325d161b1c9799f1376a6e4f5822d56800e", + "0xc2136e2aaabd540ca25376c74fb0f675f74eaf47dfe8338a55bfe7ca09ebf0c5", + "0x455e0f3f874619fce323640983b9cc6ab7e0939fa3554c1d6e6f1dc2850536a4", + "0x0cb731db488d51696f60b38c15e0fbbf2ee9ffcc070601192a9e02f6adbb19d9", + "0xe190ec154493546f06322e499ac6bd923438403c676674dd40e0a02e1e45dce5", + "0x1c7c738bf9d0bbf2210090da40774221dad6d159cedf46e51af981282b92856d", + "0xe253577ef8179f8ea765286b6ac234748508c2a79aacb1cafa6993301bf2eb97", + "0x173d63641a7e8f3a7f53fa111b1bec0a2582adb18b3a5c78c81226d311ff079b", + "0xe8d8838dbde5c9150f82e297fe3d76a0c57f12e6f9dfe32453c7e94fcd7a9eb9", + "0x9a61bb64b265aee98d017b3859ad07e76266993c20d1a88ca1bbac3d5d1fcd46", + "0x6bd8bd7da7c642f94b8e0f3186ff027e09df12e34d8d2af607f59e06b87a48b5", + "0x45f6a8d776c000e9cad3f0f120b4986ab94ef4c6bd6d56aa80abe556552c7b7e", + "0x3d907fbbde00deb31cec9c06658b215c943b54127a2c48a462ff0e0c903dcd65", + "0xf5a91976a772292a27b5b54d5fc3ff8a983516eb91c052bb8b3379b1ad0176ed", + "0xd81e25a6ce87305c0d8787dba67dcb66f27b120dc46a91705d080676c81987c5", + "0x773bd2e85389ee5ddd8ec5dcf3795b99e338fa7bb6585dedf74e4483835884ea", + "0xe13a5ffea3cfebf9a0c4624e7cb6881a107a64a271b23bc4835001600f3bc2c2", + "0x535984a643ffb027cfc10476ea2954da4051e48f6157e08530785c2037687267", + "0xff25bad36168818211de295f993032077845e7548739290870055d412a9122b2", + "0x4421ac0d5d5744b71964409561bfd967357511feba5fde27f52302a28fc6b95d", + "0x8f459e102509740b99c3c8f7c3381c283b17d9b1f666a684f07c613954303214", + "0x149d211899e93714c2d702667f5d4d846faa8ddd2797ef997eadfb8d04e70830", + "0x939857b774469e5fa629f4974b0af64c852f0ab4b9f2f694c47c2185c0884c59", + "0x455506f075651b27e59360e405bb3ceba1b284eaaae9f8b7ededab0144241397", + "0x7794f14899d895626054a6775da2d759972ca19a2c7b7fcdafefd3ac81044dbb", + "0xcb1bf88f145349f03b12ad36ba1e2654cd0490262d2686a0390f871447c2dcb5", + "0xb06dabbf192e0f83a2ef9a5c64dd13ad925c0ba3bccd49ba4b9a8cdaa03e0f90", + "0x03a872389b9add1a56eb0ea9737e5f6324849c3f9a618052671b48b11a9ee51a", + "0xd3d865376c8affae8ab310e5cb18c45caec2fd3c8ae2881f06b8229b5855a69d", + "0x4ac61a400ea11b5470f7d2fb3429ea1dc3d4873154c3f7b357246deca3ec4f68", + "0x8f14c907c7bf50bcea75ae65c7d62e66772e5ff15faf430faa5898abbcf92363", + "0x6d151ff1347f6204806c5349fa3dd19bd0221a00773a3c5423328cfcdd64bbe3", + "0x4d4f747eea3eae7dc0423da074e257f523ea688874bcd3afc28e0cdfeab0c7a8", + "0x00a6c2c059ebb164a9377e084206ac5832cc5297cdc0200ca86762e31be4f134", + "0x8de486c426447c83cd81c2f4200e2026abd8833f2803da7568a9ddf11117aaf5", + "0x6ea90881d941139490f2d278593586a7a229ac789532627569c25af254539415", + "0xcb5f64f11bd0a0907b26e13c1f0e22f755907334307c26d2427a387ce70d482c", + "0x359f8609c02c2ef873c5524687a35f1608c71eba9d34c0afb2765a6519e4de3e", + "0xb65483aa05099003d212ec7bb1eac1dd62165d67da35ae1df758778591a778f7", + "0x6bb00536c7d49f225bb7eb094fea16b263549b585c019293dd6018817a82c100", + "0x2566da7d2213e0157c91b6e6644a17b978b93feb2b522621473c3f7bed541d76", + "0xb720814c561850a476c1070fa9fdce8a7fc4269bd7c56bb14479f4b1636800b2", + "0x84932dc6d0879e3b01bd311362403854fd9e4b1690fd59c323ba3b04cf19a969", + "0xe4250b66c3b5b017603e06477a5c252e9596595fd3d787c298ad2ac45e24019b", + "0x3941e8083df52e04e2a3fea6a6c310e558e6747146e676dd0e5942655e9ee3ab", + "0x2240c0287ef3b987e01d00db447982d5143fb272b9846b1af226664197c78292", + "0x0bebcaca64b07b7536c2f6fbfeb9d5141a2d8e309dd942852409bd21d17cbab2", + "0x4e32243bedcdb9f2a5c7f8d423d2a8aa2b487f23b1e3d7d8c220aa4a55cc0a9a", + "0x279a114f51480e923292c58807523e7da08fa9aeea5214f09d27dcb229ca3b1c", + "0xe7a4d1977c4aa7b776bf74e46f66c2e15589c67684bda719e3a6526b90bbc158", + "0x6cc1f2a3880c2aa41d11c901ba91926758e252ce1f148c51a769a81ae34303f8", + "0xbf9e7f17c786e48f5ac09b605da6619ae50788bea08cd42500835802e67b8729", + "0x8a6b70454cda7934a39cd374405f3f5f7c39977273912abb841c4eab738b1237", + "0xb3f52f9b2f7e6a37074d1e20751da4b4fac4fbe0a91386966670631f2a545f7e", + "0x7ec81bbdbbb56a40ad2d00ee6f34110ad97d9935c7dad83d7968ed5512ebdc7f", + "0x40a7df3b63d2f7c4fb05e37b1836dde00becd7166c82fe0df45bbaafa8d7d338", + "0x62ea7fdccc874b5f39f0290e4c0ba8b7b0b5eb12e2a83734a1e236cc87e6453d", + "0xd990e2aeffd23a789d298673c88498322ddc13729bdc6324b62ad6e0a6b2d939", + "0x13f5571d094bb53856ce1e6573a61dece7f32e0f593da75439d2cc59690828d7", + "0xf003ae90a37ca406f346082ff47df498cbdd720e2ed487277f31bdfe495fbd5c", + "0x1ac6ef95f6e007da86fcb50652491da28924854b45cb4d4cd8d574a5ae6da337", + "0x55f27c6633a639527034f2889551243ba50847721b11380bbbcae5ed9eba7c1c", + "0xe9f06076dfe328442393dae25b8fd8667b52c20b860b619947aeb758b5161288", + "0x93665a5b3ff727fabed298a5117a07214f44c80369c12b788e98e50adc9af06f", + "0x214b1e759711d63acd3aece629f1eb5d207b306c868cac471b40d4a264c425c0", + "0x88d2ef86af833ac6146fc687e93972b5f59089a0aadf14b7a731d60c1dc0aac9", + "0xf30a23080c5cb8a76bc1772a811fc7d770ac0595c7b52b606dc660965cab5218", + "0xa136d539a24861b4bdfc4f156347b436c2ea0b69d3e28f75863ab6e90d485e6c", + "0xfdcbcaf63e67009e531ff489c46b7a1433107ef7a54274190d4011d3190d34f2", + "0x70b735311bbd5733eedb484d4d11f88d3de47f3d14afa4fa182525801991f560", + "0x0b7cfa03f5c925257041b19f0ded62df6c29999d258b164592b54badbc0a1ee6", + "0x134e8d9ece216b70d69e369c28f38eca6a0c61b1d1242dcef7b576aa4ac57d3c", + "0x5dd966a462f85da54f3d1dc30b5a629977fbb7c279f754887bda19d89177ac31", + "0x4f2700f4b972c826c3d6bae610c1bf21f716ec99a4b1e1a37bd778a37ed1d81d", + "0x26e86dbdd46fa58dc7c0f23e3402def08d7f09b6600a012eb7503e868ef82239", + "0x1f009da6c2b59286201b3c12a9e52d821e520bb067ddee3c6d57b29b60394c16", + "0x36953b01dfbbb273002d819beb306d703688d6ade4430f8b258bf925c9bfcb1e", + "0x7a94d795bc76fba6899dba6b677d526dce7f01518f4cc8cd18bdbfc31a778b9d", + "0xaa8ad1cc6c206dd616eecfc976cbf2caf1e522facc73ca9d9494d89887d1e302", + "0xc8e5fb05d201e951af9be69c7afbff266cdb11110ad69be6748da319757596f3", + "0xfb394b3865fc15ebaab85682ebe8caf1e7a6ae340c127d9c6675447a8f87807a", + "0xc978e59542a478bba83a9890692f0ae88a0bb7a3df30b7cda0dea4ed7d34eae2", + "0x00832dc5f938a74505d93d3d5c4ab78a50752d45d27eec9ed1ab7a462abd8990", + "0x31cf0cf7fe510d6bc4108ede74063e1bdc3113a21f0212e5c6ab36eaa1faed37", + "0xdc87984673693bdb250a1896305262b2346ba3af4fa0676eca2eeba52eb040ed", + "0xfc945bf877357905dda2af20404e426a82db019437bf1b59f60b31bcdedfbe55", + "0x7f4cc1a12ef5979e6953d3e40f40a4dc932c6c174f183b30f701e6fa4f1b34cd", + "0xcbb31cec8319fa2fea3e7781f981e98aa0769ec751b2e97888b2bd2b5e5d8e58", + "0xee48275f37a5306d7c5d52a1ff2200d3150248f6c596994ee0e78fdde42f3811", + "0x5378d6d66892ceaf1089db912d546f565a2993de54e82eb548545eba7250b31d", + "0xbff7a5b25847fb689db38926fbba529b66374886952d641319cd8fb85731c58a", + "0xa2b4ff87dfb05b3cb88ac945706ef5e82765d11536a428c2ad27fecd19bd8947", + "0x71be8e30b0f2cf7df16495cdfd86e0012c5bb8f728374d86e772d4efa2beda20", + "0xdfe133c543e32cffec7bdf9febab7bb0eaf3d213094a102e4a1c21842fe7afdc", + "0x7874bfed2ae039536a7f92b4aa6f7b7f231152b04895ab0c6efbd3505cde5ae6", + "0x86f49a60e84b885fae20af792a164c679be96a5975ec83fc896cfe19a27a3181", + "0xd1f4af1826401ec1636d039bd528e496ac7ea3f181205eab5751778622129dd6", + "0xefa40fc9cc91899653d4218b8473bc49c3b64d84869cc996987729df1eaa4219", + "0xbdb65de637fa89949f69e6b39635e76fe3da079179612c8101c92df404388e6d", + "0xe13e4e0e93cd3e9e881688079ab5caeb603d53f3f8c3f159a3216dac654003a2", + "0x5b3bdd265d4c516c62a9685cd2e6343c3b542762f45d381aa10adbe4cc2a8ca1", + "0x3fbe2934edb9b4c74feac47a10c34f43c73ce9c04fd4aff4da9e9d53ae1a3b27", + "0xe7f738ca0d301c92913c1c5bd670f2958bc3bccd349206f798ff38615704da02", + "0xd6023f0729c366624fc9581e69c8217352113d2b15e8824d36b05fef5640d228", + "0x2a426c808ee5b9f8c405875e564962a318666acd06bc6942dfc7a4960dfc0a78", + "0x1346589ec5380ee0dcb41cf0af9e8b39a7395a13b94b97150bcc11a71e2b4c1f", + "0x2fcafbf9b787434cf0f745acd8d880cc553a432810acf1bbd66b433ed11c2c18", + "0x33290d023a695c514e8dc94f04719532e377eb81790e288a0b017312b41a0ff5", + "0xf8befaf58584c66572ce8251e42d002cfc5355a9efc8d5f9bc9088a1f7b9edfd", + "0x1f9f9f7a63d43d358f2499ce9b04ce9f0a4b35546011215fb83a22e68df87844", + "0x9a9c4815d5827ec055e39ec001252a5035e6317a05b508bbc3e6907b53109706", + "0x3b277af862fc12912cb482182175edd1ad4dd7f15b2614e0970c3ea09d1a8de3", + "0xf8967669bc80f68ea5c966bb42117851d80c18e81dfd184efc59491971a9165d", + "0x00814afcf1ecf5f401b450afcb6d73ce3fbf6338e8bc4dcec4c32d5b2ecc6454", + "0x63ce5390474616f8b3b70820e67460c6fda75f41f2a2c66ab467b0c311733f3b", + "0x176619adb025ee77960fcdabada971e20bd2789d8e3cf045c03d4ec2e97b2f79", + "0xcd01a05497515a98f69a8a48b534ea1e741bdee0dadb2a32a97c631212e2e5a5", + "0x0b808e84cb0904210581a81e312236efed3bca37e624f6a6c66bfed4134ebf9b", + "0x42ed1705030f23cbf6525095491f6f82914727b116c85bc24ebd9d3579508554", + "0xeb6c3c39f07929e4d0a2ae0a8f9390f2f286f715095f31f82b471cc3f02ab770", + "0x6d0b0405e16a27285e6645ff8a29e05a6f8d4a4ada9ce08b729caa89af96ea48", + "0xc02fd4ca15b5c7a0eb193e1a679fef5ce2cc9e5732209c8739b31cde876b524a", + "0xed2560d142a34db7c5b20125882fb61b8edc1cf22e12c81f860c47a1239706f2", + "0x9f9d607cb7d98b099460c97bd9bf4abe27524de0aa0494068d52cf380aa36293", + "0xca530177bdd648a62d09f77f03f134e3e3bc0d64bcb93a335ee2ca73d73ac2d3", + "0xf2bd87e7e61b6efaf4339ca27cc8d73df9d3d71799bd50ce1465ce9386350023", + "0xfef3aab8dceff11c8cf8fa282e482c9a4a7c53c471a7e329e9720bf917f744ce", + "0x56002bf72faf92897f58fe2679e3571ca490d6c036f724a80b74aac20970641c", + "0xb207591550eb9be35178a224e6ca46ec82b2a6a019bf961f13e7df2026009b9d", + "0x2b52868f8b17ef786cecd0a9a4fcfcee6d9551d4c9104a5ab8d8eeadb707d2dc", + "0x7c4acd1df61f61cc5e2f4ee9decc296d98f3f3fc89ae66bd07d41c84ff56a288", + "0x30adce03c6c2b1532e07e33a24c7f73d08feccb5497e33910226388032a80812", + "0xc79ae82c871ef39b9558c1339ea365dac86469a1165fa0d36b7775c49d3ac7f1", + "0xe4e52fb5b9f83cce4205f09f720c5a4556d0511e20c5c3e0c63f9a954a010adf", + "0xf53f57bfaa2aad6de5fd3f236bc8a4361e2af82fcc198bc058e5d7a5bccd0a02", + "0x5098a75b4dd1d02507416a6e3b8cf0b0ea6cb15bd68420ebc3b7915eb7839f1f", + "0xf9c0e2cbce9fe64623de15c2f85d7c3928ab0b37275660a242770219e3a96d93", + "0xc5b62e1433192d067cbd106d3c0ca218383ddbf14c00c9a13d56449f2bbb3a08", + "0x80d8820d2078d42c1a38e8d6f9f81d58cee3846aaa899261ee1ed85ff4d42533", + "0xd886a4c921ab8b11a3e855a7893664f09629814ad6df0f3bd36cfc37a42a0bbf", + "0xc1375c4f051bd893b5e8a3d768f398b396efec9c4b0e4c2b0d352bb69c9fb394", + "0x20ae02c26b613b05737e26202e18100c3eeeb84c078bb3d4912be5cd99787a2e", + "0xcfa25f8226fe9e61e35fc2ed63a52f2b2a4c3dee8e192831f7a0cda7b77022e7", + "0xf1506b0bb1dda73bbc12eaf379580952e65880b0aef361231d8686619da83b9b", + "0x0033b129deb9e2591fd3375084de6f00fc3f72d37911eb2d623820de7c03ed9c", + "0x245b2677360776f20b01ae7278b41980a63eb45cf6f7566f5804dbcb7e3ea66c", + "0x73081e452c9aa38f130ad22fa653519888b429d3e1aae6701fa5407ed088872a", + "0xcf8224c7675985b9d8411694d881a452b850c3d41331602c6d2ddc552500d9b6", + "0x8278bf3e350fa2532dba0e7082f64bb1fc08f3d2279525545a62ea488835c756", + "0x134a0e1ae78dd0da790d1dc3b1710caaeb3a6015c82fda8efdbd1e5ceb154767", + "0xa5a77ceb096cbc27b2eae356c942fde9db8d72161e9e559ad6376143d7f4d609", + "0xb34c9ca03d44c2512795c8572527f6285c3a6e954cf17668c51e8f22de24efb0", + "0x7cbf51c29022b4cf1e6203cf1322481a36f510c35bc1d78d972101e9593fcdc9", + "0xc3162682c647172119a2cebd3ce42fab8f20be84b20b3c4a85b4b2196eefb80b", + "0x4c8e78cad3ed836eb63af411b683a3d21d7a854004d026ee928870e7d8c94239", + "0x92a9ccef236e16e8d527f9b4e25861a670763b2845b9e7f2831d83f7411c1bb1", + "0xf82d9c2c67a3a6050a1003dfbcdbc6861ca699d41b2bdd69099196122365e218", + "0xafbe03db4a223222536b6ae7e65a66303bb2c89f978a49db05dcee8ce3eb9bfd", + "0x97db73ba455ac03902bfa5335d1efd1c3a08fdc5fe1bda5c402413a6c6bf6439", + "0xe87c9f587da80c6ebf6416e7851fdbd1a5e44b7bf68b7b5b93cae18f5b71001d", + "0x04977189709d5e8243e2cff4d6e9bebae049fcd78854c414276049c1dd8899a2", + "0x0dfb0bc4588450ba7f992ac46b5e68b740bc2373a944c70a440a25222451f868", + "0x2092f8d8303fdfd369f4a24ef504a9d48acdfdfab2e37115d5778a0adfac0082", + "0x19d81b509c576de5a558f9c8fb929d84fea577428fed5af023a7f7bb502e3643", + "0x935fb703a9a030bdad9db265cb3292c2c9a038f1837e049d1d5045b09047a4f0", + "0xbec9f75266da91b02a2d1b5f5b5449f7e6ba28ae402979af40a83c9b39cb483a", + "0x320c3fb2a136d04495087d27b0a5dc6ce90bf7a1bf15ae65d48146a9661f2aa9", + "0x364c285cabc386ccf0140bd24bbc98e37a88b6f88bdb7d34ff1d4a75f29573fc", + "0xe595476b0844351af48748f788d15b53eed8507c6c45faddc189d602d8de1bcc", + "0x803f52547a1ca56abdca4b4ccc28bff586fb3372d9bf677b3ca3400184d009ba", + "0xb90914aebed7eee12fb7d25e80c2f37f68dbf2a698faf2ac92faf0cffafdaf8f", + "0x129abb430b863be8ccb3e33a3194552ede9eca4bebdf34d6b53748213a734ff8", + "0x9a6dcb68b7b37e705ce7457fb1ca954cb5549eebcc4397ed29083df51362d779", + "0x12bd07e3b6ae0add86cc1467208532430c0c8d7d0e832e9c05134b07e46d1768", + "0x69d228b5d0f85a16596aff9f2b51ffc21497d4331a0c9b6fc5b3b1834654df09", + "0x3cd093afcf1b1588b12da25cdbeab67cf99fbbe9df026f78d3abffbd378eff6b", + "0xecb8e829dae8134f700eec6d11e0157bd761fcf0779bb603cd024d0ee8496717", + "0x0fb6faa0e9b556d1ca1e91f56117d417c29b7368445c18cdd0bc5060ee6c86f4", + "0xf126590823b117b19491aae44cdcfb18a3e130f00371398a254c3c3845ff0592", + "0x5f2432d15b4f94234961ce9e6165e9761accc88999c523315001340b96227519", + "0x60d53f01e91fde0da7f3a05a795a491466a211d6cc91e9e49e42d5bc4bd78606", + "0xf575fd0cb12b72eec6b639210f04b5bbad1a7c806fbc6c2c15ea62f97c2185c3", + "0x3ffe76eb15010487bfd65c38db0425d5df9a14df60dfcf0849bcd540aa1300db", + "0x6dfcea9390145d196db57f5b427d9c5e89f336a5bc81bdc3a4e246370feee2f0", + "0xe3c2e58ff9ff293dd8e2084b333bfefb506382848b1884ad03546f457f0559c2", + "0x807d9d862e2b760628b579b66b206ebcaf247f3d041fd9863fb375dd715b74f6", + "0x11e8e648599d5bbabd99f3693c090d95a7ebb66b82e5ce33ae47f7b570154423", + "0x4b59ea6016b208f2e5bef0606a7e81f05cd679eb7635db9e27e8458980281dbe", + "0x5afe3f29cd36132c4db3fe0d989dac1eb5cd6c7c4fffd22a03691a695df95489", + "0x5fc081154e1402309a9ada4d39f90a083bda62aaf4e643a58b2a1bbff8c030c2", + "0xc8f835e4abfa2f4d3cd3e1f92ed878a78253b828ddb8e89300e3de63cfc01775", + "0xa94346dc4441d16b65996b0b44d1ee7b737f75e6857d22a7a03f5cc9b7156c97", + "0x7a9d66965c11cedc5af224b42d407423d56dc291f9a6903f013e4c2801e28f94", + "0xe1592fdfc80452f70c8372bf59824166ccfed92ca6bbfb0a7f196bfbc11a3d7d", + "0x5800287a2051dc265161d6197ab2f0221dfc918f1b37b685f8d23c66af24412e", + "0x875c7d11313b43a3b2df625bf5f0f816b2743ba44165fda1a34c0d3c9d53c54c", + "0x9d87149d991d4a54cdd689e156c37eccb9c2e3022cef97d91bc04667905f8a7b", + "0xd39a5b4c38441cb4a7dff3a22d8ab37691f21dc3af438f4a1591058c214d1284", + "0x6de58f2f7f2b3f756b6c3683596b11c1b68235f032ff31561f92c6d2e0d31575", + "0x0d4b0e13f898561309b8b4ed0dae1f0698d4433aeb81a4e66a045b69c4356c94", + "0x82e58ed2c7e584e42f9f284a1171461338cdc40661e5457ce86621fb9939e3a5", + "0x0f6af8168a7e2e6f35539a5477d9dc4901af3eca6e3257d7f0ecc0b573bf2c73", + "0xe3eb2cce31d8e65106536c608a154388fa00cc5a481ce635c55eb130f021f218", + "0xf6829b8da3ddc1f71b3b14ca1b06c13e9e454a364899c4ce726d3b9a9b55b1ca", + "0x248b513050ea763394365a1ff6a295b6648fd1af0a5703daa7dac423cf5967ad", + "0xcbbd5076dc646780f3e582343c66090e477e32b56210adb84992e340b74a798d", + "0x6e844d7759a5aff03abb0efcc0cd74285a20521afd06c0ff98b82a2bf17a7224", + "0x116a574a2fd13487716a1e3bf78cbbc998dd5b3604caa567662055de8691a459", + "0xfe36c7f148af1c056b913b4f52c8dc4fa0e6cba42b1e7f5204c1d300f9ae1ab4", + "0x114da06ec0e3d1b14027802c9c3bb80922213786464dd5b6a1ee2a12523761aa", + "0x9c451c4cce3cf45b173cf2abf03ffcd11f5d471db49c5abc66603795c6c55494", + "0xd0dca6ee9e3cf1d9277aa5b9a97acf7a45d63d0f76ea8cd84366e2c787489061", + "0x4f93256e5e4af9d0771ee8cf5cf84b80d2f7298a76c77ddaeccd835d4717864f", + "0x174ab2c74403d9538aa14bb0549c33756095c78e647b18eaa818790876996f8a", + "0x0d62cbeb56e7c3db3c6845badab75d504695b40ae7e8b1e6ad13097f5cf65154", + "0x70c96dd19bca237759ee01a00ae6ec75873e2c1384908c86310e766e8352c299", + "0x2ba3360edd065360b551e07aac2fe6a1b646fae955c29d39c1e981b8b8541a72", + "0x64400f31f7e3deb29c6f22aee609a502db3e9c6c9eebd5a93087b6a1636d44ba", + "0x255c5d43117d4d6e3ee88610b3fa54a679ab1eb358153147188a5fbde1556db1", + "0x290e2c3125591d2f128758525955799fe08eb933dc0e22519810e63a10f0ea69" + ], + "cur_hash": "0x3c869591ac4295afc75154eaaf7a8b59a41af3cdcbad9d8c48fb7ef9853f9ec6" + }, + "withdrawals": [ + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x119204e" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x1193fa3" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x118a669" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x118d295" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x1195db7" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x3c46d9e" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x118b8bb" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x11994d6" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x11822b1" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x118b828" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x118b7e1" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x117492d" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x3c1adb5" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x11713d0" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x1197bef" + ], + [ + "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", + "0x1181b16" + ] ] }, - "b_hashes": { - "prev_hashes": [ - "0x55c186b1bbde8e25b506461db3948d0ee3420042b5fe5a7410af110c1de0554c", - "0xaeed1603c512f372a2edadeb3d4cbd206835a22b25cd9d39c3e144804b26c16f", - "0x8dca61b2f49865cc3effd9e925230c787baf87e30919bd49b738ea4f9dd036b2", - "0x784d858125b4d78a6e0dba2b7ff74bbdc3d6cdc5ef978d08ab3c7ce504229394", - "0x5bb4ba2e0ac463c24628bdd92b74e7a47e0502c8c96089de64f98fd35521dfcd", - "0xcadf72a2755406bb8059cd8dba4c72f97994e51e5ea12b8133bf7f525a428169", - "0xec80579f0f3ffb6658e493e6f1483bb0806356edc411dcaaec825724a3e85e55", - "0xcda45eab952474a2adda67ba2b623716d391bae8d5b2fd81941dd84eeafe83bf", - "0x43155a9903b32ed536bff6788430cd522a275e94c405f619da62c9f41b018a56", - "0x1dda2c3bd2b5216008e68146df9d8cc1caece12dcb2ff13f2d66c16ec1f56aee", - "0x5bcd9b6bfa39e238cef925d01df0d26cd670e0ba714825862f9cb850284774d4", - "0x3b3c0b38b57668e82873490e17ca1ef17b6dad0601c34f2972d7fc8b8d79d22e", - "0x61b483cd38bcf09fd561812bba39b3e0575e93cba262fb454dee0a36e26ab024", - "0x5ff6964818c36655c7482419013438b32b7bf43dc1d0bd80afefc563849d4cd6", - "0x9d06161a501ffd95aa500f5df003b02fdab30b429c3c6de48b7cd32a367fc9c3", - "0x5e1b8c54af7fdaee9c27c67ed4990b609922c54e5fdfadfe928183304ff8cd45", - "0xafd7f313fb970bde5669de90b24b56ff010a3485ffa4229746d6d8000f892f5c", - "0xd10b7f3f17f6a0b00f9947380b0f509e595d3c52059e7bd52a2294fcd5003d4e", - "0x708d167c7dc11778cb2016d12caadb0c38c4c9f173f499c71f4030b87a7b7b28", - "0x29ba6bbb96afc0058f9ab3fe3f22a70a94a91adba5a671798fac8569abf7ba17", - "0x7ed2fa00a64618717df2375d8eeca6eb00b26a69772891d29d53bcc4eb97d7c0", - "0xa2031bdafa03772d8e8504c380ca08269e89761519083027c64bd27108650bc3", - "0x4cbb35640dae5ac34a0aaf0e22f68266ae98b4edeb0906781e24cfc0d2c1228e", - "0x58f159fbfea30bd5848f382e8cefe0df41c355f2000f0f67ed1c38ed41844b24", - "0xe09a822d4bf4cfb20b9fc45e996b452668920a22f397cd02d5ee1f13cae4d641", - "0x9e6ab278fcace59150124fe6472e7bd931674e6370f240abcb38fde46d01cc22", - "0x1b69a6375b4c78ffa61e023a2c3ffb8f21e59f567ab40c647e5248d2763ddcae", - "0x5d2d637fec24b5fa7b72709c699b0605f20c4f77d02bf7e7450a2f845054e472", - "0xa1086afa9e6aff4921a7f3744b7eb28c3441f4e4e359b3a25e7007c65c640531", - "0x8f2cb0870c743235582ca9cae7a5948af500bc1b374f93df3a574e32fbc38f3f", - "0x1a7094e313b8c766f54be0f5da29c29e43b05d6ae42ba517ac1b487bc5c96b29", - "0xcc819e929bede7df0a415f55c1ab062970f815326af426de9f5fbfb992e7e437", - "0x2f051da3ca994b22fb131a27d68bcb2c9726154744a10d46298f40140f6a56fe", - "0x6c853ce7a1da5258cd093e6cd033391a68d0a459038ce37a67d9d17cb88036c1", - "0xc8b0a654e358d7ea93973e2de403d258fdf63da30d6d4034c6827d63fb0edc94", - "0x079cd237218aeaf9a0806360b659ca28762c81f635a3cc501004bb285da58d45", - "0xce1a58a6ed09e666cb0079d48bed5325d161b1c9799f1376a6e4f5822d56800e", - "0xc2136e2aaabd540ca25376c74fb0f675f74eaf47dfe8338a55bfe7ca09ebf0c5", - "0x455e0f3f874619fce323640983b9cc6ab7e0939fa3554c1d6e6f1dc2850536a4", - "0x0cb731db488d51696f60b38c15e0fbbf2ee9ffcc070601192a9e02f6adbb19d9", - "0xe190ec154493546f06322e499ac6bd923438403c676674dd40e0a02e1e45dce5", - "0x1c7c738bf9d0bbf2210090da40774221dad6d159cedf46e51af981282b92856d", - "0xe253577ef8179f8ea765286b6ac234748508c2a79aacb1cafa6993301bf2eb97", - "0x173d63641a7e8f3a7f53fa111b1bec0a2582adb18b3a5c78c81226d311ff079b", - "0xe8d8838dbde5c9150f82e297fe3d76a0c57f12e6f9dfe32453c7e94fcd7a9eb9", - "0x9a61bb64b265aee98d017b3859ad07e76266993c20d1a88ca1bbac3d5d1fcd46", - "0x6bd8bd7da7c642f94b8e0f3186ff027e09df12e34d8d2af607f59e06b87a48b5", - "0x45f6a8d776c000e9cad3f0f120b4986ab94ef4c6bd6d56aa80abe556552c7b7e", - "0x3d907fbbde00deb31cec9c06658b215c943b54127a2c48a462ff0e0c903dcd65", - "0xf5a91976a772292a27b5b54d5fc3ff8a983516eb91c052bb8b3379b1ad0176ed", - "0xd81e25a6ce87305c0d8787dba67dcb66f27b120dc46a91705d080676c81987c5", - "0x773bd2e85389ee5ddd8ec5dcf3795b99e338fa7bb6585dedf74e4483835884ea", - "0xe13a5ffea3cfebf9a0c4624e7cb6881a107a64a271b23bc4835001600f3bc2c2", - "0x535984a643ffb027cfc10476ea2954da4051e48f6157e08530785c2037687267", - "0xff25bad36168818211de295f993032077845e7548739290870055d412a9122b2", - "0x4421ac0d5d5744b71964409561bfd967357511feba5fde27f52302a28fc6b95d", - "0x8f459e102509740b99c3c8f7c3381c283b17d9b1f666a684f07c613954303214", - "0x149d211899e93714c2d702667f5d4d846faa8ddd2797ef997eadfb8d04e70830", - "0x939857b774469e5fa629f4974b0af64c852f0ab4b9f2f694c47c2185c0884c59", - "0x455506f075651b27e59360e405bb3ceba1b284eaaae9f8b7ededab0144241397", - "0x7794f14899d895626054a6775da2d759972ca19a2c7b7fcdafefd3ac81044dbb", - "0xcb1bf88f145349f03b12ad36ba1e2654cd0490262d2686a0390f871447c2dcb5", - "0xb06dabbf192e0f83a2ef9a5c64dd13ad925c0ba3bccd49ba4b9a8cdaa03e0f90", - "0x03a872389b9add1a56eb0ea9737e5f6324849c3f9a618052671b48b11a9ee51a", - "0xd3d865376c8affae8ab310e5cb18c45caec2fd3c8ae2881f06b8229b5855a69d", - "0x4ac61a400ea11b5470f7d2fb3429ea1dc3d4873154c3f7b357246deca3ec4f68", - "0x8f14c907c7bf50bcea75ae65c7d62e66772e5ff15faf430faa5898abbcf92363", - "0x6d151ff1347f6204806c5349fa3dd19bd0221a00773a3c5423328cfcdd64bbe3", - "0x4d4f747eea3eae7dc0423da074e257f523ea688874bcd3afc28e0cdfeab0c7a8", - "0x00a6c2c059ebb164a9377e084206ac5832cc5297cdc0200ca86762e31be4f134", - "0x8de486c426447c83cd81c2f4200e2026abd8833f2803da7568a9ddf11117aaf5", - "0x6ea90881d941139490f2d278593586a7a229ac789532627569c25af254539415", - "0xcb5f64f11bd0a0907b26e13c1f0e22f755907334307c26d2427a387ce70d482c", - "0x359f8609c02c2ef873c5524687a35f1608c71eba9d34c0afb2765a6519e4de3e", - "0xb65483aa05099003d212ec7bb1eac1dd62165d67da35ae1df758778591a778f7", - "0x6bb00536c7d49f225bb7eb094fea16b263549b585c019293dd6018817a82c100", - "0x2566da7d2213e0157c91b6e6644a17b978b93feb2b522621473c3f7bed541d76", - "0xb720814c561850a476c1070fa9fdce8a7fc4269bd7c56bb14479f4b1636800b2", - "0x84932dc6d0879e3b01bd311362403854fd9e4b1690fd59c323ba3b04cf19a969", - "0xe4250b66c3b5b017603e06477a5c252e9596595fd3d787c298ad2ac45e24019b", - "0x3941e8083df52e04e2a3fea6a6c310e558e6747146e676dd0e5942655e9ee3ab", - "0x2240c0287ef3b987e01d00db447982d5143fb272b9846b1af226664197c78292", - "0x0bebcaca64b07b7536c2f6fbfeb9d5141a2d8e309dd942852409bd21d17cbab2", - "0x4e32243bedcdb9f2a5c7f8d423d2a8aa2b487f23b1e3d7d8c220aa4a55cc0a9a", - "0x279a114f51480e923292c58807523e7da08fa9aeea5214f09d27dcb229ca3b1c", - "0xe7a4d1977c4aa7b776bf74e46f66c2e15589c67684bda719e3a6526b90bbc158", - "0x6cc1f2a3880c2aa41d11c901ba91926758e252ce1f148c51a769a81ae34303f8", - "0xbf9e7f17c786e48f5ac09b605da6619ae50788bea08cd42500835802e67b8729", - "0x8a6b70454cda7934a39cd374405f3f5f7c39977273912abb841c4eab738b1237", - "0xb3f52f9b2f7e6a37074d1e20751da4b4fac4fbe0a91386966670631f2a545f7e", - "0x7ec81bbdbbb56a40ad2d00ee6f34110ad97d9935c7dad83d7968ed5512ebdc7f", - "0x40a7df3b63d2f7c4fb05e37b1836dde00becd7166c82fe0df45bbaafa8d7d338", - "0x62ea7fdccc874b5f39f0290e4c0ba8b7b0b5eb12e2a83734a1e236cc87e6453d", - "0xd990e2aeffd23a789d298673c88498322ddc13729bdc6324b62ad6e0a6b2d939", - "0x13f5571d094bb53856ce1e6573a61dece7f32e0f593da75439d2cc59690828d7", - "0xf003ae90a37ca406f346082ff47df498cbdd720e2ed487277f31bdfe495fbd5c", - "0x1ac6ef95f6e007da86fcb50652491da28924854b45cb4d4cd8d574a5ae6da337", - "0x55f27c6633a639527034f2889551243ba50847721b11380bbbcae5ed9eba7c1c", - "0xe9f06076dfe328442393dae25b8fd8667b52c20b860b619947aeb758b5161288", - "0x93665a5b3ff727fabed298a5117a07214f44c80369c12b788e98e50adc9af06f", - "0x214b1e759711d63acd3aece629f1eb5d207b306c868cac471b40d4a264c425c0", - "0x88d2ef86af833ac6146fc687e93972b5f59089a0aadf14b7a731d60c1dc0aac9", - "0xf30a23080c5cb8a76bc1772a811fc7d770ac0595c7b52b606dc660965cab5218", - "0xa136d539a24861b4bdfc4f156347b436c2ea0b69d3e28f75863ab6e90d485e6c", - "0xfdcbcaf63e67009e531ff489c46b7a1433107ef7a54274190d4011d3190d34f2", - "0x70b735311bbd5733eedb484d4d11f88d3de47f3d14afa4fa182525801991f560", - "0x0b7cfa03f5c925257041b19f0ded62df6c29999d258b164592b54badbc0a1ee6", - "0x134e8d9ece216b70d69e369c28f38eca6a0c61b1d1242dcef7b576aa4ac57d3c", - "0x5dd966a462f85da54f3d1dc30b5a629977fbb7c279f754887bda19d89177ac31", - "0x4f2700f4b972c826c3d6bae610c1bf21f716ec99a4b1e1a37bd778a37ed1d81d", - "0x26e86dbdd46fa58dc7c0f23e3402def08d7f09b6600a012eb7503e868ef82239", - "0x1f009da6c2b59286201b3c12a9e52d821e520bb067ddee3c6d57b29b60394c16", - "0x36953b01dfbbb273002d819beb306d703688d6ade4430f8b258bf925c9bfcb1e", - "0x7a94d795bc76fba6899dba6b677d526dce7f01518f4cc8cd18bdbfc31a778b9d", - "0xaa8ad1cc6c206dd616eecfc976cbf2caf1e522facc73ca9d9494d89887d1e302", - "0xc8e5fb05d201e951af9be69c7afbff266cdb11110ad69be6748da319757596f3", - "0xfb394b3865fc15ebaab85682ebe8caf1e7a6ae340c127d9c6675447a8f87807a", - "0xc978e59542a478bba83a9890692f0ae88a0bb7a3df30b7cda0dea4ed7d34eae2", - "0x00832dc5f938a74505d93d3d5c4ab78a50752d45d27eec9ed1ab7a462abd8990", - "0x31cf0cf7fe510d6bc4108ede74063e1bdc3113a21f0212e5c6ab36eaa1faed37", - "0xdc87984673693bdb250a1896305262b2346ba3af4fa0676eca2eeba52eb040ed", - "0xfc945bf877357905dda2af20404e426a82db019437bf1b59f60b31bcdedfbe55", - "0x7f4cc1a12ef5979e6953d3e40f40a4dc932c6c174f183b30f701e6fa4f1b34cd", - "0xcbb31cec8319fa2fea3e7781f981e98aa0769ec751b2e97888b2bd2b5e5d8e58", - "0xee48275f37a5306d7c5d52a1ff2200d3150248f6c596994ee0e78fdde42f3811", - "0x5378d6d66892ceaf1089db912d546f565a2993de54e82eb548545eba7250b31d", - "0xbff7a5b25847fb689db38926fbba529b66374886952d641319cd8fb85731c58a", - "0xa2b4ff87dfb05b3cb88ac945706ef5e82765d11536a428c2ad27fecd19bd8947", - "0x71be8e30b0f2cf7df16495cdfd86e0012c5bb8f728374d86e772d4efa2beda20", - "0xdfe133c543e32cffec7bdf9febab7bb0eaf3d213094a102e4a1c21842fe7afdc", - "0x7874bfed2ae039536a7f92b4aa6f7b7f231152b04895ab0c6efbd3505cde5ae6", - "0x86f49a60e84b885fae20af792a164c679be96a5975ec83fc896cfe19a27a3181", - "0xd1f4af1826401ec1636d039bd528e496ac7ea3f181205eab5751778622129dd6", - "0xefa40fc9cc91899653d4218b8473bc49c3b64d84869cc996987729df1eaa4219", - "0xbdb65de637fa89949f69e6b39635e76fe3da079179612c8101c92df404388e6d", - "0xe13e4e0e93cd3e9e881688079ab5caeb603d53f3f8c3f159a3216dac654003a2", - "0x5b3bdd265d4c516c62a9685cd2e6343c3b542762f45d381aa10adbe4cc2a8ca1", - "0x3fbe2934edb9b4c74feac47a10c34f43c73ce9c04fd4aff4da9e9d53ae1a3b27", - "0xe7f738ca0d301c92913c1c5bd670f2958bc3bccd349206f798ff38615704da02", - "0xd6023f0729c366624fc9581e69c8217352113d2b15e8824d36b05fef5640d228", - "0x2a426c808ee5b9f8c405875e564962a318666acd06bc6942dfc7a4960dfc0a78", - "0x1346589ec5380ee0dcb41cf0af9e8b39a7395a13b94b97150bcc11a71e2b4c1f", - "0x2fcafbf9b787434cf0f745acd8d880cc553a432810acf1bbd66b433ed11c2c18", - "0x33290d023a695c514e8dc94f04719532e377eb81790e288a0b017312b41a0ff5", - "0xf8befaf58584c66572ce8251e42d002cfc5355a9efc8d5f9bc9088a1f7b9edfd", - "0x1f9f9f7a63d43d358f2499ce9b04ce9f0a4b35546011215fb83a22e68df87844", - "0x9a9c4815d5827ec055e39ec001252a5035e6317a05b508bbc3e6907b53109706", - "0x3b277af862fc12912cb482182175edd1ad4dd7f15b2614e0970c3ea09d1a8de3", - "0xf8967669bc80f68ea5c966bb42117851d80c18e81dfd184efc59491971a9165d", - "0x00814afcf1ecf5f401b450afcb6d73ce3fbf6338e8bc4dcec4c32d5b2ecc6454", - "0x63ce5390474616f8b3b70820e67460c6fda75f41f2a2c66ab467b0c311733f3b", - "0x176619adb025ee77960fcdabada971e20bd2789d8e3cf045c03d4ec2e97b2f79", - "0xcd01a05497515a98f69a8a48b534ea1e741bdee0dadb2a32a97c631212e2e5a5", - "0x0b808e84cb0904210581a81e312236efed3bca37e624f6a6c66bfed4134ebf9b", - "0x42ed1705030f23cbf6525095491f6f82914727b116c85bc24ebd9d3579508554", - "0xeb6c3c39f07929e4d0a2ae0a8f9390f2f286f715095f31f82b471cc3f02ab770", - "0x6d0b0405e16a27285e6645ff8a29e05a6f8d4a4ada9ce08b729caa89af96ea48", - "0xc02fd4ca15b5c7a0eb193e1a679fef5ce2cc9e5732209c8739b31cde876b524a", - "0xed2560d142a34db7c5b20125882fb61b8edc1cf22e12c81f860c47a1239706f2", - "0x9f9d607cb7d98b099460c97bd9bf4abe27524de0aa0494068d52cf380aa36293", - "0xca530177bdd648a62d09f77f03f134e3e3bc0d64bcb93a335ee2ca73d73ac2d3", - "0xf2bd87e7e61b6efaf4339ca27cc8d73df9d3d71799bd50ce1465ce9386350023", - "0xfef3aab8dceff11c8cf8fa282e482c9a4a7c53c471a7e329e9720bf917f744ce", - "0x56002bf72faf92897f58fe2679e3571ca490d6c036f724a80b74aac20970641c", - "0xb207591550eb9be35178a224e6ca46ec82b2a6a019bf961f13e7df2026009b9d", - "0x2b52868f8b17ef786cecd0a9a4fcfcee6d9551d4c9104a5ab8d8eeadb707d2dc", - "0x7c4acd1df61f61cc5e2f4ee9decc296d98f3f3fc89ae66bd07d41c84ff56a288", - "0x30adce03c6c2b1532e07e33a24c7f73d08feccb5497e33910226388032a80812", - "0xc79ae82c871ef39b9558c1339ea365dac86469a1165fa0d36b7775c49d3ac7f1", - "0xe4e52fb5b9f83cce4205f09f720c5a4556d0511e20c5c3e0c63f9a954a010adf", - "0xf53f57bfaa2aad6de5fd3f236bc8a4361e2af82fcc198bc058e5d7a5bccd0a02", - "0x5098a75b4dd1d02507416a6e3b8cf0b0ea6cb15bd68420ebc3b7915eb7839f1f", - "0xf9c0e2cbce9fe64623de15c2f85d7c3928ab0b37275660a242770219e3a96d93", - "0xc5b62e1433192d067cbd106d3c0ca218383ddbf14c00c9a13d56449f2bbb3a08", - "0x80d8820d2078d42c1a38e8d6f9f81d58cee3846aaa899261ee1ed85ff4d42533", - "0xd886a4c921ab8b11a3e855a7893664f09629814ad6df0f3bd36cfc37a42a0bbf", - "0xc1375c4f051bd893b5e8a3d768f398b396efec9c4b0e4c2b0d352bb69c9fb394", - "0x20ae02c26b613b05737e26202e18100c3eeeb84c078bb3d4912be5cd99787a2e", - "0xcfa25f8226fe9e61e35fc2ed63a52f2b2a4c3dee8e192831f7a0cda7b77022e7", - "0xf1506b0bb1dda73bbc12eaf379580952e65880b0aef361231d8686619da83b9b", - "0x0033b129deb9e2591fd3375084de6f00fc3f72d37911eb2d623820de7c03ed9c", - "0x245b2677360776f20b01ae7278b41980a63eb45cf6f7566f5804dbcb7e3ea66c", - "0x73081e452c9aa38f130ad22fa653519888b429d3e1aae6701fa5407ed088872a", - "0xcf8224c7675985b9d8411694d881a452b850c3d41331602c6d2ddc552500d9b6", - "0x8278bf3e350fa2532dba0e7082f64bb1fc08f3d2279525545a62ea488835c756", - "0x134a0e1ae78dd0da790d1dc3b1710caaeb3a6015c82fda8efdbd1e5ceb154767", - "0xa5a77ceb096cbc27b2eae356c942fde9db8d72161e9e559ad6376143d7f4d609", - "0xb34c9ca03d44c2512795c8572527f6285c3a6e954cf17668c51e8f22de24efb0", - "0x7cbf51c29022b4cf1e6203cf1322481a36f510c35bc1d78d972101e9593fcdc9", - "0xc3162682c647172119a2cebd3ce42fab8f20be84b20b3c4a85b4b2196eefb80b", - "0x4c8e78cad3ed836eb63af411b683a3d21d7a854004d026ee928870e7d8c94239", - "0x92a9ccef236e16e8d527f9b4e25861a670763b2845b9e7f2831d83f7411c1bb1", - "0xf82d9c2c67a3a6050a1003dfbcdbc6861ca699d41b2bdd69099196122365e218", - "0xafbe03db4a223222536b6ae7e65a66303bb2c89f978a49db05dcee8ce3eb9bfd", - "0x97db73ba455ac03902bfa5335d1efd1c3a08fdc5fe1bda5c402413a6c6bf6439", - "0xe87c9f587da80c6ebf6416e7851fdbd1a5e44b7bf68b7b5b93cae18f5b71001d", - "0x04977189709d5e8243e2cff4d6e9bebae049fcd78854c414276049c1dd8899a2", - "0x0dfb0bc4588450ba7f992ac46b5e68b740bc2373a944c70a440a25222451f868", - "0x2092f8d8303fdfd369f4a24ef504a9d48acdfdfab2e37115d5778a0adfac0082", - "0x19d81b509c576de5a558f9c8fb929d84fea577428fed5af023a7f7bb502e3643", - "0x935fb703a9a030bdad9db265cb3292c2c9a038f1837e049d1d5045b09047a4f0", - "0xbec9f75266da91b02a2d1b5f5b5449f7e6ba28ae402979af40a83c9b39cb483a", - "0x320c3fb2a136d04495087d27b0a5dc6ce90bf7a1bf15ae65d48146a9661f2aa9", - "0x364c285cabc386ccf0140bd24bbc98e37a88b6f88bdb7d34ff1d4a75f29573fc", - "0xe595476b0844351af48748f788d15b53eed8507c6c45faddc189d602d8de1bcc", - "0x803f52547a1ca56abdca4b4ccc28bff586fb3372d9bf677b3ca3400184d009ba", - "0xb90914aebed7eee12fb7d25e80c2f37f68dbf2a698faf2ac92faf0cffafdaf8f", - "0x129abb430b863be8ccb3e33a3194552ede9eca4bebdf34d6b53748213a734ff8", - "0x9a6dcb68b7b37e705ce7457fb1ca954cb5549eebcc4397ed29083df51362d779", - "0x12bd07e3b6ae0add86cc1467208532430c0c8d7d0e832e9c05134b07e46d1768", - "0x69d228b5d0f85a16596aff9f2b51ffc21497d4331a0c9b6fc5b3b1834654df09", - "0x3cd093afcf1b1588b12da25cdbeab67cf99fbbe9df026f78d3abffbd378eff6b", - "0xecb8e829dae8134f700eec6d11e0157bd761fcf0779bb603cd024d0ee8496717", - "0x0fb6faa0e9b556d1ca1e91f56117d417c29b7368445c18cdd0bc5060ee6c86f4", - "0xf126590823b117b19491aae44cdcfb18a3e130f00371398a254c3c3845ff0592", - "0x5f2432d15b4f94234961ce9e6165e9761accc88999c523315001340b96227519", - "0x60d53f01e91fde0da7f3a05a795a491466a211d6cc91e9e49e42d5bc4bd78606", - "0xf575fd0cb12b72eec6b639210f04b5bbad1a7c806fbc6c2c15ea62f97c2185c3", - "0x3ffe76eb15010487bfd65c38db0425d5df9a14df60dfcf0849bcd540aa1300db", - "0x6dfcea9390145d196db57f5b427d9c5e89f336a5bc81bdc3a4e246370feee2f0", - "0xe3c2e58ff9ff293dd8e2084b333bfefb506382848b1884ad03546f457f0559c2", - "0x807d9d862e2b760628b579b66b206ebcaf247f3d041fd9863fb375dd715b74f6", - "0x11e8e648599d5bbabd99f3693c090d95a7ebb66b82e5ce33ae47f7b570154423", - "0x4b59ea6016b208f2e5bef0606a7e81f05cd679eb7635db9e27e8458980281dbe", - "0x5afe3f29cd36132c4db3fe0d989dac1eb5cd6c7c4fffd22a03691a695df95489", - "0x5fc081154e1402309a9ada4d39f90a083bda62aaf4e643a58b2a1bbff8c030c2", - "0xc8f835e4abfa2f4d3cd3e1f92ed878a78253b828ddb8e89300e3de63cfc01775", - "0xa94346dc4441d16b65996b0b44d1ee7b737f75e6857d22a7a03f5cc9b7156c97", - "0x7a9d66965c11cedc5af224b42d407423d56dc291f9a6903f013e4c2801e28f94", - "0xe1592fdfc80452f70c8372bf59824166ccfed92ca6bbfb0a7f196bfbc11a3d7d", - "0x5800287a2051dc265161d6197ab2f0221dfc918f1b37b685f8d23c66af24412e", - "0x875c7d11313b43a3b2df625bf5f0f816b2743ba44165fda1a34c0d3c9d53c54c", - "0x9d87149d991d4a54cdd689e156c37eccb9c2e3022cef97d91bc04667905f8a7b", - "0xd39a5b4c38441cb4a7dff3a22d8ab37691f21dc3af438f4a1591058c214d1284", - "0x6de58f2f7f2b3f756b6c3683596b11c1b68235f032ff31561f92c6d2e0d31575", - "0x0d4b0e13f898561309b8b4ed0dae1f0698d4433aeb81a4e66a045b69c4356c94", - "0x82e58ed2c7e584e42f9f284a1171461338cdc40661e5457ce86621fb9939e3a5", - "0x0f6af8168a7e2e6f35539a5477d9dc4901af3eca6e3257d7f0ecc0b573bf2c73", - "0xe3eb2cce31d8e65106536c608a154388fa00cc5a481ce635c55eb130f021f218", - "0xf6829b8da3ddc1f71b3b14ca1b06c13e9e454a364899c4ce726d3b9a9b55b1ca", - "0x248b513050ea763394365a1ff6a295b6648fd1af0a5703daa7dac423cf5967ad", - "0xcbbd5076dc646780f3e582343c66090e477e32b56210adb84992e340b74a798d", - "0x6e844d7759a5aff03abb0efcc0cd74285a20521afd06c0ff98b82a2bf17a7224", - "0x116a574a2fd13487716a1e3bf78cbbc998dd5b3604caa567662055de8691a459", - "0xfe36c7f148af1c056b913b4f52c8dc4fa0e6cba42b1e7f5204c1d300f9ae1ab4", - "0x114da06ec0e3d1b14027802c9c3bb80922213786464dd5b6a1ee2a12523761aa", - "0x9c451c4cce3cf45b173cf2abf03ffcd11f5d471db49c5abc66603795c6c55494", - "0xd0dca6ee9e3cf1d9277aa5b9a97acf7a45d63d0f76ea8cd84366e2c787489061", - "0x4f93256e5e4af9d0771ee8cf5cf84b80d2f7298a76c77ddaeccd835d4717864f", - "0x174ab2c74403d9538aa14bb0549c33756095c78e647b18eaa818790876996f8a", - "0x0d62cbeb56e7c3db3c6845badab75d504695b40ae7e8b1e6ad13097f5cf65154", - "0x70c96dd19bca237759ee01a00ae6ec75873e2c1384908c86310e766e8352c299", - "0x2ba3360edd065360b551e07aac2fe6a1b646fae955c29d39c1e981b8b8541a72", - "0x64400f31f7e3deb29c6f22aee609a502db3e9c6c9eebd5a93087b6a1636d44ba", - "0x255c5d43117d4d6e3ee88610b3fa54a679ab1eb358153147188a5fbde1556db1", - "0x290e2c3125591d2f128758525955799fe08eb933dc0e22519810e63a10f0ea69" - ], - "cur_hash": "0x3c869591ac4295afc75154eaaf7a8b59a41af3cdcbad9d8c48fb7ef9853f9ec6" - }, - "withdrawals": [ - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x119204e" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x1193fa3" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x118a669" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x118d295" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x1195db7" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x3c46d9e" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x118b8bb" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x11994d6" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x11822b1" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x118b828" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x118b7e1" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x117492d" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x3c1adb5" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x11713d0" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x1197bef" - ], - [ - "0xf197c6f2ac14d25ee2789a73e4847732c7f16bc9", - "0x1181b16" - ] + "checkpoint_state_trie_root": "0x319da7faf76836d1ca1c48e0540a97c0d7f2515b7fd7be7dfb1aef9ed5dd588a", + "checkpoint_consolidated_hash": [ + 16744453977166140464, + 7017271634138698061, + 14284306277120741581, + 353542841044461191 ] - }, - "checkpoint_state_trie_root": "0x319da7faf76836d1ca1c48e0540a97c0d7f2515b7fd7be7dfb1aef9ed5dd588a" + } } -}] \ No newline at end of file +] \ No newline at end of file diff --git a/trace_decoder/tests/cases/b20240052_main.json b/trace_decoder/tests/cases/b20240052_main.json index 247fd5df0..f3361124f 100644 --- a/trace_decoder/tests/cases/b20240052_main.json +++ b/trace_decoder/tests/cases/b20240052_main.json @@ -9192,7 +9192,13 @@ ] ] }, - "checkpoint_state_trie_root": "0xaeaab1c6af58526a7a36de5210273f9c7ce2dc936293520b2ecb19b1d786976a" + "checkpoint_state_trie_root": "0xaeaab1c6af58526a7a36de5210273f9c7ce2dc936293520b2ecb19b1d786976a", + "checkpoint_consolidated_hash": [ + 15218262125050039685, + 12215100074924195986, + 6375618978774892202, + 661263567094735674 + ] } } -] +] \ No newline at end of file diff --git a/trace_decoder/tests/cases/b20240058_main.json b/trace_decoder/tests/cases/b20240058_main.json index 37f5ba2cb..379bd031f 100644 --- a/trace_decoder/tests/cases/b20240058_main.json +++ b/trace_decoder/tests/cases/b20240058_main.json @@ -10040,7 +10040,13 @@ ] ] }, - "checkpoint_state_trie_root": "0xbe6449b0d590db000103d74f27cd54f4eaf3ed103cdc1c50f7db1ec5dbc26bbc" + "checkpoint_state_trie_root": "0xbe6449b0d590db000103d74f27cd54f4eaf3ed103cdc1c50f7db1ec5dbc26bbc", + "checkpoint_consolidated_hash": [ + 2482638101945590192, + 5595286337531102146, + 7727862006001321369, + 2952748840780947829 + ] } } -] +] \ No newline at end of file diff --git a/trace_decoder/tests/cases/b20472570_main.json b/trace_decoder/tests/cases/b20472570_main.json index 208deb5bf..b07002ba2 100644 --- a/trace_decoder/tests/cases/b20472570_main.json +++ b/trace_decoder/tests/cases/b20472570_main.json @@ -3339,7 +3339,13 @@ ] ] }, - "checkpoint_state_trie_root": "0x349c33a12c9ac7fee19b759a1aff095d5d734fda61db4295bc8f783b345f10fd" + "checkpoint_state_trie_root": "0x349c33a12c9ac7fee19b759a1aff095d5d734fda61db4295bc8f783b345f10fd", + "checkpoint_consolidated_hash": [ + 18077349309026416086, + 5578611654834857686, + 14273102918993724967, + 7855003975346603760 + ] } } ] diff --git a/trace_decoder/tests/cases/b28_dev.json b/trace_decoder/tests/cases/b28_dev.json index a51aac3db..afa4bf38e 100644 --- a/trace_decoder/tests/cases/b28_dev.json +++ b/trace_decoder/tests/cases/b28_dev.json @@ -318,7 +318,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0x106d584f6804109c493182d0bb8ef06380aea582090f4c2927276869a8d1e436" + "checkpoint_state_trie_root": "0x106d584f6804109c493182d0bb8ef06380aea582090f4c2927276869a8d1e436", + "checkpoint_consolidated_hash": [ + 11541128027127827815, + 532546647585733491, + 11042431666052260492, + 18221434420548059701 + ] } } ] diff --git a/trace_decoder/tests/cases/b4_dev.json b/trace_decoder/tests/cases/b4_dev.json index 46ed3e6c9..6ceed019a 100644 --- a/trace_decoder/tests/cases/b4_dev.json +++ b/trace_decoder/tests/cases/b4_dev.json @@ -466,7 +466,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0x765b89dc0bbd05503b9074455cb6b1f65f23147cc2f7e7f6cd56b262569ce02e" + "checkpoint_state_trie_root": "0x765b89dc0bbd05503b9074455cb6b1f65f23147cc2f7e7f6cd56b262569ce02e", + "checkpoint_consolidated_hash": [ + 8754384126569858367, + 13664083137212440471, + 11466794132567065062, + 13220125099178255864 + ] } } ] diff --git a/trace_decoder/tests/consistent-with-header.rs b/trace_decoder/tests/consistent-with-header.rs index 0cd477680..f82027e82 100644 --- a/trace_decoder/tests/consistent-with-header.rs +++ b/trace_decoder/tests/consistent-with-header.rs @@ -23,7 +23,7 @@ fn main() -> anyhow::Result<()> { } in cases()? { trials.push(Trial::test(format!("{name}@{batch_size}"), move || { - let gen_inputs = trace_decoder::entrypoint(trace, other.clone(), batch_size, false) + let gen_inputs = trace_decoder::entrypoint(trace, other.clone(), batch_size) .map_err(|e| format!("{e:?}"))?; // get the full cause chain check!(gen_inputs.len() >= 2); check!( diff --git a/trace_decoder/tests/simulate-execution.rs b/trace_decoder/tests/simulate-execution.rs index 080a02c62..c4cbe53b2 100644 --- a/trace_decoder/tests/simulate-execution.rs +++ b/trace_decoder/tests/simulate-execution.rs @@ -19,7 +19,7 @@ fn main() -> anyhow::Result<()> { other, } in cases()? { - let gen_inputs = trace_decoder::entrypoint(trace, other, batch_size, false).context( + let gen_inputs = trace_decoder::entrypoint(trace, other, batch_size).context( format!("error in `trace_decoder` for {name} at batch size {batch_size}"), )?; for (ix, gi) in gen_inputs.into_iter().enumerate() { diff --git a/zero_bin/common/Cargo.toml b/zero_bin/common/Cargo.toml index 7171fa2a8..1b69be922 100644 --- a/zero_bin/common/Cargo.toml +++ b/zero_bin/common/Cargo.toml @@ -14,32 +14,46 @@ anyhow = { workspace = true } async-stream = { workspace = true } cargo_metadata = { workspace = true } clap = { workspace = true } -evm_arithmetization = { workspace = true } +dotenvy = { workspace = true } futures = { workspace = true } lru = { workspace = true } once_cell = { workspace = true } plonky2 = { workspace = true } -proof_gen = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } +serde_path_to_error = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } -trace_decoder = { workspace = true } tracing = { workspace = true } +tracing-subscriber = { workspace = true } vergen = { workspace = true } directories = "5.0.1" +# Local dependencies +evm_arithmetization = { workspace = true } +proof_gen = { workspace = true } +zk_evm_common = { workspace = true } + [build-dependencies] +anyhow = { workspace = true } cargo_metadata = { workspace = true } vergen = { workspace = true } -anyhow = { workspace = true } + [features] -default = [] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "proof_gen/eth_mainnet", +] cdk_erigon = [ "evm_arithmetization/cdk_erigon", - "proof_gen/cdk_erigon" + "proof_gen/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "proof_gen/polygon_pos", ] [lints] diff --git a/zero_bin/common/src/env.rs b/zero_bin/common/src/env.rs new file mode 100644 index 000000000..f1356247e --- /dev/null +++ b/zero_bin/common/src/env.rs @@ -0,0 +1,16 @@ +use std::io; + +use dotenvy::dotenv; +use tracing::warn; + +/// Attempt to load in the local `.env` if present and set any environment +/// variables specified inside of it. +/// +/// To keep things simple, any IO error we will treat as the file not existing +/// and continue moving on without the `env` variables set. +pub fn load_dotenvy_vars_if_present() { + match dotenv() { + Ok(_) | Err(dotenvy::Error::Io(io::Error { .. })) => (), + Err(e) => warn!("Found local `.env` file but was unable to parse it! (err: {e})",), + } +} diff --git a/zero_bin/common/src/fs.rs b/zero_bin/common/src/fs.rs index 7603f5100..6d274903e 100644 --- a/zero_bin/common/src/fs.rs +++ b/zero_bin/common/src/fs.rs @@ -1,7 +1,22 @@ +use std::fs::File; use std::path::PathBuf; +use proof_gen::proof_types::GeneratedBlockProof; + pub fn generate_block_proof_file_name(directory: &Option<&str>, block_height: u64) -> PathBuf { let mut path = PathBuf::from(directory.unwrap_or("")); path.push(format!("b{}.zkproof", block_height)); path } + +pub fn get_previous_proof(path: Option) -> anyhow::Result> { + if path.is_none() { + return Ok(None); + } + + let path = path.unwrap(); + let file = File::open(path)?; + let des = &mut serde_json::Deserializer::from_reader(&file); + let proof: GeneratedBlockProof = serde_path_to_error::deserialize(des)?; + Ok(Some(proof)) +} diff --git a/zero_bin/common/src/lib.rs b/zero_bin/common/src/lib.rs index 42ce661d7..890558de6 100644 --- a/zero_bin/common/src/lib.rs +++ b/zero_bin/common/src/lib.rs @@ -1,10 +1,14 @@ +zk_evm_common::check_chain_features!(); + pub mod block_interval; pub mod debug_utils; +pub mod env; pub mod fs; pub mod parsing; pub mod pre_checks; pub mod prover_state; pub mod provider; +pub mod tracing; pub mod version; /// Size of the channel used to send block prover inputs to the per block diff --git a/zero_bin/common/src/prover_state/mod.rs b/zero_bin/common/src/prover_state/mod.rs index ca049268f..793b726f1 100644 --- a/zero_bin/common/src/prover_state/mod.rs +++ b/zero_bin/common/src/prover_state/mod.rs @@ -35,6 +35,7 @@ pub mod circuit; pub mod cli; pub mod persistence; +// TODO(Robin): https://github.com/0xPolygonZero/zk_evm/issues/531 pub(crate) type Config = PoseidonGoldilocksConfig; pub(crate) type Field = GoldilocksField; pub(crate) const SIZE: usize = 2; @@ -195,7 +196,7 @@ impl ProverStateManager { /// and finally aggregating them to a final transaction proof. fn segment_proof_on_demand( &self, - input: TrimmedGenerationInputs, + input: TrimmedGenerationInputs, segment_data: &mut GenerationSegmentData, ) -> anyhow::Result { let config = StarkConfig::standard_fast_config(); @@ -224,7 +225,7 @@ impl ProverStateManager { /// circuit. fn segment_proof_monolithic( &self, - input: TrimmedGenerationInputs, + input: TrimmedGenerationInputs, segment_data: &mut GenerationSegmentData, ) -> anyhow::Result { let p_out = p_state().state.prove_segment( @@ -256,7 +257,7 @@ impl ProverStateManager { /// needed. pub fn generate_segment_proof( &self, - input: (TrimmedGenerationInputs, GenerationSegmentData), + input: (TrimmedGenerationInputs, GenerationSegmentData), ) -> anyhow::Result { let (generation_inputs, mut segment_data) = input; diff --git a/zero_bin/worker/src/init.rs b/zero_bin/common/src/tracing.rs similarity index 92% rename from zero_bin/worker/src/init.rs rename to zero_bin/common/src/tracing.rs index f93914895..6c3f9a8bc 100644 --- a/zero_bin/worker/src/init.rs +++ b/zero_bin/common/src/tracing.rs @@ -1,5 +1,6 @@ use tracing_subscriber::{prelude::*, util::SubscriberInitExt, EnvFilter}; -pub(crate) fn tracing() { + +pub fn init() { tracing_subscriber::Registry::default() .with( tracing_subscriber::fmt::layer() diff --git a/zero_bin/leader/Cargo.toml b/zero_bin/leader/Cargo.toml index cad05cd22..27d22a347 100644 --- a/zero_bin/leader/Cargo.toml +++ b/zero_bin/leader/Cargo.toml @@ -10,37 +10,54 @@ categories.workspace = true build = "../common/build.rs" [dependencies] -paladin-core = { workspace = true } -clap = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } +alloy = { workspace = true } anyhow = { workspace = true } +axum = { workspace = true } +clap = { workspace = true } +futures = { workspace = true } +paladin-core = { workspace = true } serde = { workspace = true } -dotenvy = { workspace = true } -tokio = { workspace = true } -proof_gen = { workspace = true } serde_json = { workspace = true } serde_path_to_error = { workspace = true } -futures = { workspace = true } -alloy.workspace = true -axum = { workspace = true } +tokio = { workspace = true } toml = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } # Local dependencies +evm_arithmetization = { workspace = true } ops = { workspace = true } +proof_gen = { workspace = true } prover = { workspace = true } rpc = { workspace = true } -evm_arithmetization = { workspace = true } zero_bin_common = { workspace = true } +zk_evm_common = { workspace = true } [features] -default = [] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "ops/eth_mainnet", + "proof_gen/eth_mainnet", + "prover/eth_mainnet", + "rpc/eth_mainnet", + "zero_bin_common/eth_mainnet", +] cdk_erigon = [ "evm_arithmetization/cdk_erigon", + "ops/cdk_erigon", "proof_gen/cdk_erigon", "prover/cdk_erigon", "rpc/cdk_erigon", - "zero_bin_common/cdk_erigon" + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "ops/polygon_pos", + "proof_gen/polygon_pos", + "prover/polygon_pos", + "rpc/polygon_pos", + "zero_bin_common/polygon_pos", ] [build-dependencies] diff --git a/zero_bin/leader/src/client.rs b/zero_bin/leader/src/client.rs index ea4e5e9ae..a0f38727d 100644 --- a/zero_bin/leader/src/client.rs +++ b/zero_bin/leader/src/client.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use alloy::rpc::types::{BlockId, BlockNumberOrTag, BlockTransactionsKind}; +use alloy::rpc::types::{BlockId, BlockNumberOrTag}; use alloy::transports::http::reqwest::Url; use anyhow::{anyhow, Result}; use paladin::runtime::Runtime; @@ -49,15 +49,6 @@ pub(crate) async fn client_main( &leader_config.previous_proof, block_interval.get_start_block()?, )?; - // Grab interval checkpoint block state trie. - let checkpoint_state_trie_root = cached_provider - .get_block( - leader_config.checkpoint_block_number.into(), - BlockTransactionsKind::Hashes, - ) - .await? - .header - .state_root; // Create a channel for block prover input and use it to send prover input to // the proving task. The second element of the tuple is a flag indicating @@ -94,7 +85,7 @@ pub(crate) async fn client_main( let block_prover_input = rpc::block_prover_input( cached_provider.clone(), block_id, - checkpoint_state_trie_root, + leader_config.checkpoint_block_number, rpc_params.rpc_type, ) .await?; diff --git a/zero_bin/leader/src/init.rs b/zero_bin/leader/src/init.rs deleted file mode 100644 index f93914895..000000000 --- a/zero_bin/leader/src/init.rs +++ /dev/null @@ -1,11 +0,0 @@ -use tracing_subscriber::{prelude::*, util::SubscriberInitExt, EnvFilter}; -pub(crate) fn tracing() { - tracing_subscriber::Registry::default() - .with( - tracing_subscriber::fmt::layer() - .with_ansi(false) - .compact() - .with_filter(EnvFilter::from_default_env()), - ) - .init(); -} diff --git a/zero_bin/leader/src/main.rs b/zero_bin/leader/src/main.rs index c0311b15c..61ffb98ce 100644 --- a/zero_bin/leader/src/main.rs +++ b/zero_bin/leader/src/main.rs @@ -1,17 +1,18 @@ +zk_evm_common::check_chain_features!(); + +use std::env; use std::sync::Arc; -use std::{env, io}; -use std::{fs::File, path::PathBuf}; use anyhow::Result; use clap::Parser; use cli::Command; use client::RpcParams; -use dotenvy::dotenv; use ops::register; use paladin::runtime::Runtime; -use proof_gen::proof_types::GeneratedBlockProof; use prover::ProverConfig; -use tracing::{info, warn}; +use tracing::info; +use zero_bin_common::env::load_dotenvy_vars_if_present; +use zero_bin_common::fs::get_previous_proof; use zero_bin_common::{ block_interval::BlockInterval, prover_state::persistence::set_circuit_cache_dir_env_if_not_set, }; @@ -22,26 +23,13 @@ use crate::client::{client_main, LeaderConfig}; mod cli; mod client; mod http; -mod init; mod stdio; -fn get_previous_proof(path: Option) -> Result> { - if path.is_none() { - return Ok(None); - } - - let path = path.unwrap(); - let file = File::open(path)?; - let des = &mut serde_json::Deserializer::from_reader(&file); - let proof: GeneratedBlockProof = serde_path_to_error::deserialize(des)?; - Ok(Some(proof)) -} - #[tokio::main] async fn main() -> Result<()> { load_dotenvy_vars_if_present(); set_circuit_cache_dir_env_if_not_set()?; - init::tracing(); + zero_bin_common::tracing::init(); let args: Vec = env::args().collect(); @@ -127,15 +115,3 @@ async fn main() -> Result<()> { Ok(()) } - -/// Attempt to load in the local `.env` if present and set any environment -/// variables specified inside of it. -/// -/// To keep things simple, any IO error we will treat as the file not existing -/// and continue moving on without the `env` variables set. -fn load_dotenvy_vars_if_present() { - match dotenv() { - Ok(_) | Err(dotenvy::Error::Io(io::Error { .. })) => (), - Err(e) => warn!("Found local `.env` file but was unable to parse it! (err: {e})",), - } -} diff --git a/zero_bin/ops/Cargo.toml b/zero_bin/ops/Cargo.toml index 8975cc8d5..4e49a3ae6 100644 --- a/zero_bin/ops/Cargo.toml +++ b/zero_bin/ops/Cargo.toml @@ -9,15 +9,31 @@ keywords.workspace = true categories.workspace = true [dependencies] +keccak-hash = { workspace = true } paladin-core = { workspace = true } serde = { workspace = true } -evm_arithmetization = { workspace = true } -proof_gen = { workspace = true } tracing = { workspace = true } -trace_decoder = { workspace = true } -keccak-hash = { workspace = true } -zero_bin_common = { path = "../common" } +# Local dependencies +evm_arithmetization = { workspace = true } +proof_gen = { workspace = true } +zero_bin_common = { workspace = true } +zk_evm_common = { workspace = true } [features] -default = [] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "proof_gen/eth_mainnet", + "zero_bin_common/eth_mainnet", +] +cdk_erigon = [ + "evm_arithmetization/cdk_erigon", + "proof_gen/cdk_erigon", + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "proof_gen/polygon_pos", + "zero_bin_common/polygon_pos", +] diff --git a/zero_bin/ops/src/lib.rs b/zero_bin/ops/src/lib.rs index 4de5e0c30..3a6d3854c 100644 --- a/zero_bin/ops/src/lib.rs +++ b/zero_bin/ops/src/lib.rs @@ -1,3 +1,5 @@ +zk_evm_common::check_chain_features!(); + use std::time::Instant; use evm_arithmetization::generation::TrimmedGenerationInputs; @@ -27,7 +29,7 @@ pub struct SegmentProof { } impl Operation for SegmentProof { - type Input = evm_arithmetization::AllData; + type Input = evm_arithmetization::AllData; type Output = proof_gen::proof_types::SegmentAggregatableProof; fn execute(&self, all_data: Self::Input) -> Result { @@ -72,7 +74,7 @@ pub struct SegmentProofTestOnly { } impl Operation for SegmentProofTestOnly { - type Input = (GenerationInputs, usize); + type Input = (GenerationInputs, usize); type Output = (); fn execute(&self, inputs: Self::Input) -> Result { @@ -113,7 +115,7 @@ struct SegmentProofSpan { impl SegmentProofSpan { /// Get a unique id for the transaction proof. - fn get_id(ir: &TrimmedGenerationInputs, segment_index: usize) -> String { + fn get_id(ir: &TrimmedGenerationInputs, segment_index: usize) -> String { if ir.txn_hashes.len() == 1 { format!( "b{} - {} ({})", @@ -134,7 +136,7 @@ impl SegmentProofSpan { /// /// Either the first 8 characters of the hex-encoded hash of the first and /// last transactions, or "Dummy" if there is no transaction. - fn get_descriptor(ir: &TrimmedGenerationInputs) -> String { + fn get_descriptor(ir: &TrimmedGenerationInputs) -> String { if ir.txn_hashes.is_empty() { "Dummy".to_string() } else if ir.txn_hashes.len() == 1 { @@ -157,7 +159,7 @@ impl SegmentProofSpan { /// Create a new transaction proof span. /// /// When dropped, it logs the time taken by the transaction proof. - fn new(ir: &TrimmedGenerationInputs, segment_index: usize) -> Self { + fn new(ir: &TrimmedGenerationInputs, segment_index: usize) -> Self { let id = Self::get_id(ir, segment_index); let span = info_span!("p_gen", id).entered(); let start = Instant::now(); @@ -186,7 +188,7 @@ pub struct SegmentAggProof { pub save_inputs_on_error: bool, } -fn get_seg_agg_proof_public_values(elem: SegmentAggregatableProof) -> PublicValues { +fn get_seg_agg_proof_public_values(elem: SegmentAggregatableProof) -> PublicValues { match elem { SegmentAggregatableProof::Seg(info) => info.p_vals, SegmentAggregatableProof::Agg(info) => info.p_vals, @@ -230,7 +232,7 @@ impl Monoid for SegmentAggProof { pub struct BatchAggProof { pub save_inputs_on_error: bool, } -fn get_agg_proof_public_values(elem: BatchAggregatableProof) -> PublicValues { +fn get_agg_proof_public_values(elem: BatchAggregatableProof) -> PublicValues { match elem { BatchAggregatableProof::Segment(info) => info.p_vals, BatchAggregatableProof::Txn(info) => info.p_vals, diff --git a/zero_bin/prover/Cargo.toml b/zero_bin/prover/Cargo.toml index 6295f65b4..a2ad4ccb4 100644 --- a/zero_bin/prover/Cargo.toml +++ b/zero_bin/prover/Cargo.toml @@ -9,28 +9,52 @@ keywords.workspace = true categories.workspace = true [dependencies] -alloy.workspace = true +alloy = { workspace = true } anyhow = { workspace = true } -clap = {workspace = true, features = ["derive", "string"] } -evm_arithmetization = { workspace = true } +clap = { workspace = true, features = ["derive", "string"] } futures = { workspace = true } num-traits = { workspace = true } ops = { workspace = true } paladin-core = { workspace = true } plonky2 = { workspace = true } plonky2_maybe_rayon = { workspace = true } -proof_gen = { workspace = true } ruint = { workspace = true, features = ["num-traits", "primitive-types"] } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } -trace_decoder = { workspace = true } tracing = { workspace = true } + +# Local dependencies +evm_arithmetization = { workspace = true } +proof_gen = { workspace = true } +trace_decoder = { workspace = true } zero_bin_common = { workspace = true } +zk_evm_common = { workspace = true } + [features] -default = [] -cdk_erigon = [] +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "ops/eth_mainnet", + "proof_gen/eth_mainnet", + "trace_decoder/eth_mainnet", + "zero_bin_common/eth_mainnet", +] +cdk_erigon = [ + "evm_arithmetization/cdk_erigon", + "ops/cdk_erigon", + "proof_gen/cdk_erigon", + "trace_decoder/cdk_erigon", + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "ops/polygon_pos", + "proof_gen/polygon_pos", + "trace_decoder/polygon_pos", + "zero_bin_common/polygon_pos", +] [lints] workspace = true diff --git a/zero_bin/prover/src/lib.rs b/zero_bin/prover/src/lib.rs index 8aab306bb..29bf42127 100644 --- a/zero_bin/prover/src/lib.rs +++ b/zero_bin/prover/src/lib.rs @@ -1,3 +1,5 @@ +zk_evm_common::check_chain_features!(); + pub mod cli; use std::future::Future; @@ -13,11 +15,24 @@ use proof_gen::proof_types::GeneratedBlockProof; use serde::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; use tokio::sync::mpsc::Receiver; -use tokio::sync::oneshot; +use tokio::sync::{oneshot, Semaphore}; use trace_decoder::{BlockTrace, OtherBlockData}; use tracing::{error, info}; use zero_bin_common::fs::generate_block_proof_file_name; +// All proving tasks are executed concurrently, which can cause issues for large +// block intervals, where distant future blocks may be proven first. +// +// We then create a pool to limit the number of parallel proving block +// tasks, retrieving new blocks in increasing order when some block proofs are +// complete. +// +// While proving a block interval, we will output proofs corresponding to block +// batches as soon as they are generated. +const PARALLEL_BLOCK_PROVING_PERMIT_POOL_SIZE: usize = 16; +static PARALLEL_BLOCK_PROVING_PERMIT_POOL: Semaphore = + Semaphore::const_new(PARALLEL_BLOCK_PROVING_PERMIT_POOL_SIZE); + #[derive(Debug, Clone)] pub struct ProverConfig { pub batch_size: usize, @@ -60,13 +75,8 @@ impl BlockProverInput { let block_number = self.get_block_number(); - let use_burn_addr = cfg!(feature = "cdk_erigon"); - let block_generation_inputs = trace_decoder::entrypoint( - self.block_trace, - self.other_data, - batch_size, - use_burn_addr, - )?; + let block_generation_inputs = + trace_decoder::entrypoint(self.block_trace, self.other_data, batch_size)?; // Create segment proof. let seg_prove_ops = ops::SegmentProof { @@ -155,13 +165,8 @@ impl BlockProverInput { let block_number = self.get_block_number(); info!("Testing witness generation for block {block_number}."); - let use_burn_addr = cfg!(feature = "cdk_erigon"); - let block_generation_inputs = trace_decoder::entrypoint( - self.block_trace, - self.other_data, - batch_size, - use_burn_addr, - )?; + let block_generation_inputs = + trace_decoder::entrypoint(self.block_trace, self.other_data, batch_size)?; let seg_ops = ops::SegmentProofTestOnly { save_inputs_on_error, @@ -242,6 +247,9 @@ pub async fn prove( let previous_block_proof = prev_proof.take(); let runtime = runtime.clone(); let block_number = block_prover_input.get_block_number(); + + let prove_permit = PARALLEL_BLOCK_PROVING_PERMIT_POOL.acquire().await?; + let _abort_handle = task_set.spawn(async move { let block_number = block_prover_input.get_block_number(); info!("Proving block {block_number}"); @@ -253,6 +261,7 @@ pub async fn prove( prover_config.clone(), ) .then(move |proof| async move { + drop(prove_permit); let proof = proof.inspect_err(|e| { error!("failed to generate proof for block {block_number}, error {e:?}") })?; diff --git a/zero_bin/rpc/Cargo.toml b/zero_bin/rpc/Cargo.toml index 046461569..c694335d4 100644 --- a/zero_bin/rpc/Cargo.toml +++ b/zero_bin/rpc/Cargo.toml @@ -11,32 +11,55 @@ build = "../common/build.rs" [dependencies] __compat_primitive_types = { workspace = true } -alloy.workspace = true +alloy = { workspace = true } +alloy-compat = "0.1.0" anyhow = { workspace = true } clap = { workspace = true } -evm_arithmetization = { workspace = true } futures = { workspace = true } hex = { workspace = true } -mpt_trie = { workspace = true } +itertools = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } tower = { workspace = true, features = ["retry"] } -trace_decoder = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } url = { workspace = true } -itertools = { workspace = true } # Local dependencies compat = { workspace = true } -zero_bin_common = { workspace = true } +evm_arithmetization = { workspace = true } +mpt_trie = { workspace = true } +proof_gen = { workspace = true } prover = { workspace = true } +trace_decoder = { workspace = true } +zero_bin_common = { workspace = true } +zk_evm_common = { workspace = true } [build-dependencies] +anyhow = { workspace = true } cargo_metadata = { workspace = true } vergen = { workspace = true } -anyhow = { workspace = true } [features] -cdk_erigon = [] \ No newline at end of file +default = ["eth_mainnet"] +eth_mainnet = [ + "evm_arithmetization/eth_mainnet", + "proof_gen/eth_mainnet", + "prover/eth_mainnet", + "trace_decoder/eth_mainnet", + "zero_bin_common/eth_mainnet", +] +cdk_erigon = [ + "evm_arithmetization/cdk_erigon", + "proof_gen/cdk_erigon", + "prover/cdk_erigon", + "trace_decoder/cdk_erigon", + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "evm_arithmetization/polygon_pos", + "prover/polygon_pos", + "trace_decoder/polygon_pos", + "zero_bin_common/polygon_pos", +] diff --git a/zero_bin/rpc/src/jerigon.rs b/zero_bin/rpc/src/jerigon.rs index f23b804c3..00d56cf48 100644 --- a/zero_bin/rpc/src/jerigon.rs +++ b/zero_bin/rpc/src/jerigon.rs @@ -1,6 +1,4 @@ -use alloy::{ - primitives::B256, providers::Provider, rpc::types::eth::BlockId, transports::Transport, -}; +use alloy::{providers::Provider, rpc::types::eth::BlockId, transports::Transport}; use anyhow::Context as _; use prover::BlockProverInput; use serde::Deserialize; @@ -21,7 +19,7 @@ pub struct ZeroTxResult { pub async fn block_prover_input( cached_provider: std::sync::Arc>, target_block_id: BlockId, - checkpoint_state_trie_root: B256, + checkpoint_block_number: u64, ) -> anyhow::Result where ProviderT: Provider, @@ -46,8 +44,7 @@ where .await?; let other_data = - fetch_other_block_data(cached_provider, target_block_id, checkpoint_state_trie_root) - .await?; + fetch_other_block_data(cached_provider, target_block_id, checkpoint_block_number).await?; // Assemble Ok(BlockProverInput { diff --git a/zero_bin/rpc/src/lib.rs b/zero_bin/rpc/src/lib.rs index 58df5430a..87a581a14 100644 --- a/zero_bin/rpc/src/lib.rs +++ b/zero_bin/rpc/src/lib.rs @@ -1,7 +1,10 @@ +zk_evm_common::check_chain_features!(); + use std::sync::Arc; +use __compat_primitive_types::{H256, U256}; use alloy::{ - primitives::{Bytes, FixedBytes, B256}, + primitives::{Address, Bytes, FixedBytes, B256}, providers::Provider, rpc::types::eth::{BlockId, BlockTransactionsKind, Withdrawal}, transports::Transport, @@ -9,8 +12,9 @@ use alloy::{ use anyhow::{anyhow, Context as _}; use clap::ValueEnum; use compat::Compat; -use evm_arithmetization::proof::{BlockHashes, BlockMetadata}; +use evm_arithmetization::proof::{consolidate_hashes, BlockHashes, BlockMetadata}; use futures::{StreamExt as _, TryStreamExt as _}; +use proof_gen::types::{Field, Hasher}; use prover::BlockProverInput; use serde_json::json; use trace_decoder::{BlockLevelData, OtherBlockData}; @@ -37,7 +41,7 @@ pub enum RpcType { pub async fn block_prover_input( cached_provider: Arc>, block_id: BlockId, - checkpoint_state_trie_root: B256, + checkpoint_block_number: u64, rpc_type: RpcType, ) -> Result where @@ -46,10 +50,10 @@ where { match rpc_type { RpcType::Jerigon => { - jerigon::block_prover_input(cached_provider, block_id, checkpoint_state_trie_root).await + jerigon::block_prover_input(cached_provider, block_id, checkpoint_block_number).await } RpcType::Native => { - native::block_prover_input(cached_provider, block_id, checkpoint_state_trie_root).await + native::block_prover_input(cached_provider, block_id, checkpoint_block_number).await } } } @@ -63,7 +67,6 @@ where TransportT: Transport + Clone, { use itertools::Itertools; - // For one block, we will fetch 128 previous blocks to get hashes instead of // 256. But for two consecutive blocks (odd and even) we would fetch 256 // previous blocks in total. To overcome this, we add an offset so that we @@ -198,7 +201,7 @@ where async fn fetch_other_block_data( cached_provider: Arc>, target_block_id: BlockId, - checkpoint_state_trie_root: B256, + checkpoint_block_number: u64, ) -> anyhow::Result where ProviderT: Provider, @@ -209,7 +212,23 @@ where .await?; let target_block_number = target_block.header.number; let chain_id = cached_provider.get_provider().await?.get_chain_id().await?; - let prev_hashes = fetch_previous_block_hashes(cached_provider, target_block_number).await?; + + // Grab interval checkpoint block state trie + let checkpoint_state_trie_root = cached_provider + .get_block( + checkpoint_block_number.into(), + BlockTransactionsKind::Hashes, + ) + .await? + .header + .state_root; + + let prev_hashes = + fetch_previous_block_hashes(cached_provider.clone(), target_block_number).await?; + let checkpoint_prev_hashes = + fetch_previous_block_hashes(cached_provider, checkpoint_block_number + 1) // include the checkpoint block + .await? + .map(|it| it.compat()); let other_data = OtherBlockData { b_data: BlockLevelData { @@ -225,28 +244,48 @@ where .compat(), block_gaslimit: target_block.header.gas_limit.into(), block_chain_id: chain_id.into(), - block_base_fee: target_block - .header - .base_fee_per_gas - .context("target block is missing field `base_fee_per_gas`")? - .into(), + block_base_fee: if !cfg!(feature = "cdk_erigon") { + target_block + .header + .base_fee_per_gas + .context("target block is missing field `base_fee_per_gas`")? + .into() + } else { + target_block + .header + .base_fee_per_gas + .unwrap_or_default() // `baseFee` may be disabled to enable 0 price calls (EIP-1559) + .into() + }, block_gas_used: target_block.header.gas_used.into(), block_bloom: target_block.header.logs_bloom.compat(), - parent_beacon_block_root: target_block - .header - .parent_beacon_block_root - .context("target block is missing field `parent_beacon_block_root`")? - .compat(), - block_blob_gas_used: target_block - .header - .blob_gas_used - .context("target block is missing field `blob_gas_used`")? - .into(), - block_excess_blob_gas: target_block - .header - .excess_blob_gas - .context("target block is missing field `excess_blob_gas`")? - .into(), + parent_beacon_block_root: if cfg!(feature = "eth_mainnet") { + target_block + .header + .parent_beacon_block_root + .context("target block is missing field `parent_beacon_block_root`")? + .compat() + } else { + H256::zero() + }, + block_blob_gas_used: if cfg!(feature = "eth_mainnet") { + target_block + .header + .blob_gas_used + .context("target block is missing field `blob_gas_used`")? + .into() + } else { + U256::zero() + }, + block_excess_blob_gas: if cfg!(feature = "eth_mainnet") { + target_block + .header + .excess_blob_gas + .context("target block is missing field `excess_blob_gas`")? + .into() + } else { + U256::zero() + }, }, b_hashes: BlockHashes { prev_hashes: prev_hashes.map(|it| it.compat()).into(), @@ -264,6 +303,14 @@ where .collect(), }, checkpoint_state_trie_root: checkpoint_state_trie_root.compat(), + checkpoint_consolidated_hash: consolidate_hashes::(&checkpoint_prev_hashes), + burn_addr: if cfg!(feature = "cdk_erigon") { + // TODO: https://github.com/0xPolygonZero/zk_evm/issues/565 + // Retrieve the actual burn address from `cdk-erigon`. + Some(Address::ZERO.compat()) + } else { + None + }, }; Ok(other_data) } diff --git a/zero_bin/rpc/src/main.rs b/zero_bin/rpc/src/main.rs index 8f860689f..12594877a 100644 --- a/zero_bin/rpc/src/main.rs +++ b/zero_bin/rpc/src/main.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use alloy::primitives::B256; use alloy::providers::Provider; use alloy::rpc::types::eth::BlockId; -use alloy::rpc::types::{BlockNumberOrTag, BlockTransactionsKind}; +use alloy::rpc::types::BlockNumberOrTag; use alloy::transports::Transport; use anyhow::anyhow; use clap::{Args, Parser, Subcommand, ValueHint}; @@ -90,16 +90,6 @@ where .unwrap_or(params.start_block - 1); check_previous_proof_and_checkpoint(checkpoint_block_number, &None, params.start_block)?; - // Grab interval checkpoint block state trie - let checkpoint_state_trie_root = cached_provider - .get_block( - BlockId::Number(checkpoint_block_number.into()), - BlockTransactionsKind::Hashes, - ) - .await? - .header - .state_root; - let block_interval = BlockInterval::Range(params.start_block..params.end_block + 1); let mut block_prover_inputs = Vec::new(); let mut block_interval: BlockIntervalStream = block_interval.into_bounded_stream()?; @@ -110,7 +100,7 @@ where let result = rpc::block_prover_input( cached_provider.clone(), block_id, - checkpoint_state_trie_root, + checkpoint_block_number, params.rpc_type, ) .await?; @@ -177,12 +167,10 @@ impl Cli { block_number ))?; - let use_burn_addr = cfg!(feature = "cdk_erigon"); let generation_inputs = trace_decoder::entrypoint( block_prover_input.block_trace, block_prover_input.other_data, batch_size, - use_burn_addr, )?; if let Some(index) = tx_info.transaction_index { diff --git a/zero_bin/rpc/src/native/mod.rs b/zero_bin/rpc/src/native/mod.rs index 728a4f209..2e9527274 100644 --- a/zero_bin/rpc/src/native/mod.rs +++ b/zero_bin/rpc/src/native/mod.rs @@ -3,7 +3,6 @@ use std::ops::Deref; use std::sync::Arc; use alloy::{ - primitives::B256, providers::Provider, rpc::types::eth::{BlockId, BlockTransactionsKind}, transports::Transport, @@ -22,7 +21,7 @@ type CodeDb = BTreeSet>; pub async fn block_prover_input( provider: Arc>, block_number: BlockId, - checkpoint_state_trie_root: B256, + checkpoint_block_number: u64, ) -> anyhow::Result where ProviderT: Provider, @@ -30,7 +29,7 @@ where { let (block_trace, other_data) = try_join!( process_block_trace(provider.clone(), block_number), - crate::fetch_other_block_data(provider.clone(), block_number, checkpoint_state_trie_root,) + crate::fetch_other_block_data(provider.clone(), block_number, checkpoint_block_number) )?; Ok(BlockProverInput { diff --git a/zero_bin/rpc/src/native/state.rs b/zero_bin/rpc/src/native/state.rs index b46eabc9d..a61f73e01 100644 --- a/zero_bin/rpc/src/native/state.rs +++ b/zero_bin/rpc/src/native/state.rs @@ -1,14 +1,15 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use alloy::primitives::Bytes; use alloy::{ - primitives::{keccak256, Address, StorageKey, B256, U256}, + primitives::{keccak256, Address, StorageKey, B256}, providers::Provider, rpc::types::eth::{Block, BlockTransactionsKind, EIP1186AccountProofResponse}, transports::Transport, }; +use alloy_compat::Compat; use anyhow::Context as _; -use evm_arithmetization::testing_utils::{BEACON_ROOTS_CONTRACT_STATE_KEY, HISTORY_BUFFER_LENGTH}; use futures::future::{try_join, try_join_all}; use mpt_trie::{builder::PartialTrieBuilder, partial_trie::HashedPartialTrie}; use trace_decoder::{ @@ -17,8 +18,6 @@ use trace_decoder::{ }; use zero_bin_common::provider::CachedProvider; -use crate::Compat; - /// Processes the state witness for the given block. pub async fn process_state_witness( cached_provider: Arc>, @@ -65,6 +64,7 @@ pub fn process_states_access( ) -> anyhow::Result>> { let mut state_access = HashMap::>::new(); + #[cfg(feature = "eth_mainnet")] insert_beacon_roots_update(&mut state_access, block)?; if let Some(w) = block.withdrawals.as_ref() { @@ -88,18 +88,23 @@ pub fn process_states_access( Ok(state_access) } +#[cfg(feature = "eth_mainnet")] /// Cancun HF specific, see . fn insert_beacon_roots_update( state_access: &mut HashMap>, block: &Block, ) -> anyhow::Result<()> { - let timestamp = block.header.timestamp; + use alloy::primitives::U256; + use evm_arithmetization::testing_utils::{ + BEACON_ROOTS_CONTRACT_STATE_KEY, HISTORY_BUFFER_LENGTH, + }; - const MODULUS: u64 = HISTORY_BUFFER_LENGTH.1; + let timestamp = U256::from(block.header.timestamp); - let keys = HashSet::from_iter([ - U256::from(timestamp % MODULUS).into(), // timestamp_idx - U256::from((timestamp % MODULUS) + MODULUS).into(), // root_idx + let chunk = HISTORY_BUFFER_LENGTH.value.compat(); + let keys = HashSet::from([ + (timestamp % chunk).into(), // timestamp_idx + ((timestamp % chunk) + chunk).into(), // root_idx ]); state_access.insert(BEACON_ROOTS_CONTRACT_STATE_KEY.1.into(), keys); @@ -128,7 +133,7 @@ where // Insert account proofs for (address, proof) in account_proofs.into_iter() { - state.insert_proof(proof.account_proof.compat()); + state.insert_proof(conv_vec_bytes(proof.account_proof)); let storage_mpt = storage_proofs @@ -138,17 +143,17 @@ where Default::default(), )); for proof in proof.storage_proof { - storage_mpt.insert_proof(proof.proof.compat()); + storage_mpt.insert_proof(conv_vec_bytes(proof.proof)); } } // Insert short node variants from next proofs for (address, proof) in next_account_proofs.into_iter() { - state.insert_short_node_variants_from_proof(proof.account_proof.compat()); + state.insert_short_node_variants_from_proof(conv_vec_bytes(proof.account_proof)); if let Some(storage_mpt) = storage_proofs.get_mut(&keccak256(address)) { for proof in proof.storage_proof { - storage_mpt.insert_short_node_variants_from_proof(proof.proof.compat()); + storage_mpt.insert_short_node_variants_from_proof(conv_vec_bytes(proof.proof)); } } } @@ -156,6 +161,10 @@ where Ok((state, storage_proofs)) } +fn conv_vec_bytes(bytes: Vec) -> Vec> { + bytes.into_iter().map(|bytes| bytes.to_vec()).collect() +} + /// Fetches the proof data for the given accounts and associated storage keys. async fn fetch_proof_data( accounts_state: HashMap>, diff --git a/zero_bin/tools/artifacts/witness_b19807080.json b/zero_bin/tools/artifacts/witness_b19807080.json index aa653083a..cb84f56e1 100644 --- a/zero_bin/tools/artifacts/witness_b19807080.json +++ b/zero_bin/tools/artifacts/witness_b19807080.json @@ -1010,7 +1010,13 @@ ] ] }, - "checkpoint_state_trie_root": "0xbbd66174555d27c88e285ff4797de401470d8d2486d15513ab36e491e864bca2" + "checkpoint_state_trie_root": "0xbbd66174555d27c88e285ff4797de401470d8d2486d15513ab36e491e864bca2", + "checkpoint_consolidated_hash": [ + 7715853179812774584, + 12908177954576181071, + 11068935829000177885, + 16446535885506885907 + ] } } -] +] \ No newline at end of file diff --git a/zero_bin/tools/artifacts/witness_b3_b6.json b/zero_bin/tools/artifacts/witness_b3_b6.json index a40735b22..65f2e69de 100644 --- a/zero_bin/tools/artifacts/witness_b3_b6.json +++ b/zero_bin/tools/artifacts/witness_b3_b6.json @@ -297,7 +297,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508" + "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508", + "checkpoint_consolidated_hash": [ + 11109195215272999485, + 17437738218251795093, + 11926305777431453557, + 9928709665788626500 + ] } }, { @@ -612,7 +618,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508" + "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508", + "checkpoint_consolidated_hash": [ + 11109195215272999485, + 17437738218251795093, + 11926305777431453557, + 9928709665788626500 + ] } }, { @@ -927,7 +939,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508" + "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508", + "checkpoint_consolidated_hash": [ + 11109195215272999485, + 17437738218251795093, + 11926305777431453557, + 9928709665788626500 + ] } }, { @@ -1284,7 +1302,13 @@ }, "withdrawals": [] }, - "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508" + "checkpoint_state_trie_root": "0xfa446f2a9bf579b0ea805b88e3ee2acf601573f6ff48baaa8880915613aec508", + "checkpoint_consolidated_hash": [ + 11109195215272999485, + 17437738218251795093, + 11926305777431453557, + 9928709665788626500 + ] } } -] +] \ No newline at end of file diff --git a/zero_bin/tools/prove_stdio.sh b/zero_bin/tools/prove_stdio.sh index 35e39d400..815a7048d 100755 --- a/zero_bin/tools/prove_stdio.sh +++ b/zero_bin/tools/prove_stdio.sh @@ -95,7 +95,7 @@ fi # proof. This is useful for quickly testing decoding and all of the # other non-proving code. if [[ $TEST_ONLY == "test_only" ]]; then - cargo run --release --bin leader -- --test-only --runtime in-memory --load-strategy on-demand --block-batch-size $BLOCK_BATCH_SIZE --proof-output-dir $PROOF_OUTPUT_DIR stdio < $INPUT_FILE &> $TEST_OUT_PATH + cargo run --quiet --release --bin leader -- --test-only --runtime in-memory --load-strategy on-demand --block-batch-size $BLOCK_BATCH_SIZE --proof-output-dir $PROOF_OUTPUT_DIR stdio < $INPUT_FILE &> $TEST_OUT_PATH if grep -q 'All proof witnesses have been generated successfully.' $TEST_OUT_PATH; then echo -e "\n\nSuccess - Note this was just a test, not a proof" rm $TEST_OUT_PATH diff --git a/zero_bin/verifier/Cargo.toml b/zero_bin/verifier/Cargo.toml index ab9e5068f..ff0458197 100644 --- a/zero_bin/verifier/Cargo.toml +++ b/zero_bin/verifier/Cargo.toml @@ -6,19 +6,36 @@ edition = "2021" build = "../common/build.rs" [dependencies] +anyhow = { workspace = true } clap = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true } dotenvy = { workspace = true } -anyhow = { workspace = true } serde_json = { workspace = true } serde_path_to_error = { workspace = true } -proof_gen = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } # Local dependencies -zero_bin_common = { path = "../common" } +proof_gen = { workspace = true } +zero_bin_common = { workspace = true } +zk_evm_common = { workspace = true } [build-dependencies] +anyhow = { workspace = true } cargo_metadata = { workspace = true } vergen = { workspace = true } -anyhow = { workspace = true } + + +[features] +default = ["eth_mainnet"] +eth_mainnet = [ + "proof_gen/eth_mainnet", + "zero_bin_common/eth_mainnet", +] +cdk_erigon = [ + "proof_gen/cdk_erigon", + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "proof_gen/polygon_pos", + "zero_bin_common/polygon_pos", +] diff --git a/zero_bin/verifier/src/main.rs b/zero_bin/verifier/src/main.rs index 30f6c9ab5..aa97d6087 100644 --- a/zero_bin/verifier/src/main.rs +++ b/zero_bin/verifier/src/main.rs @@ -1,3 +1,5 @@ +zk_evm_common::check_chain_features!(); + use std::env; use std::fs::File; diff --git a/zero_bin/worker/Cargo.toml b/zero_bin/worker/Cargo.toml index 3a146c90e..ffb762627 100644 --- a/zero_bin/worker/Cargo.toml +++ b/zero_bin/worker/Cargo.toml @@ -28,3 +28,18 @@ jemallocator = "0.5.4" cargo_metadata = { workspace = true } vergen = { workspace = true } anyhow = { workspace = true } + +[features] +default = ["eth_mainnet"] +eth_mainnet = [ + "ops/eth_mainnet", + "zero_bin_common/eth_mainnet", +] +cdk_erigon = [ + "ops/cdk_erigon", + "zero_bin_common/cdk_erigon", +] +polygon_pos = [ + "ops/polygon_pos", + "zero_bin_common/polygon_pos", +] diff --git a/zero_bin/worker/src/main.rs b/zero_bin/worker/src/main.rs index 08b125ad9..3ea4c7cae 100644 --- a/zero_bin/worker/src/main.rs +++ b/zero_bin/worker/src/main.rs @@ -9,9 +9,7 @@ use zero_bin_common::prover_state::{ cli::CliProverStateConfig, persistence::{set_circuit_cache_dir_env_if_not_set, CIRCUIT_VERSION}, }; -use zero_bin_common::version; - -mod init; +use zero_bin_common::{tracing, version}; // TODO: https://github.com/0xPolygonZero/zk_evm/issues/302 // this should probably be removed. @@ -40,7 +38,7 @@ async fn main() -> Result<()> { } dotenv().ok(); - init::tracing(); + tracing::init(); set_circuit_cache_dir_env_if_not_set()?; let args = Cli::parse();