diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 110028f6eb..49d273e4ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: echo "HOME=/home/CI" >> $GITHUB_ENV - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 - name: Setup golang uses: actions/setup-go@v3 with: @@ -95,7 +95,7 @@ jobs: echo "HOME=/home/CI" >> $GITHUB_ENV - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 - name: Setup golang uses: actions/setup-go@v3 with: @@ -119,7 +119,7 @@ jobs: - name: Run light tests # light tests are run in parallel run: cargo test --verbose --release --all --features scroll --exclude integration-tests --exclude circuit-benchmarks --exclude testool - name: Run heavy tests - run: cargo test --verbose --release --features scroll --all --exclude integration-tests --exclude circuit-benchmarks serial_ -- --ignored + run: cargo test --verbose --release --features scroll --all --exclude integration-tests --exclude circuit-benchmarks serial_ -- --ignored --skip max_tx - name: Run parallel assignment tests(bytecode) run: cargo test --release --package zkevm-circuits --lib bytecode_circuit::test --features scroll,parallel_syn -- --nocapture - name: Run parallel assignment tests(state) @@ -144,7 +144,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 - name: Add target run: rustup target add ${{ matrix.target }} # Go cache for building geth-utils @@ -181,7 +181,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 # Go cache for building geth-utils - name: Go cache uses: actions/cache@v3 @@ -214,7 +214,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 # Go cache for building geth-utils - name: Go cache uses: actions/cache@v3 @@ -251,7 +251,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 components: rustfmt # Go cache for building geth-utils - name: Go cache diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a7aaf1a641..c1c9c7597f 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -61,7 +61,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 - name: Setup golang uses: actions/setup-go@v3 with: diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml index 56577a09ad..08c7d3fa51 100644 --- a/.github/workflows/lints.yml +++ b/.github/workflows/lints.yml @@ -43,7 +43,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 components: clippy # Go cache for building geth-utils - name: Go cache diff --git a/.github/workflows/test-features.yml b/.github/workflows/test-features.yml index 9a76fb4f86..7f55372f01 100644 --- a/.github/workflows/test-features.yml +++ b/.github/workflows/test-features.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2022-12-10 + toolchain: nightly-2023-12-03 # Go cache for building geth-utils - name: Go cache diff --git a/Cargo.lock b/Cargo.lock index 23f6317464..676a3326ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,29 +60,30 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if 1.0.0", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "alloy-rlp" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" +checksum = "cc0fac0fc16baf1f63f78b47c3d24718f3619b0714076f6a02957d808d52cbef" dependencies = [ "arrayvec", "bytes", @@ -270,13 +271,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -354,9 +355,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -402,9 +403,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitvec" @@ -475,9 +476,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bus-mapping" @@ -493,12 +494,10 @@ dependencies = [ "halo2_proofs", "hex", "itertools 0.11.0", - "lazy_static", "log", "mock", "mpt-zktrie", "num", - "once_cell", "poseidon-circuit", "pretty_assertions", "rand", @@ -525,9 +524,9 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -570,9 +569,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" dependencies = [ "serde", ] @@ -585,7 +584,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver 1.0.18", + "semver 1.0.20", "serde", "serde_json", "thiserror", @@ -599,11 +598,10 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856" dependencies = [ - "jobserver", "libc", ] @@ -621,9 +619,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", @@ -786,7 +784,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bech32", "bs58", "digest 0.10.7", @@ -901,9 +899,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1040,9 +1038,9 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +checksum = "28f85c3514d2a6e64160359b45a3918c3b4178bcbf4ae5d03ab2d02e521c479a" dependencies = [ "generic-array", "rand_core", @@ -1062,9 +1060,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", @@ -1074,9 +1072,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -1197,9 +1195,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -1351,7 +1352,7 @@ checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der 0.7.8", "digest 0.10.7", - "elliptic-curve 0.13.5", + "elliptic-curve 0.13.6", "rfc6979 0.4.0", "signature 2.1.0", "spki", @@ -1384,12 +1385,12 @@ dependencies = [ [[package]] name = "elliptic-curve" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct 0.2.0", - "crypto-bigint 0.5.3", + "crypto-bigint 0.5.4", "digest 0.10.7", "ff 0.13.0", "generic-array", @@ -1451,7 +1452,7 @@ checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -1475,25 +1476,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "eth-keystore" version = "0.5.0" @@ -1522,13 +1512,12 @@ version = "0.1.0" dependencies = [ "ethers-core", "ethers-signers", + "halo2-base", "halo2_proofs", "hex", "itertools 0.11.0", - "lazy_static", "num", "num-bigint", - "once_cell", "poseidon-circuit", "regex", "serde", @@ -1592,8 +1581,7 @@ dependencies = [ [[package]] name = "ethers" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a58ce802c65cf3d0756dee5a61094a92cde53c1583b246e9ee5b37226c7fc15" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-addressbook", "ethers-contract", @@ -1602,14 +1590,13 @@ dependencies = [ "ethers-middleware", "ethers-providers", "ethers-signers", - "ethers-solc 2.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ethers-solc", ] [[package]] name = "ethers-addressbook" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b856b7b8ff5c961093cb8efe151fbcce724b451941ce20781de11a531ccd578" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-core", "once_cell", @@ -1620,8 +1607,7 @@ dependencies = [ [[package]] name = "ethers-contract" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e066a0d9cfc70c454672bf16bb433b0243427420076dc5b2f49c448fb5a10628" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-contract-abigen", "ethers-contract-derive", @@ -1639,8 +1625,7 @@ dependencies = [ [[package]] name = "ethers-contract-abigen" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c113e3e86b6bc16d98484b2c3bb2d01d6fed9f489fe2e592e5cc87c3024d616b" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "Inflector", "dunce", @@ -1655,7 +1640,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn 2.0.32", + "syn 2.0.39", "toml 0.7.8", "walkdir", ] @@ -1663,8 +1648,7 @@ dependencies = [ [[package]] name = "ethers-contract-derive" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3fb5adee25701c79ec58fcf2c63594cd8829bc9ad6037ff862d5a111101ed2" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "Inflector", "ethers-contract-abigen", @@ -1673,7 +1657,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -1685,7 +1669,7 @@ dependencies = [ "bytes", "cargo_metadata", "chrono", - "elliptic-curve 0.13.5", + "elliptic-curve 0.13.6", "ethabi", "generic-array", "hex", @@ -1698,7 +1682,7 @@ dependencies = [ "serde", "serde_json", "strum 0.24.1", - "syn 2.0.32", + "syn 2.0.39", "tempfile", "thiserror", "tiny-keccak", @@ -1711,9 +1695,9 @@ version = "2.0.7" source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "ethers-core", - "ethers-solc 2.0.7 (git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7)", + "ethers-solc", "reqwest", - "semver 1.0.18", + "semver 1.0.20", "serde", "serde_json", "thiserror", @@ -1723,8 +1707,7 @@ dependencies = [ [[package]] name = "ethers-middleware" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740f4a773c19dd6d6a68c8c2e0996c096488d38997d524e21dc612c55da3bd24" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "auto_impl", @@ -1750,12 +1733,11 @@ dependencies = [ [[package]] name = "ethers-providers" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b498fd2a6c019d023e43e83488cd1fb0721f299055975aa6bac8dbf1e95f2c" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.4", + "base64 0.21.5", "bytes", "enr", "ethers-core", @@ -1787,13 +1769,12 @@ dependencies = [ [[package]] name = "ethers-signers" version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c4b7e15f212fa7cc2e1251868320221d4ff77a3d48068e69f47ce1c491df2d" +source = "git+https://github.com/scroll-tech/ethers-rs.git?branch=v2.0.7#e32dfd62e7cdec31160b91c5a646883594a586ba" dependencies = [ "async-trait", "coins-bip32", "coins-bip39", - "elliptic-curve 0.13.5", + "elliptic-curve 0.13.6", "eth-keystore", "ethers-core", "hex", @@ -1803,37 +1784,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "ethers-solc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81c89f121595cf8959e746045bb8b25a6a38d72588561e1a3b7992fc213f674" -dependencies = [ - "cfg-if 1.0.0", - "dunce", - "ethers-core", - "glob", - "hex", - "home", - "md-5", - "num_cpus", - "once_cell", - "path-slash", - "rayon", - "regex", - "semver 1.0.18", - "serde", - "serde_json", - "solang-parser", - "svm-rs", - "thiserror", - "tiny-keccak", - "tokio", - "tracing", - "walkdir", - "yansi", -] - [[package]] name = "ethers-solc" version = "2.0.7" @@ -1851,10 +1801,11 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.18", + "semver 1.0.20", "serde", "serde_json", "solang-parser", + "svm-rs", "thiserror", "tiny-keccak", "tokio", @@ -1886,9 +1837,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fastrlp" @@ -1903,9 +1854,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" dependencies = [ "simd-adler32", ] @@ -1916,7 +1867,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "bitvec", "rand_core", "subtle", ] @@ -1927,6 +1877,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ + "bitvec", "rand_core", "subtle", ] @@ -1951,9 +1902,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -2059,9 +2010,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -2074,9 +2025,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -2084,15 +2035,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -2101,9 +2052,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-locks" @@ -2117,26 +2068,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-timer" @@ -2150,9 +2101,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -2204,15 +2155,15 @@ name = "geth-utils" version = "0.1.0" dependencies = [ "env_logger", - "gobuild 0.1.0-alpha.2 (git+https://github.com/scroll-tech/gobuild.git)", + "gobuild", "log", ] [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2278,16 +2229,7 @@ dependencies = [ [[package]] name = "gobuild" version = "0.1.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e156a4ddbf3deb5e8116946c111413bd9a5679bdc1536c78a60618a7a9ac9e" -dependencies = [ - "cc", -] - -[[package]] -name = "gobuild" -version = "0.1.0-alpha.2" -source = "git+https://github.com/scroll-tech/gobuild.git#8b84111fc3b58e2134e4794a06d1f199412cf2b0" +source = "git+https://github.com/scroll-tech/gobuild.git#24935c2b8f677841f22acd6710957621bb294e0e" dependencies = [ "cc", ] @@ -2342,9 +2284,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "halo2-base" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.5#70588177930400361c731659b15b2ab3f29f7784" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#b7c53bb7456063936f4ca6df8fa8e751d9c17d85" dependencies = [ - "ff 0.12.1", + "ff 0.13.0", "halo2_proofs", "itertools 0.10.5", "num-bigint", @@ -2357,10 +2299,10 @@ dependencies = [ [[package]] name = "halo2-ecc" version = "0.2.2" -source = "git+https://github.com/scroll-tech/halo2-lib?tag=v0.1.5#70588177930400361c731659b15b2ab3f29f7784" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#b7c53bb7456063936f4ca6df8fa8e751d9c17d85" dependencies = [ - "ff 0.12.1", - "group 0.12.1", + "ff 0.13.0", + "group 0.13.0", "halo2-base", "itertools 0.10.5", "num-bigint", @@ -2376,7 +2318,7 @@ dependencies = [ [[package]] name = "halo2-gate-generator" version = "0.1.0" -source = "git+https://github.com/scroll-tech/halo2gategen.git#35b137de2f71c37dfbd236842b868013c46739d1" +source = "git+https://github.com/scroll-tech/halo2gategen#8ccf462e1eff4ed0e602d7ba19771b2c53dee0e3" dependencies = [ "halo2_proofs", "lazy_static", @@ -2392,8 +2334,9 @@ dependencies = [ [[package]] name = "halo2-mpt-circuits" version = "0.1.0" -source = "git+https://github.com/scroll-tech/mpt-circuit.git?tag=v0.7.0#578c210ceb88d3c143ee2a013ad836d19285d9c1" +source = "git+https://github.com/scroll-tech/mpt-circuit.git?branch=v0.7#32ab964ff065ee6a0ccc63590b9db73238c70b81" dependencies = [ + "env_logger", "ethers-core", "halo2_proofs", "hex", @@ -2404,6 +2347,7 @@ dependencies = [ "num-traits", "poseidon-circuit", "rand", + "rand_chacha", "serde", "serde_json", "strum 0.24.1", @@ -2411,18 +2355,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "halo2_gadgets" +version = "0.2.0" +source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.0#04d8dc09bd4df542feccac85e34dff4b38a467d3" +dependencies = [ + "arrayvec", + "bitvec", + "ff 0.13.0", + "group 0.13.0", + "halo2_proofs", + "halo2curves 0.3.2", + "lazy_static", + "rand", + "subtle", + "uint", +] + [[package]] name = "halo2_proofs" version = "0.2.0" -source = "git+https://github.com/scroll-tech/halo2.git?branch=develop#5b9a3d71a325a9ecbad164aba90a7f6a8550a015" +source = "git+https://github.com/scroll-tech/halo2.git?branch=v1.0#04d8dc09bd4df542feccac85e34dff4b38a467d3" dependencies = [ "ark-std 0.3.0", "blake2b_simd", "cfg-if 0.1.10", "crossbeam", - "ff 0.12.1", - "group 0.12.1", - "halo2curves", + "ff 0.13.0", + "group 0.13.0", + "halo2curves 0.1.0", "log", "num-bigint", "num-integer", @@ -2438,11 +2399,13 @@ dependencies = [ [[package]] name = "halo2curves" -version = "0.3.1" -source = "git+https://github.com/scroll-tech/halo2curves.git?branch=0.3.1-derive-serde#969f1e44d9713ee4cd552563bd0c762c5d53b56e" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab" dependencies = [ - "ff 0.12.1", - "group 0.12.1", + "blake2b_simd", + "ff 0.13.0", + "group 0.13.0", "lazy_static", "num-bigint", "num-traits", @@ -2451,27 +2414,34 @@ dependencies = [ "rand", "rand_core", "serde", + "serde_arrays", "static_assertions", "subtle", ] [[package]] -name = "halo2wrong" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/halo2wrong?branch=bus-auto#68c126b9506d82d0cca8a38c75a86a3d5eb955c7" +name = "halo2curves" +version = "0.3.2" +source = "git+https://github.com/privacy-scaling-explorations/halo2curves?tag=0.3.2#9f5c50810bbefe779ee5cf1d852b2fe85dc35d5e" dependencies = [ - "group 0.12.1", - "halo2_proofs", + "ff 0.13.0", + "group 0.13.0", + "lazy_static", "num-bigint", - "num-integer", "num-traits", + "pasta_curves", + "paste", + "rand", + "rand_core", + "static_assertions", + "subtle", ] [[package]] name = "handlebars" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39b3bc2a8f715298032cf5087e58573809374b08160aa7d750582bdb82d2683" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" dependencies = [ "log", "pest", @@ -2498,9 +2468,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "hashers" @@ -2528,9 +2498,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -2564,9 +2534,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -2619,7 +2589,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -2628,9 +2598,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -2642,16 +2612,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -2750,12 +2720,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -2786,7 +2756,6 @@ dependencies = [ "ethers", "halo2_proofs", "hex", - "lazy_static", "log", "mock", "paste", @@ -2804,9 +2773,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" @@ -2814,7 +2783,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "rustix", "windows-sys", ] @@ -2843,15 +2812,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.0" @@ -2860,9 +2820,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -2888,7 +2848,7 @@ checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if 1.0.0", "ecdsa 0.16.8", - "elliptic-curve 0.13.5", + "elliptic-curve 0.13.6", "once_cell", "sha2", "signature 2.1.0", @@ -2911,7 +2871,6 @@ dependencies = [ "eth-types", "halo2_proofs", "itertools 0.11.0", - "lazy_static", "log", "num-bigint", "num-traits", @@ -2954,20 +2913,20 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if 1.0.0", "windows-sys", @@ -2975,9 +2934,20 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] [[package]] name = "linked-hash-map" @@ -2987,15 +2957,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -3033,34 +3003,21 @@ dependencies = [ "winapi", ] -[[package]] -name = "maingate" -version = "0.1.0" -source = "git+https://github.com/scroll-tech/halo2wrong?branch=bus-auto#68c126b9506d82d0cca8a38c75a86a3d5eb955c7" -dependencies = [ - "group 0.12.1", - "halo2wrong", - "num-bigint", - "num-integer", - "num-traits", - "rand", - "subtle", -] - [[package]] name = "md-5" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ + "cfg-if 1.0.0", "digest 0.10.7", ] [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -3089,9 +3046,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", @@ -3101,17 +3058,16 @@ dependencies = [ [[package]] name = "misc-precompiled-circuit" version = "0.1.0" -source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?tag=v0.1.0#f647341f9951f5c2399035728d4f6765564e2e02" +source = "git+https://github.com/scroll-tech/misc-precompiled-circuit.git?branch=main#f46cf8fd0072e5531315739b20b5248f4bd2caac" dependencies = [ "halo2-gate-generator", "halo2_proofs", - "lazy_static", "num-bigint", "rand", "serde", "serde_json", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum 0.25.0", + "strum_macros 0.25.3", "subtle", ] @@ -3124,7 +3080,6 @@ dependencies = [ "ethers-signers", "external-tracer", "itertools 0.11.0", - "lazy_static", "log", "rand", "rand_chacha", @@ -3139,7 +3094,6 @@ dependencies = [ "halo2-mpt-circuits", "halo2_proofs", "hex", - "lazy_static", "log", "num-bigint", "poseidon-circuit", @@ -3224,9 +3178,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -3238,7 +3192,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "libc", ] @@ -3281,7 +3235,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -3338,9 +3292,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.1" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "parity-scale-codec" @@ -3380,13 +3334,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", "windows-targets", ] @@ -3404,13 +3358,13 @@ dependencies = [ [[package]] name = "pasta_curves" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" dependencies = [ "blake2b_simd", - "ff 0.12.1", - "group 0.12.1", + "ff 0.13.0", + "group 0.13.0", "lazy_static", "rand", "static_assertions", @@ -3441,11 +3395,11 @@ dependencies = [ [[package]] name = "pathfinder_simd" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +checksum = "0444332826c70dc47be74a7c6a5fc44e23a7905ad6858d4162b658320455ef93" dependencies = [ - "rustc_version 0.3.3", + "rustc_version 0.4.0", ] [[package]] @@ -3478,9 +3432,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.3" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -3489,9 +3443,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.3" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -3499,22 +3453,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.3" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] name = "pest_meta" -version = "2.7.3" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -3528,7 +3482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.1.0", ] [[package]] @@ -3571,7 +3525,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -3609,7 +3563,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -3702,19 +3656,19 @@ dependencies = [ [[package]] name = "poseidon" version = "0.2.0" -source = "git+https://github.com/scroll-tech/poseidon.git?branch=scroll-dev-0220#2fb4a2385bada39b50dce12fe50cb80d2fd33476" +source = "git+https://github.com/scroll-tech/poseidon.git?branch=main#5787dd3d2ce7a9e9601a035c396ac0c03449b54d" dependencies = [ - "group 0.12.1", - "halo2curves", + "halo2curves 0.1.0", "subtle", ] [[package]] name = "poseidon-circuit" version = "0.1.0" -source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=scroll-dev-0901#69524f42bdc55c581088c2fe64c2ab9a2921146b" +source = "git+https://github.com/scroll-tech/poseidon-circuit.git?branch=scroll-dev-1201#c6f058bcf3bb0c7933d1979563c414f5cc480f25" dependencies = [ "bitvec", + "ff 0.13.0", "halo2_proofs", "lazy_static", "log", @@ -3723,6 +3677,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3752,7 +3712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -3771,9 +3731,9 @@ dependencies = [ [[package]] name = "primitive-types" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", @@ -3825,27 +3785,26 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" dependencies = [ - "bitflags 1.3.2", - "byteorder", + "bitflags 2.4.1", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.6.29", + "regex-syntax 0.8.2", "unarray", ] @@ -3870,7 +3829,6 @@ dependencies = [ "log4rs", "mpt-zktrie", "num-bigint", - "once_cell", "rand", "rand_xorshift", "serde", @@ -3948,9 +3906,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -3958,66 +3916,55 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -4028,17 +3975,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -4060,6 +4007,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-rustls", "tower-service", @@ -4170,12 +4118,26 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", - "untrusted", + "spin 0.5.2", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -4209,9 +4171,9 @@ dependencies = [ [[package]] name = "ruint" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +checksum = "724fd11728a3804e9944b14cab63825024c40bf42f8af87c8b5d97c4bbacf426" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -4219,6 +4181,7 @@ dependencies = [ "bytes", "fastrlp", "num-bigint", + "num-traits", "parity-scale-codec", "primitive-types", "proptest", @@ -4269,16 +4232,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.18", + "semver 1.0.20", ] [[package]] name = "rustix" -version = "0.38.13" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -4287,43 +4250,43 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", - "ring", - "rustls-webpki 0.101.5", + "ring 0.17.5", + "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", ] [[package]] name = "rustls-webpki" -version = "0.100.2" +version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98ff011474fa39949b7e5c0428f9b4937eda7da7848bbb947786b7be0b27dab" +checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] name = "rustls-webpki" -version = "0.101.5" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -4358,9 +4321,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "cfg-if 1.0.0", "derive_more", @@ -4370,9 +4333,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4400,12 +4363,12 @@ dependencies = [ [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -4482,9 +4445,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] @@ -4512,13 +4475,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] +[[package]] +name = "serde_arrays" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" +dependencies = [ + "serde", +] + [[package]] name = "serde_cbor" version = "0.11.2" @@ -4531,20 +4503,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -4553,9 +4525,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -4606,9 +4578,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -4617,9 +4589,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -4691,9 +4663,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smol_str" @@ -4707,7 +4679,7 @@ dependencies = [ [[package]] name = "snark-verifier" version = "0.1.0" -source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.5#bc1d39ae31f3fe520c51dd150f0fefaf9653c465" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#12c1121a855564936a267b37bc9c27306de3eb3b" dependencies = [ "bytes", "ethereum-types", @@ -4731,11 +4703,11 @@ dependencies = [ [[package]] name = "snark-verifier-sdk" version = "0.0.1" -source = "git+https://github.com/scroll-tech/snark-verifier?tag=v0.1.5#bc1d39ae31f3fe520c51dd150f0fefaf9653c465" +source = "git+https://github.com/scroll-tech/snark-verifier?branch=develop#12c1121a855564936a267b37bc9c27306de3eb3b" dependencies = [ "bincode", - "env_logger", "ethereum-types", + "ff 0.13.0", "halo2-base", "hex", "itertools 0.10.5", @@ -4753,9 +4725,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -4763,9 +4735,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", @@ -4791,6 +4763,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.2" @@ -4883,7 +4861,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -4916,7 +4894,7 @@ dependencies = [ "home", "once_cell", "reqwest", - "semver 1.0.18", + "semver 1.0.20", "serde", "serde_json", "sha2", @@ -4938,15 +4916,36 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tabbycat" version = "0.1.2" @@ -4966,13 +4965,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", "windows-sys", ] @@ -4990,9 +4989,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] @@ -5018,7 +5017,6 @@ dependencies = [ "keccak256", "log", "mock", - "once_cell", "prettytable-rs", "prover", "rand", @@ -5053,51 +5051,51 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] name = "thread-id" -version = "4.2.0" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79474f573561cdc4871a0de34a51c92f7f5a56039113fbb5b9c9f96bdb756669" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" dependencies = [ "libc", - "redox_syscall 0.2.16", "winapi", ] [[package]] name = "time" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tiny-keccak" @@ -5135,9 +5133,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -5145,20 +5143,20 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -5188,9 +5186,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -5223,9 +5221,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -5236,7 +5234,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -5251,11 +5249,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5263,20 +5260,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -5326,9 +5323,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -5362,9 +5359,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -5377,9 +5374,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" @@ -5393,6 +5390,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.4.1" @@ -5465,9 +5468,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5475,24 +5478,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5502,9 +5505,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5512,28 +5515,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -5541,12 +5544,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e74f82d49d545ad128049b7e88f6576df2da6b02e9ce565c6f533be576957e" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -5555,7 +5558,7 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ - "rustls-webpki 0.100.2", + "rustls-webpki 0.100.3", ] [[package]] @@ -5588,9 +5591,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -5602,10 +5605,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] @@ -5678,9 +5681,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] @@ -5759,6 +5762,26 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zerocopy" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zeroize" version = "1.6.0" @@ -5776,7 +5799,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.39", ] [[package]] @@ -5813,22 +5836,21 @@ dependencies = [ "eth-types", "ethers-core", "ethers-signers", + "ff 0.13.0", "gadgets", "halo2-base", "halo2-ecc", + "halo2_gadgets", "halo2_proofs", "hex", "itertools 0.11.0", "keccak256", - "lazy_static", "log", - "maingate", "misc-precompiled-circuit", "mock", "mpt-zktrie", "num", "num-bigint", - "once_cell", "paste", "poseidon-circuit", "pretty_assertions", @@ -5849,9 +5871,9 @@ dependencies = [ [[package]] name = "zktrie" version = "0.2.0" -source = "git+https://github.com/scroll-tech/zktrie.git?branch=v0.7#a130ea543d291d4b71724f91cb8a49745c593a0c" +source = "git+https://github.com/scroll-tech/zktrie.git?tag=v0.7.1#a12f2f262ad3e82301e39ecdf9bfe235befc7074" dependencies = [ - "gobuild 0.1.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gobuild", ] [[package]] @@ -5875,11 +5897,15 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] + +[[patch.unused]] +name = "maingate" +version = "0.1.0" +source = "git+https://github.com/scroll-tech/halo2wrong?branch=bus-auto#75464fd39a7b276316364deb5995f2c398231b9a" diff --git a/Cargo.toml b/Cargo.toml index 4b1fb48f38..6b2be49bc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,21 +27,22 @@ anyhow = "1.0" ark-std = "0.3" ctor = "0.1" env_logger = "0.10" -ethers = { version = "2.0.7", features = ["ethers-solc"] } +ethers = { version = "=2.0.7", features = ["ethers-solc"] } ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7", features = ["scroll"] } -ethers-providers = "2.0.7" -ethers-signers = "2.0.7" -halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } -hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-0901"} +ethers-providers = "=2.0.7" +ethers-signers = "=2.0.7" +ff = "0.13" +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.0" } +hash-circuit = { package = "poseidon-circuit", git = "https://github.com/scroll-tech/poseidon-circuit.git", branch = "scroll-dev-1201" } +halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } +halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } hex = "0.4" itertools = "0.11" -lazy_static = "1.4" libsecp256k1 = "0.7" log = "0.4" num = "0.4" num-bigint = "0.4" num-traits = "0.2" -once_cell = "1.17" pretty_assertions = "1.0" rand = "0.8" rand_chacha = "0.3" @@ -51,8 +52,8 @@ regex = "1.5" serde = {version = "1.0", features = ["derive"] } serde_json = "1.0" sha3 = "0.10" -snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", tag = "v0.1.5" } -snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", tag = "v0.1.5", default-features = false, features = ["loader_halo2", "loader_evm", "halo2-pse"] } +snark-verifier = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop" } +snark-verifier-sdk = { git = "https://github.com/scroll-tech/snark-verifier", branch = "develop", default-features = false, features = ["loader_halo2", "loader_evm", "halo2-pse"] } strum = "0.25" strum_macros = "0.25" subtle = "2.4" @@ -61,20 +62,19 @@ url = "2.2" [patch.crates-io] ethers-core = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers-providers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +ethers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } ethers-etherscan = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } - +ethers-signers = { git = "https://github.com/scroll-tech/ethers-rs.git", branch = "v2.0.7" } +gobuild = { git = "https://github.com/scroll-tech/gobuild.git" } [patch."https://github.com/privacy-scaling-explorations/halo2.git"] -halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "develop" } - +halo2_proofs = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.0" } [patch."https://github.com/privacy-scaling-explorations/poseidon.git"] -poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "scroll-dev-0220" } - -[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"] -halo2curves = { git = "https://github.com/scroll-tech/halo2curves.git", branch = "0.3.1-derive-serde" } - +poseidon = { git = "https://github.com/scroll-tech/poseidon.git", branch = "main" } [patch."https://github.com/privacy-scaling-explorations/halo2wrong.git"] maingate = { git = "https://github.com/scroll-tech/halo2wrong", branch = "bus-auto" } + # Definition of benchmarks profile to use. [profile.bench] opt-level = 3 diff --git a/aggregator/src/aggregation/circuit.rs b/aggregator/src/aggregation/circuit.rs index 053c7e54ca..74f9fd6180 100644 --- a/aggregator/src/aggregation/circuit.rs +++ b/aggregator/src/aggregation/circuit.rs @@ -401,7 +401,6 @@ impl CircuitExt for AggregationCircuit { config.0.flex_gate().basic_gates[0] .iter() .map(|gate| gate.q_enable) - .into_iter() .chain( [ config.0.rlc_config.selector, diff --git a/aggregator/src/core.rs b/aggregator/src/core.rs index 92f1b426b0..f0bb11a84b 100644 --- a/aggregator/src/core.rs +++ b/aggregator/src/core.rs @@ -1008,7 +1008,6 @@ pub(crate) fn conditional_constraints( .iter() .skip(1) .take((MAX_AGG_SNARKS + 1) * 2) - .into_iter() .chunks(2) .into_iter() { diff --git a/aggregator/src/tests/mock_chunk.rs b/aggregator/src/tests/mock_chunk.rs index e5c5781496..49beba4d88 100644 --- a/aggregator/src/tests/mock_chunk.rs +++ b/aggregator/src/tests/mock_chunk.rs @@ -111,10 +111,9 @@ impl Circuit for MockChunkCircuit { let mut index = 0; let acc_len = if self.has_accumulator { ACC_LEN } else { 0 }; - for (_i, byte) in iter::repeat(0) + for byte in iter::repeat(0) .take(acc_len) .chain(self.chunk.public_input_hash().as_bytes().iter().copied()) - .enumerate() { let cell = config .rlc_config diff --git a/aggregator/src/tests/rlc/gates.rs b/aggregator/src/tests/rlc/gates.rs index c3533b9aaa..32d7da61b1 100644 --- a/aggregator/src/tests/rlc/gates.rs +++ b/aggregator/src/tests/rlc/gates.rs @@ -44,6 +44,7 @@ impl Circuit for ArithTestCircuit { let mut rng = test_rng(); let mut first_pass = true; + layouter.assign_region( || "test field circuit", |mut region| -> Result<(), Error> { diff --git a/aggregator/src/util.rs b/aggregator/src/util.rs index 12a315d826..7281bf256b 100644 --- a/aggregator/src/util.rs +++ b/aggregator/src/util.rs @@ -61,7 +61,7 @@ pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { for (i, round) in preimage_padded.chunks(INPUT_LEN_PER_ROUND).enumerate() { let f_round_offset = round_ctr * keccak_f_rows; // indices for preimages - for (j, _chunk) in round.chunks(8).into_iter().enumerate() { + for (j, _chunk) in round.chunks(8).enumerate() { let inner_offset = f_round_offset + (j + 1) * inner_round_rows; for k in 0..8 { preimage_indices.push(inner_offset + k); @@ -83,12 +83,7 @@ pub(crate) fn get_indices(preimages: &[Vec]) -> (Vec, Vec) { } // last hash is for data_hash and has various length, so we output all the possible cells for _i in 0..get_data_hash_keccak_updates(MAX_AGG_SNARKS) { - for (j, _) in (0..INPUT_LEN_PER_ROUND) - .into_iter() - .chunks(8) - .into_iter() - .enumerate() - { + for (j, _) in (0..INPUT_LEN_PER_ROUND).chunks(8).into_iter().enumerate() { let inner_offset = round_ctr * keccak_f_rows + (j + 1) * inner_round_rows; for k in 0..8 { preimage_indices.push(inner_offset + k); diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 66571d6fae..e76966d55d 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -16,7 +16,6 @@ ethers-providers.workspace = true halo2_proofs.workspace = true hash-circuit.workspace = true itertools.workspace = true -lazy_static.workspace = true log.workspace = true num.workspace = true rand = { workspace = true, optional = true } @@ -28,7 +27,6 @@ strum_macros.workspace = true # precompile related crates revm-precompile = { git = "https://github.com/scroll-tech/revm", branch = "scroll-fix" } -once_cell.workspace = true [dev-dependencies] hex.workspace = true @@ -41,8 +39,12 @@ mock = { path = "../mock" } rand.workspace = true [features] -default = ["test"] +default = ["test", "enable-stack", "enable-storage"] test = ["mock", "rand"] scroll = ["eth-types/scroll", "mock?/scroll"] # Enable shanghai feature of mock only if mock is enabled (by test). shanghai = ["eth-types/shanghai", "mock?/shanghai"] +tracer-tests = ["enable-memory"] +enable-stack = ["eth-types/enable-stack", "mock?/enable-stack"] +enable-memory = ["eth-types/enable-memory", "mock?/enable-memory"] +enable-storage = ["eth-types/enable-storage", "mock?/enable-storage"] \ No newline at end of file diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 334518a968..b3ccdebc34 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -8,7 +8,7 @@ mod execution; mod input_state_ref; #[cfg(feature = "scroll")] mod l2; -#[cfg(test)] +#[cfg(all(feature = "tracer-tests", feature = "enable-memory", test))] mod tracer_tests; mod transaction; @@ -21,6 +21,7 @@ use crate::{ precompile::is_precompiled, rpc::GethClient, state_db::{self, CodeDB, StateDB}, + util::{hash_code_keccak, KECCAK_CODE_HASH_EMPTY}, }; pub use access::{Access, AccessSet, AccessValue, CodeSource}; pub use block::{Block, BlockContext}; @@ -37,7 +38,7 @@ use ethers_providers::JsonRpcClient; pub use execution::{ BigModExp, CopyBytes, CopyDataType, CopyEvent, CopyEventStepsBuilder, CopyStep, EcAddOp, EcMulOp, EcPairingOp, EcPairingPair, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, - PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, + PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, SHA256, }; use hex::decode_to_slice; @@ -298,14 +299,25 @@ impl<'a> CircuitInputBuilder { for (tx_index, tx) in eth_block.transactions.iter().enumerate() { let chunk_tx_idx = self.block.txs.len(); if self.block.txs.len() >= self.block.circuits_params.max_txs { - log::error!( - "tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}", - self.block.circuits_params.max_txs, - chunk_tx_idx, - tx.transaction_index.unwrap_or_default(), - tx.hash - ); - return Err(Error::InternalError("tx num overflow")); + if self.block.is_relaxed() { + log::warn!( + "tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}, would process for partial block", + self.block.circuits_params.max_txs, + chunk_tx_idx, + tx.transaction_index.unwrap_or_default(), + tx.hash + ); + break; + } else { + log::error!( + "tx num overflow, MAX_TX limit {}, {}th tx(inner idx: {}) {:?}", + self.block.circuits_params.max_txs, + chunk_tx_idx, + tx.transaction_index.unwrap_or_default(), + tx.hash + ); + return Err(Error::InternalError("tx num overflow")); + } } let geth_trace = &geth_traces[tx_index]; log::info!( @@ -485,7 +497,7 @@ impl<'a> CircuitInputBuilder { RW::READ, StorageOp::new( *MESSAGE_QUEUE, - *WITHDRAW_TRIE_ROOT_SLOT, + WITHDRAW_TRIE_ROOT_SLOT, withdraw_root, withdraw_root, dummy_tx_id, @@ -666,7 +678,7 @@ impl<'a> CircuitInputBuilder { ) } else { let stack_input_num = 1024 - geth_step.op.valid_stack_ptr_range().1 as usize; - (0..stack_input_num).into_iter().map(|i| + (0..stack_input_num).map(|i| format!("{:?}", geth_step.stack.nth_last(i)) ).collect_vec().join(" ") } @@ -709,6 +721,12 @@ impl CircuitInputBuilder { .iter() .any(|tx| tx.has_l2_different_evm_behaviour_step()) } + + /// enable relax mode for testing + pub fn enable_relax_mode(mut self) -> Self { + self.block = self.block.relax(); + self + } } /// Return all the keccak inputs used during the processing of the current @@ -950,7 +968,7 @@ pub struct BuilderClient { pub fn get_state_accesses( eth_block: &EthBlock, geth_traces: &[eth_types::GethExecTrace], -) -> Result, Error> { +) -> Result { let mut block_access_trace = vec![Access::new( None, RW::WRITE, @@ -966,7 +984,7 @@ pub fn get_state_accesses( block_access_trace.extend(tx_access_trace); } - Ok(block_access_trace) + Ok(AccessSet::from(block_access_trace)) } /// Build a partial StateDB from step 3 @@ -1060,26 +1078,11 @@ impl BuilderClient

{ } /// Step 2. Get State Accesses from TxExecTraces - pub async fn get_state_accesses(&self, eth_block: &EthBlock) -> Result { - let mut access_set = AccessSet::default(); - access_set.add_account( - eth_block - .author - .ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?, - ); - let traces = self - .cli - .trace_block_prestate_by_hash( - eth_block - .hash - .ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?, - ) - .await?; - for trace in traces.into_iter() { - access_set.extend_from_traces(&trace); - } - - Ok(access_set) + pub fn get_state_accesses( + eth_block: &EthBlock, + geth_traces: &[eth_types::GethExecTrace], + ) -> Result { + get_state_accesses(eth_block, geth_traces) } /// Step 3. Query geth for all accounts, storage keys, and codes from @@ -1118,6 +1121,117 @@ impl BuilderClient

{ Ok((proofs, codes)) } + /// Yet-another Step 3. Get the account state and codes from pre-state tracing + /// the account state is limited since proof is not included, + /// but it is enough to build the sdb/cdb + /// if a hash for tx is provided, would return the prestate for this tx + pub async fn get_pre_state( + &self, + eth_block: &EthBlock, + tx_hash: Option, + ) -> Result< + ( + Vec, + HashMap>, + ), + Error, + > { + let traces = if let Some(tx_hash) = tx_hash { + vec![self.cli.trace_tx_prestate_by_hash(tx_hash).await?] + } else { + self.cli + .trace_block_prestate_by_hash( + eth_block + .hash + .ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?, + ) + .await? + }; + + let mut account_set = + HashMap::)>::new(); + let mut code_set = HashMap::new(); + + for trace in traces.into_iter() { + for (addr, prestate) in trace.into_iter() { + let (_, storages) = account_set.entry(addr).or_insert_with(|| { + let code_size = + Word::from(prestate.code.as_ref().map(|bt| bt.len()).unwrap_or(0)); + let (code_hash, keccak_code_hash) = if let Some(bt) = prestate.code { + let h = CodeDB::hash(&bt); + // only require for L2 + let keccak_h = if cfg!(feature = "scroll") { + hash_code_keccak(&bt) + } else { + h + }; + code_set.insert(addr, Vec::from(bt.as_ref())); + (h, keccak_h) + } else { + (CodeDB::empty_code_hash(), *KECCAK_CODE_HASH_EMPTY) + }; + + ( + eth_types::EIP1186ProofResponse { + address: addr, + balance: prestate.balance.unwrap_or_default(), + nonce: prestate.nonce.unwrap_or_default().into(), + code_hash, + keccak_code_hash, + code_size, + ..Default::default() + }, + HashMap::new(), + ) + }); + + if let Some(stg) = prestate.storage { + for (k, v) in stg { + storages.entry(k).or_insert(v); + } + } + } + } + + // a hacking? since the coinbase address is not touch in prestate + let coinbase_addr = eth_block + .author + .ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?; + let block_num = eth_block + .number + .ok_or(Error::EthTypeError(eth_types::Error::IncompleteBlock))?; + assert_ne!( + block_num.as_u64(), + 0, + "is not expected to access genesis block" + ); + if let std::collections::hash_map::Entry::Vacant(e) = account_set.entry(coinbase_addr) { + let coinbase_proof = self + .cli + .get_proof(coinbase_addr, Vec::new(), (block_num - 1).into()) + .await?; + e.insert((coinbase_proof, HashMap::new())); + } + + Ok(( + account_set + .into_iter() + .map(|(_, (mut acc_resp, storage_proofs))| { + acc_resp.storage_proof = storage_proofs + .into_iter() + .map(|(key, value)| eth_types::StorageProof { + key, + value, + ..Default::default() + }) + .collect(); + acc_resp + }) + .collect::>(), + code_set, + )) + } + /// Step 4. Build a partial StateDB from step 3 pub fn build_state_code_db( proofs: Vec, @@ -1184,8 +1298,8 @@ impl BuilderClient

{ > { let (mut eth_block, mut geth_traces, history_hashes, prev_state_root) = self.get_block(block_num).await?; - let access_set = self.get_state_accesses(ð_block).await?; - let (proofs, codes) = self.get_state(block_num, access_set).await?; + //let access_set = Self::get_state_accesses(ð_block, &geth_traces)?; + let (proofs, codes) = self.get_pre_state(ð_block, None).await?; let (state_db, code_db) = Self::build_state_code_db(proofs, codes); if eth_block.transactions.len() > self.circuits_params.max_txs { log::error!( @@ -1220,7 +1334,7 @@ impl BuilderClient

{ let mut access_set = AccessSet::default(); for block_num in block_num_begin..block_num_end { let (eth_block, geth_traces, _, _) = self.get_block(block_num).await?; - let mut access_list = self.get_state_accesses(ð_block).await?; + let mut access_list = Self::get_state_accesses(ð_block, &geth_traces)?; access_set.extend(&mut access_list); blocks_and_traces.push((eth_block, geth_traces)); } @@ -1243,7 +1357,7 @@ impl BuilderClient

{ let mut tx: eth_types::Transaction = self.cli.get_tx_by_hash(tx_hash).await?; tx.transaction_index = Some(0.into()); - let geth_traces = self.cli.trace_tx_by_hash(tx_hash).await?; + let geth_trace = self.cli.trace_tx_by_hash(tx_hash).await?; let mut eth_block = self .cli .get_block_by_number(tx.block_number.unwrap().into()) @@ -1251,32 +1365,13 @@ impl BuilderClient

{ eth_block.transactions = vec![tx.clone()]; - let mut block_access_trace = vec![Access::new( - None, - RW::WRITE, - AccessValue::Account { - address: eth_block.author.unwrap(), - }, - )]; - let geth_trace = &geth_traces[0]; - let tx_access_trace = gen_state_access_trace( - ð_types::Block::::default(), - &tx, - geth_trace, - )?; - block_access_trace.extend(tx_access_trace); - - let access_set = AccessSet::from(block_access_trace); - - let (proofs, codes) = self - .get_state(tx.block_number.unwrap().as_u64(), access_set) - .await?; + let (proofs, codes) = self.get_pre_state(ð_block, Some(tx_hash)).await?; let (state_db, code_db) = Self::build_state_code_db(proofs, codes); let builder = self.gen_inputs_from_state( state_db, code_db, ð_block, - &geth_traces, + &[geth_trace], Default::default(), Default::default(), )?; diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index fb151a62a7..d3e1be47b0 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -214,6 +214,9 @@ pub struct Block { pub precompile_events: PrecompileEvents, /// circuit capacity counter copy_counter: usize, + /// relax mode indicate builder and circuit would skip + /// some sanity check, used by testing and debugging + relax_mode: bool, } impl Block { @@ -311,6 +314,11 @@ impl Block { self.chain_id } + /// Return if the relax mode + pub fn is_relaxed(&self) -> bool { + self.relax_mode + } + /// .. pub fn end_state_root(&self) -> Word { self.headers @@ -323,6 +331,14 @@ impl Block { pub fn txs_mut(&mut self) -> &mut Vec { &mut self.txs } + + /// switch to relax mode (used by testing and debugging, + /// see the note in defination of `relax_mode`) + #[cfg(feature = "test")] + pub fn relax(mut self) -> Self { + self.relax_mode = true; + self + } } impl Block { diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index a4e26ba414..12c377f0ed 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -15,12 +15,12 @@ use crate::{ use eth_types::{ evm_types::{memory::MemoryWordRange, Gas, GasCost, MemoryAddress, OpcodeId, ProgramCounter}, sign_types::SignData, - GethExecStep, ToLittleEndian, Word, H256, U256, + Field, GethExecStep, ToLittleEndian, Word, H256, U256, }; use ethers_core::k256::elliptic_curve::subtle::CtOption; use gadgets::impl_expr; use halo2_proofs::{ - arithmetic::{CurveAffine, Field}, + arithmetic::{CurveAffine, Field as Halo2Field}, halo2curves::{ bn256::{Fq, Fq2, Fr, G1Affine, G2Affine}, group::prime::PrimeCurveAffine, @@ -221,6 +221,12 @@ pub enum CopyDataType { /// scenario where we wish to accumulate the value (RLC) over all rows. /// This is used for Copy Lookup from SHA3 opcode verification. RlcAcc, + /// When copy event is access-list addresses (EIP-2930), source is tx-table + /// and destination is rw-table. + AccessListAddresses, + /// When copy event is access-list storage keys (EIP-2930), source is + /// tx-table and destination is rw-table. + AccessListStorageKeys, } impl CopyDataType { /// How many bits are necessary to represent a copy data type. @@ -307,6 +313,8 @@ impl From for usize { CopyDataType::TxCalldata => 3, CopyDataType::TxLog => 4, CopyDataType::RlcAcc => 5, + CopyDataType::AccessListAddresses => 6, + CopyDataType::AccessListStorageKeys => 7, } } } @@ -320,6 +328,8 @@ impl From<&CopyDataType> for u64 { CopyDataType::TxCalldata => 3, CopyDataType::TxLog => 4, CopyDataType::RlcAcc => 5, + CopyDataType::AccessListAddresses => 6, + CopyDataType::AccessListStorageKeys => 7, } } } @@ -908,6 +918,20 @@ impl PrecompileEvents { .cloned() .collect() } + /// Get all SHA256 events. + pub fn get_sha256_events(&self) -> Vec { + self.events + .iter() + .filter_map(|e| { + if let PrecompileEvent::SHA256(op) = e { + Some(op) + } else { + None + } + }) + .cloned() + .collect() + } } /// I/O from a precompiled contract call. @@ -923,6 +947,8 @@ pub enum PrecompileEvent { EcPairing(Box), /// Represents the I/O from Modexp call. ModExp(BigModExp), + /// Represents the I/O from SHA256 call. + SHA256(SHA256), } impl Default for PrecompileEvent { @@ -1369,3 +1395,12 @@ impl Default for BigModExp { } } } + +/// Event representating an SHA256 hash in precompile sha256. +#[derive(Clone, Debug, Default)] +pub struct SHA256 { + /// input bytes + pub input: Vec, + /// digest + pub digest: [u8; 32], +} diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 251e536c09..a5615573da 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1346,7 +1346,7 @@ impl<'a> CircuitInputStateRef<'a> { ) -> Result<(), Error> { let call = self.call()?.clone(); let geth_step = steps - .get(0) + .first() .ok_or(Error::InternalError("invalid index 0"))?; let is_err = exec_step.error.is_some(); let is_return_revert_succ = (geth_step.op == OpcodeId::REVERT @@ -1510,13 +1510,13 @@ impl<'a> CircuitInputStateRef<'a> { return Ok(Some(ExecError::InvalidOpcode)); } - if let Some(error) = &step.error { + if let Some(error) = step.error { return Ok(Some(get_step_reported_error(&step.op, error))); } let call = self.call()?; - if matches!(next_step, None) { + if next_step.is_none() { // enumerating call scope successful cases // case 1: call with normal halt opcode termination if matches!( diff --git a/bus-mapping/src/circuit_input_builder/l2.rs b/bus-mapping/src/circuit_input_builder/l2.rs index 117cea4084..b480637022 100644 --- a/bus-mapping/src/circuit_input_builder/l2.rs +++ b/bus-mapping/src/circuit_input_builder/l2.rs @@ -395,10 +395,9 @@ impl CircuitInputBuilder { !existed }), ) - .fold( - Ok(HashMap::new()), - |m, parsed| -> Result, Error> { - let mut m = m?; + .try_fold( + HashMap::new(), + |mut m, parsed| -> Result, Error> { let (addr, acc) = parsed.map_err(Error::IoError)?; m.insert(addr, acc); Ok(m) @@ -415,10 +414,9 @@ impl CircuitInputBuilder { !existed }), ) - .fold( - Ok(HashMap::new()), - |m, parsed| -> Result, Error> { - let mut m = m?; + .try_fold( + HashMap::new(), + |mut m, parsed| -> Result, Error> { let ((addr, key), val) = parsed.map_err(Error::IoError)?; m.insert((addr, key), val.into()); Ok(m) diff --git a/bus-mapping/src/circuit_input_builder/tracer_tests.rs b/bus-mapping/src/circuit_input_builder/tracer_tests.rs index 3d6a73bc44..a8d992ab52 100644 --- a/bus-mapping/src/circuit_input_builder/tracer_tests.rs +++ b/bus-mapping/src/circuit_input_builder/tracer_tests.rs @@ -4,10 +4,6 @@ use crate::{ error::{ ContractAddressCollisionError, DepthError, ExecError, InsufficientBalanceError, OogError, }, - geth_errors::{ - GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW, - GETH_ERR_STACK_UNDERFLOW, - }, operation::RWCounter, state_db::Account, }; @@ -15,15 +11,14 @@ use eth_types::{ address, bytecode, evm_types::{stack::Stack, Gas, Memory, OpcodeId}, geth_types::GethData, - word, Bytecode, Hash, ToAddress, ToWord, Word, + word, Bytecode, GethExecError, Hash, ToAddress, ToWord, Word, }; -use lazy_static::lazy_static; use mock::{ test_ctx::{helpers::*, LoggerConfig, TestContext}, MOCK_COINBASE, }; use pretty_assertions::assert_eq; -use std::collections::HashSet; +use std::{collections::HashSet, sync::LazyLock}; // Helper struct that contains a CircuitInputBuilder, a particular tx and a // particular execution step so that we can easily get a @@ -78,12 +73,11 @@ impl CircuitInputBuilderTx { } } -lazy_static! { - static ref ADDR_A: Address = Address::zero(); - static ref WORD_ADDR_A: Word = ADDR_A.to_word(); - static ref ADDR_B: Address = address!("0x0000000000000000000000000000000000000123"); - static ref WORD_ADDR_B: Word = ADDR_B.to_word(); -} +static ADDR_A: LazyLock

= LazyLock::new(Address::zero); +static WORD_ADDR_A: LazyLock = LazyLock::new(|| ADDR_A.to_word()); +static ADDR_B: LazyLock
= + LazyLock::new(|| address!("0x0000000000000000000000000000000000000123")); +static WORD_ADDR_B: LazyLock = LazyLock::new(|| ADDR_B.to_word()); fn mock_internal_create() -> Call { Call { @@ -1503,7 +1497,7 @@ fn tracer_err_gas_uint_overflow() { let step = &block.geth_traces[0].struct_logs[index]; let next_step = block.geth_traces[0].struct_logs.get(index + 1); assert_eq!(step.op, OpcodeId::MSTORE); - assert_eq!(step.error, Some(GETH_ERR_GAS_UINT_OVERFLOW.to_string())); + assert_eq!(step.error, Some(GethExecError::GasUintOverflow)); let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( @@ -1674,7 +1668,7 @@ fn tracer_err_out_of_gas() { .into(); let struct_logs = &block.geth_traces[0].struct_logs; - assert_eq!(struct_logs[1].error, Some(GETH_ERR_OUT_OF_GAS.to_string())); + assert_eq!(struct_logs[1].error, Some(GethExecError::OutOfGas)); } #[test] @@ -1697,10 +1691,13 @@ fn tracer_err_stack_overflow() { let index = block.geth_traces[0].struct_logs.len() - 1; // PUSH2 let step = &block.geth_traces[0].struct_logs[index]; let next_step = block.geth_traces[0].struct_logs.get(index + 1); - assert_eq!( + assert!(matches!( step.error, - Some(format!("{GETH_ERR_STACK_OVERFLOW} 1024 (1023)")) - ); + Some(GethExecError::StackOverflow { + stack_len: 1024, + limit: 1023, + }) + )); let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( @@ -1728,10 +1725,13 @@ fn tracer_err_stack_underflow() { let index = 0; // SWAP5 let step = &block.geth_traces[0].struct_logs[index]; let next_step = block.geth_traces[0].struct_logs.get(index + 1); - assert_eq!( + assert!(matches!( step.error, - Some(format!("{GETH_ERR_STACK_UNDERFLOW} (0 <=> 6)",)) - ); + Some(GethExecError::StackUnderflow { + stack_len: 0, + required: 6, + }) + )); let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index bf41228243..e62c18621b 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -1,15 +1,10 @@ //! Error module for the bus-mapping crate use core::fmt::{Display, Formatter, Result as FmtResult}; -use eth_types::{evm_types::OpcodeId, Address, GethExecStep, Word, H256}; +use eth_types::{evm_types::OpcodeId, Address, GethExecError, GethExecStep, Word, H256}; use ethers_providers::ProviderError; use std::error::Error as StdError; -use crate::geth_errors::{ - GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW, - GETH_ERR_STACK_UNDERFLOW, GETH_ERR_WRITE_PROTECTION, -}; - /// Error type for any BusMapping related failure. #[derive(Debug)] pub enum Error { @@ -185,42 +180,43 @@ pub enum ExecError { } // TODO: Move to impl block. -pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { - if [GETH_ERR_OUT_OF_GAS, GETH_ERR_GAS_UINT_OVERFLOW].contains(&error) { - // NOTE: We report a GasUintOverflow error as an OutOfGas error - let oog_err = match op { - OpcodeId::MLOAD | OpcodeId::MSTORE | OpcodeId::MSTORE8 => { - OogError::StaticMemoryExpansion - } - OpcodeId::RETURN | OpcodeId::REVERT => OogError::DynamicMemoryExpansion, - OpcodeId::CALLDATACOPY - | OpcodeId::CODECOPY - | OpcodeId::EXTCODECOPY - | OpcodeId::RETURNDATACOPY => OogError::MemoryCopy, - OpcodeId::BALANCE | OpcodeId::EXTCODESIZE | OpcodeId::EXTCODEHASH => { - OogError::AccountAccess - } - OpcodeId::LOG0 | OpcodeId::LOG1 | OpcodeId::LOG2 | OpcodeId::LOG3 | OpcodeId::LOG4 => { - OogError::Log - } - OpcodeId::EXP => OogError::Exp, - OpcodeId::SHA3 => OogError::Sha3, - OpcodeId::CALL | OpcodeId::CALLCODE | OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => { - OogError::Call - } - OpcodeId::SLOAD | OpcodeId::SSTORE => OogError::SloadSstore, - OpcodeId::CREATE | OpcodeId::CREATE2 => OogError::Create, - OpcodeId::SELFDESTRUCT => OogError::SelfDestruct, - _ => OogError::Constant, - }; - ExecError::OutOfGas(oog_err) - } else if error.starts_with(GETH_ERR_STACK_OVERFLOW) { - ExecError::StackOverflow - } else if error.starts_with(GETH_ERR_STACK_UNDERFLOW) { - ExecError::StackUnderflow - } else if error.starts_with(GETH_ERR_WRITE_PROTECTION) { - ExecError::WriteProtection - } else { - panic!("Unknown GethExecStep.error: {error}"); +pub(crate) fn get_step_reported_error(op: &OpcodeId, error: GethExecError) -> ExecError { + match error { + GethExecError::OutOfGas | GethExecError::GasUintOverflow => { + // NOTE: We report a GasUintOverflow error as an OutOfGas error + let oog_err = match op { + OpcodeId::MLOAD | OpcodeId::MSTORE | OpcodeId::MSTORE8 => { + OogError::StaticMemoryExpansion + } + OpcodeId::RETURN | OpcodeId::REVERT => OogError::DynamicMemoryExpansion, + OpcodeId::CALLDATACOPY + | OpcodeId::CODECOPY + | OpcodeId::EXTCODECOPY + | OpcodeId::RETURNDATACOPY => OogError::MemoryCopy, + OpcodeId::BALANCE | OpcodeId::EXTCODESIZE | OpcodeId::EXTCODEHASH => { + OogError::AccountAccess + } + OpcodeId::LOG0 + | OpcodeId::LOG1 + | OpcodeId::LOG2 + | OpcodeId::LOG3 + | OpcodeId::LOG4 => OogError::Log, + OpcodeId::EXP => OogError::Exp, + OpcodeId::SHA3 => OogError::Sha3, + OpcodeId::CALL + | OpcodeId::CALLCODE + | OpcodeId::DELEGATECALL + | OpcodeId::STATICCALL => OogError::Call, + OpcodeId::SLOAD | OpcodeId::SSTORE => OogError::SloadSstore, + OpcodeId::CREATE | OpcodeId::CREATE2 => OogError::Create, + OpcodeId::SELFDESTRUCT => OogError::SelfDestruct, + _ => OogError::Constant, + }; + ExecError::OutOfGas(oog_err) + } + GethExecError::StackOverflow { .. } => ExecError::StackOverflow, + GethExecError::StackUnderflow { .. } => ExecError::StackUnderflow, + GethExecError::WriteProtection => ExecError::WriteProtection, + _ => panic!("Unknown GethExecStep.error: {error}"), } } diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 1a404d8862..15bd6c6c41 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -12,7 +12,8 @@ use crate::{ use core::fmt::Debug; use eth_types::{evm_unimplemented, GethExecStep, ToAddress, ToWord, Word}; -use crate::util::CHECK_MEM_STRICT; +#[cfg(feature = "enable-memory")] +use crate::util::GETH_TRACE_CHECK_LEVEL; #[cfg(any(feature = "test", test))] pub use self::sha3::sha3_tests::{gen_sha3_code, MemoryKind}; @@ -70,7 +71,7 @@ mod error_precompile_failed; mod error_return_data_outofbound; mod error_write_protection; -#[cfg(test)] +#[cfg(all(feature = "enable-memory", test))] mod memory_expansion_test; #[cfg(feature = "test")] pub use callop::tests::PrecompileCallArgs; @@ -383,8 +384,8 @@ pub fn gen_associated_ops( state: &mut CircuitInputStateRef, geth_steps: &[GethExecStep], ) -> Result, Error> { - let check_level = if *CHECK_MEM_STRICT { 2 } else { 0 }; // 0: no check, 1: check and log error and fix, 2: check and assert_eq - if check_level >= 1 { + #[cfg(feature = "enable-memory")] + if GETH_TRACE_CHECK_LEVEL.should_check() { let memory_enabled = !geth_steps.iter().all(|s| s.memory.is_empty()); assert!(memory_enabled); if memory_enabled { @@ -414,7 +415,7 @@ pub fn gen_associated_ops( ); } } - if check_level >= 2 { + if GETH_TRACE_CHECK_LEVEL.should_panic() { panic!("mem wrong"); } state.call_ctx_mut()?.memory = geth_steps[0].memory.clone(); diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index 3bc0d30281..0689b03411 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -12,7 +12,11 @@ use crate::{ }; use core::fmt::Debug; use eth_types::{ - evm_types::{gas_utils::tx_data_gas_cost, GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED}, + evm_types::{ + gas_utils::{tx_access_list_gas_cost, tx_data_gas_cost}, + GasCost, MAX_REFUND_QUOTIENT_OF_GAS_USED, + }, + geth_types::access_list_size, Bytecode, ToWord, Word, }; use ethers_core::utils::get_contract_address; @@ -39,10 +43,12 @@ impl TxExecSteps for BeginEndTx { pub fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result { let mut exec_step = state.new_begin_tx_step(); - let call = state.call()?.clone(); - let caller_address = call.caller_address; + // Add two copy-events for tx access-list addresses and storage keys if EIP-2930. + gen_tx_eip2930_ops(state, &mut exec_step)?; + let call = state.call()?.clone(); + let caller_address = call.caller_address; if state.tx.tx_type.is_l1_msg() { // for l1 message, no need to add rw op, but we must check // caller for its existent status @@ -186,13 +192,15 @@ pub fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result Result Result<(), Error> { + if !state.tx.tx_type.is_eip2930_tx() { + return Ok(()); + } + + let tx_id = NumberOrHash::Number(state.tx_ctx.id()); + let (address_size, storage_key_size) = access_list_size(&state.tx.access_list); + + // Add copy event for access-list addresses. + let rw_counter_start = state.block_ctx.rwc; + state.push_copy( + exec_step, + CopyEvent { + src_addr: 0, + src_addr_end: address_size, + src_type: CopyDataType::AccessListAddresses, + src_id: tx_id.clone(), + dst_addr: 0, + dst_type: CopyDataType::AccessListAddresses, + dst_id: tx_id.clone(), + log_id: None, + rw_counter_start, + // TODO + copy_bytes: CopyBytes::new(vec![], None, None), + }, + ); + + // Add copy event for access-list storage keys. + let rw_counter_start = state.block_ctx.rwc; + state.push_copy( + exec_step, + CopyEvent { + src_addr: 0, + src_addr_end: storage_key_size, + src_type: CopyDataType::AccessListStorageKeys, + src_id: tx_id.clone(), + dst_addr: 0, + dst_type: CopyDataType::AccessListStorageKeys, + dst_id: tx_id, + log_id: None, + rw_counter_start, + // TODO + copy_bytes: CopyBytes::new(vec![], None, None), + }, + ); + + Ok(()) +} diff --git a/bus-mapping/src/evm/opcodes/calldataload.rs b/bus-mapping/src/evm/opcodes/calldataload.rs index 7bc2a398a1..75b5cf81ba 100644 --- a/bus-mapping/src/evm/opcodes/calldataload.rs +++ b/bus-mapping/src/evm/opcodes/calldataload.rs @@ -150,8 +150,7 @@ mod calldataload_tests { memory_a.resize(call_data_length, 0); } - let mut memory_bytes = vec![]; - memory_bytes.resize(32 - pushdata.len(), 0); + let mut memory_bytes = vec![0; 32 - pushdata.len()]; let mut pushdata_mut = pushdata.clone(); memory_bytes.append(&mut pushdata_mut); // let code_a = bytecode! { diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 642ec094cd..61927d4092 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -753,9 +753,6 @@ pub mod tests { address: Word::from(0x2), stack_value: vec![( Word::from(0x20), - #[cfg(feature = "scroll")] - Word::zero(), - #[cfg(not(feature = "scroll"))] word!("a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89"), )], ..Default::default() diff --git a/bus-mapping/src/evm/opcodes/chainid.rs b/bus-mapping/src/evm/opcodes/chainid.rs index 3c997b5b0e..4c7e43cf92 100644 --- a/bus-mapping/src/evm/opcodes/chainid.rs +++ b/bus-mapping/src/evm/opcodes/chainid.rs @@ -54,7 +54,7 @@ mod chainid_tests { }, ( RW::WRITE, - &StackOp::new(1, StackAddress::from(1023), (*MOCK_CHAIN_ID).into()) + &StackOp::new(1, StackAddress::from(1023), (MOCK_CHAIN_ID).into()) ) ); } diff --git a/bus-mapping/src/evm/opcodes/memory_expansion_test.rs b/bus-mapping/src/evm/opcodes/memory_expansion_test.rs index 36eb5ad5a0..43b0424a47 100644 --- a/bus-mapping/src/evm/opcodes/memory_expansion_test.rs +++ b/bus-mapping/src/evm/opcodes/memory_expansion_test.rs @@ -15,14 +15,13 @@ fn might_neg_index(index: isize, len: usize) -> usize { } } -fn assert_expanded(_traces: &[GethExecStep], _before: isize, _after: isize) { - // FIXME: memory is removed - // let traces_len = traces.len(); - // let before = might_neg_index(before, traces_len); - // let after = might_neg_index(after, traces_len); - // let size_before = traces[before].memory.borrow().len(); - // let size_after = traces[after].memory.borrow().len(); - // assert_ne!(size_before, size_after); +fn assert_expanded(traces: &[GethExecStep], before: isize, after: isize) { + let traces_len = traces.len(); + let before = might_neg_index(before, traces_len); + let after = might_neg_index(after, traces_len); + let size_before = traces[before].memory.0.len(); + let size_after = traces[after].memory.0.len(); + assert_ne!(size_before, size_after); } fn trace_and_assert(code: Bytecode, before: isize, after: isize, assert_fn: FN) diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index b74a18de36..a59571f338 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -1,7 +1,9 @@ use eth_types::{GethExecStep, ToWord, Word}; use crate::{ - circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, + circuit_input_builder::{ + Call, CircuitInputStateRef, ExecState, ExecStep, PrecompileEvent, SHA256, + }, operation::CallContextField, precompile::{PrecompileAuxData, PrecompileCalls}, Error, @@ -50,6 +52,23 @@ pub fn gen_associated_ops( return_bytes: return_bytes.to_vec(), }), ), + PrecompileCalls::Sha256 => ( + if output_bytes.is_empty() { + None + } else { + Some(PrecompileEvent::SHA256(SHA256 { + input: input_bytes.to_vec(), + digest: output_bytes + .try_into() + .expect("output bytes must be 32 bytes"), + })) + }, + Some(PrecompileAuxData::SHA256 { + input_bytes: input_bytes.to_vec(), + output_bytes: output_bytes.to_vec(), + return_bytes: return_bytes.to_vec(), + }), + ), _ => { log::warn!("precompile {:?} unsupported in circuits", precompile); ( diff --git a/bus-mapping/src/geth_errors.rs b/bus-mapping/src/geth_errors.rs deleted file mode 100644 index b10b95bd22..0000000000 --- a/bus-mapping/src/geth_errors.rs +++ /dev/null @@ -1,10 +0,0 @@ -/// Geth error message for stack overflow -pub const GETH_ERR_STACK_OVERFLOW: &str = "stack limit reached"; -/// Geth error message for stack underflow -pub const GETH_ERR_STACK_UNDERFLOW: &str = "stack underflow"; -/// Geth error message for out of gas -pub const GETH_ERR_OUT_OF_GAS: &str = "out of gas"; -/// Geth error message for gas uint64 overflow -pub const GETH_ERR_GAS_UINT_OVERFLOW: &str = "gas uint64 overflow"; -/// Geth error message for write protection -pub const GETH_ERR_WRITE_PROTECTION: &str = "write protection"; diff --git a/bus-mapping/src/l2_predeployed.rs b/bus-mapping/src/l2_predeployed.rs index b336ee1d64..e7ff7e3535 100644 --- a/bus-mapping/src/l2_predeployed.rs +++ b/bus-mapping/src/l2_predeployed.rs @@ -6,29 +6,27 @@ use eth_types::Address; pub mod message_queue { use super::*; use eth_types::U256; - use once_cell::sync::Lazy; - use std::str::FromStr; + use std::{str::FromStr, sync::LazyLock}; /// address of L2MessageQueue predeploy - pub static ADDRESS: Lazy
= - Lazy::new(|| Address::from_str("0x5300000000000000000000000000000000000000").unwrap()); + pub static ADDRESS: LazyLock
= + LazyLock::new(|| Address::from_str("0x5300000000000000000000000000000000000000").unwrap()); /// the slot of withdraw root in L2MessageQueue - pub static WITHDRAW_TRIE_ROOT_SLOT: Lazy = Lazy::new(U256::zero); + pub static WITHDRAW_TRIE_ROOT_SLOT: U256 = U256::zero(); } /// Helper for L1GasPriceOracle contract pub mod l1_gas_price_oracle { use eth_types::{Address, U256}; - use once_cell::sync::Lazy; - use std::str::FromStr; + use std::{str::FromStr, sync::LazyLock}; /// L1GasPriceOracle predeployed address - pub static ADDRESS: Lazy
= - Lazy::new(|| Address::from_str("0x5300000000000000000000000000000000000002").unwrap()); + pub static ADDRESS: LazyLock
= + LazyLock::new(|| Address::from_str("0x5300000000000000000000000000000000000002").unwrap()); /// L1 base fee slot in L1GasPriceOracle - pub static BASE_FEE_SLOT: Lazy = Lazy::new(|| U256::from(1)); + pub static BASE_FEE_SLOT: LazyLock = LazyLock::new(|| U256::from(1)); /// L1 overhead slot in L1GasPriceOracle - pub static OVERHEAD_SLOT: Lazy = Lazy::new(|| U256::from(2)); + pub static OVERHEAD_SLOT: LazyLock = LazyLock::new(|| U256::from(2)); /// L1 scalar slot in L1GasPriceOracle - pub static SCALAR_SLOT: Lazy = Lazy::new(|| U256::from(3)); + pub static SCALAR_SLOT: LazyLock = LazyLock::new(|| U256::from(3)); } diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index 2a8cd256cd..b6278bc472 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(lazy_cell)] //! ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/appliedzkp/zkevm-circuits/CI%20checks/main?style=for-the-badge) //! Bus-Mapping is a crate designed to parse EVM execution traces and manipulate //! all of the data they provide in order to obtain structured witness inputs @@ -234,7 +235,6 @@ pub mod circuit_input_builder; pub mod error; pub mod evm; pub mod exec_trace; -pub(crate) mod geth_errors; pub mod l2_predeployed; pub mod mock; pub mod operation; diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 7692325f68..6239d7f836 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -59,8 +59,7 @@ impl BlockData { let access_set: AccessSet = get_state_accesses(&geth_data.eth_block, &geth_data.geth_traces) - .expect("state accesses") - .into(); + .expect("state accesses"); // Initialize all accesses accounts to zero for addr in access_set.state.keys() { sdb.set_account(addr, state_db::Account::zero()); diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 80839d5a8b..5818095e3d 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -18,8 +18,9 @@ pub(crate) fn execute_precompiled( input: &[u8], gas: u64, ) -> (Vec, u64, bool) { - let Some(Precompile::Standard(precompile_fn)) = Precompiles::berlin() - .get(address.as_fixed_bytes()) else { + let Some(Precompile::Standard(precompile_fn)) = + Precompiles::berlin().get(address.as_fixed_bytes()) + else { panic!("calling non-exist precompiled contract address") }; log::trace!( @@ -33,9 +34,9 @@ pub(crate) fn execute_precompiled( // Revm behavior is different from scroll evm, // so we need to override the behavior of invalid input match PrecompileCalls::from(address.0[19]) { - PrecompileCalls::Blake2F - | PrecompileCalls::Sha256 - | PrecompileCalls::Ripemd160 => (vec![], gas, false, false), + PrecompileCalls::Blake2F | PrecompileCalls::Ripemd160 => { + (vec![], gas, false, false) + } PrecompileCalls::Bn128Pairing => { if input.len() > N_PAIRING_PER_OP * N_BYTES_PER_PAIR { (vec![], gas, false, false) @@ -455,6 +456,15 @@ pub enum PrecompileAuxData { /// bytes returned back to the caller from the identity call. return_bytes: Vec, }, + /// SHA256 + SHA256 { + /// input bytes to the sha256 call. + input_bytes: Vec, + /// output bytes from the sha256 call. + output_bytes: Vec, + /// bytes returned back to the caller from the sha256 call. + return_bytes: Vec, + }, /// Ecrecover. Ecrecover(EcrecoverAuxData), /// Modexp. diff --git a/bus-mapping/src/rpc.rs b/bus-mapping/src/rpc.rs index 5f8ccb5336..7525671829 100644 --- a/bus-mapping/src/rpc.rs +++ b/bus-mapping/src/rpc.rs @@ -11,7 +11,7 @@ use ethers_providers::JsonRpcClient; use serde::Serialize; use std::collections::HashMap; -use crate::util::CHECK_MEM_STRICT; +use crate::util::GETH_TRACE_CHECK_LEVEL; /// Serialize a type. /// @@ -37,15 +37,19 @@ pub(crate) struct GethLoggerConfig { /// enable return data capture #[serde(rename = "EnableReturnData")] enable_return_data: bool, + /// enable return data capture + #[serde(rename = "timeout")] + timeout: Option, } impl Default for GethLoggerConfig { fn default() -> Self { Self { - enable_memory: false, - disable_stack: false, - disable_storage: false, + enable_memory: cfg!(feature = "enable-memory"), + disable_stack: !cfg!(feature = "enable-stack"), + disable_storage: !cfg!(feature = "enable-storage"), enable_return_data: true, + timeout: None, } } } @@ -137,7 +141,10 @@ impl GethClient

{ block_num: BlockNumber, ) -> Result, Error> { let num = serialize(&block_num); - let cfg = serialize(&GethLoggerConfig::default()); + let cfg = serialize(&GethLoggerConfig { + timeout: Some("300s".to_string()), + ..Default::default() + }); let resp: ResultGethExecTraces = self .0 .request("debug_traceBlockByNumber", [num, cfg]) @@ -146,10 +153,10 @@ impl GethClient

{ Ok(resp.0.into_iter().map(|step| step.result).collect()) } /// .. - pub async fn trace_tx_by_hash(&self, hash: H256) -> Result, Error> { + pub async fn trace_tx_by_hash(&self, hash: H256) -> Result { let hash = serialize(&hash); let cfg = GethLoggerConfig { - enable_memory: *CHECK_MEM_STRICT, + enable_memory: cfg!(feature = "enable-memory") && GETH_TRACE_CHECK_LEVEL.should_check(), ..Default::default() }; let cfg = serialize(&cfg); @@ -158,7 +165,7 @@ impl GethClient

{ .request("debug_traceTransaction", [hash, cfg]) .await .map_err(|e| Error::JSONRpcError(e.into()))?; - Ok(vec![resp]) + Ok(resp) } /// Call `debug_traceBlockByHash` use prestateTracer to get prestate @@ -169,6 +176,7 @@ impl GethClient

{ let hash = serialize(&hash); let cfg = serialize(&serde_json::json! ({ "tracer": "prestateTracer", + "timeout": "300s", })); let resp: ResultGethPrestateTraces = self .0 @@ -178,6 +186,23 @@ impl GethClient

{ Ok(resp.0.into_iter().map(|step| step.result).collect()) } + /// Call `debug_traceTransaction` use prestateTracer to get prestate + pub async fn trace_tx_prestate_by_hash( + &self, + hash: H256, + ) -> Result, Error> { + let hash = serialize(&hash); + let cfg = serialize(&serde_json::json! ({ + "tracer": "prestateTracer", + })); + let resp: HashMap = self + .0 + .request("debug_traceTransaction", [hash, cfg]) + .await + .map_err(|e| Error::JSONRpcError(e.into()))?; + Ok(resp) + } + /// Calls `eth_getCode` via JSON-RPC returning a contract code pub async fn get_code( &self, diff --git a/bus-mapping/src/state_db.rs b/bus-mapping/src/state_db.rs index 76421646a0..deae4d145e 100644 --- a/bus-mapping/src/state_db.rs +++ b/bus-mapping/src/state_db.rs @@ -6,20 +6,20 @@ use crate::{ util::{hash_code, KECCAK_CODE_HASH_EMPTY}, }; use eth_types::{Address, Hash, Word, H256, U256}; -use lazy_static::lazy_static; -use std::collections::{BTreeSet, HashMap, HashSet}; - -lazy_static! { - static ref ACCOUNT_ZERO: Account = Account::zero(); - /// Hash value for empty code hash. - static ref EMPTY_CODE_HASH: Hash = CodeDB::hash(&[]); - /// bytes of empty code hash, in little endian order. - pub static ref EMPTY_CODE_HASH_LE: [u8; 32] = { - let mut bytes = EMPTY_CODE_HASH.to_fixed_bytes(); - bytes.reverse(); - bytes - }; -} +use std::{ + collections::{BTreeSet, HashMap, HashSet}, + sync::LazyLock, +}; + +static ACCOUNT_ZERO: LazyLock = LazyLock::new(Account::zero); +/// Hash value for empty code hash. +static EMPTY_CODE_HASH: LazyLock = LazyLock::new(|| CodeDB::hash(&[])); +/// bytes of empty code hash, in little endian order. +pub static EMPTY_CODE_HASH_LE: LazyLock<[u8; 32]> = LazyLock::new(|| { + let mut bytes = EMPTY_CODE_HASH.to_fixed_bytes(); + bytes.reverse(); + bytes +}); const VALUE_ZERO: Word = Word::zero(); diff --git a/bus-mapping/src/util.rs b/bus-mapping/src/util.rs index fb4340b75e..e44fe09a02 100644 --- a/bus-mapping/src/util.rs +++ b/bus-mapping/src/util.rs @@ -2,9 +2,7 @@ use eth_types::{Hash, U256}; pub use eth_types::{KECCAK_CODE_HASH_EMPTY, POSEIDON_CODE_HASH_EMPTY}; use halo2_proofs::halo2curves::{bn256::Fr, group::ff::PrimeField}; -use once_cell::sync::Lazy; - -use std::str::FromStr; +use std::{convert::Infallible, str::FromStr, sync::LazyLock}; /// .. pub fn read_env_var(var_name: &'static str, default: T) -> T { @@ -12,8 +10,44 @@ pub fn read_env_var(var_name: &'static str, default: T) -> T .map(|s| s.parse::().unwrap_or_else(|_| default.clone())) .unwrap_or(default) } -/// .. -pub static CHECK_MEM_STRICT: Lazy = Lazy::new(|| read_env_var("CHECK_MEM_STRICT", false)); +/// env var for Geth trace sanity check level +pub static GETH_TRACE_CHECK_LEVEL: LazyLock = + LazyLock::new(|| read_env_var("GETH_TRACE_CHECK_LEVEL", GethTraceSanityCheckLevel::None)); + +/// Geth trace sanity check level +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GethTraceSanityCheckLevel { + /// No sanity check + None, + /// Check sanity and log error + Check, + /// Panic on error + Strict, +} + +impl GethTraceSanityCheckLevel { + /// Whether to do sanity check + pub fn should_check(&self) -> bool { + *self != GethTraceSanityCheckLevel::None + } + + /// Whether to panic on error + pub fn should_panic(&self) -> bool { + *self == GethTraceSanityCheckLevel::Strict + } +} + +impl FromStr for GethTraceSanityCheckLevel { + type Err = Infallible; + + fn from_str(s: &str) -> Result { + match s { + "strict" => Ok(GethTraceSanityCheckLevel::Strict), + _ if !s.is_empty() => Ok(GethTraceSanityCheckLevel::Check), + _ => Ok(GethTraceSanityCheckLevel::None), + } + } +} /// Default number of bytes to pack into a field element. pub const POSEIDON_HASH_BYTES_IN_FIELD: usize = 31; diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index dce51c2340..b1d5409635 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -48,7 +48,7 @@ mod tests { STOP }; - let wallet_a = LocalWallet::new(&mut rng).with_chain_id(*MOCK_CHAIN_ID); + let wallet_a = LocalWallet::new(&mut rng).with_chain_id(MOCK_CHAIN_ID); let addr_a = wallet_a.address(); let addr_b = address!("0x000000000000000000000000000000000000BBBB"); diff --git a/circuit-benchmarks/src/tx_circuit.rs b/circuit-benchmarks/src/tx_circuit.rs index 41fc55d3c6..931a82f006 100644 --- a/circuit-benchmarks/src/tx_circuit.rs +++ b/circuit-benchmarks/src/tx_circuit.rs @@ -84,7 +84,7 @@ mod tests { let max_txs: usize = 2_usize.pow(degree) / ROWS_PER_TX; let txs = vec![mock::CORRECT_MOCK_TXS[0].clone().into()]; - let circuit = TxCircuit::::new(max_txs, MAX_CALLDATA, *mock::MOCK_CHAIN_ID, 0, txs); + let circuit = TxCircuit::::new(max_txs, MAX_CALLDATA, mock::MOCK_CHAIN_ID, 0, txs); (degree as usize, circuit) } diff --git a/eth-types/Cargo.toml b/eth-types/Cargo.toml index 43d47b92d2..c3fa380526 100644 --- a/eth-types/Cargo.toml +++ b/eth-types/Cargo.toml @@ -8,9 +8,8 @@ license.workspace = true ethers-core.workspace = true ethers-signers.workspace = true hex.workspace = true -lazy_static.workspace = true -once_cell.workspace = true halo2_proofs.workspace = true +halo2-base.workspace = true regex.workspace = true serde.workspace = true serde_json.workspace = true @@ -30,3 +29,8 @@ default = ["warn-unimplemented"] warn-unimplemented = [] shanghai = [] scroll = [] + +# trace heap allocation related feature switches +enable-stack = [] +enable-memory = [] +enable-storage = [] \ No newline at end of file diff --git a/eth-types/src/bytecode.rs b/eth-types/src/bytecode.rs index 75764f362f..3d68696290 100644 --- a/eth-types/src/bytecode.rs +++ b/eth-types/src/bytecode.rs @@ -573,7 +573,7 @@ mod tests { POP STOP }; - assert_eq!(Bytecode::try_from(code.to_vec()).unwrap(), code); + assert_eq!(Bytecode::from(code.to_vec()), code); } #[test] diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index 33d722f60c..197d4251a8 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -1,7 +1,7 @@ //! Evm types needed for parsing instruction sets as well use serde::{Deserialize, Serialize}; -use std::fmt; +use std::{fmt, marker::ConstParamTy}; pub mod block_utils; pub mod gas_utils; @@ -78,8 +78,7 @@ mod gas_create { // [gasCreate2Eip3860](https://github.com/ethereum/go-ethereum/blob/eb83e7c54021573eaceb14236af3a7a8c64f6027/core/vm/gas_table.go#L321) // (similar for CREATE). // 1. size <= 49152 (MaxInitCodeSize) - // 2. gasCost = memoryGasCost + (2 + 6) * ((size + 31) / 32) should not - // overflow for Uint64. + // 2. gasCost = memoryGasCost + (2 + 6) * ((size + 31) / 32) should not overflow for Uint64. // No need to constrain the second condition, since the maximum gas cost // cannot overflow for Uint64 (36028809887100925 calculated by // `memorySize = 0x1FFFFFFFE0` and `size = 49152`) if the first condition is @@ -106,7 +105,7 @@ mod gas_create { pub use gas_create::*; /// Defines the gas consumption. -#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, ConstParamTy)] pub struct GasCost(pub u64); impl fmt::Debug for GasCost { @@ -205,6 +204,10 @@ impl GasCost { pub const PRECOMPILE_MODEXP_MIN: Self = Self(200); /// Base gas cost for precompile call: BLAKE2F pub const PRECOMPILE_BLAKE2F: Self = Self(0); + /// Gas cost per address in tx access list (EIP 2930) + pub const ACCESS_LIST_PER_ADDRESS: Self = Self(2400); + /// Gas cost per storage key in tx access list (EIP 2930) + pub const ACCESS_LIST_PER_STORAGE_KEY: Self = Self(1900); } impl GasCost { diff --git a/eth-types/src/evm_types/gas_utils.rs b/eth-types/src/evm_types/gas_utils.rs index fb455936cf..a754b1658e 100644 --- a/eth-types/src/evm_types/gas_utils.rs +++ b/eth-types/src/evm_types/gas_utils.rs @@ -1,7 +1,7 @@ //! Utility functions to help calculate gas use super::GasCost; -use crate::Word; +use crate::{AccessList, Word}; /// Calculate memory expansion gas cost by current and next memory word size. pub fn memory_expansion_gas_cost(curr_memory_word_size: u64, next_memory_word_size: u64) -> u64 { @@ -49,6 +49,18 @@ pub fn eip150_gas(gas_left: u64, gas_specified: Word) -> u64 { capped_gas } +/// Calculate gas cost for access list (EIP 2930). +pub fn tx_access_list_gas_cost(access_list: &Option) -> u64 { + access_list.as_ref().map_or(0, |access_list| { + access_list.0.len() as u64 * GasCost::ACCESS_LIST_PER_ADDRESS.as_u64() + + access_list + .0 + .iter() + .fold(0, |acc, item| acc + item.storage_keys.len() as u64) + * GasCost::ACCESS_LIST_PER_STORAGE_KEY.as_u64() + }) +} + /// Calculate gas cost for transaction data. pub fn tx_data_gas_cost(data: &[u8]) -> u64 { data.iter() diff --git a/eth-types/src/evm_types/opcode_ids.rs b/eth-types/src/evm_types/opcode_ids.rs index a15fc12f02..b7c2f5593d 100644 --- a/eth-types/src/evm_types/opcode_ids.rs +++ b/eth-types/src/evm_types/opcode_ids.rs @@ -1,10 +1,9 @@ //! Doc this use crate::{error::Error, evm_types::GasCost}; use core::fmt::Debug; -use lazy_static::lazy_static; use regex::Regex; use serde::{de, Deserialize, Serialize}; -use std::{fmt, matches, str::FromStr}; +use std::{fmt, matches, str::FromStr, sync::LazyLock}; use strum_macros::EnumIter; /// Opcode enum. One-to-one corresponding to an `u8` value. @@ -1243,10 +1242,9 @@ impl FromStr for OpcodeId { "TSTORE" => OpcodeId::INVALID(0xb4), _ => { // Parse an invalid opcode value as reported by geth - lazy_static! { - static ref RE: Regex = Regex::new("opcode 0x([[:xdigit:]]{1,2}) not defined") - .expect("invalid regex"); - } + static RE: LazyLock = LazyLock::new(|| { + Regex::new("opcode 0x([[:xdigit:]]{1,2}) not defined").expect("invalid regex") + }); if let Some(cap) = RE.captures(s) { if let Some(byte_hex) = cap.get(1).map(|m| m.as_str()) { return Ok(OpcodeId::INVALID( diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index f9f5d06cba..9a6bc09272 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -49,12 +49,17 @@ impl From for u64 { impl TxType { /// If this type is L1Msg or not pub fn is_l1_msg(&self) -> bool { - matches!(*self, TxType::L1Msg) + matches!(*self, Self::L1Msg) } - /// If this type is Eip155 or not + /// If this type is EIP-155 or not pub fn is_eip155_tx(&self) -> bool { - matches!(*self, TxType::Eip155) + matches!(*self, Self::Eip155) + } + + /// If this type is EIP-2930 or not + pub fn is_eip2930_tx(&self) -> bool { + matches!(*self, Self::Eip2930) } /// Get the type of transaction @@ -110,9 +115,14 @@ impl TxType { /// Get the RLP bytes for signing pub fn get_rlp_unsigned(tx: &crate::Transaction) -> Vec { + let sig_v = tx.v; match TxType::get_tx_type(tx) { TxType::Eip155 => { - let tx: TransactionRequest = tx.into(); + let mut tx: TransactionRequest = tx.into(); + tx.chain_id = Some(tx.chain_id.unwrap_or_else(|| { + let recv_v = TxType::Eip155.get_recovery_id(sig_v.as_u64()) as u64; + (sig_v - recv_v - 35) / 2 + })); tx.rlp().to_vec() } TxType::PreEip155 => { @@ -397,3 +407,19 @@ impl GethData { } } */ + +/// Returns the number of addresses and the cumulative number of storage keys in +/// the entire access list. +pub fn access_list_size(access_list: &Option) -> (u64, u64) { + access_list.as_ref().map_or_else( + || (0, 0), + |list| { + ( + list.0.len() as u64, + list.0 + .iter() + .fold(0, |acc, item| acc + item.storage_keys.len()) as u64, + ) + }, + ) +} diff --git a/eth-types/src/l2_types.rs b/eth-types/src/l2_types.rs index c3ab407354..9730834b29 100644 --- a/eth-types/src/l2_types.rs +++ b/eth-types/src/l2_types.rs @@ -1,13 +1,20 @@ //! L2 types used to deserialize traces for l2geth. use crate::{ - evm_types::{Gas, GasCost, Memory, OpcodeId, ProgramCounter, Stack, Storage}, - Block, GethExecStep, GethExecTrace, Hash, Transaction, Word, H256, + evm_types::{Gas, GasCost, OpcodeId, ProgramCounter}, + Block, GethExecError, GethExecStep, GethExecTrace, Hash, Transaction, Word, H256, }; use ethers_core::types::{Address, Bytes, U256, U64}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +#[cfg(feature = "enable-memory")] +use crate::evm_types::Memory; +#[cfg(feature = "enable-stack")] +use crate::evm_types::Stack; +#[cfg(feature = "enable-storage")] +use crate::evm_types::Storage; + /// l2 block full trace #[derive(Deserialize, Serialize, Default, Debug, Clone)] pub struct BlockTrace { @@ -225,9 +232,12 @@ pub struct ExecStep { #[serde(default)] pub refund: u64, pub depth: isize, - pub error: Option, + pub error: Option, + #[cfg(feature = "enable-stack")] pub stack: Option>, + #[cfg(feature = "enable-memory")] pub memory: Option>, + #[cfg(feature = "enable-storage")] pub storage: Option>, #[serde(rename = "extraData")] pub extra_data: Option, @@ -235,10 +245,6 @@ pub struct ExecStep { impl From for GethExecStep { fn from(e: ExecStep) -> Self { - let stack = e.stack.map_or_else(Stack::new, Stack::from); - let storage = e.storage.map_or_else(Storage::empty, Storage::from); - let memory = e.memory.map_or_else(Memory::default, Memory::from); - GethExecStep { pc: ProgramCounter(e.pc as usize), // FIXME @@ -248,9 +254,12 @@ impl From for GethExecStep { refund: Gas(e.refund), depth: e.depth as u16, error: e.error, - stack, - memory, - storage, + #[cfg(feature = "enable-stack")] + stack: e.stack.map_or_else(Stack::new, Stack::from), + #[cfg(feature = "enable-memory")] + memory: e.memory.map_or_else(Memory::default, Memory::from), + #[cfg(feature = "enable-storage")] + storage: e.storage.map_or_else(Storage::empty, Storage::from), } } } diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index 10fef7ca67..95f96218a4 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -3,10 +3,15 @@ #![cfg_attr(docsrs, feature(doc_cfg))] // Temporary until we have more of the crate implemented. #![allow(dead_code)] +#![allow(incomplete_features)] // We want to have UPPERCASE idents sometimes. #![allow(non_snake_case)] +#![allow(incomplete_features)] // Catch documentation errors caused by code changes. #![deny(rustdoc::broken_intra_doc_links)] +// GasCost is used as type parameter +#![feature(adt_const_params)] +#![feature(lazy_cell)] #![deny(missing_docs)] //#![deny(unsafe_code)] Allowed now until we find a // better way to handle downcasting from Operation into it's variants. @@ -25,14 +30,10 @@ pub mod sign_types; pub use bytecode::Bytecode; pub use error::Error; -use halo2_proofs::{ - arithmetic::{Field as Halo2Field, FieldExt}, - halo2curves::{bn256::Fr, group::ff::PrimeField}, -}; +use halo2_base::utils::ScalarField; +use halo2_proofs::halo2curves::{bn256::Fr, group::ff::PrimeField}; -use crate::evm_types::{ - memory::Memory, stack::Stack, storage::Storage, Gas, GasCost, OpcodeId, ProgramCounter, -}; +use crate::evm_types::{Gas, GasCost, OpcodeId, ProgramCounter}; use ethers_core::types; pub use ethers_core::{ abi::ethereum_types::{BigEndianHash, U512}, @@ -41,28 +42,52 @@ pub use ethers_core::{ Address, Block, Bytes, Signature, H160, H256, H64, U256, U64, }, }; +use serde::{de, Deserialize, Deserializer, Serialize}; +use std::{ + collections::HashMap, + fmt, + fmt::{Display, Formatter}, + str::FromStr, + sync::LazyLock, +}; -use once_cell::sync::Lazy; -use serde::{de, Deserialize, Serialize}; -use std::{collections::HashMap, fmt, str::FromStr}; +#[cfg(feature = "enable-memory")] +use crate::evm_types::Memory; +#[cfg(feature = "enable-stack")] +use crate::evm_types::Stack; +#[cfg(feature = "enable-storage")] +use crate::evm_types::Storage; -/// Trait used to reduce verbosity with the declaration of the [`FieldExt`] +/// Trait used to reduce verbosity with the declaration of the [`Field`] /// trait and its repr. pub trait Field: - FieldExt - + Halo2Field - + PrimeField + PrimeField + hash_circuit::hash::Hashable + std::convert::From + + ScalarField + std::hash::Hash { + /// Re-expose zero element as a function + fn zero() -> Self { + Self::ZERO + } + + /// Re-expose one element as a function + fn one() -> Self { + Self::ONE + } + + /// Expose the lower 128 bits + fn get_lower_128(&self) -> u128 { + u128::from_le_bytes(self.to_repr().as_ref()[..16].try_into().unwrap()) + } } // Impl custom `Field` trait for BN256 Fr to be used and consistent with the // rest of the workspace. impl Field for Fr {} -// Impl custom `Field` trait for BN256 Frq to be used and consistent with the +// Impl custom `Field` trait for BN256 Fq to be used and consistent with the // rest of the workspace. // impl Field for Fq {} @@ -295,11 +320,11 @@ impl ToScalar for usize { /// Code hash related /// the empty keccak code hash -pub static KECCAK_CODE_HASH_EMPTY: Lazy = Lazy::new(|| { +pub static KECCAK_CODE_HASH_EMPTY: LazyLock = LazyLock::new(|| { Hash::from_str("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap() }); /// the empty poseidon code hash -pub static POSEIDON_CODE_HASH_EMPTY: Lazy = Lazy::new(|| { +pub static POSEIDON_CODE_HASH_EMPTY: LazyLock = LazyLock::new(|| { Hash::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864").unwrap() }); /// Struct used to define the storage proof @@ -351,14 +376,17 @@ struct GethExecStepInternal { #[serde(rename = "gasCost")] gas_cost: GasCost, depth: u16, - error: Option, + error: Option, // stack is in hex 0x prefixed + #[cfg(feature = "enable-stack")] #[serde(default)] stack: Vec, // memory is in chunks of 32 bytes, in hex + #[cfg(feature = "enable-memory")] #[serde(default)] memory: Vec, // storage is hex -> hex + #[cfg(feature = "enable-storage")] #[serde(default)] storage: HashMap, } @@ -374,15 +402,190 @@ pub struct GethExecStep { pub gas_cost: GasCost, pub refund: Gas, pub depth: u16, - pub error: Option, + pub error: Option, // stack is in hex 0x prefixed + #[cfg(feature = "enable-stack")] pub stack: Stack, // memory is in chunks of 32 bytes, in hex + #[cfg(feature = "enable-memory")] pub memory: Memory, // storage is hex -> hex + #[cfg(feature = "enable-storage")] pub storage: Storage, } +/// Errors of StructLogger Result from Geth +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum GethExecError { + /// out of gas + OutOfGas, + /// contract creation code storage out of gas + CodeStoreOutOfGas, + /// max call depth exceeded + Depth, + /// insufficient balance for transfer + InsufficientBalance, + /// contract address collision + ContractAddressCollision, + /// execution reverted + ExecutionReverted, + /// max initcode size exceeded + MaxInitCodeSizeExceeded, + /// max code size exceeded + MaxCodeSizeExceeded, + /// invalid jump destination + InvalidJump, + /// write protection + WriteProtection, + /// return data out of bounds + ReturnDataOutOfBounds, + /// gas uint64 overflow + GasUintOverflow, + /// invalid code: must not begin with 0xef + InvalidCode, + /// nonce uint64 overflow + NonceUintOverflow, + /// stack underflow + StackUnderflow { + /// stack length + stack_len: u64, + /// required length + required: u64, + }, + /// stack limit reached + StackOverflow { + /// stack length + stack_len: u64, + /// stack limit + limit: u64, + }, + /// invalid opcode + InvalidOpcode(OpcodeId), +} + +impl GethExecError { + /// Returns the error as a string constant. + pub fn error(self) -> &'static str { + match self { + GethExecError::OutOfGas => "out of gas", + GethExecError::CodeStoreOutOfGas => "contract creation code storage out of gas", + GethExecError::Depth => "max call depth exceeded", + GethExecError::InsufficientBalance => "insufficient balance for transfer", + GethExecError::ContractAddressCollision => "contract address collision", + GethExecError::ExecutionReverted => "execution reverted", + GethExecError::MaxInitCodeSizeExceeded => "max initcode size exceeded", + GethExecError::MaxCodeSizeExceeded => "max code size exceeded", + GethExecError::InvalidJump => "invalid jump destination", + GethExecError::WriteProtection => "write protection", + GethExecError::ReturnDataOutOfBounds => "return data out of bounds", + GethExecError::GasUintOverflow => "gas uint64 overflow", + GethExecError::InvalidCode => "invalid code: must not begin with 0xef", + GethExecError::NonceUintOverflow => "nonce uint64 overflow", + GethExecError::StackUnderflow { .. } => "stack underflow", + GethExecError::StackOverflow { .. } => "stack limit reached", + GethExecError::InvalidOpcode(_) => "invalid opcode", + } + } +} + +impl Display for GethExecError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + GethExecError::StackUnderflow { + stack_len, + required, + } => { + write!(f, "stack underflow ({stack_len} <=> {required})") + } + GethExecError::StackOverflow { stack_len, limit } => { + write!(f, "stack limit reached {stack_len} ({limit})") + } + GethExecError::InvalidOpcode(op) => { + write!(f, "invalid opcode: {op}") + } + _ => f.write_str(self.error()), + } + } +} + +impl Serialize for GethExecError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + // We serialize the error as a string constant. + serializer.serialize_str(self.error()) + } +} + +impl<'de> Deserialize<'de> for GethExecError { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct GethExecErrorVisitor; + + static STACK_UNDERFLOW_RE: LazyLock = + LazyLock::new(|| regex::Regex::new(r"^stack underflow \((\d+) <=> (\d+)\)$").unwrap()); + static STACK_OVERFLOW_RE: LazyLock = + LazyLock::new(|| regex::Regex::new(r"^stack limit reached (\d+) \((\d+)\)$").unwrap()); + + impl<'de> de::Visitor<'de> for GethExecErrorVisitor { + type Value = GethExecError; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "a geth struct logger error string constant") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + let e = match v { + "out of gas" => GethExecError::OutOfGas, + "contract creation code storage out of gas" => GethExecError::CodeStoreOutOfGas, + "max call depth exceeded" => GethExecError::Depth, + "insufficient balance for transfer" => GethExecError::InsufficientBalance, + "contract address collision" => GethExecError::ContractAddressCollision, + "execution reverted" => GethExecError::ExecutionReverted, + "max initcode size exceeded" => GethExecError::MaxInitCodeSizeExceeded, + "max code size exceeded" => GethExecError::MaxCodeSizeExceeded, + "invalid jump destination" => GethExecError::InvalidJump, + "write protection" => GethExecError::WriteProtection, + "return data out of bounds" => GethExecError::ReturnDataOutOfBounds, + "gas uint64 overflow" => GethExecError::GasUintOverflow, + "invalid code: must not begin with 0xef" => GethExecError::InvalidCode, + "nonce uint64 overflow" => GethExecError::NonceUintOverflow, + _ if v.starts_with("stack underflow") => { + let caps = STACK_UNDERFLOW_RE.captures(v).unwrap(); + let stack_len = caps.get(1).unwrap().as_str().parse::().unwrap(); + let required = caps.get(2).unwrap().as_str().parse::().unwrap(); + GethExecError::StackUnderflow { + stack_len, + required, + } + } + _ if v.starts_with("stack limit reached") => { + let caps = STACK_OVERFLOW_RE.captures(v).unwrap(); + let stack_len = caps.get(1).unwrap().as_str().parse::().unwrap(); + let limit = caps.get(2).unwrap().as_str().parse::().unwrap(); + GethExecError::StackOverflow { stack_len, limit } + } + _ if v.starts_with("invalid opcode") => v + .strip_prefix("invalid opcode: ") + .map(|s| OpcodeId::from_str(s).unwrap()) + .map(GethExecError::InvalidOpcode) + .unwrap(), + _ => return Err(E::invalid_value(de::Unexpected::Str(v), &self)), + }; + Ok(e) + } + } + + deserializer.deserialize_str(GethExecErrorVisitor) + } +} + // Wrapper over u8 that provides formats the byte in hex for [`fmt::Debug`]. pub(crate) struct DebugByte(pub(crate) u8); @@ -404,18 +607,21 @@ impl<'a> fmt::Debug for DebugWord<'a> { impl fmt::Debug for GethExecStep { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Step") - .field("pc", &format_args!("0x{:04x}", self.pc.0)) + let mut f = f.debug_struct("Step"); + f.field("pc", &format_args!("0x{:04x}", self.pc.0)) .field("op", &self.op) .field("gas", &format_args!("{}", self.gas.0)) .field("gas_cost", &format_args!("{}", self.gas_cost.0)) .field("refund", &format_args!("{}", self.refund.0)) .field("depth", &self.depth) - .field("error", &self.error) - .field("stack", &self.stack) - // .field("memory", &self.memory) - .field("storage", &self.storage) - .finish() + .field("error", &self.error); + #[cfg(feature = "enable-stack")] + f.field("stack", &self.stack); + #[cfg(feature = "enable-memory")] + f.field("memory", &self.memory); + #[cfg(feature = "enable-storage")] + f.field("storage", &self.storage); + f.finish() } } @@ -433,13 +639,16 @@ impl<'de> Deserialize<'de> for GethExecStep { gas_cost: s.gas_cost, depth: s.depth, error: s.error, + #[cfg(feature = "enable-stack")] stack: Stack(s.stack.iter().map(|dw| dw.to_word()).collect::>()), + #[cfg(feature = "enable-memory")] memory: Memory::from( s.memory .iter() .map(|dw| dw.to_word()) .collect::>(), ), + #[cfg(feature = "enable-storage")] storage: Storage( s.storage .iter() @@ -486,12 +695,23 @@ pub struct GethExecTrace { /// Vector of geth execution steps of the trace. #[serde(rename = "structLogs")] pub struct_logs: Vec, - #[serde(rename = "accountAfter", default)] + #[serde( + rename = "accountAfter", + default, + deserialize_with = "parse_account_after" + )] /// List of accounts' (coinbase etc) status AFTER execution /// Only viable for scroll mode pub account_after: Vec, } +fn parse_account_after<'de, D>(d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + Deserialize::deserialize(d).map(|x: Option<_>| x.unwrap_or_default()) +} + #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] #[doc(hidden)] pub struct ResultGethPrestateTraces(pub Vec); @@ -499,7 +719,8 @@ pub struct ResultGethPrestateTraces(pub Vec); #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] #[doc(hidden)] pub struct ResultGethPrestateTrace { - pub txHash: H256, + #[serde(rename = "txHash", default)] + pub tx_hash: H256, pub result: HashMap, } @@ -553,7 +774,10 @@ macro_rules! word_map { #[cfg(test)] mod tests { use super::*; - use crate::evm_types::{memory::Memory, opcode_ids::OpcodeId, stack::Stack}; + use crate::evm_types::{opcode_ids::OpcodeId, stack::Stack}; + + #[cfg(feature = "enable-memory")] + use crate::evm_types::memory::Memory; #[test] fn test_to_u16_array() { @@ -649,8 +873,11 @@ mod tests { gas_cost: GasCost(3), depth: 1, error: None, + #[cfg(feature = "enable-stack")] stack: Stack::new(), + #[cfg(feature = "enable-storage")] storage: Storage(word_map!()), + #[cfg(feature = "enable-memory")] memory: Memory::new(), }, GethExecStep { @@ -661,8 +888,11 @@ mod tests { gas_cost: GasCost(2100), depth: 1, error: None, + #[cfg(feature = "enable-stack")] stack: Stack(vec![word!("0x1003e2d2"), word!("0x2a"), word!("0x0")]), + #[cfg(feature = "enable-storage")] storage: Storage(word_map!("0x0" => "0x6f")), + #[cfg(feature = "enable-memory")] memory: Memory::from(vec![word!("0x0"), word!("0x0"), word!("0x080")]), }, GethExecStep { @@ -673,12 +903,15 @@ mod tests { gas_cost: GasCost(42), depth: 1, error: None, + #[cfg(feature = "enable-stack")] stack: Stack(vec![ word!("0x3635c9adc5dea00000"), word!("0x40"), word!("0x0") ]), + #[cfg(feature = "enable-storage")] storage: Storage(word_map!()), + #[cfg(feature = "enable-memory")] memory: Memory::from(vec![ word!( "000000000000000000000000b8f67472dcc25589672a61905f7fd63f09e5d470" diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index 9c9f3aba74..196adbab2e 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -15,8 +15,9 @@ use ethers_core::{ utils::keccak256, }; use halo2_proofs::{ - arithmetic::{CurveAffine, FieldExt}, + arithmetic::CurveAffine, halo2curves::{ + ff::FromUniformBytes, group::{ ff::{Field as GroupField, PrimeField}, prime::PrimeCurveAffine, @@ -26,9 +27,9 @@ use halo2_proofs::{ Coordinates, }, }; -use lazy_static::lazy_static; use num_bigint::BigUint; use sha3::digest::generic_array::GenericArray; +use std::sync::LazyLock; use subtle::CtOption; /// Do a secp256k1 signature with a given randomness value. @@ -45,7 +46,7 @@ pub fn sign(randomness: Fq, sk: Fq, msg_hash: Fq) -> (Fq, Fq, u8) { let mut x_bytes = [0u8; 64]; x_bytes[..32].copy_from_slice(&x.to_bytes()); - let sig_r = Fq::from_bytes_wide(&x_bytes); // get x cordinate (E::Base) on E::Scalar + let sig_r = Fq::from_uniform_bytes(&x_bytes); // get x cordinate (E::Base) on E::Scalar let sig_s = randomness_inv * (msg_hash + sig_r * sk); (sig_r, sig_s, u8::from(sig_v)) @@ -112,27 +113,27 @@ impl SignData { } } -lazy_static! { - /// This is the sign data of default padding tx - static ref SIGN_DATA_DEFAULT: SignData = { - let (tx_req, sig) = get_dummy_tx(); - let tx = Transaction { - tx_type: TxType::PreEip155, - rlp_unsigned_bytes: tx_req.rlp_unsigned().to_vec(), - rlp_bytes: tx_req.rlp_signed(&sig).to_vec(), - v: sig.v, - r: sig.r, - s: sig.s, - // other fields are irrelevant to get the sign_data() - ..Default::default() - }; - - let sign_data = tx.sign_data().unwrap(); - assert_eq!(sign_data.get_addr(), address!("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf")); - - sign_data +static SIGN_DATA_DEFAULT: LazyLock = LazyLock::new(|| { + let (tx_req, sig) = get_dummy_tx(); + let tx = Transaction { + tx_type: TxType::PreEip155, + rlp_unsigned_bytes: tx_req.rlp_unsigned().to_vec(), + rlp_bytes: tx_req.rlp_signed(&sig).to_vec(), + v: sig.v, + r: sig.r, + s: sig.s, + // other fields are irrelevant to get the sign_data() + ..Default::default() }; -} + + let sign_data = tx.sign_data().unwrap(); + assert_eq!( + sign_data.get_addr(), + address!("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf") + ); + + sign_data +}); impl Default for SignData { // Hardcoded valid signature corresponding to a hardcoded private key and @@ -190,13 +191,10 @@ pub fn recover_pk2( ct_option_ok_or(Secp256k1Affine::from_xy(x, y), Error::Signature) } -lazy_static! { - /// Secp256k1 Curve Scalar. Referece: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended - /// Elliptic Curve Domain Parameters" document at http://www.secg.org/sec2-v2.pdf - pub static ref SECP256K1_Q: BigUint = { - BigUint::from_bytes_le(&(Fq::zero() - Fq::one()).to_repr()) + 1u64 - }; -} +/// Secp256k1 Curve Scalar. Referece: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended +/// Elliptic Curve Domain Parameters" document at http://www.secg.org/sec2-v2.pdf +pub static SECP256K1_Q: LazyLock = + LazyLock::new(|| BigUint::from_bytes_le(&(Fq::zero() - Fq::one()).to_repr()) + 1u64); /// Helper function to convert a `CtOption` into an `Result`. Similar to /// `Option::ok_or`. diff --git a/external-tracer/Cargo.toml b/external-tracer/Cargo.toml index bf9f15a3d3..e2ccd77bba 100644 --- a/external-tracer/Cargo.toml +++ b/external-tracer/Cargo.toml @@ -14,3 +14,6 @@ log.workspace = true [features] default = [] scroll = ["eth-types/scroll", "geth-utils/scroll"] +enable-stack = [] +enable-memory = [] +enable-storage = [] diff --git a/external-tracer/src/lib.rs b/external-tracer/src/lib.rs index a90b099db7..a397580248 100644 --- a/external-tracer/src/lib.rs +++ b/external-tracer/src/lib.rs @@ -49,15 +49,16 @@ pub struct LoggerConfig { impl Default for LoggerConfig { fn default() -> Self { Self { - enable_memory: false, - disable_stack: false, - disable_storage: false, + enable_memory: cfg!(feature = "enable-memory"), + disable_stack: !cfg!(feature = "enable-stack"), + disable_storage: !cfg!(feature = "enable-storage"), enable_return_data: true, } } } impl LoggerConfig { + #[cfg(feature = "enable-memory")] pub fn enable_memory() -> Self { Self { enable_memory: true, diff --git a/gadgets/src/batched_is_zero.rs b/gadgets/src/batched_is_zero.rs index 02869d68ee..f3a0dd58c7 100644 --- a/gadgets/src/batched_is_zero.rs +++ b/gadgets/src/batched_is_zero.rs @@ -7,7 +7,6 @@ use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Phase, VirtualCells}, poly::Rotation, @@ -48,7 +47,7 @@ pub struct BatchedIsZeroChip { _marker: PhantomData, } -impl BatchedIsZeroChip { +impl BatchedIsZeroChip { /// Configure the BatchedIsZeroChip pub fn configure( meta: &mut ConstraintSystem, @@ -100,9 +99,9 @@ impl BatchedIsZeroChip { .iter() .find_map(|value| Option::::from(value.invert())) { - (F::zero(), inverse) + (F::ZERO, inverse) } else { - (F::one(), F::zero()) + (F::ONE, F::ZERO) } }); @@ -134,7 +133,6 @@ impl BatchedIsZeroChip { mod test { use super::*; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr, @@ -151,15 +149,17 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option<[u64; N]>, expect_is_zero: Option, _marker: PhantomData, } - impl Circuit for TestCircuit { + impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -218,7 +218,7 @@ mod test { || "expect_is_zero", config.expect_is_zero, 0, - || Value::known(F::from(*expect_is_zero)), + || Value::known(F::from(*expect_is_zero as u64)), )?; for (value_column, value) in config.values.iter().zip(values.iter()) { region.assign_advice( diff --git a/gadgets/src/binary_number.rs b/gadgets/src/binary_number.rs index cd3e0578c1..62fbec2043 100644 --- a/gadgets/src/binary_number.rs +++ b/gadgets/src/binary_number.rs @@ -61,10 +61,9 @@ where /// Return the constant that represents a given value. To be compared with the value expression. pub fn constant_expr(&self, value: T) -> Expression { - let f = value - .as_bits() - .iter() - .fold(F::zero(), |result, bit| F::from(*bit) + result * F::from(2)); + let f = value.as_bits().iter().fold(F::ZERO, |result, bit| { + F::from(*bit as u64) + result * F::from(2) + }); Expression::Constant(f) } @@ -199,7 +198,7 @@ where || format!("binary number {column:?}"), column, offset, - || Value::known(F::from(bit)), + || Value::known(F::from(bit as u64)), )?; } Ok(()) diff --git a/gadgets/src/bus/batch_assigner.rs b/gadgets/src/bus/batch_assigner.rs index d8e02c1d73..f4e873587e 100644 --- a/gadgets/src/bus/batch_assigner.rs +++ b/gadgets/src/bus/batch_assigner.rs @@ -48,8 +48,7 @@ impl> BatchAssigner { self.assigners.invert().map(|commands| { for (helper, command) in commands { let (offset, term) = command.assign(region, helper); - bus_assigner.add_term(offset, Value::known(term)); - // TODO: Ensure this is a global offset (need Halo2 support). + bus_assigner.add_term(region.global_offset(offset), Value::known(term)); } }); } @@ -119,4 +118,11 @@ impl> BusOpCounter { pub fn is_complete(&self) -> bool { self.counts.is_empty() } + + /// Merge another instance of BusOpCounter into self. The counts are accumulated. + pub fn merge(&mut self, other: Self) { + for (key, other_count) in other.counts { + *self.counts.entry(key).or_insert(0) += other_count; + } + } } diff --git a/gadgets/src/bus/bus_builder.rs b/gadgets/src/bus/bus_builder.rs index a55801c10a..4dfce86daf 100644 --- a/gadgets/src/bus/bus_builder.rs +++ b/gadgets/src/bus/bus_builder.rs @@ -54,12 +54,17 @@ impl> BusAssigner { Self { batch_assigner: BatchAssigner::new(), codec, - term_adder: TermAdder::new(n_rows), + term_adder: TermAdder::new(0, n_rows), bus_op_counter: BusOpCounter::new(), } } - /// Return the number of rows where the bus must be enabled. + /// Return the first offset supported by this BusAssigner. + pub fn start_offset(&self) -> usize { + self.term_adder.start_offset + } + + /// Return the number of rows supported by this BusAssigner. pub fn n_rows(&self) -> usize { self.term_adder.terms.len() } @@ -86,6 +91,11 @@ impl> BusAssigner { old_batch_assigner.finish(region, self); } + fn assert_finished(&self) { + assert_eq!(self.batch_assigner.len(), 0, "finish_ports was not called"); + // TODO: better error handling. + } + /// Add a term value to the bus. pub fn add_term(&mut self, offset: usize, term: Value) { self.term_adder.add_term(offset, term); @@ -93,22 +103,44 @@ impl> BusAssigner { /// Return the collected terms. pub fn terms(&self) -> Value<&[F]> { - assert_eq!(self.batch_assigner.len(), 0, "finish_ports was not called"); - // TODO: better error handling. - + self.assert_finished(); + assert_eq!( + self.start_offset(), + 0, + "cannot use the terms of a BusAssigner fork" + ); self.term_adder.terms() } + + /// Fork this BusAssigner for parallel assignment. + pub fn fork(&self, start_offset: usize, n_rows: usize) -> Self { + Self { + batch_assigner: BatchAssigner::new(), + codec: self.codec.clone(), + term_adder: TermAdder::new(start_offset, n_rows), + bus_op_counter: BusOpCounter::new(), + } + } + + /// Merge a fork of this BusAssigner back into it. + pub fn merge(&mut self, fork: Self) { + fork.assert_finished(); + self.term_adder.merge(fork.term_adder); + self.bus_op_counter.merge(fork.bus_op_counter); + } } struct TermAdder { + start_offset: usize, terms: Vec, unknown: bool, } impl TermAdder { /// Create a term adder with a maximum number of rows. - fn new(n_rows: usize) -> Self { + fn new(start_offset: usize, n_rows: usize) -> Self { Self { + start_offset, terms: vec![F::zero(); n_rows], unknown: false, } @@ -116,10 +148,10 @@ impl TermAdder { /// Add a term value to the bus. fn add_term(&mut self, offset: usize, term: Value) { + let range = self.start_offset..self.start_offset + self.terms.len(); assert!( - offset < self.terms.len(), - "offset={offset} out of bounds n_rows={}", - self.terms.len() + range.contains(&offset), + "offset={offset} out of bounds ({range:?})" ); if self.unknown { return; @@ -128,7 +160,7 @@ impl TermAdder { self.unknown = true; self.terms.clear(); } else { - term.map(|t| self.terms[offset] += t); + term.map(|t| self.terms[offset - self.start_offset] += t); } } @@ -140,4 +172,20 @@ impl TermAdder { Value::known(&self.terms) } } + + /// Merge another TermAdder::terms() into self. + fn merge(&mut self, other: Self) { + if other.unknown { + self.unknown = true; + self.terms.clear(); + } else { + assert!(other.start_offset >= self.start_offset); + assert!(other.start_offset + other.terms.len() <= self.start_offset + self.terms.len()); + let start_index = other.start_offset - self.start_offset; + + for (index, term) in other.terms.into_iter().enumerate() { + self.terms[start_index + index] += term; + } + } + } } diff --git a/gadgets/src/comparator.rs b/gadgets/src/comparator.rs index f337e165c5..ce9f395604 100644 --- a/gadgets/src/comparator.rs +++ b/gadgets/src/comparator.rs @@ -3,7 +3,6 @@ use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Region, Value}, plonk::{ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, poly::Rotation, @@ -15,7 +14,7 @@ use crate::{ }; /// Instruction that the Comparator chip needs to implement. -pub trait ComparatorInstruction { +pub trait ComparatorInstruction { /// Assign the lhs and rhs witnesses to the Comparator chip's region. fn assign( &self, diff --git a/gadgets/src/evm_word.rs b/gadgets/src/evm_word.rs index 0447647c18..d88c15e10c 100644 --- a/gadgets/src/evm_word.rs +++ b/gadgets/src/evm_word.rs @@ -27,12 +27,12 @@ pub fn r() -> F { } let mut r = [0; 64]; r[..32].copy_from_slice(hasher.finalize().as_slice()); - F::from_bytes_wide(&r) + F::from_uniform_bytes(&r) } /// Returns encoding of big-endian representation of a 256-bit word. pub fn encode(vals: impl Iterator, r: F) -> F { - vals.fold(F::zero(), |acc, val| { + vals.fold(F::ZERO, |acc, val| { let byte = F::from(val as u64); acc * r + byte }) @@ -72,7 +72,7 @@ impl WordConfig { byte_lookup: Column, ) -> Self { // Expression representing `encode(word)`. - let mut encode_word_expr = Expression::Constant(F::zero()); + let mut encode_word_expr = Expression::Constant(F::ZERO); // Lookup each byte in the witnessed word representation to // range-constrain it to 8 bits. @@ -177,6 +177,8 @@ mod tests { // commitment which will be provided as public inputs. type Config = (WordConfig, Column); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/is_equal.rs b/gadgets/src/is_equal.rs index 35b3ff5ff6..78a927104c 100644 --- a/gadgets/src/is_equal.rs +++ b/gadgets/src/is_equal.rs @@ -2,7 +2,6 @@ use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Region, Value}, plonk::{ConstraintSystem, Error, Expression, VirtualCells}, poly::Rotation, @@ -13,7 +12,7 @@ use crate::util::Expr; use super::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; /// Instruction that the IsEqual chip needs to implement. -pub trait IsEqualInstruction { +pub trait IsEqualInstruction { /// Assign lhs and rhs witnesses to the IsEqual chip's region. fn assign( &self, @@ -121,7 +120,6 @@ mod tests { use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr as Fp, @@ -142,7 +140,7 @@ mod tests { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Vec, checks: Vec, _marker: PhantomData, @@ -151,6 +149,8 @@ mod tests { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/is_zero.rs b/gadgets/src/is_zero.rs index c245081e49..b69bb23f13 100644 --- a/gadgets/src/is_zero.rs +++ b/gadgets/src/is_zero.rs @@ -222,6 +222,8 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -349,6 +351,8 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/less_than.rs b/gadgets/src/less_than.rs index c2613a1fc6..ebde33a37b 100644 --- a/gadgets/src/less_than.rs +++ b/gadgets/src/less_than.rs @@ -2,7 +2,6 @@ use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, poly::Rotation, @@ -14,7 +13,7 @@ use crate::{ }; /// Instruction that the Lt chip needs to implement. -pub trait LtInstruction { +pub trait LtInstruction { /// Assign the lhs and rhs witnesses to the Lt chip's region. fn assign( &self, @@ -136,7 +135,7 @@ impl LtInstruction for LtChip { || Value::known(F::from(lt as u64)), )?; - let diff = (lhs - rhs) + (if lt { config.range } else { F::zero() }); + let diff = (lhs - rhs) + (if lt { config.range } else { F::ZERO }); let diff_bytes = diff.to_repr(); for (idx, diff_column) in config.diff.iter().enumerate() { region.assign_advice( @@ -192,7 +191,6 @@ mod test { use super::{LtChip, LtConfig, LtInstruction}; use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, halo2curves::bn256::Fr as Fp, @@ -246,7 +244,7 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option>, // checks[i] = lt(values[i + 1], values[i]) checks: Option>, @@ -256,6 +254,8 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -368,7 +368,7 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option>, // checks[i] = lt(values[i].0 - values[i].1) checks: Option>, @@ -378,6 +378,8 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/monotone.rs b/gadgets/src/monotone.rs index 798beeb995..bd84cf0b3a 100644 --- a/gadgets/src/monotone.rs +++ b/gadgets/src/monotone.rs @@ -2,8 +2,8 @@ //! Monotone gadget helps to check if an advice column is monotonically //! increasing within a range. With strict enabled, it disallows equality of two //! cell. +use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Layouter, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, @@ -25,7 +25,7 @@ pub(crate) struct MonotoneChip +impl MonotoneChip { /// configure which column should be check. q_enable here as a fn is @@ -90,7 +90,7 @@ impl } } -impl Chip +impl Chip for MonotoneChip { type Config = MonotoneConfig; @@ -108,8 +108,8 @@ impl Chip #[cfg(test)] mod test { use super::{MonotoneChip, MonotoneConfig, Value}; + use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, SimpleFloorPlanner}, dev::{ FailureLocation, MockProver, @@ -128,16 +128,18 @@ mod test { } #[derive(Default)] - struct TestCircuit { + struct TestCircuit { values: Option>, _marker: PhantomData, } - impl Circuit + impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/mul_add.rs b/gadgets/src/mul_add.rs index 401969d9d6..e10fb58183 100644 --- a/gadgets/src/mul_add.rs +++ b/gadgets/src/mul_add.rs @@ -416,7 +416,7 @@ impl MulAddChip { || "unused padding row", col, offset + 7, - || Value::known(F::zero()), + || Value::known(F::ZERO), )?; } @@ -501,6 +501,8 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn configure(meta: &mut halo2_proofs::plonk::ConstraintSystem) -> Self::Config { let q_enable = meta.complex_selector(); diff --git a/gadgets/src/range.rs b/gadgets/src/range.rs index 0e673e736c..81cdebaba4 100644 --- a/gadgets/src/range.rs +++ b/gadgets/src/range.rs @@ -9,14 +9,13 @@ use crate::util::Expr; use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Chip, Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, TableColumn, VirtualCells}, poly::Rotation, }; /// Instruction that the Range chip needs to implement. -pub trait UIntRangeCheckInstruction { +pub trait UIntRangeCheckInstruction { /// Assign the expr and u16 le repr witnesses to the Comparator chip's region. fn assign( &self, diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index 6d3cffd7ed..0d0eaf1cb8 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -1,10 +1,9 @@ //! Utility traits, functions used in the crate. use eth_types::{ evm_types::{GasCost, OpcodeId}, - U256, + Field, U256, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, Region}, plonk::{ConstraintSystem, Error, Expression, VirtualCells}, }; @@ -12,20 +11,21 @@ use halo2_proofs::{ /// Returns the sum of the passed in cells pub mod sum { use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns an expression for the sum of the list of expressions. - pub fn expr, I: IntoIterator>(inputs: I) -> Expression { + pub fn expr, I: IntoIterator>(inputs: I) -> Expression { inputs .into_iter() .fold(0.expr(), |acc, input| acc + input.expr()) } /// Returns the sum of the given list of values within the field. - pub fn value(values: &[u8]) -> F { + pub fn value(values: &[u8]) -> F { values .iter() - .fold(F::zero(), |acc, value| acc + F::from(*value as u64)) + .fold(F::ZERO, |acc, value| acc + F::from(*value as u64)) } } @@ -33,19 +33,20 @@ pub mod sum { /// otherwise. Inputs need to be boolean pub mod and { use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns an expression that evaluates to 1 only if all the expressions in /// the given list are 1, else returns 0. - pub fn expr, I: IntoIterator>(inputs: I) -> Expression { + pub fn expr, I: IntoIterator>(inputs: I) -> Expression { inputs .into_iter() .fold(1.expr(), |acc, input| acc * input.expr()) } /// Returns the product of all given values. - pub fn value(inputs: Vec) -> F { - inputs.iter().fold(F::one(), |acc, input| acc * input) + pub fn value(inputs: Vec) -> F { + inputs.iter().fold(F::ONE, |acc, input| acc * input) } } @@ -54,16 +55,17 @@ pub mod and { pub mod or { use super::{and, not}; use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns an expression that evaluates to 1 if any expression in the given /// list is 1. Returns 0 if all the expressions were 0. - pub fn expr, I: IntoIterator>(inputs: I) -> Expression { + pub fn expr, I: IntoIterator>(inputs: I) -> Expression { not::expr(and::expr(inputs.into_iter().map(not::expr))) } /// Returns the value after passing all given values through the OR gate. - pub fn value(inputs: Vec) -> F { + pub fn value(inputs: Vec) -> F { not::value(and::value(inputs.into_iter().map(not::value).collect())) } } @@ -72,16 +74,17 @@ pub mod or { /// `b` needs to be boolean pub mod not { use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns an expression that represents the NOT of the given expression. - pub fn expr>(b: E) -> Expression { + pub fn expr>(b: E) -> Expression { 1.expr() - b.expr() } /// Returns a value that represents the NOT of the given value. - pub fn value(b: F) -> F { - F::one() - b + pub fn value(b: F) -> F { + F::ONE - b } } @@ -89,15 +92,16 @@ pub mod not { /// `a` and `b` needs to be boolean pub mod xor { use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns an expression that represents the XOR of the given expression. - pub fn expr>(a: E, b: E) -> Expression { + pub fn expr>(a: E, b: E) -> Expression { a.expr() + b.expr() - 2.expr() * a.expr() * b.expr() } /// Returns a value that represents the XOR of the given value. - pub fn value(a: F, b: F) -> F { + pub fn value(a: F, b: F) -> F { a + b - F::from(2u64) * a * b } } @@ -106,11 +110,12 @@ pub mod xor { /// `selector == 0`. `selector` needs to be boolean. pub mod select { use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; /// Returns the `when_true` expression when the selector is true, else /// returns the `when_false` expression. - pub fn expr( + pub fn expr( selector: Expression, when_true: Expression, when_false: Expression, @@ -120,18 +125,18 @@ pub mod select { /// Returns the `when_true` value when the selector is true, else returns /// the `when_false` value. - pub fn value(selector: F, when_true: F, when_false: F) -> F { - selector * when_true + (F::one() - selector) * when_false + pub fn value(selector: F, when_true: F, when_false: F) -> F { + selector * when_true + (F::ONE - selector) * when_false } /// Returns the `when_true` word when selector is true, else returns the /// `when_false` word. - pub fn value_word( + pub fn value_word( selector: F, when_true: [u8; 32], when_false: [u8; 32], ) -> [u8; 32] { - if selector == F::one() { + if selector == F::ONE { when_true } else { when_false @@ -141,7 +146,7 @@ pub mod select { /// Trait that implements functionality to get a constant expression from /// commonly used types. -pub trait Expr { +pub trait Expr { /// Returns an expression for the type. fn expr(&self) -> Expression; } @@ -150,7 +155,7 @@ pub trait Expr { #[macro_export] macro_rules! impl_expr { ($type:ty) => { - impl $crate::util::Expr for $type { + impl $crate::util::Expr for $type { #[inline] fn expr(&self) -> Expression { Expression::Constant(F::from(*self as u64)) @@ -158,7 +163,7 @@ macro_rules! impl_expr { } }; ($type:ty, $method:path) => { - impl $crate::util::Expr for $type { + impl $crate::util::Expr for $type { #[inline] fn expr(&self) -> Expression { Expression::Constant(F::from($method(self) as u64)) @@ -174,39 +179,34 @@ impl_expr!(usize); impl_expr!(OpcodeId, OpcodeId::as_u8); impl_expr!(GasCost, GasCost::as_u64); -impl Expr for Expression { +impl Expr for Expression { #[inline] fn expr(&self) -> Expression { self.clone() } } -impl Expr for &Expression { +impl Expr for &Expression { #[inline] fn expr(&self) -> Expression { (*self).clone() } } -impl Expr for i32 { +impl Expr for i32 { #[inline] fn expr(&self) -> Expression { Expression::Constant( - F::from(self.unsigned_abs() as u64) - * if self.is_negative() { - -F::one() - } else { - F::one() - }, + F::from(self.unsigned_abs() as u64) * if self.is_negative() { -F::ONE } else { F::ONE }, ) } } /// Given a bytes-representation of an expression, it computes and returns the /// single expression. -pub fn expr_from_bytes>(bytes: &[E]) -> Expression { +pub fn expr_from_bytes>(bytes: &[E]) -> Expression { let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for byte in bytes.iter() { value = value + byte.expr() * multiplier; multiplier *= F::from(256); @@ -216,9 +216,9 @@ pub fn expr_from_bytes>(bytes: &[E]) -> Expression { /// Given a u16-array-representation of an expression, it computes and returns /// the single expression. -pub fn expr_from_u16>(u16s: &[E]) -> Expression { +pub fn expr_from_u16>(u16s: &[E]) -> Expression { let mut value = 0.expr(); - let mut multiplier = F::one(); + let mut multiplier = F::ONE; for u16 in u16s.iter() { value = value + u16.expr() * multiplier; multiplier *= F::from(2u64.pow(16)); @@ -227,7 +227,7 @@ pub fn expr_from_u16>(u16s: &[E]) -> Expression { } /// Query an expression from the constraint system. -pub fn query_expression( +pub fn query_expression( meta: &mut ConstraintSystem, mut f: impl FnMut(&mut VirtualCells) -> T, ) -> T { @@ -247,7 +247,7 @@ pub fn assign_global( mut assignment: A, ) -> Result where - F: FieldExt, + F: Field, AR: Default, A: FnMut(Region<'_, F>) -> Result, N: Fn() -> NR, @@ -265,9 +265,9 @@ where ret } -/// Returns 2**by as FieldExt -pub fn pow_of_two(by: usize) -> F { - F::from(2).pow(&[by as u64, 0, 0, 0]) +/// Returns 2**by as Field +pub fn pow_of_two(by: usize) -> F { + F::from(2).pow([by as u64, 0, 0, 0]) } /// Returns tuple consists of low and high part of U256 diff --git a/geth-utils/build.rs b/geth-utils/build.rs index 861eb3ad9d..369c449414 100644 --- a/geth-utils/build.rs +++ b/geth-utils/build.rs @@ -9,6 +9,9 @@ fn main() { // Build let mut build = gobuild::Build::new(); + if cfg!(target_os = "macos") { + build.ldflags("-w"); + } let target_files = if cfg!(feature = "scroll") { vec!["./l2geth/trace.go", "./l2geth/lib.go"] } else { diff --git a/geth-utils/l2geth/go.mod b/geth-utils/l2geth/go.mod index f261fd4b5e..940bc9bebf 100644 --- a/geth-utils/l2geth/go.mod +++ b/geth-utils/l2geth/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/imdario/mergo v0.3.16 - github.com/scroll-tech/go-ethereum v1.10.14-0.20230919024151-fa0be69a3fb9 + github.com/scroll-tech/go-ethereum v1.10.14-0.20231123003536-35313dc92055 ) require ( @@ -36,7 +36,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect ) diff --git a/geth-utils/l2geth/go.sum b/geth-utils/l2geth/go.sum index f1fc58f76f..db169fed7f 100644 --- a/geth-utils/l2geth/go.sum +++ b/geth-utils/l2geth/go.sum @@ -137,6 +137,8 @@ github.com/scroll-tech/go-ethereum v1.10.14-0.20230901060443-e1eebd17067c h1:GuA github.com/scroll-tech/go-ethereum v1.10.14-0.20230901060443-e1eebd17067c/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA= github.com/scroll-tech/go-ethereum v1.10.14-0.20230919024151-fa0be69a3fb9 h1:QiqH+ZGNNzMcKy21VGX6XYg81DXE+/9j1Ik7owm13hs= github.com/scroll-tech/go-ethereum v1.10.14-0.20230919024151-fa0be69a3fb9/go.mod h1:DiN3p2inoXOxGffxSswDKqWjQ7bU+Mp0c9v0XQXKmaA= +github.com/scroll-tech/go-ethereum v1.10.14-0.20231123003536-35313dc92055 h1:7dGW0GxAu5y+8SlXc4LtuoWXxbV5de3vDjN2qz2oO+k= +github.com/scroll-tech/go-ethereum v1.10.14-0.20231123003536-35313dc92055/go.mod h1:4HrFcoStbViFVy/9l/rvKl1XmizVAaPdgqI8v0U8hOc= github.com/scroll-tech/zktrie v0.6.0 h1:xLrMAO31Yo2BiPg1jtYKzcjpEFnXy8acbB7iIsyshPs= github.com/scroll-tech/zktrie v0.6.0/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= @@ -165,6 +167,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -175,6 +179,7 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -204,12 +209,15 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index fba1e98168..e674b36f5b 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -7,7 +7,6 @@ license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lazy_static.workspace = true ethers.workspace = true serde_json.workspace = true serde.workspace = true diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index 3773b8bf3a..ff799751fd 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -22,12 +22,15 @@ use halo2_proofs::{ Blake2bRead, Blake2bWrite, Challenge255, TranscriptReadBuffer, TranscriptWriterBuffer, }, }; -use lazy_static::lazy_static; use mock::TestContext; use rand_chacha::rand_core::SeedableRng; use rand_core::RngCore; use rand_xorshift::XorShiftRng; -use std::{collections::HashMap, marker::PhantomData, sync::Mutex}; +use std::{ + collections::HashMap, + marker::PhantomData, + sync::{LazyLock, Mutex}, +}; use tokio::sync::Mutex as TokioMutex; use zkevm_circuits::{ bytecode_circuit::TestBytecodeCircuit, @@ -106,52 +109,54 @@ const KECCAK_CIRCUIT_DEGREE: u32 = 16; const SUPER_CIRCUIT_DEGREE: u32 = 20; const EXP_CIRCUIT_DEGREE: u32 = 16; -lazy_static! { - /// Data generation. - static ref GEN_DATA: GenDataOutput = GenDataOutput::load(); - static ref RNG: XorShiftRng = XorShiftRng::from_seed([ +/// Data generation. +static GEN_DATA: LazyLock = LazyLock::new(GenDataOutput::load); +static RNG: LazyLock = LazyLock::new(|| { + XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, - ]); -} - -lazy_static! { - static ref GEN_PARAMS: Mutex>> = Mutex::new(HashMap::new()); -} - -lazy_static! { - /// Integration test for EVM circuit - pub static ref EVM_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("EVM", EVM_CIRCUIT_DEGREE)); - - /// Integration test for State circuit - pub static ref STATE_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("State", STATE_CIRCUIT_DEGREE)); - - /// Integration test for State circuit - pub static ref TX_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Tx", TX_CIRCUIT_DEGREE)); - - /// Integration test for Bytecode circuit - pub static ref BYTECODE_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Bytecode", BYTECODE_CIRCUIT_DEGREE)); - - /// Integration test for Copy circuit - pub static ref COPY_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Copy", COPY_CIRCUIT_DEGREE)); - - /// Integration test for Keccak circuit - pub static ref KECCAK_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE)); - - /// Integration test for Copy circuit - pub static ref SUPER_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Super", SUPER_CIRCUIT_DEGREE)); - - /// Integration test for Exp circuit - pub static ref EXP_CIRCUIT_TEST: TokioMutex>> = - TokioMutex::new(IntegrationTest::new("Exp", EXP_CIRCUIT_DEGREE)); -} + ]) +}); + +static GEN_PARAMS: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +/// Integration test for EVM circuit +pub static EVM_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("EVM", EVM_CIRCUIT_DEGREE))); + +/// Integration test for State circuit +pub static STATE_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("State", STATE_CIRCUIT_DEGREE))); + +/// Integration test for State circuit +pub static TX_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Tx", TX_CIRCUIT_DEGREE))); + +/// Integration test for Bytecode circuit +pub static BYTECODE_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Bytecode", BYTECODE_CIRCUIT_DEGREE))); + +/// Integration test for Copy circuit +pub static COPY_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Copy", COPY_CIRCUIT_DEGREE))); + +/// Integration test for Keccak circuit +pub static KECCAK_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE))); + +/// Integration test for Copy circuit +pub static SUPER_CIRCUIT_TEST: LazyLock< + TokioMutex< + IntegrationTest< + SuperCircuit, + >, + >, +> = LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Super", SUPER_CIRCUIT_DEGREE))); + +/// Integration test for Exp circuit +pub static EXP_CIRCUIT_TEST: LazyLock>>> = + LazyLock::new(|| TokioMutex::new(IntegrationTest::new("Exp", EXP_CIRCUIT_DEGREE))); /// Generic implementation for integration tests pub struct IntegrationTest + Circuit> { diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index e8b42df2fd..bcbd158762 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(lazy_cell)] //! Integration testing #![deny(rustdoc::broken_intra_doc_links)] @@ -12,13 +13,12 @@ use ethers::{ providers::{Http, Provider}, signers::{coins_bip39::English, MnemonicBuilder, Signer, Wallet}, }; -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, env::{self, VarError}, fs::File, - sync::Once, + sync::{LazyLock, Once}, time::Duration, }; use url::Url; @@ -40,40 +40,37 @@ pub const GENDATA_OUTPUT_PATH: &str = "gendata_output.json"; const GETH0_URL_DEFAULT: &str = "http://52.37.45.56:30303"; -lazy_static! { - /// URL of the integration test geth0 instance, which contains blocks for which proofs will be - /// generated. - pub static ref GETH0_URL: String = match env::var("GETH0_URL") { - Ok(val) => val, - Err(VarError::NotPresent) => GETH0_URL_DEFAULT.to_string(), - Err(e) => panic!("Error in GETH0_URL env var: {e:?}"), - }; - /// .. - pub static ref START_BLOCK: usize = match env::var("START_BLOCK") { - Ok(val) => str::parse::(&val).unwrap(), - Err(VarError::NotPresent) => 16140010, - Err(e) => panic!("Error in START_BLOCK env var: {e:?}"), - }; - /// .. - pub static ref END_BLOCK: usize = match env::var("END_BLOCK") { - Ok(val) => str::parse::(&val).unwrap(), - Err(VarError::NotPresent) => 16140010, - Err(e) => panic!("Error in END_BLOCK env var: {e:?}"), - }; - /// .. - pub static ref TX_ID: String = match env::var("TX_ID") { - Ok(val) => val, - Err(VarError::NotPresent) => "".to_string(), - Err(e) => panic!("Error in TX_ID env var: {e:?}"), - }; - /// .. - pub static ref CIRCUIT: String = match env::var("CIRCUIT") { - Ok(val) => val, - Err(VarError::NotPresent) => "super".to_string(), - Err(e) => panic!("Error in CIRCUIT env var: {e:?}"), - }; - -} +/// URL of the integration test geth0 instance, which contains blocks for which proofs will be +/// generated. +pub static GETH0_URL: LazyLock = LazyLock::new(|| match env::var("GETH0_URL") { + Ok(val) => val, + Err(VarError::NotPresent) => GETH0_URL_DEFAULT.to_string(), + Err(e) => panic!("Error in GETH0_URL env var: {e:?}"), +}); +/// .. +pub static START_BLOCK: LazyLock = LazyLock::new(|| match env::var("START_BLOCK") { + Ok(val) => str::parse::(&val).unwrap(), + Err(VarError::NotPresent) => 16140010, + Err(e) => panic!("Error in START_BLOCK env var: {e:?}"), +}); +/// .. +pub static END_BLOCK: LazyLock = LazyLock::new(|| match env::var("END_BLOCK") { + Ok(val) => str::parse::(&val).unwrap(), + Err(VarError::NotPresent) => 16140010, + Err(e) => panic!("Error in END_BLOCK env var: {e:?}"), +}); +/// .. +pub static TX_ID: LazyLock = LazyLock::new(|| match env::var("TX_ID") { + Ok(val) => val, + Err(VarError::NotPresent) => "".to_string(), + Err(e) => panic!("Error in TX_ID env var: {e:?}"), +}); +/// .. +pub static CIRCUIT: LazyLock = LazyLock::new(|| match env::var("CIRCUIT") { + Ok(val) => val, + Err(VarError::NotPresent) => "super".to_string(), + Err(e) => panic!("Error in CIRCUIT env var: {e:?}"), +}); static LOG_INIT: Once = Once::new(); diff --git a/integration-tests/tests/circuit_input_builder.rs b/integration-tests/tests/circuit_input_builder.rs index 18849b11bb..2ff8cca015 100644 --- a/integration-tests/tests/circuit_input_builder.rs +++ b/integration-tests/tests/circuit_input_builder.rs @@ -1,15 +1,14 @@ +#![feature(lazy_cell)] #![cfg(feature = "circuit_input_builder")] use bus_mapping::circuit_input_builder::{ build_state_code_db, get_state_accesses, BuilderClient, CircuitsParams, }; use integration_tests::{get_client, log_init, GenDataOutput}; -use lazy_static::lazy_static; use log::trace; +use std::sync::LazyLock; -lazy_static! { - pub static ref GEN_DATA: GenDataOutput = GenDataOutput::load(); -} +pub static GEN_DATA: LazyLock = LazyLock::new(GenDataOutput::load); async fn test_circuit_input_builder_block(block_num: u64) { let cli = get_client(); @@ -42,7 +41,7 @@ async fn test_circuit_input_builder_block(block_num: u64) { trace!("AccessSet: {:#?}", access_set); // 3. Query geth for all accounts, storage keys, and codes from Accesses - let (proofs, codes) = cli.get_state(block_num, access_set.into()).await.unwrap(); + let (proofs, codes) = cli.get_state(block_num, access_set).await.unwrap(); // 4. Build a partial StateDB from step 3 let (state_db, code_db) = build_state_code_db(proofs, codes); diff --git a/integration-tests/tests/mainnet.rs b/integration-tests/tests/mainnet.rs index 042966ab4c..bf978a1a0b 100644 --- a/integration-tests/tests/mainnet.rs +++ b/integration-tests/tests/mainnet.rs @@ -1,7 +1,9 @@ use bus_mapping::{ circuit_input_builder::{keccak_inputs, BuilderClient, CircuitsParams, PrecompileEcParams}, + util::read_env_var, Error::JSONRpcError, }; +use eth_types::H256; use halo2_proofs::{ circuit::Value, dev::{MockProver, VerifyFailure}, @@ -10,9 +12,11 @@ use halo2_proofs::{ }; use integration_tests::{get_client, log_init, CIRCUIT, END_BLOCK, START_BLOCK, TX_ID}; use zkevm_circuits::{ + bytecode_circuit::circuit::BytecodeCircuit, copy_circuit::CopyCircuit, evm_circuit::{witness::block_convert, EvmCircuit}, keccak_circuit::keccak_packed_multi::multi_keccak, + mpt_circuit::MptCircuit, rlp_circuit_fsm::RlpCircuit, state_circuit::StateCircuit, super_circuit::SuperCircuit, @@ -53,29 +57,49 @@ async fn test_mock_prove_tx() { } let cli = get_client(); let params = CircuitsParams { - max_rws: 100000, - max_copy_rows: 100000, + max_rws: 100_000, + max_copy_rows: 100_000, // dynamic max_txs: 10, - max_calldata: 40000, - max_mpt_rows: 40000, - max_inner_blocks: 64, - max_bytecode: 40000, + max_calldata: 40_000, + max_inner_blocks: 8, + max_bytecode: 100_000, + max_mpt_rows: 40_000, + max_poseidon_rows: 100_000, max_keccak_rows: 0, - max_exp_steps: 5000, + max_exp_steps: 5_000, max_evm_rows: 0, - max_rlp_rows: 42000, + max_rlp_rows: 42_000, ..Default::default() }; let cli = BuilderClient::new(cli, params).await.unwrap(); - let builder = cli.gen_inputs_tx(tx_id).await.unwrap(); + let mut builder = cli.gen_inputs_tx(tx_id).await.unwrap().enable_relax_mode(); if builder.block.txs.is_empty() { log::info!("skip empty block"); return; } - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + let mut block = block_convert::(&builder.block, &builder.code_db).unwrap(); + #[cfg(feature = "scroll")] + witness::block_mocking_apply_mpt(&mut block); + let mut updated_state_root = H256::default(); + block + .state_root + .unwrap_or_default() + .to_big_endian(&mut updated_state_root.0); + + builder.block.prev_state_root = block.prev_state_root; + // update both state_root in eth block (witness block's ctx and builder.block's header) + if let Some(mut last_eth_block_entry) = block.context.ctxs.last_entry() { + last_eth_block_entry.get_mut().eth_block.state_root = updated_state_root; + } + if let Some(mut last_eth_block_entry) = builder.block.headers.last_entry() { + last_eth_block_entry.get_mut().eth_block.state_root = updated_state_root; + } + // we need to re-calculate the keccak inputs since changing of state_root + block.keccak_inputs = keccak_inputs(&builder.block, &builder.code_db).unwrap(); + let errs = test_witness_block(&block); for err in &errs { log::error!("ERR: {}", err); @@ -108,6 +132,10 @@ fn test_witness_block(block: &witness::Block) -> Vec { test_with::>(block) } else if *CIRCUIT == "state" { test_with::>(block) + } else if *CIRCUIT == "mpt" { + test_with::>(block) + } else if *CIRCUIT == "bytecode" { + test_with::>(block) } else if *CIRCUIT == "super" { test_with::>(block) } else { @@ -127,14 +155,16 @@ async fn test_circuit_all_block() { let block_num = blk as u64; log::info!("test {} circuit, block number: {}", *CIRCUIT, block_num); let cli = get_client(); + let max_txs = read_env_var("MAX_TXS", 128); let params = CircuitsParams { max_rws: 4_000_000, max_copy_rows: 0, // dynamic - max_txs: 350, + max_txs, max_calldata: 2_000_000, max_inner_blocks: 64, max_bytecode: 3_000_000, max_mpt_rows: 2_000_000, + max_poseidon_rows: 4_000_000, max_keccak_rows: 0, max_exp_steps: 100_000, max_evm_rows: 0, @@ -145,6 +175,7 @@ async fn test_circuit_all_block() { let builder = cli.gen_inputs(block_num).await; if builder.is_err() { let err = builder.err().unwrap(); + println!("{err:?}"); let err_msg = match err { JSONRpcError(_json_rpc_err) => "JSONRpcError".to_string(), // too long... _ => format!("{err:?}"), @@ -152,14 +183,32 @@ async fn test_circuit_all_block() { log::error!("invalid builder {} {:?}, err num NA", block_num, err_msg); continue; } - let builder = builder.unwrap().0; + let mut builder = builder.unwrap().0.enable_relax_mode(); if builder.block.txs.is_empty() { log::info!("skip empty block"); // skip empty block continue; } - let block = block_convert::(&builder.block, &builder.code_db).unwrap(); + let mut block = block_convert::(&builder.block, &builder.code_db).unwrap(); + #[cfg(feature = "scroll")] + witness::block_mocking_apply_mpt(&mut block); + let mut updated_state_root = H256::default(); + block + .state_root + .unwrap_or_default() + .to_big_endian(&mut updated_state_root.0); + + builder.block.prev_state_root = block.prev_state_root; + // update both state_root in eth block (witness block's ctx and builder.block's header) + if let Some(mut last_eth_block_entry) = block.context.ctxs.last_entry() { + last_eth_block_entry.get_mut().eth_block.state_root = updated_state_root; + } + if let Some(mut last_eth_block_entry) = builder.block.headers.last_entry() { + last_eth_block_entry.get_mut().eth_block.state_root = updated_state_root; + } + // we need to re-calculate the keccak inputs since changing of state_root + block.keccak_inputs = keccak_inputs(&builder.block, &builder.code_db).unwrap(); let errs = test_witness_block(&block); log::info!( "test {} circuit, block number: {} err num {:?}", diff --git a/integration-tests/tests/readme.md b/integration-tests/tests/readme.md new file mode 100644 index 0000000000..efed79a489 --- /dev/null +++ b/integration-tests/tests/readme.md @@ -0,0 +1,32 @@ +# Integration Test # + +## Mainnet + +Testings in `mainnet.rs` enable accessing a RPC node with debug API, and test any block or single transaction from the node. Trace and block witness would be rebuilt from the input data (block or tx) and then being mocking proven by a specified circuit. + +The running parameters are specified by enviroment variables: + +* CIRCUIT: the circuit used to launch mocking proven, can be `super` (by default), `evm`, `copy`, `rlp`, `tx`, `state`, and we can use `none` to make a dry-run (no provding but only building of witness) +* GETH0_URL: the url of RPC node, like "https://eth-goerli.g.alchemy.com/v2/" + +For testing block, we specify blocks inside a range by `START_BLOCK` and `END_BLOCK`: + +* if we wish to test block 11001 from RPC node at `http://localhost:30303` +```bash +GETH0_URL=http://localhost:30303 START_BLOCK=11001 END_BLOCK=11001 cargo test --features=scroll --release test_circuit_all_block +``` + +For testing single transaction, we specify id of tx by `TXID`: +```bash +TX_ID=0xc820f41c097fb21e7d3dcbf450d2e20f28989eea4e36ee2ebd076b6952cf6693 GETH0_URL=http://localhost:30303 cargo test --features=scroll --release test_mock_prove_tx +``` + +### About testing mainnet block +To support most txs in mainnet some features are still missed: + +- [ ] EIP1559 in bus-mapping tx circuit (tx type = 2) +- [ ] BASEFEE opcode + +And for support most blocks in mainnet require more features: +- [ ] Support >128 max txs for rlp / sig circuit +- [ ] large copy data (> 500,000 which is hardcoded currently, 1.5M is adviced) diff --git a/integration-tests/tests/rpc.rs b/integration-tests/tests/rpc.rs index 268b3f91b8..706ee59c9d 100644 --- a/integration-tests/tests/rpc.rs +++ b/integration-tests/tests/rpc.rs @@ -1,14 +1,12 @@ +#![feature(lazy_cell)] #![cfg(feature = "rpc")] use eth_types::{StorageProof, Word}; use integration_tests::{get_client, CompiledContract, GenDataOutput, CHAIN_ID, CONTRACTS_PATH}; -use lazy_static::lazy_static; use pretty_assertions::assert_eq; -use std::{fs::File, path::Path}; +use std::{fs::File, path::Path, sync::LazyLock}; -lazy_static! { - pub static ref GEN_DATA: GenDataOutput = GenDataOutput::load(); -} +pub static GEN_DATA: LazyLock = LazyLock::new(GenDataOutput::load); #[tokio::test] async fn test_get_chain_id() { diff --git a/keccak256/Cargo.toml b/keccak256/Cargo.toml index b0da256431..3df66b1dd4 100644 --- a/keccak256/Cargo.toml +++ b/keccak256/Cargo.toml @@ -14,7 +14,6 @@ num-bigint.workspace = true num-traits.workspace = true plotters = { version = "0.3.0", optional = true } eth-types = { path = "../eth-types" } -lazy_static.workspace = true log.workspace = true env_logger.workspace = true diff --git a/keccak256/src/arith_helpers.rs b/keccak256/src/arith_helpers.rs index c95b7c58fe..c217e97535 100644 --- a/keccak256/src/arith_helpers.rs +++ b/keccak256/src/arith_helpers.rs @@ -147,7 +147,7 @@ pub fn convert_b13_lane_to_b9(x: Lane13, rot: u32) -> Lane9 { // with the special chunk in the middle let rotated: Vec = right .iter() - .chain(vec![special].iter()) + .chain([special].iter()) .chain(left.iter()) .map(|&x| convert_b13_coef(x)) .collect_vec(); @@ -199,7 +199,7 @@ pub fn inspect(x: BigUint, name: &str, base: u8) { pub fn f_from_radix_be(buf: &[u8], base: u8) -> F { let base = F::from(base as u64); buf.iter() - .fold(F::zero(), |acc, &x| acc * base + F::from(x as u64)) + .fold(F::ZERO, |acc, &x| acc * base + F::from(x as u64)) } #[cfg(test)] diff --git a/mock/Cargo.toml b/mock/Cargo.toml index 31f7d57a76..30d12357d4 100644 --- a/mock/Cargo.toml +++ b/mock/Cargo.toml @@ -7,7 +7,6 @@ license.workspace = true [dependencies] eth-types = { path = "../eth-types" } external-tracer = { path = "../external-tracer" } -lazy_static.workspace = true itertools.workspace = true ethers-signers.workspace = true ethers-core.workspace = true @@ -19,3 +18,6 @@ log.workspace = true default = [] shanghai = ["eth-types/shanghai"] scroll = ["eth-types/scroll", "external-tracer/scroll"] +enable-stack = ["eth-types/enable-stack", "external-tracer/enable-stack"] +enable-memory = ["eth-types/enable-memory", "external-tracer/enable-memory"] +enable-storage = ["eth-types/enable-storage", "external-tracer/enable-storage"] diff --git a/mock/src/block.rs b/mock/src/block.rs index e47bcfeff9..2ff51cdd72 100644 --- a/mock/src/block.rs +++ b/mock/src/block.rs @@ -66,7 +66,7 @@ impl Default for MockBlock { size: Word::zero(), mix_hash: Hash::zero(), nonce: H64::zero(), - chain_id: *MOCK_CHAIN_ID, + chain_id: MOCK_CHAIN_ID, } } } diff --git a/mock/src/lib.rs b/mock/src/lib.rs index 74b46cdf79..5d7c89c2cc 100644 --- a/mock/src/lib.rs +++ b/mock/src/lib.rs @@ -1,10 +1,11 @@ +#![feature(lazy_cell)] //! Mock types and functions to generate GethData used for tests use eth_types::{address, bytecode, bytecode::Bytecode, word, Address, Bytes, Word}; use ethers_signers::LocalWallet; -use lazy_static::lazy_static; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; +use std::sync::LazyLock; mod account; mod block; pub mod test_ctx; @@ -18,72 +19,75 @@ pub use transaction::{AddrOrWallet, MockTransaction, CORRECT_MOCK_TXS}; /// Mock block gas limit pub const MOCK_BLOCK_GAS_LIMIT: u64 = 10_000_000_000_000_000; -lazy_static! { - /// Mock 1 ETH - pub static ref MOCK_1_ETH: Word = eth(1); - /// Mock coinbase value - pub static ref MOCK_COINBASE: Address = - address!("0x00000000000000000000000000000000c014ba5e"); - /// Mock gasprice value - pub static ref MOCK_GASPRICE: Word = Word::from(1u8); - /// Mock BASEFEE value - pub static ref MOCK_BASEFEE: Word = Word::zero(); - /// Mock GASLIMIT value - pub static ref MOCK_GASLIMIT: Word = Word::from(0x2386f26fc10000u64); - /// Mock chain ID value - pub static ref MOCK_CHAIN_ID: u64 = 1338; - /// Mock DIFFICULTY value - pub static ref MOCK_DIFFICULTY: Word = Word::from(0x200000u64); - /// Mock DIFFICULTY value for l2geth (always 0) - pub static ref MOCK_DIFFICULTY_L2GETH: Word = Word::from(0x0u64); - /// Mock accounts loaded with ETH to use for test cases. - pub static ref MOCK_ACCOUNTS: Vec

= vec![ +/// Mock 1 ETH +pub static MOCK_1_ETH: LazyLock = LazyLock::new(|| eth(1)); +/// Mock coinbase value +pub static MOCK_COINBASE: LazyLock
= + LazyLock::new(|| address!("0x00000000000000000000000000000000c014ba5e")); +/// Mock gasprice value +pub static MOCK_GASPRICE: LazyLock = LazyLock::new(|| Word::from(1u8)); +/// Mock BASEFEE value +pub static MOCK_BASEFEE: LazyLock = LazyLock::new(Word::zero); +/// Mock GASLIMIT value +pub static MOCK_GASLIMIT: LazyLock = LazyLock::new(|| Word::from(0x2386f26fc10000u64)); +/// Mock chain ID value +pub static MOCK_CHAIN_ID: u64 = 1338; +/// Mock DIFFICULTY value +pub static MOCK_DIFFICULTY: LazyLock = LazyLock::new(|| Word::from(0x200000u64)); +/// Mock DIFFICULTY value for l2geth (always 0) +pub static MOCK_DIFFICULTY_L2GETH: LazyLock = LazyLock::new(|| Word::from(0x0u64)); +/// Mock accounts loaded with ETH to use for test cases. +pub static MOCK_ACCOUNTS: LazyLock> = LazyLock::new(|| { + vec![ address!("0x000000000000000000000000000000000cafe111"), address!("0x000000000000000000000000000000000cafe222"), address!("0x000000000000000000000000000000000cafe333"), address!("0x000000000000000000000000000000000cafe444"), address!("0x000000000000000000000000000000000cafe555"), - ]; - /// Mock EVM codes to use for test cases. - pub static ref MOCK_CODES: Vec = vec![ + ] +}); +/// Mock EVM codes to use for test cases. +pub static MOCK_CODES: LazyLock> = LazyLock::new(|| { + vec![ Bytes::from([0x60, 0x10, 0x00]), // PUSH1(0x10), STOP Bytes::from([0x60, 0x01, 0x60, 0x02, 0x01, 0x00]), // PUSH1(1), PUSH1(2), ADD, STOP Bytes::from([0x60, 0x01, 0x60, 0x02, 0x02, 0x00]), // PUSH1(1), PUSH1(2), MUL, STOP Bytes::from([0x60, 0x02, 0x60, 0x01, 0x03, 0x00]), // PUSH1(2), PUSH1(1), SUB, STOP Bytes::from([0x60, 0x09, 0x60, 0x03, 0x04, 0x00]), // PUSH1(9), PUSH1(3), DIV, STOP - Bytes::from([0x30; 256]), // ADDRESS * 256 - ]; - /// Mock wallets used to generate correctly signed and hashed Transactions. - pub static ref MOCK_WALLETS: Vec = { - let mut rng = ChaCha20Rng::seed_from_u64(2u64); - vec![ - LocalWallet::new(&mut rng), - LocalWallet::new(&mut rng), - LocalWallet::new(&mut rng), + Bytes::from([0x30; 256]), // ADDRESS * 256 ] - }; - /// Mock EVM bytecode for a deployed contract. - /// PUSH1 0x20 - /// PUSH1 0 - /// PUSH1 0 - /// CALLDATACOPY - /// PUSH1 0x20 - /// PUSH1 0 - /// RETURN - /// - /// bytecode: 0x6020600060003760206000F3 - /// - /// // constructor - /// PUSH12 0x6020600060003760206000F3 - /// PUSH1 0 - /// MSTORE - /// PUSH1 0xC - /// PUSH1 0x14 - /// RETURN - /// - /// bytecode: 0x6B6020600060003760206000F3600052600C6014F3 - pub static ref MOCK_DEPLOYED_CONTRACT_BYTECODE: Word = word!("6B6020600060003760206000F3600052600C6014F3"); -} +}); +/// Mock wallets used to generate correctly signed and hashed Transactions. +pub static MOCK_WALLETS: LazyLock> = LazyLock::new(|| { + let mut rng = ChaCha20Rng::seed_from_u64(2u64); + vec![ + LocalWallet::new(&mut rng), + LocalWallet::new(&mut rng), + LocalWallet::new(&mut rng), + ] +}); +/// Mock EVM bytecode for a deployed contract. +/// PUSH1 0x20 +/// PUSH1 0 +/// PUSH1 0 +/// CALLDATACOPY +/// PUSH1 0x20 +/// PUSH1 0 +/// RETURN +/// +/// bytecode: 0x6020600060003760206000F3 +/// +/// // constructor +/// PUSH12 0x6020600060003760206000F3 +/// PUSH1 0 +/// MSTORE +/// PUSH1 0xC +/// PUSH1 0x14 +/// RETURN +/// +/// bytecode: 0x6B6020600060003760206000F3600052600C6014F3 +pub static MOCK_DEPLOYED_CONTRACT_BYTECODE: LazyLock = + LazyLock::new(|| word!("6B6020600060003760206000F3600052600C6014F3")); /// Generate a [`Word`] which corresponds to a certain amount of ETH. pub fn eth(x: u64) -> Word { diff --git a/mock/src/test_ctx.rs b/mock/src/test_ctx.rs index 472e983e60..33c603f38d 100644 --- a/mock/src/test_ctx.rs +++ b/mock/src/test_ctx.rs @@ -33,7 +33,6 @@ pub use external_tracer::LoggerConfig; /// ```rust /// use eth_types::evm_types::{stack::Stack, Gas, OpcodeId}; /// use eth_types::{address, bytecode, geth_types::GethData, word, Bytecode, ToWord, Word}; -/// use lazy_static::lazy_static; /// use mock::test_ctx::{helpers::*, TestContext}; /// // code_a calls code /// // jump to 0x10 which is outside the code (and also not marked with diff --git a/mock/src/transaction.rs b/mock/src/transaction.rs index d4c0409e44..73e61e4272 100644 --- a/mock/src/transaction.rs +++ b/mock/src/transaction.rs @@ -10,74 +10,73 @@ use ethers_core::{ types::{OtherFields, TransactionRequest}, }; use ethers_signers::{LocalWallet, Signer}; -use lazy_static::lazy_static; use rand::SeedableRng; use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; - -lazy_static! { - /// Collection of correctly hashed and signed Transactions which can be used to test circuits or opcodes that have to check integrity of the Tx itself. - /// Some of the parameters of the Tx are hardcoded such as `nonce`, `value`, `gas_price` etc... - pub static ref CORRECT_MOCK_TXS: Vec = { - let mut rng = ChaCha20Rng::seed_from_u64(2u64); - - vec![ - MockTransaction::default() - .transaction_idx(1u64) - .from(AddrOrWallet::random(&mut rng)) - .to(MOCK_ACCOUNTS[0]) - .nonce(word!("0x103")) - .value(word!("0x3e8")) - .gas_price(word!("0x4d2")) - .input(vec![1, 2, 3, 4, 5, 0, 6, 7, 8, 9].into()) // call data gas cost of 0 is 4 - .build(), - MockTransaction::default() - .transaction_idx(2u64) - .from(AddrOrWallet::random(&mut rng)) - .to(MOCK_ACCOUNTS[1]) - .nonce(word!("0x104")) - .value(word!("0x3e8")) - .gas_price(word!("0x4d2")) - .input(Bytes::from(b"hello")) - .build(), - MockTransaction::default() - .transaction_idx(3u64) - .from(AddrOrWallet::random(&mut rng)) - .to(MOCK_ACCOUNTS[2]) - .nonce(word!("0x105")) - .value(word!("0x3e8")) - .gas_price(word!("0x4d2")) - .input(Bytes::from(b"hello")) - .build(), - MockTransaction::default() - .transaction_idx(4u64) - .from(AddrOrWallet::random(&mut rng)) - .to(MOCK_ACCOUNTS[3]) - .nonce(word!("0x106")) - .value(word!("0x3e8")) - .gas_price(word!("0x4d2")) - .input(Bytes::from(b"")) - .build(), - MockTransaction::default() - .transaction_idx(5u64) - .from(AddrOrWallet::random(&mut rng)) - .to(MOCK_ACCOUNTS[4]) - .nonce(word!("0x0")) - .value(word!("0x0")) - .gas_price(word!("0x4d2")) - .input(Bytes::from(b"hello")) - .build(), - MockTransaction::default() - .transaction_idx(6u64) - .from(AddrOrWallet::random(&mut rng)) - .to(AddrOrWallet::Addr(Address::zero())) - .nonce(word!("0x0")) - .value(word!("0x0")) - .gas_price(word!("0x4d2")) - .input(Bytes::from(b"hello")) - .build(), - ] - }; -} +use std::sync::LazyLock; + +/// Collection of correctly hashed and signed Transactions which can be used to test circuits or +/// opcodes that have to check integrity of the Tx itself. Some of the parameters of the Tx are +/// hardcoded such as `nonce`, `value`, `gas_price` etc... +pub static CORRECT_MOCK_TXS: LazyLock> = LazyLock::new(|| { + let mut rng = ChaCha20Rng::seed_from_u64(2u64); + + vec![ + MockTransaction::default() + .transaction_idx(1u64) + .from(AddrOrWallet::random(&mut rng)) + .to(MOCK_ACCOUNTS[0]) + .nonce(word!("0x103")) + .value(word!("0x3e8")) + .gas_price(word!("0x4d2")) + .input(vec![1, 2, 3, 4, 5, 0, 6, 7, 8, 9].into()) // call data gas cost of 0 is 4 + .build(), + MockTransaction::default() + .transaction_idx(2u64) + .from(AddrOrWallet::random(&mut rng)) + .to(MOCK_ACCOUNTS[1]) + .nonce(word!("0x104")) + .value(word!("0x3e8")) + .gas_price(word!("0x4d2")) + .input(Bytes::from(b"hello")) + .build(), + MockTransaction::default() + .transaction_idx(3u64) + .from(AddrOrWallet::random(&mut rng)) + .to(MOCK_ACCOUNTS[2]) + .nonce(word!("0x105")) + .value(word!("0x3e8")) + .gas_price(word!("0x4d2")) + .input(Bytes::from(b"hello")) + .build(), + MockTransaction::default() + .transaction_idx(4u64) + .from(AddrOrWallet::random(&mut rng)) + .to(MOCK_ACCOUNTS[3]) + .nonce(word!("0x106")) + .value(word!("0x3e8")) + .gas_price(word!("0x4d2")) + .input(Bytes::from(b"")) + .build(), + MockTransaction::default() + .transaction_idx(5u64) + .from(AddrOrWallet::random(&mut rng)) + .to(MOCK_ACCOUNTS[4]) + .nonce(word!("0x0")) + .value(word!("0x0")) + .gas_price(word!("0x4d2")) + .input(Bytes::from(b"hello")) + .build(), + MockTransaction::default() + .transaction_idx(6u64) + .from(AddrOrWallet::random(&mut rng)) + .to(AddrOrWallet::Addr(Address::zero())) + .nonce(word!("0x0")) + .value(word!("0x0")) + .gas_price(word!("0x4d2")) + .input(Bytes::from(b"hello")) + .build(), + ] +}); #[derive(Debug, Clone)] pub enum AddrOrWallet { @@ -185,7 +184,7 @@ impl Default for MockTransaction { access_list: AccessList::default(), max_priority_fee_per_gas: Word::zero(), max_fee_per_gas: Word::zero(), - chain_id: *MOCK_CHAIN_ID, + chain_id: MOCK_CHAIN_ID, } } } diff --git a/prover/Cargo.toml b/prover/Cargo.toml index fc9430f7e0..1bf513bbb3 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -28,7 +28,6 @@ itertools.workspace = true log.workspace = true log4rs = { version = "1.2.0", default_features = false, features = ["console_appender", "file_appender"] } num-bigint.workspace = true -once_cell.workspace = true rand.workspace = true rand_xorshift.workspace = true serde.workspace = true diff --git a/prover/src/common/prover/mock.rs b/prover/src/common/prover/mock.rs index e6beab5fbf..9a84af12b8 100644 --- a/prover/src/common/prover/mock.rs +++ b/prover/src/common/prover/mock.rs @@ -1,10 +1,10 @@ use super::Prover; use crate::utils::read_env_var; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; -use once_cell::sync::Lazy; use snark_verifier_sdk::CircuitExt; +use std::sync::LazyLock; -pub static MOCK_PROVE: Lazy = Lazy::new(|| read_env_var("MOCK_PROVE", false)); +pub static MOCK_PROVE: LazyLock = LazyLock::new(|| read_env_var("MOCK_PROVE", false)); impl Prover { pub fn assert_if_mock_prover>(id: &str, degree: u32, circuit: &C) { diff --git a/prover/src/config.rs b/prover/src/config.rs index 527bccecff..5874b61420 100644 --- a/prover/src/config.rs +++ b/prover/src/config.rs @@ -1,24 +1,28 @@ use crate::utils::read_env_var; use aggregator::ConfigParams; -use once_cell::sync::Lazy; -use std::{collections::HashSet, fmt, fs::File, path::Path}; +use std::{collections::HashSet, fmt, fs::File, path::Path, sync::LazyLock}; -pub static INNER_DEGREE: Lazy = Lazy::new(|| read_env_var("SCROLL_PROVER_INNER_DEGREE", 20)); +pub static INNER_DEGREE: LazyLock = + LazyLock::new(|| read_env_var("SCROLL_PROVER_INNER_DEGREE", 20)); -pub static ASSETS_DIR: Lazy = - Lazy::new(|| read_env_var("SCROLL_PROVER_ASSETS_DIR", "configs".to_string())); +pub static ASSETS_DIR: LazyLock = + LazyLock::new(|| read_env_var("SCROLL_PROVER_ASSETS_DIR", "configs".to_string())); -pub static LAYER1_CONFIG_PATH: Lazy = Lazy::new(|| asset_file_path("layer1.config")); -pub static LAYER2_CONFIG_PATH: Lazy = Lazy::new(|| asset_file_path("layer2.config")); -pub static LAYER3_CONFIG_PATH: Lazy = Lazy::new(|| asset_file_path("layer3.config")); -pub static LAYER4_CONFIG_PATH: Lazy = Lazy::new(|| asset_file_path("layer4.config")); +pub static LAYER1_CONFIG_PATH: LazyLock = + LazyLock::new(|| asset_file_path("layer1.config")); +pub static LAYER2_CONFIG_PATH: LazyLock = + LazyLock::new(|| asset_file_path("layer2.config")); +pub static LAYER3_CONFIG_PATH: LazyLock = + LazyLock::new(|| asset_file_path("layer3.config")); +pub static LAYER4_CONFIG_PATH: LazyLock = + LazyLock::new(|| asset_file_path("layer4.config")); -pub static LAYER1_DEGREE: Lazy = Lazy::new(|| layer_degree(&LAYER1_CONFIG_PATH)); -pub static LAYER2_DEGREE: Lazy = Lazy::new(|| layer_degree(&LAYER2_CONFIG_PATH)); -pub static LAYER3_DEGREE: Lazy = Lazy::new(|| layer_degree(&LAYER3_CONFIG_PATH)); -pub static LAYER4_DEGREE: Lazy = Lazy::new(|| layer_degree(&LAYER4_CONFIG_PATH)); +pub static LAYER1_DEGREE: LazyLock = LazyLock::new(|| layer_degree(&LAYER1_CONFIG_PATH)); +pub static LAYER2_DEGREE: LazyLock = LazyLock::new(|| layer_degree(&LAYER2_CONFIG_PATH)); +pub static LAYER3_DEGREE: LazyLock = LazyLock::new(|| layer_degree(&LAYER3_CONFIG_PATH)); +pub static LAYER4_DEGREE: LazyLock = LazyLock::new(|| layer_degree(&LAYER4_CONFIG_PATH)); -pub static ZKEVM_DEGREES: Lazy> = Lazy::new(|| { +pub static ZKEVM_DEGREES: LazyLock> = LazyLock::new(|| { Vec::from_iter(HashSet::from([ *INNER_DEGREE, *LAYER1_DEGREE, @@ -26,8 +30,8 @@ pub static ZKEVM_DEGREES: Lazy> = Lazy::new(|| { ])) }); -pub static AGG_DEGREES: Lazy> = - Lazy::new(|| Vec::from_iter(HashSet::from([*LAYER3_DEGREE, *LAYER4_DEGREE]))); +pub static AGG_DEGREES: LazyLock> = + LazyLock::new(|| Vec::from_iter(HashSet::from([*LAYER3_DEGREE, *LAYER4_DEGREE]))); #[derive(Clone, Copy, Debug)] pub enum LayerId { diff --git a/prover/src/consts.rs b/prover/src/consts.rs index 91390301d6..d24ac60a72 100644 --- a/prover/src/consts.rs +++ b/prover/src/consts.rs @@ -1,12 +1,12 @@ use crate::utils::read_env_var; -use once_cell::sync::Lazy; +use std::sync::LazyLock; -pub static AGG_KECCAK_ROW: Lazy = Lazy::new(|| read_env_var("AGG_KECCAK_ROW", 50)); -pub static AGG_VK_FILENAME: Lazy = - Lazy::new(|| read_env_var("AGG_VK_FILENAME", "agg_vk.vkey".to_string())); -pub static CHUNK_PROTOCOL_FILENAME: Lazy = - Lazy::new(|| read_env_var("CHUNK_PROTOCOL_FILENAME", "chunk.protocol".to_string())); -pub static CHUNK_VK_FILENAME: Lazy = - Lazy::new(|| read_env_var("CHUNK_VK_FILENAME", "chunk_vk.vkey".to_string())); -pub static DEPLOYMENT_CODE_FILENAME: Lazy = - Lazy::new(|| read_env_var("DEPLOYMENT_CODE_FILENAME", "evm_verifier.bin".to_string())); +pub static AGG_KECCAK_ROW: LazyLock = LazyLock::new(|| read_env_var("AGG_KECCAK_ROW", 50)); +pub static AGG_VK_FILENAME: LazyLock = + LazyLock::new(|| read_env_var("AGG_VK_FILENAME", "agg_vk.vkey".to_string())); +pub static CHUNK_PROTOCOL_FILENAME: LazyLock = + LazyLock::new(|| read_env_var("CHUNK_PROTOCOL_FILENAME", "chunk.protocol".to_string())); +pub static CHUNK_VK_FILENAME: LazyLock = + LazyLock::new(|| read_env_var("CHUNK_VK_FILENAME", "chunk_vk.vkey".to_string())); +pub static DEPLOYMENT_CODE_FILENAME: LazyLock = + LazyLock::new(|| read_env_var("DEPLOYMENT_CODE_FILENAME", "evm_verifier.bin".to_string())); diff --git a/prover/src/lib.rs b/prover/src/lib.rs index a398b2cf7e..8ee5a90d82 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(lazy_cell)] + pub mod aggregator; pub mod common; pub mod config; diff --git a/prover/src/test/batch.rs b/prover/src/test/batch.rs index 82f5aef45a..91f8ff96ab 100644 --- a/prover/src/test/batch.rs +++ b/prover/src/test/batch.rs @@ -6,22 +6,22 @@ use crate::{ utils::read_env_var, ChunkHash, ChunkProof, }; -use once_cell::sync::Lazy; +use std::sync::{LazyLock, Mutex}; -static mut BATCH_PROVER: Lazy = Lazy::new(|| { +static BATCH_PROVER: LazyLock> = LazyLock::new(|| { let assets_dir = read_env_var("SCROLL_PROVER_ASSETS_DIR", "./test_assets".to_string()); let params_dir = read_env_var("SCROLL_PROVER_PARAMS_DIR", "./test_params".to_string()); let prover = Prover::from_dirs(¶ms_dir, &assets_dir); log::info!("Constructed batch-prover"); - prover + Mutex::new(prover) }); -static mut BATCH_VERIFIER: Lazy = Lazy::new(|| { +static BATCH_VERIFIER: LazyLock> = LazyLock::new(|| { let assets_dir = read_env_var("SCROLL_PROVER_ASSETS_DIR", "./test_assets".to_string()); - let prover = unsafe { &mut BATCH_PROVER }; + let mut prover = BATCH_PROVER.lock().expect("poisoned batch-prover"); let params = prover.inner.params(LayerId::Layer4.degree()).clone(); let pk = prover @@ -35,20 +35,23 @@ static mut BATCH_VERIFIER: Lazy = Lazy::new(|| { let verifier = Verifier::new(params, vk, deployment_code); log::info!("Constructed batch-verifier"); - verifier + Mutex::new(verifier) }); pub fn batch_prove(test: &str, chunk_hashes_proofs: Vec<(ChunkHash, ChunkProof)>) { log::info!("{test}: batch-prove BEGIN"); - let prover = unsafe { &mut BATCH_PROVER }; - let proof = prover + let proof = BATCH_PROVER + .lock() + .expect("poisoned batch-prover") .gen_agg_evm_proof(chunk_hashes_proofs, None, None) .unwrap_or_else(|err| panic!("{test}: failed to generate batch proof: {err}")); log::info!("{test}: generated batch proof"); - let verifier = unsafe { &mut BATCH_VERIFIER }; - let verified = verifier.verify_agg_evm_proof(proof); + let verified = BATCH_VERIFIER + .lock() + .expect("poisoned batch-verifier") + .verify_agg_evm_proof(proof); assert!(verified, "{test}: failed to verify batch proof"); log::info!("{test}: batch-prove END"); diff --git a/prover/src/test/chunk.rs b/prover/src/test/chunk.rs index e9b2cf18ba..5a39247514 100644 --- a/prover/src/test/chunk.rs +++ b/prover/src/test/chunk.rs @@ -4,21 +4,23 @@ use crate::{ utils::read_env_var, ChunkHash, ChunkProof, CompressionCircuit, WitnessBlock, }; -use once_cell::sync::Lazy; -use std::env; +use std::{ + env, + sync::{LazyLock, Mutex}, +}; -static mut CHUNK_PROVER: Lazy = Lazy::new(|| { +static CHUNK_PROVER: LazyLock> = LazyLock::new(|| { let params_dir = read_env_var("SCROLL_PROVER_PARAMS_DIR", "./test_params".to_string()); let prover = Prover::from_params_dir(¶ms_dir, &ZKEVM_DEGREES); log::info!("Constructed chunk-prover"); - prover + Mutex::new(prover) }); -static mut CHUNK_VERIFIER: Lazy> = Lazy::new(|| { +static CHUNK_VERIFIER: LazyLock>> = LazyLock::new(|| { env::set_var("COMPRESSION_CONFIG", LayerId::Layer2.config_path()); - let prover = unsafe { &mut CHUNK_PROVER }; + let mut prover = CHUNK_PROVER.lock().expect("poisoned chunk-prover"); let params = prover.params(LayerId::Layer2.degree()).clone(); let pk = prover @@ -29,13 +31,13 @@ static mut CHUNK_VERIFIER: Lazy> = Lazy::new(|| { let verifier = Verifier::new(params, vk); log::info!("Constructed chunk-verifier"); - verifier + Mutex::new(verifier) }); pub fn chunk_prove(test: &str, witness_block: &WitnessBlock) -> ChunkProof { log::info!("{test}: chunk-prove BEGIN"); - let prover = unsafe { &mut CHUNK_PROVER }; + let mut prover = CHUNK_PROVER.lock().expect("poisoned chunk-prover"); let inner_id = read_env_var("INNER_LAYER_ID", LayerId::Inner.id().to_string()); let inner_id_changed = prover.pk(&inner_id).is_none(); @@ -49,7 +51,7 @@ pub fn chunk_prove(test: &str, witness_block: &WitnessBlock) -> ChunkProof { .unwrap_or_else(|err| panic!("{test}: failed to generate chunk snark: {err}")); log::info!("{test}: generated chunk snark"); - let verifier = unsafe { &mut CHUNK_VERIFIER }; + let mut verifier = CHUNK_VERIFIER.lock().expect("poisoned chunk-verifier"); // Reset VK if inner-layer ID changed. if inner_id_changed { diff --git a/prover/src/test/inner.rs b/prover/src/test/inner.rs index ab93b91bc1..5bcf3ae640 100644 --- a/prover/src/test/inner.rs +++ b/prover/src/test/inner.rs @@ -5,19 +5,19 @@ use crate::{ zkevm::circuit::{SuperCircuit, TargetCircuit}, WitnessBlock, }; -use once_cell::sync::Lazy; +use std::sync::{LazyLock, Mutex}; -static mut INNER_PROVER: Lazy = Lazy::new(|| { +static INNER_PROVER: LazyLock> = LazyLock::new(|| { let params_dir = read_env_var("SCROLL_PROVER_PARAMS_DIR", "./test_params".to_string()); let prover = Prover::from_params_dir(¶ms_dir, &[*INNER_DEGREE]); log::info!("Constructed inner-prover"); - prover + Mutex::new(prover) }); -static mut INNER_VERIFIER: Lazy::Inner>> = - Lazy::new(|| { - let prover = unsafe { &mut INNER_PROVER }; +static INNER_VERIFIER: LazyLock::Inner>>> = + LazyLock::new(|| { + let mut prover = INNER_PROVER.lock().expect("poisoned inner-prover"); let params = prover.params(*INNER_DEGREE).clone(); let inner_id = read_env_var("INNER_LAYER_ID", LayerId::Inner.id().to_string()); @@ -27,13 +27,13 @@ static mut INNER_VERIFIER: Lazy::Inner> let verifier = Verifier::new(params, vk); log::info!("Constructed inner-verifier"); - verifier + Mutex::new(verifier) }); pub fn inner_prove(test: &str, witness_block: &WitnessBlock) { log::info!("{test}: inner-prove BEGIN"); - let prover = unsafe { &mut INNER_PROVER }; + let mut prover = INNER_PROVER.lock().expect("poisoned inner-prover"); let inner_id = read_env_var("INNER_LAYER_ID", LayerId::Inner.id().to_string()); let inner_id_changed = prover.pk(&inner_id).is_none(); @@ -48,7 +48,7 @@ pub fn inner_prove(test: &str, witness_block: &WitnessBlock) { .unwrap_or_else(|err| panic!("{test}: failed to generate inner snark: {err}")); log::info!("{test}: generated inner snark"); - let verifier = unsafe { &mut INNER_VERIFIER }; + let mut verifier = INNER_VERIFIER.lock().expect("poisoned inner-verifier"); // Reset VK if inner-layer ID changed. if inner_id_changed { diff --git a/prover/src/zkevm/circuit/l1_builder.rs b/prover/src/zkevm/circuit/l1_builder.rs index ffd0dec610..f1de7a17b8 100644 --- a/prover/src/zkevm/circuit/l1_builder.rs +++ b/prover/src/zkevm/circuit/l1_builder.rs @@ -24,6 +24,7 @@ pub fn calculate_row_usage_of_witness_block( unimplemented!("Must build with feature scroll") } +#[allow(clippy::ptr_arg)] pub fn check_batch_capacity(_block_traces: &mut Vec) -> Result<()> { unimplemented!("Must build with feature scroll") } diff --git a/prover/src/zkevm/circuit/l2_builder.rs b/prover/src/zkevm/circuit/l2_builder.rs index 6b8793489a..d1c23b00b3 100644 --- a/prover/src/zkevm/circuit/l2_builder.rs +++ b/prover/src/zkevm/circuit/l2_builder.rs @@ -9,16 +9,15 @@ use eth_types::{l2_types::BlockTrace, ToWord, H256}; use halo2_proofs::halo2curves::bn256::Fr; use itertools::Itertools; use mpt_zktrie::state::{ZkTrieHash, ZktrieState}; -use once_cell::sync::Lazy; -use std::time::Instant; +use std::{sync::LazyLock, time::Instant}; use zkevm_circuits::{ evm_circuit::witness::{block_apply_mpt_state, Block}, util::SubCircuit, witness::block_convert, }; -static CHAIN_ID: Lazy = Lazy::new(|| read_env_var("CHAIN_ID", 53077)); -static AUTO_TRUNCATE: Lazy = Lazy::new(|| read_env_var("AUTO_TRUNCATE", false)); +static CHAIN_ID: LazyLock = LazyLock::new(|| read_env_var("CHAIN_ID", 53077)); +static AUTO_TRUNCATE: LazyLock = LazyLock::new(|| read_env_var("AUTO_TRUNCATE", false)); ////// params for degree = 20 //////////// pub const MAX_TXS: usize = 100; diff --git a/rust-toolchain b/rust-toolchain index 44b874bfca..27c108be5c 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2022-12-10 +nightly-2023-12-03 diff --git a/testool/Cargo.toml b/testool/Cargo.toml index 2d04aa1338..f879c12d34 100644 --- a/testool/Cargo.toml +++ b/testool/Cargo.toml @@ -21,7 +21,6 @@ keccak256 = { path = "../keccak256" } log.workspace = true itertools.workspace = true mock = { path = "../mock" } -once_cell.workspace = true prettytable-rs = "0.10" prover = { path = "../prover", optional = true } rayon.workspace = true @@ -41,7 +40,7 @@ urlencoding = "2.1.2" ctor.workspace = true [features] -default = ["ignore-test-docker", "skip-self-destruct", "shanghai"] +default = ["ignore-test-docker", "skip-self-destruct", "shanghai", "enable-stack", "enable-storage"] onephase = ["zkevm-circuits/onephase"] ignore-test-docker = [] skip-self-destruct = [] @@ -50,3 +49,7 @@ scroll = ["bus-mapping/scroll", "eth-types/scroll", "external-tracer/scroll", "m parallel_syn = ["halo2_proofs/parallel_syn", "zkevm-circuits/parallel_syn", "prover?/parallel_syn"] inner-prove = ["prover/test", "parallel_syn", "scroll", "shanghai"] chunk-prove = ["prover/test", "parallel_syn", "scroll", "shanghai"] + +enable-stack = ["zkevm-circuits/enable-stack"] +enable-memory = ["zkevm-circuits/enable-memory"] +enable-storage = ["zkevm-circuits/enable-storage"] \ No newline at end of file diff --git a/testool/codehash.txt b/testool/codehash.txt index 2b68393128..640d181703 100644 --- a/testool/codehash.txt +++ b/testool/codehash.txt @@ -8250,3 +8250,12 @@ ef05bea5645aa48c5e7325bb1a23119c961290af1c112594ee1aa06e054277db=5b61c3506080511 31d87b82490b345887422d30c21c0ce23b669af6171f9d386e845f68fdc272f7=6000600060006000600073bbbf5374fce5edbc8e2a8697c15331677e6ebf0b5af1600055600160015500 2be93bf90fda15c9f97e8936c2ed1850d6dac4aa07c7df2805f827f73c522c56=5b61c3506080511015603e576000600061c350600073aaaf5374fce5edbc8e2a8697c15331677e6ebf0b620186a0fa6000556001608051016080526000565b60805160205500 bfa65cd1585cc6bda1710e286b92a295032f7d4ddcd78151566fbd2bebfcb1af=60005460005500 +c7390d213511f68446a476321d7bd59a24a6b9128e9e3eb4dc9a31bfe440ac13=6005608d565b630accf7398114601d57633f3716928114602757602e565b60236033565b602e565b602d6042565b5b50609b565b605a60ff5d60ff5c8060015550565b605060ff5d60ff5c806000557f0accf739000000000000000000000000000000000000000000000000000000006000526000806020600080305af18060025560ff5c80600355505050565b600080358060e01c91505090565b +d1e1b266b42b75f5aa1a56c7a4f16ff0485f4d508f27827a5e13e0cfd273755f=60056080565b6343ac1c398114601d57633f3716928114602757602e565b60236033565b602e565b602d603d565b5b50608e565b60005c8060015550565b602c60005d7f43ac1c39000000000000000000000000000000000000000000000000000000006000526000806020600080305af160005c80600055816002555050565b600080358060e01c91505090565b +e2ac2549e0bd45830ae8f333545332ff08e0ad866cca801ec1e5dcc4e069fb12=60056088565b63e2da2eb08114601d57633f3716928114602757602e565b60236033565b602e565b602d603d565b5b506096565b600b60005d600080fd5b600a60005d60005c806000557fe2da2eb00000000000000000000000000000000000000000000000000000000060005260008060206000803061fffff18060025560005c6001555050565b600080358060e01c91505090565b +83f2328a9666f8e26c8aac9c5883e2cecb3254564f9fef55c48654db9b602127=60005c8060015550 +74f5a8b5474d90baa8ab32de620c11352d18f5054d63556c660c14cda76f3fd9=605860005d60005c8060015550 +c550fadcf3a57fecc535c09f22b1196a1fc767534e09eaad3255f4d95c6fcaf9=60056083565b6362fdb9be8114601d57633f3716928114602757602e565b60236033565b602e565b602d603a565b5b506091565b604e60005d565b603760005d60005c806000557f62fdb9be000000000000000000000000000000000000000000000000000000006000526000806020600080305af18060025560005c6001555050565b600080358060e01c91505090565b +1925f29aa2c260e1de3b5517a7b808a144f363ac20954997bb49180f556d32b2=601e60005d60015c8060015550 +0b13953be4348918d5eebc8e439a6512b0a3ee6300471172b449a9da42a4a1e5=600a60005d600080602060008073b00000000000000000000000000000000000000b5af160005c80600055816001555050 +acd163028aaf83a3065f93cb39c12ebe6fd07619e1bea3c95708e0da9435bf35=60005c80600055601460005d60005c806001555050 diff --git a/testool/src/main.rs b/testool/src/main.rs index ac6b703994..9f28b141c3 100644 --- a/testool/src/main.rs +++ b/testool/src/main.rs @@ -1,3 +1,5 @@ +#![feature(lazy_cell)] + /// Execute the bytecode from an empty state and run the EVM and State circuits mod abi; mod compiler; diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 8339941b4a..f64b9cc642 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -16,8 +16,7 @@ use ethers_signers::LocalWallet; use external_tracer::{LoggerConfig, TraceConfig}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, plonk::Circuit}; use itertools::Itertools; -use once_cell::sync::Lazy; -use std::{collections::HashMap, env, str::FromStr}; +use std::{collections::HashMap, env, str::FromStr, sync::LazyLock}; use thiserror::Error; use zkevm_circuits::{ bytecode_circuit::circuit::BytecodeCircuit, ecc_circuit::EccCircuit, @@ -32,7 +31,7 @@ pub fn read_env_var(var_name: &'static str, default: T) -> T .unwrap_or(default) } /// Which circuit to test. Default is evm + state. -pub static CIRCUIT: Lazy = Lazy::new(|| read_env_var("CIRCUIT", "".to_string())); +pub static CIRCUIT: LazyLock = LazyLock::new(|| read_env_var("CIRCUIT", "".to_string())); #[derive(PartialEq, Eq, Error, Debug)] pub enum StateTestError { @@ -204,7 +203,10 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { }], accounts, logger_config: LoggerConfig { - enable_memory: *bus_mapping::util::CHECK_MEM_STRICT, + enable_memory: cfg!(feature = "enable-memory") + && bus_mapping::util::GETH_TRACE_CHECK_LEVEL.should_check(), + disable_stack: !cfg!(feature = "enable-stack"), + disable_storage: !cfg!(feature = "enable-storage"), ..Default::default() }, #[cfg(feature = "shanghai")] @@ -554,6 +556,7 @@ pub fn run_test( log::info!("{test_id}: run-test BEGIN - {circuits_config:?}"); // get the geth traces + #[allow(unused_mut)] let (_, mut trace_config, post) = into_traceconfig(st.clone()); let balance_overflow = trace_config diff --git a/testool/src/statetest/mod.rs b/testool/src/statetest/mod.rs index 0693a1a475..dde5ee9ffd 100644 --- a/testool/src/statetest/mod.rs +++ b/testool/src/statetest/mod.rs @@ -6,9 +6,12 @@ pub mod spec; mod suite; mod yaml; -pub use executor::{run_test, CircuitsConfig, StateTestError}; +pub use executor::{run_test, CircuitsConfig}; pub use json::JsonStateTestBuilder; pub use results::{ResultLevel, Results}; -pub use spec::{AccountMatch, Env, StateTest, StateTestResult}; +pub use spec::{AccountMatch, StateTest, StateTestResult}; pub use suite::{load_statetests_suite, run_statetests_suite}; pub use yaml::YamlStateTestBuilder; + +#[cfg(test)] +pub use executor::StateTestError; diff --git a/testool/src/statetest/parse.rs b/testool/src/statetest/parse.rs index 0576cc8d35..ebdfa213ec 100644 --- a/testool/src/statetest/parse.rs +++ b/testool/src/statetest/parse.rs @@ -1,17 +1,14 @@ -use std::collections::HashMap; - use crate::{abi, Compiler}; - use anyhow::{bail, Context, Result}; use eth_types::{Address, Bytes, H256, U256}; use log::debug; -use once_cell::sync::Lazy; use regex::Regex; +use std::{collections::HashMap, sync::LazyLock}; type Label = String; -static YUL_FRAGMENT_PARSER: Lazy = - Lazy::new(|| Regex::new(r#"\s*(?P\w+)?\s*(?P\{[\S\s]*)"#).unwrap()); +static YUL_FRAGMENT_PARSER: LazyLock = + LazyLock::new(|| Regex::new(r#"\s*(?P\w+)?\s*(?P\{[\S\s]*)"#).unwrap()); /// returns the element as an address pub fn parse_address(as_str: &str) -> Result
{ diff --git a/testool/src/utils.rs b/testool/src/utils.rs index 4358dff10f..0d91a3a081 100644 --- a/testool/src/utils.rs +++ b/testool/src/utils.rs @@ -1,11 +1,14 @@ use std::str::FromStr; use anyhow::{bail, Result}; -use eth_types::{bytecode::OpcodeWithData, Bytecode, GethExecTrace, U256}; +use eth_types::{bytecode::OpcodeWithData, Bytecode, GethExecTrace}; use log::{error, info}; use prettytable::Table; use std::process::{Command, Stdio}; +#[cfg(any(feature = "enable-stack", feature = "enable-storage"))] +use eth_types::U256; + #[derive(Debug, Eq, PartialEq, PartialOrd)] pub enum MainnetFork { Shanghai = 15, @@ -84,6 +87,7 @@ impl MainnetFork { } pub fn print_trace(trace: GethExecTrace) -> Result<()> { + #[cfg(any(feature = "enable-stack", feature = "enable-storage"))] fn u256_to_str(u: &U256) -> String { if *u > U256::from_str("0x1000000000000000").unwrap() { format!("0x{u:x}") @@ -91,6 +95,7 @@ pub fn print_trace(trace: GethExecTrace) -> Result<()> { u.to_string() } } + #[cfg(feature = "enable-storage")] fn kv(storage: std::collections::HashMap) -> Vec { let mut keys: Vec<_> = storage.keys().collect(); keys.sort(); @@ -135,16 +140,31 @@ pub fn print_trace(trace: GethExecTrace) -> Result<()> { "PC", "OP", "GAS", "GAS_COST", "DEPTH", "ERR", "STACK", "MEMORY", "STORAGE" ]); for step in trace.struct_logs { + #[cfg(feature = "enable-stack")] + let stack = step.stack.0.iter().map(u256_to_str).collect(); + #[cfg(not(feature = "enable-stack"))] + let stack = vec![]; + + #[cfg(feature = "enable-memory")] + let memory = step.memory.0.iter().map(ToString::to_string).collect(); + #[cfg(not(feature = "enable-memory"))] + let memory = vec![]; + + #[cfg(feature = "enable-storage")] + let storage = kv(step.storage.0); + #[cfg(not(feature = "enable-storage"))] + let storage = vec![]; + table.add_row(row![ format!("{}", step.pc.0), format!("{:?}", step.op), format!("{}", step.gas.0), format!("{}", step.gas_cost.0), format!("{}", step.depth), - step.error.unwrap_or_default(), - split(step.stack.0.iter().map(u256_to_str).collect(), 30), - split(step.memory.0.iter().map(ToString::to_string).collect(), 30), - split(kv(step.storage.0), 30) + step.error.map(|e| e.error()).unwrap_or(""), + split(stack, 30), + split(memory, 30), + split(storage, 30) ]); } @@ -183,18 +203,13 @@ pub fn current_submodule_git_commit() -> Result { pub fn bytecode_of(code: &str) -> anyhow::Result { let bytecode = if let Ok(bytes) = hex::decode(code) { - match Bytecode::try_from(bytes.clone()) { - Ok(bytecode) => { - for op in bytecode.iter() { - info!("{}", op.to_string()); - } - bytecode - } - Err(err) => { - error!("Failed to parse bytecode {:?}", err); - Bytecode::from_raw_unchecked(bytes) + let bytecode = Bytecode::from(bytes.clone()); + if log::log_enabled!(log::Level::Info) { + for op in bytecode.iter() { + info!("{}", op.to_string()); } } + bytecode } else { let mut bytecode = Bytecode::default(); for op in code.split(',') { diff --git a/testool/tests b/testool/tests index 747a4828f3..e852e4ee24 160000 --- a/testool/tests +++ b/testool/tests @@ -1 +1 @@ -Subproject commit 747a4828f36c5fc8ab4f288d1cf4f1fe6662f3d6 +Subproject commit e852e4ee241657613bb50ac08bc79b8502d75c7b diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 1fc3c10801..b766da58dd 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -14,16 +14,18 @@ array-init = "2.0.0" bus-mapping = { path = "../bus-mapping" } either = "1.9" eth-types = { path = "../eth-types" } +ff.workspace = true gadgets = { path = "../gadgets" } ethers-core.workspace = true ethers-signers = { workspace = true, optional = true } +halo2-base.workspace = true +halo2-ecc.workspace = true mock = { path = "../mock", optional = true } strum.workspace = true strum_macros.workspace = true rand_xorshift.workspace = true rand.workspace = true itertools.workspace = true -lazy_static.workspace = true mpt-zktrie = { path = "../zktrie" } keccak256 = { path = "../keccak256"} log.workspace = true @@ -32,12 +34,8 @@ serde.workspace = true serde_json.workspace = true hash-circuit.workspace = true -misc-precompiled-circuit = { package = "misc-precompiled-circuit", git = "https://github.com/scroll-tech/misc-precompiled-circuit.git", tag = "v0.1.0" } - -halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", tag = "v0.1.5", default-features=false, features=["halo2-pse","display"] } -halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", tag = "v0.1.5", default-features=false, features=["halo2-pse","display"] } - -maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_02_02" } +misc-precompiled-circuit = { package = "misc-precompiled-circuit", git = "https://github.com/scroll-tech/misc-precompiled-circuit.git", branch = "main" } +halo2_gadgets = { git = "https://github.com/scroll-tech/halo2.git", branch = "v1.0", features = ["unstable"] } num-bigint.workspace = true subtle.workspace = true @@ -46,7 +44,6 @@ snark-verifier.workspace = true snark-verifier-sdk.workspace = true hex.workspace = true rayon.workspace = true -once_cell.workspace = true [dev-dependencies] bus-mapping = { path = "../bus-mapping", features = ["test"] } @@ -58,7 +55,7 @@ cli-table = "0.4" paste = "1.0" [features] -default = ["test", "test-circuits", "shanghai", "debug-annotations", "parallel_syn"] +default = ["test", "test-circuits", "shanghai", "debug-annotations", "parallel_syn", "enable-stack", "enable-storage"] test = ["ethers-signers", "mock", "bus-mapping/test"] scroll = ["bus-mapping/scroll", "eth-types/scroll", "mock?/scroll", "zktrie", "poseidon-codehash"] @@ -73,3 +70,7 @@ poseidon-codehash = [] parallel_syn = ["hash-circuit/parallel_syn", "halo2_proofs/parallel_syn"] debug-annotations = [] + +enable-stack = ["bus-mapping/enable-stack"] +enable-memory = ["bus-mapping/enable-memory"] +enable-storage = ["bus-mapping/enable-storage"] diff --git a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs index 83a912104c..807595c4cd 100644 --- a/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs +++ b/zkevm-circuits/src/bytecode_circuit/bytecode_unroller.rs @@ -81,7 +81,7 @@ pub fn unroll_to_hash_input SubCircuitConfig for BytecodeCircuitConfig { is_byte(meta), ]); - let lookup_columns = vec![value, push_data_size]; + let lookup_columns = [value, push_data_size]; let mut constraints = vec![]; diff --git a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs index dfbee5f94a..374d20193e 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit/to_poseidon_hash.rs @@ -677,7 +677,6 @@ impl SubCircuitConfig for ToHashBlockCircuitConfig(bt.iter().copied().take(6)); assert_eq!(out.len(), 1); diff --git a/zkevm-circuits/src/bytecode_circuit/dev.rs b/zkevm-circuits/src/bytecode_circuit/dev.rs index 1f0f6f3677..e5c8a74ebe 100644 --- a/zkevm-circuits/src/bytecode_circuit/dev.rs +++ b/zkevm-circuits/src/bytecode_circuit/dev.rs @@ -28,6 +28,8 @@ pub type CircuitConfig = super::circuit::BytecodeCircuitConfig; impl Circuit for BytecodeCircuit { type Config = (CircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index ede777fd2a..340d47033b 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -364,7 +364,7 @@ impl SubCircuitConfig for CopyCircuitConfig { 0.expr(), ] .into_iter() - .zip(rw_table.table_exprs(meta).into_iter()) + .zip(rw_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -391,7 +391,7 @@ impl SubCircuitConfig for CopyCircuitConfig { 0.expr(), ] .into_iter() - .zip(rw_table.table_exprs(meta).into_iter()) + .zip(rw_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -409,7 +409,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(value, CURRENT), ] .into_iter() - .zip_eq(bytecode_table.table_exprs_mini(meta).into_iter()) + .zip_eq(bytecode_table.table_exprs_mini(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -427,7 +427,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(value, CURRENT), ] .into_iter() - .zip(tx_table.table_exprs(meta).into_iter()) + .zip(tx_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -915,7 +915,7 @@ impl CopyCircuit { Self { copy_events, max_copy_rows, - _marker: PhantomData::default(), + _marker: PhantomData, external_data: ExternalData::default(), } } @@ -929,7 +929,7 @@ impl CopyCircuit { Self { copy_events, max_copy_rows, - _marker: PhantomData::default(), + _marker: PhantomData, external_data, } } diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 239d847ecb..4aa744640d 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -14,6 +14,8 @@ use halo2_proofs::{ impl Circuit for CopyCircuit { type Config = (CopyCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/copy_circuit/test.rs b/zkevm-circuits/src/copy_circuit/test.rs index 591cb6fe9f..bbf23b7acd 100644 --- a/zkevm-circuits/src/copy_circuit/test.rs +++ b/zkevm-circuits/src/copy_circuit/test.rs @@ -528,6 +528,9 @@ fn assert_error_matches(result: Result<(), Vec>, names: Vec<&str> VerifyFailure::CellNotAssigned { .. } => panic!(), VerifyFailure::ConstraintPoisoned { .. } => panic!(), VerifyFailure::Permutation { .. } => panic!(), + // &VerifyFailure::InstanceCellNotAssigned { .. } | &VerifyFailure::Shuffle { .. } => { + // todo!() + // } } } } diff --git a/zkevm-circuits/src/ecc_circuit.rs b/zkevm-circuits/src/ecc_circuit.rs index 3e1aa471c2..a047c5dd24 100644 --- a/zkevm-circuits/src/ecc_circuit.rs +++ b/zkevm-circuits/src/ecc_circuit.rs @@ -25,7 +25,6 @@ use halo2_ecc::{ }, }; use halo2_proofs::{ - arithmetic::Field as Halo2Field, circuit::{Layouter, Value}, halo2curves::{ bn256::{Fq, Fq12, Fq2, Fr, G1Affine, G2Affine}, diff --git a/zkevm-circuits/src/ecc_circuit/test.rs b/zkevm-circuits/src/ecc_circuit/test.rs index 4c85ae4b25..4de6e9fc3b 100644 --- a/zkevm-circuits/src/ecc_circuit/test.rs +++ b/zkevm-circuits/src/ecc_circuit/test.rs @@ -164,232 +164,232 @@ mod valid_invalid_cases { use super::*; use eth_types::word; use snark_verifier::util::arithmetic::PrimeCurveAffine; + use std::sync::LazyLock; - lazy_static::lazy_static! { - pub(crate) static ref EC_ADD_OPS: Vec = { - vec![ - // 1. valid: P == Q == G1::generator - { - let p = G1Affine::generator(); - EcAddOp { - p: (U256::from(1), U256::from(2)), - q: (U256::from(1), U256::from(2)), - r: Some(p.add(&p).into()), - } - }, - // 2. invalid: P not on curve - EcAddOp { - p: (U256::from(2), U256::from(3)), - q: (U256::from(1), U256::from(2)), - r: None, - }, - // 3. valid: all zeroes - EcAddOp { - p: (U256::zero(), U256::zero()), - q: (U256::zero(), U256::zero()), - r: Some(G1Affine::identity()), - }, - // 4. invalid: Px and Py > Fq::MODULUS - EcAddOp { - p: ( - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), // p + 1 - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), // p + 2 - ), - q: (U256::from(1), U256::from(2)), - r: None, - }, - // 5. valid: P == -Q + pub(crate) static EC_ADD_OPS: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: P == Q == G1::generator + { + let p = G1Affine::generator(); EcAddOp { p: (U256::from(1), U256::from(2)), - q: ( - U256::from(1), - word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"), - ), - r: Some(G1Affine::identity()), - }, - ] - }; - pub(crate) static ref EC_MUL_OPS: Vec = { - vec![ - // 1. valid: P = G1::generator, s = 3 - EcMulOp { - p: (U256::from(1), U256::from(2)), - s: Fr::from(3), - r: Some({ - let p = G1Affine::generator(); - let s = Fr::from(3); - p.mul(s).into() - }), - }, - // 2. invalid: P = (2, 3), i.e. not on curve - EcMulOp { - p: (U256::from(2), U256::from(3)), - s: Fr::from(3), - r: None, - }, - // 3. invalid: P == (p + 1, p + 2), i.e. > Fq::MODULUS - EcMulOp { - p: ( - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), // p + 1 - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), // p + 2 - ), - s: Fr::from(3), - r: None, - }, - ] - }; - pub(crate) static ref EC_PAIRING_OPS1: Vec = { - vec![ - // 1. valid: pairing_check == 1 - { - let alpha = Fr::from(0x102030); - let beta = Fr::from(0x413121); - let point_p = G1Affine::from(G1Affine::generator() * alpha); - let point_p_negated = point_p.neg(); - let point_q = G2Affine::from(G2Affine::generator() * beta); - let point_s = G1Affine::from(G1Affine::generator() * alpha * beta); - let point_t = G2Affine::generator(); - let pairs = [ - EcPairingPair::new(point_p_negated, point_q), - EcPairingPair::new(point_s, point_t), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ]; - EcPairingOp { - pairs, - output: U256::one(), - ..Default::default() - } - }, - // 2. invalid: field element > Fq::MODULUS, mod p is OK - { - let alpha = Fr::from(0x102030); - let beta = Fr::from(0x413121); - let point_p = G1Affine::from(G1Affine::generator() * alpha); - let point_p_negated = point_p.neg(); - let point_q = G2Affine::from(G2Affine::generator() * beta); - let point_t = G2Affine::from(G2Affine::generator() * alpha * beta); - let pairs = [ - EcPairingPair::new(point_p_negated, point_q), - EcPairingPair { - g1_point: ( - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), - word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), - ), - g2_point: ( - U256::from_little_endian(&point_t.x.c1.to_bytes()), - U256::from_little_endian(&point_t.x.c0.to_bytes()), - U256::from_little_endian(&point_t.y.c1.to_bytes()), - U256::from_little_endian(&point_t.y.c0.to_bytes()), - ), - }, - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ]; - EcPairingOp { - pairs, - output: U256::zero(), - ..Default::default() - } - }, - ] - }; - pub(crate) static ref EC_PAIRING_OPS2: Vec = { - vec![ - // 3. valid: pairing_check == 0 - { - let alpha = Fr::from(0x102030); - let beta = Fr::from(0x413121); - let gamma = Fr::from(0x591242); - let point_p = G1Affine::from(G1Affine::generator() * alpha); - let point_p_negated = point_p.neg(); - let point_q = G2Affine::from(G2Affine::generator() * beta); - let point_s = G1Affine::from(G1Affine::generator() * gamma); - let point_t = G2Affine::generator(); - let pairs = [ - EcPairingPair::new(point_p_negated, point_q), - EcPairingPair::new(point_s, point_t), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ]; - EcPairingOp { - pairs, - output: U256::zero(), - ..Default::default() - } - }, - // 4. invalid: not on curve G1. - EcPairingOp { - pairs: [ - EcPairingPair { - g1_point: (U256::from(3), U256::from(4)), - g2_point: (U256::zero(), U256::zero(), U256::zero(), U256::zero()), - }, - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ], - output: 0.into(), - ..Default::default() - }, - ] - }; - pub(crate) static ref EC_PAIRING_OPS3: Vec = { - vec![ - // 5. invalid: not on curve G2. - EcPairingOp { - pairs: [ - EcPairingPair { - g1_point: (U256::zero(), U256::zero()), - g2_point: (U256::from(3), U256::from(4), U256::from(5), U256::from(6)), - }, - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ], - output: 0.into(), - ..Default::default() - }, - // 6. valid: all zero. + q: (U256::from(1), U256::from(2)), + r: Some(p.add(&p).into()), + } + }, + // 2. invalid: P not on curve + EcAddOp { + p: (U256::from(2), U256::from(3)), + q: (U256::from(1), U256::from(2)), + r: None, + }, + // 3. valid: all zeroes + EcAddOp { + p: (U256::zero(), U256::zero()), + q: (U256::zero(), U256::zero()), + r: Some(G1Affine::identity()), + }, + // 4. invalid: Px and Py > Fq::MODULUS + EcAddOp { + p: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), /* p + 1 */ + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), /* p + 2 */ + ), + q: (U256::from(1), U256::from(2)), + r: None, + }, + // 5. valid: P == -Q + EcAddOp { + p: (U256::from(1), U256::from(2)), + q: ( + U256::from(1), + word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"), + ), + r: Some(G1Affine::identity()), + }, + ] + }); + + pub(crate) static EC_MUL_OPS: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: P = G1::generator, s = 3 + EcMulOp { + p: (U256::from(1), U256::from(2)), + s: Fr::from(3), + r: Some({ + let p = G1Affine::generator(); + let s = Fr::from(3); + p.mul(s).into() + }), + }, + // 2. invalid: P = (2, 3), i.e. not on curve + EcMulOp { + p: (U256::from(2), U256::from(3)), + s: Fr::from(3), + r: None, + }, + // 3. invalid: P == (p + 1, p + 2), i.e. > Fq::MODULUS + EcMulOp { + p: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), /* p + 1 */ + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), /* p + 2 */ + ), + s: Fr::from(3), + r: None, + }, + ] + }); + pub(crate) static EC_PAIRING_OPS1: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: pairing_check == 1 + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_s = G1Affine::from(G1Affine::generator() * alpha * beta); + let point_t = G2Affine::generator(); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair::new(point_s, point_t), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; EcPairingOp { - pairs: [ - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ], - output: 1.into(), + pairs, + output: U256::one(), ..Default::default() - }, - ] - }; - pub(crate) static ref EC_PAIRING_OPS4: Vec = { - vec![ - // 7. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen)] + } + }, + // 2. invalid: field element > Fq::MODULUS, mod p is OK + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_t = G2Affine::from(G2Affine::generator() * alpha * beta); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair { + g1_point: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), + ), + g2_point: ( + U256::from_little_endian(&point_t.x.c1.to_bytes()), + U256::from_little_endian(&point_t.x.c0.to_bytes()), + U256::from_little_endian(&point_t.y.c1.to_bytes()), + U256::from_little_endian(&point_t.y.c0.to_bytes()), + ), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; EcPairingOp { - pairs: [ - EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), - EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), - EcPairingPair::padding_pair(), - EcPairingPair::padding_pair(), - ], - output: 1.into(), + pairs, + output: U256::zero(), ..Default::default() - }, - // 8. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen); 2] + } + }, + ] + }); + pub(crate) static EC_PAIRING_OPS2: LazyLock> = LazyLock::new(|| { + vec![ + // 3. valid: pairing_check == 0 + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let gamma = Fr::from(0x591242); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_s = G1Affine::from(G1Affine::generator() * gamma); + let point_t = G2Affine::generator(); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair::new(point_s, point_t), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; EcPairingOp { - pairs: [ - EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), - EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), - EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), - EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), - ], - output: 1.into(), + pairs, + output: U256::zero(), ..Default::default() - }, - ] - }; - } + } + }, + // 4. invalid: not on curve G1. + EcPairingOp { + pairs: [ + EcPairingPair { + g1_point: (U256::from(3), U256::from(4)), + g2_point: (U256::zero(), U256::zero(), U256::zero(), U256::zero()), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 0.into(), + ..Default::default() + }, + ] + }); + pub(crate) static EC_PAIRING_OPS3: LazyLock> = LazyLock::new(|| { + vec![ + // 5. invalid: not on curve G2. + EcPairingOp { + pairs: [ + EcPairingPair { + g1_point: (U256::zero(), U256::zero()), + g2_point: (U256::from(3), U256::from(4), U256::from(5), U256::from(6)), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 0.into(), + ..Default::default() + }, + // 6. valid: all zero. + EcPairingOp { + pairs: [ + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 1.into(), + ..Default::default() + }, + ] + }); + pub(crate) static EC_PAIRING_OPS4: LazyLock> = LazyLock::new(|| { + vec![ + // 7. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen)] + EcPairingOp { + pairs: [ + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 1.into(), + ..Default::default() + }, + // 8. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen); 2] + EcPairingOp { + pairs: [ + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + ], + output: 1.into(), + ..Default::default() + }, + ] + }); } #[test] @@ -473,7 +473,6 @@ fn test_invalid_ec_add() { }) }; let ec_adds = (0..50) - .into_iter() .map(|_| { let px = u256_max - (rng.next_u64() % 1024); let x = Fq::from_raw(px.0); @@ -503,7 +502,6 @@ fn test_invalid_ec_add() { // 2. p is on g1 but p.x[i] is close to Fq::MODULUS.limbs[i] and p.y < p // and q is generator let ec_adds = (0..50) - .into_iter() .map(|_| { let fq_limbs: [u64; 4] = [ 0x3c208c16d87cfd47, diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 4ab4d64b49..7c1dd27b5e 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -29,7 +29,8 @@ use crate::{ evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, - KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SigTable, TxTable, + KeccakTable, LookupTable, ModExpTable, PowOfRandTable, RwTable, SHA256Table, SigTable, + TxTable, }, util::{assign_global, SubCircuit, SubCircuitConfig}, }; @@ -175,6 +176,7 @@ impl EvmCircuitConfig { )?; } } + Ok(()) }, ) @@ -257,11 +259,7 @@ impl EvmCircuit { } } -// the diff is from the num of valid opcodes: self-destruct? -#[cfg(not(feature = "scroll"))] const FIXED_TABLE_ROWS_NO_BITWISE: usize = 3647; -#[cfg(feature = "scroll")] -const FIXED_TABLE_ROWS_NO_BITWISE: usize = 3646; const FIXED_TABLE_ROWS: usize = FIXED_TABLE_ROWS_NO_BITWISE + 3 * 65536; impl SubCircuit for EvmCircuit { @@ -373,22 +371,20 @@ pub(crate) fn detect_fixed_table_tags(block: &Block) -> Vec, config: (EvmCircuitConfig, Challenges), } - lazy_static! { - /// Cached values of the ConstraintSystem after the EVM Circuit configuration and the EVM - /// Circuit configuration. These values are calculated just once. - static ref CACHE: Cache = { - let mut meta = ConstraintSystem::::default(); - let config = EvmCircuit::::configure(&mut meta); - Cache { cs: meta, config } - }; - } + /// Cached values of the ConstraintSystem after the EVM Circuit configuration and the EVM + /// Circuit configuration. These values are calculated just once. + static CACHE: LazyLock = LazyLock::new(|| { + let mut meta = ConstraintSystem::::default(); + let config = EvmCircuit::::configure(&mut meta); + Cache { cs: meta, config } + }); /// Wrapper over the EvmCircuit that behaves the same way and also /// implements the halo2 Circuit trait, but reuses the precalculated @@ -445,12 +441,15 @@ impl Circuit for EvmCircuit { BlockTable, CopyTable, KeccakTable, + SHA256Table, ExpTable, SigTable, ModExpTable, EccTable, ); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -477,6 +476,7 @@ impl Circuit for EvmCircuit { copy_table.annotate_columns(meta); let keccak_table = KeccakTable::construct(meta); keccak_table.annotate_columns(meta); + let sha256_table = SHA256Table::construct(meta); let exp_table = ExpTable::construct(meta); exp_table.annotate_columns(meta); let sig_table = SigTable::construct(meta); @@ -531,6 +531,7 @@ impl Circuit for EvmCircuit { block_table, copy_table, keccak_table, + sha256_table, exp_table, sig_table, modexp_table, @@ -557,6 +558,7 @@ impl Circuit for EvmCircuit { block_table, copy_table, keccak_table, + sha256_table, exp_table, sig_table, modexp_table, @@ -588,6 +590,14 @@ impl Circuit for EvmCircuit { block_table.dev_load(&mut layouter, &block.context, &block.txs, &challenges)?; copy_table.dev_load(&mut layouter, block, &challenges)?; keccak_table.dev_load(&mut layouter, &block.sha3_inputs, &challenges)?; + sha256_table.dev_load( + &mut layouter, + block + .get_sha256() + .iter() + .map(|evt| (&evt.input, &evt.digest)), + &challenges, + )?; exp_table.dev_load(&mut layouter, block)?; sig_table.dev_load(&mut layouter, block, &challenges)?; modexp_table.dev_load(&mut layouter, &block.get_big_modexp())?; diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index e56d7783bc..9a0d2d7e6b 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -32,7 +32,6 @@ use gadgets::{ util::not, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, Region, Value}, plonk::{ Advice, Assigned, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, Selector, @@ -40,9 +39,11 @@ use halo2_proofs::{ }, poly::Rotation, }; +use itertools::Itertools; use std::{ collections::{BTreeSet, HashMap}, iter, + sync::LazyLock, }; #[cfg(feature = "onephase")] @@ -55,10 +56,8 @@ use halo2_proofs::plonk::SecondPhase; use halo2_proofs::plonk::ThirdPhase; use strum::{EnumCount, IntoEnumIterator}; - -use once_cell::sync::Lazy; -pub(crate) static CHECK_RW_LOOKUP: Lazy = - Lazy::new(|| read_env_var("CHECK_RW_LOOKUP", false)); +pub(crate) static CHECK_RW_LOOKUP: LazyLock = + LazyLock::new(|| read_env_var("CHECK_RW_LOOKUP", false)); mod add_sub; mod addmod; @@ -150,6 +149,8 @@ use address::AddressGadget; use balance::BalanceGadget; use begin_tx::BeginTxGadget; use bitwise::BitwiseGadget; +#[cfg(feature = "scroll")] +use block_ctx::DifficultyGadget; use block_ctx::{BlockCtxU160Gadget, BlockCtxU256Gadget, BlockCtxU64Gadget}; use blockhash::BlockHashGadget; use byte::ByteGadget; @@ -211,6 +212,7 @@ use pc::PcGadget; use pop::PopGadget; use precompiles::{ EcAddGadget, EcMulGadget, EcPairingGadget, EcrecoverGadget, IdentityGadget, ModExpGadget, + SHA256Gadget, }; use push::PushGadget; use return_revert::ReturnRevertGadget; @@ -227,7 +229,7 @@ use sstore::SstoreGadget; use stop::StopGadget; use swap::SwapGadget; -pub(crate) trait ExecutionGadget { +pub(crate) trait ExecutionGadget { const NAME: &'static str; const EXECUTION_STATE: ExecutionState; @@ -250,7 +252,7 @@ pub(crate) struct ExecutionConfig { // EVM Circuit selector, which enables all usable rows. The rows where this selector is // disabled won't verify any constraint (they can be unused rows or rows with blinding // factors). - q_usable: Selector, + q_usable: Column, // Dynamic selector that is enabled at the rows where each assigned execution step starts (a // step has dynamic height). q_step: Column, @@ -334,6 +336,8 @@ pub(crate) struct ExecutionConfig { block_ctx_u64_gadget: Box>, block_ctx_u160_gadget: Box>, block_ctx_u256_gadget: Box>, + #[cfg(feature = "scroll")] + difficulty_gadget: Box>, // error gadgets error_oog_call: Box>, error_oog_precompile: Box>, @@ -360,7 +364,7 @@ pub(crate) struct ExecutionConfig { error_return_data_out_of_bound: Box>, // precompile calls precompile_ecrecover_gadget: Box>, - precompile_sha2_gadget: Box>, + precompile_sha2_gadget: Box>, precompile_ripemd_gadget: Box>, precompile_identity_gadget: Box>, precompile_modexp_gadget: Box>, @@ -378,7 +382,7 @@ impl ExecutionConfig { bus_builder: &mut BusBuilder>, ) -> Self { let mut instrument = Instrument::default(); - let q_usable = meta.complex_selector(); + let q_usable = meta.fixed_column(); let q_step = meta.advice_column(); let constants = meta.fixed_column(); meta.enable_constant(constants); @@ -407,7 +411,7 @@ impl ExecutionConfig { let mut height_map = HashMap::new(); meta.create_gate("Constrain execution state", |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step = meta.query_advice(q_step, Rotation::cur()); let q_step_first = meta.query_selector(q_step_first); let q_step_last = meta.query_selector(q_step_last); @@ -441,7 +445,7 @@ impl ExecutionConfig { }); meta.create_gate("q_step", |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step_first = meta.query_selector(q_step_first); let q_step_last = meta.query_selector(q_step_last); let q_step = meta.query_advice(q_step, Rotation::cur()); @@ -605,6 +609,8 @@ impl ExecutionConfig { block_ctx_u64_gadget: configure_gadget!(), block_ctx_u160_gadget: configure_gadget!(), block_ctx_u256_gadget: configure_gadget!(), + #[cfg(feature = "scroll")] + difficulty_gadget: configure_gadget!(), // error gadgets error_oog_constant: configure_gadget!(), error_oog_static_memory_gadget: configure_gadget!(), @@ -656,7 +662,7 @@ impl ExecutionConfig { meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, advices: [Column; STEP_WIDTH], - q_usable: Selector, + q_usable: Column, q_step: Column, num_rows_until_next_step: Column, q_step_first: Selector, @@ -721,7 +727,7 @@ impl ExecutionConfig { fn configure_gadget_impl( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, - q_usable: Selector, + q_usable: Column, q_step: Column, num_rows_until_next_step: Column, q_step_first: Selector, @@ -765,7 +771,7 @@ impl ExecutionConfig { { let step_enabled = query_expression(meta, |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step = meta.query_advice(q_step, Rotation::cur()); q_usable * q_step * state_selector.clone() }); @@ -794,7 +800,7 @@ impl ExecutionConfig { ] { if !constraints.is_empty() { meta.create_gate(name, |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let selector = selector(meta); constraints.into_iter().map(move |(name, constraint)| { ( @@ -811,7 +817,7 @@ impl ExecutionConfig { // Enforce the state transitions for this opcode meta.create_gate("Constrain state machine transitions", |meta| { - let q_usable = meta.query_selector(q_usable); + let q_usable = meta.query_fixed(q_usable, Rotation::cur()); let q_step = meta.query_advice(q_step, Rotation::cur()); let q_step_last = meta.query_selector(q_step_last); @@ -912,10 +918,10 @@ impl ExecutionConfig { fn configure_bytes_port( meta: &mut ConstraintSystem, bus_builder: &mut BusBuilder>, - q_usable: Selector, + q_usable: Column, cell_manager: &CellManager, ) -> BusPortMulti { - let q_usable = query_expression(meta, |meta| meta.query_selector(q_usable)); + let q_usable = query_expression(meta, |meta| meta.query_fixed(q_usable, Rotation::cur())); let byte_columns = { let mut cols = cell_manager @@ -941,6 +947,15 @@ impl ExecutionConfig { BusPortMulti::connect(meta, bus_builder, q_usable, ops) } + pub fn get_num_rows_required_no_padding(&self, block: &Block) -> usize { + let mut num_rows = 0; + for transaction in &block.txs { + for step in &transaction.steps { + num_rows += step.execution_state.get_step_height(); + } + } + num_rows + } pub fn get_num_rows_required(&self, block: &Block) -> usize { // Start at 1 so we can be sure there is an unused `next` row available let mut num_rows = 1; @@ -969,7 +984,12 @@ impl ExecutionConfig { // Name Advice columns for idx in 0..height { let offset = offset + idx; - self.q_usable.enable(region, offset)?; + region.assign_fixed( + || "q_usable selector", + self.q_usable, + offset, + || Value::known(F::one()), + )?; region.assign_advice( || "step selector", self.q_step, @@ -1003,158 +1023,322 @@ impl ExecutionConfig { block: &Block, challenges: &Challenges>, ) -> Result>, Error> { - let mut is_first_time = true; + // If the height is not 1, padding to fixed height will be impossible + debug_assert_eq!(ExecutionState::EndBlock.get_step_height(), 1); - layouter.assign_region( - || "Execution step", - |mut region| { - if is_first_time { - is_first_time = false; - region.assign_advice( - || "step selector", - self.q_step, - self.get_num_rows_required(block) - 1, - || Value::known(F::zero()), - )?; - return Ok(()); - } + let inverter = Inverter::new(MAX_STEP_HEIGHT as u64); + let evm_rows = block.circuits_params.max_evm_rows; + // 0 means "dynamic height". If fixed height is used in unittests, CI will be quite slow. + let no_padding = evm_rows == 0; + + // There should be 3 group of regions + // 1. real steps + // 2. padding EndBlocks. For the ease of implementation, even for `no_padding` case, we will + // still pad 1 end_block_not_last. + // 3. final EndBlock + let region1_height = self.get_num_rows_required_no_padding(block); + let region3_height = 2; // EndBlock, plus a dummy "next" row used for Rotation + let region2_height = if no_padding { + 1 + } else { + if region1_height + region3_height >= evm_rows { + log::error!( + "evm circuit row not enough, region1_height:{}, region3_height:{}, max_evm_rows:{}", + region1_height, + region3_height, + evm_rows + ); + return Err(Error::Synthesis); + } + evm_rows - region3_height - region1_height + }; + + // A quick path for "reporting" height for the halo2 first pass layouter. + let assign_shape_fn = |region: &mut Region<'_, F>, height| { + region.assign_advice( + || "step selector", + self.q_step, + height - 1, + || Value::known(F::zero()), + ) + }; - let mut offset = 0; - - let inverter = Inverter::new(MAX_STEP_HEIGHT as u64); - - // Annotate the EVMCircuit columns within it's single region. - self.annotate_circuit(&mut region); - - self.q_step_first.enable(&mut region, offset)?; - - let dummy_tx = Transaction::default(); - let last_call = block - .txs - .last() - .map(|tx| tx.calls[0].clone()) - .unwrap_or_else(Call::default); - let end_block_not_last = &block.end_block_not_last; - let end_block_last = &block.end_block_last; - // Collect all steps - let mut steps = block - .txs - .iter() - .flat_map(|tx| { - tx.steps - .iter() - .map(move |step| (tx, &tx.calls[step.call_index], step)) + let dummy_tx = Transaction::default(); + let last_call = block + .txs + .last() + .map(|tx| tx.calls[0].clone()) + .unwrap_or_default(); + let end_block_not_last = &block.end_block_not_last; + let end_block_last = &block.end_block_last; + + // A helper struct used for parallel assignment + struct StepAssignment { + tx_idx: usize, + step_idx_in_tx: usize, + height: usize, + offset: usize, + } + let total_step_num = block.txs.iter().map(|t| t.steps.len()).sum::(); + let mut step_assignments: Vec = Vec::with_capacity(total_step_num); + + // the "global offset" + let mut offset = 0; + for (tx_idx, tx) in block.txs.iter().enumerate() { + for (step_idx, step) in tx.steps.iter().enumerate() { + let height = step.execution_state.get_step_height(); + step_assignments.push(StepAssignment { + tx_idx, + step_idx_in_tx: step_idx, + offset, + height, + }); + offset += height; + } + } + assert_eq!(offset, region1_height); + offset = 0; + + // Print some logs after each tx, for debugging + let log_step_fn = |transaction: &Transaction, step: &ExecStep, offset| { + if step.execution_state == ExecutionState::EndTx { + let mut tx = transaction.clone(); + tx.call_data.clear(); + tx.calls.clear(); + tx.steps.clear(); + tx.rlp_signed.clear(); + tx.rlp_unsigned.clear(); + let total_gas = { + let gas_used = tx.gas - step.gas_left; + let current_cumulative_gas_used: u64 = if tx.id == 1 { + 0 + } else { + // first transaction needs TxReceiptFieldTag::COUNT(3) lookups + // to tx receipt, + // while later transactions need 4 (with one extra cumulative + // gas read) lookups + let rw = &block.rws[( + RwTableTag::TxReceipt, + (tx.id - 2) * (TxReceiptFieldTag::COUNT + 1) + 2, + )]; + rw.receipt_value() + }; + current_cumulative_gas_used + gas_used + }; + log::info!( + "offset {} tx_num {} total_gas {} assign last step {:?} of tx {:?}", + offset, + tx.id, + total_gas, + step, + tx + ); + } + }; + + // Calculate chunk_size and chunk_num + // Here a min_chunk_size is provided to reduce threading overhead + let chunking_fn = |name: &str, task_len: usize, min_chunk_size: usize| -> (usize, usize) { + if task_len == 0 { + return (0, 0); + } + let num_threads = std::thread::available_parallelism() + .map(|e| e.get()) + .unwrap_or(1); + //let num_threads = 1; + let chunk_size = ((task_len + num_threads - 1) / num_threads).max(min_chunk_size); + let chunk_num = (task_len + chunk_size - 1) / chunk_size; + log::debug!( + "{} chunking: len = {}, num_threads = {}, chunk_size = {}, chunk_num = {}", + name, + task_len, + num_threads, + chunk_size, + chunk_num + ); + (chunk_size, chunk_num) + }; + + // Step1: assign real steps + { + // Prepare the thread states. + let (region1_chunk_size, region1_chunk_num) = + chunking_fn("region1", step_assignments.len(), 50); + let mut region1_is_first_time: Vec<(usize, bool)> = (0..region1_chunk_num) + .map(|chunk_idx| (chunk_idx, true)) + .collect(); + // Assign in parallel. + let bus_assigner_forks = layouter.assign_regions( + || "Execution step region1", + region1_is_first_time + .iter_mut() + .map(|(chunk_idx, is_first_time)| { + |mut region: Region<'_, F>| { + let chunk_idx = *chunk_idx; + let begin = chunk_idx * region1_chunk_size; + let end = + ((chunk_idx + 1) * region1_chunk_size).min(step_assignments.len()); + let step_idxs: Vec = (begin..end).collect(); + log::trace!("region1 range {} {} {}", chunk_idx, begin, end); + let total_height = step_idxs + .iter() + .map(|idx| step_assignments[*idx].height) + .sum::(); + if *is_first_time { + *is_first_time = false; + assign_shape_fn(&mut region, total_height)?; + return Ok(None); + } + let mut offset = 0; + + let mut bus_assigner_fork = + bus_assigner.fork(region.global_offset(0), total_height); + + // Annotate the EVMCircuit columns within it's single region. + self.annotate_circuit(&mut region); + + if chunk_idx == 0 { + self.q_step_first.enable(&mut region, offset)?; + } + for step_idx in step_idxs { + let step_assignment = &step_assignments[step_idx]; + let transaction = &block.txs[step_assignment.tx_idx]; + let step = &transaction.steps[step_assignment.step_idx_in_tx]; + let call = &transaction.calls[step.call_index]; + + let height = step.execution_state.get_step_height(); + + log_step_fn(transaction, step, offset); + + let next = match step_assignments.get(step_idx + 1) { + None => (&dummy_tx, &last_call, end_block_not_last), + Some(step_assignment) => { + let transaction = &block.txs[step_assignment.tx_idx]; + let step = + &transaction.steps[step_assignment.step_idx_in_tx]; + let call = &transaction.calls[step.call_index]; + (transaction, call, step) + } + }; + + self.assign_exec_step( + &mut region, + &mut bus_assigner_fork, + offset, + block, + transaction, + call, + step, + height, + Some(next), + challenges, + )?; + + self.assign_q_step(&mut region, &inverter, offset, height)?; + + offset += height; + } + + bus_assigner_fork.finish_ports(&mut region); + debug_assert_eq!(offset, total_height); + Ok(Some(bus_assigner_fork)) + } }) - .chain(std::iter::once((&dummy_tx, &last_call, end_block_not_last))) - .peekable(); - - let evm_rows = block.circuits_params.max_evm_rows; - let no_padding = evm_rows == 0; - - // part1: assign real steps - loop { - let (transaction, call, step) = steps.next().expect("should not be empty"); - let next = steps.peek(); - if next.is_none() { - break; - } - let height = step.execution_state.get_step_height(); - - // Assign the step witness - if step.execution_state == ExecutionState::EndTx { - let mut tx = transaction.clone(); - tx.call_data.clear(); - tx.calls.clear(); - tx.steps.clear(); - tx.rlp_signed.clear(); - tx.rlp_unsigned.clear(); - let total_gas = { - let gas_used = tx.gas - step.gas_left; - let current_cumulative_gas_used: u64 = if tx.id == 1 { - 0 - } else { - // first transaction needs TxReceiptFieldTag::COUNT(3) lookups - // to tx receipt, - // while later transactions need 4 (with one extra cumulative - // gas read) lookups - let rw = &block.rws[( - RwTableTag::TxReceipt, - (tx.id - 2) * (TxReceiptFieldTag::COUNT + 1) + 2, - )]; - rw.receipt_value() - }; - current_cumulative_gas_used + gas_used - }; - log::info!( - "offset {} tx_num {} total_gas {} assign last step {:?} of tx {:?}", - offset, - tx.id, - total_gas, - step, - tx - ); - } - self.assign_exec_step( - &mut region, - bus_assigner, - offset, - block, - transaction, - call, - step, - height, - next.copied(), - challenges, - )?; - - // q_step logic - self.assign_q_step(&mut region, &inverter, offset, height)?; - - offset += height; - } + .collect_vec(), + )?; + // Merge the thread results. + let mut actual_offset = 0; + for bus_assigner_fork in bus_assigner_forks { + let bus_assigner_fork = bus_assigner_fork.unwrap(); - // part2: assign non-last EndBlock steps when padding needed - if !no_padding { - let height = ExecutionState::EndBlock.get_step_height(); - debug_assert_eq!(height, 1); - // 1 for EndBlock(last), 1 for "part 4" cells - let last_row = evm_rows - 2; - log::trace!( - "assign non-last EndBlock in range [{},{})", - offset, - last_row - ); - if offset > last_row { - log::error!( - "evm circuit row not enough, offset: {}, max_evm_rows: {}", - offset, - evm_rows - ); - return Err(Error::Synthesis); - } - self.assign_same_exec_step_in_range( - &mut region, - bus_assigner, - offset, - last_row, - block, - &dummy_tx, - &last_call, - end_block_not_last, - height, - challenges, - )?; - - for row_idx in offset..last_row { - self.assign_q_step(&mut region, &inverter, row_idx, height)?; - } - offset = last_row; - } + // Validate the offsets found by assign_regions. + assert_eq!(actual_offset, bus_assigner_fork.start_offset()); + actual_offset += bus_assigner_fork.n_rows(); + + bus_assigner.merge(bus_assigner_fork); + } + assert_eq!(region1_height, actual_offset); + } - // part3: assign the last EndBlock at offset `evm_rows - 1` - let height = ExecutionState::EndBlock.get_step_height(); - debug_assert_eq!(height, 1); - log::trace!("assign last EndBlock at offset {}", offset); + // part2: assign non-last EndBlock steps when padding needed + { + // Prepare the thread states. + let (region2_chunk_size, region2_chunk_num) = + chunking_fn("region2", region2_height, 300); + let idxs: Vec = (0..region2_height).collect(); + let mut region2_is_first_time = vec![true; region2_chunk_num]; + + log::trace!( + "assign non-last EndBlock in range [{},{})", + region1_height, + region1_height + region2_height + ); + let bus_assigner_forks = layouter.assign_regions( + || "Execution step region2", + idxs.chunks(region2_chunk_size) + .zip_eq(region2_is_first_time.iter_mut()) + .map(|(rows, is_first_time)| { + |mut region: Region<'_, F>| { + if *is_first_time { + *is_first_time = false; + assign_shape_fn(&mut region, rows.len())?; + return Ok(None); + } + + let mut bus_assigner_fork = + bus_assigner.fork(region.global_offset(0), rows.len()); + + self.assign_same_exec_step_in_range( + &mut region, + &mut bus_assigner_fork, + 0, + rows.len(), + block, + &dummy_tx, + &last_call, + end_block_not_last, + 1, + challenges, + )?; + for row_idx in 0..rows.len() { + self.assign_q_step(&mut region, &inverter, row_idx, 1)?; + } + bus_assigner_fork.finish_ports(&mut region); + Ok(Some(bus_assigner_fork)) + } + }) + .collect_vec(), + )?; + // Merge the thread results. + let mut actual_offset = region1_height; + for bus_assigner_fork in bus_assigner_forks { + let bus_assigner_fork = bus_assigner_fork.unwrap(); + + // Validate the offsets found by assign_regions. + assert_eq!(actual_offset, bus_assigner_fork.start_offset()); + actual_offset += bus_assigner_fork.n_rows(); + + bus_assigner.merge(bus_assigner_fork); + } + assert_eq!(region1_height + region2_height, actual_offset); + } + + // part3: assign the last EndBlock at offset `evm_rows - 1` + // This region don't need to be parallelized + log::trace!( + "assign last EndBlock at offset {}", + region1_height + region2_height + ); + + let mut region3_is_first_time = true; + layouter.assign_region( + || "Execution step region3", + |mut region| { + if region3_is_first_time { + region3_is_first_time = false; + assign_shape_fn(&mut region, region3_height)?; + return Ok(()); + } self.assign_exec_step( &mut region, bus_assigner, @@ -1163,33 +1347,29 @@ impl ExecutionConfig { &dummy_tx, &last_call, end_block_last, - height, + 1, None, challenges, )?; - self.assign_q_step(&mut region, &inverter, offset, height)?; - // enable q_step_last + self.assign_q_step(&mut region, &inverter, offset, 1)?; self.q_step_last.enable(&mut region, offset)?; - offset += height; - - // part4: // These are still referenced (but not used) in next rows region.assign_advice( || "step height", self.num_rows_until_next_step, - offset, + 1, || Value::known(F::zero()), )?; region.assign_advice( || "step height inv", self.q_step, - offset, + 1, || Value::known(F::zero()), )?; bus_assigner.finish_ports(&mut region); - log::debug!("assign for region done at offset {}", offset); + assert_eq!(region.global_offset(0), region1_height + region2_height); Ok(()) }, )?; @@ -1199,15 +1379,10 @@ impl ExecutionConfig { let final_withdraw_root_cell = self .end_block_gadget .withdraw_root_assigned - .borrow() + .lock() + .unwrap() .expect("withdraw_root cell should has been assigned"); - // sanity check - let evm_rows = block.circuits_params.max_evm_rows; - if evm_rows >= 2 { - assert_eq!(final_withdraw_root_cell.row_offset, evm_rows - 2); - } - let withdraw_root_rlc = challenges .evm_word() .map(|r| rlc::value(&block.withdraw_root.to_le_bytes(), r)); @@ -1320,6 +1495,7 @@ impl ExecutionConfig { challenges, self.advices.to_vec(), 1, + 1, offset_begin, ); self.assign_exec_step_int(region, offset_begin, block, transaction, call, step, false)?; @@ -1362,6 +1538,7 @@ impl ExecutionConfig { challenges, self.advices.to_vec(), MAX_STEP_HEIGHT * 3, + height, offset, ); @@ -1475,6 +1652,8 @@ impl ExecutionConfig { ExecutionState::BLOCKCTXU64 => assign_exec_step!(self.block_ctx_u64_gadget), ExecutionState::BLOCKCTXU160 => assign_exec_step!(self.block_ctx_u160_gadget), ExecutionState::BLOCKCTXU256 => assign_exec_step!(self.block_ctx_u256_gadget), + #[cfg(feature = "scroll")] + ExecutionState::DIFFICULTY => assign_exec_step!(self.difficulty_gadget), ExecutionState::BLOCKHASH => assign_exec_step!(self.blockhash_gadget), ExecutionState::SELFBALANCE => assign_exec_step!(self.selfbalance_gadget), ExecutionState::CREATE => assign_exec_step!(self.create_gadget), diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index 16e1222b8f..05c383d313 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -240,7 +240,7 @@ mod test { let mut ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(); if let Some(r) = r { - let mut last = ctx + let last = ctx .geth_traces .first_mut() .unwrap() diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index a443e35d2b..3a1995d664 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -148,12 +148,11 @@ impl ExecutionGadget for BalanceGadget { mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; use eth_types::{address, bytecode, geth_types::Account, Address, Bytecode, Word, U256}; - use lazy_static::lazy_static; use mock::{generate_mock_call_bytecode, test_ctx::TestContext, MockCallBytecodeParams}; + use std::sync::LazyLock; - lazy_static! { - static ref TEST_ADDRESS: Address = address!("0xaabbccddee000000000000000000000000000000"); - } + static TEST_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0xaabbccddee000000000000000000000000000000")); #[test] fn balance_gadget_non_existing_account() { diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index d52a49f43c..58a21c0ff9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -6,7 +6,8 @@ use crate::{ util::{ and, common_gadget::{ - TransferGadgetInfo, TransferWithGasFeeGadget, TxL1FeeGadget, TxL1MsgGadget, + TransferGadgetInfo, TransferWithGasFeeGadget, TxEip2930Gadget, TxL1FeeGadget, + TxL1MsgGadget, }, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, @@ -43,6 +44,7 @@ const PRECOMPILE_COUNT: usize = 9; #[derive(Clone, Debug)] pub(crate) struct BeginTxGadget { tx_id: Cell, + tx_type: Cell, sender_nonce: Cell, tx_nonce: Cell, tx_gas: Cell, @@ -90,6 +92,7 @@ pub(crate) struct BeginTxGadget { is_coinbase_warm: Cell, tx_l1_fee: TxL1FeeGadget, tx_l1_msg: TxL1MsgGadget, + tx_eip2930: TxEip2930Gadget, } impl ExecutionGadget for BeginTxGadget { @@ -102,10 +105,11 @@ impl ExecutionGadget for BeginTxGadget { let call_id = cb.curr.state.rw_counter.clone(); let tx_id = cb.query_cell(); - let sender_nonce = cb.query_cell(); - let [tx_nonce, tx_gas, tx_caller_address, tx_callee_address, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, tx_data_gas_cost] = + + let [tx_type, tx_nonce, tx_gas, tx_caller_address, tx_callee_address, tx_is_create, tx_call_data_length, tx_call_data_gas_cost, tx_data_gas_cost] = [ + TxContextFieldTag::TxType, TxContextFieldTag::Nonce, TxContextFieldTag::Gas, TxContextFieldTag::CallerAddress, @@ -117,9 +121,10 @@ impl ExecutionGadget for BeginTxGadget { ] .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + let tx_eip2930 = TxEip2930Gadget::construct(cb, tx_id.expr(), tx_type.expr()); let is_call_data_empty = IsZeroGadget::construct(cb, tx_call_data_length.expr()); - let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_id.expr(), tx_caller_address.expr()); + let tx_l1_msg = TxL1MsgGadget::construct(cb, tx_type.expr(), tx_caller_address.expr()); let tx_l1_fee = cb.condition(not::expr(tx_l1_msg.is_l1_msg()), |cb| { cb.require_equal( "tx.nonce == sender.nonce", @@ -255,6 +260,7 @@ impl ExecutionGadget for BeginTxGadget { eth_types::evm_types::GasCost::CREATION_TX.expr(), eth_types::evm_types::GasCost::TX.expr(), ) + tx_call_data_gas_cost.expr() + + tx_eip2930.gas_cost() + init_code_gas_cost, ) }); @@ -672,6 +678,7 @@ impl ExecutionGadget for BeginTxGadget { Self { tx_id, + tx_type, tx_nonce, sender_nonce, tx_gas, @@ -712,6 +719,7 @@ impl ExecutionGadget for BeginTxGadget { is_coinbase_warm, tx_l1_fee, tx_l1_msg, + tx_eip2930, } } @@ -732,7 +740,8 @@ impl ExecutionGadget for BeginTxGadget { let mut rws = StepRws::new(block, step); - let caller_code_hash = if tx.tx_type.is_l1_msg() { + let tx_type = tx.tx_type; + let caller_code_hash = if tx_type.is_l1_msg() { let caller_code_hash_pair = rws.next().account_codehash_pair(); assert_eq!( caller_code_hash_pair.0, caller_code_hash_pair.1, @@ -743,7 +752,7 @@ impl ExecutionGadget for BeginTxGadget { U256::zero() }; self.tx_l1_msg - .assign(region, offset, tx.tx_type, caller_code_hash)?; + .assign(region, offset, tx_type, caller_code_hash)?; ////////////// RWS //////////////// // if L1: @@ -763,7 +772,7 @@ impl ExecutionGadget for BeginTxGadget { // caller addr // callee addr // coinbase - rws.offset_add(if tx.tx_type.is_l1_msg() { + rws.offset_add(if tx_type.is_l1_msg() { if caller_code_hash.is_zero() { assert_eq!( tx.nonce, 0, @@ -846,6 +855,8 @@ impl ExecutionGadget for BeginTxGadget { self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; + self.tx_type + .assign(region, offset, Value::known(F::from(tx_type as u64)))?; self.tx_nonce .assign(region, offset, Value::known(F::from(tx.nonce)))?; self.sender_nonce @@ -936,7 +947,7 @@ impl ExecutionGadget for BeginTxGadget { stream.append(&tx.caller_address); stream.append(ð_types::U256::from(tx.nonce)); let rlp_encoding = stream.out().to_vec(); - keccak256(&rlp_encoding) + keccak256(rlp_encoding) }; for (c, v) in self .caller_nonce_hash_bytes @@ -975,7 +986,7 @@ impl ExecutionGadget for BeginTxGadget { stream.append(&tx.caller_address); stream.append(ð_types::U256::from(tx.nonce)); let rlp_encoding = stream.out().to_vec(); - keccak256(&rlp_encoding) + keccak256(rlp_encoding) }; for (c, v) in self .caller_nonce_hash_bytes @@ -1044,7 +1055,9 @@ impl ExecutionGadget for BeginTxGadget { tx.l1_fee, tx.l1_fee_committed, tx.tx_data_gas_cost, - ) + )?; + + self.tx_eip2930.assign(region, offset, tx) } } diff --git a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs index 58e06efd22..a388529a3f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs @@ -147,13 +147,16 @@ impl ExecutionGadget for BlockCtxU160Gadget { } } -#[cfg(not(feature = "scroll"))] +// TODO: +// This gadget is used for `BASEFEE` opcode. With current `scroll` feature, it's +// disabled by l2geth and converted to an invalid opcode. +// +// So need to test it after `BASEFEE` opcode is enabled in scroll l2geth. #[derive(Clone, Debug)] pub(crate) struct BlockCtxU256Gadget { value_u256: BlockCtxGadget, } -#[cfg(not(feature = "scroll"))] impl ExecutionGadget for BlockCtxU256Gadget { const NAME: &'static str = "BLOCKCTXU256"; @@ -174,6 +177,11 @@ impl ExecutionGadget for BlockCtxU256Gadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { + if cfg!(feature = "scroll") { + panic!("BASEFEE is disabled by scroll for now"); + } + log::debug!("BlockCtxU256Gadget assign for {:?}", step.opcode); + self.value_u256 .same_context .assign_exec_step(region, offset, step)?; @@ -188,17 +196,20 @@ impl ExecutionGadget for BlockCtxU256Gadget { } } +#[cfg(feature = "scroll")] #[derive(Clone, Debug)] -pub(crate) struct DifficulityGadget { +pub(crate) struct DifficultyGadget { same_context: SameContextGadget, } -impl ExecutionGadget for DifficulityGadget { - const NAME: &'static str = "DIFFICULITY"; +#[cfg(feature = "scroll")] +impl ExecutionGadget for DifficultyGadget { + const NAME: &'static str = "DIFFICULTY"; - const EXECUTION_STATE: ExecutionState = ExecutionState::BLOCKCTXU256; + const EXECUTION_STATE: ExecutionState = ExecutionState::DIFFICULTY; fn configure(cb: &mut EVMConstraintBuilder) -> Self { + // Always returns 0 for scroll. cb.stack_push(0.expr()); let opcode = cb.query_cell(); // State transition @@ -223,17 +234,10 @@ impl ExecutionGadget for DifficulityGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - self.same_context.assign_exec_step(region, offset, step)?; - - Ok(()) + self.same_context.assign_exec_step(region, offset, step) } } -// notice in scroll ,the BASEFEE is invalid and difficulity has been -// changed -#[cfg(feature = "scroll")] -pub(crate) use DifficulityGadget as BlockCtxU256Gadget; - #[cfg(test)] mod test { use crate::test_util::CircuitTestBuilder; diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 3df57e7bde..de4a9fbfd2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -1149,27 +1149,29 @@ impl ExecutionGadget for CallOpGadget { #[cfg(test)] mod test { use super::*; - use crate::{ - mpt_circuit::MptCircuit, test_util::CircuitTestBuilder, util::SubCircuit, - witness::block_convert, - }; - use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; + use crate::test_util::CircuitTestBuilder; + use bus_mapping::circuit_input_builder::CircuitsParams; use eth_types::{ - address, bytecode, - evm_types::OpcodeId, - geth_types::{Account, GethData}, - word, Address, ToWord, Word, + address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, ToWord, Word, }; - use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use itertools::Itertools; use mock::{ test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, TestContext, }; - use rayon::prelude::{ParallelBridge, ParallelIterator}; use std::default::Default; + #[cfg(feature = "scroll")] + mod scroll_imports { + pub use crate::{mpt_circuit::MptCircuit, util::SubCircuit, witness::block_convert}; + pub use bus_mapping::mock::BlockData; + pub use eth_types::geth_types::GethData; + pub use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; + } + #[cfg(feature = "scroll")] + use scroll_imports::*; + const TEST_CALL_OPCODES: &[OpcodeId] = &[ OpcodeId::CALL, OpcodeId::CALLCODE, @@ -1267,8 +1269,8 @@ mod test { TEST_CALL_OPCODES .iter() - .cartesian_product(stacks.into_iter()) - .cartesian_product(callees.into_iter()) + .cartesian_product(stacks) + .cartesian_product(callees) .par_bridge() .for_each(|((opcode, stack), callee)| { test_ok(caller(opcode, stack, true), callee, None); @@ -1812,9 +1814,6 @@ mod test_precompiles { address: Word::from(0x2), stack_value: vec![( Word::from(0x20), - #[cfg(feature = "scroll")] - Word::zero(), - #[cfg(not(feature = "scroll"))] word!("a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89"), )], ..Default::default() diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 5c8095cf87..5bc94c6420 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -837,13 +837,12 @@ mod test { address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, Bytecode, Word, }; use itertools::Itertools; - use lazy_static::lazy_static; use mock::{eth, TestContext, MOCK_ACCOUNTS}; + use std::sync::LazyLock; const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); - lazy_static! { - static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); - } + static CALLER_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0x00bbccddee000000000000000000000000002400")); fn run_test_circuits(ctx: TestContext) { CircuitTestBuilder::new_from_test_ctx(ctx) diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index a1426d2ddc..dc32fd1333 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -1,3 +1,5 @@ +use std::sync::Mutex; + use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -23,7 +25,7 @@ use halo2_proofs::{ plonk::{Error, Expression}, }; -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct EndBlockGadget { total_txs: Cell, total_txs_is_max_txs: IsEqualGadget, @@ -32,7 +34,24 @@ pub(crate) struct EndBlockGadget { max_txs: Cell, phase2_withdraw_root: Cell, phase2_withdraw_root_prev: Cell, - pub withdraw_root_assigned: std::cell::RefCell>, + pub withdraw_root_assigned: Mutex>, +} + +impl Clone for EndBlockGadget { + fn clone(&self) -> Self { + let withdraw_root_assigned: Option = + *self.withdraw_root_assigned.lock().unwrap(); + Self { + withdraw_root_assigned: Mutex::new(withdraw_root_assigned), + total_txs: self.total_txs.clone(), + total_txs_is_max_txs: self.total_txs_is_max_txs.clone(), + is_empty_block: self.is_empty_block.clone(), + max_rws: self.max_rws.clone(), + max_txs: self.max_txs.clone(), + phase2_withdraw_root: self.phase2_withdraw_root.clone(), + phase2_withdraw_root_prev: self.phase2_withdraw_root_prev.clone(), + } + } } const EMPTY_BLOCK_N_RWS: u64 = 0; @@ -172,10 +191,9 @@ impl ExecutionGadget for EndBlockGadget { offset, region.word_rlc(block.prev_withdraw_root), )?; - - self.withdraw_root_assigned - .borrow_mut() - .replace(withdraw_root.cell()); + if let Some(cell) = withdraw_root { + *self.withdraw_root_assigned.lock().unwrap() = Some(cell.cell()); + } // TODO: now we do not export withdraw_root_prev for we have only one // phase2 cell which is enabled for copy constraint // self.withdraw_root_prev_assigned @@ -185,8 +203,8 @@ impl ExecutionGadget for EndBlockGadget { // When rw_indices is not empty, we're at the last row (at a fixed offset), // where we need to access the max_rws and max_txs constant. if !step.rw_indices.is_empty() { - region.constrain_constant(max_rws_assigned, max_rws)?; - region.constrain_constant(max_txs_assigned, max_txs)?; + region.constrain_constant(max_rws_assigned.unwrap(), max_rws)?; + region.constrain_constant(max_txs_assigned.unwrap(), max_txs)?; } Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs index 0e2af7e980..9f3f0bcf13 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs @@ -134,6 +134,7 @@ impl ExecutionGadget for ErrorCodeStoreGadget { #[cfg(test)] mod test { + use crate::test_util::CircuitTestBuilder; use bus_mapping::circuit_input_builder::CircuitsParams; use eth_types::{ address, @@ -145,16 +146,12 @@ mod test { Word, // word, }; - - use lazy_static::lazy_static; use mock::{eth, TestContext, MOCK_ACCOUNTS}; - - use crate::test_util::CircuitTestBuilder; + use std::sync::LazyLock; const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); - lazy_static! { - static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); - } + static CALLER_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0x00bbccddee000000000000000000000000002400")); const MAXCODESIZE: u64 = 0x6000u64; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs index 08db7ba9fe..55d120070c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs @@ -157,16 +157,15 @@ mod test { use eth_types::{ address, bytecode, evm_types::OpcodeId, geth_types::Account, Address, Bytecode, Word, }; + use std::sync::LazyLock; - use lazy_static::lazy_static; use mock::{eth, TestContext, MOCK_ACCOUNTS}; use crate::test_util::CircuitTestBuilder; const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); - lazy_static! { - static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); - } + static CALLER_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0x00bbccddee000000000000000000000000002400")); const MAXCODESIZE: u64 = 0x6000u64; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs index 4a12a2119f..af6a9fe98f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs @@ -72,24 +72,21 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { mod test { use crate::{evm_circuit::test::rand_bytes, test_util::CircuitTestBuilder}; use eth_types::{bytecode::Bytecode, Word}; - use lazy_static::lazy_static; use mock::{generate_mock_call_bytecode, MockCallBytecodeParams, TestContext}; #[cfg(feature = "scroll")] use eth_types::address; - lazy_static! { - static ref TESTING_INVALID_CODES: [Vec; 6] = [ - // Single invalid opcode - vec![0x0e], - vec![0x4f], - vec![0xa5], - vec![0xf6], - vec![0xfe], - // Multiple invalid opcodes - vec![0x5c, 0x5e], - ]; - } + static TESTING_INVALID_CODES: [&[u8]; 6] = [ + // Single invalid opcode + &[0x0e], + &[0x4f], + &[0xa5], + &[0xf6], + &[0xfe], + // Multiple invalid opcodes + &[0x5c, 0x5e], + ]; #[test] fn invalid_opcode_root() { diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs index 0671ee6bd5..d8aa5383f5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs @@ -139,12 +139,11 @@ mod test { Address, Bytecode, ToWord, Word, U256, }; use itertools::Itertools; - use lazy_static::lazy_static; use mock::TestContext; + use std::sync::LazyLock; - lazy_static! { - static ref TEST_ADDRESS: Address = address!("0xaabbccddee000000000000000000000000000000"); - } + static TEST_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0xaabbccddee000000000000000000000000000000")); #[test] fn oog_account_access_root() { diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs index 1a5c25345d..d0289dc78f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs @@ -226,70 +226,70 @@ mod test { }; use itertools::Itertools; use mock::TestContext; + use std::sync::LazyLock; - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "multi-bytes success (more than 32 bytes)", - setup_code: bytecode! { - // place params in memory - PUSH30(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) - PUSH1(0x00) // place from 0x00 in memory - MSTORE - PUSH30(word!("0xaabbccdd001122331039abcdefefef84")) - PUSH1(0x20) // place from 0x20 in memory - MSTORE - }, - // copy 63 bytes from memory addr 0 - call_data_offset: 0x00.into(), - call_data_length: 0x3f.into(), - // return only 35 bytes and write from memory addr 72 - ret_offset: 0x48.into(), - ret_size: 0x23.into(), - address: PrecompileCalls::Identity.address().to_word(), - gas: (PrecompileCalls::Identity.base_gas_cost().as_u64() - + 2 * GasCost::PRECOMPILE_IDENTITY_PER_WORD.as_u64() - - 1).to_word(), - ..Default::default() + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "multi-bytes success (more than 32 bytes)", + setup_code: bytecode! { + // place params in memory + PUSH30(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) + PUSH1(0x00) // place from 0x00 in memory + MSTORE + PUSH30(word!("0xaabbccdd001122331039abcdefefef84")) + PUSH1(0x20) // place from 0x20 in memory + MSTORE }, - PrecompileCallArgs { - name: "modexp length in u256", - setup_code: bytecode! { - // Base size - PUSH1(0x20) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x20) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x20) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) - PUSH1(0x60) - MSTORE - PUSH32(word!("0x1000000000000000000000000000000000000000000000000000000000000009")) - PUSH1(0x80) - MSTORE - PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0xc0.into(), - ret_offset: 0xe0.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - gas: 20.to_word(), - ..Default::default() + // copy 63 bytes from memory addr 0 + call_data_offset: 0x00.into(), + call_data_length: 0x3f.into(), + // return only 35 bytes and write from memory addr 72 + ret_offset: 0x48.into(), + ret_size: 0x23.into(), + address: PrecompileCalls::Identity.address().to_word(), + gas: (PrecompileCalls::Identity.base_gas_cost().as_u64() + + 2 * GasCost::PRECOMPILE_IDENTITY_PER_WORD.as_u64() + - 1) + .to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp length in u256", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x1000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE }, - ] - }; - } + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + gas: 20.to_word(), + ..Default::default() + }, + ] + }); #[test] fn precompile_oog_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index 4b6ae8db91..df0030b642 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -253,13 +253,11 @@ mod test { use eth_types::{ address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, }; - use lazy_static::lazy_static; use mock::TestContext; + use std::sync::LazyLock; - lazy_static! { - static ref EXTERNAL_ADDRESS: Address = - address!("0xaabbccddee000000000000000000000000000000"); - } + static EXTERNAL_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0xaabbccddee000000000000000000000000000000")); fn test_ok( external_account: Option, diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index 39b32c1eee..3e36360501 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -132,13 +132,11 @@ mod test { use eth_types::{ address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, U256, }; - use lazy_static::lazy_static; use mock::{eth, TestContext}; + use std::sync::LazyLock; - lazy_static! { - static ref EXTERNAL_ADDRESS: Address = - address!("0xaabbccddee000000000000000000000000000000"); - } + static EXTERNAL_ADDRESS: LazyLock
= + LazyLock::new(|| address!("0xaabbccddee000000000000000000000000000000")); fn test_ok(external_account: Option, is_warm: bool) { let external_address = external_account diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index 76b6f3f474..996f55e586 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -58,13 +58,13 @@ impl ExecutionGadget for MulModGadget { let d = cb.query_word_rlc(); let e = cb.query_word_rlc(); - // 1. k1 * n + a_reduced == a + // 1. k1 * n + a_reduced == a let modword = ModGadget::construct(cb, [&a, &n, &a_reduced]); - // 2. a_reduced * b + 0 == d * 2^256 + e + // 2. a_reduced * b + 0 == d * 2^256 + e let mul512_left = MulAddWords512Gadget::construct(cb, [&a_reduced, &b, &d, &e], None); - // 3. k2 * n + r == d * 2^256 + e + // 3. k2 * n + r == d * 2^256 + e let mul512_right = MulAddWords512Gadget::construct(cb, [&k, &n, &d, &e], Some(&r)); // (r < n ) or n == 0 @@ -181,7 +181,7 @@ mod test { let mut ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(); if let Some(r) = r { - let mut last = ctx + let last = ctx .geth_traces .first_mut() .unwrap() diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_add.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_add.rs index c9d34f5c2c..be9dc0ca40 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_add.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_add.rs @@ -299,17 +299,17 @@ mod test { use itertools::Itertools; use mock::TestContext; use rayon::iter::{ParallelBridge, ParallelIterator}; + use std::sync::LazyLock; use crate::test_util::CircuitTestBuilder; - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecAdd (valid inputs)", - // P = (1, 2) - // Q = (1, 2) - setup_code: bytecode! { + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "ecAdd (valid inputs)", + // P = (1, 2) + // Q = (1, 2) + setup_code: bytecode! { // p_x = 1 PUSH1(0x01) PUSH1(0x00) @@ -327,18 +327,18 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (invalid input: point not on curve)", - // P = (2, 3) - // Q = (1, 2) - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (invalid input: point not on curve)", + // P = (2, 3) + // Q = (1, 2) + setup_code: bytecode! { // p_x = 2 PUSH1(0x02) PUSH1(0x00) @@ -356,19 +356,19 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - gas: 1000.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (valid inputs: truncated byte, input resulting in valid curve point)", - // P = (1, 2) - // Q = (q_x, q_y) - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + gas: 1000.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (valid inputs: truncated byte, input resulting in valid curve point)", + // P = (1, 2) + // Q = (q_x, q_y) + setup_code: bytecode! { // p_x = 1 PUSH1(0x01) PUSH1(0x00) @@ -386,18 +386,18 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x7f.into(), // the last byte is 0x00 so should be valid. - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (invalid inputs: truncated bytes, input resulting in invalid curve point)", - // P = (1, 2) - // Q = (q_x, q_y) - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x7f.into(), // the last byte is 0x00 so should be valid. + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (invalid inputs: truncated bytes, input resulting in invalid curve point)", + // P = (1, 2) + // Q = (q_x, q_y) + setup_code: bytecode! { // p_x = 1 PUSH1(0x01) PUSH1(0x00) @@ -415,19 +415,19 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - // only the last byte is 0x00, so ignoring 2 bytes does not give us point on curve. - call_data_length: 0x7e.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (valid inputs: truncated bytes, input resulting in valid curve point)", - // P = (1, 2) - // Q = (0, 0) truncated - setup_code: bytecode! { + call_data_offset: 0x00.into(), + // only the last byte is 0x00, so ignoring 2 bytes does not give us point on curve. + call_data_length: 0x7e.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (valid inputs: truncated bytes, input resulting in valid curve point)", + // P = (1, 2) + // Q = (0, 0) truncated + setup_code: bytecode! { // p_x = 1 PUSH1(0x01) PUSH1(0x00) @@ -437,28 +437,28 @@ mod test { PUSH1(0x20) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x40.into(), // q is (0, 0) - ret_offset: 0x40.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (should succeed on empty inputs)", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0x00.into(), - ret_offset: 0x00.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (valid inputs > 128 bytes)", - // P = (1, 2) - // Q = (1, 2) - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x40.into(), // q is (0, 0) + ret_offset: 0x40.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (should succeed on empty inputs)", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x00.into(), + ret_offset: 0x00.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (valid inputs > 128 bytes)", + // P = (1, 2) + // Q = (1, 2) + setup_code: bytecode! { // p_x = 1 PUSH1(0x01) PUSH1(0x00) @@ -480,18 +480,18 @@ mod test { PUSH1(0x80) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (invalid input: must mod p to be valid)", - // P = (p + 1, p + 2) - // Q = (1, 2) - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (invalid input: must mod p to be valid)", + // P = (p + 1, p + 2) + // Q = (1, 2) + setup_code: bytecode! { // p_x PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48")) PUSH1(0x00) @@ -509,18 +509,18 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x00.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (valid inputs: P == -Q)", - // P = (1, 2) - // Q = -P - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (valid inputs: P == -Q)", + // P = (1, 2) + // Q = -P + setup_code: bytecode! { // p_x PUSH1(0x01) PUSH1(0x00) @@ -538,18 +538,18 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecAdd (valid inputs: P == -Q), return size == 0", - // P = (1, 2) - // Q = -P - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecAdd (valid inputs: P == -Q), return size == 0", + // P = (1, 2) + // Q = -P + setup_code: bytecode! { // p_x PUSH1(0x01) PUSH1(0x00) @@ -567,51 +567,48 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x00.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - ..Default::default() - }, - ] - }; + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + }, + ] + }); - static ref OOG_TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecAdd OOG (valid inputs: P == -Q), return size == 0", - // P = (1, 2) - // Q = -P - setup_code: bytecode! { - // p_x - PUSH1(0x01) - PUSH1(0x00) - MSTORE - // p_y - PUSH1(0x02) - PUSH1(0x20) - MSTORE - // q_x = 1 - PUSH1(0x01) - PUSH1(0x40) - MSTORE - // q_y = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x00.into(), - address: PrecompileCalls::Bn128Add.address().to_word(), - gas: 149.into(), - ..Default::default() - }, - ] - }; - } + static OOG_TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![PrecompileCallArgs { + name: "ecAdd OOG (valid inputs: P == -Q), return size == 0", + // P = (1, 2) + // Q = -P + setup_code: bytecode! { + // p_x + PUSH1(0x01) + PUSH1(0x00) + MSTORE + // p_y + PUSH1(0x02) + PUSH1(0x20) + MSTORE + // q_x = 1 + PUSH1(0x01) + PUSH1(0x40) + MSTORE + // q_y = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + gas: 149.into(), + ..Default::default() + }] + }); #[test] fn precompile_ec_add_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs index e321b670ef..ed74a9d95b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_mul.rs @@ -1,13 +1,3 @@ -use std::ops::{Add, Sub}; - -use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; -use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar, U256}; -use gadgets::util::{and, not, or, select, split_u256, sum, Expr}; -use halo2_proofs::{ - circuit::Value, - plonk::{Error, Expression}, -}; - use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -24,19 +14,32 @@ use crate::{ table::CallContextFieldTag, witness::{Block, Call, ExecStep, Transaction}, }; +use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; +use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar, U256}; +use gadgets::util::{and, not, or, select, split_u256, sum, Expr}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; +use std::{ + ops::{Add, Sub}, + sync::LazyLock, +}; -lazy_static::lazy_static! { - // r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 - static ref FR_MODULUS: U256 = { - U256::from_dec_str("21888242871839275222246405745257275088548364400416034343698204186575808495617") - .expect("Fr::MODULUS") - }; - // q = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 - static ref FQ_MODULUS: U256 = { - U256::from_dec_str("21888242871839275222246405745257275088696311157297823662689037894645226208583") - .expect("Fq::MODULUS") - }; -} +// r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +static FR_MODULUS: LazyLock = LazyLock::new(|| { + U256::from_dec_str( + "21888242871839275222246405745257275088548364400416034343698204186575808495617", + ) + .expect("Fr::MODULUS") +}); +// q = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 +static FQ_MODULUS: LazyLock = LazyLock::new(|| { + U256::from_dec_str( + "21888242871839275222246405745257275088696311157297823662689037894645226208583", + ) + .expect("Fq::MODULUS") +}); #[derive(Clone, Debug)] pub struct EcMulGadget { @@ -501,17 +504,17 @@ mod test { use itertools::Itertools; use mock::TestContext; use rayon::iter::{ParallelBridge, ParallelIterator}; + use std::sync::LazyLock; use crate::test_util::CircuitTestBuilder; - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecMul (valid input)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = 7 - setup_code: bytecode! { + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "ecMul (valid input)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = 7 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -527,18 +530,18 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (invalid input: point not on curve)", - // P = (2, 3) - // s = 7 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (invalid input: point not on curve)", + // P = (2, 3) + // s = 7 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -554,18 +557,18 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x00.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (valid input < 96 bytes)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = blank - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (valid input < 96 bytes)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = blank + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -576,28 +579,28 @@ mod test { PUSH1(0x20) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x40.into(), - ret_offset: 0x40.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (should succeed on empty inputs)", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0x00.into(), - ret_offset: 0x00.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (valid inputs > 96 bytes)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = 7 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x40.into(), + ret_offset: 0x40.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (should succeed on empty inputs)", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x00.into(), + ret_offset: 0x00.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (valid inputs > 96 bytes)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = 7 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -622,18 +625,18 @@ mod test { PUSH1(0x60) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (invalid input: must mod p to be valid)", - // P = (p + 1, p + 2) - // s = 7 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (invalid input: must mod p to be valid)", + // P = (p + 1, p + 2) + // s = 7 + setup_code: bytecode! { // p_x PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48")) PUSH1(0x00) @@ -649,23 +652,23 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x00.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (valid: scalar larger than scalar field order n but less than base field p)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - - // For bn256 (alt_bn128) scalar field: - // n = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - - // Choose scalar s such that n < s < p - // s = 21888242871839275222246405745257275088548364400416034343698204186575808500000 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (valid: scalar larger than scalar field order n but less than base field p)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + + // For bn256 (alt_bn128) scalar field: + // n = 21888242871839275222246405745257275088548364400416034343698204186575808495617 + + // Choose scalar s such that n < s < p + // s = 21888242871839275222246405745257275088548364400416034343698204186575808500000 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -680,18 +683,18 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (valid: scalar larger than base field order)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = 2^256 - 1 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (valid: scalar larger than base field order)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = 2^256 - 1 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -707,18 +710,18 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (valid input): s == Fr::MODULUS - 1, i.e. P == -R", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = Fr::MODULUS - 1 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (valid input): s == Fr::MODULUS - 1, i.e. P == -R", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = Fr::MODULUS - 1 + setup_code: bytecode! { // p_x PUSH1(0x02) PUSH1(0x00) @@ -732,19 +735,19 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecMul (invalid input): s == Fr::MODULUS - 1, but P not on curve", - // P = (3, 4), i.e. not on curve - // s = Fr::MODULUS - 1 - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecMul (invalid input): s == Fr::MODULUS - 1, but P not on curve", + // P = (3, 4), i.e. not on curve + // s = Fr::MODULUS - 1 + setup_code: bytecode! { // p_x PUSH1(0x03) PUSH1(0x00) @@ -758,50 +761,47 @@ mod test { PUSH1(0x40) MSTORE }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - ..Default::default() - }, - ] - }; - - static ref OOG_TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecMul (valid: scalar larger than base field order)", - // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) - // s = 2^256 - 1 - setup_code: bytecode! { - // p_x - PUSH1(0x02) - PUSH1(0x00) - MSTORE - - // p_y - PUSH32(word!("0x23818CDE28CF4EA953FE59B1C377FAFD461039C17251FF4377313DA64AD07E13")) - PUSH1(0x20) - MSTORE - - // s - PUSH32(word!("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")) - PUSH1(0x40) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x60.into(), - ret_offset: 0x60.into(), - ret_size: 0x40.into(), - address: PrecompileCalls::Bn128Mul.address().to_word(), - gas: (PrecompileCalls::Bn128Mul.base_gas_cost().as_u64() - 1).to_word(), - ..Default::default() - } - ] - }; - } + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + }, + ] + }); + + static OOG_TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![PrecompileCallArgs { + name: "ecMul (valid: scalar larger than base field order)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = 2^256 - 1 + setup_code: bytecode! { + // p_x + PUSH1(0x02) + PUSH1(0x00) + MSTORE + + // p_y + PUSH32(word!("0x23818CDE28CF4EA953FE59B1C377FAFD461039C17251FF4377313DA64AD07E13")) + PUSH1(0x20) + MSTORE + + // s + PUSH32(word!("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")) + PUSH1(0x40) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + gas: (PrecompileCalls::Bn128Mul.base_gas_cost().as_u64() - 1).to_word(), + ..Default::default() + }] + }); #[test] fn precompile_ec_mul_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_pairing.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_pairing.rs index cf78daaf1e..3983657e44 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_pairing.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ec_pairing.rs @@ -388,7 +388,7 @@ impl ExecutionGadget for EcPairingGadget { self.rand_pow_64.assign( region, offset, - keccak_rand.map(|r| r.pow(&[64, 0, 0, 0])), + keccak_rand.map(|r| r.pow([64, 0, 0, 0])), )?; } Err(EcPairingError::InvalidInputLen(input_bytes)) => { @@ -469,48 +469,108 @@ mod test { use itertools::Itertools; use mock::{test_ctx::helpers::account_0_code_wallet_0_no_code, TestContext, MOCK_WALLETS}; use rayon::iter::{ParallelBridge, ParallelIterator}; + use std::sync::LazyLock; use crate::test_util::CircuitTestBuilder; - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - let mut rng = rand::thread_rng(); - vec![ - PrecompileCallArgs { - name: "ecPairing (valid): empty calldata", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0x00.into(), - ret_offset: 0x00.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (valid): zero bytes", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0xC0.into(), - ret_offset: 0xC0.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - #[cfg(feature = "scroll")] - PrecompileCallArgs { - name: "ecPairing (invalid): all zero bytes, len(input) == 5 * 192", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0x3C0.into(), - ret_offset: 0x3C0.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + let mut rng = rand::thread_rng(); + vec![ + PrecompileCallArgs { + name: "ecPairing (valid): empty calldata", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x00.into(), + ret_offset: 0x00.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (valid): zero bytes", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0xC0.into(), + ret_offset: 0xC0.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + #[cfg(feature = "scroll")] + PrecompileCallArgs { + name: "ecPairing (invalid): all zero bytes, len(input) == 5 * 192", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x3C0.into(), + ret_offset: 0x3C0.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (pairing true): 2 pairs", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE }, - PrecompileCallArgs { - name: "ecPairing (pairing true): 2 pairs", - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (pairing true): 4 pairs with random G1s", + setup_code: { + let mut setup_code = bytecode! { // G1_x1 PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) PUSH1(0x00) @@ -559,275 +619,42 @@ mod test { PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) PUSH2(0x160) MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x180.into(), - ret_offset: 0x180.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (pairing true): 4 pairs with random G1s", - setup_code: { - let mut setup_code = bytecode! { - // G1_x1 - PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) - PUSH1(0x00) - MSTORE - // G1_y1 - PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) - PUSH1(0x20) - MSTORE - // G2_x11 - PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) - PUSH1(0x40) - MSTORE - // G2_x12 - PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) - PUSH1(0x60) - MSTORE - // G2_y11 - PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) - PUSH1(0x80) - MSTORE - // G2_y12 - PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) - PUSH1(0xA0) - MSTORE - // G1_x2 - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) - PUSH1(0xC0) - MSTORE - // G1_y2 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0xE0) - MSTORE - // G2_x21 - PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) - PUSH2(0x100) - MSTORE - // G2_x22 - PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) - PUSH2(0x120) - MSTORE - // G2_y21 - PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) - PUSH2(0x140) - MSTORE - // G2_y22 - PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) - PUSH2(0x160) - MSTORE - }; - let mut memory_addr = 0x180; - for _ in 0..2 { - // G1::random - let g1 = G1Affine::random(&mut rng); - setup_code.push(32, Word::from_little_endian(&g1.x.to_bytes())); + }; + let mut memory_addr = 0x180; + for _ in 0..2 { + // G1::random + let g1 = G1Affine::random(&mut rng); + setup_code.push(32, Word::from_little_endian(&g1.x.to_bytes())); + setup_code.push(2, memory_addr); + memory_addr += 0x20; + setup_code.write_op(OpcodeId::MSTORE); + setup_code.push(32, Word::from_little_endian(&g1.y.to_bytes())); + setup_code.push(2, memory_addr); + memory_addr += 0x20; + setup_code.write_op(OpcodeId::MSTORE); + // G2::identity + for _ in 0..4 { + setup_code.push(1, 0x00); setup_code.push(2, memory_addr); memory_addr += 0x20; setup_code.write_op(OpcodeId::MSTORE); - setup_code.push(32, Word::from_little_endian(&g1.y.to_bytes())); - setup_code.push(2, memory_addr); - memory_addr += 0x20; - setup_code.write_op(OpcodeId::MSTORE); - // G2::identity - for _ in 0..4 { - setup_code.push(1, 0x00); - setup_code.push(2, memory_addr); - memory_addr += 0x20; - setup_code.write_op(OpcodeId::MSTORE); - } - } - setup_code - }, - call_data_offset: 0x00.into(), - call_data_length: 0x300.into(), - ret_offset: 0x300.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (pairing true): 4 pairs with random G2s", - setup_code: { - let mut setup_code = bytecode! { - // G1_x1 - PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) - PUSH1(0x00) - MSTORE - // G1_y1 - PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) - PUSH1(0x20) - MSTORE - // G2_x11 - PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) - PUSH1(0x40) - MSTORE - // G2_x12 - PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) - PUSH1(0x60) - MSTORE - // G2_y11 - PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) - PUSH1(0x80) - MSTORE - // G2_y12 - PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) - PUSH1(0xA0) - MSTORE - // G1_x2 - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) - PUSH1(0xC0) - MSTORE - // G1_y2 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0xE0) - MSTORE - // G2_x21 - PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) - PUSH2(0x100) - MSTORE - // G2_x22 - PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) - PUSH2(0x120) - MSTORE - // G2_y21 - PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) - PUSH2(0x140) - MSTORE - // G2_y22 - PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) - PUSH2(0x160) - MSTORE - }; - let mut memory_addr = 0x180; - for _ in 0..2 { - // G1::identity - for _ in 0..2 { - setup_code.push(1, 0x00); - setup_code.push(2, memory_addr); - memory_addr += 0x20; - setup_code.write_op(OpcodeId::MSTORE); - } - // G2::random - let g2 = G2Affine::random(&mut rng); - for fq in [g2.x.c1, g2.x.c0, g2.y.c1, g2.y.c0].iter() { - setup_code.push(32, Word::from_little_endian(&fq.to_bytes())); - setup_code.push(2, memory_addr); - memory_addr += 0x20; - setup_code.write_op(OpcodeId::MSTORE); - } } - setup_code - }, - call_data_offset: 0x00.into(), - call_data_length: 0x300.into(), - ret_offset: 0x300.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - #[cfg(feature = "scroll")] - PrecompileCallArgs { - name: "ecPairing (invalid): len(input) > 768", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 769.into(), - ret_offset: 0xC0.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (invalid): len(input) % 192 != 0", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 191.into(), - ret_offset: 191.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (invalid): len(input) % 192 != 0", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 193.into(), - ret_offset: 193.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecPairing (invalid): invalid field element, mod p is valid", - setup_code: bytecode! { - // G1_x1 - PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48")) // p + 1 - PUSH1(0x00) - MSTORE - // G1_y1 - PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49")) // p + 2 - PUSH1(0x20) - MSTORE - // G2_x11 - PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) - PUSH1(0x40) - MSTORE - // G2_x12 - PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) - PUSH1(0x60) - MSTORE - // G2_y11 - PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) - PUSH1(0x80) - MSTORE - // G2_y12 - PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) - PUSH1(0xA0) - MSTORE - // G1_x2 - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) - PUSH1(0xC0) - MSTORE - // G1_y2 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0xE0) - MSTORE - // G2_x21 - PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) - PUSH2(0x100) - MSTORE - // G2_x22 - PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) - PUSH2(0x120) - MSTORE - // G2_y21 - PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) - PUSH2(0x140) - MSTORE - // G2_y22 - PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) - PUSH2(0x160) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x180.into(), - ret_offset: 0x180.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + } + setup_code }, - PrecompileCallArgs { - name: "ecPairing (invalid): G1 point not on curve", - setup_code: bytecode! { + call_data_offset: 0x00.into(), + call_data_length: 0x300.into(), + ret_offset: 0x300.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (pairing true): 4 pairs with random G2s", + setup_code: { + let mut setup_code = bytecode! { // G1_x1 - PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18d0")) + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) PUSH1(0x00) MSTORE // G1_y1 @@ -874,229 +701,403 @@ mod test { PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) PUSH2(0x160) MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x180.into(), - ret_offset: 0x180.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + }; + let mut memory_addr = 0x180; + for _ in 0..2 { + // G1::identity + for _ in 0..2 { + setup_code.push(1, 0x00); + setup_code.push(2, memory_addr); + memory_addr += 0x20; + setup_code.write_op(OpcodeId::MSTORE); + } + // G2::random + let g2 = G2Affine::random(&mut rng); + for fq in [g2.x.c1, g2.x.c0, g2.y.c1, g2.y.c0].iter() { + setup_code.push(32, Word::from_little_endian(&fq.to_bytes())); + setup_code.push(2, memory_addr); + memory_addr += 0x20; + setup_code.write_op(OpcodeId::MSTORE); + } + } + setup_code }, - PrecompileCallArgs { - name: "ecPairing (invalid): G2 point not on curve", - setup_code: bytecode! { - // G1_x1 - PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) - PUSH1(0x00) - MSTORE - // G1_y1 - PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) - PUSH1(0x20) - MSTORE - // G2_x11 - PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebb")) - PUSH1(0x40) - MSTORE - // G2_x12 - PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) - PUSH1(0x60) - MSTORE - // G2_y11 - PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) - PUSH1(0x80) - MSTORE - // G2_y12 - PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) - PUSH1(0xA0) - MSTORE - // G1_x2 - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) - PUSH1(0xC0) - MSTORE - // G1_y2 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0xE0) - MSTORE - // G2_x21 - PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) - PUSH2(0x100) - MSTORE - // G2_x22 - PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) - PUSH2(0x120) - MSTORE - // G2_y21 - PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) - PUSH2(0x140) - MSTORE - // G2_y22 - PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) - PUSH2(0x160) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x180.into(), - ret_offset: 0x180.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x300.into(), + ret_offset: 0x300.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + #[cfg(feature = "scroll")] + PrecompileCallArgs { + name: "ecPairing (invalid): len(input) > 768", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 769.into(), + ret_offset: 0xC0.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): len(input) % 192 != 0", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 191.into(), + ret_offset: 191.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): len(input) % 192 != 0", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 193.into(), + ret_offset: 193.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): invalid field element, mod p is valid", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48")) // p + 1 + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49")) // p + 2 + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE }, - PrecompileCallArgs { - name: "ecPairing (invalid): G1: (0, 0), G2: not on curve", - setup_code: bytecode! { - // G1_x - PUSH32(0x00) - PUSH1(0x00) - MSTORE - // G1_y - PUSH32(0x00) - PUSH1(0x20) - MSTORE - // G2_x1 - PUSH32(0x01) - PUSH1(0x40) - MSTORE - // G2_x2 - PUSH32(0x02) - PUSH1(0x60) - MSTORE - // G2_y1 - PUSH32(0x03) - PUSH1(0x80) - MSTORE - // G2_y2 - PUSH32(0x04) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0xC0.into(), - ret_offset: 0xC0.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): G1 point not on curve", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18d0")) + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE }, - PrecompileCallArgs { - name: "ecPairing (invalid): G1: not on curve, G2: (0, 0, 0, 0)", - setup_code: bytecode! { - // G1_x - PUSH32(0x04) - PUSH1(0x00) - MSTORE - // G1_y - PUSH32(0x04) - PUSH1(0x20) - MSTORE - // G2_x1 - PUSH32(0x00) - PUSH1(0x40) - MSTORE - // G2_x2 - PUSH32(0x00) - PUSH1(0x60) - MSTORE - // G2_y1 - PUSH32(0x00) - PUSH1(0x80) - MSTORE - // G2_y2 - PUSH32(0x00) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0xC0.into(), - ret_offset: 0xC0.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): G2 point not on curve", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebb")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE }, - ] - }; - static ref OOG_TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecPairing (pairing true): 2 pairs", - setup_code: bytecode! { - // G1_x1 - PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) - PUSH1(0x00) - MSTORE - // G1_y1 - PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) - PUSH1(0x20) - MSTORE - // G2_x11 - PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) - PUSH1(0x40) - MSTORE - // G2_x12 - PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) - PUSH1(0x60) - MSTORE - // G2_y11 - PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) - PUSH1(0x80) - MSTORE - // G2_y12 - PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) - PUSH1(0xA0) - MSTORE - // G1_x2 - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) - PUSH1(0xC0) - MSTORE - // G1_y2 - PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) - PUSH1(0xE0) - MSTORE - // G2_x21 - PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) - PUSH2(0x100) - MSTORE - // G2_x22 - PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) - PUSH2(0x120) - MSTORE - // G2_y21 - PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) - PUSH2(0x140) - MSTORE - // G2_y22 - PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) - PUSH2(0x160) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x180.into(), - ret_offset: 0x180.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - value: 1.into(), - gas: (PrecompileCalls::Bn128Pairing.base_gas_cost().as_u64() - + 2* GasCost::PRECOMPILE_BN256PAIRING_PER_PAIR.as_u64() - 1).to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): G1: (0, 0), G2: not on curve", + setup_code: bytecode! { + // G1_x + PUSH32(0x00) + PUSH1(0x00) + MSTORE + // G1_y + PUSH32(0x00) + PUSH1(0x20) + MSTORE + // G2_x1 + PUSH32(0x01) + PUSH1(0x40) + MSTORE + // G2_x2 + PUSH32(0x02) + PUSH1(0x60) + MSTORE + // G2_y1 + PUSH32(0x03) + PUSH1(0x80) + MSTORE + // G2_y2 + PUSH32(0x04) + PUSH1(0xA0) + MSTORE }, - ] - }; - static ref INVALID_LEN_TEST: Vec = { - vec![ - #[cfg(feature = "scroll")] - PrecompileCallArgs { - name: "ecPairing (invalid): len(input) > 768", - setup_code: bytecode! {}, - call_data_offset: 0x00.into(), - call_data_length: 0x10340.into(), - ret_offset: 0xC0.into(), - ret_size: 0x20.into(), - value: 1.into(), - address: PrecompileCalls::Bn128Pairing.address().to_word(), - gas: 12_000_000.into(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0xC0.into(), + ret_offset: 0xC0.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecPairing (invalid): G1: not on curve, G2: (0, 0, 0, 0)", + setup_code: bytecode! { + // G1_x + PUSH32(0x04) + PUSH1(0x00) + MSTORE + // G1_y + PUSH32(0x04) + PUSH1(0x20) + MSTORE + // G2_x1 + PUSH32(0x00) + PUSH1(0x40) + MSTORE + // G2_x2 + PUSH32(0x00) + PUSH1(0x60) + MSTORE + // G2_y1 + PUSH32(0x00) + PUSH1(0x80) + MSTORE + // G2_y2 + PUSH32(0x00) + PUSH1(0xA0) + MSTORE }, - ] - }; - } + call_data_offset: 0x00.into(), + call_data_length: 0xC0.into(), + ret_offset: 0xC0.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + }, + ] + }); + + static OOG_TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![PrecompileCallArgs { + name: "ecPairing (pairing true): 2 pairs", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + value: 1.into(), + gas: (PrecompileCalls::Bn128Pairing.base_gas_cost().as_u64() + + 2 * GasCost::PRECOMPILE_BN256PAIRING_PER_PAIR.as_u64() + - 1) + .to_word(), + ..Default::default() + }] + }); + + static INVALID_LEN_TEST: LazyLock> = LazyLock::new(|| { + vec![ + #[cfg(feature = "scroll")] + PrecompileCallArgs { + name: "ecPairing (invalid): len(input) > 768", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x10340.into(), + ret_offset: 0xC0.into(), + ret_size: 0x20.into(), + value: 1.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + gas: 12_000_000.into(), + ..Default::default() + }, + ] + }); #[test] fn precompile_ec_pairing_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index ac5366264f..9c55dd7242 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -5,6 +5,7 @@ use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, }; +use std::sync::LazyLock; use crate::{ evm_circuit::{ @@ -24,11 +25,8 @@ use crate::{ witness::{Block, Call, ExecStep, Transaction}, }; -lazy_static::lazy_static! { - static ref FQ_MODULUS: U256 = { - word!("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") - }; -} +static FQ_MODULUS: LazyLock = + LazyLock::new(|| word!("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141")); #[derive(Clone, Debug)] pub struct EcrecoverGadget { @@ -526,332 +524,329 @@ mod test { use eth_types::{bytecode, word, ToWord}; use mock::TestContext; use rayon::{iter::ParallelIterator, prelude::IntoParallelRefIterator}; + use std::sync::LazyLock; use crate::test_util::CircuitTestBuilder; - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecrecover (padded bytes, addr recovered)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - // copy 101 bytes from memory addr 0. This should be sufficient to recover an - // address, but the signature is invalid (ecrecover does not care about this - // though) - call_data_offset: 0x00.into(), - call_data_length: 0x65.into(), - // return 32 bytes and write from memory addr 128 - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() - }, - PrecompileCallArgs { - name: "ecrecover (valid sig, addr recovered)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - // copy 128 bytes from memory addr 0. Address is recovered and the signature is - // valid. - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - // return 32 bytes and write from memory addr 128 - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "ecrecover (padded bytes, addr recovered)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (valid sig, addr recovered, extra input bytes)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - // copy 133 bytes from memory addr 0. Address is recovered and the signature is - // valid. The 5 bytes after the first 128 bytes are ignored. - call_data_offset: 0x00.into(), - call_data_length: 0x85.into(), - // return 32 bytes and write from memory addr 128 - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + // copy 101 bytes from memory addr 0. This should be sufficient to recover an + // address, but the signature is invalid (ecrecover does not care about this + // though) + call_data_offset: 0x00.into(), + call_data_length: 0x65.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (valid sig, addr recovered)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (overflowing msg_hash)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + // copy 128 bytes from memory addr 0. Address is recovered and the signature is + // valid. + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (valid sig, addr recovered, extra input bytes)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (overflowing sig_r)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + // copy 133 bytes from memory addr 0. Address is recovered and the signature is + // valid. The 5 bytes after the first 128 bytes are ignored. + call_data_offset: 0x00.into(), + call_data_length: 0x85.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (overflowing msg_hash)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (overflowing sig_s)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (overflowing sig_r)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (invalid v > 28, single byte)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(29) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (overflowing sig_s)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (invalid v < 27, single byte)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(26) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (invalid v > 28, single byte)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(29) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (invalid v, multi-byte, last byte == 28)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20, 1c == 28 (but not single byte) - PUSH32(word!("0x010000000000000000000000000000000000000000000000000000000000001c")) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (invalid v < 27, single byte)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(26) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "ecrecover (v == 1)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(0x01) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (invalid v, multi-byte, last byte == 28)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20, 1c == 28 (but not single byte) + PUSH32(word!("0x010000000000000000000000000000000000000000000000000000000000001c")) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - ] - }; - - static ref OOG_TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "ecrecover (oog)", - setup_code: bytecode! { - // msg hash from 0x00 - PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) - PUSH1(0x00) - MSTORE - // signature v from 0x20 - PUSH1(28) - PUSH1(0x20) - MSTORE - // signature r from 0x40 - PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) - PUSH1(0x40) - MSTORE - // signature s from 0x60 - PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) - PUSH1(0x60) - MSTORE - }, - // copy 128 bytes from memory addr 0. Address is recovered and the signature is - // valid. - call_data_offset: 0x00.into(), - call_data_length: 0x80.into(), - // return 32 bytes and write from memory addr 128 - ret_offset: 0x80.into(), - ret_size: 0x20.into(), - gas: 0.into(), - value: 2.into(), - address: PrecompileCalls::Ecrecover.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (v == 1)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(0x01) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE }, - ] - }; - } + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + ] + }); + + static OOG_TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![PrecompileCallArgs { + name: "ecrecover (oog)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 128 bytes from memory addr 0. Address is recovered and the signature is + // valid. + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + gas: 0.into(), + value: 2.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }] + }); #[test] fn precompile_ecrecover_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs index 6dfb7751d7..8af78c3ecd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs @@ -199,6 +199,7 @@ impl ExecutionGadget for IdentityGadget { #[cfg(test)] mod test { + use crate::test_util::CircuitTestBuilder; use bus_mapping::{ evm::{OpcodeId, PrecompileCallArgs}, precompile::PrecompileCalls, @@ -206,65 +207,62 @@ mod test { use eth_types::{bytecode, word, ToWord}; use itertools::Itertools; use mock::TestContext; + use std::sync::LazyLock; - use crate::test_util::CircuitTestBuilder; - - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "single-byte success", - setup_code: bytecode! { - // place params in memory - PUSH1(0xff) - PUSH1(0x00) - MSTORE - }, - call_data_offset: 0x1f.into(), - call_data_length: 0x01.into(), - ret_offset: 0x3f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Identity.address().to_word(), - ..Default::default() + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "single-byte success", + setup_code: bytecode! { + // place params in memory + PUSH1(0xff) + PUSH1(0x00) + MSTORE }, - PrecompileCallArgs { - name: "multi-bytes success (less than 32 bytes)", - setup_code: bytecode! { - // place params in memory - PUSH16(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) - PUSH1(0x00) - MSTORE - }, - call_data_offset: 0x00.into(), - call_data_length: 0x10.into(), - ret_offset: 0x20.into(), - ret_size: 0x10.into(), - address: PrecompileCalls::Identity.address().to_word(), - ..Default::default() + call_data_offset: 0x1f.into(), + call_data_length: 0x01.into(), + ret_offset: 0x3f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Identity.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "multi-bytes success (less than 32 bytes)", + setup_code: bytecode! { + // place params in memory + PUSH16(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) + PUSH1(0x00) + MSTORE }, - PrecompileCallArgs { - name: "multi-bytes success (more than 32 bytes)", - setup_code: bytecode! { - // place params in memory - PUSH30(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) - PUSH1(0x00) // place from 0x00 in memory - MSTORE - PUSH30(word!("0xaabbccdd001122331039abcdefefef84")) - PUSH1(0x20) // place from 0x20 in memory - MSTORE - }, - // copy 63 bytes from memory addr 0 - call_data_offset: 0x00.into(), - call_data_length: 0x3f.into(), - // return only 35 bytes and write from memory addr 72 - ret_offset: 0x48.into(), - ret_size: 0x23.into(), - address: PrecompileCalls::Identity.address().to_word(), - ..Default::default() + call_data_offset: 0x00.into(), + call_data_length: 0x10.into(), + ret_offset: 0x20.into(), + ret_size: 0x10.into(), + address: PrecompileCalls::Identity.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "multi-bytes success (more than 32 bytes)", + setup_code: bytecode! { + // place params in memory + PUSH30(word!("0x0123456789abcdef0f1e2d3c4b5a6978")) + PUSH1(0x00) // place from 0x00 in memory + MSTORE + PUSH30(word!("0xaabbccdd001122331039abcdefefef84")) + PUSH1(0x20) // place from 0x20 in memory + MSTORE }, - ] - }; - } + // copy 63 bytes from memory addr 0 + call_data_offset: 0x00.into(), + call_data_length: 0x3f.into(), + // return only 35 bytes and write from memory addr 72 + ret_offset: 0x48.into(), + ret_size: 0x23.into(), + address: PrecompileCalls::Identity.address().to_word(), + ..Default::default() + }, + ] + }); #[test] fn precompile_identity_test() { diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs index 8a512c7b97..071cdc3dd3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs @@ -1,8 +1,3 @@ -use bus_mapping::precompile::PrecompileAuxData; -use eth_types::{Field, ToScalar}; -use gadgets::util::{select, Expr}; -use halo2_proofs::{circuit::Value, plonk::Error}; - use crate::{ evm_circuit::{ execution::ExecutionGadget, @@ -15,6 +10,10 @@ use crate::{ table::CallContextFieldTag, witness::{Block, Call, ExecStep, Transaction}, }; +use bus_mapping::precompile::PrecompileAuxData; +use eth_types::{Field, ToScalar}; +use gadgets::util::{select, Expr}; +use halo2_proofs::{circuit::Value, plonk::Error}; mod ec_add; pub use ec_add::EcAddGadget; @@ -33,6 +32,9 @@ pub use ec_pairing::EcPairingGadget; mod identity; pub use identity::IdentityGadget; +mod sha256; +pub use sha256::SHA256Gadget; + #[derive(Clone, Debug)] pub struct BasePrecompileGadget { input_bytes_rlc: Cell, diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs index d475735957..573aa415cc 100755 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/modexp.rs @@ -102,7 +102,7 @@ impl RandPowRepresent { let pow_of_rand = region .challenges() .keccak_input() - .map(|v| v.pow(&[exponent as u64, 0, 0, 0])); + .map(|v| v.pow([exponent as u64, 0, 0, 0])); let value_should_assigned = linked_value.unwrap_or_else(|| Value::known(F::one())) * pow_of_rand; @@ -422,7 +422,7 @@ impl ModExpInputs { input_limbs.assign(region, offset, val_r)?; } - for (val, input_bytes) in values.zip([&self.base, &self.exp, &self.modulus]) { + for (&val, input_bytes) in values.iter().zip([&self.base, &self.exp, &self.modulus]) { assign_word(region, offset, input_bytes, val)?; } @@ -906,8 +906,7 @@ impl ExecutionGadget for ModExpGadget { }; let garbage_bytes = if call.call_data_length as usize > input_expected_len { - let mut bts = Vec::new(); - bts.resize(input_expected_len - 96, 0); //front prefix zero + let mut bts = vec![0; input_expected_len - 96]; //front prefix zero bts.append(&mut Vec::from(&data.input_bytes[input_expected_len..])); bts.resize(96, 0); //padding zero bts @@ -951,7 +950,7 @@ impl ExecutionGadget for ModExpGadget { let n_padded_zeroes_pow = region .challenges() .keccak_input() - .map(|r| r.pow(&[n_padded_zeros, 0, 0, 0])); + .map(|r| r.pow([n_padded_zeros, 0, 0, 0])); let output_rlc = region .challenges() @@ -1040,15 +1039,17 @@ mod test { precompile::PrecompileCalls, }; use eth_types::{bytecode, word, ToWord}; + use ethers_core::k256::elliptic_curve::PrimeField; use itertools::Itertools; use mock::TestContext; + use std::sync::LazyLock; use crate::test_util::CircuitTestBuilder; #[test] fn test_limbs() { use crate::table::ModExpTable; - use halo2_proofs::{arithmetic::FieldExt, halo2curves::bn256::Fr}; + use halo2_proofs::halo2curves::bn256::Fr; use misc_precompiled_circuit::circuits::modexp::Number; use num_bigint::BigUint; @@ -1103,444 +1104,442 @@ mod test { //Limb::new(None, value) } - lazy_static::lazy_static! { - static ref TEST_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "modexp success", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x1) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x1) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x63.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "modexp success", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x1) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp success", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x3) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x2) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x66.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp success with padding 0, input len > minimal 96 bytes", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x3) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x2) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x65.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x66.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success with padding 0, input len > minimal 96 bytes", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp success with padding 0, input len < minimal 96 bytes", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x3) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x2) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x40.into(), // < minimal 96 bytes - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x65.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success with padding 0, input len < minimal 96 bytes", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp no input", - setup_code: bytecode! { - // just put something in memory - PUSH1(0x1) - PUSH1(0x00) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x0.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x40.into(), // < minimal 96 bytes + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp no input", + setup_code: bytecode! { + // just put something in memory + PUSH1(0x1) + PUSH1(0x00) + MSTORE }, - PrecompileCallArgs { - name: "modexp success with garbage bytes", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x3) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x2) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000009")) - PUSH1(0x80) - MSTORE - PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0xc0.into(), - ret_offset: 0xe0.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x0.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp success with garbage bytes", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x3) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x2) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800000901000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE }, - PrecompileCallArgs { - name: "modexp zero modulus", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x2) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x0) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800090000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x63.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp zero modulus", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x2) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x0) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800090000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp all zero bit len", - setup_code: bytecode! { - // Base size - PUSH1(0x0) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x0) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x0) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800090000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x63.into(), - ret_offset: 0x9f.into(), - ret_size: 0x21.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp all zero bit len", + setup_code: bytecode! { + // Base size + PUSH1(0x0) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x0) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x0) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800090000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp zero base and exponent", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x1) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x1) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0000090000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x64.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x21.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp zero base and exponent", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x1) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000090000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp zero exponent and modulus", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x1) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x1) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0800009000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x64.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x64.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp zero exponent and modulus", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x1) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0800009000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp zero base and modulus", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x1) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x1) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0008009000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x64.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x64.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp zero base and modulus", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x1) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0008009000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - ] - }; - - static ref TEST_U256_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "modexp length in u256", - setup_code: bytecode! { - // Base size - PUSH1(0x20) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x20) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x20) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) - PUSH1(0x60) - MSTORE - PUSH32(word!("0x1000000000000000000000000000000000000000000000000000000000000009")) - PUSH1(0x80) - MSTORE - PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0xc0.into(), - ret_offset: 0xe0.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x64.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + ] + }); + + static TEST_U256_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "modexp length in u256", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x1000000000000000000000000000000000000000000000000000000000000009")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE }, - PrecompileCallArgs { - name: "modexp length in u256 and result wrapped", - setup_code: bytecode! { - // Base size - PUSH1(0x20) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x20) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x20) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) - PUSH1(0x60) - MSTORE - PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000064")) - PUSH1(0x80) - MSTORE - PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) - PUSH1(0xA0) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0xc0.into(), - ret_offset: 0xe0.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp length in u256 and result wrapped", + setup_code: bytecode! { + // Base size + PUSH1(0x20) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x20) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x20) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000008")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000064")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0xfcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47")) + PUSH1(0xA0) + MSTORE }, - ] - }; - - static ref TEST_INVALID_VECTOR: Vec = { - vec![ - PrecompileCallArgs { - name: "modexp Msize length too large invalid", - setup_code: bytecode! { - // Base size - PUSH1(0x1) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x1) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x21) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) - PUSH1(0x60) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0x63.into(), - ret_offset: 0x9f.into(), - ret_size: 0x01.into(), - address: PrecompileCalls::Modexp.address().to_word(), - gas: 100000.into(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0xc0.into(), + ret_offset: 0xe0.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + ..Default::default() + }, + ] + }); + + static TEST_INVALID_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "modexp Msize length too large invalid", + setup_code: bytecode! { + // Base size + PUSH1(0x1) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x1) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x21) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) + PUSH1(0x60) + MSTORE }, - PrecompileCallArgs { - name: "modexp Base&Esize&Msize length too large invalid", - setup_code: bytecode! { - // Base size - PUSH1(0x21) - PUSH1(0x00) - MSTORE - // Esize - PUSH1(0x21) - PUSH1(0x20) - MSTORE - // Msize - PUSH1(0x21) - PUSH1(0x40) - MSTORE - // B, E and M - PUSH32(word!("0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed")) - PUSH1(0x60) - MSTORE - PUSH32(word!("0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2")) - PUSH1(0x80) - MSTORE - PUSH32(word!("0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa")) - PUSH1(0xa0) - MSTORE - PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) - PUSH1(0xc0) - MSTORE - }, - call_data_offset: 0x0.into(), - call_data_length: 0xc3.into(), - ret_offset: 0xe0.into(), - ret_size: 0x21.into(), - address: PrecompileCalls::Modexp.address().to_word(), - gas: 1000.into(), - ..Default::default() + call_data_offset: 0x0.into(), + call_data_length: 0x63.into(), + ret_offset: 0x9f.into(), + ret_size: 0x01.into(), + address: PrecompileCalls::Modexp.address().to_word(), + gas: 100000.into(), + ..Default::default() + }, + PrecompileCallArgs { + name: "modexp Base&Esize&Msize length too large invalid", + setup_code: bytecode! { + // Base size + PUSH1(0x21) + PUSH1(0x00) + MSTORE + // Esize + PUSH1(0x21) + PUSH1(0x20) + MSTORE + // Msize + PUSH1(0x21) + PUSH1(0x40) + MSTORE + // B, E and M + PUSH32(word!("0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed")) + PUSH1(0x60) + MSTORE + PUSH32(word!("0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2")) + PUSH1(0x80) + MSTORE + PUSH32(word!("0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa")) + PUSH1(0xa0) + MSTORE + PUSH32(word!("0x08090A0000000000000000000000000000000000000000000000000000000000")) + PUSH1(0xc0) + MSTORE }, - ] - }; - } + call_data_offset: 0x0.into(), + call_data_length: 0xc3.into(), + ret_offset: 0xe0.into(), + ret_size: 0x21.into(), + address: PrecompileCalls::Modexp.address().to_word(), + gas: 1000.into(), + ..Default::default() + }, + ] + }); #[ignore] #[test] diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/sha256.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/sha256.rs new file mode 100644 index 0000000000..72af4e0324 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/sha256.rs @@ -0,0 +1,380 @@ +use bus_mapping::precompile::PrecompileAuxData; +use eth_types::{evm_types::GasCost, Field, ToScalar}; +use gadgets::util::{select, Expr}; +use halo2_proofs::{circuit::Value, plonk::Error}; + +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::{N_BYTES_MEMORY_WORD_SIZE, N_BYTES_WORD}, + step::ExecutionState, + util::{ + common_gadget::RestoreContextGadget, constraint_builder::EVMConstraintBuilder, + math_gadget::ConstantDivisionGadget, rlc, CachedRegion, Cell, + }, + }, + table::CallContextFieldTag, + witness::{Block, Call, ExecStep, Transaction}, +}; + +#[derive(Clone, Debug)] +pub struct SHA256Gadget { + input_bytes_rlc: Cell, + output_bytes_rlc: Cell, + return_bytes_rlc: Cell, + + input_word_size: ConstantDivisionGadget, + is_success: Cell, + callee_address: Cell, + caller_id: Cell, + call_data_offset: Cell, + call_data_length: Cell, + return_data_offset: Cell, + return_data_length: Cell, + restore_context: RestoreContextGadget, +} + +impl ExecutionGadget for SHA256Gadget { + const EXECUTION_STATE: ExecutionState = ExecutionState::PrecompileSha256; + + const NAME: &'static str = "SHA256"; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let (input_bytes_rlc, output_bytes_rlc, return_bytes_rlc) = ( + cb.query_cell_phase2(), + cb.query_cell_phase2(), + cb.query_cell_phase2(), + ); + let [is_success, callee_address, caller_id, call_data_offset, call_data_length, return_data_offset, return_data_length] = + [ + CallContextFieldTag::IsSuccess, + CallContextFieldTag::CalleeAddress, + CallContextFieldTag::CallerId, + CallContextFieldTag::CallDataOffset, + CallContextFieldTag::CallDataLength, + CallContextFieldTag::ReturnDataOffset, + CallContextFieldTag::ReturnDataLength, + ] + .map(|tag| cb.call_context(None, tag)); + + let input_word_size = ConstantDivisionGadget::construct( + cb, + call_data_length.expr() + (N_BYTES_WORD - 1).expr(), + N_BYTES_WORD as u64, + ); + + let gas_cost = select::expr( + is_success.expr(), + GasCost::PRECOMPILE_SHA256_BASE.expr() + + input_word_size.quotient() * GasCost::PRECOMPILE_SHA256_PER_WORD.expr(), + cb.curr.state.gas_left.expr(), + ); + + cb.precompile_info_lookup( + cb.execution_state().as_u64().expr(), + callee_address.expr(), + cb.execution_state().precompile_base_gas_cost().expr(), + ); + + // sha256 verify lookup + cb.condition(is_success.expr(), |cb| { + cb.sha256_table_lookup( + input_bytes_rlc.expr(), + call_data_length.expr(), + output_bytes_rlc.expr(), + ); + }); + + let restore_context = RestoreContextGadget::construct2( + cb, + is_success.expr(), + gas_cost.expr(), + 0.expr(), + 0x00.expr(), // ReturnDataOffset + select::expr(is_success.expr(), 0x20.expr(), 0x00.expr()), // ReturnDataLength + 0.expr(), + 0.expr(), + ); + + Self { + input_bytes_rlc, + output_bytes_rlc, + return_bytes_rlc, + + input_word_size, + is_success, + callee_address, + caller_id, + call_data_offset, + call_data_length, + return_data_offset, + return_data_length, + restore_context, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + _tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + if let Some(PrecompileAuxData::SHA256 { + input_bytes, + output_bytes, + return_bytes, + }) = &step.aux_data + { + region + .challenges() + .keccak_input() + .map(|r| rlc::value(input_bytes.iter().rev(), r)); + + region + .challenges() + .keccak_input() + .map(|r| rlc::value(output_bytes.iter().rev(), r)); + + self.input_bytes_rlc.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(input_bytes.iter().rev(), r)), + )?; + self.output_bytes_rlc.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(output_bytes.iter().rev(), r)), + )?; + self.return_bytes_rlc.assign( + region, + offset, + region + .challenges() + .keccak_input() + .map(|r| rlc::value(return_bytes.iter().rev(), r)), + )?; + } else { + log::error!("unexpected aux_data {:?} for sha256", step.aux_data); + return Err(Error::Synthesis); + } + self.input_word_size.assign( + region, + offset, + (call.call_data_length + (N_BYTES_WORD as u64) - 1).into(), + )?; + self.is_success.assign( + region, + offset, + Value::known(F::from(u64::from(call.is_success))), + )?; + self.callee_address.assign( + region, + offset, + Value::known(call.code_address.unwrap().to_scalar().unwrap()), + )?; + self.caller_id + .assign(region, offset, Value::known(F::from(call.caller_id as u64)))?; + self.call_data_offset.assign( + region, + offset, + Value::known(F::from(call.call_data_offset)), + )?; + self.call_data_length.assign( + region, + offset, + Value::known(F::from(call.call_data_length)), + )?; + self.return_data_offset.assign( + region, + offset, + Value::known(F::from(call.return_data_offset)), + )?; + self.return_data_length.assign( + region, + offset, + Value::known(F::from(call.return_data_length)), + )?; + self.restore_context + .assign(region, offset, block, call, step, 7) + } +} + +#[cfg(test)] +mod test { + use bus_mapping::{ + evm::{OpcodeId, PrecompileCallArgs}, + precompile::PrecompileCalls, + }; + use eth_types::{bytecode, word, ToWord}; + use itertools::Itertools; + use mock::TestContext; + use std::sync::LazyLock; + + use crate::test_util::CircuitTestBuilder; + + static TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![ + PrecompileCallArgs { + name: "simple success", + setup_code: bytecode! { + // place params in memory + PUSH3(0x616263) + PUSH1(0x00) + MSTORE + }, + call_data_offset: 0x1d.into(), + call_data_length: 0x03.into(), + ret_offset: 0x20.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "nil success", + setup_code: bytecode! {}, + call_data_offset: 0x00.into(), + call_data_length: 0x00.into(), + ret_offset: 0x20.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "block edge", + setup_code: bytecode! { + // place params in memory + PUSH32(word!("0x6161616161616161616161616161616161616161616161616161616161616161")) + PUSH1(0x00) + MSTORE + PUSH32(word!("0x6161616161616161616161616161616161616161616161616161616161616161")) + PUSH1(0x20) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x40.into(), + ret_offset: 0x20.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "simple truncated return", + setup_code: bytecode! { + // place params in memory + PUSH3(0x616263) + PUSH1(0x00) + MSTORE + }, + call_data_offset: 0x1d.into(), + call_data_length: 0x03.into(), + ret_offset: 0x20.into(), + ret_size: 0x10.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "overlapped return", + setup_code: bytecode! { + // place params in memory + PUSH3(0x616263) + PUSH1(0x00) + MSTORE + }, + call_data_offset: 0x1d.into(), + call_data_length: 0x03.into(), + ret_offset: 0x00.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + }, + ] + }); + + static OOG_TEST_VECTOR: LazyLock> = LazyLock::new(|| { + vec![PrecompileCallArgs { + name: "oog", + setup_code: bytecode! { + PUSH32(word!("0x6161616161616161616161616161616161616161616161616161616161616161")) + PUSH1(0x00) + MSTORE + PUSH32(word!("0x6161616161616161616161616161616161616161616161616161616161616161")) + PUSH1(0x20) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x40.into(), + ret_offset: 0x20.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + gas: 20.into(), + ..Default::default() + }] + }); + + #[test] + fn precompile_sha256_common_test() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + for (test_vector, &call_kind) in TEST_VECTOR.iter().cartesian_product(&call_kinds) { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + } + + // verify nil case is corrected handled in SHA256 event + #[test] + fn precompile_sha256_nil_test() { + let nil_vector = &TEST_VECTOR[1]; + let bytecode = nil_vector.with_call_op(OpcodeId::STATICCALL); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .block_modifier(Box::new(|blk| { + let evts = blk.get_sha256(); + assert_eq!(evts.len(), 1); + assert_eq!(evts[0].input.len(), 0); + })) + .run(); + } + + // verify nil case is corrected handled in SHA256 event + #[test] + fn precompile_sha256_oog_test() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + for (test_vector, &call_kind) in OOG_TEST_VECTOR.iter().cartesian_product(&call_kinds) { + let bytecode = test_vector.with_call_op(call_kind); + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .block_modifier(Box::new(|blk| { + assert_eq!(blk.get_sha256().len(), 0); + })) + .run(); + } + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index 97e1e2c259..47879fda67 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -275,7 +275,7 @@ impl ExecutionGadget for SarGadget { self.b.assign(region, offset, Some(b.to_le_bytes()))?; let is_neg = 127 < a.to_le_bytes()[31]; - let shf0 = u128::from(shift.to_le_bytes()[0]); + let shf0 = u64::from(shift.to_le_bytes()[0]); let shf_div64 = shf0 / 64; let shf_mod64 = shf0 % 64; let p_lo = 1 << shf_mod64; @@ -288,7 +288,7 @@ impl ExecutionGadget for SarGadget { let shf_lt256 = shift .to_le_bytes() .iter() - .fold(0, |acc, val| acc + u128::from(*val)) + .fold(0, |acc, val| acc + u64::from(*val)) - shf0; let a64s = a.0; let mut a64s_lo = [0; 4]; @@ -320,9 +320,9 @@ impl ExecutionGadget for SarGadget { .map(|(c, v)| c.assign(region, offset, Value::known(F::from_u128(v)))) .collect::, _>>()?; self.shf_div64 - .assign(region, offset, Value::known(F::from_u128(shf_div64)))?; + .assign(region, offset, Value::known(F::from(shf_div64)))?; self.shf_mod64 - .assign(region, offset, Value::known(F::from_u128(shf_mod64)))?; + .assign(region, offset, Value::known(F::from(shf_mod64)))?; self.p_lo .assign(region, offset, Value::known(F::from_u128(p_lo)))?; self.p_hi @@ -336,19 +336,18 @@ impl ExecutionGadget for SarGadget { u64::from(a.to_le_bytes()[31]).into(), )?; self.shf_div64_lt_4 - .assign(region, offset, F::from_u128(shf_div64), 4.into())?; + .assign(region, offset, F::from(shf_div64), 4.into())?; self.shf_mod64_lt_64 - .assign(region, offset, F::from_u128(shf_mod64), 64.into())?; - self.shf_lt256 - .assign(region, offset, F::from_u128(shf_lt256))?; + .assign(region, offset, F::from(shf_mod64), 64.into())?; + self.shf_lt256.assign(region, offset, F::from(shf_lt256))?; self.shf_lo_div64_eq0 - .assign(region, offset, F::from_u128(shf_div64))?; + .assign(region, offset, F::from(shf_div64))?; self.shf_lo_div64_eq1 - .assign(region, offset, F::from_u128(shf_div64), F::from(1))?; + .assign(region, offset, F::from(shf_div64), F::from(1))?; self.shf_lo_div64_eq2 - .assign(region, offset, F::from_u128(shf_div64), F::from(2))?; + .assign(region, offset, F::from(shf_div64), F::from(2))?; self.shf_lo_div64_eq3 - .assign(region, offset, F::from_u128(shf_div64), F::from(3))?; + .assign(region, offset, F::from(shf_div64), F::from(3))?; self.a64s_lo_lt_p_lo .iter() .zip(a64s_lo.into_iter()) @@ -369,20 +368,18 @@ mod test { use crate::{evm_circuit::test::rand_word, test_util::CircuitTestBuilder}; use eth_types::{bytecode, U256}; use ethers_core::types::I256; - use lazy_static::lazy_static; use mock::TestContext; use rand::Rng; + use std::sync::LazyLock; - lazy_static! { - // Maximum negative word value of i256 (integer value of -1) - static ref MAX_NEG: U256 = U256::MAX; + // Maximum negative word value of i256 (integer value of -1) + static MAX_NEG: U256 = U256::MAX; - // Maximum positive word value of i256 - static ref MAX_POS: U256 = U256::try_from(I256::MAX).unwrap(); + // Maximum positive word value of i256 + static MAX_POS: U256 = I256::MAX.into_raw(); - // Negative sign (the highest bit is 1) - static ref NEG_SIGN: U256 = MAX_POS.checked_add(1.into()).unwrap(); - } + // Negative sign (the highest bit is 1) + static NEG_SIGN: LazyLock = LazyLock::new(|| MAX_POS.checked_add(1.into()).unwrap()); #[test] fn test_sar_gadget_with_positive_a() { @@ -415,16 +412,16 @@ mod test { #[test] fn test_sar_gadget_with_max_values() { // Test either (or both) `a` or `shift` is a maximum word. - test_ok(8.into(), *MAX_NEG); - test_ok(129.into(), *MAX_NEG); - test_ok(300.into(), *MAX_NEG); - test_ok(8.into(), *MAX_POS); - test_ok(129.into(), *MAX_POS); - test_ok(300.into(), *MAX_POS); - test_ok(*MAX_NEG, *MAX_NEG); - test_ok(*MAX_NEG, *MAX_POS); - test_ok(*MAX_POS, *MAX_NEG); - test_ok(*MAX_POS, *MAX_POS); + test_ok(8.into(), MAX_NEG); + test_ok(129.into(), MAX_NEG); + test_ok(300.into(), MAX_NEG); + test_ok(8.into(), MAX_POS); + test_ok(129.into(), MAX_POS); + test_ok(300.into(), MAX_POS); + test_ok(MAX_NEG, MAX_NEG); + test_ok(MAX_NEG, MAX_POS); + test_ok(MAX_POS, MAX_NEG); + test_ok(MAX_POS, MAX_POS); } #[test] @@ -446,15 +443,15 @@ mod test { test_ok(0xFF.into(), *NEG_SIGN); test_ok(0x100.into(), *NEG_SIGN); test_ok(0x101.into(), *NEG_SIGN); - test_ok(0.into(), *MAX_NEG); - test_ok(1.into(), *MAX_NEG); - test_ok(0xFF.into(), *MAX_NEG); - test_ok(0x100.into(), *MAX_NEG); + test_ok(0.into(), MAX_NEG); + test_ok(1.into(), MAX_NEG); + test_ok(0xFF.into(), MAX_NEG); + test_ok(0x100.into(), MAX_NEG); test_ok(0xFE.into(), U256::from(2).checked_pow(254.into()).unwrap()); - test_ok(0xF8.into(), *MAX_POS); - test_ok(0xFE.into(), *MAX_POS); - test_ok(0xFF.into(), *MAX_POS); - test_ok(0x100.into(), *MAX_POS); + test_ok(0xF8.into(), MAX_POS); + test_ok(0xFE.into(), MAX_POS); + test_ok(0xFF.into(), MAX_POS); + test_ok(0x100.into(), MAX_POS); } fn test_ok(shift: U256, a: U256) { diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index 409114b3cf..785cae9507 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -169,9 +169,7 @@ impl ExecutionGadget for ShlShrGadget { let shf_lt256 = pop1 .to_le_bytes() .iter() - .fold(Some(0_u64), |acc, val| { - acc.and_then(|acc| acc.checked_add(u64::from(*val))) - }) + .try_fold(0_u64, |acc, val| acc.checked_add(u64::from(*val))) .unwrap() - shf0; let divisor = if shf_lt256 == 0 { diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index edd543d0ee..f42960e998 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -279,7 +279,7 @@ mod test { let pos_extend = 0u8; let neg_extend = 0xFFu8; - for (value, byte_extend) in vec![(pos_value, pos_extend), (neg_value, neg_extend)].iter() { + for (value, byte_extend) in [(pos_value, pos_extend), (neg_value, neg_extend)].iter() { for idx in 0..33 { test_ok( (idx as u64).into(), diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index e5a579be56..aa531f4463 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -3,7 +3,7 @@ use halo2_proofs::{ halo2curves::bn256::Fr, plonk::{Circuit, ConstraintSystem}, }; -use std::collections::HashMap; +use std::{collections::HashMap, sync::LazyLock}; // Step dimension pub(crate) const STEP_WIDTH: usize = 121; @@ -72,10 +72,10 @@ pub(crate) const N_BYTES_GAS: usize = N_BYTES_U64; // Number of bytes that will be used for call data's size. pub(crate) const N_BYTES_CALLDATASIZE: usize = N_BYTES_U64; -lazy_static::lazy_static! { - // Step slot height in evm circuit - pub(crate) static ref EXECUTION_STATE_HEIGHT_MAP : HashMap = get_step_height_map(); -} +// Step slot height in evm circuit +pub(crate) static EXECUTION_STATE_HEIGHT_MAP: LazyLock> = + LazyLock::new(get_step_height_map); + fn get_step_height_map() -> HashMap { let mut meta = ConstraintSystem::::default(); let circuit = EvmCircuit::configure(&mut meta); diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index b26236f4e9..ac48465d32 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -9,13 +9,12 @@ use crate::{ witness::Transaction, }; use bus_mapping::{evm::OpcodeId, precompile::PrecompileCalls}; -use eth_types::evm_types::GasCost; +use eth_types::{evm_types::GasCost, Field}; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Value, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, }; -use std::{fmt::Display, iter}; +use std::{fmt::Display, iter, marker::ConstParamTy}; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -36,7 +35,7 @@ impl From for ExecutionState { } #[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)] +#[derive(ConstParamTy, Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)] pub enum ExecutionState { // Internal state BeginTx, @@ -80,7 +79,9 @@ pub enum ExecutionState { BLOCKHASH, BLOCKCTXU64, // TIMESTAMP, NUMBER, GASLIMIT BLOCKCTXU160, // COINBASE - BLOCKCTXU256, // DIFFICULTY, BASEFEE + BLOCKCTXU256, // BASEFEE, DIFFICULTY (for non-scroll) + #[cfg(feature = "scroll")] + DIFFICULTY, // DIFFICULTY CHAINID, SELFBALANCE, POP, @@ -270,11 +271,13 @@ impl ExecutionState { Self::BLOCKCTXU160 => vec![OpcodeId::COINBASE], Self::BLOCKCTXU256 => { if cfg!(feature = "scroll") { - vec![OpcodeId::DIFFICULTY] + vec![OpcodeId::BASEFEE] } else { vec![OpcodeId::DIFFICULTY, OpcodeId::BASEFEE] } } + #[cfg(feature = "scroll")] + Self::DIFFICULTY => vec![OpcodeId::DIFFICULTY], Self::CHAINID => vec![OpcodeId::CHAINID], Self::SELFBALANCE => vec![OpcodeId::SELFBALANCE], Self::POP => vec![OpcodeId::POP], @@ -433,7 +436,7 @@ pub(crate) struct DynamicSelectorHalf { pub(crate) target_pairs: Vec>, } -impl DynamicSelectorHalf { +impl DynamicSelectorHalf { pub(crate) fn new(cell_manager: &mut CellManager, count: usize) -> Self { let target_pairs = cell_manager.query_cells(CellType::StoragePhase1, (count + 1) / 2); let target_odd = cell_manager.query_cell(CellType::StoragePhase1); @@ -567,7 +570,7 @@ pub(crate) struct Step { pub(crate) cell_manager: CellManager, } -impl Step { +impl Step { pub(crate) fn new( meta: &mut ConstraintSystem, advices: [Column; STEP_WIDTH], diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index a89c79db32..80fb83cd7c 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -274,6 +274,7 @@ pub enum Table { Block, Copy, Keccak, + Sha256, Exp, Sig, ModExp, @@ -423,6 +424,16 @@ pub enum Lookup { /// the final output keccak256 hash of the input. output_rlc: Expression, }, + /// Lookup to sha256 table. + Sha256Table { + /// Accumulator to the input. + input_rlc: Expression, + /// Length of input that is being hashed. + input_len: Expression, + /// Output (hash) until this state. This is the RLC representation of + /// the final output sha256 hash of the input. + output_rlc: Expression, + }, /// Lookup to exponentiation table. ExpTable { base_limbs: [Expression; 4], @@ -476,6 +487,7 @@ impl Lookup { Self::Block { .. } => Table::Block, Self::CopyTable { .. } => Table::Copy, Self::KeccakTable { .. } => Table::Keccak, + Self::Sha256Table { .. } => Table::Sha256, Self::ExpTable { .. } => Table::Exp, Self::SigTable { .. } => Table::Sig, Self::ModExpTable { .. } => Table::ModExp, @@ -596,6 +608,17 @@ impl Lookup { input_len.clone(), output_rlc.clone(), ], + Self::Sha256Table { + input_rlc, + input_len, + output_rlc, + } => vec![ + 1.expr(), // q_enable + 1.expr(), // is_final + input_rlc.clone(), + input_len.clone(), + output_rlc.clone(), + ], Self::ExpTable { base_limbs, exponent_lo_hi, diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index bf3abe6797..6d50b597b1 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -14,7 +14,6 @@ use gadgets::bus::{ bus_port::{BusOpExpr, BusOpF, Port}, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{AssignedCell, Region, Value}, halo2curves::group::ff::BatchInvert, plonk::{Advice, Assigned, Column, ConstraintSystem, Error, Expression, VirtualCells}, @@ -48,7 +47,7 @@ pub(crate) struct Cell { cell_column_index: usize, } -impl Cell { +impl Cell { pub(crate) fn new( meta: &mut VirtualCells, column: Column, @@ -68,7 +67,7 @@ impl Cell { region: &mut CachedRegion<'_, '_, F>, offset: usize, value: Value, - ) -> Result, Error> { + ) -> Result>, Error> { region.assign_advice( || { format!( @@ -83,13 +82,13 @@ impl Cell { } } -impl Expr for Cell { +impl Expr for Cell { fn expr(&self) -> Expression { self.expression.clone() } } -impl Expr for &Cell { +impl Expr for &Cell { fn expr(&self) -> Expression { self.expression.clone() } @@ -136,22 +135,29 @@ impl StepBusOp { } } -pub struct CachedRegion<'r, 'b, F: FieldExt> { +pub struct CachedRegion<'r, 'b, F: Field> { region: &'r mut Region<'b, F>, advice: Vec>, challenges: &'r Challenges>, advice_columns: Vec>, width_start: usize, height_start: usize, + // the `CachedRegion` can be seen as a written buffer for real halo2 regions. + // All writes beyond `height_limit` will not be written through to halo2 columns. + // This is used for the evm step "assign next then assign current" pattern. + // When we remove this pattern later, this field can also be removed. + // More: + height_limit: usize, } -impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { +impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { /// New cached region pub(crate) fn new( region: &'r mut Region<'b, F>, challenges: &'r Challenges>, advice_columns: Vec>, height: usize, + height_limit: usize, height_start: usize, ) -> Self { Self { @@ -160,6 +166,7 @@ impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { challenges, width_start: advice_columns[0].index(), height_start, + height_limit, advice_columns, } } @@ -198,13 +205,15 @@ impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { } /// Assign an advice column value (witness). + /// If return value is None, it means the assignment will only happen + /// inside the CachedRegion, and is not written into real halo2 columns. pub fn assign_advice<'v, V, VR, A, AR>( &'v mut self, annotation: A, column: Column, offset: usize, to: V, - ) -> Result, Error> + ) -> Result>, Error> where V: Fn() -> Value + 'v, for<'vr> Assigned: From<&'vr VR>, @@ -212,18 +221,26 @@ impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { AR: Into, { // Actually set the value - let res = self.region.assign_advice(annotation, column, offset, &to); - // Cache the value - // Note that the `value_field` in `AssignedCell` might be `Value::unkonwn` if - // the column has different phase than current one, so we call to `to` - // again here to cache the value. - if res.is_ok() { + if offset - self.height_start < self.height_limit { + let res = self.region.assign_advice(annotation, column, offset, &to); + // Cache the value + // Note that the `value_field` in `AssignedCell` might be `Value::unkonwn` if + // the column has different phase than current one, so we call to `to` + // again here to cache the value. + if res.is_ok() { + to().map(|f| { + self.advice[column.index() - self.width_start][offset - self.height_start] = + Assigned::from(&f).evaluate(); + }); + } + Ok(Some(res?)) + } else { to().map(|f| { self.advice[column.index() - self.width_start][offset - self.height_start] = Assigned::from(&f).evaluate(); }); + Ok(None) } - res } pub fn eval(&self, offset: usize, expr: Expression) -> F { @@ -277,7 +294,7 @@ impl<'r, 'b, F: FieldExt> CachedRegion<'r, 'b, F> { pub fn code_hash(&self, n: U256) -> Value { if cfg!(feature = "poseidon-codehash") { - // only FieldExt is not enough for ToScalar trait so we have to make workaround + // only Field is not enough for ToScalar trait so we have to make workaround Value::known(rlc::value(&n.to_le_bytes(), F::from(256u64))) } else { self.challenges @@ -328,7 +345,7 @@ impl Hash for StoredExpression { } } -impl StoredExpression { +impl StoredExpression { pub fn assign( &self, region: &mut CachedRegion<'_, '_, F>, @@ -375,7 +392,7 @@ pub(crate) enum CellType { impl CellType { // The phase that given `Expression` becomes evaluateable. - fn expr_phase(expr: &Expression) -> u8 { + fn expr_phase(expr: &Expression) -> u8 { use Expression::*; match expr { Challenge(challenge) => challenge.phase() + 1, @@ -397,7 +414,7 @@ impl CellType { } /// Return the storage cell of the expression - pub(crate) fn storage_for_expr(expr: &Expression) -> CellType { + pub(crate) fn storage_for_expr(expr: &Expression) -> CellType { Self::storage_for_phase(Self::expr_phase::(expr)) } } @@ -410,7 +427,7 @@ pub(crate) struct CellColumn { pub(crate) expr: Expression, } -impl Expr for CellColumn { +impl Expr for CellColumn { fn expr(&self) -> Expression { self.expr.clone() } @@ -424,7 +441,7 @@ pub(crate) struct CellManager { columns: Vec>, } -impl CellManager { +impl CellManager { pub(crate) fn new( meta: &mut ConstraintSystem, height: usize, @@ -583,7 +600,7 @@ pub(crate) struct RandomLinearCombination { pub(crate) cells: [Cell; N], } -impl RandomLinearCombination { +impl RandomLinearCombination { const N_BYTES: usize = N; pub(crate) fn new(cells: [Cell; N], randomness: Expression) -> Self { @@ -598,7 +615,7 @@ impl RandomLinearCombination { region: &mut CachedRegion<'_, '_, F>, offset: usize, bytes: Option<[u8; N]>, - ) -> Result>, Error> { + ) -> Result>>, Error> { bytes.map_or(Err(Error::Synthesis), |bytes| { self.cells .iter() @@ -611,7 +628,7 @@ impl RandomLinearCombination { } } -impl Expr for RandomLinearCombination { +impl Expr for RandomLinearCombination { fn expr(&self) -> Expression { self.expression.clone() } @@ -624,9 +641,10 @@ pub(crate) type MemoryAddress = RandomLinearCombination>(bytes: &[E]) -> Expression { + pub(crate) fn expr>(bytes: &[E]) -> Expression { debug_assert!( bytes.len() <= MAX_N_BYTES_INTEGER, "Too many bytes to compose an integer in field" @@ -640,7 +658,7 @@ pub(crate) mod from_bytes { value } - pub(crate) fn value(bytes: &[u8]) -> F { + pub(crate) fn value(bytes: &[u8]) -> F { debug_assert!( bytes.len() <= MAX_N_BYTES_INTEGER, "Too many bytes to compose an integer in field" @@ -658,9 +676,10 @@ pub(crate) mod from_bytes { /// Decodes a field element from its binary representation pub(crate) mod from_bits { use crate::{evm_circuit::param::MAX_N_BYTES_INTEGER, util::Expr}; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; - pub(crate) fn expr>(bits: &[E]) -> Expression { + pub(crate) fn expr>(bits: &[E]) -> Expression { debug_assert!( bits.len() <= MAX_N_BYTES_INTEGER * 8, "Too many bits to compose an integer in field" @@ -674,7 +693,7 @@ pub(crate) mod from_bits { value } - pub(crate) fn value(bits: &[bool]) -> F { + pub(crate) fn value(bits: &[bool]) -> F { debug_assert!( bits.len() <= MAX_N_BYTES_INTEGER * 8, "Too many bits to compose an integer in field" @@ -695,9 +714,10 @@ pub(crate) mod rlc { use std::ops::{Add, Mul}; use crate::util::Expr; - use halo2_proofs::{arithmetic::FieldExt, plonk::Expression}; + use eth_types::Field; + use halo2_proofs::plonk::Expression; - pub(crate) fn expr>(expressions: &[E], randomness: E) -> Expression { + pub(crate) fn expr>(expressions: &[E], randomness: E) -> Expression { if !expressions.is_empty() { generic(expressions.iter().map(|e| e.expr()), randomness.expr()) } else { @@ -705,7 +725,7 @@ pub(crate) mod rlc { } } - pub(crate) fn value<'a, F: FieldExt, I>(values: I, randomness: F) -> F + pub(crate) fn value<'a, F: Field, I>(values: I, randomness: F) -> F where I: IntoIterator, ::IntoIter: DoubleEndedIterator, @@ -734,13 +754,13 @@ pub(crate) mod rlc { } } -/// Returns 2**by as FieldExt -pub(crate) fn pow_of_two(by: usize) -> F { - F::from(2).pow(&[by as u64, 0, 0, 0]) +/// Returns 2**by as Field +pub(crate) fn pow_of_two(by: usize) -> F { + F::from(2).pow([by as u64, 0, 0, 0]) } /// Returns 2**by as Expression -pub(crate) fn pow_of_two_expr(by: usize) -> Expression { +pub(crate) fn pow_of_two_expr(by: usize) -> Expression { Expression::Constant(pow_of_two(by)) } @@ -812,7 +832,7 @@ pub struct Inverter { inverses: Vec, } -impl Inverter { +impl Inverter { /// Create a new Inverter with preloaded inverses up to `preload_up_to` inclusive. pub fn new(preload_up_to: u64) -> Self { let mut inverses = (0..=preload_up_to).map(F::from).collect::>(); diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index aad855e111..d7ac141f8a 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -31,9 +31,11 @@ use halo2_proofs::{ plonk::{Error, Expression}, }; +mod tx_eip2930; mod tx_l1_fee; mod tx_l1_msg; +pub(crate) use tx_eip2930::TxEip2930Gadget; pub(crate) use tx_l1_fee::TxL1FeeGadget; pub(crate) use tx_l1_msg::TxL1MsgGadget; @@ -284,13 +286,17 @@ impl RestoreContextGadget { [U256::zero(); 9] } else { field_tags + .iter() .zip([0, 1, 2, 3, 4, 5, 6, 7, 8]) - .map(|(field_tag, i)| { + .map(|(&field_tag, i)| { let idx = step.rw_indices[i + rw_offset]; let rw = block.rws[idx]; debug_assert_eq!(rw.field_tag(), Some(field_tag as u64)); rw.call_context_value() }) + .collect::>() + .try_into() + .unwrap() }; for (cell, value) in [ @@ -1809,13 +1815,11 @@ pub(crate) fn get_copy_bytes( ) -> Vec { // read real copy bytes from padded memory words let padded_bytes: Vec = (0..copy_rwc_inc) - .map(|_| { + .flat_map(|_| { let mut bytes = rws.next().memory_word_pair().0.to_le_bytes(); bytes.reverse(); bytes }) - .into_iter() - .flatten() .collect(); let values: Vec = if copy_size == 0 { vec![0; 0] diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs new file mode 100644 index 0000000000..0230a0e472 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_eip2930.rs @@ -0,0 +1,129 @@ +use super::{CachedRegion, Cell}; +use crate::{ + evm_circuit::util::{ + constraint_builder::EVMConstraintBuilder, math_gadget::IsEqualGadget, select, + }, + table::TxFieldTag, + util::Expr, + witness::Transaction, +}; +use bus_mapping::circuit_input_builder::CopyDataType; +use eth_types::{ + evm_types::GasCost, + geth_types::{access_list_size, TxType}, + Field, +}; +use halo2_proofs::{ + circuit::Value, + plonk::{Error, Expression}, +}; + +/// Transaction EIP-2930 gadget to handle optional access-list +#[derive(Clone, Debug)] +pub(crate) struct TxEip2930Gadget { + is_eip2930_tx: IsEqualGadget, + access_list_address_len: Cell, + access_list_storage_key_len: Cell, +} + +impl TxEip2930Gadget { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + tx_id: Expression, + tx_type: Expression, + ) -> Self { + let is_eip2930_tx = IsEqualGadget::construct(cb, tx_type, (TxType::Eip2930 as u64).expr()); + + let [access_list_address_len, access_list_storage_key_len] = + cb.condition(is_eip2930_tx.expr(), |cb| { + let [address_len, storage_key_len] = [ + TxFieldTag::AccessListAddressesLen, + TxFieldTag::AccessListStorageKeysLen, + ] + .map(|field_tag| cb.tx_context(tx_id.expr(), field_tag, None)); + + // Let copy-circuit to write the tx-table's access list addresses into rw-table. + cb.copy_table_lookup( + tx_id.expr(), + CopyDataType::AccessListAddresses.expr(), + tx_id.expr(), + CopyDataType::AccessListAddresses.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + address_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + // Let copy-circuit to write the tx-table's access list storage keys into rw-table. + cb.copy_table_lookup( + tx_id.expr(), + CopyDataType::AccessListStorageKeys.expr(), + tx_id.expr(), + CopyDataType::AccessListStorageKeys.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + storage_key_len.expr(), + 0.expr(), + // TODO + 0.expr(), + ); + + [address_len, storage_key_len] + }); + + Self { + is_eip2930_tx, + access_list_address_len, + access_list_storage_key_len, + } + } + + pub(crate) fn assign( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + tx: &Transaction, + ) -> Result<(), Error> { + self.is_eip2930_tx.assign( + region, + offset, + F::from(tx.tx_type as u64), + F::from(TxType::Eip2930 as u64), + )?; + + let (access_list_address_len, access_list_storage_key_len) = + access_list_size(&tx.access_list); + + self.access_list_address_len.assign( + region, + offset, + Value::known(F::from(access_list_address_len)), + )?; + self.access_list_storage_key_len.assign( + region, + offset, + Value::known(F::from(access_list_storage_key_len)), + )?; + + Ok(()) + } + + pub(crate) fn gas_cost(&self) -> Expression { + select::expr( + self.is_eip2930_tx.expr(), + self.access_list_address_len.expr() * GasCost::ACCESS_LIST_PER_ADDRESS.expr() + + self.access_list_storage_key_len.expr() + * GasCost::ACCESS_LIST_PER_STORAGE_KEY.expr(), + 0.expr(), + ) + } + + pub(crate) fn rw_delta(&self) -> Expression { + // TODO + 0.expr() + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs index d9d19caf7f..f85387023c 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget/tx_l1_msg.rs @@ -6,22 +6,17 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroGadget}, select, }, - table::{AccountFieldTag, TxFieldTag::TxType as TxTypeField}, + table::AccountFieldTag, util::Expr, }; use eth_types::{geth_types::TxType, Field, U256}; -use halo2_proofs::{ - circuit::Value, - plonk::{Error, Expression}, -}; +use halo2_proofs::plonk::{Error, Expression}; /// L1 Msg Transaction gadget for some extra handling #[derive(Clone, Debug)] pub(crate) struct TxL1MsgGadget { /// tx is l1 msg tx tx_is_l1msg: IsEqualGadget, - /// tx type - tx_type: Cell, /// caller is empty is_caller_empty: IsZeroGadget, caller_codehash: Cell, @@ -30,10 +25,9 @@ pub(crate) struct TxL1MsgGadget { impl TxL1MsgGadget { pub(crate) fn construct( cb: &mut EVMConstraintBuilder, - tx_id: Expression, + tx_type: Expression, caller_address: Expression, ) -> Self { - let tx_type = cb.tx_context(tx_id.expr(), TxTypeField, None); let tx_is_l1msg = IsEqualGadget::construct(cb, tx_type.expr(), (TxType::L1Msg as u64).expr()); let caller_codehash = cb.query_cell_phase2(); @@ -71,7 +65,6 @@ impl TxL1MsgGadget { ); Self { - tx_type, tx_is_l1msg, caller_codehash, is_caller_empty, @@ -85,8 +78,6 @@ impl TxL1MsgGadget { tx_type: TxType, code_hash: U256, ) -> Result<(), Error> { - self.tx_type - .assign(region, offset, Value::known(F::from(tx_type as u64)))?; self.tx_is_l1msg.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 5980b66cfd..2658af9714 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1494,6 +1494,24 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ); } + // SHA256 Table + + pub(crate) fn sha256_table_lookup( + &mut self, + input_rlc: Expression, + input_len: Expression, + output_rlc: Expression, + ) { + self.add_lookup( + "sha256 lookup", + Lookup::Sha256Table { + input_rlc, + input_len, + output_rlc, + }, + ); + } + // ModExp table pub(crate) fn modexp_table_lookup( &mut self, diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index a947b06ab6..c32fc7c02d 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -2,7 +2,7 @@ use crate::evm_circuit::{ step::ExecutionState, util::{constraint_builder::EVMConstraintBuilder, CellType}, }; -use halo2_proofs::arithmetic::FieldExt; +use eth_types::Field; use itertools::Itertools; type StepSize = Vec<(CellType, ColumnSize)>; @@ -19,7 +19,7 @@ pub(crate) struct Instrument { impl Instrument { /// Collects `CellManager` stats from a compiled EVMCircuit in order to /// extract metrics. - pub(crate) fn on_gadget_built( + pub(crate) fn on_gadget_built( &mut self, execution_state: ExecutionState, cb: &EVMConstraintBuilder, @@ -107,6 +107,7 @@ pub(crate) struct ExecStateReport { pub(crate) block_table: StateReportRow, pub(crate) copy_table: StateReportRow, pub(crate) keccak_table: StateReportRow, + pub(crate) sha256_table: StateReportRow, pub(crate) exp_table: StateReportRow, pub(crate) sig_table: StateReportRow, pub(crate) modexp_table: StateReportRow, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs index 704fd60fef..ac2c26fd6e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs @@ -215,27 +215,27 @@ mod tests { #[test] fn test_addwords_0_0() { - try_test!(AddWordsTestContainer, vec![Word::from(0), Word::from(0), Word::from(0)], true); + try_test!(AddWordsTestContainer, [Word::from(0), Word::from(0), Word::from(0)], true); } #[test] fn test_addwords_1_1() { - try_test!(AddWordsTestContainer, vec![Word::from(1), Word::from(1), Word::from(2)], true); + try_test!(AddWordsTestContainer, [Word::from(1), Word::from(1), Word::from(2)], true); } #[test] fn test_addwords_1000_1000() { - try_test!(AddWordsTestContainer, vec![Word::from(1000), Word::from(1000), Word::from(2000)], true); + try_test!(AddWordsTestContainer, [Word::from(1000), Word::from(1000), Word::from(2000)], true); } #[test] fn test_addwords_to_wordmax() { - try_test!(AddWordsTestContainer, vec![Word::MAX - 1, Word::from(1), Word::MAX], true); + try_test!(AddWordsTestContainer, [Word::MAX - 1, Word::from(1), Word::MAX], true); } #[test] fn test_addwords_high_low_max() { - try_test!(AddWordsTestContainer, vec![WORD_LOW_MAX, WORD_HIGH_MAX, Word::MAX], true); + try_test!(AddWordsTestContainer, [WORD_LOW_MAX, WORD_HIGH_MAX, Word::MAX], true); } #[test] diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs index a0bb5a9c25..29b9971825 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/batched_is_zero.rs @@ -142,7 +142,7 @@ mod tests { fn test_batched_1_in_array_not_iszero() { try_test!( IsZeroGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(1), Word::from(0), @@ -167,14 +167,14 @@ mod tests { #[test] fn test_batched_single_cell_not_iszero() { - try_test!(IsZeroGadgetTestContainer, vec![WORD_LOW_MAX], false); + try_test!(IsZeroGadgetTestContainer, [WORD_LOW_MAX], false); } #[test] fn test_batched_wordmax_bytes_not_iszero() { try_test!( IsZeroGadgetTestContainer, - vec![ + [ WORD_LOW_MAX, WORD_LOW_MAX, WORD_LOW_MAX, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs index 6e63f100dd..9138b61bb4 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs @@ -153,7 +153,7 @@ impl ByteOrBitSizeGadget, vec![Word::from(0)], true) + try_test!(ByteSizeGadgetContainer, [Word::from(0)], true) } #[test] fn test_bytesize_1() { - try_test!(ByteSizeGadgetContainer, vec![Word::from(1)], true) + try_test!(ByteSizeGadgetContainer, [Word::from(1)], true) } #[test] fn test_bytesize_1_neq_0() { try_test!(ByteSizeGadgetContainer, - vec![Word::from(1)], + [Word::from(1)], false ); } @@ -271,30 +271,30 @@ mod tests { #[test] fn test_bytesize_256_eq_2() { try_test!(ByteSizeGadgetContainer, - vec![Word::from(256)], + [Word::from(256)], true ); } #[test] fn test_bytesize_wordmax_eq_32() { - try_test!(ByteSizeGadgetContainer, vec![Word::MAX], true) + try_test!(ByteSizeGadgetContainer, [Word::MAX], true) } #[test] fn test_bytesize_msb_0() { - try_test!(WordMSBGadgetContainer, vec![Word::from(0)], true) + try_test!(WordMSBGadgetContainer, [Word::from(0)], true) } #[test] fn test_bytesize_msb_1() { - try_test!(WordMSBGadgetContainer, vec![Word::from(1)], true) + try_test!(WordMSBGadgetContainer, [Word::from(1)], true) } #[test] fn test_bytesize_1_msb_neq_0() { try_test!(WordMSBGadgetContainer, - vec![Word::from(1)], + [Word::from(1)], false ); } @@ -302,13 +302,13 @@ mod tests { #[test] fn test_bytesize_512_msb_eq_2() { try_test!(WordMSBGadgetContainer, - vec![Word::from(512)], + [Word::from(512)], true ); } #[test] fn test_bytesize_258_msb_neq_2() { - try_test!(ByteSizeGadgetContainer, vec![Word::from(258)], true) + try_test!(ByteSizeGadgetContainer, [Word::from(258)], true) } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs index 1a6f657bf1..c2eb0cc596 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs @@ -141,7 +141,7 @@ mod tests { // a == b check try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -150,7 +150,7 @@ mod tests { fn test_cmpword_1_eq() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -159,7 +159,7 @@ mod tests { fn test_cmpword_wordmax_eq() { try_test!( CmpWordGadgetTestContainer, - vec![Word::MAX, Word::MAX], + [Word::MAX, Word::MAX], true, ); } @@ -168,7 +168,7 @@ mod tests { fn test_cmpword_0_neq_wordmax() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::MAX], + [Word::from(0), Word::MAX], false, ); } @@ -178,7 +178,7 @@ mod tests { fn test_cmpword_0_lt_1() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); } @@ -187,7 +187,7 @@ mod tests { fn test_cmpword_1_lt_wordmax() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::MAX], + [Word::from(1), Word::MAX], true, ); } @@ -196,7 +196,7 @@ mod tests { fn test_cmpword_1_lt_0() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -205,7 +205,7 @@ mod tests { fn test_cmpword_lowmax_lt_highmax() { try_test!( CmpWordGadgetTestContainer, - vec![WORD_LOW_MAX, WORD_HIGH_MAX], + [WORD_LOW_MAX, WORD_HIGH_MAX], true, ); } @@ -214,7 +214,7 @@ mod tests { fn test_cmpword_highmax_lt_lowmax() { try_test!( CmpWordGadgetTestContainer, - vec![WORD_HIGH_MAX, WORD_LOW_MAX], + [WORD_HIGH_MAX, WORD_LOW_MAX], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs index 5568ec54c4..66d5de4317 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs @@ -107,7 +107,7 @@ mod tests { // a == b check try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -116,7 +116,7 @@ mod tests { fn test_comparison_1_eq() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -125,7 +125,7 @@ mod tests { fn test_comparison_max_eq() { try_test!( ComparisonTestContainer, - vec![Word::from(1 << 4), Word::from(1 << 4)], + [Word::from(1 << 4), Word::from(1 << 4)], true, ); } @@ -134,7 +134,7 @@ mod tests { fn test_comparison_0_neq_max() { try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(1 << 4)], + [Word::from(0), Word::from(1 << 4)], false, ); } @@ -144,7 +144,7 @@ mod tests { fn test_comparison_0_lt_1() { try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); } @@ -153,7 +153,7 @@ mod tests { fn test_comparison_1_lt_max() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(1 << 4)], + [Word::from(1), Word::from(1 << 4)], true, ); } @@ -162,7 +162,7 @@ mod tests { fn test_comparison_1_lt_0() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -174,13 +174,13 @@ mod tests { let half_max_hi = U256([0, u64::MAX, 0, 0]); try_test!( ComparisonTestContainer, - vec![half_max_lo, half_max_hi], + [half_max_lo, half_max_hi], true, ); try_test!( ComparisonTestContainer, - vec![half_max_hi, half_max_lo], + [half_max_hi, half_max_lo], false, ); } @@ -189,7 +189,7 @@ mod tests { fn test_comparison_overflow() { try_test!( ComparisonTestContainer, - vec![Word::from(10000), Word::from(1 << (4 + 1))], + [Word::from(10000), Word::from(1 << (4 + 1))], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs index fddc49ccec..7129b84160 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs @@ -169,7 +169,7 @@ mod tests { fn test_constantdivisiongadget_0div5_rem0() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(0)], + [Word::from(0)], true, ); } @@ -178,7 +178,7 @@ mod tests { fn test_constantdivisiongadget_5div5_rem0() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(5)], + [Word::from(5)], true, ); } @@ -187,7 +187,7 @@ mod tests { fn test_constantdivisiongadget_1div5_rem1() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1)], + [Word::from(1)], true, ); } @@ -196,7 +196,7 @@ mod tests { fn test_constantdivisiongadget_1div5_rem4() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1)], + [Word::from(1)], false, ); } @@ -205,7 +205,7 @@ mod tests { fn test_constantdivisiongadget_quotient_overflow() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1u64 << (4 * 8)) * 5 + 1], + [Word::from(1u64 << (4 * 8)) * 5 + 1], false, ); } @@ -214,7 +214,7 @@ mod tests { fn test_constantdivisiongadget_33_div16_rem17() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(33)], + [Word::from(33)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs index 1c472181c6..54ee7fb673 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs @@ -99,7 +99,7 @@ mod tests { fn test_isequal_0() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -108,7 +108,7 @@ mod tests { fn test_isequal_1() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -117,7 +117,7 @@ mod tests { fn test_isequal_1000() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1000), Word::from(1000)], + [Word::from(1000), Word::from(1000)], true, ); } @@ -126,7 +126,7 @@ mod tests { fn test_isequal_1_0() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -135,7 +135,7 @@ mod tests { fn test_isequal_0_1() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs index 60744208b5..ce8a5ce8b2 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs @@ -113,20 +113,16 @@ mod tests { #[test] fn test_0_is_zero() { - try_test!(IsZeroGadgetTestContainer, vec![Word::from(0)], true); + try_test!(IsZeroGadgetTestContainer, [Word::from(0)], true); } #[test] fn test_1_is_not_zero() { - try_test!(IsZeroGadgetTestContainer, vec![Word::from(1)], false); + try_test!(IsZeroGadgetTestContainer, [Word::from(1)], false); } #[test] fn test_large_num_is_not_zero() { - try_test!( - IsZeroGadgetTestContainer, - vec![Word::from(10000)], - false, - ); + try_test!(IsZeroGadgetTestContainer, [Word::from(10000)], false,); } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs index d8b4342b45..b16e757132 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs @@ -152,12 +152,12 @@ mod tests { fn test_lt_expect() { try_test!( LtGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); try_test!( LtGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], false, ); } @@ -166,7 +166,7 @@ mod tests { fn test_lt_just_in_range() { try_test!( LtGadgetTestContainer, - vec![Word::from(1), Word::from((1u64 << (N * 8)) - 1)], + [Word::from(1), Word::from((1u64 << (N * 8)) - 1)], true, ); } @@ -175,7 +175,7 @@ mod tests { fn test_lt_out_of_range() { try_test!( LtGadgetTestContainer, - vec![Word::from(1), Word::from(2 << (N * 8))], + [Word::from(1), Word::from(2 << (N * 8))], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs index 601d9a88e7..a19c62dbcd 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs @@ -115,27 +115,19 @@ mod tests { fn test_ltword_expect() { try_test!( LtWordTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); + try_test!(LtWordTestContainer, [Word::from(1), Word::MAX], true,); + try_test!(LtWordTestContainer, [WORD_LOW_MAX, WORD_HIGH_MAX], true,); try_test!( LtWordTestContainer, - vec![Word::from(1), Word::MAX], + [Word::from(90), WORD_LOW_MAX], true, ); try_test!( LtWordTestContainer, - vec![WORD_LOW_MAX, WORD_HIGH_MAX], - true, - ); - try_test!( - LtWordTestContainer, - vec![Word::from(90), WORD_LOW_MAX], - true, - ); - try_test!( - LtWordTestContainer, - vec![Word::from(90), WORD_HIGH_MAX], + [Word::from(90), WORD_HIGH_MAX], true, ); } @@ -144,14 +136,14 @@ mod tests { fn test_ltword_unexpect() { try_test!( LtWordTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); - try_test!(LtWordTestContainer, vec![Word::MAX, Word::MAX], false,); + try_test!(LtWordTestContainer, [Word::MAX, Word::MAX], false,); try_test!( LtWordTestContainer, - vec![WORD_HIGH_MAX, WORD_LOW_MAX], + [WORD_HIGH_MAX, WORD_LOW_MAX], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs index e17f9c7b39..3ed000b4eb 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs @@ -127,17 +127,17 @@ mod tests { // a == b try_test!( MinMaxTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(5), Word::from(5)], + [Word::from(5), Word::from(5)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX], + [WORD_LOW_MAX, WORD_LOW_MAX], true, ); } @@ -147,17 +147,17 @@ mod tests { // min == a, max == b try_test!( MinMaxTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(3), Word::from(5)], + [Word::from(3), Word::from(5)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX], + [WORD_LOW_MAX, WORD_LOW_MAX], true, ); } @@ -167,17 +167,17 @@ mod tests { // min == b, max == a try_test!( MinMaxTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); try_test!( MinMaxTestContainer, - vec![Word::from(256), Word::from(3)], + [Word::from(256), Word::from(3)], false, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, Word::from(123456)], + [WORD_LOW_MAX, Word::from(123456)], false, ); } @@ -187,19 +187,19 @@ mod tests { // min == a, max == b try_test!( MinMaxTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(777), Word::from(44)], + [Word::from(777), Word::from(44)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX+1, WORD_LOW_MAX], + [WORD_LOW_MAX+1, WORD_LOW_MAX], true, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index bfe233df91..bd9726bebf 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -155,37 +155,37 @@ mod tests { fn test_mod_n_expected_rem() { try_test!( ModGadgetTestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(1), Word::from(0), Word::from(0)], + [Word::from(1), Word::from(0), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(548), Word::from(50), Word::from(48)], + [Word::from(548), Word::from(50), Word::from(48)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(30), Word::from(50), Word::from(30)], + [Word::from(30), Word::from(50), Word::from(30)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_LOW_MAX, Word::from(1024), Word::from(1023)], + [WORD_LOW_MAX, Word::from(1024), Word::from(1023)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_HIGH_MAX, Word::from(1024), Word::from(0)], + [WORD_HIGH_MAX, Word::from(1024), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_CELL_MAX, Word::from(2), Word::from(0)], + [WORD_CELL_MAX, Word::from(2), Word::from(0)], true, ); } @@ -202,7 +202,7 @@ mod tests { // ModGadget, the statement would be invalid in the ModGadget. try_test!( ModGadgetTestContainer, - vec![ + [ Word::from(2), Word::from(3), Word::from(0), @@ -213,27 +213,27 @@ mod tests { ); try_test!( ModGadgetTestContainer, - vec![Word::from(1), Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1), Word::from(1)], false, ); try_test!( ModGadgetTestContainer, - vec![Word::from(46), Word::from(50), Word::from(48)], + [Word::from(46), Word::from(50), Word::from(48)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_LOW_MAX, Word::from(999999), Word::from(888888)], + [WORD_LOW_MAX, Word::from(999999), Word::from(888888)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_CELL_MAX, Word::from(999999999), Word::from(666666666)], + [WORD_CELL_MAX, Word::from(999999999), Word::from(666666666)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_HIGH_MAX, Word::from(999999), Word::from(777777)], + [WORD_HIGH_MAX, Word::from(999999), Word::from(777777)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs index 1a212906a7..9caa9b687f 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs @@ -213,7 +213,7 @@ mod tests { // 0 * 0 + 0 == 0 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(0), Word::from(0), Word::from(0), @@ -225,7 +225,7 @@ mod tests { // 1 * 0 + 0 == 0 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(0), Word::from(0), @@ -237,7 +237,7 @@ mod tests { // 1 * 1 + 0 == 1 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(0), @@ -249,7 +249,7 @@ mod tests { // 1 * 1 + 1 == 2 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(1), @@ -261,7 +261,7 @@ mod tests { // 100 * 54 + 98 == 5498 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(98), @@ -273,7 +273,7 @@ mod tests { // 100 * 54 + low_max == low_max + 5400 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_LOW_MAX, @@ -285,7 +285,7 @@ mod tests { // 100 * 54 + high_max == high_max + 5400 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_HIGH_MAX, @@ -301,7 +301,7 @@ mod tests { // high_max + low_max + 1 == 0 with overflow 1 try_test!( MulAddGadgetContainer, - vec![ + [ WORD_LOW_MAX + 1, Word::from(1), WORD_HIGH_MAX, @@ -320,7 +320,7 @@ mod tests { // overflow == 73786976294838206460 + ((1<<64)-1)*((1<<64)-1)*6 try_test!( MulAddGadgetContainer, - vec![ + [ Word::MAX, Word::MAX, Word::MAX, @@ -336,7 +336,7 @@ mod tests { // 10 * 1 + 1 != 3 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(10), Word::from(1), Word::from(1), @@ -349,7 +349,7 @@ mod tests { // 1 * 1 + 1 != word_max, no underflow try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(1), @@ -362,7 +362,7 @@ mod tests { // 100 * 54 + high_max == high_max + 5400, no overflow try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_HIGH_MAX, @@ -375,7 +375,7 @@ mod tests { // (low_max + 1) * 1 + high_max == 0 with overflow 1 try_test!( MulAddGadgetContainer, - vec![ + [ WORD_LOW_MAX + 1, Word::from(1), WORD_HIGH_MAX, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs index e65992de83..eef61035b4 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs @@ -259,7 +259,7 @@ mod tests { // 0 * 0 + 0 == 0 * 2**256 + 0 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(0), Word::from(0), Word::from(0), @@ -271,7 +271,7 @@ mod tests { // 1 * 0 + 0 == 0 * 2**256 + 0 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(1), Word::from(0), Word::from(0), @@ -283,7 +283,7 @@ mod tests { // 1 * 1 + 0 == 0 * 2**256 + 1 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(0), @@ -295,7 +295,7 @@ mod tests { // 100 * 54 + 0 == 0 * 2**256 + 5400 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -307,7 +307,7 @@ mod tests { // 100 * 54 + max == 1 * 2**256 + 5400 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(1), @@ -319,7 +319,7 @@ mod tests { // 100 * 54 + low_max == 0 * 2**256 + 5400 + low_max try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -331,7 +331,7 @@ mod tests { // 100 * 54 + high_max == 0 * 2**256 + 5400 + high_max try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -347,7 +347,7 @@ mod tests { // 10 * 1 + 0 != 1 * 2**256 + 3 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(10), Word::from(1), Word::from(1), diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs index 1c5ce03e1e..04942e96e3 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs @@ -145,43 +145,43 @@ mod tests { // 0 * 0 = 0 try_test!( MulWordByU64TestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); // max * 0 = 0 try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(0), Word::from(0)], + [Word::MAX, Word::from(0), Word::from(0)], true, ); // 1 * 1 = 1 try_test!( MulWordByU64TestContainer, - vec![Word::from(1), Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1), Word::from(1)], true, ); // max * 1 = max try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(1), Word::MAX], + [Word::MAX, Word::from(1), Word::MAX], true, ); // 2 * 2 = 4 try_test!( MulWordByU64TestContainer, - vec![Word::from(2), Word::from(2), Word::from(4)], + [Word::from(2), Word::from(2), Word::from(4)], true, ); // 22222 * 500 = 11111000 try_test!( MulWordByU64TestContainer, - vec![Word::from(22222), Word::from(500), Word::from(11111000)], + [Word::from(22222), Word::from(500), Word::from(11111000)], true, ); // low_max * 2 = low_max << 1 try_test!( MulWordByU64TestContainer, - vec![WORD_LOW_MAX, Word::from(2), WORD_LOW_MAX << 1], + [WORD_LOW_MAX, Word::from(2), WORD_LOW_MAX << 1], true, ); } @@ -190,13 +190,13 @@ mod tests { fn test_mulwordu64_unexpect() { try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(1), Word::from(1)], + [Word::MAX, Word::from(1), Word::from(1)], false, ); // high_max * 2 = overflow try_test!( MulWordByU64TestContainer, - vec![WORD_HIGH_MAX, Word::from(2), WORD_HIGH_MAX << 1], + [WORD_HIGH_MAX, Word::from(2), WORD_HIGH_MAX << 1], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs index 898c4c4801..3f790f1589 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs @@ -128,12 +128,12 @@ mod tests { fn test_pairselect_eq() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2898), Word::from(2898), Word::from(2898)], + [Word::from(2898), Word::from(2898), Word::from(2898)], true, ); } @@ -142,17 +142,17 @@ mod tests { fn test_pairselect_expect_a_and_a() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(0), Word::from(1)], + [Word::from(0), Word::from(0), Word::from(1)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2898), Word::from(2898), Word::from(1)], + [Word::from(2898), Word::from(2898), Word::from(1)], true, ); try_test!( PairSelectionTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX, Word::from(1)], + [WORD_LOW_MAX, WORD_LOW_MAX, Word::from(1)], true, ); } @@ -161,12 +161,12 @@ mod tests { fn test_pairselect_expect_a_but_b() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(1), Word::from(0)], + [Word::from(0), Word::from(1), Word::from(0)], false, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2989), Word::from(1), Word::from(2989)], + [Word::from(2989), Word::from(1), Word::from(2989)], false, ); } @@ -175,12 +175,12 @@ mod tests { fn test_pairselect_expect_b_and_b() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(1), Word::from(0)], + [Word::from(0), Word::from(1), Word::from(0)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2989), Word::from(1), Word::from(2989)], + [Word::from(2989), Word::from(1), Word::from(2989)], true, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs index e9c796a5af..709eb6a12b 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs @@ -91,13 +91,13 @@ mod tests { #[test] fn test_rangecheck_just_in_range() { - try_test!(RangeCheckTestContainer, vec![Word::from(0)], true); + try_test!(RangeCheckTestContainer, [Word::from(0)], true); - try_test!(RangeCheckTestContainer, vec![Word::from(1)], true); + try_test!(RangeCheckTestContainer, [Word::from(1)], true); // max - 1 try_test!( RangeCheckTestContainer, - vec![Word::from((1u64 << 32) - 1)], + [Word::from((1u64 << 32) - 1)], true, ); } @@ -106,7 +106,7 @@ mod tests { fn test_rangecheck_out_of_range() { try_test!( RangeCheckTestContainer, - vec![Word::from(1u64 << 32)], + [Word::from(1u64 << 32)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs index c5e53cbb0e..0a4ef65a3e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -578,7 +578,7 @@ mod test { }; try_test!( ContractCreateGadgetContainer, - vec![ + [ caller_address.to_word(), Word::from(caller_nonce), rlp_len, @@ -593,12 +593,12 @@ mod test { fn create2_address() { let caller_address = mock::MOCK_ACCOUNTS[0]; let salt = Word::from(0xbeefcafedeadu64); - let code = vec![1, 2, 3, 4, 5, 6, 7, 8]; + let code = [1, 2, 3, 4, 5, 6, 7, 8]; let code_hash = Word::from(CodeDB::hash(&code).to_fixed_bytes()); - let keccak_code_hash = Word::from(keccak256(&code)); + let keccak_code_hash = Word::from(keccak256(code)); try_test!( ContractCreateGadgetContainer, - vec![ + [ caller_address.to_word(), Word::default(), 85u64.into(), diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs index 4df280c5fd..2340e75b17 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/test_util.rs @@ -48,7 +48,7 @@ pub(crate) const WORD_SIGNED_MAX: Word = U256([u64::MAX, u64::MAX, u64::MAX, i64 pub(crate) const WORD_SIGNED_MIN: Word = U256([0, 0, 0, i64::MIN as _]); pub(crate) fn generate_power_of_randomness(randomness: F) -> Vec { - (1..32).map(|exp| randomness.pow(&[exp, 0, 0, 0])).collect() + (1..32).map(|exp| randomness.pow([exp, 0, 0, 0])).collect() } pub(crate) trait MathGadgetContainer: Clone { @@ -96,6 +96,8 @@ impl UnitTestMathGadgetBaseCircuit { impl> Circuit for UnitTestMathGadgetBaseCircuit { type Config = (UnitTestMathGadgetBaseCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { UnitTestMathGadgetBaseCircuit { @@ -180,6 +182,7 @@ impl> Circuit for UnitTestMathGadgetBaseC &challenge_values, config.advices.to_vec(), MAX_STEP_HEIGHT * 3, + MAX_STEP_HEIGHT * 3, offset, ); config.step.state.execution_state.assign( diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index 58012f1b98..56335318c6 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -24,7 +24,6 @@ use eth_types::{ Field, ToLittleEndian, U256, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Value, plonk::{Error, Expression}, }; @@ -42,7 +41,7 @@ pub(crate) mod address_low { } /// Memory address trait to adapt for right and Uint overflow cases. -pub(crate) trait CommonMemoryAddressGadget { +pub(crate) trait CommonMemoryAddressGadget { fn construct_self(cb: &mut EVMConstraintBuilder) -> Self; /// Return the memory address (offset + length). diff --git a/zkevm-circuits/src/evm_circuit/util/padding_gadget.rs b/zkevm-circuits/src/evm_circuit/util/padding_gadget.rs index 1726f78486..7ee9c9adbb 100644 --- a/zkevm-circuits/src/evm_circuit/util/padding_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/padding_gadget.rs @@ -87,7 +87,7 @@ impl PaddingGadget { }; assert!(required_input_len <= 192); assert!(n_padded_zeroes < 192); - let power_of_rand = keccak_rand.map(|r| r.pow(&[n_padded_zeroes, 0, 0, 0])); + let power_of_rand = keccak_rand.map(|r| r.pow([n_padded_zeroes, 0, 0, 0])); ( required_input_len as u64, input_rlc * power_of_rand, diff --git a/zkevm-circuits/src/exp_circuit.rs b/zkevm-circuits/src/exp_circuit.rs index caaf9d91b5..afc3582b4c 100644 --- a/zkevm-circuits/src/exp_circuit.rs +++ b/zkevm-circuits/src/exp_circuit.rs @@ -537,7 +537,7 @@ impl ExpCircuit { Self { exp_events, max_exp_rows, - _marker: PhantomData::default(), + _marker: PhantomData, } } } diff --git a/zkevm-circuits/src/exp_circuit/dev.rs b/zkevm-circuits/src/exp_circuit/dev.rs index 51291c81c6..1c2114186b 100644 --- a/zkevm-circuits/src/exp_circuit/dev.rs +++ b/zkevm-circuits/src/exp_circuit/dev.rs @@ -14,6 +14,8 @@ use halo2_proofs::{ impl Circuit for ExpCircuit { type Config = (ExpCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index b5eb4abb17..d776358895 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -1027,7 +1027,7 @@ impl SubCircuit for KeccakCircuit { /// Return the minimum number of rows required to prove the block fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { let rows_per_chunk = (NUM_ROUNDS + 1) * get_num_rows_per_round(); - let aux_tables_rows = vec![ + let aux_tables_rows = [ normalize_table_size(6), normalize_table_size(4), normalize_table_size(3), diff --git a/zkevm-circuits/src/keccak_circuit/cell_manager.rs b/zkevm-circuits/src/keccak_circuit/cell_manager.rs index a2fdb30e57..3082daea02 100644 --- a/zkevm-circuits/src/keccak_circuit/cell_manager.rs +++ b/zkevm-circuits/src/keccak_circuit/cell_manager.rs @@ -1,7 +1,7 @@ use crate::keccak_circuit::util::extract_field; +use eth_types::Field; use gadgets::util::Expr; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Value, plonk::{Advice, Column, ConstraintSystem, Expression, VirtualCells}, poly::Rotation, @@ -18,7 +18,7 @@ pub(crate) struct Cell { pub(crate) rotation: i32, } -impl Cell { +impl Cell { pub(crate) fn new( meta: &mut VirtualCells, column: Column, @@ -75,13 +75,13 @@ impl Cell { } } -impl Expr for Cell { +impl Expr for Cell { fn expr(&self) -> Expression { self.expression.clone() } } -impl Expr for &Cell { +impl Expr for &Cell { fn expr(&self) -> Expression { self.expression.clone() } @@ -103,7 +103,7 @@ pub struct CellManager { num_unused_cells: usize, } -impl CellManager { +impl CellManager { pub(crate) fn new(height: usize) -> Self { Self { height, diff --git a/zkevm-circuits/src/keccak_circuit/dev.rs b/zkevm-circuits/src/keccak_circuit/dev.rs index 22e7db5e95..fffade5318 100644 --- a/zkevm-circuits/src/keccak_circuit/dev.rs +++ b/zkevm-circuits/src/keccak_circuit/dev.rs @@ -14,6 +14,8 @@ use halo2_proofs::{ impl Circuit for KeccakCircuit { type Config = (KeccakCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index bf7e7bbfc2..7c15785a68 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -2,7 +2,6 @@ use super::{cell_manager::*, param::*, util::*}; use crate::{evm_circuit::util::rlc, util::Challenges}; use eth_types::Field; use halo2_proofs::{ - arithmetic::FieldExt, circuit::Value, plonk::{Error, Expression}, }; @@ -137,7 +136,7 @@ pub(crate) struct KeccakRegion { pub(crate) rows: Vec>, } -impl KeccakRegion { +impl KeccakRegion { pub(crate) fn new() -> Self { Self { rows: Vec::new() } } @@ -923,7 +922,7 @@ pub fn multi_keccak( .par_iter() .flat_map_iter(|bytes| keccak_rows(bytes, challenges)) .collect(); - rows.extend(real_rows.into_iter()); + rows.extend(real_rows); debug!("keccak rows len without padding: {}", rows.len()); if let Some(capacity) = capacity { let padding_rows = { diff --git a/zkevm-circuits/src/keccak_circuit/table.rs b/zkevm-circuits/src/keccak_circuit/table.rs index be6d074798..8d64c595e6 100644 --- a/zkevm-circuits/src/keccak_circuit/table.rs +++ b/zkevm-circuits/src/keccak_circuit/table.rs @@ -258,6 +258,8 @@ mod tests { impl Circuit for TableTestCircuit { type Config = [TableColumn; 2]; type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { self.clone() diff --git a/zkevm-circuits/src/keccak_circuit/util.rs b/zkevm-circuits/src/keccak_circuit/util.rs index fa6acbd273..4a98fe1335 100644 --- a/zkevm-circuits/src/keccak_circuit/util.rs +++ b/zkevm-circuits/src/keccak_circuit/util.rs @@ -2,7 +2,7 @@ use super::{keccak_packed_multi::keccak_unusable_rows, param::*}; use eth_types::{Field, ToScalar, Word}; -use halo2_proofs::{circuit::Value, halo2curves::FieldExt}; +use halo2_proofs::circuit::Value; use std::env::var; /// Description of which bits (positions) a part contains @@ -233,7 +233,7 @@ pub(crate) fn get_num_bits_per_lookup_impl(range: usize, log_height: usize) -> u num_bits as usize } -pub(crate) fn extract_field(value: Value) -> F { +pub(crate) fn extract_field(value: Value) -> F { let mut field = F::zero(); let _ = value.map(|f| { field = f; diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 42a6b7c808..a709b5f5be 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -5,8 +5,8 @@ #![allow(incomplete_features)] // Needed by DummyGadget in evm circuit #![feature(adt_const_params)] -#![feature(array_zip)] #![feature(slice_group_by)] +#![feature(lazy_cell)] // Needed by some builder patterns in testing modules. #![cfg_attr(docsrs, feature(doc_cfg))] // Temporary until we have more of the crate implemented. @@ -39,6 +39,7 @@ pub mod sig_circuit; //pub mod root_circuit; mod evm_bus; pub mod modexp_circuit; +pub mod sha256_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/modexp_circuit/dev.rs b/zkevm-circuits/src/modexp_circuit/dev.rs index 20273b6ca8..88ca65b416 100644 --- a/zkevm-circuits/src/modexp_circuit/dev.rs +++ b/zkevm-circuits/src/modexp_circuit/dev.rs @@ -9,6 +9,8 @@ use halo2_proofs::{ impl Circuit for ModExpCircuit { type Config = (ModExpCircuitConfig, MockChallenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index fa79884522..99aa2acef1 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -8,17 +8,17 @@ use crate::{ witness, }; use eth_types::Field; -#[cfg(test)] -use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; use halo2_proofs::{ - circuit::{Layouter, Value}, + circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::bn256::Fr, - plonk::{Advice, Column, ConstraintSystem, Error, Fixed}, + plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed}, }; use itertools::Itertools; -#[cfg(test)] -use mpt_zktrie::mpt_circuits::gadgets::mpt_update::hash_traces; -use mpt_zktrie::mpt_circuits::{gadgets::poseidon::PoseidonLookup, mpt, types::Proof}; +use mpt_zktrie::mpt_circuits::{ + gadgets::{mpt_update::hash_traces, poseidon::PoseidonLookup}, + mpt, + types::Proof, +}; impl PoseidonLookup for PoseidonTable { fn lookup_columns_generic(&self) -> (Column, [Column; 6]) { @@ -153,10 +153,11 @@ impl SubCircuit for MptCircuit { } } -#[cfg(test)] impl Circuit for MptCircuit { type Config = (MptCircuitConfig, PoseidonTable, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self { diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index dc109ef2cb..0b9b55b384 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -89,20 +89,12 @@ pub struct PublicData { pub next_state_root: Hash, /// Withdraw Trie Root pub withdraw_trie_root: Hash, -} - -impl Default for PublicData { - fn default() -> Self { - PublicData { - chain_id: 0, - start_l1_queue_index: 0, - transactions: vec![], - prev_state_root: H256::zero(), - next_state_root: H256::zero(), - withdraw_trie_root: H256::zero(), - block_ctxs: Default::default(), - } - } + /// Max number of supported transactions + pub max_txs: usize, + /// Max number of supported calldata bytes + pub max_calldata: usize, + /// Max number of supported inner blocks in a chunk + pub max_inner_blocks: usize, } impl PublicData { @@ -154,18 +146,20 @@ impl PublicData { let result = iter::empty() .chain(self.block_ctxs.ctxs.iter().flat_map(|(block_num, block)| { // sanity check on coinbase & difficulty - let coinbase = get_coinbase_constant(); - assert_eq!( - coinbase, block.coinbase, - "[block {}] COINBASE const: {}, block.coinbase: {}", - block_num, coinbase, block.coinbase - ); - let difficulty = get_difficulty_constant(); - assert_eq!( - difficulty, block.difficulty, - "[block {}] DIFFICULTY const: {}, block.difficulty: {}", - block_num, difficulty, block.difficulty - ); + if !self.block_ctxs.relax_mode { + let coinbase = get_coinbase_constant(); + assert_eq!( + coinbase, block.coinbase, + "[block {}] COINBASE const: {}, block.coinbase: {}", + block_num, coinbase, block.coinbase + ); + let difficulty = get_difficulty_constant(); + assert_eq!( + difficulty, block.difficulty, + "[block {}] DIFFICULTY const: {}, block.difficulty: {}", + block_num, difficulty, block.difficulty + ); + } let num_all_txs = num_all_txs_in_blocks .get(block_num) @@ -224,14 +218,93 @@ impl PublicData { H256(pi_hash) } + + fn difficulty(&self) -> Word { + self.block_ctxs + .ctxs + .first_key_value() + .map(|(_, blk)| blk.difficulty) + .unwrap_or_else(get_difficulty_constant) + } + + fn coinbase(&self) -> Address { + self.block_ctxs + .ctxs + .first_key_value() + .map(|(_, blk)| blk.coinbase) + .unwrap_or_else(get_coinbase_constant) + } + + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn q_block_context_start_offset(&self) -> usize { + // we start assigning block context values at offset == 1. + 1 + } + + fn q_block_context_end_offset(&self) -> usize { + self.q_block_context_start_offset() + self.max_inner_blocks * BLOCK_HEADER_BYTES_NUM + } + + fn q_tx_hashes_start_offset(&self) -> usize { + self.q_block_context_end_offset() + } + + fn q_tx_hashes_end_offset(&self) -> usize { + self.q_tx_hashes_start_offset() + KECCAK_DIGEST_SIZE * self.max_txs + } + + fn data_bytes_start_offset(&self) -> usize { + // we start assigning data bytes at offset == 0. + 0 + } + + fn data_bytes_end_offset(&self) -> usize { + self.data_bytes_start_offset() + + self.max_inner_blocks * BLOCK_HEADER_BYTES_NUM + + self.max_txs * KECCAK_DIGEST_SIZE + } + + fn pi_bytes_start_offset(&self) -> usize { + self.data_bytes_end_offset() + + 1 // a row is reserved for the keccak256(rlc(data_bytes)) == data_hash lookup. + + 1 // new row. + } + + fn pi_bytes_end_offset(&self) -> usize { + self.pi_bytes_start_offset() + N_BYTES_U64 + N_BYTES_WORD * 4 + } + + fn pi_hash_start_offset(&self) -> usize { + self.pi_bytes_end_offset() + + 1 // a row is reserved for the keccak256(rlc(pi_bytes)) == pi_hash lookup. + + 1 // new row. + } + + fn pi_hash_end_offset(&self) -> usize { + self.pi_hash_start_offset() + KECCAK_DIGEST_SIZE + } + + fn constants_start_offset(&self) -> usize { + // there is no keccak lookup after the region where pi_hash is assigned. Hence we start + // assigning constants (coinbase and difficulty) from where we ended the previous + // assignment. + self.pi_hash_end_offset() + 1 // new row. + } + + fn constants_end_offset(&self) -> usize { + self.constants_start_offset() + N_BYTES_ACCOUNT_ADDRESS + N_BYTES_WORD + } } impl BlockContext { - fn padding(chain_id: u64) -> Self { + fn padding(chain_id: u64, difficulty: Word, coinbase: Address) -> Self { Self { chain_id, - coinbase: get_coinbase_constant(), - difficulty: get_difficulty_constant(), + coinbase, + difficulty, gas_limit: 0, number: Default::default(), timestamp: Default::default(), @@ -244,20 +317,24 @@ impl BlockContext { impl Default for BlockContext { fn default() -> Self { - Self::padding(0) + Self::padding(0, get_difficulty_constant(), get_coinbase_constant()) } } +enum RpiFieldType { + /// Default mode where no special behaviour is observed. + DefaultType, + /// Whether the assigned field represents a block context field. + BlockCtx, + /// Whether the assigned field represents the Keccak hi-lo decomposition. + KeccakHiLo, + /// Whether the assigned field represents the block's coinbase/difficulty constants. + Constant, +} + /// Config for PiCircuit #[derive(Clone, Debug)] pub struct PiCircuitConfig { - /// Max number of supported transactions - max_txs: usize, - /// Max number of supported calldata bytes - max_calldata: usize, - /// Max number of supported inner blocks in a chunk - max_inner_blocks: usize, - /// dedicated column to store the difficulty, coinbase constants constant: Column, @@ -299,12 +376,6 @@ pub struct PiCircuitConfig { /// Circuit configuration arguments pub struct PiCircuitConfigArgs { - /// Max number of supported transactions - pub max_txs: usize, - /// Max number of supported calldata bytes - pub max_calldata: usize, - /// Max number of supported blocks in a chunk - pub max_inner_blocks: usize, /// TxTable pub tx_table: TxTable, /// BlockTable @@ -322,9 +393,6 @@ impl SubCircuitConfig for PiCircuitConfig { fn new( meta: &mut ConstraintSystem, Self::ConfigArgs { - max_txs, - max_calldata, - max_inner_blocks, block_table, tx_table, keccak_table, @@ -516,7 +584,7 @@ impl SubCircuitConfig for PiCircuitConfig { "rpi == dummy_tx_hash", meta.query_advice(rpi, Rotation::cur()), iter::once(1.expr()) - .chain(challenges.evm_word_powers_of_randomness::<31>().into_iter()) + .chain(challenges.evm_word_powers_of_randomness::<31>()) .rev() .zip( get_dummy_tx_hash() @@ -546,12 +614,12 @@ impl SubCircuitConfig for PiCircuitConfig { // | lo | b0 | b15*2^120+... | b31*r^31+...| // We use copy constraints to - // 1. copy the RLC(data_bytes, keccak_rand) in the `rpi_rlc_acc` column - // to the `rpi` column on the row that q_keccak = 1 for data bytes. - // 2. copy the len(data_bytes) in the `rpi_length_acc` column to the - // `rpi_length_acc` column on the row that q_keccak = 1 for data bytes. - // 3. copy the RLC(data_hash_bytes, word_rand) in the `rpi_rlc_acc` column - // to the `rpi_rlc_acc` column on the row that q_keccak = 1 for data hash. + // 1. copy the RLC(data_bytes, keccak_rand) in the `rpi_rlc_acc` column to the `rpi` column + // on the row that q_keccak = 1 for data bytes. + // 2. copy the len(data_bytes) in the `rpi_length_acc` column to the `rpi_length_acc` column + // on the row that q_keccak = 1 for data bytes. + // 3. copy the RLC(data_hash_bytes, word_rand) in the `rpi_rlc_acc` column to the + // `rpi_rlc_acc` column on the row that q_keccak = 1 for data hash. // The layout for entire pi circuit looks like // data bytes: | rpi | rpi_bytes | rpi_bytes_acc | rpi_rlc_acc | rpi_length_acc | @@ -584,7 +652,7 @@ impl SubCircuitConfig for PiCircuitConfig { input_exprs .into_iter() - .zip(keccak_table_exprs.into_iter()) + .zip(keccak_table_exprs) .map(|(input, table)| (q_keccak.expr() * input, table)) .collect() }); @@ -622,9 +690,6 @@ impl SubCircuitConfig for PiCircuitConfig { ); Self { - max_txs, - max_calldata, - max_inner_blocks, block_table, tx_table, keccak_table, @@ -663,7 +728,63 @@ struct Connections { } impl PiCircuitConfig { - #[allow(clippy::type_complexity)] + /// Assign witness data to the PI circuit's layout. + /// + /// The layout of the PI circuit is as follows: + /// + /// |----------|------------------------|--------------------------| + /// | | rpi initialise | | + /// | | block\[0\].number | | + /// | | block\[0\].timestamp | | + /// | | block\[0\].base_fee | | + /// | | block\[0\].gas_limit | | + /// | | block\[0\].num_all_txs | | + /// | | block\[1\].number | <- q_block_context == 1 | + /// | *PART 1* | ... | | + /// | | block\[n\].num_all_txs | | + /// | ASSIGN | PADDING | | + /// | DATA | ... | | + /// | BYTES | PADDING | | + /// | |------------------------|--------------------------| + /// | | tx_hash\[0\] | | + /// | | tx_hash\[1\] | | + /// | | ... | | + /// | | tx_hash\[n\] | <- q_tx_hashes == 1 | + /// | | DUMMY_TX_HASH | | + /// | | ... | | + /// | | DUMMY_TX_HASH | | + /// | |------------------------|--------------------------| + /// | | rlc(data_bytes) | <- q_keccak == 1 | + /// |----------|------------------------|--------------------------| + /// | | rpi initialise | | + /// | | chain_id | | + /// | *PART 2* | prev_state_root | | + /// | | next_state_root | | + /// | ASSIGN | withdraw_trie_root | | + /// | PI | data_hash | | + /// | BYTES |------------------------|--------------------------| + /// | | rlc(pi_bytes) | <- q_keccak == 1 | + /// |----------|------------------------|--------------------------| + /// | *PART 3* | rpi initialise | | + /// | ASSIGN | pi_hash_hi | | + /// | PI HASH | pi_hash_lo | | + /// |----------|------------------------|--------------------------| + /// | *PART 4* | rpi initialise | | + /// | ASSIGN | coinbase | | + /// | CONSTS | difficulty | | + /// |----------|------------------------|--------------------------| + /// + /// Where each one of the rows above, i.e. block\[0\].number, block\[0\].timestamp, ..., + /// pi_hash_lo, coinbase, difficulty are assigned using the assign_field method. + /// + /// Each `field` takes multiple rows in the actual circuit layout depending on how many bytes + /// it takes to represent the said field. For instance, pi_hash_lo represent the lower 16 bytes + /// of the public input hash, hence we need 16 rows to assign the pi_hash_lo field. In + /// addition, for those rows represent the field, the `q_field_step` fixed column is enabled. + /// + /// Since we already know the maximum number of blocks and txs that we will assign in this + /// layout, all the `q_*` columns are fixed. For blocks and txs, we pad the remaining layout + /// with a padded field and mark it by the `is_rpi_padding` identifier. fn assign( &self, region: &mut Region<'_, F>, @@ -672,165 +793,190 @@ impl PiCircuitConfig { tx_value_cells: &[AssignedCell], challenges: &Challenges>, ) -> Result<(PiHashExport, Connections), Error> { - let block_values = &public_data.block_ctxs; - let chain_id = block_values - .ctxs - .first_key_value() - .map_or(0, |(_, context)| context.chain_id); - let tx_hashes = public_data - .transactions - .iter() - .map(|tx| tx.hash) - .collect::>(); - let num_all_txs_in_blocks = public_data.get_num_all_txs(); + // 1. Assign data bytes. + let (offset, data_hash_rlc_cell) = self.assign_data_bytes( + region, + 0, /* offset == 0 */ + public_data, + block_value_cells, + tx_value_cells, + challenges, + )?; + debug_assert_eq!(offset, public_data.pi_bytes_start_offset()); - let mut offset = 0; + // 2. Assign public input bytes. + let (offset, pi_hash_rlc_cell, connections) = self.assign_pi_bytes( + region, + offset, + public_data, + block_value_cells, + tx_value_cells, + &data_hash_rlc_cell, + challenges, + )?; + debug_assert_eq!(offset, public_data.pi_hash_start_offset()); + + // 3. Assign public input hash (hi, lo) decomposition. + let (offset, pi_hash_cells) = + self.assign_pi_hash(region, offset, public_data, &pi_hash_rlc_cell, challenges)?; + debug_assert_eq!(offset, public_data.constants_start_offset()); + + // 4. Assign block coinbase and difficulty. + let offset = + self.assign_constants(region, offset, public_data, block_value_cells, challenges)?; + debug_assert_eq!(offset, public_data.constants_end_offset() + 1); + + Ok((pi_hash_cells, connections)) + } + + /// Assign data bytes, that represent the pre-image to data_hash. + /// i.e. keccak256(rlc(data_bytes)) == data_hash. + fn assign_data_bytes( + &self, + region: &mut Region<'_, F>, + offset: usize, + public_data: &PublicData, + block_value_cells: &[AssignedCell], + tx_value_cells: &[AssignedCell], + challenges: &Challenges>, + ) -> Result<(usize, AssignedCell), Error> { + // Initialise the RLC accumulator and length values. + let (mut offset, mut rpi_rlc_acc, mut rpi_length) = self.assign_rlc_init(region, offset)?; + + // Enable fixed columns for block context. + for q_offset in + public_data.q_block_context_start_offset()..public_data.q_block_context_end_offset() + { + region.assign_fixed( + || "q_block_context", + self.q_block_context, + q_offset, + || Value::known(F::one()), + )?; + } + // Enable fixed columns for tx hashes. + for q_offset in public_data.q_tx_hashes_start_offset()..public_data.q_tx_hashes_end_offset() + { + region.assign_fixed( + || "q_tx_hashes", + self.q_tx_hashes, + q_offset, + || Value::known(F::one()), + )?; + } + // Enable RLC accumulator consistency check throughout the above rows. + for q_offset in public_data.data_bytes_start_offset()..public_data.data_bytes_end_offset() { + self.q_not_end.enable(region, q_offset)?; + } + + // Assign block context values. + let n_block_ctxs = public_data.block_ctxs.ctxs.len(); + let num_txs_by_block = public_data.get_num_all_txs(); + let mut block_table_offset = 1; let mut block_copy_cells = vec![]; - let mut tx_copy_cells = vec![]; - let mut block_table_offset = 1; // first row of block is all-zeros. - - let mut rpi_length_acc = 0u64; - let mut rpi_rlc_acc = Value::known(F::zero()); - - let dummy_tx_hash = get_dummy_tx_hash(); - - /////////////////////////////////// - /////// assign data bytes //////// - /////////////////////////////////// - let data_bytes_start_row = 0; - let data_bytes_end_row = - self.max_inner_blocks * BLOCK_HEADER_BYTES_NUM + self.max_txs * KECCAK_DIGEST_SIZE; - self.assign_rlc_start(region, &mut offset, &mut rpi_rlc_acc, &mut rpi_length_acc)?; - // assign block contexts - for (i, block) in block_values + for (i, block) in public_data + .block_ctxs .ctxs .values() .cloned() - .chain( - (block_values.ctxs.len()..self.max_inner_blocks) - .into_iter() - .map(|_| BlockContext::padding(chain_id)), - ) + .chain(std::iter::repeat(BlockContext::padding( + public_data.chain_id(), + public_data.difficulty(), + public_data.coinbase(), + ))) + .take(public_data.max_inner_blocks) .enumerate() { - let is_rpi_padding = i >= block_values.ctxs.len(); - let block_num = block.number.as_u64(); - let num_all_txs = num_all_txs_in_blocks.get(&block_num).cloned().unwrap_or(0) as u16; - log::debug!( - "[pi assign] num_all_txs in block {}: {}", - block_num, - num_all_txs - ); + let is_rpi_padding = i >= n_block_ctxs; + let num_all_txs = num_txs_by_block + .get(&block.number.as_u64()) + .cloned() + .unwrap_or(0) as u16; // Assign fields in pi columns and connect them to block table - let fields = vec![ + for (field_value_be_bytes, field_offset) in [ + // block number ( block.number.as_u64().to_be_bytes().to_vec(), BLOCK_NUM_OFFSET, - ), // number + ), + // block timestamp ( block.timestamp.as_u64().to_be_bytes().to_vec(), TIMESTAMP_OFFSET, - ), // timestamp - (block.base_fee.to_be_bytes().to_vec(), BASE_FEE_OFFSET), // base_fee - (block.gas_limit.to_be_bytes().to_vec(), GAS_LIMIT_OFFSET), // gas_limit - (num_all_txs.to_be_bytes().to_vec(), NUM_ALL_TXS_OFFSET), // num_all_txs - ]; - for (bytes, block_offset) in fields { - let cells = self.assign_field_in_pi( + ), + // base fee + (block.base_fee.to_be_bytes().to_vec(), BASE_FEE_OFFSET), + // gas limit + (block.gas_limit.to_be_bytes().to_vec(), GAS_LIMIT_OFFSET), + // num txs in block + (num_all_txs.to_be_bytes().to_vec(), NUM_ALL_TXS_OFFSET), + ] { + let (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length, cells) = self.assign_field( region, - &mut offset, - bytes.as_slice(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - true, + offset, + field_value_be_bytes.as_slice(), + RpiFieldType::BlockCtx, is_rpi_padding, - false, + rpi_rlc_acc, + rpi_length, challenges, )?; + offset = tmp_offset; + rpi_rlc_acc = tmp_rpi_rlc_acc; + rpi_length = tmp_rpi_length; block_copy_cells.push(( cells[RPI_CELL_IDX].clone(), - block_table_offset + block_offset, + block_table_offset + field_offset, )); } block_table_offset += BLOCK_LEN; } - - let q_block_context_start_row = 1; - let q_block_context_end_row = 1 + BLOCK_HEADER_BYTES_NUM * self.max_inner_blocks; - for i in q_block_context_start_row..q_block_context_end_row { - // assign q_block_context - region.assign_fixed( - || "q_block_context", - self.q_block_context, - i, - || Value::known(F::one()), + // Copy block context fields to block table + for (block_cell, row_offset) in block_copy_cells.into_iter() { + region.constrain_equal( + block_cell.cell(), + block_value_cells[row_offset - 1].cell(), /* -1 for block table's first row of + * all-zeros */ )?; } - debug_assert_eq!(offset, 1 + BLOCK_HEADER_BYTES_NUM * self.max_inner_blocks); - - // assign tx hashes - let q_tx_hashes_start_row = offset; - let q_tx_hashes_end_row = q_tx_hashes_start_row + KECCAK_DIGEST_SIZE * self.max_txs; - let num_txs = tx_hashes.len(); + // Assign tx hash values. + let n_txs = public_data.transactions.len(); + let mut tx_copy_cells = vec![]; let mut data_bytes_rlc = None; let mut data_bytes_length = None; - for (i, tx_hash) in tx_hashes - .into_iter() - .chain( - (0..self.max_txs - num_txs) - .into_iter() - .map(|_| dummy_tx_hash), - ) + for (i, tx_hash) in public_data + .transactions + .iter() + .map(|tx| tx.hash) + .chain(std::iter::repeat(get_dummy_tx_hash())) + .take(public_data.max_txs) .enumerate() { - let is_rpi_padding = i >= num_txs; + let is_rpi_padding = i >= n_txs; - let cells = self.assign_field_in_pi( + let (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length, cells) = self.assign_field( region, - &mut offset, + offset, &tx_hash.to_fixed_bytes(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, + RpiFieldType::DefaultType, is_rpi_padding, - false, + rpi_rlc_acc, + rpi_length, challenges, )?; + offset = tmp_offset; + rpi_rlc_acc = tmp_rpi_rlc_acc; + rpi_length = tmp_rpi_length; tx_copy_cells.push(cells[RPI_CELL_IDX].clone()); - - if i == self.max_txs - 1 { + if i == public_data.max_txs - 1 { data_bytes_rlc = Some(cells[RPI_RLC_ACC_CELL_IDX].clone()); data_bytes_length = Some(cells[RPI_LENGTH_ACC_CELL_IDX].clone()); } } - for i in q_tx_hashes_start_row..q_tx_hashes_end_row { - region.assign_fixed( - || "q_tx_hashes", - self.q_tx_hashes, - i, - || Value::known(F::one()), - )?; - } - - assert_eq!(offset, data_bytes_end_row + 1); - - // the last row of data bytes part is disabled - for i in data_bytes_start_row..data_bytes_end_row { - self.q_not_end.enable(region, i)?; - } - - // copy block context fields to block table - for (block_cell, row_offset) in block_copy_cells.into_iter() { - region.constrain_equal( - block_cell.cell(), - block_value_cells[row_offset - 1].cell(), /* -1 for block table's first row of - * all-zeros */ - )?; - } - // copy tx_hashes to tx table + // Copy tx_hashes to tx table for (i, tx_hash_cell) in tx_copy_cells.into_iter().enumerate() { region.constrain_equal( tx_hash_cell.cell(), @@ -838,493 +984,480 @@ impl PiCircuitConfig { )?; } - // assign keccak row for computing data_hash = keccak256(data bytes) - let data_hash_row = offset; + // Assign row for validating lookup to check: + // data_hash == keccak256(rlc(data_bytes)) data_bytes_rlc.unwrap().copy_advice( || "data_bytes_rlc in the rpi col", region, self.raw_public_inputs, - data_hash_row, + offset, )?; - let data_hash = public_data.get_data_hash(); - let data_hash_rlc = rlc_be_bytes(&data_hash.to_fixed_bytes(), challenges.evm_word()); data_bytes_length.unwrap().copy_advice( || "data_bytes_length in the rpi_length_acc col", region, self.rpi_length_acc, - data_hash_row, - )?; - let data_hash_rlc_cell = region.assign_advice( - || "data_hash_rlc", - self.rpi_rlc_acc, - data_hash_row, - || data_hash_rlc, + offset, )?; - self.q_keccak.enable(region, data_hash_row)?; - offset += 1; + let data_hash_rlc_cell = { + let data_hash_rlc = rlc_be_bytes( + &public_data.get_data_hash().to_fixed_bytes(), + challenges.evm_word(), + ); + region.assign_advice( + || "data_hash_rlc", + self.rpi_rlc_acc, + offset, + || data_hash_rlc, + )? + }; + self.q_keccak.enable(region, offset)?; + + Ok((offset + 1, data_hash_rlc_cell)) + } + + /// Assign public input bytes, that represent the pre-image to pi_hash. + /// i.e. keccak256(rlc(pi_bytes)) == pi_hash. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::type_complexity)] + fn assign_pi_bytes( + &self, + region: &mut Region<'_, F>, + offset: usize, + public_data: &PublicData, + block_value_cells: &[AssignedCell], + tx_value_cells: &[AssignedCell], + data_hash_rlc_cell: &AssignedCell, + challenges: &Challenges>, + ) -> Result<(usize, AssignedCell, Connections), Error> { + let (mut offset, mut rpi_rlc_acc, mut rpi_length) = self.assign_rlc_init(region, offset)?; + + // Enable RLC accumulator consistency check throughout the above rows. + for q_offset in public_data.pi_bytes_start_offset()..public_data.pi_bytes_end_offset() { + self.q_not_end.enable(region, q_offset)?; + } + + // Assign [chain_id, prev_state_root, state_root, withdraw_trie_root]. + let mut cells = vec![]; + let rpi_cells = [ + public_data.chain_id.to_be_bytes().to_vec(), + public_data.prev_state_root.to_fixed_bytes().to_vec(), + public_data.next_state_root.to_fixed_bytes().to_vec(), + public_data.withdraw_trie_root.to_fixed_bytes().to_vec(), + ] + .iter() + .map(|value_be_bytes| { + (offset, rpi_rlc_acc, rpi_length, cells) = self.assign_field( + region, + offset, + value_be_bytes, + RpiFieldType::DefaultType, + false, // no padding in this case. + rpi_rlc_acc, + rpi_length, + challenges, + )?; + Ok(cells[RPI_CELL_IDX].clone()) + }) + .collect::, Error>>()?; - ///////////////////////////////// - ///////// assign pi bytes /////// - ///////////////////////////////// - let pi_bytes_start_row = offset; - let pi_bytes_end_row = pi_bytes_start_row + N_BYTES_U64 + N_BYTES_WORD * 4; - self.assign_rlc_start(region, &mut offset, &mut rpi_rlc_acc, &mut rpi_length_acc)?; - // assign chain_id - let cells = self.assign_field_in_pi( - region, - &mut offset, - &public_data.chain_id.to_be_bytes(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - false, - challenges, - )?; - let chain_id_cell = cells[RPI_CELL_IDX].clone(); // copy chain_id to block table - for block_idx in 0..self.max_inner_blocks { + for block_idx in 0..public_data.max_inner_blocks { region.constrain_equal( - chain_id_cell.cell(), + rpi_cells[0].cell(), block_value_cells[block_idx * BLOCK_LEN + CHAIN_ID_OFFSET].cell(), )?; } // copy chain_id to tx table - for tx_id in 0..self.max_txs { + for tx_id in 0..public_data.max_txs { region.constrain_equal( - chain_id_cell.cell(), + rpi_cells[0].cell(), tx_value_cells[tx_id * TX_LEN + CHAIN_ID_OFFSET_IN_TX - 1].cell(), )?; } - - // assign roots - // 1. prev_state_root - // 2. after_state_root - // 3. withdraw_trie_root - let roots = vec![ - public_data.prev_state_root.to_fixed_bytes(), - public_data.next_state_root.to_fixed_bytes(), - public_data.withdraw_trie_root.to_fixed_bytes(), - ]; - let root_cells = roots - .into_iter() - .map(|root_be_bytes| -> Result, Error> { - let cells = self.assign_field_in_pi( - region, - &mut offset, - root_be_bytes.as_slice(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - false, - challenges, - )?; - Ok(cells[RPI_CELL_IDX].clone()) - }) - .collect::, Error>>()?; + // connections to be done with other sub-circuits. let connections = Connections { - start_state_root: root_cells[0].clone(), - end_state_root: root_cells[1].clone(), - withdraw_root: root_cells[2].clone(), + start_state_root: rpi_cells[1].clone(), + end_state_root: rpi_cells[2].clone(), + withdraw_root: rpi_cells[3].clone(), }; - // assign data_hash - let cells = self.assign_field_in_pi( + // Assign data_hash + (offset, _, _, cells) = self.assign_field( region, - &mut offset, - &data_hash.to_fixed_bytes(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - false, + offset, + &public_data.get_data_hash().to_fixed_bytes(), + RpiFieldType::DefaultType, + false, // no padding in this case + rpi_rlc_acc, + rpi_length, challenges, )?; let data_hash_cell = cells[RPI_CELL_IDX].clone(); let pi_bytes_rlc = cells[RPI_RLC_ACC_CELL_IDX].clone(); let pi_bytes_length = cells[RPI_LENGTH_ACC_CELL_IDX].clone(); - // copy data_hash down here + // Copy data_hash value we collected from assigning data bytes. region.constrain_equal(data_hash_rlc_cell.cell(), data_hash_cell.cell())?; - for i in pi_bytes_start_row..pi_bytes_end_row { - self.q_not_end.enable(region, i)?; - } - - // +1 for the rlc_start row - assert_eq!(offset, pi_bytes_end_row + 1); - - // assign keccak row for computing pi_hash = keccak256(pi_bytes) - let pi_hash_row = offset; + // Assign row for validating lookup to check: + // pi_hash == keccak256(rlc(pi_bytes)) pi_bytes_rlc.copy_advice( || "pi_bytes_rlc in the rpi col", region, self.raw_public_inputs, - pi_hash_row, + offset, )?; - let pi_hash = public_data.get_pi(); - let pi_hash_rlc = rlc_be_bytes(&pi_hash.to_fixed_bytes(), challenges.evm_word()); pi_bytes_length.copy_advice( || "pi_bytes_length in the rpi_length_acc col", region, self.rpi_length_acc, - pi_hash_row, - )?; - let pi_hash_rlc_cell = region.assign_advice( - || "pi_hash_rlc", - self.rpi_rlc_acc, - pi_hash_row, - || pi_hash_rlc, + offset, )?; - self.q_keccak.enable(region, pi_hash_row)?; - offset += 1; + let pi_hash_rlc_cell = { + let pi_hash_rlc = rlc_be_bytes( + &public_data.get_pi().to_fixed_bytes(), + challenges.evm_word(), + ); + region.assign_advice(|| "pi_hash_rlc", self.rpi_rlc_acc, offset, || pi_hash_rlc)? + }; + self.q_keccak.enable(region, offset)?; + + Ok((offset + 1, pi_hash_rlc_cell, connections)) + } - ////////////////////////////////////////////////// - //// assign pi_hash (high, low)-decomposition //// - ////////////////////////////////////////////////// - let pi_hash_bytes_start_row = offset; - let pi_hash_bytes_end_row = pi_hash_bytes_start_row + KECCAK_DIGEST_SIZE; - self.assign_rlc_start(region, &mut offset, &mut rpi_rlc_acc, &mut rpi_length_acc)?; + /// Assign the (hi, lo) decomposition of pi_hash. + fn assign_pi_hash( + &self, + region: &mut Region<'_, F>, + offset: usize, + public_data: &PublicData, + pi_hash_rlc_cell: &AssignedCell, + challenges: &Challenges>, + ) -> Result<(usize, PiHashExport), Error> { + let (mut offset, mut rpi_rlc_acc, mut rpi_length) = self.assign_rlc_init(region, offset)?; - for i in pi_hash_bytes_start_row..pi_hash_bytes_end_row { - self.q_not_end.enable(region, i)?; + // Enable RLC accumulator consistency check throughout the above rows. + for q_offset in public_data.pi_hash_start_offset()..public_data.pi_hash_end_offset() { + self.q_not_end.enable(region, q_offset)?; } // the high 16 bytes of keccak output - let cells = self.assign_field_in_pi( + let (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length, cells) = self.assign_field( region, - &mut offset, - &pi_hash.to_fixed_bytes()[..16], - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - true, + offset, + &public_data.get_pi().to_fixed_bytes()[..16], + RpiFieldType::KeccakHiLo, + false, // no padding in this case + rpi_rlc_acc, + rpi_length, challenges, )?; - let pi_hash_hi_byte_cells = cells[3..].to_vec(); - // let pi_hash_hi_cell = cells[RPI_CELL_IDX].clone(); + (offset, rpi_rlc_acc, rpi_length) = (tmp_offset, tmp_rpi_rlc_acc, tmp_rpi_length); + let pi_hash_hi_cells = cells[3..].to_vec(); // the low 16 bytes of keccak output - let cells = self.assign_field_in_pi( + let (tmp_offset, _, _, cells) = self.assign_field( region, - &mut offset, - &pi_hash.to_fixed_bytes()[16..], - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - true, + offset, + &public_data.get_pi().to_fixed_bytes()[16..], + RpiFieldType::KeccakHiLo, + false, // no padding in this case + rpi_rlc_acc, + rpi_length, challenges, )?; - let pi_hash_lo_byte_cells = cells[3..].to_vec(); - // let pi_hash_lo_cell = cells[RPI_CELL_IDX].clone(); + offset = tmp_offset; + let pi_hash_lo_cells = cells[3..].to_vec(); - // +1 for the rlc_start row - assert_eq!(offset, pi_hash_bytes_end_row + 1); - - // copy pi hash down here + // Copy pi_hash value we collected from assigning pi bytes. region.constrain_equal(pi_hash_rlc_cell.cell(), cells[RPI_RLC_ACC_CELL_IDX].cell())?; - ////////////////////////////////////////////////// - ////////// assign COINBASE, DIFFICULTY ////////// - ////////////////////////////////////////////////// - let coinbase_diff_start_row = offset; - let coinbase_diff_end_row = - coinbase_diff_start_row + N_BYTES_ACCOUNT_ADDRESS + N_BYTES_WORD; - - self.assign_rlc_start(region, &mut offset, &mut rpi_rlc_acc, &mut rpi_length_acc)?; - - let cells = self.assign_field_in_pi_ext( - region, - &mut offset, - &get_coinbase_constant().to_fixed_bytes(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - true, - true, - challenges, - )?; - let coinbase_cell = cells[RPI_CELL_IDX].clone(); + Ok((offset, [pi_hash_hi_cells, pi_hash_lo_cells].concat())) + } - let cells = self.assign_field_in_pi_ext( - region, - &mut offset, - &get_difficulty_constant().to_be_bytes(), - &mut rpi_rlc_acc, - &mut rpi_length_acc, - false, - false, - true, - true, - challenges, - )?; - let difficulty_cell = cells[RPI_CELL_IDX].clone(); + /// Assign constants such as the block's coinbase and difficulty. + fn assign_constants( + &self, + region: &mut Region<'_, F>, + offset: usize, + public_data: &PublicData, + block_value_cells: &[AssignedCell], + challenges: &Challenges>, + ) -> Result { + let (mut offset, mut rpi_rlc_acc, mut rpi_length) = self.assign_rlc_init(region, offset)?; - for i in coinbase_diff_start_row..coinbase_diff_end_row { - self.q_not_end.enable(region, i)?; + // Enable RLC accumulator consistency check throughout the above rows. + for q_offset in public_data.constants_start_offset()..public_data.constants_end_offset() { + self.q_not_end.enable(region, q_offset)?; } - // copy to block table - for block_idx in 0..self.max_inner_blocks { + // Assign [coinbase, difficulty] as constants. + let mut cells = vec![]; + let rpi_cells = [ + public_data.coinbase().to_fixed_bytes().to_vec(), + public_data.difficulty().to_be_bytes().to_vec(), + ] + .iter() + .map(|value_be_bytes| { + (offset, rpi_rlc_acc, rpi_length, cells) = self.assign_field( + region, + offset, + value_be_bytes, + RpiFieldType::Constant, + false, // no padding in this case + rpi_rlc_acc, + rpi_length, + challenges, + )?; + Ok(cells[RPI_CELL_IDX].clone()) + }) + .collect::>, Error>>()?; + + // Copy coinbase and difficulty cells to block table + for block_idx in 0..public_data.max_inner_blocks { region.constrain_equal( - coinbase_cell.cell(), + rpi_cells[0].cell(), block_value_cells[BLOCK_LEN * block_idx + COINBASE_OFFSET].cell(), )?; region.constrain_equal( - difficulty_cell.cell(), + rpi_cells[1].cell(), block_value_cells[BLOCK_LEN * block_idx + DIFFICULTY_OFFSET].cell(), )?; } - assert_eq!( - offset, - // for data bytes start row - 1 + self.max_inner_blocks * BLOCK_HEADER_BYTES_NUM - + self.max_txs * KECCAK_DIGEST_SIZE - + 1 // for data hash row - + 1 // for pi bytes start row - + N_BYTES_U64 - + 4 * KECCAK_DIGEST_SIZE - + 1 // for pi hash row - + 1 // for pi hash bytes start row - + KECCAK_DIGEST_SIZE - + 1 // for coinbase & difficulty start row - + N_BYTES_ACCOUNT_ADDRESS - + N_BYTES_WORD, - ); - - let instance_byte_cells = [pi_hash_hi_byte_cells, pi_hash_lo_byte_cells].concat(); - - Ok((instance_byte_cells, connections)) + Ok(offset) } - fn assign_rlc_start( + /// Initialise the RLC computation at the row with the given offset. Returns the offset at the + /// next row. + fn assign_rlc_init( &self, region: &mut Region<'_, F>, - offset: &mut usize, - rpi_rlc_acc: &mut Value, - rpi_length_acc: &mut u64, - ) -> Result<(), Error> { - *rpi_rlc_acc = Value::known(F::zero()); - *rpi_length_acc = 0; - + offset: usize, + ) -> Result<(usize, Value, Value), Error> { region.assign_advice_from_constant( || "rpi_rlc_acc[0]", self.rpi_rlc_acc, - *offset, + offset, F::zero(), )?; region.assign_advice_from_constant( || "rpi_length_acc[0]", self.rpi_length_acc, - *offset, + offset, F::zero(), )?; - *offset += 1; - - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - fn assign_field_in_pi( - &self, - region: &mut Region<'_, F>, - offset: &mut usize, - value_be_bytes: &[u8], - rpi_rlc_acc: &mut Value, - rpi_length_acc: &mut u64, - is_block_context: bool, // if this field related to block context - is_rpi_padding: bool, // if this field is not included in the data bytes - keccak_hi_lo: bool, // if this field is related to keccak decomposition - challenges: &Challenges>, - ) -> Result>, Error> { - self.assign_field_in_pi_ext( - region, - offset, - value_be_bytes, - rpi_rlc_acc, - rpi_length_acc, - is_block_context, - is_rpi_padding, - keccak_hi_lo, - false, - challenges, - ) + Ok((offset + 1, Value::known(F::zero()), Value::known(F::zero()))) } + /// Assign a variable length field (in its big-endian byte representation to the PI circuit. #[allow(clippy::too_many_arguments)] - fn assign_field_in_pi_ext( + #[allow(clippy::type_complexity)] + fn assign_field( &self, region: &mut Region<'_, F>, - offset: &mut usize, + offset: usize, value_be_bytes: &[u8], - rpi_rlc_acc: &mut Value, - rpi_length_acc: &mut u64, - is_block_context: bool, // if this field related to block context - is_rpi_padding: bool, // if this field is not included in the data bytes - keccak_hi_lo: bool, // if this field is related to keccak decomposition - is_constant: bool, + field_type: RpiFieldType, + is_rpi_padding: bool, + mut rpi_rlc_acc: Value, // rlc accumulator over rpi. + mut rpi_length: Value, // no. of bytes accumulated since rlc was last initialised. challenges: &Challenges>, - ) -> Result>, Error> { - let len = value_be_bytes.len(); - - let (is_field_rlc, t) = if len * 8 > F::CAPACITY as usize { + ) -> Result<(usize, Value, Value, Vec>), Error> { + // The number of bytes to represent this field's value. + let n_bytes = value_be_bytes.len(); + + // Some fields hold 32-byte values and hence do not fit within the scalar field. In such + // cases, we mark the field as an RLC field, i.e. `is_field_rlc == true` and use the + // randomness challenge to compute a random linear combination of its be-bytes. For fields + // that do fit within the scalar field, we simply compute the linear combination with 2^8. + let (is_field_rlc, field_rand) = if n_bytes * 8 > F::CAPACITY as usize { (F::one(), challenges.evm_word()) } else { (F::zero(), Value::known(F::from(BYTE_POW_BASE))) }; - // keccak_hi_lo = true if we are re-using rpi layout for keccak bytes - let r = if keccak_hi_lo { - challenges.evm_word() - } else { - challenges.keccak_input() - }; - let value = rlc_be_bytes(value_be_bytes, t); - let mut value_bytes_acc = Value::known(F::zero()); - - // +3 for rpi, rpi_rlc_acc, rpi_length_acc - let mut cells = vec![None; 3 + value_be_bytes.len()]; - - // use copy constraints to make sure that - // 1. rpi_cells are equal. - // 2. rpi_cell equals to the last rpi_bytes_acc - // 3. the first rpi_bytes_acc equals to the first byte_cell - let mut rpi_cells = vec![]; - let mut rpi_bytes_acc_cells = vec![]; - let mut byte_cells = vec![]; - for (i, byte) in value_be_bytes.iter().enumerate() { - let row_offset = *offset + i; - - let real_value = if is_rpi_padding { - Value::known(F::zero()) - } else { - value - }; - - value_bytes_acc = value_bytes_acc - .zip(t) - .and_then(|(acc, t)| Value::known(acc * t + F::from(*byte as u64))); - - // this field is not padding then we absorb the byte into rpi_rlc_acc - if !is_rpi_padding { - *rpi_rlc_acc = rpi_rlc_acc - .zip(r) - .and_then(|(acc, rand)| Value::known(acc * rand + F::from(*byte as u64))); - *rpi_length_acc += 1; - } - // set field-related selectors - if i < len - 1 { - self.q_field_step.enable(region, row_offset)?; - } + // Some random linear combination of bytes are then hashed to check if: + // keccak256(rlc(bytes)) == hash. + // + // The above lookup to the keccak table requires us to use the EVM randomness from the + // challenge API. + // + // However for other cases, we use the keccak randomness from the challenge API. + let (is_rlc_keccak, rlc_rand) = match field_type { + RpiFieldType::KeccakHiLo | RpiFieldType::Constant => (F::zero(), challenges.evm_word()), + _ => (F::one(), challenges.keccak_input()), + }; - region.assign_fixed( - || "is_field_rlc", - self.is_field_rlc, - row_offset, - || Value::known(is_field_rlc), - )?; - region.assign_fixed( - || "is_rlc_keccak", - self.is_rlc_keccak, - row_offset, - || Value::known(F::from(!keccak_hi_lo as u64)), - )?; + // The RLC of the big-endian bytes representing this field's value. + let rpi_value = rlc_be_bytes(value_be_bytes, field_rand); - let byte_cell = if is_constant { - region.assign_advice_from_constant( - || "field byte", - self.rpi_field_bytes, - row_offset, - F::from(*byte as u64), - )? - } else { - region.assign_advice( - || "field byte", - self.rpi_field_bytes, - row_offset, - || Value::known(F::from(*byte as u64)), - )? - }; - - let rpi_bytes_acc_cell = region.assign_advice( - || "field byte acc", - self.rpi_field_bytes_acc, - row_offset, - || value_bytes_acc, - )?; - let rpi_cell = region.assign_advice( - || "field value", - self.raw_public_inputs, - row_offset, - || value, - )?; - let rpi_rlc_cell = region.assign_advice( - || "rpi_rlc_acc", - self.rpi_rlc_acc, - row_offset, - || *rpi_rlc_acc, - )?; - let rpi_length_cell = { - region.assign_advice( - || "rpi_length_acc", - self.rpi_length_acc, - row_offset, - || Value::known(F::from(*rpi_length_acc)), - )? - }; + // The linear combination accumulator for this specific field. + let rpi_bytes_acc = value_be_bytes + .iter() + .scan(Value::known(F::zero()), |acc, &byte| { + *acc = *acc * field_rand + Value::known(F::from(byte as u64)); + Some(*acc) + }) + .collect::>>(); - region.assign_advice( - || "is_rpi_padding", - self.is_rpi_padding, - row_offset, - || Value::known(F::from(is_rpi_padding as u64)), - )?; - // For block context fields, - // If it's padding, then the rpi cell does not matter any more - // as it's not included in the data bytes. This means the rpi cell is not - // constrained. Then we cannot use that to connect to block table. - // Therefore we use the `real_rpi_cell` instead. - let real_rpi_cell = - region.assign_advice(|| "real_rpi", self.real_rpi, row_offset, || real_value)?; - - rpi_cells.push(rpi_cell.clone()); - rpi_bytes_acc_cells.push(rpi_bytes_acc_cell); - byte_cells.push(byte_cell.clone()); - - if i == len - 1 { - cells[RPI_CELL_IDX] = if is_block_context { - Some(real_rpi_cell) - } else { - Some(rpi_cell) - }; - cells[RPI_RLC_ACC_CELL_IDX] = Some(rpi_rlc_cell); - cells[RPI_LENGTH_ACC_CELL_IDX] = Some(rpi_length_cell); - } - cells[i + 3] = Some(byte_cell); + // Enable the field step until the penultimate offset. + for q_offset in offset..offset + n_bytes - 1 { + self.q_field_step.enable(region, q_offset)?; } - // copy constraints + let (mut final_rpi_cell, mut final_rpi_rlc_cell, mut final_rpi_length_cell) = + (None, None, None); + let cells = + value_be_bytes + .iter() + .zip(rpi_bytes_acc.iter()) + .enumerate() + .map(|(idx, (&byte, &bytes_acc))| { + let row_offset = offset + idx; + + // Update rpi_rlc and rpi_length if this field is not meant for padding. + if !is_rpi_padding { + rpi_rlc_acc = rpi_rlc_acc * rlc_rand + Value::known(F::from(byte as u64)); + rpi_length = rpi_length.map(|v| v + F::one()); + } + + region.assign_fixed( + || "is_field_rlc", + self.is_field_rlc, + row_offset, + || Value::known(is_field_rlc), + )?; + region.assign_fixed( + || "is_rlc_keccak", + self.is_rlc_keccak, + row_offset, + || Value::known(is_rlc_keccak), + )?; + region.assign_advice( + || "is_rpi_padding", + self.is_rpi_padding, + row_offset, + || Value::known(F::from(is_rpi_padding as u64)), + )?; + let byte_cell = match field_type { + RpiFieldType::Constant => region.assign_advice_from_constant( + || "field byte", + self.rpi_field_bytes, + row_offset, + F::from(byte as u64), + )?, + _ => region.assign_advice( + || "field byte", + self.rpi_field_bytes, + row_offset, + || Value::known(F::from(byte as u64)), + )?, + }; + let rpi_bytes_acc_cell = region.assign_advice( + || "field byte acc", + self.rpi_field_bytes_acc, + row_offset, + || bytes_acc, + )?; + let rpi_cell = region.assign_advice( + || "field value", + self.raw_public_inputs, + row_offset, + || rpi_value, + )?; + let rpi_rlc_cell = region.assign_advice( + || "rpi_rlc_acc", + self.rpi_rlc_acc, + row_offset, + || rpi_rlc_acc, + )?; + let rpi_length_cell = region.assign_advice( + || "rpi_length_acc", + self.rpi_length_acc, + row_offset, + || rpi_length, + )?; + let real_rpi_cell = region.assign_advice( + || "real_rpi", + self.real_rpi, + row_offset, + || { + if is_rpi_padding { + Value::known(F::zero()) + } else { + rpi_value + } + }, + )?; + + // Update the RPI related cells for the final row. + if idx == n_bytes - 1 { + final_rpi_cell = match field_type { + RpiFieldType::BlockCtx => Some(real_rpi_cell), + _ => Some(rpi_cell.clone()), + }; + final_rpi_rlc_cell = Some(rpi_rlc_cell); + final_rpi_length_cell = Some(rpi_length_cell); + } + + Ok((byte_cell, (rpi_cell, rpi_bytes_acc_cell))) + }) + .collect::, (AssignedCell, AssignedCell))>, + Error, + >>()?; + + let (byte_cells, cells): ( + Vec>, + Vec<(AssignedCell, AssignedCell)>, + ) = cells.into_iter().unzip(); + let (rpi_cells, rpi_bytes_acc_cells): (Vec>, Vec>) = + cells.into_iter().unzip(); + + // Copy constraints: + // byte[0] == rpi_bytes_acc[0] region.constrain_equal(byte_cells[0].cell(), rpi_bytes_acc_cells[0].cell())?; - region.constrain_equal(rpi_cells[0].cell(), rpi_bytes_acc_cells[len - 1].cell())?; - for i in 1..len { + + // Copy constraints: + // rpi[0] == rpi_cells[1] == ... == rpi_cells[n_bytes - 1] + for i in 1..n_bytes { region.constrain_equal(rpi_cells[0].cell(), rpi_cells[i].cell())?; } - *offset += len; - - Ok(cells.into_iter().map(|cell| cell.unwrap()).collect()) + // Copy constraints: + // rpi[0] == rpi_bytes_acc[n - 1] + region.constrain_equal(rpi_cells[0].cell(), rpi_bytes_acc_cells[n_bytes - 1].cell())?; + + // We return cells in the following order: + // [ + // rpi_cell + // rpi_rlc_cell + // rpi_length_cell + // byte_cell[0] + // ... + // byte_cell[n_bytes - 1] + // ] + Ok(( + offset + n_bytes, + rpi_rlc_acc, + rpi_length, + [ + vec![ + final_rpi_cell.unwrap(), + final_rpi_rlc_cell.unwrap(), + final_rpi_length_cell.unwrap(), + ], + byte_cells, + ] + .concat(), + )) } fn assign_block_table( &self, region: &mut Region<'_, F>, public_data: &PublicData, - max_inner_blocks: usize, challenges: &Challenges>, ) -> Result>, Error> { let mut offset = 0; @@ -1367,9 +1500,13 @@ impl PiCircuitConfig { let block_ctxs = &public_data.block_ctxs; let num_all_txs_in_blocks = public_data.get_num_all_txs(); for block_ctx in block_ctxs.ctxs.values().cloned().chain( - (block_ctxs.ctxs.len()..max_inner_blocks) - .into_iter() - .map(|_| BlockContext::padding(public_data.chain_id)), + (block_ctxs.ctxs.len()..public_data.max_inner_blocks).map(|_| { + BlockContext::padding( + public_data.chain_id, + public_data.difficulty(), + public_data.coinbase(), + ) + }), ) { let num_txs = public_data .transactions @@ -1433,7 +1570,7 @@ impl PiCircuitConfig { offset, || Value::known(F::from((*tag == NumTxs) as u64)), )?; - if offset != max_inner_blocks * BLOCK_LEN { + if offset != public_data.max_inner_blocks * BLOCK_LEN { // it's not the last row of block table region.assign_fixed( || "q_block_tag", @@ -1478,11 +1615,8 @@ impl PiCircuitConfig { } /// Public Inputs Circuit -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] pub struct PiCircuit { - max_txs: usize, - max_calldata: usize, - max_inner_blocks: usize, /// PublicInputs data known by the verifier pub public_data: PublicData, @@ -1515,6 +1649,9 @@ impl PiCircuit { ); } let public_data = PublicData { + max_txs, + max_calldata, + max_inner_blocks, chain_id, start_l1_queue_index: block.start_l1_queue_index, transactions: block.txs.clone(), @@ -1526,9 +1663,6 @@ impl PiCircuit { Self { public_data, - max_txs, - max_calldata, - max_inner_blocks, _marker: PhantomData, connections: Default::default(), tx_value_cells: RefCell::new(None), @@ -1673,12 +1807,8 @@ impl SubCircuit for PiCircuit { .borrow() .clone() .expect("tx_value_cells must have been set"); - let block_value_cells = config.assign_block_table( - &mut region, - &self.public_data, - self.max_inner_blocks, - challenges, - )?; + let block_value_cells = + config.assign_block_table(&mut region, &self.public_data, challenges)?; // assign pi cols let (inst_byte_cells, conn) = config.assign( &mut region, diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index 51fee3dd05..a6c991e688 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -7,7 +7,7 @@ use super::*; // that depend on MAX_TXS and MAX_CALLDATA, so these two values are required // during the configuration. /// Test Circuit for PiCircuit -#[derive(Default, Clone)] +#[derive(Clone)] pub struct PiTestCircuit< F: Field, const MAX_TXS: usize, @@ -15,6 +15,30 @@ pub struct PiTestCircuit< const MAX_INNER_BLOCKS: usize, >(pub PiCircuit); +impl + Default for PiTestCircuit +{ + fn default() -> Self { + Self(PiCircuit:: { + public_data: PublicData { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_inner_blocks: MAX_INNER_BLOCKS, + chain_id: 0, + start_l1_queue_index: 0, + transactions: vec![], + prev_state_root: H256::zero(), + next_state_root: H256::zero(), + withdraw_trie_root: H256::zero(), + block_ctxs: Default::default(), + }, + connections: Default::default(), + tx_value_cells: Default::default(), + _marker: PhantomData, + }) + } +} + impl SubCircuit for PiTestCircuit { @@ -54,6 +78,8 @@ impl, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -69,9 +95,6 @@ impl SubCircuit for PoseidonCircuit { impl Circuit for PoseidonCircuit { type Config = (PoseidonCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self(Default::default(), self.1) diff --git a/zkevm-circuits/src/rlp_circuit_fsm.rs b/zkevm-circuits/src/rlp_circuit_fsm.rs index ba889b94f5..8218d23133 100644 --- a/zkevm-circuits/src/rlp_circuit_fsm.rs +++ b/zkevm-circuits/src/rlp_circuit_fsm.rs @@ -599,7 +599,7 @@ impl RlpCircuitConfig { input_exprs .into_iter() - .zip(table_exprs.into_iter()) + .zip(table_exprs) .map(|(arg, table)| (cond.expr() * arg, table)) .collect() }); @@ -617,7 +617,7 @@ impl RlpCircuitConfig { meta.query_advice(format, Rotation::cur()), ] .into_iter() - .zip(rom_table.table_exprs(meta).into_iter()) + .zip(rom_table.table_exprs(meta)) .map(|(arg, table)| (cond.expr() * arg, table)) .collect() }); @@ -1949,7 +1949,6 @@ impl SubCircuit for RlpCircuit { debug_assert!(block.txs.len() <= max_txs); let padding_txs = (block.txs.len()..max_txs) - .into_iter() .map(|i| { let mut tx = Transaction::dummy(block.chain_id); tx.id = i + 1; diff --git a/zkevm-circuits/src/rlp_circuit_fsm/dev.rs b/zkevm-circuits/src/rlp_circuit_fsm/dev.rs index 6926c15425..ec775f397c 100644 --- a/zkevm-circuits/src/rlp_circuit_fsm/dev.rs +++ b/zkevm-circuits/src/rlp_circuit_fsm/dev.rs @@ -13,6 +13,8 @@ use halo2_proofs::{ impl Circuit for RlpCircuit { type Config = (RlpCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/rlp_circuit_fsm/test.rs b/zkevm-circuits/src/rlp_circuit_fsm/test.rs index af07e18884..2e56f0924c 100644 --- a/zkevm-circuits/src/rlp_circuit_fsm/test.rs +++ b/zkevm-circuits/src/rlp_circuit_fsm/test.rs @@ -25,7 +25,7 @@ fn get_tx(is_eip155: bool) -> Transaction { .gas(word!("0x77320")) .nonce(word!("0x7f")); if is_eip155 { - tx = tx.chain_id(*MOCK_CHAIN_ID); + tx = tx.chain_id(MOCK_CHAIN_ID); } let (tx_type, unsigned_bytes) = if is_eip155 { (TxType::Eip155, tx.rlp().to_vec()) diff --git a/zkevm-circuits/src/sha256_circuit.rs b/zkevm-circuits/src/sha256_circuit.rs new file mode 100644 index 0000000000..44883f089d --- /dev/null +++ b/zkevm-circuits/src/sha256_circuit.rs @@ -0,0 +1,155 @@ +//! The SHA256 circuit is a wrapper for the circuit in sha256 crate and serve for precompile SHA-256 +//! calls + +use halo2_proofs::{ + circuit::{Layouter, Value}, + halo2curves::bn256::Fr, + plonk::{Any, Column, ConstraintSystem, Error, Expression}, +}; + +mod circuit; +#[cfg(test)] +mod test; + +pub use circuit::CircuitConfig; +use circuit::{Hasher, SHA256Table as TableTrait}; +pub use halo2_gadgets::sha256::BLOCK_SIZE; + +use crate::{ + table::{LookupTable, SHA256Table}, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness, +}; +use bus_mapping::circuit_input_builder::SHA256; +use eth_types::Field; + +impl TableTrait for SHA256Table { + fn cols(&self) -> [Column; 5] { + let tbl_cols = >::columns(self); + [ + tbl_cols[0], + tbl_cols[2], + tbl_cols[3], + tbl_cols[4], + tbl_cols[1], + ] + } +} + +/// Config args for SHA256 circuit +#[derive(Debug, Clone)] +pub struct CircuitConfigArgs { + /// SHA256 Table + pub sha256_table: SHA256Table, + /// Challenges randomness + pub challenges: Challenges>, +} + +impl SubCircuitConfig for CircuitConfig { + type ConfigArgs = CircuitConfigArgs; + + /// Return a new ModExpCircuitConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + sha256_table, + challenges, + }: Self::ConfigArgs, + ) -> Self { + Self::configure(meta, sha256_table, challenges.keccak_input()) + } +} + +/// ModExp circuit for precompile modexp +#[derive(Clone, Debug, Default)] +pub struct SHA256Circuit(Vec, usize, std::marker::PhantomData); + +const TABLE16_BLOCK_ROWS: usize = 2114; +const BLOCK_SIZE_IN_BYTES: usize = BLOCK_SIZE * 4; + +impl SHA256Circuit { + fn expected_rows(&self) -> usize { + self.0 + .iter() + .map(|evnt| (evnt.input.len()) + 9 / BLOCK_SIZE_IN_BYTES + 1) + .reduce(|acc, v| acc + v) + .unwrap_or_default() + * TABLE16_BLOCK_ROWS + } + + fn with_row_limit(self, row_limit: usize) -> Self { + if row_limit != 0 { + let expected_rows = self.expected_rows(); + assert!( + expected_rows <= row_limit, + "no enough rows for sha256 circuit, expected {expected_rows}, limit {row_limit}", + ); + log::info!("sha256 circuit work with maxium {} rows", row_limit); + } + let inp = self.0; + let block_limit = row_limit / TABLE16_BLOCK_ROWS; + + Self(inp, block_limit, Default::default()) + } +} + +impl SubCircuit for SHA256Circuit { + type Config = CircuitConfig; + + fn unusable_rows() -> usize { + 2 + } + + fn new_from_block(block: &witness::Block) -> Self { + Self(block.get_sha256(), 0, Default::default()) + .with_row_limit(block.circuits_params.max_keccak_rows) + } + + fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + let real_row = Self(block.get_sha256(), 0, Default::default()).expected_rows(); + + ( + real_row, + real_row + .max(block.circuits_params.max_keccak_rows) + .max(4096), + ) + } + + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + let chng = challenges.keccak_input(); + let mut hasher = Hasher::new(config.clone(), layouter)?; + + for hash_event in &self.0 { + hasher.update(layouter, chng, &hash_event.input)?; + + let digest = hasher.finalize(layouter, chng)?; + let ref_digest = hash_event + .digest + .chunks_exact(4) + .map(|bt| bt.iter().fold(0u32, |sum, v| sum * 256 + *v as u32)) + .collect::>(); + for (w, check) in digest.into_iter().zip(ref_digest) { + w.0.assert_if_known(|digest_word| *digest_word == check); + } + + if hasher.blocks() > self.1 { + log::error!("handled 512-bit block exceed limit ({})", self.1); + return Err(Error::Synthesis); + } + } + + // paddings + for _i in hasher.blocks()..self.1 { + hasher.update(layouter, chng, &[])?; + hasher.finalize(layouter, chng)?; + } + + Ok(()) + } +} diff --git a/zkevm-circuits/src/sha256_circuit/README.md b/zkevm-circuits/src/sha256_circuit/README.md new file mode 100644 index 0000000000..c13751dc50 --- /dev/null +++ b/zkevm-circuits/src/sha256_circuit/README.md @@ -0,0 +1,71 @@ +# SHA256 Circuit with lookup table + +This circuit use a forking of `table16` in `halo2-gadget`, with some patches: + ++ Make all code generic for the `Field` trait so that it also work with the `bn254` curve ++ Fix the digest exporting part, output correct digest (the final state ⊕ init state) with correct constraint (rows for 512-bit block increased from **2102** -> **2114**) + +The witness in table16 is then exported to an extra region so that the RLC of input and digest can be calculated and form the lookup table for the SHA256 precompile in zkevm-circuit. To achieve this, we have introduced several cols and assigned them to two regions: `input` and `digest`. The following table illustrates: + +input region (example for input 'abc'): +| | s_final | s_u16 | counter | bytes_rlc | trans_byte | copied_data | s_output| padding |padding_size| +|----------|------------------|-----------|-----------|-----------|------------|-------------|---------|-----------------|------------| +|(inherit) | *1* | | *42* |*inherit_rlc*| | | | *1* | | +|s_begin | 1 | | 0 | 0 | | | | 0 | | +|s_enable | 1 | 1 | 1 | 0x61 | b'0x61' | *0x6162* | | 0 | | +|s_enable | 1 | 0 | 2 | 0x61062 | b'0x62' | | | 0 | | +|s_enable | 1 | 1 | 3 | 0x61062063| b'0x63' | *0x6380* | | 0 | | +|s_enable | 1 | 0 | 3 | 0x61062063| b'0x80' | | | 1 | | +|.... | +|s_enable | 1 | 1 | 3 | 0x61062063| b'0x00 | *0x0018* | | 1 | 0 | +|s_last | 1 | 0 | 3 | 0x61062063| b'0x18 | | | 1 | 24 | + + +digest region (example for the hash of 'abc'): +| | s_final | s_u16 | counter | bytes_rlc | trans_byte | copied_data | s_output| padding | +|----------|------------------|-----------|-----------|-----------|------------|-------------|---------|-----------| +| | *1* | | | **0** | | | | **0** | +|s_enable | 1 | 1 | | 0xba | b'0xba' | *0xba78* | 0x6a09 | 0 | +|s_enable | 1 | 0 | | 0xba078 | b'0x78 | *0x6a09* | | +|.... | +|s_enable | 1 | 1 | | | b'0x15 | *0x15ad* | 0xcd19 | 0 | +|s_enable | 1 | 0 | | hash_rlc | b'0xad | *0xcd19* | | **0** | +| | | |*input_counter*|*hash_rlc*| | *input_rlc* | 1 | | + +Note: ++ *Italic* indicate the cell is equality constrainted whie **bold** indicate the cell is constarinted with constant ++ We suppose the `random` value for rlc is `0x1000` + +### Defination of the cols + ++ `copied_data` col is used to copy the cells with `u16` values from `table16`. ++ `trans_byte` expands each `u16` value copied from `table16` into two bytes across two adjacent rows, with the help of the selector `s_u16` ++ `padding` col marks whether the byte in current row is padding or input byte. ++ `bytes_rlc` accumulates bytes in `trans_byte` col to its RLC expression only if the byte in current row is not padding. Otherwise, it continues the value from the previous row if the current row is marked as padding. ++ `counter` counts the total input bytes if byte in current row is not padding, Otherwise it continues the value from previous row if the current row is marked as padding. ++ `s_final` is a boolean advice col that identifies in each row of an input region, marking wether the current block is the last block ++ `padding_size` calculates the accumulation of the last 8 bytes in input region and obtains the bit counts recorded in the tail of the padding, which is specified by SHA2. + +### Defination in regions: + + Each input region captures a 512-bit block and copies the 16 x 32-bit integers (in the form of a pair of assigned cells for their lo and hi 16-bit parts) inside of the `message schedule` region of table16. The region consists of 66 rows: 64 rows for 64 bytes representing the 512-bit block and 2 extra rows at the beginning. For the `s_final`, `counter`, `padding` and `bytes_rlc` cols, the cells in last row (enabled by `s_last` selector) are connected by equality constraints to the first row of next input region for the subsequent 512-bit block. Additionally the `s_final` cells is also connected with the corresponding digest reion. + + The second row at the top of the region determines how the `counter`, `padding` and `bytes_rlc` cols begin: if the inherited `s_final` cell (at the first row at the top of the region) is 1, these cols will start with an initial value (i.e., 0); else they will start with the "inherited" value of the previous 512-bit block. + + Note that it is free to specify `s_final` in each block as either 0 or 1. If `s_final` is set to 1, the last row must satisfy the "final" constraint, that is the cell in `counter` col has to equal the calculated bit size in `padding_size` cell. + + There is exactly one digest region corresponding to each input region. This region captures the 256-bit digest of the 512-bit block and copies it from the `digest` region of table16. The region consists of 34 rows: 32 rows for bytes of digests, 1 extra row at the beginning, and 1 row at the bottom. The `s_final` is inherited from the input region, and the first row for `counter`, `padding` and `bytes_rlc` cols are specified with 0 by constraints to a constant. The last row for digest bytes is also constarint the `padding` cell as 0, which also ensure there is no padding row existed in digest region. + + Like input region, digest region calculated the RLC of digest bytes. The final row in digest copied `s_final` and `counter` value inheirted from input region into the corresponding cols; `bytes_rlc` of the cell in previous cell (i.e. the RLC of digest); and the RLC of input into `copied_data` col. This row represents a row in SHA256 table used for looking up from evm circuit. + +## Performance + + Currently the SHA256 circuit can calculate SHA256 for 1k bytes within 4.891s (`k=17`), ~26% overhead to its `table16` core (3.854s), and verfication is 6.601ms, 6% overhead to `table16` (6.207ms). + + We have a [detailed performance for table16 and Brecht's sha256](https://www.notion.so/scrollzkp/Precompile-SHA256-7a0f519d5bbe4f52a9fa08ebff9a8118) (accessing priviledge required). + + With `k=21`, SHA256-circuit can calculate the hashes for as much as 16KB bytes, which should be enough for the txs in mainnet. + +## Known issue in table16 + ++ Initialize state is not constrainted in table16 diff --git a/zkevm-circuits/src/sha256_circuit/circuit.rs b/zkevm-circuits/src/sha256_circuit/circuit.rs new file mode 100644 index 0000000000..8e8eebd8a4 --- /dev/null +++ b/zkevm-circuits/src/sha256_circuit/circuit.rs @@ -0,0 +1,1254 @@ +use halo2_gadgets::sha256::{table16::*, Sha256Instructions, BLOCK_SIZE}; +use halo2_proofs::{ + circuit::{AssignedCell, Layouter, Region, Value}, + halo2curves::bn256::Fr, + plonk::{ + Advice, Any, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector, + TableColumn, + }, + poly::Rotation, +}; +use itertools::Itertools; +use std::convert::TryInto; +type BlockState = >::State; + +/// u32 size for SHA256 digit +pub const DIGEST_SIZE: usize = 8; + +/// the defination for a sha256 table +pub trait SHA256Table { + /// the cols has layout [s_enable, input_bytes, input_len, hashes, effect] + fn cols(&self) -> [Column; 5]; + + /// s_enable col with cell *EQUAL TO 1* mark the row is an effect entry for + /// *ANY* 512-bit block of SHA256 + fn s_enable(&self) -> Column { + self.cols()[0] + .try_into() + .expect("must provide cols as expected layout") + } + /// input_rlc show the RLC for input bytes, the first byte is multipled with R^(n-1) + /// in which n is the length of bytes and R is random + fn input_rlc(&self) -> Column { + self.cols()[1] + .try_into() + .expect("must provide cols as expected layout") + } + /// input_len show the accumulated lengh for input bytes + fn input_len(&self) -> Column { + self.cols()[2] + .try_into() + .expect("must provide cols as expected layout") + } + /// hashes_rlc show the RLC for the 32-bytes digest of input bytes, the first byte + /// is multipled with R^31 + fn hashes_rlc(&self) -> Column { + self.cols()[3] + .try_into() + .expect("must provide cols as expected layout") + } + /// is_effect col is a phase 0 col, when the cell is equal to 1 indicate this 512-bit + /// block is the final one for current input bytes, the input_len in this row would + /// show the length *WITHOUT* padding of input bytes + fn is_effect(&self) -> Column { + self.cols()[4] + .try_into() + .expect("must provide cols as expected layout") + } +} + +/// CircuitConfig is the configure for SHA256 circuit +#[derive(Clone, Debug)] +pub struct CircuitConfig { + table16: Table16Config, + byte_range: TableColumn, + + copied_data: Column, + trans_byte: Column, + bytes_rlc: Column, /* phase 2 col obtained from SHA256 table, used for saving the + * rlc bytes from input */ + helper: Column, /* phase 2 col used to save series of data, like the final input rlc + * cell, the padding bit count, etc */ + + s_final_block: Column, /* indicate it is the last block, it can be 0/1 in input + * region and */ + // digest region is set the same as corresponding input region + s_padding: Column, // indicate cur bytes is padding + byte_counter: Column, // counting for the input bytes + + s_output: Column, // indicate the row is used for output to sha256 table + + s_begin: Selector, // indicate as the first line in region + s_final: Selector, // indicate the last byte + s_enable: Selector, // indicate the main rows + s_common_bytes: Selector, // mark the s_enable region except for the last 8 bytes + s_padding_size: Selector, // mark the last 8 bytes for padding size + s_assigned_u16: Selector, // indicate copied_data cell is a assigned u16 word +} + +#[derive(Clone, Debug)] +struct BlockInheritments { + s_final: AssignedBits, + s_padding: AssignedBits, + byte_counter: AssignedCell, + bytes_rlc: AssignedCell, +} + +impl CircuitConfig { + fn setup_gates(&self, meta: &mut ConstraintSystem, rnd: Expression) { + let one = Expression::Constant(Fr::one()); + + meta.create_gate("halves to rlc_byte", |meta| { + let s_u16 = meta.query_selector(self.s_assigned_u16); + let u16 = meta.query_advice(self.copied_data, Rotation::cur()); + let byte = meta.query_advice(self.trans_byte, Rotation::cur()); + let byte_next = meta.query_advice(self.trans_byte, Rotation::next()); + let rlc_byte_prev = meta.query_advice(self.bytes_rlc, Rotation::prev()); + let rlc_byte = meta.query_advice(self.bytes_rlc, Rotation::cur()); + + let s_enable = meta.query_selector(self.s_enable); + let s_padding = meta.query_advice(self.s_padding, Rotation::cur()); + let s_not_padding = one.clone() - s_padding.clone(); + + // constraint u16 in table16 with byte + let byte_from_u16 = + s_u16 * (u16 - (byte.clone() * Expression::Constant(Fr::from(256u64)) + byte_next)); + + let byte_rlc = rlc_byte + - s_not_padding * (rlc_byte_prev.clone() * rnd + byte) + - s_padding * rlc_byte_prev; + + vec![byte_from_u16, s_enable * byte_rlc] + }); + + meta.create_gate("sha256 block padding", |meta| { + let s_padding = meta.query_advice(self.s_padding, Rotation::cur()); + let s_padding_prev = meta.query_advice(self.s_padding, Rotation::prev()); + + let byte = meta.query_advice(self.trans_byte, Rotation::cur()); + let byte_counter = meta.query_advice(self.byte_counter, Rotation::cur()); + let byte_counter_prev = meta.query_advice(self.byte_counter, Rotation::prev()); + + let is_final = meta.query_advice(self.s_final_block, Rotation::cur()); + + let padding_is_bool = s_padding.clone() * (one.clone() - s_padding.clone()); + let s_not_padding = one.clone() - s_padding.clone(); + + let byte_counter_continue = s_not_padding + * (byte_counter.clone() - (byte_counter_prev.clone() + one.clone())) + + s_padding.clone() * (byte_counter - byte_counter_prev); + + let padding_change = s_padding - s_padding_prev.clone(); + + // if prev padding is 1, the following padding would always 1 (no change) + let padding_continue = s_padding_prev.clone() * padding_change.clone(); + + // the byte on first padding is 128 (first bit is 1) + let padding_byte_on_change = + padding_change.clone() * (byte.clone() - Expression::Constant(Fr::from(128u64))); + + // constraint the padding byte, notice it in fact constraint the first byte of the final + // 64-bit integer is 0, but it is ok (we have no so large bytes for 48-bit + // integer) + let padding_byte_is_zero = s_padding_prev * is_final.clone() * byte; + + let padding_change_on_size = + meta.query_selector(self.s_padding_size) * is_final * padding_change; + + Constraints::with_selector( + meta.query_selector(self.s_enable), + vec![padding_is_bool, padding_continue, byte_counter_continue], + ) + .into_iter() + .chain(Constraints::with_selector( + meta.query_selector(self.s_common_bytes), + vec![padding_byte_is_zero, padding_byte_on_change], + )) + .chain(vec![padding_change_on_size.into()]) + }); + + meta.create_gate("sha256 block final", |meta| { + let is_final = meta.query_advice(self.s_final_block, Rotation::cur()); + // final is decided by the begin row + let final_continue = + is_final.clone() - meta.query_advice(self.s_final_block, Rotation::prev()); + let final_is_bool = is_final.clone() * (one.clone() - is_final.clone()); + + let byte = meta.query_advice(self.trans_byte, Rotation::cur()); + let padding_size = meta.query_advice(self.helper, Rotation::cur()); + let padding_size_prev = meta.query_advice(self.helper, Rotation::prev()); + + let padding_size_calc = padding_size.clone() + - (padding_size_prev * Expression::Constant(Fr::from(256u64)) + byte); + let final_must_padded = (one.clone() + - meta.query_advice(self.s_padding, Rotation::cur())) + * is_final.clone(); + + let padding_size_is_zero = + meta.query_selector(self.s_common_bytes) * padding_size.clone(); + + // final contintion: byte counter equal to padding size + let final_condition = meta.query_selector(self.s_final) + * (padding_size + - (meta.query_advice(self.byte_counter, Rotation::cur()) + * Expression::Constant(Fr::from(8u64)))) + * is_final.clone(); + + let u16 = meta.query_advice(self.copied_data, Rotation::cur()); + let u16_exported = meta.query_advice(self.copied_data, Rotation::next()); + let init_iv_u16 = meta.query_fixed(self.s_output, Rotation::cur()); + let is_not_final = one.clone() - is_final.clone(); + + let select_exported = meta.query_selector(self.s_assigned_u16) + * (u16_exported - is_not_final * u16 - is_final * init_iv_u16); + + Constraints::with_selector( + meta.query_selector(self.s_enable), + vec![final_continue, final_is_bool], + ) + .into_iter() + .chain(Constraints::with_selector( + meta.query_selector(self.s_padding_size), + vec![final_must_padded, padding_size_calc], + )) + .chain(vec![ + padding_size_is_zero.into(), + final_condition.into(), + select_exported.into(), + ]) + }); + + meta.create_gate("input block beginning", |meta| { + // is *last block* final + let is_final = meta.query_advice(self.s_final_block, Rotation::prev()); + let is_not_final = one.clone() - is_final.clone(); + + let inherited_counter = meta.query_advice(self.byte_counter, Rotation::prev()); + let byte_counter = meta.query_advice(self.byte_counter, Rotation::cur()); + + let applied_counter = is_not_final.clone() * (byte_counter.clone() - inherited_counter) + + is_final.clone() * byte_counter; + + let inherited_bytes_rlc = meta.query_advice(self.bytes_rlc, Rotation::prev()); + let bytes_rlc = meta.query_advice(self.bytes_rlc, Rotation::cur()); + + let applied_bytes_rlc = is_not_final.clone() + * (bytes_rlc.clone() - inherited_bytes_rlc) + + is_final.clone() * bytes_rlc; + + let inherited_s_padding = meta.query_advice(self.s_padding, Rotation::prev()); + let s_padding = meta.query_advice(self.s_padding, Rotation::cur()); + + let applied_s_padding = is_not_final.clone() + * (s_padding.clone() - inherited_s_padding.clone()) + + is_final * s_padding; + + let is_final = meta.query_advice(self.s_final_block, Rotation::cur()); + let final_is_bool = is_final.clone() * (one.clone() - is_final.clone()); + + // notice now the 'is_final' point to current block and 'is_not_final' point to last + // block (prev) this constraint make circuit can not make a full block is + // padded but not final + let enforce_final = is_not_final * inherited_s_padding * (one.clone() - is_final); + + Constraints::with_selector( + meta.query_selector(self.s_begin), + vec![ + final_is_bool, + applied_counter, + applied_bytes_rlc, + applied_s_padding, + enforce_final, + ], + ) + }); + } + + /// Configures a circuit to include this chip. + pub fn configure( + meta: &mut ConstraintSystem, + sha256_table: impl SHA256Table, + spec_challenge: Expression, + ) -> Self { + let helper = meta.advice_column(); // index 3 + let trans_byte = meta.advice_column(); // index 4 + + let bytes_rlc = sha256_table.hashes_rlc(); + let byte_counter = sha256_table.input_len(); + let copied_data = sha256_table.input_rlc(); + let s_output = sha256_table.s_enable(); + let s_final_block = sha256_table.is_effect(); + + let s_padding_size = meta.selector(); + let s_padding = meta.advice_column(); + let s_begin = meta.selector(); + let s_common_bytes = meta.selector(); + let s_final = meta.selector(); + let s_enable = meta.selector(); + let s_assigned_u16 = meta.selector(); + + let byte_range = meta.lookup_table_column(); + let table16 = Table16Chip::configure(meta); + + meta.enable_equality(copied_data); + meta.enable_equality(bytes_rlc); + meta.enable_equality(s_final_block); + meta.enable_equality(s_padding); + meta.enable_equality(byte_counter); + + let ret = Self { + table16, + byte_range, + + copied_data, + trans_byte, + bytes_rlc, + helper, + + s_final_block, + s_common_bytes, + s_padding_size, + s_padding, + byte_counter, + + s_output, + + s_begin, + s_final, + s_enable, + s_assigned_u16, + }; + + meta.lookup("byte range checking", |meta| { + let byte = meta.query_advice(ret.trans_byte, Rotation::cur()); + vec![(byte, byte_range)] + }); + + ret.setup_gates(meta, spec_challenge); + + ret + } + + #[allow(clippy::type_complexity)] + fn assign_message_block<'vr>( + &self, + region: &mut Region<'_, Fr>, + msgs: impl Iterator, u16)>, + offset: usize, + is_final: bool, + ) -> Result<(Vec>, Vec>), Error> { + let mut out_ret = Vec::new(); + let mut out_bytes = Vec::new(); + let mut size_calc = Value::known(Fr::zero()); + + for (i, (msg, ref_iv)) in msgs.enumerate() { + let row = offset + i * 2; + let next_row = row + 1; + + self.s_assigned_u16.enable(region, row)?; + + msg.copy_advice(|| "copied message input", region, self.copied_data, row)?; + let assigned = region.assign_advice( + || "dummy message cell", + self.copied_data, + next_row, + || { + if is_final { + Value::known(Bits::from(ref_iv)) + } else { + msg.value().map(Clone::clone) + } + }, + )?; + + let bytes_hi = region.assign_advice( + || "u16 message hi byte", + self.trans_byte, + row, + || msg.value().map(|v| Fr::from((u16::from(v) >> 8) as u64)), + )?; + + let bytes_lo = region.assign_advice( + || "u16 message lo byte", + self.trans_byte, + next_row, + || { + msg.value() + .map(|v| Fr::from((u16::from(v) & 255u16) as u64)) + }, + )?; + + for (j, byte_v) in [&bytes_hi, &bytes_lo].into_iter().enumerate() { + // bytes_rlc = region.assign_advice( + // || "bytes rlc", + // self.bytes_rlc, + // row + j, + // || chng * bytes_rlc.value() + byte_v.value(), + // )?; + + // here we have a trick, since digest region has only 16 messages instead 32 + size_calc = region + .assign_advice( + || "padding size calc", + self.helper, + row + j, + || { + if i < 28 { + size_calc + } else { + size_calc.map(|v| v * Fr::from(256u64)) + byte_v.value() + } + }, + )? + .value() + .map(Clone::clone); + } + + out_bytes.push(bytes_hi); + out_bytes.push(bytes_lo); + out_ret.push(AssignedBits(assigned)); + } + + Ok((out_ret, out_bytes)) + } + + fn initialize_block_head( + &self, + layouter: &mut impl Layouter, + ) -> Result { + layouter.assign_region( + || "initialize hasher", + |mut region| { + let s_final = region.assign_advice_from_constant( + || "init s_final", + self.s_final_block, + 0, + Bits::from([false]), + )?; + let s_padding = region.assign_advice_from_constant( + || "init padding", + self.s_padding, + 0, + Bits::from([false]), + )?; + let bytes_rlc = region.assign_advice_from_constant( + || "init bytes rlc", + self.bytes_rlc, + 0, + Fr::zero(), + )?; + let byte_counter = region.assign_advice_from_constant( + || "init byte counter", + self.byte_counter, + 0, + Fr::zero(), + )?; + + Ok(BlockInheritments { + s_final: AssignedBits(s_final), + s_padding: AssignedBits(s_padding), + byte_counter, + bytes_rlc, + }) + }, + ) + } + + #[allow(clippy::type_complexity)] + fn assign_input_block( + &self, + layouter: &mut impl Layouter, + chng: Value, + prev_block: BlockInheritments, + scheduled_msg: &[(AssignedBits, AssignedBits)], + padding_pos: Option, + ) -> Result { + // if no padding or the padding is in padding size pos, this block is not final + let is_final = if let Some(pos) = padding_pos { + pos <= 24 + } else { + false + }; + + let padding_pos = padding_pos.unwrap_or(64); + + layouter.assign_region( + || "sha256 input", + |mut region| { + prev_block.s_final.copy_advice( + || "inheirt s_final", + &mut region, + self.s_final_block, + 0, + )?; + prev_block.s_padding.copy_advice( + || "inheirt padding", + &mut region, + self.s_padding, + 0, + )?; + prev_block.bytes_rlc.copy_advice( + || "inheirt bytes rlc", + &mut region, + self.bytes_rlc, + 0, + )?; + prev_block.byte_counter.copy_advice( + || "inheirt byte counter", + &mut region, + self.byte_counter, + 0, + )?; + + self.s_begin.enable(&mut region, 1)?; + let mut s_final_cell = region.assign_advice( + || "header final", + self.s_final_block, + 1, + || Value::known(Bits::from([is_final])), + )?; + + let mut s_padding_cell = region.assign_advice( + || "header padding", + self.s_padding, + 1, + || { + prev_block + .s_final + .value() + .zip(prev_block.s_padding.value()) + .map(|(s_final, ref_v)| { + if s_final[0] { + Bits::from([false]) + } else { + ref_v.clone() + } + }) + }, + )?; + + let mut byte_counter_cell = region.assign_advice( + || "header rlc", + self.byte_counter, + 1, + || { + prev_block + .s_final + .value() + .zip(prev_block.byte_counter.value()) + .map(|(s_final, ref_v)| if s_final[0] { Fr::zero() } else { *ref_v }) + }, + )?; + + let mut bytes_rlc_cell = region.assign_advice( + || "header counter", + self.bytes_rlc, + 1, + || { + prev_block + .s_final + .value() + .zip(prev_block.bytes_rlc.value()) + .map(|(s_final, ref_v)| if s_final[0] { Fr::zero() } else { *ref_v }) + }, + )?; + + let header_offset = 2; + // assign message state + let (_, byte_cells) = self.assign_message_block( + &mut region, + scheduled_msg + .iter() + .flat_map(|(lo, hi)| [hi, lo]) + .zip(std::iter::repeat(0u16)) + .take(32), + header_offset, + is_final, + )?; + + for (row, byte) in (header_offset..(header_offset + 64)).zip(byte_cells) { + self.s_enable.enable(&mut region, row)?; + let now_padding = row >= padding_pos + header_offset; + + region.assign_fixed( + || "flush s_output", + self.s_output, + row, + || Value::known(Fr::zero()), + )?; + s_padding_cell = region.assign_advice( + || "padding", + self.s_padding, + row, + || Value::known(Bits::from([now_padding])), + )?; + s_final_cell = region.assign_advice( + || "final", + self.s_final_block, + row, + || s_final_cell.value().map(Clone::clone), + )?; + byte_counter_cell = region.assign_advice( + || "byte counter", + self.byte_counter, + row, + || { + byte_counter_cell.value() + + Value::known(if now_padding { Fr::zero() } else { Fr::one() }) + }, + )?; + bytes_rlc_cell = region.assign_advice( + || "bytes rlc", + self.bytes_rlc, + row, + || { + if now_padding { + bytes_rlc_cell.value().map(Clone::clone) + } else { + chng * bytes_rlc_cell.value() + byte.value() + } + }, + )?; + + // println!("padding {:#?}, counter {:#?} at row {}", + // output_block.s_padding.value(), output_block.byte_counter.value(), row); + // println!("final {:#?}, at row {}", output_block.s_final.value(), row); + + if row < 56 + header_offset { + self.s_common_bytes.enable(&mut region, row)?; + } else { + self.s_padding_size.enable(&mut region, row)?; + } + } + self.s_final.enable(&mut region, 63 + header_offset)?; + + // flush unused row + for col in [self.trans_byte, self.copied_data] { + region.assign_advice( + || "flush unused row", + col, + 64 + header_offset, + || Value::known(Fr::zero()), + )?; + } + + region.assign_advice( + || "flush unused row", + self.helper, + 1, + || Value::known(Fr::zero()), + )?; + + Ok(BlockInheritments { + s_final: AssignedBits(s_final_cell), + s_padding: AssignedBits(s_padding_cell), + byte_counter: byte_counter_cell, + bytes_rlc: bytes_rlc_cell, + }) + }, + ) + } + + #[allow(clippy::type_complexity)] + fn assign_output_region( + &self, + layouter: &mut impl Layouter, + chng: Value, + state: &[(AssignedBits, AssignedBits)], + input_block: &BlockInheritments, + is_final: bool, + ) -> Result<[(AssignedBits, AssignedBits); 8], Error> { + const IV16: [u16; 16] = [ + 0x6a09, 0xe667, 0xbb67, 0xae85, 0x3c6e, 0xf372, 0xa54f, 0xf53a, 0x510e, 0x527f, 0x9b05, + 0x688c, 0x1f83, 0xd9ab, 0x5be0, 0xcd19, + ]; + + let output_cells = layouter.assign_region( + || "sha256 digest", + |mut region| { + input_block.s_final.copy_advice( + || "inheirt s_final", + &mut region, + self.s_final_block, + 0, + )?; + region.assign_advice_from_constant( + || "header padding", + self.s_padding, + 0, + Fr::zero(), + )?; + region.assign_advice_from_constant( + || "header counter", + self.byte_counter, + 0, + Fr::zero(), + )?; + let mut digest_rlc = region.assign_advice_from_constant( + || "header rlc", + self.bytes_rlc, + 0, + Fr::zero(), + )?; + + let header_offset = 1; + // assign message state + let (export_cells, byte_cells) = self.assign_message_block( + &mut region, + state.iter().flat_map(|(lo, hi)| [hi, lo]).zip_eq(IV16), + header_offset, + is_final, + )?; + + for i in 0..32 { + let row = i + header_offset; + self.s_enable.enable(&mut region, row)?; + region.assign_fixed( + || "set s_output for init_iv", + self.s_output, + row, + || Value::known(Fr::from(IV16[i / 2] as u64)), + )?; + region.assign_advice( + || "byte counter", + self.byte_counter, + row, + || Value::known(Fr::from(i as u64 + 1)), + )?; + region.assign_advice( + || "final", + self.s_final_block, + row, + || Value::known(Bits::from([is_final])), + )?; + digest_rlc = region.assign_advice( + || "bytes rlc", + self.bytes_rlc, + row, + || chng * digest_rlc.value() + byte_cells[i].value(), + )?; + // with gate for padding continue, it + // is enough to constraint the last padding as 0 + if i == 31 { + region.assign_advice_from_constant( + || "dummy padding last", + self.s_padding, + row, + Fr::zero(), + )?; + } else { + region.assign_advice( + || "dummy padding", + self.s_padding, + row, + || Value::known(Fr::zero()), + )?; + } + } + + // build output row + let final_row = header_offset + 32; + region.assign_fixed( + || "mark s_output final", + self.s_output, + final_row, + || Value::known(Fr::one()), + )?; + digest_rlc.copy_advice( + || "copy digest rlc", + &mut region, + self.bytes_rlc, + final_row, + )?; + input_block.bytes_rlc.copy_advice( + || "copy input rlc", + &mut region, + self.copied_data, + final_row, + )?; + input_block.byte_counter.copy_advice( + || "copy bytes", + &mut region, + self.byte_counter, + final_row, + )?; + input_block.s_final.copy_advice( + || "copy final", + &mut region, + self.s_final_block, + final_row, + )?; + + region.assign_advice( + || "flush unused row", + self.trans_byte, + final_row, + || Value::known(Fr::zero()), + )?; + + region.assign_advice( + || "flush unused row", + self.helper, + 0, + || Value::known(Fr::zero()), + )?; + + Ok(export_cells + .chunks_exact(2) + .map(|ck_pair| (ck_pair[1].clone(), ck_pair[0].clone())) + .collect::>()) + }, + )?; + + Ok(output_cells.try_into().unwrap()) + } + + fn initialize_constant_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + layouter.assign_table( + || "byte range constant", + |mut tb| { + for i in 0..256 { + tb.assign_cell( + || "byte range", + self.byte_range, + i, + || Value::known(Fr::from(i as u64)), + )?; + } + + Ok(()) + }, + ) + } +} + +/// sha256 hasher for byte stream +#[derive(Debug)] +pub struct Hasher { + chip: CircuitConfig, + state: BlockState, + hasher_state: BlockInheritments, + cur_block: Vec, + length: usize, + block_usage: usize, +} + +impl Hasher { + /// return the number of 512-bit blocks which has been assigned + pub fn blocks(&self) -> usize { + self.block_usage + } + + /// return the number bytes current update, 0 indicate a clean status + pub fn updated_size(&self) -> usize { + self.length + } + + /// create a hasher, the circuit would be identify when block_usage is the same + pub fn new(chip: CircuitConfig, layouter: &mut impl Layouter) -> Result { + // constant part + chip.initialize_constant_table(layouter)?; + Table16Chip::load(chip.table16.clone(), layouter)?; + + let table16_chip = Table16Chip::construct::(chip.table16.clone()); + let state = table16_chip.initialization_vector(layouter)?; + let hasher_state = chip.initialize_block_head(layouter)?; + Ok(Self { + chip, + state, + hasher_state, + cur_block: Vec::with_capacity(BLOCK_SIZE * 4), + length: 0, + block_usage: 0, + }) + } + + /// update a single 512-bit block into layouter + fn update_block( + &mut self, + layouter: &mut impl Layouter, + chng: Value, + input: [BlockWord; BLOCK_SIZE], + padding: Option, + is_final: bool, + ) -> Result { + let table16_cfg = &self.chip.table16; + + let w_halves = table16_cfg.message_process(layouter, input)?; + self.hasher_state = self.chip.assign_input_block( + layouter, + chng, + self.hasher_state.clone(), + &w_halves[..16], + padding, + )?; + + let init_working_state = match &self.state { + Table16State::Compress(s) => s.as_ref().clone(), + Table16State::Dense(s) => table16_cfg.initialize(layouter, s.clone())?, + }; + + let compress_state = + table16_cfg.compress(layouter, init_working_state.clone(), w_halves)?; + let digest_state = table16_cfg.digest(layouter, compress_state, init_working_state)?; + + self.state = self + .chip + .assign_output_region( + layouter, + chng, + &digest_state.clone().map(|v| v.decompose()), + &self.hasher_state, + is_final, + ) + .map(|s| s.map(|v| v.into())) + .map(Table16State::Dense)?; + self.block_usage += 1; + + Ok(Table16State::Dense(digest_state)) + } + + fn block_transform(bytes: &[u8]) -> Vec { + assert_eq!(bytes.len(), BLOCK_SIZE * 4); + bytes + .chunks_exact(4) + .map(|bt| bt.iter().fold(0u32, |sum, v| sum * 256 + *v as u32)) + .map(Value::known) + .map(BlockWord) + .collect::>() + } + + /// Digest data, updating the internal state. + pub fn update( + &mut self, + layouter: &mut impl Layouter, + chng: Value, + mut data: &[u8], + ) -> Result<(), Error> { + use std::cmp::min; + + self.length += data.len(); + + // Fill the current block, if possible. + let remaining = BLOCK_SIZE * 4 - self.cur_block.len(); + let (l, r) = data.split_at(min(remaining, data.len())); + self.cur_block.extend_from_slice(l); + data = r; + + // If we still don't have a full block, we are done. + if self.cur_block.len() < BLOCK_SIZE * 4 { + return Ok(()); + } + + // transform to word block + let word_block = Self::block_transform(&self.cur_block); + + // Process the now-full current block. + self.update_block( + layouter, + chng, + word_block.as_slice().try_into().unwrap(), + None, + false, + )?; + + self.cur_block.clear(); + + // Process any additional full blocks. + let mut chunks_iter = data.chunks_exact(BLOCK_SIZE * 4); + for chunk in &mut chunks_iter { + let word_block = Self::block_transform(chunk); + self.update_block( + layouter, + chng, + word_block.as_slice().try_into().unwrap(), + None, + false, + )?; + } + + // Cache the remaining partial block, if any. + let rem = chunks_iter.remainder(); + self.cur_block.extend_from_slice(rem); + + Ok(()) + } + + /// generate the final digest and ready for new update. + pub fn finalize( + &mut self, + layouter: &mut impl Layouter, + chng: Value, + ) -> Result<[BlockWord; DIGEST_SIZE], Error> { + // check padding requirement + let mut padding_pos = Some(self.cur_block.len()); + + // of course we have at least 1 byte left (or cur_block would have been compressed) + // push the additional 1bit + self.cur_block.push(128); + let remaining = BLOCK_SIZE * 4 - self.cur_block.len(); + + // if we have no enough space (64bit), we need a extra block + if remaining < 8 { + self.cur_block.resize(BLOCK_SIZE * 4, 0u8); + let word_block = Self::block_transform(&self.cur_block); + + self.update_block( + layouter, + chng, + word_block.as_slice().try_into().unwrap(), + padding_pos, + false, + )?; + + padding_pos = Some(0); + self.cur_block.clear(); + } + + self.cur_block.resize(BLOCK_SIZE * 4 - 8, 0u8); + self.cur_block + .extend(((self.length * 8) as u64).to_be_bytes()); + assert_eq!(self.cur_block.len(), BLOCK_SIZE * 4); + + let word_block = Self::block_transform(&self.cur_block); + + let digest_state = self.update_block( + layouter, + chng, + word_block.as_slice().try_into().unwrap(), + padding_pos, + true, + )?; + self.cur_block.clear(); + self.length = 0; + + let digest_state = match digest_state { + Table16State::Dense(s) => s, + _ => panic!("unexpected state type"), + }; + + Ok(digest_state.map(|s| s.value()).map(BlockWord)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use halo2_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit}; + + struct MyCircuit(Vec<(Vec, Option<[u32; 8]>)>); + + impl Circuit for MyCircuit { + type Config = CircuitConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + struct DevTable { + s_enable: Column, + input_rlc: Column, + input_len: Column, + hashes_rlc: Column, + is_effect: Column, + } + + impl SHA256Table for DevTable { + fn cols(&self) -> [Column; 5] { + [ + self.s_enable.into(), + self.input_rlc.into(), + self.input_len.into(), + self.hashes_rlc.into(), + self.is_effect.into(), + ] + } + } + + let dev_table = DevTable { + s_enable: meta.fixed_column(), + input_rlc: meta.advice_column(), + input_len: meta.advice_column(), + hashes_rlc: meta.advice_column(), + is_effect: meta.advice_column(), + }; + meta.enable_constant(dev_table.s_enable); + + let chng = Expression::Constant(Fr::from(0x1000u64)); + Self::Config::configure(meta, dev_table, chng) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chng_v = Value::known(Fr::from(0x1000u64)); + let mut hasher = Hasher::new(config, &mut layouter)?; + + for (input, digest) in &self.0 { + hasher.update(&mut layouter, chng_v, input)?; + let ret_digest = hasher.finalize(&mut layouter, chng_v)?; + //println!("{:#x?}", ret_digest); + if let Some(check_digest) = digest { + for (w, check) in ret_digest.into_iter().zip(*check_digest) { + w.0.assert_if_known(|digest_word| *digest_word == check); + } + } + } + Ok(()) + } + } + + const DIGEST_ABC: [u32; 8] = [ + 0b10111010011110000001011010111111, + 0b10001111000000011100111111101010, + 0b01000001010000010100000011011110, + 0b01011101101011100010001000100011, + 0b10110000000000110110000110100011, + 0b10010110000101110111101010011100, + 0b10110100000100001111111101100001, + 0b11110010000000000001010110101101, + ]; + + const DIGEST_ABD: [u32; 8] = [ + 0xa52d159f, 0x262b2c6d, 0xdb724a61, 0x840befc3, 0x6eb30c88, 0x877a4030, 0xb65cbe86, + 0x298449c9, + ]; + + const DIGEST_BLOCK: [u32; 8] = [ + 0xffe054fe, 0x7ae0cb6d, 0xc65c3af9, 0xb61d5209, 0xf439851d, 0xb43d0ba5, 0x997337df, + 0x154668eb, + ]; + + const DIGEST_AX65: [u32; 8] = [ + 0x635361c4, 0x8bb9eab1, 0x4198e76e, 0xa8ab7f1a, 0x41685d6a, 0xd62aa914, 0x6d301d4f, + 0x17eb0ae0, + ]; + + const DIGEST_NIL: [u32; 8] = [ + 0xe3b0c442, 0x98fc1c14, 0x9afbf4c8, 0x996fb924, 0x27ae41e4, 0x649b934c, 0xa495991b, + 0x7852b855, + ]; + + #[test] + fn sha256_simple() { + let circuit = MyCircuit(vec![(vec![b'a', b'b', b'c'], Some(DIGEST_ABC))]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_multiple() { + let circuit = MyCircuit(vec![ + (vec![b'a', b'b', b'c'], Some(DIGEST_ABC)), + (vec![b'a', b'b', b'd'], Some(DIGEST_ABD)), + ]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_long() { + let circuit = MyCircuit(vec![(vec![b'a'; 65], Some(DIGEST_AX65))]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_long_block() { + let circuit = MyCircuit(vec![ + (vec![b'a'; 64], Some(DIGEST_BLOCK)), + (vec![b'a'; 65], Some(DIGEST_AX65)), + (vec![b'a'; 64], Some(DIGEST_BLOCK)), + (vec![b'a', b'b', b'c'], Some(DIGEST_ABC)), + ]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_nil() { + let circuit = MyCircuit(vec![ + (vec![], Some(DIGEST_NIL)), + (vec![], Some(DIGEST_NIL)), + (vec![], Some(DIGEST_NIL)), + ]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_padding_continue() { + let circuit = MyCircuit(vec![(vec![b'a'; 62], None)]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + fn sha256_complex() { + let circuit = MyCircuit(vec![ + (vec![b'a'; 65], Some(DIGEST_AX65)), + (vec![b'a', b'b', b'c'], Some(DIGEST_ABC)), + (vec![b'b'; 62], None), + (vec![b'c'; 128], None), + (vec![], Some(DIGEST_NIL)), + (vec![], Some(DIGEST_NIL)), + ]); + let prover = match MockProver::::run(17, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } + + #[test] + #[cfg(feature = "dev-graph")] + fn print_sha256_circuit() { + use plotters::prelude::*; + + let root = + BitMapBackend::new("sha-256-circuit-layout.png", (1024, 3480)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let root = root + .titled( + "16-bit Table SHA-256 Chip with SHA256 table", + ("sans-serif", 60), + ) + .unwrap(); + + let circuit = MyCircuit(vec![ + (vec![b'a', b'b', b'c'], None), + (vec![b'a', b'b', b'c'], None), + ]); + halo2_proofs::dev::CircuitLayout::default() + .render::(13, &circuit, &root) + .unwrap(); + + let prover = match MockProver::<_>::run(13, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:?}"), + }; + assert_eq!(prover.verify(), Ok(())); + } +} diff --git a/zkevm-circuits/src/sha256_circuit/test.rs b/zkevm-circuits/src/sha256_circuit/test.rs new file mode 100644 index 0000000000..1b0ce5b45d --- /dev/null +++ b/zkevm-circuits/src/sha256_circuit/test.rs @@ -0,0 +1,134 @@ +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner, Value}, + plonk::{create_proof, keygen_pk, keygen_vk, verify_proof, Circuit, ConstraintSystem, Error}, + transcript::{Blake2bRead, Blake2bWrite, Challenge255}, +}; +use rand::rngs::OsRng; + +use super::{circuit::*, BLOCK_SIZE}; + +use halo2_proofs::{ + halo2curves::bn256::{Bn256, Fr}, + plonk::{Advice, Any, Column, Expression, Fixed}, + poly::{ + commitment::ParamsProver, + kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::{ProverSHPLONK, VerifierSHPLONK}, + strategy::SingleStrategy, + }, + }, + transcript::{TranscriptReadBuffer, TranscriptWriterBuffer}, +}; + +const CAP_BLK: usize = 24; + +#[derive(Default, Clone, Copy)] +struct MyCircuit { + blocks: usize, +} + +impl Circuit for MyCircuit { + type Config = CircuitConfig; + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + unimplemented!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + struct DevTable { + s_enable: Column, + input_rlc: Column, + input_len: Column, + hashes_rlc: Column, + is_effect: Column, + } + + impl SHA256Table for DevTable { + fn cols(&self) -> [Column; 5] { + [ + self.s_enable.into(), + self.input_rlc.into(), + self.input_len.into(), + self.hashes_rlc.into(), + self.is_effect.into(), + ] + } + } + + let dev_table = DevTable { + s_enable: meta.fixed_column(), + input_rlc: meta.advice_column(), + input_len: meta.advice_column(), + hashes_rlc: meta.advice_column(), + is_effect: meta.advice_column(), + }; + meta.enable_constant(dev_table.s_enable); + + let chng = Expression::Constant(Fr::from(0x100u64)); + Self::Config::configure(meta, dev_table, chng) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let chng_v = Value::known(Fr::from(0x100u64)); + let mut hasher = Hasher::new(config, &mut layouter)?; + + for _ in 0..self.blocks { + hasher.update(&mut layouter, chng_v, &[b'a'; BLOCK_SIZE * 4])?; + } + if hasher.updated_size() > 0 { + hasher.finalize(&mut layouter, chng_v)?; + } + + for _ in hasher.blocks()..CAP_BLK { + hasher.update(&mut layouter, chng_v, &[])?; + hasher.finalize(&mut layouter, chng_v)?; + } + + Ok(()) + } +} + +#[test] +fn vk_stable() { + let k = 17; + + let params: ParamsKZG = ParamsKZG::new(k); + let empty_circuit: MyCircuit = MyCircuit { blocks: 0 }; + + // Initialize the proving key + let vk_from_empty = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail"); + + let circuit = MyCircuit { blocks: 16 }; + let vk = keygen_vk(¶ms, &circuit).expect("keygen_vk should not fail"); + let pk = keygen_pk(¶ms, vk, &circuit).expect("keygen_pk should not fail"); + + // Create a proof + let mut transcript = Blake2bWrite::<_, _, Challenge255<_>>::init(vec![]); + create_proof::, ProverSHPLONK<_>, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[], + OsRng, + &mut transcript, + ) + .expect("proof generation should not fail"); + let proof: Vec = transcript.finalize(); + + let strategy = SingleStrategy::new(¶ms); + let mut transcript = Blake2bRead::<_, _, Challenge255<_>>::init(&proof[..]); + verify_proof::, VerifierSHPLONK<_>, _, _, _>( + ¶ms, + &vk_from_empty, + strategy, + &[], + &mut transcript, + ) + .unwrap(); +} diff --git a/zkevm-circuits/src/sig_circuit/ecdsa.rs b/zkevm-circuits/src/sig_circuit/ecdsa.rs index 4ade29bb68..699e73f0ad 100644 --- a/zkevm-circuits/src/sig_circuit/ecdsa.rs +++ b/zkevm-circuits/src/sig_circuit/ecdsa.rs @@ -79,15 +79,15 @@ where .or(ctx, Existing(s_is_zero), Existing(s_in_range)); // load required constants - let zero = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::zero())); - let one = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::one())); + let zero = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::ZERO)); + let one = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::ONE)); let point_at_infinity = EcPoint::construct( ecc_chip .field_chip() - .load_constant(ctx, fe_to_biguint(&CF::zero())), + .load_constant(ctx, fe_to_biguint(&CF::ZERO)), ecc_chip .field_chip() - .load_constant(ctx, fe_to_biguint(&CF::zero())), + .load_constant(ctx, fe_to_biguint(&CF::ZERO)), ); // compute u1 = m * s^{-1} mod n @@ -179,7 +179,7 @@ where let lambda = { let a_val = base_chip.get_assigned_value(&dy); let b_val = base_chip.get_assigned_value(&dx); - let b_inv = b_val.map(|bv| bv.invert().unwrap_or(CF::zero())); + let b_inv = b_val.map(|bv| bv.invert().unwrap_or(CF::ZERO)); let quot_val = a_val.zip(b_inv).map(|(a, bi)| a * bi); let quot = base_chip.load_private(ctx, FpConfig::::fe_to_witness("_val)); // constrain quot * b - a = 0 mod p diff --git a/zkevm-circuits/src/sig_circuit/test.rs b/zkevm-circuits/src/sig_circuit/test.rs index 586c6f375c..f4366c51d6 100644 --- a/zkevm-circuits/src/sig_circuit/test.rs +++ b/zkevm-circuits/src/sig_circuit/test.rs @@ -6,7 +6,6 @@ use halo2_proofs::{ arithmetic::Field as HaloField, dev::MockProver, halo2curves::{ - bn256::Fr, group::Curve, secp256k1::{self, Secp256k1Affine}, }, @@ -23,7 +22,7 @@ fn test_edge_cases() { sign_types::{biguint_to_32bytes_le, recover_pk2, SECP256K1_Q}, word, ToBigEndian, ToLittleEndian, Word, }; - use halo2_proofs::halo2curves::{group::ff::PrimeField, secp256k1::Fq}; + use halo2_proofs::halo2curves::{bn256::Fr, group::ff::PrimeField, secp256k1::Fq}; use num::{BigUint, Integer}; use rand::SeedableRng; use rand_xorshift::XorShiftRng; @@ -156,6 +155,7 @@ fn test_edge_cases() { fn sign_verify() { use super::utils::LOG_TOTAL_NUM_ROWS; use crate::sig_circuit::utils::MAX_NUM_SIG; + use halo2_proofs::halo2curves::bn256::Fr; use rand::SeedableRng; use rand_xorshift::XorShiftRng; use sha3::{Digest, Keccak256}; @@ -269,7 +269,7 @@ fn sign_with_rng( fn run(k: u32, max_verif: usize, signatures: Vec) { // SignVerifyChip -> ECDSAChip -> MainGate instance column - let circuit = SigCircuit:: { + let circuit = SigCircuit:: { max_verif, signatures, _marker: PhantomData, diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 459cc14025..8c98c7cfd0 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -738,7 +738,6 @@ impl StateCircuitConfig { .enumerate() .map(|(part_idx, (indices, is_first_time))| { let rows = rows.as_slice(); - let updates = updates; move |mut region: Region<'_, F>| { if *is_first_time { *is_first_time = false; @@ -891,7 +890,7 @@ impl StateCircuit { n_rows, #[cfg(any(feature = "test", test, feature = "test-circuits"))] overrides: HashMap::new(), - _marker: PhantomData::default(), + _marker: PhantomData, } } } @@ -909,7 +908,7 @@ impl SubCircuit for StateCircuit { n_rows: block.circuits_params.max_rws, #[cfg(any(feature = "test", test, feature = "test-circuits"))] overrides: HashMap::new(), - _marker: PhantomData::default(), + _marker: PhantomData, } } diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs index 2cfe59c917..ee1006b1dd 100644 --- a/zkevm-circuits/src/state_circuit/dev.rs +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -17,6 +17,8 @@ where { type Config = (StateCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index b0567279a0..192216c668 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -38,10 +38,8 @@ use strum_macros::EnumIter; // 1. limb_difference fits into 16 bits. // 2. limb_difference is not zero because its inverse exists. -// 3. RLC of the pairwise limb differences before the first_different_limb is -// zero. -// 4. limb_difference equals the difference of the limbs at -// first_different_limb. +// 3. RLC of the pairwise limb differences before the first_different_limb is zero. +// 4. limb_difference equals the difference of the limbs at first_different_limb. #[derive(Clone, Copy, Debug, EnumIter)] pub enum LimbIndex { @@ -317,7 +315,7 @@ fn rlc_limb_differences( ) -> Vec> { let mut result = vec![]; let mut partial_sum = 0u64.expr(); - let powers_of_randomness = once(1.expr()).chain(powers_of_randomness.into_iter()); + let powers_of_randomness = once(1.expr()).chain(powers_of_randomness); for ((cur_limb, prev_limb), power_of_randomness) in cur .be_limbs() .iter() diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index 3f73904bba..57a0996ada 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -992,7 +992,7 @@ fn variadic_size_check() { overrides: HashMap::default(), n_rows: N_ROWS, exports: Default::default(), - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let power_of_randomness = circuit.instance(); let prover1 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -1021,7 +1021,7 @@ fn variadic_size_check() { overrides: HashMap::default(), n_rows: N_ROWS, exports: Default::default(), - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let power_of_randomness = circuit.instance(); let prover2 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -1060,7 +1060,7 @@ fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockP overrides, n_rows: N_ROWS, exports: Default::default(), - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let instance = circuit.instance(); @@ -1107,5 +1107,8 @@ fn assert_error_matches(result: Result<(), Vec>, name: &str) { VerifyFailure::CellNotAssigned { .. } => panic!(), VerifyFailure::ConstraintPoisoned { .. } => panic!(), VerifyFailure::Permutation { .. } => panic!(), + // FIXME + // &VerifyFailure::InstanceCellNotAssigned { .. } | &VerifyFailure::Shuffle { .. } => + // todo!(), } } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 8eb2d084bd..ce88802a94 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -74,12 +74,16 @@ use crate::{ pi_circuit::{PiCircuit, PiCircuitConfig, PiCircuitConfigArgs}, poseidon_circuit::{PoseidonCircuit, PoseidonCircuitConfig, PoseidonCircuitConfigArgs}, rlp_circuit_fsm::{RlpCircuit, RlpCircuitConfig, RlpCircuitConfigArgs}, + sha256_circuit::{ + CircuitConfig as SHA256CircuitConfig, CircuitConfigArgs as SHA256CircuitConfigArgs, + SHA256Circuit, + }, sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ BlockTable, BytecodeTable, CopyTable, DualByteTable, EccTable, ExpTable, FixedTable, KeccakTable, LookupTable, ModExpTable, MptTable, PoseidonTable, PowOfRandTable, - RlpFsmRlpTable as RlpTable, RwTable, SigTable, TxTable, U16Table, U8Table, + RlpFsmRlpTable as RlpTable, RwTable, SHA256Table, SigTable, TxTable, U16Table, U8Table, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{circuit_stats, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -125,6 +129,7 @@ pub struct SuperCircuitConfig { sig_circuit: SigCircuitConfig, modexp_circuit: ModExpCircuitConfig, ecc_circuit: EccCircuitConfig, + sha256_circuit: SHA256CircuitConfig, #[cfg(not(feature = "poseidon-codehash"))] bytecode_circuit: BytecodeCircuitConfig, #[cfg(feature = "poseidon-codehash")] @@ -161,9 +166,9 @@ impl SubCircuitConfig for SuperCircuitConfig { fn new( meta: &mut ConstraintSystem, Self::ConfigArgs { - max_txs, - max_calldata, - max_inner_blocks, + max_txs: _, + max_calldata: _, + max_inner_blocks: _, mock_randomness: _mock_randomness, challenges, }: Self::ConfigArgs, @@ -204,6 +209,8 @@ impl SubCircuitConfig for SuperCircuitConfig { log_circuit_info(meta, "rlp table"); let keccak_table = KeccakTable::construct(meta); log_circuit_info(meta, "keccak table"); + let sha256_table = SHA256Table::construct(meta); + log_circuit_info(meta, "sha256 table"); let sig_table = SigTable::construct(meta); log_circuit_info(meta, "sig table"); let modexp_table = ModExpTable::construct(meta); @@ -239,6 +246,15 @@ impl SubCircuitConfig for SuperCircuitConfig { ); log_circuit_info(meta, "keccak circuit"); + let sha256_circuit = SHA256CircuitConfig::new( + meta, + SHA256CircuitConfigArgs { + sha256_table: sha256_table.clone(), + challenges: challenges_expr.clone(), + }, + ); + log_circuit_info(meta, "sha256 circuit"); + let poseidon_circuit = PoseidonCircuitConfig::new(meta, PoseidonCircuitConfigArgs { poseidon_table }); log_circuit_info(meta, "poseidon circuit"); @@ -256,9 +272,6 @@ impl SubCircuitConfig for SuperCircuitConfig { let pi_circuit = PiCircuitConfig::new( meta, PiCircuitConfigArgs { - max_txs, - max_calldata, - max_inner_blocks, block_table: block_table.clone(), keccak_table: keccak_table.clone(), tx_table: tx_table.clone(), @@ -427,6 +440,7 @@ impl SubCircuitConfig for SuperCircuitConfig { copy_circuit, bytecode_circuit, keccak_circuit, + sha256_circuit, poseidon_circuit, pi_circuit, rlp_circuit, @@ -454,7 +468,7 @@ pub struct SubcircuitRowUsage { } /// The Super Circuit contains all the zkEVM circuits -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] pub struct SuperCircuit< F: Field, const MAX_TXS: usize, @@ -480,6 +494,8 @@ pub struct SuperCircuit< pub exp_circuit: ExpCircuit, /// Keccak Circuit pub keccak_circuit: KeccakCircuit, + /// SHA256 Circuit + pub sha256_circuit: SHA256Circuit, /// Poseidon hash Circuit pub poseidon_circuit: PoseidonCircuit, /// Sig Circuit @@ -493,6 +509,8 @@ pub struct SuperCircuit< /// Mpt Circuit #[cfg(feature = "zktrie")] pub mpt_circuit: MptCircuit, + + circuit_params: CircuitsParams, } impl< @@ -531,6 +549,8 @@ impl< push("copy", copy); let keccak = KeccakCircuit::min_num_rows_block(block); push("keccak", keccak); + let sha256 = SHA256Circuit::min_num_rows_block(block); + push("sha256", sha256); let tx = TxCircuit::min_num_rows_block(block); push("tx", tx); let rlp = RlpCircuit::min_num_rows_block(block); @@ -613,6 +633,7 @@ impl< let exp_circuit = ExpCircuit::new_from_block(block); let modexp_circuit = ModExpCircuit::new_from_block(block); let keccak_circuit = KeccakCircuit::new_from_block(block); + let sha256_circuit = SHA256Circuit::new_from_block(block); let poseidon_circuit = PoseidonCircuit::new_from_block(block); let rlp_circuit = RlpCircuit::new_from_block(block); let sig_circuit = SigCircuit::new_from_block(block); @@ -629,6 +650,7 @@ impl< copy_circuit, exp_circuit, keccak_circuit, + sha256_circuit, poseidon_circuit, rlp_circuit, sig_circuit, @@ -636,6 +658,7 @@ impl< ecc_circuit, #[cfg(feature = "zktrie")] mpt_circuit, + circuit_params: block.circuits_params, } } @@ -701,6 +724,9 @@ impl< log::debug!("assigning keccak_circuit"); self.keccak_circuit .synthesize_sub(&config.keccak_circuit, challenges, layouter)?; + log::debug!("assigning sha256_circuit"); + self.sha256_circuit + .synthesize_sub(&config.sha256_circuit, challenges, layouter)?; log::debug!("assigning poseidon_circuit"); self.poseidon_circuit .synthesize_sub(&config.poseidon_circuit, challenges, layouter)?; @@ -768,9 +794,15 @@ impl< { type Config = (SuperCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { - Self::default() + let dummy_block = Block:: { + circuits_params: self.circuit_params, + ..Default::default() + }; + Self::new_from_block(&dummy_block) } fn configure(meta: &mut ConstraintSystem) -> Self::Config { diff --git a/zkevm-circuits/src/super_circuit/precompile_block_trace.rs b/zkevm-circuits/src/super_circuit/precompile_block_trace.rs index 28d77212b3..df429b3f69 100644 --- a/zkevm-circuits/src/super_circuit/precompile_block_trace.rs +++ b/zkevm-circuits/src/super_circuit/precompile_block_trace.rs @@ -19,7 +19,7 @@ use eth_types::{address, bytecode, evm_types::GasCost, word, Bytecode, ToWord, W pub(crate) fn block_ec_ops() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let bytecode_ec_add = PrecompileCallArgs { name: "ecAdd (valid inputs)", @@ -230,7 +230,7 @@ pub(crate) fn block_ec_ops() -> BlockTrace { #[cfg(feature = "scroll")] pub(crate) fn block_precompile_oog() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let ec_add_input = vec![ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -326,7 +326,7 @@ pub(crate) fn block_precompile_oog() -> BlockTrace { pub(crate) fn block_invalid_precompile() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; // Tx1: 4 Out of Gas cases for EcAdd, EcMul, EcPairing and ModExp let mut contract_code_oog = Bytecode::default(); @@ -851,7 +851,7 @@ pub(crate) fn block_invalid_precompile() -> BlockTrace { pub(crate) fn block_precompile_invalid_ec_pairing_fq_overflow() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let bytecode_ec_pairing_oor = PrecompileCallArgs { name: "ecPairing (invalid): invalid field element, mod p is valid", @@ -942,3 +942,53 @@ pub(crate) fn block_precompile_invalid_ec_pairing_fq_overflow() -> BlockTrace { .l2_trace() .clone() } + +#[cfg(feature = "scroll")] +pub(crate) fn block_precompile_sha256() -> BlockTrace { + let mut rng = ChaCha20Rng::seed_from_u64(2); + + let chain_id = MOCK_CHAIN_ID; + + let bytecode_sha256 = PrecompileCallArgs { + name: "sha256: short bytes", + setup_code: bytecode! { + PUSH3(0x616263) + PUSH1(0x00) + MSTORE + }, + call_data_offset: 0x1d.into(), + call_data_length: 0x03.into(), + ret_offset: 0x20.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Sha256.address().to_word(), + ..Default::default() + } + .with_call_op(OpcodeId::CALL); + + let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); + + let addr_a = wallet_a.address(); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + + // 2 accounts and 1 tx. + TestContext::<2, 1>::new( + Some(vec![Word::zero()]), + |accs| { + accs[0].address(addr_a).balance(Word::from(1u64 << 24)); + accs[1] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode_sha256); + }, + |mut txs, accs| { + txs[0] + .from(wallet_a.clone()) + .to(accs[1].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .l2_trace() + .clone() +} diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index f368bd44f6..7688643673 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -22,6 +22,29 @@ use crate::witness::block_apply_mpt_state; use eth_types::l2_types::BlockTrace; use eth_types::{address, bytecode, word, Bytecode, ToWord, Word}; +#[test] +fn super_circuit_created_from_dummy_block() { + let dummy_block = Block:: { + circuits_params: CircuitsParams { + max_rws: 4_000_000, + max_copy_rows: 0, // dynamic + max_txs: 10, + max_calldata: 2_000_000, + max_inner_blocks: 8, + max_bytecode: 3_000_000, + max_mpt_rows: 2_000_000, + max_poseidon_rows: 4_000_000, + max_keccak_rows: 0, + max_exp_steps: 100_000, + max_evm_rows: 0, + max_rlp_rows: 2_070_000, + ..Default::default() + }, + ..Default::default() + }; + let _circuit = SuperCircuit::::new_from_block(&dummy_block); +} + #[test] fn super_circuit_degree() { let mut cs = ConstraintSystem::::default(); @@ -109,7 +132,7 @@ fn callee_bytecode(is_return: bool, offset: u64, length: u64) -> Bytecode { fn block_1tx_deploy() -> BlockTrace { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); let addr_a = wallet_a.address(); @@ -133,7 +156,7 @@ fn block_1tx_deploy() -> BlockTrace { fn block_1tx_ctx() -> TestContext<2, 1> { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let bytecode = bytecode! { GAS @@ -177,7 +200,7 @@ pub(crate) fn block_1tx() -> GethData { fn block_2tx_ctx() -> TestContext<2, 2> { let mut rng = ChaCha20Rng::seed_from_u64(2); - let chain_id = *MOCK_CHAIN_ID; + let chain_id = MOCK_CHAIN_ID; let bytecode = bytecode! { GAS @@ -449,3 +472,16 @@ fn serial_test_super_circuit_precompile_invalid_ec_pairing_fq_overflow() { test_super_circuit::(block, circuits_params); } + +#[ignore] +#[cfg(feature = "scroll")] +#[test] +fn serial_test_super_circuit_precompile_sha256() { + const MAX_TXS: usize = 1; + const MAX_CALLDATA: usize = 0x180; + + let block = precompile_block_trace::block_precompile_sha256(); + let circuits_params = precomiple_super_circuits_params(MAX_TXS, MAX_CALLDATA); + + test_super_circuit::(block, circuits_params); +} diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index b583b98b2b..fac43bc270 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -28,7 +28,6 @@ use gadgets::{ util::{and, assign_global, not, split_u256, split_u256_limb64, Expr}, }; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{AssignedCell, Layouter, Region, Value}, halo2curves::bn256::{Fq, G1Affine}, plonk::{Advice, Any, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, @@ -238,6 +237,12 @@ pub enum TxFieldTag { TxHash, /// TxType: Type of the transaction TxType, + /// Access list address count (EIP-2930) + AccessListAddressesLen, + /// Access list all storage key count (EIP-2930) + AccessListStorageKeysLen, + /// RLC of access list (EIP-2930) + AccessListRLC, /// The block number in which this tx is included. BlockNumber, } @@ -369,7 +374,6 @@ impl TxTable { let mut calldata_assignments: Vec<[Value; 4]> = Vec::new(); // Assign Tx data (all tx fields except for calldata) let padding_txs = (txs.len()..max_txs) - .into_iter() .map(|tx_id| { let mut padding_tx = Transaction::dummy(chain_id); padding_tx.id = tx_id + 1; @@ -664,7 +668,7 @@ impl LookupTable for RwTable { } impl RwTable { /// Construct a new RwTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { + pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { q_enable: meta.fixed_column(), rw_counter: meta.advice_column(), @@ -823,7 +827,7 @@ impl LookupTable for MptTable { impl MptTable { /// Construct a new MptTable - pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { + pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { Self { q_enable: meta.fixed_column(), address: meta.advice_column(), @@ -944,7 +948,7 @@ impl PoseidonTable { pub(crate) const INPUT_WIDTH: usize = Self::WIDTH - 1; /// Construct a new PoseidonTable - pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { + pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { Self { q_enable: meta.fixed_column(), hash_id: meta.advice_column(), @@ -956,7 +960,6 @@ impl PoseidonTable { } } - #[cfg(test)] /// Load mpt hashes (without the poseidon circuit) for testing purposes. pub fn load( &self, @@ -1207,7 +1210,7 @@ impl BytecodeTable { } /// A sub-table of bytecode without is_code nor push_rlc. - fn columns_mini(&self) -> Vec> { + fn columns_mini(&self) -> Vec> { vec![ self.q_enable.into(), self.code_hash.into(), @@ -1219,7 +1222,7 @@ impl BytecodeTable { /// The expressions of the sub-table of bytecode without is_code nor push_rlc. pub fn table_exprs_mini(&self, meta: &mut VirtualCells) -> Vec> { - self.columns_mini::() + self.columns_mini() .iter() .map(|&column| meta.query_any(column, Rotation::cur())) .collect() @@ -1562,6 +1565,127 @@ impl KeccakTable { } } +/// SHA256 Table, used to verify SHA256 hashing from RLC'ed input in precompile. +#[derive(Clone, Debug)] +pub struct SHA256Table { + /// True when the row is enabled + pub q_enable: Column, + /// True when the row is final + pub is_final: Column, + /// Byte array input as `RLC(reversed(input))` + pub input_rlc: Column, // RLC of input bytes + /// Byte array input length + pub input_len: Column, + /// RLC of the hash result + pub output_rlc: Column, // RLC of hash of input bytes +} + +impl LookupTable for SHA256Table { + fn columns(&self) -> Vec> { + vec![ + self.q_enable.into(), + self.is_final.into(), + self.input_rlc.into(), + self.input_len.into(), + self.output_rlc.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("q_enable"), + String::from("is_final"), + String::from("input_rlc"), + String::from("input_len"), + String::from("output_rlc"), + ] + } +} + +impl SHA256Table { + /// Construct a new KeccakTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + q_enable: meta.fixed_column(), + is_final: meta.advice_column(), + input_len: meta.advice_column(), + input_rlc: meta.advice_column_in(SecondPhase), + output_rlc: meta.advice_column_in(SecondPhase), + } + } + + /// Generate the sha256 table assignments from a byte array pair of input/output. + /// Used only for dev_load + pub fn assignments( + entry: (&[u8], &[u8; 32]), + challenges: &Challenges>, + ) -> Vec<[Value; 4]> { + let (input, output) = entry; + let input_len = Value::known(F::from(input.len() as u64)); + let input_rlc = challenges + .keccak_input() + .map(|challenge| rlc::value(input.iter().rev(), challenge)); + let output_rlc = challenges + .keccak_input() + .map(|challenge| rlc::value(&Word::from_big_endian(output).to_le_bytes(), challenge)); + + vec![[Value::known(F::one()), input_rlc, input_len, output_rlc]] + } + + /// Provide this function for the case that we want to consume a sha256 + /// table but without running the full sha256 circuit + pub fn dev_load<'a, F: Field>( + &self, + layouter: &mut impl Layouter, + entries: impl IntoIterator, &'a [u8; 32])> + Clone, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "sha256 table dev", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_fixed( + || "sha256 table all-zero row", + self.q_enable, + offset, + || Value::known(F::one()), + )?; + region.assign_advice( + || "sha256 table all-zero row", + column, + offset, + || Value::known(F::zero()), + )?; + } + offset += 1; + + let table_columns = >::advice_columns(self); + for (input, digest) in entries.clone() { + for row in Self::assignments((input, digest), challenges) { + region.assign_fixed( + || format!("table row {offset}"), + self.q_enable, + offset, + || Value::known(F::one()), + )?; + for (&column, value) in table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("table row {offset}"), + column, + offset, + || value, + )?; + } + offset += 1; + } + } + Ok(()) + }, + ) + } +} + /// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, /// TxLogs and TxCallData. #[derive(Clone, Copy, Debug)] @@ -1679,7 +1803,7 @@ impl CopyTable { .copy_bytes .bytes_write_prev .clone() - .unwrap_or(vec![]); + .unwrap_or_default(); let mut rw_counter = copy_event.rw_counter_start(); let mut rwc_inc_left = copy_event.rw_counter_delta(); @@ -2405,15 +2529,14 @@ impl LookupTable for SigTable { } } -/// 1. if EcAdd(P, Q) == R then: -/// (arg1_rlc, arg2_rlc, arg3_rlc, arg4_rlc) \mapsto (output1_rlc, output2_rlc). +/// 1. if EcAdd(P, Q) == R then: (arg1_rlc, arg2_rlc, arg3_rlc, arg4_rlc) \mapsto (output1_rlc, +/// output2_rlc). /// /// where arg1_rlc = rlc(P.x), arg2_rlc = rlc(P.y), /// arg3_rlc = rlc(Q.x), arg4_rlc = rlc(Q.x), /// output1_rlc = rlc(R.x), output2_rlc = rlc(R.y), /// -/// 2. if EcMul(P, s) == R then: -/// (arg1_rlc, arg2_rlc, arg3_rlc) \mapsto (output1_rlc, output2_rlc). +/// 2. if EcMul(P, s) == R then: (arg1_rlc, arg2_rlc, arg3_rlc) \mapsto (output1_rlc, output2_rlc). /// /// where arg1_rlc = rlc(P.x), arg2_rlc = rlc(P.y), /// arg3_rlc = s @@ -2678,7 +2801,7 @@ impl ModExpTable { let mut bytes = [0u8; 64]; remainder.to_little_endian(&mut bytes[..32]); - F::from_bytes_wide(&bytes) + F::from_uniform_bytes(&bytes) } /// fill a blank 4-row region start from offset for empty lookup @@ -2736,6 +2859,7 @@ impl ModExpTable { for i in 0..3 { for (limbs, &col) in [base_limbs, exp_limbs, modulus_limbs, result_limbs] + .iter() .zip([&self.base, &self.exp, &self.modulus, &self.result]) { region.assign_advice( @@ -2748,13 +2872,10 @@ impl ModExpTable { } // native is not used by lookup (and in fact it can be omitted in dev) - for (word, &col) in [ - &event.base, - &event.exponent, - &event.modulus, - &event.result, - ] - .zip([&self.base, &self.exp, &self.modulus, &self.result]) + for (word, &col) in + [&event.base, &event.exponent, &event.modulus, &event.result] + .iter() + .zip([&self.base, &self.exp, &self.modulus, &self.result]) { region.assign_advice( || format!("modexp table native row {}", offset + 3), diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 8c1b88c450..db3ee75eda 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -14,14 +14,15 @@ pub use dev::TxCircuitTester as TestTxCircuit; use crate::{ evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, - sig_circuit::SigCircuit, + // sig_circuit::SigCircuit, table::{ BlockContextFieldTag::{CumNumTxs, NumAllTxs, NumTxs}, BlockTable, KeccakTable, LookupTable, RlpFsmRlpTable as RlpTable, SigTable, TxFieldTag, TxFieldTag::{ - BlockNumber, CallData, CallDataGasCost, CallDataLength, CallDataRLC, CalleeAddress, - CallerAddress, ChainID, Gas, GasPrice, IsCreate, Nonce, SigR, SigS, SigV, - TxDataGasCost, TxHashLength, TxHashRLC, TxSignHash, TxSignLength, TxSignRLC, + AccessListAddressesLen, AccessListRLC, AccessListStorageKeysLen, BlockNumber, CallData, + CallDataGasCost, CallDataLength, CallDataRLC, CalleeAddress, CallerAddress, ChainID, + Gas, GasPrice, IsCreate, Nonce, SigR, SigS, SigV, TxDataGasCost, TxHashLength, + TxHashRLC, TxSignHash, TxSignLength, TxSignRLC, }, TxTable, U16Table, U8Table, }, @@ -42,13 +43,13 @@ use crate::{ use bus_mapping::circuit_input_builder::keccak_inputs_sign_verify; use eth_types::{ geth_types::{ - TxType, + access_list_size, TxType, TxType::{Eip155, L1Msg, PreEip155}, }, sign_types::SignData, Address, Field, ToAddress, ToBigEndian, ToScalar, }; -use ethers_core::utils::keccak256; +use ethers_core::utils::{keccak256, rlp::Encodable}; use gadgets::{ binary_number::{BinaryNumberChip, BinaryNumberConfig}, comparator::{ComparatorChip, ComparatorConfig, ComparatorInstruction}, @@ -79,7 +80,7 @@ use halo2_proofs::plonk::SecondPhase; use itertools::Itertools; /// Number of rows of one tx occupies in the fixed part of tx table -pub const TX_LEN: usize = 23; +pub const TX_LEN: usize = 26; /// Offset of TxHash tag in the tx table pub const TX_HASH_OFFSET: usize = 21; /// Offset of ChainID tag in the tx table @@ -345,6 +346,9 @@ impl SubCircuitConfig for TxCircuitConfig { is_tx_tag!(is_hash, TxHash); is_tx_tag!(is_block_num, BlockNumber); is_tx_tag!(is_tx_type, TxType); + is_tx_tag!(is_access_list_addresses_len, AccessListAddressesLen); + is_tx_tag!(is_access_list_storage_keys_len, AccessListStorageKeysLen); + is_tx_tag!(is_access_list_rlc, AccessListRLC); let tx_id_unchanged = IsEqualChip::configure( meta, @@ -472,6 +476,9 @@ impl SubCircuitConfig for TxCircuitConfig { (is_block_num(meta), Null), (is_chain_id_expr(meta), Tag::ChainId.into()), (is_tx_type(meta), Null), + (is_access_list_addresses_len(meta), Null), + (is_access_list_storage_keys_len(meta), Null), + (is_access_list_rlc(meta), RLC), ]; cb.require_boolean( @@ -557,6 +564,9 @@ impl SubCircuitConfig for TxCircuitConfig { cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); + // TODO: add constraints for AccessListAddressesLen, AccessListStorageKeysLen + // and AccessListRLC. + ////////////////////////////////////////////////////////// ///// Constraints for booleans that reducing degree ///// ////////////////////////////////////////////////////////// @@ -978,7 +988,7 @@ impl SubCircuitConfig for TxCircuitConfig { input_expr .into_iter() - .zip(table_expr.into_iter()) + .zip(table_expr) .map(|(input, table)| (input * condition.clone(), table)) .collect::>() }); @@ -1087,7 +1097,7 @@ impl SubCircuitConfig for TxCircuitConfig { input_expr .into_iter() - .zip(table_expr.into_iter()) + .zip(table_expr) .map(|(input, table)| (input * condition.clone(), table)) .collect::>() }); @@ -1107,7 +1117,7 @@ impl SubCircuitConfig for TxCircuitConfig { input_expr .into_iter() - .zip(table_expr.into_iter()) + .zip(table_expr) .map(|(input, table)| (input * condition.clone(), table)) .collect::>() }); @@ -1438,15 +1448,12 @@ impl TxCircuitConfig { 1.expr(), // is_final = 1 ] .into_iter() - .zip( - vec![ - meta.query_advice(tx_table.tx_id, Rotation::cur()), - meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(calldata_gas_cost_acc, Rotation::cur()), - meta.query_advice(is_final, Rotation::cur()), - ] - .into_iter(), - ) + .zip(vec![ + meta.query_advice(tx_table.tx_id, Rotation::cur()), + meta.query_fixed(tx_table.tag, Rotation::cur()), + meta.query_advice(calldata_gas_cost_acc, Rotation::cur()), + meta.query_advice(is_final, Rotation::cur()), + ]) .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); @@ -1470,15 +1477,12 @@ impl TxCircuitConfig { 1.expr(), // is_final = true ] .into_iter() - .zip( - vec![ - meta.query_advice(tx_table.tx_id, Rotation::cur()), - meta.query_fixed(tx_table.tag, Rotation::cur()), - meta.query_advice(tx_table.index, Rotation::cur()), - meta.query_advice(is_final, Rotation::cur()), - ] - .into_iter(), - ) + .zip(vec![ + meta.query_advice(tx_table.tx_id, Rotation::cur()), + meta.query_fixed(tx_table.tag, Rotation::cur()), + meta.query_advice(tx_table.index, Rotation::cur()), + meta.query_advice(is_final, Rotation::cur()), + ]) .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); @@ -1505,7 +1509,7 @@ impl TxCircuitConfig { input_exprs .into_iter() - .zip(table_exprs.into_iter()) + .zip(table_exprs) .map(|(input, table)| (input * enable.expr(), table)) .collect() }); @@ -1540,7 +1544,7 @@ impl TxCircuitConfig { input_exprs .into_iter() - .zip(rlp_table.table_exprs(meta).into_iter()) + .zip(rlp_table.table_exprs(meta)) .map(|(input, table)| (enable.expr() * input, table)) .collect() }); @@ -1572,7 +1576,7 @@ impl TxCircuitConfig { is_none, ] .into_iter() - .zip_eq(rlp_table.table_exprs(meta).into_iter()) + .zip_eq(rlp_table.table_exprs(meta)) .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); @@ -1610,7 +1614,7 @@ impl TxCircuitConfig { is_none, ] .into_iter() - .zip_eq(rlp_table.table_exprs(meta).into_iter()) + .zip_eq(rlp_table.table_exprs(meta)) .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); @@ -1662,7 +1666,7 @@ impl TxCircuitConfig { input_exprs .into_iter() - .zip(table_exprs.into_iter()) + .zip(table_exprs) .map(|(input, table)| (input * enabled.expr(), table)) .collect() }); @@ -1687,7 +1691,7 @@ impl TxCircuitConfig { meta.query_advice(tx_table.value, Rotation(2)), // output_rlc ] .into_iter() - .zip(keccak_table.table_exprs(meta).into_iter()) + .zip(keccak_table.table_exprs(meta)) .map(|(arg, table)| (enable.clone() * arg, table)) .collect() }); @@ -1740,6 +1744,8 @@ impl TxCircuitConfig { } else { get_rlp_len_tag_length(&tx.rlp_unsigned) }; + let (access_list_address_size, access_list_storage_key_size) = + access_list_size(&tx.access_list); // fixed_rows of a tx let fixed_rows = vec![ @@ -1932,6 +1938,33 @@ impl TxCircuitConfig { None, Value::known(F::from(tx.tx_type as u64)), ), + ( + AccessListAddressesLen, + None, + Value::known(F::from(access_list_address_size)), + ), + ( + AccessListStorageKeysLen, + None, + Value::known(F::from(access_list_storage_key_size)), + ), + ( + AccessListRLC, + Some(RlpTableInputValue { + tag: RLC, + is_none: false, + be_bytes_len: 0, + be_bytes_rlc: zero_rlc, + }), + // TODO: need to check if it's correct with RLP. + rlc_be_bytes( + &tx.access_list + .as_ref() + .map(|access_list| access_list.rlp_bytes()) + .unwrap_or_default(), + keccak_input, + ), + ), (BlockNumber, None, Value::known(F::from(tx.block_number))), ]; @@ -2402,7 +2435,7 @@ impl TxCircuit { chain_id, start_l1_queue_index, value_cells: RefCell::new(None), - _marker: PhantomData::default(), + _marker: PhantomData, } } @@ -2755,7 +2788,6 @@ impl SubCircuit for TxCircuit { assert!(self.txs.len() <= self.max_txs); let padding_txs = (self.txs.len()..self.max_txs) - .into_iter() .map(|i| { let mut tx = Transaction::dummy(self.chain_id); tx.id = i + 1; @@ -2824,7 +2856,6 @@ pub(crate) fn get_sign_data( chain_id: usize, ) -> Result, halo2_proofs::plonk::Error> { let padding_txs = (txs.len()..max_txs) - .into_iter() .map(|i| { let mut tx = Transaction::dummy(chain_id as u64); tx.id = i + 1; diff --git a/zkevm-circuits/src/tx_circuit/dev.rs b/zkevm-circuits/src/tx_circuit/dev.rs index 2d59cc2884..e17711710c 100644 --- a/zkevm-circuits/src/tx_circuit/dev.rs +++ b/zkevm-circuits/src/tx_circuit/dev.rs @@ -155,6 +155,8 @@ impl SubCircuit for TxCircuitTester { impl Circuit for TxCircuitTester { type Config = (TxCircuitTesterConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + #[cfg(feature = "circuit-params")] + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -214,7 +216,6 @@ impl Circuit for TxCircuitTester { config.u16_table.load(&mut layouter)?; let padding_txs = (self.tx_circuit.txs.len()..self.tx_circuit.max_txs) - .into_iter() .map(|i| { let mut tx = Transaction::dummy(self.tx_circuit.chain_id); tx.id = i + 1; diff --git a/zkevm-circuits/src/tx_circuit/test.rs b/zkevm-circuits/src/tx_circuit/test.rs index 946a182ebd..efe550b4b4 100644 --- a/zkevm-circuits/src/tx_circuit/test.rs +++ b/zkevm-circuits/src/tx_circuit/test.rs @@ -8,6 +8,7 @@ use std::cmp::max; use super::*; use crate::{ + sig_circuit::{SigCircuit, SigCircuitConfig, SigCircuitConfigArgs}, tx_circuit::{dev::TxCircuitTester, get_sign_data}, util::{log2_ceil, unusable_rows}, }; @@ -155,7 +156,7 @@ fn tx_circuit_2tx_2max_tx() { mock_tx.into() }) .collect(), - *mock::MOCK_CHAIN_ID, + mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0, @@ -171,7 +172,7 @@ fn tx_circuit_0tx_1max_tx() { const MAX_CALLDATA: usize = 80; assert_eq!( - run::(vec![], *mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), + run::(vec![], mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), Ok(()) ); } @@ -185,7 +186,7 @@ fn tx_circuit_1tx_1max_tx() { let tx: Transaction = mock::CORRECT_MOCK_TXS[0].clone().into(); assert_eq!( - run::(vec![tx], *mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), + run::(vec![tx], mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), Ok(()) ); } @@ -199,7 +200,7 @@ fn tx_circuit_1tx_2max_tx() { let tx = build_pre_eip155_tx(); assert_eq!( - run::(vec![tx], *mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), + run::(vec![tx], mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), Ok(()) ); } @@ -213,7 +214,7 @@ fn tx_circuit_l1_msg_tx() { let tx = build_l1_msg_tx(); assert_eq!( - run::(vec![tx], *mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), + run::(vec![tx], mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0), Ok(()) ); } @@ -230,7 +231,7 @@ fn tx_circuit_bad_address() { assert!(run::( vec![tx.into()], - *mock::MOCK_CHAIN_ID, + mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0 @@ -250,7 +251,7 @@ fn tx_circuit_to_is_zero() { assert_eq!( run::( vec![tx.into()], - *mock::MOCK_CHAIN_ID, + mock::MOCK_CHAIN_ID, MAX_TXS, MAX_CALLDATA, 0 diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index 2bbb96654c..95004bdb77 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -3,7 +3,6 @@ use std::collections::BTreeSet; use bus_mapping::evm::OpcodeId; use halo2_proofs::{ - arithmetic::FieldExt, circuit::{Layouter, Value}, plonk::{Challenge, Circuit, ConstraintSystem, Error, Expression, FirstPhase, VirtualCells}, }; @@ -22,7 +21,7 @@ pub use gadgets::util::{assign_global, Expr}; /// A wrapper of is_zero in gadgets which gives is_zero at any rotation pub mod is_zero; -pub(crate) fn query_expression( +pub(crate) fn query_expression( meta: &mut ConstraintSystem, mut f: impl FnMut(&mut VirtualCells) -> T, ) -> T { @@ -34,7 +33,7 @@ pub(crate) fn query_expression( expr.unwrap() } -pub(crate) fn random_linear_combine_word(bytes: [u8; 32], randomness: F) -> F { +pub(crate) fn random_linear_combine_word(bytes: [u8; 32], randomness: F) -> F { rlc::value(&bytes, randomness) } @@ -64,7 +63,7 @@ pub struct MockChallenges { impl MockChallenges { /// .. - pub fn construct(_meta: &mut ConstraintSystem) -> Self { + pub fn construct(_meta: &mut ConstraintSystem) -> Self { Self { evm_word: 0x100, keccak_input: 0x101, @@ -72,7 +71,7 @@ impl MockChallenges { } } /// .. - pub fn exprs(&self, _meta: &mut ConstraintSystem) -> Challenges> { + pub fn exprs(&self, _meta: &mut ConstraintSystem) -> Challenges> { Challenges { evm_word: Expression::Constant(F::from(self.evm_word)), keccak_input: Expression::Constant(F::from(self.keccak_input)), @@ -80,7 +79,7 @@ impl MockChallenges { } } /// .. - pub fn values(&self, _layouter: &impl Layouter) -> Challenges> { + pub fn values(&self, _layouter: &impl Layouter) -> Challenges> { Challenges { evm_word: Value::known(F::from(self.evm_word)), keccak_input: Value::known(F::from(self.keccak_input)), @@ -91,7 +90,7 @@ impl MockChallenges { impl Challenges { /// Construct `Challenges` by allocating challenges in specific phases. - pub fn construct(meta: &mut ConstraintSystem) -> Self { + pub fn construct(meta: &mut ConstraintSystem) -> Self { #[cfg(any(not(feature = "onephase"), feature = "test", test))] let _dummy_cols = [ meta.advice_column(), @@ -107,7 +106,7 @@ impl Challenges { } /// Returns `Expression` of challenges from `ConstraintSystem`. - pub fn exprs(&self, meta: &mut ConstraintSystem) -> Challenges> { + pub fn exprs(&self, meta: &mut ConstraintSystem) -> Challenges> { let [evm_word, keccak_input, lookup_input] = query_expression(meta, |meta| { [self.evm_word, self.keccak_input, self.lookup_input] .map(|challenge| meta.query_challenge(challenge)) @@ -120,7 +119,7 @@ impl Challenges { } /// Returns `Value` of challenges from `Layouter`. - pub fn values(&self, layouter: &impl Layouter) -> Challenges> { + pub fn values(&self, layouter: &impl Layouter) -> Challenges> { Challenges { evm_word: layouter.get_challenge(self.evm_word), keccak_input: layouter.get_challenge(self.keccak_input), diff --git a/zkevm-circuits/src/witness.rs b/zkevm-circuits/src/witness.rs index da94347026..928b0d15c3 100644 --- a/zkevm-circuits/src/witness.rs +++ b/zkevm-circuits/src/witness.rs @@ -4,8 +4,8 @@ mod block; pub use block::{ - block_apply_mpt_state, block_convert, block_convert_with_l1_queue_index, Block, BlockContext, - BlockContexts, + block_apply_mpt_state, block_convert, block_convert_with_l1_queue_index, + block_mocking_apply_mpt, Block, BlockContext, BlockContexts, }; mod bytecode; diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 5e587e4aaa..7cd32edae4 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -12,7 +12,7 @@ use crate::{ use bus_mapping::{ circuit_input_builder::{ self, BigModExp, CircuitsParams, CopyEvent, EcAddOp, EcMulOp, EcPairingOp, ExpEvent, - PrecompileEvents, + PrecompileEvents, SHA256, }, Error, }; @@ -81,6 +81,8 @@ pub struct Block { pub struct BlockContexts { /// Hashmap that maps block number to its block context. pub ctxs: BTreeMap, + /// relax mode flag inherited from block builder + pub relax_mode: bool, } impl Block { @@ -136,6 +138,11 @@ impl Block { self.precompile_events.get_modexp_events() } + /// Get sha256 operations from all precompiled contract calls in this block. + pub(crate) fn get_sha256(&self) -> Vec { + self.precompile_events.get_sha256_events() + } + pub(crate) fn print_evm_circuit_row_usage(&self) { let mut num_rows = 0; let mut counter = HashMap::new(); @@ -448,6 +455,7 @@ impl From<&circuit_input_builder::Block> for BlockContexts { ) }) .collect::>(), + relax_mode: block.is_relaxed(), } } } @@ -463,8 +471,7 @@ pub fn block_convert( let last_block_num = block .headers .iter() - .rev() - .next() + .next_back() .map(|(k, _)| *k) .unwrap_or_default(); let chain_id = block.chain_id(); @@ -497,7 +504,7 @@ pub fn block_convert( let withdraw_root_entry = mpt_updates.get(&super::rw::Rw::AccountStorage { tx_id: total_tx_as_txid, account_address: *bus_mapping::l2_predeployed::message_queue::ADDRESS, - storage_key: *bus_mapping::l2_predeployed::message_queue::WITHDRAW_TRIE_ROOT_SLOT, + storage_key: bus_mapping::l2_predeployed::message_queue::WITHDRAW_TRIE_ROOT_SLOT, // following field is not used in Mpt::Key so we just fill them arbitrarily rw_counter: 0, is_write: false, @@ -591,3 +598,10 @@ pub fn block_apply_mpt_state(block: &mut Block, mpt_state: &MptStat block.mpt_updates.fill_state_roots(mpt_state); block.state_root = Some(block.mpt_updates.new_root()); } + +/// Mocking generate mpt witness from mpt states +pub fn block_mocking_apply_mpt(block: &mut Block) { + block.mpt_updates.mock_fill_state_roots(); + block.state_root = Some(block.mpt_updates.new_root()); + block.prev_state_root = block.mpt_updates.old_root(); +} diff --git a/zkevm-circuits/src/witness/mpt.rs b/zkevm-circuits/src/witness/mpt.rs index 95e954b61c..ee517c34ce 100644 --- a/zkevm-circuits/src/witness/mpt.rs +++ b/zkevm-circuits/src/witness/mpt.rs @@ -5,12 +5,10 @@ use crate::{ use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word, U256}; use halo2_proofs::circuit::Value; use itertools::Itertools; -#[cfg(test)] -use mpt_zktrie::state::builder::HASH_SCHEME_DONE; use mpt_zktrie::{ mpt_circuits::{serde::SMTTrace, MPTProofType}, state, - state::witness::WitnessGenerator, + state::{builder::init_hash_scheme, witness::WitnessGenerator}, }; use serde::{Deserialize, Serialize}; pub use state::ZktrieState; @@ -130,35 +128,42 @@ impl MptUpdates { }) } - #[cfg(test)] /// initialize a mock witness generator that is consistent with the old values of self.updates - pub fn mock_fill_state_roots(&mut self) { - assert!(*HASH_SCHEME_DONE); - let mut wit_gen = WitnessGenerator::from(&ZktrieState::default()); + pub(crate) fn mock_fill_state_roots(&mut self) { + init_hash_scheme(); + let temp_trie = ZktrieState::default(); + let mut wit_gen = WitnessGenerator::from(&temp_trie); + let mut storage_touched = std::collections::HashSet::<(&Address, &Word)>::new(); for (key, update) in &mut self.updates { + // we should only handle the storage key occur for the first time + if let Key::AccountStorage { + storage_key, + address, + .. + } = key + { + if !storage_touched.insert((address, storage_key)) { + continue; + } + } let key = key.set_non_exists(Word::zero(), update.old_value); - self.old_root = U256::from_little_endian( - wit_gen - .handle_new_state( - update.proof_type(), - match key { - Key::Account { address, .. } | Key::AccountStorage { address, .. } => { - address - } - }, - update.old_value, - Word::zero(), - match key { - Key::Account { .. } => None, - Key::AccountStorage { storage_key, .. } => Some(storage_key), - }, - ) - .account_path[1] - .root - .as_ref(), + wit_gen.handle_new_state( + update.proof_type(), + match key { + Key::Account { address, .. } | Key::AccountStorage { address, .. } => address, + }, + update.old_value, + Word::zero(), + match key { + Key::Account { .. } => None, + Key::AccountStorage { storage_key, .. } => Some(storage_key), + }, ); } + self.old_root = U256::from_big_endian(wit_gen.root().as_bytes()); self.fill_state_roots_from_generator(wit_gen); + log::debug!("mocking fill_state_roots done"); + self.pretty_print(); } pub(crate) fn fill_state_roots(&mut self, init_trie: &ZktrieState) { @@ -197,7 +202,7 @@ impl MptUpdates { if gen_withdraw_proof { // generate withdraw proof let address = *bus_mapping::l2_predeployed::message_queue::ADDRESS; - let key = *bus_mapping::l2_predeployed::message_queue::WITHDRAW_TRIE_ROOT_SLOT; + let key = bus_mapping::l2_predeployed::message_queue::WITHDRAW_TRIE_ROOT_SLOT; let account_proof = wit_gen.account_proof(address); let storage_proof = wit_gen.storage_proof(address, key); // TODO: add withdraw_root to WithdrawProof? @@ -543,11 +548,11 @@ fn value_prev(row: &Rw) -> Word { #[cfg(test)] mod test { use super::*; - use mpt_zktrie::state::builder::HASH_SCHEME_DONE; + use mpt_zktrie::state::builder::init_hash_scheme; #[test] fn invalid_state_from_reading_nonce() { - assert!(*HASH_SCHEME_DONE,); + init_hash_scheme(); let key = Key::Account { address: Address::zero(), @@ -567,7 +572,7 @@ mod test { #[test] fn invalid_state_from_reading_balance() { - assert!(*HASH_SCHEME_DONE,); + init_hash_scheme(); let key = Key::Account { address: Address::zero(), @@ -602,7 +607,7 @@ mod test { #[test] fn nonce_update_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -631,7 +636,7 @@ mod test { #[test] fn nonexisting_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -662,7 +667,7 @@ mod test { #[test] fn nonce_update_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -688,7 +693,7 @@ mod test { #[test] fn nonce_update_type_2() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); updates.insert(nonce_update(Address::zero())); @@ -721,7 +726,7 @@ mod test { #[test] fn balance_update_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -750,7 +755,7 @@ mod test { #[test] fn balance_update_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -776,7 +781,7 @@ mod test { #[test] fn balance_update_type_2() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); updates.insert(nonce_update(Address::zero())); @@ -798,7 +803,7 @@ mod test { #[test] fn update_code_size_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -834,7 +839,7 @@ mod test { #[test] fn update_code_hash_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -870,7 +875,7 @@ mod test { #[test] fn update_keccak_code_hash_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -908,7 +913,7 @@ mod test { // except for type 1 -> type 2 and type 2 -> type 1 #[test] fn update_storage_existing_to_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -962,8 +967,8 @@ mod test { #[test] fn update_storage_type_1_to_type_1() { - assert!(*HASH_SCHEME_DONE); - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1026,42 +1031,42 @@ mod test { #[test] fn update_storage_type_2_to_type_2() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn update_storage_type_1_to_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn update_storage_type_2_to_existing() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn update_storage_existing_to_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn update_storage_existing_to_type_2() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn read_storage_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn read_storage_type_2() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); } #[test] fn read_empty_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1092,7 +1097,7 @@ mod test { #[test] fn write_empty_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1124,7 +1129,7 @@ mod test { #[test] fn read_singleton_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1164,7 +1169,7 @@ mod test { #[test] fn write_singleton_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1205,7 +1210,7 @@ mod test { #[test] fn write_zero_to_singleton_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1246,7 +1251,7 @@ mod test { #[test] fn write_zero_to_doubleton_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1297,7 +1302,7 @@ mod test { #[test] fn write_zero_to_storage_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1340,7 +1345,7 @@ mod test { #[test] fn empty_storage_proof_empty_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1370,7 +1375,7 @@ mod test { #[test] fn empty_storage_proof_singleton_trie() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1410,7 +1415,7 @@ mod test { #[test] fn empty_storage_proof_type_1() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1453,7 +1458,7 @@ mod test { #[ignore = "TODO(mason): is it valid to put these storage writes on empty acc?"] #[test] fn empty_account_empty_storage_proof() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. @@ -1493,7 +1498,7 @@ mod test { #[ignore = "TODO(mason): is it valid to put these storage writes on empty acc?"] #[test] fn empty_account_storage_write() { - assert!(*HASH_SCHEME_DONE); + init_hash_scheme(); let mut updates = MptUpdates::default(); // Add precompile addresses in so MPT isn't too empty. diff --git a/zkevm-circuits/src/witness/rlp_fsm.rs b/zkevm-circuits/src/witness/rlp_fsm.rs index 3a6b3cf077..bb2c2d9bc0 100644 --- a/zkevm-circuits/src/witness/rlp_fsm.rs +++ b/zkevm-circuits/src/witness/rlp_fsm.rs @@ -1,6 +1,6 @@ use eth_types::{Address, Field, H160, U256}; use gadgets::{impl_expr, util::Expr}; -use halo2_proofs::{arithmetic::FieldExt, circuit::Value, plonk::Expression}; +use halo2_proofs::{circuit::Value, plonk::Expression}; use strum_macros::EnumIter; use crate::util::Challenges; @@ -642,7 +642,7 @@ impl_expr!(Tag); impl_expr!(Format); impl_expr!(State); -impl Expr for RlpTag { +impl Expr for RlpTag { fn expr(&self) -> Expression { match self { Self::Tag(tag) => tag.expr(), @@ -653,7 +653,7 @@ impl Expr for RlpTag { /// Data table holds the raw RLP bytes #[derive(Clone, Copy, Debug)] -pub struct DataTable { +pub struct DataTable { /// The index of tx to be decoded pub tx_id: u64, /// The format of format to be decoded @@ -670,7 +670,7 @@ pub struct DataTable { pub gas_cost_acc: Value, } -impl DataTable { +impl DataTable { /// values pub fn values(&self) -> Vec> { vec![ @@ -687,7 +687,7 @@ impl DataTable { /// RLP table that is connected to the state machine in the RLP circuit. #[derive(Clone, Copy, Debug)] -pub struct RlpTable { +pub struct RlpTable { /// The index of tx we decoded pub tx_id: u64, /// The format of format we decoded @@ -710,7 +710,7 @@ pub struct RlpTable { /// State Machine #[derive(Clone, Copy, Debug)] -pub struct StateMachine { +pub struct StateMachine { /// Current state pub state: State, /// Current tag to be decoded @@ -742,7 +742,7 @@ pub struct StateMachine { /// Represents the witness in a single row of the RLP circuit. #[derive(Clone, Debug)] -pub struct RlpFsmWitnessRow { +pub struct RlpFsmWitnessRow { /// Witness to the RLP table. pub rlp_table: RlpTable, /// The state machine witness. @@ -751,7 +751,7 @@ pub struct RlpFsmWitnessRow { /// The RlpFsmWitnessGen trait is implemented by data types who's RLP encoding can /// be verified by the RLP-encoding circuit. -pub trait RlpFsmWitnessGen: Sized { +pub trait RlpFsmWitnessGen: Sized { /// Generate witness to the RLP state machine, as a vector of RlpFsmWitnessRow. fn gen_sm_witness(&self, challenges: &Challenges>) -> Vec>; diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 1ab5c1707b..a0b6f5ed80 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -180,7 +180,7 @@ impl RwMap { .collect(); let padding_length = Self::padding_len(rows.len(), target_len); let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); - (padding.chain(rows.into_iter()).collect(), padding_length) + (padding.chain(rows).collect(), padding_length) } /// Build Rws for assignment #[inline(always)] diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs index a7336a591a..c1a0349174 100644 --- a/zkevm-circuits/src/witness/step.rs +++ b/zkevm-circuits/src/witness/step.rs @@ -187,7 +187,11 @@ impl From<&circuit_input_builder::ExecStep> for ExecutionState { ExecutionState::BLOCKCTXU64 } OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, - OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, + OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, + #[cfg(not(feature = "scroll"))] + OpcodeId::DIFFICULTY => ExecutionState::BLOCKCTXU256, + #[cfg(feature = "scroll")] + OpcodeId::DIFFICULTY => ExecutionState::DIFFICULTY, OpcodeId::GAS => ExecutionState::GAS, OpcodeId::SAR => ExecutionState::SAR, OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index f02359dbb5..5680194c1b 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -18,13 +18,17 @@ use crate::{ use bus_mapping::circuit_input_builder::{self, get_dummy_tx_hash, TxL1Fee}; use eth_types::{ evm_types::gas_utils::tx_data_gas_cost, - geth_types::{TxType, TxType::PreEip155}, + geth_types::{access_list_size, TxType, TxType::PreEip155}, sign_types::{ biguint_to_32bytes_le, ct_option_ok_or, get_dummy_tx, recover_pk2, SignData, SECP256K1_Q, }, - Address, Error, Field, Signature, ToBigEndian, ToLittleEndian, ToScalar, ToWord, Word, H256, + AccessList, Address, Error, Field, Signature, ToBigEndian, ToLittleEndian, ToScalar, ToWord, + Word, H256, +}; +use ethers_core::{ + types::TransactionRequest, + utils::{keccak256, rlp::Encodable}, }; -use ethers_core::{types::TransactionRequest, utils::keccak256}; use halo2_proofs::{ circuit::Value, halo2curves::{group::ff::PrimeField, secp256k1}, @@ -85,6 +89,8 @@ pub struct Transaction { pub l1_fee: TxL1Fee, /// Committed values of L1 fee pub l1_fee_committed: TxL1Fee, + /// Optional access list for EIP-2930 + pub access_list: Option, /// The calls made in the transaction pub calls: Vec, /// The steps executioned in the transaction @@ -155,6 +161,8 @@ impl Transaction { ) -> Vec<[Value; 4]> { let tx_hash_be_bytes = keccak256(&self.rlp_signed); let tx_sign_hash_be_bytes = keccak256(&self.rlp_unsigned); + let (access_list_address_size, access_list_storage_key_size) = + access_list_size(&self.access_list); let ret = vec![ [ @@ -298,6 +306,32 @@ impl Transaction { Value::known(F::zero()), Value::known(F::from(self.tx_type as u64)), ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListAddressesLen as u64)), + Value::known(F::zero()), + Value::known(F::from(access_list_address_size)), + ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListStorageKeysLen as u64)), + Value::known(F::zero()), + Value::known(F::from(access_list_storage_key_size)), + ], + [ + Value::known(F::from(self.id as u64)), + Value::known(F::from(TxContextFieldTag::AccessListRLC as u64)), + Value::known(F::zero()), + // TODO: need to check if it's correct with RLP. + rlc_be_bytes( + &self + .access_list + .as_ref() + .map(|access_list| access_list.rlp_bytes()) + .unwrap_or_default(), + challenges.keccak_input(), + ), + ], [ Value::known(F::from(self.id as u64)), Value::known(F::from(TxContextFieldTag::BlockNumber as u64)), @@ -843,6 +877,7 @@ impl From for Transaction { (unsigned, signed) }; + let access_list = Some(mock_tx.access_list); Self { block_number: 1, id: mock_tx.transaction_index.as_usize(), @@ -867,6 +902,7 @@ impl From for Transaction { s: sig.s, l1_fee: Default::default(), l1_fee_committed: Default::default(), + access_list, calls: vec![], steps: vec![], } @@ -918,6 +954,7 @@ pub(super) fn tx_convert( s: tx.signature.s, l1_fee: tx.l1_fee, l1_fee_committed: tx.l1_fee_committed, + access_list: tx.access_list.clone(), calls: tx .calls() .iter() @@ -1016,7 +1053,7 @@ mod tests { let to = eth_tx.to.map_or(Address::zero(), |to| to); let data = eth_tx.input.to_vec(); - let tx_table = vec![ + let tx_table = [ rlc(ð_tx.nonce.to_be_bytes(), evm_word), rlc(ð_tx.gas_price.unwrap().to_be_bytes(), evm_word), Fr::from(eth_tx.gas.as_u64()), @@ -1085,7 +1122,7 @@ mod tests { let to = eth_tx.to.map_or(Address::zero(), |to| to); let data = eth_tx.input.to_vec(); - let tx_table = vec![ + let tx_table = [ rlc(ð_tx.nonce.to_be_bytes(), evm_word), Fr::from(eth_tx.gas.as_u64()), to.to_scalar().unwrap(), diff --git a/zktrie/Cargo.toml b/zktrie/Cargo.toml index 219d3c87d9..7e5b4ff5e0 100644 --- a/zktrie/Cargo.toml +++ b/zktrie/Cargo.toml @@ -8,11 +8,10 @@ license.workspace = true [dependencies] halo2_proofs.workspace = true -mpt-circuits = { package = "halo2-mpt-circuits", git = "https://github.com/scroll-tech/mpt-circuit.git", tag = "v0.7.0" } -zktrie = { git = "https://github.com/scroll-tech/zktrie.git", branch = "v0.7"} +mpt-circuits = { package = "halo2-mpt-circuits", git = "https://github.com/scroll-tech/mpt-circuit.git", branch = "v0.7" } +zktrie = { git = "https://github.com/scroll-tech/zktrie.git", tag = "v0.7.1" } hash-circuit.workspace = true eth-types = { path = "../eth-types" } -lazy_static.workspace = true num-bigint.workspace = true log.workspace = true hex.workspace = true diff --git a/zktrie/src/state.rs b/zktrie/src/state.rs index ea46a9d9fe..fd591ee6d0 100644 --- a/zktrie/src/state.rs +++ b/zktrie/src/state.rs @@ -46,10 +46,7 @@ impl ZktrieState { //proofs: impl IntoIterator, //acc_storage_roots: impl IntoIterator, ) -> Self { - assert!( - *builder::HASH_SCHEME_DONE, - "must set hash scheme into zktrie" - ); + builder::init_hash_scheme(); Self { zk_db: RefCell::new(ZkMemoryDb::new()), diff --git a/zktrie/src/state/builder.rs b/zktrie/src/state/builder.rs index d020fb49d2..07b7840e14 100644 --- a/zktrie/src/state/builder.rs +++ b/zktrie/src/state/builder.rs @@ -6,22 +6,18 @@ use eth_types::{ use std::{ convert::TryFrom, io::{Error, ErrorKind, Read}, + sync::Once, }; -use halo2_proofs::{ - arithmetic::FieldExt, - halo2curves::{bn256::Fr, group::ff::PrimeField}, -}; +use halo2_proofs::halo2curves::{bn256::Fr, group::ff::PrimeField}; use hash_circuit::hash::Hashable; -use lazy_static::lazy_static; - -lazy_static! { - /// Use this boolean to initialize the hash scheme. - pub static ref HASH_SCHEME_DONE: bool = { +/// Init hash scheme +pub fn init_hash_scheme() { + static INIT: Once = Once::new(); + INIT.call_once(|| { zktrie::init_hash_scheme(hash_scheme); - true - }; + }); } static FILED_ERROR_READ: &str = "invalid input field"; diff --git a/zktrie/src/state/witness.rs b/zktrie/src/state/witness.rs index f10e7851cb..8b509a5e43 100644 --- a/zktrie/src/state/witness.rs +++ b/zktrie/src/state/witness.rs @@ -396,7 +396,7 @@ fn smt_hash_from_bytes(bt: &[u8]) -> SMTHash { } fn hash_zktrie_key(key_buf: &[u8; 32]) -> Word { - use halo2_proofs::{arithmetic::FieldExt, halo2curves::bn256::Fr}; + use halo2_proofs::halo2curves::bn256::Fr; use hash_circuit::hash::Hashable; let first_16bytes: [u8; 16] = key_buf[..16].try_into().expect("expect first 16 bytes");