From fab8e2e4a18942fb9b7f71c044705d3a9b198886 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 13 Jun 2023 21:52:21 +0700 Subject: [PATCH] sync main branch to word-lo-hi branch (#1470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Description merge main to word-lo-hi branch ### Issue Link N/A ##### File conflicts list, other file merge from main successfully ``` Conflict file path: deleted: keccak256/src/gate_helpers.rs both modified: zkevm-circuits/src/copy_circuit.rs both modified: zkevm-circuits/src/evm_circuit/execution.rs both modified: zkevm-circuits/src/evm_circuit/execution/balance.rs both modified: zkevm-circuits/src/evm_circuit/execution/begin_tx.rs both modified: zkevm-circuits/src/evm_circuit/execution/end_tx.rs both modified: zkevm-circuits/src/evm_circuit/execution/logs.rs both modified: zkevm-circuits/src/evm_circuit/step.rs both modified: zkevm-circuits/src/evm_circuit/util.rs both modified: zkevm-circuits/src/evm_circuit/util/common_gadget.rs both modified: zkevm-circuits/src/state_circuit.rs both modified: zkevm-circuits/src/state_circuit/constraint_builder.rs both modified: zkevm-circuits/src/table.rs both modified: zkevm-circuits/src/witness/rw.rs ``` After fixing conflict, file change list ``` modified: zkevm-circuits/src/evm_circuit/execution/create.rs modified: zkevm-circuits/src/evm_circuit/util/constraint_builder.rs modified: zkevm-circuits/src/table/block_table.rs modified: zkevm-circuits/src/table/bytecode_table.rs modified: zkevm-circuits/src/table/copy_table.rs modified: zkevm-circuits/src/table/keccak_table.rs modified: zkevm-circuits/src/table/mpt_table.rs modified: zkevm-circuits/src/table/rw_table.rs modified: zkevm-circuits/src/table/tx_table.rs modified: zkevm-circuits/src/util/word.rs ``` --------- Co-authored-by: Chih Cheng Liang Co-authored-by: adria0.eth <5526331+adria0@users.noreply.github.com> Co-authored-by: adria0 Co-authored-by: Eduard S Co-authored-by: Han Co-authored-by: Paul <108982045+ChengYueJia@users.noreply.github.com> Co-authored-by: Steven Co-authored-by: Rohit Narurkar Co-authored-by: KimiWu Co-authored-by: Raphael Co-authored-by: Zhang Zhuo Co-authored-by: naure Co-authored-by: Aurélien Nicolas Co-authored-by: Enrico Bottazzi <85900164+enricobottazzi@users.noreply.github.com> Co-authored-by: Raphael --- .github/labeler.yml | 2 - .github/release-drafter.yml | 29 - Cargo.lock | 793 +--------- Cargo.toml | 1 - Makefile | 18 +- bus-mapping/Cargo.toml | 1 - bus-mapping/src/circuit_input_builder.rs | 17 +- .../src/circuit_input_builder/block.rs | 4 +- bus-mapping/src/circuit_input_builder/call.rs | 7 + .../src/circuit_input_builder/execution.rs | 47 +- .../circuit_input_builder/input_state_ref.rs | 121 +- .../src/circuit_input_builder/tracer_tests.rs | 115 +- bus-mapping/src/error.rs | 131 +- bus-mapping/src/evm.rs | 3 +- bus-mapping/src/evm/opcodes.rs | 55 +- bus-mapping/src/evm/opcodes/begin_end_tx.rs | 34 +- bus-mapping/src/evm/opcodes/calldatacopy.rs | 4 +- bus-mapping/src/evm/opcodes/callop.rs | 34 +- bus-mapping/src/evm/opcodes/codecopy.rs | 4 +- bus-mapping/src/evm/opcodes/create.rs | 445 ++++-- bus-mapping/src/evm/opcodes/extcodecopy.rs | 7 +- bus-mapping/src/evm/opcodes/extcodehash.rs | 4 +- bus-mapping/src/evm/opcodes/extcodesize.rs | 4 +- .../src/evm/opcodes/memory_expansion_test.rs | 8 - bus-mapping/src/evm/opcodes/return_revert.rs | 4 +- bus-mapping/src/evm/opcodes/returndatacopy.rs | 4 +- bus-mapping/src/evm/opcodes/sha3.rs | 174 +- bus-mapping/src/evm/opcodes/sstore.rs | 2 +- bus-mapping/src/lib.rs | 5 +- bus-mapping/src/mock.rs | 12 +- bus-mapping/src/operation.rs | 35 +- bus-mapping/src/state_db.rs | 38 +- circuit-benchmarks/Cargo.toml | 1 - circuit-benchmarks/src/pi_circuit.rs | 21 +- circuit-benchmarks/src/super_circuit.rs | 10 +- eth-types/Cargo.toml | 1 - eth-types/src/evm_types.rs | 145 +- eth-types/src/evm_types/gas_utils.rs | 8 +- eth-types/src/evm_types/opcode_ids.rs | 2 +- eth-types/src/geth_types.rs | 42 +- .../src/plain.rs => eth-types/src/keccak.rs | 167 +- eth-types/src/lib.rs | 66 +- external-tracer/src/lib.rs | 5 +- gadgets/src/batched_is_zero.rs | 1 + gadgets/src/evm_word.rs | 275 ---- gadgets/src/is_zero.rs | 9 +- gadgets/src/less_than.rs | 45 +- gadgets/src/lib.rs | 21 +- gadgets/src/monotone.rs | 310 ---- gadgets/src/mul_add.rs | 1 + gadgets/src/util.rs | 6 +- geth-utils/gethutil/trace.go | 37 +- .../src/integration_test_circuits.rs | 8 +- integration-tests/tests/circuits.rs | 8 +- keccak256/Cargo.toml | 23 - keccak256/src/arith_helpers.rs | 228 --- keccak256/src/common.rs | 49 - keccak256/src/gate_helpers.rs | 28 - keccak256/src/keccak_arith.rs | 366 ----- keccak256/src/lib.rs | 10 - mock/src/account.rs | 8 +- mock/src/test_ctx.rs | 12 +- mock/src/transaction.rs | 16 +- testool/Cargo.toml | 1 - testool/src/compiler.rs | 7 +- testool/src/statetest/executor.rs | 20 +- testool/src/statetest/json.rs | 27 +- testool/src/statetest/spec.rs | 27 +- testool/src/statetest/yaml.rs | 20 +- testool/src/utils.rs | 6 +- zkevm-circuits/Cargo.toml | 12 +- .../src/{stats.rs => bin/stats/helpers.rs} | 99 +- zkevm-circuits/src/bin/stats/main.rs | 185 +++ .../src/bytecode_circuit/circuit.rs | 226 +-- zkevm-circuits/src/bytecode_circuit/dev.rs | 1 + zkevm-circuits/src/bytecode_circuit/test.rs | 2 +- zkevm-circuits/src/copy_circuit.rs | 204 ++- zkevm-circuits/src/copy_circuit/dev.rs | 1 + zkevm-circuits/src/copy_circuit/test.rs | 8 +- zkevm-circuits/src/evm_circuit.rs | 165 +- zkevm-circuits/src/evm_circuit/execution.rs | 65 +- .../src/evm_circuit/execution/add_sub.rs | 8 +- .../src/evm_circuit/execution/addmod.rs | 4 +- .../src/evm_circuit/execution/address.rs | 2 +- .../src/evm_circuit/execution/balance.rs | 14 +- .../src/evm_circuit/execution/begin_tx.rs | 98 +- .../src/evm_circuit/execution/bitwise.rs | 3 +- .../src/evm_circuit/execution/block_ctx.rs | 6 +- .../src/evm_circuit/execution/blockhash.rs | 4 +- .../src/evm_circuit/execution/byte.rs | 4 +- .../src/evm_circuit/execution/calldatacopy.rs | 33 +- .../src/evm_circuit/execution/calldataload.rs | 7 +- .../src/evm_circuit/execution/calldatasize.rs | 2 +- .../src/evm_circuit/execution/caller.rs | 2 +- .../src/evm_circuit/execution/callop.rs | 151 +- .../src/evm_circuit/execution/callvalue.rs | 2 +- .../src/evm_circuit/execution/chainid.rs | 2 +- .../src/evm_circuit/execution/codecopy.rs | 21 +- .../src/evm_circuit/execution/codesize.rs | 2 +- .../src/evm_circuit/execution/comparator.rs | 12 +- .../src/evm_circuit/execution/create.rs | 927 +++++++++++ .../src/evm_circuit/execution/dummy.rs | 6 +- .../src/evm_circuit/execution/dup.rs | 2 +- .../src/evm_circuit/execution/end_block.rs | 4 +- .../src/evm_circuit/execution/end_tx.rs | 169 +- .../execution/error_invalid_jump.rs | 22 +- .../execution/error_invalid_opcode.rs | 2 +- .../evm_circuit/execution/error_oog_call.rs | 48 +- .../execution/error_oog_constant.rs | 22 +- .../evm_circuit/execution/error_oog_exp.rs | 14 +- .../evm_circuit/execution/error_oog_log.rs | 29 +- .../execution/error_oog_memory_copy.rs | 24 +- .../execution/error_oog_sload_sstore.rs | 33 +- .../execution/error_oog_static_memory.rs | 4 +- .../execution/error_return_data_oo_bound.rs | 6 +- .../src/evm_circuit/execution/error_stack.rs | 17 +- .../execution/error_write_protection.rs | 22 +- .../src/evm_circuit/execution/exp.rs | 5 +- .../src/evm_circuit/execution/extcodecopy.rs | 85 +- .../src/evm_circuit/execution/extcodehash.rs | 19 +- .../src/evm_circuit/execution/extcodesize.rs | 8 +- .../src/evm_circuit/execution/gas.rs | 2 +- .../src/evm_circuit/execution/gasprice.rs | 2 +- .../src/evm_circuit/execution/is_zero.rs | 2 +- .../src/evm_circuit/execution/jump.rs | 2 +- .../src/evm_circuit/execution/jumpi.rs | 3 +- .../src/evm_circuit/execution/logs.rs | 53 +- .../src/evm_circuit/execution/memory.rs | 9 +- .../src/evm_circuit/execution/mul_div_mod.rs | 5 +- .../src/evm_circuit/execution/mulmod.rs | 4 +- .../src/evm_circuit/execution/not.rs | 3 +- .../src/evm_circuit/execution/origin.rs | 2 +- .../src/evm_circuit/execution/pc.rs | 2 +- .../src/evm_circuit/execution/pop.rs | 2 +- .../src/evm_circuit/execution/push.rs | 4 +- .../evm_circuit/execution/return_revert.rs | 92 +- .../evm_circuit/execution/returndatacopy.rs | 27 +- .../evm_circuit/execution/returndatasize.rs | 2 +- .../src/evm_circuit/execution/sar.rs | 3 +- .../src/evm_circuit/execution/sdiv_smod.rs | 5 +- .../src/evm_circuit/execution/selfbalance.rs | 2 +- .../src/evm_circuit/execution/sha3.rs | 47 +- .../src/evm_circuit/execution/shl_shr.rs | 5 +- .../execution/signed_comparator.rs | 8 +- .../src/evm_circuit/execution/signextend.rs | 4 +- .../src/evm_circuit/execution/sload.rs | 9 +- .../src/evm_circuit/execution/sstore.rs | 22 +- .../src/evm_circuit/execution/stop.rs | 4 +- .../src/evm_circuit/execution/swap.rs | 4 +- zkevm-circuits/src/evm_circuit/param.rs | 10 +- zkevm-circuits/src/evm_circuit/step.rs | 196 ++- zkevm-circuits/src/evm_circuit/table.rs | 7 +- zkevm-circuits/src/evm_circuit/util.rs | 20 +- .../src/evm_circuit/util/common_gadget.rs | 50 +- .../evm_circuit/util/constraint_builder.rs | 48 +- .../src/evm_circuit/util/instrumentation.rs | 46 +- .../src/evm_circuit/util/math_gadget/rlp.rs | 62 +- .../evm_circuit/util/math_gadget/test_util.rs | 1 + .../src/evm_circuit/util/memory_gadget.rs | 20 +- zkevm-circuits/src/exp_circuit/dev.rs | 1 + zkevm-circuits/src/exp_circuit/test.rs | 2 +- zkevm-circuits/src/keccak_circuit.rs | 62 +- zkevm-circuits/src/keccak_circuit/dev.rs | 1 + zkevm-circuits/src/keccak_circuit/table.rs | 1 + zkevm-circuits/src/keccak_circuit/test.rs | 2 +- zkevm-circuits/src/lib.rs | 3 - zkevm-circuits/src/pi_circuit.rs | 24 +- zkevm-circuits/src/pi_circuit/dev.rs | 80 +- zkevm-circuits/src/pi_circuit/test.rs | 63 +- zkevm-circuits/src/root_circuit.rs | 1 + .../src/root_circuit/aggregation.rs | 2 + zkevm-circuits/src/root_circuit/test.rs | 13 +- zkevm-circuits/src/state_circuit.rs | 37 +- .../src/state_circuit/constraint_builder.rs | 32 +- zkevm-circuits/src/state_circuit/dev.rs | 1 + zkevm-circuits/src/state_circuit/test.rs | 6 +- zkevm-circuits/src/super_circuit.rs | 83 +- zkevm-circuits/src/super_circuit/test.rs | 43 +- zkevm-circuits/src/table.rs | 1404 +---------------- zkevm-circuits/src/table/block_table.rs | 110 ++ zkevm-circuits/src/table/bytecode_table.rs | 112 ++ zkevm-circuits/src/table/copy_table.rs | 291 ++++ zkevm-circuits/src/table/exp_table.rs | 207 +++ zkevm-circuits/src/table/keccak_table.rs | 145 ++ zkevm-circuits/src/table/mpt_table.rs | 111 ++ zkevm-circuits/src/table/rw_table.rs | 138 ++ zkevm-circuits/src/table/tx_table.rs | 225 +++ zkevm-circuits/src/tx_circuit/dev.rs | 1 + zkevm-circuits/src/tx_circuit/sign_verify.rs | 11 +- zkevm-circuits/src/tx_circuit/test.rs | 2 +- zkevm-circuits/src/util.rs | 11 +- zkevm-circuits/src/util/word.rs | 7 +- zkevm-circuits/src/witness.rs | 3 +- zkevm-circuits/src/witness/block.rs | 36 +- zkevm-circuits/src/witness/rw.rs | 74 +- zkevm-circuits/src/witness/step.rs | 242 --- zkevm-circuits/src/witness/tx.rs | 6 +- zkevm-circuits/tests/prover_error.rs | 13 +- 198 files changed, 5357 insertions(+), 6050 deletions(-) delete mode 100644 .github/release-drafter.yml rename keccak256/src/plain.rs => eth-types/src/keccak.rs (59%) delete mode 100644 gadgets/src/evm_word.rs delete mode 100644 gadgets/src/monotone.rs delete mode 100644 keccak256/Cargo.toml delete mode 100644 keccak256/src/arith_helpers.rs delete mode 100644 keccak256/src/common.rs delete mode 100644 keccak256/src/gate_helpers.rs delete mode 100644 keccak256/src/keccak_arith.rs delete mode 100644 keccak256/src/lib.rs rename zkevm-circuits/src/{stats.rs => bin/stats/helpers.rs} (78%) create mode 100644 zkevm-circuits/src/bin/stats/main.rs create mode 100644 zkevm-circuits/src/evm_circuit/execution/create.rs create mode 100644 zkevm-circuits/src/table/block_table.rs create mode 100644 zkevm-circuits/src/table/bytecode_table.rs create mode 100644 zkevm-circuits/src/table/copy_table.rs create mode 100644 zkevm-circuits/src/table/exp_table.rs create mode 100644 zkevm-circuits/src/table/keccak_table.rs create mode 100644 zkevm-circuits/src/table/mpt_table.rs create mode 100644 zkevm-circuits/src/table/rw_table.rs create mode 100644 zkevm-circuits/src/table/tx_table.rs delete mode 100644 zkevm-circuits/src/witness/step.rs diff --git a/.github/labeler.yml b/.github/labeler.yml index 55b78caf22..7ab7550300 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -16,8 +16,6 @@ crate-geth-utils: - geth-utils/**/* crate-integration-tests: - integration-tests/**/* -crate-keccak: - - keccak256/**/* crate-mock: - mock/**/* crate-prover: diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 950aa422ea..0000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,29 +0,0 @@ -categories: - - title: "Changes in the bus-mapping" - label: "crate-bus-mapping" - - title: "Changes in circuit-benchmarks" - label: "crate-circuit-benchmarks" - - title: "Changes in eth-types" - label: "crate-eth-types" - - title: "Changes in external-tracer" - label: "crate-external-tracer" - - title: "Changes in gadgets" - label: "crate-gadgets" - - title: "Changes in geth-utils" - label: "crate-geth-utils" - - title: "Changes in integration-tests" - label: "crate-integration-tests" - - title: "Changes in Keccak" - label: "crate-keccak" - - title: "Changes in mock" - label: "crate-mock" - - title: "Changes in prover" - label: "crate-prover" - - title: "Changes in the zkevm-circuits" - label: "crate-zkevm-circuits" -version-resolver: - default: minor -prerelease: true -template: | - ## What’s Changed - $CHANGES diff --git a/Cargo.lock b/Cargo.lock index 8c80f9102a..54417fccd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aes" version = "0.7.5" @@ -51,15 +45,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.69" @@ -123,7 +108,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" dependencies = [ "futures", "pharos", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -418,10 +403,9 @@ dependencies = [ "ethers-core", "ethers-providers", "gadgets", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "itertools", - "keccak256", "lazy_static", "log", "mock", @@ -475,12 +459,6 @@ dependencies = [ "syn", ] -[[package]] -name = "bytemuck" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" - [[package]] name = "byteorder" version = "1.4.3" @@ -496,12 +474,6 @@ dependencies = [ "serde", ] -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "cc" version = "1.0.79" @@ -520,13 +492,8 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ - "iana-time-zone", - "js-sys", "num-integer", "num-traits", - "time", - "wasm-bindgen", - "winapi", ] [[package]] @@ -547,9 +514,8 @@ dependencies = [ "env_logger", "eth-types", "ethers-signers", - "halo2_proofs 0.2.0", + "halo2_proofs", "itertools", - "keccak256", "mock", "rand", "rand_chacha", @@ -557,17 +523,6 @@ dependencies = [ "zkevm-circuits", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "bitflags", - "textwrap 0.11.0", - "unicode-width", -] - [[package]] name = "clap" version = "3.2.23" @@ -580,9 +535,9 @@ dependencies = [ "clap_lex", "indexmap", "once_cell", - "strsim 0.10.0", + "strsim", "termcolor", - "textwrap 0.16.0", + "textwrap", ] [[package]] @@ -630,25 +585,6 @@ dependencies = [ "syn", ] -[[package]] -name = "cmake" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" -dependencies = [ - "cc", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "coins-bip32" version = "0.7.0" @@ -706,12 +642,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colored" version = "2.0.0" @@ -723,12 +653,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "const-cstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" - [[package]] name = "const-oid" version = "0.9.2" @@ -753,59 +677,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", - "libc", -] - -[[package]] -name = "core-text" -version = "19.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - [[package]] name = "cpufeatures" version = "0.2.5" @@ -815,51 +686,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" -dependencies = [ - "atty", - "cast", - "clap 2.34.0", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" -dependencies = [ - "cast", - "itertools", -] - [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -994,82 +820,14 @@ dependencies = [ "cipher", ] -[[package]] -name = "cxx" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn", + "darling_core", + "darling_macro", ] [[package]] @@ -1082,18 +840,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote", + "strsim", "syn", ] @@ -1103,7 +850,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote", "syn", ] @@ -1128,31 +875,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "derive_builder" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0" -dependencies = [ - "darling 0.10.2", - "derive_builder_core", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" -dependencies = [ - "darling 0.10.2", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "derive_more" version = "0.99.17" @@ -1162,7 +884,7 @@ dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", - "rustc_version 0.4.0", + "rustc_version", "syn", ] @@ -1232,33 +954,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading", -] - [[package]] name = "dunce" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - [[package]] name = "ecc" version = "0.1.0" @@ -1451,7 +1152,7 @@ version = "0.1.0" dependencies = [ "ethers-core", "ethers-signers", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "itertools", "lazy_static", @@ -1462,7 +1163,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha3 0.10.7", "strum", "strum_macros", "subtle", @@ -1597,7 +1297,7 @@ dependencies = [ "ethers-core", "getrandom", "reqwest", - "semver 1.0.16", + "semver", "serde", "serde-aux", "serde_json", @@ -1705,7 +1405,7 @@ dependencies = [ "path-slash", "rayon", "regex", - "semver 1.0.16", + "semver", "serde", "serde_json", "solang-parser", @@ -1763,7 +1463,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" dependencies = [ - "bitvec 1.0.1", "rand_core", "subtle", ] @@ -1807,68 +1506,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-ord" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "font-kit" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fe28504d371085fae9ac7a3450f0b289ab71e07c8e57baa3fb68b9e57d6ce5" -dependencies = [ - "bitflags", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs-next", - "dwrote", - "float-ord", - "freetype", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.1.0" @@ -1878,27 +1521,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "freetype" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" -dependencies = [ - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - [[package]] name = "funty" version = "2.0.0" @@ -2026,7 +1648,7 @@ version = "0.1.0" dependencies = [ "digest 0.7.6", "eth-types", - "halo2_proofs 0.2.0", + "halo2_proofs", "rand", "rand_xorshift", "sha3 0.7.3", @@ -2080,20 +1702,10 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "glob" version = "0.3.1" @@ -2150,28 +1762,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "halo2_proofs" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e925780549adee8364c7f2b685c753f6f3df23bde520c67416e93bf615933760" -dependencies = [ - "blake2b_simd", - "ff 0.12.1", - "group 0.12.1", - "pasta_curves 0.4.1", - "plotters", - "rand_core", - "rayon", - "tabbycat", -] - [[package]] name = "halo2_proofs" version = "0.2.0" @@ -2198,7 +1788,7 @@ dependencies = [ "lazy_static", "num-bigint", "num-traits", - "pasta_curves 0.5.1", + "pasta_curves", "paste", "rand", "rand_core", @@ -2211,7 +1801,7 @@ name = "halo2wrong" version = "0.1.0" source = "git+https://github.com/privacy-scaling-explorations/halo2wrong?tag=v2023_04_20#f72db265aa3cebe297c9b9816e940d0e1d400886" dependencies = [ - "halo2_proofs 0.2.0", + "halo2_proofs", "num-bigint", "num-integer", "num-traits", @@ -2416,30 +2006,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -2456,21 +2022,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "image" -version = "0.24.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-rational", - "num-traits", - "png", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2552,7 +2103,7 @@ dependencies = [ "env_logger", "eth-types", "ethers", - "halo2_proofs 0.2.0", + "halo2_proofs", "lazy_static", "log", "mock", @@ -2617,12 +2168,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" -[[package]] -name = "jpeg-decoder" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" - [[package]] name = "js-sys" version = "0.3.61" @@ -2666,23 +2211,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "keccak256" -version = "0.1.0" -dependencies = [ - "env_logger", - "eth-types", - "halo2_proofs 0.1.0", - "itertools", - "lazy_static", - "log", - "num-bigint", - "num-traits", - "plotters", - "pretty_assertions", - "rand", -] - [[package]] name = "lalrpop" version = "0.19.9" @@ -2730,16 +2258,6 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libsecp256k1" version = "0.7.1" @@ -2788,15 +2306,6 @@ dependencies = [ "libsecp256k1-core", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2871,15 +2380,6 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "mio" version = "0.8.5" @@ -2888,7 +2388,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.42.0", ] @@ -3014,12 +2514,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "opaque-debug" version = "0.2.3" @@ -3143,21 +2637,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "pasta_curves" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" -dependencies = [ - "blake2b_simd", - "ff 0.12.1", - "group 0.12.1", - "lazy_static", - "rand", - "static_assertions", - "subtle", -] - [[package]] name = "pasta_curves" version = "0.5.1" @@ -3185,25 +2664,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" -dependencies = [ - "rustc_version 0.3.3", -] - [[package]] name = "pbkdf2" version = "0.10.1" @@ -3292,7 +2752,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" dependencies = [ "futures", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3387,70 +2847,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "chrono", - "font-kit", - "image", - "lazy_static", - "num-traits", - "pathfinder_geometry", - "plotters-backend", - "plotters-bitmap", - "plotters-svg", - "ttf-parser", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" - -[[package]] -name = "plotters-bitmap" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4a1f21490a6cf4a84c272ad20bd7844ed99a3178187a4c5ab7f2051295beef" -dependencies = [ - "gif", - "image", - "plotters-backend", -] - -[[package]] -name = "plotters-svg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" -dependencies = [ - "bitflags", - "crc32fast", - "flate2", - "miniz_oxide", -] - [[package]] name = "poseidon" version = "0.2.0" @@ -3911,7 +3307,7 @@ dependencies = [ "primitive-types 0.11.1", "rlp", "ruint-macro", - "rustc_version 0.4.0", + "rustc_version", "thiserror", ] @@ -3945,22 +3341,13 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver", ] [[package]] @@ -4034,12 +3421,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - [[package]] name = "scrypt" version = "0.8.1" @@ -4114,15 +3495,6 @@ dependencies = [ "cc", ] -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.16" @@ -4132,15 +3504,6 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "send_wrapper" version = "0.6.0" @@ -4166,16 +3529,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.152" @@ -4226,7 +3579,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro2", "quote", "syn", @@ -4360,7 +3713,7 @@ version = "0.1.0" source = "git+https://github.com/privacy-scaling-explorations/snark-verifier?tag=v2023_04_20#e5d5e4a6ccff2bba71baf77ab7a12b124d6364a1" dependencies = [ "ecc", - "halo2_proofs 0.2.0", + "halo2_proofs", "halo2curves", "hex", "itertools", @@ -4430,12 +3783,6 @@ dependencies = [ "precomputed-hash", ] -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -4494,17 +3841,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tabbycat" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c45590f0f859197b4545be1b17b2bc3cc7bb075f7d1cc0ea1dc6521c0bf256a3" -dependencies = [ - "anyhow", - "derive_builder", - "regex", -] - [[package]] name = "tap" version = "1.0.1" @@ -4537,17 +3873,16 @@ version = "0.1.0" dependencies = [ "anyhow", "bus-mapping", - "clap 3.2.23", + "clap", "env_logger", "eth-types", "ethers-core", "ethers-signers", "external-tracer", "glob", - "halo2_proofs 0.2.0", + "halo2_proofs", "handlebars", "hex", - "keccak256", "log", "mock", "once_cell", @@ -4567,15 +3902,6 @@ dependencies = [ "zkevm-circuits", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "textwrap" version = "0.16.0" @@ -4602,17 +3928,6 @@ dependencies = [ "syn", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -4622,16 +3937,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -4797,12 +4102,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "ttf-parser" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" - [[package]] name = "tungstenite" version = "0.17.3" @@ -4947,12 +4246,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5069,12 +4362,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - [[package]] name = "winapi" version = "0.3.9" @@ -5196,15 +4483,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -5216,7 +4494,7 @@ dependencies = [ "js-sys", "log", "pharos", - "rustc_version 0.4.0", + "rustc_version", "send_wrapper", "thiserror", "wasm-bindgen", @@ -5248,18 +4526,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" -[[package]] -name = "yeslogic-fontconfig-sys" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" -dependencies = [ - "const-cstr", - "dlib", - "once_cell", - "pkg-config", -] - [[package]] name = "zeroize" version = "1.5.7" @@ -5273,7 +4539,6 @@ dependencies = [ "array-init", "bus-mapping", "cli-table", - "criterion", "ctor", "ecc", "ecdsa 0.1.0", @@ -5282,11 +4547,10 @@ dependencies = [ "ethers-core", "ethers-signers", "gadgets", - "halo2_proofs 0.2.0", + "halo2_proofs", "hex", "integer", "itertools", - "keccak256", "lazy_static", "libsecp256k1", "log", @@ -5304,5 +4568,4 @@ dependencies = [ "snark-verifier", "strum", "strum_macros", - "subtle", ] diff --git a/Cargo.toml b/Cargo.toml index be096fc2fb..c8155a1bfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "zkevm-circuits", "bus-mapping", "geth-utils", - "keccak256", "gadgets", "integration-tests", "circuit-benchmarks", diff --git a/Makefile b/Makefile index f8dabed664..6dcf6e8420 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,10 @@ +CARGO = cargo + +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + CARGO += --config 'build.rustdocflags = ["-C", "link-args=-framework CoreFoundation -framework Security"]' +endif + help: ## Display this help screen @grep -h \ -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ @@ -20,8 +27,9 @@ test: ## Run tests for all the workspace members # Run heavy tests serially to avoid OOM @cargo test --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks serial_ -- --ignored --test-threads 1 + test_doc: ## Test the docs - @cargo test --release --all --all-features --doc + @$(CARGO) test --release --all --all-features --doc test_benches: ## Compiles the benchmarks @cargo test --verbose --release --all-features -p circuit-benchmarks --no-run @@ -58,15 +66,15 @@ exp_bench: ## Run Exp Circuit benchmarks circuit_benches: evm_bench state_bench ## Run All Circuit benchmarks stats_state_circuit: # Print a table with State Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_state_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- state stats_evm_circuit: # Print a table with EVM Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_evm_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- evm stats_copy_circuit: # Print a table with Copy Circuit stats by ExecState/opcode - @cargo test -p zkevm-circuits --features=test,warn-unimplemented get_copy_states_stats -- --nocapture --ignored + @cargo run --bin stats --features stats -- copy evm_exec_steps_occupancy: # Print a table for each EVM-CellManager CellType with the top 10 occupancy ExecutionSteps associated - @cargo test -p zkevm-circuits --release get_exec_steps_occupancy --features=test,warn-unimplemented -- --nocapture --ignored + @cargo run --bin stats --features stats -- exec .PHONY: clippy doc fmt test test_benches test-all evm_bench state_bench circuit_benches evm_exec_steps_occupancy stats_state_circuit stats_evm_circuit stats_copy_circuit help diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index 00204bf9b5..3632275fa6 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -8,7 +8,6 @@ license = "MIT OR Apache-2.0" [dependencies] eth-types = { path = "../eth-types" } gadgets = { path = "../gadgets" } -keccak256 = { path = "../keccak256" } mock = { path = "../mock", optional = true } ethers-core = "0.17.0" diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 2f351c84cf..11e676d561 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -366,10 +366,17 @@ pub fn get_create_init_code<'a>( call_ctx: &'a CallContext, step: &GethExecStep, ) -> Result<&'a [u8], Error> { - let offset = step.stack.nth_last(1)?; - let length = step.stack.nth_last(2)?; - Ok(&call_ctx.memory.0 - [offset.low_u64() as usize..(offset.low_u64() + length.low_u64()) as usize]) + let offset = step.stack.nth_last(1)?.low_u64() as usize; + let length = step.stack.nth_last(2)?.as_usize(); + + let mem_len = call_ctx.memory.0.len(); + if offset >= mem_len { + return Ok(&[]); + } + + let offset_end = offset.checked_add(length).unwrap_or(mem_len); + + Ok(&call_ctx.memory.0[offset..offset_end]) } /// Retrieve the memory offset and length of call. @@ -431,7 +438,7 @@ pub fn build_state_code_db( sdb.set_account( &proof.address, state_db::Account { - nonce: proof.nonce, + nonce: proof.nonce.as_u64(), balance: proof.balance, storage, code_hash: proof.code_hash, diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index cf3f62070d..650c7344bc 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -7,7 +7,7 @@ use crate::{ operation::{OperationContainer, RWCounter}, Error, }; -use eth_types::{evm_unimplemented, Address, Hash, Word}; +use eth_types::{evm_unimplemented, Address, Word}; use std::collections::HashMap; /// Context of a [`Block`] which can mutate in a [`Transaction`]. @@ -86,7 +86,6 @@ pub struct Block { pub sha3_inputs: Vec>, /// Exponentiation events in the block. pub exp_events: Vec, - code: HashMap>, /// Circuits Setup Paramteres pub circuits_params: CircuitsParams, /// Original block from geth @@ -139,7 +138,6 @@ impl Block { }, copy_events: Vec::new(), exp_events: Vec::new(), - code: HashMap::new(), sha3_inputs: Vec::new(), circuits_params, eth_block: eth_block.clone(), diff --git a/bus-mapping/src/circuit_input_builder/call.rs b/bus-mapping/src/circuit_input_builder/call.rs index 39a484ea14..564444d536 100644 --- a/bus-mapping/src/circuit_input_builder/call.rs +++ b/bus-mapping/src/circuit_input_builder/call.rs @@ -140,6 +140,13 @@ pub struct CallContext { pub return_data: Vec, } +impl CallContext { + /// Memory size in words, rounded up + pub fn memory_word_size(&self) -> u64 { + u64::try_from(self.memory.len()).expect("failed to convert usize to u64") / 32 + } +} + /// A reversion group is the collection of calls and the operations which are /// [`Operation::reversible`](crate::operation::Operation::reversible) that /// happened in them, that will be reverted at once when the call that initiated diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index e142d257dc..488a6127ff 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -4,10 +4,7 @@ use crate::{ circuit_input_builder::CallContext, error::ExecError, exec_trace::OperationRef, operation::RWCounter, }; -use eth_types::{ - evm_types::{Gas, GasCost, OpcodeId, ProgramCounter}, - GethExecStep, Word, H256, -}; +use eth_types::{evm_types::OpcodeId, GethExecStep, Word, H256}; use gadgets::impl_expr; use halo2_proofs::plonk::Expression; use strum_macros::EnumIter; @@ -18,19 +15,19 @@ pub struct ExecStep { /// Execution state pub exec_state: ExecState, /// Program Counter - pub pc: ProgramCounter, + pub pc: u64, /// Stack size pub stack_size: usize, /// Memory size pub memory_size: usize, /// Gas left - pub gas_left: Gas, + pub gas_left: u64, /// Gas cost of the step. If the error is OutOfGas caused by a "gas uint64 /// overflow", this value will **not** be the actual Gas cost of the /// step. - pub gas_cost: GasCost, + pub gas_cost: u64, /// Accumulated gas refund - pub gas_refund: Gas, + pub gas_refund: u64, /// Call index within the Transaction. pub call_index: usize, /// The global counter when this step was executed. @@ -86,6 +83,40 @@ impl ExecStep { Some(ExecError::OutOfGas(_) | ExecError::StackOverflow | ExecError::StackUnderflow) ) } + + /// Try get opcode, if possible + pub fn opcode(&self) -> Option { + match self.exec_state { + ExecState::Op(op) => Some(op), + _ => None, + } + } + + /// get rw index + pub fn rw_index(&self, index: usize) -> OperationRef { + self.bus_mapping_instance[index] + } + + /// Get the size of read and writes + pub fn rw_indices_len(&self) -> usize { + self.bus_mapping_instance.len() + } + + /// Get stack pointer + pub fn stack_pointer(&self) -> u64 { + 1024 - self.stack_size as u64 + } + + /// The memory size in word **before** this step + pub fn memory_word_size(&self) -> u64 { + let n_bytes_word = 32u64; + let memory_size = self.memory_size as u64; + // EVM always pads the memory size to word size + // https://github.com/ethereum/go-ethereum/blob/a340721aa909ea4b541ffd1ea5e9c7bd441ff769/core/vm/interpreter.go#L201-L205 + // Thus, the memory size must be a multiple of 32 bytes. + assert_eq!(memory_size % n_bytes_word, 0); + memory_size / n_bytes_word + } } /// Execution state 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 21124ad6d7..f930b8d128 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -6,7 +6,7 @@ use super::{ TransactionContext, }; use crate::{ - error::{get_step_reported_error, ExecError}, + error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError}, exec_trace::OperationRef, operation::{ AccountField, AccountOp, CallContextField, CallContextOp, MemoryOp, Op, OpEnum, Operation, @@ -18,7 +18,7 @@ use crate::{ }; use eth_types::{ evm_types::{ - gas_utils::memory_expansion_gas_cost, Gas, GasCost, MemoryAddress, OpcodeId, StackAddress, + gas_utils::memory_expansion_gas_cost, GasCost, MemoryAddress, OpcodeId, StackAddress, }, Address, Bytecode, GethExecStep, ToAddress, ToBigEndian, ToWord, Word, H256, U256, }; @@ -60,7 +60,7 @@ impl<'a> CircuitInputStateRef<'a> { pub fn new_begin_tx_step(&self) -> ExecStep { ExecStep { exec_state: ExecState::BeginTx, - gas_left: Gas(self.tx.gas()), + gas_left: self.tx.gas(), rwc: self.block_ctx.rwc, ..Default::default() } @@ -76,10 +76,25 @@ impl<'a> CircuitInputStateRef<'a> { ExecStep { exec_state: ExecState::EndTx, gas_left: if prev_step.error.is_none() { - Gas(prev_step.gas_left.0 - prev_step.gas_cost.0) + let mut gas_left = prev_step.gas_left - prev_step.gas_cost; + + // for contract creation + let call = self.tx.calls()[0].clone(); + if call.is_create() { + let code_hash = self.sdb.get_account(&call.address).1.code_hash; + let bytecode_len = self.code(code_hash).unwrap().len() as u64; + let deposit_cost = bytecode_len * GasCost::CODE_DEPOSIT_BYTE_COST; + assert!( + gas_left >= deposit_cost, + "gas left {gas_left} is not enough for deposit cost {deposit_cost}" + ); + gas_left -= deposit_cost; + } + + gas_left } else { // consume all remaining gas when non revert err happens - Gas(0) + 0 }, rwc: self.block_ctx.rwc, // For tx without code execution @@ -276,7 +291,7 @@ impl<'a> CircuitInputStateRef<'a> { // accounts, but the corresponding account in the state DB is empty // (which means code_hash=EMPTY_HASH). let account_value_prev = match op.field { - AccountField::Nonce => account.nonce, + AccountField::Nonce => account.nonce.to_word(), AccountField::Balance => account.balance, AccountField::CodeHash => { if account.is_empty() { @@ -318,7 +333,7 @@ impl<'a> CircuitInputStateRef<'a> { // Perform the write to the account in the StateDB if matches!(rw, RW::WRITE) { match op.field { - AccountField::Nonce => account.nonce = op.value, + AccountField::Nonce => account.nonce = op.value.as_u64(), AccountField::Balance => account.balance = op.value, AccountField::CodeHash => account.code_hash = H256::from(op.value.to_be_bytes()), } @@ -461,6 +476,24 @@ impl<'a> CircuitInputStateRef<'a> { Ok(()) } + /// Add address to access list for the current transaction. + pub fn tx_access_list_write( + &mut self, + step: &mut ExecStep, + address: Address, + ) -> Result<(), Error> { + let is_warm = self.sdb.check_account_in_access_list(&address); + self.push_op_reversible( + step, + TxAccessListAccountOp { + tx_id: self.tx_ctx.id(), + address, + is_warm: true, + is_warm_prev: is_warm, + }, + ) + } + /// Push 2 reversible [`AccountOp`] to update `sender` and `receiver`'s /// balance by `value`. If `fee` is existing (not None), also need to push 1 /// non-reversible [`AccountOp`] to update `sender` balance by `fee`. @@ -688,6 +721,32 @@ impl<'a> CircuitInputStateRef<'a> { )) } + /// read reversion info + pub(crate) fn reversion_info_read(&mut self, step: &mut ExecStep, call: &Call) { + for (field, value) in [ + ( + CallContextField::RwCounterEndOfReversion, + call.rw_counter_end_of_reversion.to_word(), + ), + (CallContextField::IsPersistent, call.is_persistent.to_word()), + ] { + self.call_context_read(step, call.call_id, field, value); + } + } + + /// write reversion info + pub(crate) fn reversion_info_write(&mut self, step: &mut ExecStep, call: &Call) { + for (field, value) in [ + ( + CallContextField::RwCounterEndOfReversion, + call.rw_counter_end_of_reversion.to_word(), + ), + (CallContextField::IsPersistent, call.is_persistent.to_word()), + ] { + self.call_context_write(step, call.call_id, field, value); + } + } + /// Check if address is a precompiled or not. pub fn is_precompiled(&self, address: &Address) -> bool { address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19]) @@ -1073,16 +1132,16 @@ impl<'a> CircuitInputStateRef<'a> { let memory_expansion_gas_cost = memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); let code_deposit_cost = if call.is_create() && call.is_success { - GasCost::CODE_DEPOSIT_BYTE_COST.as_u64() * last_callee_return_data_length.as_u64() + GasCost::CODE_DEPOSIT_BYTE_COST * last_callee_return_data_length.as_u64() } else { 0 }; - let gas_refund = geth_step.gas.0 - memory_expansion_gas_cost - code_deposit_cost; + let gas_refund = geth_step.gas - memory_expansion_gas_cost - code_deposit_cost; let caller_gas_left = if is_return_revert || call.is_success { - geth_step_next.gas.0 - gas_refund + geth_step_next.gas - gas_refund } else { - geth_step_next.gas.0 + geth_step_next.gas }; for (field, value) in [ @@ -1092,7 +1151,7 @@ impl<'a> CircuitInputStateRef<'a> { (caller.is_create() as u64).into(), ), (CallContextField::CodeHash, caller.code_hash.to_word()), - (CallContextField::ProgramCounter, geth_step_next.pc.0.into()), + (CallContextField::ProgramCounter, geth_step_next.pc.into()), ( CallContextField::StackPointer, geth_step_next.stack.stack_pointer().0.into(), @@ -1152,8 +1211,8 @@ impl<'a> CircuitInputStateRef<'a> { step: &GethExecStep, next_step: Option<&GethExecStep>, ) -> Result, Error> { - if let Some(error) = &step.error { - return Ok(Some(get_step_reported_error(&step.op, error))); + if let Ok(error) = ExecError::try_from(step) { + return Ok(Some(error)); } if matches!(step.op, OpcodeId::INVALID(_)) { @@ -1241,7 +1300,7 @@ impl<'a> CircuitInputStateRef<'a> { && call_ctx.memory.0.get(offset.low_u64() as usize) == Some(&0xef) { return Ok(Some(ExecError::InvalidCreationCode)); - } else if Word::from(200u64) * length > Word::from(step.gas.0) { + } else if Word::from(200u64) * length > Word::from(step.gas) { return Ok(Some(ExecError::CodeStoreOutOfGas)); } else { return Err(Error::UnexpectedExecStepError( @@ -1277,7 +1336,7 @@ impl<'a> CircuitInputStateRef<'a> { } // The *CALL*/CREATE* code was not executed - let next_pc = next_step.map(|s| s.pc.0).unwrap_or(1); + let next_pc = next_step.map(|s| s.pc).unwrap_or(1); if matches!( step.op, OpcodeId::CALL @@ -1290,7 +1349,14 @@ impl<'a> CircuitInputStateRef<'a> { && next_pc != 0 { if step.depth == 1025 { - return Ok(Some(ExecError::Depth)); + return Ok(Some(ExecError::Depth(match step.op { + OpcodeId::CALL | OpcodeId::CALLCODE => DepthError::Call, + OpcodeId::CREATE => DepthError::Create, + OpcodeId::CREATE2 => DepthError::Create2, + op => { + unreachable!("Depth error unexpected for opcode: {:?}", op) + } + }))); } let sender = self.call()?.address; @@ -1299,7 +1365,26 @@ impl<'a> CircuitInputStateRef<'a> { return Err(Error::AccountNotFound(sender)); } if account.balance < value { - return Ok(Some(ExecError::InsufficientBalance)); + return Ok(Some(ExecError::InsufficientBalance(match step.op { + OpcodeId::CALL | OpcodeId::CALLCODE => InsufficientBalanceError::Call, + OpcodeId::CREATE => InsufficientBalanceError::Create, + OpcodeId::CREATE2 => InsufficientBalanceError::Create2, + op => { + unreachable!("insufficient balance error unexpected for opcode: {:?}", op) + } + }))); + } + + // Nonce Uint overflow + // If user's nonce is equal u64::MAX, nonce will be overflow in this call + // Nonce is u64 so it's impossible to larger than u64::MAX, that's why we're using `==` + // here. + if account.nonce == u64::MAX { + return Ok(Some(ExecError::NonceUintOverflow(match step.op { + OpcodeId::CREATE => NonceUintOverflowError::Create, + OpcodeId::CREATE2 => NonceUintOverflowError::Create2, + op => unreachable!("Nonce Uint overflow error unexpected for opcode: {:?}", op), + }))); } // Address collision diff --git a/bus-mapping/src/circuit_input_builder/tracer_tests.rs b/bus-mapping/src/circuit_input_builder/tracer_tests.rs index 6fe834a612..35366d1033 100644 --- a/bus-mapping/src/circuit_input_builder/tracer_tests.rs +++ b/bus-mapping/src/circuit_input_builder/tracer_tests.rs @@ -1,17 +1,16 @@ use super::*; use crate::{ circuit_input_builder::access::gen_state_access_trace, - error::{ExecError, OogError}, + error::{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, }; use eth_types::{ address, bytecode, - evm_types::{stack::Stack, Gas, OpcodeId}, + evm_types::{stack::Stack, OpcodeId}, geth_types::GethData, word, Bytecode, Hash, ToAddress, ToWord, Word, }; @@ -31,7 +30,6 @@ struct CircuitInputBuilderTx { builder: CircuitInputBuilder, tx: Transaction, pub(crate) tx_ctx: TransactionContext, - step: ExecStep, } impl CircuitInputBuilderTx { @@ -44,7 +42,7 @@ impl CircuitInputBuilderTx { let tx_ctx = TransactionContext::new( &block.eth_block.transactions[0], &GethExecTrace { - gas: Gas(0), + gas: 0, failed: false, return_value: "".to_owned(), struct_logs: vec![geth_step.clone()], @@ -53,19 +51,10 @@ impl CircuitInputBuilderTx { ) .unwrap(); - let prev_log_id = if tx.is_steps_empty() { - 0 - } else { - tx.last_step().log_id - }; - - let call_ctx = tx_ctx.call_ctx().unwrap(); - let exec_step = ExecStep::new(geth_step, call_ctx, RWCounter::new(), 0, prev_log_id); Self { builder, tx, tx_ctx, - step: exec_step, } } @@ -219,7 +208,7 @@ fn tracer_err_depth() { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( builder.state_ref().get_step_err(step, next_step).unwrap(), - Some(ExecError::Depth) + Some(ExecError::Depth(DepthError::Call)) ); } @@ -261,10 +250,7 @@ fn tracer_err_insufficient_balance() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -288,7 +274,9 @@ fn tracer_err_insufficient_balance() { let mut builder = CircuitInputBuilderTx::new(&block, step); assert_eq!( builder.state_ref().get_step_err(step, next_step).unwrap(), - Some(ExecError::InsufficientBalance) + Some(ExecError::InsufficientBalance( + InsufficientBalanceError::Call + )) ); } @@ -417,10 +405,7 @@ fn tracer_err_address_collision() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -540,10 +525,7 @@ fn tracer_create_collision_free() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -584,7 +566,7 @@ fn tracer_create_collision_free() { builder.builder.sdb.set_account( &ADDR_B, Account { - nonce: Word::zero(), + nonce: 0, balance: Word::from(555u64), /* same value as in * `mock::new_tracer_account` */ storage: HashMap::new(), @@ -594,7 +576,7 @@ fn tracer_create_collision_free() { builder.builder.sdb.set_account( &create_address, Account { - nonce: Word::zero(), + nonce: 0, balance: Word::zero(), storage: HashMap::new(), code_hash: Hash::zero(), @@ -611,7 +593,7 @@ fn check_err_code_store_out_of_gas(step: &GethExecStep, next_step: Option<&GethE step.op == OpcodeId::RETURN && step.error.is_none() && result(next_step).is_zero() - && Word::from(200) * length > Word::from(step.gas.0) + && Word::from(200) * length > Word::from(step.gas) } #[test] @@ -673,10 +655,7 @@ fn tracer_err_code_store_out_of_gas() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -726,7 +705,7 @@ fn tracer_err_code_store_out_of_gas_tx_deploy() { txs[0] .from(accs[1].address) .gas(55000u64.into()) - .nonce(Word::zero()) + .nonce(0) .input(code_creator.into()); }, |block, _tx| block.number(0x0264), @@ -825,10 +804,7 @@ fn tracer_err_invalid_code() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -925,10 +901,7 @@ fn tracer_err_max_code_size_exceeded() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -978,7 +951,7 @@ fn tracer_err_max_code_size_exceeded_tx_deploy() { txs[0] .from(accs[1].address) .gas(60000u64.into()) - .nonce(Word::zero()) + .nonce(0) .input(code_creator.into()); }, |block, _tx| block.number(0x0264), @@ -1069,10 +1042,7 @@ fn tracer_create_stop() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1190,10 +1160,7 @@ fn tracer_err_invalid_jump() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1293,10 +1260,7 @@ fn tracer_err_execution_reverted() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1355,10 +1319,7 @@ fn tracer_stop() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1426,10 +1387,7 @@ fn tracer_err_return_data_out_of_bounds() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1582,10 +1540,7 @@ fn tracer_err_write_protection(is_call: bool) { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1787,10 +1742,7 @@ fn create2_address() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1888,10 +1840,7 @@ fn create_address() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -1924,7 +1873,7 @@ fn create_address() { builder.builder.sdb.set_account( &ADDR_B, Account { - nonce: Word::from(1), + nonce: 1, ..Account::zero() }, ); @@ -1975,10 +1924,7 @@ fn test_gen_access_trace() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), @@ -2200,10 +2146,7 @@ fn test_gen_access_trace_create_push_call_stack() { }, |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); - txs[1] - .to(accs[1].address) - .from(accs[2].address) - .nonce(Word::one()); + txs[1].to(accs[1].address).from(accs[2].address).nonce(1); }, |block, _tx| block.number(0xcafeu64), LoggerConfig::enable_memory(), diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index cf3e4d8c46..8eb668b2fb 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -98,6 +98,70 @@ pub enum OogError { SelfDestruct, } +// Given OpCodeId, returns correponding OogError. +impl From<&OpcodeId> for OogError { + fn from(op: &OpcodeId) -> Self { + match op { + OpcodeId::MLOAD | OpcodeId::MSTORE | OpcodeId::MSTORE8 => { + OogError::StaticMemoryExpansion + } + OpcodeId::CREATE | 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::CREATE2 => OogError::Create2, + OpcodeId::SELFDESTRUCT => OogError::SelfDestruct, + _ => OogError::Constant, + } + } +} + +/// Insufficient balance errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum InsufficientBalanceError { + /// Insufficient balance during CALL/CALLCODE opcode. + Call, + /// Insufficient balance during CREATE opcode. + Create, + /// Insufficient balance during CREATE2 opcode. + Create2, +} + +/// Nonce uint overflow errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NonceUintOverflowError { + /// Nonce uint overflow during CREATE opcode. + Create, + /// Nonce uint overflow during CREATE2 opcode. + Create2, +} + +/// Call depth errors by opcode/state. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DepthError { + /// Call depth errors in CALL/CALLCODE opcode. + Call, + /// Call depth errors in CREATE opcode. + Create, + /// Call depth errors in CREATE2 opcode. + Create2, +} + /// EVM Execution Error #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExecError { @@ -112,11 +176,11 @@ pub enum ExecError { /// For SSTORE, LOG0, LOG1, LOG2, LOG3, LOG4, CREATE, CALL, CREATE2, /// SELFDESTRUCT WriteProtection, - /// For CALL, CALLCODE, DELEGATECALL, STATICCALL - Depth, - /// For CALL, CALLCODE - InsufficientBalance, - /// For CREATE, CREATE2 + /// For CALL, CALLCODE, DELEGATECALL, STATICCALL, CREATE, CREATE2 + Depth(DepthError), + /// For CALL, CALLCODE, CREATE, CREATE2 + InsufficientBalance(InsufficientBalanceError), + /// For CREATE2 ContractAddressCollision, /// contract must not begin with 0xef due to EIP #3541 EVM Object Format /// (EOF) @@ -129,45 +193,30 @@ pub enum ExecError { CodeStoreOutOfGas, /// For RETURN in a CREATE, CREATE2 MaxCodeSizeExceeded, + /// For CREATE, CREATE2 + NonceUintOverflow(NonceUintOverflowError), } -// TODO: Move to impl block. -pub(crate) fn get_step_reported_error(op: &OpcodeId, error: &str) -> ExecError { - if error == GETH_ERR_OUT_OF_GAS || error == GETH_ERR_GAS_UINT_OVERFLOW { - // NOTE: We report a GasUintOverflow error as an OutOfGas error - let oog_err = match op { - OpcodeId::MLOAD | OpcodeId::MSTORE | OpcodeId::MSTORE8 => { - OogError::StaticMemoryExpansion - } - OpcodeId::CREATE | OpcodeId::RETURN | OpcodeId::REVERT => { - OogError::DynamicMemoryExpansion - } - OpcodeId::CALLDATACOPY - | OpcodeId::CODECOPY - | OpcodeId::EXTCODECOPY - | OpcodeId::RETURNDATACOPY => OogError::MemoryCopy, - OpcodeId::BALANCE | OpcodeId::EXTCODESIZE | OpcodeId::EXTCODEHASH => { - OogError::AccountAccess +// Returns a GethExecStep's error if present, else return the empty error. +impl TryFrom<&GethExecStep> for ExecError { + type Error = (); + + fn try_from(step: &GethExecStep) -> Result { + Ok(match step.error.as_ref().ok_or(())?.as_str() { + GETH_ERR_OUT_OF_GAS | GETH_ERR_GAS_UINT_OVERFLOW => { + // NOTE: We report a GasUintOverflow error as an OutOfGas error + let oog_err = OogError::from(&step.op); + ExecError::OutOfGas(oog_err) } - OpcodeId::LOG0 | OpcodeId::LOG1 | OpcodeId::LOG2 | OpcodeId::LOG3 | OpcodeId::LOG4 => { - OogError::Log + error => { + if error.starts_with(GETH_ERR_STACK_OVERFLOW) { + ExecError::StackOverflow + } else if error.starts_with(GETH_ERR_STACK_UNDERFLOW) { + ExecError::StackUnderflow + } else { + panic!("Unknown GethExecStep.error: {}", error); + } } - 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::CREATE2 => OogError::Create2, - 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 { - panic!("Unknown GethExecStep.error: {}", error); + }) } } diff --git a/bus-mapping/src/evm.rs b/bus-mapping/src/evm.rs index 319040887a..39cd478c9f 100644 --- a/bus-mapping/src/evm.rs +++ b/bus-mapping/src/evm.rs @@ -5,5 +5,4 @@ pub(crate) mod opcodes; pub use eth_types::evm_types::opcode_ids::OpcodeId; pub use opcodes::Opcode; -#[cfg(any(feature = "test", test))] -pub use opcodes::{gen_sha3_code, MemoryKind}; +pub use opcodes::Sha3CodeGen; diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index db310525cb..60fd50275a 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -1,7 +1,7 @@ //! Definition of each opcode of the EVM. use crate::{ circuit_input_builder::{CircuitInputStateRef, ExecState, ExecStep}, - error::{ExecError, OogError}, + error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError, OogError}, evm::OpcodeId, operation::TxAccessListAccountOp, Error, @@ -9,8 +9,7 @@ use crate::{ use core::fmt::Debug; use eth_types::{evm_unimplemented, GethExecStep, ToAddress}; -#[cfg(any(feature = "test", test))] -pub use self::sha3::sha3_tests::{gen_sha3_code, MemoryKind}; +pub use self::sha3::Sha3CodeGen; mod address; mod balance; @@ -72,7 +71,7 @@ use callop::CallOpcode; use callvalue::Callvalue; use codecopy::Codecopy; use codesize::Codesize; -use create::DummyCreate; +use create::Create; use dup::Dup; use error_invalid_jump::InvalidJump; use error_oog_call::OOGCall; @@ -252,19 +251,13 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps { OpcodeId::LOG4 => Log::gen_associated_ops, OpcodeId::CALL | OpcodeId::CALLCODE => CallOpcode::<7>::gen_associated_ops, OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => CallOpcode::<6>::gen_associated_ops, + OpcodeId::CREATE => Create::::gen_associated_ops, + OpcodeId::CREATE2 => Create::::gen_associated_ops, OpcodeId::RETURN | OpcodeId::REVERT => ReturnRevert::gen_associated_ops, OpcodeId::SELFDESTRUCT => { evm_unimplemented!("Using dummy gen_selfdestruct_ops for opcode SELFDESTRUCT"); DummySelfDestruct::gen_associated_ops } - OpcodeId::CREATE => { - evm_unimplemented!("Using dummy gen_create_ops for opcode {:?}", opcode_id); - DummyCreate::::gen_associated_ops - } - OpcodeId::CREATE2 => { - evm_unimplemented!("Using dummy gen_create_ops for opcode {:?}", opcode_id); - DummyCreate::::gen_associated_ops - } _ => { evm_unimplemented!("Using dummy gen_associated_ops for opcode {:?}", opcode_id); Dummy::gen_associated_ops @@ -272,10 +265,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps { } } -fn fn_gen_error_state_associated_ops( - geth_step: &GethExecStep, - error: &ExecError, -) -> Option { +fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option { match error { ExecError::InvalidJump => Some(InvalidJump::gen_associated_ops), ExecError::InvalidOpcode => Some(ErrorSimple::gen_associated_ops), @@ -288,16 +278,31 @@ fn fn_gen_error_state_associated_ops( ExecError::StackOverflow => Some(ErrorSimple::gen_associated_ops), ExecError::StackUnderflow => Some(ErrorSimple::gen_associated_ops), // call & callcode can encounter InsufficientBalance error, Use pop-7 generic CallOpcode - ExecError::InsufficientBalance => Some(CallOpcode::<7>::gen_associated_ops), + ExecError::InsufficientBalance(InsufficientBalanceError::Call) => { + Some(CallOpcode::<7>::gen_associated_ops) + } + // create & create2 can encounter insufficient balance. + ExecError::InsufficientBalance(InsufficientBalanceError::Create) => { + Some(Create::::gen_associated_ops) + } + ExecError::InsufficientBalance(InsufficientBalanceError::Create2) => { + Some(Create::::gen_associated_ops) + } + // only create2 may cause ContractAddressCollision error, so use Create::. + ExecError::ContractAddressCollision => Some(Create::::gen_associated_ops), + // create & create2 can encounter nonce uint overflow. + ExecError::NonceUintOverflow(NonceUintOverflowError::Create) => { + Some(Create::::gen_associated_ops) + } + ExecError::NonceUintOverflow(NonceUintOverflowError::Create2) => { + Some(Create::::gen_associated_ops) + } ExecError::WriteProtection => Some(ErrorWriteProtection::gen_associated_ops), ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops), - ExecError::Depth => { - let op = geth_step.op; - if !op.is_call() { - evm_unimplemented!("TODO: ErrDepth for CREATE is not implemented yet"); - } - Some(fn_gen_associated_ops(&op)) - } + // call, callcode, create & create2 can encounter DepthError error, + ExecError::Depth(DepthError::Call) => Some(CallOpcode::<7>::gen_associated_ops), + ExecError::Depth(DepthError::Create) => Some(Create::::gen_associated_ops), + ExecError::Depth(DepthError::Create2) => Some(Create::::gen_associated_ops), // more future errors place here _ => { evm_unimplemented!("TODO: error state {:?} not implemented", error); @@ -343,7 +348,7 @@ pub fn gen_associated_ops( // TODO: after more error state handled, refactor all error handling in // fn_gen_error_state_associated_ops method // For exceptions that have been implemented - if let Some(fn_gen_error_ops) = fn_gen_error_state_associated_ops(geth_step, &exec_error) { + if let Some(fn_gen_error_ops) = fn_gen_error_state_associated_ops(&exec_error) { return fn_gen_error_ops(state, geth_steps); } else { // For exceptions that fail to enter next call context, we need diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index 55cc87dff9..e8baf7f88f 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -55,12 +55,12 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result Result Result }, ); - let effective_refund = refund - .min((state.tx.gas() - exec_step.gas_left.0) / MAX_REFUND_QUOTIENT_OF_GAS_USED as u64); + let effective_refund = + refund.min((state.tx.gas() - exec_step.gas_left) / MAX_REFUND_QUOTIENT_OF_GAS_USED as u64); let (found, caller_account) = state.sdb.get_account(&call.caller_address); if !found { return Err(Error::AccountNotFound(call.caller_address)); } let caller_balance_prev = caller_account.balance; let caller_balance = - caller_balance_prev + state.tx.tx.gas_price * (exec_step.gas_left.0 + effective_refund); + caller_balance_prev + state.tx.tx.gas_price * (exec_step.gas_left + effective_refund); state.account_write( &mut exec_step, call.caller_address, @@ -271,7 +279,7 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result } let coinbase_balance_prev = coinbase_account.balance; let coinbase_balance = - coinbase_balance_prev + effective_tip * (state.tx.gas() - exec_step.gas_left.0); + coinbase_balance_prev + effective_tip * (state.tx.gas() - exec_step.gas_left); state.account_write( &mut exec_step, state.block.coinbase, @@ -306,7 +314,7 @@ fn gen_end_tx_steps(state: &mut CircuitInputStateRef) -> Result )?; } - state.block_ctx.cumulative_gas_used += state.tx.gas() - exec_step.gas_left.0; + state.block_ctx.cumulative_gas_used += state.tx.gas() - exec_step.gas_left; state.tx_receipt_write( &mut exec_step, state.tx_ctx.id(), diff --git a/bus-mapping/src/evm/opcodes/calldatacopy.rs b/bus-mapping/src/evm/opcodes/calldatacopy.rs index 238b17c993..eb724c7e5e 100644 --- a/bus-mapping/src/evm/opcodes/calldatacopy.rs +++ b/bus-mapping/src/evm/opcodes/calldatacopy.rs @@ -105,7 +105,9 @@ fn gen_copy_event( let call_data_offset = state.call()?.call_data_offset; let call_data_length = state.call()?.call_data_length; - let dst_addr = memory_offset.as_u64(); + // Get low Uint64 of memory offset to generate copy steps. Since memory + // offset could be Uint64 overflow if memory length is zero. + let dst_addr = memory_offset.low_u64(); let src_addr_end = call_data_offset.checked_add(call_data_length).unwrap(); // Reset start offset to end offset if overflow. diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index c63c70e7dc..25d0221bbc 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -31,9 +31,11 @@ impl Opcode for CallOpcode { let geth_step = &geth_steps[0]; let mut exec_step = state.new_step(geth_step)?; - let args_offset = geth_step.stack.nth_last(N_ARGS - 4)?.as_usize(); + // In offset and length are truncated to Uint64 for call opcodes as: + // + let args_offset = geth_step.stack.nth_last(N_ARGS - 4)?.low_u64() as usize; let args_length = geth_step.stack.nth_last(N_ARGS - 3)?.as_usize(); - let ret_offset = geth_step.stack.nth_last(N_ARGS - 2)?.as_usize(); + let ret_offset = geth_step.stack.nth_last(N_ARGS - 2)?.low_u64() as usize; let ret_length = geth_step.stack.nth_last(N_ARGS - 1)?.as_usize(); // we need to keep the memory until parse_call complete @@ -201,13 +203,13 @@ impl Opcode for CallOpcode { let memory_expansion_gas_cost = memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); let gas_cost = if is_warm { - GasCost::WARM_ACCESS.as_u64() + GasCost::WARM_ACCESS } else { - GasCost::COLD_ACCOUNT_ACCESS.as_u64() + GasCost::COLD_ACCOUNT_ACCESS } + if has_value { - GasCost::CALL_WITH_VALUE.as_u64() + GasCost::CALL_WITH_VALUE + if call.kind == CallKind::Call && !callee_exists { - GasCost::NEW_ACCOUNT.as_u64() + GasCost::NEW_ACCOUNT } else { 0 } @@ -215,7 +217,7 @@ impl Opcode for CallOpcode { 0 } + memory_expansion_gas_cost; let gas_specified = geth_step.stack.last()?; - let callee_gas_left = eip150_gas(geth_step.gas.0 - gas_cost, gas_specified); + let callee_gas_left = eip150_gas(geth_step.gas - gas_cost, gas_specified); // There are 4 branches from here. // add failure case for insufficient balance or error depth in the future. @@ -260,16 +262,16 @@ impl Opcode for CallOpcode { log::warn!("missing circuit part of precompile"); state.handle_return(&mut exec_step, geth_steps, false)?; - let real_cost = geth_steps[0].gas.0 - geth_steps[1].gas.0; - if real_cost != exec_step.gas_cost.0 { + let real_cost = geth_steps[0].gas - geth_steps[1].gas; + if real_cost != exec_step.gas_cost { log::warn!( "precompile gas fixed from {} to {}, step {:?}", - exec_step.gas_cost.0, + exec_step.gas_cost, real_cost, geth_steps[0] ); } - exec_step.gas_cost = GasCost(real_cost); + exec_step.gas_cost = real_cost; Ok(vec![exec_step]) } // 2. Call to account with empty code. @@ -287,17 +289,14 @@ impl Opcode for CallOpcode { // 3. Call to account with non-empty code. (false, _, false) => { for (field, value) in [ - ( - CallContextField::ProgramCounter, - (geth_step.pc.0 + 1).into(), - ), + (CallContextField::ProgramCounter, (geth_step.pc + 1).into()), ( CallContextField::StackPointer, (geth_step.stack.stack_pointer().0 + N_ARGS - 1).into(), ), ( CallContextField::GasLeft, - (geth_step.gas.0 - gas_cost - callee_gas_left).into(), + (geth_step.gas - gas_cost - callee_gas_left).into(), ), (CallContextField::MemorySize, next_memory_word_size.into()), ( @@ -742,7 +741,8 @@ mod tests { assert_eq!( *stack_value, step.stack.nth_last(offset).expect("stack value not found"), - "stack output mismatch" + "stack output mismatch {}", + test_call.name ); } } diff --git a/bus-mapping/src/evm/opcodes/codecopy.rs b/bus-mapping/src/evm/opcodes/codecopy.rs index 578f1c733f..372c8d22af 100644 --- a/bus-mapping/src/evm/opcodes/codecopy.rs +++ b/bus-mapping/src/evm/opcodes/codecopy.rs @@ -78,7 +78,9 @@ fn gen_copy_event( let bytecode: Bytecode = state.code(code_hash)?.into(); let code_size = bytecode.code.len() as u64; - let dst_addr = dst_offset.as_u64(); + // Get low Uint64 of offset to generate copy steps. Since offset could be + // Uint64 overflow if length is zero. + let dst_addr = dst_offset.low_u64(); let src_addr_end = code_size; // Reset start offset to end offset if overflow. diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index d40fa678d3..a9842dce40 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -1,36 +1,61 @@ use crate::{ - circuit_input_builder::{CircuitInputStateRef, ExecStep}, + circuit_input_builder::{ + CircuitInputStateRef, CopyDataType, CopyEvent, ExecStep, NumberOrHash, + }, + error::ExecError, evm::Opcode, - operation::{AccountField, AccountOp, CallContextField, TxAccessListAccountOp}, + operation::{AccountField, AccountOp, CallContextField, MemoryOp, RW}, state_db::CodeDB, Error, }; -use eth_types::{evm_types::gas_utils::memory_expansion_gas_cost, GethExecStep, ToWord, Word}; +use eth_types::{Bytecode, GethExecStep, ToBigEndian, ToWord, Word, H160, H256}; +use ethers_core::utils::{get_create2_address, keccak256, rlp}; #[derive(Debug, Copy, Clone)] -pub struct DummyCreate; +pub struct Create; -impl Opcode for DummyCreate { +impl Opcode for Create { fn gen_associated_ops( state: &mut CircuitInputStateRef, geth_steps: &[GethExecStep], ) -> Result, Error> { - // TODO: replace dummy create here let geth_step = &geth_steps[0]; + let mut exec_step = state.new_step(geth_step)?; + + let tx_id = state.tx_ctx.id(); + let callee = state.parse_call(geth_step)?; + let caller = state.call()?.clone(); + + state.call_context_read( + &mut exec_step, + caller.call_id, + CallContextField::TxId, + tx_id.into(), + ); - let offset = geth_step.stack.nth_last(1)?.as_usize(); + let depth = caller.depth; + state.call_context_read( + &mut exec_step, + caller.call_id, + CallContextField::Depth, + depth.into(), + ); + + state.reversion_info_read(&mut exec_step, &caller); + + // stack operation + // Get low Uint64 of offset to generate copy steps. Since offset could + // be Uint64 overflow if length is zero. + let offset = geth_step.stack.nth_last(1)?.low_u64() as usize; let length = geth_step.stack.nth_last(2)?.as_usize(); - let curr_memory_word_size = (state.call_ctx()?.memory.len() as u64) / 32; if length != 0 { state .call_ctx_mut()? .memory .extend_at_least(offset + length); } - let next_memory_word_size = (state.call_ctx()?.memory.len() as u64) / 32; - - let mut exec_step = state.new_step(geth_step)?; + let next_memory_word_size = state.call_ctx()?.memory_word_size(); let n_pop = if IS_CREATE2 { 4 } else { 3 }; for i in 0..n_pop { @@ -47,98 +72,79 @@ impl Opcode for DummyCreate { state.create_address()? }; - // TODO: rename this to initialization call? - let call = state.parse_call(geth_step)?; + let callee_account = &state.sdb.get_account(&address).1.clone(); + let callee_exists = !callee_account.is_empty(); state.stack_write( &mut exec_step, geth_step.stack.nth_last_filled(n_pop - 1), - if call.is_success { + if callee.is_success { address.to_word() } else { Word::zero() }, )?; + // stack end - let tx_id = state.tx_ctx.id(); - let current_call = state.call()?.clone(); - - // Quote from [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - // > When a CREATE or CREATE2 opcode is called, - // > immediately (i.e. before checks are done to determine - // > whether or not the address is unclaimed) - // > add the address being created to accessed_addresses, - // > but gas costs of CREATE and CREATE2 are unchanged - let is_warm = state.sdb.check_account_in_access_list(&address); - state.push_op_reversible( + // Get caller's balance and nonce + let caller_balance = state.sdb.get_balance(&caller.address); + let caller_nonce = state.sdb.get_nonce(&caller.address); + state.account_read( &mut exec_step, - TxAccessListAccountOp { - tx_id: state.tx_ctx.id(), - address, - is_warm: true, - is_warm_prev: is_warm, - }, - )?; - - // Increase caller's nonce - let nonce_prev = state.sdb.get_nonce(&call.caller_address); - state.push_op_reversible( - &mut exec_step, - AccountOp { - address: call.caller_address, - field: AccountField::Nonce, - value: (nonce_prev + 1).into(), - value_prev: nonce_prev.into(), - }, - )?; - - // Add callee into access list - let is_warm = state.sdb.check_account_in_access_list(&call.address); - state.push_op_reversible( + caller.address, + AccountField::Balance, + caller_balance, + ); + state.account_read( &mut exec_step, - TxAccessListAccountOp { - tx_id, - address: call.address, - is_warm: true, - is_warm_prev: is_warm, - }, - )?; - - state.push_call(call.clone()); + caller.address, + AccountField::Nonce, + caller_nonce.into(), + ); - state.transfer( - &mut exec_step, - call.caller_address, - call.address, - true, - true, - call.value, - )?; + // Check if an error of ErrDepth, ErrInsufficientBalance or + // ErrNonceUintOverflow occurred. + let is_precheck_ok = + depth < 1025 && caller_balance >= callee.value && caller_nonce < u64::MAX; + if is_precheck_ok { + // Increase caller's nonce + state.push_op_reversible( + &mut exec_step, + AccountOp { + address: caller.address, + field: AccountField::Nonce, + value: (caller_nonce + 1).into(), + value_prev: caller_nonce.into(), + }, + )?; - // Increase callee's nonce - let nonce_prev = state.sdb.get_nonce(&call.address); - debug_assert!(nonce_prev == 0); - state.push_op_reversible( - &mut exec_step, - AccountOp { - address: call.address, - field: AccountField::Nonce, - value: 1.into(), - value_prev: 0.into(), - }, - )?; + // add contract address to access list + state.tx_access_list_write(&mut exec_step, address)?; - let memory_expansion_gas_cost = - memory_expansion_gas_cost(curr_memory_word_size, next_memory_word_size); + // this could be good place for checking callee_exists = true, since above + // operation happens in evm create() method before checking + // ErrContractAddressCollision + let code_hash_previous = if callee_exists { + if is_precheck_ok { + exec_step.error = Some(ExecError::ContractAddressCollision); + } + callee_account.code_hash + } else { + H256::zero() + }; - // EIP-150: all but one 64th of the caller's gas is sent to the callee. - let caller_gas_left = - (geth_step.gas.0 - geth_step.gas_cost.0 - memory_expansion_gas_cost) / 64; + state.account_read( + &mut exec_step, + callee.address, + AccountField::CodeHash, + code_hash_previous.to_word(), + ); + } + // Per EIP-150, all but one 64th of the caller's gas is sent to the + // initialization call. + let caller_gas_left = (geth_step.gas - geth_step.gas_cost) / 64; for (field, value) in [ - ( - CallContextField::ProgramCounter, - (geth_step.pc.0 + 1).into(), - ), + (CallContextField::ProgramCounter, (geth_step.pc + 1).into()), ( CallContextField::StackPointer, geth_step.stack.nth_last_filled(n_pop - 1).0.into(), @@ -147,40 +153,251 @@ impl Opcode for DummyCreate { (CallContextField::MemorySize, next_memory_word_size.into()), ( CallContextField::ReversibleWriteCounter, - // +3 is because we do some transfers after pushing the call. can be just push the - // call later? - (exec_step.reversible_write_counter + 3).into(), + (exec_step.reversible_write_counter + 2).into(), ), ] { - state.call_context_write(&mut exec_step, current_call.call_id, field, value); + state.call_context_write(&mut exec_step, caller.call_id, field, value); } - for (field, value) in [ - (CallContextField::CallerId, current_call.call_id.into()), - (CallContextField::IsSuccess, call.is_success.to_word()), - (CallContextField::IsPersistent, call.is_persistent.to_word()), - (CallContextField::TxId, state.tx_ctx.id().into()), - ( - CallContextField::CallerAddress, - current_call.address.to_word(), - ), - (CallContextField::CalleeAddress, call.address.to_word()), - ( - CallContextField::RwCounterEndOfReversion, - call.rw_counter_end_of_reversion.to_word(), - ), - (CallContextField::IsPersistent, call.is_persistent.to_word()), - ] { - state.call_context_write(&mut exec_step, call.call_id, field, value); - } + state.push_call(callee.clone()); + state.reversion_info_write(&mut exec_step, &callee); + + // successful contract creation + if is_precheck_ok && !callee_exists { + let (initialization_code, code_hash) = if length > 0 { + handle_copy( + state, + &mut exec_step, + state.caller()?.call_id, + offset, + length, + )? + } else { + (vec![], CodeDB::empty_code_hash()) + }; - if call.code_hash == CodeDB::empty_code_hash() { - // 1. Create with empty initcode. + // handle keccak_table_lookup + let keccak_input = if IS_CREATE2 { + let salt = geth_step.stack.nth_last(3)?; + assert_eq!( + address, + get_create2_address( + caller.address, + salt.to_be_bytes().to_vec(), + initialization_code.clone(), + ) + ); + std::iter::once(0xffu8) + .chain(caller.address.to_fixed_bytes()) + .chain(salt.to_be_bytes()) + .chain(code_hash.to_fixed_bytes()) + .collect::>() + } else { + let mut stream = rlp::RlpStream::new(); + stream.begin_list(2); + stream.append(&caller.address); + stream.append(&Word::from(caller_nonce)); + stream.out().to_vec() + }; + assert_eq!( + address, + H160(keccak256(&keccak_input)[12..].try_into().unwrap()) + ); + + state.block.sha3_inputs.push(keccak_input); + state.block.sha3_inputs.push(initialization_code); + + // Transfer function will skip transfer if the value is zero + state.transfer( + &mut exec_step, + caller.address, + callee.address, + callee_exists, + !callee_exists, + callee.value, + )?; + + // EIP 161, increase callee's nonce + state.push_op_reversible( + &mut exec_step, + AccountOp { + address: callee.address, + field: AccountField::Nonce, + value: 1.into(), + value_prev: 0.into(), + }, + )?; + + if length > 0 { + for (field, value) in [ + (CallContextField::CallerId, caller.call_id.into()), + (CallContextField::IsSuccess, callee.is_success.to_word()), + ( + CallContextField::IsPersistent, + callee.is_persistent.to_word(), + ), + (CallContextField::TxId, state.tx_ctx.id().into()), + ( + CallContextField::CallerAddress, + callee.caller_address.to_word(), + ), + (CallContextField::CalleeAddress, callee.address.to_word()), + ( + CallContextField::RwCounterEndOfReversion, + callee.rw_counter_end_of_reversion.to_word(), + ), + (CallContextField::Depth, callee.depth.to_word()), + (CallContextField::IsRoot, false.to_word()), + (CallContextField::IsStatic, false.to_word()), + (CallContextField::IsCreate, true.to_word()), + (CallContextField::CodeHash, code_hash.to_word()), + (CallContextField::Value, callee.value), + ] { + state.call_context_write(&mut exec_step, callee.call_id, field, value); + } + } + // if it's empty init code + else { + for (field, value) in [ + (CallContextField::LastCalleeId, 0.into()), + (CallContextField::LastCalleeReturnDataOffset, 0.into()), + (CallContextField::LastCalleeReturnDataLength, 0.into()), + ] { + state.call_context_write(&mut exec_step, caller.call_id, field, value); + } + state.handle_return(&mut exec_step, geth_steps, false)?; + }; + } + // failed case: is_precheck_ok is false or callee_exists is true + else { + for (field, value) in [ + (CallContextField::LastCalleeId, 0.into()), + (CallContextField::LastCalleeReturnDataOffset, 0.into()), + (CallContextField::LastCalleeReturnDataLength, 0.into()), + ] { + state.call_context_write(&mut exec_step, caller.call_id, field, value); + } state.handle_return(&mut exec_step, geth_steps, false)?; - Ok(vec![exec_step]) - } else { - // 2. Create with non-empty initcode. - Ok(vec![exec_step]) } + + Ok(vec![exec_step]) + } +} + +fn handle_copy( + state: &mut CircuitInputStateRef, + step: &mut ExecStep, + call_id: usize, + offset: usize, + length: usize, +) -> Result<(Vec, H256), Error> { + let initialization_bytes = state.caller_ctx()?.memory.0[offset..(offset + length)].to_vec(); + let code_hash = CodeDB::hash(&initialization_bytes); + let bytes: Vec<_> = Bytecode::from(initialization_bytes.clone()) + .code + .iter() + .map(|element| (element.value, element.is_code)) + .collect(); + + let rw_counter_start = state.block_ctx.rwc; + for (i, (byte, _)) in bytes.iter().enumerate() { + state.push_op( + step, + RW::READ, + MemoryOp::new(call_id, (offset + i).into(), *byte), + ); + } + + state.push_copy( + step, + CopyEvent { + rw_counter_start, + src_type: CopyDataType::Memory, + src_id: NumberOrHash::Number(call_id), + src_addr: offset.try_into().unwrap(), + src_addr_end: (offset + length).try_into().unwrap(), + dst_type: CopyDataType::Bytecode, + dst_id: NumberOrHash::Hash(code_hash), + dst_addr: 0, + log_id: None, + bytes, + }, + ); + + Ok((initialization_bytes, code_hash)) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{circuit_input_builder::ExecState, mock::BlockData, operation::RW}; + use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, word}; + use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext}; + + #[test] + fn test_create_address_collision_error() { + let code = bytecode! { + PUSH21(word!("6B6020600060003760206000F3600052600C6014F3")) + PUSH1(0) + MSTORE + + PUSH1 (0xef) // salt + PUSH1 (0x15) // size + PUSH1 (0xB) // offset + PUSH1 (0) // value + CREATE2 + + PUSH1 (0xef) // salt + PUSH1 (0x15) // size + PUSH1 (0xB) // offset + PUSH1 (0) // value + CREATE2 + + PUSH1 (0x20) // retLength + PUSH1 (0x20) // retOffset + PUSH1 (0x20) // argsLength + PUSH1 (0x00) // argsOffset + PUSH1 (0x00) // value + DUP6 // addr from above CREATE2 + PUSH2 (0xFFFF) // gas + CALL + STOP + }; + + // Get the execution steps from the external tracer + let block: GethData = TestContext::<2, 1>::new( + None, + account_0_code_account_1_no_code(code), + |mut txs, accs| { + txs[0].from(accs[1].address).to(accs[0].address); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .into(); + + let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); + builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + + let tx_id = 1; + let transaction = &builder.block.txs()[tx_id - 1]; + let step = transaction + .steps() + .iter() + .filter(|step| step.exec_state == ExecState::Op(OpcodeId::CREATE2)) + .last() + .unwrap(); + + assert_eq!(step.error, Some(ExecError::ContractAddressCollision)); + + let container = builder.block.container.clone(); + println!("{:?}", container.stack); + println!("-----"); + println!("{:?}", step.bus_mapping_instance); + println!("-----"); + let operation = &container.stack[step.bus_mapping_instance[5].as_usize()]; + assert_eq!(operation.rw(), RW::READ); } } diff --git a/bus-mapping/src/evm/opcodes/extcodecopy.rs b/bus-mapping/src/evm/opcodes/extcodecopy.rs index 8160bc9455..108f5f0d08 100644 --- a/bus-mapping/src/evm/opcodes/extcodecopy.rs +++ b/bus-mapping/src/evm/opcodes/extcodecopy.rs @@ -11,9 +11,6 @@ use eth_types::{Bytecode, GethExecStep, ToAddress, ToWord, H256, U256}; #[derive(Clone, Copy, Debug)] pub(crate) struct Extcodecopy; -// TODO: Update to treat code_hash == 0 as account not_exists once the circuit -// is implemented https://github.com/privacy-scaling-explorations/zkevm-circuits/pull/720 - impl Opcode for Extcodecopy { fn gen_associated_ops( state: &mut CircuitInputStateRef, @@ -137,7 +134,9 @@ fn gen_copy_event( }; let code_size = bytecode.code.len() as u64; - let dst_addr = dst_offset.as_u64(); + // Get low Uint64 of offset to generate copy steps. Since offset could be + // Uint64 overflow if length is zero. + let dst_addr = dst_offset.low_u64(); let src_addr_end = code_size; // Reset start offset to end offset if overflow. diff --git a/bus-mapping/src/evm/opcodes/extcodehash.rs b/bus-mapping/src/evm/opcodes/extcodehash.rs index eeaacf2a8b..7b6bc9e24f 100644 --- a/bus-mapping/src/evm/opcodes/extcodehash.rs +++ b/bus-mapping/src/evm/opcodes/extcodehash.rs @@ -130,12 +130,12 @@ mod extcodehash_tests { EXTCODEHASH STOP }); - let mut nonce = Word::from(300u64); + let mut nonce = 300u64; let mut balance = Word::from(800u64); let mut code_ext = Bytes::from([34, 54, 56]); if !exists { - nonce = Word::zero(); + nonce = 0; balance = Word::zero(); code_ext = Bytes::default(); } diff --git a/bus-mapping/src/evm/opcodes/extcodesize.rs b/bus-mapping/src/evm/opcodes/extcodesize.rs index 440e6607e2..e8f526884a 100644 --- a/bus-mapping/src/evm/opcodes/extcodesize.rs +++ b/bus-mapping/src/evm/opcodes/extcodesize.rs @@ -97,7 +97,7 @@ mod extcodesize_tests { geth_types::{Account, GethData}, Bytecode, U256, }; - use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES}; + use mock::{TestContext, MOCK_1_ETH, MOCK_ACCOUNTS, MOCK_CODES, MOCK_COINBASE}; use pretty_assertions::assert_eq; #[test] @@ -151,7 +151,7 @@ mod extcodesize_tests { |mut txs, accs| { txs[0].to(accs[0].address).from(accs[2].address); }, - |block, _tx| block.number(0xcafeu64), + |block, _tx| block.author(*MOCK_COINBASE).number(0xcafeu64), ) .unwrap() .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..48cdcbc2d2 100644 --- a/bus-mapping/src/evm/opcodes/memory_expansion_test.rs +++ b/bus-mapping/src/evm/opcodes/memory_expansion_test.rs @@ -7,14 +7,6 @@ use mock::{ TestContext, }; -fn might_neg_index(index: isize, len: usize) -> usize { - if index < 0 { - (len as isize + index) as usize - } else { - index as usize - } -} - fn assert_expanded(_traces: &[GethExecStep], _before: isize, _after: isize) { // FIXME: memory is removed // let traces_len = traces.len(); diff --git a/bus-mapping/src/evm/opcodes/return_revert.rs b/bus-mapping/src/evm/opcodes/return_revert.rs index d3bd76fd2b..d81d63b021 100644 --- a/bus-mapping/src/evm/opcodes/return_revert.rs +++ b/bus-mapping/src/evm/opcodes/return_revert.rs @@ -40,7 +40,9 @@ impl Opcode for ReturnRevert { call.is_success.to_word(), ); - let offset = offset.as_usize(); + // Get low Uint64 of offset to generate copy steps. Since offset could + // be Uint64 overflow if length is zero. + let offset = offset.low_u64() as usize; let length = length.as_usize(); // Case A in the spec. diff --git a/bus-mapping/src/evm/opcodes/returndatacopy.rs b/bus-mapping/src/evm/opcodes/returndatacopy.rs index f1a14a4b16..a7830523d5 100644 --- a/bus-mapping/src/evm/opcodes/returndatacopy.rs +++ b/bus-mapping/src/evm/opcodes/returndatacopy.rs @@ -123,7 +123,9 @@ fn gen_copy_event( state: &mut CircuitInputStateRef, geth_step: &GethExecStep, ) -> Result { - let dst_addr = geth_step.stack.nth_last(0)?.as_u64(); + // Get low Uint64 of destination offset to generate copy steps. Since it + // could be Uint64 overflow if length is zero. + let dst_addr = geth_step.stack.nth_last(0)?.low_u64(); let data_offset = geth_step.stack.nth_last(1)?.as_u64(); let length = geth_step.stack.nth_last(2)?.as_u64(); diff --git a/bus-mapping/src/evm/opcodes/sha3.rs b/bus-mapping/src/evm/opcodes/sha3.rs index 2179860d18..ca2cc1a430 100644 --- a/bus-mapping/src/evm/opcodes/sha3.rs +++ b/bus-mapping/src/evm/opcodes/sha3.rs @@ -1,13 +1,13 @@ +use super::Opcode; use crate::{ circuit_input_builder::{ CircuitInputStateRef, CopyDataType, CopyEvent, ExecStep, NumberOrHash, }, Error, }; -use eth_types::{GethExecStep, Word, U256}; +use eth_types::{bytecode, Bytecode, GethExecStep, Word, U256}; use ethers_core::utils::keccak256; - -use super::Opcode; +use rand::{rngs::ThreadRng, Rng}; #[derive(Clone, Copy, Debug)] pub(crate) struct Sha3; @@ -40,7 +40,9 @@ impl Opcode for Sha3 { let memory = state .call_ctx()? .memory - .read_chunk(offset.as_usize().into(), size.as_usize().into()); + // Get low Uint64 of offset to generate copy steps. Since offset + // could be Uint64 overflow if length is zero. + .read_chunk(offset.low_u64().into(), size.as_usize().into()); // keccak-256 hash of the given data in memory. let sha3 = keccak256(&memory); @@ -65,8 +67,11 @@ impl Opcode for Sha3 { state.push_copy( &mut exec_step, CopyEvent { - src_addr: offset.as_u64(), - src_addr_end: offset.as_u64() + size.as_u64(), + src_addr: offset.low_u64(), + src_addr_end: offset + .low_u64() + .checked_add(size.as_u64()) + .unwrap_or(u64::MAX), src_type: CopyDataType::Memory, src_id: NumberOrHash::Number(call_id), dst_addr: 0, @@ -82,49 +87,78 @@ impl Opcode for Sha3 { } } -#[cfg(any(feature = "test", test))] -pub mod sha3_tests { - use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, Bytecode, Word}; - use ethers_core::utils::keccak256; - use mock::{ - test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, - }; - use rand::{random, Rng}; - - use crate::{ - circuit_input_builder::{CircuitsParams, ExecState}, - mock::BlockData, - operation::{MemoryOp, StackOp, RW}, - }; - +/// Generate Sha3 opcode +pub struct Sha3CodeGen { + /// The offset + pub offset: usize, + /// The size + pub size: usize, + data_len: usize, + rng: ThreadRng, +} +impl Sha3CodeGen { + /// Construct with memory less than size + pub fn mem_lt_size(offset: usize, size: usize) -> Self { + let mut rng = rand::thread_rng(); + let data_len = offset + + if size.gt(&0) { + rng.gen_range(0..size) + } else { + 0 + }; + Self { + offset, + size, + data_len, + rng, + } + } + /// Construct with memory equal to size + pub fn mem_eq_size(offset: usize, size: usize) -> Self { + let data_len = offset + size; + Self { + offset, + size, + data_len, + rng: rand::thread_rng(), + } + } + /// Construct with memory greater than size + pub fn mem_gt_size(offset: usize, size: usize) -> Self { + let mut rng = rand::thread_rng(); + let data_len = offset + + size + + if size.gt(&0) { + rng.gen_range(0..size) + } else { + 0 + }; + Self { + offset, + size, + data_len, + rng, + } + } + /// Construct with empty memory + pub fn mem_empty(offset: usize, size: usize) -> Self { + Self { + offset, + size, + data_len: 0, + rng: rand::thread_rng(), + } + } + fn rand_bytes(&mut self) -> Vec { + (0..self.data_len) + .map(|_| self.rng.gen()) + .collect::>() + } /// Generate bytecode for SHA3 opcode after having populated sufficient /// memory given the offset and size arguments for SHA3. - pub fn gen_sha3_code(offset: usize, size: usize, mem_kind: MemoryKind) -> (Bytecode, Vec) { - let mut rng = rand::thread_rng(); - let data_len = match mem_kind { - MemoryKind::LessThanSize => { - offset - + if size.gt(&0) { - rng.gen_range(0..size) - } else { - 0 - } - } - MemoryKind::EqualToSize => offset + size, - MemoryKind::MoreThanSize => { - offset - + size - + if size.gt(&0) { - rng.gen_range(0..size) - } else { - 0 - } - } - MemoryKind::Empty => 0, - }; - let data = rand_bytes(data_len); - let mut memory = Vec::with_capacity(data_len); + pub fn gen_sha3_code(&mut self) -> (Bytecode, Vec) { + let data = self.rand_bytes(); + let mut memory = Vec::with_capacity(self.data_len); // add opcodes to populate memory in the current context. let mut code = Bytecode::default(); @@ -142,33 +176,35 @@ pub mod sha3_tests { } // append SHA3 related opcodes at the tail end. let code_tail = bytecode! { - PUSH32(size) - PUSH32(offset) + PUSH32(self.size) + PUSH32(self.offset) SHA3 STOP }; code.append(&code_tail); (code, memory) } +} - /// Memory of a context with respect to the input size to SHA3. - pub enum MemoryKind { - /// Variant defining empty memory. - Empty, - /// Variant defining memory length being less than size. - LessThanSize, - /// Variant defining memory length being equal to size. - EqualToSize, - /// Variant defining memory length being more than size. - MoreThanSize, - } +#[cfg(test)] +pub(crate) mod sha3_tests { + use super::Sha3CodeGen; + use eth_types::{evm_types::OpcodeId, geth_types::GethData, Word}; + use ethers_core::utils::keccak256; + use mock::{ + test_ctx::helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, + TestContext, + }; - fn rand_bytes(size: usize) -> Vec { - (0..size).map(|_| random()).collect::>() - } + use crate::{ + circuit_input_builder::{CircuitsParams, ExecState}, + mock::BlockData, + operation::{MemoryOp, StackOp, RW}, + }; - fn test_ok(offset: usize, size: usize, mem_kind: MemoryKind) { - let (code, memory) = gen_sha3_code(offset, size, mem_kind); + fn test_ok(mut gen: Sha3CodeGen) { + let (code, memory) = gen.gen_sha3_code(); + let (size, offset) = (gen.size, gen.offset); let memory_len = memory.len(); // The memory that is hashed. @@ -265,9 +301,9 @@ pub mod sha3_tests { #[test] fn sha3_opcode_ok() { - test_ok(0x10, 0x32, MemoryKind::Empty); - test_ok(0x34, 0x44, MemoryKind::LessThanSize); - test_ok(0x222, 0x111, MemoryKind::EqualToSize); - test_ok(0x20, 0x30, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x10, 0x32)); + test_ok(Sha3CodeGen::mem_lt_size(0x34, 0x44)); + test_ok(Sha3CodeGen::mem_eq_size(0x222, 0x111)); + test_ok(Sha3CodeGen::mem_gt_size(0x20, 0x30)); } } diff --git a/bus-mapping/src/evm/opcodes/sstore.rs b/bus-mapping/src/evm/opcodes/sstore.rs index 53feaf3549..db2393227c 100644 --- a/bus-mapping/src/evm/opcodes/sstore.rs +++ b/bus-mapping/src/evm/opcodes/sstore.rs @@ -102,7 +102,7 @@ impl Opcode for Sstore { TxRefundOp { tx_id: state.tx_ctx.id(), value_prev: state.sdb.refund(), - value: geth_step.refund.0, + value: geth_step.refund, }, )?; diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index 131cf5ebe8..1cf31e1068 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -54,7 +54,6 @@ //! self, address, Address, Word, Hash, U64, GethExecTrace, GethExecStep, geth_types::GethData, bytecode //! }; //! use mock::test_ctx::{TestContext, helpers::*}; -//! use eth_types::evm_types::Gas; //! use bus_mapping::circuit_input_builder::{Block, CircuitInputBuilder}; //! //! let input_trace = r#" @@ -139,7 +138,7 @@ //! let geth_steps: Vec = serde_json::from_str(input_trace).unwrap(); //! let geth_trace = GethExecTrace { //! return_value: "".to_string(), -//! gas: Gas(block.eth_block.transactions[0].gas.as_u64()), +//! gas: block.eth_block.transactions[0].gas.as_u64(), //! failed: false, //! struct_logs: geth_steps, //! }; @@ -213,8 +212,6 @@ //! See: #![cfg_attr(docsrs, feature(doc_cfg))] -// Temporary until we have more of the crate implemented. -#![allow(dead_code)] // We want to have UPPERCASE idents sometimes. #![allow(non_snake_case)] // Catch documentation errors caused by code changes. diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 4428994f7e..57ecae2475 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -60,16 +60,8 @@ impl BlockData { } for account in geth_data.accounts { - let code_hash = code_db.insert(account.code.to_vec()); - sdb.set_account( - &account.address, - state_db::Account { - nonce: account.nonce, - balance: account.balance, - storage: account.storage, - code_hash, - }, - ); + code_db.insert(account.code.to_vec()); + sdb.set_account(&account.address, state_db::Account::from(account.clone())); } Self { diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index 8b39ce7219..0de8e6cff3 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -6,6 +6,9 @@ pub(crate) mod container; pub use container::OperationContainer; pub use eth_types::evm_types::{MemoryAddress, StackAddress}; +use gadgets::impl_expr; +use halo2_proofs::plonk::Expression; +use strum_macros::EnumIter; use core::{cmp::Ordering, fmt, fmt::Debug}; use eth_types::{Address, Word}; @@ -50,6 +53,12 @@ impl From for usize { } } +impl From for u64 { + fn from(addr: RWCounter) -> u64 { + addr.0.try_into().expect("rwc should not overflow") + } +} + impl From for RWCounter { fn from(rwc: usize) -> Self { RWCounter(rwc) @@ -82,10 +91,10 @@ impl RWCounter { } /// Enum used to differenciate between EVM Stack, Memory and Storage operations. -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy, EnumIter, Hash)] pub enum Target { /// Start is a padding operation. - Start, + Start = 1, /// Means the target of the operation is the Memory. Memory, /// Means the target of the operation is the Stack. @@ -108,6 +117,28 @@ pub enum Target { TxLog, } +impl_expr!(Target); + +impl From for usize { + fn from(value: Target) -> usize { + value as usize + } +} + +impl Target { + /// Returns true if the RwTable operation is reversible + pub fn is_reversible(self) -> bool { + matches!( + self, + Target::TxAccessListAccount + | Target::TxAccessListAccountStorage + | Target::TxRefund + | Target::Account + | Target::Storage + ) + } +} + /// Trait used for Operation Kinds. pub trait Op: Clone + Eq + Ord { diff --git a/bus-mapping/src/state_db.rs b/bus-mapping/src/state_db.rs index 227cb3bf9d..84bf451109 100644 --- a/bus-mapping/src/state_db.rs +++ b/bus-mapping/src/state_db.rs @@ -1,13 +1,14 @@ //! Implementation of an in-memory key-value database to represent the //! Ethereum State Trie. -use eth_types::{Address, Hash, Word, H256, U256}; +use eth_types::{geth_types, Address, Hash, Word, H256, U256}; use ethers_core::utils::keccak256; use lazy_static::lazy_static; use std::collections::{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] = { @@ -57,7 +58,7 @@ impl CodeDB { #[derive(Debug, PartialEq, Eq, Clone)] pub struct Account { /// Nonce - pub nonce: Word, + pub nonce: u64, /// Balance pub balance: Word, /// Storage key-value map @@ -66,11 +67,22 @@ pub struct Account { pub code_hash: Hash, } +impl From for Account { + fn from(account: geth_types::Account) -> Self { + Self { + nonce: account.nonce.as_u64(), + balance: account.balance, + storage: account.storage.clone(), + code_hash: CodeDB::hash(&account.code.to_vec()), + } + } +} + impl Account { /// Return an empty account, with all values set at zero. pub fn zero() -> Self { Self { - nonce: Word::zero(), + nonce: 0, balance: Word::zero(), storage: HashMap::new(), code_hash: *EMPTY_CODE_HASH, @@ -79,7 +91,7 @@ impl Account { /// Return if account is empty or not. pub fn is_empty(&self) -> bool { - self.nonce.is_zero() && self.balance.is_zero() && self.code_hash.eq(&EMPTY_CODE_HASH) + self.nonce == 0 && self.balance.is_zero() && self.code_hash.eq(&EMPTY_CODE_HASH) } } @@ -187,14 +199,20 @@ impl StateDB { /// Get nonce of account with `addr`. pub fn get_nonce(&self, addr: &Address) -> u64 { let (_, account) = self.get_account(addr); - account.nonce.as_u64() + account.nonce + } + + /// Get balance of account with the given address. + pub fn get_balance(&self, addr: &Address) -> Word { + let (_, account) = self.get_account(addr); + account.balance } /// Increase nonce of account with `addr` and return the previous value. pub fn increase_nonce(&mut self, addr: &Address) -> u64 { let (_, account) = self.get_account_mut(addr); - let nonce = account.nonce.as_u64(); - account.nonce = account.nonce + 1; + let nonce = account.nonce; + account.nonce += 1; nonce } @@ -291,12 +309,12 @@ mod statedb_tests { let (found, acc) = statedb.get_account_mut(&addr_a); assert!(!found); assert_eq!(acc, &Account::zero()); - acc.nonce = Word::from(100); + acc.nonce = 100; // Get existing account and check nonce let (found, acc) = statedb.get_account(&addr_a); assert!(found); - assert_eq!(acc.nonce, Word::from(100)); + assert_eq!(acc.nonce, 100); // Get non-existing storage key for existing account and set value let (found, value) = statedb.get_storage_mut(&addr_a, &Word::from(2)); @@ -318,7 +336,7 @@ mod statedb_tests { // Get existing account and check nonce let (found, acc) = statedb.get_account(&addr_b); assert!(found); - assert_eq!(acc.nonce, Word::zero()); + assert_eq!(acc.nonce, 0); // Get existing storage key and check value let (found, value) = statedb.get_storage(&addr_b, &Word::from(3)); diff --git a/circuit-benchmarks/Cargo.toml b/circuit-benchmarks/Cargo.toml index 58ee36a853..8f813e749b 100644 --- a/circuit-benchmarks/Cargo.toml +++ b/circuit-benchmarks/Cargo.toml @@ -10,7 +10,6 @@ license = "MIT OR Apache-2.0" halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } ark-std = { version = "0.3", features = ["print-trace"] } zkevm-circuits = { path = "../zkevm-circuits", features = ["test"]} -keccak256 = { path = "../keccak256" } bus-mapping = { path = "../bus-mapping", features = ["test"] } rand_xorshift = "0.3" rand = "0.8" diff --git a/circuit-benchmarks/src/pi_circuit.rs b/circuit-benchmarks/src/pi_circuit.rs index 3f0b150882..eb1295e129 100644 --- a/circuit-benchmarks/src/pi_circuit.rs +++ b/circuit-benchmarks/src/pi_circuit.rs @@ -24,7 +24,7 @@ mod tests { use rand_xorshift::XorShiftRng; use std::env::var; use zkevm_circuits::{ - pi_circuit::{PiCircuit, PiTestCircuit, PublicData}, + pi_circuit::{PiCircuit, PublicData}, util::SubCircuit, }; @@ -48,15 +48,10 @@ mod tests { let mut rng = ChaCha20Rng::seed_from_u64(2); let randomness = Fr::random(&mut rng); let rand_rpi = Fr::random(&mut rng); - let public_data = generate_publicdata::(); - let circuit = PiTestCircuit::(PiCircuit::::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data, - )); - let public_inputs = circuit.0.instance(); + let public_data = generate_publicdata(MAX_TXS); + let circuit = + PiCircuit::::new(MAX_TXS, MAX_CALLDATA, randomness, rand_rpi, public_data); + let public_inputs = circuit.instance(); let instance: Vec<&[Fr]> = public_inputs.iter().map(|input| &input[..]).collect(); let instances = &[&instance[..]]; @@ -90,7 +85,7 @@ mod tests { Challenge255, XorShiftRng, Blake2bWrite, G1Affine, Challenge255>, - PiTestCircuit, + PiCircuit, >( &general_params, &pk, @@ -125,12 +120,12 @@ mod tests { end_timer!(start3); } - fn generate_publicdata() -> PublicData { + fn generate_publicdata(max_txs: usize) -> PublicData { let mut public_data = PublicData::default(); let chain_id = 1337u64; public_data.chain_id = Word::from(chain_id); - let n_tx = MAX_TXS; + let n_tx = max_txs; for _ in 0..n_tx { let eth_tx = eth_types::Transaction::from(mock::CORRECT_MOCK_TXS[0].clone()); public_data.transactions.push(eth_tx); diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index bac05c75e1..a37ffa82d8 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -79,11 +79,9 @@ mod tests { block.sign(&wallets); - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -92,7 +90,7 @@ mod tests { max_keccak_rows: 0, }; let (_, circuit, instance, _) = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, 0x100>::build(block, circuits_params).unwrap(); + SuperCircuit::build(block, circuits_params, Fr::from(0x100)).unwrap(); let instance_refs: Vec<&[Fr]> = instance.iter().map(|v| &v[..]).collect(); // Bench setup generation @@ -120,7 +118,7 @@ mod tests { Challenge255, ChaChaRng, Blake2bWrite, G1Affine, Challenge255>, - SuperCircuit, + SuperCircuit, >( &general_params, &pk, diff --git a/eth-types/Cargo.toml b/eth-types/Cargo.toml index d0d43f8cfc..1050f3efe8 100644 --- a/eth-types/Cargo.toml +++ b/eth-types/Cargo.toml @@ -19,7 +19,6 @@ uint = "0.9.1" itertools = "0.10" libsecp256k1 = "0.7" subtle = "2.4" -sha3 = "0.10" num = "0.4" num-bigint = { version = "0.4" } strum_macros = "0.24" diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index 9837d79251..ba1c694967 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 serde::{Deserialize, Serialize}; +// use std::fmt; pub mod gas_utils; pub mod memory; @@ -14,153 +14,76 @@ pub use opcode_ids::OpcodeId; pub use stack::{Stack, StackAddress}; pub use storage::Storage; -/// Wrapper type over `usize` which represents the program counter of the Evm. -#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Default)] -pub struct ProgramCounter(pub usize); - -impl fmt::Debug for ProgramCounter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("0x{:06x}", self.0)) - } -} - -impl From for usize { - fn from(addr: ProgramCounter) -> usize { - addr.0 - } -} - -impl From for ProgramCounter { - fn from(pc: usize) -> Self { - ProgramCounter(pc) - } -} - -impl ProgramCounter { - /// Increase Self by one - pub fn inc(&mut self) { - self.0 += 1; - } - - /// Increase Self by one and return the value before the increase. - pub fn inc_pre(&mut self) -> Self { - let pre = *self; - self.inc(); - pre - } -} - -/// Defines the gas left to perate. -#[derive(Default, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Gas(pub u64); - -impl fmt::Debug for Gas { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} - +/// Once per word of the init code when creating a contract. +pub const INIT_CODE_WORD_GAS: u64 = 2; /// Quotient for max refund of gas used pub const MAX_REFUND_QUOTIENT_OF_GAS_USED: usize = 5; /// Gas stipend when CALL or CALLCODE is attached with value. pub const GAS_STIPEND_CALL_WITH_VALUE: u64 = 2300; /// Defines the gas consumption. -#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize, Default)] -pub struct GasCost(pub u64); - -impl fmt::Debug for GasCost { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_fmt(format_args!("{}", self.0)) - } -} +pub struct GasCost; impl GasCost { /// Constant cost for free step - pub const ZERO: Self = Self(0); + pub const ZERO: u64 = 0; /// Constant cost for jumpdest step, only takes one gas - pub const ONE: Self = Self(1); + pub const ONE: u64 = 1; /// Constant cost for quick step - pub const QUICK: Self = Self(2); + pub const QUICK: u64 = 2; /// Constant cost for fastest step - pub const FASTEST: Self = Self(3); + pub const FASTEST: u64 = 3; /// Constant cost for fast step - pub const FAST: Self = Self(5); + pub const FAST: u64 = 5; /// Constant cost for mid step - pub const MID: Self = Self(8); + pub const MID: u64 = 8; /// Constant cost for slow step - pub const SLOW: Self = Self(10); + pub const SLOW: u64 = 10; /// Constant cost for ext step - pub const EXT: Self = Self(20); + pub const EXT: u64 = 20; /// Constant cost for SHA3 - pub const SHA3: Self = Self(30); + pub const SHA3: u64 = 30; /// Constant cost for SELFDESTRUCT - pub const SELFDESTRUCT: Self = Self(5000); + pub const SELFDESTRUCT: u64 = 5000; /// Constant cost for CREATE and CREATE2 - pub const CREATE: Self = Self(32000); + pub const CREATE: u64 = 32000; /// Constant cost for copying every word - pub const COPY: Self = Self(3); + pub const COPY: u64 = 3; /// Constant cost for copying every word, specifically in the case of SHA3 /// opcode. - pub const COPY_SHA3: Self = Self(6); + pub const COPY_SHA3: u64 = 6; /// Constant cost for accessing account or storage key - pub const WARM_ACCESS: Self = Self(100); + pub const WARM_ACCESS: u64 = 100; /// Constant cost for a cold SLOAD - pub const COLD_SLOAD: Self = Self(2100); + pub const COLD_SLOAD: u64 = 2100; /// Constant cost for a cold account access - pub const COLD_ACCOUNT_ACCESS: Self = Self(2600); + pub const COLD_ACCOUNT_ACCESS: u64 = 2600; /// SSTORE reentrancy sentry - pub const SSTORE_SENTRY: Self = Self(2300); + pub const SSTORE_SENTRY: u64 = 2300; /// Constant cost for a storage set - pub const SSTORE_SET: Self = Self(20000); + pub const SSTORE_SET: u64 = 20000; /// Constant cost for a storage reset - pub const SSTORE_RESET: Self = Self(2900); + pub const SSTORE_RESET: u64 = 2900; /// Constant cost for a storage clear. EIP-3529 changed it to 4800 from /// 15000. - pub const SSTORE_CLEARS_SCHEDULE: Self = Self(4800); + pub const SSTORE_CLEARS_SCHEDULE: u64 = 4800; /// Constant cost for a non-creation transaction - pub const TX: Self = Self(21000); + pub const TX: u64 = 21000; /// Constant cost for a creation transaction - pub const CREATION_TX: Self = Self(53000); + pub const CREATION_TX: u64 = 53000; /// Constant cost for calling with non-zero value - pub const CALL_WITH_VALUE: Self = Self(9000); + pub const CALL_WITH_VALUE: u64 = 9000; /// Constant cost for turning empty account into non-empty account - pub const NEW_ACCOUNT: Self = Self(25000); + pub const NEW_ACCOUNT: u64 = 25000; /// Cost per byte of deploying a new contract - pub const CODE_DEPOSIT_BYTE_COST: Self = Self(200); + pub const CODE_DEPOSIT_BYTE_COST: u64 = 200; /// Denominator of quadratic part of memory expansion gas cost - pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: Self = Self(512); + pub const MEMORY_EXPANSION_QUAD_DENOMINATOR: u64 = 512; /// Coefficient of linear part of memory expansion gas cost - pub const MEMORY_EXPANSION_LINEAR_COEFF: Self = Self(3); + pub const MEMORY_EXPANSION_LINEAR_COEFF: u64 = 3; /// Constant gas for LOG[0-4] op codes - pub const LOG: Self = Self(375); + pub const LOG: u64 = 375; /// Times ceil exponent byte size for the EXP instruction, EIP-158 changed /// it from 10 to 50. - pub const EXP_BYTE_TIMES: Self = Self(50); -} - -impl GasCost { - /// Returns the `GasCost` as a `u64`. - #[inline] - pub const fn as_u64(&self) -> u64 { - self.0 - } - - /// Returns the `GasCost` as a `usize`. - #[inline] - pub const fn as_usize(&self) -> usize { - self.0 as usize - } -} - -impl From for GasCost { - fn from(cost: u8) -> Self { - GasCost(cost as u64) - } -} - -impl From for GasCost { - fn from(cost: u64) -> Self { - GasCost(cost) - } + pub const EXP_BYTE_TIMES: u64 = 50; } diff --git a/eth-types/src/evm_types/gas_utils.rs b/eth-types/src/evm_types/gas_utils.rs index 8d34a37e20..18240f9bf4 100644 --- a/eth-types/src/evm_types/gas_utils.rs +++ b/eth-types/src/evm_types/gas_utils.rs @@ -8,11 +8,11 @@ pub fn memory_expansion_gas_cost(curr_memory_word_size: u64, next_memory_word_si if next_memory_word_size == curr_memory_word_size { 0 } else { - GasCost::MEMORY_EXPANSION_LINEAR_COEFF.0 * (next_memory_word_size - curr_memory_word_size) + GasCost::MEMORY_EXPANSION_LINEAR_COEFF * (next_memory_word_size - curr_memory_word_size) + next_memory_word_size * next_memory_word_size - / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.0 + / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR - curr_memory_word_size * curr_memory_word_size - / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.0 + / GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR } } @@ -24,7 +24,7 @@ pub fn memory_copier_gas_cost( num_copy_bytes: u64, ) -> u64 { let num_words = (num_copy_bytes + 31) / 32; - num_words * GasCost::COPY.as_u64() + + num_words * GasCost::COPY + // Note that opcodes with a byte size parameter of 0 will not trigger // memory expansion, regardless of their offset parameters. if num_words > 0 { diff --git a/eth-types/src/evm_types/opcode_ids.rs b/eth-types/src/evm_types/opcode_ids.rs index d9192fcae4..dfd60db4ca 100644 --- a/eth-types/src/evm_types/opcode_ids.rs +++ b/eth-types/src/evm_types/opcode_ids.rs @@ -514,7 +514,7 @@ impl OpcodeId { } /// Returns the constant gas cost of `OpcodeId` - pub const fn constant_gas_cost(&self) -> GasCost { + pub const fn constant_gas_cost(&self) -> u64 { match self { OpcodeId::STOP => GasCost::ZERO, OpcodeId::ADD => GasCost::FASTEST, diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index ecd265e469..0b16ab307d 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -1,9 +1,10 @@ //! Types needed for generating Ethereum traces use crate::{ + keccak256, sign_types::{biguint_to_32bytes_le, ct_option_ok_or, recover_pk, SignData, SECP256K1_Q}, AccessList, Address, Block, Bytes, Error, GethExecTrace, Hash, ToBigEndian, ToLittleEndian, - Word, U64, + ToWord, Word, U64, }; use ethers_core::{ types::{transaction::response, NameOrAddress, TransactionRequest}, @@ -15,7 +16,6 @@ use num::Integer; use num_bigint::BigUint; use serde::{Serialize, Serializer}; use serde_with::serde_as; -use sha3::{Digest, Keccak256}; use std::collections::HashMap; /// Definition of all of the data related to an account. @@ -24,8 +24,9 @@ use std::collections::HashMap; pub struct Account { /// Address pub address: Address, - /// nonce - pub nonce: Word, + /// Nonce. + /// U64 type is required to serialize into proper hex with 0x prefix + pub nonce: U64, /// Balance pub balance: Word, /// EVM Code @@ -64,7 +65,8 @@ pub struct BlockConstants { pub coinbase: Address, /// time pub timestamp: Word, - /// number + /// Block number + /// U64 type is required to serialize into proper hex with 0x prefix pub number: U64, /// difficulty pub difficulty: Word, @@ -119,9 +121,11 @@ pub struct Transaction { /// Avoid direct read from this field. We set this field public to construct the struct pub to: Option
, /// Transaction nonce - pub nonce: Word, + /// U64 type is required to serialize into proper hex with 0x prefix + pub nonce: U64, /// Gas Limit / Supplied gas - pub gas_limit: Word, + /// U64 type is required to serialize into proper hex with 0x prefix + pub gas_limit: U64, /// Transfered value pub value: Word, /// Gas Price @@ -150,8 +154,8 @@ impl From<&Transaction> for crate::Transaction { crate::Transaction { from: tx.from, to: tx.to, - nonce: tx.nonce, - gas: tx.gas_limit, + nonce: tx.nonce.to_word(), + gas: tx.gas_limit.to_word(), value: tx.value, gas_price: Some(tx.gas_price), max_priority_fee_per_gas: Some(tx.gas_fee_cap), @@ -171,8 +175,8 @@ impl From<&crate::Transaction> for Transaction { Transaction { from: tx.from, to: tx.to, - nonce: tx.nonce, - gas_limit: tx.gas, + nonce: tx.nonce.as_u64().into(), + gas_limit: tx.gas.as_u64().into(), value: tx.value, gas_price: tx.gas_price.unwrap_or_default(), gas_fee_cap: tx.max_priority_fee_per_gas.unwrap_or_default(), @@ -191,11 +195,11 @@ impl From<&Transaction> for TransactionRequest { TransactionRequest { from: Some(tx.from), to: tx.to.map(NameOrAddress::Address), - gas: Some(tx.gas_limit), + gas: Some(tx.gas_limit.to_word()), gas_price: Some(tx.gas_price), value: Some(tx.value), data: Some(tx.call_data.clone()), - nonce: Some(tx.nonce), + nonce: Some(tx.nonce.to_word()), ..Default::default() } } @@ -217,11 +221,7 @@ impl Transaction { // msg = rlp([nonce, gasPrice, gas, to, value, data, sig_v, r, s]) let req: TransactionRequest = self.into(); let msg = req.chain_id(chain_id).rlp(); - let msg_hash: [u8; 32] = Keccak256::digest(&msg) - .as_slice() - .to_vec() - .try_into() - .expect("hash length isn't 32 bytes"); + let msg_hash: [u8; 32] = keccak256(&msg); let v = self .v .checked_sub(35 + chain_id * 2) @@ -256,7 +256,7 @@ impl Transaction { /// Get the "to" address. If `to` is None then compute contract adddress pub fn to_or_contract_addr(&self) -> Address { self.to - .unwrap_or_else(|| get_contract_address(self.from, self.nonce)) + .unwrap_or_else(|| get_contract_address(self.from, self.nonce.to_word())) } /// Determine if this transaction is a contract create transaction pub fn is_create(&self) -> bool { @@ -277,8 +277,8 @@ impl Transaction { input: self.call_data.clone(), gas_price: Some(self.gas_price), access_list: self.access_list.clone(), - nonce: self.nonce, - gas: self.gas_limit, + nonce: self.nonce.to_word(), + gas: self.gas_limit.to_word(), transaction_index: Some(transaction_index), r: self.r, s: self.s, diff --git a/keccak256/src/plain.rs b/eth-types/src/keccak.rs similarity index 59% rename from keccak256/src/plain.rs rename to eth-types/src/keccak.rs index 8540479505..3bef09e94a 100644 --- a/keccak256/src/plain.rs +++ b/eth-types/src/keccak.rs @@ -1,6 +1,51 @@ -use crate::common::*; +//! Plain keccak256 implementation + use itertools::Itertools; +/// The State is a 5x5 matrix of 64 bit lanes. +type State = [[u64; 5]; 5]; + +/// The number of rounds for the 1600 bits permutation used in Keccak-256. See [here](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L230) +const PERMUTATION: usize = 24; + +/// The Keccak [round constants](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L257-L282) +static ROUND_CONSTANTS: [u64; PERMUTATION] = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, +]; + +/// The Keccak [rotation offsets](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L232-L255) +static ROTATION_CONSTANTS: [[u32; 5]; 5] = [ + [0, 36, 3, 41, 18], + [1, 44, 10, 45, 2], + [62, 6, 43, 15, 61], + [28, 55, 25, 21, 56], + [27, 20, 39, 8, 14], +]; + +/// The main keccak object pub struct Keccak { state: State, sponge: Sponge, @@ -21,6 +66,7 @@ impl Default for Keccak { } impl Keccak { + /// Take more input bytes to the state pub fn update(&mut self, input: &[u8]) { let rate = self.sponge.rate; // offset for `input` @@ -67,10 +113,10 @@ impl Keccak { } #[derive(Default)] -pub struct KeccakF {} +struct KeccakF {} impl KeccakF { - pub fn permutations(&self, a: &mut State) { + fn permutations(&self, a: &mut State) { for rc in ROUND_CONSTANTS.iter().take(PERMUTATION) { *a = KeccakF::round_b(*a, *rc); } @@ -84,7 +130,7 @@ impl KeccakF { KeccakF::iota(s4, rc) } - pub fn theta(a: State) -> State { + fn theta(a: State) -> State { let mut c: [u64; 5] = [0; 5]; let mut out: State = [[0; 5]; 5]; @@ -98,7 +144,7 @@ impl KeccakF { out } - pub fn rho(a: State) -> State { + fn rho(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { out[x][y] = a[x][y].rotate_left(ROTATION_CONSTANTS[x][y]); @@ -106,7 +152,7 @@ impl KeccakF { out } - pub fn pi(a: State) -> State { + fn pi(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { @@ -115,7 +161,7 @@ impl KeccakF { out } - pub fn xi(a: State) -> State { + fn xi(a: State) -> State { let mut out: State = [[0; 5]; 5]; for (x, y) in (0..5).cartesian_product(0..5) { out[x][y] = a[x][y] ^ (!a[(x + 1) % 5][y] & a[(x + 2) % 5][y]); @@ -123,21 +169,21 @@ impl KeccakF { out } - pub fn iota(a: State, rc: u64) -> State { + fn iota(a: State, rc: u64) -> State { let mut out = a; out[0][0] ^= rc; out } } -pub struct Sponge { +struct Sponge { rate: usize, capacity: usize, keccak_f: KeccakF, } impl Sponge { - pub fn new(rate: usize, capacity: usize) -> Sponge { + fn new(rate: usize, capacity: usize) -> Sponge { Sponge { rate, capacity, @@ -145,7 +191,7 @@ impl Sponge { } } - pub fn absorb(&self, state: &mut State, message: &[u8]) { + fn absorb(&self, state: &mut State, message: &[u8]) { debug_assert!( message.len() % self.rate == 0, "Message is not divisible entirely by bytes rate" @@ -173,7 +219,7 @@ impl Sponge { } } - pub fn squeeze(&self, state: &mut State) -> Vec { + fn squeeze(&self, state: &mut State) -> Vec { let mut output: Vec = vec![]; let output_len: usize = self.capacity / 2; @@ -206,64 +252,57 @@ impl Sponge { words } } -#[cfg(test)] -fn keccak256(msg: &[u8]) -> Vec { - let mut keccak = Keccak::default(); - keccak.update(msg); - let a = keccak.digest(); +/// Convinient method to get 32 bytes digest +pub fn keccak256(msg: &[u8]) -> [u8; 32] { let mut keccak = Keccak::default(); - for byte in msg { - keccak.update(&[*byte]); - } - let b = keccak.digest(); - - assert_eq!(a, b); - - a + keccak.update(msg); + keccak.digest().try_into().expect("keccak outputs 32 bytes") } #[test] -fn test_empty_input() { - let output = [ - 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, - 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, - ]; - assert_eq!(keccak256(&[]), output); -} +fn test_keccak256() { + fn keccak256(msg: &[u8]) -> Vec { + let mut keccak = Keccak::default(); + keccak.update(msg); + let a = keccak.digest(); + + let mut keccak = Keccak::default(); + for byte in msg { + keccak.update(&[*byte]); + } + let b = keccak.digest(); -#[test] -fn test_short_input() { - let output = [ - 56, 209, 138, 203, 103, 210, 92, 139, 185, 148, 39, 100, 182, 47, 24, 225, 112, 84, 246, - 106, 129, 123, 212, 41, 84, 35, 173, 249, 237, 152, 135, 62, - ]; - assert_eq!(keccak256(&[102, 111, 111, 98, 97, 114]), output); -} + assert_eq!(a, b); -#[test] -fn test_long_input() { - let input = [ - 65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103, - 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32, - 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97, - 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110, - 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119, - 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32, - 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115, - 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32, - 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117, - 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105, 111, 110, - 115, 32, 105, 110, 32, 105, 116, 44, 32, 97, 110, 100, 32, 119, 104, 97, 116, 32, 105, 115, - 32, 116, 104, 101, 32, 117, 115, 101, 32, 111, 102, 32, 97, 32, 98, 111, 111, 107, 44, 32, - 116, 104, 111, 117, 103, 104, 116, 32, 65, 108, 105, 99, 101, 32, 119, 105, 116, 104, 111, - 117, 116, 32, 112, 105, 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, - 101, 114, 115, 97, 116, 105, 111, 110, 115, 63, - ]; - let output = [ - 60, 227, 142, 8, 143, 135, 108, 85, 13, 254, 190, 58, 30, 106, 153, 194, 188, 6, 208, 49, - 16, 102, 150, 120, 100, 130, 224, 177, 64, 98, 53, 252, + a + } + let pairs = [ + ( + "", + "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + ), + ( + "666f6f626172", + "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e", + ), + ( + "416c6963652077617320626567696e6e696e6720746f20676574207665727920\ + 7469726564206f662073697474696e672062792068657220736973746572206f\ + 6e207468652062616e6b2c20616e64206f6620686176696e67206e6f7468696e\ + 6720746f20646f3a206f6e6365206f7220747769636520736865206861642070\ + 656570656420696e746f2074686520626f6f6b20686572207369737465722077\ + 61732072656164696e672c2062757420697420686164206e6f20706963747572\ + 6573206f7220636f6e766572736174696f6e7320696e2069742c20616e642077\ + 6861742069732074686520757365206f66206120626f6f6b2c2074686f756768\ + 7420416c69636520776974686f7574207069637475726573206f7220636f6e76\ + 6572736174696f6e733f", + "3ce38e088f876c550dfebe3a1e6a99c2bc06d031106696786482e0b1406235fc", + ), ]; - assert_eq!(keccak256(&input), output); + for (input, output) in pairs { + let input = hex::decode(input).unwrap(); + let output = hex::decode(output).unwrap(); + assert_eq!(keccak256(&input), output); + } } diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index bbe3ab9eb1..d201028b9f 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -1,8 +1,6 @@ //! Ethereum and Evm types used to deserialize responses from web3 / geth. #![cfg_attr(docsrs, feature(doc_cfg))] -// Temporary until we have more of the crate implemented. -#![allow(dead_code)] // We want to have UPPERCASE idents sometimes. #![allow(non_snake_case)] // Catch documentation errors caused by code changes. @@ -20,7 +18,9 @@ pub mod error; pub mod bytecode; pub mod evm_types; pub mod geth_types; +pub mod keccak; pub mod sign_types; +pub use keccak::{keccak256, Keccak}; pub use bytecode::Bytecode; pub use error::Error; @@ -29,9 +29,7 @@ use halo2_proofs::halo2curves::{ ff::{Field as Halo2Field, FromUniformBytes, PrimeField}, }; -use crate::evm_types::{ - memory::Memory, stack::Stack, storage::Storage, Gas, GasCost, OpcodeId, ProgramCounter, -}; +use crate::evm_types::{memory::Memory, stack::Stack, storage::Storage, OpcodeId}; use ethers_core::types; pub use ethers_core::{ abi::ethereum_types::{BigEndianHash, U512}, @@ -243,6 +241,12 @@ impl ToWord for i32 { } } +impl ToWord for U64 { + fn to_word(&self) -> Word { + self.as_u64().into() + } +} + impl ToWord for Word { fn to_word(&self) -> Word { *self @@ -298,7 +302,7 @@ pub struct EIP1186ProofResponse { /// The hash of the code of the account pub code_hash: H256, /// The nonce of the account - pub nonce: U256, + pub nonce: U64, /// SHA3 of the StorageRoot pub storage_hash: H256, /// Array of rlp-serialized MerkleTree-Nodes @@ -310,13 +314,13 @@ pub struct EIP1186ProofResponse { #[derive(Deserialize)] #[doc(hidden)] struct GethExecStepInternal { - pc: ProgramCounter, + pc: u64, op: OpcodeId, - gas: Gas, + gas: u64, #[serde(default)] - refund: Gas, + refund: u64, #[serde(rename = "gasCost")] - gas_cost: GasCost, + gas_cost: u64, depth: u16, error: Option, // stack is in hex 0x prefixed @@ -334,11 +338,11 @@ struct GethExecStepInternal { #[derive(Clone, Eq, PartialEq, Serialize)] #[doc(hidden)] pub struct GethExecStep { - pub pc: ProgramCounter, + pub pc: u64, pub op: OpcodeId, - pub gas: Gas, - pub gas_cost: GasCost, - pub refund: Gas, + pub gas: u64, + pub gas_cost: u64, + pub refund: u64, pub depth: u16, pub error: Option, // stack is in hex 0x prefixed @@ -371,10 +375,10 @@ 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)) + .field("pc", &format_args!("0x{:04x}", self.pc)) .field("op", &self.op) - .field("gas", &format_args!("{}", self.gas.0)) - .field("gas_cost", &format_args!("{}", self.gas_cost.0)) + .field("gas", &format_args!("{}", self.gas)) + .field("gas_cost", &format_args!("{}", self.gas_cost)) .field("depth", &self.depth) .field("error", &self.error) .field("stack", &self.stack) @@ -439,7 +443,7 @@ pub struct ResultGethExecTrace { #[derive(Deserialize, Serialize, Clone, Debug, Eq, PartialEq)] pub struct GethExecTrace { /// Used gas - pub gas: Gas, + pub gas: u64, /// True when the transaction has failed. pub failed: bool, /// Return value of execution which is a hex encoded byte array @@ -555,16 +559,16 @@ mod tests { assert_eq!( trace, GethExecTrace { - gas: Gas(26809), + gas: 26809, failed: false, return_value: "".to_owned(), struct_logs: vec![ GethExecStep { - pc: ProgramCounter(0), + pc: 0, op: OpcodeId::PUSH1, - gas: Gas(22705), - refund: Gas(0), - gas_cost: GasCost(3), + gas: 22705, + refund: 0, + gas_cost: 3, depth: 1, error: None, stack: Stack::new(), @@ -572,11 +576,11 @@ mod tests { memory: Memory::new(), }, GethExecStep { - pc: ProgramCounter(163), + pc: 163, op: OpcodeId::SLOAD, - gas: Gas(5217), - refund: Gas(0), - gas_cost: GasCost(2100), + gas: 5217, + refund: 0, + gas_cost: 2100, depth: 1, error: None, stack: Stack(vec![word!("0x1003e2d2"), word!("0x2a"), word!("0x0")]), @@ -584,11 +588,11 @@ mod tests { memory: Memory::from(vec![word!("0x0"), word!("0x0"), word!("0x080")]), }, GethExecStep { - pc: ProgramCounter(189), + pc: 189, op: OpcodeId::SHA3, - gas: Gas(178805), - refund: Gas(0), - gas_cost: GasCost(42), + gas: 178805, + refund: 0, + gas_cost: 42, depth: 1, error: None, stack: Stack(vec![ diff --git a/external-tracer/src/lib.rs b/external-tracer/src/lib.rs index 678bc21e33..138f0fd2dd 100644 --- a/external-tracer/src/lib.rs +++ b/external-tracer/src/lib.rs @@ -27,18 +27,15 @@ pub struct TraceConfig { /// Configuration structure for `logger.Config` #[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "PascalCase")] pub struct LoggerConfig { /// enable memory capture - #[serde(rename = "EnableMemory")] pub enable_memory: bool, /// disable stack capture - #[serde(rename = "DisableStack")] pub disable_stack: bool, /// disable storage capture - #[serde(rename = "DisableStorage")] pub disable_storage: bool, /// enable return data capture - #[serde(rename = "EnableReturnData")] pub enable_return_data: bool, } diff --git a/gadgets/src/batched_is_zero.rs b/gadgets/src/batched_is_zero.rs index 2f3acb2c22..6f4ff461a9 100644 --- a/gadgets/src/batched_is_zero.rs +++ b/gadgets/src/batched_is_zero.rs @@ -158,6 +158,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/evm_word.rs b/gadgets/src/evm_word.rs deleted file mode 100644 index 16f85c70e3..0000000000 --- a/gadgets/src/evm_word.rs +++ /dev/null @@ -1,275 +0,0 @@ -//! An EVM word (256 bits) is represented as a linear combination of 32 bytes: -//! `encode(word) = b_0 + r * b_1 + ... + r^{31} * b_{31}`, -//! where `word` is a 256-bit word, b_i's are bytes, and `r` is a random factor. -//! This helper returns an expression of `encode(word)`. -//! -//! In the zkevm circuit, this `encode(word)` expression will not be directly -//! looked up. Instead, it will be folded into the bus mapping lookup. - -use crate::Variable; -use digest::{FixedOutput, Input}; -use eth_types::Field; -use halo2_proofs::{ - circuit::{Region, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector}, - poly::Rotation, -}; -use sha3::{Digest, Keccak256}; - -#[cfg(test)] -use halo2_proofs::circuit::Layouter; - -// TODO: Move into crate-level `constants` file. -/// r = hash([0, 1, ..., 255]) -pub fn r() -> F { - let mut hasher = Keccak256::new(); - for byte in 0..=u8::MAX { - hasher.process(&[byte]); - } - let mut r = [0; 64]; - r[..32].copy_from_slice(hasher.fixed_result().as_slice()); - 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| { - let byte = F::from(val as u64); - acc * r + byte - }) -} - -/// A 256-bit word represented in the circuit as 32 bytes. -pub struct Word([Variable; 32]); - -#[allow(dead_code)] -/// Configuration structure used to constraint. generate and assign an EVM Word -/// inside a circuit. -#[derive(Clone, Debug)] -pub struct WordConfig { - /// Randomness used to compress the word encoding. - r: F, - /// Selector to toggle the word encoding gate. - // TODO: This may be replaced by a synthetic selector. - q_encode: Selector, - /// Advice columns used to witness the byte representation of the word. - bytes: [Column; 32], - /// Fixed column containing all possible 8-bit values. This is used in - /// a lookup argument to range-constrain each byte. - byte_lookup: Column, - /// Expression representing `encode(word)`. - encode_word_expr: Expression, -} - -impl WordConfig { - /// Sets up the configuration of the config by creating the required columns - /// & selectors and defining the constraints that take part when using a - /// Word. - pub fn configure( - meta: &mut ConstraintSystem, - r: F, - q_encode: Selector, - bytes: [Column; 32], - byte_lookup: Column, - ) -> Self { - // Expression representing `encode(word)`. - let mut encode_word_expr = Expression::Constant(F::ZERO); - - // Lookup each byte in the witnessed word representation to - // range-constrain it to 8 bits. - for byte in bytes.iter().rev() { - meta.lookup_any("Word byte for range", |meta| { - let q_encode = meta.query_selector(q_encode); - let r = Expression::Constant(r); - let byte = meta.query_advice(*byte, Rotation::cur()); - let byte_lookup = meta.query_fixed(byte_lookup, Rotation::cur()); - - // Update encode_word_expr. - encode_word_expr = encode_word_expr.clone() * r + byte.clone(); - - vec![(q_encode * byte, byte_lookup)] - }); - } - - Self { - r, - q_encode, - bytes, - byte_lookup, - encode_word_expr, - } - } - - /// Loads the 8-bit lookup table. - /// NB: This is only used in tests, since the zkevm circuit will load a - /// global 8-bit table. - #[cfg(test)] - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_region( - || "8-bit table", - |mut meta| { - for byte in 0..=u8::MAX { - meta.assign_fixed( - || format!("load {}", byte), - self.byte_lookup, - byte.into(), - || Value::known(F::from(byte as u64)), - )?; - } - - Ok(()) - }, - ) - } - - /// Assigns the byte decomposition of a word to the `bytes` advice columns. - pub fn assign_word( - &self, - region: &mut Region<'_, F>, - offset: usize, - word: [Value; 32], - ) -> Result, Error> { - let mut bytes: Vec> = Vec::with_capacity(32); - - for (idx, (byte, column)) in word.iter().zip(self.bytes.iter()).enumerate() { - // TODO: We will likely enable this selector outside of the helper. - self.q_encode.enable(region, offset)?; - - let byte_field_elem = byte.map(|byte| F::from(byte as u64)); - let cell = region.assign_advice( - || format!("assign byte {}", idx), - *column, - offset, - || byte_field_elem, - )?; - - bytes.push(Variable::new(cell, *byte)); - } - - Ok(Word(bytes.try_into().unwrap())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use halo2_proofs::{ - arithmetic::Field as Halo2Field, - circuit::SimpleFloorPlanner, - dev::{FailureLocation, MockProver, VerifyFailure}, - halo2curves::{bn256::Fr as Fp, group::ff::PrimeField}, - plonk::{Circuit, Instance}, - }; - use rand::SeedableRng; - use rand_xorshift::XorShiftRng; - use std::marker::PhantomData; - - #[test] - fn evm_word() { - #[derive(Default)] - struct MyCircuit { - word: [Value; 32], - _marker: PhantomData, - } - - impl Circuit for MyCircuit { - // Introduce an additional instance column here to test lookups - // with public inputs. This is analogous to the bus mapping - // commitment which will be provided as public inputs. - type Config = (WordConfig, Column); - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let r = r(); - - let q_encode = meta.complex_selector(); - - let bytes: [Column; 32] = (0..32) - .map(|_| meta.advice_column()) - .collect::>() - .try_into() - .unwrap(); - let byte_lookup = meta.fixed_column(); - - let config = WordConfig::configure(meta, r, q_encode, bytes, byte_lookup); - - let pub_inputs = meta.instance_column(); - - // Make sure each encoded word has been committed to in the - // public inputs. - meta.lookup_any("Encoded word / Pub inputs", |meta| { - let q_encode = meta.query_selector(q_encode); - let pub_inputs = meta.query_instance(pub_inputs, Rotation::cur()); - - let encode_word = config.clone().encode_word_expr; - - vec![(q_encode * encode_word, pub_inputs)] - }); - - (config, pub_inputs) - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - config.0.load(&mut layouter)?; - - layouter.assign_region( - || "assign word", - |mut region| { - let offset = 0; - config.0.assign_word(&mut region, offset, self.word) - }, - )?; - - Ok(()) - } - } - - { - let rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - let word = Fp::random(rng); - let circuit = MyCircuit:: { - word: word - .to_repr() - .iter() - .map(|b| Value::known(*b)) - .collect::>() - .try_into() - .unwrap(), - _marker: PhantomData, - }; - - // Test without public inputs - let prover = MockProver::::run(9, &circuit, vec![vec![]]).unwrap(); - assert_eq!( - prover.verify(), - Err(vec![VerifyFailure::Lookup { - name: "Encoded word / Pub inputs".to_string(), - lookup_index: 32, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from(( - 1, - "assign word".to_string() - )), - offset: 0 - } - }]) - ); - - // Calculate word commitment and use it as public input. - let encoded: Fp = encode(word.to_repr().iter().rev().cloned(), r()); - let prover = MockProver::::run(9, &circuit, vec![vec![encoded]]).unwrap(); - prover.assert_satisfied_par() - } - } -} diff --git a/gadgets/src/is_zero.rs b/gadgets/src/is_zero.rs index cc6f3e3a67..be552b1f89 100644 --- a/gadgets/src/is_zero.rs +++ b/gadgets/src/is_zero.rs @@ -51,11 +51,11 @@ impl IsZeroConfig { } } +#[derive(Debug, Clone)] /// Wrapper arround [`IsZeroConfig`] for which [`Chip`] is implemented. pub struct IsZeroChip { config: IsZeroConfig, } - #[rustfmt::skip] impl IsZeroChip { /// Sets up the configuration of the chip by creating the required columns @@ -105,6 +105,11 @@ impl IsZeroChip { pub fn construct(config: IsZeroConfig) -> Self { IsZeroChip { config } } + + /// Annotates columns of this gadget embedded within a circuit region. + pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { + self.config.annotate_columns_in_region(region, prefix) + } } impl IsZeroInstruction for IsZeroChip { @@ -208,6 +213,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -335,6 +341,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/gadgets/src/less_than.rs b/gadgets/src/less_than.rs index db539af086..827aa4d184 100644 --- a/gadgets/src/less_than.rs +++ b/gadgets/src/less_than.rs @@ -19,8 +19,8 @@ pub trait LtInstruction { &self, region: &mut Region<'_, F>, offset: usize, - lhs: F, - rhs: F, + lhs: Value, + rhs: Value, ) -> Result<(), Error>; /// Load the u8 lookup table. @@ -114,28 +114,37 @@ impl LtInstruction for LtChip { &self, region: &mut Region<'_, F>, offset: usize, - lhs: F, - rhs: F, + lhs: Value, + rhs: Value, ) -> Result<(), Error> { let config = self.config(); - let lt = lhs < rhs; + let lt = lhs.zip(rhs).map(|(lhs, rhs)| lhs < rhs); + region.assign_advice( || "lt chip: lt", config.lt, offset, - || Value::known(F::from(lt as u64)), + || lt.map(|lt| F::from(lt as u64)), )?; - let diff = (lhs - rhs) + (if lt { config.range } else { F::ZERO }); - let diff_bytes = diff.to_repr(); - let diff_bytes = diff_bytes.as_ref(); + let diff_bytes = lhs.zip(rhs).map(|(lhs, rhs)| { + let mut diff = lhs - rhs; + let lt = lhs < rhs; + if lt { + diff += config.range; + } else { + diff += F::ZERO; + } + diff.to_repr() + }); + for (idx, diff_column) in config.diff.iter().enumerate() { region.assign_advice( || format!("lt chip: diff byte {}", idx), *diff_column, offset, - || Value::known(F::from(diff_bytes[idx] as u64)), + || diff_bytes.as_ref().map(|bytes| F::from(bytes[idx] as u64)), )?; } @@ -243,6 +252,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -322,7 +332,12 @@ mod test { idx + 1, || Value::known(*value), )?; - chip.assign(&mut region, idx + 1, value_prev, *value)?; + chip.assign( + &mut region, + idx + 1, + Value::known(value_prev), + Value::known(*value), + )?; value_prev = *value; } @@ -363,6 +378,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -446,7 +462,12 @@ mod test { idx + 1, || Value::known(*value_b), )?; - chip.assign(&mut region, idx + 1, *value_a, *value_b)?; + chip.assign( + &mut region, + idx + 1, + Value::known(*value_a), + Value::known(*value_b), + )?; } Ok(()) diff --git a/gadgets/src/lib.rs b/gadgets/src/lib.rs index 3ef7fae9a1..a7150262ee 100644 --- a/gadgets/src/lib.rs +++ b/gadgets/src/lib.rs @@ -13,32 +13,13 @@ pub mod batched_is_zero; pub mod binary_number; -pub mod evm_word; pub mod is_zero; pub mod less_than; -pub mod monotone; pub mod mul_add; pub mod util; use eth_types::Field; -use halo2_proofs::{ - circuit::{AssignedCell, Value}, - plonk::Expression, -}; - -#[allow(dead_code)] -/// An assigned cell in the circuit. -#[derive(Clone, Debug)] -pub struct Variable { - assig_cell: AssignedCell, - value: Value, -} - -impl Variable { - pub(crate) fn new(assig_cell: AssignedCell, value: Value) -> Self { - Self { assig_cell, value } - } -} +use halo2_proofs::plonk::Expression; /// Restrict an expression to be a boolean. pub fn bool_check(value: Expression) -> Expression { diff --git a/gadgets/src/monotone.rs b/gadgets/src/monotone.rs deleted file mode 100644 index 39d4b6cf64..0000000000 --- a/gadgets/src/monotone.rs +++ /dev/null @@ -1,310 +0,0 @@ -//! # Monotone mod -//! 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::{ - circuit::{Chip, Layouter, Value}, - plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, - poly::Rotation, -}; -use std::{marker::PhantomData, u64}; - -#[allow(dead_code)] -#[derive(Clone, Debug)] -pub(crate) struct MonotoneConfig { - range_table: Column, - value: Column, -} - -/// MonotoneChip helps to check if an advice column is monotonically increasing -/// within a range. With strict enabled, it disallows equality of two cell. -pub(crate) struct MonotoneChip { - config: MonotoneConfig, - _marker: PhantomData, -} - -#[allow(dead_code)] -impl - MonotoneChip -{ - /// configure which column should be check. q_enable here as a fn is - /// flexible for synthetic selector instead of a fixed one. - pub fn configure( - meta: &mut ConstraintSystem, - q_enable: impl FnOnce(&mut VirtualCells<'_, F>) -> Expression, - value: Column, - ) -> MonotoneConfig { - let range_table = meta.fixed_column(); - - let config = MonotoneConfig { range_table, value }; - - meta.lookup_any("Range check", |meta| { - let q_enable = q_enable(meta); - let range_table = meta.query_fixed(config.range_table, Rotation::cur()); - let value_diff = { - let value_cur = meta.query_advice(value, Rotation::cur()); - let value_prev = meta.query_advice(value, Rotation::prev()); - if INCR { - value_cur - value_prev - } else { - value_prev - value_cur - } - }; - - // If strict monotone, we subtract diff by one - // to make sure zero lookup fail - let min_diff = Expression::Constant(F::from(STRICT as u64)); - - vec![(q_enable * (value_diff - min_diff), range_table)] - }); - - config - } - - pub fn load(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - layouter.assign_region( - || "range_table", - |mut meta| { - let max = RANGE - STRICT as usize; - - for idx in 0..=max { - meta.assign_fixed( - || "range_table_value", - self.config.range_table, - idx, - || Value::known(F::from(idx as u64)), - )?; - } - - Ok(()) - }, - ) - } - - pub fn construct(config: MonotoneConfig) -> Self { - Self { - config, - _marker: PhantomData, - } - } -} - -impl Chip - for MonotoneChip -{ - type Config = MonotoneConfig; - type Loaded = (); - - fn config(&self) -> &Self::Config { - &self.config - } - - fn loaded(&self) -> &Self::Loaded { - &() - } -} - -#[cfg(test)] -mod test { - use super::*; - use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, - dev::{ - FailureLocation, MockProver, - VerifyFailure::{self, Lookup}, - }, - halo2curves::bn256::Fr as Fp, - plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Selector}, - }; - use std::marker::PhantomData; - - #[derive(Clone, Debug)] - struct TestCircuitConfig { - q_enable: Selector, - value: Column, - mono_incr: MonotoneConfig, - } - - #[derive(Default)] - struct TestCircuit { - values: Option>, - _marker: PhantomData, - } - - impl Circuit - for TestCircuit - { - type Config = TestCircuitConfig; - type FloorPlanner = SimpleFloorPlanner; - - fn without_witnesses(&self) -> Self { - Self::default() - } - - fn configure(meta: &mut ConstraintSystem) -> Self::Config { - let q_enable = meta.complex_selector(); - let value = meta.advice_column(); - - let mono_incr = MonotoneChip::::configure( - meta, - |meta| meta.query_selector(q_enable), - value, - ); - - Self::Config { - q_enable, - value, - mono_incr, - } - } - - fn synthesize( - &self, - config: Self::Config, - mut layouter: impl Layouter, - ) -> Result<(), Error> { - let monotone_chip = - MonotoneChip::::construct(config.mono_incr.clone()); - - monotone_chip.load(&mut layouter)?; - - let values: Vec<_> = self - .values - .as_ref() - .map(|values| values.iter().map(|value| F::from(*value)).collect()) - .ok_or(Error::Synthesis)?; - - layouter.assign_region( - || "witness", - |mut region| { - for (idx, value) in values.iter().enumerate() { - region.assign_advice( - || "value", - config.value, - idx, - || Value::known(*value), - )?; - if idx > 0 { - config.q_enable.enable(&mut region, idx)?; - } - } - - Ok(()) - }, - ) - } - } - - macro_rules! gen_try_test_circuit { - ($range:expr, $incr:expr, $strict:expr) => { - fn try_test_circuit(values: Vec, result: Result<(), Vec>) { - let circuit = TestCircuit:: { - values: Some(values), - _marker: PhantomData, - }; - let prover = MockProver::::run( - usize::BITS - ($range as usize).leading_zeros(), - &circuit, - vec![], - ) - .unwrap(); - if result.is_err() { - assert_eq!(prover.verify_par(), result) - } else { - prover.assert_satisfied_par() - } - } - }; - } - - #[test] - fn strict_monotonically_increasing() { - gen_try_test_circuit!(100, true, true); - - // strict monotone in range (ok) - try_test_circuit(vec![1, 2, 3, 4, 104], Ok(())); - try_test_circuit(vec![1001, 1002, 1003, 1004, 1104], Ok(())); - // non-strict monotone in range (error) - try_test_circuit( - vec![1, 2, 2, 4, 4], - Err(vec![ - Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 2, - }, - }, - Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }, - ]), - ); - // out of range (error) - try_test_circuit( - vec![1, 2, 3, 4, 105], - Err(vec![Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - // not monotone (error) - try_test_circuit( - vec![1, 2, 3, 103, 4], - Err(vec![Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - } - - #[test] - fn non_strict_monotonically_increasing() { - gen_try_test_circuit!(100, true, false); - - // strict monotone in range (ok) - try_test_circuit(vec![1, 2, 3, 4, 104], Ok(())); - try_test_circuit(vec![1001, 1002, 1003, 1004, 1104], Ok(())); - // non-strict monotone in range (ok) - try_test_circuit(vec![1, 2, 2, 4, 4], Ok(())); - // out of range (error) - try_test_circuit( - vec![1, 2, 3, 4, 105], - Err(vec![Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - // not monotone (error) - try_test_circuit( - vec![1, 2, 3, 103, 4], - Err(vec![Lookup { - name: "Range check".to_string(), - lookup_index: 0, - location: FailureLocation::InRegion { - region: halo2_proofs::dev::metadata::Region::from((1, "witness")), - offset: 4, - }, - }]), - ); - } -} diff --git a/gadgets/src/mul_add.rs b/gadgets/src/mul_add.rs index 5433b8dd80..3710f7b9c6 100644 --- a/gadgets/src/mul_add.rs +++ b/gadgets/src/mul_add.rs @@ -443,6 +443,7 @@ mod test { impl Circuit for TestCircuit { type Config = TestCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn configure(meta: &mut halo2_proofs::plonk::ConstraintSystem) -> Self::Config { let q_enable = meta.complex_selector(); diff --git a/gadgets/src/util.rs b/gadgets/src/util.rs index e0bfcaa6f1..ae455d964e 100644 --- a/gadgets/src/util.rs +++ b/gadgets/src/util.rs @@ -1,8 +1,5 @@ //! Utility traits, functions used in the crate. -use eth_types::{ - evm_types::{GasCost, OpcodeId}, - Field, U256, -}; +use eth_types::{evm_types::OpcodeId, Field, U256}; use halo2_proofs::plonk::Expression; /// Returns the sum of the passed in cells @@ -174,7 +171,6 @@ impl_expr!(u8); impl_expr!(u64); impl_expr!(usize); impl_expr!(OpcodeId, OpcodeId::as_u8); -impl_expr!(GasCost, GasCost::as_u64); impl Expr for Expression { #[inline] diff --git a/geth-utils/gethutil/trace.go b/geth-utils/gethutil/trace.go index 744ce01d40..d8ddc32114 100644 --- a/geth-utils/gethutil/trace.go +++ b/geth-utils/gethutil/trace.go @@ -128,21 +128,24 @@ func newUint64(val uint64) *uint64 { return &val } func Trace(config TraceConfig) ([]*ExecutionResult, error) { chainConfig := params.ChainConfig{ - ChainID: toBigInt(config.ChainID), - HomesteadBlock: big.NewInt(0), - DAOForkBlock: big.NewInt(0), - DAOForkSupport: true, - EIP150Block: big.NewInt(0), - EIP150Hash: common.Hash{}, - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), + ChainID: toBigInt(config.ChainID), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.Hash{}, + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ShanghaiTime: newUint64(0), + TerminalTotalDifficulty: big.NewInt(0), + TerminalTotalDifficultyPassed: true, } var txsGasLimit uint64 @@ -180,6 +183,9 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { return nil, fmt.Errorf("txs total gas: %d Exceeds block gas limit: %d", txsGasLimit, blockGasLimit) } + // For opcode PREVRANDAO + randao := common.BigToHash(toBigInt(config.Block.Difficulty)) // TODO: fix + blockCtx := vm.BlockContext{ CanTransfer: core.CanTransfer, Transfer: core.Transfer, @@ -195,6 +201,7 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { BlockNumber: toBigInt(config.Block.Number), Time: toBigInt(config.Block.Timestamp).Uint64(), Difficulty: toBigInt(config.Block.Difficulty), + Random: &randao, BaseFee: toBigInt(config.Block.BaseFee), GasLimit: blockGasLimit, } diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index 5eb08aa43a..4771edd86e 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -33,6 +33,7 @@ use zkevm_circuits::{ evm_circuit::TestEvmCircuit, exp_circuit::TestExpCircuit, keccak_circuit::TestKeccakCircuit, + pi_circuit::TestPiCircuit, state_circuit::TestStateCircuit, super_circuit::SuperCircuit, tx_circuit::TestTxCircuit, @@ -79,6 +80,7 @@ const COPY_CIRCUIT_DEGREE: u32 = 16; const KECCAK_CIRCUIT_DEGREE: u32 = 16; const SUPER_CIRCUIT_DEGREE: u32 = 20; const EXP_CIRCUIT_DEGREE: u32 = 16; +const PI_CIRCUIT_DEGREE: u32 = 17; lazy_static! { /// Data generation. @@ -119,12 +121,16 @@ lazy_static! { TokioMutex::new(IntegrationTest::new("Keccak", KECCAK_CIRCUIT_DEGREE)); /// Integration test for Copy circuit - pub static ref SUPER_CIRCUIT_TEST: TokioMutex>> = + 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)); + + /// Integration test for Pi circuit + pub static ref PI_CIRCUIT_TEST: TokioMutex>> = + TokioMutex::new(IntegrationTest::new("Pi", PI_CIRCUIT_DEGREE)); } /// Generic implementation for integration tests diff --git a/integration-tests/tests/circuits.rs b/integration-tests/tests/circuits.rs index bfce518a62..1582bd196b 100644 --- a/integration-tests/tests/circuits.rs +++ b/integration-tests/tests/circuits.rs @@ -49,6 +49,11 @@ macro_rules! declare_tests { async fn []() { run_test! (EXP_CIRCUIT_TEST, $block_tag, $real_prover); } + + #[tokio::test] + async fn []() { + run_test! (PI_CIRCUIT_TEST, $block_tag, $real_prover); + } } }; } @@ -64,7 +69,8 @@ macro_rules! unroll_tests { COPY_CIRCUIT_TEST, KECCAK_CIRCUIT_TEST, SUPER_CIRCUIT_TEST, - EXP_CIRCUIT_TEST + EXP_CIRCUIT_TEST, + PI_CIRCUIT_TEST, }; use integration_tests::log_init; mod real_prover { diff --git a/keccak256/Cargo.toml b/keccak256/Cargo.toml deleted file mode 100644 index 490dc87063..0000000000 --- a/keccak256/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "keccak256" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -[features] -dev-graph = ["halo2_proofs/dev-graph", "plotters"] - -[dependencies] -halo2_proofs = { version = "0.1.0-beta.1" } -itertools = "0.10.1" -num-bigint = "0.4.2" -num-traits = "0.2.14" -plotters = { version = "0.3.0", optional = true } -eth-types = { path = "../eth-types" } -lazy_static = "1.4" -log = "0.4.14" -env_logger = "0.9" - -[dev-dependencies] -pretty_assertions = "1.0" -rand = "0.8" diff --git a/keccak256/src/arith_helpers.rs b/keccak256/src/arith_helpers.rs deleted file mode 100644 index 4877486b08..0000000000 --- a/keccak256/src/arith_helpers.rs +++ /dev/null @@ -1,228 +0,0 @@ -use crate::common::State; -use eth_types::Field; -use itertools::Itertools; -use log::debug; -use num_bigint::BigUint; -use num_traits::Zero; -use std::ops::{Index, IndexMut}; - -pub const B2: u8 = 2; -pub const B13: u8 = 13; -pub const B9: u8 = 9; - -/// Base 9 coef mapper scalers -/// f_logic(x1, x2, x3, x4) = x1 ^ (!x2 & x3) ^ x4 -/// f_arith(x1, x2, x3, x4) = 2*x1 + x2 + 3*x3 + 2*x4 -/// where x1, x2, x3, x4 are binary. -/// We have the property that `0 <= f_arith(...) < 9` and -/// the map from `f_arith(...)` to `f_logic(...)` is injective. -pub const A1: u64 = 2; -pub const A2: u64 = 1; -pub const A3: u64 = 3; -pub const A4: u64 = 2; - -pub type Lane13 = BigUint; -pub type Lane9 = BigUint; - -#[derive(Debug)] -pub struct StateBigInt { - pub(crate) xy: Vec, -} -impl Default for StateBigInt { - fn default() -> Self { - let mut xy: Vec = Vec::with_capacity(25); - for _ in 0..25 { - xy.push(Zero::zero()); - } - Self { xy } - } -} - -impl From for StateBigInt { - fn from(state: State) -> Self { - let xy = state - .iter() - .flatten() - .map(|num| BigUint::from(*num)) - .collect(); - Self { xy } - } -} - -impl StateBigInt { - pub fn from_state_big_int(a: &StateBigInt, lane_transform: F) -> Self - where - F: Fn(BigUint) -> BigUint, - { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = lane_transform(a[(x, y)].clone()); - } - out - } -} - -impl Index<(usize, usize)> for StateBigInt { - type Output = BigUint; - fn index(&self, xy: (usize, usize)) -> &Self::Output { - debug_assert!(xy.0 < 5); - debug_assert!(xy.1 < 5); - - &self.xy[xy.0 * 5 + xy.1] - } -} - -impl IndexMut<(usize, usize)> for StateBigInt { - fn index_mut(&mut self, xy: (usize, usize)) -> &mut Self::Output { - debug_assert!(xy.0 < 5); - debug_assert!(xy.1 < 5); - - &mut self.xy[xy.0 * 5 + xy.1] - } -} - -impl Clone for StateBigInt { - fn clone(&self) -> StateBigInt { - let xy = self.xy.clone(); - StateBigInt { xy } - } -} - -pub fn convert_b2_to_b13(a: u64) -> Lane13 { - let mut lane13: BigUint = Zero::zero(); - for i in 0..64 { - let bit = (a >> i) & 1; - lane13 += bit * BigUint::from(B13).pow(i); - } - lane13 -} - -pub fn convert_b2_to_b9(a: u64) -> Lane9 { - let mut lane9: BigUint = Zero::zero(); - for i in 0..64 { - let bit = (a >> i) & 1; - lane9 += bit * BigUint::from(B9).pow(i); - } - lane9 -} - -/// Maps a sum of 12 bits to the XOR result of 12 bits. -/// -/// The input `x` is a chunk of a base 13 number and it represents the -/// arithmatic sum of 12 bits. Asking the result of the 12 bits XORed together -/// is equivalent of asking if `x` being an odd number. -/// -/// For example, if we have 5 bits set and 7 bits unset, then we have `x` as 5 -/// and the xor result to be 1. -pub fn convert_b13_coef(x: u8) -> u8 { - debug_assert!(x < 13); - x & 1 -} - -/// Maps the arithmatic result `2*a + b + 3*c + 2*d` to the bit operation result -/// `a ^ (~b & c) ^ d` -/// -/// The input `x` is a chunk of a base 9 number and it represents the arithmatic -/// result of `2*a + b + 3*c + 2*d`, where `a`, `b`, `c`, and `d` each is a bit. -pub fn convert_b9_coef(x: u8) -> u8 { - debug_assert!(x < 9); - let bit_table: [u8; 9] = [0, 0, 1, 1, 0, 0, 1, 1, 0]; - bit_table[x as usize] -} - -// We assume the input comes from Theta step and has 65 chunks -// expecting outputs from theta gate -pub fn convert_b13_lane_to_b9(x: Lane13, rot: u32) -> Lane9 { - // 65 chunks - let mut chunks = x.to_radix_le(B13.into()); - chunks.resize(65, 0); - // 0 and 64 was separated in Theta, we now combined them together - let special = chunks.first().unwrap() + chunks.get(64).unwrap(); - // middle 63 chunks - let middle = chunks.get(1..64).unwrap(); - // split at offset - let (left, right) = middle.split_at(63 - rot as usize); - // rotated has 64 chunks - // left is rotated right, and the right is wrapped over to left - // with the special chunk in the middle - let rotated: Vec = right - .iter() - .chain(vec![special].iter()) - .chain(left.iter()) - .map(|&x| convert_b13_coef(x)) - .collect_vec(); - BigUint::from_radix_le(&rotated, B9.into()).unwrap_or_default() -} - -pub fn convert_lane(lane: BigUint, from_base: u8, to_base: u8, coef_transform: F) -> BigUint -where - F: Fn(u8) -> u8, -{ - let chunks = lane.to_radix_be(from_base.into()); - let converted_chunks: Vec = chunks.iter().map(|&x| coef_transform(x)).collect(); - BigUint::from_radix_be(&converted_chunks, to_base.into()).unwrap_or_default() -} - -pub fn convert_b9_lane_to_b13(x: Lane9) -> Lane13 { - convert_lane(x, B9, B13, convert_b9_coef) -} - -pub fn convert_b9_lane_to_b2(x: Lane9) -> u64 { - convert_lane(x, B9, 2, convert_b9_coef) - .iter_u64_digits() - .take(1) - .next() - .unwrap_or(0) -} - -pub fn convert_b9_lane_to_b2_biguint(x: Lane9) -> BigUint { - convert_lane(x, B9, 2, convert_b9_coef) -} - -pub fn convert_b9_lane_to_b2_normal(x: Lane9) -> u64 { - convert_lane(x, B9, 2, |y| y) - .iter_u64_digits() - .take(1) - .next() - .unwrap_or(0) -} - -/// This function allows us to inpect coefficients of big-numbers in different -/// bases. -pub fn inspect(x: BigUint, name: &str, base: u8) { - let mut chunks = x.to_radix_le(base.into()); - chunks.resize(65, 0); - let info: Vec<(usize, u8)> = (0..65).zip(chunks.iter().copied()).collect_vec(); - debug!("inspect {} {} info {:?}", name, x, info); -} - -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)) -} - -#[cfg(test)] -mod tests { - use super::*; - use num_bigint::BigUint; - #[test] - fn test_convert_b13_lane_to_b9() { - // the number 1 is chosen that `convert_b13_coef` has no effect - let mut a = vec![0, 1, 1, 1]; - a.resize(65, 0); - let lane = BigUint::from_radix_le(&a, B13.into()).unwrap_or_default(); - assert_eq!( - convert_b13_lane_to_b9(lane.clone(), 0), - BigUint::from_radix_le(&a, B9.into()).unwrap_or_default() - ); - - // rotate by 4 - let mut b = vec![0, 0, 0, 0, 0, 1, 1, 1]; - b.resize(65, 0); - assert_eq!( - convert_b13_lane_to_b9(lane, 4), - BigUint::from_radix_le(&b, B9.into()).unwrap_or_default() - ); - } -} diff --git a/keccak256/src/common.rs b/keccak256/src/common.rs deleted file mode 100644 index 138e3264d0..0000000000 --- a/keccak256/src/common.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! Types and constants of Keccak hash function. The constants can be found in the appendices of or [pycryptodome](https://github.com/Legrandin/pycryptodome). - -/// The State is a 5x5 matrix of 64 bit lanes. -pub type State = [[u64; 5]; 5]; - -/// The number of next_inputs that are used inside the `absorb` circuit. -pub const NEXT_INPUTS_LANES: usize = 17; - -/// The number of rounds for the 1600 bits permutation used in Keccak-256. See [here](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L230) -pub const PERMUTATION: usize = 24; - -/// The Keccak [round constants](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L257-L282) -pub static ROUND_CONSTANTS: [u64; PERMUTATION] = [ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, -]; - -/// The Keccak [rotation offsets](https://github.com/Legrandin/pycryptodome/blob/016252bde04456614b68d4e4e8798bc124d91e7a/src/keccak.c#L232-L255) -pub static ROTATION_CONSTANTS: [[u32; 5]; 5] = [ - [0, 36, 3, 41, 18], - [1, 44, 10, 45, 2], - [62, 6, 43, 15, 61], - [28, 55, 25, 21, 56], - [27, 20, 39, 8, 14], -]; - -pub const LANE_SIZE: u32 = 64; diff --git a/keccak256/src/gate_helpers.rs b/keccak256/src/gate_helpers.rs deleted file mode 100644 index c14015fdc8..0000000000 --- a/keccak256/src/gate_helpers.rs +++ /dev/null @@ -1,28 +0,0 @@ -use eth_types::Field; -use num_bigint::BigUint; - -/// Convert a bigUint value to Field -/// -/// We assume the input value is smaller than the field size -pub fn biguint_to_f(x: &BigUint) -> F { - let mut x_bytes = x.to_bytes_le(); - debug_assert!( - x_bytes.len() <= 32, - "expect len <=32 but got {}", - x_bytes.len() - ); - x_bytes.resize(32, 0); - let x_bytes: [u8; 32] = x_bytes.try_into().unwrap(); - F::from_repr_vartime(x_bytes).unwrap() -} - -pub fn f_to_biguint(x: F) -> BigUint { - BigUint::from_bytes_le(&x.to_repr()) -} - -pub fn biguint_mod(x: &BigUint, modulus: u8) -> u8 { - x.to_radix_le(modulus.into()) - .first() - .copied() - .unwrap_or_default() -} diff --git a/keccak256/src/keccak_arith.rs b/keccak256/src/keccak_arith.rs deleted file mode 100644 index c59b0964f3..0000000000 --- a/keccak256/src/keccak_arith.rs +++ /dev/null @@ -1,366 +0,0 @@ -use crate::{arith_helpers::*, common::*}; -use itertools::Itertools; - -#[derive(Default)] -pub struct KeccakFArith {} - -impl KeccakFArith { - pub fn permute_and_absorb( - a: &mut StateBigInt, - next_inputs: Option<&State>, - ) -> Option { - for rc in ROUND_CONSTANTS.iter().take(PERMUTATION - 1) { - let s1 = KeccakFArith::theta(a); - let s2 = KeccakFArith::rho(&s1); - let s3 = KeccakFArith::pi(&s2); - let s4 = KeccakFArith::xi(&s3); - let s5 = KeccakFArith::iota_b9(&s4, *rc); - *a = StateBigInt::from_state_big_int(&s5, convert_b9_lane_to_b13); - } - let s1 = KeccakFArith::theta(a); - let s2 = KeccakFArith::rho(&s1); - let s3 = KeccakFArith::pi(&s2); - let s4 = KeccakFArith::xi(&s3); - let res = KeccakFArith::mixing(&s4, next_inputs, *ROUND_CONSTANTS.last().unwrap()); - *a = res.clone(); - if next_inputs.is_some() { - Some(res) - } else { - None - } - } - - pub fn theta(a: &StateBigInt) -> StateBigInt { - let mut c: Vec = Vec::with_capacity(5); - let mut out = StateBigInt::default(); - - for x in 0..5 { - c.push(&a[(x, 0)] + &a[(x, 1)] + &a[(x, 2)] + &a[(x, 3)] + &a[(x, 4)]); - } - - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = &a[(x, y)] + &c[(x + 4) % 5] + &c[(x + 1) % 5] * B13; - } - out - } - - pub fn rho(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = convert_b13_lane_to_b9(a[(x, y)].clone(), ROTATION_CONSTANTS[x][y]); - } - out - } - - pub fn pi(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(y, (2 * x + 3 * y) % 5)] = a[(x, y)].clone(); - } - out - } - - pub fn xi(a: &StateBigInt) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = a[(x, y)].clone() * A1 - + a[((x + 1) % 5, y)].clone() * A2 - + a[((x + 2) % 5, y)].clone() * A3; - } - out - } - - pub fn absorb(a: &StateBigInt, next_input: &State) -> StateBigInt { - let mut out = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out[(x, y)] = a[(x, y)].clone() + convert_b2_to_b9(next_input[x][y]) * A4 - } - out - } - - pub fn iota_b9(a: &StateBigInt, rc: u64) -> StateBigInt { - let mut out = a.clone(); - out[(0, 0)] += convert_b2_to_b9(rc) * A4; - out - } - - pub fn iota_b13(a: &StateBigInt, rc: u64) -> StateBigInt { - let mut out = a.clone(); - out[(0, 0)] += convert_b2_to_b13(rc); - out - } - - pub fn mixing(a: &StateBigInt, next_input: Option<&State>, rc: u64) -> StateBigInt { - if let Some(next_input) = next_input { - let out_1 = KeccakFArith::absorb(a, next_input); - - // Base conversion from 9 to 13 - let mut out_2 = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - out_2[(x, y)] = convert_b9_lane_to_b13(out_1[(x, y)].clone()) - } - KeccakFArith::iota_b13(&out_2, rc) - } else { - KeccakFArith::iota_b9(a, rc) - } - } -} - -pub struct Keccak { - state: State, - sponge: Sponge, -} - -impl Default for Keccak { - fn default() -> Keccak { - let security_level = (1088, 512); - - Keccak { - state: [[0; 5]; 5], - // rate & capacity in bytes - sponge: Sponge::new(security_level.0 / 8, security_level.1 / 8), - } - } -} - -impl Keccak { - pub fn update(&mut self, input: &[u8]) { - let padding_total = self.sponge.rate - (input.len() % self.sponge.rate); - let mut padding: Vec; - - if padding_total == 1 { - padding = vec![0x81]; - } else { - padding = vec![0x01]; - padding.resize(padding_total - 1, 0x00); - padding.push(0x80); - } - - let padded_input: &[u8] = &[input, &padding].concat(); - self.sponge.absorb(&mut self.state, padded_input); - } - - /// Returns keccak hash based on current state - pub fn digest(&mut self) -> Vec { - self.sponge.squeeze(&mut self.state) - } -} - -struct Sponge { - rate: usize, - capacity: usize, -} - -impl Sponge { - pub fn new(rate: usize, capacity: usize) -> Sponge { - Sponge { rate, capacity } - } - - pub fn absorb(&self, state: &mut State, message: &[u8]) { - debug_assert!( - message.len() % self.rate == 0, - "Message is not divisible entirely by bytes rate" - ); - - let chunks_total = message.len() / self.rate; - - let words: Vec = Sponge::bits_to_u64_words_le(message); - - let mut state_bit_int = StateBigInt::default(); - for chunk_i in 0..chunks_total { - let chunk_offset: usize = chunk_i * (self.rate / 8); - let mut x = 0; - let mut y = 0; - let mut next_inputs = State::default(); - for i in 0..(self.rate / 8) { - next_inputs[x][y] = words[chunk_offset + i]; - if x < 5 - 1 { - x += 1; - } else { - y += 1; - x = 0; - } - } - if chunk_i == 0 { - for (x, y) in (0..5).cartesian_product(0..5) { - state_bit_int[(x, y)] = convert_b2_to_b13(next_inputs[x][y]); - } - continue; - } - KeccakFArith::permute_and_absorb(&mut state_bit_int, Some(&next_inputs)); - } - KeccakFArith::permute_and_absorb(&mut state_bit_int, None); - for (x, y) in (0..5).cartesian_product(0..5) { - state[x][y] = convert_b9_lane_to_b2(state_bit_int[(x, y)].clone()) - } - } - - pub fn squeeze(&self, state: &mut State) -> Vec { - let mut output: Vec = vec![]; - - let output_len: usize = self.capacity / 2; - let elems_total: usize = output_len / 8; - let mut counter: usize = 0; - - 'outer: for y in 0..5 { - for sheet in state.iter().take(5) { - output.append(&mut sheet[y].to_le_bytes().to_vec()); - if counter == elems_total { - break 'outer; - } - counter += 1; - } - } - - output.resize(output_len, 0); - output - } - - fn bits_to_u64_words_le(message: &[u8]) -> Vec { - let words_total = message.len() / 8; - let mut words: Vec = vec![0; words_total]; - - for i in 0..words_total { - let mut word_bits: [u8; 8] = Default::default(); - word_bits.copy_from_slice(&message[i * 8..i * 8 + 8]); - words[i] = u64::from_le_bytes(word_bits); - } - words - } -} -#[cfg(test)] -mod tests { - use crate::{ - arith_helpers::*, - keccak_arith::{Keccak, KeccakFArith, State}, - plain::KeccakF, - }; - use itertools::Itertools; - use num_bigint::BigUint; - use num_traits::Zero; - - fn keccak256(msg: &[u8]) -> Vec { - let mut keccak = Keccak::default(); - keccak.update(msg); - keccak.digest() - } - - #[test] - fn test_helpers() { - assert_eq!(convert_b2_to_b13(0b101u64), BigUint::from(170u64)); - assert_eq!(convert_b2_to_b9(0b10u64), BigUint::from(9u64)); - assert_eq!(convert_b2_to_b9(0b101u64), BigUint::from(82u64)); - assert_eq!( - convert_b13_lane_to_b9(BigUint::from(170u64), 0), - BigUint::from(82u64) - ); - assert_eq!( - convert_b9_lane_to_b13(BigUint::from(82u64)), - BigUint::zero() - ); - assert_eq!(convert_b9_lane_to_b2(BigUint::from(82u64)), 0); - assert_eq!( - convert_b9_lane_to_b2(BigUint::from(9u64).pow(63) * 2u64), - 1u64 << 63 - ); - } - - #[test] - fn test_theta_rho() { - let input1: State = [ - [1, 0, 0, 0, 0], - [0, 0, 0, 9223372036854775808, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - [0, 0, 0, 0, 0], - ]; - let input2: State = [ - [4398046511105, 8, 2, 268436480, 2305844108725321728], - [ - 17592186044416, - 52776560230400, - 544, - 68719493120, - 2199023255552, - ], - [ - 4398046543872, - 1152921504606846984, - 262144, - 1024, - 1099511627780, - ], - [0, 52776558133248, 514, 268451840, 2305845208236949504], - [17592186077184, 1152921504608944128, 262176, 68719476736, 4], - ]; - for a in [input1, input2] { - let mut in_b13 = StateBigInt::default(); - for (x, y) in (0..5).cartesian_product(0..5) { - in_b13[(x, y)] = convert_b2_to_b13(a[x][y]); - } - let s1 = KeccakF::theta(a); - let s1_arith = KeccakFArith::theta(&in_b13); - for (x, y) in (0..5).cartesian_product(0..5) { - assert_eq!( - s1[x][y], - convert_b9_lane_to_b2_normal(convert_b13_lane_to_b9( - s1_arith[(x, y)].clone(), - 0 - )) - ); - } - let s2 = KeccakF::rho(s1); - let s2_arith = KeccakFArith::rho(&s1_arith); - for (x, y) in (0..5).cartesian_product(0..5) { - let expected = convert_b9_lane_to_b2_normal(s2_arith[(x, y)].clone()); - assert_eq!(s2[x][y], expected); - } - } - } - - #[test] - fn test_empty_input_arith() { - let output = [ - 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, - 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112, - ]; - assert_eq!(keccak256(&[]), output); - } - - #[test] - fn test_short_input_arith() { - let output = [ - 56, 209, 138, 203, 103, 210, 92, 139, 185, 148, 39, 100, 182, 47, 24, 225, 112, 84, - 246, 106, 129, 123, 212, 41, 84, 35, 173, 249, 237, 152, 135, 62, - ]; - assert_eq!(keccak256(&[102, 111, 111, 98, 97, 114]), output); - } - - #[test] - fn test_long_input_arith() { - let input = [ - 65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, - 103, 32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, - 100, 32, 111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, - 114, 32, 115, 105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, - 110, 107, 44, 32, 97, 110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, - 111, 116, 104, 105, 110, 103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, - 32, 111, 114, 32, 116, 119, 105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, - 101, 101, 112, 101, 100, 32, 105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, - 107, 32, 104, 101, 114, 32, 115, 105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, - 101, 97, 100, 105, 110, 103, 44, 32, 98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, - 110, 111, 32, 112, 105, 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, - 118, 101, 114, 115, 97, 116, 105, 111, 110, 115, 32, 105, 110, 32, 105, 116, 44, 32, - 97, 110, 100, 32, 119, 104, 97, 116, 32, 105, 115, 32, 116, 104, 101, 32, 117, 115, - 101, 32, 111, 102, 32, 97, 32, 98, 111, 111, 107, 44, 32, 116, 104, 111, 117, 103, 104, - 116, 32, 65, 108, 105, 99, 101, 32, 119, 105, 116, 104, 111, 117, 116, 32, 112, 105, - 99, 116, 117, 114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, - 116, 105, 111, 110, 115, 63, - ]; - let output = [ - 60, 227, 142, 8, 143, 135, 108, 85, 13, 254, 190, 58, 30, 106, 153, 194, 188, 6, 208, - 49, 16, 102, 150, 120, 100, 130, 224, 177, 64, 98, 53, 252, - ]; - assert_eq!(keccak256(&input), output); - } -} diff --git a/keccak256/src/lib.rs b/keccak256/src/lib.rs deleted file mode 100644 index 986db13d17..0000000000 --- a/keccak256/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Leave here until #105 uses all the functions that now are -// just used in tests - -pub mod arith_helpers; -pub mod common; -pub mod gate_helpers; -// We build arith module to get test cases for the circuit -pub mod keccak_arith; -// We build plain module for the purpose of reviewing the circuit -pub mod plain; diff --git a/mock/src/account.rs b/mock/src/account.rs index 0cdda75549..87abb710e8 100644 --- a/mock/src/account.rs +++ b/mock/src/account.rs @@ -11,7 +11,7 @@ pub struct MockAccount { /// Address pub address: Address, /// nonce - pub nonce: Word, + pub nonce: u64, /// Balance pub balance: Word, /// EVM Code @@ -24,7 +24,7 @@ impl From for Account { fn from(mock: MockAccount) -> Self { Account { address: mock.address, - nonce: mock.nonce, + nonce: mock.nonce.into(), balance: mock.balance, code: mock.code, storage: mock.storage, @@ -40,7 +40,7 @@ impl MockAccount { } /// Set nonce field for the MockAccount. - pub fn nonce(&mut self, nonce: Word) -> &mut Self { + pub fn nonce(&mut self, nonce: u64) -> &mut Self { self.nonce = nonce; self } @@ -69,7 +69,7 @@ impl MockAccount { /// Set all fields for the MockAccount based on their values in `account`. pub fn account(&mut self, account: &Account) -> &mut Self { self.address(account.address); - self.nonce(account.nonce); + self.nonce(account.nonce.as_u64()); self.balance(account.balance); self.code(account.code.clone()); self.storage(account.storage.iter().map(|(k, v)| (*k, *v))); diff --git a/mock/src/test_ctx.rs b/mock/src/test_ctx.rs index 5da3907901..11b8e0c5d2 100644 --- a/mock/src/test_ctx.rs +++ b/mock/src/test_ctx.rs @@ -25,7 +25,7 @@ pub use external_tracer::LoggerConfig; /// /// ## Example /// ```rust -/// use eth_types::evm_types::{stack::Stack, Gas, OpcodeId}; +/// use eth_types::evm_types::{stack::Stack, OpcodeId}; /// use eth_types::{address, bytecode, geth_types::GethData, word, Bytecode, ToWord, Word}; /// use lazy_static::lazy_static; /// use mock::test_ctx::{helpers::*, TestContext}; @@ -66,7 +66,7 @@ pub use external_tracer::LoggerConfig; /// txs[1] /// .to(accs[1].address) /// .from(accs[2].address) -/// .nonce(Word::one()); +/// .nonce(1); /// }, /// |block, _tx| block.number(0xcafeu64), /// ) @@ -76,7 +76,7 @@ pub use external_tracer::LoggerConfig; /// // Now we can start generating the traces and items we need to inspect /// // the behaviour of the generated env. /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestContext { /// chain id pub chain_id: Word, @@ -141,10 +141,8 @@ impl TestContext { .enumerate() .skip(1) .for_each(|(idx, tx)| { - tx.transaction_idx(u64::try_from(idx).expect("Unexpected idx conversion error")); - tx.nonce(Word::from( - u64::try_from(idx).expect("Unexpected idx conversion error"), - )); + let idx = u64::try_from(idx).expect("Unexpected idx conversion error"); + tx.transaction_idx(idx).nonce(idx); }); let tx_refs = transactions.iter_mut().collect(); diff --git a/mock/src/transaction.rs b/mock/src/transaction.rs index 9bb9b00291..1b6c6ccd1f 100644 --- a/mock/src/transaction.rs +++ b/mock/src/transaction.rs @@ -23,7 +23,7 @@ lazy_static! { vec![MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[0]) - .nonce(word!("0x103")) + .nonce(0x103u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -31,7 +31,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[1]) - .nonce(word!("0x104")) + .nonce(0x104u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -39,7 +39,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[2]) - .nonce(word!("0x105")) + .nonce(0x105u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -47,7 +47,7 @@ lazy_static! { MockTransaction::default() .from(AddrOrWallet::random(&mut rng)) .to(MOCK_ACCOUNTS[0]) - .nonce(word!("0x106")) + .nonce(0x106u64) .value(word!("0x3e8")) .gas_price(word!("0x4d2")) .input(Bytes::from(b"hello")) @@ -119,7 +119,7 @@ impl AddrOrWallet { /// any of it's details. pub struct MockTransaction { pub hash: Option, - pub nonce: Word, + pub nonce: u64, pub block_hash: Hash, pub block_number: U64, pub transaction_index: U64, @@ -143,7 +143,7 @@ impl Default for MockTransaction { fn default() -> Self { MockTransaction { hash: None, - nonce: Word::zero(), + nonce: 0, block_hash: Hash::zero(), block_number: U64::zero(), transaction_index: U64::zero(), @@ -169,7 +169,7 @@ impl From for Transaction { fn from(mock: MockTransaction) -> Self { Transaction { hash: mock.hash.unwrap_or_default(), - nonce: mock.nonce, + nonce: mock.nonce.into(), block_hash: Some(mock.block_hash), block_number: Some(mock.block_number), transaction_index: Some(mock.transaction_index), @@ -207,7 +207,7 @@ impl MockTransaction { } /// Set nonce field for the MockTransaction. - pub fn nonce(&mut self, nonce: Word) -> &mut Self { + pub fn nonce(&mut self, nonce: u64) -> &mut Self { self.nonce = nonce; self } diff --git a/testool/Cargo.toml b/testool/Cargo.toml index 5b4faed255..8af5e49e12 100644 --- a/testool/Cargo.toml +++ b/testool/Cargo.toml @@ -16,7 +16,6 @@ external-tracer = { path="../external-tracer" } glob = "0.3" handlebars = "4.3" hex = "0.4.3" -keccak256 = { path = "../keccak256" } log = "0.4" mock = { path = "../mock" } once_cell = "1.10" diff --git a/testool/src/compiler.rs b/testool/src/compiler.rs index 1a45162a63..b40f8ecb0d 100644 --- a/testool/src/compiler.rs +++ b/testool/src/compiler.rs @@ -1,8 +1,7 @@ #![allow(clippy::map_entry)] use anyhow::{bail, Context, Result}; -use eth_types::{bytecode, Bytecode, Bytes, H256}; -use keccak256::plain::Keccak; +use eth_types::{bytecode, keccak256, Bytecode, Bytes, H256}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -60,9 +59,7 @@ impl Cache { } fn hash(src: &str) -> H256 { - let mut hash = Keccak::default(); - hash.update(src.as_bytes()); - H256::from_slice(&hash.digest()) + H256::from_slice(&keccak256(src.as_bytes())) } } diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 7a978934dc..4f1fd3e6c6 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -16,9 +16,6 @@ use std::{collections::HashMap, str::FromStr}; use thiserror::Error; use zkevm_circuits::{super_circuit::SuperCircuit, test_util::CircuitTestBuilder, witness::Block}; -const MAX_TXS: usize = 1; -const MAX_CALLDATA: usize = 32; - #[derive(PartialEq, Eq, Error, Debug)] pub enum StateTestError { #[error("CannotGenerateCircuitInput({0})")] @@ -26,7 +23,7 @@ pub enum StateTestError { #[error("BalanceMismatch(expected:{expected:?}, found:{found:?})")] BalanceMismatch { expected: U256, found: U256 }, #[error("NonceMismatch(expected:{expected:?}, found:{found:?})")] - NonceMismatch { expected: U256, found: U256 }, + NonceMismatch { expected: u64, found: u64 }, #[error("CodeMismatch(expected: {expected:?}, found:{found:?})")] CodeMismatch { expected: Bytes, found: Bytes }, #[error("StorgeMismatch(slot:{slot:?} expected:{expected:?}, found: {found:?})")] @@ -142,9 +139,9 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { transactions: vec![geth_types::Transaction { from: st.from, to: st.to, - nonce: st.nonce, + nonce: U64::from(st.nonce), value: st.value, - gas_limit: st.gas_limit.into(), + gas_limit: U64::from(st.gas_limit), gas_price: st.gas_price, gas_fee_cap: U256::zero(), gas_tip_cap: U256::zero(), @@ -204,8 +201,8 @@ pub fn run_test( )); } - if suite.max_gas > 0 && geth_traces[0].gas.0 > suite.max_gas { - return Err(StateTestError::SkipTestMaxGasLimit(geth_traces[0].gas.0)); + if suite.max_gas > 0 && geth_traces[0].gas > suite.max_gas { + return Err(StateTestError::SkipTestMaxGasLimit(geth_traces[0].gas)); } let transactions = trace_config @@ -277,8 +274,8 @@ pub fn run_test( geth_data.sign(&wallets); let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -287,8 +284,7 @@ pub fn run_test( max_keccak_rows: 0, }; let (k, circuit, instance, _builder) = - SuperCircuit::::build(geth_data, circuits_params) - .unwrap(); + SuperCircuit::::build(geth_data, circuits_params, Fr::from(0x100)).unwrap(); builder = _builder; let prover = MockProver::run(k, &circuit, instance).unwrap(); diff --git a/testool/src/statetest/json.rs b/testool/src/statetest/json.rs index a083077a8f..7bd35e26b7 100644 --- a/testool/src/statetest/json.rs +++ b/testool/src/statetest/json.rs @@ -1,16 +1,13 @@ -#![allow(dead_code, unused_imports)] - use super::{ parse, spec::{AccountMatch, Env, StateTest}, }; -use crate::{abi, compiler::Compiler, utils::MainnetFork}; -use anyhow::{bail, Context, Result}; -use eth_types::{evm_types::OpcodeId, geth_types::Account, Address, Bytes, H256, U256}; +use crate::{compiler::Compiler, utils::MainnetFork}; +use anyhow::{bail, Result}; +use eth_types::{geth_types::Account, Address, U256}; use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use serde::Deserialize; -use std::{collections::HashMap, convert::TryInto, ops::RangeBounds, str::FromStr}; -use yaml_rust::Yaml; +use std::collections::HashMap; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -36,7 +33,6 @@ struct AccountPost { code: Option, nonce: Option, storage: Option>, - shouldnotexist: Option, } #[derive(Debug, Clone, Deserialize)] @@ -112,7 +108,7 @@ impl<'a> JsonStateTestBuilder<'a> { let to = parse::parse_to_address(&test.transaction.to)?; let secret_key = parse::parse_bytes(&test.transaction.secret_key)?; let from = secret_key_to_address(&SigningKey::from_bytes(&secret_key.to_vec())?); - let nonce = parse::parse_u256(&test.transaction.nonce)?; + let nonce = parse::parse_u64(&test.transaction.nonce)?; let gas_price = parse::parse_u256(&test.transaction.gas_price)?; let data_s: Vec<_> = test @@ -219,7 +215,7 @@ impl<'a> JsonStateTestBuilder<'a> { let account = Account { address, balance: parse::parse_u256(&acc.balance)?, - nonce: parse::parse_u256(&acc.nonce)?, + nonce: parse::parse_u64(&acc.nonce)?.into(), code: parse::parse_code(self.compiler, &acc.code)?, storage, }; @@ -257,7 +253,7 @@ impl<'a> JsonStateTestBuilder<'a> { nonce: acc .nonce .as_ref() - .map(|v| parse::parse_u256(v)) + .map(|v| parse::parse_u64(v)) .transpose()?, storage, }; @@ -298,6 +294,8 @@ impl<'a> JsonStateTestBuilder<'a> { #[cfg(test)] mod test { use super::*; + use eth_types::{Bytes, H256}; + use std::{collections::HashMap, str::FromStr}; const JSON: &str = r#" { @@ -390,24 +388,23 @@ mod test { )?), gas_limit: 400000, gas_price: U256::from(10u64), - nonce: U256::from(0u64), + nonce: 0, value: U256::from(100000u64), data: Bytes::from(hex::decode("6001")?), pre: HashMap::from([( acc095e, Account { address: acc095e, - nonce: U256::from(0u64), balance: U256::from(1000000000000000000u64), code: Bytes::from(hex::decode("600160010160005500")?), - storage: HashMap::new(), + ..Default::default() }, )]), result: HashMap::from([( acc095e, AccountMatch { address: acc095e, - nonce: Some(U256::from(1u64)), + nonce: Some(1u64), balance: None, code: Some(Bytes::from(hex::decode("600160010160005500")?)), storage: HashMap::from([(U256::zero(), U256::from(2u64))]), diff --git a/testool/src/statetest/spec.rs b/testool/src/statetest/spec.rs index b2d224a73f..234867ccda 100644 --- a/testool/src/statetest/spec.rs +++ b/testool/src/statetest/spec.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, bail, Context}; -use eth_types::{geth_types::Account, Address, Bytes, Word, H256, U256}; +use eth_types::{geth_types::Account, Address, Bytes, Word, H256, U256, U64}; use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use std::{collections::HashMap, str::FromStr}; @@ -18,7 +18,7 @@ pub struct AccountMatch { pub address: Address, pub balance: Option, pub code: Option, - pub nonce: Option, + pub nonce: Option, pub storage: HashMap, } @@ -29,7 +29,7 @@ impl TryInto for AccountMatch { address: self.address, balance: self.balance.context("balance")?, code: self.code.context("code")?, - nonce: self.nonce.context("nonce")?, + nonce: self.nonce.context("nonce")?.into(), storage: self.storage, }) } @@ -47,7 +47,7 @@ pub struct StateTest { pub to: Option
, pub gas_limit: u64, pub gas_price: U256, - pub nonce: U256, + pub nonce: u64, pub value: U256, pub data: Bytes, pub pre: HashMap, @@ -205,10 +205,8 @@ impl StateTest { from, Account { address: from, - nonce: U256::zero(), balance: U256::from(10).pow(18.into()), - code: Bytes::default(), - storage: HashMap::new(), + ..Default::default() }, ); @@ -242,7 +240,7 @@ impl StateTest { address, Account { address, - nonce: U256::one(), + nonce: U64::one(), code: Bytes::from(code.code()), balance, storage, @@ -250,11 +248,20 @@ impl StateTest { ); } + pre.insert( + *mock::MOCK_COINBASE, + Account { + address: *mock::MOCK_COINBASE, + balance: U256::from(1), + ..Default::default() + }, + ); + let state_test = StateTest { path: String::default(), id: String::default(), env: Env { - current_coinbase: Address::default(), + current_coinbase: *mock::MOCK_COINBASE, current_difficulty: U256::default(), current_gas_limit: 16000000, current_number: 1, @@ -266,7 +273,7 @@ impl StateTest { to, gas_limit, gas_price: U256::one(), - nonce: U256::zero(), + nonce: 0, value, data: data.into(), pre, diff --git a/testool/src/statetest/yaml.rs b/testool/src/statetest/yaml.rs index 20b8c4cd7c..28bee90c17 100644 --- a/testool/src/statetest/yaml.rs +++ b/testool/src/statetest/yaml.rs @@ -104,7 +104,7 @@ impl<'a> YamlStateTestBuilder<'a> { Self::parse_u256(&yaml_transaction["gasPrice"]).unwrap_or_else(|_| U256::one()); // TODO handle maxPriorityFeePerGas & maxFeePerGas - let nonce = Self::parse_u256(&yaml_transaction["nonce"])?; + let nonce = Self::parse_u64(&yaml_transaction["nonce"])?; let to = Self::parse_to_address(&yaml_transaction["to"])?; let secret_key = Self::parse_bytes(&yaml_transaction["secretKey"])?; let from = secret_key_to_address(&SigningKey::from_bytes(&secret_key.to_vec())?); @@ -244,7 +244,7 @@ impl<'a> YamlStateTestBuilder<'a> { nonce: if acc_nonce.is_badvalue() { None } else { - Some(Self::parse_u256(acc_nonce)?) + Some(Self::parse_u64(acc_nonce)?) }, storage, }; @@ -615,7 +615,7 @@ arith: to: Some(ccccc), gas_limit: 80000000, gas_price: U256::from(10u64), - nonce: U256::zero(), + nonce: 0, value: U256::one(), data: Bytes::from(&[0]), pre: HashMap::from([ @@ -625,9 +625,8 @@ arith: address: ccccc, balance: U256::from(1000000000000u64), code: Bytes::from(&[0x60, 0x01, 0x00]), - nonce: U256::zero(), - storage: HashMap::from([(U256::zero(), U256::one())]), + ..Default::default() }, ), ( @@ -635,10 +634,7 @@ arith: Account { address: a94f5, balance: U256::from(1000000000000u64), - code: Bytes::default(), - nonce: U256::zero(), - - storage: HashMap::new(), + ..Default::default() }, ), ]), @@ -647,7 +643,7 @@ arith: AccountMatch { address: ccccc, balance: Some(U256::from(1000000000001u64)), - nonce: Some(U256::from(0)), + nonce: Some(0), code: Some(Bytes::from(&[0x60, 0x01, 0x00])), storage: HashMap::from([(U256::zero(), U256::one())]), }, @@ -762,8 +758,8 @@ arith: CircuitsConfig::default() ), Err(StateTestError::NonceMismatch { - expected: U256::from(2), - found: U256::from(0) + expected: 2, + found: 0 }) ); diff --git a/testool/src/utils.rs b/testool/src/utils.rs index 6d8604d973..a147684a46 100644 --- a/testool/src/utils.rs +++ b/testool/src/utils.rs @@ -125,10 +125,10 @@ pub fn print_trace(trace: GethExecTrace) -> Result<()> { ]); for step in trace.struct_logs { table.add_row(row![ - format!("{}", step.pc.0), + format!("{}", step.pc), format!("{:?}", step.op), - format!("{}", step.gas.0), - format!("{}", step.gas_cost.0), + format!("{}", step.gas), + format!("{}", step.gas_cost), format!("{}", step.depth), step.error.unwrap_or_default(), split(step.stack.0.iter().map(u256_to_str).collect(), 30), diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 846dbc73c9..b20410557a 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v2023_04_20" } +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", features = ["circuit-params"], tag = "v2023_04_20" } num = "0.4" sha3 = "0.10" array-init = "2.0.0" @@ -24,7 +24,6 @@ rand_xorshift = "0.3" rand = "0.8" itertools = "0.10.3" lazy_static = "1.4" -keccak256 = { path = "../keccak256"} log = "0.4" env_logger = "0.9" ecdsa = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } @@ -33,20 +32,18 @@ maingate = { git = "https://github.com/privacy-scaling-explorations/halo2wrong" integer = { git = "https://github.com/privacy-scaling-explorations/halo2wrong", tag = "v2023_04_20" } libsecp256k1 = "0.7" num-bigint = { version = "0.4" } -subtle = "2.4" rand_chacha = "0.3" snark-verifier = { git = "https://github.com/privacy-scaling-explorations/snark-verifier", tag = "v2023_04_20", default-features = false, features = ["loader_halo2", "system_halo2"] } +cli-table = { version = "0.4", optional = true } [dev-dependencies] bus-mapping = { path = "../bus-mapping", features = ["test"] } -criterion = "0.3" ctor = "0.1.22" ethers-signers = "0.17.0" hex = "0.4.3" itertools = "0.10.1" mock = { path = "../mock" } pretty_assertions = "1.0.0" -cli-table = "0.4" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.78" @@ -55,3 +52,8 @@ default = [] test = ["ethers-signers", "mock", "bus-mapping/test"] test-circuits = [] warn-unimplemented = ["eth-types/warn-unimplemented"] +stats = ["warn-unimplemented", "dep:cli-table"] + +[[bin]] +name = "stats" +required-features = ["stats"] diff --git a/zkevm-circuits/src/stats.rs b/zkevm-circuits/src/bin/stats/helpers.rs similarity index 78% rename from zkevm-circuits/src/stats.rs rename to zkevm-circuits/src/bin/stats/helpers.rs index ed43527497..29b760ae3e 100644 --- a/zkevm-circuits/src/stats.rs +++ b/zkevm-circuits/src/bin/stats/helpers.rs @@ -1,61 +1,17 @@ use std::cmp::Ordering; -use crate::evm_circuit::step::ExecutionState; use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, ExecState}, mock::BlockData, }; +use cli_table::{ + format::{Justify, Separator}, + print_stdout, Table, WithTitle, +}; use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, Address, Bytecode, ToWord}; use mock::{eth, test_ctx::TestContext, MOCK_ACCOUNTS}; use strum::IntoEnumIterator; - -/// Helper type to print formatted tables in MarkDown -pub(crate) struct DisplayTable { - header: [String; N], - rows: Vec<[String; N]>, -} - -impl DisplayTable { - pub(crate) fn new(header: [String; N]) -> Self { - Self { - header, - rows: Vec::new(), - } - } - fn push_row(&mut self, row: [String; N]) { - self.rows.push(row) - } - fn print_row(row: &[String; N], rows_width: &[usize; N]) { - for (i, h) in row.iter().enumerate() { - if i == 0 { - print!("|"); - } - print!(" {:width$} |", h, width = rows_width[i]); - } - println!(); - } - pub(crate) fn print(&self) { - let mut rows_width = [0; N]; - for row in std::iter::once(&self.header).chain(self.rows.iter()) { - for (i, s) in row.iter().enumerate() { - if s.len() > rows_width[i] { - rows_width[i] = s.len(); - } - } - } - Self::print_row(&self.header, &rows_width); - for (i, width) in rows_width.iter().enumerate() { - if i == 0 { - print!("|"); - } - print!(" {:- Bytecode { @@ -106,12 +62,34 @@ pub(crate) fn bytecode_prefix_op_big_rws(opcode: OpcodeId) -> Bytecode { } } +/// Wrap f64 for both sorting and pretty formatting +#[derive(PartialEq, PartialOrd)] +struct PrettyF64(f64); + +impl std::fmt::Display for PrettyF64 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:1.3}", self.0) + } +} + +impl From for PrettyF64 { + fn from(value: f64) -> Self { + Self(value) + } +} + +#[derive(Table)] struct Row { + #[table(title = "Execution State")] state: ExecutionState, + #[table(title = "Opcode")] opcode: OpcodeId, + #[table(title = "Height", justify = "Justify::Right")] height: usize, + #[table(title = "Gas Cost", justify = "Justify::Right")] gas_cost: u64, - height_per_gas: f64, + #[table(title = "Height per Gas", justify = "Justify::Right")] + height_per_gas: PrettyF64, } /// This function prints to stdout a table with all the implemented states @@ -161,7 +139,6 @@ pub(crate) fn print_circuit_stats_by_states( STOP }; - let mut table = DisplayTable::new(["state", "opcode", "h", "g", "h/g"].map(|s| s.into())); let mut rows = vec![]; for state in implemented_states { if !fn_filter(state) { @@ -236,7 +213,7 @@ pub(crate) fn print_circuit_stats_by_states( .steps() .iter() .enumerate() - .find(|(_, s)| s.call_index == 1 && s.pc.0 == opcode_pc) + .find(|(_, s)| s.call_index == 1 && s.pc == (opcode_pc as u64)) .unwrap(); assert_eq!(ExecState::Op(opcode), step.exec_state); let height = fn_height(&builder.block, state, step_index); @@ -245,13 +222,13 @@ pub(crate) fn print_circuit_stats_by_states( // in the geth trace. let geth_step = &block.geth_traces[0].struct_logs[step_index - 1]; assert_eq!(opcode, geth_step.op); - let gas_cost = geth_step.gas_cost.0; + let gas_cost = geth_step.gas_cost; rows.push(Row { state, opcode, height, gas_cost, - height_per_gas: height as f64 / gas_cost as f64, + height_per_gas: (height as f64 / gas_cost as f64).into(), }); } } @@ -261,16 +238,6 @@ pub(crate) fn print_circuit_stats_by_states( .unwrap_or(Ordering::Greater) }); - for row in rows.iter() { - let row = [ - format!("{:?}", row.state), - format!("{:?}", row.opcode), - format!("{}", row.height), - format!("{}", row.gas_cost), - format!("{:1.3}", row.height_per_gas), - ]; - table.push_row(row); - } - - table.print(); + print_stdout(rows.with_title().separator(Separator::builder().build())) + .expect("the table renders"); } diff --git a/zkevm-circuits/src/bin/stats/main.rs b/zkevm-circuits/src/bin/stats/main.rs new file mode 100644 index 0000000000..30c8fc9a27 --- /dev/null +++ b/zkevm-circuits/src/bin/stats/main.rs @@ -0,0 +1,185 @@ +use cli_table::{print_stdout, Cell, Style, Table}; +use eth_types::{bytecode, evm_types::OpcodeId, ToWord}; +use halo2_proofs::{ + halo2curves::bn256::Fr, + plonk::{Circuit, ConstraintSystem}, +}; +mod helpers; +use helpers::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}; +use itertools::Itertools; +use mock::MOCK_ACCOUNTS; +use std::env; +use zkevm_circuits::evm_circuit::{ + param::{LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS}, + step::ExecutionState, + EvmCircuit, +}; +fn main() { + let args: Vec = env::args().collect(); + + match &args[1][..] { + "evm" => evm_states_stats(), + "state" => state_states_stats(), + "copy" => copy_states_stats(), + "exec" => get_exec_steps_occupancy(), + &_ => unreachable!("Unsupported arg"), + } +} + +/// Prints the stats of EVM circuit per execution state. +fn evm_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + !matches!( + state, + ExecutionState::ErrorInvalidOpcode + | ExecutionState::CREATE + | ExecutionState::CREATE2 + | ExecutionState::SELFDESTRUCT + ) + }, + |opcode| match opcode { + OpcodeId::RETURNDATACOPY => { + bytecode! { + PUSH1(0x00) // retLength + PUSH1(0x00) // retOffset + PUSH1(0x00) // argsLength + PUSH1(0x00) // argsOffset + PUSH1(0x00) // value + PUSH32(MOCK_ACCOUNTS[3].to_word()) + PUSH32(0x1_0000) // gas + CALL + PUSH2(0x01) // size + PUSH2(0x00) // offset + PUSH2(0x00) // destOffset + } + } + _ => bytecode! { + PUSH2(0x40) + PUSH2(0x50) + }, + }, + |_, state, _| state.get_step_height_option().unwrap(), + ); +} + +/// Prints the stats of State circuit per execution state. +fn state_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + !matches!( + state, + ExecutionState::ErrorInvalidOpcode + | ExecutionState::CREATE + | ExecutionState::CREATE2 + | ExecutionState::SELFDESTRUCT + ) + }, + bytecode_prefix_op_big_rws, + |block, _, step_index| { + let step = &block.txs[0].steps()[step_index]; + let step_next = &block.txs[0].steps()[step_index + 1]; + step_next.rwc.0 - step.rwc.0 + }, + ); +} + +/// Prints the stats of Copy circuit per execution state. +fn copy_states_stats() { + print_circuit_stats_by_states( + |state| { + // TODO: Enable CREATE/CREATE2 once they are supported + matches!( + state, + ExecutionState::RETURNDATACOPY + | ExecutionState::CODECOPY + | ExecutionState::LOG + | ExecutionState::CALLDATACOPY + | ExecutionState::EXTCODECOPY + | ExecutionState::RETURN_REVERT + ) + }, + bytecode_prefix_op_big_rws, + |block, _, _| { + assert!(block.copy_events.len() <= 1); + block + .copy_events + .iter() + .map(|c| c.bytes.len() * 2) + .sum::() + }, + ); +} + +/// This function prints to stdout a table with the top X ExecutionState +/// cell consumers of each EVM Cell type. +fn get_exec_steps_occupancy() { + let mut meta = ConstraintSystem::::default(); + let circuit = EvmCircuit::configure(&mut meta); + + let report = circuit.0.execution.instrument().clone().analyze(); + macro_rules! gen_report { + ($report:expr, $($id:ident, $cols:expr), +) => { + $( + let row_report = report + .iter() + .sorted_by(|a, b| a.$id.utilization.partial_cmp(&b.$id.utilization).unwrap()) + .rev() + .take(10) + .map(|exec| { + vec![ + format!("{:?}", exec.state), + format!("{:?}", exec.$id.available_cells), + format!("{:?}", exec.$id.unused_cells), + format!("{:?}", exec.$id.used_cells), + format!("{:?}", exec.$id.top_height), + format!("{:?}", exec.$id.used_columns), + format!("{:?}", exec.$id.utilization), + ] + }) + .collect::>>(); + + let table = row_report.table().title(vec![ + format!("{:?}", stringify!($id)).cell().bold(true), + format!("total_available_cells").cell().bold(true), + format!("unused_cells").cell().bold(true), + format!("cells").cell().bold(true), + format!("top_height").cell().bold(true), + format!("used columns (Max: {:?})", $cols).cell().bold(true), + format!("Utilization").cell().bold(true), + ]); + print_stdout(table).unwrap(); + )* + }; + } + + gen_report!( + report, + storage_1, + N_PHASE1_COLUMNS, + storage_2, + N_PHASE2_COLUMNS, + storage_perm, + N_COPY_COLUMNS, + byte_lookup, + N_BYTE_LOOKUPS, + fixed_table, + LOOKUP_CONFIG[0].1, + tx_table, + LOOKUP_CONFIG[1].1, + rw_table, + LOOKUP_CONFIG[2].1, + bytecode_table, + LOOKUP_CONFIG[3].1, + block_table, + LOOKUP_CONFIG[4].1, + copy_table, + LOOKUP_CONFIG[5].1, + keccak_table, + LOOKUP_CONFIG[6].1, + exp_table, + LOOKUP_CONFIG[7].1 + ); +} diff --git a/zkevm-circuits/src/bytecode_circuit/circuit.rs b/zkevm-circuits/src/bytecode_circuit/circuit.rs index cd70f56a42..d0540d3126 100644 --- a/zkevm-circuits/src/bytecode_circuit/circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit/circuit.rs @@ -10,7 +10,7 @@ use crate::{ }; use bus_mapping::state_db::EMPTY_CODE_HASH_LE; use eth_types::{Field, ToLittleEndian}; -use gadgets::is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}; +use gadgets::is_zero::{IsZeroChip, IsZeroInstruction}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{ @@ -26,6 +26,43 @@ use super::{ param::PUSH_TABLE_WIDTH, }; +#[derive(Debug, Clone, Default)] +/// Row for assignment +pub struct BytecodeCircuitRow { + offset: usize, + last_row_offset: usize, + code_hash: Value, + tag: F, + index: F, + is_code: F, + value: F, + push_data_left: u64, + value_rlc: Value, + length: F, + push_data_size: F, +} +impl BytecodeCircuitRow { + /// enable selector if we are within the range of table size. + pub fn enable(&self) -> bool { + self.offset <= self.last_row_offset + } + + /// Determine if we are at last row of the bytecode table. + pub fn last(&self) -> bool { + self.offset == self.last_row_offset + } + + /// Get offset + pub fn offset(&self) -> usize { + self.offset + } + + /// Witness to IsZero chip to determine if we are at the last row of a bytecode instance + pub fn diff(&self) -> F { + self.index + F::ONE - self.length + } +} + #[derive(Clone, Debug)] /// Bytecode circuit configuration pub struct BytecodeCircuitConfig { @@ -38,10 +75,8 @@ pub struct BytecodeCircuitConfig { value_rlc: Column, length: Column, push_data_size: Column, - push_data_left_inv: Column, - push_data_left_is_zero: IsZeroConfig, - index_length_diff_inv: Column, - index_length_diff_is_zero: IsZeroConfig, + push_data_left_is_zero: IsZeroChip, + index_length_diff_is_zero: IsZeroChip, push_table: [Column; PUSH_TABLE_WIDTH], // External tables pub(crate) keccak_table: KeccakTable, @@ -141,7 +176,6 @@ impl SubCircuitConfig for BytecodeCircuitConfig { }, index_length_diff_inv, ); - // dbg!(index_length_diff_is_zero.clone().is_zero_expression); // When q_first || q_last -> // assert cur.tag == Header @@ -419,6 +453,9 @@ impl SubCircuitConfig for BytecodeCircuitConfig { }, ); + let push_data_left_is_zero = IsZeroChip::construct(push_data_left_is_zero); + let index_length_diff_is_zero = IsZeroChip::construct(index_length_diff_is_zero); + BytecodeCircuitConfig { minimum_rows: meta.minimum_rows(), q_enable, @@ -429,9 +466,7 @@ impl SubCircuitConfig for BytecodeCircuitConfig { value_rlc, length, push_data_size, - push_data_left_inv, push_data_left_is_zero, - index_length_diff_inv, index_length_diff_is_zero, push_table, keccak_table, @@ -440,17 +475,6 @@ impl SubCircuitConfig for BytecodeCircuitConfig { } impl BytecodeCircuitConfig { - pub(crate) fn assign( - &self, - layouter: &mut impl Layouter, - size: usize, - witness: &[UnrolledBytecode], - overwrite: &UnrolledBytecode, - challenges: &Challenges>, - ) -> Result<(), Error> { - self.assign_internal(layouter, size, witness, overwrite, challenges, true) - } - pub(crate) fn assign_internal( &self, layouter: &mut impl Layouter, @@ -460,11 +484,6 @@ impl BytecodeCircuitConfig { challenges: &Challenges>, fail_fast: bool, ) -> Result<(), Error> { - let push_data_left_is_zero_chip = - IsZeroChip::construct(self.push_data_left_is_zero.clone()); - let index_length_diff_is_zero_chip = - IsZeroChip::construct(self.index_length_diff_is_zero.clone()); - // Subtract the unusable rows from the size assert!(size > self.minimum_rows); let last_row_offset = size - self.minimum_rows + 1; @@ -476,10 +495,6 @@ impl BytecodeCircuitConfig { last_row_offset ); - let empty_hash = challenges - .evm_word() - .map(|challenge| rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge)); - layouter.assign_region( || "assign bytecode", |mut region| { @@ -492,9 +507,6 @@ impl BytecodeCircuitConfig { &mut region, bytecode, challenges, - &push_data_left_is_zero_chip, - &index_length_diff_is_zero_chip, - empty_hash, &mut offset, last_row_offset, fail_fast, @@ -503,14 +515,7 @@ impl BytecodeCircuitConfig { // Padding for idx in offset..=last_row_offset { - self.set_padding_row( - &mut region, - &push_data_left_is_zero_chip, - &index_length_diff_is_zero_chip, - empty_hash, - idx, - last_row_offset, - )?; + self.set_padding_row(&mut region, challenges, idx, last_row_offset)?; } // Overwrite the witness assignment by using the values in the `overwrite` @@ -563,15 +568,11 @@ impl BytecodeCircuitConfig { ) } - #[allow(clippy::too_many_arguments)] fn assign_bytecode( &self, region: &mut Region<'_, F>, bytecode: &UnrolledBytecode, challenges: &Challenges>, - push_data_left_is_zero_chip: &IsZeroChip, - index_length_diff_is_zero_chip: &IsZeroChip, - empty_hash: Value, offset: &mut usize, last_row_offset: usize, fail_fast: bool, @@ -620,51 +621,28 @@ impl BytecodeCircuitConfig { // Set the data for this row if *offset < last_row_offset { - self.set_row( - region, - push_data_left_is_zero_chip, - index_length_diff_is_zero_chip, - *offset, - true, - *offset == last_row_offset, + let row = BytecodeCircuitRow { + offset: *offset, + last_row_offset, code_hash, - row.tag, - row.index, - row.is_code, - row.value, + tag: row.tag, + index: row.index, + is_code: row.is_code, + value: row.value, push_data_left, value_rlc, length, - F::from(push_data_size), - )?; + push_data_size: F::from(push_data_size), + }; + self.set_row(region, row.clone())?; - trace!( - "bytecode.set_row({}): last:{} h:{:?} t:{:?} i:{:?} c:{:?} v:{:?} pdl:{} rlc:{:?} l:{:?} pds:{:?}", - offset, - *offset == last_row_offset, - code_hash, - row.tag.get_lower_32(), - row.index.get_lower_32(), - row.is_code.get_lower_32(), - row.value.get_lower_32(), - push_data_left, - value_rlc, - length.get_lower_32(), - push_data_size - ); + trace!("bytecode.set_row({:?})", row); *offset += 1; push_data_left = next_push_data_left } if *offset == last_row_offset { - self.set_padding_row( - region, - push_data_left_is_zero_chip, - index_length_diff_is_zero_chip, - empty_hash, - *offset, - last_row_offset, - )?; + self.set_padding_row(region, challenges, *offset, last_row_offset)?; } } @@ -674,56 +652,35 @@ impl BytecodeCircuitConfig { fn set_padding_row( &self, region: &mut Region<'_, F>, - push_data_left_is_zero_chip: &IsZeroChip, - index_length_diff_is_zero_chip: &IsZeroChip, - empty_hash: Value, + challenges: &Challenges>, offset: usize, last_row_offset: usize, ) -> Result<(), Error> { + let empty_hash = challenges + .evm_word() + .map(|challenge| rlc::value(EMPTY_CODE_HASH_LE.as_ref(), challenge)); + self.set_row( region, - push_data_left_is_zero_chip, - index_length_diff_is_zero_chip, - offset, - offset <= last_row_offset, - offset == last_row_offset, - empty_hash, - F::from(BytecodeFieldTag::Header as u64), - F::ZERO, - F::ZERO, - F::ZERO, - 0, - Value::known(F::ZERO), - F::ZERO, - F::ZERO, + BytecodeCircuitRow { + offset, + last_row_offset, + code_hash: empty_hash, + tag: F::from(BytecodeFieldTag::Header as u64), + value_rlc: Value::known(F::ZERO), + ..Default::default() + }, ) } - #[allow(clippy::too_many_arguments)] - fn set_row( - &self, - region: &mut Region<'_, F>, - push_data_left_is_zero_chip: &IsZeroChip, - index_length_diff_is_zero_chip: &IsZeroChip, - offset: usize, - enable: bool, - last: bool, - code_hash: Value, - tag: F, - index: F, - is_code: F, - value: F, - push_data_left: u64, - value_rlc: Value, - length: F, - push_data_size: F, - ) -> Result<(), Error> { + fn set_row(&self, region: &mut Region<'_, F>, row: BytecodeCircuitRow) -> Result<(), Error> { + let offset = row.offset; // q_enable region.assign_fixed( || format!("assign q_enable {}", offset), self.q_enable, offset, - || Value::known(F::from(enable as u64)), + || Value::known(F::from(row.enable().into())), )?; // q_first @@ -731,31 +688,30 @@ impl BytecodeCircuitConfig { || format!("assign q_first {}", offset), self.q_first, offset, - || Value::known(F::from((offset == 0) as u64)), + || Value::known(F::from((offset == 0).into())), )?; // q_last - let q_last_value = if last { F::ONE } else { F::ZERO }; region.assign_fixed( || format!("assign q_last {}", offset), self.q_last, offset, - || Value::known(q_last_value), + || Value::known(F::from(row.last().into())), )?; // Advices for (name, column, value) in [ - ("tag", self.bytecode_table.tag, tag), - ("index", self.bytecode_table.index, index), - ("is_code", self.bytecode_table.is_code, is_code), - ("value", self.bytecode_table.value, value), + ("tag", self.bytecode_table.tag, row.tag), + ("index", self.bytecode_table.index, row.index), + ("is_code", self.bytecode_table.is_code, row.is_code), + ("value", self.bytecode_table.value, row.value), ( "push_data_left", self.push_data_left, - F::from(push_data_left), + F::from(row.push_data_left), ), - ("length", self.length, length), - ("push_data_size", self.push_data_size, push_data_size), + ("length", self.length, row.length), + ("push_data_size", self.push_data_size, row.push_data_size), ] { region.assign_advice( || format!("assign {} {}", name, offset), @@ -765,8 +721,8 @@ impl BytecodeCircuitConfig { )?; } for (name, column, value) in [ - ("code_hash", self.bytecode_table.code_hash, code_hash), - ("value_rlc", self.value_rlc, value_rlc), + ("code_hash", self.bytecode_table.code_hash, row.code_hash), + ("value_rlc", self.value_rlc, row.value_rlc), ] { region.assign_advice( || format!("assign {} {}", name, offset), @@ -776,17 +732,14 @@ impl BytecodeCircuitConfig { )?; } - push_data_left_is_zero_chip.assign( + self.push_data_left_is_zero.assign( region, offset, - Value::known(F::from(push_data_left)), + Value::known(F::from(row.push_data_left)), )?; - index_length_diff_is_zero_chip.assign( - region, - offset, - Value::known(index + F::ONE - length), - )?; + self.index_length_diff_is_zero + .assign(region, offset, Value::known(row.diff()))?; Ok(()) } @@ -806,11 +759,6 @@ impl BytecodeCircuitConfig { region.name_column(|| "BYTECODE_push_data_left", self.push_data_left); region.name_column(|| "BYTECODE_push_data_size", self.push_data_size); region.name_column(|| "BYTECODE_value_rlc", self.value_rlc); - region.name_column(|| "BYTECODE_push_data_left_inv", self.push_data_left_inv); - region.name_column( - || "BYTECODE_index_length_diff_inv", - self.index_length_diff_inv, - ); } /// load fixed tables diff --git a/zkevm-circuits/src/bytecode_circuit/dev.rs b/zkevm-circuits/src/bytecode_circuit/dev.rs index dd33c563ab..178aecc90d 100644 --- a/zkevm-circuits/src/bytecode_circuit/dev.rs +++ b/zkevm-circuits/src/bytecode_circuit/dev.rs @@ -14,6 +14,7 @@ use halo2_proofs::{ impl Circuit for BytecodeCircuit { type Config = (BytecodeCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/bytecode_circuit/test.rs b/zkevm-circuits/src/bytecode_circuit/test.rs index 8ebed96b0b..c765aec905 100644 --- a/zkevm-circuits/src/bytecode_circuit/test.rs +++ b/zkevm-circuits/src/bytecode_circuit/test.rs @@ -13,7 +13,7 @@ use log::error; fn bytecode_circuit_unusable_rows() { assert_eq!( BytecodeCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 25ce782de4..a0d2cf0f91 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -5,12 +5,15 @@ pub(crate) mod util; #[cfg(any(feature = "test", test, feature = "test-circuits"))] mod dev; -#[cfg(any(feature = "test", test))] +#[cfg(test)] mod test; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use dev::CopyCircuit as TestCopyCircuit; -use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent}; +use bus_mapping::{ + circuit_input_builder::{CopyDataType, CopyEvent}, + operation::Target, +}; use eth_types::{Field, Word}; use gadgets::{ @@ -29,8 +32,8 @@ use std::{collections::HashMap, marker::PhantomData}; use crate::{ evm_circuit::util::constraint_builder::{BaseConstraintBuilder, ConstrainBuilderCommon}, table::{ - BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, RwTableTag, - TxContextFieldTag, TxTable, + BytecodeFieldTag, BytecodeTable, CopyTable, LookupTable, RwTable, TxContextFieldTag, + TxTable, }, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, @@ -47,6 +50,8 @@ pub struct CopyCircuitConfig { pub is_last: Column, /// The value copied in this copy step. pub value: Column, + /// Random linear combination accumulator value. + pub value_acc_rlc: Column, /// Whether the row is padding. pub is_pad: Column, /// In case of a bytecode tag, this denotes whether or not the copied byte @@ -104,7 +109,8 @@ impl SubCircuitConfig for CopyCircuitConfig { ) -> Self { let q_step = meta.complex_selector(); let is_last = meta.advice_column(); - let value = meta.advice_column_in(SecondPhase); + let value = meta.advice_column(); + let value_acc_rlc = meta.advice_column_in(SecondPhase); let is_code = meta.advice_column(); let is_pad = meta.advice_column(); let is_first = copy_table.is_first; @@ -222,23 +228,36 @@ impl SubCircuitConfig for CopyCircuitConfig { rw_diff, ); }); - cb.condition( - and::expr([ - meta.query_advice(is_last, Rotation::cur()), - tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta), - ]), - |cb| { - cb.require_equal( - "value == rlc_acc at the last row for RlcAcc", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(rlc_acc, Rotation::cur()), - ); - }, - ); cb.gate(meta.query_fixed(q_enable, Rotation::cur())) }); + meta.create_gate( + "Last Step (check value accumulator) Memory => Bytecode or RlcAcc", + |meta: &mut halo2_proofs::plonk::VirtualCells| { + let mut cb = BaseConstraintBuilder::default(); + + cb.require_equal( + "value_acc_rlc == rlc_acc on the last row", + meta.query_advice(value_acc_rlc, Rotation::next()), + meta.query_advice(rlc_acc, Rotation::next()), + ); + + cb.gate(and::expr([ + meta.query_fixed(q_enable, Rotation::cur()), + meta.query_advice(is_last, Rotation::next()), + // To build a selector expression just having 0 when false and != 0 when true + // is enough, so we could replace the `or` by a `+`. This + // would give 2 when both expressions are true + // but it's fine. + and::expr([ + tag.value_equals(CopyDataType::Memory, Rotation::cur())(meta), + tag.value_equals(CopyDataType::Bytecode, Rotation::next())(meta), + ]) + tag.value_equals(CopyDataType::RlcAcc, Rotation::next())(meta), + ])) + }, + ); + meta.create_gate("verify step (q_step == 1)", |meta| { let mut cb = BaseConstraintBuilder::default(); @@ -250,10 +269,10 @@ impl SubCircuitConfig for CopyCircuitConfig { ]), ); cb.condition( - not::expr(meta.query_advice(is_last, Rotation::next())) - * (not::expr(tag.value_equals(CopyDataType::Padding, Rotation::cur())( - meta, - ))), + not::expr(or::expr([ + meta.query_advice(is_last, Rotation::next()), + tag.value_equals(CopyDataType::Padding, Rotation::cur())(meta), + ])), |cb| { cb.require_equal( "bytes_left == bytes_left_next + 1 for non-last step", @@ -262,25 +281,38 @@ impl SubCircuitConfig for CopyCircuitConfig { ); }, ); + cb.condition(meta.query_advice(is_first, Rotation::cur()), |cb| { + cb.require_equal( + "value == value_acc_rlc at every first copy event", + meta.query_advice(value, Rotation::cur()), + meta.query_advice(value_acc_rlc, Rotation::cur()), + ); + }); + cb.require_equal( + "write value == read value", + meta.query_advice(value, Rotation::cur()), + meta.query_advice(value, Rotation::next()), + ); + cb.require_equal( + "value_acc_rlc is same for read-write rows", + meta.query_advice(value_acc_rlc, Rotation::cur()), + meta.query_advice(value_acc_rlc, Rotation::next()), + ); cb.condition( - not::expr(tag.value_equals(CopyDataType::RlcAcc, Rotation::next())( - meta, - )), + and::expr([ + not::expr(meta.query_advice(is_last, Rotation::next())), + not::expr(meta.query_advice(is_pad, Rotation::cur())), + ]), |cb| { cb.require_equal( - "write value == read value (if not rlc acc)", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(value, Rotation::next()), + "value_acc_rlc(2) == value_acc_rlc(0) * r + value(2)", + meta.query_advice(value_acc_rlc, Rotation(2)), + meta.query_advice(value_acc_rlc, Rotation::cur()) + * challenges.keccak_input() + + meta.query_advice(value, Rotation(2)), ); }, ); - cb.condition(meta.query_advice(is_first, Rotation::cur()), |cb| { - cb.require_equal( - "write value == read value (is_first == 1)", - meta.query_advice(value, Rotation::cur()), - meta.query_advice(value, Rotation::next()), - ); - }); cb.require_zero( "value == 0 when is_pad == 1 for read", and::expr([ @@ -298,26 +330,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(is_pad, Rotation::next()), ); - cb.gate(meta.query_selector(q_step)) - }); - - meta.create_gate("verify_step (q_step == 0)", |meta| { - let mut cb = BaseConstraintBuilder::default(); - - cb.require_equal( - "rows[2].value == rows[0].value * r + rows[1].value", - meta.query_advice(value, Rotation(2)), - meta.query_advice(value, Rotation::cur()) * challenges.keccak_input() - + meta.query_advice(value, Rotation::next()), - ); - - cb.gate(and::expr([ - meta.query_fixed(q_enable, Rotation::cur()), - not::expr(meta.query_selector(q_step)), - not::expr(meta.query_advice(is_last, Rotation::cur())), - tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta), - not::expr(meta.query_advice(is_pad, Rotation::cur())), - ])) + cb.gate(and::expr([meta.query_selector(q_step)])) }); meta.lookup_any("Memory lookup", |meta| { @@ -327,8 +340,8 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ meta.query_advice(rw_counter, Rotation::cur()), not::expr(meta.query_selector(q_step)), - RwTableTag::Memory.expr(), - meta.query_advice(id.lo(), Rotation::cur()), // For call_id we use lo limb only + Target::Memory.expr(), + meta.query_advice(id.lo(), Rotation::cur()), // call_id meta.query_advice(addr, Rotation::cur()), // memory address 0.expr(), 0.expr(), @@ -349,10 +362,9 @@ impl SubCircuitConfig for CopyCircuitConfig { vec![ meta.query_advice(rw_counter, Rotation::cur()), 1.expr(), - RwTableTag::TxLog.expr(), - meta.query_advice(id.lo(), Rotation::cur()), /* For transaction ID we use lo - * limb only */ - meta.query_advice(addr, Rotation::cur()), // byte_index || field_tag || log_id + Target::TxLog.expr(), + meta.query_advice(id.lo(), Rotation::cur()), // tx_id + meta.query_advice(addr, Rotation::cur()), // byte_index || field_tag || log_id 0.expr(), 0.expr(), meta.query_advice(value, Rotation::cur()), @@ -419,6 +431,7 @@ impl SubCircuitConfig for CopyCircuitConfig { q_step, is_last, value, + value_acc_rlc, is_pad, is_code, q_enable, @@ -480,9 +493,15 @@ impl CopyCircuitConfig { )?; // is_last, value, is_pad, is_code - for (column, &(value, label)) in [self.is_last, self.value, self.is_pad, self.is_code] - .iter() - .zip_eq(circuit_row) + for (column, &(value, label)) in [ + self.is_last, + self.value, + self.value_acc_rlc, + self.is_pad, + self.is_code, + ] + .iter() + .zip_eq(circuit_row) { region.assign_advice( || format!("{} at row: {}", label, *offset), @@ -500,8 +519,10 @@ impl CopyCircuitConfig { lt_chip.assign( region, *offset, - F::from(copy_event.src_addr + u64::try_from(step_idx).unwrap() / 2u64), - F::from(copy_event.src_addr_end), + Value::known(F::from( + copy_event.src_addr + u64::try_from(step_idx).unwrap() / 2u64, + )), + Value::known(F::from(copy_event.src_addr_end)), )?; } @@ -641,6 +662,13 @@ impl CopyCircuitConfig { *offset, || Value::known(F::ZERO), )?; + // value_acc_rlc + region.assign_advice( + || format!("assign value_acc_rlc {}", *offset), + self.value_acc_rlc, + *offset, + || Value::known(F::ZERO), + )?; // rlc_acc region.assign_advice( || format!("assign rlc_acc {}", *offset), @@ -679,7 +707,7 @@ impl CopyCircuitConfig { // tag tag_chip.assign(region, *offset, &CopyDataType::Padding)?; // Assign LT gadget - lt_chip.assign(region, *offset, F::ZERO, F::ONE)?; + lt_chip.assign(region, *offset, Value::known(F::ZERO), Value::known(F::ONE))?; *offset += 1; @@ -800,45 +828,3 @@ impl SubCircuit for CopyCircuit { config.assign_copy_events(layouter, &self.copy_events, self.max_copy_rows, *challenges) } } - -#[cfg(test)] -mod copy_circuit_stats { - use crate::{ - evm_circuit::step::ExecutionState, - stats::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}, - }; - - /// Prints the stats of Copy circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_evm_states_stats -- --nocapture --ignored` - #[ignore] - #[test] - fn get_copy_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - matches!( - state, - ExecutionState::RETURNDATACOPY - | ExecutionState::CODECOPY - | ExecutionState::LOG - | ExecutionState::CALLDATACOPY - | ExecutionState::EXTCODECOPY - | ExecutionState::RETURN_REVERT - ) - }, - bytecode_prefix_op_big_rws, - |block, _, _| { - assert!(block.copy_events.len() <= 1); - block - .copy_events - .iter() - .map(|c| c.bytes.len() * 2) - .sum::() - }, - ); - } -} diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index 0c00f862d4..06e87e93e0 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -14,6 +14,7 @@ use halo2_proofs::{ impl Circuit for CopyCircuit { type Config = (CopyCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + 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 81d0183ee3..b60b965c9a 100644 --- a/zkevm-circuits/src/copy_circuit/test.rs +++ b/zkevm-circuits/src/copy_circuit/test.rs @@ -1,5 +1,3 @@ -#![allow(unused_imports)] - use crate::{ copy_circuit::*, evm_circuit::{test::rand_bytes, witness::block_convert}, @@ -8,7 +6,7 @@ use crate::{ }; use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, CircuitsParams}, - evm::{gen_sha3_code, MemoryKind}, + evm::Sha3CodeGen, mock::BlockData, }; use eth_types::{bytecode, geth_types::GethData, ToWord, Word}; @@ -22,7 +20,7 @@ use mock::{test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOC fn copy_circuit_unusable_rows() { assert_eq!( CopyCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } @@ -153,7 +151,7 @@ fn gen_extcodecopy_data() -> CircuitInputBuilder { } fn gen_sha3_data() -> CircuitInputBuilder { - let (code, _) = gen_sha3_code(0x20, 0x200, MemoryKind::EqualToSize); + let (code, _) = Sha3CodeGen::mem_eq_size(0x20, 0x200).gen_sha3_code(); let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); let block: GethData = test_ctx.into(); let mut builder = BlockData::new_from_geth_data_with_params( diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index dba99e85e2..0b0f1f0931 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -8,12 +8,13 @@ use halo2_proofs::{ mod execution; pub mod param; -pub(crate) mod step; +pub mod step; pub mod table; pub(crate) mod util; #[cfg(any(feature = "test", test))] pub(crate) mod test; +use self::step::HasExecutionState; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use self::EvmCircuit as TestEvmCircuit; @@ -38,7 +39,7 @@ use witness::Block; pub struct EvmCircuitConfig { fixed_table: [Column; 4], byte_table: [Column; 1], - pub(crate) execution: Box>, + pub execution: Box>, // External tables tx_table: TxTable, rw_table: RwTable, @@ -73,7 +74,6 @@ impl SubCircuitConfig for EvmCircuitConfig { type ConfigArgs = EvmCircuitConfigArgs; /// Configure EvmCircuitConfig - #[allow(clippy::too_many_arguments)] fn new( meta: &mut ConstraintSystem, Self::ConfigArgs { @@ -222,7 +222,7 @@ impl EvmCircuit { 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 += step.execution_state().get_step_height(); } } @@ -280,7 +280,7 @@ pub(crate) fn detect_fixed_table_tags(block: &Block) -> Vec for EvmCircuitCached { type Config = (EvmCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self(self.0.without_witnesses()) @@ -359,6 +360,7 @@ pub(crate) mod cached { impl Circuit for EvmCircuit { type Config = (EvmCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() @@ -436,40 +438,26 @@ impl Circuit for EvmCircuit { #[cfg(test)] mod evm_circuit_stats { use crate::{ - evm_circuit::{ - param::{ - LOOKUP_CONFIG, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, - }, - step::ExecutionState, - EvmCircuit, - }, - stats::print_circuit_stats_by_states, + evm_circuit::EvmCircuit, test_util::CircuitTestBuilder, util::{unusable_rows, SubCircuit}, witness::block_convert, }; use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; - use cli_table::{print_stdout, Cell, Style, Table}; - use eth_types::{bytecode, evm_types::OpcodeId, geth_types::GethData, ToWord}; - use halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::Fr, - plonk::{Circuit, ConstraintSystem}, - }; - use itertools::Itertools; - use mock::{ - test_ctx::{ - helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, - TestContext, - }, - MOCK_ACCOUNTS, + + use eth_types::{bytecode, geth_types::GethData}; + use halo2_proofs::{self, dev::MockProver, halo2curves::bn256::Fr}; + + use mock::test_ctx::{ + helpers::{account_0_code_account_1_no_code, tx_from_1_to_0}, + TestContext, }; #[test] fn evm_circuit_unusable_rows() { assert_eq!( EvmCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } @@ -492,127 +480,6 @@ mod evm_circuit_stats { .run(); } - /// Prints the stats of EVM circuit per execution state. See - /// `print_circuit_stats_by_states` for more details. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release --all-features - /// get_evm_states_stats -- --nocapture --ignored` - #[ignore] - #[test] - fn get_evm_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - !matches!( - state, - ExecutionState::ErrorInvalidOpcode - | ExecutionState::CREATE - | ExecutionState::CREATE2 - | ExecutionState::SELFDESTRUCT - ) - }, - |opcode| match opcode { - OpcodeId::RETURNDATACOPY => { - bytecode! { - PUSH1(0x00) // retLength - PUSH1(0x00) // retOffset - PUSH1(0x00) // argsLength - PUSH1(0x00) // argsOffset - PUSH1(0x00) // value - PUSH32(MOCK_ACCOUNTS[3].to_word()) - PUSH32(0x1_0000) // gas - CALL - PUSH2(0x01) // size - PUSH2(0x00) // offset - PUSH2(0x00) // destOffset - } - } - _ => bytecode! { - PUSH2(0x40) - PUSH2(0x50) - }, - }, - |_, state, _| state.get_step_height_option().unwrap(), - ); - } - - /// This function prints to stdout a table with the top X ExecutionState - /// cell consumers of each EVM Cell type. - /// - /// Run with: - /// `cargo test -p zkevm-circuits --release get_exec_steps_occupancy - /// --features test -- --nocapture --ignored` - #[ignore] - #[test] - fn get_exec_steps_occupancy() { - let mut meta = ConstraintSystem::::default(); - let circuit = EvmCircuit::configure(&mut meta); - - let report = circuit.0.execution.instrument().clone().analyze(); - macro_rules! gen_report { - ($report:expr, $($id:ident, $cols:expr), +) => { - $( - let row_report = report - .iter() - .sorted_by(|a, b| a.$id.utilization.partial_cmp(&b.$id.utilization).unwrap()) - .rev() - .take(10) - .map(|exec| { - vec![ - format!("{:?}", exec.state), - format!("{:?}", exec.$id.available_cells), - format!("{:?}", exec.$id.unused_cells), - format!("{:?}", exec.$id.used_cells), - format!("{:?}", exec.$id.top_height), - format!("{:?}", exec.$id.used_columns), - format!("{:?}", exec.$id.utilization), - ] - }) - .collect::>>(); - - let table = row_report.table().title(vec![ - format!("{:?}", stringify!($id)).cell().bold(true), - format!("total_available_cells").cell().bold(true), - format!("unused_cells").cell().bold(true), - format!("cells").cell().bold(true), - format!("top_height").cell().bold(true), - format!("used columns (Max: {:?})", $cols).cell().bold(true), - format!("Utilization").cell().bold(true), - ]); - print_stdout(table).unwrap(); - )* - }; - } - - gen_report!( - report, - storage_1, - N_PHASE1_COLUMNS, - storage_2, - N_PHASE2_COLUMNS, - storage_perm, - N_COPY_COLUMNS, - byte_lookup, - N_BYTE_LOOKUPS, - fixed_table, - LOOKUP_CONFIG[0].1, - tx_table, - LOOKUP_CONFIG[1].1, - rw_table, - LOOKUP_CONFIG[2].1, - bytecode_table, - LOOKUP_CONFIG[3].1, - block_table, - LOOKUP_CONFIG[4].1, - copy_table, - LOOKUP_CONFIG[5].1, - keccak_table, - LOOKUP_CONFIG[6].1, - exp_table, - LOOKUP_CONFIG[7].1 - ); - } #[test] fn variadic_size_check() { let params = CircuitsParams { diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 0a23973b59..7766784f7a 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -4,6 +4,7 @@ use super::{ FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_BYTE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, RW_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, + step::HasExecutionState, util::{instrumentation::Instrument, CachedRegion, CellManager, StoredExpression}, }; use crate::{ @@ -57,6 +58,7 @@ mod chainid; mod codecopy; mod codesize; mod comparator; +mod create; mod dummy; mod dup; mod end_block; @@ -129,6 +131,7 @@ use chainid::ChainIdGadget; use codecopy::CodeCopyGadget; use codesize::CodesizeGadget; use comparator::ComparatorGadget; +use create::CreateGadget; use dummy::DummyGadget; use dup::DupGadget; use end_block::EndBlockGadget; @@ -197,7 +200,7 @@ pub(crate) trait ExecutionGadget { } #[derive(Clone, Debug)] -pub(crate) struct ExecutionConfig { +pub 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). @@ -268,8 +271,8 @@ pub(crate) struct ExecutionConfig { shl_shr_gadget: Box>, returndatasize_gadget: Box>, returndatacopy_gadget: Box>, - create_gadget: Box>, - create2_gadget: Box>, + create_gadget: Box>, + create2_gadget: Box>, selfdestruct_gadget: Box>, signed_comparator_gadget: Box>, signextend_gadget: Box>, @@ -595,7 +598,7 @@ impl ExecutionConfig { config } - pub(crate) fn instrument(&self) -> &Instrument { + pub fn instrument(&self) -> &Instrument { &self.instrument } @@ -927,7 +930,7 @@ impl ExecutionConfig { if next.is_none() { break; } - let height = step.execution_state.get_step_height(); + let height = step.execution_state().get_step_height(); // Assign the step witness self.assign_exec_step( @@ -1074,8 +1077,8 @@ impl ExecutionConfig { return Ok(()); } assert_eq!(height, 1); - assert!(step.rw_indices.is_empty()); - assert!(matches!(step.execution_state, ExecutionState::EndBlock)); + assert!(step.rw_indices_len() == 0); + assert!(matches!(step.execution_state(), ExecutionState::EndBlock)); // Disable access to next step deliberately for "repeatable" step let region = &mut CachedRegion::<'_, '_, F>::new( @@ -1088,7 +1091,7 @@ impl ExecutionConfig { self.assign_exec_step_int(region, offset_begin, block, transaction, call, step)?; region.replicate_assignment_for_range( - || format!("repeat {:?} rows", step.execution_state), + || format!("repeat {:?} rows", step.execution_state()), offset_begin + 1, offset_end, )?; @@ -1109,11 +1112,11 @@ impl ExecutionConfig { next: Option<(&Transaction, &Call, &ExecStep)>, challenges: &Challenges>, ) -> Result<(), Error> { - if !matches!(step.execution_state, ExecutionState::EndBlock) { + if !matches!(step.execution_state(), ExecutionState::EndBlock) { log::trace!( "assign_exec_step offset: {} state {:?} step: {:?} call: {:?}", offset, - step.execution_state, + step.execution_state(), step, call ); @@ -1165,7 +1168,7 @@ impl ExecutionConfig { }; } - match step.execution_state { + match step.execution_state() { // internal states ExecutionState::BeginTx => assign_exec_step!(self.begin_tx_gadget), ExecutionState::EndTx => assign_exec_step!(self.end_tx_gadget), @@ -1279,9 +1282,6 @@ impl ExecutionConfig { assign_exec_step!(self.error_stack) } - ExecutionState::ErrorInsufficientBalance => { - assign_exec_step!(self.call_op_gadget) - } ExecutionState::ErrorInvalidJump => { assign_exec_step!(self.error_invalid_jump) } @@ -1291,12 +1291,6 @@ impl ExecutionConfig { ExecutionState::ErrorWriteProtection => { assign_exec_step!(self.error_write_protection) } - ExecutionState::ErrorDepth => { - assign_exec_step!(self.error_depth) - } - ExecutionState::ErrorContractAddressCollision => { - assign_exec_step!(self.error_contract_address_collision) - } ExecutionState::ErrorInvalidCreationCode => { assign_exec_step!(self.error_invalid_creation_code) } @@ -1304,7 +1298,7 @@ impl ExecutionConfig { assign_exec_step!(self.error_return_data_out_of_bound) } - _ => evm_unimplemented!("unimplemented ExecutionState: {:?}", step.execution_state), + unimpl_state => evm_unimplemented!("unimplemented ExecutionState: {:?}", unimpl_state), } // Fill in the witness values for stored expressions @@ -1312,8 +1306,8 @@ impl ExecutionConfig { // enable with `RUST_LOG=debug` if log::log_enabled!(log::Level::Debug) { - let is_padding_step = matches!(step.execution_state, ExecutionState::EndBlock) - && step.rw_indices.is_empty(); + let is_padding_step = matches!(step.execution_state(), ExecutionState::EndBlock) + && step.rw_indices_len() == 0; if !is_padding_step { // expensive function call Self::check_rw_lookup( @@ -1337,8 +1331,8 @@ impl ExecutionConfig { let mut assigned_stored_expressions = Vec::new(); for stored_expression in self .stored_expressions_map - .get(&step.execution_state) - .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state)) + .get(&step.execution_state()) + .unwrap_or_else(|| panic!("Execution state unknown: {:?}", step.execution_state())) { let assigned = stored_expression.assign(region, offset)?; assigned.map(|v| { @@ -1373,10 +1367,8 @@ impl ExecutionConfig { } } - let rlc_assignments: BTreeSet<_> = step - .rw_indices - .iter() - .map(|rw_idx| block.rws[*rw_idx]) + let rlc_assignments: BTreeSet<_> = (0..step.rw_indices_len()) + .map(|index| block.get_rws(step, index)) .map(|rw| rw.table_assignment().unwrap().rlc(lookup_randomness)) .fold(BTreeSet::::new(), |mut set, value| { set.insert(value); @@ -1393,10 +1385,10 @@ impl ExecutionConfig { // Check that the number of rw operations generated from the bus-mapping // correspond to the number of assigned rw lookups by the EVM Circuit // plus the number of rw lookups done by the copy circuit. - if step.rw_indices.len() != assigned_rw_values.len() + step.copy_rw_counter_delta as usize { + if step.rw_indices_len() != assigned_rw_values.len() + step.copy_rw_counter_delta as usize { log::error!( "step.rw_indices.len: {} != assigned_rw_values.len: {} + step.copy_rw_counter_delta: {} in step: {:?}", - step.rw_indices.len(), + step.rw_indices_len(), assigned_rw_values.len(), step.copy_rw_counter_delta, step @@ -1420,23 +1412,22 @@ impl ExecutionConfig { // corresponding rw lookup, but in the bus-mapping they are // generated at the end of the step. let idx = if is_rev { - step.rw_indices.len() - rev_count + step.rw_indices_len() - rev_count } else { idx - rev_count }; - let rw_idx = step.rw_indices[idx]; - let rw = block.rws[rw_idx]; + + let rw = block.get_rws(step, idx); let table_assignments = rw.table_assignment(); let rlc = table_assignments.unwrap().rlc(lookup_randomness); if rlc != assigned_rw_value.1 { log::error!( - "incorrect rw witness. lookup input name: \"{}\"\nassigned={:?}\nrlc ={:?}\nrw index: {:?}, {}th rw of step {:?}, rw: {:?}", + "incorrect rw witness. lookup input name: \"{}\"\nassigned={:?}\nrlc ={:?}\n{}th rw of step {:?}, rw: {:?}", assigned_rw_value.0, assigned_rw_value.1, rlc, - rw_idx, idx, - step.execution_state, + step.execution_state(), rw); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs index a7c7e282bc..72d6d55164 100644 --- a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs +++ b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs @@ -86,13 +86,13 @@ impl ExecutionGadget for AddSubGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let indices = if opcode == OpcodeId::SUB { - [step.rw_indices[2], step.rw_indices[1], step.rw_indices[0]] + [2, 1, 0] } else { - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] + [0, 1, 2] }; - let [a, b, c] = indices.map(|idx| block.rws[idx].stack_value()); + let [a, b, c] = indices.map(|index| block.get_rws(step, index).stack_value()); self.add_words.assign(region, offset, [a, b], c)?; self.is_sub.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index 81b0ceb39f..1fe9fa5ec2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -159,9 +159,7 @@ impl ExecutionGadget for AddModGadget { self.same_context.assign_exec_step(region, offset, step)?; // get stack values - let [mut r, n, b, a] = [3, 2, 1, 0] - .map(|idx| step.rw_indices[idx]) - .map(|idx| block.rws[idx].stack_value()); + let [mut r, n, b, a] = [3, 2, 1, 0].map(|index| block.get_rws(step, index).stack_value()); // assing a,b & n stack values self.a.assign(region, offset, Some(a.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/address.rs b/zkevm-circuits/src/evm_circuit/execution/address.rs index 326269bb04..7057d67893 100644 --- a/zkevm-circuits/src/evm_circuit/execution/address.rs +++ b/zkevm-circuits/src/evm_circuit/execution/address.rs @@ -63,7 +63,7 @@ impl ExecutionGadget for AddressGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[1]].stack_value(); + let address = block.get_rws(step, 1).stack_value(); debug_assert_eq!(call.address, address.to_address()); self.address.assign( diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index 7db43da465..2b20681fc3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -25,7 +25,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct BalanceGadget { same_context: SameContextGadget, - address: Word, + address_word: Word, reversion_info: ReversionInfo, tx_id: Cell, is_warm: Cell, @@ -92,7 +92,7 @@ impl ExecutionGadget for BalanceGadget { Self { same_context, - address, + address_word: address, reversion_info, tx_id, is_warm, @@ -113,8 +113,8 @@ impl ExecutionGadget for BalanceGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); - self.address + let address = block.get_rws(step, 0).stack_value(); + self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; self.tx_id @@ -127,11 +127,11 @@ impl ExecutionGadget for BalanceGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, Some(code_hash.to_le_bytes()))?; self.not_exists @@ -139,7 +139,7 @@ impl ExecutionGadget for BalanceGadget { let balance = if code_hash.is_zero() { eth_types::Word::zero() } else { - block.rws[step.rw_indices[6]].account_value_pair().0 + block.get_rws(step, 6).account_value_pair().0 }; self.balance .assign(region, offset, Some(balance.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 8869db6bd0..ce90c49793 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -1,7 +1,7 @@ use crate::{ evm_circuit::{ execution::ExecutionGadget, - param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_WORD}, + param::{N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_U64, N_BYTES_WORD}, step::ExecutionState, util::{ and, @@ -12,14 +12,16 @@ use crate::{ }, is_precompiled, math_gadget::{ - ContractCreateGadget, IsEqualGadget, IsZeroGadget, MulWordByU64Gadget, - RangeCheckGadget, + ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroGadget, + MulWordByU64Gadget, RangeCheckGadget, }, not, or, select, CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{AccountFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag}, + table::{ + AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxFieldTag as TxContextFieldTag, + }, util::{word::Word32Cell, Expr}, }; use eth_types::{evm_types::GasCost, Field, ToLittleEndian, ToScalar}; @@ -42,6 +44,7 @@ pub(crate) struct BeginTxGadget { tx_value: Word32Cell, tx_call_data_length: Cell, tx_call_data_gas_cost: Cell, + tx_call_data_word_length: ConstantDivisionGadget, reversion_info: ReversionInfo, sufficient_gas_left: RangeCheckGadget, transfer_with_gas_fee: TransferWithGasFeeGadget, @@ -51,6 +54,12 @@ pub(crate) struct BeginTxGadget { create: ContractCreateGadget, callee_not_exists: IsZeroGadget, is_caller_callee_equal: Cell, + // EIP-3651 (Warm COINBASE) + coinbase: Cell, + // Caller, callee and a list addresses are added to the access list before + // coinbase, and may be duplicate. + // + is_coinbase_warm: Cell, } impl ExecutionGadget for BeginTxGadget { @@ -127,13 +136,25 @@ impl ExecutionGadget for BeginTxGadget { let mul_gas_fee_by_gas = MulWordByU64Gadget::construct(cb, tx_gas_price.clone().into(), tx_gas.expr()); + let tx_call_data_word_length = + ConstantDivisionGadget::construct(cb, tx_call_data_length.expr() + 31.expr(), 32); + + // Calculate gas cost of init code for EIP-3860. + let init_code_gas_cost = select::expr( + tx_is_create.expr(), + tx_call_data_word_length.quotient().expr() + * eth_types::evm_types::INIT_CODE_WORD_GAS.expr(), + 0.expr(), + ); + // TODO: Take gas cost of access list (EIP 2930) into consideration. // Use intrinsic gas let intrinsic_gas_cost = select::expr( tx_is_create.expr(), GasCost::CREATION_TX.expr(), GasCost::TX.expr(), - ) + tx_call_data_gas_cost.expr(); + ) + tx_call_data_gas_cost.expr() + + init_code_gas_cost; // Check gas_left is sufficient let gas_left = tx_gas.expr() - intrinsic_gas_cost; @@ -158,6 +179,18 @@ impl ExecutionGadget for BeginTxGadget { None, ); // rwc_delta += 1 + // Query coinbase address. + let coinbase = cb.query_cell(); + let is_coinbase_warm = cb.query_bool(); + cb.block_lookup(BlockContextFieldTag::Coinbase.expr(), None, coinbase.expr()); + cb.account_access_list_write( + tx_id.expr(), + coinbase.expr(), + 1.expr(), + is_coinbase_warm.expr(), + None, + ); // rwc_delta += 1 + // Read code_hash of callee let phase2_code_hash = cb.query_cell_phase2(); let is_empty_code_hash = @@ -262,8 +295,9 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account (Caller) Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - a TransferWithGasFeeGadget // - Write Account (Callee) Nonce (Reversible) // - Write CallContext Depth @@ -279,7 +313,7 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsRoot // - Write CallContext IsCreate // - Write CallContext CodeHash - rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), @@ -318,11 +352,12 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - Read Account CodeHash // - a TransferWithGasFeeGadget - rw_counter: Delta(8.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(9.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), ..StepStateTransition::any() }); @@ -362,8 +397,9 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsPersistent // - Write CallContext IsSuccess // - Write Account Nonce - // - Write TxAccessListAccount - // - Write TxAccessListAccount + // - Write TxAccessListAccount (Caller) + // - Write TxAccessListAccount (Callee) + // - Write TxAccessListAccount (Coinbase) for EIP-3651 // - Read Account CodeHash // - a TransferWithGasFeeGadget // - Write CallContext Depth @@ -379,7 +415,7 @@ impl ExecutionGadget for BeginTxGadget { // - Write CallContext IsRoot // - Write CallContext IsCreate // - Write CallContext CodeHash - rw_counter: Delta(21.expr() + transfer_with_gas_fee.rw_delta()), + rw_counter: Delta(22.expr() + transfer_with_gas_fee.rw_delta()), call_id: To(call_id.expr()), is_root: To(true.expr()), is_create: To(tx_is_create.expr()), @@ -406,6 +442,7 @@ impl ExecutionGadget for BeginTxGadget { tx_value: tx_value.into(), tx_call_data_length, tx_call_data_gas_cost, + tx_call_data_word_length, reversion_info, sufficient_gas_left, transfer_with_gas_fee, @@ -415,6 +452,8 @@ impl ExecutionGadget for BeginTxGadget { create, callee_not_exists, is_caller_callee_equal, + coinbase, + is_coinbase_warm, } } @@ -432,6 +471,8 @@ impl ExecutionGadget for BeginTxGadget { let mut rws = StepRws::new(block, step); rws.offset_add(7); + + let is_coinbase_warm = rws.next().tx_access_list_value_pair().1; let mut callee_code_hash = zero; if !is_precompiled(&tx.callee_address) && !tx.is_create { callee_code_hash = rws.next().account_value_pair().1; @@ -504,6 +545,8 @@ impl ExecutionGadget for BeginTxGadget { offset, Value::known(F::from(tx.call_data_gas_cost)), )?; + self.tx_call_data_word_length + .assign(region, offset, tx.call_data_length as u128 + 31)?; self.reversion_info.assign( region, offset, @@ -557,6 +600,23 @@ impl ExecutionGadget for BeginTxGadget { None, )?; + self.coinbase.assign( + region, + offset, + Value::known( + block + .context + .coinbase + .to_scalar() + .expect("unexpected Address -> Scalar conversion failure"), + ), + )?; + self.is_coinbase_warm.assign( + region, + offset, + Value::known(F::from(is_coinbase_warm as u64)), + )?; + Ok(()) } } @@ -573,8 +633,8 @@ mod test { fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -664,7 +724,7 @@ mod test { // This test checks that the rw table assignment and evm circuit are consistent // in not applying an RLC to account and tx nonces. // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/592 - let multibyte_nonce = Word::from(700); + let multibyte_nonce = 700; let to = MOCK_ACCOUNTS[0]; let from = MOCK_ACCOUNTS[1]; @@ -776,12 +836,12 @@ mod test { accs[0] .address(MOCK_ACCOUNTS[0]) .balance(eth(20)) - .nonce(nonce.into()); + .nonce(nonce); }, |mut txs, _accs| { txs[0] .from(MOCK_ACCOUNTS[0]) - .nonce(nonce.into()) + .nonce(nonce) .gas_price(gwei(2)) .gas(Word::from(0x10000)) .value(eth(2)) diff --git a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs index 90979590fa..eef97570fb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs +++ b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs @@ -87,8 +87,7 @@ impl ExecutionGadget for BitwiseGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [a, b, c] = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + let [a, b, c] = [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.a.assign(region, offset, Some(a.to_le_bytes()))?; self.b.assign(region, offset, Some(b.to_le_bytes()))?; self.c.assign(region, offset, Some(c.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs index 31ed3e8a16..f53b0c96b1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs @@ -90,7 +90,7 @@ impl ExecutionGadget for BlockCtxU64Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u64.value.assign( region, @@ -131,7 +131,7 @@ impl ExecutionGadget for BlockCtxU160Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u160.value.assign( region, @@ -176,7 +176,7 @@ impl ExecutionGadget for BlockCtxU256Gadget { .same_context .assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value_u256 .value diff --git a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs index 6b26afa7d6..a811c09af9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs @@ -108,7 +108,7 @@ impl ExecutionGadget for BlockHashGadget { .to_scalar() .expect("unexpected U256 -> Scalar conversion failure"); - let block_number = block.rws[step.rw_indices[0]].stack_value(); + let block_number = block.get_rws(step, 0).stack_value(); self.block_number .assign(region, offset, block_number, current_block_number)?; @@ -118,7 +118,7 @@ impl ExecutionGadget for BlockHashGadget { self.block_hash.assign( region, offset, - Some(block.rws[step.rw_indices[1]].stack_value().to_le_bytes()), + Some(block.get_rws(step, 1).stack_value().to_le_bytes()), )?; self.diff_lt.assign( diff --git a/zkevm-circuits/src/evm_circuit/execution/byte.rs b/zkevm-circuits/src/evm_circuit/execution/byte.rs index dd4b4317d7..a21c0570e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/byte.rs +++ b/zkevm-circuits/src/evm_circuit/execution/byte.rs @@ -101,8 +101,8 @@ impl ExecutionGadget for ByteGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); + let index = block.get_rws(step, 0).stack_value().to_le_bytes(); + let value = block.get_rws(step, 1).stack_value().to_le_bytes(); self.index.assign(region, offset, Some(index))?; self.value.assign(region, offset, Some(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs index 216cfb15d9..6d1fb475cc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs @@ -179,8 +179,7 @@ impl ExecutionGadget for CallDataCopyGadget { self.same_context.assign_exec_step(region, offset, step)?; let [memory_offset, data_offset, length] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_offset, length)?; @@ -264,9 +263,9 @@ mod test { fn test_root_ok( call_data_length: usize, - memory_offset: usize, length: usize, data_offset: Word, + memory_offset: Word, ) { let bytecode = bytecode! { PUSH32(length) @@ -303,9 +302,9 @@ mod test { fn test_internal_ok( call_data_offset: usize, call_data_length: usize, - dst_offset: usize, length: usize, data_offset: Word, + dst_offset: Word, ) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); @@ -344,31 +343,37 @@ mod test { #[test] fn calldatacopy_gadget_simple() { - test_root_ok(0x40, 0x40, 10, 0x00.into()); - test_internal_ok(0x40, 0x40, 0xA0, 10, 0x10.into()); + test_root_ok(0x40, 10, 0x00.into(), 0x40.into()); + test_internal_ok(0x40, 0x40, 10, 0x10.into(), 0xA0.into()); } #[test] fn calldatacopy_gadget_large() { - test_root_ok(0x204, 0x103, 0x101, 0x102.into()); - test_internal_ok(0x30, 0x204, 0x103, 0x101, 0x102.into()); + test_root_ok(0x204, 0x101, 0x102.into(), 0x103.into()); + test_internal_ok(0x30, 0x204, 0x101, 0x102.into(), 0x103.into()); } #[test] fn calldatacopy_gadget_out_of_bound() { - test_root_ok(0x40, 0x40, 40, 0x20.into()); - test_internal_ok(0x40, 0x20, 0xA0, 10, 0x28.into()); + test_root_ok(0x40, 40, 0x20.into(), 0x40.into()); + test_internal_ok(0x40, 0x20, 10, 0x28.into(), 0xA0.into()); } #[test] fn calldatacopy_gadget_zero_length() { - test_root_ok(0x40, 0x40, 0, 0x00.into()); - test_internal_ok(0x40, 0x40, 0xA0, 0, 0x10.into()); + test_root_ok(0x40, 0, 0x00.into(), 0x40.into()); + test_internal_ok(0x40, 0x40, 0, 0x10.into(), 0xA0.into()); } #[test] fn calldatacopy_gadget_data_offset_overflow() { - test_root_ok(0x40, 0x40, 0, Word::MAX); - test_internal_ok(0x40, 0x40, 0xA0, 0, Word::MAX); + test_root_ok(0x40, 10, Word::MAX, 0x40.into()); + test_internal_ok(0x40, 0x40, 10, Word::MAX, 0xA0.into()); + } + + #[test] + fn calldatacopy_gadget_overflow_memory_offset_and_zero_length() { + test_root_ok(0x40, 0, 0x40.into(), Word::MAX); + test_internal_ok(0x40, 0x40, 0, 0x10.into(), Word::MAX); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs index f443980188..555a105667 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs @@ -226,7 +226,7 @@ impl ExecutionGadget for CallDataLoadGadget { self.call_data_offset .assign(region, offset, Value::known(F::from(call_data_offset)))?; - let data_offset = block.rws[step.rw_indices[0]].stack_value(); + let data_offset = block.get_rws(step, 0).stack_value(); let offset_not_overflow = self.data_offset .assign(region, offset, data_offset, F::from(call_data_length))?; @@ -253,8 +253,9 @@ impl ExecutionGadget for CallDataLoadGadget { } else { // Fetch from memory. if src_addr + (i as u64) < call.call_data_offset + call.call_data_length { - *byte = - block.rws[step.rw_indices[OFFSET_RW_MEMORY_INDICES + i]].memory_value(); + *byte = block + .get_rws(step, OFFSET_RW_MEMORY_INDICES + i) + .memory_value(); } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs index e9485a61eb..dc6028e53b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs @@ -70,7 +70,7 @@ impl ExecutionGadget for CallDataSizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let call_data_size = block.rws[step.rw_indices[1]].stack_value(); + let call_data_size = block.get_rws(step, 1).stack_value(); self.call_data_size.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/caller.rs b/zkevm-circuits/src/evm_circuit/execution/caller.rs index 19bc8f79ae..a57b97dc8e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/caller.rs +++ b/zkevm-circuits/src/evm_circuit/execution/caller.rs @@ -71,7 +71,7 @@ impl ExecutionGadget for CallerGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let caller = block.rws[step.rw_indices[1]].stack_value(); + let caller = block.get_rws(step, 1).stack_value(); self.caller_address.assign( region, diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index d2b4653b44..3f13f31e48 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -175,16 +175,20 @@ impl ExecutionGadget for CallOpGadget { // skip the transfer (this is necessary for non-existing accounts, which // will not be crated when value is 0 and so the callee balance lookup // would be invalid). - let transfer = cb.condition(and::expr(&[is_call.expr(), is_precheck_ok.expr()]), |cb| { - TransferGadget::construct( - cb, - caller_address.expr(), - callee_address.expr(), - not::expr(call_gadget.callee_not_exists.expr()), - call_gadget.value.clone(), - &mut callee_reversion_info, - ) - }); + let transfer = cb.condition( + is_call.expr() * not::expr(is_insufficient_balance.expr()), + |cb| { + TransferGadget::construct( + cb, + caller_address.expr(), + callee_address.expr(), + not::expr(call_gadget.callee_not_exists.expr()), + 0.expr(), + call_gadget.value.clone(), + &mut callee_reversion_info, + ) + }, + ); // For CALLCODE opcode, verify caller balance is greater than or equal to stack // `value` in successful case. that is `is_insufficient_balance` is false. @@ -440,22 +444,15 @@ impl ExecutionGadget for CallOpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call = opcode == OpcodeId::CALL; let is_callcode = opcode == OpcodeId::CALLCODE; let is_delegatecall = opcode == OpcodeId::DELEGATECALL; - let [tx_id, is_static, depth, current_callee_address] = [ - step.rw_indices[0], - step.rw_indices[3], - step.rw_indices[4], - step.rw_indices[5], - ] - .map(|idx| block.rws[idx].call_context_value()); + let [tx_id, is_static, depth, current_callee_address] = + [0, 3, 4, 5].map(|index| block.get_rws(step, index).call_context_value()); let is_error_depth = depth.low_u64() > 1024; self.is_depth_ok .assign(region, offset, F::from(depth.low_u64()), F::from(1025))?; - let stack_index = 6; - // This offset is used to change the index offset of `step.rw_indices`. // Since both CALL and CALLCODE have an extra stack pop `value`, and // opcode DELEGATECALL has two extra call context lookups - current @@ -463,46 +460,34 @@ impl ExecutionGadget for CallOpGadget { let mut rw_offset = 0; let [current_caller_address, current_value] = if is_delegatecall { rw_offset += 2; - [step.rw_indices[6], step.rw_indices[7]].map(|idx| block.rws[idx].call_context_value()) + [6, 7].map(|index| block.get_rws(step, index).call_context_value()) } else { [U256::zero(), U256::zero()] }; - let [gas, callee_address] = [ - step.rw_indices[stack_index + rw_offset], - step.rw_indices[stack_index + 1 + rw_offset], - ] - .map(|idx| block.rws[idx].stack_value()); + let [gas, callee_address] = + [6, 7].map(|i| block.get_rws(step, i + rw_offset).stack_value()); let value = if is_call || is_callcode { + let value = block.get_rws(step, 8 + rw_offset).stack_value(); rw_offset += 1; - block.rws[step.rw_indices[7 + rw_offset]].stack_value() + value } else { U256::zero() }; - let [cd_offset, cd_length, rd_offset, rd_length, is_success] = [ - step.rw_indices[stack_index + 2 + rw_offset], - step.rw_indices[stack_index + 3 + rw_offset], - step.rw_indices[stack_index + 4 + rw_offset], - step.rw_indices[stack_index + 5 + rw_offset], - step.rw_indices[stack_index + 6 + rw_offset], - ] - .map(|idx| block.rws[idx].stack_value()); - let callee_code_hash = block.rws[step.rw_indices[13 + rw_offset]] - .account_value_pair() - .0; + let [cd_offset, cd_length, rd_offset, rd_length, is_success] = + [8, 9, 10, 11, 12].map(|i| block.get_rws(step, i + rw_offset).stack_value()); + let callee_code_hash = block.get_rws(step, 13 + rw_offset).account_value_pair().0; let callee_exists = !callee_code_hash.is_zero(); - let (is_warm, is_warm_prev) = - block.rws[step.rw_indices[14 + rw_offset]].tx_access_list_value_pair(); + let (is_warm, is_warm_prev) = block + .get_rws(step, 14 + rw_offset) + .tx_access_list_value_pair(); - let [callee_rw_counter_end_of_reversion, callee_is_persistent] = [ - step.rw_indices[15 + rw_offset], - step.rw_indices[16 + rw_offset], - ] - .map(|idx| block.rws[idx].call_context_value()); + let [callee_rw_counter_end_of_reversion, callee_is_persistent] = + [15, 16].map(|index| block.get_rws(step, index + rw_offset).call_context_value()); // check if it is insufficient balance case. // get caller balance - let (caller_balance, _) = block.rws[step.rw_indices[17 + rw_offset]].account_value_pair(); + let (caller_balance, _) = block.get_rws(step, 17 + rw_offset).account_value_pair(); self.caller_balance_word .assign(region, offset, Some(caller_balance.to_le_bytes()))?; self.is_insufficient_balance @@ -510,15 +495,11 @@ impl ExecutionGadget for CallOpGadget { let is_insufficient = (value > caller_balance) && (is_call || is_callcode); // only call opcode do transfer in sucessful case. - let (caller_balance_pair, callee_balance_pair) = + let [caller_balance_pair, callee_balance_pair] = if is_call && !is_insufficient && !is_error_depth && !value.is_zero() { - rw_offset += 2; - ( - block.rws[step.rw_indices[16 + rw_offset]].account_value_pair(), - block.rws[step.rw_indices[17 + rw_offset]].account_value_pair(), - ) + [18, 19].map(|index| block.get_rws(step, index + rw_offset).account_value_pair()) } else { - ((U256::zero(), U256::zero()), (U256::zero(), U256::zero())) + [(U256::zero(), U256::zero()), (U256::zero(), U256::zero())] }; self.opcode @@ -619,7 +600,7 @@ impl ExecutionGadget for CallOpGadget { has_value, !callee_exists, )?; - let gas_available = step.gas_left - gas_cost; + let gas_available: u64 = step.gas_left - gas_cost; self.one_64th_gas .assign(region, offset, gas_available.into())?; @@ -641,6 +622,7 @@ mod test { use bus_mapping::circuit_input_builder::CircuitsParams; use eth_types::{ address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, ToWord, Word, + U64, }; use itertools::Itertools; @@ -713,31 +695,31 @@ mod test { }, // With memory expansion Stack { - cd_offset: 64, + cd_offset: 64.into(), cd_length: 320, - rd_offset: 0, + rd_offset: Word::zero(), rd_length: 32, ..Default::default() }, Stack { - cd_offset: 0, + cd_offset: Word::zero(), cd_length: 32, - rd_offset: 64, + rd_offset: 64.into(), rd_length: 320, ..Default::default() }, Stack { - cd_offset: 0xFFFFFF, + cd_offset: 0xFFFFFF.into(), cd_length: 0, - rd_offset: 0xFFFFFF, + rd_offset: 0xFFFFFF.into(), rd_length: 0, ..Default::default() }, // With memory expansion and value Stack { - cd_offset: 64, + cd_offset: 64.into(), cd_length: 320, - rd_offset: 0, + rd_offset: 0.into(), rd_length: 32, value: Word::from(10).pow(18.into()), ..Default::default() @@ -754,13 +736,28 @@ mod test { } } + #[test] + fn callop_overflow_offset_and_zero_length() { + let stack = Stack { + cd_offset: Word::MAX, + cd_length: 0, + rd_offset: Word::MAX, + rd_length: 0, + ..Default::default() + }; + + TEST_CALL_OPCODES + .iter() + .for_each(|opcode| test_ok(caller(opcode, stack, true), callee(bytecode! {}))); + } + #[derive(Clone, Copy, Debug, Default)] struct Stack { gas: u64, value: Word, - cd_offset: u64, + cd_offset: Word, cd_length: u64, - rd_offset: u64, + rd_offset: Word, rd_length: u64, } @@ -770,7 +767,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -787,9 +784,9 @@ mod test { // Call twice for testing both cold and warm access let mut bytecode = bytecode! { PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }; if is_call_or_callcode { bytecode.push(32, stack.value); @@ -799,9 +796,9 @@ mod test { PUSH32(Word::from(stack.gas)) .write_op(*opcode) PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }); if is_call_or_callcode { bytecode.push(32, stack.value); @@ -829,9 +826,9 @@ mod test { let mut bytecode = bytecode! { PUSH32(Word::from(stack.rd_length)) - PUSH32(Word::from(stack.rd_offset)) + PUSH32(stack.rd_offset) PUSH32(Word::from(stack.cd_length)) - PUSH32(Word::from(stack.cd_offset)) + PUSH32(stack.cd_offset) }; if is_call_or_callcode { bytecode.push(32, stack.value); @@ -897,16 +894,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs index 12a9e95f2c..237c954096 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs @@ -71,7 +71,7 @@ impl ExecutionGadget for CallValueGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let call_value = block.rws[step.rw_indices[1]].stack_value(); + let call_value = block.get_rws(step, 1).stack_value(); self.call_value .assign(region, offset, region.word_rlc(call_value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/chainid.rs b/zkevm-circuits/src/evm_circuit/execution/chainid.rs index 6f6146e626..e635b1e391 100644 --- a/zkevm-circuits/src/evm_circuit/execution/chainid.rs +++ b/zkevm-circuits/src/evm_circuit/execution/chainid.rs @@ -63,7 +63,7 @@ impl ExecutionGadget for ChainIdGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let chain_id = block.rws[step.rw_indices[0]].stack_value(); + let chain_id = block.get_rws(step, 0).stack_value(); self.chain_id .assign(region, offset, region.word_rlc(chain_id))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index c4b0642609..f47fef2936 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -152,7 +152,7 @@ impl ExecutionGadget for CodeCopyGadget { // 3. `size` is the number of // bytes to be read and written (0s to be copied for out of bounds). let [dest_offset, code_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i]].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); let bytecode = block .bytecodes @@ -200,7 +200,7 @@ mod tests { use eth_types::{bytecode, Word}; use mock::TestContext; - fn test_ok(code_offset: Word, memory_offset: usize, size: usize, large: bool) { + fn test_ok(code_offset: Word, memory_offset: Word, size: usize, large: bool) { let mut code = bytecode! {}; if large { for _ in 0..size { @@ -210,7 +210,7 @@ mod tests { let tail = bytecode! { PUSH32(Word::from(size)) PUSH32(code_offset) - PUSH32(Word::from(memory_offset)) + PUSH32(memory_offset) CODECOPY STOP }; @@ -224,18 +224,23 @@ mod tests { #[test] fn codecopy_gadget_simple() { - test_ok(0x00.into(), 0x00, 0x20, false); - test_ok(0x30.into(), 0x20, 0x30, false); - test_ok(0x20.into(), 0x10, 0x42, false); + test_ok(0x00.into(), 0x00.into(), 0x20, false); + test_ok(0x30.into(), 0x20.into(), 0x30, false); + test_ok(0x20.into(), 0x10.into(), 0x42, false); } #[test] fn codecopy_gadget_large() { - test_ok(0x102.into(), 0x103, 0x101, true); + test_ok(0x102.into(), 0x103.into(), 0x101, true); } #[test] fn codecopy_gadget_code_offset_overflow() { - test_ok(Word::MAX, 0x103, 0x101, true); + test_ok(Word::MAX, 0x103.into(), 0x101, true); + } + + #[test] + fn codecopy_gadget_overflow_memory_offset_and_zero_size() { + test_ok(0x102.into(), Word::MAX, 0, false); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/codesize.rs b/zkevm-circuits/src/evm_circuit/execution/codesize.rs index bd98f3ea76..86df028c24 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codesize.rs @@ -76,7 +76,7 @@ impl ExecutionGadget for CodesizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let codesize = block.rws[step.rw_indices[0]].stack_value().as_u64(); + let codesize = block.get_rws(step, 0).stack_value().as_u64(); for (c, b) in self .codesize_bytes diff --git a/zkevm-circuits/src/evm_circuit/execution/comparator.rs b/zkevm-circuits/src/evm_circuit/execution/comparator.rs index e22fbf4864..9d593ef747 100644 --- a/zkevm-circuits/src/evm_circuit/execution/comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/comparator.rs @@ -115,7 +115,7 @@ impl ExecutionGadget for ComparatorGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // EQ op check self.is_eq.assign( @@ -133,13 +133,9 @@ impl ExecutionGadget for ComparatorGadget { F::from(OpcodeId::GT.as_u8() as u64), )?; - let indices = if is_gt == F::ONE { - [step.rw_indices[1], step.rw_indices[0]] - } else { - [step.rw_indices[0], step.rw_indices[1]] - }; - let [a, b] = indices.map(|idx| block.rws[idx].stack_value().to_le_bytes()); - let result = block.rws[step.rw_indices[2]].stack_value(); + let indices = if is_gt == F::ONE { [1, 0] } else { [0, 1] }; + let [a, b] = indices.map(|index| block.get_rws(step, index).stack_value().to_le_bytes()); + let result = block.get_rws(step, 2).stack_value(); // `a[0..16] <= b[0..16]` self.comparison_lo.assign( diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs new file mode 100644 index 0000000000..252e56c5d8 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -0,0 +1,927 @@ +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + param::{ + N_BYTES_ACCOUNT_ADDRESS, N_BYTES_GAS, N_BYTES_MEMORY_ADDRESS, N_BYTES_MEMORY_WORD_SIZE, + N_BYTES_U64, N_BYTES_WORD, + }, + step::ExecutionState, + util::{ + common_gadget::TransferGadget, + constraint_builder::{ + ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, + Transition::{Delta, To}, + }, + math_gadget::{ + ConstantDivisionGadget, ContractCreateGadget, IsZeroGadget, LtGadget, LtWordGadget, + }, + memory_gadget::{MemoryAddressGadget, MemoryExpansionGadget}, + not, CachedRegion, Cell, Word, + }, + witness::{Block, Call, ExecStep, Transaction}, + }, + table::{AccountFieldTag, CallContextFieldTag}, + util::{word::Word32Cell, Expr}, +}; +use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId, state_db::CodeDB}; +use eth_types::{ + evm_types::{GasCost, INIT_CODE_WORD_GAS}, + Field, ToBigEndian, ToLittleEndian, ToScalar, U256, +}; +use ethers_core::utils::keccak256; +use gadgets::util::{and, expr_from_bytes, or, select}; +use halo2_proofs::{circuit::Value, plonk::Error}; + +use std::iter::once; + +/// Gadget for CREATE and CREATE2 opcodes +#[derive(Clone, Debug)] +pub(crate) struct CreateGadget { + opcode: Cell, + tx_id: Cell, + reversion_info: ReversionInfo, + depth: Cell, + + is_create2: IsZeroGadget, + is_success: Cell, + was_warm: Cell, + value: Word32Cell, + + caller_balance: Word, + callee_reversion_info: ReversionInfo, + callee_nonce: Cell, + prev_code_hash: Cell, + transfer: TransferGadget, + create: ContractCreateGadget, + + init_code: MemoryAddressGadget, + init_code_word_size: ConstantDivisionGadget, + init_code_rlc: Cell, + keccak_output: Word, + + is_depth_in_range: LtGadget, + is_insufficient_balance: LtWordGadget, + is_nonce_in_range: LtGadget, + not_address_collision: IsZeroGadget, + + memory_expansion: MemoryExpansionGadget, + gas_left: ConstantDivisionGadget, +} + +impl ExecutionGadget + for CreateGadget +{ + const NAME: &'static str = "CREATE"; + + const EXECUTION_STATE: ExecutionState = S; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let opcode = cb.query_cell(); + cb.opcode_lookup(opcode.expr(), 1.expr()); + cb.require_in_set( + "Opcode is CREATE or CREATE2", + opcode.expr(), + vec![OpcodeId::CREATE2.expr(), OpcodeId::CREATE.expr()], + ); + let is_create2 = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CREATE2.expr()); + + // Use rw_counter of the step which triggers next call as its call_id. + let callee_call_id = cb.curr.state.rw_counter.clone(); + let current_call_id = cb.curr.state.call_id.clone(); + let is_success = cb.query_bool(); + + // read from call context + let tx_id = cb.call_context(None, CallContextFieldTag::TxId); + let depth = cb.call_context(None, CallContextFieldTag::Depth); + let mut reversion_info = cb.reversion_info_read(None); + + let keccak_output = cb.query_word_rlc(); + let create = ContractCreateGadget::construct(cb); + let contract_addr = expr_from_bytes(&keccak_output.cells[..N_BYTES_ACCOUNT_ADDRESS]); + let contract_addr_rlc = cb.word_rlc::( + keccak_output + .cells + .iter() + .take(N_BYTES_ACCOUNT_ADDRESS) + .map(Expr::expr) + .collect::>() + .try_into() + .unwrap(), + ); + + // stack operations + let value = cb.query_word32(); + let offset = cb.query_cell_phase2(); + let length = cb.query_word_rlc(); + cb.stack_pop(value.expr()); + cb.stack_pop(offset.expr()); + cb.stack_pop(length.expr()); + cb.condition(is_create2.expr(), |cb| { + cb.stack_pop(create.salt_word_rlc(cb).expr()); + }); + cb.stack_push(is_success.expr() * contract_addr_rlc); + + // read caller's balance and nonce + let caller_nonce = create.caller_nonce(); + let caller_balance = cb.query_word_rlc(); + cb.account_read( + create.caller_address(), + AccountFieldTag::Balance, + caller_balance.expr(), + ); + cb.account_read( + create.caller_address(), + AccountFieldTag::Nonce, + caller_nonce.expr(), + ); + + // Pre-check: call depth, user's nonce and user's balance + let is_depth_in_range = LtGadget::construct(cb, depth.expr(), 1025.expr()); + let is_insufficient_balance = + LtWordGadget::construct(cb, &caller_balance, &value.clone().into()); + let is_nonce_in_range = LtGadget::construct(cb, caller_nonce.expr(), u64::MAX.expr()); + let is_precheck_ok = and::expr([ + is_depth_in_range.expr(), + not::expr(is_insufficient_balance.expr()), + is_nonce_in_range.expr(), + ]); + + // verify gas cost + let init_code = MemoryAddressGadget::construct(cb, offset, length); + let memory_expansion = MemoryExpansionGadget::construct(cb, [init_code.address()]); + let init_code_word_size = ConstantDivisionGadget::construct( + cb, + init_code.length() + (N_BYTES_WORD - 1).expr(), + N_BYTES_WORD as u64, + ); + let keccak_gas_cost = init_code_word_size.quotient() + * select::expr( + is_create2.expr(), + (INIT_CODE_WORD_GAS + GasCost::COPY_SHA3).expr(), + INIT_CODE_WORD_GAS.expr(), + ); + let gas_cost = GasCost::CREATE.expr() + memory_expansion.gas_cost() + keccak_gas_cost; + let gas_remaining = cb.curr.state.gas_left.expr() - gas_cost.clone(); + let gas_left = ConstantDivisionGadget::construct(cb, gas_remaining.clone(), 64); + let callee_gas_left = gas_remaining - gas_left.quotient(); + + let was_warm = cb.query_bool(); + let init_code_rlc = cb.query_cell_phase2(); + let prev_code_hash = cb.query_cell(); + let callee_nonce = cb.query_cell(); + let not_address_collision = cb.condition(is_precheck_ok.expr(), |cb| { + // increase caller's nonce + cb.account_write( + create.caller_address(), + AccountFieldTag::Nonce, + caller_nonce.expr() + 1.expr(), + caller_nonce.expr(), + Some(&mut reversion_info), + ); + + // add callee to access list + cb.account_access_list_write( + tx_id.expr(), + contract_addr.clone(), + 1.expr(), + was_warm.expr(), + Some(&mut reversion_info), + ); + + // read contract's previous hash + cb.account_read( + contract_addr.clone(), + AccountFieldTag::CodeHash, + prev_code_hash.expr(), + ); + + // ErrContractAddressCollision, if any one of following criteria meets. + // Nonce is not zero or account code hash is not either 0 or EMPTY_CODE_HASH. + IsZeroGadget::construct( + cb, + callee_nonce.expr() + + prev_code_hash.expr() * (prev_code_hash.expr() - cb.empty_code_hash_rlc()), + ) + }); + + for (field_tag, value) in [ + ( + CallContextFieldTag::ProgramCounter, + cb.curr.state.program_counter.expr() + 1.expr(), + ), + ( + CallContextFieldTag::StackPointer, + cb.curr.state.stack_pointer.expr() + 2.expr() + is_create2.expr(), + ), + (CallContextFieldTag::GasLeft, gas_left.quotient()), + ( + CallContextFieldTag::MemorySize, + memory_expansion.next_memory_word_size(), + ), + ( + CallContextFieldTag::ReversibleWriteCounter, + cb.curr.state.reversible_write_counter.expr() + 2.expr(), + ), + ] { + cb.call_context_lookup(true.expr(), None, field_tag, value); + } + + let mut callee_reversion_info = cb.reversion_info_write(Some(callee_call_id.expr())); + let transfer = cb.condition( + and::expr([is_precheck_ok.clone(), not_address_collision.expr()]), + |cb| { + cb.condition(init_code.has_length(), |cb| { + // the init code is being copied from memory to bytecode, so a copy table lookup + // to verify that the associated fields for the copy event. + cb.copy_table_lookup( + current_call_id.expr(), + CopyDataType::Memory.expr(), + create.code_hash_word_rlc(cb), + CopyDataType::Bytecode.expr(), + init_code.offset(), + init_code.address(), + 0.expr(), // dst_addr + init_code.length(), // length + init_code_rlc.expr(), // rlc_acc + init_code.length(), // rwc_inc + ); + }); + + // keccak table lookup to verify contract address. + cb.keccak_table_lookup( + create.input_rlc(cb), + create.input_length(), + keccak_output.expr(), + ); + + // propagate is_persistent + cb.require_equal( + "callee_is_persistent == is_persistent ⋅ is_success", + callee_reversion_info.is_persistent(), + reversion_info.is_persistent() * is_success.expr(), + ); + + // transfer + let transfer = TransferGadget::construct( + cb, + create.caller_address(), + contract_addr.clone(), + 0.expr(), + 1.expr(), + value.clone(), + &mut callee_reversion_info, + ); + + // EIP 161, the nonce of a newly created contract is 1 + cb.account_write( + contract_addr.clone(), + AccountFieldTag::Nonce, + 1.expr(), + 0.expr(), + Some(&mut callee_reversion_info), + ); + + cb.condition(init_code.has_length(), |cb| { + for (field_tag, value) in [ + (CallContextFieldTag::CallerId, current_call_id.expr()), + (CallContextFieldTag::IsSuccess, is_success.expr()), + ( + CallContextFieldTag::IsPersistent, + callee_reversion_info.is_persistent(), + ), + (CallContextFieldTag::TxId, tx_id.expr()), + (CallContextFieldTag::CallerAddress, create.caller_address()), + (CallContextFieldTag::CalleeAddress, contract_addr), + ( + CallContextFieldTag::RwCounterEndOfReversion, + callee_reversion_info.rw_counter_end_of_reversion(), + ), + (CallContextFieldTag::Depth, depth.expr() + 1.expr()), + (CallContextFieldTag::IsRoot, false.expr()), + (CallContextFieldTag::IsStatic, false.expr()), + (CallContextFieldTag::IsCreate, true.expr()), + (CallContextFieldTag::CodeHash, create.code_hash_word_rlc(cb)), + ] { + cb.call_context_lookup( + true.expr(), + Some(callee_call_id.expr()), + field_tag, + value, + ); + } + + cb.require_step_state_transition(StepStateTransition { + // +1: move to new context + rw_counter: Delta(cb.rw_counter_offset() + 1.expr()), + call_id: To(callee_call_id.expr()), + is_root: To(false.expr()), + is_create: To(true.expr()), + code_hash: To(create.code_hash_word_rlc(cb)), + gas_left: To(callee_gas_left), + reversible_write_counter: To( + 1.expr() + transfer.reversible_w_delta().expr() + ), + ..StepStateTransition::new_context() + }) + }); + + // handle state transition if empty init code + cb.condition(not::expr(init_code.has_length()), |cb| { + for field_tag in [ + CallContextFieldTag::LastCalleeId, + CallContextFieldTag::LastCalleeReturnDataOffset, + CallContextFieldTag::LastCalleeReturnDataLength, + ] { + cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + } + cb.require_step_state_transition(StepStateTransition { + rw_counter: Delta(cb.rw_counter_offset()), + program_counter: Delta(1.expr()), + stack_pointer: Delta(2.expr() + is_create2.expr()), + gas_left: Delta(-gas_cost.expr()), + reversible_write_counter: Delta( + 3.expr() + transfer.reversible_w_delta().expr(), + ), + ..Default::default() + }) + }); + + transfer + }, + ); + + cb.condition( + is_success.expr() * (1.expr() - reversion_info.is_persistent()), + |cb| { + cb.require_equal( + "callee_rw_counter_end_of_reversion == rw_counter_end_of_reversion-(reversible_write_counter + 1)", + callee_reversion_info.rw_counter_end_of_reversion(), + reversion_info.rw_counter_of_reversion(1.expr()), + ); + }, + ); + + // Handle the case where an error of ErrDepth, ErrInsufficientBalance, + // ErrNonceUintOverflow or ErrContractAddressCollision occurred. + cb.condition( + or::expr([ + not::expr(is_precheck_ok), + not::expr(not_address_collision.expr()), + ]), + |cb| { + // Save caller's call state + for field_tag in [ + CallContextFieldTag::LastCalleeId, + CallContextFieldTag::LastCalleeReturnDataOffset, + CallContextFieldTag::LastCalleeReturnDataLength, + ] { + cb.call_context_lookup(true.expr(), None, field_tag, 0.expr()); + } + + cb.require_step_state_transition(StepStateTransition { + rw_counter: Delta(cb.rw_counter_offset()), + program_counter: Delta(1.expr()), + stack_pointer: Delta(2.expr() + is_create2.expr()), + memory_word_size: To(memory_expansion.next_memory_word_size()), + gas_left: Delta(-gas_cost.expr()), + ..StepStateTransition::default() + }); + }, + ); + + Self { + opcode, + reversion_info, + tx_id, + was_warm, + value, + depth, + callee_reversion_info, + transfer, + init_code, + init_code_rlc, + memory_expansion, + gas_left, + init_code_word_size, + create, + caller_balance, + is_depth_in_range, + is_insufficient_balance, + is_nonce_in_range, + keccak_output, + not_address_collision, + is_success, + prev_code_hash, + callee_nonce, + is_create2, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + let opcode = step.opcode().unwrap(); + let is_create2 = opcode == OpcodeId::CREATE2; + self.opcode + .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; + self.is_create2.assign( + region, + offset, + F::from(opcode.as_u64()) - F::from(OpcodeId::CREATE2.as_u64()), + )?; + + self.tx_id + .assign(region, offset, Value::known(tx.id.to_scalar().unwrap()))?; + self.depth.assign( + region, + offset, + Value::known(call.depth.to_scalar().unwrap()), + )?; + self.reversion_info.assign( + region, + offset, + block.get_rws(step, 2).call_context_value().as_usize(), + block.get_rws(step, 3).call_context_value().as_usize() != 0, + )?; + + // 0..3 : TxId, Depth, RwCounterEndOfReversion and IsPersistent + // stack value starts from 4 + let [value, init_code_start, init_code_length] = + [4, 5, 6].map(|idx| block.get_rws(step, idx).stack_value()); + self.value + .assign(region, offset, Some(value.to_le_bytes()))?; + let salt = if is_create2 { + block.get_rws(step, 7).stack_value() + } else { + U256::zero() + }; + let rw_offset = if is_create2 { 8 } else { 7 }; + + // Pre-check: call depth, user's nonce and user's balance + let (caller_balance, _) = block.get_rws(step, rw_offset + 1).account_value_pair(); + let (caller_nonce, _) = block.get_rws(step, rw_offset + 2).account_value_pair(); + let is_precheck_ok = + if call.depth < 1025 && caller_balance >= value && caller_nonce.as_u64() < u64::MAX { + 1 + } else { + 0 + }; + + self.caller_balance + .assign(region, offset, Some(caller_balance.to_le_bytes()))?; + let (callee_prev_code_hash, was_warm) = if is_precheck_ok == 1 { + let (_, was_warm) = block + .get_rws(step, rw_offset + 4) + .tx_access_list_value_pair(); + let (callee_prev_code_hash, _) = + block.get_rws(step, rw_offset + 5).account_value_pair(); + (callee_prev_code_hash, was_warm) + } else { + (U256::from(0), false) + }; + + // 3 RWs while is_precheck_ok is true + // account_write(caller), tx_access_list_write(callee) and account_read(callee) + let [callee_rw_counter_end_of_reversion, callee_is_persistent] = [ + rw_offset + 11 - (1 - is_precheck_ok) * 3, + rw_offset + 12 - (1 - is_precheck_ok) * 3, + ] + .map(|i| block.get_rws(step, i).call_context_value()); + + // retrieve code_hash for creating address + let is_address_collision = !callee_prev_code_hash.is_zero(); + let code_hash_previous_rlc = if is_address_collision { + region.code_hash(callee_prev_code_hash) + } else { + Value::known(F::ZERO) + }; + self.prev_code_hash + .assign(region, offset, code_hash_previous_rlc)?; + self.not_address_collision.assign( + region, + offset, + F::from((is_address_collision).into()), + )?; + + // gas cost of memory expansion + let init_code_address = + self.init_code + .assign(region, offset, init_code_start, init_code_length)?; + let (_, memory_expansion_gas_cost) = self.memory_expansion.assign( + region, + offset, + step.memory_word_size(), + [init_code_address], + )?; + let (init_code_word_size, _) = self.init_code_word_size.assign( + region, + offset, + (31u64 + init_code_length.as_u64()).into(), + )?; + let gas_left = step.gas_left + - GasCost::CREATE + - memory_expansion_gas_cost + - u64::try_from(init_code_word_size).unwrap() + * if is_create2 { + INIT_CODE_WORD_GAS + GasCost::COPY_SHA3 + } else { + INIT_CODE_WORD_GAS + }; + self.gas_left.assign(region, offset, gas_left.into())?; + self.callee_reversion_info.assign( + region, + offset, + callee_rw_counter_end_of_reversion.low_u64() as usize, + callee_is_persistent.low_u64() != 0, + )?; + + // assign witness while pre-check is okay + let copy_rw_increase = init_code_length.as_usize(); + let code_hash = if is_precheck_ok == 1 { + // transfer + let [caller_balance_pair, callee_balance_pair] = if !value.is_zero() { + [ + rw_offset + copy_rw_increase + 14, + rw_offset + copy_rw_increase + 15, + ] + .map(|i| block.get_rws(step, i).account_value_pair()) + } else { + [(0.into(), 0.into()), (0.into(), 0.into())] + }; + self.transfer.assign( + region, + offset, + caller_balance_pair, + callee_balance_pair, + value, + )?; + + // copy_table_lookup + let values: Vec<_> = (rw_offset + 13..rw_offset + 13 + copy_rw_increase) + .map(|i| block.get_rws(step, i).memory_value()) + .collect(); + let code_hash = CodeDB::hash(&values); + let keccak_input: Vec = if is_create2 { + once(0xffu8) + .chain(call.address.to_fixed_bytes()) + .chain(salt.to_be_bytes()) + .chain(code_hash.to_fixed_bytes()) + .collect() + } else { + let mut stream = ethers_core::utils::rlp::RlpStream::new(); + stream.begin_list(2); + stream.append(&call.address); + stream.append(&caller_nonce); + stream.out().to_vec() + }; + let mut keccak_output = keccak256(keccak_input); + keccak_output.reverse(); + + self.keccak_output + .assign(region, offset, Some(keccak_output))?; + self.init_code_rlc.assign( + region, + offset, + region.keccak_rlc(&values.iter().rev().cloned().collect::>()), + )?; + self.was_warm + .assign(region, offset, Value::known(F::from(was_warm.into())))?; + self.callee_nonce + .assign(region, offset, Value::known(F::ZERO))?; + + code_hash + } else { + CodeDB::empty_code_hash() + }; + + self.create.assign( + region, + offset, + call.address, + caller_nonce.as_u64(), + Some(U256::from(code_hash.to_fixed_bytes())), + Some(salt), + )?; + + // If transfer value is zero, there is no balance update + let transfer_offset = if value.is_zero() { 2 } else { 0 }; + self.is_success.assign( + region, + offset, + Value::known(if is_precheck_ok == 0 || is_address_collision { + F::ZERO + } else if init_code_length.as_usize() == 0 { + F::ONE + } else { + block + .get_rws(step, 18 + rw_offset + copy_rw_increase - transfer_offset) + .call_context_value() + .to_scalar() + .unwrap() + }), + )?; + + self.is_insufficient_balance + .assign(region, offset, caller_balance, value)?; + self.is_depth_in_range + .assign(region, offset, F::from(call.depth as u64), F::from(1025))?; + self.is_nonce_in_range.assign( + region, + offset, + F::from(caller_nonce.as_u64()), + F::from(u64::MAX), + )?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::test_util::CircuitTestBuilder; + use eth_types::{ + address, bytecode, evm_types::OpcodeId, geth_types::Account, word, Address, Bytecode, Word, + }; + use itertools::Itertools; + use lazy_static::lazy_static; + use mock::{eth, TestContext}; + + const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); + lazy_static! { + static ref CALLER_ADDRESS: Address = address!("0x00bbccddee000000000000000000000000002400"); + } + + fn run_test_circuits(ctx: TestContext<2, 1>) { + CircuitTestBuilder::new_from_test_ctx(ctx).run(); + } + + // RETURN or REVERT with a piece of random data, + // We don't care the init code content bcs we don't really run it. + // Here, we assign data with [0x60; 10], + fn initialization_bytecode(is_success: bool) -> Bytecode { + let memory_bytes = [0x60; 10]; + let memory_address = 0; + let memory_value = Word::from_big_endian(&memory_bytes); + let mut code = bytecode! { + PUSH10(memory_value) + PUSH1(memory_address) + MSTORE + PUSH2(5) + PUSH2(32u64 - u64::try_from(memory_bytes.len()).unwrap()) + }; + code.write_op(if is_success { + OpcodeId::RETURN + } else { + OpcodeId::REVERT + }); + code + } + + fn creator_bytecode( + initialization_bytecode: Bytecode, + value: Word, + is_create2: bool, + is_persistent: bool, + ) -> Bytecode { + let initialization_bytes = initialization_bytecode.code(); + let mut code = bytecode! { + PUSH32(Word::from_big_endian(&initialization_bytes)) + PUSH1(0) + MSTORE + }; + if is_create2 { + code.append(&bytecode! {PUSH1(45)}); // salt; + } + code.append(&bytecode! { + PUSH1(initialization_bytes.len()) // size + PUSH1(32 - initialization_bytes.len()) // length + PUSH2(value) // value + }); + code.write_op(if is_create2 { + OpcodeId::CREATE2 + } else { + OpcodeId::CREATE + }); + if !is_persistent { + code.append(&bytecode! { + PUSH1(0) + PUSH1(0) + REVERT + }); + } + code + } + + fn creator_bytecode_address_collision(initialization_bytecode: Bytecode) -> Bytecode { + let initialization_bytes = initialization_bytecode.code(); + let mut code = bytecode! { + PUSH32(Word::from_big_endian(&initialization_bytes)) + PUSH1(0) + MSTORE + }; + + code.append(&bytecode! {PUSH1(45)}); // salt; + code.append(&bytecode! { + PUSH1(initialization_bytes.len()) // size + PUSH1(32 - initialization_bytes.len()) // length + PUSH2(23414) // value (endowment in CREATE2) + }); + code.write_op(OpcodeId::CREATE2); + + // construct address collision by create2 twice + code.append(&bytecode! {PUSH1(45)}); // salt; + + code.append(&bytecode! { + PUSH1(initialization_bytes.len()) // size + PUSH1(32 - initialization_bytes.len()) // length + PUSH2(23414) // value (endowment in CREATE2) + }); + code.write_op(OpcodeId::CREATE2); + code.append(&bytecode! { + PUSH1(0) + PUSH1(0) + REVERT + }); + + code + } + + fn test_context(caller: Account) -> TestContext<2, 1> { + TestContext::new( + None, + |accs| { + accs[0] + .address(address!("0x000000000000000000000000000000000000cafe")) + .balance(eth(10)); + accs[1].account(&caller); + }, + |mut txs, accs| { + txs[0] + .from(accs[0].address) + .to(accs[1].address) + .gas(word!("0x2386F26FC10000")); + }, + |block, _| block, + ) + .unwrap() + } + + #[test] + fn test_create() { + for ((is_success, is_create2), is_persistent) in [true, false] + .iter() + .cartesian_product(&[true, false]) + .cartesian_product(&[true, false]) + { + let init_code = initialization_bytecode(*is_success); + let root_code = creator_bytecode(init_code, 23414.into(), *is_create2, *is_persistent); + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: 1.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + } + + #[test] + fn test_create_rlp_nonce() { + for nonce in [0, 1, 127, 128, 255, 256, 0x10000, u64::MAX - 1] { + let caller = Account { + address: *CALLER_ADDRESS, + code: creator_bytecode(initialization_bytecode(true), 23414.into(), false, true) + .into(), + nonce: nonce.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)) + } + } + + #[test] + fn test_create_empty_init_code() { + for is_create2 in [true, false] { + let caller = Account { + address: *CALLER_ADDRESS, + code: creator_bytecode(vec![].into(), 23414.into(), is_create2, true).into(), + nonce: 10.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + } + + #[test] + fn test_create_overflow_offset_and_zero_size() { + for is_create2 in [true, false] { + let mut bytecode = bytecode! { + PUSH1(0) // size + PUSH32(Word::MAX) // offset + PUSH2(23414) // value + }; + bytecode.write_op(if is_create2 { + OpcodeId::CREATE2 + } else { + OpcodeId::CREATE + }); + let caller = Account { + address: *CALLER_ADDRESS, + code: bytecode.into(), + nonce: 10.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + } + + #[test] + fn test_create_address_collision_error() { + let initialization_code = initialization_bytecode(false); + let root_code = creator_bytecode_address_collision(initialization_code); + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: 1.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + + // Ignore this test case. It could run successfully but slow for CI. + #[ignore] + #[test] + fn test_create_error_depth() { + let code = bytecode! { + PUSH1(0x20) + PUSH1(0x0) + PUSH1(0x0) + CODECOPY + PUSH1(0x20) + PUSH1(0x0) + PUSH1(0x0) + CREATE + } + .into(); + let caller = Account { + address: *CALLER_ADDRESS, + code, + nonce: 1.into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + + #[test] + fn test_create_insufficient_balance() { + let value = 23414.into(); + for is_create2 in [true, false] { + let caller = Account { + address: mock::MOCK_ACCOUNTS[0], + nonce: 1.into(), + code: creator_bytecode(initialization_bytecode(false), value, is_create2, true) + .into(), + balance: value - 1, + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + } + + #[test] + fn test_create_nonce_uint_overflow() { + // TRICKY: + // Caller nonce could not be set to `u64::MAX` directly. Since geth + // [preCheck](https://github.com/ethereum/go-ethereum/blob/4a9fa31450d3cdcea84735b68cd5a0a8450473f8/core/state_transition.go#L262) + // function has a check for nonce uint overflow. So there are two nested + // CREATE (or CREATE2) in bytecode, which could increase nonce from + // `u64::MAX - 1` to `u64::MAX` in the internal loop. + for is_create2 in [true, false] { + let init_code = initialization_bytecode(true); + let mut root_code = creator_bytecode(init_code.clone(), 23414.into(), is_create2, true); + root_code.append(&root_code.clone()); + + // bytecodes.into_iter().for_each(|code| { + let caller = Account { + address: *CALLER_ADDRESS, + code: root_code.into(), + nonce: (u64::MAX - 1).into(), + balance: eth(10), + ..Default::default() + }; + run_test_circuits(test_context(caller)); + } + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/dummy.rs b/zkevm-circuits/src/evm_circuit/execution/dummy.rs index 693b533baf..eda17c468d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dummy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dummy.rs @@ -55,16 +55,16 @@ impl // process of being implemented but are still // using DummyGadget. // See `bus-mapping/src/evm/opcodes.rs` - if step.rw_indices.len() != N_POP + N_PUSH { + if step.rw_indices_len() != N_POP + N_PUSH { log::warn!("DummyGadget: wrong number of rw indices for {:?}", step); } for i in 0..N_POP { - let value = block.rws[step.rw_indices[i]].stack_value(); + let value = block.get_rws(step, i).stack_value(); self.pops[i].assign(region, offset, Some(value.to_le_bytes()))?; } for i in 0..N_PUSH { - let value = block.rws[step.rw_indices[N_POP + i]].stack_value(); + let value = block.get_rws(step, N_POP + i).stack_value(); self.pushes[i].assign(region, offset, Some(value.to_le_bytes()))?; } Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/dup.rs b/zkevm-circuits/src/evm_circuit/execution/dup.rs index 51c808b978..9dc0816dd7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dup.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dup.rs @@ -65,7 +65,7 @@ impl ExecutionGadget for DupGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value.assign(region, offset, region.word_rlc(value))?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index 66b1f7f075..1f1d39c9a6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -124,7 +124,7 @@ impl ExecutionGadget for EndBlockGadget { step: &ExecStep, ) -> Result<(), Error> { self.is_empty_block - .assign(region, offset, F::from(step.rw_counter as u64 - 1))?; + .assign(region, offset, F::from(u64::from(step.rwc) - 1))?; let max_rws = F::from(block.circuits_params.max_rws as u64); let max_rws_assigned = self.max_rws.assign(region, offset, Value::known(max_rws))?; @@ -137,7 +137,7 @@ impl ExecutionGadget for EndBlockGadget { let max_txs_assigned = self.max_txs.assign(region, offset, Value::known(max_txs))?; // 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() { + if !step.rw_indices_len() == 0 { region.constrain_constant(max_rws_assigned, max_rws)?; region.constrain_constant(max_txs_assigned, max_txs)?; } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index fe6e9b45b4..4647efbce7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -17,14 +17,13 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{ - BlockContextFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, TxReceiptFieldTag, - }, + table::{BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag, TxReceiptFieldTag}, util::{ word::{Word, WordExpr}, Expr, }, }; +use bus_mapping::operation::Target; use eth_types::{evm_types::MAX_REFUND_QUOTIENT_OF_GAS_USED, Field, ToScalar}; use halo2_proofs::{circuit::Value, plonk::Error}; use strum::EnumCount; @@ -210,9 +209,9 @@ impl ExecutionGadget for EndTxGadget { step: &ExecStep, ) -> Result<(), Error> { let gas_used = tx.gas - step.gas_left; - let (refund, _) = block.rws[step.rw_indices[2]].tx_refund_value_pair(); + let (refund, _) = block.get_rws(step, 2).tx_refund_value_pair(); let [(caller_balance, caller_balance_prev), (coinbase_balance, coinbase_balance_prev)] = - [step.rw_indices[3], step.rw_indices[4]].map(|idx| block.rws[idx].account_value_pair()); + [3, 4].map(|index| block.get_rws(step, index).account_value_pair()); self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; @@ -291,7 +290,7 @@ impl ExecutionGadget for EndTxGadget { // 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, + Target::TxReceipt, (tx.id - 2) * (TxReceiptFieldTag::COUNT + 1) + 2, )]; rw.receipt_value() @@ -318,9 +317,10 @@ impl ExecutionGadget for EndTxGadget { mod test { use crate::test_util::CircuitTestBuilder; use bus_mapping::circuit_input_builder::CircuitsParams; - use eth_types::{self, bytecode}; - - use mock::{eth, test_ctx::helpers::account_0_code_account_1_no_code, TestContext}; + use eth_types::{self, bytecode, Word}; + use mock::{ + eth, gwei, test_ctx::helpers::account_0_code_account_1_no_code, TestContext, MOCK_ACCOUNTS, + }; fn test_ok(ctx: TestContext) { CircuitTestBuilder::new_from_test_ctx(ctx) @@ -333,19 +333,144 @@ mod test { #[test] fn end_tx_gadget_simple() { - // TODO: Enable this with respective code when SSTORE is implemented. - // Tx with non-capped refund - // test_ok(vec![mock_tx( - // address!("0x00000000000000000000000000000000000000fe"), - // Some(27000), - // None, - // )]); - // Tx with capped refund - // test_ok(vec![mock_tx( - // address!("0x00000000000000000000000000000000000000fe"), - // Some(65000), - // None, - // )]); + let key_1: Word = 0x030201.into(); + let original_value: Word = 0x060504.into(); + let zero_value: Word = 0x0.into(); + + // Testing refunds + // When setting smart contract's non-zero values in storage to zero, + // the sender of the transaction receives some refund (each transaction's step may include a + // non-zero refund field). Moreover, the sum of the refunds is capped at $max_refund + // = gas_used / MAX_REFUND_QUOTIENT_OF_GAS_USED$ where MAX_REFUND_QUOTIENT_OF_GAS_USED = 5 (see EIP EIP 3529, https://eips.ethereum.org/EIPS/eip-3529) + // We test here that the refunds, capped or uncapped, have been correctly implemented by + // configuring an account, MOCK_ACCOUNTS[0], with a non null storage and some code cleaning + // the storage. + + // 1) Testing Tx with non capped refunds + // In this first test, we reset only one value to minimize the gas_used so that the refund + // is smaller than max_refund. More particularly, we expect here to have 21_000 (tx) + // + 5_000 (sstore) + 3 (push) + 3 (push) gas used, hence a max_refund of 5_201 while + // the refund should add up to 4_800 (refund of 1 sstore). Hence, we can check after the + // transaction that MOCK_ACCOUNTS[0]'s balance decreased by: + // (21_000 + 5_000 + 3 + 3 - 4_800) * 2_000_000_000 (=gas_cost). + // To see the refund and balances, uncomment the code below, set TestContext to + // TestContext::<4, 2> and run the following command: cargo test + // evm_circuit::execution::end_tx::test::end_tx_gadget_simple -- --exact --nocapture + let bytecode_uncapped = bytecode! { + PUSH32(zero_value) + PUSH32(key_1) + SSTORE + STOP + }; + let storage_uncapped = vec![(key_1, original_value)].into_iter(); + + let ctx = TestContext::<2, 1>::new( + None, + |accs| { + accs[0] + .address(MOCK_ACCOUNTS[0]) + .balance(Word::from(10u64.pow(19))) + .code(bytecode_uncapped) + .storage(storage_uncapped.into_iter()); + accs[1] + .address(MOCK_ACCOUNTS[1]) + .balance(Word::from(10u64.pow(19))); + // accs[2] + // .address(MOCK_ACCOUNTS[2]) + // .code(bytecode! { + // PUSH32(MOCK_ACCOUNTS[1]) + // BALANCE + // STOP + // }) + // .balance(Word::from(10u64.pow(19))); + // accs[3] + // .address(MOCK_ACCOUNTS[3]) + // .balance(Word::from(10u64.pow(19))); + }, + |mut txs, accs| { + txs[0] + .to(accs[0].address) + .from(accs[1].address) + .gas(Word::from(30_000)) + .gas_price(gwei(2)) + .nonce(0); + // txs[1] + // .to(accs[2].address) + // .from(accs[3].address) + // .gas(Word::from(30_000)) + // .gas_price(gwei(2)) + // .nonce(0); + }, + |block, _tx| block, + ) + .unwrap(); + + // for (i, trace) in ctx.geth_traces.iter().enumerate() { + // println!( + // "\n---------- trace {}: gas={}, failed={}, return_value={}", + // i, trace.gas, trace.failed, trace.return_value + // ); + // for (j, step) in trace.struct_logs.iter().enumerate() { + // println!( + // " step {}: gas={}, gas_cost={}, refund={}, top_stack={}", + // j, + // step.gas, + // step.gas_cost, + // step.refund, + // if step.stack.last().is_ok() { + // step.stack.last().unwrap() + // } else { + // Word::from(0) + // }, + // ) + // } + // } + test_ok(ctx); + + // 2) Testing Tx with capped refunds + // In this second test, we reset several values so that the refund is greater than + // max_refund and hence the effective refund is capped. More particularly, we expect + // here to have 21_000 (tx) + 2 * (5_000 (sstore) + 3 (push) + 3 (push)) gas used, hence a + // max_refund of 6_202 while the refund should add up to 9_600 (refund of 2 sstore). + // Hence, we can check after the transaction that MOCK_ACCOUNTS[0]'s balance decreased by: + // (21_000 + 2 * (5_000 + 3 + 3) - 6_202) * 2_000_000_000 (=gas_cost). + let key_2: Word = 0x030202.into(); + let bytecode_capped = bytecode! { + PUSH32(zero_value) + PUSH32(key_1) + SSTORE + PUSH32(zero_value) + PUSH32(key_2) + SSTORE + STOP + }; + let storage_capped = vec![(key_1, original_value), (key_2, original_value)].into_iter(); + + test_ok( + TestContext::<2, 1>::new( + None, + |accs| { + accs[0] + .address(MOCK_ACCOUNTS[0]) + .balance(Word::from(10u64.pow(19))) + .code(bytecode_capped) + .storage(storage_capped); + accs[1] + .address(MOCK_ACCOUNTS[1]) + .balance(Word::from(10u64.pow(19))); + }, + |mut txs, accs| { + txs[0] + .to(accs[0].address) + .from(accs[1].address) + .gas(Word::from(50_000)) + .gas_price(gwei(2)) + .nonce(0); + }, + |block, _tx| block, + ) + .unwrap(), + ); // Multiple txs test_ok( diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs index 72e0767dd8..888c340aba 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -112,13 +112,13 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_jumpi = opcode == OpcodeId::JUMPI; self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; let condition = if is_jumpi { - block.rws[step.rw_indices[1]].stack_value() + block.get_rws(step, 1).stack_value() } else { U256::zero() }; @@ -132,7 +132,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { self.code_len .assign(region, offset, Value::known(F::from(code_len)))?; - let dest = block.rws[step.rw_indices[0]].stack_value(); + let dest = block.get_rws(step, 0).stack_value(); self.dest.assign(region, offset, dest, F::from(code_len))?; // set default value in case can not find value, is_code from bytecode table @@ -185,7 +185,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; @@ -258,7 +258,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -382,16 +382,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] 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 41b4e69026..868a830306 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs @@ -56,7 +56,7 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = F::from(step.opcode.unwrap().as_u64()); + let opcode = F::from(step.opcode().unwrap().as_u64()); self.opcode.assign(region, offset, Value::known(opcode))?; self.common_error_gadget diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs index 5dfa5fd744..df949011e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -120,37 +120,29 @@ impl ExecutionGadget for ErrorOOGCallGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call_or_callcode = usize::from([OpcodeId::CALL, OpcodeId::CALLCODE].contains(&opcode)); let [tx_id, is_static] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].call_context_value()); - let stack_index = 2; - let [gas, callee_address] = [ - step.rw_indices[stack_index], - step.rw_indices[stack_index + 1], - ] - .map(|idx| block.rws[idx].stack_value()); + [0, 1].map(|index| block.get_rws(step, index).call_context_value()); + let [gas, callee_address] = [2, 3].map(|index| block.get_rws(step, index).stack_value()); let value = if is_call_or_callcode == 1 { - block.rws[step.rw_indices[stack_index + 2]].stack_value() + block.get_rws(step, 4).stack_value() } else { U256::zero() }; - let [cd_offset, cd_length, rd_offset, rd_length] = [ - step.rw_indices[stack_index + is_call_or_callcode + 2], - step.rw_indices[stack_index + is_call_or_callcode + 3], - step.rw_indices[stack_index + is_call_or_callcode + 4], - step.rw_indices[stack_index + is_call_or_callcode + 5], - ] - .map(|idx| block.rws[idx].stack_value()); - - let callee_code_hash = block.rws[step.rw_indices[9 + is_call_or_callcode]] + let [cd_offset, cd_length, rd_offset, rd_length] = + [4, 5, 6, 7].map(|i| block.get_rws(step, is_call_or_callcode + i).stack_value()); + + let callee_code_hash = block + .get_rws(step, 9 + is_call_or_callcode) .account_value_pair() .0; let callee_exists = !callee_code_hash.is_zero(); - let (is_warm, is_warm_prev) = - block.rws[step.rw_indices[10 + is_call_or_callcode]].tx_access_list_value_pair(); + let (is_warm, is_warm_prev) = block + .get_rws(step, 10 + is_call_or_callcode) + .tx_access_list_value_pair(); let memory_expansion_gas_cost = self.call.assign( region, @@ -235,7 +227,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; use std::default::Default; @@ -296,7 +288,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -310,16 +302,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code.clone()) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code.clone()) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(caller); + accs[2].account(callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs index e13d874b9b..90bfd06163 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs @@ -65,7 +65,7 @@ impl ExecutionGadget for ErrorOOGConstantGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -94,7 +94,7 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, + Address, ToWord, Word, U64, }; use mock::{ @@ -104,8 +104,8 @@ mod test { fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -226,16 +226,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -256,7 +248,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs index ed6da019aa..98c262f7b5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs @@ -67,7 +67,7 @@ impl ExecutionGadget for ErrorOOGExpGadget { // static_gas = 10 // dynamic_gas = exponent_byte_size * 50 // gas_cost = dynamic_gas + static_gas - exponent_byte_size.byte_size() * GasCost::EXP_BYTE_TIMES.0.expr() + exponent_byte_size.byte_size() * GasCost::EXP_BYTE_TIMES.expr() + OpcodeId::EXP.constant_gas_cost().expr(), ); @@ -97,8 +97,8 @@ impl ExecutionGadget for ErrorOOGExpGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); - let [base, exponent] = [0, 1].map(|idx| block.rws[step.rw_indices[idx]].stack_value()); + let opcode = step.opcode().unwrap(); + let [base, exponent] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); log::debug!( "ErrorOutOfGasEXP: gas_left = {}, gas_cost = {}", @@ -172,9 +172,9 @@ mod tests { EXP }; - let gas_cost = OpcodeId::PUSH32.constant_gas_cost().0 * 2 - + OpcodeId::EXP.constant_gas_cost().0 - + ((exponent.bits() as u64 + 7) / 8) * GasCost::EXP_BYTE_TIMES.0; + let gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 2 + + OpcodeId::EXP.constant_gas_cost() + + ((exponent.bits() as u64 + 7) / 8) * GasCost::EXP_BYTE_TIMES; Self { bytecode, gas_cost } } @@ -189,7 +189,7 @@ mod tests { txs[0] .from(accs[1].address) .to(accs[0].address) - .gas((GasCost::TX.0 + testing_data.gas_cost - 1).into()); + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); }, |block, _tx| block.number(0xcafe_u64), ) diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs index 075088eb09..0892ce5958 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -67,8 +67,8 @@ impl ExecutionGadget for ErrorOOGLogGadget { // access let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); - let gas_cost = GasCost::LOG.as_u64().expr() - + GasCost::LOG.as_u64().expr() * topic_count + let gas_cost = GasCost::LOG.expr() + + GasCost::LOG.expr() * topic_count + 8.expr() * memory_address.length() + memory_expansion.gas_cost(); @@ -103,12 +103,11 @@ impl ExecutionGadget for ErrorOOGLogGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - let [memory_start, msize] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [memory_start, msize] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address @@ -146,7 +145,7 @@ mod test { use bus_mapping::evm::OpcodeId; use eth_types::{ self, address, bytecode, bytecode::Bytecode, evm_types::GasCost, geth_types::Account, - Address, ToWord, Word, + Address, ToWord, Word, U64, }; use mock::{ @@ -155,8 +154,8 @@ mod test { fn gas(call_data: &[u8]) -> Word { Word::from( - GasCost::TX.as_u64() - + 2 * OpcodeId::PUSH32.constant_gas_cost().as_u64() + GasCost::TX + + 2 * OpcodeId::PUSH32.constant_gas_cost() + call_data .iter() .map(|&x| if x == 0 { 4 } else { 16 }) @@ -268,16 +267,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -298,7 +289,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs index b9511f0000..63499da840 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_memory_copy.rs @@ -155,7 +155,7 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_extcodecopy = opcode == OpcodeId::EXTCODECOPY; log::debug!( @@ -167,8 +167,8 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { let (is_warm, external_address) = if is_extcodecopy { ( - block.rws[step.rw_indices[1]].tx_access_list_value_pair().0, - block.rws[step.rw_indices[2]].stack_value(), + block.get_rws(step, 1).tx_access_list_value_pair().0, + block.get_rws(step, 2).stack_value(), ) } else { (false, U256::zero()) @@ -176,7 +176,7 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { let rw_offset = if is_extcodecopy { 3 } else { 0 }; let [dst_offset, src_offset, copy_size] = [rw_offset, rw_offset + 1, rw_offset + 2] - .map(|idx| block.rws[step.rw_indices[idx]].stack_value()); + .map(|index| block.get_rws(step, index).stack_value()); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -213,7 +213,7 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { region, offset, Value::known(F::from(step.gas_left)), - Value::known(F::from(constant_gas_cost.0 + memory_copier_gas)), + Value::known(F::from(constant_gas_cost + memory_copier_gas)), )?; self.is_extcodecopy.assign( region, @@ -301,8 +301,8 @@ mod tests { let memory_word_size = (dst_offset + copy_size + 31) / 32; - let gas_cost = OpcodeId::PUSH32.constant_gas_cost().0 * 3 - + opcode.constant_gas_cost().0 + let gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 3 + + opcode.constant_gas_cost() + memory_copier_gas_cost(0, memory_word_size, copy_size); Self { bytecode, gas_cost } @@ -321,8 +321,8 @@ mod tests { let memory_word_size = (dst_offset + copy_size + 31) / 32; - let mut gas_cost = OpcodeId::PUSH32.constant_gas_cost().0 * 4 - + GasCost::COLD_ACCOUNT_ACCESS.0 + let mut gas_cost = OpcodeId::PUSH32.constant_gas_cost() * 4 + + GasCost::COLD_ACCOUNT_ACCESS + memory_copier_gas_cost(0, memory_word_size, copy_size); if is_warm { @@ -334,8 +334,8 @@ mod tests { EXTCODECOPY }); - gas_cost += OpcodeId::PUSH32.constant_gas_cost().0 * 4 - + GasCost::WARM_ACCESS.0 + gas_cost += OpcodeId::PUSH32.constant_gas_cost() * 4 + + GasCost::WARM_ACCESS + memory_copier_gas_cost(memory_word_size, memory_word_size, copy_size); } @@ -352,7 +352,7 @@ mod tests { txs[0] .from(accs[1].address) .to(accs[0].address) - .gas((GasCost::TX.0 + testing_data.gas_cost - 1).into()); + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); }, |block, _tx| block.number(0xcafe_u64), ) diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs index ac00733760..a17e9487f2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_sload_sstore.rs @@ -115,7 +115,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { let insufficient_gas_sentry = LtGadget::construct( cb, cb.curr.state.gas_left.expr(), - (GasCost::SSTORE_SENTRY.0 + 1).expr(), + (GasCost::SSTORE_SENTRY + 1).expr(), ); cb.require_equal( "Gas left is less than gas cost or gas sentry (only for SSTORE)", @@ -159,15 +159,14 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_sstore = opcode == OpcodeId::SSTORE; - let key = block.rws[step.rw_indices[3]].stack_value(); - let (is_warm, _) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let key = block.get_rws(step, 3).stack_value(); + let (is_warm, _) = block.get_rws(step, 4).tx_access_list_value_pair(); let (value, value_prev, original_value, gas_cost) = if is_sstore { - let value = block.rws[step.rw_indices[5]].stack_value(); - let (_, value_prev, _, original_value) = - block.rws[step.rw_indices[6]].storage_value_aux(); + let value = block.get_rws(step, 5).stack_value(); + let (_, value_prev, _, original_value) = block.get_rws(step, 6).storage_value_aux(); let gas_cost = cal_sstore_gas_cost_for_assignment(value, value_prev, original_value, is_warm); (value, value_prev, original_value, gas_cost) @@ -181,7 +180,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { is_sstore, step.gas_left, gas_cost, - if is_sstore { GasCost::SSTORE_SENTRY.0 } else { 0 }, + if is_sstore { GasCost::SSTORE_SENTRY } else { 0 }, ); self.opcode @@ -229,7 +228,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { region, offset, Value::known(F::from(step.gas_left)), - Value::known(F::from(GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap())), + Value::known(F::from(GasCost::SSTORE_SENTRY.checked_add(1).unwrap())), )?; // Additional one stack pop and one account storage read for SSTORE. @@ -390,14 +389,14 @@ mod test { SLOAD }; let mut gas_cost = - OpcodeId::PUSH32.constant_gas_cost().0 + cal_sload_gas_cost_for_assignment(false); + OpcodeId::PUSH32.constant_gas_cost() + cal_sload_gas_cost_for_assignment(false); if is_warm { bytecode.append(&bytecode! { PUSH32(key) SLOAD }); - gas_cost += OpcodeId::PUSH32.constant_gas_cost().0 - + cal_sload_gas_cost_for_assignment(true); + gas_cost += + OpcodeId::PUSH32.constant_gas_cost() + cal_sload_gas_cost_for_assignment(true); } Self { @@ -425,10 +424,10 @@ mod test { original_value, false, ); - let mut gas_cost = 2 * OpcodeId::PUSH32.constant_gas_cost().0 + let mut gas_cost = 2 * OpcodeId::PUSH32.constant_gas_cost() + max( sstore_gas_cost, - GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap(), + GasCost::SSTORE_SENTRY.checked_add(1).unwrap(), ); if is_warm { bytecode.append(&bytecode! { @@ -442,10 +441,10 @@ mod test { original_value, true, ); - gas_cost += 2 * OpcodeId::PUSH32.constant_gas_cost().0 + gas_cost += 2 * OpcodeId::PUSH32.constant_gas_cost() + max( sstore_gas_cost, - GasCost::SSTORE_SENTRY.0.checked_add(1).unwrap(), + GasCost::SSTORE_SENTRY.checked_add(1).unwrap(), ); } @@ -477,7 +476,7 @@ mod test { txs[0] .from(accs[1].address) .to(accs[0].address) - .gas((GasCost::TX.0 + testing_data.gas_cost - 1).into()); + .gas((GasCost::TX + testing_data.gas_cost - 1).into()); }, |block, _tx| block.number(0xcafe_u64), ) diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs index 65f901ede5..995913dbfd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_static_memory.rs @@ -95,10 +95,10 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // Inputs/Outputs - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address .assign(region, offset, Some(address.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs index e5c106da5b..5363dd2125 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_return_data_oo_bound.rs @@ -123,13 +123,13 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; let [dest_offset, data_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i as usize]].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.memory_offset .assign(region, offset, Value::known(F::from(dest_offset.as_u64())))?; @@ -138,7 +138,7 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { self.sum .assign(region, offset, [data_offset, size], remainder_end)?; - let return_data_length = block.rws[step.rw_indices[3]].call_context_value(); + let return_data_length = block.get_rws(step, 3).call_context_value(); self.return_data_length.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs index e7b255c7ef..61d746d83e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs @@ -57,7 +57,7 @@ impl ExecutionGadget for ErrorStackGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -75,6 +75,7 @@ mod test { use bus_mapping::{circuit_input_builder::CircuitsParams, evm::OpcodeId}; use eth_types::{ self, address, bytecode, bytecode::Bytecode, geth_types::Account, Address, ToWord, Word, + U64, }; use mock::TestContext; @@ -199,16 +200,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] @@ -229,7 +222,7 @@ mod test { Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs index 28c7416084..0f1fbe7a0c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -100,7 +100,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { call: &Call, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let is_call = opcode == OpcodeId::CALL; self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -108,8 +108,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { if is_call { [gas, code_address, value] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); } self.gas.assign(region, offset, Some(gas.to_le_bytes()))?; @@ -144,7 +143,7 @@ mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ address, bytecode, bytecode::Bytecode, evm_types::OpcodeId, geth_types::Account, Address, - ToWord, Word, + ToWord, Word, U64, }; use mock::TestContext; @@ -161,10 +160,11 @@ mod test { fn callee(code: Bytecode) -> Account { let code = code.to_vec(); let is_empty = code.is_empty(); + Account { address: Address::repeat_byte(0xff), code: code.into(), - nonce: if is_empty { 0 } else { 1 }.into(), + nonce: U64::from(!is_empty as u64), balance: if is_empty { 0 } else { 0xdeadbeefu64 }.into(), ..Default::default() } @@ -285,16 +285,8 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(Word::from(10u64.pow(19))); - accs[1] - .address(caller.address) - .code(caller.code) - .nonce(caller.nonce) - .balance(caller.balance); - accs[2] - .address(callee.address) - .code(callee.code) - .nonce(callee.nonce) - .balance(callee.balance); + accs[1].account(&caller); + accs[2].account(&callee); }, |mut txs, accs| { txs[0] diff --git a/zkevm-circuits/src/evm_circuit/execution/exp.rs b/zkevm-circuits/src/evm_circuit/execution/exp.rs index 6e4fe08250..5a1f8dd4e3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/exp.rs @@ -187,7 +187,7 @@ impl ExecutionGadget for ExponentiationGadget { // Finally we build an expression for the dynamic gas cost as: // dynamic_gas = 50 * exponent_byte_size - let dynamic_gas_cost = GasCost::EXP_BYTE_TIMES.0.expr() * exponent_byte_size.byte_size(); + let dynamic_gas_cost = GasCost::EXP_BYTE_TIMES.expr() * exponent_byte_size.byte_size(); let step_state_transition = StepStateTransition { rw_counter: Transition::Delta(3.expr()), // 2 stack pops, 1 stack push program_counter: Transition::Delta(1.expr()), @@ -227,8 +227,7 @@ impl ExecutionGadget for ExponentiationGadget { self.same_context.assign_exec_step(region, offset, step)?; let [base, exponent, exponentiation] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.base.assign(region, offset, Some(base.to_le_bytes()))?; self.exponent diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index 376f0f64b7..49a24fbb44 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -9,6 +9,7 @@ use crate::{ Transition, }, from_bytes, + math_gadget::IsZeroGadget, memory_gadget::{MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget}, not, select, CachedRegion, Cell, Word, }, @@ -33,6 +34,7 @@ pub(crate) struct ExtcodecopyGadget { reversion_info: ReversionInfo, is_warm: Cell, code_hash: Cell, + not_exists: IsZeroGadget, code_size: Cell, copy_rwc_inc: Cell, memory_expansion: MemoryExpansionGadget, @@ -79,11 +81,14 @@ impl ExecutionGadget for ExtcodecopyGadget { AccountFieldTag::CodeHash, code_hash.expr(), ); - // TODO: If external_address doesn't exist, we will get code_hash = 0. With - // this value, the bytecode_length lookup will not work, and the copy - // from code_hash = 0 will not work. We should use EMPTY_HASH when - // code_hash = 0. - cb.bytecode_length(code_hash.expr(), code_size.expr()); + let not_exists = IsZeroGadget::construct(cb, code_hash.expr()); + let exists = not::expr(not_exists.expr()); + cb.condition(exists.expr(), |cb| { + cb.bytecode_length(code_hash.expr(), code_size.expr()); + }); + cb.condition(not_exists.expr(), |cb| { + cb.require_zero("code_size is zero when non_exists", code_size.expr()); + }); let memory_address = MemoryAddressGadget::construct(cb, memory_offset, memory_length); let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); @@ -148,6 +153,7 @@ impl ExecutionGadget for ExtcodecopyGadget { is_warm, reversion_info, code_hash, + not_exists, code_size, copy_rwc_inc, memory_expansion, @@ -167,7 +173,7 @@ impl ExecutionGadget for ExtcodecopyGadget { self.same_context.assign_exec_step(region, offset, step)?; let [external_address, memory_offset, code_offset, memory_length] = - [0, 1, 2, 3].map(|idx| block.rws[step.rw_indices[idx]].stack_value()); + [0, 1, 2, 3].map(|idx| block.get_rws(step, idx).stack_value()); self.external_address_word .assign(region, offset, Some(external_address.to_le_bytes()))?; let memory_address = @@ -183,13 +189,15 @@ impl ExecutionGadget for ExtcodecopyGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[7]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 7).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[8]].account_value_pair().0; + let code_hash = block.get_rws(step, 8).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; + self.not_exists + .assign_value(region, offset, region.word_rlc(code_hash))?; let code_size = if code_hash.is_zero() { 0 @@ -252,7 +260,7 @@ mod test { fn test_ok( external_account: Option, code_offset: Word, - memory_offset: usize, + memory_offset: Word, length: usize, is_warm: bool, ) { @@ -291,10 +299,7 @@ mod test { .balance(Word::from(1u64 << 20)); accs[2].address(external_address); if let Some(external_account) = external_account { - accs[2] - .balance(external_account.balance) - .nonce(external_account.nonce) - .code(external_account.code); + accs[2].account(&external_account); } }, |mut txs, accs| { @@ -312,8 +317,8 @@ mod test { #[test] fn extcodecopy_empty_account() { - test_ok(None, 0x00.into(), 0x00, 0x36, true); // warm account - test_ok(None, 0x00.into(), 0x00, 0x36, false); // cold account + test_ok(None, Word::zero(), Word::zero(), 0x36, true); // warm account + test_ok(None, Word::zero(), Word::zero(), 0x36, false); // cold account } #[test] @@ -324,8 +329,8 @@ mod test { code: Bytes::from([10, 40]), ..Default::default() }), - 0x00.into(), - 0x00, + Word::zero(), + Word::zero(), 0x36, true, ); // warm account @@ -336,8 +341,8 @@ mod test { code: Bytes::from([10, 40]), ..Default::default() }), - 0x00.into(), - 0x00, + Word::zero(), + Word::zero(), 0x36, false, ); // cold account @@ -351,8 +356,8 @@ mod test { code: Bytes::from(rand_bytes_array::<256>()), ..Default::default() }), - 0x00.into(), - 0x00, + Word::zero(), + Word::zero(), 0x36, true, ); @@ -362,8 +367,8 @@ mod test { code: Bytes::from(rand_bytes_array::<256>()), ..Default::default() }), - 0x00.into(), - 0x00, + Word::zero(), + Word::zero(), 0x36, false, ); @@ -378,7 +383,7 @@ mod test { ..Default::default() }), 0x20.into(), - 0x00, + Word::zero(), 0x104, true, ); @@ -389,7 +394,7 @@ mod test { ..Default::default() }), 0x20.into(), - 0x00, + Word::zero(), 0x104, false, ); @@ -404,7 +409,7 @@ mod test { ..Default::default() }), Word::MAX, - 0x00, + Word::zero(), 0x36, true, ); @@ -415,9 +420,35 @@ mod test { ..Default::default() }), Word::MAX, - 0x00, + Word::zero(), 0x36, false, ); } + + #[test] + fn extcodecopy_overflow_memory_offset_and_zero_length() { + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + 0x20.into(), + Word::MAX, + 0, + true, + ); + test_ok( + Some(Account { + address: *EXTERNAL_ADDRESS, + code: Bytes::from(rand_bytes_array::<256>()), + ..Default::default() + }), + 0x20.into(), + Word::MAX, + 0, + true, + ); + } } diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index 0d4b0ddd59..1d14cffa34 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -93,7 +93,7 @@ impl ExecutionGadget for ExtcodehashGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; @@ -106,11 +106,11 @@ impl ExecutionGadget for ExtcodehashGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; @@ -122,7 +122,7 @@ impl ExecutionGadget for ExtcodehashGadget { mod test { use crate::test_util::CircuitTestBuilder; use eth_types::{ - address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, U256, + address, bytecode, geth_types::Account, Address, Bytecode, Bytes, ToWord, Word, U256, U64, }; use lazy_static::lazy_static; use mock::TestContext; @@ -134,7 +134,7 @@ mod test { fn test_ok(external_account: Option, is_warm: bool) { let external_address = external_account - .as_ref() + .clone() .map(|a| a.address) .unwrap_or(*EXTERNAL_ADDRESS); @@ -166,10 +166,7 @@ mod test { accs[1].address(external_address); if let Some(external_account) = external_account { - accs[1] - .balance(external_account.balance) - .nonce(external_account.nonce) - .code(external_account.code); + accs[1].account(&external_account); } accs[2] .address(address!("0x0000000000000000000000000000000000000010")) @@ -200,7 +197,7 @@ mod test { test_ok( Some(Account { address: *EXTERNAL_ADDRESS, - nonce: U256::from(259), + nonce: U64::from(259), code: Bytes::from([3]), ..Default::default() }), @@ -227,7 +224,7 @@ mod test { // code = []. let nonce_only_account = Account { address: *EXTERNAL_ADDRESS, - nonce: U256::from(200), + nonce: U64::from(200), ..Default::default() }; // This account state is possible if another account sends ETH to a previously diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index bd635a7018..8c00bd63c7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -111,7 +111,7 @@ impl ExecutionGadget for ExtcodesizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.rws[step.rw_indices[0]].stack_value(); + let address = block.get_rws(step, 0).stack_value(); self.address_word .assign(region, offset, Some(address.to_le_bytes()))?; @@ -125,17 +125,17 @@ impl ExecutionGadget for ExtcodesizeGadget { call.is_persistent, )?; - let (_, is_warm) = block.rws[step.rw_indices[4]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.rws[step.rw_indices[5]].account_value_pair().0; + let code_hash = block.get_rws(step, 5).account_value_pair().0; self.code_hash .assign(region, offset, region.word_rlc(code_hash))?; self.not_exists .assign_value(region, offset, region.word_rlc(code_hash))?; - let code_size = block.rws[step.rw_indices[6]].stack_value().as_u64(); + let code_size = block.get_rws(step, 6).stack_value().as_u64(); self.code_size .assign(region, offset, Some(code_size.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/gas.rs b/zkevm-circuits/src/evm_circuit/execution/gas.rs index 4c9cb5dcd7..f8151dc7ca 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gas.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gas.rs @@ -78,7 +78,7 @@ impl ExecutionGadget for GasGadget { offset, Some( step.gas_left - .saturating_sub(OpcodeId::GAS.constant_gas_cost().as_u64()) + .saturating_sub(OpcodeId::GAS.constant_gas_cost()) .to_le_bytes(), ), )?; diff --git a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs index ed4c749d90..2e6c5561cb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs @@ -72,7 +72,7 @@ impl ExecutionGadget for GasPriceGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let gas_price = block.rws[step.rw_indices[1]].stack_value(); + let gas_price = block.get_rws(step, 1).stack_value(); self.tx_id .assign(region, offset, Value::known(F::from(tx.id as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs index a0d5e12520..36671e5f5a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs @@ -64,7 +64,7 @@ impl ExecutionGadget for IsZeroGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); let value = region.word_rlc(value); self.value.assign(region, offset, value)?; self.is_zero.assign_value(region, offset, value)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/jump.rs b/zkevm-circuits/src/evm_circuit/execution/jump.rs index ead6133a5d..fc03df544b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jump.rs @@ -70,7 +70,7 @@ impl ExecutionGadget for JumpGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let destination = block.rws[step.rw_indices[0]].stack_value(); + let destination = block.get_rws(step, 0).stack_value(); self.destination.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index 4a463bfd5a..8c131e21e5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -93,8 +93,7 @@ impl ExecutionGadget for JumpiGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [destination, condition] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [destination, condition] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let condition = region.word_rlc(condition); self.dest.assign(region, offset, destination)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index b9e248624a..77f1c7d66e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/logs.rs +++ b/zkevm-circuits/src/evm_circuit/execution/logs.rs @@ -14,7 +14,7 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{CallContextFieldTag, RwTableTag, TxLogFieldTag}, + table::{CallContextFieldTag, TxLogFieldTag}, util::{build_tx_log_expression, word::Word, Expr}, }; use array_init::array_init; @@ -151,8 +151,8 @@ impl ExecutionGadget for LogGadget { ); }); - let gas_cost = GasCost::LOG.as_u64().expr() - + GasCost::LOG.as_u64().expr() * topic_count.clone() + let gas_cost = GasCost::LOG.expr() + + GasCost::LOG.expr() * topic_count.clone() + 8.expr() * memory_address.length() + memory_expansion.gas_cost(); // State transition @@ -194,9 +194,7 @@ impl ExecutionGadget for LogGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [memory_start, msize] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); - + let [memory_start, msize] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_start, msize)?; @@ -205,28 +203,33 @@ impl ExecutionGadget for LogGadget { self.memory_expansion .assign(region, offset, step.memory_word_size(), [memory_address])?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); let topic_count = opcode.postfix().expect("opcode with postfix") as usize; assert!(topic_count <= 4); - let is_persistent = call.is_persistent as u64; - let mut topic_stack_entry = if topic_count > 0 { - step.rw_indices[6 + call.is_persistent as usize] - } else { - // if topic_count == 0, this value will be no used anymore - (RwTableTag::Stack, 0usize) - }; - + let is_persistent = call.is_persistent as usize; + let mut topics = (0..topic_count).map(|topic| { + // We compute the index of the correct read-write record from + // bus-mapping/src/evm/opcodes/logs.rs::gen_log_step + // It takes 6 + is_persistent reads or writes to reach the topic stack write section. + // Each topic takes at least 1 stack read. They take an additional tx log write if the + // call is persistent. + block + .get_rws(step, 6 + is_persistent + topic * (1 + is_persistent)) + .stack_value() + }); for i in 0..4 { - let mut topic = region.word_rlc(U256::zero()); - if i < topic_count { - topic = region.word_rlc(block.rws[topic_stack_entry].stack_value()); - self.topic_selectors[i].assign(region, offset, Value::known(F::ONE))?; - topic_stack_entry.1 += 1; - } else { - self.topic_selectors[i].assign(region, offset, Value::known(F::ZERO))?; - } - self.phase2_topics[i].assign(region, offset, topic)?; + let topic = topics.next(); + self.topic_selectors[i].assign( + region, + offset, + Value::known(F::from(topic.is_some().into())), + )?; + self.phase2_topics[i].assign( + region, + offset, + region.word_rlc(topic.unwrap_or_default()), + )?; } self.contract_address.assign( @@ -238,7 +241,7 @@ impl ExecutionGadget for LogGadget { .expect("unexpected Address -> Scalar conversion failure"), ), )?; - + let is_persistent = call.is_persistent as u64; self.is_static_call .assign(region, offset, Value::known(F::from(call.is_static as u64)))?; self.is_persistent diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 121dbc3ca9..fc33c9e7bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -130,11 +130,10 @@ impl ExecutionGadget for MemoryGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // Inputs/Outputs - let [address, value] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [address, value] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); self.address.assign( region, offset, @@ -190,7 +189,7 @@ mod test { }; let gas_limit = - GasCost::TX.as_u64() + OpcodeId::PUSH32.as_u64() + OpcodeId::PUSH32.as_u64() + gas_cost; + GasCost::TX + OpcodeId::PUSH32.as_u64() + OpcodeId::PUSH32.as_u64() + gas_cost; let ctx = TestContext::<2, 1>::new( None, @@ -248,7 +247,7 @@ mod test { + 31; let memory_size = memory_address / 32; - GasCost::FASTEST.as_u64() + 3 * memory_size + memory_size * memory_size / 512 + GasCost::FASTEST + 3 * memory_size + memory_size * memory_size / 512 }; for opcode in [OpcodeId::MSTORE, OpcodeId::MLOAD, OpcodeId::MSTORE8] { diff --git a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs index e38ffd9d80..8804392410 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs @@ -121,9 +121,8 @@ impl ExecutionGadget for MulDivModGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); - let (a, b, c, d) = match step.opcode.unwrap() { + let [pop1, pop2, push] = [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); + let (a, b, c, d) = match step.opcode().unwrap() { OpcodeId::MUL => (pop1, pop2, U256::from(0), push), OpcodeId::DIV => (push, pop2, pop1 - push * pop2, pop1), OpcodeId::MOD => ( diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index 92a9bca0ce..f6b1a9c4b2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -116,9 +116,7 @@ impl ExecutionGadget for MulModGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [r, n, b, a] = [3, 2, 1, 0] - .map(|idx| step.rw_indices[idx]) - .map(|idx| block.rws[idx].stack_value()); + let [r, n, b, a] = [3, 2, 1, 0].map(|index| block.get_rws(step, index).stack_value()); self.words[0].assign(region, offset, Some(a.to_le_bytes()))?; self.words[1].assign(region, offset, Some(b.to_le_bytes()))?; self.words[2].assign(region, offset, Some(n.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/not.rs b/zkevm-circuits/src/evm_circuit/execution/not.rs index 7a076021b3..37ddcc3f1b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/not.rs +++ b/zkevm-circuits/src/evm_circuit/execution/not.rs @@ -74,8 +74,7 @@ impl ExecutionGadget for NotGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [input, output] = - [step.rw_indices[0], step.rw_indices[1]].map(|idx| block.rws[idx].stack_value()); + let [input, output] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); self.input .assign(region, offset, Some(input.to_le_bytes()))?; self.output diff --git a/zkevm-circuits/src/evm_circuit/execution/origin.rs b/zkevm-circuits/src/evm_circuit/execution/origin.rs index f47f186463..f53d0e2099 100644 --- a/zkevm-circuits/src/evm_circuit/execution/origin.rs +++ b/zkevm-circuits/src/evm_circuit/execution/origin.rs @@ -72,7 +72,7 @@ impl ExecutionGadget for OriginGadget { _: &Call, step: &ExecStep, ) -> Result<(), Error> { - let origin = block.rws[step.rw_indices[1]].stack_value(); + let origin = block.get_rws(step, 1).stack_value(); // Assing TxId. self.tx_id diff --git a/zkevm-circuits/src/evm_circuit/execution/pc.rs b/zkevm-circuits/src/evm_circuit/execution/pc.rs index 9180cb5452..905ae21d5c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pc.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pc.rs @@ -72,7 +72,7 @@ impl ExecutionGadget for PcGadget { self.same_context.assign_exec_step(region, offset, step)?; self.value - .assign(region, offset, Some(step.program_counter.to_le_bytes()))?; + .assign(region, offset, Some(step.pc.to_le_bytes()))?; Ok(()) } diff --git a/zkevm-circuits/src/evm_circuit/execution/pop.rs b/zkevm-circuits/src/evm_circuit/execution/pop.rs index 77c4ad3c3f..93103a4ae3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pop.rs @@ -60,7 +60,7 @@ impl ExecutionGadget for PopGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.phase2_value .assign(region, offset, region.word_rlc(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/push.rs b/zkevm-circuits/src/evm_circuit/execution/push.rs index e7aa067b9a..bc9f9cd7dc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/push.rs +++ b/zkevm-circuits/src/evm_circuit/execution/push.rs @@ -129,9 +129,9 @@ impl ExecutionGadget for PushGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); - let value = block.rws[step.rw_indices[0]].stack_value(); + let value = block.get_rws(step, 0).stack_value(); self.value .assign(region, offset, Some(value.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index b2fad3fbdf..9f1c0eeed2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -19,7 +19,7 @@ use crate::{ util::Expr, }; use bus_mapping::{circuit_input_builder::CopyDataType, evm::OpcodeId, state_db::CodeDB}; -use eth_types::{Field, ToScalar, U256}; +use eth_types::{evm_types::GasCost, Field, ToScalar, U256}; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -27,6 +27,7 @@ pub(crate) struct ReturnRevertGadget { opcode: Cell, range: MemoryAddressGadget, + deployed_code_rlc: Cell, is_success: Cell, restore_context: RestoreContextGadget, @@ -94,11 +95,15 @@ impl ExecutionGadget for ReturnRevertGadget { let is_contract_deployment = is_create.clone() * is_success.expr() * not::expr(copy_rw_increase_is_zero.expr()); - let (caller_id, address, reversion_info, code_hash) = + let code_deposit_cost = is_contract_deployment.clone() + * GasCost::CODE_DEPOSIT_BYTE_COST.expr() + * range.length(); + let (caller_id, address, reversion_info, code_hash, deployed_code_rlc) = cb.condition(is_contract_deployment.clone(), |cb| { // We don't need to place any additional constraints on code_hash because the // copy circuit enforces that it is the hash of the bytes in the copy lookup. let code_hash = cb.query_cell_phase2(); + let deployed_code_rlc = cb.query_cell_phase2(); cb.copy_table_lookup( cb.curr.state.call_id.expr(), CopyDataType::Memory.expr(), @@ -108,7 +113,7 @@ impl ExecutionGadget for ReturnRevertGadget { range.address(), 0.expr(), range.length(), - 0.expr(), + deployed_code_rlc.expr(), copy_rw_increase.expr(), ); @@ -127,7 +132,13 @@ impl ExecutionGadget for ReturnRevertGadget { Some(&mut reversion_info), ); - (caller_id, address, reversion_info, code_hash) + ( + caller_id, + address, + reversion_info, + code_hash, + deployed_code_rlc, + ) }); // Case B in the specs. @@ -147,7 +158,7 @@ impl ExecutionGadget for ReturnRevertGadget { + not::expr(is_success.expr()) * cb.curr.state.reversible_write_counter.expr(), ), - gas_left: Delta(-memory_expansion.gas_cost()), + gas_left: Delta(-memory_expansion.gas_cost() - code_deposit_cost.expr()), reversible_write_counter: To(0.expr()), memory_word_size: To(0.expr()), ..StepStateTransition::default() @@ -218,6 +229,7 @@ impl ExecutionGadget for ReturnRevertGadget { Self { opcode, range, + deployed_code_rlc, is_success, copy_length, copy_rw_increase, @@ -245,10 +257,10 @@ impl ExecutionGadget for ReturnRevertGadget { self.opcode.assign( region, offset, - Value::known(F::from(step.opcode.unwrap().as_u64())), + Value::known(F::from(step.opcode().unwrap().as_u64())), )?; - let [memory_offset, length] = [0, 1].map(|i| block.rws[step.rw_indices[i]].stack_value()); + let [memory_offset, length] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); let range = self.range.assign(region, offset, memory_offset, length)?; self.memory_expansion .assign(region, offset, step.memory_word_size(), [range])?; @@ -277,8 +289,13 @@ impl ExecutionGadget for ReturnRevertGadget { if call.is_create() && call.is_success { let values: Vec<_> = (3..3 + length.as_usize()) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .map(|index| block.get_rws(step, index).memory_value()) .collect(); + self.deployed_code_rlc.assign( + region, + offset, + region.keccak_rlc(&values.iter().rev().cloned().collect::>()), + )?; let mut code_hash = CodeDB::hash(&values).to_fixed_bytes(); code_hash.reverse(); self.code_hash.assign( @@ -347,7 +364,7 @@ mod test { address, bytecode, evm_types::OpcodeId, geth_types::{Account, GethData}, - Address, Bytecode, ToWord, Word, U256, + Address, Bytecode, Bytes, ToWord, Word, U256, U64, }; use itertools::Itertools; use mock::{eth, TestContext, MOCK_ACCOUNTS}; @@ -355,16 +372,16 @@ mod test { const CALLEE_ADDRESS: Address = Address::repeat_byte(0xff); const CALLER_ADDRESS: Address = Address::repeat_byte(0x34); - fn callee_bytecode(is_return: bool, offset: u64, length: u64) -> Bytecode { - let memory_bytes = [0x60; 10]; + fn callee_bytecode(is_return: bool, offset: u128, length: u64) -> Bytecode { + let memory_bytes = [0x60; 6]; let memory_address = 0; let memory_value = Word::from_big_endian(&memory_bytes); let mut code = bytecode! { - PUSH10(memory_value) + PUSH6(memory_value) PUSH1(memory_address) MSTORE PUSH2(length) - PUSH2(32u64 - u64::try_from(memory_bytes.len()).unwrap() + offset) + PUSH17(Word::from(offset) + 32 - memory_bytes.len()) }; code.write_op(if is_return { OpcodeId::RETURN @@ -420,13 +437,13 @@ mod test { let callee = Account { address: CALLEE_ADDRESS, code: callee_bytecode(*is_return, *callee_offset, *callee_length).into(), - nonce: Word::one(), + nonce: U64::one(), ..Default::default() }; let caller = Account { address: CALLER_ADDRESS, code: caller_bytecode(*caller_offset, *caller_length).into(), - nonce: Word::one(), + nonce: U64::one(), ..Default::default() }; @@ -484,7 +501,7 @@ mod test { { let initializer = callee_bytecode(*is_return, *offset, *length).code(); - let root_code = bytecode! { + let mut root_code = bytecode! { PUSH32(Word::from_big_endian(&initializer)) PUSH1(0) MSTORE @@ -495,14 +512,13 @@ mod test { CREATE }; - - let caller = Account { - address: CALLER_ADDRESS, - code: root_code.into(), - nonce: Word::one(), - balance: eth(10), - ..Default::default() - }; + if !is_return { + root_code.append(&bytecode! { + PUSH1(0) + PUSH1(0) + REVERT + }); + } let ctx = TestContext::<2, 1>::new( None, @@ -510,7 +526,11 @@ mod test { accs[0] .address(address!("0x000000000000000000000000000000000000cafe")) .balance(eth(10)); - accs[1].account(&caller); + accs[1] + .address(CALLER_ADDRESS) + .code::(root_code.into()) + .nonce(1) + .balance(eth(10)); }, |mut txs, accs| { txs[0] @@ -550,7 +570,7 @@ mod test { let caller = Account { address: CALLER_ADDRESS, code: root_code.into(), - nonce: Word::one(), + nonce: U64::one(), balance: eth(10), ..Default::default() }; @@ -618,10 +638,8 @@ mod test { PUSH1(0) // dest offset RETURNDATACOPY }); - - let block: GethData = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode.clone()) - .unwrap() - .into(); + let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode.clone()).unwrap(); + let block: GethData = test_ctx.clone().into(); // collect return opcode, retrieve next step, assure both contract create // successfully @@ -652,7 +670,17 @@ mod test { .iter() .for_each(|size| assert_eq!(size, &Word::zero())); - let text_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(); - CircuitTestBuilder::new_from_test_ctx(text_ctx).run(); + CircuitTestBuilder::new_from_test_ctx(test_ctx).run(); + } + + #[test] + fn test_return_overflow_offset_and_zero_length() { + for is_return in [true, false] { + let code = callee_bytecode(is_return, u128::MAX, 0); + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(), + ) + .run(); + } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index d8af064cf6..aed699e68a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -176,7 +176,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { self.same_context.assign_exec_step(region, offset, step)?; let [dest_offset, data_offset, size] = - [0, 1, 2].map(|i| block.rws[step.rw_indices[i as usize]].stack_value()); + [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); self.data_offset.assign( region, @@ -194,7 +194,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { (5, CallContextFieldTag::LastCalleeReturnDataLength), ] .map(|(i, tag)| { - let rw = block.rws[step.rw_indices[i as usize]]; + let rw = block.get_rws(step, i); assert_eq!(rw.field_tag(), Some(tag as u64)); rw.call_context_value() }); @@ -275,9 +275,9 @@ mod test { fn test_ok_internal( return_data_offset: usize, return_data_size: usize, - dest_offset: usize, - offset: usize, size: usize, + offset: usize, + dest_offset: Word, ) { let (addr_a, addr_b) = (mock::MOCK_ACCOUNTS[0], mock::MOCK_ACCOUNTS[1]); @@ -330,38 +330,43 @@ mod test { #[test] fn returndatacopy_gadget_do_nothing() { - test_ok_internal(0x00, 0x02, 0x10, 0x00, 0x00); + test_ok_internal(0, 2, 0, 0, 0x10.into()); } #[test] fn returndatacopy_gadget_simple() { - test_ok_internal(0x00, 0x02, 0x10, 0x00, 0x02); + test_ok_internal(0, 2, 2, 0, 0x10.into()); } #[test] fn returndatacopy_gadget_large() { - test_ok_internal(0x00, 0x20, 0x20, 0x00, 0x20); + test_ok_internal(0, 0x20, 0x20, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_large_partial() { - test_ok_internal(0x00, 0x20, 0x20, 0x10, 0x10); + test_ok_internal(0, 0x20, 0x10, 0x10, 0x20.into()); } #[test] fn returndatacopy_gadget_zero_length() { - test_ok_internal(0x00, 0x00, 0x20, 0x00, 0x00); + test_ok_internal(0, 0, 0, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_long_length() { // rlc value matters only if length > 255, i.e., size.cells.len() > 1 - test_ok_internal(0x00, 0x200, 0x20, 0x00, 0x150); + test_ok_internal(0, 0x200, 0x150, 0, 0x20.into()); } #[test] fn returndatacopy_gadget_big_offset() { // rlc value matters only if length > 255, i.e., size.cells.len() > 1 - test_ok_internal(0x200, 0x200, 0x200, 0x00, 0x150); + test_ok_internal(0x200, 0x200, 0x150, 0, 0x200.into()); + } + + #[test] + fn returndatacopy_gadget_overflow_offset_and_zero_length() { + test_ok_internal(0, 0x20, 0, 0x20, Word::MAX); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs index 51aac27a97..60aaafa0f0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs @@ -69,7 +69,7 @@ impl ExecutionGadget for ReturnDataSizeGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let return_data_size = block.rws[step.rw_indices[1]].stack_value(); + let return_data_size = block.get_rws(step, 1).stack_value(); self.return_data_size.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index 97e1e2c259..87dbd318a5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -266,8 +266,7 @@ impl ExecutionGadget for SarGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [shift, a, b] = indices.map(|idx| block.rws[idx].stack_value()); + let [shift, a, b] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); self.shift .assign(region, offset, Some(shift.to_le_bytes()))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs index 95a8b451df..7398ced5ed 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs @@ -150,14 +150,13 @@ impl ExecutionGadget for SignedDivModGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); + let [pop1, pop2, push] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let pop1_abs = get_abs(pop1); let pop2_abs = get_abs(pop2); let push_abs = get_abs(push); let is_pop1_neg = is_neg(pop1); let is_pop2_neg = is_neg(pop2); - let (quotient, divisor, remainder, dividend) = match step.opcode.unwrap() { + let (quotient, divisor, remainder, dividend) = match step.opcode().unwrap() { OpcodeId::SDIV => ( push, pop2, diff --git a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs index 9d0f938bb0..b170e88360 100644 --- a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs @@ -78,7 +78,7 @@ impl ExecutionGadget for SelfbalanceGadget { ), )?; - let self_balance = block.rws[step.rw_indices[2]].stack_value(); + let self_balance = block.get_rws(step, 2).stack_value(); self.phase2_self_balance .assign(region, offset, region.word_rlc(self_balance))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sha3.rs b/zkevm-circuits/src/evm_circuit/execution/sha3.rs index 3f8c11f714..736b961dfa 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sha3.rs @@ -114,8 +114,7 @@ impl ExecutionGadget for Sha3Gadget { self.same_context.assign_exec_step(region, offset, step)?; let [memory_offset, size, sha3_output] = - [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]] - .map(|idx| block.rws[idx].stack_value()); + [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_offset, size)?; @@ -132,7 +131,7 @@ impl ExecutionGadget for Sha3Gadget { )?; let values: Vec = (3..3 + (size.low_u64() as usize)) - .map(|i| block.rws[step.rw_indices[i]].memory_value()) + .map(|i| block.get_rws(step, i).memory_value()) .collect(); let rlc_acc = region @@ -158,14 +157,12 @@ impl ExecutionGadget for Sha3Gadget { #[cfg(test)] mod tests { use crate::test_util::CircuitTestBuilder; - use bus_mapping::{ - circuit_input_builder::CircuitsParams, - evm::{gen_sha3_code, MemoryKind}, - }; + use bus_mapping::{circuit_input_builder::CircuitsParams, evm::Sha3CodeGen}; + use eth_types::{bytecode, U256}; use mock::TestContext; - fn test_ok(offset: usize, size: usize, mem_kind: MemoryKind) { - let (code, _) = gen_sha3_code(offset, size, mem_kind); + fn test_ok(mut gen: Sha3CodeGen) { + let (code, _) = gen.gen_sha3_code(); CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(), ) @@ -178,22 +175,36 @@ mod tests { #[test] fn sha3_gadget_zero_length() { - test_ok(0x20, 0x00, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_gt_size(0x20, 0x00)); } #[test] fn sha3_gadget_simple() { - test_ok(0x00, 0x08, MemoryKind::Empty); - test_ok(0x10, 0x10, MemoryKind::LessThanSize); - test_ok(0x24, 0x16, MemoryKind::EqualToSize); - test_ok(0x32, 0x78, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x00, 0x08)); + test_ok(Sha3CodeGen::mem_lt_size(0x10, 0x10)); + test_ok(Sha3CodeGen::mem_eq_size(0x24, 0x16)); + test_ok(Sha3CodeGen::mem_gt_size(0x32, 0x78)); } #[test] fn sha3_gadget_large() { - test_ok(0x101, 0x202, MemoryKind::Empty); - test_ok(0x202, 0x303, MemoryKind::LessThanSize); - test_ok(0x303, 0x404, MemoryKind::EqualToSize); - test_ok(0x404, 0x505, MemoryKind::MoreThanSize); + test_ok(Sha3CodeGen::mem_empty(0x101, 0x202)); + test_ok(Sha3CodeGen::mem_lt_size(0x202, 0x303)); + test_ok(Sha3CodeGen::mem_eq_size(0x303, 0x404)); + test_ok(Sha3CodeGen::mem_gt_size(0x404, 0x505)); + } + + #[test] + fn sha3_gadget_overflow_offset_and_zero_size() { + let bytecode = bytecode! { + PUSH1(0) + PUSH32(U256::MAX) + SHA3 + }; + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); } } diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index 409114b3cf..798a1101fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -163,8 +163,7 @@ impl ExecutionGadget for ShlShrGadget { step: &ExecStep, ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let indices = [step.rw_indices[0], step.rw_indices[1], step.rw_indices[2]]; - let [pop1, pop2, push] = indices.map(|idx| block.rws[idx].stack_value()); + let [pop1, pop2, push] = [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); let shf0 = u64::from(pop1.to_le_bytes()[0]); let shf_lt256 = pop1 .to_le_bytes() @@ -180,7 +179,7 @@ impl ExecutionGadget for ShlShrGadget { U256::from(0) }; - let (quotient, remainder, dividend) = match step.opcode.unwrap() { + let (quotient, remainder, dividend) = match step.opcode().unwrap() { OpcodeId::SHL => (pop2, U256::from(0), push), OpcodeId::SHR => (push, pop2 - push * divisor, pop2), _ => unreachable!(), diff --git a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs index d243fef860..8ee8357385 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs @@ -153,7 +153,7 @@ impl ExecutionGadget for SignedComparatorGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); // SLT opcode is the default check in the SCMP gadget. Swap rw for SGT. self.is_sgt.assign( @@ -163,11 +163,11 @@ impl ExecutionGadget for SignedComparatorGadget { F::from(OpcodeId::SGT.as_u8() as u64), )?; let indices = if opcode == OpcodeId::SGT { - [step.rw_indices[1], step.rw_indices[0]] + [1, 0] } else { - [step.rw_indices[0], step.rw_indices[1]] + [0, 1] }; - let [a, b] = indices.map(|idx| block.rws[idx].stack_value()); + let [a, b] = indices.map(|idx| block.get_rws(step, idx).stack_value()); let a_le_bytes = a.to_le_bytes(); let b_le_bytes = b.to_le_bytes(); diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index 02427df086..ba16143688 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -166,8 +166,8 @@ impl ExecutionGadget for SignextendGadget { self.same_context.assign_exec_step(region, offset, step)?; // Inputs/Outputs - let index = block.rws[step.rw_indices[0]].stack_value().to_le_bytes(); - let value = block.rws[step.rw_indices[1]].stack_value().to_le_bytes(); + let index = block.get_rws(step, 0).stack_value().to_le_bytes(); + let value = block.get_rws(step, 1).stack_value().to_le_bytes(); self.index.assign(region, offset, Some(index))?; self.value.assign(region, offset, Some(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sload.rs b/zkevm-circuits/src/evm_circuit/execution/sload.rs index da64916ac1..32759f9aa1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sload.rs @@ -117,19 +117,18 @@ impl ExecutionGadget for SloadGadget { .expect("unexpected Address -> Scalar conversion failure"), ), )?; - - let [key, value] = - [step.rw_indices[4], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); + let key = block.get_rws(step, 4).stack_value(); + let value = block.get_rws(step, 6).stack_value(); self.phase2_key .assign(region, offset, region.word_rlc(key))?; self.phase2_value .assign(region, offset, region.word_rlc(value))?; - let (_, committed_value) = block.rws[step.rw_indices[5]].aux_pair(); + let (_, committed_value) = block.get_rws(step, 5).aux_pair(); self.phase2_committed_value .assign(region, offset, region.word_rlc(committed_value))?; - let (_, is_warm) = block.rws[step.rw_indices[7]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 7).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index dcc8468dc9..fd0a90b558 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -93,7 +93,7 @@ impl ExecutionGadget for SstoreGadget { // Constrain for SSTORE reentrancy sentry. let sufficient_gas_sentry = LtGadget::construct( cb, - GasCost::SSTORE_SENTRY.0.expr(), + GasCost::SSTORE_SENTRY.expr(), cb.curr.state.gas_left.expr(), ); cb.require_equal( @@ -184,31 +184,31 @@ impl ExecutionGadget for SstoreGadget { ), )?; - let [key, value] = - [step.rw_indices[5], step.rw_indices[6]].map(|idx| block.rws[idx].stack_value()); + let key = block.get_rws(step, 5).stack_value(); + let value = block.get_rws(step, 6).stack_value(); self.phase2_key .assign(region, offset, region.word_rlc(key))?; self.phase2_value .assign(region, offset, region.word_rlc(value))?; - let (_, value_prev, _, original_value) = block.rws[step.rw_indices[7]].storage_value_aux(); + let (_, value_prev, _, original_value) = block.get_rws(step, 7).storage_value_aux(); self.phase2_value_prev .assign(region, offset, region.word_rlc(value_prev))?; self.phase2_original_value .assign(region, offset, region.word_rlc(original_value))?; - let (_, is_warm) = block.rws[step.rw_indices[8]].tx_access_list_value_pair(); + let (_, is_warm) = block.get_rws(step, 8).tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let (tx_refund, tx_refund_prev) = block.rws[step.rw_indices[9]].tx_refund_value_pair(); + let (tx_refund, tx_refund_prev) = block.get_rws(step, 9).tx_refund_value_pair(); self.tx_refund_prev .assign(region, offset, Value::known(F::from(tx_refund_prev)))?; self.sufficient_gas_sentry.assign_value( region, offset, - Value::known(F::from(GasCost::SSTORE_SENTRY.0)), + Value::known(F::from(GasCost::SSTORE_SENTRY)), Value::known(F::from(step.gas_left)), )?; @@ -397,11 +397,11 @@ fn calc_expected_tx_refund( if !original_value.is_zero() { if value_prev.is_zero() { // recreate slot (2.2.1.1) - tx_refund_new -= GasCost::SSTORE_CLEARS_SCHEDULE.as_u64() + tx_refund_new -= GasCost::SSTORE_CLEARS_SCHEDULE } if value.is_zero() { // delete slot (2.2.1.2) - tx_refund_new += GasCost::SSTORE_CLEARS_SCHEDULE.as_u64() + tx_refund_new += GasCost::SSTORE_CLEARS_SCHEDULE } } @@ -409,10 +409,10 @@ fn calc_expected_tx_refund( if original_value == value { if original_value.is_zero() { // reset to original inexistent slot (2.2.2.1) - tx_refund_new += GasCost::SSTORE_SET.as_u64() - GasCost::WARM_ACCESS.as_u64(); + tx_refund_new += GasCost::SSTORE_SET - GasCost::WARM_ACCESS; } else { // reset to original existing slot (2.2.2.2) - tx_refund_new += GasCost::SSTORE_RESET.as_u64() - GasCost::WARM_ACCESS.as_u64(); + tx_refund_new += GasCost::SSTORE_RESET - GasCost::WARM_ACCESS; } } } diff --git a/zkevm-circuits/src/evm_circuit/execution/stop.rs b/zkevm-circuits/src/evm_circuit/execution/stop.rs index b79d6dc906..5b635381c6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/stop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/stop.rs @@ -116,10 +116,10 @@ impl ExecutionGadget for StopGadget { self.is_out_of_range.assign( region, offset, - F::from(code.bytes.len() as u64) - F::from(step.program_counter), + F::from(code.bytes.len() as u64) - F::from(step.pc), )?; - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/swap.rs b/zkevm-circuits/src/evm_circuit/execution/swap.rs index 951d8922f7..7238aa5833 100644 --- a/zkevm-circuits/src/evm_circuit/execution/swap.rs +++ b/zkevm-circuits/src/evm_circuit/execution/swap.rs @@ -70,8 +70,8 @@ impl ExecutionGadget for SwapGadget { self.same_context.assign_exec_step(region, offset, step)?; for (cell, value) in self.phase2_values.iter().zip( - [step.rw_indices[0], step.rw_indices[1]] - .map(|idx| block.rws[idx].stack_value()) + [0, 1] + .map(|index| block.get_rws(step, index).stack_value()) .iter(), ) { cell.assign(region, offset, region.word_rlc(*value))?; diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index a2262d21e9..e21530bb9c 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -16,16 +16,16 @@ pub const MAX_STEP_HEIGHT: usize = 21; pub(crate) const STEP_STATE_HEIGHT: usize = 1; /// Number of Advice Phase2 columns in the EVM circuit -pub(crate) const N_PHASE2_COLUMNS: usize = 4; +pub const N_PHASE2_COLUMNS: usize = 4; /// Number of Advice Phase1 columns in the EVM circuit -pub(crate) const N_PHASE1_COLUMNS: usize = +pub const N_PHASE1_COLUMNS: usize = STEP_WIDTH - EVM_LOOKUP_COLS - N_PHASE2_COLUMNS - N_COPY_COLUMNS - N_BYTE_LOOKUPS; // Number of copy columns -pub(crate) const N_COPY_COLUMNS: usize = 2; +pub const N_COPY_COLUMNS: usize = 2; -pub(crate) const N_BYTE_LOOKUPS: usize = 24; +pub const N_BYTE_LOOKUPS: usize = 24; /// Amount of lookup columns in the EVM circuit dedicated to lookups. pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS @@ -38,7 +38,7 @@ pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS + EXP_TABLE_LOOKUPS; /// Lookups done per row. -pub(crate) const LOOKUP_CONFIG: &[(Table, usize)] = &[ +pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ (Table::Fixed, FIXED_TABLE_LOOKUPS), (Table::Tx, TX_TABLE_LOOKUPS), (Table::Rw, RW_TABLE_LOOKUPS), diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index eed86d232c..c9081600a8 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -10,8 +10,12 @@ use crate::{ Expr, }, }; -use bus_mapping::evm::OpcodeId; -use eth_types::Field; +use bus_mapping::{ + circuit_input_builder::ExecState, + error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError, OogError}, + evm::OpcodeId, +}; +use eth_types::{evm_unimplemented, Field}; use halo2_proofs::{ circuit::Value, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, @@ -124,6 +128,168 @@ impl Display for ExecutionState { write!(f, "{:?}", self) } } +impl From<&ExecError> for ExecutionState { + fn from(error: &ExecError) -> Self { + match error { + ExecError::InvalidOpcode => ExecutionState::ErrorInvalidOpcode, + ExecError::StackOverflow | ExecError::StackUnderflow => ExecutionState::ErrorStack, + ExecError::WriteProtection => ExecutionState::ErrorWriteProtection, + ExecError::Depth(depth_error) => match depth_error { + DepthError::Call => ExecutionState::CALL_OP, + DepthError::Create => ExecutionState::CREATE, + DepthError::Create2 => ExecutionState::CREATE2, + }, + ExecError::InsufficientBalance(insufficient_balance_err) => { + match insufficient_balance_err { + InsufficientBalanceError::Call => ExecutionState::CALL_OP, + InsufficientBalanceError::Create => ExecutionState::CREATE, + InsufficientBalanceError::Create2 => ExecutionState::CREATE2, + } + } + ExecError::NonceUintOverflow(nonce_overflow_err) => match nonce_overflow_err { + NonceUintOverflowError::Create => ExecutionState::CREATE, + NonceUintOverflowError::Create2 => ExecutionState::CREATE2, + }, + ExecError::ContractAddressCollision => ExecutionState::CREATE2, + ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, + ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, + ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, + ExecError::CodeStoreOutOfGas => ExecutionState::ErrorOutOfGasCodeStore, + ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorMaxCodeSizeExceeded, + ExecError::OutOfGas(oog_error) => match oog_error { + OogError::Constant => ExecutionState::ErrorOutOfGasConstant, + OogError::StaticMemoryExpansion => { + ExecutionState::ErrorOutOfGasStaticMemoryExpansion + } + OogError::DynamicMemoryExpansion => { + ExecutionState::ErrorOutOfGasDynamicMemoryExpansion + } + OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, + OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, + OogError::CodeStore => ExecutionState::ErrorOutOfGasCodeStore, + OogError::Log => ExecutionState::ErrorOutOfGasLOG, + OogError::Exp => ExecutionState::ErrorOutOfGasEXP, + OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, + OogError::Call => ExecutionState::ErrorOutOfGasCall, + OogError::SloadSstore => ExecutionState::ErrorOutOfGasSloadSstore, + OogError::Create2 => ExecutionState::ErrorOutOfGasCREATE2, + OogError::SelfDestruct => ExecutionState::ErrorOutOfGasSELFDESTRUCT, + }, + } + } +} +impl From<&ExecStep> for ExecutionState { + fn from(step: &ExecStep) -> Self { + if let Some(error) = step.error.as_ref() { + return error.into(); + } + match step.exec_state { + ExecState::Op(op) => { + if op.is_dup() { + return ExecutionState::DUP; + } + if op.is_push() { + return ExecutionState::PUSH; + } + if op.is_swap() { + return ExecutionState::SWAP; + } + if op.is_log() { + return ExecutionState::LOG; + } + + macro_rules! dummy { + ($name:expr) => {{ + evm_unimplemented!("{:?} is implemented with DummyGadget", $name); + $name + }}; + } + + match op { + OpcodeId::ADD | OpcodeId::SUB => ExecutionState::ADD_SUB, + OpcodeId::ADDMOD => ExecutionState::ADDMOD, + OpcodeId::ADDRESS => ExecutionState::ADDRESS, + OpcodeId::BALANCE => ExecutionState::BALANCE, + OpcodeId::MUL | OpcodeId::DIV | OpcodeId::MOD => ExecutionState::MUL_DIV_MOD, + OpcodeId::MULMOD => ExecutionState::MULMOD, + OpcodeId::SDIV | OpcodeId::SMOD => ExecutionState::SDIV_SMOD, + OpcodeId::EQ | OpcodeId::LT | OpcodeId::GT => ExecutionState::CMP, + OpcodeId::SLT | OpcodeId::SGT => ExecutionState::SCMP, + OpcodeId::SIGNEXTEND => ExecutionState::SIGNEXTEND, + OpcodeId::STOP => ExecutionState::STOP, + OpcodeId::AND => ExecutionState::BITWISE, + OpcodeId::XOR => ExecutionState::BITWISE, + OpcodeId::OR => ExecutionState::BITWISE, + OpcodeId::NOT => ExecutionState::NOT, + OpcodeId::EXP => ExecutionState::EXP, + OpcodeId::POP => ExecutionState::POP, + OpcodeId::PUSH32 => ExecutionState::PUSH, + OpcodeId::BYTE => ExecutionState::BYTE, + OpcodeId::MLOAD => ExecutionState::MEMORY, + OpcodeId::MSTORE => ExecutionState::MEMORY, + OpcodeId::MSTORE8 => ExecutionState::MEMORY, + OpcodeId::JUMPDEST => ExecutionState::JUMPDEST, + OpcodeId::JUMP => ExecutionState::JUMP, + OpcodeId::JUMPI => ExecutionState::JUMPI, + OpcodeId::GASPRICE => ExecutionState::GASPRICE, + OpcodeId::PC => ExecutionState::PC, + OpcodeId::MSIZE => ExecutionState::MSIZE, + OpcodeId::CALLER => ExecutionState::CALLER, + OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, + OpcodeId::EXTCODEHASH => ExecutionState::EXTCODEHASH, + OpcodeId::EXTCODESIZE => ExecutionState::EXTCODESIZE, + OpcodeId::BLOCKHASH => ExecutionState::BLOCKHASH, + OpcodeId::TIMESTAMP | OpcodeId::NUMBER | OpcodeId::GASLIMIT => { + ExecutionState::BLOCKCTXU64 + } + OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, + OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, + OpcodeId::GAS => ExecutionState::GAS, + OpcodeId::SAR => ExecutionState::SAR, + OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, + OpcodeId::SHA3 => ExecutionState::SHA3, + OpcodeId::SHL | OpcodeId::SHR => ExecutionState::SHL_SHR, + OpcodeId::SLOAD => ExecutionState::SLOAD, + OpcodeId::SSTORE => ExecutionState::SSTORE, + OpcodeId::CALLDATASIZE => ExecutionState::CALLDATASIZE, + OpcodeId::CALLDATACOPY => ExecutionState::CALLDATACOPY, + OpcodeId::CHAINID => ExecutionState::CHAINID, + OpcodeId::ISZERO => ExecutionState::ISZERO, + OpcodeId::CALL + | OpcodeId::CALLCODE + | OpcodeId::DELEGATECALL + | OpcodeId::STATICCALL => ExecutionState::CALL_OP, + OpcodeId::ORIGIN => ExecutionState::ORIGIN, + OpcodeId::CODECOPY => ExecutionState::CODECOPY, + OpcodeId::CALLDATALOAD => ExecutionState::CALLDATALOAD, + OpcodeId::CODESIZE => ExecutionState::CODESIZE, + OpcodeId::EXTCODECOPY => ExecutionState::EXTCODECOPY, + OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, + OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, + OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, + OpcodeId::CREATE => ExecutionState::CREATE, + OpcodeId::CREATE2 => ExecutionState::CREATE2, + // dummy ops + OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), + _ => unimplemented!("unimplemented opcode {:?}", op), + } + } + ExecState::BeginTx => ExecutionState::BeginTx, + ExecState::EndTx => ExecutionState::EndTx, + ExecState::EndBlock => ExecutionState::EndBlock, + } + } +} + +pub trait HasExecutionState { + fn execution_state(&self) -> ExecutionState; +} + +impl HasExecutionState for ExecStep { + fn execution_state(&self) -> ExecutionState { + ExecutionState::from(self) + } +} impl ExecutionState { pub(crate) const fn as_u64(&self) -> u64 { @@ -166,7 +332,7 @@ impl ExecutionState { || self.halts_in_exception() } - pub(crate) fn responsible_opcodes(&self) -> Vec { + pub fn responsible_opcodes(&self) -> Vec { if matches!(self, Self::ErrorStack) { return OpcodeId::valid_opcodes() .into_iter() @@ -337,7 +503,7 @@ impl ExecutionState { /// Enum of Responsible opcode mapping to execution state. #[derive(Debug)] -pub(crate) enum ResponsibleOp { +pub enum ResponsibleOp { /// Raw opcode Op(OpcodeId), /// Corresponding to ExecutionState::ErrorStack @@ -352,7 +518,7 @@ impl From for ResponsibleOp { } impl ResponsibleOp { - pub(crate) fn opcode(&self) -> OpcodeId { + pub fn opcode(&self) -> OpcodeId { *match self { ResponsibleOp::Op(opcode) => opcode, ResponsibleOp::InvalidStackPtr(opcode, _) => opcode, @@ -555,12 +721,10 @@ impl Step { ) -> Result<(), Error> { self.state .execution_state - .assign(region, offset, step.execution_state as usize)?; - self.state.rw_counter.assign( - region, - offset, - Value::known(F::from(step.rw_counter as u64)), - )?; + .assign(region, offset, step.execution_state() as usize)?; + self.state + .rw_counter + .assign(region, offset, Value::known(F::from(step.rwc.into())))?; self.state .call_id .assign(region, offset, Value::known(F::from(call.call_id as u64)))?; @@ -575,15 +739,13 @@ impl Step { self.state .code_hash .assign(region, offset, Some(call.code_hash.to_fixed_bytes()))?; - self.state.program_counter.assign( - region, - offset, - Value::known(F::from(step.program_counter)), - )?; + self.state + .program_counter + .assign(region, offset, Value::known(F::from(step.pc)))?; self.state.stack_pointer.assign( region, offset, - Value::known(F::from(step.stack_pointer as u64)), + Value::known(F::from(step.stack_pointer())), )?; self.state .gas_left diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index db4949af11..0a5557f083 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -1,4 +1,3 @@ -pub use crate::table::TxContextFieldTag; use crate::{ evm_circuit::step::{ExecutionState, ResponsibleOp}, impl_expr, @@ -108,12 +107,12 @@ impl FixedTableTag { })), Self::ConstantGasCost => Box::new( OpcodeId::iter() - .filter(move |opcode| opcode.constant_gas_cost().0 > 0) + .filter(move |opcode| opcode.constant_gas_cost() > 0) .map(move |opcode| { [ tag, F::from(opcode.as_u64()), - F::from(opcode.constant_gas_cost().0), + F::from(opcode.constant_gas_cost()), F::ZERO, ] }), @@ -123,7 +122,7 @@ impl FixedTableTag { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, EnumIter)] -pub(crate) enum Table { +pub enum Table { Fixed, Tx, Rw, diff --git a/zkevm-circuits/src/evm_circuit/util.rs b/zkevm-circuits/src/evm_circuit/util.rs index a16d16e723..9fa7abdaf4 100644 --- a/zkevm-circuits/src/evm_circuit/util.rs +++ b/zkevm-circuits/src/evm_circuit/util.rs @@ -10,7 +10,6 @@ use crate::{ }, table::Table, }, - table::RwTableTag, witness::{Block, ExecStep, Rw, RwMap}, }; use bus_mapping::state_db::CodeDB; @@ -200,10 +199,23 @@ impl<'r, 'b, F: Field> CachedRegion<'r, 'b, F> { .evm_word() .map(|r| rlc::value(&n.to_le_bytes(), r)) } + + pub fn keccak_rlc(&self, le_bytes: &[u8]) -> Value { + self.challenges + .keccak_input() + .map(|r| rlc::value(le_bytes, r)) + } + pub fn empty_code_hash_rlc(&self) -> Value { self.word_rlc(CodeDB::empty_code_hash().to_word()) } + pub fn code_hash(&self, n: U256) -> Value { + self.challenges + .evm_word() + .map(|r| rlc::value(&n.to_le_bytes(), r)) + } + /// Constrains a cell to have a constant value. /// /// Returns an error if the cell is in a column where equality has not been @@ -668,7 +680,7 @@ pub(crate) fn is_precompiled(address: &Address) -> bool { /// Helper struct to read rw operations from a step sequentially. pub(crate) struct StepRws<'a> { rws: &'a RwMap, - rw_indices: &'a Vec<(RwTableTag, usize)>, + step: &'a ExecStep, offset: usize, } @@ -677,7 +689,7 @@ impl<'a> StepRws<'a> { pub(crate) fn new(block: &'a Block, step: &'a ExecStep) -> Self { Self { rws: &block.rws, - rw_indices: &step.rw_indices, + step, offset: 0, } } @@ -687,7 +699,7 @@ impl<'a> StepRws<'a> { } /// Return the next rw operation from the step. pub(crate) fn next(&mut self) -> Rw { - let rw = self.rws[self.rw_indices[self.offset]]; + let rw = self.rws[self.step.rw_index(self.offset)]; self.offset += 1; rw } diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 4d4f5cccb4..338c1a7214 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -80,7 +80,7 @@ impl SameContextGadget { offset: usize, step: &ExecStep, ) -> Result<(), Error> { - let opcode = step.opcode.unwrap(); + let opcode = step.opcode().unwrap(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; @@ -233,8 +233,7 @@ impl RestoreContextGadget { [U256::zero(); 9] } else { [0, 1, 2, 3, 4, 5, 6, 7, 8] - .map(|i| step.rw_indices[i + rw_offset]) - .map(|idx| block.rws[idx].call_context_value()) + .map(|i| block.get_rws(step, i + rw_offset).call_context_value()) }; for (cell, value) in [ @@ -401,7 +400,7 @@ impl TransferWithGasFeeGadget { // If receiver doesn't exist, create it cb.condition( or::expr([ - not::expr(value_is_zero.expr()) * not::expr(receiver_exists.clone()), + not::expr(value_is_zero.expr()) * not::expr(receiver_exists.expr()), must_create.clone(), ]), |cb| { @@ -446,7 +445,7 @@ impl TransferWithGasFeeGadget { 1.expr() + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) or::expr([ - not::expr(self.value_is_zero.expr()) * not::expr(self.receiver_exists.clone()), + not::expr(self.value_is_zero.expr()) * not::expr(self.receiver_exists.expr()), self.must_create.clone()] ) * 1.expr() + // +1 Write Account (sender) Balance @@ -458,7 +457,7 @@ impl TransferWithGasFeeGadget { // NOTE: Write Account (sender) Balance (Not Reversible tx fee) // +1 Write Account (receiver) CodeHash (account creation via code_hash update) or::expr([ - not::expr(self.value_is_zero.expr()) * not::expr(self.receiver_exists.clone()), + not::expr(self.value_is_zero.expr()) * not::expr(self.receiver_exists.expr()), self.must_create.clone()] ) * 1.expr() + // +1 Write Account (sender) Balance @@ -512,6 +511,8 @@ impl TransferWithGasFeeGadget { pub(crate) struct TransferGadget { sender: UpdateBalanceGadget, receiver: UpdateBalanceGadget, + must_create: Expression, + receiver_exists: Expression, pub(crate) value_is_zero: IsZeroWordGadget>, } @@ -521,13 +522,17 @@ impl TransferGadget { sender_address: Expression, receiver_address: Expression, receiver_exists: Expression, + must_create: Expression, value: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { let value_is_zero = IsZeroWordGadget::construct(cb, value.clone()); // If receiver doesn't exist, create it cb.condition( - not::expr(value_is_zero.expr()) * not::expr(receiver_exists), + or::expr([ + not::expr(value_is_zero.expr()) * not::expr(receiver_exists.expr()), + must_create.clone(), + ]), |cb| { cb.account_write_word( receiver_address.clone(), @@ -556,8 +561,10 @@ impl TransferGadget { }); Self { + must_create, sender, receiver, + receiver_exists, value_is_zero, } } @@ -570,6 +577,17 @@ impl TransferGadget { &self.receiver } + pub(crate) fn reversible_w_delta(&self) -> Expression { + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) + or::expr([ + not::expr(self.value_is_zero.expr()) * not::expr(self.receiver_exists.clone()), + self.must_create.clone()] + ) * 1.expr() + + // +1 Write Account (sender) Balance + // +1 Write Account (receiver) Balance + not::expr(self.value_is_zero.expr()) * 2.expr() + } + pub(crate) fn assign( &self, region: &mut CachedRegion<'_, '_, F>, @@ -808,14 +826,14 @@ impl CommonCallGadget is_empty_account: bool, ) -> Result { let gas_cost = if is_warm_prev { - GasCost::WARM_ACCESS.as_u64() + GasCost::WARM_ACCESS } else { - GasCost::COLD_ACCOUNT_ACCESS.as_u64() + GasCost::COLD_ACCOUNT_ACCESS } + if has_value { - GasCost::CALL_WITH_VALUE.as_u64() + GasCost::CALL_WITH_VALUE // Only CALL opcode could invoke transfer to make empty account into non-empty. + if is_call && is_empty_account { - GasCost::NEW_ACCOUNT.as_u64() + GasCost::NEW_ACCOUNT } else { 0 } @@ -958,13 +976,11 @@ impl SstoreGasGadget { } pub(crate) fn cal_sload_gas_cost_for_assignment(is_warm: bool) -> u64 { - let gas_cost = if is_warm { + if is_warm { GasCost::WARM_ACCESS } else { GasCost::COLD_SLOAD - }; - - gas_cost.0 + } } pub(crate) fn cal_sstore_gas_cost_for_assignment( @@ -985,9 +1001,9 @@ pub(crate) fn cal_sstore_gas_cost_for_assignment( GasCost::WARM_ACCESS }; if is_warm { - warm_case_gas.0 + warm_case_gas } else { - warm_case_gas.0 + GasCost::COLD_SLOAD.0 + warm_case_gas + GasCost::COLD_SLOAD } } diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 78353e68da..d9fe5cf01c 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -6,8 +6,8 @@ use crate::{ util::{Cell, RandomLinearCombination}, }, table::{ - AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, RwTableTag, TxContextFieldTag, - TxLogFieldTag, TxReceiptFieldTag, + AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, TxContextFieldTag, TxLogFieldTag, + TxReceiptFieldTag, }, util::{ build_tx_log_expression, @@ -17,7 +17,7 @@ use crate::{ Challenges, Expr, }, }; -use bus_mapping::state_db::EMPTY_CODE_HASH_LE; +use bus_mapping::{operation::Target, state_db::EMPTY_CODE_HASH_LE}; use eth_types::Field; use gadgets::util::not; use halo2_proofs::{ @@ -841,7 +841,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { name: &str, counter: Expression, is_write: Expression, - tag: RwTableTag, + tag: Target, values: RwValues, ) { let name = format!("rw lookup {}", name); @@ -862,7 +862,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { &mut self, name: &'static str, is_write: Expression, - tag: RwTableTag, + tag: Target, values: RwValues, ) { self.rw_lookup_with_counter( @@ -890,7 +890,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { fn reversible_write( &mut self, name: &'static str, - tag: RwTableTag, + tag: Target, values: RwValues, reversion_info: Option<&mut ReversionInfo>, ) { @@ -951,7 +951,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.reversible_write( "TxAccessListAccount write", - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, RwValues::new( tx_id, account_address, @@ -984,7 +984,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "account access list read", false.expr(), - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, RwValues::new( tx_id, account_address, @@ -1029,7 +1029,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.reversible_write( "TxAccessListAccountStorage write", - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, RwValues::new( tx_id, account_address, @@ -1069,7 +1069,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "TxAccessListAccountStorage read", false.expr(), - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, RwValues::new( tx_id, account_address, @@ -1089,7 +1089,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "TxRefund read", false.expr(), - RwTableTag::TxRefund, + Target::TxRefund, RwValues::new( tx_id, 0.expr(), @@ -1127,7 +1127,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.reversible_write( "TxRefund write", - RwTableTag::TxRefund, + Target::TxRefund, RwValues::new( tx_id, 0.expr(), @@ -1163,7 +1163,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "Account read", false.expr(), - RwTableTag::Account, + Target::Account, RwValues::new( 0.expr(), account_address, @@ -1204,7 +1204,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.reversible_write( "Account write", - RwTableTag::Account, + Target::Account, RwValues::new( 0.expr(), account_address, @@ -1250,7 +1250,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "account_storage_read", false.expr(), - RwTableTag::AccountStorage, + Target::Storage, RwValues::new( tx_id, account_address, @@ -1300,7 +1300,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ) { self.reversible_write( "AccountStorage write", - RwTableTag::AccountStorage, + Target::Storage, RwValues::new( tx_id, account_address, @@ -1366,7 +1366,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "CallContext lookup", is_write, - RwTableTag::CallContext, + Target::CallContext, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), @@ -1389,7 +1389,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "CallContext lookup", 0.expr(), - RwTableTag::CallContext, + Target::CallContext, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), @@ -1412,7 +1412,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "CallContext lookup", 1.expr(), - RwTableTag::CallContext, + Target::CallContext, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), 0.expr(), @@ -1528,7 +1528,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "Stack lookup", is_write, - RwTableTag::Stack, + Target::Stack, RwValues::new( self.curr.state.call_id.expr(), self.curr.state.stack_pointer.expr() + stack_pointer_offset, @@ -1554,7 +1554,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "Memory lookup", is_write, - RwTableTag::Memory, + Target::Memory, RwValues::new( call_id.unwrap_or_else(|| self.curr.state.call_id.expr()), memory_address, @@ -1580,7 +1580,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "log data lookup", 1.expr(), - RwTableTag::TxLog, + Target::TxLog, RwValues::new( tx_id, build_tx_log_expression(index, field_tag.expr(), log_id), @@ -1607,7 +1607,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { self.rw_lookup( "tx receipt lookup", is_write, - RwTableTag::TxReceipt, + Target::TxReceipt, RwValues::new( tx_id, 0.expr(), @@ -1629,7 +1629,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { "Start lookup", counter, 0.expr(), - RwTableTag::Start, + Target::Start, RwValues { id: 0.expr(), address: 0.expr(), diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 7f03fe6864..eef04dbace 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -12,7 +12,7 @@ type ColumnSize = (usize, usize, usize); /// Instrument captures metrics during the compilation of a circuit. #[derive(Clone, Debug, Default)] -pub(crate) struct Instrument { +pub struct Instrument { // States -> Cell Types -> (width, height, num_cells) states: Vec<(ExecutionState, StepSize)>, } @@ -38,7 +38,7 @@ impl Instrument { /// Dissasembles the instrumentation data and returns a collection of /// `ExecStateReport`s. One for each EVM `ExecutionState`. - pub(crate) fn analyze(&self) -> Vec { + pub fn analyze(&self) -> Vec { let mut report_collection = vec![]; for (state, sizes) in &self.states { // Create a state report @@ -111,20 +111,20 @@ impl Instrument { /// Struct which contains a Cost/ColumnType report for a particular EVM /// `ExecutionStep`. #[derive(Clone, Debug, Default)] -pub(crate) struct ExecStateReport { - pub(crate) state: ExecutionState, - pub(crate) storage_1: StateReportRow, - pub(crate) storage_2: StateReportRow, - pub(crate) storage_perm: StateReportRow, - pub(crate) byte_lookup: StateReportRow, - pub(crate) fixed_table: StateReportRow, - pub(crate) tx_table: StateReportRow, - pub(crate) rw_table: StateReportRow, - pub(crate) bytecode_table: StateReportRow, - pub(crate) block_table: StateReportRow, - pub(crate) copy_table: StateReportRow, - pub(crate) keccak_table: StateReportRow, - pub(crate) exp_table: StateReportRow, +pub struct ExecStateReport { + pub state: ExecutionState, + pub storage_1: StateReportRow, + pub storage_2: StateReportRow, + pub storage_perm: StateReportRow, + pub byte_lookup: StateReportRow, + pub fixed_table: StateReportRow, + pub tx_table: StateReportRow, + pub rw_table: StateReportRow, + pub bytecode_table: StateReportRow, + pub block_table: StateReportRow, + pub copy_table: StateReportRow, + pub keccak_table: StateReportRow, + pub exp_table: StateReportRow, } impl From for ExecStateReport { @@ -148,17 +148,17 @@ impl From<&ExecutionState> for ExecStateReport { /// Struct that contains all of the measurament values required to evaluate the /// costs of a particular `ColumnType` of an `ExecStateReport` #[derive(Debug, Clone, Default)] -pub(crate) struct StateReportRow { +pub struct StateReportRow { // Given a rigion of x columns and y rows, we have x * y cells available for computation. - pub(crate) available_cells: usize, + pub available_cells: usize, // The cells not used in the computation in the x*y region. These are the wasted cells. - pub(crate) unused_cells: usize, + pub unused_cells: usize, // The cells used in the computation in the x*y region. - pub(crate) used_cells: usize, + pub used_cells: usize, // The largest y within all the `CellType`. - pub(crate) top_height: usize, + pub top_height: usize, // If we fully utilize y, how large is the x really needed? - pub(crate) used_columns: usize, + pub used_columns: usize, // The percentage of cells used in computation in the x * y region. - pub(crate) utilization: f64, + pub utilization: f64, } 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 1cae8e56cf..f8f7f69274 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -46,10 +46,15 @@ impl RlpU64Gadget { .map(|(byte, indicator)| byte.expr() * indicator.expr()), ); let most_significant_byte_is_zero = IsZeroGadget::construct(cb, most_significant_byte); + let is_lt_128 = cb.query_bool(); let value = expr_from_bytes(&value_rlc.cells); cb.condition(most_significant_byte_is_zero.expr(), |cb| { cb.require_zero("if most significant byte is 0, value is 0", value.clone()); + cb.require_zero( + "if most significant byte is 0, value is less than 128", + 1.expr() - is_lt_128.expr(), + ); }); for (i, is_most_significant) in is_most_significant_byte.iter().enumerate() { @@ -67,10 +72,18 @@ impl RlpU64Gadget { }); } - let is_lt_128 = cb.query_bool(); - cb.condition(is_lt_128.expr(), |cb| { - cb.range_lookup(value, 128); - }); + // If is_lt_128, then value < 128, checked by a lookup. + + // Otherwise, then value >= 128, checked as follows: + // - Either the first byte is not the most significant, and there is a more significant one; + // - Or the first byte is the most significant, and it is >= 128. value ∈ [128, 256) (value + // - 128) ∈ [0, 128) + let byte_128 = value_rlc.cells[0].expr() - 128.expr(); + let is_first = is_most_significant_byte[0].expr(); + let byte_128_or_zero = byte_128 * is_first; + + let value_lt_128 = select::expr(is_lt_128.expr(), value, byte_128_or_zero); + cb.range_lookup(value_lt_128, 128); Self { value_rlc, @@ -194,7 +207,7 @@ pub struct ContractCreateGadget { /// appropriate RLC wherever needed. code_hash: [Cell; N_BYTES_WORD], /// Random salt for CREATE2. - salt: RandomLinearCombination, + salt: [Cell; N_BYTES_WORD], } impl ContractCreateGadget { @@ -203,7 +216,7 @@ impl ContractCreateGadget { let caller_address = cb.query_keccak_rlc(); let nonce = RlpU64Gadget::construct(cb); let code_hash = array_init::array_init(|_| cb.query_byte()); - let salt = cb.query_keccak_rlc(); + let salt = array_init::array_init(|_| cb.query_byte()); Self { caller_address, @@ -230,11 +243,6 @@ impl ContractCreateGadget { self.nonce.assign(region, offset, caller_nonce)?; - self.salt.assign( - region, - offset, - Some(salt.map(|v| v.to_le_bytes()).unwrap_or_default()), - )?; for (c, v) in self .code_hash .iter() @@ -242,6 +250,13 @@ impl ContractCreateGadget { { c.assign(region, offset, Value::known(F::from(v as u64)))?; } + for (c, v) in self + .salt + .iter() + .zip(salt.map(|v| v.to_le_bytes()).unwrap_or_default()) + { + c.assign(region, offset, Value::known(F::from(v as u64)))?; + } Ok(()) } @@ -280,9 +295,28 @@ impl ContractCreateGadget { ) } + /// Salt EVM word RLC. + pub(crate) fn salt_word_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { + cb.word_rlc::( + self.salt + .iter() + .map(Expr::expr) + .collect::>() + .try_into() + .unwrap(), + ) + } + /// Salt keccak RLC. - pub(crate) fn salt_keccak_rlc(&self) -> Expression { - self.salt.expr() + pub(crate) fn salt_keccak_rlc(&self, cb: &EVMConstraintBuilder) -> Expression { + cb.keccak_rlc::( + self.salt + .iter() + .map(Expr::expr) + .collect::>() + .try_into() + .unwrap(), + ) } /// Caller address' RLC value. @@ -326,7 +360,7 @@ impl ContractCreateGadget { let challenge_power_84 = challenge_power_64.clone() * challenge_power_20; (0xff.expr() * challenge_power_84) + (self.caller_address_rlc() * challenge_power_64) - + (self.salt_keccak_rlc() * challenge_power_32) + + (self.salt_keccak_rlc(cb) * challenge_power_32) + self.code_hash_keccak_rlc(cb) } else { // RLC(RLP([caller_address, caller_nonce])) 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 823cf99bd4..0541bc0fc1 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 @@ -90,6 +90,7 @@ impl UnitTestMathGadgetBaseCircuit { impl> Circuit for UnitTestMathGadgetBaseCircuit { type Config = (UnitTestMathGadgetBaseCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { UnitTestMathGadgetBaseCircuit { diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index 1859f9bf02..5499299f6f 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -147,10 +147,18 @@ impl MemoryAddressGadget { self.has_length() * from_bytes::expr(&self.memory_offset.limbs[..N_BYTES_MEMORY_ADDRESS]) } + pub(crate) fn offset_rlc(&self) -> Expression { + self.memory_offset.expr() + } + pub(crate) fn length(&self) -> Expression { from_bytes::expr(&self.memory_length.limbs[..N_BYTES_MEMORY_ADDRESS]) } + pub(crate) fn length_rlc(&self) -> Expression { + self.memory_length.expr() + } + pub(crate) fn address(&self) -> Expression { self.offset() + self.length() } @@ -242,12 +250,12 @@ impl let curr_quad_memory_cost = ConstantDivisionGadget::construct( cb, curr_memory_word_size.clone() * curr_memory_word_size.clone(), - GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR, ); let next_quad_memory_cost = ConstantDivisionGadget::construct( cb, next_memory_word_size.clone() * next_memory_word_size.clone(), - GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR.as_u64(), + GasCost::MEMORY_EXPANSION_QUAD_DENOMINATOR, ); // Calculate the gas cost for the memory expansion. @@ -322,7 +330,7 @@ impl )?; // Calculate the gas cost for the expansian - let memory_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF.as_u64() + let memory_cost = GasCost::MEMORY_EXPANSION_LINEAR_COEFF * (next_memory_word_size - curr_memory_word_size) + (next_quad_memory_cost - curr_quad_memory_cost) as u64; @@ -336,13 +344,13 @@ impl /// This gas cost is the difference between the next and current memory costs: /// `memory_cost = Gmem * memory_size + floor(memory_size * memory_size / 512)` #[derive(Clone, Debug)] -pub(crate) struct MemoryCopierGasGadget { +pub(crate) struct MemoryCopierGasGadget { word_size: MemoryWordSizeGadget, gas_cost: Expression, gas_cost_range_check: RangeCheckGadget, } -impl MemoryCopierGasGadget { +impl MemoryCopierGasGadget { pub const WORD_SIZE: u64 = 32u64; /// Input requirements: @@ -381,7 +389,7 @@ impl MemoryCopierGasGadget { memory_expansion_gas_cost: u64, ) -> Result { let word_size = self.word_size.assign(region, offset, num_bytes)?; - let gas_cost = word_size * GAS_COPY.as_u64() + memory_expansion_gas_cost; + let gas_cost = word_size * GAS_COPY + memory_expansion_gas_cost; self.gas_cost_range_check .assign(region, offset, F::from(gas_cost))?; // Return the memory copier gas cost diff --git a/zkevm-circuits/src/exp_circuit/dev.rs b/zkevm-circuits/src/exp_circuit/dev.rs index 45a1831b6d..ebb5cb4d31 100644 --- a/zkevm-circuits/src/exp_circuit/dev.rs +++ b/zkevm-circuits/src/exp_circuit/dev.rs @@ -14,6 +14,7 @@ use halo2_proofs::{ impl Circuit for ExpCircuit { type Config = (ExpCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/exp_circuit/test.rs b/zkevm-circuits/src/exp_circuit/test.rs index 2df199f217..2b5012458d 100644 --- a/zkevm-circuits/src/exp_circuit/test.rs +++ b/zkevm-circuits/src/exp_circuit/test.rs @@ -19,7 +19,7 @@ use mock::TestContext; fn exp_circuit_unusable_rows() { assert_eq!( ExpCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 796994d802..9183b306c2 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -45,7 +45,7 @@ use halo2_proofs::{ plonk::{Column, ConstraintSystem, Error, Expression, Fixed, TableColumn, VirtualCells}, poly::Rotation, }; -use log::info; +use log::debug; /// KeccakConfig #[derive(Clone, Debug)] @@ -181,9 +181,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { decode::expr(absorb_res), absorb_result.expr(), ); - info!("- Post absorb:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post absorb:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Process inputs. @@ -223,9 +223,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { for _ in input_bytes.iter() { is_paddings.push(cell_manager.query_cell(meta)); } - info!("- Post padding:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post padding:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Theta @@ -281,9 +281,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { } } s = os.clone(); - info!("- Post theta:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post theta:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Rho/Pi @@ -369,9 +369,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { }); lookup_counter += 1; } - info!("- Post rho/pi:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post rho/pi:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // Chi @@ -449,9 +449,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { cb.require_equal("next row check", s[i][j].clone(), s_next[i][j].clone()); } } - info!("- Post chi:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post chi:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; let mut lookup_counter = 0; @@ -489,9 +489,9 @@ impl SubCircuitConfig for KeccakCircuitConfig { .unwrap(), true, ); - info!("- Post squeeze:"); - info!("Lookups: {}", lookup_counter); - info!("Columns: {}", cell_manager.get_width()); + debug!("- Post squeeze:"); + debug!("Lookups: {}", lookup_counter); + debug!("Columns: {}", cell_manager.get_width()); total_lookup_counter += lookup_counter; // The round constraints that we've been building up till now @@ -826,21 +826,21 @@ impl SubCircuitConfig for KeccakCircuitConfig { meta.annotate_lookup_column(col, || format!("KECCAK_pack_table_{}", idx)) }); - info!("Degree: {}", meta.degree()); - info!("Minimum rows: {}", meta.minimum_rows()); - info!("Total Lookups: {}", total_lookup_counter); - info!("Total Columns: {}", cell_manager.get_width()); - info!("num unused cells: {}", cell_manager.get_num_unused_cells()); - info!("part_size absorb: {}", get_num_bits_per_absorb_lookup()); - info!("part_size theta: {}", get_num_bits_per_theta_c_lookup()); - info!( + debug!("Degree: {}", meta.degree()); + debug!("Minimum rows: {}", meta.minimum_rows()); + debug!("Total Lookups: {}", total_lookup_counter); + debug!("Total Columns: {}", cell_manager.get_width()); + debug!("num unused cells: {}", cell_manager.get_num_unused_cells()); + debug!("part_size absorb: {}", get_num_bits_per_absorb_lookup()); + debug!("part_size theta: {}", get_num_bits_per_theta_c_lookup()); + debug!( "part_size theta c: {}", get_num_bits_per_lookup(THETA_C_LOOKUP_RANGE) ); - info!("part_size theta t: {}", get_num_bits_per_lookup(4)); - info!("part_size rho/pi: {}", get_num_bits_per_rho_pi_lookup()); - info!("part_size chi base: {}", get_num_bits_per_base_chi_lookup()); - info!( + debug!("part_size theta t: {}", get_num_bits_per_lookup(4)); + debug!("part_size rho/pi: {}", get_num_bits_per_rho_pi_lookup()); + debug!("part_size chi base: {}", get_num_bits_per_base_chi_lookup()); + debug!( "uniform part sizes: {:?}", target_part_sizes(get_num_bits_per_theta_c_lookup()) ); diff --git a/zkevm-circuits/src/keccak_circuit/dev.rs b/zkevm-circuits/src/keccak_circuit/dev.rs index 44e7ac07a5..12709bc41e 100644 --- a/zkevm-circuits/src/keccak_circuit/dev.rs +++ b/zkevm-circuits/src/keccak_circuit/dev.rs @@ -14,6 +14,7 @@ use halo2_proofs::{ impl Circuit for KeccakCircuit { type Config = (KeccakCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/keccak_circuit/table.rs b/zkevm-circuits/src/keccak_circuit/table.rs index 74c5959f51..5ab2fc5e23 100644 --- a/zkevm-circuits/src/keccak_circuit/table.rs +++ b/zkevm-circuits/src/keccak_circuit/table.rs @@ -246,6 +246,7 @@ mod tests { impl Circuit for TableTestCircuit { type Config = [TableColumn; 2]; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { self.clone() diff --git a/zkevm-circuits/src/keccak_circuit/test.rs b/zkevm-circuits/src/keccak_circuit/test.rs index 939dad046d..9711f978d5 100644 --- a/zkevm-circuits/src/keccak_circuit/test.rs +++ b/zkevm-circuits/src/keccak_circuit/test.rs @@ -22,7 +22,7 @@ fn serial_keccak_circuit_unusable_rows() { std::env::set_var("KECCAK_ROWS", format!("{keccak_rows}")); assert_eq!( KeccakCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } std::env::set_var("KECCAK_ROWS", format!("{DEFAULT_KECCAK_ROWS}")); diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 5deee79e11..de9b5e2b79 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -33,9 +33,6 @@ pub mod table; #[cfg(any(feature = "test", test))] pub mod test_util; -#[cfg(any(feature = "test", test))] -mod stats; - pub mod tx_circuit; pub mod util; pub mod witness; diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index a5ff18a185..cebe069cba 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -6,15 +6,14 @@ mod dev; #[cfg(any(feature = "test", test))] mod test; #[cfg(any(feature = "test", test, feature = "test-circuits"))] -pub use dev::PiTestCircuit; +pub use dev::PiCircuit as TestPiCircuit; use eth_types::{ geth_types::{BlockConstants, Transaction}, sign_types::SignData, - Address, BigEndianHash, Field, ToBigEndian, ToLittleEndian, ToScalar, Word, H256, + Address, BigEndianHash, Field, Keccak, ToBigEndian, ToLittleEndian, ToScalar, Word, H256, }; use halo2_proofs::plonk::{Instance, SecondPhase}; -use keccak256::plain::Keccak; use param::*; use std::marker::PhantomData; @@ -53,8 +52,8 @@ pub struct BlockValues { /// Values of the tx table (as in the spec) #[derive(Default, Debug, Clone)] pub struct TxValues { - nonce: Word, - gas: Word, // gas limit + nonce: u64, + gas: u64, // gas limit gas_price: Word, from_addr: Address, to_addr: Address, @@ -141,9 +140,9 @@ impl PublicData { let mut msg_hash_le = [0u8; 32]; msg_hash_le.copy_from_slice(sign_data.msg_hash.to_bytes().as_slice()); tx_vals.push(TxValues { - nonce: tx.nonce, + nonce: tx.nonce.as_u64(), gas_price: tx.gas_price, - gas: tx.gas_limit, + gas: tx.gas_limit.as_u64(), from_addr: tx.from, to_addr: tx.to_or_zero(), is_create: tx.is_create(), @@ -1319,11 +1318,8 @@ impl SubCircuit for PiCircuit { let tx = if i < txs.len() { &txs[i] } else { &tx_default }; for (tag, value) in &[ - ( - TxFieldTag::Nonce, - rlc(tx.nonce.to_le_bytes(), self.randomness), - ), - (TxFieldTag::Gas, rlc(tx.gas.to_le_bytes(), self.randomness)), + (TxFieldTag::Nonce, F::from(tx.nonce)), + (TxFieldTag::Gas, F::from(tx.gas)), ( TxFieldTag::GasPrice, rlc(tx.gas_price.to_le_bytes(), self.randomness), @@ -1522,8 +1518,8 @@ fn raw_public_inputs_col( let tx = if i < txs.len() { &txs[i] } else { &tx_default }; for val in &[ - rlc(tx.nonce.to_le_bytes(), randomness), - rlc(tx.gas.to_le_bytes(), randomness), + F::from(tx.nonce), + F::from(tx.gas), rlc(tx.gas_price.to_le_bytes(), randomness), tx.from_addr.to_scalar().expect("tx.from too big"), tx.to_addr.to_scalar().expect("tx.to too big"), diff --git a/zkevm-circuits/src/pi_circuit/dev.rs b/zkevm-circuits/src/pi_circuit/dev.rs index 1af92653ee..2ca0345ecb 100644 --- a/zkevm-circuits/src/pi_circuit/dev.rs +++ b/zkevm-circuits/src/pi_circuit/dev.rs @@ -1,74 +1,40 @@ +pub use super::PiCircuit; use super::*; -// We define the PiTestCircuit as a wrapper over PiCircuit extended to take the -// generic const parameters MAX_TXS and MAX_CALLDATA. This is necessary because -// the trait Circuit requires an implementation of `configure` that doesn't take -// any circuit parameters, and the PiCircuit defines gates that use rotations -// that depend on MAX_TXS and MAX_CALLDATA, so these two values are required -// during the configuration. -/// Test Circuit for PiCircuit -#[derive(Default, Clone)] -pub struct PiTestCircuit( - pub PiCircuit, -); - -impl SubCircuit - for PiTestCircuit -{ - type Config = PiCircuitConfig; - - fn unusable_rows() -> usize { - PiCircuit::::unusable_rows() - } - - fn new_from_block(block: &witness::Block) -> Self { - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); - - Self(PiCircuit::new_from_block(block)) - } - - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); - - PiCircuit::min_num_rows_block(block) - } - - /// Compute the public inputs for this circuit. - fn instance(&self) -> Vec> { - self.0.instance() - } - - fn synthesize_sub( - &self, - _config: &Self::Config, - _challenges: &Challenges>, - _layouter: &mut impl Layouter, - ) -> Result<(), Error> { - panic!("use PiCircuit for embedding instead"); - } +/// Public Input Circuit configuration parameters +#[derive(Default)] +pub struct PiCircuitParams { + /// Max Txs + pub max_txs: usize, + /// Max Calldata + pub max_calldata: usize, } -impl Circuit - for PiTestCircuit -{ +impl Circuit for PiCircuit { type Config = (PiCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = PiCircuitParams; fn without_witnesses(&self) -> Self { Self::default() } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn params(&self) -> Self::Params { + PiCircuitParams { + max_txs: self.max_txs, + max_calldata: self.max_calldata, + } + } + + fn configure_with_params(meta: &mut ConstraintSystem, params: Self::Params) -> Self::Config { let block_table = BlockTable::construct(meta); let tx_table = TxTable::construct(meta); ( PiCircuitConfig::new( meta, PiCircuitConfigArgs { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: params.max_txs, + max_calldata: params.max_calldata, block_table, tx_table, }, @@ -77,12 +43,16 @@ impl Circuit ) } + fn configure(_meta: &mut ConstraintSystem) -> Self::Config { + unreachable!(); + } + fn synthesize( &self, (config, challenges): Self::Config, mut layouter: impl Layouter, ) -> Result<(), Error> { let challenges = challenges.values(&mut layouter); - self.0.synthesize_sub(&config, &challenges, &mut layouter) + self.synthesize_sub(&config, &challenges, &mut layouter) } } diff --git a/zkevm-circuits/src/pi_circuit/test.rs b/zkevm-circuits/src/pi_circuit/test.rs index ddd453e110..5b6db6499b 100644 --- a/zkevm-circuits/src/pi_circuit/test.rs +++ b/zkevm-circuits/src/pi_circuit/test.rs @@ -11,16 +11,19 @@ use rand_chacha::ChaCha20Rng; #[test] fn pi_circuit_unusable_rows() { - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 8; assert_eq!( PiCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(PiCircuitParams { + max_txs: 2, + max_calldata: 8, + }), ) } -fn run( +fn run( k: u32, + max_txs: usize, + max_calldata: usize, public_data: PublicData, ) -> Result<(), Vec> { let mut rng = ChaCha20Rng::seed_from_u64(2); @@ -29,14 +32,8 @@ fn run( let mut public_data = public_data; public_data.chain_id = *MOCK_CHAIN_ID; - let circuit = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, - randomness, - rand_rpi, - public_data, - )); - let public_inputs = circuit.0.instance(); + let circuit = PiCircuit::::new(max_txs, max_calldata, randomness, rand_rpi, public_data); + let public_inputs = circuit.instance(); let prover = match MockProver::run(k, &circuit, public_inputs) { Ok(prover) => prover, @@ -47,18 +44,18 @@ fn run( #[test] fn test_default_pi() { - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 8; + let max_txs = 2; + let max_calldata = 8; let public_data = PublicData::default(); let k = 17; - assert_eq!(run::(k, public_data), Ok(())); + assert_eq!(run::(k, max_txs, max_calldata, public_data), Ok(())); } #[test] fn test_simple_pi() { - const MAX_TXS: usize = 8; - const MAX_CALLDATA: usize = 200; + let max_txs = 8; + let max_calldata = 200; let mut public_data = PublicData::default(); @@ -70,34 +67,32 @@ fn test_simple_pi() { } let k = 17; - assert_eq!(run::(k, public_data), Ok(())); + assert_eq!(run::(k, max_txs, max_calldata, public_data), Ok(())); } -fn run_size_check( - public_data: [PublicData; 2], -) { +fn run_size_check(max_txs: usize, max_calldata: usize, public_data: [PublicData; 2]) { let mut rng = ChaCha20Rng::seed_from_u64(2); let randomness = F::random(&mut rng); let rand_rpi = F::random(&mut rng); - let circuit = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, + let circuit = PiCircuit::::new( + max_txs, + max_calldata, randomness, rand_rpi, public_data[0].clone(), - )); - let public_inputs = circuit.0.instance(); + ); + let public_inputs = circuit.instance(); let prover1 = MockProver::run(20, &circuit, public_inputs).unwrap(); - let circuit2 = PiTestCircuit::(PiCircuit::new( - MAX_TXS, - MAX_CALLDATA, + let circuit2 = PiCircuit::new( + max_txs, + max_calldata, randomness, rand_rpi, public_data[1].clone(), - )); - let public_inputs = circuit2.0.instance(); + ); + let public_inputs = circuit2.instance(); let prover2 = MockProver::run(20, &circuit, public_inputs).unwrap(); assert_eq!(prover1.fixed(), prover2.fixed()); @@ -106,8 +101,8 @@ fn run_size_check( #[test] fn variadic_size_check() { - const MAX_TXS: usize = 8; - const MAX_CALLDATA: usize = 200; + let max_txs = 8; + let max_calldata = 200; let mut pub_dat_1 = PublicData { chain_id: *MOCK_CHAIN_ID, @@ -133,5 +128,5 @@ fn variadic_size_check() { .push(CORRECT_MOCK_TXS[i].clone().into()); } - run_size_check::([pub_dat_1, pub_dat_2]); + run_size_check::(max_txs, max_calldata, [pub_dat_1, pub_dat_2]); } diff --git a/zkevm-circuits/src/root_circuit.rs b/zkevm-circuits/src/root_circuit.rs index 4a45a62e1a..d990f68b65 100644 --- a/zkevm-circuits/src/root_circuit.rs +++ b/zkevm-circuits/src/root_circuit.rs @@ -110,6 +110,7 @@ where { type Config = AggregationConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self { diff --git a/zkevm-circuits/src/root_circuit/aggregation.rs b/zkevm-circuits/src/root_circuit/aggregation.rs index db3a2127fe..5e51ad5d9f 100644 --- a/zkevm-circuits/src/root_circuit/aggregation.rs +++ b/zkevm-circuits/src/root_circuit/aggregation.rs @@ -446,6 +446,7 @@ where { type Config = AggregationConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self { @@ -609,6 +610,7 @@ pub mod test { impl Circuit for StandardPlonk { type Config = StandardPlonkConfig; type FloorPlanner = V1; + type Params = (); fn without_witnesses(&self) -> Self { *self diff --git a/zkevm-circuits/src/root_circuit/test.rs b/zkevm-circuits/src/root_circuit/test.rs index 36106bc899..0f372fe629 100644 --- a/zkevm-circuits/src/root_circuit/test.rs +++ b/zkevm-circuits/src/root_circuit/test.rs @@ -20,12 +20,10 @@ use rand::rngs::OsRng; fn test_root_circuit() { let (params, protocol, proof, instance) = { // Preprocess - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; const TEST_MOCK_RANDOMNESS: u64 = 0x100; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -34,11 +32,8 @@ fn test_root_circuit() { max_keccak_rows: 0, }; let (k, circuit, instance, _) = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, TEST_MOCK_RANDOMNESS>::build( - block_1tx(), - circuits_params, - ) - .unwrap(); + SuperCircuit::<_>::build(block_1tx(), circuits_params, TEST_MOCK_RANDOMNESS.into()) + .unwrap(); let params = ParamsKZG::::setup(k, OsRng); let pk = keygen_pk(¶ms, keygen_vk(¶ms, &circuit).unwrap(), &circuit).unwrap(); let protocol = compile( diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 1a24c9e974..dc387a25b7 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -9,6 +9,7 @@ mod param; mod dev; #[cfg(any(feature = "test", test))] mod test; +use bus_mapping::operation::Target; #[cfg(any(feature = "test", test, feature = "test-circuits"))] pub use dev::StateCircuit as TestStateCircuit; @@ -17,7 +18,7 @@ use self::{ lexicographic_ordering::LimbIndex, }; use crate::{ - table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable, RwTableTag}, + table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable}, util::{word, Challenges, Expr, SubCircuit, SubCircuitConfig}, witness::{self, MptUpdates, Rw, RwMap}, }; @@ -263,7 +264,7 @@ impl StateCircuitConfig { assert_eq!(state_root, old_root); state_root = new_root; } - if matches!(row.tag(), RwTableTag::CallContext) && !row.is_write() { + if matches!(row.tag(), Target::CallContext) && !row.is_write() { assert_eq!(row.value_assignment(), 0.into(), "{:?}", row); } } @@ -385,7 +386,7 @@ impl StateCircuitConfig { /// Keys for sorting the rows of the state circuit #[derive(Clone, Copy)] pub struct SortKeysConfig { - tag: BinaryNumberConfig, + tag: BinaryNumberConfig, id: MpiConfig, address: MpiConfig, field_tag: Column, @@ -595,33 +596,3 @@ fn queries(meta: &mut VirtualCells<'_, F>, c: &StateCircuitConfig) state_root_prev: meta_query_word(meta, c.state_root, Rotation::prev()), } } - -#[cfg(test)] -mod state_circuit_stats { - use crate::{ - evm_circuit::step::ExecutionState, - stats::{bytecode_prefix_op_big_rws, print_circuit_stats_by_states}, - }; - - #[test] - pub fn get_state_states_stats() { - print_circuit_stats_by_states( - |state| { - // TODO: Enable CREATE/CREATE2 once they are supported - !matches!( - state, - ExecutionState::ErrorInvalidOpcode - | ExecutionState::CREATE - | ExecutionState::CREATE2 - | ExecutionState::SELFDESTRUCT - ) - }, - bytecode_prefix_op_big_rws, - |block, _, step_index| { - let step = &block.txs[0].steps()[step_index]; - let step_next = &block.txs[0].steps()[step_index + 1]; - step_next.rwc.0 - step.rwc.0 - }, - ); - } -} diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index 6fc559df2d..4d2f33d871 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -3,9 +3,10 @@ use super::{ }; use crate::{ evm_circuit::util::{math_gadget::generate_lagrange_base_polynomial, not}, - table::{AccountFieldTag, MPTProofType, RwTableTag}, + table::{AccountFieldTag, MPTProofType}, util::{word, Expr}, }; +use bus_mapping::operation::Target; use eth_types::Field; use gadgets::binary_number::BinaryNumberConfig; use halo2_proofs::plonk::Expression; @@ -114,35 +115,34 @@ impl ConstraintBuilder { pub fn build(&mut self, q: &Queries) { self.build_general_constraints(q); - self.condition(q.tag_matches(RwTableTag::Start), |cb| { + self.condition(q.tag_matches(Target::Start), |cb| { cb.build_start_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Memory), |cb| { + self.condition(q.tag_matches(Target::Memory), |cb| { cb.build_memory_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Stack), |cb| { + self.condition(q.tag_matches(Target::Stack), |cb| { cb.build_stack_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::AccountStorage), |cb| { + self.condition(q.tag_matches(Target::Storage), |cb| { cb.build_account_storage_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::TxAccessListAccount), |cb| { + self.condition(q.tag_matches(Target::TxAccessListAccount), |cb| { cb.build_tx_access_list_account_constraints(q) }); - self.condition( - q.tag_matches(RwTableTag::TxAccessListAccountStorage), - |cb| cb.build_tx_access_list_account_storage_constraints(q), - ); - self.condition(q.tag_matches(RwTableTag::TxRefund), |cb| { + self.condition(q.tag_matches(Target::TxAccessListAccountStorage), |cb| { + cb.build_tx_access_list_account_storage_constraints(q) + }); + self.condition(q.tag_matches(Target::TxRefund), |cb| { cb.build_tx_refund_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::Account), |cb| { + self.condition(q.tag_matches(Target::Account), |cb| { cb.build_account_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::CallContext), |cb| { + self.condition(q.tag_matches(Target::CallContext), |cb| { cb.build_call_context_constraints(q) }); - self.condition(q.tag_matches(RwTableTag::TxLog), |cb| { + self.condition(q.tag_matches(Target::TxLog), |cb| { cb.build_tx_log_constraints(q) }); } @@ -657,8 +657,8 @@ impl Queries { self.mpt_proof_type.clone() } - fn tag_matches(&self, tag: RwTableTag) -> Expression { - BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) + fn tag_matches(&self, tag: Target) -> Expression { + BinaryNumberConfig::::value_equals_expr(tag, self.tag_bits.clone()) } fn first_access(&self) -> Expression { diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs index ce79871912..b56cfece0f 100644 --- a/zkevm-circuits/src/state_circuit/dev.rs +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -17,6 +17,7 @@ where { type Config = (StateCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index 4721667553..49d25454cf 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -1,7 +1,7 @@ #![allow(unused_imports)] pub use super::{dev::*, *}; use crate::{ - table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, + table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, util::{unusable_rows, SubCircuit}, witness::{MptUpdates, Rw, RwMap}, }; @@ -32,7 +32,7 @@ const N_ROWS: usize = 1 << 16; fn state_circuit_unusable_rows() { assert_eq!( StateCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } @@ -786,7 +786,7 @@ fn invalid_stack_address_change() { #[test] fn invalid_tags() { let first_row_offset = -isize::try_from(N_ROWS).unwrap(); - let tags: BTreeSet = RwTableTag::iter().map(|x| x as usize).collect(); + let tags: BTreeSet = Target::iter().map(|x| x as usize).collect(); for i in 0..16 { if tags.contains(&i) { continue; diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index f39667797e..a7c20c83c1 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -98,17 +98,17 @@ pub struct SuperCircuitConfig { } /// Circuit configuration arguments -pub struct SuperCircuitConfigArgs { +pub struct SuperCircuitConfigArgs { /// Max txs pub max_txs: usize, /// Max calldata pub max_calldata: usize, /// Mock randomness - pub mock_randomness: u64, + pub mock_randomness: F, } impl SubCircuitConfig for SuperCircuitConfig { - type ConfigArgs = SuperCircuitConfigArgs; + type ConfigArgs = SuperCircuitConfigArgs; /// Configure SuperCircuitConfig fn new( @@ -131,9 +131,8 @@ impl SubCircuitConfig for SuperCircuitConfig { // Use a mock randomness instead of the randomness derived from the challange // (either from mock or real prover) to help debugging assignments. - let power_of_randomness: [Expression; 31] = array::from_fn(|i| { - Expression::Constant(F::from(mock_randomness).pow([1 + i as u64, 0, 0, 0])) - }); + let power_of_randomness: [Expression; 31] = + array::from_fn(|i| Expression::Constant(mock_randomness.pow([1 + i as u64, 0, 0, 0]))); let challenges = Challenges::mock( power_of_randomness[0].clone(), @@ -225,12 +224,7 @@ impl SubCircuitConfig for SuperCircuitConfig { /// The Super Circuit contains all the zkEVM circuits #[derive(Clone, Default, Debug)] -pub struct SuperCircuit< - F: Field, - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MOCK_RANDOMNESS: u64, -> { +pub struct SuperCircuit { /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -247,15 +241,16 @@ pub struct SuperCircuit< pub exp_circuit: ExpCircuit, /// Keccak Circuit pub keccak_circuit: KeccakCircuit, + /// Circuits Parameters + pub circuits_params: CircuitsParams, + /// Mock randomness + pub mock_randomness: F, } -impl - SuperCircuit -{ +impl SuperCircuit { /// Return the number of rows required to verify a given block pub fn get_num_rows_required(block: &Block) -> usize { let num_rows_evm_circuit = EvmCircuit::::get_num_rows_required(block); - assert_eq!(block.circuits_params.max_txs, MAX_TXS); let num_rows_tx_circuit = TxCircuitConfig::::get_num_rows_required(block.circuits_params.max_txs); num_rows_evm_circuit.max(num_rows_tx_circuit) @@ -265,9 +260,7 @@ impl - SubCircuit for SuperCircuit -{ +impl SubCircuit for SuperCircuit { type Config = SuperCircuitConfig; fn unusable_rows() -> usize { @@ -294,7 +287,7 @@ impl { + SuperCircuit::<_> { evm_circuit, state_circuit, tx_circuit, @@ -303,6 +296,8 @@ impl - Circuit for SuperCircuit -{ +/// Super Circuit configuration parameters +#[derive(Default)] +pub struct SuperCircuitParams { + max_txs: usize, + max_calldata: usize, + mock_randomness: F, +} + +impl Circuit for SuperCircuit { type Config = SuperCircuitConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = SuperCircuitParams; fn without_witnesses(&self) -> Self { Self::default() } - fn configure(meta: &mut ConstraintSystem) -> Self::Config { + fn params(&self) -> Self::Params { + SuperCircuitParams { + max_txs: self.circuits_params.max_txs, + max_calldata: self.circuits_params.max_calldata, + mock_randomness: self.mock_randomness, + } + } + + fn configure_with_params(meta: &mut ConstraintSystem, params: Self::Params) -> Self::Config { Self::Config::new( meta, SuperCircuitConfigArgs { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, - mock_randomness: MOCK_RANDOMNESS, + max_txs: params.max_txs, + max_calldata: params.max_calldata, + mock_randomness: params.mock_randomness, }, ) } + fn configure(_meta: &mut ConstraintSystem) -> Self::Config { + unreachable!(); + } + fn synthesize( &self, config: Self::Config, @@ -416,9 +430,7 @@ impl - SuperCircuit -{ +impl SuperCircuit { /// From the witness data, generate a SuperCircuit instance with all of the /// sub-circuits filled with their corresponding witnesses. /// @@ -428,6 +440,7 @@ impl Result<(u32, Self, Vec>, CircuitInputBuilder), bus_mapping::Error> { let block_data = BlockData::new_from_geth_data_with_params(geth_data.clone(), circuits_params); @@ -436,7 +449,7 @@ impl Result<(u32, Self, Vec>), bus_mapping::Error> { let mut block = block_convert(&builder.block, &builder.code_db).unwrap(); - block.randomness = F::from(MOCK_RANDOMNESS); - assert_eq!(block.circuits_params.max_txs, MAX_TXS); - assert_eq!(block.circuits_params.max_calldata, MAX_CALLDATA); + block.randomness = mock_randomness; let (_, rows_needed) = Self::min_num_rows_block(&block); let k = log2_ceil(Self::unusable_rows() + rows_needed); log::debug!("super circuit uses k = {}", k); - let circuit = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS>::new_from_block(&block); + let circuit = SuperCircuit::new_from_block(&block); let instance = circuit.instance(); Ok((k, circuit, instance)) diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index 989d143958..697da5cb0c 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -12,23 +12,20 @@ use eth_types::{address, bytecode, geth_types::GethData, Word}; #[test] fn super_circuit_degree() { let mut cs = ConstraintSystem::::default(); - SuperCircuit::<_, 1, 32, 0x100>::configure(&mut cs); + let params = SuperCircuitParams { + max_txs: 1, + max_calldata: 32, + mock_randomness: Fr::from(0x100), + }; + SuperCircuit::configure_with_params(&mut cs, params); log::info!("super circuit degree: {}", cs.degree()); log::info!("super circuit minimum_rows: {}", cs.minimum_rows()); assert!(cs.degree() <= 9); } -fn test_super_circuit< - const MAX_TXS: usize, - const MAX_CALLDATA: usize, - const MOCK_RANDOMNESS: u64, ->( - block: GethData, - circuits_params: CircuitsParams, -) { +fn test_super_circuit(block: GethData, circuits_params: CircuitsParams, mock_randomness: Fr) { let (k, circuit, instance, _) = - SuperCircuit::::build(block, circuits_params) - .unwrap(); + SuperCircuit::::build(block, circuits_params, mock_randomness).unwrap(); let prover = MockProver::run(k, &circuit, instance).unwrap(); let res = prover.verify_par(); if let Err(err) = res { @@ -131,11 +128,9 @@ const TEST_MOCK_RANDOMNESS: u64 = 0x100; #[test] fn serial_test_super_circuit_1tx_1max_tx() { let block = block_1tx(); - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 32; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -143,17 +138,15 @@ fn serial_test_super_circuit_1tx_1max_tx() { max_evm_rows: 0, max_keccak_rows: 0, }; - test_super_circuit::(block, circuits_params); + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } #[ignore] #[test] fn serial_test_super_circuit_1tx_2max_tx() { let block = block_1tx(); - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 2, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -161,17 +154,15 @@ fn serial_test_super_circuit_1tx_2max_tx() { max_evm_rows: 0, max_keccak_rows: 0, }; - test_super_circuit::(block, circuits_params); + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } #[ignore] #[test] fn serial_test_super_circuit_2tx_2max_tx() { let block = block_2tx(); - const MAX_TXS: usize = 2; - const MAX_CALLDATA: usize = 32; let circuits_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 2, + max_calldata: 32, max_rws: 256, max_copy_rows: 256, max_exp_steps: 256, @@ -179,5 +170,5 @@ fn serial_test_super_circuit_2tx_2max_tx() { max_evm_rows: 0, max_keccak_rows: 0, }; - test_super_circuit::(block, circuits_params); + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 628f22006c..c42d8c6ed9 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -3,14 +3,13 @@ use crate::{ copy_circuit::util::number_or_hash_to_field, evm_circuit::util::rlc, - exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, impl_expr, util::{build_tx_log_address, keccak, word, Challenges}, witness::{ Block, BlockContext, Bytecode, MptUpdateRow, MptUpdates, Rw, RwMap, RwRow, Transaction, }, }; -use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep, ExpEvent}; +use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep}; use core::iter::once; use eth_types::{Field, ToScalar, U256}; use gadgets::{ @@ -26,6 +25,35 @@ use itertools::Itertools; use std::array; use strum_macros::{EnumCount, EnumIter}; +/// block table +pub(crate) mod block_table; +/// bytecode table +pub(crate) mod bytecode_table; +/// copy Table +pub(crate) mod copy_table; +/// exp(exponentiation) table +pub(crate) mod exp_table; +/// keccak table +pub(crate) mod keccak_table; +/// mpt table +pub(crate) mod mpt_table; +/// rw table +pub(crate) mod rw_table; +/// tx table +pub(crate) mod tx_table; + +pub(crate) use block_table::{BlockContextFieldTag, BlockTable}; +pub(crate) use bytecode_table::{BytecodeFieldTag, BytecodeTable}; +pub(crate) use copy_table::CopyTable; +pub(crate) use exp_table::ExpTable; +pub(crate) use keccak_table::KeccakTable; + +pub(crate) use mpt_table::{MPTProofType, MptTable}; +pub(crate) use rw_table::RwTable; +pub(crate) use tx_table::{ + TxContextFieldTag, TxFieldTag, TxLogFieldTag, TxReceiptFieldTag, TxTable, +}; + /// Trait used to define lookup tables pub trait LookupTable { /// Returns the list of ALL the table columns following the table order. @@ -86,254 +114,6 @@ impl> + Copy, const W: usize> LookupTable for [ } } -/// Tag used to identify each field in the transaction in a row of the -/// transaction table. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum TxFieldTag { - /// Unused tag - Null = 0, - /// Nonce - Nonce, - /// Gas - Gas, - /// GasPrice - GasPrice, - /// CallerAddress - CallerAddress, - /// CalleeAddress - CalleeAddress, - /// IsCreate - IsCreate, - /// Value - Value, - /// CallDataLength - CallDataLength, - /// Gas cost for transaction call data (4 for byte == 0, 16 otherwise) - CallDataGasCost, - /// TxSignHash: Hash of the transaction without the signature, used for - /// signing. - TxSignHash, - /// CallData - CallData, -} -impl_expr!(TxFieldTag); - -/// Alias for TxFieldTag used by EVM Circuit -pub type TxContextFieldTag = TxFieldTag; - -/// Table that contains the fields of all Transactions in a block -#[derive(Clone, Debug)] -pub struct TxTable { - /// Tx ID - pub tx_id: Column, - /// Tag (TxContextFieldTag) - pub tag: Column, - /// Index for Tag = CallData - pub index: Column, - /// Value - pub value_word: word::Word>, - /// Value - #[deprecated(note = "value_word is fav")] - pub value: Column, -} - -impl TxTable { - /// Construct a new TxTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - tx_id: meta.advice_column(), - tag: meta.fixed_column(), - index: meta.advice_column(), - value_word: word::Word::new([meta.advice_column(), meta.advice_column()]), - value: meta.advice_column(), - } - } - - /// Assign the `TxTable` from a list of block `Transaction`s, followig the - /// same layout that the Tx Circuit uses. - pub fn load( - &self, - layouter: &mut impl Layouter, - txs: &[Transaction], - max_txs: usize, - max_calldata: usize, - challenges: &Challenges>, - ) -> Result<(), Error> { - assert!( - txs.len() <= max_txs, - "txs.len() <= max_txs: txs.len()={}, max_txs={}", - txs.len(), - max_txs - ); - let sum_txs_calldata = txs.iter().map(|tx| tx.call_data.len()).sum(); - assert!( - sum_txs_calldata <= max_calldata, - "sum_txs_calldata <= max_calldata: sum_txs_calldata={}, max_calldata={}", - sum_txs_calldata, - max_calldata, - ); - - fn assign_row( - region: &mut Region<'_, F>, - offset: usize, - advice_columns: &[Column], - tag: &Column, - row: &[Value; 4], - msg: &str, - ) -> Result<(), Error> { - for (index, column) in advice_columns.iter().enumerate() { - region.assign_advice( - || format!("tx table {} row {}", msg, offset), - *column, - offset, - || row[if index > 0 { index + 1 } else { index }], - )?; - } - region.assign_fixed( - || format!("tx table {} row {}", msg, offset), - *tag, - offset, - || row[1], - )?; - Ok(()) - } - - layouter.assign_region( - || "tx table", - |mut region| { - let mut offset = 0; - let advice_columns = - [vec![self.tx_id, self.index], self.value_word.limbs.to_vec()].concat(); - assign_row( - &mut region, - offset, - &advice_columns, - &self.tag, - &[(); 4].map(|_| Value::known(F::ZERO)), - "all-zero", - )?; - offset += 1; - - // Tx Table contains an initial region that has a size parametrized by max_txs - // with all the tx data except for calldata, and then a second - // region that has a size parametrized by max_calldata with all - // the tx calldata. This is required to achieve a constant fixed column tag - // regardless of the number of input txs or the calldata size of each tx. - let mut calldata_assignments: Vec<[Value; 4]> = Vec::new(); - // Assign Tx data (all tx fields except for calldata) - let padding_txs: Vec<_> = (txs.len()..max_txs) - .map(|i| Transaction { - id: i + 1, - ..Default::default() - }) - .collect(); - for tx in txs.iter().chain(padding_txs.iter()) { - let [tx_data, tx_calldata] = tx.table_assignments(*challenges); - for row in tx_data { - assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; - offset += 1; - } - calldata_assignments.extend(tx_calldata.iter()); - } - // Assign Tx calldata - let padding_calldata = (sum_txs_calldata..max_calldata).map(|_| { - [ - Value::known(F::ZERO), - Value::known(F::from(TxContextFieldTag::CallData as u64)), - Value::known(F::ZERO), - Value::known(F::ZERO), - ] - }); - for row in calldata_assignments.into_iter().chain(padding_calldata) { - assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; - offset += 1; - } - Ok(()) - }, - ) - } -} - -impl LookupTable for TxTable { - fn columns(&self) -> Vec> { - vec![ - self.tx_id.into(), - self.tag.into(), - self.index.into(), - self.value_word.lo().into(), - self.value_word.hi().into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("tx_id"), - String::from("tag"), - String::from("index"), - String::from("value_lo"), - String::from("value_hi"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_advice(self.tx_id, Rotation::cur()), - meta.query_fixed(self.tag, Rotation::cur()), - meta.query_advice(self.index, Rotation::cur()), - meta.query_advice(self.value_word.lo(), Rotation::cur()), - meta.query_advice(self.value_word.hi(), Rotation::cur()), - ] - } -} - -/// Tag to identify the operation type in a RwTable row -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)] -pub enum RwTableTag { - /// Start (used for padding) - Start = 1, - /// Stack operation - Stack, - /// Memory operation - Memory, - /// Account Storage operation - AccountStorage, - /// Tx Access List Account operation - TxAccessListAccount, - /// Tx Access List Account Storage operation - TxAccessListAccountStorage, - /// Tx Refund operation - TxRefund, - /// Account operation - Account, - /// Call Context operation - CallContext, - /// Tx Log operation - TxLog, - /// Tx Receipt operation - TxReceipt, -} -impl_expr!(RwTableTag); - -impl RwTableTag { - /// Returns true if the RwTable operation is reversible - pub fn is_reversible(self) -> bool { - matches!( - self, - RwTableTag::TxAccessListAccount - | RwTableTag::TxAccessListAccountStorage - | RwTableTag::TxRefund - | RwTableTag::Account - | RwTableTag::AccountStorage - ) - } -} - -impl From for usize { - fn from(t: RwTableTag) -> Self { - t as usize - } -} - /// Tag for an AccountField in RwTable #[derive(Clone, Copy, Debug, EnumIter, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum AccountFieldTag { @@ -348,30 +128,6 @@ pub enum AccountFieldTag { } impl_expr!(AccountFieldTag); -/// Tag for a TxLogField in RwTable -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] -pub enum TxLogFieldTag { - /// Address field - Address = 1, - /// Topic field - Topic, - /// Data field - Data, -} -impl_expr!(TxLogFieldTag); - -/// Tag for a TxReceiptField in RwTable -#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, EnumCount)] -pub enum TxReceiptFieldTag { - /// Tx result - PostStateOrStatus = 1, - /// CumulativeGasUsed in the tx - CumulativeGasUsed, - /// Number of logs in the tx - LogLength, -} -impl_expr!(TxReceiptFieldTag); - /// Tag for a CallContextField in RwTable #[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] pub enum CallContextFieldTag { @@ -429,1103 +185,3 @@ pub enum CallContextFieldTag { ReversibleWriteCounter, } impl_expr!(CallContextFieldTag); - -/// The RwTable shared between EVM Circuit and State Circuit, which contains -/// traces of the EVM state operations. -#[derive(Clone, Copy, Debug)] -pub struct RwTable { - /// Read Write Counter - pub rw_counter: Column, - /// Is Write - pub is_write: Column, - /// Tag - pub tag: Column, - /// Key1 (Id) - pub id: Column, - /// Key2 (Address) - pub address: Column, - /// Key3 (FieldTag) - pub field_tag: Column, - /// Key3 (StorageKey) - pub storage_key: word::Word>, - /// Value - pub value: word::Word>, - /// Value Previous - pub value_prev: word::Word>, - /// InitVal (Committed Value) - pub init_val: word::Word>, -} - -impl LookupTable for RwTable { - fn columns(&self) -> Vec> { - vec![ - self.rw_counter.into(), - self.is_write.into(), - self.tag.into(), - self.id.into(), - self.address.into(), - self.field_tag.into(), - self.storage_key.lo().into(), - self.storage_key.hi().into(), - self.value.lo().into(), - self.value.hi().into(), - self.value_prev.lo().into(), - self.value_prev.hi().into(), - self.init_val.lo().into(), - self.init_val.hi().into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("rw_counter"), - String::from("is_write"), - String::from("tag"), - String::from("id"), - String::from("address"), - String::from("field_tag"), - String::from("storage_key_lo"), - String::from("storage_key_hi"), - String::from("value_lo"), - String::from("value_hi"), - String::from("value_prev_lo"), - String::from("value_prev_hi"), - String::from("init_val_lo"), - String::from("init_val_hi"), - ] - } -} -impl RwTable { - /// Construct a new RwTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - rw_counter: meta.advice_column(), - is_write: meta.advice_column(), - tag: meta.advice_column(), - id: meta.advice_column(), - address: meta.advice_column(), - field_tag: meta.advice_column(), - storage_key: word::Word::new([meta.advice_column(), meta.advice_column()]), - value: word::Word::new([meta.advice_column(), meta.advice_column()]), - value_prev: word::Word::new([meta.advice_column(), meta.advice_column()]), - init_val: word::Word::new([meta.advice_column(), meta.advice_column()]), - } - } - fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - row: &RwRow>, - ) -> Result<(), Error> { - for (column, value) in [ - (self.address, row.address), - (self.rw_counter, row.rw_counter), - (self.is_write, row.is_write), - (self.tag, row.tag), - (self.id, row.id), - (self.field_tag, row.field_tag), - ] { - region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; - } - for (column, value) in [ - (self.storage_key, row.storage_key), - (self.value, row.value), - (self.value_prev, row.value_prev), - (self.init_val, row.init_val), - ] { - value.assign_advice(region, || "assign rw row on rw table", column, offset)?; - } - - Ok(()) - } - - /// Assign the `RwTable` from a `RwMap`, following the same - /// table layout that the State Circuit uses. - pub fn load( - &self, - layouter: &mut impl Layouter, - rws: &[Rw], - n_rows: usize, - ) -> Result<(), Error> { - layouter.assign_region( - || "rw table", - |mut region| self.load_with_region(&mut region, rws, n_rows), - ) - } - - pub(crate) fn load_with_region( - &self, - region: &mut Region<'_, F>, - rws: &[Rw], - n_rows: usize, - ) -> Result<(), Error> { - let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); - for (offset, row) in rows.iter().enumerate() { - self.assign(region, offset, &row.table_assignment())?; - } - Ok(()) - } -} - -/// The types of proofs in the MPT table -#[derive(Clone, Copy, Debug)] -pub enum MPTProofType { - /// Nonce updated - NonceMod = AccountFieldTag::Nonce as isize, - /// Balance updated - BalanceMod = AccountFieldTag::Balance as isize, - /// Code hash exists - CodeHashMod = AccountFieldTag::CodeHash as isize, - /// Account does not exist - NonExistingAccountProof = AccountFieldTag::NonExisting as isize, - /// Storage updated - StorageMod, - /// Storage does not exist - NonExistingStorageProof, -} -impl_expr!(MPTProofType); - -impl From for MPTProofType { - fn from(tag: AccountFieldTag) -> Self { - match tag { - AccountFieldTag::Nonce => Self::NonceMod, - AccountFieldTag::Balance => Self::BalanceMod, - AccountFieldTag::CodeHash => Self::CodeHashMod, - AccountFieldTag::NonExisting => Self::NonExistingAccountProof, - } - } -} - -/// The MptTable shared between MPT Circuit and State Circuit -#[derive(Clone, Copy, Debug)] -pub struct MptTable([Column; 12]); - -impl LookupTable for MptTable { - fn columns(&self) -> Vec> { - self.0.iter().map(|&col| col.into()).collect() - } - - fn annotations(&self) -> Vec { - vec![ - String::from("address"), - String::from("storage_key_lo"), - String::from("storage_key_hi"), - String::from("proof_type"), - String::from("new_root_lo"), - String::from("new_root_hi"), - String::from("old_root_lo"), - String::from("old_root_hi"), - String::from("new_value_lo"), - String::from("new_value_hi"), - String::from("old_value_lo"), - String::from("old_value_hi"), - ] - } -} - -impl MptTable { - /// Construct a new MptTable - pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { - Self([ - meta.advice_column(), // Address - meta.advice_column(), // Storage key lo - meta.advice_column(), // Storage key hi - meta.advice_column(), // Proof type - meta.advice_column(), // New root lo - meta.advice_column(), // New root hi - meta.advice_column(), // Old root lo - meta.advice_column(), // Old root hi - meta.advice_column(), // New value lo - meta.advice_column(), // New value hi - meta.advice_column(), // Old value lo - meta.advice_column(), // Old value hi - ]) - } - - pub(crate) fn assign( - &self, - region: &mut Region<'_, F>, - offset: usize, - row: &MptUpdateRow>, - ) -> Result<(), Error> { - for (column, value) in self.0.iter().zip_eq(row.values()) { - region.assign_advice(|| "assign mpt table row value", *column, offset, || *value)?; - } - Ok(()) - } - - pub(crate) fn load( - &self, - layouter: &mut impl Layouter, - updates: &MptUpdates, - ) -> Result<(), Error> { - layouter.assign_region( - || "mpt table", - |mut region| self.load_with_region(&mut region, updates), - ) - } - - pub(crate) fn load_with_region( - &self, - region: &mut Region<'_, F>, - updates: &MptUpdates, - ) -> Result<(), Error> { - for (offset, row) in updates.table_assignments().iter().enumerate() { - self.assign(region, offset, row)?; - } - Ok(()) - } -} - -/// Tag to identify the field in a Bytecode Table row -#[derive(Clone, Copy, Debug)] -pub enum BytecodeFieldTag { - /// Header field - Header, - /// Byte field - Byte, -} -impl_expr!(BytecodeFieldTag); - -/// Table with Bytecode indexed by its Code Hash -#[derive(Clone, Debug)] -pub struct BytecodeTable { - /// Code Hash - pub code_hash_word: word::Word>, - #[deprecated] - /// Code Hash - pub code_hash: Column, - /// Tag - pub tag: Column, - /// Index - pub index: Column, - /// Is Code is true when the byte is not an argument to a PUSH* instruction. - pub is_code: Column, - /// Value - pub value: Column, -} - -impl BytecodeTable { - /// Construct a new BytecodeTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); - let code_hash_word = word::Word::new([meta.advice_column(), meta.advice_column()]); - let code_hash = meta.advice_column(); - Self { - code_hash_word, - code_hash, - tag, - index, - is_code, - value, - } - } - - /// Assign the `BytecodeTable` from a list of bytecodes, followig the same - /// table layout that the Bytecode Circuit uses. - pub fn load<'a, F: Field>( - &self, - layouter: &mut impl Layouter, - bytecodes: impl IntoIterator + Clone, - challenges: &Challenges>, - ) -> Result<(), Error> { - layouter.assign_region( - || "bytecode table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "bytecode table all-zero row", - column, - offset, - || Value::known(F::ZERO), - )?; - } - offset += 1; - - let bytecode_table_columns = - >::advice_columns(self); - for bytecode in bytecodes.clone() { - for row in bytecode.table_assignments(challenges) { - for (&column, value) in bytecode_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("bytecode table row {}", offset), - column, - offset, - || value, - )?; - } - offset += 1; - } - } - Ok(()) - }, - ) - } -} - -impl LookupTable for BytecodeTable { - fn columns(&self) -> Vec> { - vec![ - self.code_hash_word.lo().into(), - self.code_hash_word.hi().into(), - self.tag.into(), - self.index.into(), - self.is_code.into(), - self.value.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("code_hash_lo"), - String::from("code_hash_hi"), - String::from("tag"), - String::from("index"), - String::from("is_code"), - String::from("value"), - ] - } -} - -/// Tag to identify the field in a Block Table row -// Keep the sequence consistent with OpcodeId for scalar -#[derive(Clone, Copy, Debug)] -pub enum BlockContextFieldTag { - /// Coinbase field - Coinbase = 1, - /// Timestamp field - Timestamp, - /// Number field - Number, - /// Difficulty field - Difficulty, - /// Gas Limit field - GasLimit, - /// Base Fee field - BaseFee = 8, - /// Block Hash field - BlockHash, - /// Chain ID field. Although this is not a field in the block header, we - /// add it here for convenience. - ChainId, -} -impl_expr!(BlockContextFieldTag); - -/// Table with Block header fields -#[derive(Clone, Debug)] -pub struct BlockTable { - /// Tag - pub tag: Column, - /// Index - pub index: Column, - /// Value - pub value_word: word::Word>, - #[deprecated] - /// Value - pub value: Column, -} - -impl BlockTable { - /// Construct a new BlockTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - tag: meta.advice_column(), - index: meta.advice_column(), - value_word: word::Word::new([meta.advice_column(), meta.advice_column()]), - value: meta.advice_column(), - } - } - - /// Assign the `BlockTable` from a `BlockContext`. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &BlockContext, - randomness: Value, - ) -> Result<(), Error> { - layouter.assign_region( - || "block table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "block table all-zero row", - column, - offset, - || Value::known(F::ZERO), - )?; - } - offset += 1; - - let block_table_columns = >::advice_columns(self); - for row in block.table_assignments(randomness) { - for (&column, value) in block_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("block table row {}", offset), - column, - offset, - || value, - )?; - } - offset += 1; - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for BlockTable { - fn columns(&self) -> Vec> { - vec![ - self.tag.into(), - self.index.into(), - self.value_word.lo().into(), - self.value_word.hi().into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("tag"), - String::from("index"), - String::from("value_lo"), - String::from("value_hi"), - ] - } -} - -/// Keccak Table, used to verify keccak hashing from RLC'ed input. -#[derive(Clone, Debug)] -pub struct KeccakTable { - /// True when the row is enabled - pub is_enabled: Column, - /// Byte array input as `RLC(reversed(input))` - pub input_rlc: Column, // RLC of input bytes - /// Byte array input length - pub input_len: Column, - /// Output hash word - pub output: word::Word>, - #[deprecated] - /// RLC of the hash result - pub output_rlc: Column, -} - -impl LookupTable for KeccakTable { - fn columns(&self) -> Vec> { - vec![ - self.is_enabled.into(), - self.input_rlc.into(), - self.input_len.into(), - self.output.lo().into(), - self.output.hi().into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_enabled"), - String::from("input_rlc"), - String::from("input_len"), - String::from("output_lo"), - String::from("output_hi"), - ] - } -} - -impl KeccakTable { - /// Construct a new KeccakTable - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - is_enabled: meta.advice_column(), - input_rlc: meta.advice_column_in(SecondPhase), - input_len: meta.advice_column(), - output: word::Word::new([meta.advice_column(), meta.advice_column()]), - output_rlc: meta.advice_column_in(SecondPhase), - } - } - - /// Generate the keccak table assignments from a byte array input. - pub fn assignments( - input: &[u8], - challenges: &Challenges>, - ) -> Vec<[Value; 5]> { - let input_rlc = challenges - .keccak_input() - .map(|challenge| rlc::value(input.iter().rev(), challenge)); - let input_len = F::from(input.len() as u64); - let output = word::Word::from(keccak(input)); - - vec![[ - Value::known(F::ONE), - input_rlc, - Value::known(input_len), - Value::known(output.lo()), - Value::known(output.hi()), - ]] - } - - /// Assign a table row for keccak table - pub fn assign_row( - &self, - region: &mut Region, - offset: usize, - values: [Value; 5], - ) -> Result<(), Error> { - for (&column, value) in >::advice_columns(self) - .iter() - .zip(values.iter()) - { - region.assign_advice(|| format!("assign {}", offset), column, offset, || *value)?; - } - Ok(()) - } - - /// Provide this function for the case that we want to consume a keccak - /// table but without running the full keccak circuit - pub fn dev_load<'a, F: Field>( - &self, - layouter: &mut impl Layouter, - inputs: impl IntoIterator> + Clone, - challenges: &Challenges>, - ) -> Result<(), Error> { - layouter.assign_region( - || "keccak table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "keccak table all-zero row", - column, - offset, - || Value::known(F::ZERO), - )?; - } - offset += 1; - - let keccak_table_columns = >::advice_columns(self); - for input in inputs.clone() { - for row in Self::assignments(input, challenges) { - // let mut column_index = 0; - for (&column, value) in keccak_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("keccak table row {}", offset), - column, - offset, - || value, - )?; - } - offset += 1; - } - } - Ok(()) - }, - ) - } - - /// returns matchings between the circuit columns passed as parameters and - /// the table collumns - pub fn match_columns( - &self, - value_rlc: Column, - length: Column, - code_hash: Column, - ) -> Vec<(Column, Column)> { - vec![ - (value_rlc, self.input_rlc), - (length, self.input_len), - (code_hash, self.output_rlc), - ] - } -} - -/// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, -/// TxLogs and TxCallData. -#[derive(Clone, Copy, Debug)] -pub struct CopyTable { - /// Whether the row is the first read-write pair for a copy event. - pub is_first: Column, - /// The relevant ID for the read-write row, represented as a random linear - /// combination. The ID may be one of the below: - /// 1. Call ID/Caller ID for CopyDataType::Memory - /// 2. The hi/lo limbs of bytecode hash for CopyDataType::Bytecode - /// 3. Transaction ID for CopyDataType::TxCalldata, CopyDataType::TxLog - pub id: word::Word>, - /// The source/destination address for this copy step. Can be memory - /// address, byte index in the bytecode, tx call data, and tx log data. - pub addr: Column, - /// The end of the source buffer for the copy event. Any data read from an - /// address greater than or equal to this value will be 0. - pub src_addr_end: Column, - /// The number of bytes left to be copied. - pub bytes_left: Column, - /// An accumulator value in the RLC representation. This is used for - /// specific purposes, for instance, when `tag == CopyDataType::RlcAcc`. - /// Having an additional column for the `rlc_acc` simplifies the lookup - /// to copy table. - pub rlc_acc: Column, - /// The associated read-write counter for this row. - pub rw_counter: Column, - /// Decrementing counter denoting reverse read-write counter. - pub rwc_inc_left: Column, - /// Binary chip to constrain the copy table conditionally depending on the - /// current row's tag, whether it is Bytecode, Memory, TxCalldata or - /// TxLog. - pub tag: BinaryNumberConfig, -} - -type CopyTableRow = [(Value, &'static str); 8]; -type CopyCircuitRow = [(Value, &'static str); 4]; - -impl CopyTable { - /// Construct a new CopyTable - pub fn construct(meta: &mut ConstraintSystem, q_enable: Column) -> Self { - Self { - is_first: meta.advice_column(), - id: word::Word::new([meta.advice_column(), meta.advice_column()]), - tag: BinaryNumberChip::configure(meta, q_enable, None), - addr: meta.advice_column(), - src_addr_end: meta.advice_column(), - bytes_left: meta.advice_column(), - rlc_acc: meta.advice_column_in(SecondPhase), - rw_counter: meta.advice_column(), - rwc_inc_left: meta.advice_column(), - } - } - - /// Generate the copy table and copy circuit assignments from a copy event. - pub fn assignments( - copy_event: &CopyEvent, - challenges: Challenges>, - ) -> Vec<(CopyDataType, CopyTableRow, CopyCircuitRow)> { - let mut assignments = Vec::new(); - // rlc_acc - let rlc_acc = if copy_event.dst_type == CopyDataType::RlcAcc { - let values = copy_event - .bytes - .iter() - .map(|(value, _)| *value) - .collect::>(); - challenges - .keccak_input() - .map(|keccak_input| rlc::value(values.iter().rev(), keccak_input)) - } else { - Value::known(F::ZERO) - }; - let mut value_acc = Value::known(F::ZERO); - for (step_idx, (is_read_step, copy_step)) in copy_event - .bytes - .iter() - .flat_map(|(value, is_code)| { - let read_step = CopyStep { - value: *value, - is_code: if copy_event.src_type == CopyDataType::Bytecode { - Some(*is_code) - } else { - None - }, - }; - let write_step = CopyStep { - value: *value, - is_code: if copy_event.dst_type == CopyDataType::Bytecode { - Some(*is_code) - } else { - None - }, - }; - once((true, read_step)).chain(once((false, write_step))) - }) - .enumerate() - { - // is_first - let is_first = Value::known(if step_idx == 0 { F::ONE } else { F::ZERO }); - // is last - let is_last = if step_idx == copy_event.bytes.len() * 2 - 1 { - Value::known(F::ONE) - } else { - Value::known(F::ZERO) - }; - - // id - let id = if is_read_step { - number_or_hash_to_field(©_event.src_id, challenges.evm_word()) - } else { - number_or_hash_to_field(©_event.dst_id, challenges.evm_word()) - }; - - // tag binary bumber chip - let tag = if is_read_step { - copy_event.src_type - } else { - copy_event.dst_type - }; - - // addr - let copy_step_addr: u64 = - if is_read_step { - copy_event.src_addr - } else { - copy_event.dst_addr - } + (u64::try_from(step_idx).unwrap() - if is_read_step { 0 } else { 1 }) / 2u64; - - let addr = if tag == CopyDataType::TxLog { - Value::known( - build_tx_log_address( - copy_step_addr, - TxLogFieldTag::Data, - copy_event.log_id.unwrap(), - ) - .to_scalar() - .unwrap(), - ) - } else { - Value::known(F::from(copy_step_addr)) - }; - - // bytes_left - let bytes_left = u64::try_from(copy_event.bytes.len() * 2 - step_idx).unwrap() / 2; - // value - let value = if copy_event.dst_type == CopyDataType::RlcAcc { - if is_read_step { - Value::known(F::from(copy_step.value as u64)) - } else { - value_acc = value_acc * challenges.keccak_input() - + Value::known(F::from(copy_step.value as u64)); - value_acc - } - } else { - Value::known(F::from(copy_step.value as u64)) - }; - // is_pad - let is_pad = Value::known(F::from( - (is_read_step && copy_step_addr >= copy_event.src_addr_end) as u64, - )); - - // is_code - let is_code = Value::known(copy_step.is_code.map_or(F::ZERO, |v| F::from(v as u64))); - - assignments.push(( - tag, - [ - (is_first, "is_first"), - (id, "id"), - (addr, "addr"), - ( - Value::known(F::from(copy_event.src_addr_end)), - "src_addr_end", - ), - (Value::known(F::from(bytes_left)), "bytes_left"), - (rlc_acc, "rlc_acc"), - ( - Value::known(F::from(copy_event.rw_counter(step_idx))), - "rw_counter", - ), - ( - Value::known(F::from(copy_event.rw_counter_increase_left(step_idx))), - "rwc_inc_left", - ), - ], - [ - (is_last, "is_last"), - (value, "value"), - (is_pad, "is_pad"), - (is_code, "is_code"), - ], - )); - } - assignments - } - - /// Assign the `CopyTable` from a `Block`. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &Block, - challenges: &Challenges>, - ) -> Result<(), Error> { - layouter.assign_region( - || "copy table", - |mut region| { - let mut offset = 0; - for column in >::advice_columns(self) { - region.assign_advice( - || "copy table all-zero row", - column, - offset, - || Value::known(F::ZERO), - )?; - } - offset += 1; - - let tag_chip = BinaryNumberChip::construct(self.tag); - let copy_table_columns = >::advice_columns(self); - for copy_event in block.copy_events.iter() { - for (tag, row, _) in Self::assignments(copy_event, *challenges) { - for (&column, (value, label)) in copy_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("{} at row: {}", label, offset), - column, - offset, - || value, - )?; - } - tag_chip.assign(&mut region, offset, &tag)?; - offset += 1; - } - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for CopyTable { - fn columns(&self) -> Vec> { - vec![ - self.is_first.into(), - self.id.lo().into(), - self.id.hi().into(), - self.addr.into(), - self.src_addr_end.into(), - self.bytes_left.into(), - self.rlc_acc.into(), - self.rw_counter.into(), - self.rwc_inc_left.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_first"), - String::from("id_lo"), - String::from("id_hi"), - String::from("addr"), - String::from("src_addr_end"), - String::from("bytes_left"), - String::from("rlc_acc"), - String::from("rw_counter"), - String::from("rwc_inc_left"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_advice(self.is_first, Rotation::cur()), - meta.query_advice(self.id.lo(), Rotation::cur()), // src_id - meta.query_advice(self.id.hi(), Rotation::cur()), // src_id - self.tag.value(Rotation::cur())(meta), // src_tag - meta.query_advice(self.id.lo(), Rotation::next()), // dst_id - meta.query_advice(self.id.hi(), Rotation::next()), // dst_id - self.tag.value(Rotation::next())(meta), // dst_tag - meta.query_advice(self.addr, Rotation::cur()), // src_addr - meta.query_advice(self.src_addr_end, Rotation::cur()), // src_addr_end - meta.query_advice(self.addr, Rotation::next()), // dst_addr - meta.query_advice(self.bytes_left, Rotation::cur()), // length - meta.query_advice(self.rlc_acc, Rotation::cur()), // rlc_acc - meta.query_advice(self.rw_counter, Rotation::cur()), // rw_counter - meta.query_advice(self.rwc_inc_left, Rotation::cur()), // rwc_inc_left - ] - } -} - -/// Lookup table within the Exponentiation circuit. -#[derive(Clone, Copy, Debug)] -pub struct ExpTable { - /// Whether the row is the start of a step. - pub is_step: Column, - /// An identifier for every exponentiation trace, at the moment this is the - /// read-write counter at the time of the lookups done to the - /// exponentiation table. - pub identifier: Column, - /// Whether this row is the last row in the exponentiation operation's - /// trace. - pub is_last: Column, - /// The integer base of the exponentiation. - pub base_limb: Column, - /// The integer exponent of the exponentiation. - pub exponent_lo_hi: Column, - /// The intermediate result of exponentiation by squaring. - pub exponentiation_lo_hi: Column, -} - -impl ExpTable { - /// Construct the Exponentiation table. - pub fn construct(meta: &mut ConstraintSystem) -> Self { - Self { - is_step: meta.fixed_column(), - identifier: meta.advice_column(), - is_last: meta.advice_column(), - base_limb: meta.advice_column(), - exponent_lo_hi: meta.advice_column(), - exponentiation_lo_hi: meta.advice_column(), - } - } - - /// Given an exponentiation event and randomness, get assignments to the - /// exponentiation table. - pub fn assignments(exp_event: &ExpEvent) -> Vec<[F; 5]> { - let mut assignments = Vec::new(); - let base_limbs = split_u256_limb64(&exp_event.base); - let identifier = F::from(exp_event.identifier as u64); - let mut exponent = exp_event.exponent; - for (step_idx, exp_step) in exp_event.steps.iter().rev().enumerate() { - let is_last = if step_idx == exp_event.steps.len() - 1 { - F::ONE - } else { - F::ZERO - }; - let (exp_lo, exp_hi) = split_u256(&exp_step.d); - let (exponent_lo, exponent_hi) = split_u256(&exponent); - - // row 1 - assignments.push([ - identifier, - is_last, - base_limbs[0].as_u64().into(), - exponent_lo - .to_scalar() - .expect("exponent should fit to scalar"), - exp_lo - .to_scalar() - .expect("exponentiation lo should fit to scalar"), - ]); - // row 2 - assignments.push([ - identifier, - F::ZERO, - base_limbs[1].as_u64().into(), - exponent_hi - .to_scalar() - .expect("exponent hi should fit to scalar"), - exp_hi - .to_scalar() - .expect("exponentiation hi should fit to scalar"), - ]); - // row 3 - assignments.push([ - identifier, - F::ZERO, - base_limbs[2].as_u64().into(), - F::ZERO, - F::ZERO, - ]); - // row 4 - assignments.push([ - identifier, - F::ZERO, - base_limbs[3].as_u64().into(), - F::ZERO, - F::ZERO, - ]); - for _ in ROWS_PER_STEP..OFFSET_INCREMENT { - assignments.push([F::ZERO, F::ZERO, F::ZERO, F::ZERO, F::ZERO]); - } - - // update intermediate exponent. - let (exponent_div2, remainder) = exponent.div_mod(U256::from(2)); - if remainder.is_zero() { - // exponent is even - exponent = exponent_div2; - } else { - // exponent is odd - exponent = exponent - 1; - } - } - assignments - } - - /// Assign witness data from a block to the exponentiation table. - pub fn load( - &self, - layouter: &mut impl Layouter, - block: &Block, - ) -> Result<(), Error> { - layouter.assign_region( - || "exponentiation table", - |mut region| { - let mut offset = 0; - let exp_table_columns = >::advice_columns(self); - for exp_event in block.exp_events.iter() { - for row in Self::assignments::(exp_event) { - for (&column, value) in exp_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("exponentiation table row {}", offset), - column, - offset, - || Value::known(value), - )?; - } - let is_step = if offset % OFFSET_INCREMENT == 0 { - F::ONE - } else { - F::ZERO - }; - region.assign_fixed( - || format!("exponentiation table row {}", offset), - self.is_step, - offset, - || Value::known(is_step), - )?; - offset += 1; - } - } - - // pad an empty row - let row = [F::from_u128(0); 5]; - for (column, value) in exp_table_columns.iter().zip_eq(row) { - region.assign_advice( - || format!("exponentiation table row {}", offset), - *column, - offset, - || Value::known(value), - )?; - } - - Ok(()) - }, - ) - } -} - -impl LookupTable for ExpTable { - fn columns(&self) -> Vec> { - vec![ - self.is_step.into(), - self.identifier.into(), - self.is_last.into(), - self.base_limb.into(), - self.exponent_lo_hi.into(), - self.exponentiation_lo_hi.into(), - ] - } - - fn annotations(&self) -> Vec { - vec![ - String::from("is_step"), - String::from("identifier"), - String::from("is_last"), - String::from("base_limb"), - String::from("exponent_lo_hi"), - String::from("exponentiation_lo_hi"), - ] - } - - fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { - vec![ - meta.query_fixed(self.is_step, Rotation::cur()), - meta.query_advice(self.identifier, Rotation::cur()), - meta.query_advice(self.is_last, Rotation::cur()), - meta.query_advice(self.base_limb, Rotation::cur()), - meta.query_advice(self.base_limb, Rotation::next()), - meta.query_advice(self.base_limb, Rotation(2)), - meta.query_advice(self.base_limb, Rotation(3)), - meta.query_advice(self.exponent_lo_hi, Rotation::cur()), - meta.query_advice(self.exponent_lo_hi, Rotation::next()), - meta.query_advice(self.exponentiation_lo_hi, Rotation::cur()), - meta.query_advice(self.exponentiation_lo_hi, Rotation::next()), - ] - } -} diff --git a/zkevm-circuits/src/table/block_table.rs b/zkevm-circuits/src/table/block_table.rs new file mode 100644 index 0000000000..80e81c4459 --- /dev/null +++ b/zkevm-circuits/src/table/block_table.rs @@ -0,0 +1,110 @@ +use super::*; + +/// Tag to identify the field in a Block Table row +// Keep the sequence consistent with OpcodeId for scalar +#[derive(Clone, Copy, Debug)] +pub enum BlockContextFieldTag { + /// Coinbase field + Coinbase = 1, + /// Timestamp field + Timestamp, + /// Number field + Number, + /// Difficulty field + Difficulty, + /// Gas Limit field + GasLimit, + /// Base Fee field + BaseFee = 8, + /// Block Hash field + BlockHash, + /// Chain ID field. Although this is not a field in the block header, we + /// add it here for convenience. + ChainId, +} +impl_expr!(BlockContextFieldTag); + +/// Table with Block header fields +#[derive(Clone, Debug)] +pub struct BlockTable { + /// Tag + pub tag: Column, + /// Index + pub index: Column, + /// Value + pub value_word: word::Word>, + #[deprecated] + /// Value + pub value: Column, +} + +impl BlockTable { + /// Construct a new BlockTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + tag: meta.advice_column(), + index: meta.advice_column(), + value_word: word::Word::new([meta.advice_column(), meta.advice_column()]), + value: meta.advice_column(), + } + } + + /// Assign the `BlockTable` from a `BlockContext`. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &BlockContext, + randomness: Value, + ) -> Result<(), Error> { + layouter.assign_region( + || "block table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "block table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let block_table_columns = >::advice_columns(self); + for row in block.table_assignments(randomness) { + for (&column, value) in block_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("block table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for BlockTable { + fn columns(&self) -> Vec> { + vec![ + self.tag.into(), + self.index.into(), + self.value_word.lo().into(), + self.value_word.hi().into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("tag"), + String::from("index"), + String::from("value_lo"), + String::from("value_hi"), + ] + } +} diff --git a/zkevm-circuits/src/table/bytecode_table.rs b/zkevm-circuits/src/table/bytecode_table.rs new file mode 100644 index 0000000000..32ea26a17c --- /dev/null +++ b/zkevm-circuits/src/table/bytecode_table.rs @@ -0,0 +1,112 @@ +use super::*; + +/// Tag to identify the field in a Bytecode Table row +#[derive(Clone, Copy, Debug)] +pub enum BytecodeFieldTag { + /// Header field + Header, + /// Byte field + Byte, +} +impl_expr!(BytecodeFieldTag); + +/// Table with Bytecode indexed by its Code Hash +#[derive(Clone, Debug)] +pub struct BytecodeTable { + /// Code Hash + pub code_hash_word: word::Word>, + #[deprecated] + /// Code Hash + pub code_hash: Column, + /// Tag + pub tag: Column, + /// Index + pub index: Column, + /// Is Code is true when the byte is not an argument to a PUSH* instruction. + pub is_code: Column, + /// Value + pub value: Column, +} + +impl BytecodeTable { + /// Construct a new BytecodeTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + let [tag, index, is_code, value] = array::from_fn(|_| meta.advice_column()); + let code_hash_word = word::Word::new([meta.advice_column(), meta.advice_column()]); + let code_hash = meta.advice_column(); + Self { + code_hash_word, + code_hash, + tag, + index, + is_code, + value, + } + } + + /// Assign the `BytecodeTable` from a list of bytecodes, followig the same + /// table layout that the Bytecode Circuit uses. + pub fn load<'a, F: Field>( + &self, + layouter: &mut impl Layouter, + bytecodes: impl IntoIterator + Clone, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "bytecode table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "bytecode table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let bytecode_table_columns = + >::advice_columns(self); + for bytecode in bytecodes.clone() { + for row in bytecode.table_assignments(challenges) { + for (&column, value) in bytecode_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("bytecode table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + } + Ok(()) + }, + ) + } +} + +impl LookupTable for BytecodeTable { + fn columns(&self) -> Vec> { + vec![ + self.code_hash_word.lo().into(), + self.code_hash_word.hi().into(), + self.tag.into(), + self.index.into(), + self.is_code.into(), + self.value.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("code_hash_lo"), + String::from("code_hash_hi"), + String::from("tag"), + String::from("index"), + String::from("is_code"), + String::from("value"), + ] + } +} diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs new file mode 100644 index 0000000000..aebbaf197a --- /dev/null +++ b/zkevm-circuits/src/table/copy_table.rs @@ -0,0 +1,291 @@ +use super::*; + +type CopyTableRow = [(Value, &'static str); 8]; +type CopyCircuitRow = [(Value, &'static str); 4]; + +/// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, +/// TxLogs and TxCallData. +#[derive(Clone, Copy, Debug)] +pub struct CopyTable { + /// Whether the row is the first read-write pair for a copy event. + pub is_first: Column, + /// The relevant ID for the read-write row, represented as a random linear + /// combination. The ID may be one of the below: + /// 1. Call ID/Caller ID for CopyDataType::Memory + /// 2. The hi/lo limbs of bytecode hash for CopyDataType::Bytecode + /// 3. Transaction ID for CopyDataType::TxCalldata, CopyDataType::TxLog + pub id: word::Word>, + /// The source/destination address for this copy step. Can be memory + /// address, byte index in the bytecode, tx call data, and tx log data. + pub addr: Column, + /// The end of the source buffer for the copy event. Any data read from an + /// address greater than or equal to this value will be 0. + pub src_addr_end: Column, + /// The number of bytes left to be copied. + pub bytes_left: Column, + /// An accumulator value in the RLC representation. This is used for + /// specific purposes, for instance, when `tag == CopyDataType::RlcAcc`. + /// Having an additional column for the `rlc_acc` simplifies the lookup + /// to copy table. + pub rlc_acc: Column, + /// The associated read-write counter for this row. + pub rw_counter: Column, + /// Decrementing counter denoting reverse read-write counter. + pub rwc_inc_left: Column, + /// Binary chip to constrain the copy table conditionally depending on the + /// current row's tag, whether it is Bytecode, Memory, TxCalldata or + /// TxLog. + pub tag: BinaryNumberConfig, +} + +impl CopyTable { + /// Construct a new CopyTable + pub fn construct(meta: &mut ConstraintSystem, q_enable: Column) -> Self { + Self { + is_first: meta.advice_column(), + id: word::Word::new([meta.advice_column(), meta.advice_column()]), + tag: BinaryNumberChip::configure(meta, q_enable, None), + addr: meta.advice_column(), + src_addr_end: meta.advice_column(), + bytes_left: meta.advice_column(), + rlc_acc: meta.advice_column_in(SecondPhase), + rw_counter: meta.advice_column(), + rwc_inc_left: meta.advice_column(), + } + } + + /// Generate the copy table and copy circuit assignments from a copy event. + pub fn assignments( + copy_event: &CopyEvent, + challenges: Challenges>, + ) -> Vec<(CopyDataType, CopyTableRow, CopyCircuitRow)> { + let mut assignments = Vec::new(); + // rlc_acc + let rlc_acc = if copy_event.dst_type == CopyDataType::RlcAcc { + let values = copy_event + .bytes + .iter() + .map(|(value, _)| *value) + .collect::>(); + challenges + .keccak_input() + .map(|keccak_input| rlc::value(values.iter().rev(), keccak_input)) + } else { + Value::known(F::ZERO) + }; + let mut value_acc = Value::known(F::ZERO); + for (step_idx, (is_read_step, copy_step)) in copy_event + .bytes + .iter() + .flat_map(|(value, is_code)| { + let read_step = CopyStep { + value: *value, + is_code: if copy_event.src_type == CopyDataType::Bytecode { + Some(*is_code) + } else { + None + }, + }; + let write_step = CopyStep { + value: *value, + is_code: if copy_event.dst_type == CopyDataType::Bytecode { + Some(*is_code) + } else { + None + }, + }; + once((true, read_step)).chain(once((false, write_step))) + }) + .enumerate() + { + // is_first + let is_first = Value::known(if step_idx == 0 { F::ONE } else { F::ZERO }); + // is last + let is_last = if step_idx == copy_event.bytes.len() * 2 - 1 { + Value::known(F::ONE) + } else { + Value::known(F::ZERO) + }; + + // id + let id = if is_read_step { + number_or_hash_to_field(©_event.src_id, challenges.evm_word()) + } else { + number_or_hash_to_field(©_event.dst_id, challenges.evm_word()) + }; + + // tag binary bumber chip + let tag = if is_read_step { + copy_event.src_type + } else { + copy_event.dst_type + }; + + // addr + let copy_step_addr: u64 = + if is_read_step { + copy_event.src_addr + } else { + copy_event.dst_addr + } + (u64::try_from(step_idx).unwrap() - if is_read_step { 0 } else { 1 }) / 2u64; + + let addr = if tag == CopyDataType::TxLog { + Value::known( + build_tx_log_address( + copy_step_addr, + TxLogFieldTag::Data, + copy_event.log_id.unwrap(), + ) + .to_scalar() + .unwrap(), + ) + } else { + Value::known(F::from(copy_step_addr)) + }; + + // bytes_left + let bytes_left = u64::try_from(copy_event.bytes.len() * 2 - step_idx).unwrap() / 2; + // value + let value = if copy_event.dst_type == CopyDataType::RlcAcc { + if is_read_step { + Value::known(F::from(copy_step.value as u64)) + } else { + value_acc = value_acc * challenges.keccak_input() + + Value::known(F::from(copy_step.value as u64)); + value_acc + } + } else { + Value::known(F::from(copy_step.value as u64)) + }; + // is_pad + let is_pad = Value::known(F::from( + (is_read_step && copy_step_addr >= copy_event.src_addr_end) as u64, + )); + + // is_code + let is_code = Value::known(copy_step.is_code.map_or(F::ZERO, |v| F::from(v as u64))); + + assignments.push(( + tag, + [ + (is_first, "is_first"), + (id, "id"), + (addr, "addr"), + ( + Value::known(F::from(copy_event.src_addr_end)), + "src_addr_end", + ), + (Value::known(F::from(bytes_left)), "bytes_left"), + (rlc_acc, "rlc_acc"), + ( + Value::known(F::from(copy_event.rw_counter(step_idx))), + "rw_counter", + ), + ( + Value::known(F::from(copy_event.rw_counter_increase_left(step_idx))), + "rwc_inc_left", + ), + ], + [ + (is_last, "is_last"), + (value, "value"), + (is_pad, "is_pad"), + (is_code, "is_code"), + ], + )); + } + assignments + } + + /// Assign the `CopyTable` from a `Block`. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &Block, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "copy table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "copy table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let tag_chip = BinaryNumberChip::construct(self.tag); + let copy_table_columns = >::advice_columns(self); + for copy_event in block.copy_events.iter() { + for (tag, row, _) in Self::assignments(copy_event, *challenges) { + for (&column, (value, label)) in copy_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("{} at row: {}", label, offset), + column, + offset, + || value, + )?; + } + tag_chip.assign(&mut region, offset, &tag)?; + offset += 1; + } + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for CopyTable { + fn columns(&self) -> Vec> { + vec![ + self.is_first.into(), + self.id.lo().into(), + self.id.hi().into(), + self.addr.into(), + self.src_addr_end.into(), + self.bytes_left.into(), + self.rlc_acc.into(), + self.rw_counter.into(), + self.rwc_inc_left.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_first"), + String::from("id_lo"), + String::from("id_hi"), + String::from("addr"), + String::from("src_addr_end"), + String::from("bytes_left"), + String::from("rlc_acc"), + String::from("rw_counter"), + String::from("rwc_inc_left"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_advice(self.is_first, Rotation::cur()), + meta.query_advice(self.id.lo(), Rotation::cur()), // src_id + meta.query_advice(self.id.hi(), Rotation::cur()), // src_id + self.tag.value(Rotation::cur())(meta), // src_tag + meta.query_advice(self.id.lo(), Rotation::next()), // dst_id + meta.query_advice(self.id.hi(), Rotation::next()), // dst_id + self.tag.value(Rotation::next())(meta), // dst_tag + meta.query_advice(self.addr, Rotation::cur()), // src_addr + meta.query_advice(self.src_addr_end, Rotation::cur()), // src_addr_end + meta.query_advice(self.addr, Rotation::next()), // dst_addr + meta.query_advice(self.bytes_left, Rotation::cur()), // length + meta.query_advice(self.rlc_acc, Rotation::cur()), // rlc_acc + meta.query_advice(self.rw_counter, Rotation::cur()), // rw_counter + meta.query_advice(self.rwc_inc_left, Rotation::cur()), // rwc_inc_left + ] + } +} diff --git a/zkevm-circuits/src/table/exp_table.rs b/zkevm-circuits/src/table/exp_table.rs new file mode 100644 index 0000000000..c2ee645e53 --- /dev/null +++ b/zkevm-circuits/src/table/exp_table.rs @@ -0,0 +1,207 @@ +use super::*; + +use crate::{ + exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, + table::LookupTable, + witness::Block, +}; +use bus_mapping::circuit_input_builder::ExpEvent; + +/// Lookup table within the Exponentiation circuit. +#[derive(Clone, Copy, Debug)] +pub struct ExpTable { + /// Whether the row is the start of a step. + pub is_step: Column, + /// An identifier for every exponentiation trace, at the moment this is the + /// read-write counter at the time of the lookups done to the + /// exponentiation table. + pub identifier: Column, + /// Whether this row is the last row in the exponentiation operation's + /// trace. + pub is_last: Column, + /// The integer base of the exponentiation. + pub base_limb: Column, + /// The integer exponent of the exponentiation. + pub exponent_lo_hi: Column, + /// The intermediate result of exponentiation by squaring. + pub exponentiation_lo_hi: Column, +} + +impl ExpTable { + /// Construct the Exponentiation table. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + is_step: meta.fixed_column(), + identifier: meta.advice_column(), + is_last: meta.advice_column(), + base_limb: meta.advice_column(), + exponent_lo_hi: meta.advice_column(), + exponentiation_lo_hi: meta.advice_column(), + } + } + + /// Given an exponentiation event and randomness, get assignments to the + /// exponentiation table. + pub fn assignments(exp_event: &ExpEvent) -> Vec<[F; 5]> { + let mut assignments = Vec::new(); + let base_limbs = split_u256_limb64(&exp_event.base); + let identifier = F::from(exp_event.identifier as u64); + let mut exponent = exp_event.exponent; + for (step_idx, exp_step) in exp_event.steps.iter().rev().enumerate() { + let is_last = if step_idx == exp_event.steps.len() - 1 { + F::ONE + } else { + F::ZERO + }; + let (exp_lo, exp_hi) = split_u256(&exp_step.d); + let (exponent_lo, exponent_hi) = split_u256(&exponent); + + // row 1 + assignments.push([ + identifier, + is_last, + base_limbs[0].as_u64().into(), + exponent_lo + .to_scalar() + .expect("exponent should fit to scalar"), + exp_lo + .to_scalar() + .expect("exponentiation lo should fit to scalar"), + ]); + // row 2 + assignments.push([ + identifier, + F::ZERO, + base_limbs[1].as_u64().into(), + exponent_hi + .to_scalar() + .expect("exponent hi should fit to scalar"), + exp_hi + .to_scalar() + .expect("exponentiation hi should fit to scalar"), + ]); + // row 3 + assignments.push([ + identifier, + F::ZERO, + base_limbs[2].as_u64().into(), + F::ZERO, + F::ZERO, + ]); + // row 4 + assignments.push([ + identifier, + F::ZERO, + base_limbs[3].as_u64().into(), + F::ZERO, + F::ZERO, + ]); + for _ in ROWS_PER_STEP..OFFSET_INCREMENT { + assignments.push([F::ZERO, F::ZERO, F::ZERO, F::ZERO, F::ZERO]); + } + + // update intermediate exponent. + let (exponent_div2, remainder) = exponent.div_mod(U256::from(2)); + if remainder.is_zero() { + // exponent is even + exponent = exponent_div2; + } else { + // exponent is odd + exponent = exponent - 1; + } + } + assignments + } + + /// Assign witness data from a block to the exponentiation table. + pub fn load( + &self, + layouter: &mut impl Layouter, + block: &Block, + ) -> Result<(), Error> { + layouter.assign_region( + || "exponentiation table", + |mut region| { + let mut offset = 0; + let exp_table_columns = >::advice_columns(self); + for exp_event in block.exp_events.iter() { + for row in Self::assignments::(exp_event) { + for (&column, value) in exp_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("exponentiation table row {}", offset), + column, + offset, + || Value::known(value), + )?; + } + let is_step = if offset % OFFSET_INCREMENT == 0 { + F::ONE + } else { + F::ZERO + }; + region.assign_fixed( + || format!("exponentiation table row {}", offset), + self.is_step, + offset, + || Value::known(is_step), + )?; + offset += 1; + } + } + + // pad an empty row + let row = [F::from_u128(0); 5]; + for (column, value) in exp_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("exponentiation table row {}", offset), + *column, + offset, + || Value::known(value), + )?; + } + + Ok(()) + }, + ) + } +} + +impl LookupTable for ExpTable { + fn columns(&self) -> Vec> { + vec![ + self.is_step.into(), + self.identifier.into(), + self.is_last.into(), + self.base_limb.into(), + self.exponent_lo_hi.into(), + self.exponentiation_lo_hi.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_step"), + String::from("identifier"), + String::from("is_last"), + String::from("base_limb"), + String::from("exponent_lo_hi"), + String::from("exponentiation_lo_hi"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_fixed(self.is_step, Rotation::cur()), + meta.query_advice(self.identifier, Rotation::cur()), + meta.query_advice(self.is_last, Rotation::cur()), + meta.query_advice(self.base_limb, Rotation::cur()), + meta.query_advice(self.base_limb, Rotation::next()), + meta.query_advice(self.base_limb, Rotation(2)), + meta.query_advice(self.base_limb, Rotation(3)), + meta.query_advice(self.exponent_lo_hi, Rotation::cur()), + meta.query_advice(self.exponent_lo_hi, Rotation::next()), + meta.query_advice(self.exponentiation_lo_hi, Rotation::cur()), + meta.query_advice(self.exponentiation_lo_hi, Rotation::next()), + ] + } +} diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs new file mode 100644 index 0000000000..e9c070df43 --- /dev/null +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -0,0 +1,145 @@ +use super::*; + +/// Keccak Table, used to verify keccak hashing from RLC'ed input. +#[derive(Clone, Debug)] +pub struct KeccakTable { + /// True when the row is enabled + pub is_enabled: Column, + /// Byte array input as `RLC(reversed(input))` + pub input_rlc: Column, // RLC of input bytes + /// Byte array input length + pub input_len: Column, + /// Output hash word + pub output: word::Word>, + #[deprecated] + /// RLC of the hash result + pub output_rlc: Column, +} + +impl LookupTable for KeccakTable { + fn columns(&self) -> Vec> { + vec![ + self.is_enabled.into(), + self.input_rlc.into(), + self.input_len.into(), + self.output.lo().into(), + self.output.hi().into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("is_enabled"), + String::from("input_rlc"), + String::from("input_len"), + String::from("output_lo"), + String::from("output_hi"), + ] + } +} + +impl KeccakTable { + /// Construct a new KeccakTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + is_enabled: meta.advice_column(), + input_rlc: meta.advice_column_in(SecondPhase), + input_len: meta.advice_column(), + output: word::Word::new([meta.advice_column(), meta.advice_column()]), + output_rlc: meta.advice_column_in(SecondPhase), + } + } + + /// Generate the keccak table assignments from a byte array input. + pub fn assignments( + input: &[u8], + challenges: &Challenges>, + ) -> Vec<[Value; 5]> { + let input_rlc = challenges + .keccak_input() + .map(|challenge| rlc::value(input.iter().rev(), challenge)); + let input_len = F::from(input.len() as u64); + let output = word::Word::from(keccak(input)); + + vec![[ + Value::known(F::ONE), + input_rlc, + Value::known(input_len), + Value::known(output.lo()), + Value::known(output.hi()), + ]] + } + + /// Assign a table row for keccak table + pub fn assign_row( + &self, + region: &mut Region, + offset: usize, + values: [Value; 5], + ) -> Result<(), Error> { + for (&column, value) in >::advice_columns(self) + .iter() + .zip(values.iter()) + { + region.assign_advice(|| format!("assign {}", offset), column, offset, || *value)?; + } + Ok(()) + } + + /// Provide this function for the case that we want to consume a keccak + /// table but without running the full keccak circuit + pub fn dev_load<'a, F: Field>( + &self, + layouter: &mut impl Layouter, + inputs: impl IntoIterator> + Clone, + challenges: &Challenges>, + ) -> Result<(), Error> { + layouter.assign_region( + || "keccak table", + |mut region| { + let mut offset = 0; + for column in >::advice_columns(self) { + region.assign_advice( + || "keccak table all-zero row", + column, + offset, + || Value::known(F::ZERO), + )?; + } + offset += 1; + + let keccak_table_columns = >::advice_columns(self); + for input in inputs.clone() { + for row in Self::assignments(input, challenges) { + // let mut column_index = 0; + for (&column, value) in keccak_table_columns.iter().zip_eq(row) { + region.assign_advice( + || format!("keccak table row {}", offset), + column, + offset, + || value, + )?; + } + offset += 1; + } + } + Ok(()) + }, + ) + } + + /// returns matchings between the circuit columns passed as parameters and + /// the table collumns + pub fn match_columns( + &self, + value_rlc: Column, + length: Column, + code_hash: Column, + ) -> Vec<(Column, Column)> { + vec![ + (value_rlc, self.input_rlc), + (length, self.input_len), + (code_hash, self.output_rlc), + ] + } +} diff --git a/zkevm-circuits/src/table/mpt_table.rs b/zkevm-circuits/src/table/mpt_table.rs new file mode 100644 index 0000000000..5e591f29f7 --- /dev/null +++ b/zkevm-circuits/src/table/mpt_table.rs @@ -0,0 +1,111 @@ +use super::*; + +/// The types of proofs in the MPT table +#[derive(Clone, Copy, Debug)] +pub enum MPTProofType { + /// Nonce updated + NonceMod = AccountFieldTag::Nonce as isize, + /// Balance updated + BalanceMod = AccountFieldTag::Balance as isize, + /// Code hash exists + CodeHashMod = AccountFieldTag::CodeHash as isize, + /// Account does not exist + NonExistingAccountProof = AccountFieldTag::NonExisting as isize, + /// Storage updated + StorageMod, + /// Storage does not exist + NonExistingStorageProof, +} +impl_expr!(MPTProofType); + +impl From for MPTProofType { + fn from(tag: AccountFieldTag) -> Self { + match tag { + AccountFieldTag::Nonce => Self::NonceMod, + AccountFieldTag::Balance => Self::BalanceMod, + AccountFieldTag::CodeHash => Self::CodeHashMod, + AccountFieldTag::NonExisting => Self::NonExistingAccountProof, + } + } +} + +/// The MptTable shared between MPT Circuit and State Circuit +#[derive(Clone, Copy, Debug)] +pub struct MptTable([Column; 12]); + +impl LookupTable for MptTable { + fn columns(&self) -> Vec> { + self.0.iter().map(|&col| col.into()).collect() + } + + fn annotations(&self) -> Vec { + vec![ + String::from("address"), + String::from("storage_key_lo"), + String::from("storage_key_hi"), + String::from("proof_type"), + String::from("new_root_lo"), + String::from("new_root_hi"), + String::from("old_root_lo"), + String::from("old_root_hi"), + String::from("new_value_lo"), + String::from("new_value_hi"), + String::from("old_value_lo"), + String::from("old_value_hi"), + ] + } +} + +impl MptTable { + /// Construct a new MptTable + pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { + Self([ + meta.advice_column(), // Address + meta.advice_column(), // Storage key lo + meta.advice_column(), // Storage key hi + meta.advice_column(), // Proof type + meta.advice_column(), // New root lo + meta.advice_column(), // New root hi + meta.advice_column(), // Old root lo + meta.advice_column(), // Old root hi + meta.advice_column(), // New value lo + meta.advice_column(), // New value hi + meta.advice_column(), // Old value lo + meta.advice_column(), // Old value hi + ]) + } + + pub(crate) fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + row: &MptUpdateRow>, + ) -> Result<(), Error> { + for (column, value) in self.0.iter().zip_eq(row.values()) { + region.assign_advice(|| "assign mpt table row value", *column, offset, || *value)?; + } + Ok(()) + } + + pub(crate) fn load( + &self, + layouter: &mut impl Layouter, + updates: &MptUpdates, + ) -> Result<(), Error> { + layouter.assign_region( + || "mpt table", + |mut region| self.load_with_region(&mut region, updates), + ) + } + + pub(crate) fn load_with_region( + &self, + region: &mut Region<'_, F>, + updates: &MptUpdates, + ) -> Result<(), Error> { + for (offset, row) in updates.table_assignments().iter().enumerate() { + self.assign(region, offset, row)?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/table/rw_table.rs b/zkevm-circuits/src/table/rw_table.rs new file mode 100644 index 0000000000..e8f8ad67e4 --- /dev/null +++ b/zkevm-circuits/src/table/rw_table.rs @@ -0,0 +1,138 @@ +use super::*; + +/// The RwTable shared between EVM Circuit and State Circuit, which contains +/// traces of the EVM state operations. +#[derive(Clone, Copy, Debug)] +pub struct RwTable { + /// Read Write Counter + pub rw_counter: Column, + /// Is Write + pub is_write: Column, + /// Tag + pub tag: Column, + /// Key1 (Id) + pub id: Column, + /// Key2 (Address) + pub address: Column, + /// Key3 (FieldTag) + pub field_tag: Column, + /// Key3 (StorageKey) + pub storage_key: word::Word>, + /// Value + pub value: word::Word>, + /// Value Previous + pub value_prev: word::Word>, + /// InitVal (Committed Value) + pub init_val: word::Word>, +} + +impl LookupTable for RwTable { + fn columns(&self) -> Vec> { + vec![ + self.rw_counter.into(), + self.is_write.into(), + self.tag.into(), + self.id.into(), + self.address.into(), + self.field_tag.into(), + self.storage_key.lo().into(), + self.storage_key.hi().into(), + self.value.lo().into(), + self.value.hi().into(), + self.value_prev.lo().into(), + self.value_prev.hi().into(), + self.init_val.lo().into(), + self.init_val.hi().into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("rw_counter"), + String::from("is_write"), + String::from("tag"), + String::from("id"), + String::from("address"), + String::from("field_tag"), + String::from("storage_key_lo"), + String::from("storage_key_hi"), + String::from("value_lo"), + String::from("value_hi"), + String::from("value_prev_lo"), + String::from("value_prev_hi"), + String::from("init_val_lo"), + String::from("init_val_hi"), + ] + } +} +impl RwTable { + /// Construct a new RwTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + rw_counter: meta.advice_column(), + is_write: meta.advice_column(), + tag: meta.advice_column(), + id: meta.advice_column(), + address: meta.advice_column(), + field_tag: meta.advice_column(), + storage_key: word::Word::new([meta.advice_column(), meta.advice_column()]), + value: word::Word::new([meta.advice_column(), meta.advice_column()]), + value_prev: word::Word::new([meta.advice_column(), meta.advice_column()]), + init_val: word::Word::new([meta.advice_column(), meta.advice_column()]), + } + } + fn assign( + &self, + region: &mut Region<'_, F>, + offset: usize, + row: &RwRow>, + ) -> Result<(), Error> { + for (column, value) in [ + (self.address, row.address), + (self.rw_counter, row.rw_counter), + (self.is_write, row.is_write), + (self.tag, row.tag), + (self.id, row.id), + (self.field_tag, row.field_tag), + ] { + region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + } + for (column, value) in [ + (self.storage_key, row.storage_key), + (self.value, row.value), + (self.value_prev, row.value_prev), + (self.init_val, row.init_val), + ] { + value.assign_advice(region, || "assign rw row on rw table", column, offset)?; + } + + Ok(()) + } + + /// Assign the `RwTable` from a `RwMap`, following the same + /// table layout that the State Circuit uses. + pub fn load( + &self, + layouter: &mut impl Layouter, + rws: &[Rw], + n_rows: usize, + ) -> Result<(), Error> { + layouter.assign_region( + || "rw table", + |mut region| self.load_with_region(&mut region, rws, n_rows), + ) + } + + pub(crate) fn load_with_region( + &self, + region: &mut Region<'_, F>, + rws: &[Rw], + n_rows: usize, + ) -> Result<(), Error> { + let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); + for (offset, row) in rows.iter().enumerate() { + self.assign(region, offset, &row.table_assignment())?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/table/tx_table.rs b/zkevm-circuits/src/table/tx_table.rs new file mode 100644 index 0000000000..706aa5983b --- /dev/null +++ b/zkevm-circuits/src/table/tx_table.rs @@ -0,0 +1,225 @@ +use super::*; + +/// Tag used to identify each field in the transaction in a row of the +/// transaction table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TxFieldTag { + /// Unused tag + Null = 0, + /// Nonce + Nonce, + /// Gas + Gas, + /// GasPrice + GasPrice, + /// CallerAddress + CallerAddress, + /// CalleeAddress + CalleeAddress, + /// IsCreate + IsCreate, + /// Value + Value, + /// CallDataLength + CallDataLength, + /// Gas cost for transaction call data (4 for byte == 0, 16 otherwise) + CallDataGasCost, + /// TxSignHash: Hash of the transaction without the signature, used for + /// signing. + TxSignHash, + /// CallData + CallData, +} +impl_expr!(TxFieldTag); + +/// Tag for a TxLogField in RwTable +#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter)] +pub enum TxLogFieldTag { + /// Address field + Address = 1, + /// Topic field + Topic, + /// Data field + Data, +} +impl_expr!(TxLogFieldTag); + +/// Tag for a TxReceiptField in RwTable +#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, EnumCount)] +pub enum TxReceiptFieldTag { + /// Tx result + PostStateOrStatus = 1, + /// CumulativeGasUsed in the tx + CumulativeGasUsed, + /// Number of logs in the tx + LogLength, +} +impl_expr!(TxReceiptFieldTag); + +/// Alias for TxFieldTag used by EVM Circuit +pub type TxContextFieldTag = TxFieldTag; + +/// Table that contains the fields of all Transactions in a block +#[derive(Clone, Debug)] +pub struct TxTable { + /// Tx ID + pub tx_id: Column, + /// Tag (TxContextFieldTag) + pub tag: Column, + /// Index for Tag = CallData + pub index: Column, + /// Value + pub value_word: word::Word>, + /// Value + #[deprecated(note = "value_word is fav")] + pub value: Column, +} + +impl TxTable { + /// Construct a new TxTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + tx_id: meta.advice_column(), + tag: meta.fixed_column(), + index: meta.advice_column(), + value_word: word::Word::new([meta.advice_column(), meta.advice_column()]), + value: meta.advice_column(), + } + } + + /// Assign the `TxTable` from a list of block `Transaction`s, followig the + /// same layout that the Tx Circuit uses. + pub fn load( + &self, + layouter: &mut impl Layouter, + txs: &[Transaction], + max_txs: usize, + max_calldata: usize, + challenges: &Challenges>, + ) -> Result<(), Error> { + assert!( + txs.len() <= max_txs, + "txs.len() <= max_txs: txs.len()={}, max_txs={}", + txs.len(), + max_txs + ); + let sum_txs_calldata = txs.iter().map(|tx| tx.call_data.len()).sum(); + assert!( + sum_txs_calldata <= max_calldata, + "sum_txs_calldata <= max_calldata: sum_txs_calldata={}, max_calldata={}", + sum_txs_calldata, + max_calldata, + ); + + fn assign_row( + region: &mut Region<'_, F>, + offset: usize, + advice_columns: &[Column], + tag: &Column, + row: &[Value; 4], + msg: &str, + ) -> Result<(), Error> { + for (index, column) in advice_columns.iter().enumerate() { + region.assign_advice( + || format!("tx table {} row {}", msg, offset), + *column, + offset, + || row[if index > 0 { index + 1 } else { index }], + )?; + } + region.assign_fixed( + || format!("tx table {} row {}", msg, offset), + *tag, + offset, + || row[1], + )?; + Ok(()) + } + + layouter.assign_region( + || "tx table", + |mut region| { + let mut offset = 0; + let advice_columns = + [vec![self.tx_id, self.index], self.value_word.limbs.to_vec()].concat(); + assign_row( + &mut region, + offset, + &advice_columns, + &self.tag, + &[(); 4].map(|_| Value::known(F::ZERO)), + "all-zero", + )?; + offset += 1; + + // Tx Table contains an initial region that has a size parametrized by max_txs + // with all the tx data except for calldata, and then a second + // region that has a size parametrized by max_calldata with all + // the tx calldata. This is required to achieve a constant fixed column tag + // regardless of the number of input txs or the calldata size of each tx. + let mut calldata_assignments: Vec<[Value; 4]> = Vec::new(); + // Assign Tx data (all tx fields except for calldata) + let padding_txs: Vec<_> = (txs.len()..max_txs) + .map(|i| Transaction { + id: i + 1, + ..Default::default() + }) + .collect(); + for tx in txs.iter().chain(padding_txs.iter()) { + let [tx_data, tx_calldata] = tx.table_assignments(*challenges); + for row in tx_data { + assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; + offset += 1; + } + calldata_assignments.extend(tx_calldata.iter()); + } + // Assign Tx calldata + let padding_calldata = (sum_txs_calldata..max_calldata).map(|_| { + [ + Value::known(F::ZERO), + Value::known(F::from(TxContextFieldTag::CallData as u64)), + Value::known(F::ZERO), + Value::known(F::ZERO), + ] + }); + for row in calldata_assignments.into_iter().chain(padding_calldata) { + assign_row(&mut region, offset, &advice_columns, &self.tag, &row, "")?; + offset += 1; + } + Ok(()) + }, + ) + } +} + +impl LookupTable for TxTable { + fn columns(&self) -> Vec> { + vec![ + self.tx_id.into(), + self.tag.into(), + self.index.into(), + self.value_word.lo().into(), + self.value_word.hi().into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("tx_id"), + String::from("tag"), + String::from("index"), + String::from("value_lo"), + String::from("value_hi"), + ] + } + + fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { + vec![ + meta.query_advice(self.tx_id, Rotation::cur()), + meta.query_fixed(self.tag, Rotation::cur()), + meta.query_advice(self.index, Rotation::cur()), + meta.query_advice(self.value_word.lo(), Rotation::cur()), + meta.query_advice(self.value_word.hi(), Rotation::cur()), + ] + } +} diff --git a/zkevm-circuits/src/tx_circuit/dev.rs b/zkevm-circuits/src/tx_circuit/dev.rs index 5ddeb02c34..38f210ad53 100644 --- a/zkevm-circuits/src/tx_circuit/dev.rs +++ b/zkevm-circuits/src/tx_circuit/dev.rs @@ -16,6 +16,7 @@ use log::error; impl Circuit for TxCircuit { type Config = (TxCircuitConfig, Challenges); type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index 25dc38022d..0683ee998f 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -12,7 +12,7 @@ use crate::{ use ecc::{maingate, EccConfig, GeneralEccChip}; use ecdsa::ecdsa::{AssignedEcdsaSig, AssignedPublicKey, EcdsaChip}; use eth_types::{ - self, + self, keccak256, sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData}, Field, }; @@ -33,7 +33,6 @@ use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use itertools::Itertools; -use keccak256::plain::Keccak; use log::error; use maingate::{ AssignedValue, MainGate, MainGateConfig, MainGateInstructions, RangeChip, RangeConfig, @@ -506,12 +505,7 @@ impl SignVerifyChip { let pk_le = pk_bytes_le(&sign_data.pk); let pk_be = pk_bytes_swap_endianness(&pk_le); let pk_hash = (!padding) - .then(|| { - let mut keccak = Keccak::default(); - keccak.update(&pk_be); - let hash: [_; 32] = keccak.digest().try_into().expect("vec to array of size 32"); - hash - }) + .then(|| keccak256(&pk_be)) .unwrap_or_default() .map(|byte| Value::known(F::from(byte as u64))); let pk_hash_hi = pk_hash[..12].to_vec(); @@ -753,6 +747,7 @@ mod sign_verify_tests { impl Circuit for TestCircuitSignVerify { type Config = TestCircuitSignVerifyConfig; type FloorPlanner = SimpleFloorPlanner; + type Params = (); fn without_witnesses(&self) -> Self { Self::default() diff --git a/zkevm-circuits/src/tx_circuit/test.rs b/zkevm-circuits/src/tx_circuit/test.rs index 1c794d3f86..102b32c11d 100644 --- a/zkevm-circuits/src/tx_circuit/test.rs +++ b/zkevm-circuits/src/tx_circuit/test.rs @@ -12,7 +12,7 @@ use mock::AddrOrWallet; fn tx_circuit_unusable_rows() { assert_eq!( TxCircuit::::unusable_rows(), - unusable_rows::>(), + unusable_rows::>(()), ) } diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index fa64217319..b669e5e2c8 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -9,10 +9,9 @@ use halo2_proofs::{ VirtualCells, }, }; -use keccak256::plain::Keccak; use crate::{evm_circuit::util::rlc, table::TxLogFieldTag, witness}; -use eth_types::{Field, ToAddress, Word}; +use eth_types::{keccak256, Field, ToAddress, Word}; pub use ethers_core::types::{Address, U256}; pub use gadgets::util::Expr; @@ -203,9 +202,7 @@ pub fn log2_ceil(n: usize) -> u32 { } pub(crate) fn keccak(msg: &[u8]) -> Word { - let mut keccak = Keccak::default(); - keccak.update(msg); - Word::from_big_endian(keccak.digest().as_slice()) + Word::from_big_endian(keccak256(msg).as_slice()) } pub(crate) fn is_push(byte: u8) -> bool { @@ -232,9 +229,9 @@ pub(crate) fn get_push_size(byte: u8) -> u64 { /// For circuit with column queried at more than 3 distinct rotation, we can /// calculate the unusable rows as (x - 3) + 6 where x is the number of distinct /// rotation. -pub(crate) fn unusable_rows>() -> usize { +pub(crate) fn unusable_rows>(params: C::Params) -> usize { let mut cs = ConstraintSystem::default(); - C::configure(&mut cs); + C::configure_with_params(&mut cs, params); cs.blinding_factors() + 1 } diff --git a/zkevm-circuits/src/util/word.rs b/zkevm-circuits/src/util/word.rs index fcc7467228..ca9bb34a3c 100644 --- a/zkevm-circuits/src/util/word.rs +++ b/zkevm-circuits/src/util/word.rs @@ -253,7 +253,7 @@ impl Word { (lo, hi) } - /// Wrap `Word` into into `Word` + /// Wrap `Word` into `Word` pub fn into_value(self) -> Word> { let [lo, hi] = self.0.limbs; Word::new([Value::known(lo), Value::known(hi)]) @@ -263,11 +263,6 @@ impl Word { pub fn map(&self, mut func: impl FnMut(T) -> T2) -> Word { Word(WordLimbs::::new([func(self.lo()), func(self.hi())])) } - /// Convert the word to Known Value - pub fn into_value(self) -> Word> { - let [lo, hi] = self.0.limbs; - Word::new([Value::known(lo), Value::known(hi)]) - } } impl std::ops::Deref for Word { diff --git a/zkevm-circuits/src/witness.rs b/zkevm-circuits/src/witness.rs index 019706ad0e..dde790dd39 100644 --- a/zkevm-circuits/src/witness.rs +++ b/zkevm-circuits/src/witness.rs @@ -9,9 +9,8 @@ pub use bytecode::Bytecode; mod mpt; pub use mpt::{MptUpdate, MptUpdateRow, MptUpdates}; mod rw; +pub use bus_mapping::circuit_input_builder::ExecStep; pub use rw::{Rw, RwMap, RwRow}; -mod step; -pub use step::ExecStep; mod tx; pub use bus_mapping::circuit_input_builder::Call; pub use tx::Transaction; diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index a8f52d822e..e3c4de3764 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -1,12 +1,11 @@ use std::collections::HashMap; -#[cfg(any(feature = "test", test))] -use crate::evm_circuit::{detect_fixed_table_tags, EvmCircuit}; -#[cfg(feature = "test")] -use crate::util::SubCircuit; - -use crate::{evm_circuit::util::rlc, table::BlockContextFieldTag}; - +use crate::{ + evm_circuit::{detect_fixed_table_tags, util::rlc, EvmCircuit}, + exp_circuit::param::OFFSET_INCREMENT, + table::BlockContextFieldTag, + util::{log2_ceil, SubCircuit}, +}; use bus_mapping::{ circuit_input_builder::{self, CircuitsParams, CopyEvent, ExpEvent}, Error, @@ -14,7 +13,7 @@ use bus_mapping::{ use eth_types::{Address, Field, ToLittleEndian, ToScalar, Word}; use halo2_proofs::circuit::Value; -use super::{step::step_convert, tx::tx_convert, Bytecode, ExecStep, RwMap, Transaction}; +use super::{tx::tx_convert, Bytecode, ExecStep, Rw, RwMap, Transaction}; // TODO: Remove fields that are duplicated in`eth_block` /// Block is the struct used by all circuits, which contains all the needed @@ -61,22 +60,19 @@ impl Block { for (tx_idx, tx) in self.txs.iter().enumerate() { println!("tx {}", tx_idx); for step in &tx.steps { - println!(" step {:?} rwc: {}", step.execution_state, step.rw_counter); - for rw_ref in &step.rw_indices { - println!(" - {:?}", self.rws[*rw_ref]); + println!(" step {:?} rwc: {}", step.exec_state, step.rwc.0); + for rw_idx in 0..step.bus_mapping_instance.len() { + println!(" - {:?}", self.get_rws(step, rw_idx)); } } } } -} -#[cfg(feature = "test")] -use crate::exp_circuit::param::OFFSET_INCREMENT; -#[cfg(feature = "test")] -use crate::util::log2_ceil; + /// Get a read-write record + pub(crate) fn get_rws(&self, step: &ExecStep, index: usize) -> Rw { + self.rws[step.rw_index(index)] + } -#[cfg(feature = "test")] -impl Block { /// Obtains the expected Circuit degree needed in order to be able to test /// the EvmCircuit with this block without needing to configure the /// `ConstraintSystem`. @@ -251,8 +247,8 @@ pub fn block_convert( .enumerate() .map(|(idx, tx)| tx_convert(tx, idx + 1)) .collect(), - end_block_not_last: step_convert(&block.block_steps.end_block_not_last), - end_block_last: step_convert(&block.block_steps.end_block_last), + end_block_not_last: block.block_steps.end_block_not_last.clone(), + end_block_last: block.block_steps.end_block_last.clone(), bytecodes: code_db .0 .values() diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 6fd3345da1..a28530dcbe 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -1,38 +1,48 @@ #![allow(missing_docs)] use std::collections::HashMap; -use bus_mapping::operation::{self, AccountField, CallContextField, TxLogField, TxReceiptField}; +use bus_mapping::{ + exec_trace::OperationRef, + operation::{self, AccountField, CallContextField, Target, TxLogField, TxReceiptField}, +}; use eth_types::{Address, Field, ToAddress, ToScalar, Word, U256}; use halo2_proofs::circuit::Value; use itertools::Itertools; -use crate::util::word; - use crate::{ - table::{AccountFieldTag, CallContextFieldTag, RwTableTag, TxLogFieldTag, TxReceiptFieldTag}, - util::build_tx_log_address, + table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, + util::{build_tx_log_address, word}, }; use super::MptUpdates; /// Rw constainer for a witness block #[derive(Debug, Default, Clone)] -pub struct RwMap(pub HashMap>); +pub struct RwMap(pub HashMap>); -impl std::ops::Index<(RwTableTag, usize)> for RwMap { +impl std::ops::Index<(Target, usize)> for RwMap { type Output = Rw; - fn index(&self, (tag, idx): (RwTableTag, usize)) -> &Self::Output { + fn index(&self, (tag, idx): (Target, usize)) -> &Self::Output { &self.0.get(&tag).unwrap()[idx] } } + +impl std::ops::Index for RwMap { + type Output = Rw; + + fn index(&self, OperationRef(tag, idx): OperationRef) -> &Self::Output { + &self.0.get(&tag).unwrap()[idx] + } +} + impl RwMap { /// Check rw_counter is continuous and starting from 1 pub fn check_rw_counter_sanity(&self) { for (idx, rw_counter) in self .0 .iter() - .filter(|(tag, _rs)| !matches!(tag, RwTableTag::Start)) + .filter(|(tag, _rs)| !matches!(tag, Target::Start)) .flat_map(|(_tag, rs)| rs) .map(|r| r.rw_counter()) .sorted() @@ -464,19 +474,19 @@ impl Rw { } } - pub(crate) fn tag(&self) -> RwTableTag { + pub(crate) fn tag(&self) -> Target { match self { - Self::Start { .. } => RwTableTag::Start, - Self::Memory { .. } => RwTableTag::Memory, - Self::Stack { .. } => RwTableTag::Stack, - Self::AccountStorage { .. } => RwTableTag::AccountStorage, - Self::TxAccessListAccount { .. } => RwTableTag::TxAccessListAccount, - Self::TxAccessListAccountStorage { .. } => RwTableTag::TxAccessListAccountStorage, - Self::TxRefund { .. } => RwTableTag::TxRefund, - Self::Account { .. } => RwTableTag::Account, - Self::CallContext { .. } => RwTableTag::CallContext, - Self::TxLog { .. } => RwTableTag::TxLog, - Self::TxReceipt { .. } => RwTableTag::TxReceipt, + Self::Start { .. } => Target::Start, + Self::Memory { .. } => Target::Memory, + Self::Stack { .. } => Target::Stack, + Self::AccountStorage { .. } => Target::Storage, + Self::TxAccessListAccount { .. } => Target::TxAccessListAccount, + Self::TxAccessListAccountStorage { .. } => Target::TxAccessListAccountStorage, + Self::TxRefund { .. } => Target::TxRefund, + Self::Account { .. } => Target::Account, + Self::CallContext { .. } => Target::CallContext, + Self::TxLog { .. } => Target::TxLog, + Self::TxReceipt { .. } => Target::TxReceipt, } } @@ -610,7 +620,7 @@ impl From<&operation::OperationContainer> for RwMap { let mut rws = HashMap::default(); rws.insert( - RwTableTag::Start, + Target::Start, container .start .iter() @@ -620,7 +630,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxAccessListAccount, + Target::TxAccessListAccount, container .tx_access_list_account .iter() @@ -635,7 +645,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxAccessListAccountStorage, + Target::TxAccessListAccountStorage, container .tx_access_list_account_storage .iter() @@ -651,7 +661,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxRefund, + Target::TxRefund, container .tx_refund .iter() @@ -665,7 +675,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Account, + Target::Account, container .account .iter() @@ -684,7 +694,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::AccountStorage, + Target::Storage, container .storage .iter() @@ -701,7 +711,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::CallContext, + Target::CallContext, container .call_context .iter() @@ -749,7 +759,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Stack, + Target::Stack, container .stack .iter() @@ -763,7 +773,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::Memory, + Target::Memory, container .memory .iter() @@ -779,7 +789,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxLog, + Target::TxLog, container .tx_log .iter() @@ -799,7 +809,7 @@ impl From<&operation::OperationContainer> for RwMap { .collect(), ); rws.insert( - RwTableTag::TxReceipt, + Target::TxReceipt, container .tx_receipt .iter() diff --git a/zkevm-circuits/src/witness/step.rs b/zkevm-circuits/src/witness/step.rs deleted file mode 100644 index 2baa55690f..0000000000 --- a/zkevm-circuits/src/witness/step.rs +++ /dev/null @@ -1,242 +0,0 @@ -use bus_mapping::{ - circuit_input_builder, - error::{ExecError, OogError}, - evm::OpcodeId, - operation, -}; -use eth_types::evm_unimplemented; - -use crate::{ - evm_circuit::{ - param::{N_BYTES_WORD, STACK_CAPACITY}, - step::ExecutionState, - }, - table::RwTableTag, -}; - -/// Step executed in a transaction -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ExecStep { - /// The index in the Transaction calls - pub call_index: usize, - /// The indices in the RW trace incurred in this step - pub rw_indices: Vec<(RwTableTag, usize)>, - /// Number of rw operations performed via a copy event in this step. - pub copy_rw_counter_delta: u64, - /// The execution state for the step - pub execution_state: ExecutionState, - /// The Read/Write counter before the step - pub rw_counter: usize, - /// The program counter - pub program_counter: u64, - /// The stack pointer - pub stack_pointer: usize, - /// The amount of gas left - pub gas_left: u64, - /// The gas cost in this step - pub gas_cost: u64, - /// The memory size in bytes - pub memory_size: u64, - /// The counter for reversible writes at the beginning of the step - pub reversible_write_counter: usize, - /// The number of reversible writes from this step - pub reversible_write_counter_delta: usize, - /// The counter for log index within tx - pub log_id: usize, - /// The opcode corresponds to the step - pub opcode: Option, -} - -impl ExecStep { - /// The memory size in word **before** this step - pub fn memory_word_size(&self) -> u64 { - // EVM always pads the memory size to word size - // https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go#L212-L216 - // Thus, the memory size must be a multiple of 32 bytes. - assert_eq!(self.memory_size % N_BYTES_WORD as u64, 0); - self.memory_size / N_BYTES_WORD as u64 - } -} - -impl From<&ExecError> for ExecutionState { - fn from(error: &ExecError) -> Self { - match error { - ExecError::InvalidOpcode => ExecutionState::ErrorInvalidOpcode, - ExecError::StackOverflow | ExecError::StackUnderflow => ExecutionState::ErrorStack, - ExecError::WriteProtection => ExecutionState::ErrorWriteProtection, - ExecError::Depth => ExecutionState::ErrorDepth, - ExecError::InsufficientBalance => ExecutionState::ErrorInsufficientBalance, - ExecError::ContractAddressCollision => ExecutionState::ErrorContractAddressCollision, - ExecError::InvalidCreationCode => ExecutionState::ErrorInvalidCreationCode, - ExecError::InvalidJump => ExecutionState::ErrorInvalidJump, - ExecError::ReturnDataOutOfBounds => ExecutionState::ErrorReturnDataOutOfBound, - ExecError::CodeStoreOutOfGas => ExecutionState::ErrorOutOfGasCodeStore, - ExecError::MaxCodeSizeExceeded => ExecutionState::ErrorMaxCodeSizeExceeded, - ExecError::OutOfGas(oog_error) => match oog_error { - OogError::Constant => ExecutionState::ErrorOutOfGasConstant, - OogError::StaticMemoryExpansion => { - ExecutionState::ErrorOutOfGasStaticMemoryExpansion - } - OogError::DynamicMemoryExpansion => { - ExecutionState::ErrorOutOfGasDynamicMemoryExpansion - } - OogError::MemoryCopy => ExecutionState::ErrorOutOfGasMemoryCopy, - OogError::AccountAccess => ExecutionState::ErrorOutOfGasAccountAccess, - OogError::CodeStore => ExecutionState::ErrorOutOfGasCodeStore, - OogError::Log => ExecutionState::ErrorOutOfGasLOG, - OogError::Exp => ExecutionState::ErrorOutOfGasEXP, - OogError::Sha3 => ExecutionState::ErrorOutOfGasSHA3, - OogError::Call => ExecutionState::ErrorOutOfGasCall, - OogError::SloadSstore => ExecutionState::ErrorOutOfGasSloadSstore, - OogError::Create2 => ExecutionState::ErrorOutOfGasCREATE2, - OogError::SelfDestruct => ExecutionState::ErrorOutOfGasSELFDESTRUCT, - }, - } - } -} - -impl From<&circuit_input_builder::ExecStep> for ExecutionState { - fn from(step: &circuit_input_builder::ExecStep) -> Self { - if let Some(error) = step.error.as_ref() { - return error.into(); - } - match step.exec_state { - circuit_input_builder::ExecState::Op(op) => { - if op.is_dup() { - return ExecutionState::DUP; - } - if op.is_push() { - return ExecutionState::PUSH; - } - if op.is_swap() { - return ExecutionState::SWAP; - } - if op.is_log() { - return ExecutionState::LOG; - } - - macro_rules! dummy { - ($name:expr) => {{ - evm_unimplemented!("{:?} is implemented with DummyGadget", $name); - $name - }}; - } - - match op { - OpcodeId::ADD | OpcodeId::SUB => ExecutionState::ADD_SUB, - OpcodeId::ADDMOD => ExecutionState::ADDMOD, - OpcodeId::ADDRESS => ExecutionState::ADDRESS, - OpcodeId::BALANCE => ExecutionState::BALANCE, - OpcodeId::MUL | OpcodeId::DIV | OpcodeId::MOD => ExecutionState::MUL_DIV_MOD, - OpcodeId::MULMOD => ExecutionState::MULMOD, - OpcodeId::SDIV | OpcodeId::SMOD => ExecutionState::SDIV_SMOD, - OpcodeId::EQ | OpcodeId::LT | OpcodeId::GT => ExecutionState::CMP, - OpcodeId::SLT | OpcodeId::SGT => ExecutionState::SCMP, - OpcodeId::SIGNEXTEND => ExecutionState::SIGNEXTEND, - OpcodeId::STOP => ExecutionState::STOP, - OpcodeId::AND => ExecutionState::BITWISE, - OpcodeId::XOR => ExecutionState::BITWISE, - OpcodeId::OR => ExecutionState::BITWISE, - OpcodeId::NOT => ExecutionState::NOT, - OpcodeId::EXP => ExecutionState::EXP, - OpcodeId::POP => ExecutionState::POP, - OpcodeId::PUSH32 => ExecutionState::PUSH, - OpcodeId::BYTE => ExecutionState::BYTE, - OpcodeId::MLOAD => ExecutionState::MEMORY, - OpcodeId::MSTORE => ExecutionState::MEMORY, - OpcodeId::MSTORE8 => ExecutionState::MEMORY, - OpcodeId::JUMPDEST => ExecutionState::JUMPDEST, - OpcodeId::JUMP => ExecutionState::JUMP, - OpcodeId::JUMPI => ExecutionState::JUMPI, - OpcodeId::GASPRICE => ExecutionState::GASPRICE, - OpcodeId::PC => ExecutionState::PC, - OpcodeId::MSIZE => ExecutionState::MSIZE, - OpcodeId::CALLER => ExecutionState::CALLER, - OpcodeId::CALLVALUE => ExecutionState::CALLVALUE, - OpcodeId::EXTCODEHASH => ExecutionState::EXTCODEHASH, - OpcodeId::EXTCODESIZE => ExecutionState::EXTCODESIZE, - OpcodeId::BLOCKHASH => ExecutionState::BLOCKHASH, - OpcodeId::TIMESTAMP | OpcodeId::NUMBER | OpcodeId::GASLIMIT => { - ExecutionState::BLOCKCTXU64 - } - OpcodeId::COINBASE => ExecutionState::BLOCKCTXU160, - OpcodeId::DIFFICULTY | OpcodeId::BASEFEE => ExecutionState::BLOCKCTXU256, - OpcodeId::GAS => ExecutionState::GAS, - OpcodeId::SAR => ExecutionState::SAR, - OpcodeId::SELFBALANCE => ExecutionState::SELFBALANCE, - OpcodeId::SHA3 => ExecutionState::SHA3, - OpcodeId::SHL | OpcodeId::SHR => ExecutionState::SHL_SHR, - OpcodeId::SLOAD => ExecutionState::SLOAD, - OpcodeId::SSTORE => ExecutionState::SSTORE, - OpcodeId::CALLDATASIZE => ExecutionState::CALLDATASIZE, - OpcodeId::CALLDATACOPY => ExecutionState::CALLDATACOPY, - OpcodeId::CHAINID => ExecutionState::CHAINID, - OpcodeId::ISZERO => ExecutionState::ISZERO, - OpcodeId::CALL - | OpcodeId::CALLCODE - | OpcodeId::DELEGATECALL - | OpcodeId::STATICCALL => ExecutionState::CALL_OP, - OpcodeId::ORIGIN => ExecutionState::ORIGIN, - OpcodeId::CODECOPY => ExecutionState::CODECOPY, - OpcodeId::CALLDATALOAD => ExecutionState::CALLDATALOAD, - OpcodeId::CODESIZE => ExecutionState::CODESIZE, - OpcodeId::EXTCODECOPY => ExecutionState::EXTCODECOPY, - OpcodeId::RETURN | OpcodeId::REVERT => ExecutionState::RETURN_REVERT, - OpcodeId::RETURNDATASIZE => ExecutionState::RETURNDATASIZE, - OpcodeId::RETURNDATACOPY => ExecutionState::RETURNDATACOPY, - // dummy ops - OpcodeId::CREATE => dummy!(ExecutionState::CREATE), - OpcodeId::CREATE2 => dummy!(ExecutionState::CREATE2), - OpcodeId::SELFDESTRUCT => dummy!(ExecutionState::SELFDESTRUCT), - _ => unimplemented!("unimplemented opcode {:?}", op), - } - } - circuit_input_builder::ExecState::BeginTx => ExecutionState::BeginTx, - circuit_input_builder::ExecState::EndTx => ExecutionState::EndTx, - circuit_input_builder::ExecState::EndBlock => ExecutionState::EndBlock, - } - } -} - -pub(super) fn step_convert(step: &circuit_input_builder::ExecStep) -> ExecStep { - ExecStep { - call_index: step.call_index, - rw_indices: step - .bus_mapping_instance - .iter() - .map(|x| { - let tag = match x.target() { - operation::Target::Memory => RwTableTag::Memory, - operation::Target::Stack => RwTableTag::Stack, - operation::Target::Storage => RwTableTag::AccountStorage, - operation::Target::TxAccessListAccount => RwTableTag::TxAccessListAccount, - operation::Target::TxAccessListAccountStorage => { - RwTableTag::TxAccessListAccountStorage - } - operation::Target::TxRefund => RwTableTag::TxRefund, - operation::Target::Account => RwTableTag::Account, - operation::Target::CallContext => RwTableTag::CallContext, - operation::Target::TxReceipt => RwTableTag::TxReceipt, - operation::Target::TxLog => RwTableTag::TxLog, - operation::Target::Start => RwTableTag::Start, - }; - (tag, x.as_usize()) - }) - .collect(), - copy_rw_counter_delta: step.copy_rw_counter_delta, - execution_state: ExecutionState::from(step), - rw_counter: usize::from(step.rwc), - program_counter: usize::from(step.pc) as u64, - stack_pointer: STACK_CAPACITY - step.stack_size, - gas_left: step.gas_left.0, - gas_cost: step.gas_cost.as_u64(), - opcode: match step.exec_state { - circuit_input_builder::ExecState::Op(op) => Some(op), - _ => None, - }, - memory_size: step.memory_size as u64, - reversible_write_counter: step.reversible_write_counter, - reversible_write_counter_delta: step.reversible_write_counter_delta, - log_id: step.log_id, - } -} diff --git a/zkevm-circuits/src/witness/tx.rs b/zkevm-circuits/src/witness/tx.rs index a6119f12fd..38be201d3f 100644 --- a/zkevm-circuits/src/witness/tx.rs +++ b/zkevm-circuits/src/witness/tx.rs @@ -4,10 +4,10 @@ use halo2_proofs::circuit::Value; use crate::{evm_circuit::util::rlc, table::TxContextFieldTag, util::Challenges}; -use super::{step::step_convert, Call, ExecStep}; +use super::{Call, ExecStep}; /// Transaction in a witness block -#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[derive(Debug, Default, Clone)] pub struct Transaction { /// The transaction identifier in the block pub id: usize, @@ -135,6 +135,6 @@ pub(super) fn tx_convert(tx: &circuit_input_builder::Transaction, id: usize) -> call_data_length: tx.tx.call_data.len(), call_data_gas_cost: tx.tx.call_data_gas_cost(), calls: tx.calls().to_vec(), - steps: tx.steps().iter().map(step_convert).collect(), + steps: tx.steps().to_vec(), } } diff --git a/zkevm-circuits/tests/prover_error.rs b/zkevm-circuits/tests/prover_error.rs index d55ca439d0..ef70f98246 100644 --- a/zkevm-circuits/tests/prover_error.rs +++ b/zkevm-circuits/tests/prover_error.rs @@ -1,8 +1,8 @@ // This file is intended to be used with fixtures generated from zkevm-chain. // Copy the `errors/` directory into the zkevm-circuits git root // as `block` and run via `cargo test -p zkevm-circuits --features test -// prover_error -- --nocapture --ignored`. Change any constant variables like -// `MAX_TXS` to suit your needs. +// prover_error -- --nocapture --ignored`. Change any circuit parameters like +// `max_txs` to suit your needs. use bus_mapping::{circuit_input_builder::CircuitsParams, mock::BlockData}; use env_logger::Env; use eth_types::{ @@ -36,14 +36,12 @@ fn load_json(path: &str) -> Value { #[ignore] fn prover_error() { // change any of these values to your needs - const MAX_TXS: usize = 1; - const MAX_CALLDATA: usize = 256; const MOCK_RANDOMNESS: u64 = 0x100; let k = 19; let chain_id = Word::from(99); let circuit_params = CircuitsParams { - max_txs: MAX_TXS, - max_calldata: MAX_CALLDATA, + max_txs: 1, + max_calldata: 256, max_rws: 16388, ..Default::default() }; @@ -104,8 +102,7 @@ fn prover_error() { block.randomness = Fr::from(MOCK_RANDOMNESS); block }; - let circuit = - SuperCircuit::<_, MAX_TXS, MAX_CALLDATA, MOCK_RANDOMNESS>::new_from_block(&block_witness); + let circuit = SuperCircuit::new_from_block(&block_witness); let res = MockProver::run(k, &circuit, circuit.instance()) .expect("MockProver::run") .verify_par();