From cd5edab478bedc742300decb0f7e15717077610b Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Thu, 15 Feb 2024 19:03:28 +0800 Subject: [PATCH 01/16] Streamline the math gadget API (#1765) ### Description We improve the readability by sweeping gadget constructors under the constraint builder. ### Issue Link Quick search and replace ### Type of change Refactor (no updates to logic) ### Contents - changed `XGadget::construct(cb, ...)` to `cb.x(...)` - some IsZero checks are replaced with IsEqual checks. ### Rationale We keep consumption of the following gadgets unchanged because they are not used frequently enough. - abs_word - add_word - binary_number - byte_size - cmp_words - comparison - modulo - mul_add_words - mul_add_words512 - mul_word_u64 --- .../src/evm_circuit/execution/addmod.rs | 2 +- .../src/evm_circuit/execution/balance.rs | 2 +- .../src/evm_circuit/execution/begin_tx.rs | 7 +- .../src/evm_circuit/execution/byte.rs | 2 +- .../src/evm_circuit/execution/callop.rs | 58 ++++++++--------- .../src/evm_circuit/execution/comparator.rs | 4 +- .../src/evm_circuit/execution/create.rs | 20 +++--- .../src/evm_circuit/execution/end_block.rs | 11 ++-- .../src/evm_circuit/execution/end_tx.rs | 4 +- .../evm_circuit/execution/error_code_store.rs | 3 +- .../execution/error_invalid_creation_code.rs | 2 +- .../execution/error_invalid_jump.rs | 6 +- .../execution/error_oog_account_access.rs | 3 +- .../evm_circuit/execution/error_oog_call.rs | 2 +- .../execution/error_oog_constant.rs | 3 +- .../evm_circuit/execution/error_oog_create.rs | 5 +- .../evm_circuit/execution/error_oog_log.rs | 4 +- .../execution/error_oog_precompile.rs | 3 +- .../execution/error_oog_static_memory.rs | 2 +- .../execution/error_return_data_oo_bound.rs | 7 +- .../execution/error_write_protection.rs | 11 ++-- .../src/evm_circuit/execution/exp.rs | 6 +- .../src/evm_circuit/execution/extcodecopy.rs | 2 +- .../src/evm_circuit/execution/extcodesize.rs | 2 +- .../src/evm_circuit/execution/invalid_tx.rs | 5 +- .../src/evm_circuit/execution/is_zero.rs | 2 +- .../src/evm_circuit/execution/jumpi.rs | 2 +- .../src/evm_circuit/execution/memory.rs | 4 +- .../src/evm_circuit/execution/mul_div_mod.rs | 4 +- .../src/evm_circuit/execution/mulmod.rs | 4 +- .../src/evm_circuit/execution/push.rs | 12 ++-- .../evm_circuit/execution/return_revert.rs | 5 +- .../src/evm_circuit/execution/sar.rs | 20 +++--- .../src/evm_circuit/execution/sdiv_smod.rs | 8 +-- .../src/evm_circuit/execution/shl_shr.rs | 9 ++- .../execution/signed_comparator.rs | 8 +-- .../src/evm_circuit/execution/signextend.rs | 5 +- .../src/evm_circuit/execution/sstore.rs | 16 ++--- .../src/evm_circuit/util/common_gadget.rs | 25 ++++--- .../evm_circuit/util/constraint_builder.rs | 65 +++++++++++++++++-- .../evm_circuit/util/math_gadget/abs_word.rs | 2 +- .../util/math_gadget/comparison.rs | 4 +- .../evm_circuit/util/math_gadget/is_equal.rs | 2 +- .../util/math_gadget/is_equal_word.rs | 14 ++-- .../evm_circuit/util/math_gadget/lt_word.rs | 2 +- .../evm_circuit/util/math_gadget/min_max.rs | 2 +- .../evm_circuit/util/math_gadget/modulo.rs | 8 +-- .../src/evm_circuit/util/math_gadget/rlp.rs | 2 +- .../src/evm_circuit/util/memory_gadget.rs | 13 ++-- zkevm-circuits/src/evm_circuit/util/tx.rs | 5 +- 50 files changed, 230 insertions(+), 189 deletions(-) diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index c8def073eb..9bd928a7ce 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -69,7 +69,7 @@ impl ExecutionGadget for AddModGadget { let a_reduced = cb.query_word32(); let d = cb.query_word32(); - let n_is_zero = IsZeroWordGadget::construct(cb, &n); + let n_is_zero = cb.is_zero_word(&n); // 1. check k * N + a_reduced == a without overflow let muladd_k_n_areduced = diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index ccc4809cc8..ef3f6dfeea 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -60,7 +60,7 @@ impl ExecutionGadget for BalanceGadget { AccountFieldTag::CodeHash, code_hash.to_word(), ); - let not_exists = IsZeroWordGadget::construct(cb, &code_hash); + let not_exists = cb.is_zero_word(&code_hash); let exists = not::expr(not_exists.expr()); let balance = cb.query_word32(); cb.condition(exists.expr(), |cb| { diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index fc4e2f321b..80de4c1973 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -81,7 +81,7 @@ impl ExecutionGadget for BeginTxGadget { let gas_left = tx.gas.expr() - tx.intrinsic_gas(); let sufficient_gas_left = RangeCheckGadget::construct(cb, gas_left.clone()); - let tx_caller_address_is_zero = IsZeroWordGadget::construct(cb, &tx.caller_address); + let tx_caller_address_is_zero = cb.is_zero_word(&tx.caller_address); cb.require_equal( "CallerAddress != 0 (not a padding tx)", tx_caller_address_is_zero.expr(), @@ -155,9 +155,8 @@ impl ExecutionGadget for BeginTxGadget { // Read code_hash of callee let code_hash = cb.query_word_unchecked(); - let is_empty_code_hash = - IsEqualWordGadget::construct(cb, &code_hash.to_word(), &cb.empty_code_hash()); - let callee_not_exists = IsZeroWordGadget::construct(cb, &code_hash); + let is_empty_code_hash = cb.is_eq_word(&code_hash.to_word(), &cb.empty_code_hash()); + let callee_not_exists = cb.is_zero_word(&code_hash); // no_callee_code is true when the account exists and has empty // code hash, or when the account doesn't exist (which we encode with // code_hash = 0). diff --git a/zkevm-circuits/src/evm_circuit/execution/byte.rs b/zkevm-circuits/src/evm_circuit/execution/byte.rs index dd3b89ac68..fb06fb0579 100644 --- a/zkevm-circuits/src/evm_circuit/execution/byte.rs +++ b/zkevm-circuits/src/evm_circuit/execution/byte.rs @@ -53,7 +53,7 @@ impl ExecutionGadget for ByteGadget { let is_byte_selected = array_init(|idx| { // Check if this byte is selected looking only at the LSB of the // index word - IsEqualGadget::construct(cb, index.limbs[0].expr(), (31 - idx).expr()) + cb.is_eq(index.limbs[0].expr(), (31 - idx).expr()) }); // Sum all possible selected bytes diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 4af82c36bb..66716640c1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -11,7 +11,8 @@ use crate::{ Transition::{Delta, To}, }, math_gadget::{ - ConstantDivisionGadget, IsZeroGadget, LtGadget, LtWordGadget, MinMaxGadget, + ConstantDivisionGadget, IsEqualGadget, IsZeroGadget, LtGadget, LtWordGadget, + MinMaxGadget, }, memory_gadget::{CommonMemoryAddressGadget, MemoryAddressGadget}, not, or, @@ -44,10 +45,10 @@ use std::cmp::min; pub(crate) struct CallOpGadget { opcode: Cell, - is_call: IsZeroGadget, - is_callcode: IsZeroGadget, - is_delegatecall: IsZeroGadget, - is_staticcall: IsZeroGadget, + is_call: IsEqualGadget, + is_callcode: IsEqualGadget, + is_delegatecall: IsEqualGadget, + is_staticcall: IsEqualGadget, tx_id: Cell, reversion_info: ReversionInfo, current_callee_address: WordLoHiCell, @@ -94,12 +95,10 @@ impl ExecutionGadget for CallOpGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); cb.opcode_lookup(opcode.expr(), 1.expr()); - let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); - let is_callcode = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALLCODE.expr()); - let is_delegatecall = - IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::DELEGATECALL.expr()); - let is_staticcall = - IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::STATICCALL.expr()); + let is_call = cb.is_eq(opcode.expr(), OpcodeId::CALL.expr()); + let is_callcode = cb.is_eq(opcode.expr(), OpcodeId::CALLCODE.expr()); + let is_delegatecall = cb.is_eq(opcode.expr(), OpcodeId::DELEGATECALL.expr()); + let is_staticcall = cb.is_eq(opcode.expr(), OpcodeId::STATICCALL.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(); @@ -196,9 +195,9 @@ impl ExecutionGadget for CallOpGadget { // rwc_delta = 8 + is_delegatecall * 2 + call_gadget.rw_delta() + // callee_reversion_info.rw_delta() let is_insufficient_balance = - LtWordGadget::construct(cb, &caller_balance.to_word(), &call_gadget.value.to_word()); + cb.is_lt_word(&caller_balance.to_word(), &call_gadget.value.to_word()); // depth < 1025 - let is_depth_ok = LtGadget::construct(cb, depth.expr(), 1025.expr()); + let is_depth_ok = cb.is_lt(depth.expr(), 1025.expr()); let is_precheck_ok = and::expr([ is_depth_ok.expr(), @@ -215,18 +214,15 @@ impl ExecutionGadget for CallOpGadget { // whether the call is to a precompiled contract. // precompile contracts are stored from address 0x01 to 0x09. - let is_code_address_zero = IsZeroGadget::construct(cb, call_gadget.callee_address.expr()); - let is_precompile_lt = - LtGadget::construct(cb, call_gadget.callee_address.expr(), 0x0A.expr()); + let is_code_address_zero = cb.is_zero(call_gadget.callee_address.expr()); + let is_precompile_lt = cb.is_lt(call_gadget.callee_address.expr(), 0x0A.expr()); let is_precompile = and::expr([ not::expr(is_code_address_zero.expr()), is_precompile_lt.expr(), ]); let precompile_return_length = cb.query_cell(); - let precompile_return_length_zero = - IsZeroGadget::construct(cb, precompile_return_length.expr()); - let precompile_return_data_copy_size = MinMaxGadget::construct( - cb, + let precompile_return_length_zero = cb.is_zero(precompile_return_length.expr()); + let precompile_return_data_copy_size = cb.min_max( precompile_return_length.expr(), call_gadget.rd_address.length(), ); @@ -275,10 +271,10 @@ impl ExecutionGadget for CallOpGadget { let gas_cost = call_gadget.gas_cost_expr(is_warm_prev.expr(), is_call.expr()); // Apply EIP 150 let gas_available = cb.curr.state.gas_left.expr() - gas_cost.clone(); - let one_64th_gas = ConstantDivisionGadget::construct(cb, gas_available.clone(), 64); + let one_64th_gas = cb.div_by_const(gas_available.clone(), 64); let all_but_one_64th_gas = gas_available - one_64th_gas.quotient(); let capped_callee_gas_left = - MinMaxGadget::construct(cb, call_gadget.gas_expr(), all_but_one_64th_gas.clone()); + cb.min_max(call_gadget.gas_expr(), all_but_one_64th_gas.clone()); let callee_gas_left = select::expr( call_gadget.gas_is_u64.expr(), capped_callee_gas_left.min(), @@ -480,10 +476,10 @@ impl ExecutionGadget for CallOpGadget { let callee_gas_left = callee_gas_left.expr() + call_gadget.has_value.clone() * GAS_STIPEND_CALL_WITH_VALUE.expr(); - let precompile_output_word_size_div: ConstantDivisionGadget = - ConstantDivisionGadget::construct(cb, precompile_output_rws.expr(), 32); + let precompile_output_word_size_div = + cb.div_by_const(precompile_output_rws.expr(), 32); let precompile_output_word_size_div_remainder_zero = - IsZeroGadget::construct(cb, precompile_output_word_size_div.remainder()); + cb.is_zero(precompile_output_word_size_div.remainder()); let precompile_output_word_size = precompile_output_word_size_div.quotient() + 1.expr() - precompile_output_word_size_div_remainder_zero.expr(); @@ -891,22 +887,26 @@ impl ExecutionGadget for CallOpGadget { self.is_call.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::CALL.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::CALL.as_u64()), )?; self.is_callcode.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::CALLCODE.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::CALLCODE.as_u64()), )?; self.is_delegatecall.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::DELEGATECALL.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::DELEGATECALL.as_u64()), )?; self.is_staticcall.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::STATICCALL.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::STATICCALL.as_u64()), )?; self.tx_id .assign(region, offset, Value::known(F::from(tx_id.low_u64())))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/comparator.rs b/zkevm-circuits/src/evm_circuit/execution/comparator.rs index 5dcfbca0b8..a0f81f8109 100644 --- a/zkevm-circuits/src/evm_circuit/execution/comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/comparator.rs @@ -41,10 +41,10 @@ impl ExecutionGadget for ComparatorGadget { let b = cb.query_word_unchecked(); // Check if opcode is EQ - let is_eq = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::EQ.expr()); + let is_eq = cb.is_eq(opcode.expr(), OpcodeId::EQ.expr()); // Check if opcode is GT. For GT we swap the stack inputs so that we // actually do greater than instead of smaller than. - let is_gt = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::GT.expr()); + let is_gt = cb.is_eq(opcode.expr(), OpcodeId::GT.expr()); let word_comparison = CmpWordsGadget::construct(cb, a.clone(), b.clone()); diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 61b842308d..9b560a16ce 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -13,7 +13,7 @@ use crate::{ Transition::{Delta, To}, }, math_gadget::{ - ConstantDivisionGadget, ContractCreateGadget, IsZeroGadget, IsZeroWordGadget, + ConstantDivisionGadget, ContractCreateGadget, IsEqualGadget, IsZeroWordGadget, LtGadget, LtWordGadget, }, memory_gadget::{ @@ -53,7 +53,7 @@ pub(crate) struct CreateGadget, depth: Cell, - is_create2: IsZeroGadget, + is_create2: IsEqualGadget, is_success: Cell, was_warm: Cell, value: Word32Cell, @@ -95,7 +95,7 @@ impl ExecutionGadget< opcode.expr(), vec![OpcodeId::CREATE2.expr(), OpcodeId::CREATE.expr()], ); - let is_create2 = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CREATE2.expr()); + let is_create2 = cb.is_eq(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(); @@ -143,10 +143,9 @@ impl ExecutionGadget< ); // 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.to_word(), &value.to_word()); - let is_nonce_in_range = LtGadget::construct(cb, caller_nonce.expr(), u64::MAX.expr()); + let is_depth_in_range = cb.is_lt(depth.expr(), 1025.expr()); + let is_insufficient_balance = cb.is_lt_word(&caller_balance.to_word(), &value.to_word()); + let is_nonce_in_range = cb.is_lt(caller_nonce.expr(), u64::MAX.expr()); let is_precheck_ok = and::expr([ is_depth_in_range.expr(), not::expr(is_insufficient_balance.expr()), @@ -169,7 +168,7 @@ impl ExecutionGadget< ); 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 gas_left = cb.div_by_const(gas_remaining.clone(), 64); let callee_gas_left = gas_remaining - gas_left.quotient(); let was_warm = cb.query_bool(); @@ -209,7 +208,7 @@ impl ExecutionGadget< // empty_code_hash_word))` to represent `(callee_nonce == 0 && (prev_code_hash_word // == 0 or prev_code_hash_word == empty_code_hash_word))` let prev_code_hash_word = prev_code_hash.to_word(); - let prev_code_hash_is_zero = IsZeroWordGadget::construct(cb, &prev_code_hash_word); + let prev_code_hash_is_zero = cb.is_zero_word(&prev_code_hash_word); cb.condition(not::expr(prev_code_hash_is_zero.expr()), |cb| { cb.account_read( contract_addr.to_word(), @@ -525,7 +524,8 @@ impl ExecutionGadget< self.is_create2.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::CREATE2.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::CREATE2.as_u64()), )?; self.tx_id diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index 51e35e2d5e..63c5e530e1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -6,7 +6,7 @@ use crate::{ constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Same, }, - math_gadget::{IsEqualGadget, IsZeroGadget}, + math_gadget::IsEqualGadget, not, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -22,7 +22,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; pub(crate) struct EndBlockGadget { total_txs: Cell, total_txs_is_max_txs: IsEqualGadget, - is_empty_block: IsZeroGadget, + is_empty_block: IsEqualGadget, max_rws: Cell, max_txs: Cell, } @@ -36,10 +36,9 @@ impl ExecutionGadget for EndBlockGadget { let max_txs = cb.query_copy_cell(); let max_rws = cb.query_copy_cell(); let total_txs = cb.query_cell(); - let total_txs_is_max_txs = IsEqualGadget::construct(cb, total_txs.expr(), max_txs.expr()); + let total_txs_is_max_txs = cb.is_eq(total_txs.expr(), max_txs.expr()); // Note that rw_counter starts at 1 - let is_empty_block = - IsZeroGadget::construct(cb, cb.curr.state.rw_counter.clone().expr() - 1.expr()); + let is_empty_block = cb.is_eq(cb.curr.state.rw_counter.clone().expr(), 1.expr()); let total_rws_before_padding = cb.curr.state.rw_counter.clone().expr() - 1.expr() + select::expr( @@ -126,7 +125,7 @@ impl ExecutionGadget for EndBlockGadget { step: &ExecStep, ) -> Result<(), Error> { self.is_empty_block - .assign(region, offset, F::from(u64::from(step.rwc) - 1))?; + .assign(region, offset, F::from(u64::from(step.rwc)), F::ONE)?; 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))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index e8bfc5b779..8c68776fdc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -67,7 +67,7 @@ impl ExecutionGadget for EndTxGadget { ); let refund = cb.query_cell(); cb.tx_refund_read(tx_id.expr(), WordLoHi::from_lo_unchecked(refund.expr())); - let effective_refund = MinMaxGadget::construct(cb, max_refund.quotient(), refund.expr()); + let effective_refund = cb.min_max(max_refund.quotient(), refund.expr()); // Add effective_refund * tx_gas_price back to caller's balance let mul_gas_price_by_refund = MulWordByU64Gadget::construct( @@ -85,7 +85,7 @@ impl ExecutionGadget for EndTxGadget { // Add gas_used * effective_tip to coinbase's balance let coinbase = cb.query_word_unchecked(); let coinbase_code_hash = cb.query_word_unchecked(); - let coinbase_code_hash_is_zero = IsZeroWordGadget::construct(cb, &coinbase_code_hash); + let coinbase_code_hash_is_zero = cb.is_zero_word(&coinbase_code_hash); cb.account_read( coinbase.to_word(), AccountFieldTag::CodeHash, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs index 3440bd9f44..2506639307 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs @@ -72,8 +72,7 @@ impl ExecutionGadget for ErrorCodeStoreGadget { ); // constrain code size > MAXCODESIZE - let max_code_size_exceed = - LtGadget::construct(cb, MAXCODESIZE.expr(), memory_address.length()); + let max_code_size_exceed = cb.is_lt(MAXCODESIZE.expr(), memory_address.length()); // check must be one of CodeStoreOutOfGas or MaxCodeSizeExceeded cb.require_in_set( diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs index e076a831bc..6b7482046a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs @@ -46,7 +46,7 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { cb.memory_lookup(0.expr(), memory_address.offset(), first_byte.expr(), None); let is_first_byte_invalid = - IsEqualGadget::construct(cb, first_byte.expr(), INVALID_INIT_CODE_FIRST_BYTE.expr()); + cb.is_eq(first_byte.expr(), INVALID_INIT_CODE_FIRST_BYTE.expr()); cb.require_true( "is_first_byte_invalid is true", is_first_byte_invalid.expr(), 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 e5b40f5f71..6fab8e624b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -54,14 +54,14 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { vec![OpcodeId::JUMP.expr(), OpcodeId::JUMPI.expr()], ); - let is_jumpi = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::JUMPI.expr()); + let is_jumpi = cb.is_eq(opcode.expr(), OpcodeId::JUMPI.expr()); // initialize is_jump_dest - let is_jump_dest = IsEqualGadget::construct(cb, value.expr(), OpcodeId::JUMPDEST.expr()); + let is_jump_dest = cb.is_eq(value.expr(), OpcodeId::JUMPDEST.expr()); // first default this condition, if use will re-construct with real condition // value - let is_condition_zero = IsZeroWordGadget::construct(cb, &condition); + let is_condition_zero = cb.is_zero_word(&condition); // Pop the value from the stack cb.stack_pop(dest.original_word().to_word()); diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs index 9e36738ef2..7878b67252 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs @@ -62,8 +62,7 @@ impl ExecutionGadget for ErrorOOGAccountAccessGadget { GasCost::COLD_ACCOUNT_ACCESS.expr(), ); - let insufficient_gas_cost = - LtGadget::construct(cb, cb.curr.state.gas_left.expr(), gas_cost); + let insufficient_gas_cost = cb.is_lt(cb.curr.state.gas_left.expr(), gas_cost); cb.require_equal( "Gas left is less than gas cost", 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 38aeae1db7..53b61484aa 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -79,7 +79,7 @@ impl ExecutionGadget for ErrorOOGCallGadget { let gas_cost = call_gadget.gas_cost_expr(is_warm.expr(), is_call.expr()); // Check if the amount of gas available is less than the amount of gas required - let insufficient_gas = LtGadget::construct(cb, cb.curr.state.gas_left.expr(), gas_cost); + let insufficient_gas = cb.is_lt(cb.curr.state.gas_left.expr(), gas_cost); cb.require_equal( "Either Memory address is overflow or gas left is less than cost", 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 acaec49d6d..c0061b31c5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs @@ -38,8 +38,7 @@ impl ExecutionGadget for ErrorOOGConstantGadget { cb.constant_gas_lookup(opcode.expr(), gas_required.expr()); // Check if the amount of gas available is less than the amount of gas // required - let insufficient_gas = - LtGadget::construct(cb, cb.curr.state.gas_left.expr(), gas_required.expr()); + let insufficient_gas = cb.is_lt(cb.curr.state.gas_left.expr(), gas_required.expr()); cb.require_equal( "constant gas left is less than gas required ", insufficient_gas.expr(), diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs index b22619ae01..99756d1d23 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs @@ -68,8 +68,7 @@ impl ExecutionGadget for ErrorOOGCreateGadget { cb.stack_pop(memory_address.length_word()); cb.condition(is_create2.expr().0, |cb| cb.stack_pop(salt.to_word())); - let init_code_size_overflow = - LtGadget::construct(cb, MAX_INIT_CODE_SIZE.expr(), memory_address.length()); + let init_code_size_overflow = cb.is_lt(MAX_INIT_CODE_SIZE.expr(), memory_address.length()); let minimum_word_size = MemoryWordSizeGadget::construct(cb, memory_address.length()); let memory_expansion = MemoryExpansionGadget::construct(cb, [memory_address.address()]); @@ -81,7 +80,7 @@ impl ExecutionGadget for ErrorOOGCreateGadget { CREATE_GAS_PER_CODE_WORD.expr(), ); let gas_cost = GasCost::CREATE.expr() + memory_expansion.gas_cost() + code_store_gas_cost; - let insufficient_gas = LtGadget::construct(cb, cb.curr.state.gas_left.expr(), gas_cost); + let insufficient_gas = cb.is_lt(cb.curr.state.gas_left.expr(), gas_cost); cb.require_equal( "Memory address is overflow, init code size is overflow, or gas left is less than cost", 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 4dbb9f4a24..bf129c5088 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -55,7 +55,7 @@ impl ExecutionGadget for ErrorOOGLogGadget { cb.require_zero("is_static_call is false in LOGN", is_static_call.expr()); let topic_count = opcode.expr() - OpcodeId::LOG0.as_u8().expr(); - let is_opcode_logn = LtGadget::construct(cb, topic_count.clone(), 5.expr()); + let is_opcode_logn = cb.is_lt(topic_count.clone(), 5.expr()); cb.require_equal( "topic count in [0..5) which means opcode is Log0...Log4 ", is_opcode_logn.expr(), @@ -73,7 +73,7 @@ impl ExecutionGadget for ErrorOOGLogGadget { // Check if the amount of gas available is less than the amount of gas // required - let insufficient_gas = LtGadget::construct(cb, cb.curr.state.gas_left.expr(), gas_cost); + let insufficient_gas = cb.is_lt(cb.curr.state.gas_left.expr(), gas_cost); cb.require_equal( "Memory address is overflow or gas left is less than cost", or::expr([memory_address.overflow(), insufficient_gas.expr()]), diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs index 72ebf106e9..4dd3df7ca8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs @@ -114,8 +114,7 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { ); // gas_left < required_gas - let insufficient_gas = - LtGadget::construct(cb, cb.curr.state.gas_left.expr(), required_gas.expr()); + let insufficient_gas = cb.is_lt(cb.curr.state.gas_left.expr(), required_gas.expr()); cb.require_equal("gas_left < required_gas", insufficient_gas.expr(), 1.expr()); let restore_context = RestoreContextGadget::construct2( 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 588d8c4cc4..948062ce7d 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 @@ -55,7 +55,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { ); // Check if this is an MSTORE8 - let is_mstore8 = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::MSTORE8.expr()); + let is_mstore8 = cb.is_eq(opcode.expr(), OpcodeId::MSTORE8.expr()); // pop memory_offset from stack let memory_address = MemoryExpandedAddressGadget::construct_self(cb); 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 a20954ef5f..52a9839239 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 @@ -69,18 +69,17 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { // Check if `data_offset` is Uint64 overflow. let data_offset_larger_u64 = sum::expr(&data_offset.limbs[N_BYTES_U64..]); - let is_data_offset_within_u64 = IsZeroGadget::construct(cb, data_offset_larger_u64); + let is_data_offset_within_u64 = cb.is_zero(data_offset_larger_u64); // Check if `remainder_end` is Uint64 overflow. let sum = AddWordsGadget::construct(cb, [data_offset, size], remainder_end.clone()); let is_end_u256_overflow = sum.carry().as_ref().unwrap(); let remainder_end_larger_u64 = sum::expr(&remainder_end.limbs[N_BYTES_U64..]); - let is_remainder_end_within_u64 = IsZeroGadget::construct(cb, remainder_end_larger_u64); + let is_remainder_end_within_u64 = cb.is_zero(remainder_end_larger_u64); // check if `remainder_end` exceeds return data length. - let is_remainder_end_exceed_len = LtGadget::construct( - cb, + let is_remainder_end_exceed_len = cb.is_lt( return_data_length.expr(), from_bytes::expr(&remainder_end.limbs[..N_BYTES_U64]), ); 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 9e148bc6d3..531bdc9813 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -5,7 +5,7 @@ use crate::{ util::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, - math_gadget::{IsZeroGadget, IsZeroWordGadget}, + math_gadget::{IsEqualGadget, IsZeroWordGadget}, AccountAddress, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -22,7 +22,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct ErrorWriteProtectionGadget { opcode: Cell, - is_call: IsZeroGadget, + is_call: IsEqualGadget, gas: WordLoHiCell, code_address: AccountAddress, value: WordLoHiCell, @@ -37,11 +37,11 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let is_call = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::CALL.expr()); + let is_call = cb.is_eq(opcode.expr(), OpcodeId::CALL.expr()); let gas_word = cb.query_word_unchecked(); let code_address = cb.query_account_address(); let value = cb.query_word_unchecked(); - let is_value_zero = IsZeroWordGadget::construct(cb, &value); + let is_value_zero = cb.is_zero_word(&value); // require_in_set method will spilit into more low degree expressions if exceed // max_degree. otherwise need to do fixed lookup for these opcodes @@ -122,7 +122,8 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { self.is_call.assign( region, offset, - F::from(opcode.as_u64()) - F::from(OpcodeId::CALL.as_u64()), + F::from(opcode.as_u64()), + F::from(OpcodeId::CALL.as_u64()), )?; self.is_value_zero .assign(region, offset, WordLoHi::from(value))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/exp.rs b/zkevm-circuits/src/evm_circuit/execution/exp.rs index 0945e2819e..24de01a70e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/exp.rs @@ -78,11 +78,11 @@ impl ExecutionGadget for ExponentiationGadget { // We simplify constraints depending on whether or not the exponent is 0 or 1. // In order to do this, we build some utility expressions. - let exponent_lo_is_zero = IsZeroGadget::construct(cb, exponent_lo.clone()); - let exponent_hi_is_zero = IsZeroGadget::construct(cb, exponent_hi.clone()); + let exponent_lo_is_zero = cb.is_zero(exponent_lo.clone()); + let exponent_hi_is_zero = cb.is_zero(exponent_hi.clone()); let exponent_is_zero_expr = and::expr([exponent_lo_is_zero.expr(), exponent_hi_is_zero.expr()]); - let exponent_lo_is_one = IsEqualGadget::construct(cb, exponent_lo.clone(), 1.expr()); + let exponent_lo_is_one = cb.is_eq(exponent_lo.clone(), 1.expr()); let exponent_is_one_expr = and::expr([exponent_lo_is_one.expr(), exponent_hi_is_zero.expr()]); diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index f7e8ba4793..bb05e5237d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -91,7 +91,7 @@ impl ExecutionGadget for ExtcodecopyGadget { AccountFieldTag::CodeHash, code_hash.to_word(), ); - let not_exists = IsZeroWordGadget::construct(cb, &code_hash.to_word()); + let not_exists = cb.is_zero_word(&code_hash.to_word()); let exists = not::expr(not_exists.expr()); cb.condition(exists.expr(), |cb| { cb.bytecode_length(code_hash.to_word(), code_size.expr()); diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index 631f75cf85..908b1e2b7b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -69,7 +69,7 @@ impl ExecutionGadget for ExtcodesizeGadget { AccountFieldTag::CodeHash, code_hash.to_word(), ); - let not_exists = IsZeroWordGadget::construct(cb, &code_hash); + let not_exists = cb.is_zero_word(&code_hash); let exists = not::expr(not_exists.expr()); let code_size = cb.query_u64(); diff --git a/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs b/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs index a87dc847e6..1100175592 100644 --- a/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs @@ -47,7 +47,7 @@ impl ExecutionGadget for InvalidTxGadget { AccountFieldTag::Nonce, WordLoHi::from_lo_unchecked(account_nonce.expr()), ); - let is_nonce_match = IsEqualGadget::construct(cb, account_nonce.expr(), tx.nonce.expr()); + let is_nonce_match = cb.is_eq(account_nonce.expr(), tx.nonce.expr()); // Check if the gas limit is larger or equal to the intrinsic gas cost let insufficient_gas_limit = @@ -60,8 +60,7 @@ impl ExecutionGadget for InvalidTxGadget { AccountFieldTag::Balance, balance.to_word(), ); - let insufficient_balance = - LtWordGadget::construct(cb, &balance.to_word(), &tx.total_cost().to_word()); + let insufficient_balance = cb.is_lt_word(&balance.to_word(), &tx.total_cost().to_word()); // At least one of the invalid conditions needs to be true let invalid_tx = or::expr([ diff --git a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs index 1c9b189808..c2f52e4100 100644 --- a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs @@ -34,7 +34,7 @@ impl ExecutionGadget for IsZeroGadget { let opcode = cb.query_cell(); let value = cb.query_word_unchecked(); - let is_zero_word = math_gadget::IsZeroWordGadget::construct(cb, &value); + let is_zero_word = cb.is_zero_word(&value); cb.stack_pop(value.to_word()); cb.stack_push(WordLoHi::from_lo_unchecked(is_zero_word.expr())); diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index 67e7b343c7..40b9878ea0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -44,7 +44,7 @@ impl ExecutionGadget for JumpiGadget { cb.stack_pop(condition.to_word()); // Determine if the jump condition is met - let is_condition_zero = IsZeroWordGadget::construct(cb, &condition); + let is_condition_zero = cb.is_zero_word(&condition); let should_jump = 1.expr() - is_condition_zero.expr(); // Lookup opcode at destination when should_jump diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 3aa47184ff..2956730d7f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -46,9 +46,9 @@ impl ExecutionGadget for MemoryGadget { let value = cb.query_word32(); // Check if this is an MLOAD - let is_mload = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::MLOAD.expr()); + let is_mload = cb.is_eq(opcode.expr(), OpcodeId::MLOAD.expr()); // Check if this is an MSTORE8 - let is_mstore8 = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::MSTORE8.expr()); + let is_mstore8 = cb.is_eq(opcode.expr(), OpcodeId::MSTORE8.expr()); // This is an MSTORE/MSTORE8 let is_store = not::expr(is_mload.expr()); // This is an MSTORE/MLOAD 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 84290e6bf3..19bf5f2735 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs @@ -63,8 +63,8 @@ impl ExecutionGadget for MulDivModGadget { let d = cb.query_word32(); let mul_add_words = MulAddWordsGadget::construct(cb, [&a, &b, &c, &d]); - let divisor_is_zero = IsZeroWordGadget::construct(cb, &b); - let lt_word = LtWordGadget::construct(cb, &c.to_word(), &b.to_word()); + let divisor_is_zero = cb.is_zero_word(&b); + let lt_word = cb.is_lt_word(&c.to_word(), &b.to_word()); // Pop a and b from the stack, push result on the stack // The first pop is multiplier for MUL and dividend for DIV/MOD diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index 6b6f7299d2..d2d021c6f7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -70,8 +70,8 @@ impl ExecutionGadget for MulModGadget { let mul512_right = MulAddWords512Gadget::construct(cb, [&k, &n, &d, &e], Some(&r)); // (r < n ) or n == 0 - let n_is_zero = IsZeroWordGadget::construct(cb, &n); - let lt = LtWordGadget::construct(cb, &r.to_word(), &n.to_word()); + let n_is_zero = cb.is_zero_word(&n); + let lt = cb.is_lt_word(&r.to_word(), &n.to_word()); cb.add_constraint( " (1 - (r < n) - (n==0)) ", 1.expr() - lt.expr() - n_is_zero.expr(), diff --git a/zkevm-circuits/src/evm_circuit/execution/push.rs b/zkevm-circuits/src/evm_circuit/execution/push.rs index 2948da9272..d6e95b6552 100644 --- a/zkevm-circuits/src/evm_circuit/execution/push.rs +++ b/zkevm-circuits/src/evm_circuit/execution/push.rs @@ -10,7 +10,7 @@ use crate::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Delta, }, - math_gadget::{IsZeroGadget, LtGadget}, + math_gadget::{IsEqualGadget, LtGadget}, not, or, select, sum, CachedRegion, Cell, }, witness::{Block, Call, ExecStep, Transaction}, @@ -27,7 +27,7 @@ use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] pub(crate) struct PushGadget { same_context: SameContextGadget, - is_push0: IsZeroGadget, + is_push0: IsEqualGadget, value: Word32Cell, is_pushed: [Cell; 32], is_padding: [Cell; 32], @@ -42,7 +42,7 @@ impl ExecutionGadget for PushGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let opcode = cb.query_cell(); - let is_push0 = IsZeroGadget::construct(cb, opcode.expr() - OpcodeId::PUSH0.expr()); + let is_push0 = cb.is_eq(opcode.expr(), OpcodeId::PUSH0.expr()); let value = cb.query_word32(); cb.stack_push(value.to_word()); @@ -56,8 +56,7 @@ impl ExecutionGadget for PushGadget { cb.bytecode_length(cb.curr.state.code_hash.to_word(), code_length.expr()); let num_bytes_needed = opcode.expr() - OpcodeId::PUSH0.expr(); - let is_out_of_bound = - LtGadget::construct(cb, code_length_left.expr(), num_bytes_needed.expr()); + let is_out_of_bound = cb.is_lt(code_length_left.expr(), num_bytes_needed.expr()); let num_bytes_padding = select::expr( is_out_of_bound.expr(), num_bytes_needed.expr() - code_length_left.expr(), @@ -170,7 +169,8 @@ impl ExecutionGadget for PushGadget { self.is_push0.assign( region, offset, - F::from(opcode.as_u64() - OpcodeId::PUSH0.as_u64()), + F::from(opcode.as_u64()), + OpcodeId::PUSH0.as_u64().into(), )?; let bytecode = block diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index d0b7bd1dcb..02a1a5854a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -92,7 +92,7 @@ impl ExecutionGadget for ReturnRevertGadget { // These are globally defined because they are used across multiple cases. let copy_rw_increase = cb.query_cell(); - let copy_rw_increase_is_zero = IsZeroGadget::construct(cb, copy_rw_increase.expr()); + let copy_rw_increase_is_zero = cb.is_zero(copy_rw_increase.expr()); let memory_expansion = MemoryExpansionGadget::construct(cb, [range.address()]); @@ -218,8 +218,7 @@ impl ExecutionGadget for ReturnRevertGadget { CallContextFieldTag::ReturnDataLength, ] .map(|field_tag| cb.call_context(None, field_tag)); - let copy_length = - MinMaxGadget::construct(cb, return_data_length.expr(), range.length()); + let copy_length = cb.min_max(return_data_length.expr(), range.length()); cb.require_equal( "increase rw counter twice for each memory to memory byte copied", copy_length.min() + copy_length.min(), diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index 9cfb4ac939..fd09750a16 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -99,8 +99,8 @@ impl ExecutionGadget for SarGadget { let p_lo = cb.query_cell(); let p_hi = cb.query_cell(); let p_top = cb.query_cell(); - let is_neg = LtGadget::construct(cb, 127.expr(), a.limbs[31].expr()); - let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.limbs[1..32])); + let is_neg = cb.is_lt(127.expr(), a.limbs[31].expr()); + let shf_lt256 = cb.is_zero(sum::expr(&shift.limbs[1..32])); for idx in 0..4 { cb.require_equal( @@ -112,23 +112,23 @@ impl ExecutionGadget for SarGadget { // Constrain `a64s_lo[idx] < p_lo`. let a64s_lo_lt_p_lo = array_init(|idx| { - let lt = LtGadget::construct(cb, a64s_lo[idx].expr(), p_lo.expr()); + let lt = cb.is_lt(a64s_lo[idx].expr(), p_lo.expr()); cb.require_zero("a64s_lo[idx] < p_lo", 1.expr() - lt.expr()); lt }); // Constrain `a64s_hi[idx] < p_hi`. let a64s_hi_lt_p_hi = array_init(|idx| { - let lt = LtGadget::construct(cb, a64s_hi[idx].expr(), p_hi.expr()); + let lt = cb.is_lt(a64s_hi[idx].expr(), p_hi.expr()); cb.require_zero("a64s_hi[idx] < p_hi", 1.expr() - lt.expr()); lt }); // Merge contraints - let shf_lo_div64_eq0 = IsZeroGadget::construct(cb, shf_div64.expr()); - let shf_lo_div64_eq1 = IsEqualGadget::construct(cb, shf_div64.expr(), 1.expr()); - let shf_lo_div64_eq2 = IsEqualGadget::construct(cb, shf_div64.expr(), 2.expr()); - let shf_lo_div64_eq3 = IsEqualGadget::construct(cb, shf_div64.expr(), 3.expr()); + let shf_lo_div64_eq0 = cb.is_zero(shf_div64.expr()); + let shf_lo_div64_eq1 = cb.is_eq(shf_div64.expr(), 1.expr()); + let shf_lo_div64_eq2 = cb.is_eq(shf_div64.expr(), 2.expr()); + let shf_lo_div64_eq3 = cb.is_eq(shf_div64.expr(), 3.expr()); let shf_div64_eq0 = shf_lt256.expr() * shf_lo_div64_eq0.expr(); let shf_div64_eq1 = shf_lt256.expr() * shf_lo_div64_eq1.expr(); let shf_div64_eq2 = shf_lt256.expr() * shf_lo_div64_eq2.expr(); @@ -179,9 +179,9 @@ impl ExecutionGadget for SarGadget { ); // Shift constraint - let shf_div64_lt_4 = LtGadget::construct(cb, shf_div64.expr(), 4.expr()); + let shf_div64_lt_4 = cb.is_lt(shf_div64.expr(), 4.expr()); cb.require_equal("shf_div64 < 4", shf_div64_lt_4.expr(), 1.expr()); - let shf_mod64_lt_64 = LtGadget::construct(cb, shf_mod64.expr(), 64.expr()); + let shf_mod64_lt_64 = cb.is_lt(shf_mod64.expr(), 64.expr()); cb.require_equal("shf_mod64 < 64", shf_mod64_lt_64.expr(), 1.expr()); cb.require_equal( "shift[0] == shf_mod64 + shf_div64 * 64", diff --git a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs index 85a271661d..b6779c420f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs @@ -52,9 +52,9 @@ impl ExecutionGadget for SignedDivModGadget { let divisor_abs = AbsWordGadget::construct(cb); let remainder_abs = AbsWordGadget::construct(cb); let dividend_abs = AbsWordGadget::construct(cb); - let quotient_is_zero = IsZeroWordGadget::construct(cb, quotient_abs.x()); - let divisor_is_zero = IsZeroWordGadget::construct(cb, divisor_abs.x()); - let remainder_is_zero = IsZeroWordGadget::construct(cb, remainder_abs.x()); + let quotient_is_zero = cb.is_zero_word(quotient_abs.x()); + let divisor_is_zero = cb.is_zero_word(divisor_abs.x()); + let remainder_is_zero = cb.is_zero_word(remainder_abs.x()); cb.stack_pop(dividend_abs.x().to_word()); cb.stack_pop(divisor_abs.x().to_word()); @@ -109,7 +109,7 @@ impl ExecutionGadget for SignedDivModGadget { // `sign(dividend) == sign(divisor) ^ sign(quotient)` cannot be applied // for this case. let dividend_is_signed_overflow = - LtGadget::construct(cb, 127.expr(), dividend_abs.x_abs().limbs[31].expr()); + cb.is_lt(127.expr(), dividend_abs.x_abs().limbs[31].expr()); // Constrain sign(dividend) == sign(divisor) ^ sign(quotient) when both // quotient and divisor are non-zero and dividend is not signed overflow. diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index 92c614d861..ef822f0074 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -69,11 +69,10 @@ impl ExecutionGadget for ShlShrGadget { let mul_add_words = MulAddWordsGadget::construct(cb, ["ient, &divisor, &remainder, ÷nd]); - let shf_lt256 = IsZeroGadget::construct(cb, sum::expr(&shift.limbs[1..32])); - let divisor_is_zero = IsZeroWordGadget::construct(cb, &divisor); - let remainder_is_zero = IsZeroWordGadget::construct(cb, &remainder); - let remainder_lt_divisor = - LtWordGadget::construct(cb, &remainder.to_word(), &divisor.to_word()); + let shf_lt256 = cb.is_zero(sum::expr(&shift.limbs[1..32])); + let divisor_is_zero = cb.is_zero_word(&divisor); + let remainder_is_zero = cb.is_zero_word(&remainder); + let remainder_lt_divisor = cb.is_lt_word(&remainder.to_word(), &divisor.to_word()); // Constrain stack pops and pushes as: // - for SHL, two pops are shift and quotient, and push is dividend. diff --git a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs index c6ebfcfe35..2211fa90d5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs @@ -51,7 +51,7 @@ impl ExecutionGadget for SignedComparatorGadget { // The Signed Comparator gadget is used for both opcodes SLT and SGT. // Depending on whether the opcode is SLT or SGT, we // swap the order in which the inputs are placed on the stack. - let is_sgt = IsEqualGadget::construct(cb, opcode.expr(), OpcodeId::SGT.expr()); + let is_sgt = cb.is_eq(opcode.expr(), OpcodeId::SGT.expr()); // Both a and b are to be treated as two's complement signed 256-bit // (32 cells) integers. This means, the first bit denotes the sign @@ -59,8 +59,8 @@ impl ExecutionGadget for SignedComparatorGadget { // number is negative if the most significant cell >= 128 // (0b10000000). a and b being in the little-endian notation, the // most-significant byte is the last byte. - let sign_check_a = LtGadget::construct(cb, a.limbs[31].expr(), 128.expr()); - let sign_check_b = LtGadget::construct(cb, b.limbs[31].expr(), 128.expr()); + let sign_check_a = cb.is_lt(a.limbs[31].expr(), 128.expr()); + let sign_check_b = cb.is_lt(b.limbs[31].expr(), 128.expr()); // sign_check_a_lt expression implies a is positive since its MSB < 2**7 // sign_check_b_lt expression implies b is positive since its MSB < 2**7 @@ -77,7 +77,7 @@ impl ExecutionGadget for SignedComparatorGadget { // b_hi) && (a_lo < b_lo))) let (a_lo, a_hi) = a.to_word().to_lo_hi(); let (b_lo, b_hi) = b.to_word().to_lo_hi(); - let lt_lo = LtGadget::construct(cb, a_lo, b_lo); + let lt_lo = cb.is_lt(a_lo, b_lo); let comparison_hi = ComparisonGadget::construct(cb, a_hi, b_hi); let a_lt_b_lo = lt_lo.expr(); let (a_lt_b_hi, a_eq_b_hi) = comparison_hi.expr(); diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index fbf48b3d2c..093688bea0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -52,12 +52,11 @@ impl ExecutionGadget for SignextendGadget { // need to do any changes. So just sum all the non-LSB byte // values here and then check if it's non-zero so we can use // that as an additional condition to enable the selector. - let is_msb_sum_zero = IsZeroGadget::construct(cb, sum::expr(&index.limbs[1..32])); + let is_msb_sum_zero = cb.is_zero(sum::expr(&index.limbs[1..32])); // Check if this byte is selected looking only at the LSB of the index // word - let is_byte_selected = - array_init(|idx| IsEqualGadget::construct(cb, index.limbs[0].expr(), idx.expr())); + let is_byte_selected = array_init(|idx| cb.is_eq(index.limbs[0].expr(), idx.expr())); // We need to find the byte we have to get the sign from so we can // extend correctly. We go byte by byte and check if `idx == diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index 244e1595c0..ebcb7b52db 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -248,16 +248,14 @@ impl SstoreTxRefundGadget { value_prev: T, original_value: T, ) -> Self { - let value_prev_is_zero_gadget = IsZeroWordGadget::construct(cb, &value_prev.to_word()); - let value_is_zero_gadget = IsZeroWordGadget::construct(cb, &value.to_word()); - let original_is_zero_gadget = IsZeroWordGadget::construct(cb, &original_value.to_word()); - - let original_eq_value_gadget = - IsEqualWordGadget::construct(cb, &original_value.to_word(), &value.to_word()); - let prev_eq_value_gadget = - IsEqualWordGadget::construct(cb, &value_prev.to_word(), &value.to_word()); + let value_prev_is_zero_gadget = cb.is_zero_word(&value_prev.to_word()); + let value_is_zero_gadget = cb.is_zero_word(&value.to_word()); + let original_is_zero_gadget = cb.is_zero_word(&original_value.to_word()); + + let original_eq_value_gadget = cb.is_eq_word(&original_value.to_word(), &value.to_word()); + let prev_eq_value_gadget = cb.is_eq_word(&value_prev.to_word(), &value.to_word()); let original_eq_prev_gadget = - IsEqualWordGadget::construct(cb, &original_value.to_word(), &value_prev.to_word()); + cb.is_eq_word(&original_value.to_word(), &value_prev.to_word()); let value_prev_is_zero = value_prev_is_zero_gadget.expr(); let value_is_zero = value_is_zero_gadget.expr(); diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 420051b90c..0c323be986 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -385,7 +385,7 @@ impl TransferToGadget { mut reversion_info: Option<&mut ReversionInfo>, account_write: bool, ) -> Self { - let value_is_zero = IsZeroWordGadget::construct(cb, &value); + let value_is_zero = cb.is_zero_word(&value); if account_write { Self::create_account( cb, @@ -497,7 +497,7 @@ impl TransferWithGasFeeGadget { ) -> Self { let sender_sub_fee = UpdateBalanceGadget::construct(cb, sender_address.to_word(), vec![gas_fee], None); - let value_is_zero = IsZeroWordGadget::construct(cb, &value); + let value_is_zero = cb.is_zero_word(&value); // If receiver doesn't exist, create it TransferToGadget::create_account( cb, @@ -618,7 +618,7 @@ impl TransferGadget { value: Word32Cell, reversion_info: &mut ReversionInfo, ) -> Self { - let value_is_zero = IsZeroWordGadget::construct(cb, &value); + let value_is_zero = cb.is_zero_word(&value); // If receiver doesn't exist, create it TransferToGadget::create_account( cb, @@ -773,12 +773,12 @@ impl, const IS_SUCCESS_CAL }); // Recomposition of random linear combination to integer - let gas_is_u64 = IsZeroGadget::construct(cb, sum::expr(&gas_word.limbs[N_BYTES_GAS..])); + let gas_is_u64 = cb.is_zero(sum::expr(&gas_word.limbs[N_BYTES_GAS..])); let memory_expansion = MemoryExpansionGadget::construct(cb, [cd_address.address(), rd_address.address()]); // construct common gadget - let value_is_zero = IsZeroWordGadget::construct(cb, &value); + let value_is_zero = cb.is_zero_word(&value); let has_value = select::expr( is_delegatecall.expr() + is_staticcall.expr(), 0.expr(), @@ -791,9 +791,8 @@ impl, const IS_SUCCESS_CAL AccountFieldTag::CodeHash, callee_code_hash.to_word(), ); - let is_empty_code_hash = - IsEqualWordGadget::construct(cb, &callee_code_hash, &cb.empty_code_hash()); - let callee_not_exists = IsZeroWordGadget::construct(cb, &callee_code_hash); + let is_empty_code_hash = cb.is_eq_word(&callee_code_hash, &cb.empty_code_hash()); + let callee_not_exists = cb.is_zero_word(&callee_code_hash); Self { is_success, @@ -973,9 +972,9 @@ impl + Clone> SstoreGasGadget { value_prev: T, original_value: T, ) -> Self { - let value_eq_prev = IsEqualWordGadget::construct(cb, &value, &value_prev); - let original_eq_prev = IsEqualWordGadget::construct(cb, &original_value, &value_prev); - let original_is_zero = IsZeroWordGadget::construct(cb, &original_value); + let value_eq_prev = cb.is_eq_word(&value, &value_prev); + let original_eq_prev = cb.is_eq_word(&original_value, &value_prev); + let original_is_zero = cb.is_zero_word(&original_value); let warm_case_gas = select::expr( value_eq_prev.expr(), GasCost::WARM_ACCESS.expr(), @@ -1193,7 +1192,7 @@ impl WordByteCapGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder, cap: Expression) -> Self { let word = WordByteRangeGadget::construct(cb); let value = select::expr(word.overflow(), cap.expr(), word.valid_value()); - let lt_cap = LtGadget::construct(cb, value, cap); + let lt_cap = cb.is_lt(value, cap); Self { word, lt_cap } } @@ -1255,7 +1254,7 @@ impl WordByteRangeGadget { debug_assert!(VALID_BYTES < 32); let original = cb.query_word32(); - let not_overflow = IsZeroGadget::construct(cb, sum::expr(&original.limbs[VALID_BYTES..])); + let not_overflow = cb.is_zero(sum::expr(&original.limbs[VALID_BYTES..])); Self { original, diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index a38026c1ab..8d58c5b18e 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1,3 +1,10 @@ +use super::{ + math_gadget::{ + ConstantDivisionGadget, IsEqualGadget, IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, + LtGadget, LtWordGadget, MinMaxGadget, + }, + rlc, AccountAddress, CachedRegion, CellType, MemoryAddress, StoredExpression, U64Cell, +}; use crate::{ evm_circuit::{ param::STACK_CAPACITY, @@ -29,10 +36,6 @@ use halo2_proofs::{ }, }; -use super::{ - rlc, AccountAddress, CachedRegion, CellType, MemoryAddress, StoredExpression, U64Cell, -}; - // Max degree allowed in all expressions passing through the ConstraintBuilder. // It aims to cap `extended_k` to 2, which allows constraint degree to 2^2+1, // but each ExecutionGadget has implicit selector degree 3, so here it only @@ -581,6 +584,60 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { constrain!(log_id); } + // Math gadgets + + pub(crate) fn is_zero(&mut self, value: Expression) -> IsZeroGadget { + IsZeroGadget::construct(self, value) + } + + pub(crate) fn is_zero_word>(&mut self, value: &T) -> IsZeroWordGadget { + IsZeroWordGadget::construct(self, value) + } + + pub(crate) fn is_eq(&mut self, lhs: Expression, rhs: Expression) -> IsEqualGadget { + IsEqualGadget::construct(self, lhs, rhs) + } + + pub(crate) fn is_eq_word, T2: WordExpr>( + &mut self, + lhs: &T1, + rhs: &T2, + ) -> IsEqualWordGadget { + IsEqualWordGadget::construct(self, lhs, rhs) + } + + pub(crate) fn is_lt( + &mut self, + lhs: Expression, + rhs: Expression, + ) -> LtGadget { + LtGadget::construct(self, lhs, rhs) + } + + pub(crate) fn is_lt_word + Clone>( + &mut self, + lhs: &WordLoHi, + rhs: &WordLoHi, + ) -> LtWordGadget { + LtWordGadget::construct(self, lhs, rhs) + } + + pub(crate) fn min_max( + &mut self, + lhs: Expression, + rhs: Expression, + ) -> MinMaxGadget { + MinMaxGadget::construct(self, lhs, rhs) + } + + pub(crate) fn div_by_const( + &mut self, + numerator: Expression, + denominator: u64, + ) -> ConstantDivisionGadget { + ConstantDivisionGadget::construct(self, numerator, denominator) + } + // Fixed pub(crate) fn range_lookup(&mut self, value: Expression, range: u64) { diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs index 119e7d77a2..7778c33950 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/abs_word.rs @@ -38,7 +38,7 @@ impl AbsWordGadget { let sum = cb.query_word32(); let (x_lo, x_hi) = x.to_word().to_lo_hi(); let (x_abs_lo, x_abs_hi) = x_abs.to_word().to_lo_hi(); - let is_neg = LtGadget::construct(cb, 127.expr(), x.limbs[31].expr()); + let is_neg = cb.is_lt(127.expr(), x.limbs[31].expr()); cb.add_constraint( "x_abs_lo == x_lo when x >= 0", diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs index 5568ec54c4..af4d244f1d 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs @@ -21,8 +21,8 @@ impl ComparisonGadget { lhs: Expression, rhs: Expression, ) -> Self { - let lt = LtGadget::::construct(cb, lhs, rhs); - let eq = IsZeroGadget::::construct(cb, sum::expr(<.diff_bytes())); + let lt = cb.is_lt(lhs, rhs); + let eq = cb.is_zero(sum::expr(<.diff_bytes())); Self { lt, eq } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs index f0ddb64048..69c8c092b8 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs @@ -16,7 +16,7 @@ impl IsEqualGadget { lhs: Expression, rhs: Expression, ) -> Self { - let is_zero = IsZeroGadget::construct(cb, lhs - rhs); + let is_zero = cb.is_zero(lhs - rhs); Self { is_zero } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs index 75ebc05f85..b7bb201251 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal_word.rs @@ -14,13 +14,13 @@ use crate::{ util::word::{WordExpr, WordLoHi}, }; -use super::IsZeroGadget; +use super::IsEqualGadget; /// Returns `1` when `lhs == rhs`, and returns `0` otherwise. #[derive(Clone, Debug)] pub struct IsEqualWordGadget { - is_zero_lo: IsZeroGadget, - is_zero_hi: IsZeroGadget, + is_zero_lo: IsEqualGadget, + is_zero_hi: IsEqualGadget, _marker: PhantomData<(T1, T2)>, } @@ -28,8 +28,8 @@ impl, T2: WordExpr> IsEqualWordGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder, lhs: &T1, rhs: &T2) -> Self { let (lhs_lo, lhs_hi) = lhs.to_word().to_lo_hi(); let (rhs_lo, rhs_hi) = rhs.to_word().to_lo_hi(); - let is_zero_lo = IsZeroGadget::construct(cb, lhs_lo - rhs_lo); - let is_zero_hi = IsZeroGadget::construct(cb, lhs_hi - rhs_hi); + let is_zero_lo = cb.is_eq(lhs_lo, rhs_lo); + let is_zero_hi = cb.is_eq(lhs_hi, rhs_hi); Self { is_zero_lo, @@ -51,8 +51,8 @@ impl, T2: WordExpr> IsEqualWordGadget { ) -> Result { let (lhs_lo, lhs_hi) = lhs.to_lo_hi(); let (rhs_lo, rhs_hi) = rhs.to_lo_hi(); - self.is_zero_lo.assign(region, offset, lhs_lo - rhs_lo)?; - self.is_zero_hi.assign(region, offset, lhs_hi - rhs_hi)?; + self.is_zero_lo.assign(region, offset, lhs_lo, rhs_lo)?; + self.is_zero_hi.assign(region, offset, lhs_hi, rhs_hi)?; Ok(F::from(2)) } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs index c27825aa4a..b5499f2e1b 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs @@ -24,7 +24,7 @@ impl LtWordGadget { let (lhs_lo, lhs_hi) = lhs.to_lo_hi(); let (rhs_lo, rhs_hi) = rhs.to_lo_hi(); let comparison_hi = ComparisonGadget::construct(cb, lhs_hi.expr(), rhs_hi.expr()); - let lt_lo = LtGadget::construct(cb, lhs_lo.expr(), rhs_lo.expr()); + let lt_lo = cb.is_lt(lhs_lo.expr(), rhs_lo.expr()); Self { comparison_hi, lt_lo, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs index ee6b101ab8..f5201d40e1 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs @@ -19,7 +19,7 @@ impl MinMaxGadget { lhs: Expression, rhs: Expression, ) -> Self { - let lt = LtGadget::construct(cb, lhs.clone(), rhs.clone()); + let lt = cb.is_lt(lhs.clone(), rhs.clone()); let max = select::expr(lt.expr(), rhs.clone(), lhs.clone()); let min = select::expr(lt.expr(), lhs, rhs); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index 3409c4861d..28779640cf 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -36,11 +36,11 @@ impl ModGadget { let (a, n, r) = (words[0], words[1], words[2]); let k = cb.query_word32(); let a_or_zero = cb.query_word32(); - let n_is_zero = IsZeroWordGadget::construct(cb, n); - let a_or_is_zero = IsZeroWordGadget::construct(cb, &a_or_zero); + let n_is_zero = cb.is_zero_word(n); + let a_or_is_zero = cb.is_zero_word(&a_or_zero); let mul_add_words = MulAddWordsGadget::construct(cb, [&k, n, r, &a_or_zero]); - let eq = IsEqualWordGadget::construct(cb, a, &a_or_zero); - let lt = LtWordGadget::construct(cb, &r.to_word(), &n.to_word()); + let eq = cb.is_eq_word(a, &a_or_zero); + let lt = cb.is_lt_word(&r.to_word(), &n.to_word()); // Constrain the aux variable a_or_zero to be =a or =0 if n==0: // (a == a_or_zero) ^ (n == 0 & a_or_zero == 0) cb.add_constraint( 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 6883049b48..352a832c32 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -48,7 +48,7 @@ impl RlpU64Gadget { .zip(&is_most_significant_byte) .map(|(byte, indicator)| byte.expr() * indicator.expr()), ); - let most_significant_byte_is_zero = IsZeroGadget::construct(cb, most_significant_byte); + let most_significant_byte_is_zero = cb.is_zero(most_significant_byte); let is_lt_128 = cb.query_bool(); let value = expr_from_bytes(&value_rlc.cells); diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index eef9446fe3..3d13fb37a6 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -85,7 +85,7 @@ impl MemoryAddressGadget { memory_offset: WordLoHiCell, memory_length: MemoryAddress, ) -> Self { - let memory_length_is_zero = IsZeroGadget::construct(cb, memory_length.sum_expr()); + let memory_length_is_zero = cb.is_zero(memory_length.sum_expr()); let memory_offset_bytes = cb.query_memory_address(); let has_length = 1.expr() - memory_length_is_zero.expr(); @@ -196,16 +196,15 @@ impl CommonMemoryAddressGadget for MemoryExpandedAddressGadget { let length = cb.query_word32(); let sum = cb.query_word32(); - let sum_lt_cap = LtGadget::construct( - cb, + let sum_lt_cap = cb.is_lt( from_bytes::expr(&sum.limbs[..N_BYTES_U64]), (MAX_EXPANDED_MEMORY_ADDRESS + 1).expr(), ); let sum_overflow_hi = sum::expr(&sum.limbs[N_BYTES_U64..]); - let sum_within_u64 = IsZeroGadget::construct(cb, sum_overflow_hi); + let sum_within_u64 = cb.is_zero(sum_overflow_hi); - let length_is_zero = IsZeroGadget::construct(cb, sum::expr(&length.limbs)); + let length_is_zero = cb.is_zero(sum::expr(&length.limbs)); let offset_length_sum = AddWordsGadget::construct(cb, [offset, length], sum); Self { @@ -340,7 +339,7 @@ pub(crate) struct MemoryWordSizeGadget { impl MemoryWordSizeGadget { pub(crate) fn construct(cb: &mut EVMConstraintBuilder, address: Expression) -> Self { - let memory_word_size = ConstantDivisionGadget::construct(cb, address + 31.expr(), 32); + let memory_word_size = cb.div_by_const(address + 31.expr(), 32); Self { memory_word_size } } @@ -613,7 +612,7 @@ impl let is_empty = not::expr(&selectors[0]); let cap = select::expr(is_empty.expr(), 0.expr(), MAX_BYTES.expr()); let signed_len = addr_end - addr_start; - let min_gadget = MinMaxGadget::construct(cb, cap, signed_len); + let min_gadget = cb.min_max(cap, signed_len); // If we claim that the buffer is empty, we prove that the end is at or before the start. // buffer_len = max(0, signed_len) = 0 diff --git a/zkevm-circuits/src/evm_circuit/util/tx.rs b/zkevm-circuits/src/evm_circuit/util/tx.rs index 0ac5cc4a01..d99b029ffd 100644 --- a/zkevm-circuits/src/evm_circuit/util/tx.rs +++ b/zkevm-circuits/src/evm_circuit/util/tx.rs @@ -81,7 +81,7 @@ impl EndTxHelperGadget { gas_used: Expression, num_rw: Expression, ) -> Self { - let is_first_tx = IsEqualGadget::construct(cb, tx_id.expr(), 1.expr()); + let is_first_tx = cb.is_eq(tx_id.expr(), 1.expr()); // Constrain tx receipt fields cb.tx_receipt_lookup( @@ -240,8 +240,7 @@ impl TxDataGadget { // Calculate transaction gas fee let mul_gas_fee_by_gas = MulWordByU64Gadget::construct(cb, gas_price.clone(), gas.expr()); - let call_data_word_length = - ConstantDivisionGadget::construct(cb, call_data_length.expr() + 31.expr(), 32); + let call_data_word_length = cb.div_by_const(call_data_length.expr() + 31.expr(), 32); let (cost_sum, gas_mul_gas_price_plus_value) = if calculate_total_cost { let cost_sum = cb.query_word32(); From db0d403b1a5ccc55c67a957f9fc8c240c1cafe78 Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Sat, 17 Feb 2024 01:03:31 +0800 Subject: [PATCH 02/16] Upgrade rust version (#1769) ### Description We've been using the version one year ago. Let's get a new year vibe. ### Issue Link ### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update - [ ] Refactor (no updates to logic) ### Contents Major clippy fix - Clippy now detect useless vector. It is a little bit [buggy](https://github.com/rust-lang/rust-clippy/issues/12101), and it sometimes complains without giving the exact line. --- Cargo.lock | 242 +++++++++--------- .../src/circuit_input_builder/access.rs | 4 +- .../circuit_input_builder/input_state_ref.rs | 4 +- bus-mapping/src/precompile.rs | 5 +- eth-types/src/bytecode.rs | 14 +- eth-types/src/evm_types.rs | 3 +- eth-types/src/lib.rs | 6 +- eth-types/src/sign_types.rs | 4 +- geth-utils/src/mpt.rs | 2 +- rust-toolchain | 2 +- testool/src/compiler.rs | 1 - testool/src/statetest/results.rs | 2 - testool/src/statetest/yaml.rs | 11 +- testool/src/utils.rs | 16 +- zkevm-circuits/src/bytecode_circuit.rs | 2 +- .../src/circuit_tools/cell_manager.rs | 2 +- .../src/circuit_tools/constraint_builder.rs | 4 +- zkevm-circuits/src/copy_circuit.rs | 12 +- zkevm-circuits/src/evm_circuit/execution.rs | 2 +- .../execution/error_oog_precompile.rs | 2 +- .../src/evm_circuit/execution/mulmod.rs | 6 +- .../src/evm_circuit/execution/shl_shr.rs | 4 +- .../src/evm_circuit/execution/signextend.rs | 2 +- zkevm-circuits/src/evm_circuit/step.rs | 4 +- .../evm_circuit/util/constraint_builder.rs | 3 +- .../evm_circuit/util/math_gadget/add_words.rs | 12 +- .../evm_circuit/util/math_gadget/byte_size.rs | 12 +- .../evm_circuit/util/math_gadget/cmp_words.rs | 18 +- .../util/math_gadget/comparison.rs | 20 +- .../util/math_gadget/constant_division.rs | 12 +- .../evm_circuit/util/math_gadget/is_equal.rs | 10 +- .../evm_circuit/util/math_gadget/is_zero.rs | 10 +- .../src/evm_circuit/util/math_gadget/lt.rs | 8 +- .../evm_circuit/util/math_gadget/lt_word.rs | 24 +- .../evm_circuit/util/math_gadget/min_max.rs | 24 +- .../evm_circuit/util/math_gadget/modulo.rs | 26 +- .../util/math_gadget/mul_add_words.rs | 26 +- .../util/math_gadget/mul_add_words512.rs | 16 +- .../util/math_gadget/mul_word_u64.rs | 18 +- .../util/math_gadget/pair_select.rs | 18 +- .../util/math_gadget/range_check.rs | 8 +- .../src/evm_circuit/util/math_gadget/rlp.rs | 2 +- .../src/evm_circuit/util/memory_gadget.rs | 12 +- zkevm-circuits/src/exp_circuit.rs | 2 +- zkevm-circuits/src/mpt_circuit/helpers.rs | 2 +- .../src/mpt_circuit/mod_extension.rs | 6 +- zkevm-circuits/src/mpt_circuit/start.rs | 2 +- .../src/mpt_circuit/storage_leaf.rs | 2 +- zkevm-circuits/src/pi_circuit.rs | 4 +- zkevm-circuits/src/state_circuit.rs | 4 +- .../state_circuit/lexicographic_ordering.rs | 8 +- zkevm-circuits/src/state_circuit/lookups.rs | 1 - zkevm-circuits/src/state_circuit/test.rs | 6 +- zkevm-circuits/src/table/tx_table.rs | 2 +- zkevm-circuits/src/tx_circuit/sign_verify.rs | 2 +- zkevm-circuits/src/witness/rw.rs | 2 +- 56 files changed, 335 insertions(+), 343 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be30d586ab..ab96dd69af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", @@ -182,14 +182,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "823b8bb275161044e2ac7a25879cb3e2480cb403e3943022c7c769c599b756aa" dependencies = [ - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -427,9 +426,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" dependencies = [ "serde", ] @@ -466,9 +465,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "num-traits", ] @@ -629,9 +628,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5104de16b218eddf8e34ffe2f86f74bfa4e61e95a1b89732fccf6325efd0557" +checksum = "18d59688ad0945eaf6b84cb44fedbe93484c81b48970e98f09db8a22832d7961" dependencies = [ "cfg-if", "cpufeatures", @@ -691,9 +690,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -1000,9 +999,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" @@ -1465,9 +1464,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1781,7 +1780,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1806,9 +1805,9 @@ dependencies = [ [[package]] name = "halo2curves" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3675880dc0cc7cd468943266297198a28f88210ba60ca5e0e04d121edf86b46" +checksum = "db81d01d0bbfec9f624d7590fc6929ee2537a64ec1e080d8f8c9e2d2da291405" dependencies = [ "blake2b_simd", "ff", @@ -1893,9 +1892,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -2103,9 +2102,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2178,12 +2177,12 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.4", - "rustix", + "hermit-abi 0.3.6", + "libc", "windows-sys 0.52.0", ] @@ -2213,18 +2212,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -2305,9 +2304,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -2439,9 +2438,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -2528,28 +2527,33 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -2570,9 +2574,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -2584,7 +2588,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.4", + "hermit-abi 0.3.6", "libc", ] @@ -2829,9 +2833,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" dependencies = [ "memchr", "thiserror", @@ -2840,9 +2844,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" dependencies = [ "pest", "pest_generator", @@ -2850,9 +2854,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" dependencies = [ "pest", "pest_meta", @@ -2863,9 +2867,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.6" +version = "2.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" dependencies = [ "once_cell", "pest", @@ -2879,7 +2883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.1.0", + "indexmap 2.2.3", ] [[package]] @@ -2945,18 +2949,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", @@ -2987,9 +2991,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "poseidon" @@ -3091,7 +3095,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.21.1", ] [[package]] @@ -3251,9 +3255,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -3274,9 +3278,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64 0.21.7", "bytes", @@ -3300,6 +3304,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -3477,9 +3482,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -3682,18 +3687,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -3702,9 +3707,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", @@ -4019,9 +4024,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svm-rs" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20689c7d03b6461b502d0b95d6c24874c7d24dea2688af80486a130a06af3b07" +checksum = "11297baafe5fa0c99d5722458eac6a5e25c01eb1b8e5cd137f54079093daa7a4" dependencies = [ "dirs", "fs2", @@ -4059,6 +4064,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4088,13 +4099,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] @@ -4162,18 +4172,18 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -4182,12 +4192,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -4202,10 +4213,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -4235,9 +4247,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -4336,7 +4348,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", @@ -4349,18 +4361,18 @@ version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -4585,9 +4597,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4595,9 +4607,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -4610,9 +4622,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -4622,9 +4634,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4632,9 +4644,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -4645,15 +4657,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -4661,9 +4673,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "winapi" @@ -4830,9 +4842,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] diff --git a/bus-mapping/src/circuit_input_builder/access.rs b/bus-mapping/src/circuit_input_builder/access.rs index 1626d6f97a..086396e749 100644 --- a/bus-mapping/src/circuit_input_builder/access.rs +++ b/bus-mapping/src/circuit_input_builder/access.rs @@ -72,7 +72,7 @@ impl From> for AccessSet { for access in list { match access.value { AccessValue::Account { address } => { - state.entry(address).or_insert_with(HashSet::new); + state.entry(address).or_default(); } AccessValue::Storage { address, key } => match state.entry(address) { Entry::Vacant(entry) => { @@ -85,7 +85,7 @@ impl From> for AccessSet { } }, AccessValue::Code { address } => { - state.entry(address).or_insert_with(HashSet::new); + state.entry(address).or_default(); code.insert(address); } } 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 38c0db5e30..3b5b74c1e8 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -1225,7 +1225,7 @@ impl<'a> CircuitInputStateRef<'a> { ) -> Result<(), Error> { let call = self.call()?.clone(); let geth_step = steps - .get(0) + .first() .ok_or(Error::InternalError("invalid index 0"))?; let is_revert_or_return_call_success = (geth_step.op == OpcodeId::REVERT || geth_step.op == OpcodeId::RETURN) @@ -1419,7 +1419,7 @@ impl<'a> CircuitInputStateRef<'a> { let call = self.call()?; - if matches!(next_step, None) { + if next_step.is_none() { // enumerating call scope successful cases // case 1: call with normal halt opcode termination if matches!( diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 2792fa4d5b..9b43d844a9 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -36,8 +36,9 @@ pub(crate) fn execute_precompiled( #[cfg(not(target_arch = "wasm32"))] { - let Some(Precompile::Standard(precompile_fn)) = Precompiles::berlin() - .get(address.as_fixed_bytes()) else { + let Some(Precompile::Standard(precompile_fn)) = + Precompiles::berlin().get(address.as_fixed_bytes()) + else { panic!("calling non-exist precompiled contract address") }; let (return_data, gas_cost, is_oog, is_ok) = match precompile_fn(input, gas) { diff --git a/eth-types/src/bytecode.rs b/eth-types/src/bytecode.rs index 627ff10eae..09160e274e 100644 --- a/eth-types/src/bytecode.rs +++ b/eth-types/src/bytecode.rs @@ -1,7 +1,6 @@ //! EVM byte code generator use crate::{evm_types::OpcodeId, keccak256, Bytes, Hash, ToBigEndian, ToWord, Word}; -use std::{collections::HashMap, iter, str::FromStr}; - +use std::{collections::HashMap, fmt::Display, iter, str::FromStr}; /// Error type for Bytecode related failures #[derive(Debug)] pub enum Error { @@ -254,12 +253,13 @@ impl FromStr for OpcodeWithData { } } -impl ToString for OpcodeWithData { - fn to_string(&self) -> String { - match self { +impl Display for OpcodeWithData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { OpcodeWithData::Opcode(opcode) => format!("{:?}", opcode), OpcodeWithData::PushWithData(n, word) => format!("PUSH{}({})", n, word), - } + }; + f.write_str(&str) } } @@ -576,6 +576,6 @@ mod tests { POP STOP }; - assert_eq!(Bytecode::try_from(code.code()).unwrap(), code); + assert_eq!(Bytecode::from(code.code()), code); } } diff --git a/eth-types/src/evm_types.rs b/eth-types/src/evm_types.rs index d9fa5ec40b..39adaa4a17 100644 --- a/eth-types/src/evm_types.rs +++ b/eth-types/src/evm_types.rs @@ -34,8 +34,7 @@ mod gas_create { // [gasCreate2Eip3860](https://github.com/ethereum/go-ethereum/blob/eb83e7c54021573eaceb14236af3a7a8c64f6027/core/vm/gas_table.go#L321) // (similar for CREATE). // 1. size <= 49152 (MaxInitCodeSize) - // 2. gasCost = memoryGasCost + (2 + 6) * ((size + 31) / 32) should not - // overflow for Uint64. + // 2. gasCost = memoryGasCost + (2 + 6) * ((size + 31) / 32) should not overflow for Uint64. // No need to constrain the second condition, since the maximum gas cost // cannot overflow for Uint64 (36028809887100925 calculated by // `memorySize = 0x1FFFFFFFE0` and `size = 49152`) if the first condition is diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index afa4569187..81ec46560b 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -8,7 +8,11 @@ #![deny(missing_docs)] //#![deny(unsafe_code)] Allowed now until we find a // better way to handle downcasting from Operation into it's variants. -#![allow(clippy::upper_case_acronyms)] // Too pedantic + +// Too pedantic +#![allow(clippy::upper_case_acronyms)] +// Clippy is buggy on this one. Remove after https://github.com/rust-lang/rust-clippy/issues/12101 is resolved. +#![allow(clippy::useless_vec)] #[macro_use] pub mod macros; diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index c93245af29..696b57bb6b 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -135,9 +135,7 @@ pub fn ct_option_ok_or(v: CtOption, err: E) -> Result { /// Return a copy of the serialized public key with swapped Endianness. pub fn pk_bytes_swap_endianness(pk: &[T]) -> [T; 64] { assert_eq!(pk.len(), 64); - let mut pk_swap = <&[T; 64]>::try_from(pk) - .map(|r| r.clone()) - .expect("pk.len() != 64"); + let mut pk_swap = <&[T; 64]>::try_from(pk).cloned().expect("pk.len() != 64"); pk_swap[..32].reverse(); pk_swap[32..].reverse(); pk_swap diff --git a/geth-utils/src/mpt.rs b/geth-utils/src/mpt.rs index 4fe0f3aa4c..46c60d0e65 100644 --- a/geth-utils/src/mpt.rs +++ b/geth-utils/src/mpt.rs @@ -86,7 +86,7 @@ pub fn get_witness(block_no: u64, mods: &[TrieModification], node_url: &str) -> let json = serde_json::to_string(&req).expect("Invalid request"); let c_config = CString::new(json).expect("invalid config"); - let result = unsafe { go::GetMptWitness(c_config.as_ptr() as *const i8) }; + let result = unsafe { go::GetMptWitness(c_config.as_ptr()) }; let c_str = unsafe { CStr::from_ptr(result) }; let json = c_str .to_str() diff --git a/rust-toolchain b/rust-toolchain index b6ce6a50ba..c5f610378b 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2023-04-24 +nightly-2024-02-14 diff --git a/testool/src/compiler.rs b/testool/src/compiler.rs index 53296cab3c..7f03d039f4 100644 --- a/testool/src/compiler.rs +++ b/testool/src/compiler.rs @@ -47,7 +47,6 @@ impl Cache { let entry = format!("{}={}\n", hex::encode(code_hash), hex::encode(&bytecode)); std::fs::OpenOptions::new() .read(true) - .write(true) .create(true) .append(true) .open(&self.path)? diff --git a/testool/src/statetest/results.rs b/testool/src/statetest/results.rs index 98b0fa5247..e07e74c383 100644 --- a/testool/src/statetest/results.rs +++ b/testool/src/statetest/results.rs @@ -392,7 +392,6 @@ impl Results { if let Some(path) = &self.cache { let mut file = std::fs::OpenOptions::new() .read(true) - .write(true) .create(true) .append(true) .open(path)?; @@ -441,7 +440,6 @@ impl Results { if let Some(path) = &self.cache { std::fs::OpenOptions::new() .read(true) - .write(true) .create(true) .append(true) .open(path)? diff --git a/testool/src/statetest/yaml.rs b/testool/src/statetest/yaml.rs index 3fe0b2eba3..967e4ec8e8 100644 --- a/testool/src/statetest/yaml.rs +++ b/testool/src/statetest/yaml.rs @@ -410,6 +410,8 @@ impl<'a> YamlStateTestBuilder<'a> { #[cfg(test)] mod test { + use std::fmt::Display; + use super::*; use crate::{ config::TestSuite, @@ -524,9 +526,9 @@ arith: } } } - impl ToString for Template { - fn to_string(&self) -> String { - TEMPLATE + impl Display for Template { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = TEMPLATE .replace("{{ gas_limit }}", &self.gas_limit) .replace("{{ pre_code }}", &self.pre_code) .replace("{{ res_storage }}", &self.res_storage) @@ -540,7 +542,8 @@ arith: } else { "Istanbul" }, - ) + ); + f.write_str(&str) } } diff --git a/testool/src/utils.rs b/testool/src/utils.rs index e4e4d647ba..fca65d0de0 100644 --- a/testool/src/utils.rs +++ b/testool/src/utils.rs @@ -181,18 +181,12 @@ pub fn current_submodule_git_commit() -> Result { pub fn bytecode_of(code: &str) -> anyhow::Result { let bytecode = if let Ok(bytes) = hex::decode(code) { - match Bytecode::try_from(bytes.clone()) { - Ok(bytecode) => { - for op in bytecode.iter() { - info!("{}", op.to_string()); - } - bytecode - } - Err(err) => { - error!("Failed to parse bytecode {:?}", err); - Bytecode::from_raw_unchecked(bytes) - } + let bytecode = Bytecode::from(bytes.clone()); + + for op in bytecode.iter() { + info!("{}", op.to_string()); } + bytecode } else { let mut bytecode = Bytecode::default(); for op in code.split(',') { diff --git a/zkevm-circuits/src/bytecode_circuit.rs b/zkevm-circuits/src/bytecode_circuit.rs index 5cd71e9b72..b02f233868 100644 --- a/zkevm-circuits/src/bytecode_circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit.rs @@ -347,7 +347,7 @@ impl SubCircuitConfig for BytecodeCircuitConfig { is_byte(meta), ]); - let lookup_columns = vec![value, push_data_size]; + let lookup_columns = [value, push_data_size]; let mut constraints = vec![]; diff --git a/zkevm-circuits/src/circuit_tools/cell_manager.rs b/zkevm-circuits/src/circuit_tools/cell_manager.rs index 4063e24ca1..5dae4ef2af 100644 --- a/zkevm-circuits/src/circuit_tools/cell_manager.rs +++ b/zkevm-circuits/src/circuit_tools/cell_manager.rs @@ -239,7 +239,7 @@ impl Eq for CellColumn {} impl PartialOrd for CellColumn { fn partial_cmp(&self, other: &Self) -> Option { - self.height.partial_cmp(&other.height) + Some(self.cmp(other)) } } diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 3c1e8126e6..814b2eaa0f 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -322,7 +322,7 @@ impl ConstraintBuilder { pub(crate) fn query_default(&mut self) -> Cell { self.query_cells_dyn(C::default(), 1) - .get(0) + .first() .expect("No cell found") .clone() } @@ -536,7 +536,7 @@ impl ConstraintBuilder { )); self.stored_expressions .entry(self.region_id) - .or_insert_with(Vec::new) + .or_default() .push(StoredExpression { name, cell: cell.clone(), diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 7e9cc18759..489dfbcca2 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -363,7 +363,7 @@ impl SubCircuitConfig for CopyCircuitConfig { 0.expr(), // init_val_hi ] .into_iter() - .zip_eq(rw_table.table_exprs(meta).into_iter()) + .zip_eq(rw_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -388,7 +388,7 @@ impl SubCircuitConfig for CopyCircuitConfig { 0.expr(), // init_val_hi ] .into_iter() - .zip_eq(rw_table.table_exprs(meta).into_iter()) + .zip_eq(rw_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -406,7 +406,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(value, Rotation::cur()), ] .into_iter() - .zip_eq(bytecode_table.table_exprs(meta).into_iter()) + .zip_eq(bytecode_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -423,7 +423,7 @@ impl SubCircuitConfig for CopyCircuitConfig { meta.query_advice(value, Rotation::cur()), ] .into_iter() - .zip(tx_table.table_exprs(meta).into_iter()) + .zip(tx_table.table_exprs(meta)) .map(|(arg, table)| (cond.clone() * arg, table)) .collect() }); @@ -807,7 +807,7 @@ impl CopyCircuit { Self { copy_events, max_copy_rows, - _marker: PhantomData::default(), + _marker: PhantomData, external_data: ExternalData::default(), } } @@ -821,7 +821,7 @@ impl CopyCircuit { Self { copy_events, max_copy_rows, - _marker: PhantomData::default(), + _marker: PhantomData, external_data, } } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 87595ef6af..1f7ed1c19e 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -974,7 +974,7 @@ impl ExecutionConfig { .txs .last() .map(|tx| tx.calls()[0].clone()) - .unwrap_or_else(Call::default); + .unwrap_or_default(); let end_block_not_last = &block.end_block_not_last; let end_block_last = &block.end_block_last; // Collect all steps diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs index 4dd3df7ca8..5242dbc363 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs @@ -63,7 +63,7 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { }); // calculate required gas for precompile - let precompiles_required_gas = vec![ + let precompiles_required_gas = [ // ( // addr_bits.value_equals(PrecompileCalls::ECRecover), // GasCost::PRECOMPILE_ECRECOVER_BASE.expr(), diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index d2d021c6f7..65bf39dd28 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -60,13 +60,13 @@ impl ExecutionGadget for MulModGadget { let d = cb.query_word32(); let e = cb.query_word32(); - // 1. k1 * n + a_reduced == a + // 1. k1 * n + a_reduced == a let modword = ModGadget::construct(cb, [&a, &n, &a_reduced]); - // 2. a_reduced * b + 0 == d * 2^256 + e + // 2. a_reduced * b + 0 == d * 2^256 + e let mul512_left = MulAddWords512Gadget::construct(cb, [&a_reduced, &b, &d, &e], None); - // 3. k2 * n + r == d * 2^256 + e + // 3. k2 * n + r == d * 2^256 + e let mul512_right = MulAddWords512Gadget::construct(cb, [&k, &n, &d, &e], Some(&r)); // (r < n ) or n == 0 diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index ef822f0074..5dba3de3e4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -179,9 +179,7 @@ impl ExecutionGadget for ShlShrGadget { let shf_lt256 = pop1 .to_le_bytes() .iter() - .fold(Some(0_u64), |acc, val| { - acc.and_then(|acc| acc.checked_add(u64::from(*val))) - }) + .try_fold(0u64, |acc, val| acc.checked_add(u64::from(*val))) .unwrap() - shf0; let divisor = if shf_lt256 == 0 { diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index 093688bea0..faec033ad9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -280,7 +280,7 @@ mod test { let pos_extend = 0u8; let neg_extend = 0xFFu8; - for (value, byte_extend) in vec![(pos_value, pos_extend), (neg_value, neg_extend)].iter() { + for (value, byte_extend) in [(pos_value, pos_extend), (neg_value, neg_extend)].iter() { for idx in 0..33 { test_ok( (idx as u64).into(), diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index bc301f2076..b17b7619eb 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -29,7 +29,7 @@ use halo2_proofs::{ circuit::Value, plonk::{Advice, Column, ConstraintSystem, Error, Expression}, }; -use std::{fmt::Display, iter}; +use std::{fmt::Display, iter, marker::ConstParamTy}; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -50,7 +50,7 @@ impl From for ExecutionState { } #[allow(non_camel_case_types, missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, EnumIter, ConstParamTy)] /// All the possible execution states that the computation of EVM can arrive. /// Some states are shared by multiple opcodes. pub enum ExecutionState { diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 8d58c5b18e..faa52eb515 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -207,7 +207,8 @@ pub(crate) trait ConstrainBuilderCommon { .fold(1.expr(), |acc, item| acc * (value.clone() - item.clone())), ); } - + /// Under active development + #[allow(dead_code)] fn add_constraints(&mut self, constraints: Vec<(&'static str, Expression)>) { for (name, constraint) in constraints { self.add_constraint(name, constraint); diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs index 8ad2e86284..0978d32ab8 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/add_words.rs @@ -221,27 +221,27 @@ mod tests { #[test] fn test_addwords_0_0() { - try_test!(AddWordsTestContainer, vec![Word::from(0), Word::from(0), Word::from(0)], true); + try_test!(AddWordsTestContainer, [Word::from(0), Word::from(0), Word::from(0)], true); } #[test] fn test_addwords_1_1() { - try_test!(AddWordsTestContainer, vec![Word::from(1), Word::from(1), Word::from(2)], true); + try_test!(AddWordsTestContainer, [Word::from(1), Word::from(1), Word::from(2)], true); } #[test] fn test_addwords_1000_1000() { - try_test!(AddWordsTestContainer, vec![Word::from(1000), Word::from(1000), Word::from(2000)], true); + try_test!(AddWordsTestContainer, [Word::from(1000), Word::from(1000), Word::from(2000)], true); } #[test] fn test_addwords_to_wordmax() { - try_test!(AddWordsTestContainer, vec![Word::MAX - 1, Word::from(1), Word::MAX], true); + try_test!(AddWordsTestContainer, [Word::MAX - 1, Word::from(1), Word::MAX], true); } #[test] fn test_addwords_high_low_max() { - try_test!(AddWordsTestContainer, vec![WORD_LOW_MAX, WORD_HIGH_MAX, Word::MAX], true); + try_test!(AddWordsTestContainer, [WORD_LOW_MAX, WORD_HIGH_MAX, Word::MAX], true); } #[test] @@ -291,7 +291,7 @@ mod tests { let sum_7_low_max = U256([0xfffffffffffffff9u64, 0xffffffffffffffffu64, CARRY_HI, 0u64]); try_test!( AddWordsTestContainer, - vec![ + [ WORD_LOW_MAX, WORD_LOW_MAX, WORD_LOW_MAX, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs index 1d82f26f0d..34f86127ec 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/byte_size.rs @@ -86,7 +86,7 @@ impl ByteSizeGadget { region, offset, Value::known( - F::from(u64::try_from(most_significant_nonzero_byte).unwrap()) + F::from(u64::from(most_significant_nonzero_byte)) .invert() .unwrap(), ), @@ -157,18 +157,18 @@ mod tests { #[test] fn test_bytesize_0() { - try_test!(ByteSizeGadgetContainer, vec![Word::from(0)], true) + try_test!(ByteSizeGadgetContainer, [Word::from(0)], true) } #[test] fn test_bytesize_1() { - try_test!(ByteSizeGadgetContainer, vec![Word::from(1)], true) + try_test!(ByteSizeGadgetContainer, [Word::from(1)], true) } #[test] fn test_bytesize_1_neq_0() { try_test!(ByteSizeGadgetContainer, - vec![Word::from(1)], + [Word::from(1)], false ); } @@ -176,13 +176,13 @@ mod tests { #[test] fn test_bytesize_256_eq_2() { try_test!(ByteSizeGadgetContainer, - vec![Word::from(256)], + [Word::from(256)], true ); } #[test] fn test_bytesize_wordmax_eq_32() { - try_test!(ByteSizeGadgetContainer, vec![Word::MAX], true) + try_test!(ByteSizeGadgetContainer, [Word::MAX], true) } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs index 44fc4248b0..c7b3e0288e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/cmp_words.rs @@ -141,7 +141,7 @@ mod tests { // a == b check try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -150,7 +150,7 @@ mod tests { fn test_cmpword_1_eq() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -159,7 +159,7 @@ mod tests { fn test_cmpword_wordmax_eq() { try_test!( CmpWordGadgetTestContainer, - vec![Word::MAX, Word::MAX], + [Word::MAX, Word::MAX], true, ); } @@ -168,7 +168,7 @@ mod tests { fn test_cmpword_0_neq_wordmax() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::MAX], + [Word::from(0), Word::MAX], false, ); } @@ -178,7 +178,7 @@ mod tests { fn test_cmpword_0_lt_1() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); } @@ -187,7 +187,7 @@ mod tests { fn test_cmpword_1_lt_wordmax() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::MAX], + [Word::from(1), Word::MAX], true, ); } @@ -196,7 +196,7 @@ mod tests { fn test_cmpword_1_lt_0() { try_test!( CmpWordGadgetTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -205,7 +205,7 @@ mod tests { fn test_cmpword_lowmax_lt_highmax() { try_test!( CmpWordGadgetTestContainer, - vec![WORD_LOW_MAX, WORD_HIGH_MAX], + [WORD_LOW_MAX, WORD_HIGH_MAX], true, ); } @@ -214,7 +214,7 @@ mod tests { fn test_cmpword_highmax_lt_lowmax() { try_test!( CmpWordGadgetTestContainer, - vec![WORD_HIGH_MAX, WORD_LOW_MAX], + [WORD_HIGH_MAX, WORD_LOW_MAX], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs index af4d244f1d..60e4e4ea5d 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/comparison.rs @@ -107,7 +107,7 @@ mod tests { // a == b check try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -116,7 +116,7 @@ mod tests { fn test_comparison_1_eq() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -125,7 +125,7 @@ mod tests { fn test_comparison_max_eq() { try_test!( ComparisonTestContainer, - vec![Word::from(1 << 4), Word::from(1 << 4)], + [Word::from(1 << 4), Word::from(1 << 4)], true, ); } @@ -134,7 +134,7 @@ mod tests { fn test_comparison_0_neq_max() { try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(1 << 4)], + [Word::from(0), Word::from(1 << 4)], false, ); } @@ -144,7 +144,7 @@ mod tests { fn test_comparison_0_lt_1() { try_test!( ComparisonTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); } @@ -153,7 +153,7 @@ mod tests { fn test_comparison_1_lt_max() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(1 << 4)], + [Word::from(1), Word::from(1 << 4)], true, ); } @@ -162,7 +162,7 @@ mod tests { fn test_comparison_1_lt_0() { try_test!( ComparisonTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -174,13 +174,13 @@ mod tests { let half_max_hi = U256([0, u64::MAX, 0, 0]); try_test!( ComparisonTestContainer, - vec![half_max_lo, half_max_hi], + [half_max_lo, half_max_hi], true, ); try_test!( ComparisonTestContainer, - vec![half_max_hi, half_max_lo], + [half_max_hi, half_max_lo], false, ); } @@ -189,7 +189,7 @@ mod tests { fn test_comparison_overflow() { try_test!( ComparisonTestContainer, - vec![Word::from(10000), Word::from(1 << (4 + 1))], + [Word::from(10000), Word::from(1 << (4 + 1))], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs index dff6e51a6b..ed32eae807 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/constant_division.rs @@ -154,7 +154,7 @@ mod tests { fn test_constantdivisiongadget_0div5_rem0() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(0)], + [Word::from(0)], true, ); } @@ -163,7 +163,7 @@ mod tests { fn test_constantdivisiongadget_5div5_rem0() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(5)], + [Word::from(5)], true, ); } @@ -172,7 +172,7 @@ mod tests { fn test_constantdivisiongadget_1div5_rem1() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1)], + [Word::from(1)], true, ); } @@ -181,7 +181,7 @@ mod tests { fn test_constantdivisiongadget_1div5_rem4() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1)], + [Word::from(1)], false, ); } @@ -190,7 +190,7 @@ mod tests { fn test_constantdivisiongadget_quotient_overflow() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(1u64 << (4 * 8)) * 5 + 1], + [Word::from(1u64 << (4 * 8)) * 5 + 1], false, ); } @@ -199,7 +199,7 @@ mod tests { fn test_constantdivisiongadget_33_div16_rem17() { try_test!( ConstantDivisionTestContainer, - vec![Word::from(33)], + [Word::from(33)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs index 69c8c092b8..8e26ccf089 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_equal.rs @@ -83,7 +83,7 @@ mod tests { fn test_isequal_0() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); } @@ -92,7 +92,7 @@ mod tests { fn test_isequal_1() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1)], true, ); } @@ -101,7 +101,7 @@ mod tests { fn test_isequal_1000() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1000), Word::from(1000)], + [Word::from(1000), Word::from(1000)], true, ); } @@ -110,7 +110,7 @@ mod tests { fn test_isequal_1_0() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); } @@ -119,7 +119,7 @@ mod tests { fn test_isequal_0_1() { try_test!( IsEqualGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs index 5e88655313..c9fbcb4d42 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/is_zero.rs @@ -100,20 +100,16 @@ mod tests { #[test] fn test_0_is_zero() { - try_test!(IsZeroGadgetTestContainer, vec![Word::from(0)], true); + try_test!(IsZeroGadgetTestContainer, [Word::from(0)], true); } #[test] fn test_1_is_not_zero() { - try_test!(IsZeroGadgetTestContainer, vec![Word::from(1)], false); + try_test!(IsZeroGadgetTestContainer, [Word::from(1)], false); } #[test] fn test_large_num_is_not_zero() { - try_test!( - IsZeroGadgetTestContainer, - vec![Word::from(10000)], - false, - ); + try_test!(IsZeroGadgetTestContainer, [Word::from(10000)], false,); } } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs index 51366b5480..5801141d59 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt.rs @@ -148,12 +148,12 @@ mod tests { fn test_lt_expect() { try_test!( LtGadgetTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); try_test!( LtGadgetTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], false, ); } @@ -162,7 +162,7 @@ mod tests { fn test_lt_just_in_range() { try_test!( LtGadgetTestContainer, - vec![Word::from(1), Word::from((1u64 << (N * 8)) - 1)], + [Word::from(1), Word::from((1u64 << (N * 8)) - 1)], true, ); } @@ -171,7 +171,7 @@ mod tests { fn test_lt_out_of_range() { try_test!( LtGadgetTestContainer, - vec![Word::from(1), Word::from(2 << (N * 8))], + [Word::from(1), Word::from(2 << (N * 8))], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs index b5499f2e1b..dff46d9a57 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/lt_word.rs @@ -113,27 +113,19 @@ mod tests { fn test_ltword_expect() { try_test!( LtWordTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); + try_test!(LtWordTestContainer, [Word::from(1), Word::MAX], true,); + try_test!(LtWordTestContainer, [WORD_LOW_MAX, WORD_HIGH_MAX], true,); try_test!( LtWordTestContainer, - vec![Word::from(1), Word::MAX], + [Word::from(90), WORD_LOW_MAX], true, ); try_test!( LtWordTestContainer, - vec![WORD_LOW_MAX, WORD_HIGH_MAX], - true, - ); - try_test!( - LtWordTestContainer, - vec![Word::from(90), WORD_LOW_MAX], - true, - ); - try_test!( - LtWordTestContainer, - vec![Word::from(90), WORD_HIGH_MAX], + [Word::from(90), WORD_HIGH_MAX], true, ); } @@ -142,14 +134,14 @@ mod tests { fn test_ltword_unexpect() { try_test!( LtWordTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); - try_test!(LtWordTestContainer, vec![Word::MAX, Word::MAX], false,); + try_test!(LtWordTestContainer, [Word::MAX, Word::MAX], false,); try_test!( LtWordTestContainer, - vec![WORD_HIGH_MAX, WORD_LOW_MAX], + [WORD_HIGH_MAX, WORD_LOW_MAX], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs index f5201d40e1..b2b4589bcc 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/min_max.rs @@ -110,17 +110,17 @@ mod tests { // a == b try_test!( MinMaxTestContainer, - vec![Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(5), Word::from(5)], + [Word::from(5), Word::from(5)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX], + [WORD_LOW_MAX, WORD_LOW_MAX], true, ); } @@ -130,17 +130,17 @@ mod tests { // min == a, max == b try_test!( MinMaxTestContainer, - vec![Word::from(0), Word::from(1)], + [Word::from(0), Word::from(1)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(3), Word::from(5)], + [Word::from(3), Word::from(5)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX], + [WORD_LOW_MAX, WORD_LOW_MAX], true, ); } @@ -150,17 +150,17 @@ mod tests { // min == b, max == a try_test!( MinMaxTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], false, ); try_test!( MinMaxTestContainer, - vec![Word::from(256), Word::from(3)], + [Word::from(256), Word::from(3)], false, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX, Word::from(123456)], + [WORD_LOW_MAX, Word::from(123456)], false, ); } @@ -170,19 +170,19 @@ mod tests { // min == a, max == b try_test!( MinMaxTestContainer, - vec![Word::from(1), Word::from(0)], + [Word::from(1), Word::from(0)], true, ); try_test!( MinMaxTestContainer, - vec![Word::from(777), Word::from(44)], + [Word::from(777), Word::from(44)], true, ); try_test!( MinMaxTestContainer, - vec![WORD_LOW_MAX+1, WORD_LOW_MAX], + [WORD_LOW_MAX+1, WORD_LOW_MAX], true, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs index 28779640cf..0c1ffdca6e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/modulo.rs @@ -156,37 +156,37 @@ mod tests { fn test_mod_n_expected_rem() { try_test!( ModGadgetTestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(1), Word::from(0), Word::from(0)], + [Word::from(1), Word::from(0), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(548), Word::from(50), Word::from(48)], + [Word::from(548), Word::from(50), Word::from(48)], true, ); try_test!( ModGadgetTestContainer, - vec![Word::from(30), Word::from(50), Word::from(30)], + [Word::from(30), Word::from(50), Word::from(30)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_LOW_MAX, Word::from(1024), Word::from(1023)], + [WORD_LOW_MAX, Word::from(1024), Word::from(1023)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_HIGH_MAX, Word::from(1024), Word::from(0)], + [WORD_HIGH_MAX, Word::from(1024), Word::from(0)], true, ); try_test!( ModGadgetTestContainer, - vec![WORD_CELL_MAX, Word::from(2), Word::from(0)], + [WORD_CELL_MAX, Word::from(2), Word::from(0)], true, ); } @@ -203,7 +203,7 @@ mod tests { // ModGadget, the statement would be invalid in the ModGadget. try_test!( ModGadgetTestContainer, - vec![ + [ Word::from(2), Word::from(3), Word::from(0), @@ -214,27 +214,27 @@ mod tests { ); try_test!( ModGadgetTestContainer, - vec![Word::from(1), Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1), Word::from(1)], false, ); try_test!( ModGadgetTestContainer, - vec![Word::from(46), Word::from(50), Word::from(48)], + [Word::from(46), Word::from(50), Word::from(48)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_LOW_MAX, Word::from(999999), Word::from(888888)], + [WORD_LOW_MAX, Word::from(999999), Word::from(888888)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_CELL_MAX, Word::from(999999999), Word::from(666666666)], + [WORD_CELL_MAX, Word::from(999999999), Word::from(666666666)], false, ); try_test!( ModGadgetTestContainer, - vec![WORD_HIGH_MAX, Word::from(999999), Word::from(777777)], + [WORD_HIGH_MAX, Word::from(999999), Word::from(777777)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs index e94d49897b..b526e6380e 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words.rs @@ -211,7 +211,7 @@ mod tests { // 0 * 0 + 0 == 0 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(0), Word::from(0), Word::from(0), @@ -223,7 +223,7 @@ mod tests { // 1 * 0 + 0 == 0 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(0), Word::from(0), @@ -235,7 +235,7 @@ mod tests { // 1 * 1 + 0 == 1 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(0), @@ -247,7 +247,7 @@ mod tests { // 1 * 1 + 1 == 2 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(1), @@ -259,7 +259,7 @@ mod tests { // 100 * 54 + 98 == 5498 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(98), @@ -271,7 +271,7 @@ mod tests { // 100 * 54 + low_max == low_max + 5400 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_LOW_MAX, @@ -283,7 +283,7 @@ mod tests { // 100 * 54 + high_max == high_max + 5400 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_HIGH_MAX, @@ -299,7 +299,7 @@ mod tests { // high_max + low_max + 1 == 0 with overflow 1 try_test!( MulAddGadgetContainer, - vec![ + [ WORD_LOW_MAX + 1, Word::from(1), WORD_HIGH_MAX, @@ -318,7 +318,7 @@ mod tests { // overflow == 73786976294838206460 + ((1<<64)-1)*((1<<64)-1)*6 try_test!( MulAddGadgetContainer, - vec![ + [ Word::MAX, Word::MAX, Word::MAX, @@ -334,7 +334,7 @@ mod tests { // 10 * 1 + 1 != 3 try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(10), Word::from(1), Word::from(1), @@ -347,7 +347,7 @@ mod tests { // 1 * 1 + 1 != word_max, no underflow try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(1), @@ -360,7 +360,7 @@ mod tests { // 100 * 54 + high_max == high_max + 5400, no overflow try_test!( MulAddGadgetContainer, - vec![ + [ Word::from(100), Word::from(54), WORD_HIGH_MAX, @@ -373,7 +373,7 @@ mod tests { // (low_max + 1) * 1 + high_max == 0 with overflow 1 try_test!( MulAddGadgetContainer, - vec![ + [ WORD_LOW_MAX + 1, Word::from(1), WORD_HIGH_MAX, diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs index 27562c961b..fed7c98d87 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_add_words512.rs @@ -258,7 +258,7 @@ mod tests { // 0 * 0 + 0 == 0 * 2**256 + 0 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(0), Word::from(0), Word::from(0), @@ -270,7 +270,7 @@ mod tests { // 1 * 0 + 0 == 0 * 2**256 + 0 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(1), Word::from(0), Word::from(0), @@ -282,7 +282,7 @@ mod tests { // 1 * 1 + 0 == 0 * 2**256 + 1 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(1), Word::from(1), Word::from(0), @@ -294,7 +294,7 @@ mod tests { // 100 * 54 + 0 == 0 * 2**256 + 5400 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -306,7 +306,7 @@ mod tests { // 100 * 54 + max == 1 * 2**256 + 5400 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(1), @@ -318,7 +318,7 @@ mod tests { // 100 * 54 + low_max == 0 * 2**256 + 5400 + low_max try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -330,7 +330,7 @@ mod tests { // 100 * 54 + high_max == 0 * 2**256 + 5400 + high_max try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(100), Word::from(54), Word::from(0), @@ -346,7 +346,7 @@ mod tests { // 10 * 1 + 0 != 1 * 2**256 + 3 try_test!( MulAddWords512GadgetContainer, - vec![ + [ Word::from(10), Word::from(1), Word::from(1), diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs index 220235a002..ec3d6e69aa 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/mul_word_u64.rs @@ -142,43 +142,43 @@ mod tests { // 0 * 0 = 0 try_test!( MulWordByU64TestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); // max * 0 = 0 try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(0), Word::from(0)], + [Word::MAX, Word::from(0), Word::from(0)], true, ); // 1 * 1 = 1 try_test!( MulWordByU64TestContainer, - vec![Word::from(1), Word::from(1), Word::from(1)], + [Word::from(1), Word::from(1), Word::from(1)], true, ); // max * 1 = max try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(1), Word::MAX], + [Word::MAX, Word::from(1), Word::MAX], true, ); // 2 * 2 = 4 try_test!( MulWordByU64TestContainer, - vec![Word::from(2), Word::from(2), Word::from(4)], + [Word::from(2), Word::from(2), Word::from(4)], true, ); // 22222 * 500 = 11111000 try_test!( MulWordByU64TestContainer, - vec![Word::from(22222), Word::from(500), Word::from(11111000)], + [Word::from(22222), Word::from(500), Word::from(11111000)], true, ); // low_max * 2 = low_max << 1 try_test!( MulWordByU64TestContainer, - vec![WORD_LOW_MAX, Word::from(2), WORD_LOW_MAX << 1], + [WORD_LOW_MAX, Word::from(2), WORD_LOW_MAX << 1], true, ); } @@ -187,13 +187,13 @@ mod tests { fn test_mulwordu64_unexpect() { try_test!( MulWordByU64TestContainer, - vec![Word::MAX, Word::from(1), Word::from(1)], + [Word::MAX, Word::from(1), Word::from(1)], false, ); // high_max * 2 = overflow try_test!( MulWordByU64TestContainer, - vec![WORD_HIGH_MAX, Word::from(2), WORD_HIGH_MAX << 1], + [WORD_HIGH_MAX, Word::from(2), WORD_HIGH_MAX << 1], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs index 5282aecded..90a562965d 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/pair_select.rs @@ -128,12 +128,12 @@ mod tests { fn test_pairselect_eq() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(0), Word::from(0)], + [Word::from(0), Word::from(0), Word::from(0)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2898), Word::from(2898), Word::from(2898)], + [Word::from(2898), Word::from(2898), Word::from(2898)], true, ); } @@ -142,17 +142,17 @@ mod tests { fn test_pairselect_expect_a_and_a() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(0), Word::from(1)], + [Word::from(0), Word::from(0), Word::from(1)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2898), Word::from(2898), Word::from(1)], + [Word::from(2898), Word::from(2898), Word::from(1)], true, ); try_test!( PairSelectionTestContainer, - vec![WORD_LOW_MAX, WORD_LOW_MAX, Word::from(1)], + [WORD_LOW_MAX, WORD_LOW_MAX, Word::from(1)], true, ); } @@ -161,12 +161,12 @@ mod tests { fn test_pairselect_expect_a_but_b() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(1), Word::from(0)], + [Word::from(0), Word::from(1), Word::from(0)], false, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2989), Word::from(1), Word::from(2989)], + [Word::from(2989), Word::from(1), Word::from(2989)], false, ); } @@ -175,12 +175,12 @@ mod tests { fn test_pairselect_expect_b_and_b() { try_test!( PairSelectionTestContainer, - vec![Word::from(0), Word::from(1), Word::from(0)], + [Word::from(0), Word::from(1), Word::from(0)], true, ); try_test!( PairSelectionTestContainer, - vec![Word::from(2989), Word::from(1), Word::from(2989)], + [Word::from(2989), Word::from(1), Word::from(2989)], true, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs index 989011c68a..abf282f66c 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/range_check.rs @@ -87,13 +87,13 @@ mod tests { #[test] fn test_rangecheck_just_in_range() { - try_test!(RangeCheckTestContainer, vec![Word::from(0)], true); + try_test!(RangeCheckTestContainer,[Word::from(0)], true); - try_test!(RangeCheckTestContainer, vec![Word::from(1)], true); + try_test!(RangeCheckTestContainer,[Word::from(1)], true); // max - 1 try_test!( RangeCheckTestContainer, - vec![Word::from((1u64 << 32) - 1)], + [Word::from((1u64 << 32) - 1)], true, ); } @@ -102,7 +102,7 @@ mod tests { fn test_rangecheck_out_of_range() { try_test!( RangeCheckTestContainer, - vec![Word::from(1u64 << 32)], + [Word::from(1u64 << 32)], false, ); } diff --git a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs index 352a832c32..d6a8cff542 100644 --- a/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs +++ b/zkevm-circuits/src/evm_circuit/util/math_gadget/rlp.rs @@ -509,7 +509,7 @@ mod test { }; try_test!( ContractCreateGadgetContainer, - vec![ + [ caller_address.to_word(), Word::from(caller_nonce), rlp_len, diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index 3d13fb37a6..bbd2bbcfac 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -801,7 +801,7 @@ mod test { // buffer len = data len try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(2), Word::from(256), @@ -812,7 +812,7 @@ mod test { // buffer len > data len try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(2), Word::from(256), @@ -826,7 +826,7 @@ mod test { // buffer len < data len try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(1), Word::from(256), @@ -837,7 +837,7 @@ mod test { // buffer len <= 0 try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(1), Word::from(0), Word::from(256), @@ -848,7 +848,7 @@ mod test { // empty buffer try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(0), Word::from(256), @@ -859,7 +859,7 @@ mod test { // MAX_BYTES < buffer size try_test!( BufferReaderGadgetTestContainer, - vec![ + [ Word::from(0), Word::from(31), Word::from(256), diff --git a/zkevm-circuits/src/exp_circuit.rs b/zkevm-circuits/src/exp_circuit.rs index 80a8173385..5251308dcd 100644 --- a/zkevm-circuits/src/exp_circuit.rs +++ b/zkevm-circuits/src/exp_circuit.rs @@ -490,7 +490,7 @@ impl ExpCircuit { Self { exp_events, max_exp_rows, - _marker: PhantomData::default(), + _marker: PhantomData, } } } diff --git a/zkevm-circuits/src/mpt_circuit/helpers.rs b/zkevm-circuits/src/mpt_circuit/helpers.rs index 2504f30a6d..b780b47a63 100644 --- a/zkevm-circuits/src/mpt_circuit/helpers.rs +++ b/zkevm-circuits/src/mpt_circuit/helpers.rs @@ -1339,7 +1339,7 @@ impl MainRLPGadget { ensure_minimal_rlp: cb.query_cell(), keccak_r: Some(cb.keccak_r.expr()), }; - let all_bytes = vec![vec![config.rlp_byte.clone()], config.bytes.clone()].concat(); + let all_bytes = [vec![config.rlp_byte.clone()], config.bytes.clone()].concat(); // Decode the RLP item config.rlp = diff --git a/zkevm-circuits/src/mpt_circuit/mod_extension.rs b/zkevm-circuits/src/mpt_circuit/mod_extension.rs index e5c2721048..c1ea30addd 100644 --- a/zkevm-circuits/src/mpt_circuit/mod_extension.rs +++ b/zkevm-circuits/src/mpt_circuit/mod_extension.rs @@ -100,8 +100,8 @@ impl ModExtensionGadget { (parent_data[1].hash.lo().expr(), parent_data[1].hash.hi().expr(), parent_data[1].drifted_parent_hash.lo().expr(), parent_data[1].drifted_parent_hash.hi().expr()) }}; - let parent_data_lo = vec![lo_s, lo_c]; - let parent_data_hi = vec![hi_s, hi_c]; + let parent_data_lo = [lo_s, lo_c]; + let parent_data_hi = [hi_s, hi_c]; let ( parent_data_rlc, key_rlc_before, @@ -139,7 +139,7 @@ impl ModExtensionGadget { for is_s in [true, false] { config.is_key_part_odd[is_s.idx()] = cb.query_cell(); - let items = vec![ + let items = [ key_items[is_s.idx()].clone(), key_nibbles[is_s.idx()].clone(), ]; diff --git a/zkevm-circuits/src/mpt_circuit/start.rs b/zkevm-circuits/src/mpt_circuit/start.rs index 0e1fce33b6..37ee126e36 100644 --- a/zkevm-circuits/src/mpt_circuit/start.rs +++ b/zkevm-circuits/src/mpt_circuit/start.rs @@ -96,7 +96,7 @@ impl StartConfig { self.proof_type .assign(region, offset, start.proof_type.scalar())?; - let mut root = vec![WordLoHi::zero(); 2]; + let mut root = [WordLoHi::zero(); 2]; for is_s in [true, false] { root[is_s.idx()] = rlp_values[is_s.idx()].word(); } diff --git a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs index 5fb89eaffd..6a698a8195 100644 --- a/zkevm-circuits/src/mpt_circuit/storage_leaf.rs +++ b/zkevm-circuits/src/mpt_circuit/storage_leaf.rs @@ -436,7 +436,7 @@ impl StorageLeafConfig { let mut key_data = vec![KeyDataWitness::default(); 2]; let mut parent_data = vec![ParentDataWitness::default(); 2]; let mut key_rlc = vec![0.scalar(); 2]; - let mut value_word = vec![WordLoHi::zero(); 2]; + let mut value_word = [WordLoHi::zero(); 2]; for is_s in [true, false] { self.is_mod_extension[is_s.idx()].assign( region, diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 548b339f0b..1c9f700202 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -402,8 +402,8 @@ impl SubCircuitConfig for PiCircuitConfig { let default_calldata_row_constraint4 = tx_id_is_zero_config.expr() * gas_cost.expr(); // if tx_id != 0 then - // 1. tx_id_next == tx_id: idx_next == idx + 1, gas_cost_next == gas_cost + - // gas_next, is_final == false; + // 1. tx_id_next == tx_id: idx_next == idx + 1, gas_cost_next == gas_cost + gas_next, + // is_final == false; // 2. tx_id_next == tx_id + 1 + x (where x is in [0, 2^16)): idx_next == 0, // gas_cost_next == gas_next, is_final == true; // 3. tx_id_next == 0: is_final == true, idx_next == 0, gas_cost_next == 0; diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 4dcbda633f..81825492f4 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -168,7 +168,7 @@ impl SubCircuitConfig for StateCircuitConfig { lookups, rw_table, mpt_table, - _marker: PhantomData::default(), + _marker: PhantomData, }; let mut constraint_builder = ConstraintBuilder::new(); @@ -447,7 +447,7 @@ impl StateCircuit { n_rows, #[cfg(test)] overrides: HashMap::new(), - _marker: PhantomData::default(), + _marker: PhantomData, } } } diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index 062a85cb00..4d90c23dae 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -38,10 +38,8 @@ use strum_macros::EnumIter; // 1. limb_difference fits into 16 bits. // 2. limb_difference is not zero because its inverse exists. -// 3. RLC of the pairwise limb differences before the first_different_limb is -// zero. -// 4. limb_difference equals the difference of the limbs at -// first_different_limb. +// 3. RLC of the pairwise limb differences before the first_different_limb is zero. +// 4. limb_difference equals the difference of the limbs at first_different_limb. #[derive(Clone, Copy, Debug, EnumIter)] pub enum LimbIndex { @@ -312,7 +310,7 @@ fn rlc_limb_differences( ) -> Vec> { let mut result = vec![]; let mut partial_sum = 0u64.expr(); - let powers_of_randomness = once(1.expr()).chain(powers_of_randomness.into_iter()); + let powers_of_randomness = once(1.expr()).chain(powers_of_randomness); for ((cur_limb, prev_limb), power_of_randomness) in cur .be_limbs() .iter() diff --git a/zkevm-circuits/src/state_circuit/lookups.rs b/zkevm-circuits/src/state_circuit/lookups.rs index 41b45a3fb1..697be10c5f 100644 --- a/zkevm-circuits/src/state_circuit/lookups.rs +++ b/zkevm-circuits/src/state_circuit/lookups.rs @@ -31,7 +31,6 @@ impl Config { vec![exp] .into_iter() .zip_eq(self.u16_table.table_exprs(meta)) - .map(|(exp, table_expr)| (exp, table_expr)) .collect() }); } diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index f0a81c9fc8..e072443f88 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -929,7 +929,7 @@ fn variadic_size_check() { updates, overrides: HashMap::default(), n_rows: N_ROWS, - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let power_of_randomness = circuit.instance(); let prover1 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -957,7 +957,7 @@ fn variadic_size_check() { updates, overrides: HashMap::default(), n_rows: N_ROWS, - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let power_of_randomness = circuit.instance(); let prover2 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -997,7 +997,7 @@ fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockP updates, overrides, n_rows: N_ROWS, - _marker: std::marker::PhantomData::default(), + _marker: std::marker::PhantomData, }; let instance = circuit.instance(); diff --git a/zkevm-circuits/src/table/tx_table.rs b/zkevm-circuits/src/table/tx_table.rs index 6e2777d52c..4237fc0c6a 100644 --- a/zkevm-circuits/src/table/tx_table.rs +++ b/zkevm-circuits/src/table/tx_table.rs @@ -158,7 +158,7 @@ impl TxTable { .collect(); for tx in txs.iter().chain(padding_txs.iter()) { let tx_id = Value::known(F::from(tx.id)); - let tx_data = vec![ + let tx_data = [ (TxContextFieldTag::Nonce, WordLoHi::from(tx.nonce.as_u64())), (TxContextFieldTag::Gas, WordLoHi::from(tx.gas())), (TxContextFieldTag::GasPrice, WordLoHi::from(tx.gas_price)), diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index 9ff41cc2f8..b99f2a46a2 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -105,7 +105,7 @@ impl Default for SignVerifyChip { aux_generator: Secp256k1Affine::default(), window_size: 4, max_verif: 0, - _marker: PhantomData::default(), + _marker: PhantomData, } } } diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 5004848c6c..7c83101b49 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -133,7 +133,7 @@ impl RwMap { .collect(); let padding_length = Self::padding_len(rows.len(), target_len); let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); - (padding.chain(rows.into_iter()).collect(), padding_length) + (padding.chain(rows).collect(), padding_length) } /// Build Rws for assignment pub fn table_assignments(&self) -> Vec { From e19d1637c45dce2e269320b97a28b9cbac188a72 Mon Sep 17 00:00:00 2001 From: Kimi Wu Date: Mon, 19 Feb 2024 17:58:10 +0800 Subject: [PATCH 03/16] feat/#1665 Precompile ECRECOVER (#1720) closed #1665 This PR was ported from - https://github.com/scroll-tech/zkevm-circuits/pull/529 - https://github.com/scroll-tech/zkevm-circuits/pull/930 which manly includes, 1. the main logic in ecrecover gadget (`ecrecover.rs`) 2. signature verifcation circuiit (`sig_circuit.rs`). What I did is to change rlc to word lo/hi. 3. a new table, `sig_table.rs` 4. ecc circuit (`ecc_circuit.rs`). It's not be used in `ecRecover`, but it was implemented in Scroll's PR. If I removed ecc_circuit, it would be inconvienent for people porting `ecAdd`, `ecMul` or `ecPairing`. That's why I keep it here. 5. dependencies update, using `halo2lib` (includes `halo2-base` and `halo2-ecc`) --------- Co-authored-by: Rohit Narurkar Co-authored-by: Zhang Zhuo --- .github/workflows/main-tests.yml | 2 +- Cargo.lock | 49 + Cargo.toml | 6 + bus-mapping/Cargo.toml | 2 + bus-mapping/src/circuit_input_builder.rs | 8 + .../src/circuit_input_builder/block.rs | 11 +- .../src/circuit_input_builder/execution.rs | 48 +- .../circuit_input_builder/input_state_ref.rs | 8 +- .../src/circuit_input_builder/transaction.rs | 12 +- bus-mapping/src/evm/opcodes/callop.rs | 24 +- .../src/evm/opcodes/precompiles/ecrecover.rs | 60 + .../src/evm/opcodes/precompiles/mod.rs | 38 +- bus-mapping/src/precompile.rs | 95 +- circuit-benchmarks/src/super_circuit.rs | 1 + eth-types/src/geth_types.rs | 21 +- eth-types/src/sign_types.rs | 39 +- .../src/integration_test_circuits.rs | 3 + .../tests/circuit_input_builder.rs | 1 + testool/src/statetest/executor.rs | 2 + zkevm-circuits/Cargo.toml | 3 + zkevm-circuits/src/ecc_circuit.rs | 1377 +++++++++++++++++ zkevm-circuits/src/ecc_circuit/dev.rs | 46 + zkevm-circuits/src/ecc_circuit/test.rs | 620 ++++++++ zkevm-circuits/src/ecc_circuit/util.rs | 107 ++ zkevm-circuits/src/evm_circuit.rs | 14 +- zkevm-circuits/src/evm_circuit/execution.rs | 16 +- .../execution/error_oog_precompile.rs | 13 +- .../execution/precompiles/ecrecover.rs | 575 +++++++ .../evm_circuit/execution/precompiles/mod.rs | 3 + zkevm-circuits/src/evm_circuit/param.rs | 7 +- zkevm-circuits/src/evm_circuit/step.rs | 10 +- zkevm-circuits/src/evm_circuit/table.rs | 33 +- .../evm_circuit/util/constraint_builder.rs | 25 +- .../src/evm_circuit/util/instrumentation.rs | 4 + .../src/evm_circuit/util/precompile_gadget.rs | 16 +- zkevm-circuits/src/lib.rs | 1 + zkevm-circuits/src/root_circuit/test.rs | 1 + zkevm-circuits/src/sig_circuit.rs | 1049 +++++++++++++ zkevm-circuits/src/sig_circuit/dev.rs | 64 + zkevm-circuits/src/sig_circuit/ecdsa.rs | 226 +++ zkevm-circuits/src/sig_circuit/test.rs | 267 ++++ zkevm-circuits/src/sig_circuit/utils.rs | 115 ++ zkevm-circuits/src/super_circuit.rs | 6 +- zkevm-circuits/src/super_circuit/test.rs | 3 + zkevm-circuits/src/table.rs | 3 + zkevm-circuits/src/table/sig_table.rs | 131 ++ zkevm-circuits/src/tx_circuit/sign_verify.rs | 8 +- zkevm-circuits/src/witness/block.rs | 29 +- 48 files changed, 5143 insertions(+), 59 deletions(-) create mode 100644 bus-mapping/src/evm/opcodes/precompiles/ecrecover.rs create mode 100644 zkevm-circuits/src/ecc_circuit.rs create mode 100644 zkevm-circuits/src/ecc_circuit/dev.rs create mode 100644 zkevm-circuits/src/ecc_circuit/test.rs create mode 100644 zkevm-circuits/src/ecc_circuit/util.rs create mode 100644 zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs create mode 100644 zkevm-circuits/src/sig_circuit.rs create mode 100644 zkevm-circuits/src/sig_circuit/dev.rs create mode 100644 zkevm-circuits/src/sig_circuit/ecdsa.rs create mode 100644 zkevm-circuits/src/sig_circuit/test.rs create mode 100644 zkevm-circuits/src/sig_circuit/utils.rs create mode 100644 zkevm-circuits/src/table/sig_table.rs diff --git a/.github/workflows/main-tests.yml b/.github/workflows/main-tests.yml index 5a4826347a..4fb06d1367 100644 --- a/.github/workflows/main-tests.yml +++ b/.github/workflows/main-tests.yml @@ -79,7 +79,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --verbose --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks + args: --verbose --release --all --all-features --exclude integration-tests --exclude circuit-benchmarks -- --test-threads 24 - name: Run testool internal tests uses: actions-rs/cargo@v1 with: diff --git a/Cargo.lock b/Cargo.lock index ab96dd69af..5f6781d1d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,7 +357,9 @@ dependencies = [ "lazy_static", "log", "mock", + "num", "pretty_assertions", + "rand", "revm-precompile", "serde", "serde_json", @@ -1787,6 +1789,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "halo2-base" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#40ba7e3bbf013b55c59283534c9489701f9212d0" +dependencies = [ + "ff", + "halo2_proofs", + "itertools 0.10.5", + "num-bigint", + "num-integer", + "num-traits", + "rand_chacha", + "rustc-hash", +] + +[[package]] +name = "halo2-ecc" +version = "0.2.2" +source = "git+https://github.com/scroll-tech/halo2-lib?branch=develop#40ba7e3bbf013b55c59283534c9489701f9212d0" +dependencies = [ + "ff", + "group", + "halo2-base", + "itertools 0.10.5", + "num-bigint", + "num-integer", + "num-traits", + "rand", + "rand_chacha", + "rand_core", + "serde", + "serde_json", +] + [[package]] name = "halo2_proofs" version = "0.3.0" @@ -3465,6 +3501,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -4964,6 +5006,8 @@ dependencies = [ "ethers-signers", "gadgets", "getrandom", + "halo2-base", + "halo2-ecc", "halo2_proofs", "hex", "integer", @@ -5017,3 +5061,8 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "halo2curves" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 00da3baf01..0e88d667b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,12 @@ members = [ [patch.crates-io] halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } +[patch."https://github.com/scroll-tech/halo2.git"] +halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.git", tag = "v0.3.0" } + +[patch."https://github.com/privacy-scaling-explorations/halo2curves.git"] +halo2curves = { version = "0.1.0", features = ["derive_serde"] } + # Definition of benchmarks profile to use. [profile.bench] opt-level = 3 diff --git a/bus-mapping/Cargo.toml b/bus-mapping/Cargo.toml index d58111b309..bf8dd08b25 100644 --- a/bus-mapping/Cargo.toml +++ b/bus-mapping/Cargo.toml @@ -16,6 +16,8 @@ halo2_proofs = { git = "https://github.com/privacy-scaling-explorations/halo2.gi itertools = "0.10" lazy_static = "1.4" log = "0.4.14" +num = "0.4" +rand = { version = "0.8", optional = true } serde = {version = "1.0.130", features = ["derive"] } serde_json = "1.0.66" strum = "0.24" diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index 6861a870ae..b8ea23f419 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -31,6 +31,7 @@ use eth_types::{ use ethers_providers::JsonRpcClient; pub use execution::{ CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash, + PrecompileEvent, PrecompileEvents, N_BYTES_PER_PAIR, N_PAIRING_PER_OP, }; pub use input_state_ref::CircuitInputStateRef; use itertools::Itertools; @@ -112,6 +113,11 @@ pub struct FixedCParams { /// calculated, so the same circuit will not be able to prove different /// witnesses. pub max_keccak_rows: usize, + /// This number indicate what 100% usage means, for example if we can support up to 2 + /// ecPairing inside circuit, and max_vertical_circuit_rows is set to 1_000_000, + /// then if there is 1 ecPairing in the input, we will return 500_000 as the "row usage" + /// for the ec circuit. + pub max_vertical_circuit_rows: usize, } /// Unset Circuits Parameters @@ -153,6 +159,7 @@ impl Default for FixedCParams { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, } } } @@ -497,6 +504,7 @@ impl CircuitInputBuilder { max_bytecode, max_evm_rows, max_keccak_rows, + max_vertical_circuit_rows: 0, } }; let mut cib = CircuitInputBuilder:: { diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index d2d97f4a0b..655eb8c073 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -1,7 +1,8 @@ //! Block-related utility module use super::{ - execution::ExecState, transaction::Transaction, CopyEvent, ExecStep, ExpEvent, Withdrawal, + execution::ExecState, transaction::Transaction, CopyEvent, ExecStep, ExpEvent, PrecompileEvent, + PrecompileEvents, Withdrawal, }; use crate::{ operation::{OperationContainer, RWCounter}, @@ -87,6 +88,8 @@ pub struct Block { pub sha3_inputs: Vec>, /// Exponentiation events in the block. pub exp_events: Vec, + /// IO to/from the precompiled contract calls. + pub precompile_events: PrecompileEvents, /// Original block from geth pub eth_block: eth_types::Block, } @@ -145,6 +148,7 @@ impl Block { copy_events: Vec::new(), exp_events: Vec::new(), sha3_inputs: Vec::new(), + precompile_events: PrecompileEvents { events: Vec::new() }, eth_block: eth_block.clone(), }) } @@ -191,4 +195,9 @@ impl Block { pub fn add_exp_event(&mut self, event: ExpEvent) { self.exp_events.push(event); } + + /// Push a precompile event to the block. + pub fn add_precompile_event(&mut self, event: PrecompileEvent) { + self.precompile_events.events.push(event); + } } diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index f23e80f7f2..045ba2e44b 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -5,9 +5,9 @@ use crate::{ error::{ExecError, OogError}, exec_trace::OperationRef, operation::RWCounter, - precompile::PrecompileCalls, + precompile::{PrecompileAuxData, PrecompileCalls}, }; -use eth_types::{evm_types::OpcodeId, GethExecStep, Word, H256}; +use eth_types::{evm_types::OpcodeId, sign_types::SignData, GethExecStep, Word, H256}; use gadgets::impl_expr; use halo2_proofs::plonk::Expression; use strum_macros::EnumIter; @@ -49,6 +49,8 @@ pub struct ExecStep { pub copy_rw_counter_delta: u64, /// Error generated by this step pub error: Option, + /// Optional auxiliary data that is attached to precompile call internal states. + pub aux_data: Option, } impl ExecStep { @@ -77,6 +79,7 @@ impl ExecStep { bus_mapping_instance: Vec::new(), copy_rw_counter_delta: 0, error: None, + aux_data: None, } } @@ -364,3 +367,44 @@ impl Default for ExpEvent { } } } + +/// I/Os from all precompiled contract calls in a block. +#[derive(Clone, Debug, Default)] +pub struct PrecompileEvents { + /// All events. + pub events: Vec, +} + +impl PrecompileEvents { + /// Get all ecrecover events. + pub fn get_ecrecover_events(&self) -> Vec { + self.events + .iter() + .map(|e| { + let PrecompileEvent::Ecrecover(sign_data) = e; + sign_data + }) + .cloned() + .collect() + } +} + +/// I/O from a precompiled contract call. +#[derive(Clone, Debug)] +pub enum PrecompileEvent { + /// Represents the I/O from Ecrecover call. + Ecrecover(SignData), +} + +impl Default for PrecompileEvent { + fn default() -> Self { + Self::Ecrecover(SignData::default()) + } +} + +/// The number of pairing inputs per pairing operation. If the inputs provided to the precompile +/// call are < 4, we append (G1::infinity, G2::generator) until we have the required no. of inputs. +pub const N_PAIRING_PER_OP: usize = 4; + +/// The number of bytes taken to represent a pair (G1, G2). +pub const N_BYTES_PER_PAIR: usize = 192; 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 3b5b74c1e8..09b8089ef1 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -2,7 +2,7 @@ use super::{ get_call_memory_offset_length, get_create_init_code, Block, BlockContext, Call, CallContext, - CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, Transaction, + CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, PrecompileEvent, Transaction, TransactionContext, }; use crate::{ @@ -1404,6 +1404,11 @@ impl<'a> CircuitInputStateRef<'a> { self.block.add_exp_event(event) } + /// Push an event representing auxiliary data for a precompile call to the state. + pub fn push_precompile_event(&mut self, event: PrecompileEvent) { + self.block.add_precompile_event(event) + } + pub(crate) fn get_step_err( &self, step: &GethExecStep, @@ -1614,7 +1619,6 @@ impl<'a> CircuitInputStateRef<'a> { PrecompileCalls::Sha256 | PrecompileCalls::Ripemd160 | PrecompileCalls::Blake2F - | PrecompileCalls::ECRecover | PrecompileCalls::Bn128Add | PrecompileCalls::Bn128Mul | PrecompileCalls::Bn128Pairing diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 9b86e197e1..6ca84d27ce 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -182,7 +182,7 @@ pub struct Transaction { /// The transaction id pub id: u64, /// The raw transaction fields - tx: geth_types::Transaction, + pub tx: geth_types::Transaction, /// Calls made in the transaction pub(crate) calls: Vec, /// Execution steps @@ -190,6 +190,16 @@ pub struct Transaction { } impl Transaction { + /// Create a dummy Transaction with zero values + pub fn dummy() -> Self { + Self { + id: 0, + calls: Vec::new(), + steps: Vec::new(), + tx: geth_types::Transaction::dummy(), + } + } + /// Create a new Self. pub fn new( id: u64, diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 316bfe454d..350915302a 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -362,7 +362,7 @@ impl Opcode for CallOpcode { // insert a copy event (input) for this step and generate memory op let rw_counter_start = state.block_ctx.rwc; - if call.call_data_length > 0 { + let input_bytes = if call.call_data_length > 0 { let n_input_bytes = if let Some(input_len) = precompile_call.input_len() { min(input_len, call.call_data_length as usize) } else { @@ -390,11 +390,14 @@ impl Opcode for CallOpcode { bytes: input_bytes.iter().map(|s| (*s, false)).collect(), }, ); - } + Some(input_bytes) + } else { + None + }; // write the result in the callee's memory let rw_counter_start = state.block_ctx.rwc; - if call.is_success && !result.is_empty() { + let output_bytes = if call.is_success && !result.is_empty() { let (output_bytes, _prev_bytes) = state .gen_copy_steps_for_precompile_callee_memory(&mut exec_step, &result)?; @@ -413,11 +416,14 @@ impl Opcode for CallOpcode { bytes: output_bytes.iter().map(|s| (*s, false)).collect(), }, ); - } + Some(output_bytes) + } else { + None + }; // insert another copy event (output) for this step. let rw_counter_start = state.block_ctx.rwc; - if call.is_success && length > 0 { + let return_bytes = if call.is_success && length > 0 { let return_bytes = state.gen_copy_steps_for_precompile_returndata( &mut exec_step, call.return_data_offset, @@ -439,7 +445,10 @@ impl Opcode for CallOpcode { bytes: return_bytes.iter().map(|s| (*s, false)).collect(), }, ); - } + Some(return_bytes) + } else { + None + }; if has_oog_err { let mut oog_step = ErrorOOGPrecompile::gen_associated_ops( @@ -462,6 +471,9 @@ impl Opcode for CallOpcode { geth_steps[1].clone(), call.clone(), precompile_call, + &input_bytes.unwrap_or_default(), + &output_bytes.unwrap_or_default(), + &return_bytes.unwrap_or_default(), )?; // Set gas left and gas cost for precompile step. diff --git a/bus-mapping/src/evm/opcodes/precompiles/ecrecover.rs b/bus-mapping/src/evm/opcodes/precompiles/ecrecover.rs new file mode 100644 index 0000000000..6bcf5f36ca --- /dev/null +++ b/bus-mapping/src/evm/opcodes/precompiles/ecrecover.rs @@ -0,0 +1,60 @@ +use eth_types::{ + sign_types::{biguint_to_32bytes_le, recover_pk, SignData, SECP256K1_Q}, + ToBigEndian, ToLittleEndian, +}; +use halo2_proofs::halo2curves::{ + group::{ff::PrimeField, prime::PrimeCurveAffine}, + secp256k1::{Fq, Secp256k1Affine}, +}; +use num::{BigUint, Integer}; + +use crate::{ + circuit_input_builder::PrecompileEvent, + precompile::{EcrecoverAuxData, PrecompileAuxData}, +}; + +pub(crate) fn opt_data( + input_bytes: &[u8], + output_bytes: &[u8], + return_bytes: &[u8], +) -> (Option, Option) { + let aux_data = EcrecoverAuxData::new(input_bytes, output_bytes, return_bytes); + + // We skip the validation through sig circuit if r or s was not in canonical form. + let opt_sig_r: Option = Fq::from_bytes(&aux_data.sig_r.to_le_bytes()).into(); + let opt_sig_s: Option = Fq::from_bytes(&aux_data.sig_s.to_le_bytes()).into(); + if opt_sig_r.zip(opt_sig_s).is_none() { + return (None, Some(PrecompileAuxData::Ecrecover(aux_data))); + } + + if let Some(sig_v) = aux_data.recovery_id() { + let recovered_pk = recover_pk( + sig_v, + &aux_data.sig_r, + &aux_data.sig_s, + &aux_data.msg_hash.to_be_bytes(), + ) + .unwrap_or(Secp256k1Affine::identity()); + let sign_data = SignData { + signature: ( + Fq::from_bytes(&aux_data.sig_r.to_le_bytes()).unwrap(), + Fq::from_bytes(&aux_data.sig_s.to_le_bytes()).unwrap(), + sig_v, + ), + pk: recovered_pk, + msg: aux_data.input_bytes.clone().into(), + msg_hash: { + let msg_hash = BigUint::from_bytes_be(&aux_data.msg_hash.to_be_bytes()); + let msg_hash = msg_hash.mod_floor(&*SECP256K1_Q); + let msg_hash_le = biguint_to_32bytes_le(msg_hash); + Fq::from_repr(msg_hash_le).unwrap() + }, + }; + ( + Some(PrecompileEvent::Ecrecover(sign_data)), + Some(PrecompileAuxData::Ecrecover(aux_data)), + ) + } else { + (None, Some(PrecompileAuxData::Ecrecover(aux_data))) + } +} diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles/mod.rs index 70e62ce54c..ac16d21f6b 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/mod.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/mod.rs @@ -3,15 +3,22 @@ use eth_types::{GethExecStep, ToWord, Word}; use crate::{ circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, operation::CallContextField, - precompile::PrecompileCalls, + precompile::{PrecompileAuxData, PrecompileCalls}, Error, }; +mod ecrecover; + +use ecrecover::opt_data as opt_data_ecrecover; + pub fn gen_associated_ops( state: &mut CircuitInputStateRef, geth_step: GethExecStep, call: Call, precompile: PrecompileCalls, + input_bytes: &[u8], + output_bytes: &[u8], + return_bytes: &[u8], ) -> Result { assert_eq!(call.code_address(), Some(precompile.into())); let mut exec_step = state.new_step(&geth_step)?; @@ -19,6 +26,35 @@ pub fn gen_associated_ops( common_call_ctx_reads(state, &mut exec_step, &call)?; + let (opt_event, aux_data) = match precompile { + PrecompileCalls::Ecrecover => opt_data_ecrecover(input_bytes, output_bytes, return_bytes), + PrecompileCalls::Identity => ( + None, + Some(PrecompileAuxData::Base { + input_bytes: input_bytes.to_vec(), + output_bytes: output_bytes.to_vec(), + return_bytes: return_bytes.to_vec(), + }), + ), + _ => { + log::warn!("precompile {:?} unsupported in circuits", precompile); + ( + None, + Some(PrecompileAuxData::Base { + input_bytes: input_bytes.to_vec(), + output_bytes: output_bytes.to_vec(), + return_bytes: return_bytes.to_vec(), + }), + ) + } + }; + log::trace!("precompile event {opt_event:?}, aux data {aux_data:?}"); + + if let Some(event) = opt_event { + state.push_precompile_event(event); + } + exec_step.aux_data = aux_data; + Ok(exec_step) } diff --git a/bus-mapping/src/precompile.rs b/bus-mapping/src/precompile.rs index 9b43d844a9..3ccc940964 100644 --- a/bus-mapping/src/precompile.rs +++ b/bus-mapping/src/precompile.rs @@ -2,7 +2,7 @@ use eth_types::{ evm_types::{GasCost, OpcodeId}, - Address, Bytecode, Word, + Address, Bytecode, ToBigEndian, Word, }; #[cfg(not(target_arch = "wasm32"))] use revm_precompile::{Precompile, PrecompileError, Precompiles}; @@ -63,7 +63,7 @@ pub(crate) fn execute_precompiled( #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PrecompileCalls { /// Elliptic Curve Recovery - ECRecover = 0x01, + Ecrecover = 0x01, /// SHA2-256 hash function Sha256 = 0x02, /// Ripemd-160 hash function @@ -82,6 +82,12 @@ pub enum PrecompileCalls { Blake2F = 0x09, } +impl Default for PrecompileCalls { + fn default() -> Self { + Self::Ecrecover + } +} + impl From for Address { fn from(value: PrecompileCalls) -> Self { let mut addr = [0u8; 20]; @@ -105,7 +111,7 @@ impl From for usize { impl From for PrecompileCalls { fn from(value: u8) -> Self { match value { - 0x01 => Self::ECRecover, + 0x01 => Self::Ecrecover, 0x02 => Self::Sha256, 0x03 => Self::Ripemd160, 0x04 => Self::Identity, @@ -123,7 +129,7 @@ impl PrecompileCalls { /// Get the base gas cost for the precompile call. pub fn base_gas_cost(&self) -> u64 { match self { - Self::ECRecover => GasCost::PRECOMPILE_ECRECOVER_BASE, + Self::Ecrecover => GasCost::PRECOMPILE_ECRECOVER_BASE, Self::Sha256 => GasCost::PRECOMPILE_SHA256_BASE, Self::Ripemd160 => GasCost::PRECOMPILE_RIPEMD160_BASE, Self::Identity => GasCost::PRECOMPILE_IDENTITY_BASE, @@ -143,7 +149,7 @@ impl PrecompileCalls { /// Maximum length of input bytes considered for the precompile call. pub fn input_len(&self) -> Option { match self { - Self::ECRecover | Self::Bn128Add => Some(128), + Self::Ecrecover | Self::Bn128Add => Some(128), Self::Bn128Mul => Some(96), _ => None, } @@ -221,3 +227,82 @@ impl PrecompileCallArgs { code } } + +/// Auxiliary data for Ecrecover +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct EcrecoverAuxData { + /// Keccak hash of the message being signed. + pub msg_hash: Word, + /// v-component of signature. + pub sig_v: Word, + /// r-component of signature. + pub sig_r: Word, + /// s-component of signature. + pub sig_s: Word, + /// Address that was recovered. + pub recovered_addr: Address, + /// Input bytes to the ecrecover call. + pub input_bytes: Vec, + /// Output bytes from the ecrecover call. + pub output_bytes: Vec, + /// Bytes returned to the caller from the ecrecover call. + pub return_bytes: Vec, +} + +impl EcrecoverAuxData { + /// Create a new instance of ecrecover auxiliary data. + pub fn new(input: &[u8], output: &[u8], return_bytes: &[u8]) -> Self { + let mut resized_input = input.to_vec(); + resized_input.resize(128, 0u8); + let mut resized_output = output.to_vec(); + resized_output.resize(32, 0u8); + + // assert that recovered address is 20 bytes. + assert!(resized_output[0x00..0x0c].iter().all(|&b| b == 0)); + let recovered_addr = Address::from_slice(&resized_output[0x0c..0x20]); + + Self { + msg_hash: Word::from_big_endian(&resized_input[0x00..0x20]), + sig_v: Word::from_big_endian(&resized_input[0x20..0x40]), + sig_r: Word::from_big_endian(&resized_input[0x40..0x60]), + sig_s: Word::from_big_endian(&resized_input[0x60..0x80]), + recovered_addr, + input_bytes: input.to_vec(), + output_bytes: output.to_vec(), + return_bytes: return_bytes.to_vec(), + } + } + + /// Sanity check and returns recovery ID. + pub fn recovery_id(&self) -> Option { + let sig_v_bytes = self.sig_v.to_be_bytes(); + let sig_v = sig_v_bytes[31]; + if sig_v_bytes.iter().take(31).all(|&b| b == 0) && (sig_v == 27 || sig_v == 28) { + Some(sig_v - 27) + } else { + None + } + } +} + +/// Auxiliary data attached to an internal state for precompile verification. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PrecompileAuxData { + /// Base precompile (used for Identity, SHA256, RIPEMD-160 and BLAKE2F). + Base { + /// input bytes to the identity call. + input_bytes: Vec, + /// output bytes from the identity call. + output_bytes: Vec, + /// bytes returned back to the caller from the identity call. + return_bytes: Vec, + }, + /// Ecrecover. + Ecrecover(EcrecoverAuxData), +} + +impl Default for PrecompileAuxData { + fn default() -> Self { + Self::Ecrecover(EcrecoverAuxData::default()) + } +} diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index 450a912d94..e9868aabbe 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -89,6 +89,7 @@ mod tests { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; let (_, circuit, instance, _) = SuperCircuit::build(block, circuits_params, Fr::from(0x100)).unwrap(); diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index b606d8547b..b40c107a47 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -247,6 +247,24 @@ impl From<&Transaction> for TransactionRequest { } impl Transaction { + /// Create a dummy Transaction with zero values + pub fn dummy() -> Self { + Self { + from: Address::zero(), + to: Some(Address::zero()), + nonce: U64::zero(), + gas_limit: U64::zero(), + value: Word::zero(), + gas_price: Word::zero(), + gas_tip_cap: Word::zero(), + gas_fee_cap: Word::zero(), + call_data: Bytes::new(), + access_list: None, + v: 0, + r: Word::zero(), + s: Word::zero(), + } + } /// Return the SignData associated with this Transaction. pub fn sign_data(&self, chain_id: u64) -> Result { let sig_r_le = self.r.to_le_bytes(); @@ -277,8 +295,9 @@ impl Transaction { libsecp256k1::Error::InvalidMessage, )?; Ok(SignData { - signature: (sig_r, sig_s), + signature: (sig_r, sig_s, v), pk, + msg, msg_hash, }) } diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index 696b57bb6b..af5dd261c4 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -1,13 +1,15 @@ //! secp256k1 signature types and helper functions. use crate::{ToBigEndian, Word}; +use ethers_core::{ + types::{Address, Bytes}, + utils::keccak256, +}; use halo2_proofs::{ arithmetic::{CurveAffine, Field}, halo2curves::{ - group::{ - ff::{FromUniformBytes, PrimeField}, - Curve, - }, + ff::FromUniformBytes, + group::{ff::PrimeField, prime::PrimeCurveAffine, Curve}, secp256k1::{self, Secp256k1Affine}, Coordinates, }, @@ -21,7 +23,7 @@ pub fn sign( randomness: secp256k1::Fq, sk: secp256k1::Fq, msg_hash: secp256k1::Fq, -) -> (secp256k1::Fq, secp256k1::Fq) { +) -> (secp256k1::Fq, secp256k1::Fq, u8) { let randomness_inv = Option::::from(randomness.invert()).expect("cannot invert randomness"); let generator = Secp256k1Affine::generator(); @@ -38,34 +40,51 @@ pub fn sign( let sig_r = secp256k1::Fq::from_uniform_bytes(&x_bytes); // get x coordinate (E::Base) on E::Scalar let sig_s = randomness_inv * (msg_hash + sig_r * sk); - (sig_r, sig_s) + let sig_v = sig_point.to_affine().y.is_odd().unwrap_u8(); + (sig_r, sig_s, sig_v) } /// Signature data required by the SignVerify Chip as input to verify a /// signature. #[derive(Clone, Debug)] pub struct SignData { - /// Secp256k1 signature point - pub signature: (secp256k1::Fq, secp256k1::Fq), + /// Secp256k1 signature point (r, s, v) + /// v must be 0 or 1 + pub signature: (secp256k1::Fq, secp256k1::Fq, u8), /// Secp256k1 public key pub pk: Secp256k1Affine, + /// Message being hashed before signing. + pub msg: Bytes, /// Hash of the message that is being signed pub msg_hash: secp256k1::Fq, } +impl SignData { + /// Recover address of the signature + pub fn get_addr(&self) -> Address { + if self.pk == Secp256k1Affine::identity() { + return Address::zero(); + } + let pk_hash = keccak256(pk_bytes_swap_endianness(&pk_bytes_le(&self.pk))); + Address::from_slice(&pk_hash[12..]) + } +} + lazy_static! { static ref SIGN_DATA_DEFAULT: SignData = { let generator = Secp256k1Affine::generator(); let sk = secp256k1::Fq::ONE; let pk = generator * sk; let pk = pk.to_affine(); + let msg = Bytes::new(); let msg_hash = secp256k1::Fq::ONE; let randomness = secp256k1::Fq::ONE; - let (sig_r, sig_s) = sign(randomness, sk, msg_hash); + let (sig_r, sig_s, sig_v) = sign(randomness, sk, msg_hash); SignData { - signature: (sig_r, sig_s), + signature: (sig_r, sig_s, sig_v), pk, + msg, msg_hash, } }; diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index 639143f738..9631b36316 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -66,6 +66,8 @@ const MAX_EVM_ROWS: usize = 10000; const MAX_EXP_STEPS: usize = 1000; const MAX_KECCAK_ROWS: usize = 38000; +/// MAX_VERTICAL_CIRCUIT_ROWS +const MAX_VERTICAL_CIRCUIT_ROWS: usize = 0; const CIRCUITS_PARAMS: FixedCParams = FixedCParams { max_rws: MAX_RWS, @@ -77,6 +79,7 @@ const CIRCUITS_PARAMS: FixedCParams = FixedCParams { max_evm_rows: MAX_EVM_ROWS, max_exp_steps: MAX_EXP_STEPS, max_keccak_rows: MAX_KECCAK_ROWS, + max_vertical_circuit_rows: MAX_VERTICAL_CIRCUIT_ROWS, }; const EVM_CIRCUIT_DEGREE: u32 = 18; diff --git a/integration-tests/tests/circuit_input_builder.rs b/integration-tests/tests/circuit_input_builder.rs index 339f243d02..d2fbd6a22b 100644 --- a/integration-tests/tests/circuit_input_builder.rs +++ b/integration-tests/tests/circuit_input_builder.rs @@ -25,6 +25,7 @@ async fn test_circuit_input_builder_block(block_num: u64) { max_evm_rows: 0, max_exp_steps: 1000, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }, ) .await diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 1a6fa800b2..a2b2a3c849 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -337,6 +337,7 @@ pub fn run_test( max_evm_rows: 0, max_exp_steps: 5000, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; let block_data = BlockData::new_from_geth_data_with_params(geth_data, circuits_params); @@ -375,6 +376,7 @@ pub fn run_test( max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; let (k, circuit, instance, _builder) = SuperCircuit::::build(geth_data, circuits_params, Fr::from(0x100)).unwrap(); diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 475444f48f..3a64d19a9f 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -40,6 +40,9 @@ serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.78" thiserror = "1.0" hex = {version = "0.4.3", features = ["serde"]} +halo2-base = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } +halo2-ecc = { git = "https://github.com/scroll-tech/halo2-lib", branch = "develop", default-features=false, features=["halo2-pse","display"] } + [target.'cfg(target_arch = "wasm32")'.dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/zkevm-circuits/src/ecc_circuit.rs b/zkevm-circuits/src/ecc_circuit.rs new file mode 100644 index 0000000000..0764e19d76 --- /dev/null +++ b/zkevm-circuits/src/ecc_circuit.rs @@ -0,0 +1,1377 @@ +//! The ECC circuit is responsible for verifying ECC-related operations from precompiled contract +//! calls, namely, EcAdd, EcMul and EcPairing. + +use std::{iter, marker::PhantomData}; + +use bus_mapping::{ + circuit_input_builder::{EcAddOp, EcMulOp, EcPairingOp, N_BYTES_PER_PAIR, N_PAIRING_PER_OP}, + precompile::PrecompileCalls, +}; +use eth_types::{Field, ToLittleEndian, ToScalar, U256}; +use halo2_base::{ + gates::{GateInstructions, RangeInstructions}, + utils::{decompose_bigint_option, fe_to_biguint, modulus}, + AssignedValue, Context, QuantumCell, SKIP_FIRST_PASS, +}; +use halo2_ecc::{ + bigint::{big_is_zero, CRTInteger, OverflowInteger}, + bn254::pairing::PairingChip, + ecc::{EcPoint, EccChip}, + fields::{ + fp::{FpConfig, FpStrategy}, + fp12::Fp12Chip, + fp2::Fp2Chip, + FieldChip, FieldExtPoint, + }, +}; +use halo2_proofs::{ + arithmetic::Field as Halo2Field, + circuit::{Layouter, Value}, + halo2curves::{ + bn256::{Fq, Fq12, Fq2, Fr, G1Affine, G2Affine}, + CurveAffine, + }, + plonk::{ConstraintSystem, Error, Expression}, +}; +use itertools::Itertools; +use log::error; +use snark_verifier::util::arithmetic::PrimeCurveAffine; + +use crate::{ + evm_circuit::{param::N_BYTES_WORD, EvmCircuit}, + keccak_circuit::KeccakCircuit, + table::{EccTable, LookupTable}, + util::{Challenges, SubCircuit, SubCircuitConfig}, + witness::Block, +}; + +mod dev; +mod test; +mod util; + +use util::{ + EcAddAssigned, EcAddDecomposed, EcMulAssigned, EcMulDecomposed, EcOpsAssigned, + EcPairingAssigned, EcPairingDecomposed, G1Assigned, G1Decomposed, G2Decomposed, ScalarAssigned, + LOG_TOTAL_NUM_ROWS, +}; + +macro_rules! log_context_cursor { + ($ctx: ident) => {{ + log::trace!("Ctx cell pos: {:?}", $ctx.advice_alloc); + }}; +} + +/// Arguments accepted to configure the EccCircuitConfig. +#[derive(Clone, Debug)] +pub struct EccCircuitConfigArgs { + /// ECC table that is connected to the ECC circuit. + pub ecc_table: EccTable, + /// zkEVM challenge API. + pub challenges: Challenges>, +} + +/// Config for the ECC circuit. +#[derive(Clone, Debug)] +pub struct EccCircuitConfig { + /// Field config for halo2_proofs::halo2curves::bn256::Fq. + fp_config: FpConfig, + /// Lookup table for I/Os to the EcAdd, EcMul and EcPairing operations. + ecc_table: EccTable, + + /// Number of limbs to represent Fp. + num_limbs: usize, + /// Number of bits per limb. + limb_bits: usize, + + _marker: PhantomData, +} + +impl SubCircuitConfig for EccCircuitConfig { + type ConfigArgs = EccCircuitConfigArgs; + + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + ecc_table, + challenges: _, + }: Self::ConfigArgs, + ) -> Self { + let num_limbs = 3; + let limb_bits = 88; + let num_advice = [35, 1]; + + let fp_config = FpConfig::configure( + meta, + FpStrategy::Simple, + &num_advice, + &[17], // num lookup advice + 1, // num fixed + 13, // lookup bits + limb_bits, + num_limbs, + modulus::(), + 0, + LOG_TOTAL_NUM_ROWS as usize, // k + ); + + for column in >::advice_columns(&ecc_table) { + meta.enable_equality(column); + } + + Self { + fp_config, + ecc_table, + num_limbs, + limb_bits, + _marker: PhantomData, + } + } +} + +/// The ECC Circuit is a sub-circuit of the super circuit, responsible for verifying the following +/// ECC operations: +/// 1. Point Addition (R = P + Q) +/// 2. Scalar Multiplication (R = s.P) +/// 3. Pairing-based bilinear function +/// +/// We follow a strategy to pre-allocate maximum number of cells for each of the above ECC +/// operations, which means a witness that exceeds the pre-allocated number of cells for any of the +/// operations will be invalid. +#[derive(Clone, Debug, Default)] +pub struct EccCircuit { + /// Maximum number of EcAdd operations supported in one instance of the ECC Circuit. + pub max_add_ops: usize, + /// Maximum number of scalar multiplication operations supported in one instance of the ECC + /// Circuit. + pub max_mul_ops: usize, + /// Maximum number of pairing operations supported in one instance of the ECC Circuit. + pub max_pairing_ops: usize, + + /// EcAdd operations provided as witness data to the ECC circuit. + pub add_ops: Vec, + /// EcMul operations provided as witness data to the ECC circuit. + pub mul_ops: Vec, + /// EcPairing operations provided as witness data to the ECC circuit. + pub pairing_ops: Vec, + + _marker: PhantomData, +} + +impl EccCircuit { + /// Return the minimum number of rows required to prove an input of a + /// particular size. + pub fn min_num_rows() -> usize { + // EccCircuit can't determine usable rows independently. + // Instead, the blinding area is determined by other advise columns with most counts of + // rotation queries. This value is typically determined by either the Keccak or EVM + // circuit. + + let max_blinding_factor = Self::unusable_rows() - 1; + + // same formula as halo2-lib's FlexGate + (1 << LOG_TOTAL_NUM_ROWS) - (max_blinding_factor + 3) + } + + /// Assign witness from the ecXX ops to the circuit. + pub(crate) fn assign( + &self, + layouter: &mut impl Layouter, + config: &>::Config, + challenges: &Challenges>, + ) -> Result<(), Error> { + if self.add_ops.len() > self.max_add_ops + || self.mul_ops.len() > self.max_mul_ops + || self.pairing_ops.len() > self.max_pairing_ops + { + error!( + "add ops = {}, mul ops = {}, pairing ops = {} > max add ops = {}, max mul ops = {}, max pairing ops = {}", + self.add_ops.len(), + self.mul_ops.len(), + self.pairing_ops.len(), + self.max_add_ops, + self.max_mul_ops, + self.max_pairing_ops, + ); + return Err(Error::Synthesis); + } + + // keccak powers of randomness. + let keccak_powers = std::iter::successors(Some(Value::known(F::one())), |coeff| { + Some(challenges.keccak_input() * coeff) + }) + .take(N_PAIRING_PER_OP * N_BYTES_PER_PAIR) + .map(|x| QuantumCell::Witness(x)) + .collect_vec(); + + let powers_of_256 = iter::successors(Some(F::one()), |coeff| Some(F::from(256) * coeff)) + .take(N_BYTES_WORD) + .map(|x| QuantumCell::Constant(x)) + .collect_vec(); + + let ecc_chip = EccChip::>::construct(config.fp_config.clone()); + let fr_chip = FpConfig::::construct( + config.fp_config.range.clone(), + config.limb_bits, + config.num_limbs, + modulus::(), + ); + let pairing_chip = PairingChip::construct(config.fp_config.clone()); + let fp12_chip = + Fp12Chip::, Fq12, XI_0>::construct(config.fp_config.clone()); + + let mut first_pass = SKIP_FIRST_PASS; + + let assigned_ec_ops = layouter.assign_region( + || "ecc circuit", + |region| { + if first_pass { + first_pass = false; + return Ok(EcOpsAssigned::default()); + } + + let mut ctx = config.fp_config.new_context(region); + + macro_rules! decompose_ec_op { + ($op_type:ident, $ops:expr, $n_ops:expr, $decompose_fn:ident) => { + $ops.iter() + .filter(|op| !op.skip_by_ecc_circuit()) + .chain(std::iter::repeat(&$op_type::default())) + .take($n_ops) + .map(|op| { + self.$decompose_fn( + &mut ctx, + &ecc_chip, + &fr_chip, + &pairing_chip, + &fp12_chip, + &powers_of_256, + &op, + ) + }) + .collect_vec() + }; + } + + macro_rules! assign_ec_op { + ($decomposed_ops:expr, $assign_fn:ident) => { + $decomposed_ops + .iter() + .map(|decomposed_op| { + self.$assign_fn(&mut ctx, decomposed_op, &ecc_chip, &keccak_powers) + }) + .collect_vec() + }; + } + + // P + Q == R + let ec_adds_decomposed = + decompose_ec_op!(EcAddOp, self.add_ops, self.max_add_ops, decompose_ec_add_op); + + // s.P = R + let ec_muls_decomposed = + decompose_ec_op!(EcMulOp, self.mul_ops, self.max_mul_ops, decompose_ec_mul_op); + + // e(G1 . G2) * ... * e(G1 . G2) -> Gt + let ec_pairings_decomposed = decompose_ec_op!( + EcPairingOp, + self.pairing_ops, + self.max_pairing_ops, + decompose_ec_pairing_op + ); + + // finalize after first phase. + config.fp_config.finalize(&mut ctx); + ctx.next_phase(); + + let ec_adds_assigned = assign_ec_op!(ec_adds_decomposed, assign_ec_add); + let ec_muls_assigned = assign_ec_op!(ec_muls_decomposed, assign_ec_mul); + let ec_pairings_assigned = assign_ec_op!(ec_pairings_decomposed, assign_ec_pairing); + + // Finalize the Fp config always at the end of assignment. + let lookup_cells = config.fp_config.finalize(&mut ctx); + log::info!("total number of lookup cells: {}", lookup_cells); + ctx.print_stats(&["EccCircuit: FpConfig context"]); + + Ok(EcOpsAssigned { + ec_adds_assigned, + ec_muls_assigned, + ec_pairings_assigned, + }) + }, + )?; + + layouter.assign_region( + || "expose ecc table", + |mut region| { + // handle EcAdd ops. + for (idx, ec_add_assigned) in assigned_ec_ops.ec_adds_assigned.iter().enumerate() { + region.assign_fixed( + || "assign ecc_table op_type", + config.ecc_table.op_type, + idx, + || Value::known(F::from(u64::from(PrecompileCalls::Bn128Add))), + )?; + ec_add_assigned.is_valid.copy_advice( + &mut region, + config.ecc_table.is_valid, + idx, + ); + // P_x + ec_add_assigned.point_p.x_rlc.copy_advice( + &mut region, + config.ecc_table.arg1_rlc, + idx, + ); + // P_y + ec_add_assigned.point_p.y_rlc.copy_advice( + &mut region, + config.ecc_table.arg2_rlc, + idx, + ); + // Q_x + ec_add_assigned.point_q.x_rlc.copy_advice( + &mut region, + config.ecc_table.arg3_rlc, + idx, + ); + // Q_y + ec_add_assigned.point_q.y_rlc.copy_advice( + &mut region, + config.ecc_table.arg4_rlc, + idx, + ); + // R_x + ec_add_assigned.point_r.x_rlc.copy_advice( + &mut region, + config.ecc_table.output1_rlc, + idx, + ); + // R_y + ec_add_assigned.point_r.y_rlc.copy_advice( + &mut region, + config.ecc_table.output2_rlc, + idx, + ); + // input_rlc == 0 + region.assign_advice( + || format!("input_rlc at offset = {idx}"), + config.ecc_table.input_rlc, + idx, + || Value::known(F::zero()), + )?; + } + + // handle EcMul ops. + for (idx, ec_mul_assigned) in assigned_ec_ops.ec_muls_assigned.iter().enumerate() { + let idx = idx + self.max_add_ops; + region.assign_fixed( + || "assign ecc_table op_type", + config.ecc_table.op_type, + idx, + || Value::known(F::from(u64::from(PrecompileCalls::Bn128Mul))), + )?; + // Is valid + ec_mul_assigned.is_valid.copy_advice( + &mut region, + config.ecc_table.is_valid, + idx, + ); + // P_x + ec_mul_assigned.point_p.x_rlc.copy_advice( + &mut region, + config.ecc_table.arg1_rlc, + idx, + ); + // P_y + ec_mul_assigned.point_p.y_rlc.copy_advice( + &mut region, + config.ecc_table.arg2_rlc, + idx, + ); + // Scalar s + ec_mul_assigned.scalar_s.scalar.native.copy_advice( + &mut region, + config.ecc_table.arg3_rlc, + idx, + ); + // R_x + ec_mul_assigned.point_r.x_rlc.copy_advice( + &mut region, + config.ecc_table.output1_rlc, + idx, + ); + // R_y + ec_mul_assigned.point_r.y_rlc.copy_advice( + &mut region, + config.ecc_table.output2_rlc, + idx, + ); + for &col in [config.ecc_table.arg4_rlc, config.ecc_table.input_rlc].iter() { + region.assign_advice( + || format!("{col:?} at offset = {idx}"), + col, + idx, + || Value::known(F::zero()), + )?; + } + } + + // handle EcPairing ops. + for (idx, ec_pairing_assigned) in + assigned_ec_ops.ec_pairings_assigned.iter().enumerate() + { + let idx = idx + self.max_add_ops + self.max_mul_ops; + region.assign_fixed( + || "assign ecc_table op_type", + config.ecc_table.op_type, + idx, + || Value::known(F::from(u64::from(PrecompileCalls::Bn128Pairing))), + )?; + // is valid. + ec_pairing_assigned.is_valid.copy_advice( + &mut region, + config.ecc_table.is_valid, + idx, + ); + // RLC(input_bytes) + ec_pairing_assigned.input_rlc.copy_advice( + &mut region, + config.ecc_table.input_rlc, + idx, + ); + // success + ec_pairing_assigned.success.copy_advice( + &mut region, + config.ecc_table.output1_rlc, + idx, + ); + for &col in [ + config.ecc_table.arg1_rlc, + config.ecc_table.arg2_rlc, + config.ecc_table.arg3_rlc, + config.ecc_table.arg4_rlc, + config.ecc_table.output2_rlc, + ] + .iter() + { + region.assign_advice( + || format!("{col:?} at offset = {idx}"), + col, + idx, + || Value::known(F::zero()), + )?; + } + } + + Ok(()) + }, + )?; + + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn decompose_ec_add_op( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + _fr_chip: &FpConfig, + _pairing_chip: &PairingChip, + _fp12_chip: &Fp12Chip, Fq12, XI_0>, + powers_of_256: &[QuantumCell], + op: &EcAddOp, + ) -> EcAddDecomposed { + log::trace!("[ECC] ==> EcAdd Assignment START:"); + log_context_cursor!(ctx); + + let (px, px_cells, px_valid, px_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.p.0, powers_of_256); + let (py, py_cells, py_valid, py_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.p.1, powers_of_256); + let p_is_on_curve_or_infinity = + self.is_on_curveg1_or_infinity(ctx, ecc_chip, &px, px_is_zero, &py, py_is_zero); + let (qx, qx_cells, qx_valid, qx_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.q.0, powers_of_256); + let (qy, qy_cells, qy_valid, qy_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.q.1, powers_of_256); + let q_is_on_curve_or_infinity = + self.is_on_curveg1_or_infinity(ctx, ecc_chip, &qx, qx_is_zero, &qy, qy_is_zero); + + let point_p = EcPoint::construct(px, py); + let point_q = EcPoint::construct(qx, qy); + + let inputs_valid = ecc_chip.field_chip().range().gate().and_many( + ctx, + vec![ + QuantumCell::Existing(px_valid), + QuantumCell::Existing(py_valid), + QuantumCell::Existing(p_is_on_curve_or_infinity), + QuantumCell::Existing(qx_valid), + QuantumCell::Existing(qy_valid), + QuantumCell::Existing(q_is_on_curve_or_infinity), + ], + ); + let inputs_invalid = ecc_chip + .field_chip() + .range() + .gate() + .not(ctx, QuantumCell::Existing(inputs_valid)); + + log::trace!("[ECC] EcAdd Inputs Assigned:"); + log_context_cursor!(ctx); + + // We follow the approach mentioned below to handle many edge cases for the points P, Q and + // R so that we can maintain the same fixed and permutation columns and reduce the overall + // validation process from the EVM Circuit. + // + // To check the validity of P + Q == R, we check: + // r + P + Q - R == r + // where r is a random point on the curve. + // + // We cover cases such as: + // - P == (0, 0) and/or Q == (0, 0) + // - P == -Q, i.e. P + Q == R == (0, 0) + let res = op.r.unwrap_or(G1Affine::identity()); + let point_r = self.handle_g1(ctx, ecc_chip, res, powers_of_256); + let rx_is_zero = ecc_chip.field_chip.is_zero(ctx, &point_r.ec_point.x); + let ry_is_zero = ecc_chip.field_chip.is_zero(ctx, &point_r.ec_point.y); + + let rand_point = ecc_chip.load_random_point::(ctx); + let point_p_is_zero = ecc_chip.field_chip.range().gate().or_and( + ctx, + QuantumCell::Existing(inputs_invalid), + QuantumCell::Existing(px_is_zero), + QuantumCell::Existing(py_is_zero), + ); + let point_q_is_zero = ecc_chip.field_chip.range().gate().or_and( + ctx, + QuantumCell::Existing(inputs_invalid), + QuantumCell::Existing(qx_is_zero), + QuantumCell::Existing(qy_is_zero), + ); + let point_r_is_zero = ecc_chip.field_chip.range().gate().or_and( + ctx, + QuantumCell::Existing(inputs_invalid), + QuantumCell::Existing(rx_is_zero), + QuantumCell::Existing(ry_is_zero), + ); + + // sum1 = if P == (0, 0) then r else r + P + let sum1 = ecc_chip.add_unequal(ctx, &rand_point, &point_p, true); + let sum1 = ecc_chip.select(ctx, &rand_point, &sum1, &point_p_is_zero); + + // sum2 = if Q == (0, 0) then sum1 else sum1 + Q + let sum2 = ecc_chip.add_unequal(ctx, &sum1, &point_q, true); + let sum2 = ecc_chip.select(ctx, &sum1, &sum2, &point_q_is_zero); + + // sum3 = if R == (0, 0) then sum2 else sum2 - R + let sum3 = ecc_chip.sub_unequal(ctx, &sum2, &point_r.ec_point, true); + let sum3 = ecc_chip.select(ctx, &sum2, &sum3, &point_r_is_zero); + + ecc_chip.assert_equal(ctx, &rand_point, &sum3); + + log::trace!("[ECC] EcAdd Assignmnet END:"); + log_context_cursor!(ctx); + + EcAddDecomposed { + is_valid: inputs_valid, + point_p: G1Decomposed { + ec_point: point_p, + x_cells: px_cells, + y_cells: py_cells, + }, + point_q: G1Decomposed { + ec_point: point_q, + x_cells: qx_cells, + y_cells: qy_cells, + }, + point_r, + } + } + + /// Decomposes an EcMul operation to return each G1 element as cells representing its byte + /// form. + #[allow(clippy::too_many_arguments)] + fn decompose_ec_mul_op( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + fr_chip: &FpConfig, + _pairing_chip: &PairingChip, + _fp12_chip: &Fp12Chip, Fq12, XI_0>, + powers_of_256: &[QuantumCell], + op: &EcMulOp, + ) -> EcMulDecomposed { + log::trace!("[ECC] ==> EcMul Assignmnet START:"); + log_context_cursor!(ctx); + + let (px, px_cells, px_valid, px_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.p.0, powers_of_256); + let (py, py_cells, py_valid, py_is_zero) = + self.precheck_fq(ctx, ecc_chip, op.p.1, powers_of_256); + let p_is_on_curve_or_infinity = + self.is_on_curveg1_or_infinity(ctx, ecc_chip, &px, px_is_zero, &py, py_is_zero); + + // point at infinity + let infinity = EcPoint::construct( + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&Fq::zero())), + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&Fq::zero())), + ); + // for invalid case, take a random point. + let dummy_g1 = ecc_chip.load_random_point::(ctx); + + let point_p = EcPoint::construct(px, py); + let is_valid = ecc_chip.field_chip().range().gate().and_many( + ctx, + vec![ + QuantumCell::Existing(px_valid), + QuantumCell::Existing(py_valid), + QuantumCell::Existing(p_is_on_curve_or_infinity), + ], + ); + let point_p = ecc_chip.select(ctx, &point_p, &dummy_g1, &is_valid); + + let scalar_s = self.handle_fr(ctx, fr_chip, op.s); + + let res = op.r.unwrap_or(G1Affine::identity()); + let point_r = self.handle_g1(ctx, ecc_chip, res, powers_of_256); + + log::trace!("[ECC] EcMul Inputs Assigned:"); + log_context_cursor!(ctx); + + let point_r_got = ecc_chip.scalar_mult( + ctx, + &point_p, + &scalar_s.scalar.limbs().to_vec(), + fr_chip.limb_bits, + 4, + ); + let point_r_got = ecc_chip.select(ctx, &point_r_got, &infinity, &is_valid); + ecc_chip.assert_equal(ctx, &point_r.ec_point, &point_r_got); + + log::trace!("[ECC] EcMul Assignmnet END:"); + log_context_cursor!(ctx); + + EcMulDecomposed { + is_valid, + point_p: G1Decomposed { + ec_point: point_p, + x_cells: px_cells, + y_cells: py_cells, + }, + scalar_s, + point_r, + } + } + + /// Decomposes an EcPairing operation and returns cells that represent the LE-bytes of all + /// (G1, G2) pairs. In phase2 they will be RLC'd with the keccak randomness. + #[allow(clippy::too_many_arguments)] + fn decompose_ec_pairing_op( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + _fr_chip: &FpConfig, + pairing_chip: &PairingChip, + fp12_chip: &Fp12Chip, Fq12, XI_0>, + powers_of_256: &[QuantumCell], + op: &EcPairingOp, + ) -> EcPairingDecomposed { + log::trace!("[ECC] ==> EcPairing Assignment START:"); + log_context_cursor!(ctx); + + let fp2_chip = Fp2Chip::, Fq2>::construct(pairing_chip.fp_chip.clone()); + let ecc2_chip = EccChip::construct(fp2_chip.clone()); + + let decomposed_pairs = op + .pairs + .iter() + .map(|pair| { + // process x and y co-ordinates of G1. + let (g1x, g1x_cells, g1x_valid, g1x_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g1_point.0, powers_of_256); + let (g1y, g1y_cells, g1y_valid, g1y_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g1_point.1, powers_of_256); + let g1_point = EcPoint::>::construct(g1x, g1y); + let g1_is_on_curve_or_infinity = self.is_on_curveg1_or_infinity( + ctx, + ecc_chip, + &g1_point.x, + g1x_is_zero, + &g1_point.y, + g1y_is_zero, + ); + let is_g1_valid = ecc_chip.field_chip().range().gate().and_many( + ctx, + vec![ + QuantumCell::Existing(g1x_valid), + QuantumCell::Existing(g1y_valid), + QuantumCell::Existing(g1_is_on_curve_or_infinity), + ], + ); + let is_g1_identity = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(g1x_is_zero), + QuantumCell::Existing(g1y_is_zero), + ); + + // process x and y co-ordinates of G2. + let (g2x0, g2x0_cells, g2x0_valid, g2x0_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g2_point.1, powers_of_256); + let (g2x1, g2x1_cells, g2x1_valid, g2x1_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g2_point.0, powers_of_256); + let (g2y0, g2y0_cells, g2y0_valid, g2y0_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g2_point.3, powers_of_256); + let (g2y1, g2y1_cells, g2y1_valid, g2y1_is_zero) = + self.precheck_fq(ctx, ecc_chip, pair.g2_point.2, powers_of_256); + let g2_point = EcPoint::>>::construct( + FieldExtPoint::construct(vec![g2x0, g2x1]), + FieldExtPoint::construct(vec![g2y0, g2y1]), + ); + let g2x_is_zero = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(g2x0_is_zero), + QuantumCell::Existing(g2x1_is_zero), + ); + let g2y_is_zero = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(g2y0_is_zero), + QuantumCell::Existing(g2y1_is_zero), + ); + let g2_is_on_curve_or_infinity = self.is_on_curveg2_or_infinity( + ctx, + &fp2_chip, + &g2_point.x, + g2x_is_zero, + &g2_point.y, + g2y_is_zero, + ); + let is_g2_valid = ecc_chip.field_chip().range().gate().and_many( + ctx, + vec![ + QuantumCell::Existing(g2x0_valid), + QuantumCell::Existing(g2x1_valid), + QuantumCell::Existing(g2y0_valid), + QuantumCell::Existing(g2y1_valid), + QuantumCell::Existing(g2_is_on_curve_or_infinity), + ], + ); + let is_g2_identity = ecc_chip.field_chip().range().gate().and_many( + ctx, + vec![ + QuantumCell::Existing(g2x0_is_zero), + QuantumCell::Existing(g2x1_is_zero), + QuantumCell::Existing(g2y0_is_zero), + QuantumCell::Existing(g2y1_is_zero), + ], + ); + + // Whether the pair (G1, G2) is valid, i.e. + // - G1 is a point on curve or infinity. + // - G2 is a point on curve or infinity. + let is_pair_valid = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(is_g1_valid), + QuantumCell::Existing(is_g2_valid), + ); + // Whether the pair (G1, G2) is a zero pair, i.e. + // - G1 == G1::infinity && G2 is valid. + // - G2 == G2::infinity && G1 is valid. + let is_zero_pair = { + let is_zero_pair = ecc_chip.field_chip().range().gate().or( + ctx, + QuantumCell::Existing(is_g1_identity), + QuantumCell::Existing(is_g2_identity), + ); + ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(is_zero_pair), + QuantumCell::Existing(is_pair_valid), + ) + }; + + ( + is_zero_pair, + is_pair_valid, + G1Decomposed { + ec_point: g1_point, + x_cells: g1x_cells, + y_cells: g1y_cells, + }, + G2Decomposed { + ec_point: g2_point, + x_c0_cells: g2x0_cells, + x_c1_cells: g2x1_cells, + y_c0_cells: g2y0_cells, + y_c1_cells: g2y1_cells, + }, + ) + }) + .collect_vec(); + + log::trace!("[ECC] EcPairing g1s and g2s Assigned:"); + log_context_cursor!(ctx); + + // EVM input for EcPairing in Big-Endian representation, padded by 0 bytes so that the + // total number of bytes are N_PAIRING_PER_OP * N_BYTES_PER_PAIR. + let input_cells = decomposed_pairs + .iter() + .flat_map(|(_, _, g1, g2)| { + std::iter::empty() + .chain(g1.x_cells.iter().rev()) + .chain(g1.y_cells.iter().rev()) + .chain(g2.x_c1_cells.iter().rev()) + .chain(g2.x_c0_cells.iter().rev()) + .chain(g2.y_c1_cells.iter().rev()) + .chain(g2.y_c0_cells.iter().rev()) + .cloned() + .collect::>>() + }) + .collect::>>(); + + log::trace!("[ECC] EcPairing Inputs RLC Assigned:"); + log_context_cursor!(ctx); + + // Whether all the pairs are (G1::identity, G2::valid) or (G1::valid, G2::identity) form. + let all_pairs_zero = ecc_chip.field_chip().range().gate().and_many( + ctx, + decomposed_pairs + .iter() + .map(|(is_zero_pair, _, _, _)| QuantumCell::Existing(*is_zero_pair)) + .collect_vec(), + ); + + // dummy G1, G2 points and G1::identity, G2::generator. + let dummy_g1 = ecc_chip.load_random_point::(ctx); + let dummy_g2 = ecc2_chip.load_random_point::(ctx); + let identity_g1 = EcPoint::construct( + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&Fq::zero())), + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&Fq::zero())), + ); + let generator_g2 = { + let g2_gen = G2Affine::generator(); + EcPoint::>>::construct( + ecc2_chip.field_chip().load_constant(ctx, g2_gen.x), + ecc2_chip.field_chip().load_constant(ctx, g2_gen.y), + ) + }; + // A pairing op satisfying the pairing check. + type TupleG1sG2s = ( + Vec>>, + Vec>>>, + ); + let (dummy_pair_check_ok_g1s, dummy_pair_check_ok_g2s): TupleG1sG2s = + EcPairingOp::dummy_pairing_check_ok() + .pairs + .iter() + .map(|pair| { + let (g1_point, g2_point) = + pair.as_g1_g2().expect("dummy pairing check OK pair"); + ( + EcPoint::>::construct( + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&g1_point.x)), + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&g1_point.y)), + ), + EcPoint::>>::construct( + ecc2_chip.field_chip().load_constant(ctx, g2_point.x), + ecc2_chip.field_chip().load_constant(ctx, g2_point.y), + ), + ) + }) + .unzip(); + + // process pairs so that we pass only valid input to the multi_miller_loop. + let pairs = decomposed_pairs + .iter() + .enumerate() + .map(|(idx, (is_zero_pair, is_pair_valid, g1, g2))| { + // we should swap (G1, G2) with (G1::identity, G2::generator) if: + // - G1 == (0, 0) && G2 is valid + // - G2 == (0, 0, 0, 0) && G1 is valid + // + // we should swap (G1, G2) with (G1::random, G2::random) if: + // - G1 is invalid + // - G2 is invalid + ( + { + let swapped_g1 = + ecc_chip.select(ctx, &g1.ec_point, &dummy_g1, is_pair_valid); + let swapped_g1 = + ecc_chip.select(ctx, &identity_g1, &swapped_g1, is_zero_pair); + ecc_chip.select( + ctx, + &dummy_pair_check_ok_g1s[idx], + &swapped_g1, + &all_pairs_zero, + ) + }, + { + let swapped_x = + fp2_chip.select(ctx, &g2.ec_point.x, &dummy_g2.x, is_pair_valid); + let swapped_x = + fp2_chip.select(ctx, &generator_g2.x, &swapped_x, is_zero_pair); + let swapped_x = fp2_chip.select( + ctx, + &dummy_pair_check_ok_g2s[idx].x, + &swapped_x, + &all_pairs_zero, + ); + let swapped_y = + fp2_chip.select(ctx, &g2.ec_point.y, &dummy_g2.y, is_pair_valid); + let swapped_y = + fp2_chip.select(ctx, &generator_g2.y, &swapped_y, is_zero_pair); + let swapped_y = fp2_chip.select( + ctx, + &dummy_pair_check_ok_g2s[idx].y, + &swapped_y, + &all_pairs_zero, + ); + EcPoint::construct(swapped_x, swapped_y) + }, + ) + }) + .collect_vec(); + let pairs = pairs.iter().map(|(g1, g2)| (g1, g2)).collect_vec(); + + // if the entire input to ecPairing is valid. + let is_valid = ecc_chip.field_chip().range().gate().and_many( + ctx, + decomposed_pairs + .iter() + .map(|&(_, is_pair_valid, _, _)| QuantumCell::Existing(is_pair_valid)) + .collect_vec(), + ); + + // multi-miller loop and final exponentiation to do pairing check. + let success = { + let gt = { + let gt = pairing_chip.multi_miller_loop(ctx, pairs); + pairing_chip.final_exp(ctx, >) + }; + // whether pairing check was successful. + let one = fp12_chip.load_constant(ctx, Fq12::one()); + fp12_chip.is_equal(ctx, >, &one) + }; + // success == true only if pairing check and validity are both satisfied. + let success = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(is_valid), + QuantumCell::Existing(success), + ); + // if all inputs were zeroes, i.e. either: + // - G1 == (0, 0) and G2 == random valid point on G2 + // - G2 == (0, 0, 0, 0) and G1 == random valid point on G1 + // + // then success == true, i.e. success - all_pairs_zero == boolean + let success_minus_all_pairs_zero = ecc_chip + .field_chip() + .range() + .gate() + .load_witness(ctx, success.value - all_pairs_zero.value); + ecc_chip + .field_chip() + .range() + .gate() + .assert_bit(ctx, success_minus_all_pairs_zero); + + let op_output = ecc_chip.field_chip().range().gate().load_witness( + ctx, + Value::known(op.output.to_scalar().expect("EcPairing output = {0, 1}")), + ); + ecc_chip.field_chip().range().gate().assert_equal( + ctx, + QuantumCell::Existing(success), + QuantumCell::Existing(op_output), + ); + + log::trace!("[ECC] EcPairingAssignment END:"); + log_context_cursor!(ctx); + + EcPairingDecomposed { + is_valid, + input_cells, + success, + } + } + + /// Handles Phase2 for EcAdd operation and returns the RLC'd x and y co-ordinates of the G1 + /// elements. + fn assign_ec_add( + &self, + ctx: &mut Context, + ec_add_decomposed: &EcAddDecomposed, + ecc_chip: &EccChip>, + keccak_powers: &[QuantumCell], + ) -> EcAddAssigned { + EcAddAssigned { + is_valid: ec_add_decomposed.is_valid, + point_p: G1Assigned { + x_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_p.x_cells.clone(), + keccak_powers.iter().cloned(), + ), + y_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_p.y_cells.clone(), + keccak_powers.iter().cloned(), + ), + }, + point_q: G1Assigned { + x_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_q.x_cells.clone(), + keccak_powers.iter().cloned(), + ), + y_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_q.y_cells.clone(), + keccak_powers.iter().cloned(), + ), + }, + point_r: G1Assigned { + x_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_r.x_cells.clone(), + keccak_powers.iter().cloned(), + ), + y_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_add_decomposed.point_r.y_cells.clone(), + keccak_powers.iter().cloned(), + ), + }, + } + } + + /// Handles Phase2 for EcMul operation and returns the RLC'd x and y co-ordinates of the G1 + /// elements, and the assigned scalar field element. + fn assign_ec_mul( + &self, + ctx: &mut Context, + ec_mul_decomposed: &EcMulDecomposed, + ecc_chip: &EccChip>, + keccak_powers: &[QuantumCell], + ) -> EcMulAssigned { + EcMulAssigned { + is_valid: ec_mul_decomposed.is_valid, + point_p: G1Assigned { + x_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_mul_decomposed.point_p.x_cells.clone(), + keccak_powers.iter().cloned(), + ), + y_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_mul_decomposed.point_p.y_cells.clone(), + keccak_powers.iter().cloned(), + ), + }, + scalar_s: ec_mul_decomposed.scalar_s.clone(), + point_r: G1Assigned { + x_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_mul_decomposed.point_r.x_cells.clone(), + keccak_powers.iter().cloned(), + ), + y_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_mul_decomposed.point_r.y_cells.clone(), + keccak_powers.iter().cloned(), + ), + }, + } + } + + /// Handles Phase2 for EcPairing operation and returns the RLC'd input bytes. + fn assign_ec_pairing( + &self, + ctx: &mut Context, + ec_pairing_decomposed: &EcPairingDecomposed, + ecc_chip: &EccChip>, + keccak_powers: &[QuantumCell], + ) -> EcPairingAssigned { + EcPairingAssigned { + is_valid: ec_pairing_decomposed.is_valid, + input_rlc: ecc_chip.field_chip().range().gate().inner_product( + ctx, + ec_pairing_decomposed.input_cells.clone().into_iter().rev(), + keccak_powers.iter().cloned(), + ), + success: ec_pairing_decomposed.success, + } + } + + /// Handle G1 point and return its decomposed state. + fn handle_g1( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + g1: G1Affine, + powers_of_256: &[QuantumCell], + ) -> G1Decomposed { + let ec_point = ecc_chip.load_private(ctx, (Value::known(g1.x), Value::known(g1.y))); + let (x_cells, y_cells) = self.decompose_g1(g1); + self.assert_crt_repr(ctx, ecc_chip, &ec_point.x, &x_cells, powers_of_256); + self.assert_crt_repr(ctx, ecc_chip, &ec_point.y, &y_cells, powers_of_256); + G1Decomposed { + ec_point, + x_cells, + y_cells, + } + } + + /// Handle a scalar field element and return its assigned state. + fn handle_fr( + &self, + ctx: &mut Context, + fr_chip: &FpConfig, + s: Fr, + ) -> ScalarAssigned { + let scalar = fr_chip.load_private(ctx, FpConfig::::fe_to_witness(&Value::known(s))); + ScalarAssigned { scalar } + } + + /// Precheck a 32-bytes word input supposed to be bn256::Fq and return its CRT integer + /// representation. We also return the LE-bytes and assigned values to indicate whether the + /// value is within Fq::MODULUS and whether or not it is zero. + fn precheck_fq( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + word_value: U256, + powers_of_256: &[QuantumCell], + ) -> ( + CRTInteger, // CRT representation. + Vec>, // LE bytes as witness. + AssignedValue, // value < Fq::MODULUS + AssignedValue, // value == 0 + ) { + let value = Value::known(num_bigint::BigInt::from( + num_bigint::BigUint::from_bytes_le(&word_value.to_le_bytes()), + )); + let vec_value = decompose_bigint_option::( + value.as_ref(), + ecc_chip.field_chip.num_limbs, + ecc_chip.field_chip.limb_bits, + ); + let limbs = ecc_chip + .field_chip() + .range() + .gate() + .assign_witnesses(ctx, vec_value); + let native_value = OverflowInteger::evaluate( + ecc_chip.field_chip().range().gate(), + ctx, + &limbs, + ecc_chip.field_chip.limb_bases.iter().cloned(), + ); + let overflow_int = OverflowInteger::construct(limbs, ecc_chip.field_chip.limb_bits); + let crt_int = CRTInteger::construct(overflow_int, native_value, value); + let cells = word_value + .to_le_bytes() + .map(|b| QuantumCell::Witness(Value::known(F::from(b as u64)))); + self.assert_crt_repr(ctx, ecc_chip, &crt_int, &cells, powers_of_256); + let is_lt_mod = ecc_chip.field_chip().is_less_than_p(ctx, &crt_int); + let is_zero = big_is_zero::positive( + ecc_chip.field_chip().range().gate(), + ctx, + &crt_int.truncation, + ); + let is_zero = ecc_chip.field_chip().range().gate().and( + ctx, + QuantumCell::Existing(is_lt_mod), + QuantumCell::Existing(is_zero), + ); + (crt_int, cells.to_vec(), is_lt_mod, is_zero) + } + + /// Return an assigned value that indicates whether the given point is on curve G1 or identity + /// point. + fn is_on_curveg1_or_infinity( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + x: &CRTInteger, + x_is_zero: AssignedValue, + y: &CRTInteger, + y_is_zero: AssignedValue, + ) -> AssignedValue { + let lhs = ecc_chip.field_chip().mul_no_carry(ctx, y, y); + let mut rhs = ecc_chip.field_chip().mul(ctx, x, x); + rhs = ecc_chip.field_chip().mul_no_carry(ctx, &rhs, x); + + let b = FpConfig::::fe_to_constant(G1Affine::b()); + rhs = ecc_chip.field_chip().add_constant_no_carry(ctx, &rhs, b); + let mut diff = ecc_chip.field_chip().sub_no_carry(ctx, &lhs, &rhs); + diff = ecc_chip.field_chip().carry_mod(ctx, &diff); + + let is_on_curve = ecc_chip.field_chip().is_zero(ctx, &diff); + + ecc_chip.field_chip().range().gate().or_and( + ctx, + QuantumCell::Existing(is_on_curve), + QuantumCell::Existing(x_is_zero), + QuantumCell::Existing(y_is_zero), + ) + } + + /// Return an assigned value that indicates whether the given point is on curve G2 or identity + /// point. + fn is_on_curveg2_or_infinity( + &self, + ctx: &mut Context, + fp2_chip: &Fp2Chip, Fq2>, + x: &FieldExtPoint>, + x_is_zero: AssignedValue, + y: &FieldExtPoint>, + y_is_zero: AssignedValue, + ) -> AssignedValue { + let lhs = fp2_chip.mul_no_carry(ctx, y, y); + let mut rhs = fp2_chip.mul(ctx, x, x); + rhs = fp2_chip.mul_no_carry(ctx, &rhs, x); + + let b = Fp2Chip::, Fq2>::fe_to_constant(G2Affine::b()); + rhs = fp2_chip.add_constant_no_carry(ctx, &rhs, b); + let mut diff = fp2_chip.sub_no_carry(ctx, &lhs, &rhs); + diff = fp2_chip.carry_mod(ctx, &diff); + + let is_on_curve = fp2_chip.is_zero(ctx, &diff); + + fp2_chip.range().gate().or_and( + ctx, + QuantumCell::Existing(is_on_curve), + QuantumCell::Existing(x_is_zero), + QuantumCell::Existing(y_is_zero), + ) + } + + /// Assert that a CRT integer's bytes representation matches the limb values. + fn assert_crt_repr( + &self, + ctx: &mut Context, + ecc_chip: &EccChip>, + crt_int: &CRTInteger, + bytes: &[QuantumCell], + powers_of_256: &[QuantumCell], + ) { + debug_assert_eq!(bytes.len(), 32); + debug_assert!(powers_of_256.len() >= 11); + + let limbs = [ + bytes[0..11].to_vec(), + bytes[11..22].to_vec(), + bytes[22..32].to_vec(), + ] + .map(|limb_bytes| { + ecc_chip.field_chip().range().gate().inner_product( + ctx, + limb_bytes, + powers_of_256[0..11].to_vec(), + ) + }); + + for (&limb_recovered, &limb_value) in limbs.iter().zip_eq(crt_int.truncation.limbs.iter()) { + ecc_chip.field_chip().range().gate().assert_equal( + ctx, + QuantumCell::Existing(limb_recovered), + QuantumCell::Existing(limb_value), + ); + } + } + + /// Decompose G1 element into cells representing its x and y co-ordinates. + fn decompose_g1(&self, g1: G1Affine) -> (Vec>, Vec>) { + ( + g1.x.to_bytes() + .iter() + .map(|&x| QuantumCell::Witness(Value::known(F::from(u64::from(x))))) + .collect_vec(), + g1.y.to_bytes() + .iter() + .map(|&y| QuantumCell::Witness(Value::known(F::from(u64::from(y))))) + .collect_vec(), + ) + } +} + +impl SubCircuit for EccCircuit { + type Config = EccCircuitConfig; + + fn new_from_block(block: &Block) -> Self { + Self { + max_add_ops: block.circuits_params.max_ec_ops.ec_add, + max_mul_ops: block.circuits_params.max_ec_ops.ec_mul, + max_pairing_ops: block.circuits_params.max_ec_ops.ec_pairing, + add_ops: block.get_ec_add_ops(), + mul_ops: block.get_ec_mul_ops(), + pairing_ops: block.get_ec_pairing_ops(), + _marker: PhantomData, + } + } + + /// Returns number of unusable rows of the SubCircuit, which should be + /// `meta.blinding_factors() + 1`. + fn unusable_rows() -> usize { + [ + KeccakCircuit::::unusable_rows(), + EvmCircuit::::unusable_rows(), + // may include additional subcircuits here + ] + .into_iter() + .max() + .unwrap() + } + + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.fp_config.range.load_lookup_table(layouter)?; + self.assign(layouter, config, challenges)?; + Ok(()) + } + + fn min_num_rows_block(block: &Block) -> (usize, usize) { + let row_num = if block.circuits_params.max_vertical_circuit_rows == 0 { + Self::min_num_rows() + } else { + block.circuits_params.max_vertical_circuit_rows + }; + + let ec_adds = block.get_ec_add_ops().len(); + let ec_muls = block.get_ec_mul_ops().len(); + let ec_pairings = block.get_ec_pairing_ops().len(); + let max_ec_ops = &block.circuits_params.max_ec_ops; + log::debug!("ecc circuit row usage: ecadd {ec_adds}/{}, ecmul {ec_muls}/{}, ecpairing {ec_pairings}/{}", + max_ec_ops.ec_add, max_ec_ops.ec_mul, max_ec_ops.ec_pairing); + + // Instead of showing actual minimum row usage, + // halo2-lib based circuits use min_row_num to represent a percentage of total-used capacity + // This functionality allows l2geth to decide if additional ops can be added. + let min_row_num = [ + (row_num / max_ec_ops.ec_add) * ec_adds, + (row_num / max_ec_ops.ec_mul) * ec_muls, + (row_num / max_ec_ops.ec_pairing) * ec_pairings, + ] + .into_iter() + .max() + .unwrap(); + + (min_row_num, row_num) + } +} diff --git a/zkevm-circuits/src/ecc_circuit/dev.rs b/zkevm-circuits/src/ecc_circuit/dev.rs new file mode 100644 index 0000000000..21147349bd --- /dev/null +++ b/zkevm-circuits/src/ecc_circuit/dev.rs @@ -0,0 +1,46 @@ +use eth_types::Field; +use halo2_proofs::{ + circuit::{Layouter, SimpleFloorPlanner}, + plonk::{Challenge, Circuit, ConstraintSystem, Error}, +}; + +use crate::{ + table::EccTable, + util::{Challenges, SubCircuit, SubCircuitConfig}, +}; + +use super::{EccCircuit, EccCircuitConfig, EccCircuitConfigArgs}; + +impl Circuit for EccCircuit { + type Config = (EccCircuitConfig, Challenges); + type FloorPlanner = SimpleFloorPlanner; + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let ecc_table = EccTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenge_exprs = challenges.exprs(meta); + ( + EccCircuitConfig::new( + meta, + EccCircuitConfigArgs { + ecc_table, + challenges: challenge_exprs, + }, + ), + challenges, + ) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenge_values = config.1.values(&layouter); + self.synthesize_sub(&config.0, &challenge_values, &mut layouter) + } +} diff --git a/zkevm-circuits/src/ecc_circuit/test.rs b/zkevm-circuits/src/ecc_circuit/test.rs new file mode 100644 index 0000000000..4de6e9fc3b --- /dev/null +++ b/zkevm-circuits/src/ecc_circuit/test.rs @@ -0,0 +1,620 @@ +use std::{ + marker::PhantomData, + ops::{Add, Mul, Neg}, +}; + +use bus_mapping::circuit_input_builder::{ + EcAddOp, EcMulOp, EcPairingOp, EcPairingPair, PrecompileEcParams, +}; +use eth_types::{Field, U256}; +use halo2_proofs::{ + arithmetic::Field as ArithmeticField, + dev::MockProver, + halo2curves::bn256::{Fr, G1Affine, G2Affine}, +}; +use rand::{CryptoRng, Rng, RngCore}; + +use crate::ecc_circuit::EccCircuit; + +fn run( + k: u32, + max_ec_ops: PrecompileEcParams, + add_ops: Vec, + mul_ops: Vec, + pairing_ops: Vec, +) { + let circuit = EccCircuit:: { + max_add_ops: max_ec_ops.ec_add, + max_mul_ops: max_ec_ops.ec_mul, + max_pairing_ops: max_ec_ops.ec_pairing, + add_ops, + mul_ops, + pairing_ops, + _marker: PhantomData, + }; + + let prover = match MockProver::run(k, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + + if MUST_FAIL { + if let Ok(()) = prover.verify() { + panic!("expected failure, found success"); + } + } else if let Err(e) = prover.verify() { + panic!("{e:#?}"); + } +} + +trait GenRand { + fn gen_rand(r: &mut R, is_neg: bool) -> Self; +} + +impl GenRand for EcAddOp { + fn gen_rand(mut r: &mut R, is_neg: bool) -> Self { + let p = G1Affine::random(&mut r); + let q = G1Affine::random(&mut r); + let r = if is_neg { + G1Affine::random(&mut r) + } else { + p.add(&q).into() + }; + Self { + p: ( + U256::from_little_endian(&p.x.to_bytes()), + U256::from_little_endian(&p.y.to_bytes()), + ), + q: ( + U256::from_little_endian(&q.x.to_bytes()), + U256::from_little_endian(&q.y.to_bytes()), + ), + r: Some(r), + } + } +} + +impl GenRand for EcMulOp { + fn gen_rand(mut r: &mut R, is_neg: bool) -> Self { + let p = G1Affine::random(&mut r); + let s = match r.gen::() { + true => ::random(&mut r), + false => Fr::one(), + }; + let r = if is_neg { + G1Affine::random(&mut r) + } else { + p.mul(&s).into() + }; + Self { + p: ( + U256::from_little_endian(&p.x.to_bytes()), + U256::from_little_endian(&p.y.to_bytes()), + ), + s, + r: Some(r), + } + } +} + +impl GenRand for EcPairingOp { + fn gen_rand(mut r: &mut R, is_neg: bool) -> Self { + let alpha = Fr::random(&mut r); + let beta = Fr::random(&mut r); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_s = G1Affine::from(G1Affine::generator() * alpha * beta); + let point_t = G2Affine::generator(); + + let alpha = Fr::random(&mut r); + let beta = Fr::random(&mut r); + let point_a = G1Affine::from(G1Affine::generator() * alpha); + let point_a_negated = point_a.neg(); + let point_b = G2Affine::from(G2Affine::generator() * beta); + let point_c = G1Affine::from(G1Affine::generator() * alpha * beta); + let point_d = G2Affine::generator(); + + let mut pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair::new(point_s, point_t), + EcPairingPair::new(point_a_negated, point_b), + EcPairingPair::new(point_c, point_d), + ]; + let output = eth_types::U256::one(); + + if is_neg { + match r.gen::() { + // change output. + true => Self { + pairs, + output: eth_types::U256::one() - output, + ..Default::default() + }, + // change a point in one of the pairs. + false => { + let altered: G1Affine = point_p_negated.add(&G1Affine::generator()).into(); + pairs[0].g1_point.0 = U256::from_little_endian(&altered.x.to_bytes()); + pairs[0].g1_point.1 = U256::from_little_endian(&altered.y.to_bytes()); + Self { + pairs, + output, + ..Default::default() + } + } + } + } else { + Self { + pairs, + output, + ..Default::default() + } + } + } +} + +fn gen(mut r: &mut R, max_len: usize, is_neg: bool) -> Vec { + std::iter::repeat(0) + .take(max_len) + .map(move |_| T::gen_rand(&mut r, is_neg)) + .collect() +} + +mod valid_invalid_cases { + use super::*; + use eth_types::word; + use snark_verifier::util::arithmetic::PrimeCurveAffine; + use std::sync::LazyLock; + + pub(crate) static EC_ADD_OPS: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: P == Q == G1::generator + { + let p = G1Affine::generator(); + EcAddOp { + p: (U256::from(1), U256::from(2)), + q: (U256::from(1), U256::from(2)), + r: Some(p.add(&p).into()), + } + }, + // 2. invalid: P not on curve + EcAddOp { + p: (U256::from(2), U256::from(3)), + q: (U256::from(1), U256::from(2)), + r: None, + }, + // 3. valid: all zeroes + EcAddOp { + p: (U256::zero(), U256::zero()), + q: (U256::zero(), U256::zero()), + r: Some(G1Affine::identity()), + }, + // 4. invalid: Px and Py > Fq::MODULUS + EcAddOp { + p: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), /* p + 1 */ + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), /* p + 2 */ + ), + q: (U256::from(1), U256::from(2)), + r: None, + }, + // 5. valid: P == -Q + EcAddOp { + p: (U256::from(1), U256::from(2)), + q: ( + U256::from(1), + word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45"), + ), + r: Some(G1Affine::identity()), + }, + ] + }); + + pub(crate) static EC_MUL_OPS: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: P = G1::generator, s = 3 + EcMulOp { + p: (U256::from(1), U256::from(2)), + s: Fr::from(3), + r: Some({ + let p = G1Affine::generator(); + let s = Fr::from(3); + p.mul(s).into() + }), + }, + // 2. invalid: P = (2, 3), i.e. not on curve + EcMulOp { + p: (U256::from(2), U256::from(3)), + s: Fr::from(3), + r: None, + }, + // 3. invalid: P == (p + 1, p + 2), i.e. > Fq::MODULUS + EcMulOp { + p: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), /* p + 1 */ + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), /* p + 2 */ + ), + s: Fr::from(3), + r: None, + }, + ] + }); + pub(crate) static EC_PAIRING_OPS1: LazyLock> = LazyLock::new(|| { + vec![ + // 1. valid: pairing_check == 1 + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_s = G1Affine::from(G1Affine::generator() * alpha * beta); + let point_t = G2Affine::generator(); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair::new(point_s, point_t), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; + EcPairingOp { + pairs, + output: U256::one(), + ..Default::default() + } + }, + // 2. invalid: field element > Fq::MODULUS, mod p is OK + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_t = G2Affine::from(G2Affine::generator() * alpha * beta); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair { + g1_point: ( + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD48"), + word!("0x30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD49"), + ), + g2_point: ( + U256::from_little_endian(&point_t.x.c1.to_bytes()), + U256::from_little_endian(&point_t.x.c0.to_bytes()), + U256::from_little_endian(&point_t.y.c1.to_bytes()), + U256::from_little_endian(&point_t.y.c0.to_bytes()), + ), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; + EcPairingOp { + pairs, + output: U256::zero(), + ..Default::default() + } + }, + ] + }); + pub(crate) static EC_PAIRING_OPS2: LazyLock> = LazyLock::new(|| { + vec![ + // 3. valid: pairing_check == 0 + { + let alpha = Fr::from(0x102030); + let beta = Fr::from(0x413121); + let gamma = Fr::from(0x591242); + let point_p = G1Affine::from(G1Affine::generator() * alpha); + let point_p_negated = point_p.neg(); + let point_q = G2Affine::from(G2Affine::generator() * beta); + let point_s = G1Affine::from(G1Affine::generator() * gamma); + let point_t = G2Affine::generator(); + let pairs = [ + EcPairingPair::new(point_p_negated, point_q), + EcPairingPair::new(point_s, point_t), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ]; + EcPairingOp { + pairs, + output: U256::zero(), + ..Default::default() + } + }, + // 4. invalid: not on curve G1. + EcPairingOp { + pairs: [ + EcPairingPair { + g1_point: (U256::from(3), U256::from(4)), + g2_point: (U256::zero(), U256::zero(), U256::zero(), U256::zero()), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 0.into(), + ..Default::default() + }, + ] + }); + pub(crate) static EC_PAIRING_OPS3: LazyLock> = LazyLock::new(|| { + vec![ + // 5. invalid: not on curve G2. + EcPairingOp { + pairs: [ + EcPairingPair { + g1_point: (U256::zero(), U256::zero()), + g2_point: (U256::from(3), U256::from(4), U256::from(5), U256::from(6)), + }, + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 0.into(), + ..Default::default() + }, + // 6. valid: all zero. + EcPairingOp { + pairs: [ + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 1.into(), + ..Default::default() + }, + ] + }); + pub(crate) static EC_PAIRING_OPS4: LazyLock> = LazyLock::new(|| { + vec![ + // 7. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen)] + EcPairingOp { + pairs: [ + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + EcPairingPair::padding_pair(), + EcPairingPair::padding_pair(), + ], + output: 1.into(), + ..Default::default() + }, + // 8. valid: [(G1::gen, G2::gen), (-G1::gen, G2::gen); 2] + EcPairingOp { + pairs: [ + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator(), G2Affine::generator()), + EcPairingPair::new(G1Affine::generator().neg(), G2Affine::generator()), + ], + output: 1.into(), + ..Default::default() + }, + ] + }); +} + +#[test] +fn test_ecc_circuit_valid_invalid() { + use crate::ecc_circuit::util::LOG_TOTAL_NUM_ROWS; + use halo2_proofs::halo2curves::bn256::Fr; + use valid_invalid_cases::{ + EC_ADD_OPS, EC_MUL_OPS, EC_PAIRING_OPS1, EC_PAIRING_OPS2, EC_PAIRING_OPS3, EC_PAIRING_OPS4, + }; + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams::default(), + EC_ADD_OPS.clone(), + EC_MUL_OPS.clone(), + EC_PAIRING_OPS1.clone(), + ); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams { + ec_add: 0, + ec_mul: 0, + ec_pairing: 2, + }, + vec![], + vec![], + EC_PAIRING_OPS2.clone(), + ); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams { + ec_add: 0, + ec_mul: 0, + ec_pairing: 2, + }, + vec![], + vec![], + EC_PAIRING_OPS3.clone(), + ); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams { + ec_add: 0, + ec_mul: 0, + ec_pairing: 2, + }, + vec![], + vec![], + EC_PAIRING_OPS4.clone(), + ); +} + +#[ignore = "generate a lot of random invalid inputs for bn254 add"] +#[test] +fn test_invalid_ec_add() { + use crate::ecc_circuit::util::LOG_TOTAL_NUM_ROWS; + use halo2_proofs::halo2curves::{bn256::Fq, group::ff::PrimeField}; + use itertools::Itertools; + use num::Integer; + use rand::thread_rng; + + let u256_max = U256::from_little_endian(&[0xff; 32]); + + // 1. p is on g1 but p.x > p and p.y < p + // and p.x is close to 2^256 + // and q is generator + let mut rng = thread_rng(); + let get_g1_from_x_coordinate = |x: Fq| { + let b = Fq::from(3); + // y^2 = x^3 + b (mod Fq) + let y2 = x * x * x + b; + let y = Fq::sqrt(&y2); + y.map(|y| { + ( + U256::from_little_endian(&x.to_repr()), + U256::from_little_endian(&y.to_repr()), + ) + }) + }; + let ec_adds = (0..50) + .map(|_| { + let px = u256_max - (rng.next_u64() % 1024); + let x = Fq::from_raw(px.0); + (px, get_g1_from_x_coordinate(x)) + }) + .filter(|(_, p)| p.is_some().into()) + .map(|(px, p)| { + let p = (px, p.unwrap().1); + let q = (U256::from(1), U256::from(2)); + + EcAddOp { p, q, r: None } + }) + .collect_vec(); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams { + ec_add: ec_adds.len(), + ec_mul: 0, + ec_pairing: 0, + }, + ec_adds, + vec![], + vec![], + ); + + // 2. p is on g1 but p.x[i] is close to Fq::MODULUS.limbs[i] and p.y < p + // and q is generator + let ec_adds = (0..50) + .map(|_| { + let fq_limbs: [u64; 4] = [ + 0x3c208c16d87cfd47, + 0x97816a916871ca8d, + 0xb85045b68181585d, + 0x30644e72e131a029, + ]; + let mut px_limbs = [0u64; 4]; + px_limbs.iter_mut().enumerate().for_each(|(j, limb)| { + if j.is_odd() { + *limb = fq_limbs[j] + rng.next_u64() % 1024; + } else { + *limb = fq_limbs[j] - rng.next_u64() % 1024; + } + }); + let x = Fq::from_raw(px_limbs); + let px = U256(px_limbs); + (px, get_g1_from_x_coordinate(x)) + }) + .filter(|(_, p)| p.is_some().into()) + .map(|(px, p)| { + let p = (px, p.unwrap().1); + let q = (U256::from(1), U256::from(2)); + + EcAddOp { p, q, r: None } + }) + .collect_vec(); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams { + ec_add: ec_adds.len(), + ec_mul: 0, + ec_pairing: 0, + }, + ec_adds, + vec![], + vec![], + ); +} + +#[test] +fn test_ecc_circuit_positive() { + use crate::ecc_circuit::util::LOG_TOTAL_NUM_ROWS; + use halo2_proofs::halo2curves::bn256::Fr; + + let mut rng = rand::thread_rng(); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams::default(), + gen(&mut rng, 9, false), + gen(&mut rng, 9, false), + gen(&mut rng, 1, false), + ); +} + +#[test] +fn test_ecc_circuit_negative() { + use crate::ecc_circuit::util::LOG_TOTAL_NUM_ROWS; + use halo2_proofs::halo2curves::bn256::Fr; + + let mut rng = rand::thread_rng(); + + run::( + LOG_TOTAL_NUM_ROWS, + PrecompileEcParams::default(), + gen(&mut rng, 9, true), + gen(&mut rng, 9, true), + gen(&mut rng, 1, true), + ); +} + +#[test] +fn variadic_size_check() { + use crate::ecc_circuit::util::LOG_TOTAL_NUM_ROWS; + use halo2_proofs::halo2curves::bn256::Fr; + use valid_invalid_cases::{EC_ADD_OPS, EC_MUL_OPS, EC_PAIRING_OPS1, EC_PAIRING_OPS2}; + + let mut rng = rand::thread_rng(); + + let default_params = PrecompileEcParams::default(); + + let circuit = EccCircuit:: { + max_add_ops: default_params.ec_add, + max_mul_ops: default_params.ec_mul, + max_pairing_ops: default_params.ec_pairing, + add_ops: gen(&mut rng, 25, false), + mul_ops: gen(&mut rng, 20, false), + pairing_ops: EC_PAIRING_OPS1.clone(), + _marker: PhantomData, + }; + let prover1 = MockProver::::run(LOG_TOTAL_NUM_ROWS, &circuit, vec![]).unwrap(); + + let circuit = EccCircuit:: { + max_add_ops: default_params.ec_add, + max_mul_ops: default_params.ec_mul, + max_pairing_ops: default_params.ec_pairing, + add_ops: { + let mut ops = gen(&mut rng, 30, false); + ops.extend_from_slice(&EC_ADD_OPS); + ops + }, + mul_ops: { + let mut ops = gen(&mut rng, 30, false); + ops.extend_from_slice(&EC_MUL_OPS); + ops + }, + pairing_ops: EC_PAIRING_OPS2.clone(), + _marker: PhantomData, + }; + let prover2 = MockProver::::run(LOG_TOTAL_NUM_ROWS, &circuit, vec![]).unwrap(); + + assert_eq!(prover1.fixed(), prover2.fixed()); + assert_eq!(prover1.permutation(), prover2.permutation()); +} diff --git a/zkevm-circuits/src/ecc_circuit/util.rs b/zkevm-circuits/src/ecc_circuit/util.rs new file mode 100644 index 0000000000..92c49c35d3 --- /dev/null +++ b/zkevm-circuits/src/ecc_circuit/util.rs @@ -0,0 +1,107 @@ +use eth_types::Field; +use halo2_base::{AssignedValue, QuantumCell}; +use halo2_ecc::{bigint::CRTInteger, ecc::EcPoint, fields::FieldExtPoint}; + +// Total number of rows allowable for ECC circuit +pub const LOG_TOTAL_NUM_ROWS: u32 = 20; + +// Cell usage accounting for EcAdd, EcMul and EcPairing +// Roud up to nearest 100 +pub(super) const EC_ADD_CELLS: usize = 6_900; // actual: 6_851 +pub(super) const EC_MUL_CELLS: usize = 405_500; // actual: 405_476 +pub(super) const EC_PAIRING_CELLS: usize = 6_627_500; // actual: 6_627_442 +pub(super) const COLUMN_NUM_LIMIT: usize = 150; // Max number of columns allowed + +/// Decomposed state of a G1 curve point. +pub(super) struct G1Decomposed { + /// EcPoint on G1. + pub ec_point: EcPoint>, + /// Cells for the x-coordinate of the G1 curve point in LE format. + pub x_cells: Vec>, + /// Cells for the y-coordinate of the G1 curve point in LE format. + pub y_cells: Vec>, +} + +/// State of the G1 curve point after RLC operations. +pub(super) struct G1Assigned { + /// RLC of x-coordinate that will be copied to the ECC Table. + pub x_rlc: AssignedValue, + /// RLC of y-coordinate that will be copied to the ECC Table. + pub y_rlc: AssignedValue, +} + +/// State of a scalar field element. Since the scalar fits within the field, we don't need to take +/// RLC over its bytes/cells. Hence we can decompose and assign the scalar in the first phase. +#[derive(Clone)] +pub(super) struct ScalarAssigned { + pub scalar: CRTInteger, +} + +/// Decomposed state of a G2 curve point. +pub(super) struct G2Decomposed { + /// The assigned EcPoint. + pub ec_point: EcPoint>>, + /// Cells for the coeff 0 of x-coordinate of the G2 curve point in LE format. + pub x_c0_cells: Vec>, + /// Cells for the coeff 1 of x-coordinate of the G2 curve point in LE format. + pub x_c1_cells: Vec>, + /// Cells for the coeff 0 of y-coordinate of the G2 curve point in LE format. + pub y_c0_cells: Vec>, + /// Cells for the coeff 1 of y-coordinate of the G2 curve point in LE format. + pub y_c1_cells: Vec>, +} + +/// State of EcAdd operation post first phase. +pub(super) struct EcAddDecomposed { + pub is_valid: AssignedValue, + pub point_p: G1Decomposed, + pub point_q: G1Decomposed, + pub point_r: G1Decomposed, +} + +/// State of EcAdd operation post second phase. +pub(super) struct EcAddAssigned { + pub is_valid: AssignedValue, + pub point_p: G1Assigned, + pub point_q: G1Assigned, + pub point_r: G1Assigned, +} + +/// State of EcMul operation post first phase. +pub(super) struct EcMulDecomposed { + pub is_valid: AssignedValue, + pub point_p: G1Decomposed, + pub scalar_s: ScalarAssigned, + pub point_r: G1Decomposed, +} + +/// State of EcMul operation post second phase. +pub(super) struct EcMulAssigned { + pub is_valid: AssignedValue, + pub point_p: G1Assigned, + pub scalar_s: ScalarAssigned, + pub point_r: G1Assigned, +} + +/// State of EcPairing operation post first phase. +pub(super) struct EcPairingDecomposed { + pub is_valid: AssignedValue, + pub input_cells: Vec>, + pub success: AssignedValue, +} + +/// State of EcPairing operation post second phase. +pub(super) struct EcPairingAssigned { + pub is_valid: AssignedValue, + /// RLC of (G1, G2) pairs. + pub input_rlc: AssignedValue, + pub success: AssignedValue, +} + +/// Wrapper struct that holds all the ecXX states post second phase. +#[derive(Default)] +pub(super) struct EcOpsAssigned { + pub ec_adds_assigned: Vec>, + pub ec_muls_assigned: Vec>, + pub ec_pairings_assigned: Vec>, +} diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 7afff29d6d..6e1e5b1147 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -21,8 +21,8 @@ pub use crate::witness; use crate::{ evm_circuit::param::{MAX_STEP_HEIGHT, STEP_STATE_HEIGHT}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, RwTable, TxTable, - UXTable, + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, RwTable, + SigTable, TxTable, UXTable, }, util::{Challenges, SubCircuit, SubCircuitConfig}, }; @@ -50,6 +50,7 @@ pub struct EvmCircuitConfig { copy_table: CopyTable, keccak_table: KeccakTable, exp_table: ExpTable, + sig_table: SigTable, } /// Circuit configuration arguments @@ -74,6 +75,8 @@ pub struct EvmCircuitConfigArgs { pub u8_table: UXTable<8>, /// U16Table pub u16_table: UXTable<16>, + /// SigTable + pub sig_table: SigTable, /// Feature config pub feature_config: FeatureConfig, } @@ -95,6 +98,7 @@ impl SubCircuitConfig for EvmCircuitConfig { exp_table, u8_table, u16_table, + sig_table, feature_config, }: Self::ConfigArgs, ) -> Self { @@ -112,6 +116,7 @@ impl SubCircuitConfig for EvmCircuitConfig { ©_table, &keccak_table, &exp_table, + &sig_table, feature_config, )); @@ -129,6 +134,7 @@ impl SubCircuitConfig for EvmCircuitConfig { exp_table.annotate_columns(meta); u8_table.annotate_columns(meta); u16_table.annotate_columns(meta); + sig_table.annotate_columns(meta); Self { fixed_table, @@ -142,6 +148,7 @@ impl SubCircuitConfig for EvmCircuitConfig { copy_table, keccak_table, exp_table, + sig_table, } } } @@ -389,6 +396,7 @@ impl Circuit for EvmCircuit { let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); + let sig_table = SigTable::construct(meta); ( EvmCircuitConfig::new( meta, @@ -403,6 +411,7 @@ impl Circuit for EvmCircuit { exp_table, u8_table, u16_table, + sig_table, feature_config: params, }, ), @@ -448,6 +457,7 @@ impl Circuit for EvmCircuit { config.u8_table.load(&mut layouter)?; config.u16_table.load(&mut layouter)?; + config.sig_table.dev_load(&mut layouter, block)?; self.synthesize_sub(&config, &challenges, &mut layouter) } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 1f7ed1c19e..0e106b8412 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -2,7 +2,7 @@ use super::{ param::{ BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_U16_LOOKUPS, - N_U8_LOOKUPS, RW_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, + N_U8_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, }, step::HasExecutionState, util::{instrumentation::Instrument, CachedRegion, StoredExpression}, @@ -190,7 +190,7 @@ use opcode_not::NotGadget; use origin::OriginGadget; use pc::PcGadget; use pop::PopGadget; -use precompiles::IdentityGadget; +use precompiles::{EcrecoverGadget, IdentityGadget}; use push::PushGadget; use return_revert::ReturnRevertGadget; use returndatacopy::ReturnDataCopyGadget; @@ -337,6 +337,8 @@ pub struct ExecutionConfig { error_invalid_creation_code: Box>, error_precompile_failed: Box>, error_return_data_out_of_bound: Box>, + // precompile calls + precompile_ecrecover_gadget: Box>, precompile_identity_gadget: Box>, invalid_tx: Option>>, } @@ -357,6 +359,7 @@ impl ExecutionConfig { copy_table: &dyn LookupTable, keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, + sig_table: &dyn LookupTable, feature_config: FeatureConfig, ) -> Self { let mut instrument = Instrument::default(); @@ -615,6 +618,7 @@ impl ExecutionConfig { error_return_data_out_of_bound: configure_gadget!(), // precompile calls precompile_identity_gadget: configure_gadget!(), + precompile_ecrecover_gadget: configure_gadget!(), // step and presets step: step_curr, height_map, @@ -635,6 +639,7 @@ impl ExecutionConfig { copy_table, keccak_table, exp_table, + sig_table, &challenges, &cell_manager, ); @@ -880,6 +885,7 @@ impl ExecutionConfig { copy_table: &dyn LookupTable, keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, + sig_table: &dyn LookupTable, challenges: &Challenges>, cell_manager: &CellManager, ) { @@ -899,6 +905,7 @@ impl ExecutionConfig { Table::Copy => copy_table, Table::Keccak => keccak_table, Table::Exp => exp_table, + Table::Sig => sig_table, } .table_exprs(meta); vec![( @@ -1110,6 +1117,7 @@ impl ExecutionConfig { ("EVM_lookup_copy", COPY_TABLE_LOOKUPS), ("EVM_lookup_keccak", KECCAK_TABLE_LOOKUPS), ("EVM_lookup_exp", EXP_TABLE_LOOKUPS), + ("EVM_lookup_sig", SIG_TABLE_LOOKUPS), ("EVM_adv_phase2", N_PHASE2_COLUMNS), ("EVM_copy", N_COPY_COLUMNS), ("EVM_lookup_u8", N_U8_LOOKUPS), @@ -1410,7 +1418,9 @@ impl ExecutionConfig { ExecutionState::ErrorPrecompileFailed => { assign_exec_step!(self.error_precompile_failed) } - // precompile calls + ExecutionState::PrecompileEcrecover => { + assign_exec_step!(self.precompile_ecrecover_gadget) + } ExecutionState::PrecompileIdentity => { assign_exec_step!(self.precompile_identity_gadget) } diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs index 5242dbc363..6514a47790 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs @@ -64,10 +64,10 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { // calculate required gas for precompile let precompiles_required_gas = [ - // ( - // addr_bits.value_equals(PrecompileCalls::ECRecover), - // GasCost::PRECOMPILE_ECRECOVER_BASE.expr(), - // ), + ( + addr_bits.value_equals(PrecompileCalls::Ecrecover), + GasCost::PRECOMPILE_ECRECOVER_BASE.expr(), + ), // addr_bits.value_equals(PrecompileCalls::Sha256), // addr_bits.value_equals(PrecompileCalls::Ripemd160), // addr_bits.value_equals(PrecompileCalls::Blake2F), @@ -181,6 +181,7 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { // required_gas let precompile_call: PrecompileCalls = precompile_addr.to_fixed_bytes()[19].into(); let required_gas = match precompile_call { + PrecompileCalls::Ecrecover => precompile_call.base_gas_cost(), // PrecompileCalls::Bn128Pairing => { // precompile_call.base_gas_cost() // + n_pairs * GasCost::PRECOMPILE_BN256PAIRING_PER_PAIR @@ -189,8 +190,8 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { let n_words = (call.call_data_length + 31) / 32; precompile_call.base_gas_cost() + n_words * GasCost::PRECOMPILE_IDENTITY_PER_WORD } - // PrecompileCalls::Bn128Add | PrecompileCalls::Bn128Mul | PrecompileCalls::ECRecover => - // { precompile_call.base_gas_cost() + // PrecompileCalls::Bn128Add | PrecompileCalls::Bn128Mul => { + // precompile_call.base_gas_cost() // } _ => unreachable!(), }; diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs new file mode 100644 index 0000000000..1e14430c99 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -0,0 +1,575 @@ +use bus_mapping::precompile::{PrecompileAuxData, PrecompileCalls}; +use eth_types::{evm_types::GasCost, word, Field, ToLittleEndian, ToScalar, U256}; +use ethers_core::k256::elliptic_curve::PrimeField; +use gadgets::util::{and, not, or, select, Expr}; +use halo2_proofs::{circuit::Value, halo2curves::secp256k1::Fq, plonk::Error}; + +use crate::{ + evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + util::{ + common_gadget::RestoreContextGadget, + constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, + from_bytes, + math_gadget::{IsEqualGadget, IsZeroGadget, IsZeroWordGadget, LtWordGadget, ModGadget}, + CachedRegion, Cell, + }, + }, + table::CallContextFieldTag, + util::word::{Word32Cell, WordExpr, WordLimbs, WordLoHi, WordLoHiCell}, + witness::{Block, Call, ExecStep, Transaction}, +}; + +#[derive(Clone, Debug)] +pub struct EcrecoverGadget { + is_recovered: Cell, + recovered_addr: Cell, + + fq_modulus: Word32Cell, + msg_hash: Word32Cell, + msg_hash_raw: Word32Cell, + msg_hash_mod: ModGadget, + sig_r: Word32Cell, + sig_s: Word32Cell, + sig_v: WordLoHiCell, + + sig_r_canonical: LtWordGadget, + sig_s_canonical: LtWordGadget, + is_zero_sig_r: IsZeroWordGadget>, + is_zero_sig_s: IsZeroWordGadget>, + + is_zero_sig_v_hi: IsZeroGadget, + is_sig_v_27: IsEqualGadget, + is_sig_v_28: IsEqualGadget, + + is_success: Cell, + callee_address: Cell, + caller_id: Cell, + restore_context: RestoreContextGadget, +} + +impl ExecutionGadget for EcrecoverGadget { + const EXECUTION_STATE: ExecutionState = ExecutionState::PrecompileEcrecover; + const NAME: &'static str = "ECRECOVER"; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + let is_recovered = cb.query_bool(); + let recovered_addr = cb.query_cell(); + + let fq_modulus = cb.query_word32(); + let msg_hash = cb.query_word32(); + let msg_hash_raw = cb.query_word32(); + let sig_r = cb.query_word32(); + let sig_s = cb.query_word32(); + let sig_v = cb.query_word_unchecked(); + + let msg_hash_mod = ModGadget::construct(cb, [&msg_hash_raw, &fq_modulus, &msg_hash]); + + // verify sig_r and sig_s + // the range is 0 < sig_r/sig_s < Fq::MODULUS + let mut sig_r_be = sig_r.limbs.clone(); + let mut sig_s_be = sig_s.limbs.clone(); + sig_r_be.reverse(); + sig_s_be.reverse(); + let sig_r_canonical = LtWordGadget::construct( + cb, + &WordLimbs::new(sig_r_be.clone()).to_word(), + &fq_modulus.to_word(), + ); + let sig_s_canonical = LtWordGadget::construct( + cb, + &WordLimbs::new(sig_s_be.clone()).to_word(), + &fq_modulus.to_word(), + ); + let is_zero_sig_r = IsZeroWordGadget::construct(cb, &sig_r); + let is_zero_sig_s = IsZeroWordGadget::construct(cb, &sig_s); + let is_valid_r_s = and::expr([ + sig_r_canonical.expr(), + sig_s_canonical.expr(), + not::expr(or::expr([is_zero_sig_r.expr(), is_zero_sig_s.expr()])), + ]); + + // sig_v is valid if sig_v == 27 || sig_v == 28 + let is_zero_sig_v_hi = IsZeroGadget::construct(cb, sig_v.hi().expr()); + let is_sig_v_27 = IsEqualGadget::construct(cb, sig_v.lo().expr(), 27.expr()); + let is_sig_v_28 = IsEqualGadget::construct(cb, sig_v.lo().expr(), 28.expr()); + let is_valid_sig_v = and::expr([ + or::expr([is_sig_v_27.expr(), is_sig_v_28.expr()]), + is_zero_sig_v_hi.expr(), + ]); + + let [is_success, callee_address, caller_id] = [ + CallContextFieldTag::IsSuccess, + CallContextFieldTag::CalleeAddress, + CallContextFieldTag::CallerId, + ] + .map(|tag| cb.call_context(None, tag)); + + let input_len = PrecompileCalls::Ecrecover.input_len().unwrap(); + for (field_tag, value) in [ + (CallContextFieldTag::CallDataOffset, 0.expr()), + (CallContextFieldTag::CallDataLength, input_len.expr()), + ( + CallContextFieldTag::ReturnDataOffset, + select::expr(is_recovered.expr(), input_len.expr(), 0.expr()), + ), + ( + CallContextFieldTag::ReturnDataLength, + select::expr(is_recovered.expr(), 32.expr(), 0.expr()), + ), + ] { + cb.call_context_lookup_read(None, field_tag, WordLoHi::from_lo_unchecked(value)); + } + + let gas_cost = select::expr( + is_success.expr(), + GasCost::PRECOMPILE_ECRECOVER_BASE.expr(), + cb.curr.state.gas_left.expr(), + ); + + // lookup to the sign_verify table: + let is_valid_sig = and::expr([is_valid_r_s.expr(), is_valid_sig_v.expr()]); + cb.condition(is_valid_sig.expr(), |cb| { + let mut msg_hash_le = msg_hash.limbs.clone(); + msg_hash_le.reverse(); + cb.sig_table_lookup( + WordLimbs::new(msg_hash_le).to_word(), + sig_v.lo().expr() - 27.expr(), + sig_r.to_word(), + sig_s.to_word(), + select::expr(is_recovered.expr(), recovered_addr.expr(), 0.expr()), + is_recovered.expr(), + ); + }); + + cb.condition(not::expr(is_valid_sig.expr()), |cb| { + cb.require_zero( + "is_recovered == false if r, s or v not canonical", + is_recovered.expr(), + ); + }); + + cb.condition(not::expr(is_recovered.expr()), |cb| { + cb.require_zero( + "address == 0 if address could not be recovered", + recovered_addr.expr(), + ); + }); + + cb.precompile_info_lookup( + cb.execution_state().as_u64().expr(), + callee_address.expr(), + cb.execution_state().precompile_base_gas_cost().expr(), + ); + + let restore_context = RestoreContextGadget::construct2( + cb, + is_success.expr(), + gas_cost.expr(), + 0.expr(), + 0.expr(), + select::expr(is_recovered.expr(), 32.expr(), 0.expr()), + 0.expr(), + 0.expr(), + ); + + Self { + is_recovered, + recovered_addr, + fq_modulus, + + msg_hash, + msg_hash_raw, + msg_hash_mod, + sig_r, + sig_s, + sig_v, + + sig_r_canonical, + sig_s_canonical, + is_zero_sig_v_hi, + is_zero_sig_r, + is_zero_sig_s, + is_sig_v_27, + is_sig_v_28, + + is_success, + callee_address, + caller_id, + restore_context, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + _tx: &Transaction, + call: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + if let Some(PrecompileAuxData::Ecrecover(aux_data)) = &step.aux_data { + let recovered = !aux_data.recovered_addr.is_zero(); + self.is_recovered + .assign(region, offset, Value::known(F::from(recovered as u64)))?; + let mut recovered_addr = aux_data.recovered_addr.to_fixed_bytes(); + recovered_addr.reverse(); + self.recovered_addr.assign( + region, + offset, + Value::known(from_bytes::value(&recovered_addr)), + )?; + self.fq_modulus + .assign_u256(region, offset, word!(Fq::MODULUS))?; + + let sig_r = U256::from(aux_data.sig_r.to_le_bytes()); + let sig_s = U256::from(aux_data.sig_s.to_le_bytes()); + self.sig_r.assign_u256(region, offset, sig_r)?; + self.sig_s.assign_u256(region, offset, sig_s)?; + self.sig_v.assign_u256(region, offset, aux_data.sig_v)?; + + let (quotient, remainder) = aux_data.msg_hash.div_mod(word!(Fq::MODULUS)); + self.msg_hash_raw + .assign_u256(region, offset, aux_data.msg_hash)?; + self.msg_hash.assign_u256(region, offset, remainder)?; + self.msg_hash_mod.assign( + region, + offset, + aux_data.msg_hash, + word!(Fq::MODULUS), + remainder, + quotient, + )?; + + self.sig_r_canonical + .assign(region, offset, aux_data.sig_r, word!(Fq::MODULUS))?; + self.sig_s_canonical + .assign(region, offset, aux_data.sig_s, word!(Fq::MODULUS))?; + self.is_zero_sig_r.assign_u256(region, offset, sig_r)?; + self.is_zero_sig_s.assign_u256(region, offset, sig_s)?; + + let sig_v_bytes = aux_data.sig_v.to_le_bytes(); + self.is_zero_sig_v_hi + .assign(region, offset, from_bytes::value(&sig_v_bytes[16..]))?; + self.is_sig_v_27 + .assign(region, offset, F::from(sig_v_bytes[0] as u64), F::from(27))?; + self.is_sig_v_28 + .assign(region, offset, F::from(sig_v_bytes[0] as u64), F::from(28))?; + } + + self.is_success.assign( + region, + offset, + Value::known(F::from(u64::from(call.is_success))), + )?; + + self.callee_address.assign( + region, + offset, + Value::known(call.code_address().unwrap().to_scalar().unwrap()), + )?; + self.caller_id + .assign(region, offset, Value::known(F::from(call.caller_id as u64)))?; + + self.restore_context + .assign(region, offset, block, call, step, 7) + } +} + +#[cfg(test)] +mod test { + use bus_mapping::{ + evm::OpcodeId, + precompile::{PrecompileCallArgs, PrecompileCalls}, + }; + use eth_types::{bytecode, word, ToWord}; + use mock::TestContext; + // use rayon::{iter::ParallelIterator, prelude::IntoParallelRefIterator}; + + use crate::test_util::CircuitTestBuilder; + + lazy_static::lazy_static! { + static ref TEST_VECTOR: Vec = { + vec![ + PrecompileCallArgs { + name: "ecrecover (valid sig, addr recovered)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 128 bytes from memory addr 0. Address is recovered and the signature is + // valid. + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + + PrecompileCallArgs { + name: "ecrecover (overflowing msg_hash)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + + PrecompileCallArgs { + name: "ecrecover (invalid overflowing sig_r)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x00.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + + PrecompileCallArgs { + name: "ecrecover (invalid overflowing sig_s)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x00.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + + PrecompileCallArgs { + name: "ecrecover (invalid v > 28, single byte)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(29) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x00.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + + PrecompileCallArgs { + name: "ecrecover (invalid v < 27, single byte)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(26) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x00.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + PrecompileCallArgs { + name: "ecrecover (invalid v, multi-byte, last byte == 28)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20, 1c == 28 (but not single byte) + PUSH32(word!("0x010000000000000000000000000000000000000000000000000000000000001c")) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x00.into(), + ret_size: 0x00.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }, + ] + }; + } + + lazy_static::lazy_static! { + static ref OOG_TEST_VECTOR: Vec = { + vec![PrecompileCallArgs { + name: "ecrecover (oog)", + setup_code: bytecode! { + // msg hash from 0x00 + PUSH32(word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3")) + PUSH1(0x00) + MSTORE + // signature v from 0x20 + PUSH1(28) + PUSH1(0x20) + MSTORE + // signature r from 0x40 + PUSH32(word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608")) + PUSH1(0x40) + MSTORE + // signature s from 0x60 + PUSH32(word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada")) + PUSH1(0x60) + MSTORE + }, + // copy 128 bytes from memory addr 0. Address is recovered and the signature is + // valid. + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + // return 32 bytes and write from memory addr 128 + ret_offset: 0x80.into(), + ret_size: 0x20.into(), + gas: 0.into(), + value: 2.into(), + address: PrecompileCalls::Ecrecover.address().to_word(), + ..Default::default() + }] + }; + } + + #[test] + fn precompile_ecrecover_test() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + TEST_VECTOR.iter().for_each(|test_vector| { + for &call_kind in &call_kinds { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + }); + } + + #[test] + fn precompile_ecrecover_oog_test() { + let call_kinds = vec![ + OpcodeId::CALL, + OpcodeId::STATICCALL, + OpcodeId::DELEGATECALL, + OpcodeId::CALLCODE, + ]; + + OOG_TEST_VECTOR.iter().for_each(|test_vector| { + for &call_kind in &call_kinds { + let bytecode = test_vector.with_call_op(call_kind); + + CircuitTestBuilder::new_from_test_ctx( + TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(), + ) + .run(); + } + }) + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs index 942ed62ef4..5aad1f14fe 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs @@ -1,2 +1,5 @@ +mod ecrecover; +pub use ecrecover::EcrecoverGadget; + mod identity; pub use identity::IdentityGadget; diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index 4a06970258..c69cfeb503 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -42,7 +42,8 @@ pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS + BLOCK_TABLE_LOOKUPS + COPY_TABLE_LOOKUPS + KECCAK_TABLE_LOOKUPS - + EXP_TABLE_LOOKUPS; + + EXP_TABLE_LOOKUPS + + SIG_TABLE_LOOKUPS; /// Lookups done per row. pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ @@ -54,6 +55,7 @@ pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ (Table::Copy, COPY_TABLE_LOOKUPS), (Table::Keccak, KECCAK_TABLE_LOOKUPS), (Table::Exp, EXP_TABLE_LOOKUPS), + (Table::Sig, SIG_TABLE_LOOKUPS), ]; /// Fixed Table lookups done in EVMCircuit @@ -80,6 +82,9 @@ pub const KECCAK_TABLE_LOOKUPS: usize = 1; /// Exp Table lookups done in EVMCircuit pub const EXP_TABLE_LOOKUPS: usize = 1; +/// Sig Table lookups done in EVMCircuit +pub const SIG_TABLE_LOOKUPS: usize = 1; + /// Maximum number of bytes that an integer can fit in field without wrapping /// around. pub(crate) const MAX_N_BYTES_INTEGER: usize = 31; diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index b17b7619eb..85deb21ba6 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -36,7 +36,7 @@ use strum_macros::EnumIter; impl From for ExecutionState { fn from(value: PrecompileCalls) -> Self { match value { - PrecompileCalls::ECRecover => ExecutionState::PrecompileEcRecover, + PrecompileCalls::Ecrecover => ExecutionState::PrecompileEcrecover, PrecompileCalls::Sha256 => ExecutionState::PrecompileSha256, PrecompileCalls::Ripemd160 => ExecutionState::PrecompileRipemd160, PrecompileCalls::Identity => ExecutionState::PrecompileIdentity, @@ -158,7 +158,7 @@ pub enum ExecutionState { ErrorOutOfGasCREATE, ErrorOutOfGasSELFDESTRUCT, // Precompiles - PrecompileEcRecover, + PrecompileEcrecover, PrecompileSha256, PrecompileRipemd160, PrecompileIdentity, @@ -330,7 +330,7 @@ impl From<&ExecStep> for ExecutionState { } } ExecState::Precompile(precompile) => match precompile { - PrecompileCalls::ECRecover => ExecutionState::PrecompileEcRecover, + PrecompileCalls::Ecrecover => ExecutionState::PrecompileEcrecover, PrecompileCalls::Sha256 => ExecutionState::PrecompileSha256, PrecompileCalls::Ripemd160 => ExecutionState::PrecompileRipemd160, PrecompileCalls::Identity => ExecutionState::PrecompileIdentity, @@ -370,7 +370,7 @@ impl ExecutionState { pub(crate) fn is_precompiled(&self) -> bool { matches!( self, - Self::PrecompileEcRecover + Self::PrecompileEcrecover | Self::PrecompileSha256 | Self::PrecompileRipemd160 | Self::PrecompileIdentity @@ -384,7 +384,7 @@ impl ExecutionState { pub(crate) fn precompile_base_gas_cost(&self) -> u64 { (match self { - Self::PrecompileEcRecover => PrecompileCalls::ECRecover, + Self::PrecompileEcrecover => PrecompileCalls::Ecrecover, Self::PrecompileSha256 => PrecompileCalls::Sha256, Self::PrecompileRipemd160 => PrecompileCalls::Ripemd160, Self::PrecompileIdentity => PrecompileCalls::Identity, diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index a42b2c0e0c..da51d63c69 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -141,7 +141,7 @@ impl FixedTableTag { ), Self::PrecompileInfo => Box::new( vec![ - PrecompileCalls::ECRecover, + PrecompileCalls::Ecrecover, PrecompileCalls::Sha256, PrecompileCalls::Ripemd160, PrecompileCalls::Identity, @@ -191,6 +191,8 @@ pub enum Table { Keccak, /// Lookup for exp table Exp, + /// Lookup for sig table + Sig, } #[derive(Clone, Debug)] @@ -359,6 +361,15 @@ pub(crate) enum Lookup { exponent_lo_hi: [Expression; 2], exponentiation_lo_hi: [Expression; 2], }, + SigTable { + msg_hash: WordLoHi>, + sig_v: Expression, + sig_r: WordLoHi>, + sig_s: WordLoHi>, + recovered_addr: Expression, + is_valid: Expression, + }, + /// Conditional lookup enabled by the first element. Conditional(Expression, Box>), } @@ -378,6 +389,7 @@ impl Lookup { Self::CopyTable { .. } => Table::Copy, Self::KeccakTable { .. } => Table::Keccak, Self::ExpTable { .. } => Table::Exp, + Self::SigTable { .. } => Table::Sig, Self::Conditional(_, lookup) => lookup.table(), } } @@ -496,6 +508,25 @@ impl Lookup { exponentiation_lo_hi[0].clone(), exponentiation_lo_hi[1].clone(), ], + Self::SigTable { + msg_hash, + sig_v, + sig_r, + sig_s, + recovered_addr, + is_valid, + } => vec![ + 1.expr(), // q_enable + msg_hash.lo(), + msg_hash.hi(), + sig_v.clone(), + sig_r.lo(), + sig_r.hi(), + sig_s.lo(), + sig_s.hi(), + recovered_addr.clone(), + is_valid.clone(), + ], Self::Conditional(condition, lookup) => lookup .input_exprs() .into_iter() diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index faa52eb515..01342631f3 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1490,7 +1490,30 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ); } - // Keccak Table + /// Sig Table + pub(crate) fn sig_table_lookup( + &mut self, + msg_hash: WordLoHi>, + sig_v: Expression, + sig_r: WordLoHi>, + sig_s: WordLoHi>, + recovered_addr: Expression, + is_valid: Expression, + ) { + self.add_lookup( + "sig table", + Lookup::SigTable { + msg_hash, + sig_v, + sig_r, + sig_s, + recovered_addr, + is_valid, + }, + ); + } + + /// Keccak Table pub(crate) fn keccak_table_lookup( &mut self, input_rlc: Expression, diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 1674fcb636..a238a542f8 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -104,6 +104,9 @@ impl Instrument { CellType::Lookup(Table::Exp) => { report.exp_table = data_entry; } + CellType::Lookup(Table::Sig) => { + report.sig_table = data_entry; + } } } report_collection.push(report); @@ -131,6 +134,7 @@ pub struct ExecStateReport { pub copy_table: StateReportRow, pub keccak_table: StateReportRow, pub exp_table: StateReportRow, + pub sig_table: StateReportRow, } impl From for ExecStateReport { diff --git a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs index da0ce68e32..0929467d41 100644 --- a/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/precompile_gadget.rs @@ -39,6 +39,7 @@ impl PrecompileGadget { let conditions = vec![ address.value_equals(PrecompileCalls::Identity), + address.value_equals(PrecompileCalls::Ecrecover), // match more precompiles ] .into_iter() @@ -49,6 +50,7 @@ impl PrecompileGadget { let next_states = vec![ ExecutionState::PrecompileIdentity, // add more precompile execution states + ExecutionState::PrecompileEcrecover, ]; let constraints: Vec> = vec![ @@ -56,10 +58,18 @@ impl PrecompileGadget { // Identity cb.require_equal( "input length and precompile return length are the same", - cd_length, - precompile_return_length, + cd_length.clone(), + precompile_return_length.clone(), ); - }), // add more precompile constraint closures + }), + Box::new(|cb| { + // EcRecover + cb.require_equal( + "ECRecover: input length is 128 bytes", + cd_length.clone(), + 128.expr(), + ); + }), ]; cb.constrain_mutually_exclusive_next_step(conditions, next_states, constraints); diff --git a/zkevm-circuits/src/lib.rs b/zkevm-circuits/src/lib.rs index 56bf0d4934..d3924cf133 100644 --- a/zkevm-circuits/src/lib.rs +++ b/zkevm-circuits/src/lib.rs @@ -34,6 +34,7 @@ pub mod mpt_circuit; pub mod pi_circuit; #[cfg(not(target_arch = "wasm32"))] pub mod root_circuit; +pub mod sig_circuit; pub mod state_circuit; pub mod super_circuit; pub mod table; diff --git a/zkevm-circuits/src/root_circuit/test.rs b/zkevm-circuits/src/root_circuit/test.rs index 5474c18385..de87bd58e1 100644 --- a/zkevm-circuits/src/root_circuit/test.rs +++ b/zkevm-circuits/src/root_circuit/test.rs @@ -32,6 +32,7 @@ fn test_root_circuit() { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; let (k, circuit, instance, _) = SuperCircuit::<_>::build(block_1tx(), circuits_params, TEST_MOCK_RANDOMNESS.into()) diff --git a/zkevm-circuits/src/sig_circuit.rs b/zkevm-circuits/src/sig_circuit.rs new file mode 100644 index 0000000000..7cb3883815 --- /dev/null +++ b/zkevm-circuits/src/sig_circuit.rs @@ -0,0 +1,1049 @@ +//! Circuit to verify multiple ECDSA secp256k1 signatures. +// This module uses halo2-ecc's ecdsa chip +// - to prove the correctness of secp signatures +// - to compute the RLC in circuit +// - to perform keccak lookup table +// +// Naming notes: +// - *_be: Big-Endian bytes +// - *_le: Little-Endian bytes + +#[cfg(any(test, feature = "test-circuits"))] +mod dev; +mod ecdsa; +#[cfg(test)] +mod test; +mod utils; + +use crate::{ + evm_circuit::{util::not, EvmCircuit}, + keccak_circuit::KeccakCircuit, + sig_circuit::{ + ecdsa::ecdsa_verify_no_pubkey_check, + utils::{calc_required_advices, FpChip}, + }, + table::{KeccakTable, SigTable}, + util::{word::WordLoHi, Challenges, Expr, SubCircuit, SubCircuitConfig}, +}; +use eth_types::{ + self, + sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData}, + Field, +}; +use halo2_base::{ + gates::{range::RangeConfig, GateInstructions, RangeInstructions}, + utils::modulus, + AssignedValue, Context, QuantumCell, SKIP_FIRST_PASS, +}; +use halo2_ecc::{ + bigint::CRTInteger, + ecc::EccChip, + fields::{ + fp::{FpConfig, FpStrategy}, + FieldChip, + }, +}; +pub(crate) use utils::*; + +use halo2_proofs::{ + circuit::{Layouter, Value}, + halo2curves::secp256k1::{Fp, Fq, Secp256k1Affine}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, + poly::Rotation, +}; + +use ethers_core::utils::keccak256; +use itertools::Itertools; +use log::error; +use std::{iter, marker::PhantomData}; + +/// Circuit configuration arguments +pub struct SigCircuitConfigArgs { + /// KeccakTable + pub _keccak_table: KeccakTable, + /// SigTable + pub sig_table: SigTable, + /// Challenges + pub challenges: Challenges>, +} + +/// SignVerify Configuration +#[derive(Debug, Clone)] +pub struct SigCircuitConfig +where + F: Field + halo2_base::utils::ScalarField, +{ + /// ECDSA + ecdsa_config: FpChip, + // ecdsa_config: FpConfig, + /// An advice column to store RLC witnesses + rlc_column: Column, + /// selector for keccak lookup table + q_keccak: Selector, + /// Used to lookup pk->pk_hash(addr) + _keccak_table: KeccakTable, + /// The exposed table to be used by tx circuit and ecrecover + sig_table: SigTable, +} + +impl SubCircuitConfig for SigCircuitConfig +where + F: Field + halo2_base::utils::ScalarField, +{ + type ConfigArgs = SigCircuitConfigArgs; + + /// Return a new SigConfig + fn new( + meta: &mut ConstraintSystem, + Self::ConfigArgs { + _keccak_table, + sig_table, + challenges: _, + }: Self::ConfigArgs, + ) -> Self { + // need an additional phase 2 column/basic gate to hold the witnesses during RLC + // computations + let num_advice = [calc_required_advices(MAX_NUM_SIG), 1]; + let num_lookup_advice = [calc_required_lookup_advices(MAX_NUM_SIG)]; + log::info!("configuring ECDSA chip with multiple phases"); + + let ecdsa_config = FpConfig::configure( + meta, + FpStrategy::Simple, + &num_advice, + &num_lookup_advice, + 1, + LOG_TOTAL_NUM_ROWS - 1, + LIMB_BITS, + NUM_LIMBS, + modulus::(), + 0, + LOG_TOTAL_NUM_ROWS, // maximum k of the chip + ); + + // we need one phase 2 column to store RLC results + let rlc_column = meta.advice_column_in(halo2_proofs::plonk::SecondPhase); + + meta.enable_equality(rlc_column); + + meta.enable_equality(sig_table.recovered_addr); + meta.enable_equality(sig_table.sig_r.lo()); + meta.enable_equality(sig_table.sig_r.hi()); + meta.enable_equality(sig_table.sig_s.lo()); + meta.enable_equality(sig_table.sig_s.hi()); + meta.enable_equality(sig_table.sig_v); + meta.enable_equality(sig_table.is_valid); + meta.enable_equality(sig_table.msg_hash.lo()); + meta.enable_equality(sig_table.msg_hash.hi()); + + // Ref. spec SignVerifyChip 1. Verify that keccak(pub_key_bytes) = pub_key_hash + // by keccak table lookup, where pub_key_bytes is built from the pub_key + // in the ecdsa_chip. + let q_keccak = meta.complex_selector(); + + meta.lookup_any("keccak lookup table", |meta| { + // When address is 0, we disable the signature verification by using a dummy pk, + // msg_hash and signature which is not constrained to match msg_hash_rlc nor + // the address. + // Layout: + // | q_keccak | rlc | + // | -------- | --------------- | + // | 1 | is_address_zero | + // | | pk_rlc | + // | | pk_hash_lo | + // | | pk_hash_hi | + let q_keccak = meta.query_selector(q_keccak); + let is_address_zero = meta.query_advice(rlc_column, Rotation::cur()); + let is_enable = q_keccak * not::expr(is_address_zero); + + let input = [ + is_enable.clone(), + is_enable.clone() * meta.query_advice(rlc_column, Rotation(1)), + is_enable.clone() * 64usize.expr(), + is_enable.clone() * meta.query_advice(rlc_column, Rotation(2)), + is_enable * meta.query_advice(rlc_column, Rotation(3)), + ]; + let table = [ + meta.query_advice(_keccak_table.is_enabled, Rotation::cur()), + meta.query_advice(_keccak_table.input_rlc, Rotation::cur()), + meta.query_advice(_keccak_table.input_len, Rotation::cur()), + meta.query_advice(_keccak_table.output.lo(), Rotation::cur()), + meta.query_advice(_keccak_table.output.hi(), Rotation::cur()), + ]; + + input.into_iter().zip(table).collect() + }); + + Self { + ecdsa_config, + _keccak_table, + sig_table, + q_keccak, + rlc_column, + } + } +} + +/// Verify a message hash is signed by the public +/// key corresponding to an Ethereum Address. +#[derive(Clone, Debug, Default)] +pub struct SigCircuit { + /// Max number of verifications + pub max_verif: usize, + /// Without padding + pub signatures: Vec, + /// Marker + pub _marker: PhantomData, +} + +impl SubCircuit for SigCircuit { + type Config = SigCircuitConfig; + + fn new_from_block(block: &crate::witness::Block) -> Self { + assert!(block.circuits_params.max_txs <= MAX_NUM_SIG); + + SigCircuit { + max_verif: MAX_NUM_SIG, + signatures: block.get_sign_data(true), + _marker: Default::default(), + } + } + + /// Returns number of unusable rows of the SubCircuit, which should be + /// `meta.blinding_factors() + 1`. + fn unusable_rows() -> usize { + [ + KeccakCircuit::::unusable_rows(), + EvmCircuit::::unusable_rows(), + // may include additional subcircuits here + ] + .into_iter() + .max() + .unwrap() + } + + fn synthesize_sub( + &self, + config: &Self::Config, + challenges: &Challenges>, + layouter: &mut impl Layouter, + ) -> Result<(), Error> { + config.ecdsa_config.range.load_lookup_table(layouter)?; + self.assign(config, layouter, &self.signatures, challenges)?; + Ok(()) + } + + // Since sig circuit / halo2-lib use veticle cell assignment, + // so the returned pair is consisted of same values + fn min_num_rows_block(block: &crate::witness::Block) -> (usize, usize) { + let row_num = if block.circuits_params.max_vertical_circuit_rows == 0 { + Self::min_num_rows() + } else { + block.circuits_params.max_vertical_circuit_rows + }; + + let ecdsa_verif_count = + block.txs.len() + block.precompile_events.get_ecrecover_events().len(); + // Reserve one ecdsa verification for padding tx such that the bad case in which some tx + // calls MAX_NUM_SIG - 1 ecrecover precompile won't happen. If that case happens, the sig + // circuit won't have more space for the padding tx's ECDSA verification. Then the + // prover won't be able to produce any valid proof. + let max_num_verif = MAX_NUM_SIG - 1; + + // Instead of showing actual minimum row usage, + // halo2-lib based circuits use min_row_num to represent a percentage of total-used capacity + // This functionality allows l2geth to decide if additional ops can be added. + let min_row_num = (row_num / max_num_verif) * ecdsa_verif_count; + + (min_row_num, row_num) + } +} + +impl SigCircuit { + /// Return a new SigCircuit + pub fn new(max_verif: usize) -> Self { + Self { + max_verif, + signatures: Vec::new(), + _marker: PhantomData, + } + } + + /// Return the minimum number of rows required to prove an input of a + /// particular size. + pub fn min_num_rows() -> usize { + // SigCircuit can't determine usable rows independently. + // Instead, the blinding area is determined by other advise columns with most counts of + // rotation queries. This value is typically determined by either the Keccak or EVM + // circuit. + + // the cells are allocated vertically, i.e., given a TOTAL_NUM_ROWS * NUM_ADVICE + // matrix, the allocator will try to use all the cells in the first column, then + // the second column, etc. + + let max_blinding_factor = Self::unusable_rows() - 1; + + // same formula as halo2-lib's FlexGate + (1 << LOG_TOTAL_NUM_ROWS) - (max_blinding_factor + 3) + } +} + +impl SigCircuit { + /// Verifies the ecdsa relationship. I.e., prove that the signature + /// is (in)valid or not under the given public key and the message hash in + /// the circuit. Does not enforce the signature is valid. + /// + /// Returns the cells for + /// - public keys + /// - message hashes + /// - a boolean whether the signature is correct or not + /// + /// WARNING: this circuit does not enforce the returned value to be true + /// make sure the caller checks this result! + fn assign_ecdsa( + &self, + ctx: &mut Context, + ecdsa_chip: &FpChip, + sign_data: &SignData, + ) -> Result>, Error> { + let gate = ecdsa_chip.gate(); + let zero = gate.load_zero(ctx); + + let SignData { + signature, + pk, + msg: _, + msg_hash, + } = sign_data; + let (sig_r, sig_s, v) = signature; + + // build ecc chip from Fp chip + let ecc_chip = EccChip::>::construct(ecdsa_chip.clone()); + let pk_assigned = ecc_chip.load_private(ctx, (Value::known(pk.x), Value::known(pk.y))); + let pk_is_valid = ecc_chip.is_on_curve_or_infinity::(ctx, &pk_assigned); + gate.assert_is_const(ctx, &pk_is_valid, F::ONE); + + // build Fq chip from Fp chip + let fq_chip = FqChip::construct(ecdsa_chip.range.clone(), 88, 3, modulus::()); + let integer_r = + fq_chip.load_private(ctx, FqChip::::fe_to_witness(&Value::known(*sig_r))); + let integer_s = + fq_chip.load_private(ctx, FqChip::::fe_to_witness(&Value::known(*sig_s))); + let msg_hash = + fq_chip.load_private(ctx, FqChip::::fe_to_witness(&Value::known(*msg_hash))); + + // returns the verification result of ecdsa signature + // + // WARNING: this circuit does not enforce the returned value to be true + // make sure the caller checks this result! + let (sig_is_valid, pk_is_zero, y_coord, y_coord_is_zero) = + ecdsa_verify_no_pubkey_check::( + &ecc_chip.field_chip, + ctx, + &pk_assigned, + &integer_r, + &integer_s, + &msg_hash, + 4, + 4, + ); + + // ======================================= + // constrains v == y.is_oddness() + // ======================================= + assert!(*v == 0 || *v == 1, "v is not boolean"); + + // we constrain: + // - v + 2*tmp = y where y is already range checked (88 bits) + // - v is a binary + // - tmp is also < 88 bits (this is crucial otherwise tmp may wrap around and break + // soundness) + + let assigned_y_is_odd = gate.load_witness(ctx, Value::known(F::from(*v as u64))); + gate.assert_bit(ctx, assigned_y_is_odd); + + // the last 88 bits of y + let assigned_y_limb = &y_coord.limbs()[0]; + let mut y_value = F::ZERO; + assigned_y_limb.value().map(|&x| y_value = x); + + // y_tmp = (y_value - y_last_bit)/2 + let y_tmp = (y_value - F::from(*v as u64)) * F::TWO_INV; + let assigned_y_tmp = gate.load_witness(ctx, Value::known(y_tmp)); + + // y_tmp_double = (y_value - y_last_bit) + let y_tmp_double = gate.mul( + ctx, + QuantumCell::Existing(assigned_y_tmp), + QuantumCell::Constant(F::from(2)), + ); + let y_rec = gate.add( + ctx, + QuantumCell::Existing(y_tmp_double), + QuantumCell::Existing(assigned_y_is_odd), + ); + let y_is_ok = gate.is_equal( + ctx, + QuantumCell::Existing(*assigned_y_limb), + QuantumCell::Existing(y_rec), + ); + + // last step we want to constrain assigned_y_tmp is 87 bits + let assigned_y_tmp = gate.select( + ctx, + QuantumCell::Existing(zero), + QuantumCell::Existing(assigned_y_tmp), + QuantumCell::Existing(y_coord_is_zero), + ); + ecc_chip + .field_chip + .range + .range_check(ctx, &assigned_y_tmp, 87); + + let y_coord_not_zero = gate.not(ctx, QuantumCell::Existing(y_coord_is_zero)); + let sig_is_valid = gate.and_many( + ctx, + vec![ + QuantumCell::Existing(sig_is_valid), + QuantumCell::Existing(y_is_ok), + QuantumCell::Existing(y_coord_not_zero), + ], + ); + + Ok(AssignedECDSA { + _pk: pk_assigned, + pk_is_zero, + msg_hash, + integer_r, + integer_s, + v: assigned_y_is_odd, + sig_is_valid, + }) + } + + fn enable_keccak_lookup( + &self, + config: &SigCircuitConfig, + ctx: &mut Context, + offset: usize, + is_address_zero: &AssignedValue, + pk_rlc: &AssignedValue, + pk_hash: &WordLoHi>, + ) -> Result<(), Error> { + log::trace!("keccak lookup"); + + // Layout: + // | q_keccak | rlc | + // | -------- | --------------- | + // | 1 | is_address_zero | + // | | pk_rlc | + // | | pk_hash_lo | + // | | pk_hash_hi | + config.q_keccak.enable(&mut ctx.region, offset)?; + + // is_address_zero + let tmp_cell = ctx.region.assign_advice( + || "is_address_zero", + config.rlc_column, + offset, + || is_address_zero.value, + )?; + ctx.region + .constrain_equal(is_address_zero.cell, tmp_cell.cell())?; + + // pk_rlc + let tmp_cell = ctx.region.assign_advice( + || "pk_rlc", + config.rlc_column, + offset + 1, + || pk_rlc.value, + )?; + ctx.region.constrain_equal(pk_rlc.cell, tmp_cell.cell())?; + + // pk_hash + let pk_cell_lo = ctx.region.assign_advice( + || "pk_hash_lo", + config.rlc_column, + offset + 2, + || pk_hash.lo().value, + )?; + ctx.region + .constrain_equal(pk_hash.lo().cell, pk_cell_lo.cell())?; + let pk_cell_hi = ctx.region.assign_advice( + || "pk_hash_hi", + config.rlc_column, + offset + 3, + || pk_hash.hi().value, + )?; + ctx.region + .constrain_equal(pk_hash.hi().cell, pk_cell_hi.cell())?; + + log::trace!("finished keccak lookup"); + Ok(()) + } + + /// Input the signature data, + /// Output the cells for byte decomposition of the keys and messages + fn sign_data_decomposition( + &self, + ctx: &mut Context, + ecdsa_chip: &FpChip, + sign_data: &SignData, + assigned_data: &AssignedECDSA>, + ) -> Result, Error> { + // build ecc chip from Fp chip + let ecc_chip = EccChip::>::construct(ecdsa_chip.clone()); + + let zero = ecdsa_chip.range.gate.load_zero(ctx); + + // ================================================ + // step 0. powers of aux parameters + // ================================================ + let word_lo_hi_powers = + iter::successors(Some(F::ONE), |coeff| Some(F::from(256) * coeff)).take(32); + let powers_of_256_cells = word_lo_hi_powers + .map(|x| QuantumCell::Constant(x)) + .collect_vec(); + + // ================================================ + // pk hash cells + // ================================================ + let pk_le = pk_bytes_le(&sign_data.pk); + let pk_be = pk_bytes_swap_endianness(&pk_le); + let pk_hash = keccak256(pk_be).map(|byte| Value::known(F::from(byte as u64))); + + log::trace!("pk hash {:0x?}", pk_hash); + let pk_hash_cells = pk_hash + .iter() + .map(|&x| QuantumCell::Witness(x)) + .rev() + .collect_vec(); + + // address is the random linear combination of the public key + // it is fine to use a phase 1 gate here + let address = ecdsa_chip.range.gate.inner_product( + ctx, + powers_of_256_cells[..20].to_vec(), + pk_hash_cells[..20].to_vec(), + ); + let address = ecdsa_chip.range.gate.select( + ctx, + QuantumCell::Existing(zero), + QuantumCell::Existing(address), + QuantumCell::Existing(assigned_data.pk_is_zero), + ); + let is_address_zero = ecdsa_chip.range.gate.is_equal( + ctx, + QuantumCell::Existing(address), + QuantumCell::Existing(zero), + ); + log::trace!("address: {:?}", address.value()); + + // ================================================ + // message hash cells + // ================================================ + + let assert_crt = |ctx: &mut Context, + bytes: [u8; 32], + crt_integer: &CRTInteger| + -> Result<_, Error> { + let byte_cells: Vec> = bytes + .iter() + .map(|&x| QuantumCell::Witness(Value::known(F::from(x as u64)))) + .collect_vec(); + self.assert_crt_int_byte_repr( + ctx, + &ecdsa_chip.range, + crt_integer, + &byte_cells, + &powers_of_256_cells, + )?; + Ok(byte_cells) + }; + + // assert the assigned_msg_hash_le is the right decomposition of msg_hash + // msg_hash is an overflowing integer with 3 limbs, of sizes 88, 88, and 80 + let assigned_msg_hash_le = + assert_crt(ctx, sign_data.msg_hash.to_bytes(), &assigned_data.msg_hash)?; + + // ================================================ + // pk cells + // ================================================ + let pk_x_le = sign_data + .pk + .x + .to_bytes() + .iter() + .map(|&x| QuantumCell::Witness(Value::known(F::from_u128(x as u128)))) + .collect_vec(); + let pk_y_le = sign_data + .pk + .y + .to_bytes() + .iter() + .map(|&y| QuantumCell::Witness(Value::known(F::from_u128(y as u128)))) + .collect_vec(); + let pk_assigned = ecc_chip.load_private( + ctx, + (Value::known(sign_data.pk.x), Value::known(sign_data.pk.y)), + ); + + self.assert_crt_int_byte_repr( + ctx, + &ecdsa_chip.range, + &pk_assigned.x, + &pk_x_le, + &powers_of_256_cells, + )?; + self.assert_crt_int_byte_repr( + ctx, + &ecdsa_chip.range, + &pk_assigned.y, + &pk_y_le, + &powers_of_256_cells, + )?; + + let assigned_pk_le_selected = [pk_y_le, pk_x_le].concat(); + log::trace!("finished data decomposition"); + + let r_cells = assert_crt( + ctx, + sign_data.signature.0.to_bytes(), + &assigned_data.integer_r, + )?; + let s_cells = assert_crt( + ctx, + sign_data.signature.1.to_bytes(), + &assigned_data.integer_s, + )?; + + Ok(SignDataDecomposed { + pk_hash_cells, + msg_hash_cells: assigned_msg_hash_le, + pk_cells: assigned_pk_le_selected, + address, + is_address_zero, + r_cells, + s_cells, + }) + } + + #[allow(clippy::too_many_arguments)] + fn assign_sig_verify( + &self, + ctx: &mut Context, + rlc_chip: &RangeConfig, + sign_data_decomposed: &SignDataDecomposed, + challenges: &Challenges>, + assigned_ecdsa: &AssignedECDSA>, + ) -> Result<([AssignedValue; 4], AssignedSignatureVerify), Error> { + // ================================================ + // step 0. powers of aux parameters + // ================================================ + let word_lo_hi_powers = iter::successors(Some(Value::known(F::ONE)), |coeff| { + Some(Value::known(F::from(256)) * coeff) + }) + .take(16) + .map(|x| QuantumCell::Witness(x)) + .collect_vec(); + + let keccak_challenge_powers = iter::successors(Some(Value::known(F::ONE)), |coeff| { + Some(challenges.keccak_input() * coeff) + }) + .take(64) + .map(|x| QuantumCell::Witness(x)) + .collect_vec(); + + // ================================================ + // step 1 message hash + // ================================================ + // Ref. spec SignVerifyChip 3. Verify that the signed message in the ecdsa_chip + // corresponds to msg_hash + let msg_hash_cells = { + let msg_hash_lo_cell_bytes = &sign_data_decomposed.msg_hash_cells[..16]; + let msg_hash_hi_cell_bytes = &sign_data_decomposed.msg_hash_cells[16..]; + + let msg_hash_cell_lo = rlc_chip.gate.inner_product( + ctx, + msg_hash_lo_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + let msg_hash_cell_hi = rlc_chip.gate.inner_product( + ctx, + msg_hash_hi_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + + WordLoHi::new([msg_hash_cell_lo, msg_hash_cell_hi]) + }; + + log::trace!( + "assigned msg hash: ({:?}, {:?})", + msg_hash_cells.lo().value(), + msg_hash_cells.hi().value() + ); + + // ================================================ + // step 2 random linear combination of pk + // ================================================ + let pk_rlc = rlc_chip.gate.inner_product( + ctx, + sign_data_decomposed.pk_cells.clone(), + keccak_challenge_powers, + ); + log::trace!("pk rlc: {:?}", pk_rlc.value()); + + // ================================================ + // step 3 pk_hash + // ================================================ + let pk_hash_cells = { + let pk_hash_lo_cell_bytes = &sign_data_decomposed.pk_hash_cells[..16]; + let pk_hash_hi_cell_bytes = &sign_data_decomposed.pk_hash_cells[16..]; + + let pk_hash_cell_lo = rlc_chip.gate.inner_product( + ctx, + pk_hash_lo_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + let pk_hash_cell_hi = rlc_chip.gate.inner_product( + ctx, + pk_hash_hi_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + + WordLoHi::new([pk_hash_cell_lo, pk_hash_cell_hi]) + }; + + // step 4: r,s + let r_cells = { + let r_lo_cell_bytes = &sign_data_decomposed.r_cells[..16]; + let r_hi_cell_bytes = &sign_data_decomposed.r_cells[16..]; + + let r_cell_lo = rlc_chip.gate.inner_product( + ctx, + r_lo_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + let r_cell_hi = rlc_chip.gate.inner_product( + ctx, + r_hi_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + + WordLoHi::new([r_cell_lo, r_cell_hi]) + }; + let s_cells = { + let s_lo_cell_bytes = &sign_data_decomposed.s_cells[..16]; + let s_hi_cell_bytes = &sign_data_decomposed.s_cells[16..]; + + let s_cell_lo = rlc_chip.gate.inner_product( + ctx, + s_lo_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers.clone(), + ); + let s_cell_hi = rlc_chip.gate.inner_product( + ctx, + s_hi_cell_bytes.iter().cloned().collect_vec(), + word_lo_hi_powers, + ); + + WordLoHi::new([s_cell_lo, s_cell_hi]) + }; + + log::trace!( + "pk hash halo2ecc: ({:?}, {:?})", + pk_hash_cells.lo().value(), + pk_hash_cells.lo().value() + ); + log::trace!("finished sign verify"); + + let to_be_keccak_checked = [ + sign_data_decomposed.is_address_zero, + pk_rlc, + pk_hash_cells.lo(), + pk_hash_cells.hi(), + ]; + let assigned_sig_verif = AssignedSignatureVerify { + address: sign_data_decomposed.address, + // msg_len: sign_data.msg.len(), + // msg_rlc: challenges + // .keccak_input() + // .map(|r| rlc::value(sign_data.msg.iter().rev(), r)), + msg_hash: msg_hash_cells, + sig_is_valid: assigned_ecdsa.sig_is_valid, + r: r_cells, + s: s_cells, + v: assigned_ecdsa.v, + }; + Ok((to_be_keccak_checked, assigned_sig_verif)) + } + + /// Assign witness data to the sig circuit. + pub(crate) fn assign( + &self, + config: &SigCircuitConfig, + layouter: &mut impl Layouter, + signatures: &[SignData], + challenges: &Challenges>, + ) -> Result>, Error> { + if signatures.len() > self.max_verif { + error!( + "signatures.len() = {} > max_verif = {}", + signatures.len(), + self.max_verif + ); + return Err(Error::Synthesis); + } + let mut first_pass = SKIP_FIRST_PASS; + let ecdsa_chip = &config.ecdsa_config; + + let assigned_sig_verifs = layouter.assign_region( + || "ecdsa chip verification", + |region| { + if first_pass { + first_pass = false; + return Ok(vec![]); + } + + let mut ctx = ecdsa_chip.new_context(region); + + // ================================================ + // step 1: assert the signature is valid in circuit + // ================================================ + let assigned_ecdsas = signatures + .iter() + .chain(std::iter::repeat(&SignData::default())) + .take(self.max_verif) + .map(|sign_data| self.assign_ecdsa(&mut ctx, ecdsa_chip, sign_data)) + .collect::>>, Error>>()?; + + // ================================================ + // step 2: decompose the keys and messages + // ================================================ + let sign_data_decomposed = signatures + .iter() + .chain(std::iter::repeat(&SignData::default())) + .take(self.max_verif) + .zip_eq(assigned_ecdsas.iter()) + .map(|(sign_data, assigned_ecdsa)| { + self.sign_data_decomposition( + &mut ctx, + ecdsa_chip, + sign_data, + assigned_ecdsa, + ) + }) + .collect::>, Error>>()?; + + // IMPORTANT: Move to Phase2 before RLC + log::info!("before proceeding to the next phase"); + + // finalize the current lookup table before moving to next phase + ecdsa_chip.finalize(&mut ctx); + ctx.print_stats(&["ECDSA context"]); + ctx.next_phase(); + + // ================================================ + // step 3: compute RLC of keys and messages + // ================================================ + let (assigned_keccak_values, assigned_sig_values): ( + Vec<[AssignedValue; 4]>, + Vec>, + ) = signatures + .iter() + .chain(std::iter::repeat(&SignData::default())) + .take(self.max_verif) + .zip_eq(assigned_ecdsas.iter()) + .zip_eq(sign_data_decomposed.iter()) + .map(|((_, assigned_ecdsa), sign_data_decomp)| { + self.assign_sig_verify( + &mut ctx, + &ecdsa_chip.range, + sign_data_decomp, + challenges, + assigned_ecdsa, + ) + }) + .collect::; 4], AssignedSignatureVerify)>, + Error, + >>()? + .into_iter() + .unzip(); + + // ================================================ + // step 4: deferred keccak checks + // ================================================ + for (i, [is_address_zero, pk_rlc, pk_hash_lo, pk_hash_hi]) in + assigned_keccak_values.iter().enumerate() + { + let offset = i * 4; + self.enable_keccak_lookup( + config, + &mut ctx, + offset, + is_address_zero, + pk_rlc, + &WordLoHi::new([*pk_hash_lo, *pk_hash_hi]), + )?; + } + + // IMPORTANT: this assigns all constants to the fixed columns + // IMPORTANT: this copies cells to the lookup advice column to perform range + // check lookups + // This is not optional. + let lookup_cells = ecdsa_chip.finalize(&mut ctx); + log::info!("total number of lookup cells: {}", lookup_cells); + + ctx.print_stats(&["ECDSA context"]); + Ok(assigned_sig_values) + }, + )?; + + layouter.assign_region( + || "expose sig table", + |mut region| { + // step 5: export as a lookup table + for (idx, assigned_sig_verif) in assigned_sig_verifs.iter().enumerate() { + region.assign_fixed( + || "assign sig_table selector", + config.sig_table.q_enable, + idx, + || Value::known(F::ONE), + )?; + + assigned_sig_verif + .v + .copy_advice(&mut region, config.sig_table.sig_v, idx); + + assigned_sig_verif.r.lo().copy_advice( + &mut region, + config.sig_table.sig_r.lo(), + idx, + ); + assigned_sig_verif.r.hi().copy_advice( + &mut region, + config.sig_table.sig_r.hi(), + idx, + ); + + assigned_sig_verif.s.lo().copy_advice( + &mut region, + config.sig_table.sig_s.lo(), + idx, + ); + assigned_sig_verif.s.hi().copy_advice( + &mut region, + config.sig_table.sig_s.hi(), + idx, + ); + + assigned_sig_verif.address.copy_advice( + &mut region, + config.sig_table.recovered_addr, + idx, + ); + + assigned_sig_verif.sig_is_valid.copy_advice( + &mut region, + config.sig_table.is_valid, + idx, + ); + + assigned_sig_verif.msg_hash.lo().copy_advice( + &mut region, + config.sig_table.msg_hash.lo(), + idx, + ); + assigned_sig_verif.msg_hash.hi().copy_advice( + &mut region, + config.sig_table.msg_hash.hi(), + idx, + ); + } + Ok(()) + }, + )?; + + Ok(assigned_sig_verifs) + } + + /// Assert an CRTInteger's byte representation is correct. + /// inputs + /// - crt_int with 3 limbs [88, 88, 80] + /// - byte representation of the integer + /// - a sequence of [1, 2^8, 2^16, ...] + /// - a overriding flag that sets output to 0 if set + fn assert_crt_int_byte_repr( + &self, + ctx: &mut Context, + range_chip: &RangeConfig, + crt_int: &CRTInteger, + byte_repr: &[QuantumCell], + word_lo_hi_powers: &[QuantumCell], + ) -> Result<(), Error> { + // length of byte representation is 32 + assert_eq!(byte_repr.len(), 32); + // need to support decomposition of up to 88 bits + assert!(word_lo_hi_powers.len() >= 11); + + let flex_gate_chip = &range_chip.gate; + + // apply the overriding flag + let limb1_value = crt_int.truncation.limbs[0]; + let limb2_value = crt_int.truncation.limbs[1]; + let limb3_value = crt_int.truncation.limbs[2]; + + // assert the byte_repr is the right decomposition of overflow_int + // overflow_int is an overflowing integer with 3 limbs, of sizes 88, 88, and 80 + // we reconstruct the three limbs from the bytes repr, and + // then enforce equality with the CRT integer + let limb1_recover = flex_gate_chip.inner_product( + ctx, + byte_repr[0..11].to_vec(), + word_lo_hi_powers[0..11].to_vec(), + ); + let limb2_recover = flex_gate_chip.inner_product( + ctx, + byte_repr[11..22].to_vec(), + word_lo_hi_powers[0..11].to_vec(), + ); + let limb3_recover = flex_gate_chip.inner_product( + ctx, + byte_repr[22..].to_vec(), + word_lo_hi_powers[0..10].to_vec(), + ); + flex_gate_chip.assert_equal( + ctx, + QuantumCell::Existing(limb1_value), + QuantumCell::Existing(limb1_recover), + ); + flex_gate_chip.assert_equal( + ctx, + QuantumCell::Existing(limb2_value), + QuantumCell::Existing(limb2_recover), + ); + flex_gate_chip.assert_equal( + ctx, + QuantumCell::Existing(limb3_value), + QuantumCell::Existing(limb3_recover), + ); + log::trace!( + "limb 1 \ninput {:?}\nreconstructed {:?}", + limb1_value.value(), + limb1_recover.value() + ); + log::trace!( + "limb 2 \ninput {:?}\nreconstructed {:?}", + limb2_value.value(), + limb2_recover.value() + ); + log::trace!( + "limb 3 \ninput {:?}\nreconstructed {:?}", + limb3_value.value(), + limb3_recover.value() + ); + + Ok(()) + } +} diff --git a/zkevm-circuits/src/sig_circuit/dev.rs b/zkevm-circuits/src/sig_circuit/dev.rs new file mode 100644 index 0000000000..684385b45b --- /dev/null +++ b/zkevm-circuits/src/sig_circuit/dev.rs @@ -0,0 +1,64 @@ +use super::*; + +use crate::util::Challenges; + +use bus_mapping::circuit_input_builder::keccak_inputs_sign_verify; +use halo2_proofs::{circuit::SimpleFloorPlanner, plonk::Circuit}; + +/// SigCircuitTesterConfig +#[derive(Clone, Debug)] +pub struct SigCircuitTesterConfig { + sign_verify: SigCircuitConfig, + challenges: crate::util::Challenges, +} + +impl SigCircuitTesterConfig { + pub(crate) fn new(meta: &mut ConstraintSystem) -> Self { + let keccak_table = KeccakTable::construct(meta); + let sig_table = SigTable::construct(meta); + let challenges = Challenges::construct(meta); + let challenges_expr = challenges.exprs(meta); + let sign_verify = SigCircuitConfig::new( + meta, + SigCircuitConfigArgs { + _keccak_table: keccak_table, + challenges: challenges_expr, + sig_table, + }, + ); + + SigCircuitTesterConfig { + sign_verify, + challenges, + } + } +} + +impl Circuit for SigCircuit { + type Config = SigCircuitTesterConfig; + type FloorPlanner = SimpleFloorPlanner; + type Params = (); + + fn without_witnesses(&self) -> Self { + Self::default() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + SigCircuitTesterConfig::new(meta) + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + let challenges = config.challenges.values(&mut layouter); + self.synthesize_sub(&config.sign_verify, &challenges, &mut layouter)?; + config.sign_verify._keccak_table.dev_load( + &mut layouter, + &keccak_inputs_sign_verify(&self.signatures), + &challenges, + )?; + Ok(()) + } +} diff --git a/zkevm-circuits/src/sig_circuit/ecdsa.rs b/zkevm-circuits/src/sig_circuit/ecdsa.rs new file mode 100644 index 0000000000..7105066123 --- /dev/null +++ b/zkevm-circuits/src/sig_circuit/ecdsa.rs @@ -0,0 +1,226 @@ +//! This module implements the ECDSA circuit. Modified from +//! + +use halo2_base::{ + gates::{GateInstructions, RangeInstructions}, + utils::{fe_to_biguint, modulus, CurveAffineExt}, + AssignedValue, Context, + QuantumCell::Existing, +}; +use halo2_ecc::{ + bigint::{big_less_than, CRTInteger}, + ecc::{ec_add_unequal, ec_sub_unequal, fixed_base, scalar_multiply, EcPoint, EccChip}, + fields::{fp::FpConfig, FieldChip, PrimeField, Selectable}, +}; + +// CF is the coordinate field of GA +// SF is the scalar field of GA +// p = coordinate field modulus +// n = scalar field modulus +// Only valid when p is very close to n in size (e.g. for Secp256k1) +// returns +// - if the signature is valid +// - the y coordinate for rG (will be used for ECRecovery later) +#[allow(clippy::too_many_arguments)] +pub(crate) fn ecdsa_verify_no_pubkey_check( + base_chip: &FpConfig, + ctx: &mut Context, + pubkey: &EcPoint as FieldChip>::FieldPoint>, + r: &CRTInteger, + s: &CRTInteger, + msghash: &CRTInteger, + var_window_bits: usize, + fixed_window_bits: usize, +) -> ( + AssignedValue, + AssignedValue, + CRTInteger, + AssignedValue, +) +where + GA: CurveAffineExt, +{ + let ecc_chip = EccChip::>::construct(base_chip.clone()); + let scalar_chip = FpConfig::::construct( + base_chip.range.clone(), + base_chip.limb_bits, + base_chip.num_limbs, + modulus::(), + ); + let n = scalar_chip.load_constant(ctx, scalar_chip.p.to_biguint().unwrap()); + + // check whether the pubkey is (0, 0), i.e. in the case of ecrecover, no pubkey could be + // recovered. + let (is_pubkey_zero, is_pubkey_not_zero) = { + let is_pubkey_x_zero = ecc_chip.field_chip().is_zero(ctx, &pubkey.x); + let is_pubkey_y_zero = ecc_chip.field_chip().is_zero(ctx, &pubkey.y); + let is_pubkey_zero = ecc_chip.field_chip().range().gate().and( + ctx, + Existing(is_pubkey_x_zero), + Existing(is_pubkey_y_zero), + ); + ( + is_pubkey_zero, + ecc_chip + .field_chip() + .range() + .gate() + .not(ctx, Existing(is_pubkey_zero)), + ) + }; + + // check r,s are in [1, n - 1] + let r_is_zero = scalar_chip.is_soft_zero(ctx, r); + let r_in_range = scalar_chip.is_soft_nonzero(ctx, r); + let r_is_valid = base_chip + .range() + .gate() + .or(ctx, Existing(r_is_zero), Existing(r_in_range)); + let s_is_zero = scalar_chip.is_soft_zero(ctx, s); + let s_in_range = scalar_chip.is_soft_nonzero(ctx, s); + let s_is_valid = base_chip + .range() + .gate() + .or(ctx, Existing(s_is_zero), Existing(s_in_range)); + + // load required constants + let zero = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::ZERO)); + let one = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(SF::ONE)); + let neg_one = scalar_chip.load_constant(ctx, FpConfig::::fe_to_constant(-SF::ONE)); + + // compute u1 = m * s^{-1} mod n + let s2 = scalar_chip.select(ctx, &one, s, &s_is_zero); + let u1 = scalar_chip.divide(ctx, msghash, &s2); + let u1 = scalar_chip.select(ctx, &zero, &u1, &s_is_zero); + let u1_is_one = { + let diff = scalar_chip.sub_no_carry(ctx, &u1, &one); + let diff = scalar_chip.carry_mod(ctx, &diff); + scalar_chip.is_zero(ctx, &diff) + }; + + // compute u2 = r * s^{-1} mod n + let u2 = scalar_chip.divide(ctx, r, &s2); + let u2 = scalar_chip.select(ctx, &zero, &u2, &s_is_zero); + + // u3 = 1 if u1 == 1 + // u3 = -1 if u1 != 1 + // this ensures u1 + u3 != 0 + let u3 = scalar_chip.select(ctx, &one, &neg_one, &u1_is_one); + + let u1_plus_u3 = scalar_chip.add_no_carry(ctx, &u1, &u3); + let u1_plus_u3 = scalar_chip.carry_mod(ctx, &u1_plus_u3); + + // compute (u1+u3) * G + let u1u3_mul = fixed_base::scalar_multiply::( + base_chip, + ctx, + &GA::generator(), + &u1_plus_u3.truncation.limbs, + base_chip.limb_bits, + fixed_window_bits, + ); + // compute u2 * pubkey + let u2_prime = scalar_chip.select(ctx, &one, &u2, &s_is_zero); + let pubkey_prime = ecc_chip.load_random_point::(ctx); + let pubkey_prime = ecc_chip.select(ctx, &pubkey_prime, pubkey, &is_pubkey_zero); + let u2_mul = scalar_multiply::( + base_chip, + ctx, + &pubkey_prime, + &u2_prime.truncation.limbs, + base_chip.limb_bits, + var_window_bits, + ); + let point_at_infinity = EcPoint::construct( + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&CF::ZERO)), + ecc_chip + .field_chip() + .load_constant(ctx, fe_to_biguint(&CF::ZERO)), + ); + let u2_is_zero = + base_chip + .range() + .gate() + .or(ctx, Existing(s_is_zero), Existing(is_pubkey_zero)); + let u2_mul = ecc_chip.select(ctx, &point_at_infinity, &u2_mul, &u2_is_zero); + // compute u3*G this is directly assigned for G so no scalar_multiply is required + let u3_mul = { + let generator = GA::generator(); + let neg_generator = -generator; + let generator = ecc_chip.assign_constant_point(ctx, generator); + let neg_generator = ecc_chip.assign_constant_point(ctx, neg_generator); + ecc_chip.select(ctx, &generator, &neg_generator, &u1_is_one) + }; + + // compute u2 * pubkey + u3 * G + base_chip.enforce_less_than_p(ctx, u2_mul.x()); + base_chip.enforce_less_than_p(ctx, u3_mul.x()); + let u2_pk_u3_g = ec_add_unequal(base_chip, ctx, &u2_mul, &u3_mul, false); + + // check + // - (u1 + u3) * G + // - u2 * pubkey + u3 * G + // are not equal + let u1_u2_x_eq = ecc_chip.is_equal(ctx, &u1u3_mul, &u2_pk_u3_g); + let u1_u2_not_eq = base_chip.range.gate().not(ctx, Existing(u1_u2_x_eq)); + + // compute (x1, y1) = u1 * G + u2 * pubkey and check (r mod n) == x1 as integers + // which is basically u1u3_mul + u2_mul - u3_mul + // WARNING: For optimization reasons, does not reduce x1 mod n, which is + // invalid unless p is very close to n in size. + // + // WARNING: this may be trigger errors if: + // - u1u3_mul == u2_mul + // + // if r is sampled truly from random then this will not happen + // to completely ensure the correctness we may need to sample u3 from random, but it is quite + // costly. + let sum = ec_add_unequal(base_chip, ctx, &u1u3_mul, &u2_mul, false); + + // safe: we have already checked u1.G + u2.pk != 0 + // so u1.G + u3.G + u2.pk != u3.G + let sum = ec_sub_unequal(base_chip, ctx, &sum, &u3_mul, false); + let equal_check = base_chip.is_equal(ctx, &sum.x, r); + + // TODO: maybe the big_less_than is optional? + let u1_small = big_less_than::assign::( + base_chip.range(), + ctx, + &u1.truncation, + &n.truncation, + base_chip.limb_bits, + base_chip.limb_bases[1], + ); + let u2_small = big_less_than::assign::( + base_chip.range(), + ctx, + &u2.truncation, + &n.truncation, + base_chip.limb_bits, + base_chip.limb_bases[1], + ); + + // check + // - (r in [0, n - 1]) + // - (s in [0, n - 1]) + // - (u1_mul != - u2_mul) + // - (r == x1 mod n) + // - pk != (0, 0) + let res = base_chip.range().gate().and_many( + ctx, + vec![ + Existing(r_is_valid), + Existing(s_is_valid), + Existing(u1_small), + Existing(u2_small), + Existing(u1_u2_not_eq), + Existing(equal_check), + Existing(is_pubkey_not_zero), + ], + ); + + let y_is_zero = scalar_chip.is_soft_zero(ctx, &sum.y); + (res, is_pubkey_zero, sum.y, y_is_zero) +} diff --git a/zkevm-circuits/src/sig_circuit/test.rs b/zkevm-circuits/src/sig_circuit/test.rs new file mode 100644 index 0000000000..5d845ae43b --- /dev/null +++ b/zkevm-circuits/src/sig_circuit/test.rs @@ -0,0 +1,267 @@ +use super::*; +use eth_types::sign_types::{sign, SignData}; +use halo2_proofs::{ + arithmetic::Field as HaloField, + dev::MockProver, + halo2curves::{ + bn256::Fr, + group::Curve, + secp256k1::{self, Secp256k1Affine}, + }, +}; +use rand::{Rng, RngCore}; +use std::marker::PhantomData; + +use super::utils::LOG_TOTAL_NUM_ROWS; +use crate::sig_circuit::utils::MAX_NUM_SIG; +use eth_types::{ + sign_types::{biguint_to_32bytes_le, recover_pk, SECP256K1_Q}, + word, ToBigEndian, ToLittleEndian, Word, +}; +use ethers_core::k256::elliptic_curve::PrimeField; +use halo2_proofs::halo2curves::secp256k1::Fq; +use num::{BigUint, Integer}; +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; +use sha3::{Digest, Keccak256}; +use snark_verifier::util::arithmetic::PrimeCurveAffine; + +#[test] +fn test_edge_cases() { + let mut rng = XorShiftRng::seed_from_u64(1); + + // helper + let to_sig = |(r, s, v): (Word, Word, u8)| -> (Fq, Fq, u8) { + ( + Fq::from_bytes(&r.to_le_bytes()).unwrap(), + Fq::from_bytes(&s.to_le_bytes()).unwrap(), + v, + ) + }; + + // Vec<(msg_hash, r, s, v)> + // + // good data for ecrecover is (big-endian): + // - msg_hash: 0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3 + // - r: 0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608 + // - s: 0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada + // - v: 28, i.e. v == 1 for sig circuit + let good_ecrecover_data = ( + word!("0x456e9aea5e197a1f1af7a3e85a3212fa4049a3ba34c2289b4c860fc0b0c64ef3"), + word!("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac8038825608"), + word!("0x4f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada"), + 1u8, + ); + let ecrecover_data = vec![ + // 1. good data + good_ecrecover_data, + // 2. msg_hash == 0 + ( + Word::zero(), + good_ecrecover_data.1, + good_ecrecover_data.2, + good_ecrecover_data.3, + ), + // 3. r == 0 + ( + good_ecrecover_data.0, + Word::zero(), + good_ecrecover_data.2, + good_ecrecover_data.3, + ), + // 4. s == 0 + ( + good_ecrecover_data.0, + good_ecrecover_data.1, + Word::zero(), + good_ecrecover_data.3, + ), + // 5. r == 0 and s == 0 + ( + good_ecrecover_data.0, + Word::zero(), + Word::zero(), + good_ecrecover_data.3, + ), + // 6. random r and s for random msg hash + { + let mut bytes = [0u8; 32]; + rng.fill(&mut bytes[..]); + let msg_hash = Word::from_big_endian(&bytes); + rng.fill(&mut bytes[..]); + let r = Word::from_big_endian(&bytes); + rng.fill(&mut bytes[..]); + let s = Word::from_big_endian(&bytes); + (msg_hash, r, s, 0u8) + }, + // 7. v == 0 when v should be 1 + ( + good_ecrecover_data.0, + good_ecrecover_data.1, + good_ecrecover_data.2, + 1 - good_ecrecover_data.3, + ), + // 8. msg_hash outside FQ::MODULUS + ( + Word::MAX, + good_ecrecover_data.1, + good_ecrecover_data.2, + good_ecrecover_data.3, + ), + ]; + let signatures = ecrecover_data + .iter() + .map(|&(msg_hash, r, s, v)| SignData { + signature: to_sig((r, s, v)), + pk: recover_pk(v, &r, &s, &msg_hash.to_be_bytes()) + .unwrap_or(Secp256k1Affine::identity()), + msg_hash: { + let msg_hash = BigUint::from_bytes_be(&msg_hash.to_be_bytes()); + let msg_hash = msg_hash.mod_floor(&*SECP256K1_Q); + let msg_hash_le = biguint_to_32bytes_le(msg_hash); + secp256k1::Fq::from_repr(msg_hash_le).unwrap() + }, + ..Default::default() + }) + .collect(); + log::debug!("signatures="); + log::debug!("{:#?}", signatures); + + run(LOG_TOTAL_NUM_ROWS as u32, 8, signatures); +} + +#[test] +fn sign_verify_zero_msg_hash() { + let mut rng = XorShiftRng::seed_from_u64(1); + + log::debug!("testing for msg_hash = 0"); + let mut signatures = Vec::new(); + + let (sk, pk) = gen_key_pair(&mut rng); + let msg = gen_msg(&mut rng); + let msg_hash = secp256k1::Fq::zero(); + let (r, s, v) = sign_with_rng(&mut rng, sk, msg_hash); + signatures.push(SignData { + signature: (r, s, v), + pk, + msg: msg.into(), + msg_hash, + }); + + let k = LOG_TOTAL_NUM_ROWS as u32; + run(k, 1, signatures); + + log::debug!("end of testing for msg_hash = 0"); +} + +#[test] +fn sign_verify_nonzero_msg_hash() { + let mut rng = XorShiftRng::seed_from_u64(1); + + log::debug!("testing for msg_hash = 1"); + let mut signatures = Vec::new(); + + let (sk, pk) = gen_key_pair(&mut rng); + let msg = gen_msg(&mut rng); + let msg_hash = secp256k1::Fq::one(); + let (r, s, v) = sign_with_rng(&mut rng, sk, msg_hash); + signatures.push(SignData { + signature: (r, s, v), + pk, + msg: msg.into(), + msg_hash, + }); + + let k = LOG_TOTAL_NUM_ROWS as u32; + run(k, 1, signatures); + + log::debug!("end of testing for msg_hash = 1"); +} + +#[test] +fn test_sign_verify() { + let max_sigs = [1, 4]; + for max_sig in max_sigs { + sign_verify(max_sig as usize); + } +} + +#[test] +#[ignore] +fn test_sign_verify_max_num_sig() { + sign_verify(MAX_NUM_SIG); +} + +fn sign_verify(max_sig: usize) { + let mut rng = XorShiftRng::seed_from_u64(1); + + // random msg_hash + log::debug!("testing for {} signatures", max_sig); + let mut signatures = Vec::new(); + for _ in 0..max_sig { + let (sk, pk) = gen_key_pair(&mut rng); + let msg = gen_msg(&mut rng); + let msg_hash: [u8; 32] = Keccak256::digest(&msg) + .as_slice() + .to_vec() + .try_into() + .expect("hash length isn't 32 bytes"); + let msg_hash = secp256k1::Fq::from_bytes(&msg_hash).unwrap(); + let (r, s, v) = sign_with_rng(&mut rng, sk, msg_hash); + signatures.push(SignData { + signature: (r, s, v), + pk, + msg: msg.into(), + msg_hash, + }); + } + + let k = LOG_TOTAL_NUM_ROWS as u32; + run(k, max_sig, signatures); + + log::debug!("end of testing for {} signatures", max_sig); +} + +// Generate a test key pair +fn gen_key_pair(rng: impl RngCore) -> (secp256k1::Fq, Secp256k1Affine) { + // generate a valid signature + let generator = Secp256k1Affine::generator(); + let sk = secp256k1::Fq::random(rng); + let pk = generator * sk; + let pk = pk.to_affine(); + + (sk, pk) +} + +// Generate a test message. +fn gen_msg(mut rng: impl RngCore) -> Vec { + let msg_len: usize = rng.gen_range(0..128); + let mut msg = vec![0; msg_len]; + rng.fill_bytes(&mut msg); + msg +} + +// Returns (r, s, v) +fn sign_with_rng( + rng: impl RngCore, + sk: secp256k1::Fq, + msg_hash: secp256k1::Fq, +) -> (secp256k1::Fq, secp256k1::Fq, u8) { + let randomness = secp256k1::Fq::random(rng); + sign(randomness, sk, msg_hash) +} + +fn run(k: u32, max_verif: usize, signatures: Vec) { + // SignVerifyChip -> ECDSAChip -> MainGate instance column + let circuit = SigCircuit:: { + max_verif, + signatures, + _marker: PhantomData, + }; + + let prover = match MockProver::run(k, &circuit, vec![]) { + Ok(prover) => prover, + Err(e) => panic!("{e:#?}"), + }; + assert_eq!(prover.verify(), Ok(())); +} diff --git a/zkevm-circuits/src/sig_circuit/utils.rs b/zkevm-circuits/src/sig_circuit/utils.rs new file mode 100644 index 0000000000..b7aacf8aed --- /dev/null +++ b/zkevm-circuits/src/sig_circuit/utils.rs @@ -0,0 +1,115 @@ +use eth_types::Field; +use halo2_base::{AssignedValue, QuantumCell}; +use halo2_ecc::{ + bigint::CRTInteger, + ecc::EcPoint, + fields::{fp::FpConfig, FieldChip}, +}; +use halo2_proofs::halo2curves::secp256k1::{Fp, Fq}; + +use crate::util::word::WordLoHi; + +// Hard coded parameters. +// TODO: allow for a configurable param. +pub(super) const MAX_NUM_SIG: usize = 128; +/// Each ecdsa signature requires 461540 cells +pub(super) const CELLS_PER_SIG: usize = 461540; +/// Each ecdsa signature requires 63489 lookup cells +pub(super) const LOOKUP_CELLS_PER_SIG: usize = 63489; +/// Total number of rows allocated for ecdsa chip +pub(super) const LOG_TOTAL_NUM_ROWS: usize = 20; +/// Max number of columns allowed +pub(super) const COLUMN_NUM_LIMIT: usize = 58; +/// Max number of lookup columns allowed +pub(super) const LOOKUP_COLUMN_NUM_LIMIT: usize = 9; + +// halo2-ecc's ECDSA config +// +// get the following parameters by running +// `cargo test --release --package zkevm-circuits --lib sig_circuit::test::sign_verify -- +// --nocapture` +// - num_advice: 56 +// - num_lookup_advice: 8 +// - num_fixed: 1 +// - lookup_bits: 19 +// - limb_bits: 88 +// - num_limbs: 3 +// +/// Number of bits of a limb +pub(super) const LIMB_BITS: usize = 88; +/// Number of limbs +pub(super) const NUM_LIMBS: usize = 3; + +pub(super) fn calc_required_advices(num_verif: usize) -> usize { + let mut num_adv = 1; + let total_cells = num_verif * CELLS_PER_SIG; + let row_num = 1 << LOG_TOTAL_NUM_ROWS; + while num_adv < COLUMN_NUM_LIMIT { + if num_adv * row_num > total_cells { + log::debug!( + "ecdsa chip uses {} advice columns for {} signatures", + num_adv, + num_verif + ); + return num_adv; + } + num_adv += 1; + } + panic!("the required advice columns exceeds {COLUMN_NUM_LIMIT} for {num_verif} signatures"); +} + +pub(super) fn calc_required_lookup_advices(num_verif: usize) -> usize { + let mut num_adv = 1; + let total_cells = num_verif * LOOKUP_CELLS_PER_SIG; + let row_num = 1 << LOG_TOTAL_NUM_ROWS; + while num_adv < LOOKUP_COLUMN_NUM_LIMIT { + if num_adv * row_num > total_cells { + log::debug!( + "ecdsa chip uses {} lookup advice columns for {} signatures", + num_adv, + num_verif + ); + return num_adv; + } + num_adv += 1; + } + panic!("the required lookup advice columns exceeds {LOOKUP_COLUMN_NUM_LIMIT} for {num_verif} signatures"); +} + +/// Chip to handle overflow integers of ECDSA::Fq, the scalar field +pub(super) type FqChip = FpConfig; +/// Chip to handle ECDSA::Fp, the base field +pub(super) type FpChip = FpConfig; + +pub(crate) struct AssignedECDSA> { + pub(super) _pk: EcPoint, + pub(super) pk_is_zero: AssignedValue, + pub(super) msg_hash: CRTInteger, + pub(super) integer_r: CRTInteger, + pub(super) integer_s: CRTInteger, + pub(super) v: AssignedValue, + pub(super) sig_is_valid: AssignedValue, +} + +#[derive(Debug, Clone)] +pub(crate) struct AssignedSignatureVerify { + pub(crate) address: AssignedValue, + // pub(crate) msg_len: usize, + // pub(crate) msg_rlc: Value, + pub(crate) msg_hash: WordLoHi>, + pub(crate) r: WordLoHi>, + pub(crate) s: WordLoHi>, + pub(crate) v: AssignedValue, + pub(crate) sig_is_valid: AssignedValue, +} + +pub(super) struct SignDataDecomposed { + pub(super) pk_hash_cells: Vec>, + pub(super) msg_hash_cells: Vec>, + pub(super) pk_cells: Vec>, + pub(super) address: AssignedValue, + pub(super) is_address_zero: AssignedValue, + pub(super) r_cells: Vec>, + pub(super) s_cells: Vec>, + // v: AssignedValue<'v, F>, // bool +} diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 786d235fed..786493e399 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -62,8 +62,8 @@ use crate::{ pi_circuit::{PiCircuit, PiCircuitConfig, PiCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, TxTable, - UXTable, WdTable, + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, SigTable, + TxTable, UXTable, WdTable, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, util::{log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, @@ -136,6 +136,7 @@ impl SubCircuitConfig for SuperCircuitConfig { power_of_randomness[0].clone(), power_of_randomness[0].clone(), ); + let sig_table = SigTable::construct(meta); let keccak_circuit = KeccakCircuitConfig::new( meta, @@ -210,6 +211,7 @@ impl SubCircuitConfig for SuperCircuitConfig { exp_table, u8_table, u16_table, + sig_table, feature_config, }, ); diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index ab6213611b..741fbdad8b 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -140,6 +140,7 @@ fn serial_test_super_circuit_1tx_1max_tx() { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } @@ -157,6 +158,7 @@ fn serial_test_super_circuit_1tx_2max_tx() { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } @@ -174,6 +176,7 @@ fn serial_test_super_circuit_2tx_2max_tx() { max_bytecode: 512, max_evm_rows: 0, max_keccak_rows: 0, + max_vertical_circuit_rows: 0, }; 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 fde4ef38e1..fb0be4aefd 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -37,6 +37,8 @@ pub(crate) mod keccak_table; pub mod mpt_table; /// rw table pub(crate) mod rw_table; +/// signature table +pub(crate) mod sig_table; /// tx table pub(crate) mod tx_table; /// ux table @@ -53,6 +55,7 @@ pub(crate) use ux_table::UXTable; pub use mpt_table::{MPTProofType, MptTable}; pub(crate) use rw_table::RwTable; +pub(crate) use sig_table::SigTable; pub(crate) use tx_table::{ TxContextFieldTag, TxFieldTag, TxLogFieldTag, TxReceiptFieldTag, TxTable, }; diff --git a/zkevm-circuits/src/table/sig_table.rs b/zkevm-circuits/src/table/sig_table.rs new file mode 100644 index 0000000000..037b0c9cfe --- /dev/null +++ b/zkevm-circuits/src/table/sig_table.rs @@ -0,0 +1,131 @@ +use super::*; + +use eth_types::sign_types::SignData; + +/// The sig table is used to verify signatures, used in tx circuit and ecrecover precompile. +#[derive(Clone, Copy, Debug)] +pub struct SigTable { + /// Indicates whether or not the gates are enabled on the current row. + pub q_enable: Column, + /// Keccak256 hash of the message that's signed. + pub msg_hash: WordLoHi>, + /// signature's `r` component. + pub sig_r: WordLoHi>, + /// signature's `s` component. + pub sig_s: WordLoHi>, + /// should be in range [0, 1] + pub sig_v: Column, + /// The recovered address, i.e. the 20-bytes address that must have signed the message. + pub recovered_addr: Column, + /// Indicates whether or not the signature is valid or not upon signature verification. + pub is_valid: Column, +} + +impl SigTable { + /// Construct the SigTable. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self { + q_enable: meta.fixed_column(), + msg_hash: WordLoHi::new([meta.advice_column(), meta.advice_column()]), + sig_r: WordLoHi::new([meta.advice_column(), meta.advice_column()]), + sig_s: WordLoHi::new([meta.advice_column(), meta.advice_column()]), + sig_v: meta.advice_column(), + recovered_addr: meta.advice_column(), + is_valid: meta.advice_column(), + } + } + + /// Assign witness data from a block to the verification table. + pub fn dev_load( + &self, + layouter: &mut impl Layouter, + block: &Block, + ) -> Result<(), Error> { + layouter.assign_region( + || "sig table (dev load)", + |mut region| { + let signatures: Vec = block.get_sign_data(false); + + for (offset, sign_data) in signatures.iter().enumerate() { + let msg_hash = + WordLoHi::from(U256::from(sign_data.msg_hash.to_bytes())).into_value(); + let sig_r = + WordLoHi::from(U256::from(sign_data.signature.0.to_bytes())).into_value(); + let sig_s = + WordLoHi::from(U256::from(sign_data.signature.1.to_bytes())).into_value(); + let sig_v = Value::known(F::from(sign_data.signature.2 as u64)); + let recovered_addr = Value::known(sign_data.get_addr().to_scalar().unwrap()); + region.assign_fixed( + || format!("sig table q_enable {offset}"), + self.q_enable, + offset, + || Value::known(F::ONE), + )?; + for (column, value) in [ + (self.sig_v, sig_v), + (self.recovered_addr, recovered_addr), + ( + self.is_valid, + Value::known(F::from(!sign_data.get_addr().is_zero() as u64)), + ), + ] { + region.assign_advice( + || "assign sign data on sig table", + column, + offset, + || value, + )?; + } + for (column, value) in [ + (self.msg_hash, msg_hash), + (self.sig_r, sig_r), + (self.sig_s, sig_s), + ] { + value.assign_advice( + &mut region, + || "assign sign data on sig table", + column, + offset, + )?; + } + } + + Ok(()) + }, + )?; + + Ok(()) + } +} + +impl LookupTable for SigTable { + fn columns(&self) -> Vec> { + vec![ + self.q_enable.into(), + self.msg_hash.lo().into(), + self.msg_hash.hi().into(), + self.sig_v.into(), + self.sig_r.lo().into(), + self.sig_r.hi().into(), + self.sig_s.lo().into(), + self.sig_s.hi().into(), + self.recovered_addr.into(), + self.is_valid.into(), + ] + } + + fn annotations(&self) -> Vec { + vec![ + String::from("q_enable"), + String::from("msg_hash_lo"), + String::from("msg_hash_hi"), + String::from("sig_v"), + String::from("sig_r_lo"), + String::from("sig_r_hi"), + String::from("sig_s_lo"), + String::from("sig_s_hi"), + String::from("recovered_addr"), + String::from("is_valid"), + ] + } +} diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index b99f2a46a2..15587021c6 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -350,9 +350,10 @@ impl SignVerifyChip { let SignData { signature, pk, + msg: _, msg_hash, } = sign_data; - let (sig_r, sig_s) = signature; + let (sig_r, sig_s, _) = signature; let ChipsRef { main_gate: _, @@ -717,7 +718,7 @@ mod sign_verify_tests { use super::*; use crate::util::Challenges; use bus_mapping::circuit_input_builder::keccak_inputs_sign_verify; - use eth_types::sign_types::sign; + use eth_types::{sign_types::sign, Bytes}; use halo2_proofs::{ arithmetic::Field as HaloField, circuit::SimpleFloorPlanner, @@ -841,7 +842,7 @@ mod sign_verify_tests { rng: impl RngCore, sk: secp256k1::Fq, msg_hash: secp256k1::Fq, - ) -> (secp256k1::Fq, secp256k1::Fq) { + ) -> (secp256k1::Fq, secp256k1::Fq, u8) { let randomness = secp256k1::Fq::random(rng); sign(randomness, sk, msg_hash) } @@ -868,6 +869,7 @@ mod sign_verify_tests { signature: sig, pk, msg_hash, + msg: Bytes::new(), }); } diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 1628534093..507e8e370c 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -7,11 +7,13 @@ use crate::{ util::{log2_ceil, word::WordLoHi, SubCircuit}, }; use bus_mapping::{ - circuit_input_builder::{self, CopyEvent, ExpEvent, FeatureConfig, FixedCParams, Withdrawal}, + circuit_input_builder::{ + self, CopyEvent, ExpEvent, FeatureConfig, FixedCParams, PrecompileEvents, Withdrawal, + }, state_db::CodeDB, Error, }; -use eth_types::{Address, Field, ToScalar, Word, H256}; +use eth_types::{sign_types::SignData, Address, Field, ToScalar, Word, H256}; use halo2_proofs::circuit::Value; use itertools::Itertools; @@ -51,6 +53,8 @@ pub struct Block { pub prev_state_root: Word, // TODO: Make this H256 /// Keccak inputs pub keccak_inputs: Vec>, + /// IO to/from the precompiled contract calls. + pub precompile_events: PrecompileEvents, /// Original Block from geth pub eth_block: eth_types::Block, } @@ -73,6 +77,26 @@ impl Block { } } + /// Get signature (witness) from the block for tx signatures and ecRecover calls. + pub(crate) fn get_sign_data(&self, padding: bool) -> Vec { + let mut signatures: Vec = self + .txs + .iter() + .map(|tx| tx.tx.sign_data(self.context.chain_id.as_u64())) + .filter_map(|res| res.ok()) + .collect::>(); + signatures.extend_from_slice(&self.precompile_events.get_ecrecover_events()); + if padding && self.txs.len() < self.circuits_params.max_txs { + // padding tx's sign data + signatures.push( + Transaction::dummy() + .sign_data(self.context.chain_id.as_u64()) + .unwrap(), + ); + } + signatures + } + /// Get a read-write record pub(crate) fn get_rws(&self, step: &ExecStep, index: usize) -> Rw { self.rws[step.rw_index(index)] @@ -294,6 +318,7 @@ pub fn block_convert( exp_circuit_pad_to: ::default(), prev_state_root: block.prev_state_root, keccak_inputs: circuit_input_builder::keccak_inputs(block, code_db)?, + precompile_events: block.precompile_events.clone(), eth_block: block.eth_block.clone(), }; let public_data = public_data_convert(&block); From bc1bdaea74ff7eda0c7cb80abd80a06a97028ec1 Mon Sep 17 00:00:00 2001 From: ntampakas Date: Wed, 21 Feb 2024 09:58:30 +0200 Subject: [PATCH 04/16] Change testool URL (enforce HTTP to HTTPS redirection) (#1772) ### Description The testool URL has been changed and now points to a CloudFront distribution domain name. Access to the testool S3 bucket is through CloudFront in order to enforce HTTP to HTTPS redirection. ### Issue Link As requested in a Discord discussion ### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update - [ ] Refactor (no updates to logic) ### How Has This Been Tested? Executed the workflow manually --- .github/testoolScripts/cleanup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/testoolScripts/cleanup.sh b/.github/testoolScripts/cleanup.sh index a503363652..6129bb8d76 100755 --- a/.github/testoolScripts/cleanup.sh +++ b/.github/testoolScripts/cleanup.sh @@ -6,7 +6,7 @@ runner_vpc_id="vpc-05dedcb650bd24f8d" # Get runner status runner=$(aws ec2 describe-instances --profile $profile --filters Name=tag:Name,Values=[testool] Name=network-interface.vpc-id,Values=[$runner_vpc_id] --query "Reservations[*].Instances[*][InstanceId]" --output text | xargs) -echo "Reports: http://testool-public.s3-website.eu-central-1.amazonaws.com" +echo "Reports: https://testool.pse.dev" echo "Shuting down instance..." aws ec2 stop-instances --profile $profile --instance-ids $runner From 26cd21b2ad090510547d76387644856578038f09 Mon Sep 17 00:00:00 2001 From: Magamedrasul Ibragimov Date: Wed, 21 Feb 2024 18:52:51 +0400 Subject: [PATCH 05/16] Remove mod.rs files (#1774) ### Description Refactor the module system to be the same within a project ### Type of change - [X] Refactor (no updates to logic) ### Rationale We use two different ways to address the module system - without `mod.rs` (only with directories/files) and with `mod.rs` in some places. Unless there's a specific reason for that - one OR the other must be used. I refactored the code and removed `mod.rs` files. ### How Has This Been Tested? I did `cargo build --release` --- .../src/evm/opcodes/{precompiles/mod.rs => precompiles.rs} | 0 testool/src/{statetest/mod.rs => statetest.rs} | 0 .../evm_circuit/execution/{precompiles/mod.rs => precompiles.rs} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename bus-mapping/src/evm/opcodes/{precompiles/mod.rs => precompiles.rs} (100%) rename testool/src/{statetest/mod.rs => statetest.rs} (100%) rename zkevm-circuits/src/evm_circuit/execution/{precompiles/mod.rs => precompiles.rs} (100%) diff --git a/bus-mapping/src/evm/opcodes/precompiles/mod.rs b/bus-mapping/src/evm/opcodes/precompiles.rs similarity index 100% rename from bus-mapping/src/evm/opcodes/precompiles/mod.rs rename to bus-mapping/src/evm/opcodes/precompiles.rs diff --git a/testool/src/statetest/mod.rs b/testool/src/statetest.rs similarity index 100% rename from testool/src/statetest/mod.rs rename to testool/src/statetest.rs diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles.rs similarity index 100% rename from zkevm-circuits/src/evm_circuit/execution/precompiles/mod.rs rename to zkevm-circuits/src/evm_circuit/execution/precompiles.rs From 272377570706e14745aa22573a29e8270d085bae Mon Sep 17 00:00:00 2001 From: soham <22412996+zemse@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:44:54 +0530 Subject: [PATCH 06/16] fix nondeterministic constraint generation (#1706) ### Description Change HashMap to BTreeMap in memory.rs and in cell_placement_strategy. ### Issue Link #1700 ### Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ### Contents - fix in memory.rs - fix in cell_placement_strategy ### Rationale Generates different circuit keys making proof verification fail for different witnesses. ### How Has This Been Tested? Manual testing. --- zkevm-circuits/src/circuit_tools/memory.rs | 6 +- zkevm-circuits/src/mpt_circuit.rs | 63 +++++++++++++------ .../src/util/cell_placement_strategy.rs | 8 +-- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/zkevm-circuits/src/circuit_tools/memory.rs b/zkevm-circuits/src/circuit_tools/memory.rs index 873ec23acb..a36edaa15d 100644 --- a/zkevm-circuits/src/circuit_tools/memory.rs +++ b/zkevm-circuits/src/circuit_tools/memory.rs @@ -7,7 +7,7 @@ use halo2_proofs::{ poly::Rotation, }; use std::{ - collections::HashMap, + collections::BTreeMap, marker::PhantomData, ops::{Index, IndexMut}, }; @@ -20,7 +20,7 @@ use super::{ #[derive(Clone, Debug, Default)] pub(crate) struct Memory> { - banks: HashMap, + banks: BTreeMap, _phantom: PhantomData, tag_counter: usize, } @@ -42,7 +42,7 @@ impl> IndexMut for Memory> Memory { pub(crate) fn new() -> Self { Self { - banks: HashMap::new(), + banks: BTreeMap::new(), _phantom: PhantomData, tag_counter: 0, } diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index 8f4dfbbc9f..a69fb5b94e 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -766,10 +766,38 @@ pub fn load_proof_from_file(path: &str) -> Vec { mod tests { use super::*; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; - use std::{fs, ops::Deref}; + use itertools::Itertools; + use std::{fs, ops::Deref, path::PathBuf}; #[test] fn test_mpt() { + let degree = 15; + get_witnesses() + .enumerate() + .for_each(|(idx, (path, num_rows, circuit))| { + println!("{} {:?}", idx, path); + let prover = MockProver::::run(degree, &circuit, vec![]).unwrap(); + assert_eq!(prover.verify_at_rows(0..num_rows, 0..num_rows,), Ok(())); + // assert_eq!(prover.verify_par(), Ok(())); + // prover.assert_satisfied(); + }); + } + + #[test] + fn variadic_size_check() { + let mut circuits = get_witnesses(); + let first = circuits.next().unwrap().2; + let second = circuits.next().unwrap().2; + + let degree = 15; + let prover_1 = MockProver::::run(degree, &first, vec![]).unwrap(); + let prover_2 = MockProver::::run(degree, &second, vec![]).unwrap(); + + assert_eq!(prover_1.fixed(), prover_2.fixed()); + assert_eq!(prover_1.permutation(), prover_2.permutation()); + } + + fn get_witnesses() -> impl Iterator)> { let path = "src/mpt_circuit/tests"; let files = fs::read_dir(path).unwrap(); files @@ -781,8 +809,8 @@ mod tests { false } }) - .enumerate() - .for_each(|(idx, f)| { + .sorted_by(|a, b| a.file_name().cmp(&b.file_name())) + .map(|f| { let path = f.path(); let mut parts = path.to_str().unwrap().split('-'); parts.next(); @@ -796,24 +824,21 @@ mod tests { keccak_data.push(k.deref().clone()); } } - let disable_preimage_check = nodes[0].start.clone().unwrap().disable_preimage_check; let degree = 15; let max_nodes = 520; - let circuit = MPTCircuit:: { - nodes, - keccak_data, - degree, - max_nodes, - disable_preimage_check, - _marker: PhantomData, - }; - - println!("{} {:?}", idx, path); - let prover = MockProver::::run(degree as u32, &circuit, vec![]).unwrap(); - assert_eq!(prover.verify_at_rows(0..num_rows, 0..num_rows,), Ok(())); - // assert_eq!(prover.verify(), Ok(())); - // prover.assert_satisfied(); - }); + ( + path, + num_rows, + MPTCircuit:: { + nodes, + keccak_data, + degree, + max_nodes, + disable_preimage_check, + _marker: PhantomData, + }, + ) + }) } } diff --git a/zkevm-circuits/src/util/cell_placement_strategy.rs b/zkevm-circuits/src/util/cell_placement_strategy.rs index 88e0ff0511..3ccecbd9c9 100644 --- a/zkevm-circuits/src/util/cell_placement_strategy.rs +++ b/zkevm-circuits/src/util/cell_placement_strategy.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use eth_types::Field; use halo2_proofs::plonk::{Advice, Column, ConstraintSystem}; @@ -8,7 +8,7 @@ use super::cell_manager::{ }; #[derive(Clone, Debug, Default)] -pub(crate) struct CMFixedWidthStrategyDistribution(HashMap>>); +pub(crate) struct CMFixedWidthStrategyDistribution(BTreeMap>>); impl CMFixedWidthStrategyDistribution { pub(crate) fn add(&mut self, cell_type: CellType, advice: Column) { @@ -34,7 +34,7 @@ pub(crate) struct CMFixedWidthStrategy { advices: CMFixedWidthStrategyDistribution, height_offset: usize, - next: HashMap, + next: BTreeMap, perm_substitution: bool, max_height: usize, @@ -52,7 +52,7 @@ impl CMFixedWidthStrategy { CMFixedWidthStrategy { advices, height_offset, - next: HashMap::default(), + next: BTreeMap::default(), perm_substitution: false, max_height: usize::max_value(), } From 8d230b348e8eca19f0a80d617920efb714b7be34 Mon Sep 17 00:00:00 2001 From: Eduard S Date: Thu, 22 Feb 2024 18:33:19 +0100 Subject: [PATCH 07/16] SubCircuit halo2 stats (#1763) ### Description Add a new command to the stats binary that reports halo2 circuit stats for each circuit, and the supercircuit. ### Issue Link Resolve https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/1645 ### Type of change - [x] New feature (non-breaking change which adds functionality) ### Contents - Added new module in `stats` that records halo2 circuit stats from the configured `ConstraintSystem`, based on Scroll's code from https://github.com/scroll-tech/zkevm-circuits/blob/7d9bc181953cfc6e7baf82ff0ce651281fd70a8a/zkevm-circuits/src/util.rs#L294 - The collection of stats works in two phases, first it records per-circuit stats and then total stats (to get super circuit stats). The per-circuit stats works by configuring all the shared tables into the same `ConstraintSystem` (and obtaining stats via deltas), and then configuring each sub-circuit starting from the shared tables `ConstraintSystems` each time. - In order to separate the shared column configuration from the sub-circuit and constraints configuration, I had to extract the column definition of the BinaryNumberChip into BinaryNumberBits so that the CopyTable can be constructed without any constraint. - Added peak memory estimation based on @han0110's analysis ### Results These results are for `k = 26`. At the current worst-case estimation of At 0.0139 gas/row this gives us 2^26 * 0.0139 = ~900k gas. For 30M gas we would need 33 chunks like that. |circuit|constraints|rots|min/max(rots)|fix_cols|sels|advs|perms|lookups|degree|mem_gb| |:----|:----|:----|:----|:----|:----|:----|:----|:----|:----|:----| |tx_table|0|0|0/0|1|0|4|0|0|3|58| |wd_table|0|0|0/0|0|0|5|0|0|3|56| |rw_table|0|0|0/0|0|0|14|0|0|3|110| |mpt_table|0|0|0/0|0|0|12|0|0|3|98| |bytecode_table|0|0|0/0|0|0|6|0|0|3|62| |block_table|0|0|0/0|2|0|2|0|0|3|54| |copy_table|0|0|0/0|1|0|12|0|0|3|106| |exp_table|0|0|0/0|1|0|5|0|0|3|64| |keccak_table|0|0|0/0|0|0|5|0|0|3|56| |sig_table|0|0|0/0|1|0|9|0|0|3|88| |u8_table|0|0|0/0|1|0|0|0|0|3|34| |u10_table|0|0|0/0|1|0|0|0|0|3|34| |u16_table|0|0|0/0|1|0|0|0|0|3|34| |keccak|2523|105|-89/207|18|0|198|0|123|4|3000| |pi|21|2|0/1|3|7|10|15|3|9|754| |tx|2|2|0/1|13|4|6|9|6|6|780| |bytecode|23|2|0/1|5|0|6|0|2|8|342| |copy|34|3|0/2|1|1|14|0|12|9|466| |state|203|3|-1/1|3|0|49|0|36|10|2224| |exp|44|11|0/10|0|1|10|0|0|5|142| |evm|42318|21|0/20|5|3|131|3|53|7|2976| |super|45168|106|-89/207|57|16|498|25|235|10|21736| PD: Yes, the estimation is 21TB of memory for a super circuit proof :( Nevertheless there are various things we can do to improve this. --- gadgets/src/binary_number.rs | 78 +++- integration-tests/Cargo.toml | 2 +- zkevm-circuits/Cargo.toml | 3 +- zkevm-circuits/src/bin/stats/halo2_stats.rs | 387 ++++++++++++++++++ zkevm-circuits/src/bin/stats/main.rs | 232 ++++++++++- zkevm-circuits/src/copy_circuit.rs | 14 +- zkevm-circuits/src/state_circuit.rs | 5 +- .../state_circuit/lexicographic_ordering.rs | 5 +- zkevm-circuits/src/super_circuit.rs | 36 +- zkevm-circuits/src/table.rs | 22 +- zkevm-circuits/src/table/copy_table.rs | 89 ++-- zkevm-circuits/src/table/mpt_table.rs | 2 +- zkevm-circuits/src/util.rs | 4 +- 13 files changed, 782 insertions(+), 97 deletions(-) create mode 100644 zkevm-circuits/src/bin/stats/halo2_stats.rs diff --git a/gadgets/src/binary_number.rs b/gadgets/src/binary_number.rs index 788074c5fd..68a3dd2e86 100644 --- a/gadgets/src/binary_number.rs +++ b/gadgets/src/binary_number.rs @@ -9,7 +9,7 @@ use halo2_proofs::{ plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, poly::Rotation, }; -use std::{collections::BTreeSet, marker::PhantomData}; +use std::{collections::BTreeSet, marker::PhantomData, ops::Deref}; use strum::IntoEnumIterator; /// Helper trait that implements functionality to represent a generic type as @@ -34,11 +34,66 @@ where } } +/// Columns of the binary number chip. This can be instantiated without the associated constraints +/// of the BinaryNumberChip in order to be used as part of a shared table for unit tests. +#[derive(Clone, Copy, Debug)] +pub struct BinaryNumberBits( + /// Must be constrained to be binary for correctness. + pub [Column; N], +); + +impl Deref for BinaryNumberBits { + type Target = [Column; N]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl BinaryNumberBits { + /// Construct a new BinaryNumberBits without adding any constraints. + pub fn construct(meta: &mut ConstraintSystem) -> Self { + Self([0; N].map(|_| meta.advice_column())) + } + + /// Assign a value to the binary number bits. A generic type that implements + /// the AsBits trait can be provided for assignment. + pub fn assign>( + &self, + region: &mut Region<'_, F>, + offset: usize, + value: &T, + ) -> Result<(), Error> { + for (&bit, &column) in value.as_bits().iter().zip(self.iter()) { + region.assign_advice( + || format!("binary number {:?}", column), + column, + offset, + || Value::known(F::from(bit as u64)), + )?; + } + Ok(()) + } + + /// Returns the expression value of the bits at the given rotation. + pub fn value( + &self, + rotation: Rotation, + ) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression { + let bits = self.0; + move |meta: &mut VirtualCells<'_, F>| { + let bits = bits.map(|bit| meta.query_advice(bit, rotation)); + bits.iter() + .fold(0.expr(), |result, bit| bit.clone() + result * 2.expr()) + } + } +} + /// Config for the binary number chip. #[derive(Clone, Copy, Debug)] pub struct BinaryNumberConfig { /// Must be constrained to be binary for correctness. - pub bits: [Column; N], + pub bits: BinaryNumberBits, _marker: PhantomData, } @@ -51,12 +106,7 @@ where &self, rotation: Rotation, ) -> impl FnOnce(&mut VirtualCells<'_, F>) -> Expression { - let bits = self.bits; - move |meta: &mut VirtualCells<'_, F>| { - let bits = bits.map(|bit| meta.query_advice(bit, rotation)); - bits.iter() - .fold(0.expr(), |result, bit| bit.clone() + result * 2.expr()) - } + self.bits.value(rotation) } /// Return the constant that represents a given value. To be compared with the value expression. @@ -140,10 +190,10 @@ where /// Configure constraints for the binary number chip. pub fn configure( meta: &mut ConstraintSystem, + bits: BinaryNumberBits, selector: Column, value: Option>, ) -> BinaryNumberConfig { - let bits = [0; N].map(|_| meta.advice_column()); bits.map(|bit| { meta.create_gate("bit column is 0 or 1", |meta| { let selector = meta.query_fixed(selector, Rotation::cur()); @@ -194,15 +244,7 @@ where offset: usize, value: &T, ) -> Result<(), Error> { - for (&bit, &column) in value.as_bits().iter().zip(&self.config.bits) { - region.assign_advice( - || format!("binary number {:?}", column), - column, - offset, - || Value::known(F::from(bit as u64)), - )?; - } - Ok(()) + self.config.bits.assign(region, offset, value) } } diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 068341dcdf..bd8e732c20 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -13,7 +13,7 @@ serde_json = { version = "1.0.66", features = ["unbounded_depth"] } serde = { version = "1.0.130", features = ["derive"] } bus-mapping = { path = "../bus-mapping", features = ["test"] } eth-types = { path = "../eth-types" } -zkevm-circuits = { path = "../zkevm-circuits", features = ["test-circuits"] } +zkevm-circuits = { path = "../zkevm-circuits", features = ["test-circuits", "mock-challenge"] } tokio = { version = "1.13", features = ["macros", "rt-multi-thread"] } url = "2.2.2" pretty_assertions = "1.0.0" diff --git a/zkevm-circuits/Cargo.toml b/zkevm-circuits/Cargo.toml index 3a64d19a9f..b48e98e538 100644 --- a/zkevm-circuits/Cargo.toml +++ b/zkevm-circuits/Cargo.toml @@ -63,7 +63,8 @@ test-circuits = [] # Test utilities for testool crate to consume test-util = ["dep:mock"] warn-unimplemented = ["eth-types/warn-unimplemented"] -stats = ["warn-unimplemented", "dep:cli-table"] +stats = ["warn-unimplemented", "dep:cli-table", "test-util", "test-circuits", "mock-challenge"] +mock-challenge = [] [[bin]] name = "stats" diff --git a/zkevm-circuits/src/bin/stats/halo2_stats.rs b/zkevm-circuits/src/bin/stats/halo2_stats.rs new file mode 100644 index 0000000000..fe17d11e3f --- /dev/null +++ b/zkevm-circuits/src/bin/stats/halo2_stats.rs @@ -0,0 +1,387 @@ +//! Utility functions and types to get circuit stats from any halo2 circuit + +use eth_types::Field; +use halo2_proofs::plonk::ConstraintSystem; +use std::collections::BTreeSet; + +// From Scroll https://github.com/scroll-tech/zkevm-circuits/blob/7d9bc181953cfc6e7baf82ff0ce651281fd70a8a/zkevm-circuits/src/util.rs#L275 +#[allow(dead_code)] +#[derive(Debug, Default)] +pub(crate) struct CircuitStats { + pub num_constraints: usize, + pub num_fixed_columns: usize, + pub num_lookups: usize, + pub num_shuffles: usize, + pub num_advice_columns: usize, + pub num_instance_columns: usize, + pub num_selectors: usize, + pub num_permutation_columns: usize, + pub degree: usize, + pub blinding_factors: usize, + pub num_challenges: usize, + pub max_phase: u8, + pub num_rotation: usize, + pub min_rotation: i32, + pub max_rotation: i32, + pub verification_msm_size: usize, + // Aux data to diff between records + num_advice_queries: usize, + num_gates: usize, +} + +impl CircuitStats { + // Peak memory analysis by Han: + // + // fn create_proof(params: &KZGParams, pk: &ProvingKey, circuit: &Circuit, instances: &[&[F]]) { + // Let: + // + // - k: log 2 of number of rows + // - n: `1 << k` + // - d: Degree of circuit + // - e: Extension magnitude, equal to `(d - 1).next_power_of_two()` + // - c_f: number of fixed columns + // - c_a: number of advice columns + // - c_i: number of instance columns + // - c_p: number of columns enabled with copy constraint + // - c_pg: number of grand product in permutation argument, equal to `div_ceil(c_p, d - 2)` + // - c_l: number of lookup argument + // + // The memory usage M.C and M.S stands for: + // + // - M.C: number of "elliptic curve points" (with symbol â—¯) + // - M.S: number of "field elements" (with symbol â–³) + // - M.E: number of "field elements" that will be extended by "* e" (with symbol ⬡) + // + // So the actual memory usage in terms of bytes will be: + // + // M = 32 * n * (2 * M.C + M.S + e * M.E) + // + // We'll ignore other values with sublinear amount to n. + // + // + // 0. In the beginning: + // + // `params` has: + // â—¯ powers_of_tau + // â—¯ ifft(powers_of_tau) + // + // M.C = 2 (+= 2) + // M.S = 0 + // M.E = 0 + // + // `pk` has: + // ⬡ l0 + // ⬡ l_last + // ⬡ l_active_row + // â–³ fixed_lagranges (c_f) + // â–³ fixed_monomials (c_f) + // ⬡ fixed_extended_lagranges (c_f) + // â–³ permutation_lagranges (c_p) + // â–³ permutation_monomials (c_p) + // ⬡ permutation_extended_lagranges (c_p) + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p (+= 2 * c_f + 2 * c_p) + // M.E = 3 + c_f + c_p (+= 3 + c_f + c_p) + // + // And let's ignore `circuit` + // + // + // ### 1. Pad instances as lagrange form and compute its monomial form. + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i (+= 2 * c_i) + // M.E = 3 + c_f + c_p + // ``` + // let instance_lagranges = instances.to_lagranges(); + // let instance_monomials = instance_lagranges.to_monomials(); + // ``` + // + // + // ### 2. Synthesize circuit and collect advice column values. + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i + c_a (+= c_a) + // M.E = 3 + c_f + c_p + // ``` + // let advice_lagranges = circuit.synthesize_all_phases(); + // ``` + // + // + // ### 3. Generate permuted input and table of lookup argument. + // For each lookup argument, we have: + // + // â–³ compressed_input_lagranges - cached for later computation + // â–³ permuted_input_lagranges + // â–³ permuted_input_monomials + // â–³ compressed_table_lagranges - cached for later computation + // â–³ permuted_table_lagranges + // â–³ permuted_table_monomials + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i + c_a + 6 * c_l (+= 6 * c_l) + // M.E = 3 + c_f + c_p + // ``` + // let ( + // compressed_input_lagranges, + // permuted_input_lagranges, + // permuted_input_monomials, + // compressed_table_lagranges, + // permuted_table_lagranges, + // permuted_table_monomials, + // ) = lookup_permuted() + // ``` + // + // + // ### 4. Generate grand products of permutation argument. + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i + c_a + 6 * c_l + c_pg (+= c_pg) + // M.E = 3 + c_f + c_p + c_pg (+= c_pg) + // ``` + // let ( + // perm_grand_product_monomials, + // perm_grand_product_extended_lagranges, + // ) = permutation_grand_products(); + // ``` + // + // + // ### 5. Generate grand products of lookup argument. + // And then drops unnecessary lagranges values. + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg (-= 3 * c_l) + // M.E = 3 + c_f + c_p + c_pg + // > let lookup_product_monomials = lookup_grand_products(); + // > drop(compressed_input_lagranges); + // > drop(permuted_input_lagranges); + // > drop(compressed_table_lagranges); + // > drop(permuted_table_lagranges); + // + // + // ### 6. Generate random polynomial. + // + // M.C = 2 + // M.S = 1 + 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg (+= 1) + // M.E = 3 + c_f + c_p + c_pg + // ``` + // let random_monomial = random(); + // ``` + // + // + // ### 7. Turn advice_lagranges into advice_monomials. + // ``` + // let advice_monomials = advice_lagranges.to_monomials(); + // drop(advice_lagranges); + // ``` + // + // + // ### 8. Generate necessary extended lagranges. + // + // M.C = 2 + // M.S = 1 + 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg + // M.E = 3 + c_f + c_p + c_pg + c_i + c_a (+= c_i + c_a) + // ``` + // let instances_extended_lagrnages = instances_monomials.to_extended_lagranges(); + // let advice_extended_lagrnages = advice_monomials.to_extended_lagranges(); + // ``` + // + // + // ### 9. While computing the quotient, these extended lagranges: + // + // ⬡ permuted_input_extended_lagranges + // ⬡ permuted_table_extended_lagranges + // ⬡ lookup_product_extended_lagranges + // + // of each lookup argument are generated on the fly and drop before next. + // + // And 1 extra quotient_extended_lagrange is created. So the peak memory: + // + // M.C = 2 + // M.S = 1 + 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg + // M.E = 4 + c_f + c_p + c_pg + c_i + c_a + 3 * (c_l > 0) (+= 3 * (c_l > 0) + 1) + // ``` + // let quotient_extended_lagrange = quotient_extended_lagrange(); + // ``` + // + // + // ### 10. After quotient is comuputed, drop all the other extended lagranges. + // + // M.C = 2 + // M.S = 1 + 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg + // M.E = 4 + c_f + c_p (-= c_pg + c_i + c_a + 3 * (c_l > 0)) + // drop(instances_extended_lagrnages) + // drop(advice_extended_lagrnages) + // drop(perm_grand_product_extended_lagranges) + // + // + // ### 11. Turn quotient_extended_lagrange into monomial form. + // And then cut int `d - 1` pieces. + // + // M.C = 2 + // M.S = 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg + d (+= d - 1) + // M.E = 3 + c_f + c_p (-= 1) + // ``` + // let quotient_monomials = quotient_monomials() + // drop(quotient_extended_lagrange) + // ``` + // + // + // ### 12. Evaluate and open all polynomial except instance ones. + // } + pub(crate) fn estimate_peak_mem(&self, k: u32) -> usize { + let field_bytes = 32; + let c_f = self.num_fixed_columns; + let c_a = self.num_advice_columns; + let c_i = self.num_instance_columns; + let c_p = self.num_permutation_columns; + let c_l = self.num_lookups; + let c_pg = c_p.div_ceil(self.degree - 2); + let e = (self.degree - 1).next_power_of_two(); + // The estimated peak memory formula comes from step 9 of the analysis, which is the step + // of proving that needs the most memory (after that step, allocations start getting freed) + + // number of "elliptic curve points" + let m_c = 2; + // number of "field elements" + let m_s = 1 + 2 * c_f + 2 * c_p + 2 * c_i + c_a + 3 * c_l + c_pg; + // number of "field elements" that will be extended by "* e" + let m_e = 4 + c_f + c_p + c_pg + c_i + c_a + 3 * (c_l > 0) as usize; + let unit = 2 * m_c + m_s + e * m_e; + unit * 2usize.pow(k) * field_bytes + } +} + +// Return the stats in `meta`, accounting only for the circuit delta from the last aggregated stats +// in `agg`. +// Adapted from Scroll https://github.com/scroll-tech/zkevm-circuits/blob/7d9bc181953cfc6e7baf82ff0ce651281fd70a8a/zkevm-circuits/src/util.rs#L294 +pub(crate) fn circuit_stats( + agg: &CircuitStats, + meta: &ConstraintSystem, +) -> CircuitStats { + let max_phase = meta + .advice_column_phase() + .iter() + .skip(agg.num_advice_columns) + .max() + .copied() + .unwrap_or_default(); + + let rotations = meta + .advice_queries() + .iter() + .skip(agg.num_advice_queries) + .map(|(_, q)| q.0) + .collect::>(); + + let degree = meta.degree(); + let num_fixed_columns = meta.num_fixed_columns() - agg.num_fixed_columns; + let num_lookups = meta.lookups().len() - agg.num_lookups; + let num_shuffles = meta.shuffles().len() - agg.num_shuffles; + let num_advice_columns = meta.num_advice_columns() - agg.num_advice_columns; + let num_instance_columns = meta.num_instance_columns() - agg.num_instance_columns; + let num_selectors = meta.num_selectors() - agg.num_selectors; + let num_permutation_columns = + meta.permutation().get_columns().len() - agg.num_permutation_columns; + + // This calculation has some differences with the Scroll implementation at + // https://github.com/scroll-tech/zkevm-circuits/blob/7d9bc181953cfc6e7baf82ff0ce651281fd70a8a/zkevm-circuits/src/util.rs#L320-L326 + // - Remove `num_instance_columns` because it doesn't contribute when using the KZG commitment + // scheme + // - Assume SHPLONK for batch opening scheme (replaces `rotations.len()` by `1`) + // - Add `degree -1` to account for quotients + // - Add `div_ceil(num_permutation_columns, degree - 2)` for permutation arguments grand + // products. + let verification_msm_size = num_advice_columns + + num_permutation_columns // Preprocessed permutation column + + num_permutation_columns.div_ceil(degree-2) // Grand product for permutations + + num_shuffles // Grand product of each shuffle + + num_selectors // Assume no selector compression (giving us an upper bound estimation) + + num_fixed_columns + // Grand product, permuted input expression and permuted table expression for each lookup + + 3 * num_lookups + + 2 // SHPLONK batch opening scheme + + (degree -1); // quotients + + CircuitStats { + num_constraints: meta + .gates() + .iter() + .skip(agg.num_gates) + .map(|g| g.polynomials().len()) + .sum::(), + num_fixed_columns, + num_lookups, + num_shuffles, + num_advice_columns, + num_instance_columns, + num_selectors, + num_permutation_columns, + degree, + blinding_factors: meta.blinding_factors(), + num_challenges: meta.num_challenges() - agg.num_challenges, + max_phase, + num_rotation: rotations.len(), + min_rotation: rotations.first().cloned().unwrap_or_default(), + max_rotation: rotations.last().cloned().unwrap_or_default(), + verification_msm_size, + num_advice_queries: meta.advice_queries().len() - agg.num_advice_queries, + num_gates: meta.gates().len() - agg.num_gates, + } +} + +pub(crate) struct StatsCollection { + aggregate: bool, + shared_cs: ConstraintSystem, + pub(crate) agg: CircuitStats, + pub(crate) list: Vec<(String, CircuitStats)>, +} + +impl StatsCollection { + // With aggregate=true, all records are overwritten each time, leading to a single + // aggregate stats that represents the final circuit. + // With aggregate=false, each record is stored in a different entry with a name, and the + // ConstraintSystem is reset so that each entry is independent. + pub(crate) fn new(aggregate: bool) -> Self { + Self { + aggregate, + shared_cs: ConstraintSystem::default(), + agg: CircuitStats::default(), + list: Vec::new(), + } + } + + // Record a shared table + pub(crate) fn record_shared(&mut self, name: &str, meta: &mut ConstraintSystem) { + // Shared tables should only add columns, and nothing more + assert_eq!(meta.lookups().len(), 0); + assert_eq!(meta.shuffles().len(), 0); + assert_eq!(meta.permutation().get_columns().len(), 0); + assert_eq!(meta.degree(), 3); // 3 comes from the permutation argument + assert_eq!(meta.blinding_factors(), 5); // 5 is the minimum blinding factor + assert_eq!(meta.advice_queries().len(), 0); + assert_eq!(meta.gates().len(), 0); + + if self.aggregate { + self.agg = circuit_stats(&CircuitStats::default(), meta); + } else { + let stats = circuit_stats(&self.agg, meta); + self.agg = circuit_stats(&CircuitStats::default(), meta); + self.list.push((name.to_string(), stats)); + // Keep the ConstraintSystem with all the tables + self.shared_cs = meta.clone(); + } + } + + // Record a subcircuit + pub(crate) fn record(&mut self, name: &str, meta: &mut ConstraintSystem) { + if self.aggregate { + self.agg = circuit_stats(&CircuitStats::default(), meta); + } else { + let stats = circuit_stats(&self.agg, meta); + self.list.push((name.to_string(), stats)); + // Revert meta to the ConstraintSystem just with the tables + *meta = self.shared_cs.clone(); + } + } +} diff --git a/zkevm-circuits/src/bin/stats/main.rs b/zkevm-circuits/src/bin/stats/main.rs index 73514d35cc..dbf9b4b9a3 100644 --- a/zkevm-circuits/src/bin/stats/main.rs +++ b/zkevm-circuits/src/bin/stats/main.rs @@ -1,23 +1,43 @@ +//! Build with `--features="stats"` + +mod halo2_stats; +mod helpers; + use bus_mapping::circuit_input_builder::FeatureConfig; 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}, + plonk::{Circuit, ConstraintSystem, Expression}, }; -mod helpers; +use halo2_stats::StatsCollection; 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_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, N_U16_LOOKUPS, - N_U8_LOOKUPS, +use std::{array, env, iter}; +use zkevm_circuits::{ + bytecode_circuit::{BytecodeCircuitConfig, BytecodeCircuitConfigArgs}, + copy_circuit::{CopyCircuitConfig, CopyCircuitConfigArgs}, + evm_circuit::{ + param::{ + LOOKUP_CONFIG, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_PHASE2_COLUMNS, N_U16_LOOKUPS, + N_U8_LOOKUPS, + }, + step::ExecutionState, + EvmCircuit, EvmCircuitConfig, EvmCircuitConfigArgs, + }, + exp_circuit::ExpCircuitConfig, + keccak_circuit::{KeccakCircuitConfig, KeccakCircuitConfigArgs}, + pi_circuit::{PiCircuitConfig, PiCircuitConfigArgs}, + state_circuit::{StateCircuitConfig, StateCircuitConfigArgs}, + table::{ + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, SigTable, + TxTable, UXTable, WdTable, }, - step::ExecutionState, - EvmCircuit, + tx_circuit::{TxCircuitConfig, TxCircuitConfigArgs}, + util::{Challenges, SubCircuitConfig}, }; + fn main() { let args: Vec = env::args().collect(); @@ -26,6 +46,7 @@ fn main() { "state" => state_states_stats(), "copy" => copy_states_stats(), "exec" => get_exec_steps_occupancy(), + "general" => general_subcircuit_stats(), &_ => unreachable!("Unsupported arg"), } } @@ -204,3 +225,196 @@ fn get_exec_steps_occupancy() { LOOKUP_CONFIG[7].1 ); } + +#[allow(unused_variables)] +fn record_stats( + stats: &mut StatsCollection, + meta: &mut ConstraintSystem, +) { + let max_txs = 1; + let max_withdrawals = 5; + let max_calldata = 32; + let mock_randomness = F::from(0x100); + let feature_config = FeatureConfig::default(); + + // Shared Tables + let tx_table = TxTable::construct(meta); + stats.record_shared("tx_table", meta); + let wd_table = WdTable::construct(meta); + stats.record_shared("wd_table", meta); + let rw_table = RwTable::construct(meta); + stats.record_shared("rw_table", meta); + let mpt_table = MptTable::construct(meta); + stats.record_shared("mpt_table", meta); + let bytecode_table = BytecodeTable::construct(meta); + stats.record_shared("bytecode_table", meta); + let block_table = BlockTable::construct(meta); + stats.record_shared("block_table", meta); + let q_copy_table = meta.fixed_column(); + let copy_table = CopyTable::construct(meta, q_copy_table); + stats.record_shared("copy_table", meta); + let exp_table = ExpTable::construct(meta); + stats.record_shared("exp_table", meta); + let keccak_table = KeccakTable::construct(meta); + stats.record_shared("keccak_table", meta); + let sig_table = SigTable::construct(meta); + stats.record_shared("sig_table", meta); + let u8_table = UXTable::construct(meta); + stats.record_shared("u8_table", meta); + let u10_table = UXTable::construct(meta); + stats.record_shared("u10_table", meta); + let u16_table = UXTable::construct(meta); + stats.record_shared("u16_table", meta); + + // Use a mock randomness instead of the randomness derived from the challenge + // (either from mock or real prover) to help debugging assignments. + 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(), + power_of_randomness[0].clone(), + ); + + let keccak_circuit = KeccakCircuitConfig::new( + meta, + KeccakCircuitConfigArgs { + keccak_table: keccak_table.clone(), + challenges: challenges.clone(), + }, + ); + stats.record("keccak", meta); + + let pi_circuit = PiCircuitConfig::new( + meta, + PiCircuitConfigArgs { + max_txs, + max_withdrawals, + max_calldata, + block_table: block_table.clone(), + tx_table: tx_table.clone(), + wd_table, + keccak_table: keccak_table.clone(), + challenges: challenges.clone(), + }, + ); + stats.record("pi", meta); + let tx_circuit = TxCircuitConfig::new( + meta, + TxCircuitConfigArgs { + tx_table: tx_table.clone(), + keccak_table: keccak_table.clone(), + challenges: challenges.clone(), + }, + ); + stats.record("tx", meta); + let bytecode_circuit = BytecodeCircuitConfig::new( + meta, + BytecodeCircuitConfigArgs { + bytecode_table: bytecode_table.clone(), + keccak_table: keccak_table.clone(), + challenges: challenges.clone(), + }, + ); + stats.record("bytecode", meta); + let copy_circuit = CopyCircuitConfig::new( + meta, + CopyCircuitConfigArgs { + tx_table: tx_table.clone(), + rw_table, + bytecode_table: bytecode_table.clone(), + copy_table, + q_enable: q_copy_table, + challenges: challenges.clone(), + }, + ); + stats.record("copy", meta); + let state_circuit = StateCircuitConfig::new( + meta, + StateCircuitConfigArgs { + rw_table, + mpt_table, + u8_table, + u10_table, + u16_table, + challenges: challenges.clone(), + }, + ); + stats.record("state", meta); + let exp_circuit = ExpCircuitConfig::new(meta, exp_table); + stats.record("exp", meta); + let evm_circuit = EvmCircuitConfig::new( + meta, + EvmCircuitConfigArgs { + challenges, + tx_table, + rw_table, + bytecode_table, + block_table, + copy_table, + keccak_table, + exp_table, + u8_table, + u16_table, + sig_table, + feature_config, + }, + ); + stats.record("evm", meta); +} + +fn general_subcircuit_stats() { + let mut stats_list = StatsCollection::::new(false); + let mut meta = ConstraintSystem::::default(); + record_stats(&mut stats_list, &mut meta); + let mut stats_agg = StatsCollection::::new(true); + let mut meta = ConstraintSystem::::default(); + record_stats(&mut stats_agg, &mut meta); + + let mut table = Vec::new(); + for (name, stats) in stats_list + .list + .iter() + .chain(iter::once(&("super".to_string(), stats_agg.agg))) + { + // At 0.0139 gas/row this gives us 2^26 * 0.0139 = ~900k gas. For 30M gas we would need 33 + // chunks. + let k = 26; + let peak_mem_gb = stats.estimate_peak_mem(k) / 1024 / 1024 / 1024; + table.push(vec![ + name.cell(), + stats.num_constraints.cell(), + stats.num_rotation.cell(), + format!("{}/{}", stats.min_rotation, stats.max_rotation).cell(), + stats.num_fixed_columns.cell(), + stats.num_selectors.cell(), + stats.num_advice_columns.cell(), + stats.num_permutation_columns.cell(), + stats.num_lookups.cell(), + stats.degree.cell(), + peak_mem_gb.cell(), + ]); + } + let table = table + .table() + .title( + [ + "circuit", + "constraints", + "rots", + "min/max(rots)", + "fix_cols", + "selectors", + "adv_cols", + "perm_cols", + "lookups", + "degree", + "mem_gb", + ] + .iter() + .map(|s| s.cell().bold(true)), + ) + .bold(true); + + assert!(print_stdout(table).is_ok()); +} diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 489dfbcca2..dff1f99a42 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -69,6 +69,8 @@ pub struct CopyCircuitConfig { /// The Copy Table contains the columns that are exposed via the lookup /// expressions pub copy_table: CopyTable, + /// BinaryNumber config out of the tag bits from `copy_table` + copy_table_tag: BinaryNumberConfig, /// Lt chip to check: src_addr < src_addr_end. /// Since `src_addr` and `src_addr_end` are u64, 8 bytes are sufficient for /// the Lt chip. @@ -128,7 +130,7 @@ impl SubCircuitConfig for CopyCircuitConfig { let rlc_acc = copy_table.rlc_acc; let rw_counter = copy_table.rw_counter; let rwc_inc_left = copy_table.rwc_inc_left; - let tag = copy_table.tag; + let tag_bits = copy_table.tag; // annotate table columns tx_table.annotate_columns(meta); @@ -136,6 +138,7 @@ impl SubCircuitConfig for CopyCircuitConfig { bytecode_table.annotate_columns(meta); copy_table.annotate_columns(meta); + let tag = BinaryNumberChip::configure(meta, tag_bits, q_enable, None); let addr_lt_addr_end = LtChip::configure( meta, |meta| meta.query_selector(q_step), @@ -471,6 +474,7 @@ impl SubCircuitConfig for CopyCircuitConfig { q_enable, addr_lt_addr_end, copy_table, + copy_table_tag: tag, tx_table, rw_table, bytecode_table, @@ -526,7 +530,11 @@ impl CopyCircuitConfig { .zip_eq(table_row) { // Leave sr_addr_end and bytes_left unassigned when !is_read - if !is_read && (label == "src_addr_end" || label == "bytes_left") { + // Leave tag_bit columns unassigned, since they are already assigned via + // `tag_chip.assign` + if (!is_read && (label == "src_addr_end" || label == "bytes_left")) + || label == "tag_bit" + { } else { region.assign_advice( || format!("{} at row: {}", label, offset), @@ -605,7 +613,7 @@ impl CopyCircuitConfig { ); let filler_rows = max_copy_rows - copy_rows_needed - DISABLED_ROWS; - let tag_chip = BinaryNumberChip::construct(self.copy_table.tag); + let tag_chip = BinaryNumberChip::construct(self.copy_table_tag); let lt_chip = LtChip::construct(self.addr_lt_addr_end); lt_chip.load(layouter)?; diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 81825492f4..141eab0e51 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -26,7 +26,7 @@ use constraint_builder::{ConstraintBuilder, Queries}; use eth_types::{Address, Field, Word}; use gadgets::{ batched_is_zero::{BatchedIsZeroChip, BatchedIsZeroConfig}, - binary_number::{BinaryNumberChip, BinaryNumberConfig}, + binary_number::{BinaryNumberBits, BinaryNumberChip, BinaryNumberConfig}, }; use halo2_proofs::{ circuit::{Layouter, Region, Value}, @@ -107,7 +107,8 @@ impl SubCircuitConfig for StateCircuitConfig { let lookups = LookupsChip::configure(meta, u8_table, u10_table, u16_table); let rw_counter = MpiChip::configure(meta, selector, [rw_table.rw_counter], lookups); - let tag = BinaryNumberChip::configure(meta, selector, Some(rw_table.tag)); + let bits = BinaryNumberBits::construct(meta); + let tag = BinaryNumberChip::configure(meta, bits, selector, Some(rw_table.tag)); let id = MpiChip::configure(meta, selector, [rw_table.id], lookups); let address = MpiChip::configure(meta, selector, [rw_table.address], lookups); diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index 4d90c23dae..b8a5f4de45 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -1,7 +1,7 @@ use super::{lookups, param::*, SortKeysConfig}; use crate::{evm_circuit::param::N_BYTES_WORD, impl_expr, util::Expr, witness::Rw}; use eth_types::{Field, ToBigEndian}; -use gadgets::binary_number::{AsBits, BinaryNumberChip, BinaryNumberConfig}; +use gadgets::binary_number::{AsBits, BinaryNumberBits, BinaryNumberChip, BinaryNumberConfig}; use halo2_proofs::{ circuit::{Region, Value}, plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, VirtualCells}, @@ -108,7 +108,8 @@ impl Config { powers_of_randomness: [Expression; N_BYTES_WORD - 1], ) -> Self { let selector = meta.fixed_column(); - let first_different_limb = BinaryNumberChip::configure(meta, selector, None); + let bits = BinaryNumberBits::construct(meta); + let first_different_limb = BinaryNumberChip::configure(meta, bits, selector, None); let limb_difference = meta.advice_column(); let limb_difference_inverse = meta.advice_column(); diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 786493e399..53b7a90c30 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -97,6 +97,8 @@ pub struct SuperCircuitConfig { keccak_circuit: KeccakCircuitConfig, pi_circuit: PiCircuitConfig, exp_circuit: ExpCircuitConfig, + #[cfg(not(feature = "mock-challenge"))] + challenges: Challenges, } impl SubCircuitConfig for SuperCircuitConfig { @@ -128,21 +130,31 @@ impl SubCircuitConfig for SuperCircuitConfig { let u16_table = UXTable::construct(meta); // Use a mock randomness instead of the randomness derived from the challenge - // (either from mock or real prover) to help debugging assignments. + // (either from mock or real prover) to help debugging assignments, when "mock-challenge" + // feature is enabled. + #[allow(unused_variables)] 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( + // Use the real challenges for real use, when "mock-challenge" feature is disabled. + #[cfg(not(feature = "mock-challenge"))] + let challenges = Challenges::construct(meta); + + #[cfg(feature = "mock-challenge")] + let challenges_exprs = Challenges::mock( power_of_randomness[0].clone(), power_of_randomness[0].clone(), ); + #[cfg(not(feature = "mock-challenge"))] + let challenges_exprs = challenges.exprs(meta); + let sig_table = SigTable::construct(meta); let keccak_circuit = KeccakCircuitConfig::new( meta, KeccakCircuitConfigArgs { keccak_table: keccak_table.clone(), - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); @@ -156,7 +168,7 @@ impl SubCircuitConfig for SuperCircuitConfig { tx_table: tx_table.clone(), wd_table, keccak_table: keccak_table.clone(), - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); let tx_circuit = TxCircuitConfig::new( @@ -164,7 +176,7 @@ impl SubCircuitConfig for SuperCircuitConfig { TxCircuitConfigArgs { tx_table: tx_table.clone(), keccak_table: keccak_table.clone(), - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); let bytecode_circuit = BytecodeCircuitConfig::new( @@ -172,7 +184,7 @@ impl SubCircuitConfig for SuperCircuitConfig { BytecodeCircuitConfigArgs { bytecode_table: bytecode_table.clone(), keccak_table: keccak_table.clone(), - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); let copy_circuit = CopyCircuitConfig::new( @@ -183,7 +195,7 @@ impl SubCircuitConfig for SuperCircuitConfig { bytecode_table: bytecode_table.clone(), copy_table, q_enable: q_copy_table, - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); let state_circuit = StateCircuitConfig::new( @@ -194,14 +206,14 @@ impl SubCircuitConfig for SuperCircuitConfig { u8_table, u10_table, u16_table, - challenges: challenges.clone(), + challenges: challenges_exprs.clone(), }, ); let exp_circuit = ExpCircuitConfig::new(meta, exp_table); let evm_circuit = EvmCircuitConfig::new( meta, EvmCircuitConfigArgs { - challenges, + challenges: challenges_exprs, tx_table, rw_table, bytecode_table, @@ -230,6 +242,8 @@ impl SubCircuitConfig for SuperCircuitConfig { keccak_circuit, pi_circuit, exp_circuit, + #[cfg(not(feature = "mock-challenge"))] + challenges, } } } @@ -421,10 +435,14 @@ impl Circuit for SuperCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { let block = self.evm_circuit.block.as_ref().unwrap(); + #[cfg(feature = "mock-challenge")] let challenges = Challenges::mock( Value::known(block.randomness), Value::known(block.randomness), ); + #[cfg(not(feature = "mock-challenge"))] + let challenges = config.challenges.values(&mut layouter); + let rws = &self.state_circuit.rows; config.block_table.load(&mut layouter, &block.context)?; diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index fb0be4aefd..93c1d0f1e1 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -11,7 +11,7 @@ use bus_mapping::circuit_input_builder::{CopyDataType, CopyEvent, CopyStep}; use core::iter::once; use eth_types::{Field, ToScalar, U256}; use gadgets::{ - binary_number::{BinaryNumberChip, BinaryNumberConfig}, + binary_number::BinaryNumberBits, util::{split_u256, split_u256_limb64}, }; use halo2_proofs::{ @@ -46,20 +46,18 @@ pub(crate) mod ux_table; /// withdrawal table pub(crate) mod wd_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 use block_table::{BlockContextFieldTag, BlockTable}; +pub use bytecode_table::{BytecodeFieldTag, BytecodeTable}; +pub use copy_table::CopyTable; +pub use exp_table::ExpTable; pub use keccak_table::KeccakTable; -pub(crate) use ux_table::UXTable; +pub use ux_table::UXTable; pub use mpt_table::{MPTProofType, MptTable}; -pub(crate) use rw_table::RwTable; -pub(crate) use sig_table::SigTable; -pub(crate) use tx_table::{ - TxContextFieldTag, TxFieldTag, TxLogFieldTag, TxReceiptFieldTag, TxTable, -}; -pub(crate) use wd_table::WdTable; +pub use rw_table::RwTable; +pub use sig_table::SigTable; +pub use tx_table::{TxContextFieldTag, TxFieldTag, TxLogFieldTag, TxReceiptFieldTag, TxTable}; +pub use wd_table::WdTable; /// Trait used to define lookup tables pub trait LookupTable { diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs index cbd9cbbdb2..eeeb1a2eb1 100644 --- a/zkevm-circuits/src/table/copy_table.rs +++ b/zkevm-circuits/src/table/copy_table.rs @@ -1,6 +1,8 @@ use super::*; +use gadgets::binary_number::AsBits; -type CopyTableRow = [(Value, &'static str); 9]; +// The row also includes the 3 column bits from the tag +type CopyTableRow = [(Value, &'static str); 12]; type CopyCircuitRow = [(Value, &'static str); 5]; /// Copy Table, used to verify copies of byte chunks between Memory, Bytecode, @@ -34,10 +36,12 @@ pub struct CopyTable { pub rwc_inc_left: Column, /// Selector for the tag BinaryNumberChip pub q_enable: Column, + // We use the `BinaryNumberBits` instead of `BinaryNumberChip` in order to construct the table + // without attaching any constraints. /// 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, + pub tag: BinaryNumberBits<3>, } impl CopyTable { @@ -47,7 +51,7 @@ impl CopyTable { is_first: meta.advice_column(), id: WordLoHi::new([meta.advice_column(), meta.advice_column()]), q_enable, - tag: BinaryNumberChip::configure(meta, q_enable, None), + tag: BinaryNumberBits::construct(meta), addr: meta.advice_column(), src_addr_end: meta.advice_column(), bytes_left: meta.advice_column(), @@ -160,36 +164,43 @@ impl CopyTable { // is_code let is_code = Value::known(copy_step.is_code.map_or(F::ZERO, |v| F::from(v as u64))); + let tag_bits: [_; 3] = tag + .as_bits() + .map(|b| (Value::known(F::from(b as u64)), "tag_bit")); + let copy_table_row = [ + (is_first, "is_first"), + (id.lo(), "id_lo"), + (id.hi(), "id_hi"), + (addr, "addr"), + ( + Value::known(F::from(copy_event.src_addr_end)), + "src_addr_end", + ), + (Value::known(F::from(bytes_left)), "bytes_left"), + ( + match (copy_event.src_type, copy_event.dst_type) { + (CopyDataType::Memory, CopyDataType::Bytecode) => rlc_acc, + (_, CopyDataType::RlcAcc) => rlc_acc, + (CopyDataType::RlcAcc, _) => rlc_acc, + _ => Value::known(F::ZERO), + }, + "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", + ), + tag_bits[0], + tag_bits[1], + tag_bits[2], + ]; assignments.push(( tag, - [ - (is_first, "is_first"), - (id.lo(), "id_lo"), - (id.hi(), "id_hi"), - (addr, "addr"), - ( - Value::known(F::from(copy_event.src_addr_end)), - "src_addr_end", - ), - (Value::known(F::from(bytes_left)), "bytes_left"), - ( - match (copy_event.src_type, copy_event.dst_type) { - (CopyDataType::Memory, CopyDataType::Bytecode) => rlc_acc, - (_, CopyDataType::RlcAcc) => rlc_acc, - (CopyDataType::RlcAcc, _) => rlc_acc, - _ => Value::known(F::ZERO), - }, - "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", - ), - ], + copy_table_row, [ (is_last, "is_last"), (value, "value"), @@ -223,10 +234,9 @@ impl CopyTable { } 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 (_, 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), @@ -235,7 +245,6 @@ impl CopyTable { || value, )?; } - tag_chip.assign(&mut region, offset, &tag)?; offset += 1; } } @@ -259,7 +268,7 @@ impl CopyTable { impl LookupTable for CopyTable { fn columns(&self) -> Vec> { - vec![ + let mut columns = vec![ self.is_first.into(), self.id.lo().into(), self.id.hi().into(), @@ -269,11 +278,13 @@ impl LookupTable for CopyTable { self.rlc_acc.into(), self.rw_counter.into(), self.rwc_inc_left.into(), - ] + ]; + columns.extend(self.tag.iter().map(|c| Into::>::into(*c))); + columns } fn annotations(&self) -> Vec { - vec![ + let mut names = vec![ String::from("is_first"), String::from("id_lo"), String::from("id_hi"), @@ -283,7 +294,9 @@ impl LookupTable for CopyTable { String::from("rlc_acc"), String::from("rw_counter"), String::from("rwc_inc_left"), - ] + ]; + names.extend((0..self.tag.len()).map(|i| format!("tag_bit{i}"))); + names } fn table_exprs(&self, meta: &mut VirtualCells) -> Vec> { diff --git a/zkevm-circuits/src/table/mpt_table.rs b/zkevm-circuits/src/table/mpt_table.rs index 2e24e0fef7..08ae579283 100644 --- a/zkevm-circuits/src/table/mpt_table.rs +++ b/zkevm-circuits/src/table/mpt_table.rs @@ -100,7 +100,7 @@ impl LookupTable for MptTable { impl MptTable { /// Construct a new MptTable - pub(crate) fn construct(meta: &mut ConstraintSystem) -> Self { + pub fn construct(meta: &mut ConstraintSystem) -> Self { Self { address: meta.advice_column(), storage_key: WordLoHi::new([meta.advice_column(), meta.advice_column()]), diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index e8da067cff..b0d8d0449f 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -92,7 +92,9 @@ impl Challenges { [&self.keccak_input, &self.lookup_input] } - pub(crate) fn mock(keccak_input: T, lookup_input: T) -> Self { + /// Returns a mock Challenges for testing purposes + #[cfg(feature = "mock-challenge")] + pub fn mock(keccak_input: T, lookup_input: T) -> Self { Self { keccak_input, lookup_input, From 4bdc733a7c19b76823c4ca28e835e0a922110e39 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Mon, 26 Feb 2024 10:58:22 +0200 Subject: [PATCH 08/16] Fix typos (#1780) --- bin/mpt-test/src/circuit/state_update.rs | 6 +++--- bus-mapping/README.md | 2 +- bus-mapping/src/circuit_input_builder.rs | 6 +++--- bus-mapping/src/circuit_input_builder/block.rs | 2 +- bus-mapping/src/circuit_input_builder/call.rs | 2 +- bus-mapping/src/circuit_input_builder/execution.rs | 2 +- .../src/circuit_input_builder/tracer_tests.rs | 2 +- bus-mapping/src/error.rs | 2 +- bus-mapping/src/evm/opcodes/callop.rs | 2 +- .../src/evm/opcodes/error_write_protection.rs | 2 +- bus-mapping/src/lib.rs | 4 ++-- bus-mapping/src/mock.rs | 2 +- bus-mapping/src/operation.rs | 4 ++-- bus-mapping/src/operation/container.rs | 2 +- eth-types/src/geth_types.rs | 4 ++-- eth-types/src/keccak.rs | 2 +- eth-types/src/sign_types.rs | 2 +- gadgets/src/is_zero.rs | 2 +- geth-utils/gethutil/mpt/state/statedb.go | 4 ++-- geth-utils/gethutil/mpt/trie/hasher.go | 4 ++-- geth-utils/gethutil/mpt/types/block.go | 2 +- geth-utils/gethutil/trace.go | 2 +- geth-utils/src/mpt.rs | 2 +- integration-tests/build.rs | 2 +- integration-tests/src/bin/gen_blockchain_data.rs | 2 +- integration-tests/src/integration_test_circuits.rs | 4 ++-- integration-tests/tests/circuit_input_builder.rs | 2 +- mock/src/test_ctx.rs | 4 ++-- testool/README.md | 2 +- testool/src/abi.rs | 2 +- testool/src/main.rs | 2 +- zkevm-circuits/src/bin/stats/helpers.rs | 2 +- zkevm-circuits/src/bytecode_circuit/test.rs | 2 +- .../src/circuit_tools/constraint_builder.rs | 12 ++++++------ zkevm-circuits/src/copy_circuit.rs | 2 +- zkevm-circuits/src/ecc_circuit.rs | 6 +++--- zkevm-circuits/src/evm_circuit/execution/addmod.rs | 4 ++-- .../src/evm_circuit/execution/calldatacopy.rs | 2 +- .../src/evm_circuit/execution/calldataload.rs | 2 +- zkevm-circuits/src/evm_circuit/execution/callop.rs | 2 +- zkevm-circuits/src/evm_circuit/execution/codecopy.rs | 4 ++-- zkevm-circuits/src/evm_circuit/execution/end_tx.rs | 2 +- .../src/evm_circuit/execution/extcodecopy.rs | 2 +- zkevm-circuits/src/evm_circuit/execution/logs.rs | 4 ++-- zkevm-circuits/src/evm_circuit/execution/origin.rs | 2 +- .../src/evm_circuit/execution/returndatacopy.rs | 2 +- zkevm-circuits/src/evm_circuit/execution/sar.rs | 2 +- zkevm-circuits/src/evm_circuit/param.rs | 2 +- zkevm-circuits/src/evm_circuit/step.rs | 2 +- zkevm-circuits/src/evm_circuit/table.rs | 4 ++-- zkevm-circuits/src/evm_circuit/util/common_gadget.rs | 2 +- .../src/evm_circuit/util/constraint_builder.rs | 2 +- .../src/evm_circuit/util/instrumentation.rs | 2 +- zkevm-circuits/src/evm_circuit/util/memory_gadget.rs | 2 +- zkevm-circuits/src/keccak_circuit.rs | 4 ++-- .../src/keccak_circuit/keccak_packed_multi.rs | 2 +- zkevm-circuits/src/mpt_circuit.rs | 2 +- zkevm-circuits/src/mpt_circuit/extension.rs | 2 +- zkevm-circuits/src/root_circuit/aggregation.rs | 2 +- zkevm-circuits/src/super_circuit.rs | 2 +- zkevm-circuits/src/table/bytecode_table.rs | 2 +- zkevm-circuits/src/table/copy_table.rs | 2 +- zkevm-circuits/src/table/keccak_table.rs | 2 +- zkevm-circuits/src/test_util.rs | 6 +++--- zkevm-circuits/src/tx_circuit/sign_verify.rs | 4 ++-- zkevm-circuits/src/util/cell_manager.rs | 2 +- zkevm-circuits/src/util/word.rs | 2 +- zkevm-circuits/src/witness/block.rs | 2 +- zkevm-circuits/src/witness/rw.rs | 2 +- 69 files changed, 95 insertions(+), 95 deletions(-) diff --git a/bin/mpt-test/src/circuit/state_update.rs b/bin/mpt-test/src/circuit/state_update.rs index 8060959e34..a9271e6339 100644 --- a/bin/mpt-test/src/circuit/state_update.rs +++ b/bin/mpt-test/src/circuit/state_update.rs @@ -57,13 +57,13 @@ pub struct StateUpdateCircuitConfig { /// The MPT table, where the state updates are stored pub pi_mpt: MptTable, - /// Intance column, used to check public inputs + /// Instance column, used to check public inputs pub instance: Column, /// ONE if the first row, ZERO otherwise pub is_first: Column, - /// ONE if row is paddding, ZERO otherwise + /// ONE if row is padding, ZERO otherwise pub is_padding: IsZeroConfig, /// ONE is the last used row, ZERO otherwise @@ -465,7 +465,7 @@ impl Circuit for StateUpdateCircuit { ).unwrap() ); - // at beggining, set the old root and number of proofs + // at beginning, set the old root and number of proofs if offset == 0 { pi.push(Some(old_root_lo)); diff --git a/bus-mapping/README.md b/bus-mapping/README.md index e0cbea24da..de664a1bc7 100644 --- a/bus-mapping/README.md +++ b/bus-mapping/README.md @@ -158,7 +158,7 @@ On that way, we would get something like this for the Memory ops: | `key` | `val` | `rw` | `gc` | Note | |:------:| ------------- | ------- | ---- | ---------------------------------------- | | `0x40` | `0` | `Write` | | Init | -| `0x40` | `0x80` | `Write` | 0 | Assume written at the begining of `code` | +| `0x40` | `0x80` | `Write` | 0 | Assume written at the beginning of `code`| | `0x40` | `0x80` | `Read` | 4 | `56 MLOAD` | | - | | | | | | `0x80` | `0` | `Write` | | Init | diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index b8ea23f419..bfa77d1811 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -84,7 +84,7 @@ impl FeatureConfig { #[derive(Debug, Clone, Copy)] pub struct FixedCParams { /// Maximum number of rw operations in the state circuit (RwTable length / - /// nummber of rows). This must be at least the number of rw operations + /// number of rows). This must be at least the number of rw operations /// + 1, in order to allocate at least a Start row. pub max_rws: usize, // TODO: evm_rows: Maximum number of rows in the EVM Circuit @@ -190,7 +190,7 @@ pub struct CircuitInputBuilder { pub code_db: CodeDB, /// Block pub block: Block, - /// Circuits Setup Paramteres + /// Circuits Setup Parameters pub circuits_params: C, /// Block Context pub block_ctx: BlockContext, @@ -544,7 +544,7 @@ pub fn keccak_inputs(block: &Block, code_db: &CodeDB) -> Result>, Er } /// Generate the keccak inputs required by the SignVerify Chip from the -/// signature datas. +/// signature data. pub fn keccak_inputs_sign_verify(sigs: &[SignData]) -> Vec> { let mut inputs = Vec::new(); for sig in sigs { diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index 655eb8c073..fa0abc6f41 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -60,7 +60,7 @@ pub struct Block { /// chain id pub chain_id: Word, /// history hashes contains most recent 256 block hashes in history, where - /// the lastest one is at history_hashes[history_hashes.len() - 1]. + /// the latest one is at history_hashes[history_hashes.len() - 1]. pub history_hashes: Vec, /// coinbase pub coinbase: Address, diff --git a/bus-mapping/src/circuit_input_builder/call.rs b/bus-mapping/src/circuit_input_builder/call.rs index 564444d536..0f16847b36 100644 --- a/bus-mapping/src/circuit_input_builder/call.rs +++ b/bus-mapping/src/circuit_input_builder/call.rs @@ -63,7 +63,7 @@ pub struct Call { pub kind: CallKind, /// This call is being executed without write access (STATIC) pub is_static: bool, - /// This call generated implicity by a Transaction. + /// This call generated implicitly by a Transaction. pub is_root: bool, /// This call is persistent or call stack reverts at some point pub is_persistent: bool, diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index 045ba2e44b..f99cb3bac7 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -337,7 +337,7 @@ impl From<(Word, Word, Word)> for ExpStep { } } -/// Event representating an exponentiation `a ^ b == d (mod 2^256)`. +/// Event representing an exponentiation `a ^ b == d (mod 2^256)`. #[derive(Clone, Debug)] pub struct ExpEvent { /// Identifier for the exponentiation trace. diff --git a/bus-mapping/src/circuit_input_builder/tracer_tests.rs b/bus-mapping/src/circuit_input_builder/tracer_tests.rs index 0855d491eb..9ce73c4599 100644 --- a/bus-mapping/src/circuit_input_builder/tracer_tests.rs +++ b/bus-mapping/src/circuit_input_builder/tracer_tests.rs @@ -146,7 +146,7 @@ fn check_err_depth(step: &GethExecStep, next_step: Option<&GethExecStep>) -> boo #[test] fn tracer_err_depth() { - // Recursive CALL will exaust the call depth + // Recursive CALL will exhaust the call depth let code = bytecode! { PUSH1(0x0) // retLength PUSH1(0x0) // retOffset diff --git a/bus-mapping/src/error.rs b/bus-mapping/src/error.rs index 313ed43652..a125fdb421 100644 --- a/bus-mapping/src/error.rs +++ b/bus-mapping/src/error.rs @@ -105,7 +105,7 @@ pub enum OogError { SelfDestruct, } -// Given OpCodeId, returns correponding OogError. +// Given OpCodeId, returns corresponding OogError. impl From<&OpcodeId> for OogError { fn from(op: &OpcodeId) -> Self { match op { diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 350915302a..395b22504f 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -170,7 +170,7 @@ impl Opcode for CallOpcode { // read balance of caller to compare to value for insufficient_balance checking // in circuit, also use for callcode successful case check balance is // indeed larger than transfer value. for call opcode, it does in - // tranfer gadget implicitly. + // transfer gadget implicitly. state.account_read( &mut exec_step, call.caller_address, diff --git a/bus-mapping/src/evm/opcodes/error_write_protection.rs b/bus-mapping/src/evm/opcodes/error_write_protection.rs index 2c9bd0b87e..65c3cb9eee 100644 --- a/bus-mapping/src/evm/opcodes/error_write_protection.rs +++ b/bus-mapping/src/evm/opcodes/error_write_protection.rs @@ -44,7 +44,7 @@ impl Opcode for ErrorWriteProtection { .contains(&geth_step.op)); if geth_step.op == OpcodeId::CALL { - // get only the frist three stack elements since the third one is the value we + // get only the first three stack elements since the third one is the value we // want to check. for i in 0..3 { state.stack_read( diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index abd38f9141..f370e4b356 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -187,7 +187,7 @@ //! | `key` | `val` | `rw` | `gc` | Note | //! |:------:| ------------- | ------- | ---- | ---------------------------------------- | //! | `0x40` | `0` | `Write` | | Init | -//! | `0x40` | `0x80` | `Write` | 0 | Assume written at the begining of `code` | +//! | `0x40` | `0x80` | `Write` | 0 | Assume written at the beginning of `code`| //! | `0x40` | `0x80` | `Read` | 4 | `56 MLOAD` | //! | - | | | | | //! | `0x80` | `0` | `Write` | | Init | @@ -213,7 +213,7 @@ //! See: #![cfg_attr(docsrs, feature(doc_cfg))] -// We want to have UPPERCASE idents sometimes. +// We want to have UPPERCASE indents sometimes. #![allow(non_snake_case)] // Catch documentation errors caused by code changes. #![deny(rustdoc::broken_intra_doc_links)] diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 08a581e318..31c449c9a5 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -20,7 +20,7 @@ pub struct BlockData { /// chain id pub chain_id: Word, /// history hashes contains most recent 256 block hashes in history, where - /// the lastest one is at history_hashes[history_hashes.len() - 1]. + /// the latest one is at history_hashes[history_hashes.len() - 1]. pub history_hashes: Vec, /// Block from geth pub eth_block: eth_types::Block, diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index 26469c77b5..e2a3061f1d 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -90,7 +90,7 @@ impl RWCounter { } } -/// Enum used to differenciate between EVM Stack, Memory and Storage operations. +/// Enum used to differentiate between EVM Stack, Memory and Storage operations. /// This is also used as the RwTableTag for the RwTable. #[derive(Debug, Clone, PartialEq, Eq, Copy, EnumIter, Hash)] pub enum Target { @@ -1038,7 +1038,7 @@ impl Operation { &mut self.op } - // /// Matches over an `Operation` returning the [`Target`] of the iternal + // /// Matches over an `Operation` returning the [`Target`] of the internal // op /// it stores inside. // pub const fn target(&self) -> Target { // self.op.target() diff --git a/bus-mapping/src/operation/container.rs b/bus-mapping/src/operation/container.rs index 21d1918895..cf2e4dcf22 100644 --- a/bus-mapping/src/operation/container.rs +++ b/bus-mapping/src/operation/container.rs @@ -16,7 +16,7 @@ use itertools::Itertools; /// [`ExecStep`](crate::circuit_input_builder::ExecStep). /// /// Finally, the container also provides the capability of retrieving all of the -/// `Stack`, `Memory` or `Storage` operations ordered according to the criterias +/// `Stack`, `Memory` or `Storage` operations ordered according to the criteria /// they have specified. /// That serves as a way to get an input with which is easy to work with in /// order to construct the State proof. diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index b40c107a47..7c81808d29 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -167,7 +167,7 @@ pub struct Transaction { /// Gas Limit / Supplied gas /// U64 type is required to serialize into proper hex with 0x prefix pub gas_limit: U64, - /// Transfered value + /// Transferred value pub value: Word, /// Gas Price pub gas_price: Word, @@ -359,7 +359,7 @@ impl Transaction { ..response::Transaction::default() } } - /// Convinient method for gas limit + /// Convenient method for gas limit pub fn gas(&self) -> u64 { self.gas_limit.as_u64() } diff --git a/eth-types/src/keccak.rs b/eth-types/src/keccak.rs index 3bef09e94a..b697a5743c 100644 --- a/eth-types/src/keccak.rs +++ b/eth-types/src/keccak.rs @@ -253,7 +253,7 @@ impl Sponge { } } -/// Convinient method to get 32 bytes digest +/// Convenient method to get 32 bytes digest pub fn keccak256(msg: &[u8]) -> [u8; 32] { let mut keccak = Keccak::default(); keccak.update(msg); diff --git a/eth-types/src/sign_types.rs b/eth-types/src/sign_types.rs index af5dd261c4..c3104903fd 100644 --- a/eth-types/src/sign_types.rs +++ b/eth-types/src/sign_types.rs @@ -140,7 +140,7 @@ pub fn recover_pk( } lazy_static! { - /// Secp256k1 Curve Scalar. Referece: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended + /// Secp256k1 Curve Scalar. Reference: Section 2.4.1 (parameter `n`) in "SEC 2: Recommended /// Elliptic Curve Domain Parameters" document at http://www.secg.org/sec2-v2.pdf pub static ref SECP256K1_Q: BigUint = BigUint::from_bytes_le(&(secp256k1::Fq::ZERO - secp256k1::Fq::ONE).to_repr()) + 1u64; } diff --git a/gadgets/src/is_zero.rs b/gadgets/src/is_zero.rs index ed44aa56a0..db89ff28ba 100644 --- a/gadgets/src/is_zero.rs +++ b/gadgets/src/is_zero.rs @@ -52,7 +52,7 @@ impl IsZeroConfig { } #[derive(Debug, Clone)] -/// Wrapper arround [`IsZeroConfig`] for which [`Chip`] is implemented. +/// Wrapper around [`IsZeroConfig`] for which [`Chip`] is implemented. pub struct IsZeroChip { config: IsZeroConfig, } diff --git a/geth-utils/gethutil/mpt/state/statedb.go b/geth-utils/gethutil/mpt/state/statedb.go index 616f629b6d..787319705c 100644 --- a/geth-utils/gethutil/mpt/state/statedb.go +++ b/geth-utils/gethutil/mpt/state/statedb.go @@ -767,7 +767,7 @@ func (s *StateDB) Copy() *StateDB { // nil if object, exist := s.stateObjects[addr]; exist { // Even though the original object is dirty, we are not copying the journal, - // so we need to make sure that anyside effect the journal would have caused + // so we need to make sure that any side effect the journal would have caused // during a commit (or similar op) is already applied to the copy. state.stateObjects[addr] = object.deepCopy(state) @@ -816,7 +816,7 @@ func (s *StateDB) Copy() *StateDB { } if s.snaps != nil { // In order for the miner to be able to use and make additions - // to the snapshot tree, we need to copy that aswell. + // to the snapshot tree, we need to copy that as well. // Otherwise, any block mined by ourselves will cause gaps in the tree, // and force the miner to operate trie-backed only state.snaps = s.snaps diff --git a/geth-utils/gethutil/mpt/trie/hasher.go b/geth-utils/gethutil/mpt/trie/hasher.go index c96677415b..4904340b7b 100644 --- a/geth-utils/gethutil/mpt/trie/hasher.go +++ b/geth-utils/gethutil/mpt/trie/hasher.go @@ -40,7 +40,7 @@ func (b *sliceBuffer) Reset() { type hasher struct { sha crypto.KeccakState tmp sliceBuffer - parallel bool // Whether to use paralallel threads when hashing + parallel bool // Whether to use parallel threads when hashing } // hasherPool holds pureHashers @@ -189,7 +189,7 @@ func (h *hasher) HashData(data []byte) HashNode { } // ProofHash is used to construct trie proofs, and returns the 'collapsed' -// node (for later RLP encoding) aswell as the hashed node -- unless the +// node (for later RLP encoding) as well as the hashed node -- unless the // node is smaller than 32 bytes, in which case it will be returned as is. // This method does not do anything on value- or hash-nodes. func (h *hasher) ProofHash(original Node) (collapsed, hashed Node) { diff --git a/geth-utils/gethutil/mpt/types/block.go b/geth-utils/gethutil/mpt/types/block.go index 360f1eb47c..b22efb6692 100644 --- a/geth-utils/gethutil/mpt/types/block.go +++ b/geth-utils/gethutil/mpt/types/block.go @@ -314,7 +314,7 @@ func (b *Block) Header() *Header { return CopyHeader(b.header) } func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } // Size returns the true RLP encoded storage size of the block, either by encoding -// and returning it, or returning a previsouly cached value. +// and returning it, or returning a previously cached value. func (b *Block) Size() common.StorageSize { if size := b.size.Load(); size != nil { return size.(common.StorageSize) diff --git a/geth-utils/gethutil/trace.go b/geth-utils/gethutil/trace.go index e6d8464d36..f0cb76cffd 100644 --- a/geth-utils/gethutil/trace.go +++ b/geth-utils/gethutil/trace.go @@ -117,7 +117,7 @@ type Transaction struct { type TraceConfig struct { ChainID *hexutil.Big `json:"chain_id"` // HistoryHashes contains most recent 256 block hashes in history, - // where the lastest one is at HistoryHashes[len(HistoryHashes)-1]. + // where the latest one is at HistoryHashes[len(HistoryHashes)-1]. HistoryHashes []*hexutil.Big `json:"history_hashes"` Block Block `json:"block_constants"` Accounts map[common.Address]Account `json:"accounts"` diff --git a/geth-utils/src/mpt.rs b/geth-utils/src/mpt.rs index 46c60d0e65..6f212f2f54 100644 --- a/geth-utils/src/mpt.rs +++ b/geth-utils/src/mpt.rs @@ -96,7 +96,7 @@ pub fn get_witness(block_no: u64, mods: &[TrieModification], node_url: &str) -> unsafe { go::FreeString(c_str.as_ptr()) }; // Note: previously this function returned a Vec of Nodes, but now returning a JSON string - // to avoid imporing zkEVM circuit here (that will create a circular dependency). + // to avoid importing zkEVM circuit here (that will create a circular dependency). // TODO: consider defining Node types in another crate. json diff --git a/integration-tests/build.rs b/integration-tests/build.rs index b42601fe88..6b937e3bf9 100644 --- a/integration-tests/build.rs +++ b/integration-tests/build.rs @@ -45,7 +45,7 @@ enum BuildError { /// Vec is empty #[error("ArtifactError")] ArtifactError, - /// Functon compile_output failed to encode CompilerInput to Vec + /// Function compile_output failed to encode CompilerInput to Vec #[error("CompileOutputFailure({0:})")] CompileOutputFailure(String), /// Could not convert Vec to CompilerOutput diff --git a/integration-tests/src/bin/gen_blockchain_data.rs b/integration-tests/src/bin/gen_blockchain_data.rs index e9eac2682a..dd4f3ad4ef 100644 --- a/integration-tests/src/bin/gen_blockchain_data.rs +++ b/integration-tests/src/bin/gen_blockchain_data.rs @@ -63,7 +63,7 @@ async fn dump_tx_trace(prov: Provider, receipt: TransactionReceipt, name: .expect("Failed to get transaction debug trace"); let filename = format!("{}_trace.json", name); serde_json::to_writer(&File::create(filename).expect("cannot create file"), &trace) - .expect("Could not seralize trace as json data"); + .expect("Could not serialize trace as json data"); } fn erc20_transfer( diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index 9631b36316..bfa239d236 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -164,7 +164,7 @@ fn test_actual_circuit>( let mut transcript = PoseidonTranscript::new(Vec::new()); - // change instace to slice + // change instance to slice let instance: Vec<&[Fr]> = instance.iter().map(|v| v.as_slice()).collect(); log::info!("gen circuit proof"); @@ -214,7 +214,7 @@ fn test_actual_root_circuit>( let mut transcript = EvmTranscript::<_, NativeLoader, _, _>::new(vec![]); - // change instace to slice + // change instance to slice let instance: Vec<&[Fr]> = instance.iter().map(|v| v.as_slice()).collect(); log::info!("gen root circuit proof"); diff --git a/integration-tests/tests/circuit_input_builder.rs b/integration-tests/tests/circuit_input_builder.rs index d2fbd6a22b..f5eb9de752 100644 --- a/integration-tests/tests/circuit_input_builder.rs +++ b/integration-tests/tests/circuit_input_builder.rs @@ -74,7 +74,7 @@ macro_rules! declare_tests { } // This test builds the complete circuit inputs for the block where 1 ETH is -// transfered. +// transferred. declare_tests!(test_circuit_input_builder_block_transfer_0, "Transfer 0"); // This test builds the complete circuit inputs for the block where the Greeter // contract is deployed. diff --git a/mock/src/test_ctx.rs b/mock/src/test_ctx.rs index 23c3e38218..164b27e589 100644 --- a/mock/src/test_ctx.rs +++ b/mock/src/test_ctx.rs @@ -80,7 +80,7 @@ pub struct TestContext { /// Account list pub accounts: [Account; NACC], /// history hashes contains most recent 256 block hashes in history, where - /// the lastest one is at history_hashes[history_hashes.len() - 1]. + /// the latest one is at history_hashes[history_hashes.len() - 1]. pub history_hashes: Vec, /// Block from geth pub eth_block: eth_types::Block, @@ -172,7 +172,7 @@ impl TestContext { } } -/// Collection of helper functions which contribute to specific rutines on the +/// Collection of helper functions which contribute to specific routines on the /// builder pattern used to construct [`TestContext`]s. pub mod helpers { use super::*; diff --git a/testool/README.md b/testool/README.md index 4a619374c5..0f389d8ad6 100644 --- a/testool/README.md +++ b/testool/README.md @@ -102,7 +102,7 @@ Sometimes there are some files or specific tests that we want to disable at all. When the command line parameter `--report` is defined, it automatically: - After the execution, a two files are created in the `report` folder. They are - - `-.hml` with the browseable results of the execution. + - `-.hml` with the browsable results of the execution. - `-.csv` with the raw results of the execution - The HTML file also contains the diff with the previous result. The previous result file is the more recent csv file with different commit from the current one diff --git a/testool/src/abi.rs b/testool/src/abi.rs index e3e9287bbb..34e1699253 100644 --- a/testool/src/abi.rs +++ b/testool/src/abi.rs @@ -15,7 +15,7 @@ pub fn encode_funccall(spec: &str) -> Result { let func_name = func_name_params[0]; let func_params = &func_name_params[1..func_name_params.len() - 1]; - // transform func_params and args into the appropiate types + // transform func_params and args into the appropriate types let map_type = |t| match t { "uint" => ParamType::Uint(256), diff --git a/testool/src/main.rs b/testool/src/main.rs index e94bffff9d..1687af80fb 100644 --- a/testool/src/main.rs +++ b/testool/src/main.rs @@ -109,7 +109,7 @@ fn go() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); log::info!("Using suite '{}'", args.suite); - log::info!("Parsing and compliling tests..."); + log::info!("Parsing and compiling tests..."); let compiler = Compiler::new(true, Some(PathBuf::from(CODEHASH_FILE)))?; let suite = config.suite(&args.suite)?.clone(); let state_tests = load_statetests_suite(&suite.path, config, compiler)?; diff --git a/zkevm-circuits/src/bin/stats/helpers.rs b/zkevm-circuits/src/bin/stats/helpers.rs index fa470f0143..4594c187e4 100644 --- a/zkevm-circuits/src/bin/stats/helpers.rs +++ b/zkevm-circuits/src/bin/stats/helpers.rs @@ -213,7 +213,7 @@ pub(crate) fn print_circuit_stats_by_states( assert_eq!(ExecState::Op(opcode), step.exec_state); let height = fn_height(&builder.block, state, step_index); - // Substract 1 to step_index to remove the `BeginTx` step, which doesn't appear + // Subtract 1 to step_index to remove the `BeginTx` step, which doesn't appear // in the geth trace. let geth_step = &block.geth_traces[0].struct_logs[step_index - 1]; assert_eq!(opcode, geth_step.op); diff --git a/zkevm-circuits/src/bytecode_circuit/test.rs b/zkevm-circuits/src/bytecode_circuit/test.rs index e28f8c2cf8..b5f69c4481 100644 --- a/zkevm-circuits/src/bytecode_circuit/test.rs +++ b/zkevm-circuits/src/bytecode_circuit/test.rs @@ -194,7 +194,7 @@ fn bytecode_invalid_is_code() { }) .verify(false); } - // Mark the 4rd byte as data (is code) + // Mark the 4th byte as data (is code) { BytecodeCircuit::::from_bytes(bytecodes.clone(), k) .mut_rows(|rows| { diff --git a/zkevm-circuits/src/circuit_tools/constraint_builder.rs b/zkevm-circuits/src/circuit_tools/constraint_builder.rs index 814b2eaa0f..b61d58b26d 100644 --- a/zkevm-circuits/src/circuit_tools/constraint_builder.rs +++ b/zkevm-circuits/src/circuit_tools/constraint_builder.rs @@ -32,7 +32,7 @@ fn get_condition_expr(conditions: &Vec>) -> Expression { - /// Desciption + /// Description pub description: &'static str, /// Condition under which the lookup needs to be done pub regional_condition: Expression, @@ -47,7 +47,7 @@ pub struct TableData { /// Lookup data #[derive(Clone, Debug)] pub struct LookupData { - /// Desciption + /// Description pub description: String, /// Condition under which the lookup needs to be done pub regional_condition: Expression, @@ -130,7 +130,7 @@ pub struct ConstraintBuilder { max_degree: usize, /// conditions for constraints conditions: Vec>, - /// Columns whoes equality constraints needed to be enable + /// Columns whose equality constraints needed to be enable equalities: Vec>, /// The tables pub tables: HashMap>>, @@ -147,7 +147,7 @@ pub struct ConstraintBuilder { pub region_id: usize, /// lookup input challenge pub lookup_challenge: Option>, - /// state contect + /// state context pub state_context: Vec>, /// state constraints start pub region_constraints_start: usize, @@ -213,7 +213,7 @@ impl ConstraintBuilder { } pub(crate) fn pop_region(&mut self) { - // Apply the region condition to all contraints added in this region + // Apply the region condition to all constraints added in this region let condition = get_condition_expr(&self.state_context); for idx in self.region_constraints_start..self.constraints.len() { self.constraints[idx].1 = condition.expr() * self.constraints[idx].1.clone(); @@ -810,7 +810,7 @@ impl ExprResult for Expression { } } -/// Implement `ExprResult` for tupples +/// Implement `ExprResult` for tuples #[macro_export] macro_rules! impl_expr_result { ($($type:ty),*) => { diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index dff1f99a42..644a951900 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -431,7 +431,7 @@ impl SubCircuitConfig for CopyCircuitConfig { .collect() }); - meta.create_gate("id_hi === 0 when Momory", |meta| { + meta.create_gate("id_hi === 0 when Memory", |meta| { let mut cb = BaseConstraintBuilder::default(); let cond = tag.value_equals(CopyDataType::Memory, Rotation::cur())(meta) diff --git a/zkevm-circuits/src/ecc_circuit.rs b/zkevm-circuits/src/ecc_circuit.rs index 0764e19d76..5c4c0d092a 100644 --- a/zkevm-circuits/src/ecc_circuit.rs +++ b/zkevm-circuits/src/ecc_circuit.rs @@ -570,7 +570,7 @@ impl EccCircuit { ecc_chip.assert_equal(ctx, &rand_point, &sum3); - log::trace!("[ECC] EcAdd Assignmnet END:"); + log::trace!("[ECC] EcAdd Assignment END:"); log_context_cursor!(ctx); EcAddDecomposed { @@ -602,7 +602,7 @@ impl EccCircuit { powers_of_256: &[QuantumCell], op: &EcMulOp, ) -> EcMulDecomposed { - log::trace!("[ECC] ==> EcMul Assignmnet START:"); + log::trace!("[ECC] ==> EcMul Assignment START:"); log_context_cursor!(ctx); let (px, px_cells, px_valid, px_is_zero) = @@ -653,7 +653,7 @@ impl EccCircuit { let point_r_got = ecc_chip.select(ctx, &point_r_got, &infinity, &is_valid); ecc_chip.assert_equal(ctx, &point_r.ec_point, &point_r_got); - log::trace!("[ECC] EcMul Assignmnet END:"); + log::trace!("[ECC] EcMul Assignment END:"); log_context_cursor!(ctx); EcMulDecomposed { diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index 9bd928a7ce..a952b166bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -64,7 +64,7 @@ impl ExecutionGadget for AddModGadget { let n = cb.query_word32(); let r = cb.query_word32(); - // auxiliar witness + // auxiliary witness let k = cb.query_word32(); let a_reduced = cb.query_word32(); let d = cb.query_word32(); @@ -161,7 +161,7 @@ impl ExecutionGadget for AddModGadget { // get stack values 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 + // assign a,b & n stack values self.a.assign_u256(region, offset, a)?; self.b.assign_u256(region, offset, b)?; self.n.assign_u256(region, offset, n)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs index f0d5155cac..99f6cdd933 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs @@ -116,7 +116,7 @@ impl ExecutionGadget for CallDataCopyGadget { CopyDataType::Memory.expr(), ); cb.condition(memory_address.has_length(), |cb| { - // Set source start to the minimun value of data offset and call data length. + // Set source start to the minimum value of data offset and call data length. let src_addr = call_data_offset.expr() + select::expr( data_offset.lt_cap(), diff --git a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs index bc133db5c2..67b2efda19 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs @@ -115,7 +115,7 @@ impl ExecutionGadget for CallDataLoadGadget { }, ); - // Set source start to the minimun value of data offset and call data length. + // Set source start to the minimum value of data offset and call data length. let src_addr = call_data_offset.expr() + select::expr( data_offset.lt_cap(), diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 66716640c1..761e54d50f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -234,7 +234,7 @@ impl ExecutionGadget for CallOpGadget { // Verify transfer only for CALL opcode in the successful case. If value == 0, // 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 + // will not be created when value is 0 and so the callee balance lookup // would be invalid). let transfer = cb.condition(is_call.expr() * is_precheck_ok.expr(), |cb| { TransferGadget::construct( diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index 45c2fc0360..fc019ec674 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -68,7 +68,7 @@ impl ExecutionGadget for CodeCopyGadget { cb.stack_pop(code_offset.original_word().to_word()); cb.stack_pop(WordLoHi::from_lo_unchecked(length.expr())); - // Construct memory address in the destionation (memory) to which we copy code. + // Construct memory address in the destination (memory) to which we copy code. let dst_memory_addr = MemoryAddressGadget::construct(cb, dst_memory_offset, length); // Fetch the hash of bytecode running in current environment. @@ -89,7 +89,7 @@ impl ExecutionGadget for CodeCopyGadget { let copy_rwc_inc = cb.query_cell(); cb.condition(dst_memory_addr.has_length(), |cb| { - // Set source start to the minimun value of code offset and code size. + // Set source start to the minimum value of code offset and code size. let src_addr = select::expr( code_offset.lt_cap(), code_offset.valid_value(), diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 8c68776fdc..5f650f693a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -467,7 +467,7 @@ mod test { .sorted_by_key(|a| a.rw_counter()) .tuple_windows() .for_each(|(a, b)| { - // chech there is no consecutive write with same txid value + // check there is no consecutive write with same txid value assert!(a.rw_counter() != b.rw_counter()); assert!(a.value_assignment() != b.value_assignment()); }) diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index bb05e5237d..5cda8007d8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -116,7 +116,7 @@ impl ExecutionGadget for ExtcodecopyGadget { let copy_rwc_inc = cb.query_cell(); cb.condition(memory_address.has_length(), |cb| { - // Set source start to the minimun value of code offset and code size. + // Set source start to the minimum value of code offset and code size. let src_addr = select::expr( code_offset.lt_cap(), code_offset.valid_value(), diff --git a/zkevm-circuits/src/evm_circuit/execution/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index 47456bcafc..327cae451f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/logs.rs +++ b/zkevm-circuits/src/evm_circuit/execution/logs.rs @@ -402,10 +402,10 @@ mod test { code.write_op(cur_op_code); // second log op code - // prepare additinal bytes for memory reading + // prepare additional bytes for memory reading code.append(&prepare_code(&pushdata, 0x20)); mstart = 0x00usize; - // when mszie > 0x20 (32) needs multi copy steps + // when msize > 0x20 (32) needs multi copy steps msize = 0x30usize; for topic in topics { code.push(32, *topic); diff --git a/zkevm-circuits/src/evm_circuit/execution/origin.rs b/zkevm-circuits/src/evm_circuit/execution/origin.rs index 720d8d21b0..2c51b35397 100644 --- a/zkevm-circuits/src/evm_circuit/execution/origin.rs +++ b/zkevm-circuits/src/evm_circuit/execution/origin.rs @@ -73,7 +73,7 @@ impl ExecutionGadget for OriginGadget { ) -> Result<(), Error> { let origin = block.get_rws(step, 1).stack_value(); - // Assing TxId. + // Assign TxId. self.tx_id .assign(region, offset, Value::known(F::from(tx.id)))?; diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index d84d324046..7e1a8b57aa 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -94,7 +94,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { WordLoHi::from_lo_unchecked(return_data_size.expr()), ); - // 3. contraints for copy: copy overflow check + // 3. constraints for copy: copy overflow check // i.e., offset + size <= return_data_size let in_bound_check = RangeCheckGadget::construct( cb, diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index fd09750a16..1bd8dc9805 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -124,7 +124,7 @@ impl ExecutionGadget for SarGadget { lt }); - // Merge contraints + // Merge constraints let shf_lo_div64_eq0 = cb.is_zero(shf_div64.expr()); let shf_lo_div64_eq1 = cb.is_eq(shf_div64.expr(), 1.expr()); let shf_lo_div64_eq2 = cb.is_eq(shf_div64.expr(), 2.expr()); diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index c69cfeb503..e2e4550418 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -180,7 +180,7 @@ lazy_static::lazy_static! { }; // Step slot height in evm circuit // We enable the invalid_tx feature to get invalid tx's ExecutionState height - // We garentee the heights of other ExecutionStates remains unchanged in the following test + // We guarantee the heights of other ExecutionStates remains unchanged in the following test pub(crate) static ref EXECUTION_STATE_HEIGHT_MAP : HashMap = get_step_height_map(*INVALID_TX_CONFIG); } fn get_step_height_map(feature_config: FeatureConfig) -> HashMap { diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index 85deb21ba6..195cc77321 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -596,7 +596,7 @@ impl ExecutionState { .collect() } - /// Get the state hight + /// Get the state height pub fn get_step_height_option(&self) -> Option { EXECUTION_STATE_HEIGHT_MAP.get(self).copied() } diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index da51d63c69..cfa5170fd6 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -47,7 +47,7 @@ pub enum FixedTableTag { Pow2, /// Lookup constant gas cost for opcodes ConstantGasCost, - /// Preocmpile information + /// Precompile information PrecompileInfo, } impl_expr!(FixedTableTag); @@ -253,7 +253,7 @@ impl RwValues { #[derive(Clone, Debug)] pub(crate) enum Lookup { - /// Lookup to fixed table, which contains serveral pre-built tables such as + /// Lookup to fixed table, which contains several pre-built tables such as /// range tables or bitwise tables. Fixed { /// Tag to specify which table to lookup. diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 0c323be986..04ee168d49 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -748,7 +748,7 @@ impl, const IS_SUCCESS_CAL let cd_address = MemAddrGadget::construct_self(cb); let rd_address = MemAddrGadget::construct_self(cb); // Lookup values from stack - // `callee_address` is poped from stack and used to check if it exists in + // `callee_address` is popped from stack and used to check if it exists in // access list and get code hash. // For CALLCODE, both caller and callee addresses are `current_callee_address`. // For DELEGATECALL, caller address is `current_caller_address` and diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 01342631f3..c7fce109ab 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1562,7 +1562,7 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { /// and constrain it using the provided respective constraint. This mechanism is specifically /// used for constraining the internal states for precompile calls. Each precompile call /// expects a different cell layout, but since the next state can be at the most one precompile - /// state, we can re-use cells assgined across all thos conditions. + /// state, we can re-use cells assigned across all those conditions. pub(crate) fn constrain_mutually_exclusive_next_step( &mut self, conditions: Vec>, diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index a238a542f8..fb7f76db2f 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -37,7 +37,7 @@ impl Instrument { self.states.push((execution_state, sizes)); } - /// Dissasembles the instrumentation data and returns a collection of + /// Disassembles the instrumentation data and returns a collection of /// `ExecStateReport`s. One for each EVM `ExecutionState`. pub fn analyze(&self) -> Vec { let mut report_collection = vec![]; diff --git a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs index bbd2bbcfac..3f59a5d0b0 100644 --- a/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/memory_gadget.rs @@ -568,7 +568,7 @@ pub(crate) struct BufferReaderGadget; MAX_BYTES], - /// The selectors that indicate if the corrsponding byte contains real data + /// The selectors that indicate if the corresponding byte contains real data /// or is padded selectors: [Cell; MAX_BYTES], /// The min gadget to take the minimum of addr_start and addr_end diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index b02544fec5..529cd61c9d 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -296,7 +296,7 @@ impl SubCircuitConfig for KeccakCircuitConfig { // that allows reusing the same parts in an optimal way for the chi step. // We can save quite a few columns by not recombining the parts after rho/pi and // re-splitting the words again before chi. Instead we do chi directly - // on the output parts of rho/pi. For rho/pi specically we do + // on the output parts of rho/pi. For rho/pi specially we do // `s[j][2 * i + 3 * j) % 5] = normalize(rot(s[i][j], RHOM[i][j]))`. cell_manager.get_strategy().start_region(); let mut lookup_counter = 0; @@ -1005,7 +1005,7 @@ impl SubCircuit for KeccakCircuit { keccak_unusable_rows() } - /// The `block.circuits_params.keccak_padding` parmeter, when enabled, sets + /// The `block.circuits_params.keccak_padding` parameter, when enabled, sets /// up the circuit to support a fixed number of permutations/keccak_f's, /// independently of the permutations required by `inputs`. fn new_from_block(block: &witness::Block) -> Self { diff --git a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs index 26a4aea374..25f16a5f96 100644 --- a/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs +++ b/zkevm-circuits/src/keccak_circuit/keccak_packed_multi.rs @@ -450,7 +450,7 @@ pub(crate) mod transform { } } -// Transfroms values to cells +// Transforms values to cells pub(crate) mod transform_to { use super::{AssignKeccakRegion, Cell, KeccakRegion, Part, PartValue}; use crate::{ diff --git a/zkevm-circuits/src/mpt_circuit.rs b/zkevm-circuits/src/mpt_circuit.rs index a69fb5b94e..5a2c79b02b 100644 --- a/zkevm-circuits/src/mpt_circuit.rs +++ b/zkevm-circuits/src/mpt_circuit.rs @@ -201,7 +201,7 @@ pub enum FixedTableTag { RangeKeyLen256, /// For checking there are 0s after the RLP stream ends RangeKeyLen16, - /// Extesion key odd key + /// Extension key odd key ExtOddKey, /// RLP decoding RLP, diff --git a/zkevm-circuits/src/mpt_circuit/extension.rs b/zkevm-circuits/src/mpt_circuit/extension.rs index 6552eeeb71..e3ed78e819 100644 --- a/zkevm-circuits/src/mpt_circuit/extension.rs +++ b/zkevm-circuits/src/mpt_circuit/extension.rs @@ -61,7 +61,7 @@ impl ExtensionGadget { circuit!([meta, cb], { // Data let key_items = [ - // Special case, requring string fail tests + // Special case, requiring string fail tests ctx.rlp_item( meta, cb, diff --git a/zkevm-circuits/src/root_circuit/aggregation.rs b/zkevm-circuits/src/root_circuit/aggregation.rs index debbca689b..f53ae78b26 100644 --- a/zkevm-circuits/src/root_circuit/aggregation.rs +++ b/zkevm-circuits/src/root_circuit/aggregation.rs @@ -518,7 +518,7 @@ pub mod test { pub struct StandardPlonk(F); impl StandardPlonk { - /// Create a `StandardPlonk` with random instnace. + /// Create a `StandardPlonk` with random instance. pub fn rand(mut rng: R) -> Self { Self(F::from(rng.next_u32() as u64)) } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 53b7a90c30..55fabdf08a 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -285,7 +285,7 @@ impl SuperCircuit { } } -// Eventhough the SuperCircuit is not a subcircuit we implement the SubCircuit +// Even though the SuperCircuit is not a subcircuit we implement the SubCircuit // trait for it in order to get the `new_from_block` and `instance` methods that // allow us to generalize integration tests. impl SubCircuit for SuperCircuit { diff --git a/zkevm-circuits/src/table/bytecode_table.rs b/zkevm-circuits/src/table/bytecode_table.rs index f433a2a777..ca73aab745 100644 --- a/zkevm-circuits/src/table/bytecode_table.rs +++ b/zkevm-circuits/src/table/bytecode_table.rs @@ -40,7 +40,7 @@ impl BytecodeTable { } } - /// Assign the `BytecodeTable` from a list of bytecodes, followig the same + /// Assign the `BytecodeTable` from a list of bytecodes, following the same /// table layout that the Bytecode Circuit uses. pub fn load( &self, diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs index eeeb1a2eb1..4b93bdf2ba 100644 --- a/zkevm-circuits/src/table/copy_table.rs +++ b/zkevm-circuits/src/table/copy_table.rs @@ -119,7 +119,7 @@ impl CopyTable { number_or_hash_to_word(©_event.dst_id) }; - // tag binary bumber chip + // tag binary number chip let tag = if is_read_step { copy_event.src_type } else { diff --git a/zkevm-circuits/src/table/keccak_table.rs b/zkevm-circuits/src/table/keccak_table.rs index 6957a09e98..e2d906e40e 100644 --- a/zkevm-circuits/src/table/keccak_table.rs +++ b/zkevm-circuits/src/table/keccak_table.rs @@ -125,7 +125,7 @@ impl KeccakTable { } /// returns matchings between the circuit columns passed as parameters and - /// the table collumns + /// the table columns pub fn match_columns( &self, value_rlc: Column, diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index e73b5d18e7..9a890b7364 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -269,13 +269,13 @@ pub enum Circuit { #[derive(Debug, Error)] /// Errors for Circuit test pub enum CircuitTestError { - /// We didn't specify enough attibutes to define a block for the circuit test + /// We didn't specify enough attributes to define a block for the circuit test #[error("NotEnoughAttributes")] NotEnoughAttributes, /// Something wrong in the handle_block #[error("CannotHandleBlock({0})")] CannotHandleBlock(String), - /// Something worng in the block_convert + /// Something wrong in the block_convert #[error("CannotConvertBlock({0})")] CannotConvertBlock(String), /// Problem constructing MockProver @@ -299,7 +299,7 @@ pub enum CircuitTestError { impl CircuitTestError { /// Filter out EVM circuit failures /// - /// Errors must come from EVM circuit and must be unsatisifed constraints or lookup failure + /// Errors must come from EVM circuit and must be unsatisfied constraints or lookup failure pub fn assert_evm_failure(&self) { match self { Self::VerificationFailed { circuit, reasons } => { diff --git a/zkevm-circuits/src/tx_circuit/sign_verify.rs b/zkevm-circuits/src/tx_circuit/sign_verify.rs index 15587021c6..7a9e0b2f5f 100644 --- a/zkevm-circuits/src/tx_circuit/sign_verify.rs +++ b/zkevm-circuits/src/tx_circuit/sign_verify.rs @@ -390,9 +390,9 @@ impl SignVerifyChip { // Ref. spec SignVerifyChip 4. Verify the ECDSA signature ecdsa_chip.verify(ctx, &sig, &pk_assigned, &msg_hash)?; - // TODO: Update once halo2wrong suports the following methods: + // TODO: Update once halo2wrong supports the following methods: // - `IntegerChip::assign_integer_from_bytes_le` - // - `GeneralEccChip::assing_point_from_bytes_le` + // - `GeneralEccChip::assign_point_from_bytes_le` Ok(AssignedECDSA { pk_x_le, diff --git a/zkevm-circuits/src/util/cell_manager.rs b/zkevm-circuits/src/util/cell_manager.rs index 574301ea78..abc2d1c815 100644 --- a/zkevm-circuits/src/util/cell_manager.rs +++ b/zkevm-circuits/src/util/cell_manager.rs @@ -277,7 +277,7 @@ impl CellManagerColumns { } } - /// Returns a column of a given cell type and index amoung all columns of that cell type. + /// Returns a column of a given cell type and index among all columns of that cell type. pub fn get_column(&self, cell_type: CellType, column_idx: usize) -> Option<&CellColumn> { if let Some(columns) = self.columns.get(&cell_type) { columns.get(column_idx) diff --git a/zkevm-circuits/src/util/word.rs b/zkevm-circuits/src/util/word.rs index c2e3e381a8..0205a234da 100644 --- a/zkevm-circuits/src/util/word.rs +++ b/zkevm-circuits/src/util/word.rs @@ -1,5 +1,5 @@ //! Define generic Word type with utility functions -// Naming Convesion +// Naming Conversion // - Limbs: An EVN word is 256 bits. Limbs N means split 256 into N limb. For example, N = 4, each // limb is 256/4 = 64 bits diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 507e8e370c..2718c795e9 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -191,7 +191,7 @@ pub struct BlockContext { pub number: Word, /// The timestamp of the block pub timestamp: Word, - /// The difficulty of the blcok + /// The difficulty of the block pub difficulty: Word, /// The base fee, the minimum amount of gas fee for a transaction pub base_fee: Word, diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 7c83101b49..950a8329b1 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -16,7 +16,7 @@ use crate::{ use super::MptUpdates; -/// Rw constainer for a witness block +/// Rw container for a witness block #[derive(Debug, Default, Clone)] pub struct RwMap(pub HashMap>); From 8e2e74aa22baf81d9071b1573c501778a0bab46e Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Mon, 26 Feb 2024 19:03:58 +0800 Subject: [PATCH 09/16] [Doc] Cleanup Bus-Mapping, ExecutionSteps, and ExecutionTrace (#1775) ### Description - Bus-Mapping has two library docs, one is inside the lib.rs, another locates in README.md, untested. We remove the one in lib.rs and make the README one checked. - ExecutionSteps and ExecutionTrace were We hope the update can reduce confusion. ### Issue Link ### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [x] This change requires a documentation update - [ ] Refactor (no updates to logic) ### How Has This Been Tested? ``` cargo test --doc ``` --------- Co-authored-by: sm.wu --- Makefile | 2 +- bus-mapping/README.md | 166 +++++++------- .../circuit_input_builder/input_state_ref.rs | 5 +- bus-mapping/src/evm/opcodes.rs | 3 +- bus-mapping/src/evm/opcodes/error_oog_exp.rs | 2 +- .../src/evm/opcodes/error_oog_memory_copy.rs | 3 +- .../src/evm/opcodes/error_oog_sload_sstore.rs | 3 +- bus-mapping/src/exec_trace.rs | 2 +- bus-mapping/src/lib.rs | 217 +----------------- eth-types/src/lib.rs | 4 +- .../src/evm_circuit/util/instrumentation.rs | 2 +- 11 files changed, 92 insertions(+), 317 deletions(-) diff --git a/Makefile b/Makefile index 274e6555e9..d0eb037d53 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ stats_evm_circuit: # Print a table with EVM Circuit stats by ExecState/opcode stats_copy_circuit: # Print a table with Copy Circuit stats by ExecState/opcode @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 +evm_exec_steps_occupancy: # Print a table for each EVM-CellManager CellType with the top 10 occupancy ExecSteps associated @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/README.md b/bus-mapping/README.md index de664a1bc7..a1db65fcf5 100644 --- a/bus-mapping/README.md +++ b/bus-mapping/README.md @@ -1,54 +1,36 @@ # ZKEVM Bus-Mapping -![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/appliedzkp/zkevm-circuits/CI%20checks/main?style=for-the-badge) +The `Bus-Mapping` crate is designed to parse EVM execution traces and manipulate the data they provide to obtain structured witness inputs for the EVM Proof and the State Proof. -`Bus-Mapping` is a crate designed to parse EVM execution traces and manipulate -all of the data they provide in order to obtain structured witness inputs -for the EVM Proof and the State Proof. +## Introduction -### Introduction -At the moment every node on ethereum has to validate every transaction in -the ethereum virtual machine. This means that every transaction adds work -that everyone needs to do to verify Ethereum’s history. Worse still is that -each transaction needs to be verified by every new node. Which means the -amount of work a new node needs to do the sync the network is growing -constantly. We want to build a proof of validity for the Ethereum blocks to -avoid this. +Currently, every node on Ethereum must validate every transaction in the Ethereum Virtual Machine (EVM). This means that each transaction adds work that everyone must do to verify Ethereum’s history. Furthermore, each transaction needs to be verified by every new node, leading to a growing amount of work for new nodes to sync the network. To address this, we aim to build a proof of validity for Ethereum blocks. -This means making a proof of validity for the EVM + state reads / writes + -signatures. -To simplify we separate our proofs into two components. +This involves creating a proof of validity for the EVM, including state reads/writes and signatures. To simplify, we divide our proofs into two components: -- State proof: State/memory/stack ops have been performed correctly. This -does not check if the correct location has been read/written. We allow our -prover to pick any location here and in the EVM proof confirm it is correct. +- State Proof: Validates that state/memory/stack operations have been performed correctly. However, it does not verify if the correct location has been read/written. Our prover can select any location here and in the EVM proof confirm its correctness. +- EVM Proof: Validates that the correct opcode is called at the correct time, checking the validity of these opcodes. It also confirms that for each of these opcodes, the state proof performed the correct operation. -- EVM proof: This checks that the correct opcode is called at the correct -time. It checks the validity of these opcodes. It also confirms that for -each of these opcodes the state proof performed the correct operation. +Only after verifying both proofs can we be confident that the Ethereum block is executed correctly. -Only after verifying both proofs are we confident that that Ethereum block -is executed correctly. +## Bus Mapping -### Bus Mapping The goal of this crate is to serve as: -- A parsing lib for EVM execution traces. -- A way to infer some witness data that can only be constructed once we've - analyzed the full exec trace. -- An easy interface to collect all of the data to witness into the circuits - and witness it with few function calls. - -### Parsing -Provided a JSON file or a JSON as a stream of bytes, which contains an -execution trace from an EVM, you can parse it and construct an -`ExecutionTrace` instance from it. That will automatically fill all of the -bus-mapping instances of each -[`ExecutionStep`](crate::exec_trace::ExecutionStep) plus fill in an -[`OperationContainer`](crate::operation::container::OperationContainer) with -all of the Memory, Stack and Storage ops performed by the provided trace. + +- A parsing library for EVM execution traces. +- A way to infer some witness data that can only be constructed once we've analyzed the full exec trace. +- An easy interface to collect all of the data to witness into the circuits and witness it with few function calls. + +## Parsing + +Given a JSON file or a JSON stream containing an execution trace from an EVM, you can parse it and construct a [`GethExecTrace`](eth_types::GethExecTrace) instance from it. This will automatically populate all of the bus-mapping instances of each [`ExecStep`](crate::circuit_input_builder::ExecStep) and fill an [`OperationContainer`](crate::operation::OperationContainer) with all of the memory, stack, and storage operations performed by the provided trace. ```rust -use bus_mapping::{ExecutionTrace, ExecutionStep, BlockConstants, Error, evm::EvmWord}; +use bus_mapping::mock::BlockData; +use eth_types::{ + GethExecTrace, GethExecStep, geth_types::GethData, bytecode +}; +use mock::test_ctx::{TestContext, helpers::*}; let input_trace = r#" [ @@ -98,31 +80,48 @@ let input_trace = r#" ] "#; -let block_ctants = BlockConstants::new( - Word::from(0), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), - pasta_curves::Fp::zero(), -); - -// Here we have the ExecutionTrace completely formed with all of the data to witness structured. -let obtained_exec_trace = ExecutionTrace::from_trace_bytes( - input_trace.as_bytes(), - block_ctants, -).expect("Error on trace generation"); +// We use the [`TestContext`] struct to mock a block. +let code = bytecode! { + // Write 0x6f to storage slot 0 + PUSH1(0x6fu64) + PUSH1(0x00u64) + SSTORE + // Load storage slot 0 + PUSH1(0x00u64) + SLOAD + 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), + tx_from_1_to_0, + |block, _tx| block.number(0xcafeu64), +) +.unwrap() +.into(); +// Here we update the circuit input with the data from the transaction trace. +let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); +let builder = builder + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); +let geth_steps: Vec = serde_json::from_str(input_trace).unwrap(); +let _geth_trace = GethExecTrace { + return_value: "".to_string(), + gas: block.eth_block.transactions[0].gas.as_u64(), + invalid: false, + failed: false, + struct_logs: geth_steps, +}; // Get an ordered vector with all of the Stack operations of this trace. -let stack_ops = obtained_exec_trace.sorted_stack_ops(); - +let _stack_ops = builder.block.container.sorted_stack(); // You can also iterate over the steps of the trace and witness the EVM Proof. -obtained_exec_trace.steps().iter(); +let _ = builder.block.txs()[0].steps().iter(); ``` -Assume we have the following trace: +Assuming we have the following trace: + ```text,ignore pc op stack (top -> down) memory -- -------------- ---------------------------------- --------------------------------------- @@ -148,37 +147,28 @@ pc op stack (top -> down) memory ... ``` -Once you have the trace built (following the code found above) you can -basically: -- Get an iterator/vector over the `Stack`, `Memory` or `Storage` operations - ordered on the way the State Proof needs. +Once you have built the trace (following the code above), you can: + +- Get an iterator/vector over the `Stack`, `Memory`, or `Storage` operations ordered in the way the State Proof needs. + +For the Memory operations, it might look like this: -On that way, we would get something like this for the Memory ops: ```text,ignore -| `key` | `val` | `rw` | `gc` | Note | -|:------:| ------------- | ------- | ---- | ---------------------------------------- | -| `0x40` | `0` | `Write` | | Init | -| `0x40` | `0x80` | `Write` | 0 | Assume written at the beginning of `code`| -| `0x40` | `0x80` | `Read` | 4 | `56 MLOAD` | -| - | | | | | -| `0x80` | `0` | `Write` | | Init | -| `0x80` | `0xdeadbeef` | `Write` | 10 | `63 MSTORE` | -| `0x80` | `0xdeadbeef` | `Read` | 16 | `70 MLOAD` | -| `0x80` | `0x1d97c6efb` | `Write` | 24 | `73 MSTORE` | -| - | | | | | -| `0xa0` | `0` | `Write` | | Init | -| `0xa0` | `0xcafeb0ba` | `Write` | 34 | `83 MSTORE` +| `key` | `val` | `rw` | `gc` | Note | +| :----: | ------------- | ------- | ---- | ----------------------------------------- | +| `0x40` | `0` | `Write` | | Init | +| `0x40` | `0x80` | `Write` | 0 | Assume written at the beginning of `code` | +| `0x40` | `0x80` | `Read` | 4 | `56 MLOAD` | +| - | | | | | +| `0x80` | `0` | `Write` | | Init | +| `0x80` | `0xdeadbeef` | `Write` | 10 | `63 MSTORE` | +| `0x80` | `0xdeadbeef` | `Read` | 16 | `70 MLOAD` | +| `0x80` | `0x1d97c6efb` | `Write` | 24 | `73 MSTORE` | +| - | | | | | +| `0xa0` | `0` | `Write` | | Init | +| `0xa0` | `0xcafeb0ba` | `Write` | 34 | `83 MSTORE` | ``` -Where as you see, we group by `memory_address` and then order by -`global_counter`. - -- Iterate over the `ExecutionTrace` itself over -each `ExecutionStep`'s is formed by and check which Stack/Memory&Storage operations are linked to each step. -This is also automatically done via the -[`Opcode`](crate::evm::opcodes::Opcode) trait defined in this crate. +Here, we group by `memory_address` and then order by `global_counter`. -### Documentation -For extra documentation, check the book with the specs written for the -entire ZK-EVM solution. -See: +- Iterate over the [`GethExecTrace`](eth_types::GethExecTrace) itself, over each [`ExecStep`](crate::circuit_input_builder::ExecStep) formed by, and check which Stack/Memory&Storage operations are linked to each step. This is also automatically done via the [`Opcode`](crate::evm::opcodes::Opcode) trait defined in this crate. 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 09b8089ef1..57fd76d1db 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -121,7 +121,7 @@ impl<'a> CircuitInputStateRef<'a> { } } - /// Push an [`Operation`](crate::operation::Operation) into the + /// Push an [`Operation`] into the /// [`OperationContainer`](crate::operation::OperationContainer) with the /// next [`RWCounter`](crate::operation::RWCounter) and then adds a /// reference to the stored operation ([`OperationRef`]) inside the @@ -196,8 +196,7 @@ impl<'a> CircuitInputStateRef<'a> { self.push_op(step, RW::WRITE, op) } - /// Push an [`Operation`](crate::operation::Operation) with reversible to be - /// true into the + /// Push an [`Operation`] with reversible to be true into the /// [`OperationContainer`](crate::operation::OperationContainer) with the /// next [`RWCounter`](crate::operation::RWCounter) and then adds a /// reference to the stored operation diff --git a/bus-mapping/src/evm/opcodes.rs b/bus-mapping/src/evm/opcodes.rs index 7e2812351a..3be582fc20 100644 --- a/bus-mapping/src/evm/opcodes.rs +++ b/bus-mapping/src/evm/opcodes.rs @@ -115,8 +115,7 @@ pub use crate::precompile::PrecompileCallArgs; /// Generic opcode trait which defines the logic of the /// [`Operation`](crate::operation::Operation) that should be generated for one -/// or multiple [`ExecStep`](crate::circuit_input_builder::ExecStep) depending -/// of the [`OpcodeId`] it contains. +/// or multiple [`ExecStep`] depending of the [`OpcodeId`] it contains. pub trait Opcode: Debug { /// Generate the associated [`MemoryOp`](crate::operation::MemoryOp)s, /// [`StackOp`](crate::operation::StackOp)s, and diff --git a/bus-mapping/src/evm/opcodes/error_oog_exp.rs b/bus-mapping/src/evm/opcodes/error_oog_exp.rs index 23d7d7c76d..c8ae7f4960 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_exp.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_exp.rs @@ -7,7 +7,7 @@ use crate::{ use eth_types::GethExecStep; /// Placeholder structure used to implement [`Opcode`] trait over it -/// corresponding to the [`OogError::Exp`](crate::error::OogError::Exp). +/// corresponding to the [`OogError::Exp`]. #[derive(Clone, Copy, Debug)] pub(crate) struct OOGExp; diff --git a/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs b/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs index 50327115ba..ef1b35c916 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_memory_copy.rs @@ -8,8 +8,7 @@ use crate::{ use eth_types::{evm_types::OpcodeId, GethExecStep, ToAddress}; /// Placeholder structure used to implement [`Opcode`] trait over it -/// corresponding to the -/// [`OogError::MemoryCopy`](crate::error::OogError::MemoryCopy). +/// corresponding to the [`OogError::MemoryCopy`]. #[derive(Clone, Copy, Debug)] pub(crate) struct OOGMemoryCopy; diff --git a/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs b/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs index 285ba59607..4ab139d39b 100644 --- a/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs +++ b/bus-mapping/src/evm/opcodes/error_oog_sload_sstore.rs @@ -8,8 +8,7 @@ use crate::{ use eth_types::{GethExecStep, ToWord}; /// Placeholder structure used to implement [`Opcode`] trait over it -/// corresponding to the -/// [`OogError::SloadSstore`](crate::error::OogError::SloadSstore). +/// corresponding to the [`OogError::SloadSstore`]. #[derive(Clone, Copy, Debug)] pub(crate) struct OOGSloadSstore; diff --git a/bus-mapping/src/exec_trace.rs b/bus-mapping/src/exec_trace.rs index 41e405c1cd..b5d49e0166 100644 --- a/bus-mapping/src/exec_trace.rs +++ b/bus-mapping/src/exec_trace.rs @@ -5,7 +5,7 @@ use std::fmt; #[derive(Clone, Copy, PartialEq, Eq)] /// The target and index of an `Operation` in the context of an -/// `ExecutionTrace`. +/// [`GethExecTrace`](eth_types::GethExecTrace). pub struct OperationRef(pub Target, pub usize); impl fmt::Debug for OperationRef { diff --git a/bus-mapping/src/lib.rs b/bus-mapping/src/lib.rs index f370e4b356..dfa5675791 100644 --- a/bus-mapping/src/lib.rs +++ b/bus-mapping/src/lib.rs @@ -1,217 +1,6 @@ -//! ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/appliedzkp/zkevm-circuits/CI%20checks/main?style=for-the-badge) -//! Bus-Mapping is a crate designed to parse EVM execution traces and manipulate -//! all of the data they provide in order to obtain structured witness inputs -//! for the EVM Proof and the State Proof. -//! -//! ## Introduction -//! At the moment every node on ethereum has to validate every transaction in -//! the ethereum virtual machine. This means that every transaction adds work -//! that everyone needs to do to verify Ethereum’s history. Worse still is that -//! each transaction needs to be verified by every new node. Which means the -//! amount of work a new node needs to do the sync the network is growing -//! constantly. We want to build a proof of validity for the Ethereum blocks to -//! avoid this. -//! -//! This means making a proof of validity for the EVM + state reads / writes + -//! signatures. -//! To simplify we separate our proofs into two components. -//! -//! - State proof: State/memory/stack ops have been performed correctly. This -//! does not check if the correct location has been read/written. We allow our -//! prover to pick any location here and in the EVM proof confirm it is correct. -//! -//! - EVM proof: This checks that the correct opcode is called at the correct -//! time. It checks the validity of these opcodes. It also confirms that for -//! each of these opcodes the state proof performed the correct operation. -//! -//! Only after verifying both proofs are we confident that that Ethereum block -//! is executed correctly. -//! -//! ## Bus Mapping -//! The goal of this crate is to serve as: -//! - A parsing lib for EVM execution traces. -//! - A way to infer some witness data that can only be constructed once we've analyzed the full -//! exec trace. -//! - An easy interface to collect all of the data to witness into the circuits and witness it with -//! few function calls. -//! -//! ## Parsing -//! Provided a JSON file or a JSON as a stream of bytes, which contains an -//! execution trace from an EVM, you can parse it and construct an -//! `ExecutionTrace` instance from it. That will automatically fill all of the -//! bus-mapping instances of each -//! [`GethExecStep`](eth_types::GethExecStep). Then the -//! [`CircuitInputBuilder`](crate::circuit_input_builder::CircuitInputBuilder) -//! will fill in an -//! [`OperationContainer`](crate::operation::container::OperationContainer) -//! with all of the Memory, Stack and Storage ops performed -//! by the provided trace. -//! -//! ```rust -//! use bus_mapping::{Error, mock::BlockData}; -//! use bus_mapping::state_db::{self, StateDB, CodeDB}; -//! use eth_types::{ -//! self, address, Address, Word, Hash, U64, GethExecTrace, GethExecStep, geth_types::GethData, bytecode -//! }; -//! use mock::test_ctx::{TestContext, helpers::*}; -//! use bus_mapping::circuit_input_builder::{Block, CircuitInputBuilder}; -//! -//! let input_trace = r#" -//! [ -//! { -//! "pc": 5, -//! "op": "PUSH1", -//! "gas": 82, -//! "gasCost": 3, -//! "refund": 0, -//! "depth": 1, -//! "stack": [], -//! "memory": [ -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000080" -//! ] -//! }, -//! { -//! "pc": 7, -//! "op": "MLOAD", -//! "gas": 79, -//! "gasCost": 3, -//! "refund": 0, -//! "depth": 1, -//! "stack": [ -//! "40" -//! ], -//! "memory": [ -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000080" -//! ] -//! }, -//! { -//! "pc": 8, -//! "op": "STOP", -//! "gas": 76, -//! "gasCost": 0, -//! "refund": 0, -//! "depth": 1, -//! "stack": [ -//! "80" -//! ], -//! "memory": [ -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000000", -//! "0000000000000000000000000000000000000000000000000000000000000080" -//! ] -//! } -//! ] -//! "#; -//! -//! // We use the [`TestContext`] struct to mock a block. -//! let code = bytecode! { -//! // Write 0x6f to storage slot 0 -//! PUSH1(0x6fu64) -//! PUSH1(0x00u64) -//! SSTORE -//! // Load storage slot 0 -//! PUSH1(0x00u64) -//! SLOAD -//! 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), -//! tx_from_1_to_0, -//! |block, _tx| block.number(0xcafeu64), -//! ) -//! .unwrap() -//! .into(); -//! -//! // Here we update the circuit input with the data from the transaction trace. -//! let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); -//! let builder = builder -//! .handle_block(&block.eth_block, &block.geth_traces) -//! .unwrap(); -//! -//! let geth_steps: Vec = serde_json::from_str(input_trace).unwrap(); -//! let geth_trace = GethExecTrace { -//! return_value: "".to_string(), -//! gas: block.eth_block.transactions[0].gas.as_u64(), -//! invalid: false, -//! failed: false, -//! struct_logs: geth_steps, -//! }; -//! -//! // Get an ordered vector with all of the Stack operations of this trace. -//! let stack_ops = builder.block.container.sorted_stack(); -//! -//! // You can also iterate over the steps of the trace and witness the EVM Proof. -//! builder.block.txs()[0].steps().iter(); -//! ``` -//! -//! Assume we have the following trace: -//! ```text,ignore -//! pc op stack (top -> down) memory -//! -- -------------- ---------------------------------- --------------------------------------- -//! ... -//! 53 JUMPDEST [ , , , ] {40: 80, 80: , a0: } -//! 54 PUSH1 40 [ , , , 40] {40: 80, 80: , a0: } -//! 56 MLOAD [ , , , 80] {40: 80, 80: , a0: } -//! 57 PUSH4 deadbeaf [ , , deadbeef, 80] {40: 80, 80: , a0: } -//! 62 DUP2 [ , 80, deadbeef, 80] {40: 80, 80: , a0: } -//! 63 MSTORE [ , , , 80] {40: 80, 80: deadbeef, a0: } -//! 64 PUSH4 faceb00c [ , , faceb00c, 80] {40: 80, 80: deadbeef, a0: } -//! 69 DUP2 [ , 80, faceb00c, 80] {40: 80, 80: deadbeef, a0: } -//! 70 MLOAD [ , deadbeef, faceb00c, 80] {40: 80, 80: deadbeef, a0: } -//! 71 ADD [ , , 1d97c6efb, 80] {40: 80, 80: deadbeef, a0: } -//! 72 DUP2 [ , 80, 1d97c6efb, 80] {40: 80, 80: deadbeef, a0: } -//! 73 MSTORE [ , , , 80] {40: 80, 80: 1d97c6efb, a0: } -//! 74 PUSH4 cafeb0ba [ , , cafeb0ba, 80] {40: 80, 80: 1d97c6efb, a0: } -//! 79 PUSH1 20 [ , 20, cafeb0ba, 80] {40: 80, 80: 1d97c6efb, a0: } -//! 81 DUP3 [ 80, 20, cafeb0ba, 80] {40: 80, 80: 1d97c6efb, a0: } -//! 82 ADD [ , a0, cafeb0ba, 80] {40: 80, 80: 1d97c6efb, a0: } -//! 83 MSTORE [ , , , 80] {40: 80, 80: 1d97c6efb, a0: cafeb0ba} -//! 84 POP [ , , , ] {40: 80, 80: 1d97c6efb, a0: cafeb0ba} -//! ... -//! ``` -//! -//! Once you have the trace built (following the code found above) you can -//! basically: -//! - Get an iterator/vector over the `Stack`, `Memory` or `Storage` operations ordered on the way -//! the State Proof needs. -//! -//! On that way, we would get something like this for the Memory ops: -//! ```text,ignore -//! | `key` | `val` | `rw` | `gc` | Note | -//! |:------:| ------------- | ------- | ---- | ---------------------------------------- | -//! | `0x40` | `0` | `Write` | | Init | -//! | `0x40` | `0x80` | `Write` | 0 | Assume written at the beginning of `code`| -//! | `0x40` | `0x80` | `Read` | 4 | `56 MLOAD` | -//! | - | | | | | -//! | `0x80` | `0` | `Write` | | Init | -//! | `0x80` | `0xdeadbeef` | `Write` | 10 | `63 MSTORE` | -//! | `0x80` | `0xdeadbeef` | `Read` | 16 | `70 MLOAD` | -//! | `0x80` | `0x1d97c6efb` | `Write` | 24 | `73 MSTORE` | -//! | - | | | | | -//! | `0xa0` | `0` | `Write` | | Init | -//! | `0xa0` | `0xcafeb0ba` | `Write` | 34 | `83 MSTORE` -//! ``` -//! -//! Where as you see, we group by `memory_address` and then order by -//! `global_counter`. -//! -//! - Iterate over the `ExecutionTrace` itself over -//! each `ExecutionStep`'s is formed by and check which Stack/Memory&Storage -//! operations are linked to each step. This is also automatically done via the -//! [`Opcode`](crate::evm::opcodes::Opcode) trait defined in this crate. -//! -//! ## Documentation -//! For extra documentation, check the book with the specs written for the -//! entire ZK-EVM solution. -//! See: - +#![doc = include_str!("../README.md")] +// doc test attr +#![doc(test(attr(deny(warnings))))] #![cfg_attr(docsrs, feature(doc_cfg))] // We want to have UPPERCASE indents sometimes. #![allow(non_snake_case)] diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index 81ec46560b..f1f9fb7d5c 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -476,14 +476,14 @@ impl<'de> Deserialize<'de> for GethExecStep { } /// Helper type built to deal with the weird `result` field added between -/// `GethExecutionTrace`s in `debug_traceBlockByHash` and +/// [`GethExecTrace`]s in `debug_traceBlockByHash` and /// `debug_traceBlockByNumber` Geth JSON-RPC calls. #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] #[doc(hidden)] pub struct ResultGethExecTraces(pub Vec); /// Helper type built to deal with the weird `result` field added between -/// `GethExecutionTrace`s in `debug_traceBlockByHash` and +/// [`GethExecTrace`]s in `debug_traceBlockByHash` and /// `debug_traceBlockByNumber` Geth JSON-RPC calls. #[derive(Clone, Debug, Eq, PartialEq, Deserialize)] #[doc(hidden)] diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index fb7f76db2f..059f896745 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -116,7 +116,7 @@ impl Instrument { } /// Struct which contains a Cost/ColumnType report for a particular EVM -/// `ExecutionStep`. +/// [`ExecStep`](bus_mapping::circuit_input_builder::ExecStep). #[derive(Clone, Debug, Default)] pub struct ExecStateReport { pub state: ExecutionState, From 3bbc757a20d9d9f7759db47c096d5395361bee74 Mon Sep 17 00:00:00 2001 From: Miha Stopar Date: Mon, 26 Feb 2024 16:10:39 +0100 Subject: [PATCH 10/16] Stacktrie getproof (#1779) ### Description - Fix in the stack trie `GetProof` method - Added description for `TestTransactions` ### Issue Link https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/1752 ### Type of change - [x ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update - [ ] Refactor (no updates to logic) ### How Has This Been Tested? `TestTransactions` and `TestGetProof` in `zkevm-circuits/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go`. --- .github/workflows/geth-utils.yml | 2 +- .github/workflows/integration.yml | 14 +- .github/workflows/lints.yml | 12 +- .github/workflows/main-tests.yml | 40 +- .github/workflows/test-features.yml | 12 +- geth-utils/gethutil/mpt/trie/stacktrie.go | 46 +- .../witness/gen_witness_transactions_test.go | 66 +- geth-utils/go.mod | 48 +- geth-utils/go.sum | 975 +----------------- 9 files changed, 144 insertions(+), 1071 deletions(-) diff --git a/.github/workflows/geth-utils.yml b/.github/workflows/geth-utils.yml index 9fc22d6574..2ac5fe0c61 100644 --- a/.github/workflows/geth-utils.yml +++ b/.github/workflows/geth-utils.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.21' - name: Format uses: Jerome1337/gofmt-action@v1.0.5 diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index bdfb6b546f..1c027235f3 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -70,19 +70,9 @@ jobs: with: override: false - name: Setup golang - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: ~1.19 - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml index ad1f9c938d..3f83a0bc5f 100644 --- a/.github/workflows/lints.yml +++ b/.github/workflows/lints.yml @@ -38,16 +38,10 @@ jobs: with: components: rustfmt, clippy override: false - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 + - name: Setup golang + uses: actions/setup-go@v4 with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: lint-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: diff --git a/.github/workflows/main-tests.yml b/.github/workflows/main-tests.yml index 4fb06d1367..2801dc8c9f 100644 --- a/.github/workflows/main-tests.yml +++ b/.github/workflows/main-tests.yml @@ -46,19 +46,9 @@ jobs: with: override: false - name: Setup golang - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: ~1.19 - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: @@ -104,19 +94,9 @@ jobs: with: override: false - name: Setup golang - uses: actions/setup-go@v3 - with: - go-version: ~1.19 - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 + uses: actions/setup-go@v4 with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: @@ -155,16 +135,10 @@ jobs: override: false - name: Add target run: rustup target add x86_64-unknown-linux-gnu - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 + - name: Setup golang + uses: actions/setup-go@v4 with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: diff --git a/.github/workflows/test-features.yml b/.github/workflows/test-features.yml index 8e98681661..f04075e447 100644 --- a/.github/workflows/test-features.yml +++ b/.github/workflows/test-features.yml @@ -36,16 +36,10 @@ jobs: - uses: actions-rs/toolchain@v1 with: override: false - - # Go cache for building geth-utils - - name: Go cache - uses: actions/cache@v3 + - name: Setup golang + uses: actions/setup-go@v4 with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ github.workflow }}-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - + go-version: ~1.21 - name: Cargo cache uses: actions/cache@v3 with: diff --git a/geth-utils/gethutil/mpt/trie/stacktrie.go b/geth-utils/gethutil/mpt/trie/stacktrie.go index c5a04e5926..c5c1849402 100644 --- a/geth-utils/gethutil/mpt/trie/stacktrie.go +++ b/geth-utils/gethutil/mpt/trie/stacktrie.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "main/gethutil/mpt/types" + "slices" "sync" "github.com/ethereum/go-ethereum/common" @@ -230,9 +231,6 @@ func (st *StackTrie) getDiffIndex(key []byte) int { // Helper function to that inserts a (key, value) pair into // the trie. func (st *StackTrie) insert(key, value []byte) { - if key[0] == 1 && key[1] == 0 { - fmt.Println("d") - } switch st.nodeType { case branchNode: /* Branch */ idx := int(key[st.keyOffset]) @@ -659,7 +657,6 @@ func (st *StackTrie) UpdateAndGetProofs(db ethdb.KeyValueReader, list types.Deri func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, error) { k := KeybytesToHex(key) - i := 0 if st.nodeType == emptyNode { return [][]byte{}, nil @@ -678,13 +675,11 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er } var proof [][]byte - var nodes []*StackTrie - c := st isHashed := false - for i < len(k) { + for i := 0; i < len(k); i++ { if c.nodeType == extNode { nodes = append(nodes, c) c = st.children[0] @@ -701,33 +696,19 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er isHashed = true c_rlp, error := db.Get(c.val) if error != nil { - fmt.Println(error) panic(error) } - fmt.Println(c_rlp) proof = append(proof, c_rlp) + branchChild := st.getNodeFromBranchRLP(c_rlp, k[i]) - for i < len(k)-1 { - node := st.getNodeFromBranchRLP(c_rlp, k[i]) - i += 1 - fmt.Println(node) - - if len(node) == 1 && node[0] == 128 { // no child at this position - break - } - - c_rlp, error = db.Get(node) - if error != nil { - fmt.Println(error) - panic(error) - } - fmt.Println(c_rlp) - - proof = append(proof, c_rlp) + // branchChild is of length 1 when there is no child at this position in the branch + // (`branchChild = [128]` in this case), but it is also of length 1 when `c_rlp` is a leaf. + if len(branchChild) == 1 { + // no child at this position - 128 is RLP encoding for nil object + break } - - break + c.val = branchChild } } @@ -740,7 +721,6 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er lNodes := len(nodes) for i := lNodes - 1; i >= 0; i-- { node := nodes[i] - fmt.Println(node) if node.nodeType == leafNode { rlp, error := db.Get(node.val) @@ -760,11 +740,9 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er } } - } - - fmt.Println("----------") - for i := 0; i < len(proof); i++ { - fmt.Println(proof[i]) + // The proof is now reversed (only for non-hashed), + // let's reverse it back to have the leaf at the bottom: + slices.Reverse(proof) } return proof, nil diff --git a/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go b/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go index cd8c3b090d..723234f255 100644 --- a/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go +++ b/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go @@ -15,8 +15,54 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -func TestTransactions(t *testing.T) { - t.Skip("failing test") +/* +TestNonHashedTransactionsStackTrieGetProof inserts 70 transactions into a stacktrie. +For each of the 70 modifications of the trie it asks for a proof - GetProof is called before +and after the modification. The transactions in the trie are not hashed and thus GetProof +does not require to query a database to get the preimages. + +When the first transaction is added, a leaf is added to the trie +(it has index 1 which is used as a key when inserting into a trie, the key is changed by +calling KeybytesToHex(key) in TryUpdate to [0, 1, 16]). + +When the second transaction is added (it has index 2, when inserting into a trie, +it gets changed to [0, 2, 16]), the extension node E is created with nibble 0 (note that +both leaves/transactions have the first nibble 0) and the underlying branch B with children +at positions 1 and 2 (second nibble of the two leaves). + +At this point, the proof for the second transaction is (proofC when index = 2): +[226 16 160 212 52 159 164 192 63 244 122 229 5 208 58 20 16 54 62 169 98 62 238 163 88 174 155 252 118 132 91 148 62 122 23] +[248 81 128 160 32 244 75 78 180 11 251 73 229 254 70 16 254 170 54 254 200 97 231 24 180 34 220 244 153 76 1 194 23 63 64 224 160 46 141 2 37 188 204 110 232 46 31 230 82 226 213 98 71 18 241 37 90 213 167 221 58 33 36 249 248 180 207 235 47 128 128 128 128 128 128 128 128 128 128 128 128 128 128] +[248 200 2 131 4 147 224 131 1 226 65 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 184 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 37 160 163 65 77 46 20 3 175 162 34 86 182 41 225 43 90 92 158 249 153 67 193 148 178 63 8 81 26 169 176 56 242 78 160 21 37 82 209 254 5 113 171 235 198 244 52 17 233 162 51 76 151 85 224 28 101 146 160 197 216 253 237 240 187 19 184] + +Note that the first proof element is an extension node with nibble 0 = 16 - 16 (see +the second byte). The third byte (32 = 160 - 128) denotes the length of the subsequent stream +which represents the hash of the underlying branch. +The second proof element is the underlying branch. The second byte (81) denotes the length +of the subsequent RLP stream. The third byte (128) denotes the nil element at position 0. +Then there are two children at positions 1 (32 244 75...) and 2 (46 141 2...). Note that +these two 32-long value are the hashes of the two leaves/transactions in the branch. +The bytes 128 at the end of the branch RLP represents nil objects at positions 3 - 15. +The last proof element is the second transaction, for example the third nibble (2) +represents the index of the transaction. + +When further 13 transactions are added, the branch B gets populated at positions 3 - 15 +(the position 0 remains nil). + +When the 16-th transaction is added (it has index 16, it gets changed to [1, 0, 16]), +the extension node E is turned into branch B1 which has children at positions 0 and 1. +At position 0 it has a branch B (which used to be the underlying branch of E1) and +at position 1 it has a leaf that represents the 16-transaction. + +At this point, the proof for the second transaction is (proofC when index = 16): +[248 81 160 204 44 112 166 132 56 23 211 247 164 233 113 161 247 117 64 34 142 106 19 106 151 213 163 170 185 19 10 144 231 85 229 160 23 243 146 56 210 93 132 177 170 102 160 150 91 57 192 254 50 122 118 157 138 67 46 2 247 8 89 216 105 197 213 36 128 128 128 128 128 128 128 128 128 128 128 128 128 128 128] +[248 203 16 131 4 147 224 131 1 226 65 148 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 131 1 0 0 184 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 38 160 4 18 182 163 225 56 150 243 120 135 140 57 183 97 55 103 25 62 11 37 106 178 135 7 12 58 133 148 112 183 105 66 160 106 208 180 0 175 152 228 224 143 226 84 16 188 253 79 140 234 80 104 143 32 229 83 105 24 251 62 24 122 66 11 59] + +The first proof element is a branch with children at position 0 (branch B) and 1 (newly added leaf). +The second element is the 16-th transaction. For example, the third byte (16) represents +the transaction index. +*/ +func TestNonHashedTransactionsStackTrieGetProof(t *testing.T) { txs := make([]*types.Transaction, 70) key, _ := crypto.GenerateKey() signer := types.LatestSigner(params.TestChainConfig) @@ -41,9 +87,14 @@ func TestTransactions(t *testing.T) { fmt.Println("===") } -// No update for each step, just final proof. -func TestGetProof(t *testing.T) { - txs := make([]*types.Transaction, 70) +/* +TestHashedTransactionsStackTrieGetProof inserts 2 transactions into a stacktrie, +the trie is then hashed (DeriveSha call). +The proof is asked for one of the two transactions. The transactions in the trie are hashed and thus +GetProof requires to query a database to get the preimages. +*/ +func TestHashedTransactionsStackTrieGetProof(t *testing.T) { + txs := make([]*types.Transaction, 2) key, _ := crypto.GenerateKey() signer := types.LatestSigner(params.TestChainConfig) @@ -51,6 +102,11 @@ func TestGetProof(t *testing.T) { amount := math.BigPow(2, int64(i)) price := big.NewInt(300000) data := make([]byte, 100) + data[3] = 3 + data[4] = 3 + data[5] = 3 + data[6] = 3 + data[7] = 3 tx := types.NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data) signedTx, err := types.SignTx(tx, signer, key) if err != nil { diff --git a/geth-utils/go.mod b/geth-utils/go.mod index a80c801fe2..a417f0750e 100644 --- a/geth-utils/go.mod +++ b/geth-utils/go.mod @@ -1,10 +1,56 @@ module main -go 1.16 +go 1.21 require github.com/ethereum/go-ethereum v1.11.5 require golang.org/x/crypto v0.1.0 +require ( + github.com/DataDog/zstd v1.5.2 // indirect + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/VictoriaMetrics/fastcache v1.6.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.9.1 // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 // indirect + github.com/cockroachdb/redact v1.1.3 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/getsentry/sentry-go v0.18.0 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect + github.com/holiman/uint256 v1.2.0 // indirect + github.com/klauspost/compress v1.15.15 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.39.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect + golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect +) + // Uncomment for debugging // replace github.com/ethereum/go-ethereum => ../../go-ethereum diff --git a/geth-utils/go.sum b/geth-utils/go.sum index 62c668a662..11b360c307 100644 --- a/geth-utils/go.sum +++ b/geth-utils/go.sum @@ -1,327 +1,106 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.21.1/go.mod h1:fBF9PQNqB8scdgpZ3ufzaLntG0AG7C1WjPMsiFOmfHM= -github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= -github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= -github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= -github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= -github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= -github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= -github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/consensys/gnark-crypto v0.9.1-0.20230105202408-1a7a29904a7c/go.mod h1:CkbdF9hbRidRJYMRzmfX8TMOr95I2pYXRHF18MzRrvA= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crate-crypto/go-ipa v0.0.0-20220523130400-f11357ae11c7/go.mod h1:gFnFS95y8HstDP6P9pPwzrxOOC5TRDkwbM+ao15ChAI= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= -github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= -github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= -github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/docker v1.6.2/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20220405120441-9037c2b61cbf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= -github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs= -github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fjl/gencodec v0.0.0-20220412091415-8bb9e558978c/go.mod h1:AzA8Lj6YtixmJWL+wkKoBGsLWy9gFrAzi4g+5bCKwpY= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= -github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= -github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= -github.com/gballet/go-verkle v0.0.0-20220902153445-097bd83b7732/go.mod h1:o/XfIXWi4/GqbQirfRm5uTbXMG5NpqxkxblnbZ+QM9I= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -330,201 +109,59 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= -github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0= -github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= -github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= -github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= -github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/kataras/blocks v0.0.6/go.mod h1:UK+Iwk0Oxpc0GdoJja7sEildotAUKK1LYeYcVF0COWc= -github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= -github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= -github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= -github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= -github.com/kataras/iris/v12 v12.2.0-beta5/go.mod h1:q26aoWJ0Knx/00iPKg5iizDK7oQQSPjbD8np0XDh6dc= -github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= -github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw= github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= -github.com/kataras/neffos v0.0.20/go.mod h1:srdvC/Uo8mgrApWW0AYtiiLgMbyNPf69qPsd2FhE6MQ= -github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= -github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= -github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= -github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -532,808 +169,241 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170224010052-a616ab194758/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= -github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg= -github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= -github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= -github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= -github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= -github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= -github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= -github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= -github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/supranational/blst v0.3.8-0.20220526154634-513d2456b344/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g= -github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk= -github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= -github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= -github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= -github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= -github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= -github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= -github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= -github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= -github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191126055441-b0650ceb63d9/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1343,61 +413,32 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 41e340864ee6c1cb92d63d2de22bc38bdd0deaee Mon Sep 17 00:00:00 2001 From: Chih Cheng Liang Date: Tue, 5 Mar 2024 20:11:05 +0800 Subject: [PATCH 11/16] Merge Transfer and TransferWithGas gadget (#1777) ### Description - Transfer and TransferWithGas gadgets are mostly the same. We can merge them and duplicate them. - There are duplicated logics for creating the receiver account (writing empty hash). We remove the duplicated one. - The update balance gadget is vague, it is hard to understand whether we want to add or subtract balance. We extract a helpful method for that. - deduplicate the transfer_to logic in busmapping. - Fix the receiver creation condition on busmapping. - Fix the order of the sender account read-write in begin tx assignment. Thank @curryrasul for initializing this conversation. ### Issue Link ### Type of change Refactor (no updates to logic) ### Content ### Test ``` cargo test -p zkevm-circuits create cargo test -p zkevm-circuits begin_tx cargo test -p zkevm-circuits end_tx ``` --- .../circuit_input_builder/input_state_ref.rs | 69 +--- bus-mapping/src/evm/opcodes/begin_end_tx.rs | 2 +- bus-mapping/src/evm/opcodes/callop.rs | 1 + bus-mapping/src/evm/opcodes/create.rs | 3 +- .../src/evm_circuit/execution/begin_tx.rs | 43 +-- .../src/evm_circuit/execution/callop.rs | 22 +- .../src/evm_circuit/execution/create.rs | 22 +- .../src/evm_circuit/execution/end_tx.rs | 42 +-- .../src/evm_circuit/util/common_gadget.rs | 307 +++++------------- .../evm_circuit/util/constraint_builder.rs | 21 ++ 10 files changed, 171 insertions(+), 361 deletions(-) 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 57fd76d1db..07c250ead4 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -559,13 +559,13 @@ impl<'a> CircuitInputStateRef<'a> { /// balance by `value`. If `fee` is existing (not None), also need to push 1 /// non-reversible [`AccountOp`] to update `sender` balance by `fee`. #[allow(clippy::too_many_arguments)] - pub fn transfer_with_fee( + pub fn transfer( &mut self, step: &mut ExecStep, sender: Address, receiver: Address, receiver_exists: bool, - must_create: bool, + is_create: bool, value: Word, fee: Option, ) -> Result<(), Error> { @@ -608,82 +608,35 @@ impl<'a> CircuitInputStateRef<'a> { sender_balance_prev, sender_balance ); - // If receiver doesn't exist, create it - if !receiver_exists && (!value.is_zero() || must_create) { + + if !value.is_zero() { self.push_op_reversible( step, AccountOp { - address: receiver, - field: AccountField::CodeHash, - value: CodeDB::empty_code_hash().to_word(), - value_prev: Word::zero(), + address: sender, + field: AccountField::Balance, + value: sender_balance, + value_prev: sender_balance_prev, }, )?; } - if value.is_zero() { - // Skip transfer if value == 0 - return Ok(()); - } - self.push_op_reversible( - step, - AccountOp { - address: sender, - field: AccountField::Balance, - value: sender_balance, - value_prev: sender_balance_prev, - }, - )?; - - let (_found, receiver_account) = self.sdb.get_account(&receiver); - let receiver_balance_prev = receiver_account.balance; - let receiver_balance = receiver_account.balance + value; - self.push_op_reversible( - step, - AccountOp { - address: receiver, - field: AccountField::Balance, - value: receiver_balance, - value_prev: receiver_balance_prev, - }, - )?; + self.transfer_to(step, receiver, receiver_exists, is_create, value, true)?; Ok(()) } - /// Same functionality with `transfer_with_fee` but with `fee` set zero. - pub fn transfer( - &mut self, - step: &mut ExecStep, - sender: Address, - receiver: Address, - receiver_exists: bool, - must_create: bool, - value: Word, - ) -> Result<(), Error> { - self.transfer_with_fee( - step, - sender, - receiver, - receiver_exists, - must_create, - value, - None, - ) - } - /// Transfer to an address. Create an account if it is not existed before. pub fn transfer_to( &mut self, step: &mut ExecStep, receiver: Address, receiver_exists: bool, - must_create: bool, + is_create: bool, value: Word, reversible: bool, ) -> Result<(), Error> { - // If receiver doesn't exist, create it - if (!receiver_exists && !value.is_zero()) || must_create { + if !receiver_exists && (!value.is_zero() || is_create) { self.account_write( step, receiver, diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index 1b6c79a5c8..f9ee29d714 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -122,7 +122,7 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result Opcode for CallOpcode { callee_exists, false, call.value, + None, )?; } diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index 51b399b099..c1a0bbf940 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -217,8 +217,9 @@ impl Opcode for Create { caller.address, callee.address, callee_exists, - !callee_exists, + true, callee.value, + None, )?; // EIP 161, increase callee's nonce diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 80de4c1973..817b7dbff7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -5,7 +5,7 @@ use crate::{ step::ExecutionState, util::{ and, - common_gadget::TransferWithGasFeeGadget, + common_gadget::TransferGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::{Delta, To}, @@ -14,7 +14,7 @@ use crate::{ math_gadget::{ ContractCreateGadget, IsEqualWordGadget, IsZeroWordGadget, RangeCheckGadget, }, - not, or, + not, tx::{BeginTxHelperGadget, TxDataGadget}, AccountAddress, CachedRegion, Cell, StepRws, }, @@ -41,7 +41,7 @@ pub(crate) struct BeginTxGadget { call_callee_address: AccountAddress, reversion_info: ReversionInfo, sufficient_gas_left: RangeCheckGadget, - transfer_with_gas_fee: TransferWithGasFeeGadget, + transfer_with_gas_fee: TransferGadget, code_hash: WordLoHiCell, is_empty_code_hash: IsEqualWordGadget>, WordLoHi>>, caller_nonce_hash_bytes: Word32Cell, @@ -170,17 +170,16 @@ impl ExecutionGadget for BeginTxGadget { AccountFieldTag::CodeHash, code_hash.to_word(), ); - // Transfer value from caller to callee, creating account if necessary. - let transfer_with_gas_fee = TransferWithGasFeeGadget::construct( + let transfer_with_gas_fee = TransferGadget::construct( cb, tx.caller_address.to_word(), tx.callee_address.to_word(), not::expr(callee_not_exists.expr()), - or::expr([tx.is_create.expr(), callee_not_exists.expr()]), + tx.is_create.expr(), tx.value.clone(), - tx.mul_gas_fee_by_gas.product().clone(), &mut reversion_info, + Some(tx.mul_gas_fee_by_gas.product().clone()), ); let caller_nonce_hash_bytes = cb.query_word32(); @@ -467,18 +466,15 @@ impl ExecutionGadget for BeginTxGadget { } let callee_exists = is_precompiled(&tx.to_or_contract_addr()) || !callee_code_hash.is_zero(); - let caller_balance_sub_fee_pair = rws.next().account_balance_pair(); - let must_create = tx.is_create(); - if !callee_exists && (!tx.value.is_zero() || must_create) { - callee_code_hash = rws.next().account_codehash_pair().1; - } - let mut caller_balance_sub_value_pair = (zero, zero); - let mut callee_balance_pair = (zero, zero); - if !tx.value.is_zero() { - caller_balance_sub_value_pair = rws.next().account_balance_pair(); - callee_balance_pair = rws.next().account_balance_pair(); - }; - + self.transfer_with_gas_fee.assign( + region, + offset, + &mut rws, + callee_exists, + tx.value, + tx.is_create(), + Some(gas_fee), + )?; self.begin_tx.assign(region, offset, tx)?; self.tx.assign(region, offset, tx)?; @@ -502,15 +498,6 @@ impl ExecutionGadget for BeginTxGadget { )?; self.sufficient_gas_left .assign(region, offset, F::from(tx.gas() - step.gas_cost))?; - self.transfer_with_gas_fee.assign( - region, - offset, - caller_balance_sub_fee_pair, - caller_balance_sub_value_pair, - callee_balance_pair, - tx.value, - gas_fee, - )?; self.code_hash .assign_u256(region, offset, callee_code_hash)?; self.is_empty_code_hash.assign_u256( diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 761e54d50f..571a33dc5b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -60,7 +60,7 @@ pub(crate) struct CallOpGadget { is_warm: Cell, is_warm_prev: Cell, callee_reversion_info: ReversionInfo, - transfer: TransferGadget, + transfer: TransferGadget, // current handling Call* opcode's caller balance caller_balance: WordLoHi>, // check if insufficient balance case @@ -242,9 +242,10 @@ impl ExecutionGadget for CallOpGadget { caller_address.to_word(), callee_address.to_word(), not::expr(call_gadget.callee_not_exists.expr()), - 0.expr(), + false.expr(), call_gadget.value.clone(), &mut callee_reversion_info, + None, ) }); // rwc_delta = 8 + is_delegatecall * 2 + call_gadget.rw_delta() + @@ -866,20 +867,9 @@ impl ExecutionGadget for CallOpGadget { depth.low_u64() < 1025 && (!(is_call || is_callcode) || caller_balance >= value); // conditionally assign - if is_call && is_precheck_ok && !value.is_zero() { - if !callee_exists { - rws.next().account_codehash_pair(); // callee hash - } - - let caller_balance_pair = rws.next().account_balance_pair(); - let callee_balance_pair = rws.next().account_balance_pair(); - self.transfer.assign( - region, - offset, - caller_balance_pair, - callee_balance_pair, - value, - )?; + if is_call && is_precheck_ok { + self.transfer + .assign(region, offset, &mut rws, callee_exists, value, false, None)?; } self.opcode diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index 9b560a16ce..c999a73e40 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -63,7 +63,7 @@ pub(crate) struct CreateGadget, prev_code_hash: WordLoHiCell, prev_code_hash_is_zero: IsZeroWordGadget>>, - transfer: TransferGadget, + transfer: TransferGadget, create: ContractCreateGadget, init_code: MemoryAddressGadget, @@ -333,10 +333,11 @@ impl ExecutionGadget< cb, create.caller_address(), contract_addr.to_word(), - 0.expr(), - 1.expr(), + false.expr(), + true.expr(), value.clone(), &mut callee_reversion_info, + None, ); // EIP 161, the nonce of a newly created contract is 1 @@ -638,21 +639,14 @@ impl ExecutionGadget< let code_hash = if is_precheck_ok { if !is_address_collision { - // transfer - if callee_prev_code_hash.is_zero() { - rws.next(); // codehash update - } - let [caller_balance_pair, callee_balance_pair] = if !value.is_zero() { - [(); 2].map(|_| rws.next().account_balance_pair()) - } else { - [(0.into(), 0.into()), (0.into(), 0.into())] - }; self.transfer.assign( region, offset, - caller_balance_pair, - callee_balance_pair, + &mut rws, + !callee_prev_code_hash.is_zero(), value, + true, + None, )?; } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 5f650f693a..27ca5f119e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -11,7 +11,7 @@ use crate::{ MulWordByU64Gadget, }, tx::EndTxHelperGadget, - CachedRegion, Cell, + CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, @@ -75,10 +75,9 @@ impl ExecutionGadget for EndTxGadget { tx_gas_price.clone(), effective_refund.min() + cb.curr.state.gas_left.expr(), ); - let gas_fee_refund = UpdateBalanceGadget::construct( - cb, + let gas_fee_refund = cb.increase_balance( tx_caller_address.to_word(), - vec![mul_gas_price_by_refund.product().clone()], + mul_gas_price_by_refund.product().clone(), None, ); @@ -111,7 +110,6 @@ impl ExecutionGadget for EndTxGadget { false.expr(), mul_effective_tip_by_gas_used.product().clone(), None, - true, ); let end_tx = EndTxHelperGadget::construct( @@ -152,9 +150,11 @@ impl ExecutionGadget for EndTxGadget { step: &ExecStep, ) -> Result<(), Error> { let gas_used = tx.gas() - step.gas_left; - let (refund, _) = block.get_rws(step, 2).tx_refund_value_pair(); - let (caller_balance, caller_balance_prev) = block.get_rws(step, 3).account_balance_pair(); - let (coinbase_code_hash_prev, _) = block.get_rws(step, 4).account_codehash_pair(); + let mut rws = StepRws::new(block, step); + rws.offset_add(2); + let (refund, _) = rws.next().tx_refund_value_pair(); + let (caller_balance, caller_balance_prev) = rws.next().account_balance_pair(); + let (coinbase_code_hash_prev, _) = rws.next().account_codehash_pair(); self.tx_id .assign(region, offset, Value::known(F::from(tx.id)))?; @@ -208,24 +208,14 @@ impl ExecutionGadget for EndTxGadget { .assign_u256(region, offset, coinbase_code_hash_prev)?; self.coinbase_code_hash_is_zero .assign_u256(region, offset, coinbase_code_hash_prev)?; - if !coinbase_reward.is_zero() { - let coinbase_balance_pair = block - .get_rws( - step, - if coinbase_code_hash_prev.is_zero() { - 6 - } else { - 5 - }, - ) - .account_balance_pair(); - self.coinbase_reward.assign( - region, - offset, - coinbase_balance_pair, - effective_tip * gas_used, - )?; - } + self.coinbase_reward.assign( + region, + offset, + &mut rws, + !coinbase_code_hash_prev.is_zero(), + coinbase_reward, + false, + )?; self.is_persistent.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index 04ee168d49..ebf88f135f 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -3,7 +3,7 @@ use super::{ from_bytes, math_gadget::{IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, LtGadget}, memory_gadget::{CommonMemoryAddressGadget, MemoryExpansionGadget}, - AccountAddress, CachedRegion, + AccountAddress, CachedRegion, StepRws, }; use crate::{ evm_circuit::{ @@ -17,7 +17,7 @@ use crate::{ Transition::{Delta, Same, To}, }, math_gadget::{AddWordsGadget, RangeCheckGadget}, - not, or, Cell, + not, Cell, }, }, table::{AccountFieldTag, CallContextFieldTag}, @@ -31,7 +31,7 @@ use bus_mapping::state_db::CodeDB; use eth_types::{ evm_types::GasCost, Field, OpsIdentity, ToAddress, ToLittleEndian, ToScalar, ToWord, U256, }; -use gadgets::util::{select, sum}; +use gadgets::util::{or, select, sum}; use halo2_proofs::{ circuit::Value, plonk::{Error, Expression}, @@ -309,7 +309,7 @@ impl pub(crate) fn construct( cb: &mut EVMConstraintBuilder, address: WordLoHi>, - updates: Vec>, + updates: &[Word32Cell], reversion_info: Option<&mut ReversionInfo>, ) -> Self { debug_assert!(updates.len() == N_ADDENDS - 1); @@ -368,10 +368,10 @@ impl #[derive(Clone, Debug)] pub(crate) struct TransferToGadget { - receiver: UpdateBalanceGadget, + receiver_balance: UpdateBalanceGadget, receiver_exists: Expression, - must_create: Expression, - pub(crate) value_is_zero: IsZeroWordGadget>, + is_create: Expression, + value_is_zero: IsZeroWordGadget>, } impl TransferToGadget { @@ -380,51 +380,18 @@ impl TransferToGadget { cb: &mut EVMConstraintBuilder, receiver_address: WordLoHi>, receiver_exists: Expression, - must_create: Expression, + is_create: Expression, value: Word32Cell, mut reversion_info: Option<&mut ReversionInfo>, - account_write: bool, ) -> Self { let value_is_zero = cb.is_zero_word(&value); - if account_write { - Self::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - reversion_info.as_deref_mut(), - ); - } - let receiver = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - receiver_address, - vec![value.clone()], - reversion_info, - ) - }); - Self { - receiver, - receiver_exists, - must_create, - value_is_zero, - } - } - - pub(crate) fn create_account( - cb: &mut EVMConstraintBuilder, - receiver_address: WordLoHi>, - receiver_exists: Expression, - must_create: Expression, - value_is_zero: Expression, - reversion_info: Option<&mut ReversionInfo>, - ) { + // Create account + // See https://github.com/ethereum/go-ethereum/pull/28666 for the context of this check. cb.condition( and::expr([ not::expr(receiver_exists.expr()), - or::expr([not::expr(value_is_zero.expr()), must_create]), + or::expr([not::expr(value_is_zero.expr()), is_create.expr()]), ]), |cb| { cb.account_write( @@ -432,23 +399,46 @@ impl TransferToGadget { AccountFieldTag::CodeHash, cb.empty_code_hash(), WordLoHi::zero(), - reversion_info, + reversion_info.as_deref_mut(), ); }, ); + + let receiver_balance = cb.condition(not::expr(value_is_zero.expr()), |cb| { + cb.increase_balance(receiver_address, value.clone(), reversion_info) + }); + + Self { + receiver_balance, + receiver_exists, + is_create, + value_is_zero, + } } pub(crate) fn assign( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (receiver_balance, prev_receiver_balance): (U256, U256), + rws: &mut StepRws, + receiver_exists: bool, value: U256, + is_create: bool, ) -> Result<(), Error> { - self.receiver.assign( + if !receiver_exists && (!value.is_zero() || is_create) { + // receiver's code_hash + rws.next().account_codehash_pair(); + } + + let (receiver_balance, receiver_balance_prev) = if !value.is_zero() { + rws.next().account_balance_pair() + } else { + (0.into(), 0.into()) + }; + self.receiver_balance.assign( region, offset, - prev_receiver_balance, + receiver_balance_prev, vec![value], receiver_balance, )?; @@ -460,70 +450,58 @@ impl TransferToGadget { pub(crate) fn rw_delta(&self) -> Expression { // +1 Write Account (receiver) CodeHash (account creation via code_hash update) and::expr([ - not::expr(self.receiver_exists.expr()), - or::expr([not::expr(self.value_is_zero.expr()), self.must_create.clone()]), - ]) + + not::expr(self.receiver_exists.expr()), + or::expr([not::expr(self.value_is_zero.expr()), self.is_create.expr()]) + ])+ // +1 Write Account (receiver) Balance not::expr(self.value_is_zero.expr()) } } -// TODO: Merge with TransferGadget -/// The TransferWithGasFeeGadget handles an irreversible gas fee subtraction to -/// the sender and a transfer of value from sender to receiver. The value -/// transfer is only performed if the value is not zero. If the transfer is -/// performed and the receiver account doesn't exist, it will be created by -/// setting it's code_hash = EMPTY_HASH. The receiver account is also created -/// unconditionally if must_create is true. This gadget is used in BeginTx. +/// The [`TransferGadget`] handles +/// - (optional) an irreversible gas fee subtraction to the sender, and +/// - a transfer of value from sender to receiver. +/// +/// The value transfer is only performed if the value is not zero. +/// It also create the receiver account when the conditions in [`TransferToGadget`] is met. +/// This gadget is used in BeginTx, Call ops, and Create. #[derive(Clone, Debug)] -pub(crate) struct TransferWithGasFeeGadget { - sender_sub_fee: UpdateBalanceGadget, +pub(crate) struct TransferGadget { + sender_sub_fee: Option>, sender_sub_value: UpdateBalanceGadget, receiver: TransferToGadget, pub(crate) value_is_zero: IsZeroWordGadget>, } -impl TransferWithGasFeeGadget { +impl TransferGadget { #[allow(clippy::too_many_arguments)] pub(crate) fn construct( cb: &mut EVMConstraintBuilder, sender_address: WordLoHi>, receiver_address: WordLoHi>, receiver_exists: Expression, - must_create: Expression, + is_create: Expression, value: Word32Cell, - gas_fee: Word32Cell, reversion_info: &mut ReversionInfo, + gas_fee: Option>, ) -> Self { - let sender_sub_fee = - UpdateBalanceGadget::construct(cb, sender_address.to_word(), vec![gas_fee], None); + let sender_sub_fee = if WITH_FEE { + Some(cb.decrease_balance(sender_address.to_word(), gas_fee.expect("fee exists"), None)) + } else { + None + }; let value_is_zero = cb.is_zero_word(&value); - // If receiver doesn't exist, create it - TransferToGadget::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - Some(reversion_info), - ); // Skip transfer if value == 0 let sender_sub_value = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - sender_address, - vec![value.clone()], - Some(reversion_info), - ) + cb.decrease_balance(sender_address, value.clone(), Some(reversion_info)) }); let receiver = TransferToGadget::construct( cb, receiver_address, receiver_exists, - must_create, + is_create, value, Some(reversion_info), - false, ); Self { @@ -536,27 +514,19 @@ impl TransferWithGasFeeGadget { pub(crate) fn rw_delta(&self) -> Expression { // +1 Write Account (sender) Balance (Not Reversible tx fee) - 1.expr() + - // +1 Write Account (receiver) CodeHash (account creation via code_hash update) - and::expr([not::expr(self.receiver.receiver_exists.expr()), or::expr([ - not::expr(self.value_is_zero.expr()), - self.receiver.must_create.clone()] - )]) * 1.expr() + + WITH_FEE.expr() + // +1 Write Account (sender) Balance - // +1 Write Account (receiver) Balance - not::expr(self.value_is_zero.expr()) * 2.expr() + not::expr(self.value_is_zero.expr()) + + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) + self.receiver.rw_delta() } pub(crate) fn reversible_w_delta(&self) -> Expression { // NOTE: Write Account (sender) Balance (Not Reversible tx fee) - // +1 Write Account (receiver) CodeHash (account creation via code_hash update) - and::expr([not::expr(self.receiver.receiver_exists.expr()), or::expr([ - not::expr(self.value_is_zero.expr()), - self.receiver.must_create.clone()] - )]) + // +1 Write Account (sender) Balance - // +1 Write Account (receiver) Balance - not::expr(self.value_is_zero.expr()) * 2.expr() + not::expr(self.value_is_zero.expr()) + + // +1 Write Account (receiver) CodeHash (account creation via code_hash update) + self.receiver.rw_delta() } #[allow(clippy::too_many_arguments)] @@ -564,137 +534,40 @@ impl TransferWithGasFeeGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - (sender_balance_sub_fee, prev_sender_balance_sub_fee): (U256, U256), - (sender_balance_sub_value, prev_sender_balance_sub_value): (U256, U256), - (receiver_balance, prev_receiver_balance): (U256, U256), + rws: &mut StepRws, + receiver_exists: bool, value: U256, - gas_fee: U256, + is_create: bool, + gas_fee: Option, ) -> Result<(), Error> { - self.sender_sub_fee.assign( - region, - offset, - prev_sender_balance_sub_fee, - vec![gas_fee], - sender_balance_sub_fee, - )?; - self.sender_sub_value.assign( - region, - offset, - prev_sender_balance_sub_value, - vec![value], - sender_balance_sub_value, - )?; - self.receiver.assign( - region, - offset, - (receiver_balance, prev_receiver_balance), - value, - )?; - self.value_is_zero - .assign_value(region, offset, Value::known(WordLoHi::from(value)))?; - Ok(()) - } -} - -/// The TransferGadget handles a transfer of value from sender to receiver. The -/// transfer is only performed if the value is not zero. If the transfer is -/// performed and the receiver account doesn't exist, it will be created by -/// setting it's code_hash = EMPTY_HASH. This gadget is used in callop. -#[derive(Clone, Debug)] -pub(crate) struct TransferGadget { - sender: UpdateBalanceGadget, - receiver: TransferToGadget, - pub(crate) value_is_zero: IsZeroWordGadget>, -} - -impl TransferGadget { - pub(crate) fn construct( - cb: &mut EVMConstraintBuilder, - sender_address: WordLoHi>, - receiver_address: WordLoHi>, - receiver_exists: Expression, - must_create: Expression, - // _prev_code_hash: WordLoHi>, - value: Word32Cell, - reversion_info: &mut ReversionInfo, - ) -> Self { - let value_is_zero = cb.is_zero_word(&value); - // If receiver doesn't exist, create it - TransferToGadget::create_account( - cb, - receiver_address.clone(), - receiver_exists.clone(), - must_create.clone(), - value_is_zero.expr(), - Some(reversion_info), - ); - // Skip transfer if value == 0 - let sender = cb.condition(not::expr(value_is_zero.expr()), |cb| { - UpdateBalanceGadget::construct( - cb, - sender_address, - vec![value.clone()], - Some(reversion_info), - ) - }); - let receiver = TransferToGadget::construct( - cb, - receiver_address, - receiver_exists, - must_create, - value, - Some(reversion_info), - false, - ); - - Self { - sender, - receiver, - value_is_zero, + if WITH_FEE { + let sender_balance_sub_fee = rws.next().account_balance_pair(); + self.sender_sub_fee.as_ref().expect("Exists").assign( + region, + offset, + sender_balance_sub_fee.1, + vec![gas_fee.expect("exists")], + sender_balance_sub_fee.0, + )?; } - } - - 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.receiver_exists.clone()), - self.receiver.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>, - offset: usize, - (sender_balance, sender_balance_prev): (U256, U256), - (receiver_balance, receiver_balance_prev): (U256, U256), - value: U256, - ) -> Result<(), Error> { - self.sender.assign( + let sender_balance_sub_value = if !value.is_zero() { + rws.next().account_balance_pair() + } else { + (0.into(), 0.into()) + }; + self.sender_sub_value.assign( region, offset, - sender_balance_prev, + sender_balance_sub_value.1, vec![value], - sender_balance, - )?; - self.receiver.assign( - region, - offset, - (receiver_balance, receiver_balance_prev), - value, + sender_balance_sub_value.0, )?; + self.receiver + .assign(region, offset, rws, receiver_exists, value, is_create)?; self.value_is_zero .assign_value(region, offset, Value::known(WordLoHi::from(value)))?; Ok(()) } - - pub(crate) fn rw_delta(&self) -> Expression { - // +1 Write Account (sender) Balance - not::expr(self.value_is_zero.expr()) + self.receiver.rw_delta() - } } #[derive(Clone, Debug)] diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index c7fce109ab..5e20bff23f 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -1,4 +1,5 @@ use super::{ + common_gadget::UpdateBalanceGadget, math_gadget::{ ConstantDivisionGadget, IsEqualGadget, IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, LtGadget, LtWordGadget, MinMaxGadget, @@ -639,6 +640,26 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ConstantDivisionGadget::construct(self, numerator, denominator) } + // Common Gadget + + pub(crate) fn increase_balance( + &mut self, + address: WordLoHi>, + value: Word32Cell, + reversion_info: Option<&mut ReversionInfo>, + ) -> UpdateBalanceGadget { + UpdateBalanceGadget::construct(self, address, &[value], reversion_info) + } + + pub(crate) fn decrease_balance( + &mut self, + address: WordLoHi>, + value: Word32Cell, + reversion_info: Option<&mut ReversionInfo>, + ) -> UpdateBalanceGadget { + UpdateBalanceGadget::construct(self, address, &[value], reversion_info) + } + // Fixed pub(crate) fn range_lookup(&mut self, value: Expression, range: u64) { From b82ea410c10aede551dc9cae52545f2cc7fb308d Mon Sep 17 00:00:00 2001 From: Kimi Wu Date: Thu, 7 Mar 2024 18:39:42 +0800 Subject: [PATCH 12/16] Feat/#1752 fix witness generation - adding testing (#1784) ### Description closed #1752 - Adding different numbers of txs to test different trie status (e.g. one leaf only, one ext. , one branch and one leaf ...etc) - Fixing `GetProof` (if input is an ext node, it always assign first child of the root ext. node, `st.children[0]`) - Fixing `getNodeFromBranchRLP` (if input is an leaf, it could throw an exception) - Refactoring `getNodeFromBranchRLP` ### Issue Link #1752 ### Type of change - [x] Bug fix (non-breaking change which fixes an issue) --- geth-utils/gethutil/mpt/trie/stacktrie.go | 143 +++++++++------ .../witness/gen_witness_transactions_test.go | 172 +++++++++++++----- 2 files changed, 216 insertions(+), 99 deletions(-) diff --git a/geth-utils/gethutil/mpt/trie/stacktrie.go b/geth-utils/gethutil/mpt/trie/stacktrie.go index c5c1849402..0b214071f3 100644 --- a/geth-utils/gethutil/mpt/trie/stacktrie.go +++ b/geth-utils/gethutil/mpt/trie/stacktrie.go @@ -243,16 +243,17 @@ func (st *StackTrie) insert(key, value []byte) { break } } + // Add new child if st.children[idx] == nil { - st.children[idx] = stackTrieFromPool(st.db) - st.children[idx].keyOffset = st.keyOffset + 1 + st.children[idx] = newLeaf(st.keyOffset+1, key, value, st.db) + } else { + st.children[idx].insert(key, value) } - st.children[idx].insert(key, value) + case extNode: /* Ext */ // Compare both key chunks and see where they differ diffidx := st.getDiffIndex(key) - // Check if chunks are identical. If so, recurse into // the child node. Otherwise, the key has to be split // into 1) an optional common prefix, 2) the fullnode @@ -551,50 +552,70 @@ func (st *StackTrie) Commit() (common.Hash, error) { return common.BytesToHash(st.val), nil } -func (st *StackTrie) getNodeFromBranchRLP(branch []byte, ind byte) []byte { - start := 2 // when branch[0] == 248 - if branch[0] == 249 { - start = 3 - } - - i := 0 - insideInd := -1 - cInd := byte(0) - for { - if start+i == len(branch)-1 { // -1 because of the last 128 (branch value) - return []byte{0} - } - b := branch[start+i] - if insideInd == -1 && b == 128 { - if cInd == ind { +const RLP_SHORT_STR_FLAG = 128 +const RLP_SHORT_LIST_FLAG = 192 +const RLP_LONG_LIST_FLAG = 248 +const LEN_OF_HASH = 32 + +// Note: +// In RLP encoding, if the value is between [0x80, 0xb7] ([128, 183]), +// it means following data is a short string (0 - 55bytes). +// Which implies if the value is 128, it's an empty string. +func (st *StackTrie) getNodeFromBranchRLP(branch []byte, idx int) []byte { + + start := int(branch[0]) + start_idx := 0 + if start >= RLP_SHORT_LIST_FLAG && start < RLP_LONG_LIST_FLAG { + // In RLP encoding, length in the range of [192 248] is a short list. + // In stack trie, it usually means an extension node and the first byte is nibble + // and that's why we start from 2 + start_idx = 2 + } else if start >= RLP_LONG_LIST_FLAG { + // In RLP encoding, length in the range of [248 ~ ] is a long list. + // The RLP byte minus 248 (branch[0] - 248) is the length in bytes of the length of the payload + // and the payload is right after the length. + // That's why we add 2 here + // e.g. [248 81 128 160 ...] + // `81` is the length of the payload and payload starts from `128` + start_idx = start - RLP_LONG_LIST_FLAG + 2 + } + + // If 1st node is neither 128(empty node) nor 160, it should be a leaf + b := int(branch[start_idx]) + if b != RLP_SHORT_STR_FLAG && b != (RLP_SHORT_STR_FLAG+LEN_OF_HASH) { + return []byte{0} + } + + current_idx := 0 + for i := start_idx; i < len(branch); i++ { + b = int(branch[i]) + switch b { + case RLP_SHORT_STR_FLAG: // 128 + // if the current index is we're looking for, return an empty node directly + if current_idx == idx { return []byte{128} - } else { - cInd += 1 } - } else if insideInd == -1 && b != 128 { - if b == 160 { - if cInd == ind { - return branch[start+i+1 : start+i+1+32] - } - insideInd = 32 - } else { - // non-hashed node - if cInd == ind { - return branch[start+i+1 : start+i+1+int(b)-192] - } - insideInd = int(b) - 192 + current_idx++ + case RLP_SHORT_STR_FLAG + LEN_OF_HASH: // 160 + if current_idx == idx { + return branch[i+1 : i+1+LEN_OF_HASH] } - cInd += 1 - } else { - if insideInd == 1 { - insideInd = -1 - } else { - insideInd-- + // jump to next encoded element + i += LEN_OF_HASH + current_idx++ + default: + if b >= 192 && b < 248 { + length := b - 192 + if current_idx == idx { + return branch[i+1 : i+1+length] + } + i += length + current_idx++ } } - - i++ } + + return []byte{0} } type StackProof struct { @@ -602,6 +623,14 @@ type StackProof struct { proofC [][]byte } +func (sp *StackProof) GetProofS() [][]byte { + return sp.proofS +} + +func (sp *StackProof) GetProofC() [][]byte { + return sp.proofC +} + func (st *StackTrie) UpdateAndGetProof(db ethdb.KeyValueReader, indexBuf, value []byte) (StackProof, error) { proofS, err := st.GetProof(db, indexBuf) if err != nil { @@ -618,6 +647,8 @@ func (st *StackTrie) UpdateAndGetProof(db ethdb.KeyValueReader, indexBuf, value return StackProof{proofS, proofC}, nil } +// We refer to the link below for this function. +// https://github.com/ethereum/go-ethereum/blob/00905f7dc406cfb67f64cd74113777044fb886d8/core/types/hashing.go#L105-L134 func (st *StackTrie) UpdateAndGetProofs(db ethdb.KeyValueReader, list types.DerivableList) ([]StackProof, error) { valueBuf := types.EncodeBufferPool.Get().(*bytes.Buffer) defer types.EncodeBufferPool.Put(valueBuf) @@ -631,25 +662,33 @@ func (st *StackTrie) UpdateAndGetProofs(db ethdb.KeyValueReader, list types.Deri for i := 1; i < list.Len() && i <= 0x7f; i++ { indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) value := types.EncodeForDerive(list, i, valueBuf) - proof, err := st.UpdateAndGetProof(db, indexBuf, value) if err != nil { return nil, err } - proofs = append(proofs, proof) } + + // special case when index is 0 + // rlp.AppendUint64() encodes index 0 to [128] if list.Len() > 0 { indexBuf = rlp.AppendUint64(indexBuf[:0], 0) value := types.EncodeForDerive(list, 0, valueBuf) - // TODO: get proof - st.Update(indexBuf, value) + proof, err := st.UpdateAndGetProof(db, indexBuf, value) + if err != nil { + return nil, err + } + proofs = append(proofs, proof) } + for i := 0x80; i < list.Len(); i++ { indexBuf = rlp.AppendUint64(indexBuf[:0], uint64(i)) value := types.EncodeForDerive(list, i, valueBuf) - // TODO: get proof - st.Update(indexBuf, value) + proof, err := st.UpdateAndGetProof(db, indexBuf, value) + if err != nil { + return nil, err + } + proofs = append(proofs, proof) } return proofs, nil @@ -657,7 +696,6 @@ func (st *StackTrie) UpdateAndGetProofs(db ethdb.KeyValueReader, list types.Deri func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, error) { k := KeybytesToHex(key) - if st.nodeType == emptyNode { return [][]byte{}, nil } @@ -682,7 +720,8 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er for i := 0; i < len(k); i++ { if c.nodeType == extNode { nodes = append(nodes, c) - c = st.children[0] + c = c.children[0] + } else if c.nodeType == branchNode { nodes = append(nodes, c) c = c.children[k[i]] @@ -700,11 +739,11 @@ func (st *StackTrie) GetProof(db ethdb.KeyValueReader, key []byte) ([][]byte, er } proof = append(proof, c_rlp) - branchChild := st.getNodeFromBranchRLP(c_rlp, k[i]) + branchChild := st.getNodeFromBranchRLP(c_rlp, int(k[i])) // branchChild is of length 1 when there is no child at this position in the branch // (`branchChild = [128]` in this case), but it is also of length 1 when `c_rlp` is a leaf. - if len(branchChild) == 1 { + if len(branchChild) == 1 && (branchChild[0] == 128 || branchChild[0] == 0) { // no child at this position - 128 is RLP encoding for nil object break } diff --git a/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go b/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go index 723234f255..962b9461b3 100644 --- a/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go +++ b/geth-utils/gethutil/mpt/witness/gen_witness_transactions_test.go @@ -1,6 +1,7 @@ package witness import ( + "bytes" "fmt" "main/gethutil/mpt/trie" "main/gethutil/mpt/types" @@ -8,16 +9,40 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) +func makeTransactions(n int) []*types.Transaction { + txs := make([]*types.Transaction, n) + key, _ := crypto.GenerateKey() + signer := types.LatestSigner(params.TestChainConfig) + + for i := range txs { + amount := big.NewInt(int64(i)*10 ^ 9) + gas_price := big.NewInt(300_000) + data := make([]byte, 100) + // randomly assigned for debugging + data[3] = 3 + data[4] = 3 + data[5] = 3 + data[6] = 3 + data[7] = 3 + tx := types.NewTransaction(uint64(i), common.Address{}, amount, 10*10^6, gas_price, data) + signedTx, err := types.SignTx(tx, signer, key) + if err != nil { + panic(err) + } + txs[i] = signedTx + } + return txs +} + /* -TestNonHashedTransactionsStackTrieGetProof inserts 70 transactions into a stacktrie. -For each of the 70 modifications of the trie it asks for a proof - GetProof is called before +transactionsStackTrieInsertionTemplate inserts n transactions into a stacktrie. +For each of the n modifications of the trie it asks for a proof - GetProof is called before and after the modification. The transactions in the trie are not hashed and thus GetProof does not require to query a database to get the preimages. @@ -62,59 +87,84 @@ The first proof element is a branch with children at position 0 (branch B) and 1 The second element is the 16-th transaction. For example, the third byte (16) represents the transaction index. */ -func TestNonHashedTransactionsStackTrieGetProof(t *testing.T) { - txs := make([]*types.Transaction, 70) - key, _ := crypto.GenerateKey() - signer := types.LatestSigner(params.TestChainConfig) - - for i := range txs { - amount := math.BigPow(2, int64(i)) - price := big.NewInt(300000) - data := make([]byte, 100) - tx := types.NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data) - signedTx, err := types.SignTx(tx, signer, key) - if err != nil { - panic(err) - } - txs[i] = signedTx - } +func transactionsStackTrieInsertionTemplate(t *testing.T, n int) { + txs := makeTransactions(n) db := rawdb.NewMemoryDatabase() stackTrie := trie.NewStackTrie(db) - stackTrie.UpdateAndGetProofs(db, types.Transactions(txs)) + proofs, _ := stackTrie.UpdateAndGetProofs(db, types.Transactions(txs)) - fmt.Println("===") + rlp_last_tx, _ := txs[n-1].MarshalBinary() + last_proofC := proofs[len(proofs)-1].GetProofC() + + // Proof of the first tx is appended at the end of the proofs if len(tx) < 0x80 + // That's why we minus 2 here. + if len(txs) > 1 && len(txs) < 256 { + last_proofC = proofs[len(proofs)-2].GetProofC() + } + last_leaf_proof := last_proofC[len(last_proofC)-1] + + if !bytes.Equal(last_leaf_proof, rlp_last_tx) { + fmt.Println("- last_tx ", rlp_last_tx) + fmt.Println("- last_proof ", last_leaf_proof) + t.Fail() + } +} + +func TestStackTrieInsertion_1Tx(t *testing.T) { + // Only one leaf + transactionsStackTrieInsertionTemplate(t, 1) +} + +func TestStackTrieInsertion_2Txs(t *testing.T) { + // One ext. node and one leaf + transactionsStackTrieInsertionTemplate(t, 2) +} + +func TestStackTrieInsertion_3Txs(t *testing.T) { + // One ext. node, one branch and one leaf + transactionsStackTrieInsertionTemplate(t, 3) +} + +func TestStackTrieInsertion_4Txs(t *testing.T) { + // One ext. node, one branch and two leaves + transactionsStackTrieInsertionTemplate(t, 4) +} + +func TestStackTrieInsertion_16Txs(t *testing.T) { + // One ext. node and one branch with full leaves (16 leaves) + transactionsStackTrieInsertionTemplate(t, 16) +} + +func TestStackTrieInsertion_17Txs(t *testing.T) { + // One ext. node, 3 branches and 17 leaves. + // The original ext. node turns into a branch (B1) which has children at position 0 and 1. + // At position 0 of B1, it has a branch with full leaves + // At position 1 of B1, it has a newly leaf + transactionsStackTrieInsertionTemplate(t, 17) +} + +func TestStackTrieInsertion_33Txs(t *testing.T) { + // Follow above test and have one more branch generated + transactionsStackTrieInsertionTemplate(t, 33) +} + +func TestStackTrieInsertion_ManyTxs(t *testing.T) { + // Just randomly picking a large number. + // The cap of block gas limit is 30M, the minimum gas cost of a tx is 21k + // 30M / 21k ~= 1429 + transactionsStackTrieInsertionTemplate(t, 2000) } /* -TestHashedTransactionsStackTrieGetProof inserts 2 transactions into a stacktrie, +batchedTransactionsStackTrieProofTemplate inserts n transactions into a stacktrie, the trie is then hashed (DeriveSha call). -The proof is asked for one of the two transactions. The transactions in the trie are hashed and thus +The proof is asked for one of the n transactions. The transactions in the trie are hashed and thus GetProof requires to query a database to get the preimages. */ -func TestHashedTransactionsStackTrieGetProof(t *testing.T) { - txs := make([]*types.Transaction, 2) - key, _ := crypto.GenerateKey() - signer := types.LatestSigner(params.TestChainConfig) - - for i := range txs { - amount := math.BigPow(2, int64(i)) - price := big.NewInt(300000) - data := make([]byte, 100) - data[3] = 3 - data[4] = 3 - data[5] = 3 - data[6] = 3 - data[7] = 3 - tx := types.NewTransaction(uint64(i), common.Address{}, amount, 123457, price, data) - signedTx, err := types.SignTx(tx, signer, key) - if err != nil { - panic(err) - } - txs[i] = signedTx - } - +func batchedTransactionsStackTrieProofTemplate(n int) { + txs := makeTransactions(n) db := rawdb.NewMemoryDatabase() stackTrie := trie.NewStackTrie(db) @@ -130,7 +180,35 @@ func TestHashedTransactionsStackTrieGetProof(t *testing.T) { return } - fmt.Println(proofS) - + fmt.Println("proofS", proofS) fmt.Println("===") } + +func TestBatchedTxsProof_1Tx(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(1) +} + +func TestBatchedTxsProof_2Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(2) +} + +func TestBatchedTxsProof_3Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(3) +} +func TestBatchedTxsProof_4Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(4) +} + +func TestBatchedTxsProof_16Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(16) +} + +func TestBatchedTxsProof_17Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(17) +} +func TestBatchedTxsProof_33Txs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(33) +} +func TestBatchedTxsProof_ManyTxs(t *testing.T) { + batchedTransactionsStackTrieProofTemplate(2000) +} From 4996eb6c11fb4eb4bcde7eff55827ecf6cfaaf03 Mon Sep 17 00:00:00 2001 From: Magamedrasul Ibragimov Date: Tue, 12 Mar 2024 12:02:51 +0400 Subject: [PATCH 13/16] fix(zkevm-circuits/begin_tx): add missing constraints (#1776) ### Description This PR aims to fix #1475 by adding missing constraints. ### Issue Link #1475 ### Type of change - [X] Bug fix (non-breaking change which fixes an issue) ### Questions / Need Help 1. Meaning of `value_prev` argument in `account_access_list_write_unchecked`. Why is it sometimes set to 0, and sometimes to bool values such as `is_coinbase_warm` or `is_caller_callee_equal`. What is `is_caller_callee_equal` for? 2. There's expression: https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/main/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs#L163 Why do we have summation - if `is_empty_code_hash.expr()` is enough (as well as `callee_not_exists` is enough)? 3. What's [caller_nonce_hash_bytes](https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/main/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs#L186)? Is it Keccak(sender, nonce) and we have to constrain this exact value? --------- Co-authored-by: Eduard S --- .../src/circuit_input_builder/block.rs | 3 +- bus-mapping/src/evm/opcodes/begin_end_tx.rs | 26 +++++- .../src/evm_circuit/execution/begin_tx.rs | 80 ++++++++++++++++--- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index fa0abc6f41..219a844ce4 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -84,7 +84,8 @@ pub struct Block { pub block_steps: BlockSteps, /// Copy events in this block. pub copy_events: Vec, - /// Inputs to the SHA3 opcode + /// Inputs to the SHA3 opcode as well as data hashed during the EVM execution like init code + /// and address creation inputs. pub sha3_inputs: Vec>, /// Exponentiation events in the block. pub exp_events: Vec, diff --git a/bus-mapping/src/evm/opcodes/begin_end_tx.rs b/bus-mapping/src/evm/opcodes/begin_end_tx.rs index f9ee29d714..69ad7432f0 100644 --- a/bus-mapping/src/evm/opcodes/begin_end_tx.rs +++ b/bus-mapping/src/evm/opcodes/begin_end_tx.rs @@ -1,6 +1,8 @@ use super::TxExecSteps; use crate::{ - circuit_input_builder::{Call, CircuitInputStateRef, ExecState, ExecStep}, + circuit_input_builder::{ + Call, CircuitInputStateRef, CopyDataType, CopyEvent, ExecState, ExecStep, NumberOrHash, + }, operation::{AccountField, AccountOp, CallContextField, TxReceiptField, TxRefundOp, RW}, state_db::CodeDB, Error, @@ -148,6 +150,28 @@ fn gen_begin_tx_steps(state: &mut CircuitInputStateRef) -> Result 0 { + state.push_copy( + &mut exec_step, + CopyEvent { + src_addr: 0, + src_addr_end: state.tx.call_data.len() as u64, + src_type: CopyDataType::TxCalldata, + src_id: NumberOrHash::Number(state.tx.id as usize), + dst_addr: 0, + dst_type: CopyDataType::RlcAcc, + dst_id: NumberOrHash::Number(0), + log_id: None, + rw_counter_start: state.block_ctx.rwc, + bytes: state.tx.call_data.iter().map(|b| (*b, false)).collect(), + }, + ); + } } // There are 4 branches from here. diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 817b7dbff7..307d2198f0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -12,21 +12,22 @@ use crate::{ }, is_precompiled, math_gadget::{ - ContractCreateGadget, IsEqualWordGadget, IsZeroWordGadget, RangeCheckGadget, + ContractCreateGadget, IsEqualWordGadget, IsZeroGadget, IsZeroWordGadget, + RangeCheckGadget, }, - not, + not, rlc, tx::{BeginTxHelperGadget, TxDataGadget}, AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, ExecStep, Transaction}, }, - table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag}, + table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag}, util::{ word::{Word32Cell, WordExpr, WordLoHi, WordLoHiCell}, Expr, }, }; -use bus_mapping::state_db::CodeDB; +use bus_mapping::{circuit_input_builder::CopyDataType, state_db::CodeDB}; use eth_types::{evm_types::PRECOMPILE_COUNT, keccak256, Field, OpsIdentity, ToWord, U256}; use halo2_proofs::{ circuit::Value, @@ -45,6 +46,9 @@ pub(crate) struct BeginTxGadget { code_hash: WordLoHiCell, is_empty_code_hash: IsEqualWordGadget>, WordLoHi>>, caller_nonce_hash_bytes: Word32Cell, + calldata_length: Cell, + calldata_length_is_zero: IsZeroGadget, + calldata_rlc: Cell, create: ContractCreateGadget, callee_not_exists: IsZeroWordGadget>, is_caller_callee_equal: Cell, @@ -183,12 +187,23 @@ impl ExecutionGadget for BeginTxGadget { ); let caller_nonce_hash_bytes = cb.query_word32(); + let calldata_length = cb.query_cell(); + let calldata_length_is_zero = cb.is_zero(calldata_length.expr()); + let calldata_rlc = cb.query_cell_phase2(); let create = ContractCreateGadget::construct(cb); cb.require_equal_word( "tx caller address equivalence", tx.caller_address.to_word(), create.caller_address(), ); + + cb.require_equal( + "tx nonce equivalence", + tx.nonce.expr(), + create.caller_nonce(), + ); + + // 1. Handle contract creation transaction. cb.condition(tx.is_create.expr(), |cb| { cb.require_equal_word( "call callee address equivalence", @@ -201,21 +216,45 @@ impl ExecutionGadget for BeginTxGadget { ) .to_word(), ); - }); - cb.require_equal( - "tx nonce equivalence", - tx.nonce.expr(), - create.caller_nonce(), - ); - - // 1. Handle contract creation transaction. - cb.condition(tx.is_create.expr(), |cb| { cb.keccak_table_lookup( create.input_rlc(cb), create.input_length(), caller_nonce_hash_bytes.to_word(), ); + cb.tx_context_lookup( + tx_id.expr(), + TxContextFieldTag::CallDataLength, + None, + WordLoHi::from_lo_unchecked(calldata_length.expr()), + ); + // If calldata_length > 0 we use the copy circuit to calculate the calldata_rlc for the + // keccack input. + cb.condition(not::expr(calldata_length_is_zero.expr()), |cb| { + cb.copy_table_lookup( + WordLoHi::from_lo_unchecked(tx_id.expr()), + CopyDataType::TxCalldata.expr(), + WordLoHi::zero(), + CopyDataType::RlcAcc.expr(), + 0.expr(), + calldata_length.expr(), + 0.expr(), + calldata_length.expr(), + calldata_rlc.expr(), + 0.expr(), + ) + }); + // If calldata_length == 0, the copy circuit will not contain any entry, so we skip the + // lookup and instead force calldata_rlc to be 0 for the keccack input. + cb.condition(calldata_length_is_zero.expr(), |cb| { + cb.require_equal("calldata_rlc = 0", calldata_rlc.expr(), 0.expr()); + }); + cb.keccak_table_lookup( + calldata_rlc.expr(), + calldata_length.expr(), + cb.curr.state.code_hash.to_word(), + ); + cb.account_write( call_callee_address.to_word(), AccountFieldTag::Nonce, @@ -434,6 +473,9 @@ impl ExecutionGadget for BeginTxGadget { code_hash, is_empty_code_hash, caller_nonce_hash_bytes, + calldata_length, + calldata_length_is_zero, + calldata_rlc, create, callee_not_exists, is_caller_callee_equal, @@ -522,6 +564,18 @@ impl ExecutionGadget for BeginTxGadget { offset, U256::from_big_endian(&untrimmed_contract_addr), )?; + self.calldata_length.assign( + region, + offset, + Value::known(F::from(tx.call_data.len() as u64)), + )?; + self.calldata_length_is_zero + .assign(region, offset, F::from(tx.call_data.len() as u64))?; + let calldata_rlc = region + .challenges() + .keccak_input() + .map(|randomness| rlc::value(tx.call_data.iter().rev(), randomness)); + self.calldata_rlc.assign(region, offset, calldata_rlc)?; self.create.assign( region, offset, From 443da4bd3b43bc07d7830f3b6c84a29c1267ccff Mon Sep 17 00:00:00 2001 From: Ming Date: Thu, 21 Mar 2024 13:19:11 +0800 Subject: [PATCH 14/16] [proof-chunk] merge to main branch (#1785) ### Description This PR makes the first move toward single block chunking into multiple proof & aggregation ### Issue Link https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/1498 ### Type of change - [x] Breaking change (fix or feature that would cause existing functionality to not work as expected) ### Rationale design rationale are list below as separated section --------- Co-authored-by: CeciliaZ030 <45245961+CeciliaZ030@users.noreply.github.com> --- Cargo.lock | 2 + bus-mapping/src/circuit_input_builder.rs | 776 ++++++++++++++---- .../src/circuit_input_builder/block.rs | 31 +- bus-mapping/src/circuit_input_builder/call.rs | 4 +- .../src/circuit_input_builder/chunk.rs | 107 +++ .../src/circuit_input_builder/execution.rs | 10 + .../circuit_input_builder/input_state_ref.rs | 31 +- .../src/circuit_input_builder/transaction.rs | 2 +- bus-mapping/src/exec_trace.rs | 2 + bus-mapping/src/mock.rs | 11 +- bus-mapping/src/operation.rs | 139 +++- bus-mapping/src/operation/container.rs | 69 +- circuit-benchmarks/src/copy_circuit.rs | 21 +- circuit-benchmarks/src/evm_circuit.rs | 18 +- circuit-benchmarks/src/exp_circuit.rs | 28 +- circuit-benchmarks/src/state_circuit.rs | 6 +- circuit-benchmarks/src/super_circuit.rs | 4 +- gadgets/Cargo.toml | 1 + gadgets/src/lib.rs | 1 + gadgets/src/permutation.rs | 358 ++++++++ integration-tests/Cargo.toml | 1 + .../src/integration_test_circuits.rs | 104 ++- .../tests/circuit_input_builder.rs | 1 + testool/src/statetest/executor.rs | 20 +- zkevm-circuits/src/bin/stats/main.rs | 21 +- zkevm-circuits/src/bytecode_circuit.rs | 10 +- zkevm-circuits/src/copy_circuit.rs | 34 +- zkevm-circuits/src/copy_circuit/dev.rs | 3 +- zkevm-circuits/src/copy_circuit/test.rs | 66 +- zkevm-circuits/src/evm_circuit.rs | 309 +++++-- zkevm-circuits/src/evm_circuit/execution.rs | 547 ++++++++---- .../src/evm_circuit/execution/add_sub.rs | 3 +- .../src/evm_circuit/execution/addmod.rs | 3 +- .../src/evm_circuit/execution/address.rs | 3 +- .../src/evm_circuit/execution/balance.rs | 3 +- .../src/evm_circuit/execution/begin_chunk.rs | 59 ++ .../src/evm_circuit/execution/begin_tx.rs | 3 +- .../src/evm_circuit/execution/bitwise.rs | 3 +- .../src/evm_circuit/execution/block_ctx.rs | 3 +- .../src/evm_circuit/execution/blockhash.rs | 3 +- .../src/evm_circuit/execution/byte.rs | 3 +- .../src/evm_circuit/execution/calldatacopy.rs | 3 +- .../src/evm_circuit/execution/calldataload.rs | 3 +- .../src/evm_circuit/execution/calldatasize.rs | 3 +- .../src/evm_circuit/execution/caller.rs | 3 +- .../src/evm_circuit/execution/callop.rs | 10 +- .../src/evm_circuit/execution/callvalue.rs | 3 +- .../src/evm_circuit/execution/chainid.rs | 3 +- .../src/evm_circuit/execution/codecopy.rs | 3 +- .../src/evm_circuit/execution/codesize.rs | 3 +- .../src/evm_circuit/execution/comparator.rs | 3 +- .../src/evm_circuit/execution/create.rs | 3 +- .../src/evm_circuit/execution/dummy.rs | 3 +- .../src/evm_circuit/execution/dup.rs | 3 +- .../src/evm_circuit/execution/end_block.rs | 66 +- .../src/evm_circuit/execution/end_chunk.rs | 215 +++++ .../src/evm_circuit/execution/end_tx.rs | 7 +- .../evm_circuit/execution/error_code_store.rs | 3 +- .../execution/error_invalid_creation_code.rs | 3 +- .../execution/error_invalid_jump.rs | 3 +- .../execution/error_invalid_opcode.rs | 3 +- .../execution/error_oog_account_access.rs | 3 +- .../evm_circuit/execution/error_oog_call.rs | 3 +- .../execution/error_oog_constant.rs | 3 +- .../evm_circuit/execution/error_oog_create.rs | 3 +- .../execution/error_oog_dynamic_memory.rs | 3 +- .../evm_circuit/execution/error_oog_exp.rs | 3 +- .../evm_circuit/execution/error_oog_log.rs | 3 +- .../execution/error_oog_memory_copy.rs | 3 +- .../execution/error_oog_precompile.rs | 3 +- .../evm_circuit/execution/error_oog_sha3.rs | 3 +- .../execution/error_oog_sload_sstore.rs | 3 +- .../execution/error_oog_static_memory.rs | 3 +- .../execution/error_precompile_failed.rs | 3 +- .../execution/error_return_data_oo_bound.rs | 3 +- .../src/evm_circuit/execution/error_stack.rs | 3 +- .../execution/error_write_protection.rs | 3 +- .../src/evm_circuit/execution/exp.rs | 3 +- .../src/evm_circuit/execution/extcodecopy.rs | 3 +- .../src/evm_circuit/execution/extcodehash.rs | 3 +- .../src/evm_circuit/execution/extcodesize.rs | 3 +- .../src/evm_circuit/execution/gas.rs | 9 +- .../src/evm_circuit/execution/gasprice.rs | 3 +- .../src/evm_circuit/execution/invalid_tx.rs | 2 + .../src/evm_circuit/execution/is_zero.rs | 3 +- .../src/evm_circuit/execution/jump.rs | 3 +- .../src/evm_circuit/execution/jumpdest.rs | 3 +- .../src/evm_circuit/execution/jumpi.rs | 3 +- .../src/evm_circuit/execution/logs.rs | 3 +- .../src/evm_circuit/execution/memory.rs | 3 +- .../src/evm_circuit/execution/msize.rs | 3 +- .../src/evm_circuit/execution/mul_div_mod.rs | 3 +- .../src/evm_circuit/execution/mulmod.rs | 3 +- .../src/evm_circuit/execution/not.rs | 3 +- .../src/evm_circuit/execution/origin.rs | 3 +- .../src/evm_circuit/execution/padding.rs | 79 ++ .../src/evm_circuit/execution/pc.rs | 3 +- .../src/evm_circuit/execution/pop.rs | 3 +- .../execution/precompiles/ecrecover.rs | 3 +- .../execution/precompiles/identity.rs | 3 +- .../src/evm_circuit/execution/push.rs | 3 +- .../evm_circuit/execution/return_revert.rs | 3 +- .../evm_circuit/execution/returndatacopy.rs | 3 +- .../evm_circuit/execution/returndatasize.rs | 3 +- .../src/evm_circuit/execution/sar.rs | 3 +- .../src/evm_circuit/execution/sdiv_smod.rs | 3 +- .../src/evm_circuit/execution/selfbalance.rs | 3 +- .../src/evm_circuit/execution/sha3.rs | 3 +- .../src/evm_circuit/execution/shl_shr.rs | 3 +- .../execution/signed_comparator.rs | 3 +- .../src/evm_circuit/execution/signextend.rs | 3 +- .../src/evm_circuit/execution/sload.rs | 3 +- .../src/evm_circuit/execution/sstore.rs | 3 +- .../src/evm_circuit/execution/stop.rs | 3 +- .../src/evm_circuit/execution/swap.rs | 3 +- zkevm-circuits/src/evm_circuit/param.rs | 13 +- zkevm-circuits/src/evm_circuit/step.rs | 14 + zkevm-circuits/src/evm_circuit/table.rs | 11 + .../src/evm_circuit/util/common_gadget.rs | 109 ++- .../evm_circuit/util/constraint_builder.rs | 162 +++- .../src/evm_circuit/util/instrumentation.rs | 4 + zkevm-circuits/src/evm_circuit/util/tx.rs | 3 +- zkevm-circuits/src/exp_circuit.rs | 15 +- zkevm-circuits/src/exp_circuit/test.rs | 51 +- zkevm-circuits/src/keccak_circuit.rs | 16 +- zkevm-circuits/src/pi_circuit.rs | 18 +- zkevm-circuits/src/pi_circuit/test.rs | 27 +- zkevm-circuits/src/root_circuit.rs | 373 +++++++-- .../src/root_circuit/aggregation.rs | 257 ++++-- zkevm-circuits/src/root_circuit/dev.rs | 100 ++- zkevm-circuits/src/root_circuit/test.rs | 350 +++++++- zkevm-circuits/src/sig_circuit.rs | 13 +- zkevm-circuits/src/state_circuit.rs | 177 +++- .../src/state_circuit/constraint_builder.rs | 50 +- zkevm-circuits/src/state_circuit/dev.rs | 60 +- .../state_circuit/lexicographic_ordering.rs | 26 +- zkevm-circuits/src/state_circuit/test.rs | 160 ++-- zkevm-circuits/src/super_circuit.rs | 227 ++++- zkevm-circuits/src/super_circuit/test.rs | 114 ++- zkevm-circuits/src/table.rs | 29 + zkevm-circuits/src/table/chunk_ctx_table.rs | 145 ++++ zkevm-circuits/src/table/copy_table.rs | 5 +- zkevm-circuits/src/table/exp_table.rs | 5 +- zkevm-circuits/src/table/rw_table.rs | 372 ++++++++- zkevm-circuits/src/test_util.rs | 317 +++++-- zkevm-circuits/src/tx_circuit.rs | 15 +- zkevm-circuits/src/util.rs | 20 +- zkevm-circuits/src/util/chunk_ctx.rs | 226 +++++ zkevm-circuits/src/witness.rs | 5 +- zkevm-circuits/src/witness/block.rs | 89 +- zkevm-circuits/src/witness/chunk.rs | 317 +++++++ zkevm-circuits/src/witness/rw.rs | 477 +++++++++-- zkevm-circuits/tests/prover_error.rs | 16 +- 153 files changed, 6543 insertions(+), 1257 deletions(-) create mode 100644 bus-mapping/src/circuit_input_builder/chunk.rs create mode 100644 gadgets/src/permutation.rs create mode 100644 zkevm-circuits/src/evm_circuit/execution/begin_chunk.rs create mode 100644 zkevm-circuits/src/evm_circuit/execution/end_chunk.rs create mode 100644 zkevm-circuits/src/evm_circuit/execution/padding.rs create mode 100644 zkevm-circuits/src/table/chunk_ctx_table.rs create mode 100644 zkevm-circuits/src/util/chunk_ctx.rs create mode 100755 zkevm-circuits/src/witness/chunk.rs diff --git a/Cargo.lock b/Cargo.lock index 5f6781d1d6..a99cb46ac0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1675,6 +1675,7 @@ dependencies = [ "digest 0.7.6", "eth-types", "halo2_proofs", + "itertools 0.10.5", "rand", "sha3 0.7.3", "strum 0.24.1", @@ -2189,6 +2190,7 @@ dependencies = [ "ethers-contract-abigen", "glob", "halo2_proofs", + "itertools 0.10.5", "lazy_static", "log", "mock", diff --git a/bus-mapping/src/circuit_input_builder.rs b/bus-mapping/src/circuit_input_builder.rs index bfa77d1811..8f6e1d6a0c 100644 --- a/bus-mapping/src/circuit_input_builder.rs +++ b/bus-mapping/src/circuit_input_builder.rs @@ -4,6 +4,7 @@ mod access; mod block; mod call; +mod chunk; mod execution; mod input_state_ref; #[cfg(test)] @@ -11,17 +12,21 @@ mod tracer_tests; mod transaction; mod withdrawal; -use self::access::gen_state_access_trace; +use self::{access::gen_state_access_trace, chunk::Chunk}; use crate::{ error::Error, evm::opcodes::{gen_associated_ops, gen_associated_steps}, - operation::{CallContextField, Operation, RWCounter, StartOp, RW}, + operation::{ + CallContextField, Op, Operation, OperationContainer, PaddingOp, RWCounter, StartOp, + StepStateField, StepStateOp, RW, + }, rpc::GethClient, state_db::{self, CodeDB, StateDB}, }; pub use access::{Access, AccessSet, AccessValue, CodeSource}; pub use block::{Block, BlockContext}; pub use call::{Call, CallContext, CallKind}; +pub use chunk::ChunkContext; use core::fmt::Debug; use eth_types::{ self, geth_types, @@ -43,6 +48,9 @@ use std::{ pub use transaction::{Transaction, TransactionContext}; pub use withdrawal::{Withdrawal, WithdrawalContext}; +/// number of execution state fields +pub const N_EXEC_STATE: usize = 10; + /// Runtime Config /// /// Default to mainnet block @@ -80,9 +88,14 @@ impl FeatureConfig { } } +// RW_BUFFER_SIZE need to set to cover max rwc row contributed by a ExecStep +const RW_BUFFER_SIZE: usize = 30; + /// Circuit Setup Parameters -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct FixedCParams { + /// + pub total_chunks: usize, /// Maximum number of rw operations in the state circuit (RwTable length / /// number of rows). This must be at least the number of rw operations /// + 1, in order to allocate at least a Start row. @@ -125,29 +138,53 @@ pub struct FixedCParams { /// To reduce the testing overhead, we determine the parameters by the testing inputs. /// A new [`FixedCParams`] will be computed from the generated circuit witness. #[derive(Debug, Clone, Copy)] -pub struct DynamicCParams {} - +pub struct DynamicCParams { + /// Toatal number of chunks + pub total_chunks: usize, +} /// Circuit Setup Parameters. These can be fixed/concrete or unset/dynamic. pub trait CircuitsParams: Debug + Copy { - /// Returns the max number of rws allowed + /// Return the total number of chunks + fn total_chunks(&self) -> usize; + /// Set total number of chunks + fn set_total_chunk(&mut self, total_chunks: usize); + /// Return the maximun Rw fn max_rws(&self) -> Option; } impl CircuitsParams for FixedCParams { + fn total_chunks(&self) -> usize { + self.total_chunks + } + fn set_total_chunk(&mut self, total_chunks: usize) { + self.total_chunks = total_chunks; + } fn max_rws(&self) -> Option { Some(self.max_rws) } } impl CircuitsParams for DynamicCParams { + fn total_chunks(&self) -> usize { + self.total_chunks + } + fn set_total_chunk(&mut self, total_chunks: usize) { + self.total_chunks = total_chunks; + } fn max_rws(&self) -> Option { None } } +impl Default for DynamicCParams { + fn default() -> Self { + DynamicCParams { total_chunks: 1 } + } +} impl Default for FixedCParams { /// Default values for most of the unit tests of the Circuit Parameters fn default() -> Self { FixedCParams { + total_chunks: 1, max_rws: 1000, max_txs: 1, max_withdrawals: 1, @@ -173,7 +210,9 @@ impl Default for FixedCParams { /// the block. 2. For each [`eth_types::Transaction`] in the block, take the /// [`eth_types::GethExecTrace`] to build the circuit input associated with /// each transaction, and the bus-mapping operations associated with each -/// [`eth_types::GethExecStep`] in the [`eth_types::GethExecTrace`]. +/// [`eth_types::GethExecStep`] in the [`eth_types::GethExecTrace`]. 3. If `Rw`s +/// generated during Transactions exceed the `max_rws` threshold, seperate witness +/// into multiple chunks. /// /// The generated bus-mapping operations are: /// [`StackOp`](crate::operation::StackOp)s, @@ -182,7 +221,7 @@ impl Default for FixedCParams { /// [`OpcodeId`](crate::evm::OpcodeId)s used in each `ExecTrace` step so that /// the State Proof witnesses are already generated on a structured manner and /// ready to be added into the State circuit. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CircuitInputBuilder { /// StateDB key-value DB pub sdb: StateDB, @@ -190,10 +229,14 @@ pub struct CircuitInputBuilder { pub code_db: CodeDB, /// Block pub block: Block, - /// Circuits Setup Parameters - pub circuits_params: C, + /// Chunk + pub chunks: Vec, /// Block Context pub block_ctx: BlockContext, + /// Chunk Context + pub chunk_ctx: ChunkContext, + /// Circuits Setup Parameters before chunking + pub circuits_params: C, /// Feature config pub feature_config: FeatureConfig, } @@ -208,16 +251,28 @@ impl<'a, C: CircuitsParams> CircuitInputBuilder { params: C, feature_config: FeatureConfig, ) -> Self { + let total_chunks = params.total_chunks(); + let chunks = vec![Chunk::default(); total_chunks]; Self { sdb, code_db, block, - circuits_params: params, + chunks, block_ctx: BlockContext::new(), + chunk_ctx: ChunkContext::new(total_chunks), + circuits_params: params, feature_config, } } + /// Set the total number of chunks for existing CircuitInputBuilder, + /// API for chunking the existing tests then run with a specific chunk + pub fn set_total_chunk(&mut self, total_chunks: usize) { + self.circuits_params.set_total_chunk(total_chunks); + self.chunks = vec![Chunk::default(); total_chunks]; + self.chunk_ctx.total_chunks = total_chunks; + } + /// Obtain a mutable reference to the state that the `CircuitInputBuilder` /// maintains, contextualized to a particular transaction and a /// particular execution step in that transaction. @@ -231,6 +286,7 @@ impl<'a, C: CircuitsParams> CircuitInputBuilder { code_db: &mut self.code_db, block: &mut self.block, block_ctx: &mut self.block_ctx, + chunk_ctx: &mut self.chunk_ctx, tx, tx_ctx, max_rws: self.circuits_params.max_rws(), @@ -288,81 +344,495 @@ impl<'a, C: CircuitsParams> CircuitInputBuilder { } } + // chunking and mutable bumping chunk_ctx once condition match + // return true on bumping to next chunk + fn check_and_chunk( + &mut self, + geth_trace: &GethExecTrace, + tx: Transaction, + tx_ctx: TransactionContext, + next_geth_step: Option<(usize, &GethExecStep)>, + last_call: Option, + ) -> Result { + // we dont chunk if + // 1. on last chunk + // 2. still got some buffer room before max_rws + let Some(max_rws) = self.circuits_params.max_rws() else { + // terminiate earlier due to no max_rws + return Ok(false); + }; + + if self.chunk_ctx.is_last_chunk() || self.chunk_rws() + RW_BUFFER_SIZE < max_rws { + return Ok(false); + }; + + // Optain the first op of the next GethExecStep, for fixed case also lookahead + let (mut cib, mut tx, mut tx_ctx) = (self.clone(), tx, tx_ctx); + let mut cib_ref = cib.state_ref(&mut tx, &mut tx_ctx); + let mut next_ops = if let Some((i, step)) = next_geth_step { + log::trace!("chunk at {}th opcode {:?} ", i, step.op); + gen_associated_ops(&step.op, &mut cib_ref, &geth_trace.struct_logs[i..])?.remove(0) + } else { + log::trace!("chunk at EndTx"); + gen_associated_steps(&mut cib_ref, ExecState::EndTx)? + }; + + let last_copy = self.block.copy_events.len(); + // Generate EndChunk and proceed to the next if it's not the last chunk + // Set next step pre-state as end_chunk state + self.set_end_chunk(&next_ops, Some(&tx)); + + // need to update next_ops.rwc to catch block_ctx.rwc in `set_end_chunk` + next_ops.rwc = self.block_ctx.rwc; + + // tx.id start from 1, so it's equivalent to `next_tx_index` + self.commit_chunk_ctx(true, tx.id as usize, last_copy, last_call); + self.set_begin_chunk(&next_ops, Some(&tx)); + + Ok(true) + } + /// Handle a transaction with its corresponding execution trace to generate /// all the associated operations. Each operation is registered in /// `self.block.container`, and each step stores the /// [`OperationRef`](crate::exec_trace::OperationRef) to each of the /// generated operations. + /// When dynamic builder handles Tx with is_chuncked = false, we don't chunk + /// When fixed builder handles Tx with is_chuncked = true, we chunk fn handle_tx( &mut self, eth_tx: ð_types::Transaction, geth_trace: &GethExecTrace, is_last_tx: bool, tx_index: u64, - ) -> Result<(), Error> { + ) -> Result<(ExecStep, Option), Error> { let mut tx = self.new_tx(tx_index, eth_tx, !geth_trace.failed)?; let mut tx_ctx = TransactionContext::new(eth_tx, geth_trace, is_last_tx)?; - if !geth_trace.invalid { + let res = if !geth_trace.invalid { // Generate BeginTx step let begin_tx_step = gen_associated_steps( &mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::BeginTx, )?; + let mut last_call = Some(tx.calls().get(begin_tx_step.call_index).unwrap().clone()); tx.steps_mut().push(begin_tx_step); - for (index, geth_step) in geth_trace.struct_logs.iter().enumerate() { - let mut state_ref = self.state_ref(&mut tx, &mut tx_ctx); - log::trace!("handle {}th opcode {:?} ", index, geth_step.op); + let mut trace = geth_trace.struct_logs.iter().enumerate().peekable(); + while let Some((peek_i, peek_step)) = trace.peek() { + // Check the peek step and chunk if needed + self.check_and_chunk( + geth_trace, + tx.clone(), + tx_ctx.clone(), + Some((*peek_i, peek_step)), + last_call.clone(), + )?; + // Proceed to the next step + let (i, step) = trace.next().expect("Peeked step should exist"); + log::trace!( + "handle {}th opcode {:?} {:?} rws = {:?}", + i, + step.op, + step, + self.chunk_rws() + ); let exec_steps = gen_associated_ops( - &geth_step.op, - &mut state_ref, - &geth_trace.struct_logs[index..], + &step.op, + &mut self.state_ref(&mut tx, &mut tx_ctx), + &geth_trace.struct_logs[i..], )?; + last_call = exec_steps + .last() + .map(|step| tx.calls().get(step.call_index).unwrap().clone()); tx.steps_mut().extend(exec_steps); } + // Peek the end_tx_step + self.check_and_chunk( + geth_trace, + tx.clone(), + tx_ctx.clone(), + None, + last_call.clone(), + )?; + // Generate EndTx step let end_tx_step = gen_associated_steps(&mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::EndTx)?; - tx.steps_mut().push(end_tx_step); + tx.steps_mut().push(end_tx_step.clone()); + (end_tx_step, last_call) } else if self.feature_config.invalid_tx { // Generate InvalidTx step let invalid_tx_step = gen_associated_steps( &mut self.state_ref(&mut tx, &mut tx_ctx), ExecState::InvalidTx, )?; - tx.steps_mut().push(invalid_tx_step); + tx.steps_mut().push(invalid_tx_step.clone()); + // Peek the end_tx_step + let is_chunk = + self.check_and_chunk(geth_trace, tx.clone(), tx_ctx.clone(), None, None)?; + if is_chunk { + // TODO we dont support chunk after invalid_tx + // becasuse begin_chunk will constraints what next step execution state. + // And for next step either begin_tx or invalid_tx will both failed because + // begin_tx/invalid_tx define new execution state. + unimplemented!("dont support invalid_tx with multiple chunks") + } + + (invalid_tx_step, None) } else { panic!("invalid tx support not enabled") - } + }; self.sdb.commit_tx(); self.block.txs.push(tx); - Ok(()) + Ok(res) + } + + // generate chunk related steps + fn gen_chunk_associated_steps( + &mut self, + step: &mut ExecStep, + rw: RW, + tx: Option<&Transaction>, + ) { + let mut dummy_tx = Transaction::default(); + let mut dummy_tx_ctx = TransactionContext::default(); + + let rw_counters = (0..N_EXEC_STATE) + .map(|_| self.block_ctx.rwc.inc_pre()) + .collect::>(); + // just bump rwc in chunk_ctx as block_ctx rwc to assure same delta apply + let rw_counters_inner_chunk = (0..N_EXEC_STATE) + .map(|_| self.chunk_ctx.rwc.inc_pre()) + .collect::>(); + + let tags = { + let state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); + let last_call = tx + .map(|tx| tx.calls()[step.call_index].clone()) + .or_else(|| state.block.txs.last().map(|tx| tx.calls[0].clone())) + .unwrap(); + [ + (StepStateField::CodeHash, last_call.code_hash.to_word()), + (StepStateField::CallID, Word::from(last_call.call_id)), + (StepStateField::IsRoot, Word::from(last_call.is_root as u64)), + ( + StepStateField::IsCreate, + Word::from(last_call.is_create() as u64), + ), + (StepStateField::ProgramCounter, Word::from(step.pc)), + ( + StepStateField::StackPointer, + Word::from(step.stack_pointer()), + ), + (StepStateField::GasLeft, Word::from(step.gas_left)), + ( + StepStateField::MemoryWordSize, + Word::from(step.memory_word_size()), + ), + ( + StepStateField::ReversibleWriteCounter, + Word::from(step.reversible_write_counter), + ), + (StepStateField::LogID, Word::from(step.log_id)), + ] + }; + + debug_assert_eq!(N_EXEC_STATE, tags.len()); + let state = self.state_ref(&mut dummy_tx, &mut dummy_tx_ctx); + + tags.iter() + .zip_eq(rw_counters) + .zip_eq(rw_counters_inner_chunk) + .for_each(|(((tag, value), rw_counter), inner_rw_counter)| { + push_op( + &mut state.block.container, + step, + rw_counter, + inner_rw_counter, + rw, + StepStateOp { + field: tag.clone(), + value: *value, + }, + ); + }); + } + + /// Set the end status of a chunk including the current globle rwc + /// and commit the current chunk context, proceed to the next chunk + /// if needed + pub fn commit_chunk_ctx( + &mut self, + to_next: bool, + next_tx_index: usize, + next_copy_index: usize, + last_call: Option, + ) { + self.chunk_ctx.end_rwc = self.block_ctx.rwc.0; + self.chunk_ctx.end_tx_index = next_tx_index; + self.chunk_ctx.end_copy_index = next_copy_index; + self.cur_chunk_mut().ctx = self.chunk_ctx.clone(); + if to_next { + // add `-1` to include previous set and deal with transaction cross-chunk case + self.chunk_ctx + .bump(self.block_ctx.rwc.0, next_tx_index - 1, next_copy_index); + self.cur_chunk_mut().prev_last_call = last_call; + } + } + + fn set_begin_chunk(&mut self, first_step: &ExecStep, tx: Option<&Transaction>) { + let mut begin_chunk = ExecStep { + exec_state: ExecState::BeginChunk, + rwc: first_step.rwc, + gas_left: first_step.gas_left, + call_index: first_step.call_index, + ..ExecStep::default() + }; + self.gen_chunk_associated_steps(&mut begin_chunk, RW::READ, tx); + self.chunks[self.chunk_ctx.idx].begin_chunk = Some(begin_chunk); + } + + fn set_end_chunk(&mut self, next_step: &ExecStep, tx: Option<&Transaction>) { + let mut end_chunk = ExecStep { + exec_state: ExecState::EndChunk, + rwc: next_step.rwc, + rwc_inner_chunk: next_step.rwc_inner_chunk, + gas_left: next_step.gas_left, + call_index: next_step.call_index, + ..ExecStep::default() + }; + self.gen_chunk_associated_steps(&mut end_chunk, RW::WRITE, tx); + self.gen_chunk_padding(&mut end_chunk); + self.chunks[self.chunk_ctx.idx].end_chunk = Some(end_chunk); + } + + fn gen_chunk_padding(&mut self, step: &mut ExecStep) { + // rwc index start from 1 + let end_rwc = self.chunk_ctx.rwc.0; + let total_rws = end_rwc - 1; + let max_rws = self.cur_chunk().fixed_param.max_rws; + + assert!( + total_rws < max_rws, + "total_rws <= max_rws, total_rws={}, max_rws={}", + total_rws, + max_rws + ); + + let mut padding = step.clone(); + padding.exec_state = ExecState::Padding; + padding.bus_mapping_instance = vec![]; // there is no rw in padding step + + if self.chunk_ctx.is_first_chunk() { + push_op( + &mut self.block.container, + step, + RWCounter(1), + RWCounter(1), + RW::READ, + StartOp {}, + ); + } + + if max_rws - total_rws > 1 { + let (padding_start, padding_end) = (total_rws + 1, max_rws - 1); + push_op( + &mut self.block.container, + step, + RWCounter(padding_start), + RWCounter(padding_start), + RW::READ, + PaddingOp {}, + ); + if padding_end != padding_start { + push_op( + &mut self.block.container, + step, + RWCounter(padding_end), + RWCounter(padding_end), + RW::READ, + PaddingOp {}, + ); + } + } + self.chunks[self.chunk_ctx.idx].padding = Some(padding); + } + + /// Get the i-th mutable chunk + pub fn get_chunk_mut(&mut self, i: usize) -> &mut Chunk { + self.chunks.get_mut(i).expect("Chunk does not exist") + } + + /// Get the i-th chunk + pub fn get_chunk(&self, i: usize) -> Chunk { + self.chunks.get(i).expect("Chunk does not exist").clone() + } + + /// Get the current chunk + pub fn cur_chunk(&self) -> Chunk { + self.chunks[self.chunk_ctx.idx].clone() + } + + /// Get a mutable reference of current chunk + pub fn cur_chunk_mut(&mut self) -> &mut Chunk { + &mut self.chunks[self.chunk_ctx.idx] + } + + /// Get the previous chunk + pub fn prev_chunk(&self) -> Option { + if self.chunk_ctx.idx == 0 { + return None; + } + self.chunks.get(self.chunk_ctx.idx - 1).cloned() + } + + /// Total Rw in this chunk + pub fn chunk_rws(&self) -> usize { + self.chunk_ctx.rwc.0 - 1 } } impl CircuitInputBuilder { + /// First part of handle_block, only called by fixed Builder + pub fn begin_handle_block( + &mut self, + eth_block: &EthBlock, + geth_traces: &[eth_types::GethExecTrace], + ) -> Result<(Option, Option), Error> { + assert!( + self.circuits_params.max_rws().unwrap_or_default() > self.last_exec_step_rws_reserved(), + "Fixed max_rws not enough for rws reserve" + ); + + // accumulates gas across all txs in the block + let mut res = eth_block + .transactions + .iter() + .enumerate() + .map(|(idx, tx)| { + let geth_trace = &geth_traces[idx]; + // Transaction index starts from 1 + let tx_id = idx + 1; + self.handle_tx( + tx, + geth_trace, + tx_id == eth_block.transactions.len(), + tx_id as u64, + ) + .map(|(exec_step, last_call)| (Some(exec_step), last_call)) + }) + .collect::, Option)>, _>>()?; + // set eth_block + self.block.eth_block = eth_block.clone(); + self.set_value_ops_call_context_rwc_eor(); + if !res.is_empty() { + Ok(res.remove(res.len() - 1)) + } else { + Ok((None, None)) + } + } + /// Handle a block by handling each transaction to generate all the /// associated operations. pub fn handle_block( - &mut self, + mut self, eth_block: &EthBlock, geth_traces: &[eth_types::GethExecTrace], - ) -> Result<&CircuitInputBuilder, Error> { + ) -> Result, Error> { // accumulates gas across all txs in the block - self.begin_handle_block(eth_block, geth_traces)?; - self.set_end_block(self.circuits_params.max_rws)?; + let (last_step, last_call) = self.begin_handle_block(eth_block, geth_traces)?; + // since there is no next step, we cook dummy next step from last step to reuse + // existing field while update its `rwc`. + let mut dummy_next_step = { + let mut dummy_next_step = last_step.unwrap_or_default(); + // raise last step rwc to match with next step + (0..dummy_next_step.rw_indices_len()).for_each(|_| { + dummy_next_step.rwc.inc_pre(); + dummy_next_step.rwc_inner_chunk.inc_pre(); + }); + dummy_next_step + }; + + assert!(self.circuits_params.max_rws().is_some()); + + let last_copy = self.block.copy_events.len(); + + // TODO figure out and resolve generic param type and move fixed_param set inside + // commit_chunk_ctx. After fixed, then we can set fixed_param on all chunks + (0..self.circuits_params.total_chunks()).for_each(|idx| { + self.get_chunk_mut(idx).fixed_param = self.circuits_params; + }); + + // We fill dummy virtual steps: BeginChunk,EndChunk for redundant chunks + let last_process_chunk_id = self.chunk_ctx.idx; + (last_process_chunk_id..self.circuits_params.total_chunks()).try_for_each(|idx| { + if idx == self.circuits_params.total_chunks() - 1 { + self.set_end_block()?; + self.commit_chunk_ctx( + false, + eth_block.transactions.len(), + last_copy, + last_call.clone(), + ); + } else { + self.set_end_chunk(&dummy_next_step, None); + + self.commit_chunk_ctx( + true, + eth_block.transactions.len(), + last_copy, + last_call.clone(), + ); + // update dummy_next_step rwc to be used for next + dummy_next_step.rwc = self.block_ctx.rwc; + dummy_next_step.rwc_inner_chunk = self.chunk_ctx.rwc; + self.set_begin_chunk(&dummy_next_step, None); + dummy_next_step.rwc = self.block_ctx.rwc; + dummy_next_step.rwc_inner_chunk = self.chunk_ctx.rwc; + // update virtual step: end_block/padding so it can carry state context correctly + // TODO: enhance virtual step updating mechanism by having `running_next_step` + // defined in circuit_input_builder, so we dont need to + self.block.end_block = dummy_next_step.clone(); + self.cur_chunk_mut().padding = { + let mut padding = dummy_next_step.clone(); + padding.exec_state = ExecState::Padding; + Some(padding) + }; + } + Ok::<(), Error>(()) + })?; + + let used_chunks = self.chunk_ctx.idx + 1; + assert!( + used_chunks <= self.circuits_params.total_chunks(), + "Used more chunks than given total_chunks" + ); + assert!( + self.chunks.len() == self.chunk_ctx.idx + 1, + "number of chunks {} mis-match with chunk_ctx id {}", + self.chunks.len(), + self.chunk_ctx.idx + 1, + ); + + // Truncate chunks to the actual used amount & correct ctx.total_chunks + // Set length to the actual used amount of chunks + self.chunks.truncate(self.chunk_ctx.idx + 1); + self.chunks.iter_mut().for_each(|chunk| { + chunk.ctx.total_chunks = used_chunks; + }); + Ok(self) } - fn set_end_block(&mut self, max_rws: usize) -> Result<(), Error> { - let mut end_block_not_last = self.block.block_steps.end_block_not_last.clone(); - let mut end_block_last = self.block.block_steps.end_block_last.clone(); - end_block_not_last.rwc = self.block_ctx.rwc; - end_block_last.rwc = self.block_ctx.rwc; + fn set_end_block(&mut self) -> Result<(), Error> { + let mut end_block = self.block.end_block.clone(); + end_block.rwc = self.block_ctx.rwc; + end_block.exec_state = ExecState::EndBlock; + end_block.rwc_inner_chunk = self.chunk_ctx.rwc; let mut dummy_tx = Transaction::default(); let mut dummy_tx_ctx = TransactionContext::default(); @@ -370,65 +840,120 @@ impl CircuitInputBuilder { if let Some(call_id) = state.block.txs.last().map(|tx| tx.calls[0].call_id) { state.call_context_read( - &mut end_block_last, + &mut end_block, call_id, CallContextField::TxId, Word::from(state.block.txs.len() as u64), )?; } - let mut push_op = |step: &mut ExecStep, rwc: RWCounter, rw: RW, op: StartOp| { - let op_ref = state.block.container.insert(Operation::new(rwc, rw, op)); - step.bus_mapping_instance.push(op_ref); + // EndBlock step should also be padded to max_rws similar to EndChunk + self.gen_chunk_padding(&mut end_block); + self.block.end_block = end_block; + Ok(()) + } +} + +fn push_op( + container: &mut OperationContainer, + step: &mut ExecStep, + rwc: RWCounter, + rwc_inner_chunk: RWCounter, + rw: RW, + op: T, +) { + let op_ref = container.insert(Operation::new(rwc, rwc_inner_chunk, rw, op)); + step.bus_mapping_instance.push(op_ref); +} + +impl CircuitInputBuilder { + /// return the rw row reserved for end_block/end_chunk + pub fn last_exec_step_rws_reserved(&self) -> usize { + // rw ops reserved for EndBlock + let end_block_rws = if self.chunk_ctx.is_last_chunk() && self.chunk_rws() > 0 { + 1 + } else { + 0 }; + // rw ops reserved for EndChunk + let end_chunk_rws = if !self.chunk_ctx.is_last_chunk() { + N_EXEC_STATE + } else { + 0 + }; + end_block_rws + end_chunk_rws + 1 + } - // rwc index start from 1 - let total_rws = state.block_ctx.rwc.0 - 1; - // We need at least 1 extra Start row - #[allow(clippy::int_plus_one)] - { - assert!( - total_rws + 1 <= max_rws, - "total_rws + 1 <= max_rws, total_rws={}, max_rws={}", - total_rws, - max_rws - ); - } - let (padding_start, padding_end) = (1, max_rws - total_rws); // rw counter start from 1 - push_op( - &mut end_block_last, - RWCounter(padding_start), - RW::READ, - StartOp {}, - ); - if padding_end != padding_start { - push_op( - &mut end_block_last, - RWCounter(padding_end), - RW::READ, - StartOp {}, - ); - } + fn compute_param(&self, eth_block: &EthBlock) -> FixedCParams { + let max_txs = eth_block.transactions.len(); + let max_withdrawals = eth_block.withdrawals.as_ref().unwrap().len(); + let max_bytecode = self.code_db.num_rows_required_for_bytecode_table(); - self.block.block_steps.end_block_not_last = end_block_not_last; - self.block.block_steps.end_block_last = end_block_last; - Ok(()) + let max_calldata = eth_block + .transactions + .iter() + .fold(0, |acc, tx| acc + tx.input.len()); + let max_exp_steps = self + .block + .exp_events + .iter() + .fold(0usize, |acc, e| acc + e.steps.len()); + // The `+ 2` is used to take into account the two extra empty copy rows needed + // to satisfy the query at `Rotation(2)` performed inside of the + // `rows[2].value == rows[0].value * r + rows[1].value` requirement in the RLC + // Accumulation gate. + let max_copy_rows = self + .block + .copy_events + .iter() + .fold(0, |acc, c| acc + c.bytes.len()) + * 2 + + 4; // disabled and unused rows. + + let max_rws = >::into(self.block_ctx.rwc) - 1 + + self.last_exec_step_rws_reserved(); + + // Computing the number of rows for the EVM circuit requires the size of ExecStep, + // which is determined in the code of zkevm-circuits and cannot be imported here. + // When the evm circuit receives a 0 value it dynamically computes the minimum + // number of rows necessary. + let max_evm_rows = 0; + // Similarly, computing the number of rows for the Keccak circuit requires + // constants that cannot be accessed from here (NUM_ROUNDS and KECCAK_ROWS). + // With a 0 value the keccak circuit computes dynamically the minimum number of rows + // needed. + let max_keccak_rows = 0; + FixedCParams { + total_chunks: self.circuits_params.total_chunks(), + max_rws, + max_txs, + max_withdrawals, + max_calldata, + max_copy_rows, + max_exp_steps, + max_bytecode, + max_evm_rows, + max_keccak_rows, + max_vertical_circuit_rows: 0, + } } } -impl CircuitInputBuilder { - /// First part of handle_block, common for dynamic and static circuit parameters. - pub fn begin_handle_block( - &mut self, +impl CircuitInputBuilder { + fn dry_run( + &self, eth_block: &EthBlock, geth_traces: &[eth_types::GethExecTrace], - ) -> Result<(), Error> { + ) -> Result, Error> { + let mut cib = self.clone(); + cib.circuits_params.total_chunks = 1; + cib.chunk_ctx.total_chunks = 1; // accumulates gas across all txs in the block for (idx, tx) in eth_block.transactions.iter().enumerate() { let geth_trace = &geth_traces[idx]; // Transaction index starts from 1 let tx_id = idx + 1; - self.handle_tx( + cib.handle_tx( tx, geth_trace, tx_id == eth_block.transactions.len(), @@ -436,88 +961,51 @@ impl CircuitInputBuilder { )?; } // set eth_block - self.block.eth_block = eth_block.clone(); - self.set_value_ops_call_context_rwc_eor(); - Ok(()) + cib.block.eth_block = eth_block.clone(); + cib.set_value_ops_call_context_rwc_eor(); + + debug_assert!( + cib.chunk_ctx.idx == 0, + "processing {} > 1 chunk", + cib.chunk_ctx.idx + ); // dry run mode only one chunk + + Ok(cib) } -} -impl CircuitInputBuilder { + /// Handle a block by handling each transaction to generate all the - /// associated operations. From these operations, the optimal circuit parameters - /// are derived and set. + /// associated operations. Dry run the block to determind the target + /// [`FixedCParams`] from to total number of chunks. pub fn handle_block( - mut self, + self, eth_block: &EthBlock, geth_traces: &[eth_types::GethExecTrace], ) -> Result, Error> { - self.begin_handle_block(eth_block, geth_traces)?; - - // Compute subcircuits parameters - let c_params = { - let max_txs = eth_block.transactions.len(); - let max_withdrawals = eth_block.withdrawals.as_ref().unwrap().len(); - let max_bytecode = self.code_db.num_rows_required_for_bytecode_table(); - - let max_calldata = eth_block - .transactions - .iter() - .fold(0, |acc, tx| acc + tx.input.len()); - let max_exp_steps = self - .block - .exp_events - .iter() - .fold(0usize, |acc, e| acc + e.steps.len()); - // The `+ 2` is used to take into account the two extra empty copy rows needed - // to satisfy the query at `Rotation(2)` performed inside of the - // `rows[2].value == rows[0].value * r + rows[1].value` requirement in the RLC - // Accumulation gate. - let max_copy_rows = self - .block - .copy_events - .iter() - .fold(0, |acc, c| acc + c.bytes.len()) - * 2 - + 4; // disabled and unused rows. - - let total_rws_before_padding: usize = - >::into(self.block_ctx.rwc) - 1; // -1 since rwc start from index `1` - let max_rws_after_padding = total_rws_before_padding - + 1 // fill 1 to have exactly one StartOp padding in below `set_end_block` - + if total_rws_before_padding > 0 { 1 /*end_block -> CallContextFieldTag::TxId lookup*/ } else { 0 }; - // Computing the number of rows for the EVM circuit requires the size of ExecStep, - // which is determined in the code of zkevm-circuits and cannot be imported here. - // When the evm circuit receives a 0 value it dynamically computes the minimum - // number of rows necessary. - let max_evm_rows = 0; - // Similarly, computing the number of rows for the Keccak circuit requires - // constants that cannot be accessed from here (NUM_ROUNDS and KECCAK_ROWS). - // With a 0 value the keccak circuit computes dynamically the minimum number of rows - // needed. - let max_keccak_rows = 0; - FixedCParams { - max_rws: max_rws_after_padding, - max_txs, - max_withdrawals, - max_calldata, - max_copy_rows, - max_exp_steps, - max_bytecode, - max_evm_rows, - max_keccak_rows, - max_vertical_circuit_rows: 0, - } - }; - let mut cib = CircuitInputBuilder:: { + // Run the block without chunking and compute the blockwise params + let mut target_params = self + .dry_run(eth_block, geth_traces) + .expect("Dry run failure") + .compute_param(eth_block); + + // Calculate the chunkwise params from total number of chunks + let total_chunks = self.circuits_params.total_chunks; + target_params.total_chunks = total_chunks; + // count rws buffer here to left some space for extra virtual steps + target_params.max_rws = (target_params.max_rws + 1) / total_chunks + RW_BUFFER_SIZE; + + // Use a new builder with targeted params to handle the block + // chunking context is set to dynamic so for the actual param is update per chunk + let cib = CircuitInputBuilder:: { sdb: self.sdb, code_db: self.code_db, block: self.block, - circuits_params: c_params, + chunks: self.chunks, block_ctx: self.block_ctx, + chunk_ctx: ChunkContext::new(total_chunks), + circuits_params: target_params, feature_config: self.feature_config, }; - - cib.set_end_block(c_params.max_rws)?; - Ok(cib) + cib.handle_block(eth_block, geth_traces) } } @@ -809,14 +1297,14 @@ impl BuilderClient

{ prev_state_root: Word, ) -> Result, Error> { let block = Block::new(self.chain_id, history_hashes, prev_state_root, eth_block)?; - let mut builder = CircuitInputBuilder::new( + let builder = CircuitInputBuilder::new( sdb, code_db, block, self.circuits_params, self.feature_config, ); - builder.handle_block(eth_block, geth_traces)?; + let builder = builder.handle_block(eth_block, geth_traces)?; Ok(builder) } diff --git a/bus-mapping/src/circuit_input_builder/block.rs b/bus-mapping/src/circuit_input_builder/block.rs index 219a844ce4..eb592a73ec 100644 --- a/bus-mapping/src/circuit_input_builder/block.rs +++ b/bus-mapping/src/circuit_input_builder/block.rs @@ -43,19 +43,9 @@ impl BlockContext { } } -/// Block-wise execution steps that don't belong to any Transaction. -#[derive(Debug)] -pub struct BlockSteps { - /// EndBlock step that is repeated after the last transaction and before - /// reaching the last EVM row. - pub end_block_not_last: ExecStep, - /// Last EndBlock step that appears in the last EVM row. - pub end_block_last: ExecStep, -} - // TODO: Remove fields that are duplicated in`eth_block` /// Circuit Input related to a block. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Block { /// chain id pub chain_id: Word, @@ -80,8 +70,11 @@ pub struct Block { pub container: OperationContainer, /// Transactions contained in the block pub txs: Vec, - /// Block-wise steps - pub block_steps: BlockSteps, + /// End block step + pub end_block: ExecStep, + + // /// Chunk context + // pub chunk_context: ChunkContext, /// Copy events in this block. pub copy_events: Vec, /// Inputs to the SHA3 opcode as well as data hashed during the EVM execution like init code @@ -136,15 +129,9 @@ impl Block { prev_state_root, container: OperationContainer::new(), txs: Vec::new(), - block_steps: BlockSteps { - end_block_not_last: ExecStep { - exec_state: ExecState::EndBlock, - ..ExecStep::default() - }, - end_block_last: ExecStep { - exec_state: ExecState::EndBlock, - ..ExecStep::default() - }, + end_block: ExecStep { + exec_state: ExecState::EndBlock, + ..ExecStep::default() }, copy_events: Vec::new(), exp_events: Vec::new(), diff --git a/bus-mapping/src/circuit_input_builder/call.rs b/bus-mapping/src/circuit_input_builder/call.rs index 0f16847b36..2052e2c1ea 100644 --- a/bus-mapping/src/circuit_input_builder/call.rs +++ b/bus-mapping/src/circuit_input_builder/call.rs @@ -123,7 +123,7 @@ impl Call { } /// Context of a [`Call`]. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct CallContext { /// Index of call pub index: usize, @@ -151,7 +151,7 @@ impl CallContext { /// [`Operation::reversible`](crate::operation::Operation::reversible) that /// happened in them, that will be reverted at once when the call that initiated /// this reversion group eventually ends with failure (and thus reverts). -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ReversionGroup { /// List of `index` and `reversible_write_counter_offset` of calls belong to /// this group. `reversible_write_counter_offset` is the number of diff --git a/bus-mapping/src/circuit_input_builder/chunk.rs b/bus-mapping/src/circuit_input_builder/chunk.rs new file mode 100644 index 0000000000..30ace0d91b --- /dev/null +++ b/bus-mapping/src/circuit_input_builder/chunk.rs @@ -0,0 +1,107 @@ +use super::{Call, ExecStep, FixedCParams}; +use crate::operation::RWCounter; + +#[derive(Debug, Default, Clone)] +pub struct Chunk { + /// current context + pub ctx: ChunkContext, + /// fixed param for the chunk + pub fixed_param: FixedCParams, + /// Begin op of a chunk + pub begin_chunk: Option, + /// End op of a chunk + pub end_chunk: Option, + /// Padding step that is repeated after the last transaction and before + /// reaching the last EVM row. + pub padding: Option, + /// + pub prev_last_call: Option, +} + +/// Context of chunking, used to track the current chunk index and inner rw counter +/// also the global rw counter from start to end. +#[derive(Debug, Clone)] +pub struct ChunkContext { + /// Index of current chunk, start from 0 + pub idx: usize, + /// Used to track the inner chunk counter in every operation in the chunk. + /// it will be reset for every new chunk. + /// Contains the next available value. + pub rwc: RWCounter, + /// Number of chunks + pub total_chunks: usize, + /// Initial global rw counter + pub initial_rwc: usize, + /// End global rw counter + pub end_rwc: usize, + /// tx range in block: [initial_tx_index, end_tx_index) + pub initial_tx_index: usize, + /// + pub end_tx_index: usize, + /// copy range in block: [initial_copy_index, end_copy_index) + pub initial_copy_index: usize, + /// + pub end_copy_index: usize, +} + +impl Default for ChunkContext { + fn default() -> Self { + Self::new(1) + } +} + +impl ChunkContext { + /// Create a new Self + pub fn new(total_chunks: usize) -> Self { + Self { + rwc: RWCounter::new(), + idx: 0, + total_chunks, + initial_rwc: 1, // rw counter start from 1 + end_rwc: 0, // end_rwc should be set in later phase + initial_tx_index: 0, + end_tx_index: 0, + initial_copy_index: 0, + end_copy_index: 0, + } + } + + /// New chunking context with one chunk + pub fn new_one_chunk() -> Self { + Self { + rwc: RWCounter::new(), + idx: 0, + total_chunks: 1, + initial_rwc: 1, // rw counter start from 1 + end_rwc: 0, // end_rwc should be set in later phase + initial_tx_index: 0, + end_tx_index: 0, + initial_copy_index: 0, + end_copy_index: 0, + } + } + + /// Proceed the context to next chunk, record the initial rw counter + /// update the chunk idx and reset the inner rw counter + pub fn bump(&mut self, initial_rwc: usize, initial_tx: usize, initial_copy: usize) { + assert!(self.idx + 1 < self.total_chunks, "Exceed total chunks"); + self.idx += 1; + self.rwc = RWCounter::new(); + self.initial_rwc = initial_rwc; + self.initial_tx_index = initial_tx; + self.initial_copy_index = initial_copy; + self.end_rwc = 0; + self.end_tx_index = 0; + self.end_copy_index = 0; + } + + /// Is first chunk + pub fn is_first_chunk(&self) -> bool { + self.idx == 0 + } + + /// Is last chunk + pub fn is_last_chunk(&self) -> bool { + self.total_chunks - self.idx - 1 == 0 + } +} diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index f99cb3bac7..fd5fc4310c 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -35,6 +35,8 @@ pub struct ExecStep { pub call_index: usize, /// The global counter when this step was executed. pub rwc: RWCounter, + /// The inner chunk counter when this step was executed. + pub rwc_inner_chunk: RWCounter, /// Reversible Write Counter. Counter of write operations in the call that /// will need to be undone in case of a revert. Value at the beginning of /// the step. @@ -59,6 +61,7 @@ impl ExecStep { step: &GethExecStep, call_ctx: &CallContext, rwc: RWCounter, + rwc_inner_chunk: RWCounter, reversible_write_counter: usize, log_id: usize, ) -> Self { @@ -73,6 +76,7 @@ impl ExecStep { gas_refund: step.refund, call_index: call_ctx.index, rwc, + rwc_inner_chunk, reversible_write_counter, reversible_write_counter_delta: 0, log_id, @@ -143,12 +147,18 @@ pub enum ExecState { Op(OpcodeId), /// Precompile call Precompile(PrecompileCalls), + /// Virtual step Begin Chunk + BeginChunk, /// Virtual step Begin Tx BeginTx, /// Virtual step End Tx EndTx, + /// Virtual step Padding + Padding, /// Virtual step End Block EndBlock, + /// Virtual step End Chunk + EndChunk, /// Invalid Tx InvalidTx, } 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 07c250ead4..214564d7a5 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -2,8 +2,8 @@ use super::{ get_call_memory_offset_length, get_create_init_code, Block, BlockContext, Call, CallContext, - CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, PrecompileEvent, Transaction, - TransactionContext, + CallKind, ChunkContext, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, PrecompileEvent, + Transaction, TransactionContext, }; use crate::{ error::{DepthError, ExecError, InsufficientBalanceError, NonceUintOverflowError}, @@ -37,6 +37,8 @@ pub struct CircuitInputStateRef<'a> { pub block: &'a mut Block, /// Block Context pub block_ctx: &'a mut BlockContext, + /// Chunk Context + pub chunk_ctx: &'a mut ChunkContext, /// Transaction pub tx: &'a mut Transaction, /// Transaction Context @@ -49,11 +51,11 @@ impl<'a> CircuitInputStateRef<'a> { /// Create a new step from a `GethExecStep` pub fn new_step(&self, geth_step: &GethExecStep) -> Result { let call_ctx = self.tx_ctx.call_ctx()?; - Ok(ExecStep::new( geth_step, call_ctx, self.block_ctx.rwc, + self.chunk_ctx.rwc, call_ctx.reversible_write_counter, self.tx_ctx.log_id, )) @@ -65,6 +67,7 @@ impl<'a> CircuitInputStateRef<'a> { exec_state: ExecState::InvalidTx, gas_left: self.tx.gas(), rwc: self.block_ctx.rwc, + rwc_inner_chunk: self.chunk_ctx.rwc, ..Default::default() } } @@ -75,6 +78,7 @@ impl<'a> CircuitInputStateRef<'a> { exec_state: ExecState::BeginTx, gas_left: self.tx.gas(), rwc: self.block_ctx.rwc, + rwc_inner_chunk: self.chunk_ctx.rwc, ..Default::default() } } @@ -110,6 +114,7 @@ impl<'a> CircuitInputStateRef<'a> { 0 }, rwc: self.block_ctx.rwc, + rwc_inner_chunk: self.chunk_ctx.rwc, // For tx without code execution reversible_write_counter: if let Some(call_ctx) = self.tx_ctx.calls().last() { call_ctx.reversible_write_counter @@ -131,10 +136,12 @@ impl<'a> CircuitInputStateRef<'a> { if let OpEnum::Account(op) = op.clone().into_enum() { self.check_update_sdb_account(rw, &op) } - let op_ref = - self.block - .container - .insert(Operation::new(self.block_ctx.rwc.inc_pre(), rw, op)); + let op_ref = self.block.container.insert(Operation::new( + self.block_ctx.rwc.inc_pre(), + self.chunk_ctx.rwc.inc_pre(), + rw, + op, + )); step.bus_mapping_instance.push(op_ref); self.check_rw_num_limit() } @@ -142,9 +149,13 @@ impl<'a> CircuitInputStateRef<'a> { /// Check whether rws will overflow circuit limit. pub fn check_rw_num_limit(&self) -> Result<(), Error> { if let Some(max_rws) = self.max_rws { - let rwc = self.block_ctx.rwc.0; + let rwc = self.chunk_ctx.rwc.0; if rwc > max_rws { - log::error!("rwc > max_rws, rwc={}, max_rws={}", rwc, max_rws); + log::error!( + "chunk inner rwc > max_rws, rwc={}, max_rws={}", + rwc, + max_rws + ); return Err(Error::RwsNotEnough(max_rws, rwc)); }; } @@ -210,6 +221,7 @@ impl<'a> CircuitInputStateRef<'a> { self.check_apply_op(&op.clone().into_enum()); let op_ref = self.block.container.insert(Operation::new_reversible( self.block_ctx.rwc.inc_pre(), + self.chunk_ctx.rwc.inc_pre(), RW::WRITE, op, )); @@ -1009,6 +1021,7 @@ impl<'a> CircuitInputStateRef<'a> { self.check_apply_op(&op); let rev_op_ref = self.block.container.insert_op_enum( self.block_ctx.rwc.inc_pre(), + self.chunk_ctx.rwc.inc_pre(), RW::WRITE, false, op, diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 6ca84d27ce..6783a78191 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -12,7 +12,7 @@ use crate::{ use super::{call::ReversionGroup, Call, CallContext, CallKind, CodeSource, ExecStep}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] /// Context of a [`Transaction`] which can mutate in an [`ExecStep`]. pub struct TransactionContext { /// Unique identifier of transaction of the block. The value is `index + 1`. diff --git a/bus-mapping/src/exec_trace.rs b/bus-mapping/src/exec_trace.rs index b5d49e0166..28c886240b 100644 --- a/bus-mapping/src/exec_trace.rs +++ b/bus-mapping/src/exec_trace.rs @@ -14,6 +14,7 @@ impl fmt::Debug for OperationRef { "OperationRef{{ {}, {} }}", match self.0 { Target::Start => "Start", + Target::Padding => "Padding", Target::Memory => "Memory", Target::Stack => "Stack", Target::Storage => "Storage", @@ -24,6 +25,7 @@ impl fmt::Debug for OperationRef { Target::CallContext => "CallContext", Target::TxReceipt => "TxReceipt", Target::TxLog => "TxLog", + Target::StepState => "StepState", }, self.1 )) diff --git a/bus-mapping/src/mock.rs b/bus-mapping/src/mock.rs index 31c449c9a5..892eb41c3f 100644 --- a/bus-mapping/src/mock.rs +++ b/bus-mapping/src/mock.rs @@ -96,8 +96,15 @@ impl BlockData { } impl BlockData { - /// Create a new block from the given Geth data with default CircuitsParams. + /// Create a new block with one chunk + /// from the given Geth data with default CircuitsParams. pub fn new_from_geth_data(geth_data: GethData) -> Self { + Self::new_from_geth_data_chunked(geth_data, 1) + } + + /// Create a new block with given number of chunks + /// from the given Geth data with default CircuitsParams. + pub fn new_from_geth_data_chunked(geth_data: GethData, total_chunks: usize) -> Self { let (sdb, code_db) = Self::init_dbs(&geth_data); Self { @@ -107,7 +114,7 @@ impl BlockData { history_hashes: geth_data.history_hashes, eth_block: geth_data.eth_block, geth_traces: geth_data.geth_traces, - circuits_params: DynamicCParams {}, + circuits_params: DynamicCParams { total_chunks }, } } } diff --git a/bus-mapping/src/operation.rs b/bus-mapping/src/operation.rs index e2a3061f1d..c0b4ab4e78 100644 --- a/bus-mapping/src/operation.rs +++ b/bus-mapping/src/operation.rs @@ -94,7 +94,7 @@ impl RWCounter { /// This is also used as the RwTableTag for the RwTable. #[derive(Debug, Clone, PartialEq, Eq, Copy, EnumIter, Hash)] pub enum Target { - /// Start is a padding operation. + /// Start operation in the first row Start = 1, /// Means the target of the operation is the Memory. Memory, @@ -116,6 +116,11 @@ pub enum Target { TxReceipt, /// Means the target of the operation is the TxLog. TxLog, + + /// Chunking: StepState + StepState, + /// Chunking: padding operation. + Padding, } impl_expr!(Target); @@ -886,6 +891,116 @@ impl Op for StartOp { } } +/// Represents a field parameter of the StepStateField. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum StepStateField { + /// caller id field + CallID, + /// is_root field + IsRoot, + /// is_create field + IsCreate, + /// code_hash field + CodeHash, + /// program_counter field + ProgramCounter, + /// stack_pointer field + StackPointer, + /// gas_left field + GasLeft, + /// memory_word_size field + MemoryWordSize, + /// reversible_write_counter field + ReversibleWriteCounter, + /// log_id field + LogID, +} + +/// StepStateOp represents exec state store and load +#[derive(Clone, PartialEq, Eq)] +pub struct StepStateOp { + /// field of CallContext + pub field: StepStateField, + /// value of CallContext + pub value: Word, +} + +impl fmt::Debug for StepStateOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("StepStateOp { ")?; + f.write_fmt(format_args!( + "field: {:?}, value: {:?}", + self.field, self.value, + ))?; + f.write_str(" }") + } +} + +impl PartialOrd for StepStateOp { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for StepStateOp { + fn cmp(&self, other: &Self) -> Ordering { + self.field.cmp(&other.field) + } +} + +impl Op for StepStateOp { + fn into_enum(self) -> OpEnum { + OpEnum::StepState(self) + } + + fn reverse(&self) -> Self { + unreachable!("StepStateOp can't be reverted") + } +} + +impl StepStateOp { + /// Create a new instance of a `StepStateOp` from it's components. + pub const fn new(field: StepStateField, value: Word) -> StepStateOp { + StepStateOp { field, value } + } + + /// Returns the [`Target`] (operation type) of this operation. + pub const fn target(&self) -> Target { + Target::StepState + } + + /// Returns the [`Word`] read or written by this operation. + pub const fn value(&self) -> &Word { + &self.value + } +} + +/// Represent a Padding padding operation +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct PaddingOp {} + +impl PartialOrd for PaddingOp { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PaddingOp { + fn cmp(&self, _other: &Self) -> Ordering { + Ordering::Equal + } +} + +impl Op for PaddingOp { + fn into_enum(self) -> OpEnum { + OpEnum::Padding(self) + } + + fn reverse(&self) -> Self { + unreachable!("Padding can't be reverted") + } +} + /// Represents TxReceipt read/write operation. #[derive(Clone, PartialEq, Eq)] pub struct TxReceiptOp { @@ -956,12 +1071,17 @@ pub enum OpEnum { TxLog(TxLogOp), /// Start Start(StartOp), + /// Padding + Padding(PaddingOp), + /// StepState + StepState(StepStateOp), } /// Operation is a Wrapper over a type that implements Op with a RWCounter. #[derive(Debug, Clone)] pub struct Operation { rwc: RWCounter, + rwc_inner_chunk: RWCounter, rw: RW, /// True when this Operation should be reverted or not when /// handle_reversion. @@ -994,9 +1114,10 @@ impl Ord for Operation { impl Operation { /// Create a new Operation from an `op` with a `rwc` - pub fn new(rwc: RWCounter, rw: RW, op: T) -> Self { + pub fn new(rwc: RWCounter, rwc_inner_chunk: RWCounter, rw: RW, op: T) -> Self { Self { rwc, + rwc_inner_chunk, rw, reversible: false, op, @@ -1004,9 +1125,10 @@ impl Operation { } /// Create a new reversible Operation from an `op` with a `rwc` - pub fn new_reversible(rwc: RWCounter, rw: RW, op: T) -> Self { + pub fn new_reversible(rwc: RWCounter, rwc_inner_chunk: RWCounter, rw: RW, op: T) -> Self { Self { rwc, + rwc_inner_chunk, rw, reversible: true, op, @@ -1018,6 +1140,11 @@ impl Operation { self.rwc } + /// Return this `Operation` `rwc_inner_chunk` + pub fn rwc_inner_chunk(&self) -> RWCounter { + self.rwc_inner_chunk + } + /// Return this `Operation` `rw` pub fn rw(&self) -> RW { self.rw @@ -1101,11 +1228,13 @@ mod operation_tests { fn unchecked_op_transmutations_are_safe() { let stack_op = StackOp::new(1, StackAddress::from(1024), Word::from(0x40)); - let stack_op_as_operation = Operation::new(RWCounter(1), RW::WRITE, stack_op.clone()); + let stack_op_as_operation = + Operation::new(RWCounter(1), RWCounter(1), RW::WRITE, stack_op.clone()); let memory_op = MemoryOp::new(1, MemoryAddress(0x40), 0x40); - let memory_op_as_operation = Operation::new(RWCounter(1), RW::WRITE, memory_op.clone()); + let memory_op_as_operation = + Operation::new(RWCounter(1), RWCounter(1), RW::WRITE, memory_op.clone()); assert_eq!(stack_op, stack_op_as_operation.op); assert_eq!(memory_op, memory_op_as_operation.op) diff --git a/bus-mapping/src/operation/container.rs b/bus-mapping/src/operation/container.rs index cf2e4dcf22..2ccac336cd 100644 --- a/bus-mapping/src/operation/container.rs +++ b/bus-mapping/src/operation/container.rs @@ -1,7 +1,7 @@ use super::{ - AccountOp, CallContextOp, MemoryOp, Op, OpEnum, Operation, RWCounter, StackOp, StartOp, - StorageOp, Target, TxAccessListAccountOp, TxAccessListAccountStorageOp, TxLogOp, TxReceiptOp, - TxRefundOp, RW, + AccountOp, CallContextOp, MemoryOp, Op, OpEnum, Operation, PaddingOp, RWCounter, StackOp, + StartOp, StepStateOp, StorageOp, Target, TxAccessListAccountOp, TxAccessListAccountStorageOp, + TxLogOp, TxReceiptOp, TxRefundOp, RW, }; use crate::exec_trace::OperationRef; use itertools::Itertools; @@ -44,6 +44,10 @@ pub struct OperationContainer { pub tx_log: Vec>, /// Operations of Start pub start: Vec>, + /// Operations of Padding + pub padding: Vec>, + /// Operations of StepState + pub step_state: Vec>, } impl Default for OperationContainer { @@ -68,6 +72,8 @@ impl OperationContainer { tx_receipt: Vec::new(), tx_log: Vec::new(), start: Vec::new(), + padding: Vec::new(), + step_state: Vec::new(), } } @@ -77,9 +83,10 @@ impl OperationContainer { /// vector. pub fn insert(&mut self, op: Operation) -> OperationRef { let rwc = op.rwc(); + let rwc_inner_chunk = op.rwc_inner_chunk(); let rw = op.rw(); let reversible = op.reversible(); - self.insert_op_enum(rwc, rw, reversible, op.op.into_enum()) + self.insert_op_enum(rwc, rwc_inner_chunk, rw, reversible, op.op.into_enum()) } /// Inserts an [`OpEnum`] into the container returning a lightweight @@ -89,32 +96,35 @@ impl OperationContainer { pub fn insert_op_enum( &mut self, rwc: RWCounter, + rwc_inner_chunk: RWCounter, rw: RW, reversible: bool, op_enum: OpEnum, ) -> OperationRef { match op_enum { OpEnum::Memory(op) => { - self.memory.push(Operation::new(rwc, rw, op)); + self.memory + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::Memory, self.memory.len() - 1)) } OpEnum::Stack(op) => { - self.stack.push(Operation::new(rwc, rw, op)); + self.stack + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::Stack, self.stack.len() - 1)) } OpEnum::Storage(op) => { self.storage.push(if reversible { - Operation::new_reversible(rwc, rw, op) + Operation::new_reversible(rwc, rwc_inner_chunk, rw, op) } else { - Operation::new(rwc, rw, op) + Operation::new(rwc, rwc_inner_chunk, rw, op) }); OperationRef::from((Target::Storage, self.storage.len() - 1)) } OpEnum::TxAccessListAccount(op) => { self.tx_access_list_account.push(if reversible { - Operation::new_reversible(rwc, rw, op) + Operation::new_reversible(rwc, rwc_inner_chunk, rw, op) } else { - Operation::new(rwc, rw, op) + Operation::new(rwc, rwc_inner_chunk, rw, op) }); OperationRef::from(( Target::TxAccessListAccount, @@ -123,9 +133,9 @@ impl OperationContainer { } OpEnum::TxAccessListAccountStorage(op) => { self.tx_access_list_account_storage.push(if reversible { - Operation::new_reversible(rwc, rw, op) + Operation::new_reversible(rwc, rwc_inner_chunk, rw, op) } else { - Operation::new(rwc, rw, op) + Operation::new(rwc, rwc_inner_chunk, rw, op) }); OperationRef::from(( Target::TxAccessListAccountStorage, @@ -134,36 +144,50 @@ impl OperationContainer { } OpEnum::TxRefund(op) => { self.tx_refund.push(if reversible { - Operation::new_reversible(rwc, rw, op) + Operation::new_reversible(rwc, rwc_inner_chunk, rw, op) } else { - Operation::new(rwc, rw, op) + Operation::new(rwc, rwc_inner_chunk, rw, op) }); OperationRef::from((Target::TxRefund, self.tx_refund.len() - 1)) } OpEnum::Account(op) => { self.account.push(if reversible { - Operation::new_reversible(rwc, rw, op) + Operation::new_reversible(rwc, rwc_inner_chunk, rw, op) } else { - Operation::new(rwc, rw, op) + Operation::new(rwc, rwc_inner_chunk, rw, op) }); OperationRef::from((Target::Account, self.account.len() - 1)) } OpEnum::CallContext(op) => { - self.call_context.push(Operation::new(rwc, rw, op)); + self.call_context + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::CallContext, self.call_context.len() - 1)) } OpEnum::TxReceipt(op) => { - self.tx_receipt.push(Operation::new(rwc, rw, op)); + self.tx_receipt + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::TxReceipt, self.tx_receipt.len() - 1)) } OpEnum::TxLog(op) => { - self.tx_log.push(Operation::new(rwc, rw, op)); + self.tx_log + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::TxLog, self.tx_log.len() - 1)) } OpEnum::Start(op) => { - self.start.push(Operation::new(rwc, rw, op)); + self.start + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); OperationRef::from((Target::Start, self.start.len() - 1)) } + OpEnum::Padding(op) => { + self.padding + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); + OperationRef::from((Target::Padding, self.padding.len() - 1)) + } + OpEnum::StepState(op) => { + self.step_state + .push(Operation::new(rwc, rwc_inner_chunk, rw, op)); + OperationRef::from((Target::StepState, self.step_state.len() - 1)) + } } } @@ -198,20 +222,25 @@ mod container_test { #[test] fn operation_container_test() { + // global counter same as intra_chunk_counter in single chunk let mut global_counter = RWCounter::default(); + let mut intra_chunk_counter = RWCounter::default(); let mut operation_container = OperationContainer::default(); let stack_operation = Operation::new( global_counter.inc_pre(), + intra_chunk_counter.inc_pre(), RW::WRITE, StackOp::new(1, StackAddress(1023), Word::from(0x100)), ); let memory_operation = Operation::new( global_counter.inc_pre(), + intra_chunk_counter.inc_pre(), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(1), 1), ); let storage_operation = Operation::new( global_counter.inc_pre(), + intra_chunk_counter.inc_pre(), RW::WRITE, StorageOp::new( Address::zero(), diff --git a/circuit-benchmarks/src/copy_circuit.rs b/circuit-benchmarks/src/copy_circuit.rs index d77155f8bc..8638b1a1e1 100644 --- a/circuit-benchmarks/src/copy_circuit.rs +++ b/circuit-benchmarks/src/copy_circuit.rs @@ -26,7 +26,7 @@ mod tests { use std::env::var; use zkevm_circuits::{ copy_circuit::TestCopyCircuit, - evm_circuit::witness::{block_convert, Block}, + evm_circuit::witness::{block_convert, chunk_convert, Block, Chunk}, util::SubCircuit, }; @@ -51,10 +51,11 @@ mod tests { ]); // Create the circuit - let mut block = generate_full_events_block(degree); + let (mut block, chunk) = generate_full_events_block(degree); block.circuits_params.max_rws = 10_000; block.circuits_params.max_copy_rows = 10_000; - let circuit = TestCopyCircuit::::new_from_block(&block); + let circuit = TestCopyCircuit::::new_from_block(&block, &chunk); + // Bench setup generation let setup_message = format!("{} {} with degree = {}", BENCHMARK_ID, setup_prfx, degree); let start1 = start_timer!(|| setup_message); @@ -109,7 +110,7 @@ mod tests { } /// generate enough copy events to fillup copy circuit - fn generate_full_events_block(degree: u32) -> Block { + fn generate_full_events_block(degree: u32) -> (Block, Chunk) { // A empiric value 55 here to let the code generate enough copy event without // exceed the max_rws limit. let copy_event_num = (1 << degree) / 55; @@ -144,19 +145,19 @@ mod tests { ) .unwrap(); let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( + let builder = BlockData::new_from_geth_data_with_params( block.clone(), FixedCParams { max_rws: 1 << (degree - 1), ..Default::default() }, ) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); + .new_circuit_input_builder() + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); assert_eq!(block.copy_events.len(), copy_event_num); - block + (block, chunk) } } diff --git a/circuit-benchmarks/src/evm_circuit.rs b/circuit-benchmarks/src/evm_circuit.rs index 7ceeb80f8f..10af1a3e10 100644 --- a/circuit-benchmarks/src/evm_circuit.rs +++ b/circuit-benchmarks/src/evm_circuit.rs @@ -24,7 +24,10 @@ mod evm_circ_benches { use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::env::var; - use zkevm_circuits::evm_circuit::{witness::block_convert, TestEvmCircuit}; + use zkevm_circuits::evm_circuit::{ + witness::{block_convert, chunk_convert}, + TestEvmCircuit, + }; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -44,17 +47,16 @@ mod evm_circ_benches { .unwrap() .into(); - let mut builder = + let builder = BlockData::new_from_geth_data_with_params(empty_data.clone(), FixedCParams::default()) - .new_circuit_input_builder(); - - builder - .handle_block(&empty_data.eth_block, &empty_data.geth_traces) - .unwrap(); + .new_circuit_input_builder() + .handle_block(&empty_data.eth_block, &empty_data.geth_traces) + .unwrap(); let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); - let circuit = TestEvmCircuit::::new(block); + let circuit = TestEvmCircuit::::new(block, chunk); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, diff --git a/circuit-benchmarks/src/exp_circuit.rs b/circuit-benchmarks/src/exp_circuit.rs index 8b3d6830a3..11d91b69dc 100644 --- a/circuit-benchmarks/src/exp_circuit.rs +++ b/circuit-benchmarks/src/exp_circuit.rs @@ -26,7 +26,7 @@ mod tests { use rand_xorshift::XorShiftRng; use std::env::var; use zkevm_circuits::{ - evm_circuit::witness::{block_convert, Block}, + evm_circuit::witness::{block_convert, chunk_convert, Block, Chunk}, exp_circuit::TestExpCircuit, }; @@ -49,11 +49,8 @@ mod tests { let base = Word::from(132); let exponent = Word::from(27); - let block = generate_full_events_block(degree, base, exponent); - let circuit = TestExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); + let (block, chunk) = generate_full_events_block(degree, base, exponent); + let circuit = TestExpCircuit::::new(block.exp_events, chunk.fixed_param.max_exp_steps); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ @@ -121,7 +118,11 @@ mod tests { end_timer!(start3); } - fn generate_full_events_block(degree: u32, base: Word, exponent: Word) -> Block { + fn generate_full_events_block( + degree: u32, + base: Word, + exponent: Word, + ) -> (Block, Chunk) { let code = bytecode! { PUSH32(exponent) PUSH32(base) @@ -137,17 +138,18 @@ mod tests { ) .unwrap(); let block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( + let builder = BlockData::new_from_geth_data_with_params( block.clone(), FixedCParams { max_rws: 1 << (degree - 1), ..Default::default() }, ) - .new_circuit_input_builder(); - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - block_convert(&builder).unwrap() + .new_circuit_input_builder() + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); + (block, chunk) } } diff --git a/circuit-benchmarks/src/state_circuit.rs b/circuit-benchmarks/src/state_circuit.rs index 2235930f7d..251a4089ad 100644 --- a/circuit-benchmarks/src/state_circuit.rs +++ b/circuit-benchmarks/src/state_circuit.rs @@ -21,9 +21,7 @@ mod tests { use rand::SeedableRng; use rand_xorshift::XorShiftRng; use std::env::var; - use zkevm_circuits::{ - evm_circuit::witness::RwMap, state_circuit::StateCircuit, util::SubCircuit, - }; + use zkevm_circuits::{state_circuit::StateCircuit, util::SubCircuit, witness::Chunk}; #[cfg_attr(not(feature = "benches"), ignore)] #[test] @@ -39,7 +37,7 @@ mod tests { .parse() .expect("Cannot parse DEGREE env var as u32"); - let empty_circuit = StateCircuit::::new(RwMap::default(), 1 << 16); + let empty_circuit = StateCircuit::::new(&Chunk::default()); // Initialize the polynomial commitment parameters let mut rng = XorShiftRng::from_seed([ diff --git a/circuit-benchmarks/src/super_circuit.rs b/circuit-benchmarks/src/super_circuit.rs index e9868aabbe..e800ec5806 100644 --- a/circuit-benchmarks/src/super_circuit.rs +++ b/circuit-benchmarks/src/super_circuit.rs @@ -80,6 +80,7 @@ mod tests { block.sign(&wallets); let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 1, max_withdrawals: 1, max_calldata: 32, @@ -91,8 +92,9 @@ mod tests { max_keccak_rows: 0, max_vertical_circuit_rows: 0, }; - let (_, circuit, instance, _) = + let (_, mut circuits, mut instances, _) = SuperCircuit::build(block, circuits_params, Fr::from(0x100)).unwrap(); + let (circuit, instance) = (circuits.remove(0), instances.remove(0)); let instance_refs: Vec<&[Fr]> = instance.iter().map(|v| &v[..]).collect(); // Bench setup generation diff --git a/gadgets/Cargo.toml b/gadgets/Cargo.toml index e1869999d5..c156da2c78 100644 --- a/gadgets/Cargo.toml +++ b/gadgets/Cargo.toml @@ -11,6 +11,7 @@ sha3 = "0.7.2" eth-types = { path = "../eth-types" } digest = "0.7.6" strum = "0.24" +itertools = "0.10" [dev-dependencies] rand = "0.8" diff --git a/gadgets/src/lib.rs b/gadgets/src/lib.rs index a7150262ee..fea14604d0 100644 --- a/gadgets/src/lib.rs +++ b/gadgets/src/lib.rs @@ -16,6 +16,7 @@ pub mod binary_number; pub mod is_zero; pub mod less_than; pub mod mul_add; +pub mod permutation; pub mod util; use eth_types::Field; diff --git a/gadgets/src/permutation.rs b/gadgets/src/permutation.rs new file mode 100644 index 0000000000..796086cb35 --- /dev/null +++ b/gadgets/src/permutation.rs @@ -0,0 +1,358 @@ +//! Chip that implements permutation fingerprints +//! fingerprints &= \prod_{row_j} \left ( \alpha - \sum_k (\gamma^k \cdot cell_j(k)) \right ) +//! power of gamma are defined in columns to trade more columns with less degrees +use std::iter; +#[rustfmt::skip] +// | q_row_non_first | q_row_enable | q_row_last | alpha | gamma | gamma power 2 | ... | row fingerprint | accumulated fingerprint | +// |-----------------|--------------|------------|-----------|-----------|-----------------| | --------------- | ---------------------- | +// | 0 |1 |0 |alpha | gamma | gamma **2 | ... | F | F | +// | 1 |1 |0 |alpha | gamma | gamma **2 | ... | F | F | +// | 1 |1 |1 |alpha | gamma | gamma **2 | ... | F | F | + +use std::marker::PhantomData; + +use crate::util::Expr; +use eth_types::Field; +use halo2_proofs::{ + circuit::{AssignedCell, Region, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector}, + poly::Rotation, +}; +use itertools::Itertools; + +/// Config for PermutationChipConfig +#[derive(Clone, Debug)] +pub struct PermutationChipConfig { + /// acc_fingerprints + pub acc_fingerprints: Column, + row_fingerprints: Column, + alpha: Column, + power_of_gamma: Vec>, + /// q_row_non_first + pub q_row_non_first: Selector, // 1 between (first, end], exclude first + /// q_row_enable + pub q_row_enable: Selector, // 1 for all rows (including first) + /// q_row_last + pub q_row_last: Selector, // 1 in the last row + + _phantom: PhantomData, + + row_fingerprints_cur_expr: Expression, + acc_fingerprints_cur_expr: Expression, +} + +/// (alpha, gamma, row_fingerprints_prev_cell, row_fingerprints_next_cell, prev_acc_fingerprints, +/// next_acc_fingerprints) +type PermutationAssignedCells = ( + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, +); + +impl PermutationChipConfig { + /// assign + pub fn assign( + &self, + region: &mut Region<'_, F>, + alpha: Value, + gamma: Value, + acc_fingerprints_prev: Value, + col_values: &[Vec>], + prefix: &'static str, + ) -> Result, Error> { + self.annotate_columns_in_region(region, prefix); + + // get accumulated fingerprints of each row + let fingerprints = + get_permutation_fingerprints(col_values, alpha, gamma, acc_fingerprints_prev); + + // power_of_gamma start from gamma**1 + let power_of_gamma = { + let num_of_col = col_values.first().map(|row| row.len()).unwrap_or_default(); + std::iter::successors(Some(gamma), |prev| (*prev * gamma).into()) + .take(num_of_col.saturating_sub(1)) + .collect::>>() + }; + + let mut alpha_first_cell = None; + let mut gamma_first_cell = None; + let mut acc_fingerprints_prev_cell = None; + let mut acc_fingerprints_next_cell = None; + + let mut row_fingerprints_prev_cell = None; + let mut row_fingerprints_next_cell = None; + + for (offset, (row_acc_fingerprints, row_fingerprints)) in fingerprints.iter().enumerate() { + // skip first fingerprint for its prev_fingerprint + if offset != 0 { + self.q_row_non_first.enable(region, offset)?; + } + + self.q_row_enable.enable(region, offset)?; + + let row_acc_fingerprint_cell = region.assign_advice( + || format!("acc_fingerprints at index {}", offset), + self.acc_fingerprints, + offset, + || *row_acc_fingerprints, + )?; + + let row_fingerprints_cell = region.assign_advice( + || format!("row_fingerprints at index {}", offset), + self.row_fingerprints, + offset, + || *row_fingerprints, + )?; + + let alpha_cell = region.assign_advice( + || format!("alpha at index {}", offset), + self.alpha, + offset, + || alpha, + )?; + let gamma_cells = self + .power_of_gamma + .iter() + .zip_eq(power_of_gamma.iter()) + .map(|(col, value)| { + region.assign_advice( + || format!("gamma at index {}", offset), + *col, + offset, + || *value, + ) + }) + .collect::>, Error>>()?; + + if offset == 0 { + alpha_first_cell = Some(alpha_cell); + gamma_first_cell = Some(gamma_cells[0].clone()); + acc_fingerprints_prev_cell = Some(row_acc_fingerprint_cell.clone()); + row_fingerprints_prev_cell = Some(row_fingerprints_cell.clone()) + } + // last offset + if offset == fingerprints.len() - 1 { + self.q_row_last.enable(region, offset)?; + acc_fingerprints_next_cell = Some(row_acc_fingerprint_cell); + row_fingerprints_next_cell = Some(row_fingerprints_cell) + } + } + + Ok(( + alpha_first_cell.unwrap(), + gamma_first_cell.unwrap(), + row_fingerprints_prev_cell.unwrap(), + row_fingerprints_next_cell.unwrap(), + acc_fingerprints_prev_cell.unwrap(), + acc_fingerprints_next_cell.unwrap(), + )) + } + + /// Annotates columns of this gadget embedded within a circuit region. + pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { + [ + ( + self.acc_fingerprints, + "GADGETS_PermutationChipConfig_acc_fingerprints".to_string(), + ), + ( + self.row_fingerprints, + "GADGETS_PermutationChipConfig_row_fingerprints".to_string(), + ), + ( + self.alpha, + "GADGETS_PermutationChipConfig_alpha".to_string(), + ), + ] + .iter() + .cloned() + .chain(self.power_of_gamma.iter().enumerate().map(|(i, col)| { + ( + *col, + format!("GADGETS_PermutationChipConfig_gamma_{}", i + 1), + ) + })) + .for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), col)); + } + + /// acc_fingerprints_cur_expr + pub fn acc_fingerprints_cur_expr(&self) -> Expression { + self.acc_fingerprints_cur_expr.clone() + } + + /// row_fingerprints_cur_expr + pub fn row_fingerprints_cur_expr(&self) -> Expression { + self.row_fingerprints_cur_expr.clone() + } +} + +/// permutation fingerprint gadget +#[derive(Debug, Clone)] +pub struct PermutationChip { + /// config + pub config: PermutationChipConfig, +} + +impl PermutationChip { + /// configure + pub fn configure( + meta: &mut ConstraintSystem, + cols: Vec>, + ) -> PermutationChipConfig { + let acc_fingerprints = meta.advice_column(); + let row_fingerprints = meta.advice_column(); + let alpha = meta.advice_column(); + + // trade more columns with less degrees + let power_of_gamma = (0..cols.len() - 1) + .map(|_| meta.advice_column()) + .collect::>>(); // first element is gamma**1 + + let q_row_non_first = meta.complex_selector(); + let q_row_enable = meta.complex_selector(); + let q_row_last = meta.selector(); + + meta.enable_equality(acc_fingerprints); + meta.enable_equality(row_fingerprints); + meta.enable_equality(alpha); + meta.enable_equality(power_of_gamma[0]); + + let mut acc_fingerprints_cur_expr: Expression = 0.expr(); + let mut row_fingerprints_cur_expr: Expression = 0.expr(); + + meta.create_gate( + "acc_fingerprints_cur = acc_fingerprints_prev * row_fingerprints_cur", + |meta| { + let q_row_non_first = meta.query_selector(q_row_non_first); + let acc_fingerprints_prev = meta.query_advice(acc_fingerprints, Rotation::prev()); + let acc_fingerprints_cur = meta.query_advice(acc_fingerprints, Rotation::cur()); + let row_fingerprints_cur = meta.query_advice(row_fingerprints, Rotation::cur()); + + acc_fingerprints_cur_expr = acc_fingerprints_cur.clone(); + + [q_row_non_first + * (acc_fingerprints_cur - acc_fingerprints_prev * row_fingerprints_cur)] + }, + ); + + meta.create_gate( + "row_fingerprints_cur = fingerprints(column_exprs)", + |meta| { + let alpha = meta.query_advice(alpha, Rotation::cur()); + let row_fingerprints_cur = meta.query_advice(row_fingerprints, Rotation::cur()); + + row_fingerprints_cur_expr = row_fingerprints_cur.clone(); + + let power_of_gamma = iter::once(1.expr()) + .chain( + power_of_gamma + .iter() + .map(|column| meta.query_advice(*column, Rotation::cur())), + ) + .collect::>>(); + + let q_row_enable = meta.query_selector(q_row_enable); + let cols_cur_exprs = cols + .iter() + .map(|col| meta.query_advice(*col, Rotation::cur())) + .collect::>>(); + + let perf_term = cols_cur_exprs + .iter() + .zip_eq(power_of_gamma.iter()) + .map(|(a, b)| a.clone() * b.clone()) + .fold(0.expr(), |a, b| a + b); + [q_row_enable * (row_fingerprints_cur - (alpha - perf_term))] + }, + ); + + meta.create_gate("challenges consistency", |meta| { + let q_row_non_first = meta.query_selector(q_row_non_first); + let alpha_prev = meta.query_advice(alpha, Rotation::prev()); + let alpha_cur = meta.query_advice(alpha, Rotation::cur()); + + [ + vec![q_row_non_first.clone() * (alpha_prev - alpha_cur)], + power_of_gamma + .iter() + .map(|col| { + let gamma_prev = meta.query_advice(*col, Rotation::prev()); + let gamma_cur = meta.query_advice(*col, Rotation::cur()); + q_row_non_first.clone() * (gamma_prev - gamma_cur) + }) + .collect(), + ] + .concat() + }); + + meta.create_gate("power of gamma", |meta| { + let q_row_non_first = meta.query_selector(q_row_non_first); + let gamma = meta.query_advice(power_of_gamma[0], Rotation::cur()); + power_of_gamma + .iter() + .tuple_windows() + .map(|(col1, col2)| { + let col1_cur = meta.query_advice(*col1, Rotation::cur()); + let col2_cur = meta.query_advice(*col2, Rotation::cur()); + q_row_non_first.clone() * (col2_cur - col1_cur * gamma.clone()) + }) + .collect::>>() + }); + + PermutationChipConfig { + acc_fingerprints, + acc_fingerprints_cur_expr, + row_fingerprints, + row_fingerprints_cur_expr, + q_row_non_first, + q_row_enable, + q_row_last, + alpha, + power_of_gamma, + _phantom: PhantomData:: {}, + } + } +} + +impl PermutationChip {} + +/// get permutation fingerprint of rows +pub fn get_permutation_fingerprints( + col_values: &[Vec>], + alpha: Value, + gamma: Value, + acc_fingerprints_prev: Value, +) -> Vec<(Value, Value)> { + let power_of_gamma = { + let num_of_col = col_values.first().map(|row| row.len()).unwrap_or_default(); + std::iter::successors(Some(Value::known(F::ONE)), |prev| (*prev * gamma).into()) + .take(num_of_col) + .collect::>>() + }; + let mut result = vec![]; + col_values + .iter() + .map(|row| { + // row = alpha - (gamma^1 x1 + gamma^2 x2 + ...) + let tmp = row + .iter() + .zip_eq(power_of_gamma.iter()) + .map(|(a, b)| *a * b) + .fold(Value::known(F::ZERO), |prev, cur| prev + cur); + alpha - tmp + }) + .enumerate() + .for_each(|(i, value)| { + // fingerprint = row0 * row1 * ... rowi + // (fingerprinti, rowi) + if i == 0 { + result.push((acc_fingerprints_prev, value)); + } else { + result.push((result[result.len() - 1].0 * value, value)); + } + }); + result +} diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index bd8e732c20..2990ac5cbb 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -24,6 +24,7 @@ rand_chacha = "0.3" paste = "1.0" rand_xorshift = "0.3.0" rand_core = "0.6.4" +itertools = "0.10" mock = { path = "../mock" } [dev-dependencies] diff --git a/integration-tests/src/integration_test_circuits.rs b/integration-tests/src/integration_test_circuits.rs index bfa239d236..3a44ffa6f4 100644 --- a/integration-tests/src/integration_test_circuits.rs +++ b/integration-tests/src/integration_test_circuits.rs @@ -8,10 +8,13 @@ use halo2_proofs::{ self, circuit::Value, dev::{CellValue, MockProver}, - halo2curves::bn256::{Bn256, Fr, G1Affine}, + halo2curves::{ + bn256::{Bn256, Fr, G1Affine}, + pairing::Engine, + }, plonk::{ create_proof, keygen_pk, keygen_vk, permutation::Assembly, verify_proof, Circuit, - ProvingKey, + ConstraintSystem, ProvingKey, }, poly::{ commitment::ParamsProver, @@ -22,6 +25,7 @@ use halo2_proofs::{ }, }, }; +use itertools::Itertools; use lazy_static::lazy_static; use mock::TestContext; use rand_chacha::rand_core::SeedableRng; @@ -37,17 +41,19 @@ use zkevm_circuits::{ pi_circuit::TestPiCircuit, root_circuit::{ compile, Config, EvmTranscript, NativeLoader, PoseidonTranscript, RootCircuit, Shplonk, + SnarkWitness, UserChallenge, }, state_circuit::TestStateCircuit, super_circuit::SuperCircuit, tx_circuit::TestTxCircuit, util::SubCircuit, - witness::{block_convert, Block}, + witness::{block_convert, chunk_convert, Block, Chunk}, }; /// TEST_MOCK_RANDOMNESS const TEST_MOCK_RANDOMNESS: u64 = 0x100; - +/// +const TOTAL_CHUNKS: usize = 1; /// MAX_TXS const MAX_TXS: usize = 4; /// MAX_WITHDRAWALS @@ -70,6 +76,7 @@ const MAX_KECCAK_ROWS: usize = 38000; const MAX_VERTICAL_CIRCUIT_ROWS: usize = 0; const CIRCUITS_PARAMS: FixedCParams = FixedCParams { + total_chunks: TOTAL_CHUNKS, max_rws: MAX_RWS, max_txs: MAX_TXS, max_withdrawals: MAX_WITHDRAWALS, @@ -286,8 +293,8 @@ impl + Circuit> IntegrationTest { match self.key.clone() { Some(key) => key, None => { - let block = new_empty_block(); - let circuit = C::new_from_block(&block); + let (block, chunk) = new_empty_block_chunk(); + let circuit = C::new_from_block(&block, &chunk); let general_params = get_general_params(self.degree); let verifying_key = @@ -307,8 +314,8 @@ impl + Circuit> IntegrationTest { let params = get_general_params(self.degree); let pk = self.get_key(); - let block = new_empty_block(); - let circuit = C::new_from_block(&block); + let (block, chunk) = new_empty_block_chunk(); + let circuit = C::new_from_block(&block, &chunk); let instance = circuit.instance(); let protocol = compile( @@ -321,8 +328,12 @@ impl + Circuit> IntegrationTest { let circuit = RootCircuit::>::new( ¶ms, &protocol, - Value::unknown(), - Value::unknown(), + vec![SnarkWitness::new( + &protocol, + Value::unknown(), + Value::unknown(), + )], + None, ) .unwrap(); @@ -351,10 +362,26 @@ impl + Circuit> IntegrationTest { let fixed = mock_prover.fixed(); if let Some(prev_fixed) = self.fixed.clone() { - assert!( - fixed.eq(&prev_fixed), - "circuit fixed columns are not constant for different witnesses" - ); + fixed + .iter() + .enumerate() + .zip_eq(prev_fixed.iter()) + .for_each(|((index, col1), col2)| { + if !col1.eq(col2) { + println!("on column index {} not equal", index); + col1.iter().enumerate().zip_eq(col2.iter()).for_each( + |((index, cellv1), cellv2)| { + assert!( + cellv1.eq(cellv2), + "cellv1 {:?} != cellv2 {:?} on index {}", + cellv1, + cellv2, + index + ); + }, + ); + } + }); } else { self.fixed = Some(fixed.clone()); } @@ -376,6 +403,24 @@ impl + Circuit> IntegrationTest { match self.root_fixed.clone() { Some(prev_fixed) => { + fixed.iter().enumerate().zip_eq(prev_fixed.iter()).for_each( + |((index, col1), col2)| { + if !col1.eq(col2) { + println!("on column index {} not equal", index); + col1.iter().enumerate().zip_eq(col2.iter()).for_each( + |((index, cellv1), cellv2)| { + assert!( + cellv1.eq(cellv2), + "cellv1 {:?} != cellv2 {:?} on index {}", + cellv1, + cellv2, + index + ); + }, + ); + } + }, + ); assert!( fixed.eq(&prev_fixed), "root circuit fixed columns are not constant for different witnesses" @@ -417,8 +462,9 @@ impl + Circuit> IntegrationTest { block_tag, ); let mut block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); block.randomness = Fr::from(TEST_MOCK_RANDOMNESS); - let circuit = C::new_from_block(&block); + let circuit = C::new_from_block(&block, &chunk); let instance = circuit.instance(); #[allow(clippy::collapsible_else_if)] @@ -432,6 +478,11 @@ impl + Circuit> IntegrationTest { .with_num_instance(instance.iter().map(|instance| instance.len()).collect()), ); + // get chronological_rwtable and byaddr_rwtable columns index + let mut cs = ConstraintSystem::<::Fr>::default(); + let config = SuperCircuit::configure(&mut cs); + let rwtable_columns = config.get_rwtable_columns(); + let proof = { let mut proof_cache = PROOF_CACHE.lock().await; if let Some(proof) = proof_cache.get(&proof_name) { @@ -447,11 +498,19 @@ impl + Circuit> IntegrationTest { }; log::info!("root circuit new"); + let user_challenge = &UserChallenge { + column_indexes: rwtable_columns, + num_challenges: 2, // alpha, gamma + }; let root_circuit = RootCircuit::>::new( ¶ms, &protocol, - Value::known(&instance), - Value::known(&proof), + vec![SnarkWitness::new( + &protocol, + Value::known(&instance), + Value::known(&proof), + )], + Some(user_challenge), ) .unwrap(); @@ -486,16 +545,17 @@ impl + Circuit> IntegrationTest { } } -fn new_empty_block() -> Block { +fn new_empty_block_chunk() -> (Block, Chunk) { let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) .unwrap() .into(); - let mut builder = BlockData::new_from_geth_data_with_params(block.clone(), CIRCUITS_PARAMS) - .new_circuit_input_builder(); - builder + let builder = BlockData::new_from_geth_data_with_params(block.clone(), CIRCUITS_PARAMS) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); - block_convert(&builder).unwrap() + let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); + (block, chunk) } fn get_general_params(degree: u32) -> ParamsKZG { diff --git a/integration-tests/tests/circuit_input_builder.rs b/integration-tests/tests/circuit_input_builder.rs index f5eb9de752..e68f2eb67a 100644 --- a/integration-tests/tests/circuit_input_builder.rs +++ b/integration-tests/tests/circuit_input_builder.rs @@ -16,6 +16,7 @@ async fn test_circuit_input_builder_block(block_num: u64) { let cli = BuilderClient::new( cli, FixedCParams { + total_chunks: 1, max_rws: 16384, max_txs: 1, max_withdrawals: 1, diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index a2b2a3c849..2282d41eb8 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -17,7 +17,7 @@ use thiserror::Error; use zkevm_circuits::{ super_circuit::SuperCircuit, test_util::{CircuitTestBuilder, CircuitTestError}, - witness::Block, + witness::{Block, Chunk}, }; #[derive(PartialEq, Eq, Error, Debug)] @@ -324,10 +324,11 @@ pub fn run_test( eth_block: eth_block.clone(), }; - let mut builder; + let builder; if !circuits_config.super_circuit { let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 1, max_withdrawals: 1, max_rws: 55000, @@ -341,15 +342,16 @@ pub fn run_test( }; let block_data = BlockData::new_from_geth_data_with_params(geth_data, circuits_params); - builder = block_data.new_circuit_input_builder(); - builder + builder = block_data + .new_circuit_input_builder() .handle_block(ð_block, &geth_traces) .map_err(|err| StateTestError::CircuitInput(err.to_string()))?; let block: Block = zkevm_circuits::evm_circuit::witness::block_convert(&builder).unwrap(); - - CircuitTestBuilder::<1, 1>::new_from_block(block) + let chunks: Vec> = + zkevm_circuits::evm_circuit::witness::chunk_convert(&block, &builder).unwrap(); + CircuitTestBuilder::<1, 1>::new_from_block(block, chunks) .run_with_result() .map_err(|err| match err { CircuitTestError::VerificationFailed { reasons, .. } => { @@ -367,6 +369,7 @@ pub fn run_test( geth_data.sign(&wallets); let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 1, max_withdrawals: 1, max_calldata: 32, @@ -378,10 +381,13 @@ pub fn run_test( max_keccak_rows: 0, max_vertical_circuit_rows: 0, }; - let (k, circuit, instance, _builder) = + let (k, mut circuits, mut instances, _builder) = SuperCircuit::::build(geth_data, circuits_params, Fr::from(0x100)).unwrap(); builder = _builder; + let circuit = circuits.remove(0); + let instance = instances.remove(0); + let prover = MockProver::run(k, &circuit, instance).unwrap(); prover .verify() diff --git a/zkevm-circuits/src/bin/stats/main.rs b/zkevm-circuits/src/bin/stats/main.rs index dbf9b4b9a3..da58dd0615 100644 --- a/zkevm-circuits/src/bin/stats/main.rs +++ b/zkevm-circuits/src/bin/stats/main.rs @@ -31,11 +31,11 @@ use zkevm_circuits::{ pi_circuit::{PiCircuitConfig, PiCircuitConfigArgs}, state_circuit::{StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, SigTable, - TxTable, UXTable, WdTable, + BlockTable, BytecodeTable, ChunkCtxTable, CopyTable, ExpTable, KeccakTable, MptTable, + RwTable, SigTable, TxTable, UXTable, WdTable, }, tx_circuit::{TxCircuitConfig, TxCircuitConfigArgs}, - util::{Challenges, SubCircuitConfig}, + util::{chunk_ctx::ChunkContextConfig, Challenges, SubCircuitConfig}, }; fn main() { @@ -222,7 +222,11 @@ fn get_exec_steps_occupancy() { keccak_table, LOOKUP_CONFIG[6].1, exp_table, - LOOKUP_CONFIG[7].1 + LOOKUP_CONFIG[7].1, + sig_table, + LOOKUP_CONFIG[8].1, + chunk_ctx_table, + LOOKUP_CONFIG[9].1 ); } @@ -266,6 +270,10 @@ fn record_stats( let u16_table = UXTable::construct(meta); stats.record_shared("u16_table", meta); + let chunkctx_table = ChunkCtxTable::construct(meta); + // chunkctx table with gates + stats.record("chunkctx_table", meta); + // Use a mock randomness instead of the randomness derived from the challenge // (either from mock or real prover) to help debugging assignments. let power_of_randomness: [Expression; 31] = @@ -343,6 +351,10 @@ fn record_stats( stats.record("state", meta); let exp_circuit = ExpCircuitConfig::new(meta, exp_table); stats.record("exp", meta); + + let sig_table = SigTable::construct(meta); + + let chunk_ctx_config = ChunkContextConfig::new(meta, &challenges); let evm_circuit = EvmCircuitConfig::new( meta, EvmCircuitConfigArgs { @@ -357,6 +369,7 @@ fn record_stats( u8_table, u16_table, sig_table, + chunk_ctx_config, feature_config, }, ); diff --git a/zkevm-circuits/src/bytecode_circuit.rs b/zkevm-circuits/src/bytecode_circuit.rs index b02f233868..3cc4859a0a 100644 --- a/zkevm-circuits/src/bytecode_circuit.rs +++ b/zkevm-circuits/src/bytecode_circuit.rs @@ -20,7 +20,7 @@ use crate::{ word::{empty_code_hash_word_value, Word32, WordExpr, WordLoHi}, Challenges, Expr, SubCircuit, SubCircuitConfig, }, - witness::{self}, + witness::{self, Chunk}, }; use bus_mapping::state_db::{CodeDB, EMPTY_CODE_HASH_LE}; use eth_types::{Bytecode, Field}; @@ -800,15 +800,15 @@ impl SubCircuit for BytecodeCircuit { 6 } - fn new_from_block(block: &witness::Block) -> Self { - Self::new(block.bytecodes.clone(), block.circuits_params.max_bytecode) + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { + Self::new(block.bytecodes.clone(), chunk.fixed_param.max_bytecode) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { ( block.bytecodes.num_rows_required_for_bytecode_table(), - block.circuits_params.max_bytecode, + chunk.fixed_param.max_bytecode, ) } diff --git a/zkevm-circuits/src/copy_circuit.rs b/zkevm-circuits/src/copy_circuit.rs index 644a951900..4466bf9694 100644 --- a/zkevm-circuits/src/copy_circuit.rs +++ b/zkevm-circuits/src/copy_circuit.rs @@ -18,7 +18,7 @@ use crate::{ }, util::{Challenges, SubCircuit, SubCircuitConfig}, witness, - witness::{RwMap, Transaction}, + witness::{Chunk, Rw, RwMap, Transaction}, }; use bus_mapping::{ circuit_input_builder::{CopyDataType, CopyEvent}, @@ -793,6 +793,8 @@ pub struct ExternalData { pub max_rws: usize, /// StateCircuit -> rws pub rws: RwMap, + /// Prev chunk last Rw + pub prev_chunk_last_rw: Option, /// BytecodeCircuit -> bytecodes pub bytecodes: CodeDB, } @@ -838,11 +840,8 @@ impl CopyCircuit { /// to assign lookup tables. This constructor is only suitable to be /// used by the SuperCircuit, which already assigns the external lookup /// tables. - pub fn new_from_block_no_external(block: &witness::Block) -> Self { - Self::new( - block.copy_events.clone(), - block.circuits_params.max_copy_rows, - ) + pub fn new_from_block_no_external(block: &witness::Block, chunk: &Chunk) -> Self { + Self::new(block.copy_events.clone(), chunk.fixed_param.max_copy_rows) } } @@ -855,23 +854,28 @@ impl SubCircuit for CopyCircuit { 6 } - fn new_from_block(block: &witness::Block) -> Self { + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { + let chunked_copy_events = block + .copy_events + .get(chunk.chunk_context.initial_copy_index..chunk.chunk_context.end_copy_index) + .unwrap_or_default(); Self::new_with_external_data( - block.copy_events.clone(), - block.circuits_params.max_copy_rows, + chunked_copy_events.to_owned(), + chunk.fixed_param.max_copy_rows, ExternalData { - max_txs: block.circuits_params.max_txs, - max_calldata: block.circuits_params.max_calldata, + max_txs: chunk.fixed_param.max_txs, + max_calldata: chunk.fixed_param.max_calldata, txs: block.txs.clone(), - max_rws: block.circuits_params.max_rws, - rws: block.rws.clone(), + max_rws: chunk.fixed_param.max_rws, + rws: chunk.chrono_rws.clone(), + prev_chunk_last_rw: chunk.prev_chunk_last_chrono_rw, bytecodes: block.bytecodes.clone(), }, ) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { ( block .copy_events @@ -879,7 +883,7 @@ impl SubCircuit for CopyCircuit { .map(|c| c.bytes.len() * 2) .sum::() + 2, - block.circuits_params.max_copy_rows, + chunk.fixed_param.max_copy_rows, ) } diff --git a/zkevm-circuits/src/copy_circuit/dev.rs b/zkevm-circuits/src/copy_circuit/dev.rs index cd5b4c2a5c..4e45dd2b0c 100644 --- a/zkevm-circuits/src/copy_circuit/dev.rs +++ b/zkevm-circuits/src/copy_circuit/dev.rs @@ -61,8 +61,9 @@ impl Circuit for CopyCircuit { config.0.rw_table.load( &mut layouter, - &self.external_data.rws.table_assignments(), + &self.external_data.rws.table_assignments(true), self.external_data.max_rws, + self.external_data.prev_chunk_last_rw, )?; config diff --git a/zkevm-circuits/src/copy_circuit/test.rs b/zkevm-circuits/src/copy_circuit/test.rs index 62a690f6ba..7f2056847b 100644 --- a/zkevm-circuits/src/copy_circuit/test.rs +++ b/zkevm-circuits/src/copy_circuit/test.rs @@ -2,7 +2,7 @@ use crate::{ copy_circuit::*, evm_circuit::{test::rand_bytes, witness::block_convert}, util::unusable_rows, - witness::Block, + witness::{chunk_convert, Block}, }; use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, FixedCParams}, @@ -43,17 +43,23 @@ pub fn test_copy_circuit( pub fn test_copy_circuit_from_block( k: u32, block: Block, + chunk: Chunk, ) -> Result<(), Vec> { + let chunked_copy_events = block + .copy_events + .get(chunk.chunk_context.initial_copy_index..chunk.chunk_context.end_copy_index) + .unwrap_or_default(); test_copy_circuit::( k, - block.copy_events, - block.circuits_params.max_copy_rows, + chunked_copy_events.to_owned(), + chunk.fixed_param.max_copy_rows, ExternalData { - max_txs: block.circuits_params.max_txs, - max_calldata: block.circuits_params.max_calldata, + max_txs: chunk.fixed_param.max_txs, + max_calldata: chunk.fixed_param.max_calldata, txs: block.txs, - max_rws: block.circuits_params.max_rws, - rws: block.rws, + max_rws: chunk.fixed_param.max_rws, + rws: chunk.chrono_rws, + prev_chunk_last_rw: chunk.prev_chunk_last_chrono_rw, bytecodes: block.bytecodes, }, ) @@ -163,48 +169,51 @@ fn gen_tx_log_data() -> CircuitInputBuilder { let test_ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(code).unwrap(); let block: GethData = test_ctx.into(); // Needs default params for variadic check - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) - .new_circuit_input_builder(); - builder + + BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder + .unwrap() } #[test] fn copy_circuit_valid_calldatacopy() { let builder = gen_calldatacopy_data(); let block = block_convert::(&builder).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + assert_eq!(test_copy_circuit_from_block(14, block, chunk), Ok(())); } #[test] fn copy_circuit_valid_codecopy() { let builder = gen_codecopy_data(); let block = block_convert::(&builder).unwrap(); - assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + assert_eq!(test_copy_circuit_from_block(10, block, chunk), Ok(())); } #[test] fn copy_circuit_valid_extcodecopy() { let builder = gen_extcodecopy_data(); let block = block_convert::(&builder).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + assert_eq!(test_copy_circuit_from_block(14, block, chunk), Ok(())); } #[test] fn copy_circuit_valid_sha3() { let builder = gen_sha3_data(); let block = block_convert::(&builder).unwrap(); - assert_eq!(test_copy_circuit_from_block(14, block), Ok(())); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + assert_eq!(test_copy_circuit_from_block(14, block, chunk), Ok(())); } #[test] fn copy_circuit_valid_tx_log() { let builder = gen_tx_log_data(); let block = block_convert::(&builder).unwrap(); - assert_eq!(test_copy_circuit_from_block(10, block), Ok(())); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + assert_eq!(test_copy_circuit_from_block(10, block, chunk), Ok(())); } #[test] @@ -216,9 +225,10 @@ fn copy_circuit_invalid_calldatacopy() { builder.block.copy_events[0].bytes[0].0.wrapping_add(1); let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); assert_error_matches( - test_copy_circuit_from_block(14, block), + test_copy_circuit_from_block(14, block, chunk), vec!["Memory lookup", "Tx calldata lookup"], ); } @@ -232,9 +242,10 @@ fn copy_circuit_invalid_codecopy() { builder.block.copy_events[0].bytes[0].0.wrapping_add(1); let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); assert_error_matches( - test_copy_circuit_from_block(10, block), + test_copy_circuit_from_block(10, block, chunk), vec!["Memory lookup", "Bytecode lookup"], ); } @@ -248,9 +259,10 @@ fn copy_circuit_invalid_extcodecopy() { builder.block.copy_events[0].bytes[0].0.wrapping_add(1); let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); assert_error_matches( - test_copy_circuit_from_block(14, block), + test_copy_circuit_from_block(14, block, chunk), vec!["Memory lookup", "Bytecode lookup"], ); } @@ -264,9 +276,10 @@ fn copy_circuit_invalid_sha3() { builder.block.copy_events[0].bytes[0].0.wrapping_add(1); let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); assert_error_matches( - test_copy_circuit_from_block(14, block), + test_copy_circuit_from_block(14, block, chunk), vec!["Memory lookup"], ); } @@ -280,9 +293,10 @@ fn copy_circuit_invalid_tx_log() { builder.block.copy_events[0].bytes[0].0.wrapping_add(1); let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); assert_error_matches( - test_copy_circuit_from_block(10, block), + test_copy_circuit_from_block(10, block, chunk), vec!["Memory lookup", "TxLog lookup"], ); } @@ -295,10 +309,8 @@ fn variadic_size_check() { let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) .unwrap() .into(); - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) - .new_circuit_input_builder(); - builder + let builder = BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); let block2 = block_convert::(&builder).unwrap(); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 6e1e5b1147..b595225b31 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -1,5 +1,6 @@ //! The EVM circuit implementation. +use gadgets::permutation::{PermutationChip, PermutationChipConfig}; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, plonk::*, @@ -13,9 +14,9 @@ pub(crate) mod util; #[cfg(test)] pub(crate) mod test; -use self::step::HasExecutionState; #[cfg(feature = "test-circuits")] pub use self::EvmCircuit as TestEvmCircuit; +use self::{step::HasExecutionState, witness::rw::ToVec}; pub use crate::witness; use crate::{ @@ -24,7 +25,8 @@ use crate::{ BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, RwTable, SigTable, TxTable, UXTable, }, - util::{Challenges, SubCircuit, SubCircuitConfig}, + util::{chunk_ctx::ChunkContextConfig, Challenges, SubCircuit, SubCircuitConfig}, + witness::{Chunk, RwMap}, }; use bus_mapping::{circuit_input_builder::FeatureConfig, evm::OpcodeId}; use eth_types::Field; @@ -44,13 +46,21 @@ pub struct EvmCircuitConfig { pub execution: Box>, // External tables tx_table: TxTable, - rw_table: RwTable, + pub(crate) rw_table: RwTable, bytecode_table: BytecodeTable, block_table: BlockTable, copy_table: CopyTable, keccak_table: KeccakTable, exp_table: ExpTable, sig_table: SigTable, + /// rw permutation config + pub rw_permutation_config: PermutationChipConfig, + + // pi for chunk context continuity + pi_chunk_continuity: Column, + + // chunk_ctx_config + chunk_ctx_config: ChunkContextConfig, } /// Circuit configuration arguments @@ -77,6 +87,8 @@ pub struct EvmCircuitConfigArgs { pub u16_table: UXTable<16>, /// SigTable pub sig_table: SigTable, + /// chunk_ctx config + pub chunk_ctx_config: ChunkContextConfig, /// Feature config pub feature_config: FeatureConfig, } @@ -99,10 +111,12 @@ impl SubCircuitConfig for EvmCircuitConfig { u8_table, u16_table, sig_table, + chunk_ctx_config, feature_config, }: Self::ConfigArgs, ) -> Self { let fixed_table = [(); 4].map(|_| meta.fixed_column()); + let execution = Box::new(ExecutionConfig::configure( meta, challenges, @@ -117,11 +131,12 @@ impl SubCircuitConfig for EvmCircuitConfig { &keccak_table, &exp_table, &sig_table, + &chunk_ctx_config.chunk_ctx_table, + &chunk_ctx_config.is_first_chunk, + &chunk_ctx_config.is_last_chunk, feature_config, )); - u8_table.annotate_columns(meta); - u16_table.annotate_columns(meta); fixed_table.iter().enumerate().for_each(|(idx, &col)| { meta.annotate_lookup_any_column(col, || format!("fix_table_{}", idx)) }); @@ -135,6 +150,15 @@ impl SubCircuitConfig for EvmCircuitConfig { u8_table.annotate_columns(meta); u16_table.annotate_columns(meta); sig_table.annotate_columns(meta); + chunk_ctx_config.chunk_ctx_table.annotate_columns(meta); + + let rw_permutation_config = PermutationChip::configure( + meta, + >::advice_columns(&rw_table), + ); + + let pi_chunk_continuity = meta.instance_column(); + meta.enable_equality(pi_chunk_continuity); Self { fixed_table, @@ -149,6 +173,9 @@ impl SubCircuitConfig for EvmCircuitConfig { keccak_table, exp_table, sig_table, + rw_permutation_config, + chunk_ctx_config, + pi_chunk_continuity, } } } @@ -183,31 +210,35 @@ impl EvmCircuitConfig { pub struct EvmCircuit { /// Block pub block: Option>, + /// Chunk + pub chunk: Option>, fixed_table_tags: Vec, } impl EvmCircuit { /// Return a new EvmCircuit - pub fn new(block: Block) -> Self { + pub fn new(block: Block, chunk: Chunk) -> Self { Self { block: Some(block), + chunk: Some(chunk), fixed_table_tags: FixedTableTag::iter().collect(), } } #[cfg(any(test, feature = "test-circuits"))] /// Construct the EvmCircuit with only subset of Fixed table tags required by tests to save /// testing time - pub(crate) fn get_test_circuit_from_block(block: Block) -> Self { + pub(crate) fn get_test_circuit_from_block(block: Block, chunk: Chunk) -> Self { let fixed_table_tags = detect_fixed_table_tags(&block); Self { block: Some(block), + chunk: Some(chunk), fixed_table_tags, } } #[cfg(any(test, feature = "test-circuits"))] /// Calculate which rows are "actually" used in the circuit - pub(crate) fn get_active_rows(block: &Block) -> (Vec, Vec) { - let max_offset = Self::get_num_rows_required(block); + pub(crate) fn get_active_rows(block: &Block, chunk: &Chunk) -> (Vec, Vec) { + let max_offset = Self::get_num_rows_required(block, chunk); // some gates are enabled on all rows let gates_row_ids = (0..max_offset).collect(); // lookups are enabled at "q_step" rows and byte lookup rows @@ -216,25 +247,29 @@ impl EvmCircuit { } /// Get the minimum number of rows required to process the block /// If unspecified, then compute it - pub(crate) fn get_num_rows_required(block: &Block) -> usize { - let evm_rows = block.circuits_params.max_evm_rows; + pub(crate) fn get_num_rows_required(block: &Block, chunk: &Chunk) -> usize { + let evm_rows = chunk.fixed_param.max_evm_rows; if evm_rows == 0 { - Self::get_min_num_rows_required(block) + Self::get_min_num_rows_required(block, chunk) } else { // It must have at least one unused row. - block.circuits_params.max_evm_rows + 1 + chunk.fixed_param.max_evm_rows + 1 } } /// Compute the minimum number of rows required to process the block - fn get_min_num_rows_required(block: &Block) -> usize { + fn get_min_num_rows_required(block: &Block, chunk: &Chunk) -> usize { let mut num_rows = 0; for transaction in &block.txs { for step in transaction.steps() { - num_rows += step.execution_state().get_step_height(); + if chunk.chunk_context.initial_rwc <= step.rwc.0 + || step.rwc.0 < chunk.chunk_context.end_rwc + { + num_rows += step.execution_state().get_step_height(); + } } } - // It must have one row for EndBlock and at least one unused one + // It must have one row for EndBlock/EndChunk and at least one unused one num_rows + 2 } } @@ -248,13 +283,14 @@ impl SubCircuit for EvmCircuit { MAX_STEP_HEIGHT + STEP_STATE_HEIGHT + 3 } - fn new_from_block(block: &witness::Block) -> Self { - Self::new(block.clone()) + fn new_from_block(block: &witness::Block, chunk: &witness::Chunk) -> Self { + Self::new(block.clone(), chunk.clone()) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - let num_rows_required_for_execution_steps: usize = Self::get_num_rows_required(block); + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { + let num_rows_required_for_execution_steps: usize = + Self::get_num_rows_required(block, chunk); let num_rows_required_for_fixed_table: usize = detect_fixed_table_tags(block) .iter() .map(|tag| tag.build::().count()) @@ -264,7 +300,7 @@ impl SubCircuit for EvmCircuit { num_rows_required_for_execution_steps, num_rows_required_for_fixed_table, ), - block.circuits_params.max_evm_rows, + chunk.fixed_param.max_evm_rows, ) } @@ -276,9 +312,93 @@ impl SubCircuit for EvmCircuit { layouter: &mut impl Layouter, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); + let chunk = self.chunk.as_ref().unwrap(); config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; - config.execution.assign_block(layouter, block, challenges) + + let _max_offset_index = config + .execution + .assign_block(layouter, block, chunk, challenges)?; + + let (rw_rows_padding, _) = RwMap::table_assignments_padding( + &chunk.chrono_rws.table_assignments(true), + chunk.fixed_param.max_rws, + chunk.prev_chunk_last_chrono_rw, + ); + let ( + alpha_cell, + gamma_cell, + row_fingerprints_prev_cell, + row_fingerprints_next_cell, + acc_fingerprints_prev_cell, + acc_fingerprints_next_cell, + ) = layouter.assign_region( + || "evm circuit", + |mut region| { + region.name_column(|| "EVM_pi_chunk_continuity", config.pi_chunk_continuity); + config.rw_table.load_with_region( + &mut region, + // pass non-padding rws to `load_with_region` since it will be padding + // inside + &chunk.chrono_rws.table_assignments(true), + // align with state circuit to padding to same max_rws + chunk.fixed_param.max_rws, + chunk.prev_chunk_last_chrono_rw, + )?; + let permutation_cells = config.rw_permutation_config.assign( + &mut region, + Value::known(chunk.permu_alpha), + Value::known(chunk.permu_gamma), + // Value::known(chunk.chrono_rw_prev_fingerprint), + Value::known(chunk.chrono_rw_fingerprints.prev_mul_acc), + &rw_rows_padding.to2dvec(), + "evm circuit", + )?; + Ok(permutation_cells) + }, + )?; + + // constrain fields related to proof chunk in public input + [ + alpha_cell, + gamma_cell, + row_fingerprints_prev_cell, + row_fingerprints_next_cell, + acc_fingerprints_prev_cell, + acc_fingerprints_next_cell, + ] + .iter() + .enumerate() + .try_for_each(|(i, cell)| { + layouter.constrain_instance(cell.cell(), config.pi_chunk_continuity, i) + })?; + Ok(()) + } + + /// Compute the public inputs for this circuit. + fn instance(&self) -> Vec> { + let chunk = self.chunk.as_ref().unwrap(); + + let (rw_table_chunked_index, rw_table_total_chunks) = + (chunk.chunk_context.idx, chunk.chunk_context.total_chunks); + + vec![ + vec![ + F::from(rw_table_chunked_index as u64), + F::from(rw_table_chunked_index as u64) + F::ONE, + F::from(rw_table_total_chunks as u64), + F::from(chunk.chunk_context.initial_rwc as u64), + F::from(chunk.chunk_context.end_rwc as u64), + ], + vec![ + chunk.permu_alpha, + chunk.permu_gamma, + chunk.chrono_rw_fingerprints.prev_ending_row, + chunk.chrono_rw_fingerprints.ending_row, + chunk.chrono_rw_fingerprints.prev_mul_acc, + chunk.chrono_rw_fingerprints.mul_acc, + ], + ] } } @@ -358,8 +478,12 @@ pub(crate) mod cached { } impl EvmCircuitCached { - pub(crate) fn get_test_circuit_from_block(block: Block) -> Self { - Self(EvmCircuit::::get_test_circuit_from_block(block)) + pub(crate) fn get_test_circuit_from_block(block: Block, chunk: Chunk) -> Self { + Self(EvmCircuit::::get_test_circuit_from_block(block, chunk)) + } + + pub(crate) fn instance(&self) -> Vec> { + self.0.instance() } } } @@ -395,6 +519,7 @@ impl Circuit for EvmCircuit { let u16_table = UXTable::construct(meta); let challenges = Challenges::construct(meta); let challenges_expr = challenges.exprs(meta); + let chunk_ctx_config = ChunkContextConfig::new(meta, &challenges_expr); let sig_table = SigTable::construct(meta); ( @@ -412,6 +537,7 @@ impl Circuit for EvmCircuit { u8_table, u16_table, sig_table, + chunk_ctx_config, feature_config: params, }, ), @@ -429,6 +555,7 @@ impl Circuit for EvmCircuit { mut layouter: impl Layouter, ) -> Result<(), Error> { let block = self.block.as_ref().unwrap(); + let chunk = self.chunk.as_ref().unwrap(); let (config, challenges) = config; let challenges = challenges.values(&mut layouter); @@ -436,29 +563,33 @@ impl Circuit for EvmCircuit { config.tx_table.load( &mut layouter, &block.txs, - block.circuits_params.max_txs, - block.circuits_params.max_calldata, - )?; - block.rws.check_rw_counter_sanity(); - config.rw_table.load( - &mut layouter, - &block.rws.table_assignments(), - block.circuits_params.max_rws, + chunk.fixed_param.max_txs, + chunk.fixed_param.max_calldata, )?; + chunk.chrono_rws.check_rw_counter_sanity(); config .bytecode_table .load(&mut layouter, block.bytecodes.clone())?; config.block_table.load(&mut layouter, &block.context)?; - config.copy_table.load(&mut layouter, block, &challenges)?; + config + .copy_table + .load(&mut layouter, block, chunk, &challenges)?; config .keccak_table .dev_load(&mut layouter, &block.sha3_inputs, &challenges)?; - config.exp_table.load(&mut layouter, block)?; + config.exp_table.load(&mut layouter, block, chunk)?; config.u8_table.load(&mut layouter)?; config.u16_table.load(&mut layouter)?; config.sig_table.dev_load(&mut layouter, block)?; + // synthesize chunk context + config.chunk_ctx_config.assign_chunk_context( + &mut layouter, + &chunk.chunk_context, + Self::get_num_rows_required(block, chunk) - 1, + )?; + self.synthesize_sub(&config, &challenges, &mut layouter) } } @@ -469,14 +600,14 @@ mod evm_circuit_stats { evm_circuit::EvmCircuit, test_util::CircuitTestBuilder, util::{unusable_rows, SubCircuit}, - witness::block_convert, + witness::{block_convert, chunk_convert}, }; use bus_mapping::{ circuit_input_builder::{FeatureConfig, FixedCParams}, mock::BlockData, }; - use eth_types::{bytecode, geth_types::GethData}; + use eth_types::{address, bytecode, geth_types::GethData, Word}; use halo2_proofs::{self, dev::MockProver, halo2curves::bn256::Fr}; use mock::test_ctx::{ @@ -516,12 +647,72 @@ mod evm_circuit_stats { CircuitTestBuilder::new_from_test_ctx( TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b).unwrap(), ) - .block_modifier(Box::new(|block| { - block.circuits_params.max_evm_rows = (1 << 18) - 100 + .block_modifier(Box::new(|_block, chunk| { + chunk + .iter_mut() + .for_each(|chunk| chunk.fixed_param.max_evm_rows = (1 << 18) - 100); })) .run(); } + #[test] + fn reproduce_heavytest_error() { + let bytecode = bytecode! { + GAS + STOP + }; + + let addr_a = address!("0x000000000000000000000000000000000000AAAA"); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + + let block: GethData = TestContext::<2, 1>::new( + None, + |accs| { + accs[0] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode); + accs[1].address(addr_a).balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .into(); + + let circuits_params = FixedCParams { + total_chunks: 1, + max_txs: 1, + max_withdrawals: 5, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + max_vertical_circuit_rows: 0, + }; + let builder = BlockData::new_from_geth_data_with_params(block.clone(), circuits_params) + .new_circuit_input_builder() + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + let block = block_convert::(&builder).unwrap(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + let k = block.get_test_degree(&chunk); + let circuit = EvmCircuit::::get_test_circuit_from_block(block, chunk); + let instance = circuit.instance(); + let prover1 = MockProver::::run(k, &circuit, instance).unwrap(); + let res = prover1.verify(); + if let Err(err) = res { + panic!("Failed verification {:?}", err); + } + } #[test] fn variadic_size_check() { let params = FixedCParams { @@ -532,16 +723,17 @@ mod evm_circuit_stats { let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) .unwrap() .into(); - let mut builder = BlockData::new_from_geth_data_with_params(block.clone(), params) - .new_circuit_input_builder(); - builder + let builder = BlockData::new_from_geth_data_with_params(block.clone(), params) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); let block = block_convert::(&builder).unwrap(); - let k = block.get_test_degree(); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + let k = block.get_test_degree(&chunk); - let circuit = EvmCircuit::::get_test_circuit_from_block(block); - let prover1 = MockProver::::run(k, &circuit, vec![]).unwrap(); + let circuit = EvmCircuit::::get_test_circuit_from_block(block, chunk); + let instance = circuit.instance(); + let prover1 = MockProver::::run(k, &circuit, instance).unwrap(); let code = bytecode! { STOP @@ -554,16 +746,31 @@ mod evm_circuit_stats { ) .unwrap() .into(); - let mut builder = BlockData::new_from_geth_data_with_params(block.clone(), params) - .new_circuit_input_builder(); - builder + let builder = BlockData::new_from_geth_data_with_params(block.clone(), params) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); let block = block_convert::(&builder).unwrap(); - let k = block.get_test_degree(); - let circuit = EvmCircuit::::get_test_circuit_from_block(block); - let prover2 = MockProver::::run(k, &circuit, vec![]).unwrap(); - + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + let k = block.get_test_degree(&chunk); + let circuit = EvmCircuit::::get_test_circuit_from_block(block, chunk); + let instance = circuit.instance(); + let prover2 = MockProver::::run(k, &circuit, instance).unwrap(); + + assert_eq!(prover1.fixed().len(), prover2.fixed().len()); + prover1 + .fixed() + .iter() + .zip(prover2.fixed().iter()) + .enumerate() + .for_each(|(i, (f1, f2))| { + assert_eq!( + f1, f2, + "at index {}. Usually it happened when mismatch constant constraint, e.g. + region.constrain_constant() are calling in-consisntent", + i + ) + }); assert_eq!(prover1.fixed(), prover2.fixed()); assert_eq!(prover1.permutation(), prover2.permutation()); } diff --git a/zkevm-circuits/src/evm_circuit/execution.rs b/zkevm-circuits/src/evm_circuit/execution.rs index 0e106b8412..e4c628ed16 100644 --- a/zkevm-circuits/src/evm_circuit/execution.rs +++ b/zkevm-circuits/src/evm_circuit/execution.rs @@ -1,8 +1,9 @@ use super::{ param::{ - BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, EXP_TABLE_LOOKUPS, - FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_COPY_COLUMNS, N_PHASE1_COLUMNS, N_U16_LOOKUPS, - N_U8_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, TX_TABLE_LOOKUPS, + BLOCK_TABLE_LOOKUPS, BYTECODE_TABLE_LOOKUPS, CHUNK_CTX_TABLE_LOOKUPS, COPY_TABLE_LOOKUPS, + EXP_TABLE_LOOKUPS, FIXED_TABLE_LOOKUPS, KECCAK_TABLE_LOOKUPS, N_COPY_COLUMNS, + N_PHASE1_COLUMNS, N_U16_LOOKUPS, N_U8_LOOKUPS, RW_TABLE_LOOKUPS, SIG_TABLE_LOOKUPS, + TX_TABLE_LOOKUPS, }, step::HasExecutionState, util::{instrumentation::Instrument, CachedRegion, StoredExpression}, @@ -18,9 +19,9 @@ use crate::{ }, evaluate_expression, rlc, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, - table::LookupTable, + table::{chunk_ctx_table::ChunkCtxFieldTag, LookupTable}, util::{ cell_manager::{CMFixedWidthStrategy, CellManager, CellType}, Challenges, Expr, @@ -28,7 +29,8 @@ use crate::{ }; use bus_mapping::{circuit_input_builder::FeatureConfig, operation::Target}; use eth_types::{evm_unimplemented, Field}; -use gadgets::util::not; + +use gadgets::{is_zero::IsZeroConfig, util::not}; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{ @@ -38,7 +40,7 @@ use halo2_proofs::{ poly::Rotation, }; use std::{ - collections::{BTreeSet, HashMap}, + collections::{BTreeSet, HashMap, HashSet}, iter, }; use strum::IntoEnumIterator; @@ -47,6 +49,7 @@ mod add_sub; mod addmod; mod address; mod balance; +mod begin_chunk; mod begin_tx; mod bitwise; mod block_ctx; @@ -66,6 +69,7 @@ mod create; mod dummy; mod dup; mod end_block; +mod end_chunk; mod end_tx; mod error_code_store; mod error_invalid_creation_code; @@ -106,6 +110,7 @@ mod mulmod; #[path = "execution/not.rs"] mod opcode_not; mod origin; +mod padding; mod pc; mod pop; mod precompiles; @@ -125,7 +130,10 @@ mod sstore; mod stop; mod swap; -use self::{block_ctx::BlockCtxGadget, sha3::Sha3Gadget}; +use self::{ + begin_chunk::BeginChunkGadget, block_ctx::BlockCtxGadget, end_chunk::EndChunkGadget, + sha3::Sha3Gadget, +}; use add_sub::AddSubGadget; use addmod::AddModGadget; use address::AddressGadget; @@ -188,6 +196,7 @@ use mul_div_mod::MulDivModGadget; use mulmod::MulModGadget; use opcode_not::NotGadget; use origin::OriginGadget; +use padding::PaddingGadget; use pc::PcGadget; use pop::PopGadget; use precompiles::{EcrecoverGadget, IdentityGadget}; @@ -213,11 +222,13 @@ pub(crate) trait ExecutionGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self; + #[allow(clippy::too_many_arguments)] fn assign_exec_step( &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + chunk: &Chunk, transaction: &Transaction, call: &Call, step: &ExecStep, @@ -250,7 +261,10 @@ pub struct ExecutionConfig { // internal state gadgets begin_tx_gadget: Box>, end_block_gadget: Box>, + padding_gadget: Box>, end_tx_gadget: Box>, + begin_chunk_gadget: Box>, + end_chunk_gadget: Box>, // opcode gadgets add_sub_gadget: Box>, addmod_gadget: Box>, @@ -343,6 +357,8 @@ pub struct ExecutionConfig { invalid_tx: Option>>, } +type TxCallStep<'a> = (&'a Transaction, &'a Call, &'a ExecStep); + impl ExecutionConfig { #[allow(clippy::too_many_arguments)] #[allow(clippy::redundant_closure_call)] @@ -360,6 +376,9 @@ impl ExecutionConfig { keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, sig_table: &dyn LookupTable, + chunk_ctx_table: &dyn LookupTable, + is_first_chunk: &IsZeroConfig, + is_last_chunk: &IsZeroConfig, feature_config: FeatureConfig, ) -> Self { let mut instrument = Instrument::default(); @@ -390,6 +409,22 @@ impl ExecutionConfig { let step_curr = Step::new(meta, advices, 0); let mut height_map = HashMap::new(); + let (execute_state_first_step_whitelist, execute_state_last_step_whitelist) = ( + HashSet::from_iter( + vec![ + ExecutionState::BeginTx, + ExecutionState::Padding, + ExecutionState::BeginChunk, + ] + .into_iter() + .chain( + feature_config + .invalid_tx + .then_some(ExecutionState::InvalidTx), + ), + ), + HashSet::from([ExecutionState::EndBlock, ExecutionState::EndChunk]), + ); meta.create_gate("Constrain execution state", |meta| { let q_usable = meta.query_selector(q_usable); @@ -399,37 +434,55 @@ impl ExecutionConfig { let execution_state_selector_constraints = step_curr.state.execution_state.configure(); - // NEW: Enabled, this will break hand crafted tests, maybe we can remove them? - let first_step_check = { - let begin_tx_invalid_tx_end_block_selector = step_curr.execution_state_selector( - [ExecutionState::BeginTx, ExecutionState::EndBlock] - .into_iter() - .chain( - feature_config - .invalid_tx - .then_some(ExecutionState::InvalidTx), - ), - ); + let first_step_first_chunk_check = { + let exestates = step_curr + .execution_state_selector(execute_state_first_step_whitelist.iter().cloned()); iter::once(( - "First step should be BeginTx, InvalidTx or EndBlock", - q_step_first * (1.expr() - begin_tx_invalid_tx_end_block_selector), + "First step first chunk should be BeginTx or EndBlock or BeginChunk", + (1.expr() - is_first_chunk.expr()) + * q_step_first.clone() + * (1.expr() - exestates), )) }; - let last_step_check = { + let first_step_non_first_chunk_check = { + let begin_chunk_selector = + step_curr.execution_state_selector([ExecutionState::BeginChunk]); + iter::once(( + "First step (non first chunk) should be BeginChunk", + (1.expr() - is_first_chunk.expr()) + * q_step_first + * (1.expr() - begin_chunk_selector), + )) + }; + + let last_step_last_chunk_check = { let end_block_selector = step_curr.execution_state_selector([ExecutionState::EndBlock]); iter::once(( - "Last step should be EndBlock", - q_step_last * (1.expr() - end_block_selector), + "Last step last chunk should be EndBlock", + is_last_chunk.expr() * q_step_last.clone() * (1.expr() - end_block_selector), + )) + }; + + let last_step_non_last_chunk_check = { + let end_chunk_selector = + step_curr.execution_state_selector([ExecutionState::EndChunk]); + iter::once(( + "Last step (non last chunk) should be EndChunk", + (1.expr() - is_last_chunk.expr()) + * q_step_last + * (1.expr() - end_chunk_selector), )) }; execution_state_selector_constraints .into_iter() .map(move |(name, poly)| (name, q_usable.clone() * q_step.clone() * poly)) - .chain(first_step_check) - .chain(last_step_check) + .chain(first_step_first_chunk_check) + .chain(first_step_non_first_chunk_check) + .chain(last_step_last_chunk_check) + .chain(last_step_non_last_chunk_check) }); meta.create_gate("q_step", |meta| { @@ -447,8 +500,8 @@ impl ExecutionConfig { cb.condition(q_step_first, |cb| { cb.require_equal("q_step == 1", q_step.clone(), 1.expr()); cb.require_equal( - "rw_counter is initialized to be 1", - step_curr.state.rw_counter.expr(), + "inner_rw_counter is initialized to be 1", + step_curr.state.inner_rw_counter.expr(), 1.expr(), ) }); @@ -501,13 +554,16 @@ impl ExecutionConfig { Box::new(Self::configure_gadget( meta, advices, + &challenges, q_usable, q_step, num_rows_until_next_step, q_step_first, q_step_last, - &challenges, &step_curr, + chunk_ctx_table, + &execute_state_first_step_whitelist, + &execute_state_last_step_whitelist, &mut height_map, &mut stored_expressions_map, &mut debug_expressions_map, @@ -531,8 +587,11 @@ impl ExecutionConfig { advices, // internal states begin_tx_gadget: configure_gadget!(), - end_block_gadget: configure_gadget!(), + padding_gadget: configure_gadget!(), end_tx_gadget: configure_gadget!(), + begin_chunk_gadget: configure_gadget!(), + end_chunk_gadget: configure_gadget!(), + end_block_gadget: configure_gadget!(), invalid_tx: feature_config.invalid_tx.then(|| configure_gadget!()), // opcode gadgets add_sub_gadget: configure_gadget!(), @@ -640,6 +699,7 @@ impl ExecutionConfig { keccak_table, exp_table, sig_table, + chunk_ctx_table, &challenges, &cell_manager, ); @@ -654,13 +714,16 @@ impl ExecutionConfig { fn configure_gadget>( meta: &mut ConstraintSystem, advices: [Column; STEP_WIDTH], + challenges: &Challenges>, q_usable: Selector, q_step: Column, num_rows_until_next_step: Column, q_step_first: Selector, q_step_last: Selector, - challenges: &Challenges>, step_curr: &Step, + chunk_ctx_table: &dyn LookupTable, + execute_state_first_step_whitelist: &HashSet, + execute_state_last_step_whitelist: &HashSet, height_map: &mut HashMap, stored_expressions_map: &mut HashMap>>, debug_expressions_map: &mut HashMap)>>, @@ -686,6 +749,7 @@ impl ExecutionConfig { // Now actually configure the gadget with the correct minimal height let step_next = &Step::new(meta, advices, height); + let mut cb = EVMConstraintBuilder::new( meta, step_curr.clone(), @@ -708,11 +772,15 @@ impl ExecutionConfig { height_map, stored_expressions_map, debug_expressions_map, + execute_state_first_step_whitelist, + execute_state_last_step_whitelist, instrument, G::NAME, G::EXECUTION_STATE, height, cb, + chunk_ctx_table, + challenges, ); gadget @@ -730,11 +798,15 @@ impl ExecutionConfig { height_map: &mut HashMap, stored_expressions_map: &mut HashMap>>, debug_expressions_map: &mut HashMap)>>, + execute_state_first_step_whitelist: &HashSet, + execute_state_last_step_whitelist: &HashSet, instrument: &mut Instrument, name: &'static str, execution_state: ExecutionState, height: usize, mut cb: EVMConstraintBuilder, + chunk_ctx_table: &dyn LookupTable, + challenges: &Challenges>, ) { // Enforce the step height for this opcode let num_rows_until_next_step_next = cb @@ -747,6 +819,12 @@ impl ExecutionConfig { instrument.on_gadget_built(execution_state, &cb); + let step_curr_rw_counter = cb.curr.state.rw_counter.clone(); + let step_curr_rw_counter_offset = cb.rw_counter_offset(); + if execution_state == ExecutionState::BeginChunk { + cb.debug_expression("step_curr_rw_counter.expr()", step_curr_rw_counter.expr()); + } + let debug_expressions = cb.debug_expressions.clone(); // Extract feature config here before cb is built. @@ -794,6 +872,59 @@ impl ExecutionConfig { } } + // constraint global rw counter value at first/last step via chunk_ctx_table lookup + // we can't do it inside constraint_builder(cb) + // because lookup expression in constraint builder DO NOT support apply conditional + // `step_first/step_last` selector at lookup cell. + if execute_state_first_step_whitelist.contains(&execution_state) { + meta.lookup_any("first must lookup initial rw_counter", |meta| { + let q_usable = meta.query_selector(q_usable); + let q_step_first = meta.query_selector(q_step_first); + let execute_state_selector = step_curr.execution_state_selector([execution_state]); + + vec![( + q_usable + * q_step_first + * execute_state_selector + * rlc::expr( + &[ + ChunkCtxFieldTag::InitialRWC.expr(), + step_curr.state.rw_counter.expr(), + ], + challenges.lookup_input(), + ), + rlc::expr( + &chunk_ctx_table.table_exprs(meta), + challenges.lookup_input(), + ), + )] + }); + } + + if execute_state_last_step_whitelist.contains(&execution_state) { + meta.lookup_any("last step must lookup end rw_counter", |meta| { + let q_usable = meta.query_selector(q_usable); + let q_step_last = meta.query_selector(q_step_last); + let execute_state_selector = step_curr.execution_state_selector([execution_state]); + vec![( + q_usable + * q_step_last + * execute_state_selector + * rlc::expr( + &[ + ChunkCtxFieldTag::EndRWC.expr(), + step_curr_rw_counter.expr() + step_curr_rw_counter_offset.clone(), + ], + challenges.lookup_input(), + ), + rlc::expr( + &chunk_ctx_table.table_exprs(meta), + challenges.lookup_input(), + ), + )] + }); + } + // Enforce the state transitions for this opcode meta.create_gate("Constrain state machine transitions", |meta| { let q_usable = meta.query_selector(q_usable); @@ -805,12 +936,30 @@ impl ExecutionConfig { .chain( [ ( - "EndTx can only transit to BeginTx, InvalidTx or EndBlock", + "EndTx can only transit to BeginTx or Padding or EndBlock or EndChunk or InvalidTx", ExecutionState::EndTx, - vec![ExecutionState::BeginTx, ExecutionState::EndBlock] - .into_iter() - .chain(enable_invalid_tx.then_some(ExecutionState::InvalidTx)) - .collect(), + vec![ + ExecutionState::BeginTx, + ExecutionState::EndBlock, + ExecutionState::Padding, + ExecutionState::EndChunk, + ].into_iter() + .chain(enable_invalid_tx.then_some(ExecutionState::InvalidTx)) + .collect(), + ), + ( + "EndChunk can only transit to EndChunk", + ExecutionState::EndChunk, + vec![ExecutionState::EndChunk], + ), + ( + "Padding can only transit to Padding or EndBlock or EndChunk", + ExecutionState::Padding, + vec![ + ExecutionState::Padding, + ExecutionState::EndBlock, + ExecutionState::EndChunk, + ], ), ( "EndBlock can only transit to EndBlock", @@ -840,12 +989,21 @@ impl ExecutionConfig { .collect(), ), ( - "Only EndTx, InvalidTx or EndBlock can transit to EndBlock", + "Only BeginChunk or EndTx or InvalidTx or EndBlock or Padding can transit to EndBlock", ExecutionState::EndBlock, - vec![ExecutionState::EndTx, ExecutionState::EndBlock] - .into_iter() - .chain(enable_invalid_tx.then_some(ExecutionState::InvalidTx)) - .collect(), + vec![ + ExecutionState::BeginChunk, + ExecutionState::EndTx, + ExecutionState::EndBlock, + ExecutionState::Padding, + ].into_iter() + .chain(enable_invalid_tx.then_some(ExecutionState::InvalidTx)) + .collect(), + ), + ( + "Only BeginChunk can transit to BeginChunk", + ExecutionState::BeginChunk, + vec![ExecutionState::BeginChunk], ), ] .into_iter() @@ -886,6 +1044,7 @@ impl ExecutionConfig { keccak_table: &dyn LookupTable, exp_table: &dyn LookupTable, sig_table: &dyn LookupTable, + chunk_ctx_table: &dyn LookupTable, challenges: &Challenges>, cell_manager: &CellManager, ) { @@ -906,6 +1065,7 @@ impl ExecutionConfig { Table::Keccak => keccak_table, Table::Exp => exp_table, Table::Sig => sig_table, + Table::ChunkCtx => chunk_ctx_table, } .table_exprs(meta); vec![( @@ -962,8 +1122,9 @@ impl ExecutionConfig { &self, layouter: &mut impl Layouter, block: &Block, + chunk: &Chunk, challenges: &Challenges>, - ) -> Result<(), Error> { + ) -> Result { // Track number of calls to `layouter.assign_region` as layouter assignment passes. let mut assign_pass = 0; layouter.assign_region( @@ -977,116 +1138,190 @@ impl ExecutionConfig { self.q_step_first.enable(&mut region, offset)?; let dummy_tx = Transaction::default(); - let last_call = block + // chunk_txs is just a super set of execstep including both belong to this chunk and + // outside of this chunk + let chunk_txs: &[Transaction] = block .txs + .get(chunk.chunk_context.initial_tx_index..chunk.chunk_context.end_tx_index) + .unwrap_or_default(); + + // If it's the very first chunk in a block set last call & begin_chunk to default + let prev_chunk_last_call = chunk.prev_last_call.clone().unwrap_or_default(); + let cur_chunk_last_call = chunk_txs .last() .map(|tx| tx.calls()[0].clone()) - .unwrap_or_default(); - let end_block_not_last = &block.end_block_not_last; - let end_block_last = &block.end_block_last; - // Collect all steps - let mut steps = block - .txs - .iter() - .flat_map(|tx| { + .unwrap_or_else(|| prev_chunk_last_call.clone()); + + let padding = chunk.padding.as_ref().expect("padding can't be None"); + + // conditionally adding first step as begin chunk + let maybe_begin_chunk = { + if let Some(begin_chunk) = &chunk.begin_chunk { + vec![(&dummy_tx, &prev_chunk_last_call, begin_chunk)] + } else { + vec![] + } + }; + + let mut tx_call_steps = maybe_begin_chunk + .into_iter() + .chain(chunk_txs.iter().flat_map(|tx| { tx.steps() .iter() + // chunk_txs is just a super set of execstep. To filter targetting + // execstep we need to further filter by [initial_rwc, end_rwc) + .filter(|step| { + step.rwc.0 >= chunk.chunk_context.initial_rwc + && step.rwc.0 < chunk.chunk_context.end_rwc + }) .map(move |step| (tx, &tx.calls()[step.call_index], step)) - }) - .chain(std::iter::once((&dummy_tx, &last_call, end_block_not_last))) + })) + // this dummy step is just for real step assignment proceed to `second last` + .chain(std::iter::once((&dummy_tx, &cur_chunk_last_call, padding))) .peekable(); - let evm_rows = block.circuits_params.max_evm_rows; - let no_padding = evm_rows == 0; + let evm_rows = chunk.fixed_param.max_evm_rows; + + let mut assign_padding_or_step = |cur_tx_call_step: TxCallStep, + mut offset: usize, + next_tx_call_step: Option, + padding_end: Option| + -> Result { + let (_tx, call, step) = cur_tx_call_step; + let height = step.execution_state().get_step_height(); + + // If padding, assign padding range with (dummy_tx, call, step) + // otherwise, assign one row with cur (tx, call, step), with next (tx, call, + // step) to lookahead + if let Some(padding_end) = padding_end { + // padding_end is the absolute position over all rows, + // must be greater then current offset + if offset >= padding_end { + log::error!( + "evm circuit offset larger than padding: {} > {}", + offset, + padding_end + ); + return Err(Error::Synthesis); + } + log::trace!("assign Padding in range [{},{})", offset, padding_end); + self.assign_same_exec_step_in_range( + &mut region, + offset, + padding_end, + block, + chunk, + (&dummy_tx, call, step), + height, + challenges, + assign_pass, + )?; + let padding_start = offset; + for row_idx in padding_start..padding_end { + self.assign_q_step(&mut region, row_idx, height)?; + offset += height; + } + } else { + self.assign_exec_step( + &mut region, + offset, + block, + chunk, + cur_tx_call_step, + height, + next_tx_call_step, + challenges, + assign_pass, + )?; + self.assign_q_step(&mut region, offset, height)?; + offset += height; + } + + Ok(offset) // return latest offset + }; + + let mut second_last_real_step = None; + let mut second_last_real_step_offset = 0; // part1: assign real steps - loop { - let (transaction, call, step) = steps.next().expect("should not be empty"); - let next = steps.peek(); + while let Some(cur) = tx_call_steps.next() { + let next = tx_call_steps.peek(); if next.is_none() { break; } - let height = step.execution_state().get_step_height(); - // Assign the step witness - self.assign_exec_step( - &mut region, - offset, - block, - transaction, - call, - step, - height, - next.copied(), - challenges, - assign_pass, - )?; + second_last_real_step = Some(cur); + // record offset of current step before assignment + second_last_real_step_offset = offset; + offset = assign_padding_or_step(cur, offset, next.copied(), None)?; + } - // q_step logic - self.assign_q_step(&mut region, offset, height)?; + // next step priority: padding > end_chunk > end_block + let mut next_step_after_real_step = None; - offset += height; + // part2: assign padding + if evm_rows > 0 { + if next_step_after_real_step.is_none() { + next_step_after_real_step = Some(padding.clone()); + } + offset = assign_padding_or_step( + (&dummy_tx, &cur_chunk_last_call, padding), + offset, + None, + Some(evm_rows - 1), + )?; } - // part2: assign non-last EndBlock steps when padding needed - if !no_padding { - if offset >= evm_rows { - log::error!( - "evm circuit offset larger than padding: {} > {}", - offset, - evm_rows - ); - return Err(Error::Synthesis); - } - let height = ExecutionState::EndBlock.get_step_height(); - debug_assert_eq!(height, 1); - let last_row = evm_rows - 1; - log::trace!( - "assign non-last EndBlock in range [{},{})", + // part3: assign end chunk or end block + if let Some(end_chunk) = &chunk.end_chunk { + debug_assert_eq!(ExecutionState::EndChunk.get_step_height(), 1); + offset = assign_padding_or_step( + (&dummy_tx, &cur_chunk_last_call, end_chunk), offset, - last_row + None, + None, + )?; + if next_step_after_real_step.is_none() { + next_step_after_real_step = Some(end_chunk.clone()); + } + } else { + assert!( + chunk.chunk_context.is_last_chunk(), + "If not end_chunk, must be end_block at last chunk" ); - self.assign_same_exec_step_in_range( - &mut region, + debug_assert_eq!(ExecutionState::EndBlock.get_step_height(), 1); + offset = assign_padding_or_step( + (&dummy_tx, &cur_chunk_last_call, &block.end_block), offset, - last_row, - block, - &dummy_tx, - &last_call, - end_block_not_last, - height, - challenges, - assign_pass, + None, + None, )?; - - for row_idx in offset..last_row { - self.assign_q_step(&mut region, row_idx, height)?; + if next_step_after_real_step.is_none() { + next_step_after_real_step = Some(block.end_block.clone()); } - offset = last_row; } - // part3: assign the last EndBlock at offset `evm_rows - 1` - let height = ExecutionState::EndBlock.get_step_height(); - debug_assert_eq!(height, 1); - log::trace!("assign last EndBlock at offset {}", offset); - self.assign_exec_step( - &mut region, - offset, - block, - &dummy_tx, - &last_call, - end_block_last, - height, - None, - challenges, - assign_pass, - )?; - self.assign_q_step(&mut region, offset, height)?; - // enable q_step_last - self.q_step_last.enable(&mut region, offset)?; - offset += height; - // part4: + // re-assigned real second last step, because we know next_step_after_real_step now + assert!(next_step_after_real_step.is_some()); + if let Some(last_real_step) = second_last_real_step { + _ = assign_padding_or_step( + last_real_step, + second_last_real_step_offset, + Some(( + &dummy_tx, + &cur_chunk_last_call, + &next_step_after_real_step.unwrap(), + )), + None, + )?; + } + + // part5: + // enable last row + self.q_step_last.enable(&mut region, offset - 1)?; // offset - 1 is the last row + + // part6: // These are still referenced (but not used) in next rows region.assign_advice( || "step height", @@ -1102,7 +1337,7 @@ impl ExecutionConfig { )?; assign_pass += 1; - Ok(()) + Ok(offset) }, ) } @@ -1118,6 +1353,7 @@ impl ExecutionConfig { ("EVM_lookup_keccak", KECCAK_TABLE_LOOKUPS), ("EVM_lookup_exp", EXP_TABLE_LOOKUPS), ("EVM_lookup_sig", SIG_TABLE_LOOKUPS), + ("EVM_lookupchunk_ctx", CHUNK_CTX_TABLE_LOOKUPS), ("EVM_adv_phase2", N_PHASE2_COLUMNS), ("EVM_copy", N_COPY_COLUMNS), ("EVM_lookup_u8", N_U8_LOOKUPS), @@ -1149,9 +1385,8 @@ impl ExecutionConfig { offset_begin: usize, offset_end: usize, block: &Block, - transaction: &Transaction, - call: &Call, - step: &ExecStep, + chunk: &Chunk, + cur_step: TxCallStep, height: usize, challenges: &Challenges>, assign_pass: usize, @@ -1159,9 +1394,10 @@ impl ExecutionConfig { if offset_end <= offset_begin { return Ok(()); } + let (_, _, step) = cur_step; assert_eq!(height, 1); assert!(step.rw_indices_len() == 0); - assert!(matches!(step.execution_state(), ExecutionState::EndBlock)); + assert!(matches!(step.execution_state(), ExecutionState::Padding)); // Disable access to next step deliberately for "repeatable" step let region = &mut CachedRegion::<'_, '_, F>::new( @@ -1175,9 +1411,8 @@ impl ExecutionConfig { region, offset_begin, block, - transaction, - call, - step, + chunk, + cur_step, false, assign_pass, )?; @@ -1197,15 +1432,15 @@ impl ExecutionConfig { region: &mut Region<'_, F>, offset: usize, block: &Block, - transaction: &Transaction, - call: &Call, - step: &ExecStep, + chunk: &Chunk, + cur_step: TxCallStep, height: usize, - next: Option<(&Transaction, &Call, &ExecStep)>, + next_step: Option, challenges: &Challenges>, assign_pass: usize, ) -> Result<(), Error> { - if !matches!(step.execution_state(), ExecutionState::EndBlock) { + let (_transaction, call, step) = cur_step; + if !matches!(step.execution_state(), ExecutionState::Padding) { log::trace!( "assign_exec_step offset: {} state {:?} step: {:?} call: {:?}", offset, @@ -1229,29 +1464,19 @@ impl ExecutionConfig { // These may be used in stored expressions and // so their witness values need to be known to be able // to correctly calculate the intermediate value. - if let Some((transaction_next, call_next, step_next)) = next { + if let Some(next_step) = next_step { self.assign_exec_step_int( region, offset + height, block, - transaction_next, - call_next, - step_next, + chunk, + next_step, true, assign_pass, )?; } - self.assign_exec_step_int( - region, - offset, - block, - transaction, - call, - step, - false, - assign_pass, - ) + self.assign_exec_step_int(region, offset, block, chunk, cur_step, false, assign_pass) } #[allow(clippy::too_many_arguments)] @@ -1260,9 +1485,8 @@ impl ExecutionConfig { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, - transaction: &Transaction, - call: &Call, - step: &ExecStep, + chunk: &Chunk, + tx_call_step: TxCallStep, // Set to true when we're assigning the next step before the current step to have // next step assignments for evaluation of the stored expressions in current step that // depend on the next step. @@ -1270,12 +1494,13 @@ impl ExecutionConfig { // Layouter assignment pass assign_pass: usize, ) -> Result<(), Error> { + let (transaction, call, step) = tx_call_step; self.step .assign_exec_step(region, offset, block, call, step)?; macro_rules! assign_exec_step { ($gadget:expr) => { - $gadget.assign_exec_step(region, offset, block, transaction, call, step)? + $gadget.assign_exec_step(region, offset, block, chunk, transaction, call, step)? }; } @@ -1283,7 +1508,10 @@ impl ExecutionConfig { // internal states ExecutionState::BeginTx => assign_exec_step!(self.begin_tx_gadget), ExecutionState::EndTx => assign_exec_step!(self.end_tx_gadget), + ExecutionState::Padding => assign_exec_step!(self.padding_gadget), ExecutionState::EndBlock => assign_exec_step!(self.end_block_gadget), + ExecutionState::BeginChunk => assign_exec_step!(self.begin_chunk_gadget), + ExecutionState::EndChunk => assign_exec_step!(self.end_chunk_gadget), ExecutionState::InvalidTx => { assign_exec_step!(self .invalid_tx @@ -1445,14 +1673,14 @@ 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_len() == 0; + let is_padding_step = matches!(step.execution_state(), ExecutionState::Padding); if !is_padding_step { // expensive function call Self::check_rw_lookup( &assigned_stored_expressions, step, block, + chunk, region.challenges(), ); } @@ -1510,6 +1738,7 @@ impl ExecutionConfig { assigned_stored_expressions: &[(String, F)], step: &ExecStep, block: &Block, + _chunk: &Chunk, challenges: &Challenges>, ) { let mut lookup_randomness = F::ZERO; diff --git a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs index 5355bed337..3429cfb567 100644 --- a/zkevm-circuits/src/evm_circuit/execution/add_sub.rs +++ b/zkevm-circuits/src/evm_circuit/execution/add_sub.rs @@ -8,7 +8,7 @@ use crate::{ math_gadget::{AddWordsGadget, PairSelectGadget}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi}, @@ -80,6 +80,7 @@ impl ExecutionGadget for AddSubGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/addmod.rs b/zkevm-circuits/src/evm_circuit/execution/addmod.rs index a952b166bc..fe1d598b9c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/addmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/addmod.rs @@ -14,7 +14,7 @@ use crate::{ }, not, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -152,6 +152,7 @@ impl ExecutionGadget for AddModGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/address.rs b/zkevm-circuits/src/evm_circuit/execution/address.rs index 0a98827638..024566b71a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/address.rs +++ b/zkevm-circuits/src/evm_circuit/execution/address.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -60,6 +60,7 @@ impl ExecutionGadget for AddressGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/balance.rs b/zkevm-circuits/src/evm_circuit/execution/balance.rs index ef3f6dfeea..487a63089f 100644 --- a/zkevm-circuits/src/evm_circuit/execution/balance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/balance.rs @@ -11,7 +11,7 @@ use crate::{ math_gadget::IsZeroWordGadget, not, select, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -111,6 +111,7 @@ impl ExecutionGadget for BalanceGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_chunk.rs b/zkevm-circuits/src/evm_circuit/execution/begin_chunk.rs new file mode 100644 index 0000000000..c8e519e682 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/begin_chunk.rs @@ -0,0 +1,59 @@ +use std::marker::PhantomData; + +use crate::{ + evm_circuit::{ + step::ExecutionState, + util::{ + constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, + CachedRegion, + }, + witness::{Block, Call, Chunk, ExecStep, Transaction}, + }, + util::Expr, +}; +use eth_types::Field; +use halo2_proofs::plonk::Error; + +use super::ExecutionGadget; + +#[derive(Clone, Debug)] +pub(crate) struct BeginChunkGadget { + _marker: PhantomData, +} + +impl ExecutionGadget for BeginChunkGadget { + const NAME: &'static str = "BeginChunk"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::BeginChunk; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + // state lookup + cb.step_state_lookup(0.expr()); + let step_state_transition = StepStateTransition { + rw_counter: Delta(cb.rw_counter_offset()), + ..StepStateTransition::same() + }; + cb.require_step_state_transition(step_state_transition); + Self { + _marker: PhantomData {}, + } + } + + fn assign_exec_step( + &self, + _region: &mut CachedRegion<'_, '_, F>, + _offset: usize, + _block: &Block, + _chunk: &Chunk, + _: &Transaction, + _: &Call, + _step: &ExecStep, + ) -> Result<(), Error> { + Ok(()) + } +} + +#[cfg(test)] +mod test { + // begin_chunk unittest covered by end_chunk +} diff --git a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs index 307d2198f0..8e25071d80 100644 --- a/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/begin_tx.rs @@ -19,7 +19,7 @@ use crate::{ tx::{BeginTxHelperGadget, TxDataGadget}, AccountAddress, CachedRegion, Cell, StepRws, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag}, util::{ @@ -489,6 +489,7 @@ impl ExecutionGadget for BeginTxGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs index 947ee2a7dd..b5d95cf791 100644 --- a/zkevm-circuits/src/evm_circuit/execution/bitwise.rs +++ b/zkevm-circuits/src/evm_circuit/execution/bitwise.rs @@ -8,7 +8,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr}, @@ -84,6 +84,7 @@ impl ExecutionGadget for BitwiseGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs index b247438a2d..dcc6b97cf1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/block_ctx.rs @@ -6,7 +6,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::BlockContextFieldTag, util::{ @@ -66,6 +66,7 @@ impl ExecutionGadget for BlockCtxGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs index ea37a199b7..ec7708185d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/blockhash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/blockhash.rs @@ -13,7 +13,7 @@ use crate::{ math_gadget::LtGadget, CachedRegion, Cell, WordLoHi, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::BlockContextFieldTag, util::word::WordExpr, @@ -98,6 +98,7 @@ impl ExecutionGadget for BlockHashGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/byte.rs b/zkevm-circuits/src/evm_circuit/execution/byte.rs index fb06fb0579..42002e91e8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/byte.rs +++ b/zkevm-circuits/src/evm_circuit/execution/byte.rs @@ -8,7 +8,7 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroGadget}, sum, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -97,6 +97,7 @@ impl ExecutionGadget for ByteGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs index 99f6cdd933..1384d23670 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatacopy.rs @@ -15,7 +15,7 @@ use crate::{ }, not, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -178,6 +178,7 @@ impl ExecutionGadget for CallDataCopyGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs index 67b2efda19..ac59cdd2bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldataload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldataload.rs @@ -19,7 +19,7 @@ use crate::{ memory_gadget::BufferReaderGadget, not, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, util::{ @@ -205,6 +205,7 @@ impl ExecutionGadget for CallDataLoadGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs index 1ead801063..06972e6dd4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/calldatasize.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -65,6 +65,7 @@ impl ExecutionGadget for CallDataSizeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/caller.rs b/zkevm-circuits/src/evm_circuit/execution/caller.rs index 6a3839ab34..ea35c8111b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/caller.rs +++ b/zkevm-circuits/src/evm_circuit/execution/caller.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -65,6 +65,7 @@ impl ExecutionGadget for CallerGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/callop.rs b/zkevm-circuits/src/evm_circuit/execution/callop.rs index 571a33dc5b..0a7b27b905 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callop.rs @@ -21,9 +21,14 @@ use crate::{ }, witness::{Block, Call, ExecStep, Transaction}, }, + util::word::WordExpr, +}; + +use crate::{ + evm_circuit::witness::Chunk, table::{AccountFieldTag, CallContextFieldTag}, util::{ - word::{WordExpr, WordLoHi, WordLoHiCell}, + word::{WordLoHi, WordLoHiCell}, Expr, }, }; @@ -801,6 +806,7 @@ impl ExecutionGadget for CallOpGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, @@ -1384,7 +1390,7 @@ mod test { CircuitTestBuilder::new_from_test_ctx(ctx) .params(FixedCParams { - max_rws: 500, + max_rws: 1 << 12, ..Default::default() }) .run(); diff --git a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs index f590984d77..6b809785ef 100644 --- a/zkevm-circuits/src/evm_circuit/execution/callvalue.rs +++ b/zkevm-circuits/src/evm_circuit/execution/callvalue.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -67,6 +67,7 @@ impl ExecutionGadget for CallValueGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/chainid.rs b/zkevm-circuits/src/evm_circuit/execution/chainid.rs index c355c47c47..ed1e476632 100644 --- a/zkevm-circuits/src/evm_circuit/execution/chainid.rs +++ b/zkevm-circuits/src/evm_circuit/execution/chainid.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::BlockContextFieldTag, util::{ @@ -65,6 +65,7 @@ impl ExecutionGadget for ChainIdGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs index fc019ec674..2a8450efd3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codecopy.rs @@ -17,7 +17,7 @@ use crate::{ }, not, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi}, @@ -145,6 +145,7 @@ impl ExecutionGadget for CodeCopyGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/codesize.rs b/zkevm-circuits/src/evm_circuit/execution/codesize.rs index cab8f2253c..59fa6e8ba3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/codesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/codesize.rs @@ -12,7 +12,7 @@ use crate::{ }, CachedRegion, Cell, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordExpr, Expr}, }; @@ -69,6 +69,7 @@ impl ExecutionGadget for CodesizeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _transaction: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/comparator.rs b/zkevm-circuits/src/evm_circuit/execution/comparator.rs index a0f81f8109..26c9f60cc5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/comparator.rs @@ -8,7 +8,7 @@ use crate::{ math_gadget::{CmpWordsGadget, IsEqualGadget}, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi, WordLoHiCell}, @@ -92,6 +92,7 @@ impl ExecutionGadget for ComparatorGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/create.rs b/zkevm-circuits/src/evm_circuit/execution/create.rs index c999a73e40..d0b21b8509 100644 --- a/zkevm-circuits/src/evm_circuit/execution/create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/create.rs @@ -21,7 +21,7 @@ use crate::{ }, not, AccountAddress, CachedRegion, Cell, StepRws, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -514,6 +514,7 @@ impl ExecutionGadget< region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/dummy.rs b/zkevm-circuits/src/evm_circuit/execution/dummy.rs index ad85b35bc4..f861cf60f9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dummy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dummy.rs @@ -5,7 +5,7 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{constraint_builder::EVMConstraintBuilder, CachedRegion}, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::word::{WordExpr, WordLoHiCell}, }; @@ -47,6 +47,7 @@ impl region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/dup.rs b/zkevm-circuits/src/evm_circuit/execution/dup.rs index 71a8c8d7e8..a49c0ec4df 100644 --- a/zkevm-circuits/src/evm_circuit/execution/dup.rs +++ b/zkevm-circuits/src/evm_circuit/execution/dup.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHiCell}, @@ -62,6 +62,7 @@ impl ExecutionGadget for DupGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/end_block.rs b/zkevm-circuits/src/evm_circuit/execution/end_block.rs index 63c5e530e1..73188658f2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_block.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_block.rs @@ -3,19 +3,19 @@ use crate::{ execution::ExecutionGadget, step::ExecutionState, util::{ + common_gadget::RwTablePaddingGadget, constraint_builder::{ ConstrainBuilderCommon, EVMConstraintBuilder, StepStateTransition, Transition::Same, }, math_gadget::IsEqualGadget, not, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, util::{word::WordLoHi, Expr}, }; use eth_types::{Field, OpsIdentity}; -use gadgets::util::select; use halo2_proofs::{circuit::Value, plonk::Error}; #[derive(Clone, Debug)] @@ -23,8 +23,8 @@ pub(crate) struct EndBlockGadget { total_txs: Cell, total_txs_is_max_txs: IsEqualGadget, is_empty_block: IsEqualGadget, - max_rws: Cell, max_txs: Cell, + rw_table_padding_gadget: RwTablePaddingGadget, } impl ExecutionGadget for EndBlockGadget { @@ -34,19 +34,11 @@ impl ExecutionGadget for EndBlockGadget { fn configure(cb: &mut EVMConstraintBuilder) -> Self { let max_txs = cb.query_copy_cell(); - let max_rws = cb.query_copy_cell(); let total_txs = cb.query_cell(); let total_txs_is_max_txs = cb.is_eq(total_txs.expr(), max_txs.expr()); // Note that rw_counter starts at 1 let is_empty_block = cb.is_eq(cb.curr.state.rw_counter.clone().expr(), 1.expr()); - let total_rws_before_padding = cb.curr.state.rw_counter.clone().expr() - 1.expr() - + select::expr( - is_empty_block.expr(), - 0.expr(), - 1.expr(), // If the block is not empty, we will do 1 call_context lookup below - ); - // 1. Constraint total_rws and total_txs witness values depending on the empty // block case. cb.condition(is_empty_block.expr(), |cb| { @@ -82,11 +74,16 @@ impl ExecutionGadget for EndBlockGadget { // meaningful txs in the tx_table is total_tx. }); + let total_inner_rws_before_padding = cb.curr.state.inner_rw_counter.clone().expr() + - 1.expr() // start from 1 + + cb.rw_counter_offset(); // 3. Verify rw_counter counts to the same number of meaningful rows in // rw_table to ensure there is no malicious insertion. // Verify that there are at most total_rws meaningful entries in the rw_table - cb.rw_table_start_lookup(1.expr()); - cb.rw_table_start_lookup(max_rws.expr() - total_rws_before_padding.expr()); + // - startop only exist in first chunk + + let rw_table_padding_gadget = + RwTablePaddingGadget::construct(cb, total_inner_rws_before_padding); // Since every lookup done in the EVM circuit must succeed and uses // a unique rw_counter, we know that at least there are // total_rws meaningful entries in the rw_table. @@ -108,10 +105,10 @@ impl ExecutionGadget for EndBlockGadget { Self { max_txs, - max_rws, total_txs, total_txs_is_max_txs, is_empty_block, + rw_table_padding_gadget, } } @@ -120,28 +117,39 @@ impl ExecutionGadget for EndBlockGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, ) -> Result<(), Error> { self.is_empty_block .assign(region, offset, F::from(u64::from(step.rwc)), F::ONE)?; - 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))?; + + let inner_rws_before_padding = + step.rwc_inner_chunk.0 as u64 - 1 + if u64::from(step.rwc) > 1 { 1 } else { 0 }; + self.rw_table_padding_gadget.assign_exec_step( + region, + offset, + block, + chunk, + inner_rws_before_padding, + step, + )?; let total_txs = F::from(block.txs.len() as u64); - let max_txs = F::from(block.circuits_params.max_txs as u64); + let max_txs = F::from(chunk.fixed_param.max_txs as u64); self.total_txs .assign(region, offset, Value::known(total_txs))?; self.total_txs_is_max_txs .assign(region, offset, total_txs, max_txs)?; 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. + // When rw_indices is not empty, means current endblock is non-padding step, we're at the + // last row (at a fixed offset), where we need to access max_txs + // constant. if step.rw_indices_len() != 0 { - region.constrain_constant(max_rws_assigned, max_rws)?; region.constrain_constant(max_txs_assigned, max_txs)?; } + Ok(()) } } @@ -164,23 +172,17 @@ mod test { // finish required tests using this witness block CircuitTestBuilder::<2, 1>::new_from_test_ctx(ctx) - .block_modifier(Box::new(move |block| { - block.circuits_params.max_evm_rows = evm_circuit_pad_to + .block_modifier(Box::new(move |_block, chunk| { + chunk + .iter_mut() + .for_each(|chunk| chunk.fixed_param.max_evm_rows = evm_circuit_pad_to); })) .run(); } - // Test where the EVM circuit contains an exact number of rows corresponding to - // the trace steps + 1 EndBlock + // Test steps + 1 EndBlock without padding #[test] - fn end_block_exact() { + fn end_block_no_padding() { test_circuit(0); } - - // Test where the EVM circuit has a fixed size and contains several padding - // EndBlocks at the end after the trace steps - #[test] - fn end_block_padding() { - test_circuit(50); - } } diff --git a/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs b/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs new file mode 100644 index 0000000000..b10e4e1e34 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/end_chunk.rs @@ -0,0 +1,215 @@ +use std::marker::PhantomData; + +use crate::{ + evm_circuit::{ + step::ExecutionState, + util::{ + common_gadget::RwTablePaddingGadget, + constraint_builder::{EVMConstraintBuilder, StepStateTransition}, + CachedRegion, + }, + witness::{Block, Call, Chunk, ExecStep, Transaction}, + }, + util::Expr, +}; +use bus_mapping::{exec_trace::OperationRef, operation::Target}; +use eth_types::Field; +use halo2_proofs::plonk::Error; + +use super::ExecutionGadget; + +#[derive(Clone, Debug)] +pub(crate) struct EndChunkGadget { + _marker: PhantomData, + rw_table_padding_gadget: RwTablePaddingGadget, +} + +impl ExecutionGadget for EndChunkGadget { + const NAME: &'static str = "EndChunk"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::EndChunk; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + // State transition on non-last evm step + // TODO/FIXME make EndChunk must be in last evm step and remove below constraint + cb.not_step_last(|cb| { + // Propagate all the way down. + cb.require_step_state_transition(StepStateTransition::same()); + }); + + // step state write to rw_table + cb.step_state_lookup(1.expr()); + + let rw_table_padding_gadget = RwTablePaddingGadget::construct( + cb, + cb.curr.state.inner_rw_counter.clone().expr() - 1.expr() + cb.rw_counter_offset(), /* start from 1 */ + ); + + Self { + rw_table_padding_gadget, + _marker: PhantomData {}, + } + } + + fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + block: &Block, + chunk: &Chunk, + _: &Transaction, + _: &Call, + step: &ExecStep, + ) -> Result<(), Error> { + let rwc_before_padding = step + .bus_mapping_instance + .iter() + .filter(|x| { + let OperationRef(c, _) = x; + *c != Target::Start && *c != Target::Padding + }) + .count(); + self.rw_table_padding_gadget.assign_exec_step( + region, + offset, + block, + chunk, + (step.rwc_inner_chunk.0 - 1 + rwc_before_padding) as u64, + step, + )?; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::{ + test_util::CircuitTestBuilder, + witness::{block_convert, chunk_convert}, + }; + use bus_mapping::{circuit_input_builder::FixedCParams, mock::BlockData}; + use eth_types::{address, bytecode, geth_types::GethData, Word}; + use halo2_proofs::halo2curves::bn256::Fr; + use mock::TestContext; + + macro_rules! test_2_txs_with_various_chunk_size { + ($($name:ident: $value:expr,)*) => { + $( + #[test] + fn $name() { + let (total_chunks, total_rws) = $value; + test_2_txs_with_chunk_size(total_chunks, total_rws); + } + )* + } + } + #[test] + fn test_chunking_rwmap_logic() { + let bytecode = bytecode! { + GAS + STOP + }; + let addr_a = address!("0x000000000000000000000000000000000000AAAA"); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + let test_ctx = TestContext::<2, 2>::new( + None, + |accs| { + accs[0] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode); + accs[1].address(addr_a).balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + txs[1] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + let block: GethData = test_ctx.into(); + let builder = BlockData::new_from_geth_data_with_params( + block.clone(), + FixedCParams { + total_chunks: 4, + max_rws: 64, + max_txs: 2, + ..Default::default() + }, + ) + .new_circuit_input_builder() + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); + let block = block_convert::(&builder).unwrap(); + let chunks = chunk_convert(&block, &builder).unwrap(); + // assert last fingerprint acc are equal + if let Some(last_chunk) = chunks.last() { + assert_eq!( + last_chunk.by_address_rw_fingerprints.mul_acc, + last_chunk.chrono_rw_fingerprints.mul_acc + ) + } + } + + fn test_2_txs_with_chunk_size(total_chunks: usize, total_rws: usize) { + let bytecode = bytecode! { + GAS + STOP + }; + let addr_a = address!("0x000000000000000000000000000000000000AAAA"); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + let test_ctx = TestContext::<2, 2>::new( + None, + |accs| { + accs[0] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode); + accs[1].address(addr_a).balance(Word::from(1u64 << 20)); + }, + |mut txs, accs| { + txs[0] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + txs[1] + .from(accs[1].address) + .to(accs[0].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap(); + CircuitTestBuilder::new_from_test_ctx(test_ctx) + .params({ + FixedCParams { + total_chunks, + max_evm_rows: 1 << 12, + max_rws: total_rws / total_chunks, + max_txs: 2, + ..Default::default() + } + }) + .run_multiple_chunks_with_result(Some(total_chunks)) + .unwrap(); + } + + test_2_txs_with_various_chunk_size! { + test_2_txs_with_1_400: (1, 400), + test_2_txs_with_2_400: (2, 400), + test_2_txs_with_3_400: (3, 400), + test_2_txs_with_4_400: (4, 400), + test_2_txs_with_1_600: (1, 600), + test_2_txs_with_2_600: (2, 600), + test_2_txs_with_3_600: (3, 600), + test_2_txs_with_4_600: (4, 600), + test_2_txs_with_5_600: (5, 600), + test_2_txs_with_6_600: (6, 600), + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs index 27ca5f119e..ce2cc770bf 100644 --- a/zkevm-circuits/src/evm_circuit/execution/end_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/end_tx.rs @@ -13,7 +13,7 @@ use crate::{ tx::EndTxHelperGadget, CachedRegion, Cell, StepRws, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, BlockContextFieldTag, CallContextFieldTag, TxContextFieldTag}, util::{ @@ -145,6 +145,7 @@ impl ExecutionGadget for EndTxGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, @@ -416,7 +417,7 @@ mod test { fn end_tx_consistent_tx_id_write() { // check there is no consecutive txid write with same txid in rw_table - let block = CircuitTestBuilder::new_from_test_ctx( + let (block, _) = CircuitTestBuilder::new_from_test_ctx( TestContext::<2, 3>::new( None, account_0_code_account_1_no_code(bytecode! { STOP }), @@ -442,7 +443,7 @@ mod test { max_txs: 5, ..Default::default() }) - .build_block() + .build_block(None) .unwrap(); block.rws.0[&Target::CallContext] diff --git a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs index 2506639307..0421d8d54c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_code_store.rs @@ -10,7 +10,7 @@ use crate::{ memory_gadget::{CommonMemoryAddressGadget, MemoryAddressGadget}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{word::WordExpr, Expr}, @@ -99,6 +99,7 @@ impl ExecutionGadget for ErrorCodeStoreGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs index 6b7482046a..beb2f871d9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_creation_code.rs @@ -9,7 +9,7 @@ use crate::{ memory_gadget::{CommonMemoryAddressGadget, MemoryAddressGadget}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordExpr, Expr}, }; @@ -69,6 +69,7 @@ impl ExecutionGadget for ErrorInvalidCreationCodeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 6fab8e624b..453f5ef011 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_jump.rs @@ -9,7 +9,7 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroWordGadget}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi, WordLoHiCell}, @@ -111,6 +111,7 @@ impl ExecutionGadget for ErrorInvalidJumpGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, 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 f5257605bb..2726c8cc2a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_invalid_opcode.rs @@ -6,7 +6,7 @@ use crate::evm_circuit::{ common_gadget::CommonErrorGadget, constraint_builder::EVMConstraintBuilder, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use eth_types::Field; use gadgets::util::Expr; @@ -52,6 +52,7 @@ impl ExecutionGadget for ErrorInvalidOpcodeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs index 7878b67252..49b65ae27c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_account_access.rs @@ -9,7 +9,7 @@ use crate::{ math_gadget::LtGadget, select, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{word::WordExpr, Expr}, @@ -87,6 +87,7 @@ impl ExecutionGadget for ErrorOOGAccountAccessGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, 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 53b61484aa..4c2a851d27 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -13,7 +13,7 @@ use crate::{ }, table::CallContextFieldTag, util::Expr, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use bus_mapping::evm::OpcodeId; use eth_types::{Field, U256}; @@ -119,6 +119,7 @@ impl ExecutionGadget for ErrorOOGCallGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 c0061b31c5..fdf5c01889 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_constant.rs @@ -9,7 +9,7 @@ use crate::{ math_gadget::LtGadget, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::Expr, }; @@ -60,6 +60,7 @@ impl ExecutionGadget for ErrorOOGConstantGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs index 99756d1d23..2f5a78d2b6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_create.rs @@ -15,7 +15,7 @@ use crate::{ }, }, util::{word::Word32Cell, Expr}, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use eth_types::{ evm_types::{ @@ -117,6 +117,7 @@ impl ExecutionGadget for ErrorOOGCreateGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs index a2662ccb78..81edf3ab9e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_dynamic_memory.rs @@ -13,7 +13,7 @@ use crate::{ CachedRegion, Cell, }, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use eth_types::{evm_types::OpcodeId, Field}; use gadgets::util::{or, Expr}; @@ -77,6 +77,7 @@ impl ExecutionGadget for ErrorOOGDynamicMemoryGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, 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 dc819860bb..4f4921c3bb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_exp.rs @@ -9,7 +9,7 @@ use crate::{ math_gadget::{ByteSizeGadget, LtGadget}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr}, @@ -97,6 +97,7 @@ impl ExecutionGadget for ErrorOOGExpGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 bf129c5088..9bbe5f45fd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_log.rs @@ -12,7 +12,7 @@ use crate::{ }, or, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::Expr, @@ -98,6 +98,7 @@ impl ExecutionGadget for ErrorOOGLogGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 697279a626..e7b8087647 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 @@ -13,7 +13,7 @@ use crate::{ }, or, select, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -149,6 +149,7 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, transaction: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs index 6514a47790..828687fda2 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_precompile.rs @@ -11,7 +11,7 @@ use crate::{ }, }, table::CallContextFieldTag, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use bus_mapping::precompile::PrecompileCalls; use eth_types::{evm_types::GasCost, Field, ToScalar}; @@ -145,6 +145,7 @@ impl ExecutionGadget for ErrorOOGPrecompileGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _transaction: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs b/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs index 480d1ec077..ab3db568d5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_sha3.rs @@ -13,7 +13,7 @@ use crate::{ }, or, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::Expr, }; @@ -89,6 +89,7 @@ impl ExecutionGadget for ErrorOOGSha3Gadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 b0b8bc97dc..c6ed23f111 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 @@ -13,7 +13,7 @@ use crate::{ math_gadget::{LtGadget, PairSelectGadget}, or, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -155,6 +155,7 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, 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 948062ce7d..b19b3cb2df 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 @@ -12,7 +12,7 @@ use crate::{ }, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordLoHi, Expr}, }; @@ -102,6 +102,7 @@ impl ExecutionGadget for ErrorOOGStaticMemoryGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs b/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs index 7eb8f82813..54b3dc63b4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_precompile_failed.rs @@ -14,7 +14,7 @@ use crate::{ word::{Word32Cell, WordExpr, WordLoHi}, Expr, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; use bus_mapping::evm::OpcodeId; use eth_types::{Field, OpsIdentity, U256}; @@ -119,6 +119,7 @@ impl ExecutionGadget for ErrorPrecompileFailedGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, 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 52a9839239..42234d499a 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 @@ -10,7 +10,7 @@ use crate::{ math_gadget::{AddWordsGadget, IsZeroGadget, LtGadget}, not, or, sum, CachedRegion, Cell, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -121,6 +121,7 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs index 08a40469e3..ea5974c0f5 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_stack.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_stack.rs @@ -7,7 +7,7 @@ use crate::{ common_gadget::CommonErrorGadget, constraint_builder::EVMConstraintBuilder, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::Expr, }; @@ -53,6 +53,7 @@ impl ExecutionGadget for ErrorStackGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, 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 531bdc9813..baf2b244de 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -8,7 +8,7 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroWordGadget}, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -99,6 +99,7 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/exp.rs b/zkevm-circuits/src/evm_circuit/execution/exp.rs index 24de01a70e..41deac1b77 100644 --- a/zkevm-circuits/src/evm_circuit/execution/exp.rs +++ b/zkevm-circuits/src/evm_circuit/execution/exp.rs @@ -17,7 +17,7 @@ use crate::{ math_gadget::{ByteSizeGadget, IsEqualGadget, IsZeroGadget}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::word::{Word32Cell, Word4, WordExpr}, }; @@ -191,6 +191,7 @@ impl ExecutionGadget for ExponentiationGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index 5cda8007d8..ef72eb0a9d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -15,7 +15,7 @@ use crate::{ }, not, select, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::word::{Word32Cell, WordExpr, WordLoHi}, @@ -176,6 +176,7 @@ impl ExecutionGadget for ExtcodecopyGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, transaction: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index 99cd8b5b6a..fd9fcf370d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -10,7 +10,7 @@ use crate::{ }, select, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -100,6 +100,7 @@ impl ExecutionGadget for ExtcodehashGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index 908b1e2b7b..b80a2c3532 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::IsZeroWordGadget, not, select, AccountAddress, CachedRegion, Cell, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -117,6 +117,7 @@ impl ExecutionGadget for ExtcodesizeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/gas.rs b/zkevm-circuits/src/evm_circuit/execution/gas.rs index 1e626ccc95..05f5fc0b14 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gas.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gas.rs @@ -10,7 +10,7 @@ use crate::{ }, CachedRegion, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordExpr, Expr}, }; @@ -63,8 +63,9 @@ impl ExecutionGadget for GasGadget { &self, region: &mut CachedRegion<'_, '_, F>, offset: usize, - _block: &Block, - _transaction: &Transaction, + _: &Block, + _: &Chunk, + _: &Transaction, _call: &Call, step: &ExecStep, ) -> Result<(), Error> { @@ -139,7 +140,7 @@ mod test { .unwrap(); CircuitTestBuilder::<2, 1>::new_from_test_ctx(ctx) - .block_modifier(Box::new(|block| { + .block_modifier(Box::new(|block, _chunk| { // The above block has 2 steps (GAS and STOP). We forcefully assign a // wrong `gas_left` value for the second step, to assert that // the circuit verification fails for this scenario. diff --git a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs index 53ff2ab71f..c9160f4356 100644 --- a/zkevm-circuits/src/evm_circuit/execution/gasprice.rs +++ b/zkevm-circuits/src/evm_circuit/execution/gasprice.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, util::{ @@ -71,6 +71,7 @@ impl ExecutionGadget for GasPriceGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs b/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs index 1100175592..286d0a5152 100644 --- a/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs +++ b/zkevm-circuits/src/evm_circuit/execution/invalid_tx.rs @@ -13,6 +13,7 @@ use crate::{ }, table::AccountFieldTag, util::word::{Word32Cell, WordExpr, WordLoHi}, + witness::Chunk, }; use eth_types::{Field, ToScalar}; use gadgets::util::{not, or, Expr, Scalar}; @@ -95,6 +96,7 @@ impl ExecutionGadget for InvalidTxGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs index c2f52e4100..82a9db9bdd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/is_zero.rs +++ b/zkevm-circuits/src/evm_circuit/execution/is_zero.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, math_gadget, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi, WordLoHiCell}, @@ -61,6 +61,7 @@ impl ExecutionGadget for IsZeroGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/jump.rs b/zkevm-circuits/src/evm_circuit/execution/jump.rs index 615d2ba7c3..38ed5d680d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jump.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jump.rs @@ -10,7 +10,7 @@ use crate::{ }, CachedRegion, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordExpr, Expr}, }; @@ -59,6 +59,7 @@ impl ExecutionGadget for JumpGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs b/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs index 57cf0edd67..c5652a0d2d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpdest.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::Expr, }; @@ -43,6 +43,7 @@ impl ExecutionGadget for JumpdestGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, _: &Block, + _: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs index 40b9878ea0..08b07e6ce7 100644 --- a/zkevm-circuits/src/evm_circuit/execution/jumpi.rs +++ b/zkevm-circuits/src/evm_circuit/execution/jumpi.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::IsZeroWordGadget, select, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHi, WordLoHiCell}, @@ -90,6 +90,7 @@ impl ExecutionGadget for JumpiGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index 327cae451f..12b472de3a 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::{ }, not, sum, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxLogFieldTag}, util::{ @@ -195,6 +195,7 @@ impl ExecutionGadget for LogGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/memory.rs b/zkevm-circuits/src/evm_circuit/execution/memory.rs index 2956730d7f..798f2ecfbc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/memory.rs +++ b/zkevm-circuits/src/evm_circuit/execution/memory.rs @@ -13,7 +13,7 @@ use crate::{ memory_gadget::MemoryExpansionGadget, not, CachedRegion, MemoryAddress, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr}, @@ -119,6 +119,7 @@ impl ExecutionGadget for MemoryGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/msize.rs b/zkevm-circuits/src/evm_circuit/execution/msize.rs index b3c2274b44..857e499f68 100644 --- a/zkevm-circuits/src/evm_circuit/execution/msize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/msize.rs @@ -11,7 +11,7 @@ use crate::{ }, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordLoHi, Expr}, }; @@ -66,6 +66,7 @@ impl ExecutionGadget for MsizeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, _: &Block, + _: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, 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 19bf5f2735..2847fd45c6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mul_div_mod.rs @@ -11,7 +11,7 @@ use crate::{ math_gadget::{IsZeroWordGadget, LtWordGadget, MulAddWordsGadget}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -125,6 +125,7 @@ impl ExecutionGadget for MulDivModGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs index 65bf39dd28..ba46cc3304 100644 --- a/zkevm-circuits/src/evm_circuit/execution/mulmod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/mulmod.rs @@ -11,7 +11,7 @@ use crate::{ math_gadget::{IsZeroWordGadget, LtWordGadget, ModGadget, MulAddWords512Gadget}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -112,6 +112,7 @@ impl ExecutionGadget for MulModGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/not.rs b/zkevm-circuits/src/evm_circuit/execution/not.rs index d60e4dbbd8..610d8d77ed 100644 --- a/zkevm-circuits/src/evm_circuit/execution/not.rs +++ b/zkevm-circuits/src/evm_circuit/execution/not.rs @@ -8,7 +8,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr}, @@ -71,6 +71,7 @@ impl ExecutionGadget for NotGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/origin.rs b/zkevm-circuits/src/evm_circuit/execution/origin.rs index 2c51b35397..9ccf8e8b44 100644 --- a/zkevm-circuits/src/evm_circuit/execution/origin.rs +++ b/zkevm-circuits/src/evm_circuit/execution/origin.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, AccountAddress, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{CallContextFieldTag, TxContextFieldTag}, util::{word::WordExpr, Expr}, @@ -67,6 +67,7 @@ impl ExecutionGadget for OriginGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/padding.rs b/zkevm-circuits/src/evm_circuit/execution/padding.rs new file mode 100644 index 0000000000..bd663f6a62 --- /dev/null +++ b/zkevm-circuits/src/evm_circuit/execution/padding.rs @@ -0,0 +1,79 @@ +use std::marker::PhantomData; + +use crate::evm_circuit::{ + execution::ExecutionGadget, + step::ExecutionState, + util::{ + constraint_builder::{EVMConstraintBuilder, StepStateTransition}, + CachedRegion, + }, + witness::{Block, Call, Chunk, ExecStep, Transaction}, +}; +use eth_types::Field; +use halo2_proofs::plonk::Error; + +#[derive(Clone, Debug)] +pub(crate) struct PaddingGadget { + _phantom: PhantomData, +} + +impl ExecutionGadget for PaddingGadget { + const NAME: &'static str = "Padding"; + + const EXECUTION_STATE: ExecutionState = ExecutionState::Padding; + + fn configure(cb: &mut EVMConstraintBuilder) -> Self { + cb.require_step_state_transition(StepStateTransition { + ..StepStateTransition::same() + }); + + Self { + _phantom: PhantomData, + } + } + + fn assign_exec_step( + &self, + _region: &mut CachedRegion<'_, '_, F>, + _offset: usize, + _block: &Block, + _chunk: &Chunk, + _: &Transaction, + _: &Call, + _step: &ExecStep, + ) -> Result<(), Error> { + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::test_util::CircuitTestBuilder; + + use eth_types::bytecode; + + use mock::TestContext; + + fn test_circuit(evm_circuit_pad_to: usize) { + let bytecode = bytecode! { + PUSH1(0) + STOP + }; + + let ctx = TestContext::<2, 1>::simple_ctx_with_bytecode(bytecode).unwrap(); + + // finish required tests using this witness block + CircuitTestBuilder::<2, 1>::new_from_test_ctx(ctx) + .block_modifier(Box::new(move |block, _chunk| { + block.circuits_params.max_evm_rows = evm_circuit_pad_to + })) + .run(); + } + + // Test where the EVM circuit has a fixed size and contains several Padding + // at the end after the trace steps + #[test] + fn padding() { + test_circuit(50); + } +} diff --git a/zkevm-circuits/src/evm_circuit/execution/pc.rs b/zkevm-circuits/src/evm_circuit/execution/pc.rs index b89f28e789..89f5501dcd 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pc.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pc.rs @@ -10,7 +10,7 @@ use crate::{ }, CachedRegion, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{word::WordExpr, Expr}, }; @@ -64,6 +64,7 @@ impl ExecutionGadget for PcGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, _: &Block, + _: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/pop.rs b/zkevm-circuits/src/evm_circuit/execution/pop.rs index 59d0ac32dd..f197268806 100644 --- a/zkevm-circuits/src/evm_circuit/execution/pop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/pop.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHiCell}, @@ -57,6 +57,7 @@ impl ExecutionGadget for PopGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs index 1e14430c99..bfb194f44a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/ecrecover.rs @@ -18,7 +18,7 @@ use crate::{ }, table::CallContextFieldTag, util::word::{Word32Cell, WordExpr, WordLimbs, WordLoHi, WordLoHiCell}, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }; #[derive(Clone, Debug)] @@ -206,6 +206,7 @@ impl ExecutionGadget for EcrecoverGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs b/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs index ffec75d633..07250e3da3 100644 --- a/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs +++ b/zkevm-circuits/src/evm_circuit/execution/precompiles/identity.rs @@ -14,7 +14,7 @@ use crate::{ }, }, table::CallContextFieldTag, - witness::{Block, ExecStep, Transaction}, + witness::{Block, Chunk, ExecStep, Transaction}, }; #[derive(Clone, Debug)] @@ -105,6 +105,7 @@ impl ExecutionGadget for IdentityGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/push.rs b/zkevm-circuits/src/evm_circuit/execution/push.rs index d6e95b6552..21bb13b3d9 100644 --- a/zkevm-circuits/src/evm_circuit/execution/push.rs +++ b/zkevm-circuits/src/evm_circuit/execution/push.rs @@ -13,7 +13,7 @@ use crate::{ math_gadget::{IsEqualGadget, LtGadget}, not, or, select, sum, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr}, @@ -159,6 +159,7 @@ impl ExecutionGadget for PushGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index 02a1a5854a..48f9e352e1 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -15,7 +15,7 @@ use crate::{ }, not, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -282,6 +282,7 @@ impl ExecutionGadget for ReturnRevertGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index 7e1a8b57aa..1bf7334d4c 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -16,7 +16,7 @@ use crate::{ }, CachedRegion, Cell, MemoryAddress, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -170,6 +170,7 @@ impl ExecutionGadget for ReturnDataCopyGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs index dfd410e684..f17b61e27e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatasize.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -65,6 +65,7 @@ impl ExecutionGadget for ReturnDataSizeGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/sar.rs b/zkevm-circuits/src/evm_circuit/execution/sar.rs index 1bd8dc9805..6a73a0d83a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sar.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sar.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroGadget, LtGadget}, select, sum, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, Word4, WordExpr}, @@ -268,6 +268,7 @@ impl ExecutionGadget for SarGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs index b6779c420f..966edf9944 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sdiv_smod.rs @@ -13,7 +13,7 @@ use crate::{ }, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -156,6 +156,7 @@ impl ExecutionGadget for SignedDivModGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs index f6ec95b612..7bb83d4f1a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs +++ b/zkevm-circuits/src/evm_circuit/execution/selfbalance.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::{AccountFieldTag, CallContextFieldTag}, util::{ @@ -65,6 +65,7 @@ impl ExecutionGadget for SelfbalanceGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/sha3.rs b/zkevm-circuits/src/evm_circuit/execution/sha3.rs index 12cbac2ee4..9cff5746c4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sha3.rs @@ -18,7 +18,7 @@ use crate::{ }, rlc, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::word::{WordExpr, WordLoHi, WordLoHiCell}, }; @@ -117,6 +117,7 @@ impl ExecutionGadget for Sha3Gadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _tx: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs index 5dba3de3e4..6b795db87d 100644 --- a/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs +++ b/zkevm-circuits/src/evm_circuit/execution/shl_shr.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::{IsZeroGadget, IsZeroWordGadget, LtWordGadget, MulAddWordsGadget}, sum, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -169,6 +169,7 @@ impl ExecutionGadget for ShlShrGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs index 2211fa90d5..23be044ca4 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signed_comparator.rs @@ -9,7 +9,7 @@ use crate::{ math_gadget::{ComparisonGadget, IsEqualGadget, LtGadget}, select, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32Cell, WordExpr, WordLoHi}, @@ -144,6 +144,7 @@ impl ExecutionGadget for SignedComparatorGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _transaction: &Transaction, _call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/signextend.rs b/zkevm-circuits/src/evm_circuit/execution/signextend.rs index faec033ad9..d9c703e68e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/signextend.rs +++ b/zkevm-circuits/src/evm_circuit/execution/signextend.rs @@ -13,7 +13,7 @@ use crate::{ math_gadget::{IsEqualGadget, IsZeroGadget}, select, sum, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{Word32, Word32Cell, WordExpr}, @@ -158,6 +158,7 @@ impl ExecutionGadget for SignextendGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/sload.rs b/zkevm-circuits/src/evm_circuit/execution/sload.rs index 7a222c9b44..0065e00ad8 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sload.rs @@ -9,7 +9,7 @@ use crate::{ }, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -103,6 +103,7 @@ impl ExecutionGadget for SloadGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/sstore.rs b/zkevm-circuits/src/evm_circuit/execution/sstore.rs index ebcb7b52db..bcc946cf8b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::{IsEqualWordGadget, IsZeroWordGadget, LtGadget}, not, CachedRegion, Cell, U64Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -167,6 +167,7 @@ impl ExecutionGadget for SstoreGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, tx: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/stop.rs b/zkevm-circuits/src/evm_circuit/execution/stop.rs index 8ce8ca02ec..de2a943ab6 100644 --- a/zkevm-circuits/src/evm_circuit/execution/stop.rs +++ b/zkevm-circuits/src/evm_circuit/execution/stop.rs @@ -12,7 +12,7 @@ use crate::{ math_gadget::ComparisonGadget, CachedRegion, Cell, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, table::CallContextFieldTag, util::{ @@ -108,6 +108,7 @@ impl ExecutionGadget for StopGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, call: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/execution/swap.rs b/zkevm-circuits/src/evm_circuit/execution/swap.rs index 5bbd27df22..92d2706cbb 100644 --- a/zkevm-circuits/src/evm_circuit/execution/swap.rs +++ b/zkevm-circuits/src/evm_circuit/execution/swap.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta}, CachedRegion, }, - witness::{Block, Call, ExecStep, Transaction}, + witness::{Block, Call, Chunk, ExecStep, Transaction}, }, util::{ word::{WordExpr, WordLoHiCell}, @@ -66,6 +66,7 @@ impl ExecutionGadget for SwapGadget { region: &mut CachedRegion<'_, '_, F>, offset: usize, block: &Block, + _chunk: &Chunk, _: &Transaction, _: &Call, step: &ExecStep, diff --git a/zkevm-circuits/src/evm_circuit/param.rs b/zkevm-circuits/src/evm_circuit/param.rs index e2e4550418..f205e14475 100644 --- a/zkevm-circuits/src/evm_circuit/param.rs +++ b/zkevm-circuits/src/evm_circuit/param.rs @@ -9,7 +9,7 @@ use halo2_proofs::{ use std::collections::HashMap; // Step dimension -pub(crate) const STEP_WIDTH: usize = 128; +pub(crate) const STEP_WIDTH: usize = 139; /// Step height pub const MAX_STEP_HEIGHT: usize = 19; /// The height of the state of a step, used by gates that connect two @@ -43,7 +43,8 @@ pub(crate) const EVM_LOOKUP_COLS: usize = FIXED_TABLE_LOOKUPS + COPY_TABLE_LOOKUPS + KECCAK_TABLE_LOOKUPS + EXP_TABLE_LOOKUPS - + SIG_TABLE_LOOKUPS; + + SIG_TABLE_LOOKUPS + + CHUNK_CTX_TABLE_LOOKUPS; /// Lookups done per row. pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ @@ -56,6 +57,7 @@ pub const LOOKUP_CONFIG: &[(Table, usize)] = &[ (Table::Keccak, KECCAK_TABLE_LOOKUPS), (Table::Exp, EXP_TABLE_LOOKUPS), (Table::Sig, SIG_TABLE_LOOKUPS), + (Table::ChunkCtx, CHUNK_CTX_TABLE_LOOKUPS), ]; /// Fixed Table lookups done in EVMCircuit @@ -65,7 +67,7 @@ pub const FIXED_TABLE_LOOKUPS: usize = 8; pub const TX_TABLE_LOOKUPS: usize = 4; /// Rw Table lookups done in EVMCircuit -pub const RW_TABLE_LOOKUPS: usize = 8; +pub const RW_TABLE_LOOKUPS: usize = 13; /// Bytecode Table lookups done in EVMCircuit pub const BYTECODE_TABLE_LOOKUPS: usize = 4; @@ -85,6 +87,9 @@ pub const EXP_TABLE_LOOKUPS: usize = 1; /// Sig Table lookups done in EVMCircuit pub const SIG_TABLE_LOOKUPS: usize = 1; +/// chunk_ctx Table lookups done in EVMCircuit +pub const CHUNK_CTX_TABLE_LOOKUPS: usize = 1; + /// Maximum number of bytes that an integer can fit in field without wrapping /// around. pub(crate) const MAX_N_BYTES_INTEGER: usize = 31; @@ -168,7 +173,7 @@ pub(crate) const N_BYTES_TX: usize = N_BYTES_TX_NONCE + N_BYTES_TX_CALLDATA_GASCOST + N_BYTES_TX_TXSIGNHASH; -pub(crate) const N_BYTES_WITHDRAWAL: usize = N_BYTES_U64 //id +pub(crate) const N_BYTES_WITHDRAWAL: usize = N_BYTES_U64 //id + N_BYTES_U64 // validator id + N_BYTES_ACCOUNT_ADDRESS // address + N_BYTES_U64; // amount diff --git a/zkevm-circuits/src/evm_circuit/step.rs b/zkevm-circuits/src/evm_circuit/step.rs index 195cc77321..f26f9290f6 100644 --- a/zkevm-circuits/src/evm_circuit/step.rs +++ b/zkevm-circuits/src/evm_circuit/step.rs @@ -58,6 +58,9 @@ pub enum ExecutionState { BeginTx, EndTx, EndBlock, + Padding, + BeginChunk, + EndChunk, InvalidTx, // Opcode successful cases STOP, @@ -342,7 +345,10 @@ impl From<&ExecStep> for ExecutionState { }, ExecState::BeginTx => ExecutionState::BeginTx, ExecState::EndTx => ExecutionState::EndTx, + ExecState::Padding => ExecutionState::Padding, ExecState::EndBlock => ExecutionState::EndBlock, + ExecState::BeginChunk => ExecutionState::BeginChunk, + ExecState::EndChunk => ExecutionState::EndChunk, ExecState::InvalidTx => ExecutionState::InvalidTx, } } @@ -738,6 +744,8 @@ pub(crate) struct StepState { pub(crate) execution_state: DynamicSelectorHalf, /// The Read/Write counter pub(crate) rw_counter: Cell, + /// The Read/Write counter accumulated in current chunk + pub(crate) inner_rw_counter: Cell, /// The unique identifier of call in the whole proof, using the /// `rw_counter` at the call step. pub(crate) call_id: Cell, @@ -792,6 +800,7 @@ impl Step { ExecutionState::amount(), ), rw_counter: cell_manager.query_cell(meta, CellType::StoragePhase1), + inner_rw_counter: cell_manager.query_cell(meta, CellType::StoragePhase1), call_id: cell_manager.query_cell(meta, CellType::StoragePhase1), is_root: cell_manager.query_cell(meta, CellType::StoragePhase1), is_create: cell_manager.query_cell(meta, CellType::StoragePhase1), @@ -836,6 +845,11 @@ impl Step { self.state .rw_counter .assign(region, offset, Value::known(F::from(step.rwc.into())))?; + self.state.inner_rw_counter.assign( + region, + offset, + Value::known(F::from(step.rwc_inner_chunk.into())), + )?; self.state .call_id .assign(region, offset, Value::known(F::from(call.call_id as u64)))?; diff --git a/zkevm-circuits/src/evm_circuit/table.rs b/zkevm-circuits/src/evm_circuit/table.rs index cfa5170fd6..b0efede59f 100644 --- a/zkevm-circuits/src/evm_circuit/table.rs +++ b/zkevm-circuits/src/evm_circuit/table.rs @@ -193,6 +193,8 @@ pub enum Table { Exp, /// Lookup for sig table Sig, + /// Lookup for chunk context + ChunkCtx, } #[derive(Clone, Debug)] @@ -369,6 +371,13 @@ pub(crate) enum Lookup { recovered_addr: Expression, is_valid: Expression, }, + /// Lookup to block table, which contains constants of this block. + ChunkCtx { + /// Tag to specify which field to read. + field_tag: Expression, + /// value + value: Expression, + }, /// Conditional lookup enabled by the first element. Conditional(Expression, Box>), @@ -382,6 +391,7 @@ impl Lookup { pub(crate) fn table(&self) -> Table { match self { Self::Fixed { .. } => Table::Fixed, + Self::ChunkCtx { .. } => Table::ChunkCtx, Self::Tx { .. } => Table::Tx, Self::Rw { .. } => Table::Rw, Self::Bytecode { .. } => Table::Bytecode, @@ -527,6 +537,7 @@ impl Lookup { recovered_addr.clone(), is_valid.clone(), ], + Self::ChunkCtx { field_tag, value } => vec![field_tag.clone(), value.clone()], Self::Conditional(condition, lookup) => lookup .input_exprs() .into_iter() diff --git a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs index ebf88f135f..a887b3fda7 100644 --- a/zkevm-circuits/src/evm_circuit/util/common_gadget.rs +++ b/zkevm-circuits/src/evm_circuit/util/common_gadget.rs @@ -20,12 +20,12 @@ use crate::{ not, Cell, }, }, - table::{AccountFieldTag, CallContextFieldTag}, + table::{chunk_ctx_table::ChunkCtxFieldTag, AccountFieldTag, CallContextFieldTag}, util::{ word::{Word32, Word32Cell, WordExpr, WordLoHi, WordLoHiCell}, Expr, }, - witness::{Block, Call, ExecStep}, + witness::{Block, Call, Chunk, ExecStep}, }; use bus_mapping::state_db::CodeDB; use eth_types::{ @@ -234,7 +234,7 @@ impl RestoreContextGadget { gas_left: To(gas_left), memory_word_size: To(caller_memory_word_size.expr()), reversible_write_counter: To(reversible_write_counter), - log_id: Same, + ..Default::default() }); Self { @@ -1169,3 +1169,106 @@ impl WordByteRangeGadget { self.not_overflow.expr() } } + +/// current SRS size < 2^30 so use 4 bytes (2^32) in LtGadet should be enough +const MAX_RW_BYTES: usize = u32::BITS as usize / 8; + +/// Check consecutive Rw::Padding in rw_table to assure rw_table no malicious insertion +#[derive(Clone, Debug)] +pub(crate) struct RwTablePaddingGadget { + is_empty_rwc: IsZeroGadget, + max_rws: Cell, + chunk_index: Cell, + is_end_padding_exist: LtGadget, + is_first_chunk: IsZeroGadget, +} + +impl RwTablePaddingGadget { + pub(crate) fn construct( + cb: &mut EVMConstraintBuilder, + inner_rws_before_padding: Expression, + ) -> Self { + let max_rws = cb.query_copy_cell(); + let chunk_index = cb.query_cell(); + let is_empty_rwc = + IsZeroGadget::construct(cb, cb.curr.state.rw_counter.clone().expr() - 1.expr()); + + cb.chunk_context_lookup(ChunkCtxFieldTag::CurrentChunkIndex, chunk_index.expr()); + let is_first_chunk = IsZeroGadget::construct(cb, chunk_index.expr()); + + // Verify rw_counter counts to the same number of meaningful rows in + // rw_table to ensure there is no malicious insertion. + // Verify that there are at most total_rws meaningful entries in the rw_table + // - startop only exist in first chunk + // - end paddings are consecutively + cb.condition(is_first_chunk.expr(), |cb| { + cb.rw_table_start_lookup(1.expr()); + }); + + let is_end_padding_exist = LtGadget::<_, MAX_RW_BYTES>::construct( + cb, + 1.expr(), + max_rws.expr() - inner_rws_before_padding.expr(), + ); + + cb.condition(is_end_padding_exist.expr(), |cb| { + cb.rw_table_padding_lookup(inner_rws_before_padding.expr() + 1.expr()); + cb.rw_table_padding_lookup(max_rws.expr() - 1.expr()); + }); + + // Since every lookup done in the EVM circuit must succeed and uses + // a unique rw_counter, we know that at least there are + // total_rws meaningful entries in the rw_table. + // We conclude that the number of meaningful entries in the rw_table + // is total_rws. + + Self { + is_empty_rwc, + max_rws, + chunk_index, + is_first_chunk, + is_end_padding_exist, + } + } + + pub(crate) fn assign_exec_step( + &self, + region: &mut CachedRegion<'_, '_, F>, + offset: usize, + _block: &Block, + chunk: &Chunk, + inner_rws_before_padding: u64, + step: &ExecStep, + ) -> Result<(), Error> { + let total_rwc = u64::from(step.rwc) - 1; + self.is_empty_rwc + .assign(region, offset, F::from(total_rwc))?; + + let max_rws = F::from(chunk.fixed_param.max_rws as u64); + let max_rws_assigned = self.max_rws.assign(region, offset, Value::known(max_rws))?; + + self.chunk_index.assign( + region, + offset, + Value::known(F::from(chunk.chunk_context.idx as u64)), + )?; + + self.is_first_chunk + .assign(region, offset, F::from(chunk.chunk_context.idx as u64))?; + + self.is_end_padding_exist.assign( + region, + offset, + F::ONE, + max_rws.sub(F::from(inner_rws_before_padding)), + )?; + + // When rw_indices is not empty, means current step is non-padding step, we're at the + // last row (at a fixed offset), where we need to access the max_rws + // constant. + if step.rw_indices_len() != 0 { + region.constrain_constant(max_rws_assigned, max_rws)?; + } + Ok(()) + } +} diff --git a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs index 5e20bff23f..6fdcd6d12b 100644 --- a/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs +++ b/zkevm-circuits/src/evm_circuit/util/constraint_builder.rs @@ -14,8 +14,8 @@ use crate::{ util::{Cell, RandomLinearCombination}, }, table::{ - AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, TxContextFieldTag, TxLogFieldTag, - TxReceiptFieldTag, + chunk_ctx_table::ChunkCtxFieldTag, AccountFieldTag, BytecodeFieldTag, CallContextFieldTag, + StepStateFieldTag, TxContextFieldTag, TxLogFieldTag, TxReceiptFieldTag, }, util::{ build_tx_log_expression, query_expression, @@ -97,6 +97,22 @@ impl StepStateTransition { log_id: Transition::Any, } } + + pub(crate) fn same() -> Self { + Self { + rw_counter: Transition::Same, + call_id: Transition::Same, + is_root: Transition::Same, + is_create: Transition::Same, + code_hash: Transition::Same, + program_counter: Transition::Same, + stack_pointer: Transition::Same, + gas_left: Transition::Same, + memory_word_size: Transition::Same, + reversible_write_counter: Transition::Same, + log_id: Transition::Same, + } + } } /// ReversionInfo counts `rw_counter` of reversion for gadgets, by tracking how @@ -573,6 +589,27 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { }; } + // Note: special case handling: inner_rw_counter shared the same Transition with + // rw_conuter + match &step_state_transition.rw_counter { + Transition::Same => self.require_equal( + "State transition (same) constraint of inner_rw_counter", + self.next.state.inner_rw_counter.expr(), + self.curr.state.inner_rw_counter.expr(), + ), + Transition::Delta(delta) => self.require_equal( + concat!("State transition (delta) constraint of inner_rw_counter"), + self.next.state.inner_rw_counter.expr(), + self.curr.state.inner_rw_counter.expr() + delta.clone(), + ), + Transition::To(to) => self.require_equal( + "State transition (to) constraint of inner_rw_counter", + self.next.state.inner_rw_counter.expr(), + to.clone(), + ), + _ => {} + } + constrain!(rw_counter); constrain!(call_id); constrain!(is_root); @@ -1432,8 +1469,104 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ); } - // RwTable Padding (Start tag) + pub(crate) fn chunk_context_lookup( + &mut self, + field_tag: ChunkCtxFieldTag, + value: Expression, + ) { + self.add_lookup( + "chunk_ctx lookup", + Lookup::ChunkCtx { + field_tag: field_tag.expr(), + value, + }, + ); + } + + pub(crate) fn step_state_lookup(&mut self, is_write: Expression) { + self.rw_lookup( + "StepState lookup codehash", + is_write.clone(), + Target::StepState, + RwValues::new( + 0.expr(), + 0.expr(), + StepStateFieldTag::CodeHash.expr(), + WordLoHi::zero(), + self.curr.state.code_hash.to_word(), + WordLoHi::zero(), + WordLoHi::zero(), + ), + ); + + [ + ( + "StepState lookup CallID", + self.curr.state.call_id.clone(), + StepStateFieldTag::CallID, + ), + ( + "StepState lookup IsRoot", + self.curr.state.is_root.clone(), + StepStateFieldTag::IsRoot, + ), + ( + "StepState lookup IsCreate", + self.curr.state.is_create.clone(), + StepStateFieldTag::IsCreate, + ), + ( + "StepState lookup ProgramCounter", + self.curr.state.program_counter.clone(), + StepStateFieldTag::ProgramCounter, + ), + ( + "StepState lookup StackPointer", + self.curr.state.stack_pointer.clone(), + StepStateFieldTag::StackPointer, + ), + ( + "StepState lookup GasLeft", + self.curr.state.gas_left.clone(), + StepStateFieldTag::GasLeft, + ), + ( + "StepState lookup MemoryWordSize", + self.curr.state.memory_word_size.clone(), + StepStateFieldTag::MemoryWordSize, + ), + ( + "StepState lookup ReversibleWriteCounter", + self.curr.state.reversible_write_counter.clone(), + StepStateFieldTag::ReversibleWriteCounter, + ), + ( + "StepState lookup LogID", + self.curr.state.log_id.clone(), + StepStateFieldTag::LogID, + ), + ] + .iter() + .for_each(|(name, cell, field_tag)| { + self.rw_lookup( + name, + is_write.clone(), + Target::StepState, + RwValues::new( + 0.expr(), + 0.expr(), + field_tag.expr(), + WordLoHi::zero(), + WordLoHi::from_lo_unchecked(cell.expr()), + WordLoHi::zero(), + WordLoHi::zero(), + ), + ); + }); + } + // RwTable Start (Start tag) + #[allow(dead_code)] pub(crate) fn rw_table_start_lookup(&mut self, counter: Expression) { self.rw_lookup_with_counter( "Start lookup", @@ -1452,6 +1585,26 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { ); } + // RwTable Padding (Padding tag) + #[allow(dead_code)] + pub(crate) fn rw_table_padding_lookup(&mut self, counter: Expression) { + self.rw_lookup_with_counter( + "Padding lookup", + counter, + 0.expr(), + Target::Padding, + RwValues::new( + 0.expr(), + 0.expr(), + 0.expr(), + WordLoHi::zero(), + WordLoHi::zero(), + WordLoHi::zero(), + WordLoHi::zero(), + ), + ); + } + // Copy Table #[allow(clippy::too_many_arguments)] @@ -1552,7 +1705,6 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { } // Validation - pub(crate) fn validate_degree(&self, degree: usize, name: &'static str) { // We need to subtract IMPLICIT_DEGREE from MAX_DEGREE because all expressions // will be multiplied by state selector and q_step/q_step_first @@ -1685,11 +1837,13 @@ impl<'a, F: Field> EVMConstraintBuilder<'a, F> { Some(condition) => lookup.conditional(condition), None => lookup, }; + let compressed_expr = self.split_expression( "Lookup compression", rlc::expr(&lookup.input_exprs(), self.challenges.lookup_input()), MAX_DEGREE - IMPLICIT_DEGREE, ); + self.store_expression(name, compressed_expr, CellType::Lookup(lookup.table())); } diff --git a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs index 059f896745..5c820dea92 100644 --- a/zkevm-circuits/src/evm_circuit/util/instrumentation.rs +++ b/zkevm-circuits/src/evm_circuit/util/instrumentation.rs @@ -107,6 +107,9 @@ impl Instrument { CellType::Lookup(Table::Sig) => { report.sig_table = data_entry; } + CellType::Lookup(Table::ChunkCtx) => { + report.chunk_ctx_table = data_entry; + } } } report_collection.push(report); @@ -135,6 +138,7 @@ pub struct ExecStateReport { pub keccak_table: StateReportRow, pub exp_table: StateReportRow, pub sig_table: StateReportRow, + pub chunk_ctx_table: StateReportRow, } impl From for ExecStateReport { diff --git a/zkevm-circuits/src/evm_circuit/util/tx.rs b/zkevm-circuits/src/evm_circuit/util/tx.rs index d99b029ffd..162aa685d2 100644 --- a/zkevm-circuits/src/evm_circuit/util/tx.rs +++ b/zkevm-circuits/src/evm_circuit/util/tx.rs @@ -143,7 +143,8 @@ impl EndTxHelperGadget { }); }); cb.condition( - cb.next.execution_state_selector([ExecutionState::EndBlock]), + cb.next + .execution_state_selector([ExecutionState::EndBlock, ExecutionState::Padding]), |cb| { cb.require_step_state_transition(StepStateTransition { rw_counter: Delta(rw_counter_offset.expr()), diff --git a/zkevm-circuits/src/exp_circuit.rs b/zkevm-circuits/src/exp_circuit.rs index 5251308dcd..63749a2ffa 100644 --- a/zkevm-circuits/src/exp_circuit.rs +++ b/zkevm-circuits/src/exp_circuit.rs @@ -12,7 +12,7 @@ use crate::{ evm_circuit::util::constraint_builder::BaseConstraintBuilder, table::{ExpTable, LookupTable}, util::{Challenges, SubCircuit, SubCircuitConfig}, - witness, + witness::{self, Chunk}, }; use bus_mapping::circuit_input_builder::{ExpEvent, ExpStep}; use eth_types::{Field, ToScalar, U256}; @@ -521,20 +521,17 @@ impl SubCircuit for ExpCircuit { 11 } - fn new_from_block(block: &witness::Block) -> Self { + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { // Hardcoded to pass unit tests for now. In the future, insert: - // "block.circuits_params.max_exp_rows" - Self::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ) + // "chunk.fixed_param.max_exp_rows" + Self::new(block.exp_events.clone(), chunk.fixed_param.max_exp_steps) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { ( Self::Config::min_num_rows(&block.exp_events), - block.circuits_params.max_exp_steps, + chunk.fixed_param.max_exp_steps, ) } diff --git a/zkevm-circuits/src/exp_circuit/test.rs b/zkevm-circuits/src/exp_circuit/test.rs index 01304fda34..6f2129bda6 100644 --- a/zkevm-circuits/src/exp_circuit/test.rs +++ b/zkevm-circuits/src/exp_circuit/test.rs @@ -2,6 +2,7 @@ use crate::{ evm_circuit::witness::{block_convert, Block}, exp_circuit::ExpCircuit, util::{unusable_rows, SubCircuit}, + witness::{chunk_convert, Chunk}, }; use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, FixedCParams}, @@ -20,11 +21,8 @@ fn exp_circuit_unusable_rows() { } /// Test exponentiation circuit with the provided block witness -pub fn test_exp_circuit(k: u32, block: Block) { - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); +pub fn test_exp_circuit(k: u32, block: Block, chunk: Chunk) { + let circuit = ExpCircuit::::new(block.exp_events, chunk.fixed_param.max_exp_steps); let prover = MockProver::::run(k, &circuit, vec![]).unwrap(); prover.assert_satisfied() } @@ -51,35 +49,34 @@ fn gen_data(code: Bytecode, default_params: bool) -> CircuitInputBuilder::simple_ctx_with_bytecode(code).unwrap(); let block: GethData = test_ctx.into(); // Needs default parameters for variadic size test - let builder = if default_params { - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) - .new_circuit_input_builder(); - builder + + if default_params { + BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - builder + .unwrap() } else { - let builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder(); - builder + BlockData::new_from_geth_data(block.clone()) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap() - }; - builder + } } fn test_ok(base: Word, exponent: Word, k: Option) { let code = gen_code_single(base, exponent); let builder = gen_data(code, false); let block = block_convert::(&builder).unwrap(); - test_exp_circuit(k.unwrap_or(18), block); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + test_exp_circuit(k.unwrap_or(18), block, chunk); } fn test_ok_multiple(args: Vec<(Word, Word)>) { let code = gen_code_multiple(args); let builder = gen_data(code, false); let block = block_convert::(&builder).unwrap(); - test_exp_circuit(20, block); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + test_exp_circuit(20, block, chunk); } #[test] @@ -123,17 +120,13 @@ fn variadic_size_check() { let block: GethData = TestContext::<0, 0>::new(None, |_| {}, |_, _| {}, |b, _| b) .unwrap() .into(); - let mut builder = - BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) - .new_circuit_input_builder(); - builder + let builder = BlockData::new_from_geth_data_with_params(block.clone(), FixedCParams::default()) + .new_circuit_input_builder() .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); let block = block_convert::(&builder).unwrap(); - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + let circuit = ExpCircuit::::new(block.exp_events, chunk.fixed_param.max_exp_steps); let prover1 = MockProver::::run(k, &circuit, vec![]).unwrap(); // Non-empty @@ -148,10 +141,8 @@ fn variadic_size_check() { }; let builder = gen_data(code, true); let block = block_convert::(&builder).unwrap(); - let circuit = ExpCircuit::::new( - block.exp_events.clone(), - block.circuits_params.max_exp_steps, - ); + let chunk = chunk_convert::(&block, &builder).unwrap().remove(0); + let circuit = ExpCircuit::::new(block.exp_events, chunk.fixed_param.max_exp_steps); let prover2 = MockProver::::run(k, &circuit, vec![]).unwrap(); assert_eq!(prover1.fixed(), prover2.fixed()); diff --git a/zkevm-circuits/src/keccak_circuit.rs b/zkevm-circuits/src/keccak_circuit.rs index 529cd61c9d..14cf9453de 100644 --- a/zkevm-circuits/src/keccak_circuit.rs +++ b/zkevm-circuits/src/keccak_circuit.rs @@ -35,7 +35,7 @@ use crate::{ word::{Word32, WordExpr}, Challenges, SubCircuit, SubCircuitConfig, }, - witness, + witness::{self, Chunk}, }; use eth_types::Field; use gadgets::util::{and, not, select, sum, Expr}; @@ -1005,26 +1005,26 @@ impl SubCircuit for KeccakCircuit { keccak_unusable_rows() } - /// The `block.circuits_params.keccak_padding` parameter, when enabled, sets + /// The `chunk.fixed_param.keccak_padding` parmeter, when enabled, sets /// up the circuit to support a fixed number of permutations/keccak_f's, /// independently of the permutations required by `inputs`. - fn new_from_block(block: &witness::Block) -> Self { + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { Self::new( - block.circuits_params.max_keccak_rows, + chunk.fixed_param.max_keccak_rows, block.keccak_inputs.clone(), ) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { - let rows_per_chunk = (NUM_ROUNDS + 1) * get_num_rows_per_round(); + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { + let rows_perchunk = (NUM_ROUNDS + 1) * get_num_rows_per_round(); ( block .keccak_inputs .iter() - .map(|bytes| (bytes.len() as f64 / 136.0).ceil() as usize * rows_per_chunk) + .map(|bytes| (bytes.len() as f64 / 136.0).ceil() as usize * rows_perchunk) .sum(), - block.circuits_params.max_keccak_rows, + chunk.fixed_param.max_keccak_rows, ) } diff --git a/zkevm-circuits/src/pi_circuit.rs b/zkevm-circuits/src/pi_circuit.rs index 1c9f700202..920b0e13d7 100644 --- a/zkevm-circuits/src/pi_circuit.rs +++ b/zkevm-circuits/src/pi_circuit.rs @@ -34,7 +34,7 @@ use crate::{ table::{BlockTable, KeccakTable, LookupTable, TxFieldTag, TxTable, WdTable}, tx_circuit::TX_LEN, util::{word::WordLoHi, Challenges, SubCircuit, SubCircuitConfig}, - witness, + witness::{self, Chunk}, }; use gadgets::{ is_zero::IsZeroChip, @@ -1460,25 +1460,25 @@ impl SubCircuit for PiCircuit { 6 } - fn new_from_block(block: &witness::Block) -> Self { + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { let public_data = public_data_convert(block); PiCircuit::new( - block.circuits_params.max_txs, - block.circuits_params.max_withdrawals, - block.circuits_params.max_calldata, + chunk.fixed_param.max_txs, + chunk.fixed_param.max_withdrawals, + chunk.fixed_param.max_calldata, public_data, ) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { let calldata_len = block.txs.iter().map(|tx| tx.call_data.len()).sum(); ( Self::Config::circuit_len_all(block.txs.len(), block.withdrawals().len(), calldata_len), Self::Config::circuit_len_all( - block.circuits_params.max_txs, - block.circuits_params.max_withdrawals, - block.circuits_params.max_calldata, + chunk.fixed_param.max_txs, + chunk.fixed_param.max_withdrawals, + chunk.fixed_param.max_calldata, ), ) } diff --git a/zkevm-circuits/src/pi_circuit/test.rs b/zkevm-circuits/src/pi_circuit/test.rs index 49c9fe21a6..e8f39ed19e 100644 --- a/zkevm-circuits/src/pi_circuit/test.rs +++ b/zkevm-circuits/src/pi_circuit/test.rs @@ -1,6 +1,10 @@ use std::collections::HashMap; -use crate::{pi_circuit::dev::PiCircuitParams, util::unusable_rows, witness::block_convert}; +use crate::{ + pi_circuit::dev::PiCircuitParams, + util::unusable_rows, + witness::{block_convert, chunk_convert}, +}; use super::*; use bus_mapping::{ @@ -129,7 +133,7 @@ fn test_1tx_1maxtx() { wallets.insert(wallet_a.address(), wallet_a); let mut block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( + let builder = BlockData::new_from_geth_data_with_params( block.clone(), FixedCParams { max_txs: MAX_TXS, @@ -139,17 +143,15 @@ fn test_1tx_1maxtx() { ..Default::default() }, ) - .new_circuit_input_builder(); + .new_circuit_input_builder() + .handle_block(&block.eth_block, &block.geth_traces) + .unwrap(); block.sign(&wallets); - - builder - .handle_block(&block.eth_block, &block.geth_traces) - .unwrap(); - let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); // MAX_TXS, MAX_TXS align with `CircuitsParams` - let circuit = PiCircuit::::new_from_block(&block); + let circuit = PiCircuit::::new_from_block(&block, &chunk); let public_inputs = circuit.instance(); let prover = match MockProver::run(degree, &circuit, public_inputs) { @@ -209,7 +211,7 @@ fn test_1wd_1wdmax() { wallets.insert(wallet_a.address(), wallet_a); let mut block: GethData = test_ctx.into(); - let mut builder = BlockData::new_from_geth_data_with_params( + let builder = BlockData::new_from_geth_data_with_params( block.clone(), FixedCParams { max_txs: MAX_TXS, @@ -223,13 +225,14 @@ fn test_1wd_1wdmax() { block.sign(&wallets); - builder + let builder = builder .handle_block(&block.eth_block, &block.geth_traces) .unwrap(); let block = block_convert(&builder).unwrap(); + let chunk = chunk_convert(&block, &builder).unwrap().remove(0); // MAX_TXS, MAX_TXS align with `CircuitsParams` - let circuit = PiCircuit::::new_from_block(&block); + let circuit = PiCircuit::::new_from_block(&block, &chunk); let public_inputs = circuit.instance(); let prover = match MockProver::run(degree, &circuit, public_inputs) { diff --git a/zkevm-circuits/src/root_circuit.rs b/zkevm-circuits/src/root_circuit.rs index 6428adcbb1..18fb5f72f9 100644 --- a/zkevm-circuits/src/root_circuit.rs +++ b/zkevm-circuits/src/root_circuit.rs @@ -4,11 +4,11 @@ use halo2_proofs::{ arithmetic::Field as Halo2Field, circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::{serde::SerdeObject, CurveAffine, CurveExt}, - plonk::{Circuit, ConstraintSystem, Error}, + plonk::{Any, Circuit, Column, ConstraintSystem, Error}, poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, }; use itertools::Itertools; -use maingate::MainGateInstructions; +use maingate::{MainGateInstructions, RegionCtx}; use snark_verifier::{ pcs::{ @@ -19,7 +19,7 @@ use snark_verifier::{ util::arithmetic::MultiMillerLoop, verifier::plonk::PlonkProtocol, }; -use std::{iter, marker::PhantomData, rc::Rc}; +use std::{marker::PhantomData, rc::Rc}; mod aggregation; @@ -27,6 +27,7 @@ mod aggregation; mod dev; #[cfg(test)] mod test; + #[cfg(feature = "test-circuits")] pub use self::RootCircuit as TestRootCircuit; @@ -42,6 +43,76 @@ pub use snark_verifier::{ system::halo2::{compile, transcript::evm::EvmTranscript, Config}, }; +const NUM_OF_SUPERCIRCUIT_INSTANCES: usize = 2; + +/// SuperCircuitInstance is to demystifying supercircuit instance to meaningful name. +#[derive(Clone)] +struct SuperCircuitInstance { + // chunk_ctx + pub chunk_index: T, + pub chunk_index_next: T, + pub total_chunk: T, + pub initial_rwc: T, + pub end_rwc: T, + + // pi circuit + pub pi_digest_lo: T, + pub pi_digest_hi: T, + + // state circuit + pub sc_permu_alpha: T, + pub sc_permu_gamma: T, + pub sc_rwtable_row_prev_fingerprint: T, + pub sc_rwtable_row_curr_fingerprint: T, + pub sc_rwtable_prev_fingerprint: T, + pub sc_rwtable_curr_fingerprint: T, + + // evm circuit + pub ec_permu_alpha: T, + pub ec_permu_gamma: T, + pub ec_rwtable_row_prev_fingerprint: T, + pub ec_rwtable_row_curr_fingerprint: T, + pub ec_rwtable_prev_fingerprint: T, + pub ec_rwtable_curr_fingerprint: T, +} + +impl SuperCircuitInstance { + /// Construct `SnarkInstance` with vector + pub fn new(instances: impl IntoIterator) -> Self { + let mut iter_instances = instances.into_iter(); + Self { + chunk_index: iter_instances.next().unwrap(), + total_chunk: iter_instances.next().unwrap(), + initial_rwc: iter_instances.next().unwrap(), + chunk_index_next: iter_instances.next().unwrap(), + end_rwc: iter_instances.next().unwrap(), + pi_digest_lo: iter_instances.next().unwrap(), + pi_digest_hi: iter_instances.next().unwrap(), + sc_permu_alpha: iter_instances.next().unwrap(), + sc_permu_gamma: iter_instances.next().unwrap(), + sc_rwtable_row_prev_fingerprint: iter_instances.next().unwrap(), + sc_rwtable_row_curr_fingerprint: iter_instances.next().unwrap(), + sc_rwtable_prev_fingerprint: iter_instances.next().unwrap(), + sc_rwtable_curr_fingerprint: iter_instances.next().unwrap(), + ec_permu_alpha: iter_instances.next().unwrap(), + ec_permu_gamma: iter_instances.next().unwrap(), + ec_rwtable_row_prev_fingerprint: iter_instances.next().unwrap(), + ec_rwtable_row_curr_fingerprint: iter_instances.next().unwrap(), + ec_rwtable_prev_fingerprint: iter_instances.next().unwrap(), + ec_rwtable_curr_fingerprint: iter_instances.next().unwrap(), + } + } +} + +/// UserChallange +#[derive(Clone)] +pub struct UserChallenge { + /// column_indexes + pub column_indexes: Vec>, + /// num_challenges + pub num_challenges: usize, +} + /// RootCircuit for aggregating SuperCircuit into a much smaller proof. #[derive(Clone)] pub struct RootCircuit<'a, M: MultiMillerLoop, As> @@ -49,8 +120,10 @@ where M::G1Affine: CurveAffine, { svk: KzgSvk, - snark: SnarkWitness<'a, M::G1Affine>, + protocol: &'a PlonkProtocol, + snark_witnesses: Vec>, instance: Vec, + user_challenges: Option<&'a UserChallenge>, _marker: PhantomData, } @@ -76,43 +149,63 @@ where /// proof and its instance. Returns `None` if given proof is invalid. pub fn new( params: &ParamsKZG, - super_circuit_protocol: &'a PlonkProtocol, - super_circuit_instances: Value<&'a Vec>>, - super_circuit_proof: Value<&'a [u8]>, + protocol: &'a PlonkProtocol, + snark_witnesses: Vec>, + user_challenges: Option<&'a UserChallenge>, ) -> Result { - let num_instances = super_circuit_protocol.num_instance.iter().sum::() + 4 * LIMBS; - let instance = { - let mut instance = Ok(vec![M::Fr::ZERO; num_instances]); - super_circuit_instances - .as_ref() - .zip(super_circuit_proof.as_ref()) - .map(|(super_circuit_instances, super_circuit_proof)| { - let snark = Snark::new( - super_circuit_protocol, - super_circuit_instances, - super_circuit_proof, - ); - instance = aggregate::(params, [snark]).map(|accumulator_limbs| { - iter::empty() - // Propagate `SuperCircuit`'s instance - .chain(super_circuit_instances.iter().flatten().cloned()) - // Output aggregated accumulator limbs - .chain(accumulator_limbs) - .collect_vec() - }); + let num_raw_instances = protocol.num_instance.iter().sum::(); + + // compute real instance value + let (flatten_first_chunk_instances, accumulator_limbs) = { + let (mut instance, mut accumulator_limbs) = ( + vec![M::Fr::ZERO; num_raw_instances], + Ok(vec![M::Fr::ZERO; 4 * LIMBS]), + ); + // compute aggregate_limbs + snark_witnesses + .iter() + .fold(Value::known(vec![]), |acc_snark, snark_witness| { + snark_witness + .instances + .as_ref() + .zip(snark_witness.proof.as_ref()) + .map(|(super_circuit_instances, super_circuit_proof)| { + Snark::new(protocol, super_circuit_instances, super_circuit_proof) + }) + .zip(acc_snark) + .map(|(snark, mut acc_snark)| { + acc_snark.push(snark); + acc_snark + }) + }) + .map(|snarks| { + if !snarks.is_empty() { + accumulator_limbs = aggregate::(params, snarks) + .map(|accumulator_limbs| accumulator_limbs.to_vec()); + } }); - instance? + + // retrieve first instance + if let Some(snark_witness) = snark_witnesses.first() { + snark_witness + .instances + .map(|instances| instance = instances.iter().flatten().cloned().collect_vec()); + } + + (instance, accumulator_limbs?) }; - debug_assert_eq!(instance.len(), num_instances); + + debug_assert_eq!(flatten_first_chunk_instances.len(), num_raw_instances); + let mut flatten_instance = + exposed_instances(&SuperCircuitInstance::new(flatten_first_chunk_instances)); + flatten_instance.extend(accumulator_limbs); Ok(Self { svk: KzgSvk::::new(params.get_g()[0]), - snark: SnarkWitness::new( - super_circuit_protocol, - super_circuit_instances, - super_circuit_proof, - ), - instance, + protocol, + snark_witnesses, + instance: flatten_instance, + user_challenges, _marker: PhantomData, }) } @@ -120,13 +213,15 @@ where /// Returns accumulator indices in instance columns, which will be in /// the last `4 * LIMBS` rows of instance column in `MainGate`. pub fn accumulator_indices(&self) -> Vec<(usize, usize)> { - let offset = self.snark.protocol().num_instance.iter().sum::(); - (offset..).map(|idx| (0, idx)).take(4 * LIMBS).collect() + (NUM_OF_SUPERCIRCUIT_INSTANCES..) + .map(|idx| (0, idx)) + .take(4 * LIMBS) + .collect() } /// Returns number of instance pub fn num_instance(&self) -> Vec { - vec![self.snark.protocol().num_instance.iter().sum::() + 4 * LIMBS] + vec![self.instance.len()] } /// Returns instance @@ -159,7 +254,13 @@ where fn without_witnesses(&self) -> Self { Self { svk: self.svk, - snark: self.snark.without_witnesses(), + protocol: self.protocol, + snark_witnesses: self + .snark_witnesses + .iter() + .map(|snark_witness| snark_witness.without_witnesses()) + .collect_vec(), + user_challenges: self.user_challenges, instance: vec![M::Fr::ZERO; self.instance.len()], _marker: PhantomData, } @@ -175,21 +276,197 @@ where mut layouter: impl Layouter, ) -> Result<(), Error> { config.load_table(&mut layouter)?; - let (instance, accumulator_limbs) = - config.aggregate::(&mut layouter, &self.svk, [self.snark])?; + let key = &self.svk; + let (instances, accumulator_limbs) = layouter.assign_region( + || "Aggregate snarks", + |mut region| { + config.named_column_in_region(&mut region); + let ctx = RegionCtx::new(region, 0); + let (loaded_instances, accumulator_limbs, loader, proofs) = + config.aggregate::(ctx, &key.clone(), &self.snark_witnesses)?; + + // aggregate user challenge for rwtable permutation challenge + let (alpha, gamma) = { + let (mut challenges, _) = config.aggregate_user_challenges::( + loader.clone(), + self.user_challenges, + proofs, + )?; + (challenges.remove(0), challenges.remove(0)) + }; + + // convert vector instances SuperCircuitInstance struct + let supercircuit_instances = loaded_instances + .iter() + .map(SuperCircuitInstance::new) + .collect::>>(); + + // constraint first and last chunk + supercircuit_instances + .first() + .zip(supercircuit_instances.last()) + .map(|(first_chunk, _last)| { + // `last.sc_rwtable_next_fingerprint == + // last.ec_rwtable_next_fingerprint` will be checked inside super circuit so + // here no need to checked + // Other field in last instances already be checked in chunk + // continuity + + // define 0, 1, total_chunk as constant + let (zero_const, one_const, total_chunk_const) = { + let zero_const = loader + .scalar_chip() + .assign_constant(&mut loader.ctx_mut(), M::Fr::from(0)) + .unwrap(); + let one_const = loader + .scalar_chip() + .assign_constant(&mut loader.ctx_mut(), M::Fr::from(1)) + .unwrap(); + let total_chunk_const = loader + .scalar_chip() + .assign_constant(&mut loader.ctx_mut(), M::Fr::from(1)) + .unwrap(); + + (zero_const, one_const, total_chunk_const) + }; + + // `first.sc_rwtable_row_prev_fingerprint == + // first.ec_rwtable_row_prev_fingerprint` will be checked inside circuit + vec![ + // chunk ctx + (first_chunk.chunk_index.assigned(), &zero_const), + (first_chunk.total_chunk.assigned(), &total_chunk_const), + // rwc + (first_chunk.initial_rwc.assigned(), &one_const), + // constraint permutation fingerprint + // challenge: alpha + (first_chunk.sc_permu_alpha.assigned(), &alpha.assigned()), + (first_chunk.ec_permu_alpha.assigned(), &alpha.assigned()), + // challenge: gamma + (first_chunk.sc_permu_gamma.assigned(), &gamma.assigned()), + (first_chunk.ec_permu_gamma.assigned(), &gamma.assigned()), + // fingerprint + ( + first_chunk.ec_rwtable_prev_fingerprint.assigned(), + &one_const, + ), + ( + first_chunk.sc_rwtable_prev_fingerprint.assigned(), + &one_const, + ), + ] + .iter() + .for_each(|(a, b)| { + loader + .scalar_chip() + .assert_equal(&mut loader.ctx_mut(), a, b) + .unwrap(); + }); + + first_chunk + }) + .expect("error"); + + // constraint consistency between chunk + supercircuit_instances.iter().tuple_windows().for_each( + |(instance_i, instance_i_plus_one)| { + vec![ + ( + instance_i.chunk_index_next.assigned(), + instance_i_plus_one.chunk_index.assigned(), + ), + ( + instance_i.total_chunk.assigned(), + instance_i_plus_one.total_chunk.assigned(), + ), + ( + instance_i.end_rwc.assigned(), + instance_i_plus_one.initial_rwc.assigned(), + ), + ( + instance_i.pi_digest_lo.assigned(), + instance_i_plus_one.pi_digest_lo.assigned(), + ), + ( + instance_i.pi_digest_hi.assigned(), + instance_i_plus_one.pi_digest_hi.assigned(), + ), + // state circuit + ( + instance_i.sc_permu_alpha.assigned(), + instance_i_plus_one.sc_permu_alpha.assigned(), + ), + ( + instance_i.sc_permu_gamma.assigned(), + instance_i_plus_one.sc_permu_gamma.assigned(), + ), + ( + instance_i.sc_rwtable_row_curr_fingerprint.assigned(), + instance_i_plus_one + .sc_rwtable_row_prev_fingerprint + .assigned(), + ), + ( + instance_i.sc_rwtable_curr_fingerprint.assigned(), + instance_i_plus_one.sc_rwtable_prev_fingerprint.assigned(), + ), + // evm circuit + ( + instance_i.ec_permu_alpha.assigned(), + instance_i_plus_one.ec_permu_alpha.assigned(), + ), + ( + instance_i.ec_permu_gamma.assigned(), + instance_i_plus_one.ec_permu_gamma.assigned(), + ), + ( + instance_i.ec_rwtable_curr_fingerprint.assigned(), + instance_i_plus_one.ec_rwtable_prev_fingerprint.assigned(), + ), + ( + instance_i.ec_rwtable_row_curr_fingerprint.assigned(), + instance_i_plus_one + .ec_rwtable_row_prev_fingerprint + .assigned(), + ), + ] + .iter() + .for_each(|(a, b)| { + loader + .scalar_chip() + .assert_equal(&mut loader.ctx_mut(), a, b) + .unwrap(); + }); + }, + ); + + Ok(( + exposed_instances(supercircuit_instances.first().unwrap()) + .iter() + .map(|instance| instance.assigned().to_owned()) + .collect_vec(), + accumulator_limbs, + )) + }, + )?; // Constrain equality to instance values let main_gate = config.main_gate(); - for (row, limb) in instance - .into_iter() - .flatten() - .flatten() - .chain(accumulator_limbs) - .enumerate() - { + for (row, limb) in instances.into_iter().chain(accumulator_limbs).enumerate() { main_gate.expose_public(layouter.namespace(|| ""), limb, row)?; } Ok(()) } } + +/// get instances to expose +fn exposed_instances(supercircuit_instances: &SuperCircuitInstance) -> Vec { + let instances = vec![ + // pi circuit + supercircuit_instances.pi_digest_lo, + supercircuit_instances.pi_digest_hi, + ]; + assert_eq!(NUM_OF_SUPERCIRCUIT_INSTANCES, instances.len()); + instances +} diff --git a/zkevm-circuits/src/root_circuit/aggregation.rs b/zkevm-circuits/src/root_circuit/aggregation.rs index f53ae78b26..90d161eda5 100644 --- a/zkevm-circuits/src/root_circuit/aggregation.rs +++ b/zkevm-circuits/src/root_circuit/aggregation.rs @@ -1,5 +1,5 @@ use eth_types::Field; -use halo2::halo2curves::CurveExt; +use halo2::{circuit::Region, halo2curves::CurveExt}; use halo2_proofs::{ circuit::{AssignedCell, Layouter, Value}, halo2curves::{ @@ -15,7 +15,7 @@ use snark_verifier::{ self, halo2::{ halo2_wrong_ecc::{self, integer::rns::Rns, maingate::*, EccConfig}, - Scalar, + EcPoint, Scalar, }, native::NativeLoader, }, @@ -24,11 +24,20 @@ use snark_verifier::{ PolynomialCommitmentScheme, }, system::halo2::transcript, - util::arithmetic::{fe_to_limbs, FromUniformBytes, MultiMillerLoop, PrimeField}, - verifier::{self, plonk::PlonkProtocol, SnarkVerifier}, + util::{ + arithmetic::{fe_to_limbs, FromUniformBytes, MultiMillerLoop, PrimeField}, + transcript::Transcript, + }, + verifier::{ + self, + plonk::{PlonkProof, PlonkProtocol}, + SnarkVerifier, + }, }; use std::{io, iter, rc::Rc}; +use super::UserChallenge; + /// Number of limbs to decompose a elliptic curve base field element into. pub const LIMBS: usize = 4; /// Number of bits of each decomposed limb. @@ -57,6 +66,8 @@ const R_P: usize = 60; pub type EccChip = halo2_wrong_ecc::BaseFieldEccChip; /// `Halo2Loader` with hardcoded `EccChip`. pub type Halo2Loader<'a, C> = loader::halo2::Halo2Loader<'a, C, EccChip>; +/// `LoadedScalar` with hardcoded `EccChip`. +pub type LoadedScalar<'a, C> = Scalar<'a, C, EccChip>; /// `PoseidonTranscript` with hardcoded parameter with 128-bits security. pub type PoseidonTranscript = transcript::halo2::PoseidonTranscript; @@ -97,9 +108,12 @@ impl<'a, C: CurveAffine> From> for SnarkWitness<'a, C> { /// SnarkWitness #[derive(Clone, Copy)] pub struct SnarkWitness<'a, C: CurveAffine> { - protocol: &'a PlonkProtocol, - instances: Value<&'a Vec>>, - proof: Value<&'a [u8]>, + /// protocol + pub protocol: &'a PlonkProtocol, + /// instance + pub instances: Value<&'a Vec>>, + /// proof + pub proof: Value<&'a [u8]>, } impl<'a, C: CurveAffine> SnarkWitness<'a, C> { @@ -203,19 +217,94 @@ impl AggregationConfig { self.range_chip().load_table(layouter) } + /// named columns in region + pub fn named_column_in_region(&self, region: &mut Region<'_, F>) { + // annotate advices columns of `main_gate`. We can't annotate fixed_columns of + // `main_gate` bcs there is no methods exported. + for (i, col) in self.main_gate_config.advices().iter().enumerate() { + region.name_column(|| format!("ROOT_main_gate_{}", i), *col); + } + } + + /// Aggregate witnesses into challenge and return + #[allow(clippy::type_complexity)] + pub fn aggregate_user_challenges<'c, M, As>( + &self, + loader: Rc>, + user_challenges: Option<&UserChallenge>, + proofs: Vec>, As>>, + ) -> Result< + ( + Vec>, + Vec>>, + ), + Error, + > + where + M: MultiMillerLoop, + M::Fr: Field, + M::G1Affine: CurveAffine, + for<'b> As: PolynomialCommitmentScheme< + M::G1Affine, + Rc>, + VerifyingKey = KzgSvk, + Output = KzgAccumulator>>, + > + AccumulationScheme< + M::G1Affine, + Rc>, + Accumulator = KzgAccumulator>>, + VerifyingKey = KzgAsVerifyingKey, + >, + { + type PoseidonTranscript<'a, C, S> = + transcript::halo2::PoseidonTranscript>, S, T, RATE, R_F, R_P>; + + let witnesses = proofs + .iter() + .flat_map(|proof| { + user_challenges + .map(|user_challenges| { + user_challenges + .column_indexes + .iter() + .map(|col| &proof.witnesses[col.index()]) + .collect::>() + }) + .unwrap_or_default() + }) + .collect_vec(); + + let empty_proof = Vec::new(); + let mut transcript = PoseidonTranscript::new(&loader, Value::known(empty_proof.as_slice())); + witnesses.iter().for_each(|rwtable_col_ec_point| { + transcript.common_ec_point(rwtable_col_ec_point).unwrap(); + }); + let num_challenges = user_challenges + .map(|user_challenges| user_challenges.num_challenges) + .unwrap_or_default(); + + let witnesses = witnesses + .into_iter() + .cloned() + .collect::>>>(); + Ok((transcript.squeeze_n_challenges(num_challenges), witnesses)) + } + /// Aggregate snarks into a single accumulator and decompose it into /// `4 * LIMBS` limbs. /// Returns assigned instances of snarks and aggregated accumulator limbs. #[allow(clippy::type_complexity)] - pub fn aggregate<'a, M, As>( + pub fn aggregate<'a, 'c, M, As>( &self, - layouter: &mut impl Layouter, + ctx: RegionCtx<'c, M::Fr>, svk: &KzgSvk, - snarks: impl IntoIterator>, + snarks: &[SnarkWitness<'a, M::G1Affine>], ) -> Result< ( - Vec>>>, + Vec>>>, Vec>, + Rc>, + Vec>, As>>, ), Error, > @@ -235,92 +324,84 @@ impl AggregationConfig { VerifyingKey = KzgAsVerifyingKey, >, { + let ecc_chip = self.ecc_chip::(); + let loader = Halo2Loader::<'c, _>::new(ecc_chip, ctx); + type PoseidonTranscript<'a, C, S> = transcript::halo2::PoseidonTranscript>, S, T, RATE, R_F, R_P>; - let snarks = snarks.into_iter().collect_vec(); - layouter.assign_region( - || "Aggregate snarks", - |mut region| { - // annotate advices columns of `main_gate`. We can't annotate fixed_columns of - // `main_gate` bcs there is no methods exported. - for (i, col) in self.main_gate_config.advices().iter().enumerate() { - region.name_column(|| format!("ROOT_main_gate_{}", i), *col); - } - - let ctx = RegionCtx::new(region, 0); - let ecc_chip = self.ecc_chip::(); - let loader = Halo2Loader::new(ecc_chip, ctx); - - // Verify the cheap part and get accumulator (left-hand and right-hand side of - // pairing) of individual proof. - let (instances, accumulators) = snarks - .iter() - .map(|snark| { - let protocol = snark.protocol.loaded(&loader); - let instances = snark.loaded_instances(&loader); - let mut transcript = PoseidonTranscript::new(&loader, snark.proof()); - let proof = PlonkSuccinctVerifier::::read_proof( - svk, - &protocol, - &instances, - &mut transcript, - ) - .unwrap(); - let accumulators = - PlonkSuccinctVerifier::verify(svk, &protocol, &instances, &proof) - .unwrap(); - (instances, accumulators) - }) - .collect_vec() - .into_iter() - .unzip::<_, _, Vec<_>, Vec<_>>(); - - // Verify proof for accumulation of all accumulators into new one. - let accumulator = { - let as_vk = Default::default(); - let as_proof = Vec::new(); - let accumulators = accumulators.into_iter().flatten().collect_vec(); - let mut transcript = - PoseidonTranscript::new(&loader, Value::known(as_proof.as_slice())); - let proof = >::read_proof( - &as_vk, - &accumulators, - &mut transcript, - ) - .unwrap(); - >::verify(&as_vk, &accumulators, &proof).unwrap() - }; + let mut plonk_svp = vec![]; + // Verify the cheap part and get accumulator (left-hand and right-hand side of + // pairing) of individual proof. + // Verify the cheap part and get accumulator (left-hand and right-hand side of + // pairing) of individual proof. + let (instances, accumulators) = snarks + .iter() + .map(|snark| { + let protocol = snark.protocol.loaded(&loader); + + let instances = snark.loaded_instances(&loader); + let mut transcript = PoseidonTranscript::new(&loader, snark.proof()); + let proof = PlonkSuccinctVerifier::::read_proof( + svk, + &protocol, + &instances, + &mut transcript, + ) + .unwrap(); + let accumulators = + PlonkSuccinctVerifier::verify(svk, &protocol, &instances, &proof).unwrap(); + plonk_svp.push(proof); + (instances, accumulators) + }) + .collect_vec() + .into_iter() + .unzip::<_, _, Vec<_>, Vec<_>>(); + + // Verify proof for accumulation of all accumulators into new one. + let accumulator = { + let as_vk = Default::default(); + let as_proof = Vec::new(); + let accumulators = accumulators.into_iter().flatten().collect_vec(); + let mut transcript = + PoseidonTranscript::new(&loader, Value::known(as_proof.as_slice())); + let proof = >::read_proof( + &as_vk, + &accumulators, + &mut transcript, + ) + .unwrap(); + >::verify(&as_vk, &accumulators, &proof).unwrap() + }; - let instances = instances + let instances = instances + .iter() + .map(|instances| { + instances .iter() - .map(|instances| { + .flat_map(|instances| { instances .iter() - .map(|instances| { - instances - .iter() - .map(|instance| instance.assigned().to_owned()) - .collect_vec() - }) + .map(|instance| instance.to_owned()) .collect_vec() }) - .collect_vec(); - let accumulator_limbs = [accumulator.lhs, accumulator.rhs] - .iter() - .map(|ec_point| { - loader - .ecc_chip() - .assign_ec_point_to_limbs(&mut loader.ctx_mut(), ec_point.assigned()) - }) - .collect::, Error>>()? - .into_iter() - .flatten() - .collect(); + .collect_vec() + }) + .collect_vec(); + + let accumulator_limbs = [accumulator.lhs, accumulator.rhs] + .iter() + .map(|ec_point| { + loader + .ecc_chip() + .assign_ec_point_to_limbs(&mut loader.ctx_mut(), ec_point.assigned()) + }) + .collect::, Error>>()? + .into_iter() + .flatten() + .collect(); - Ok((instances, accumulator_limbs)) - }, - ) + Ok((instances, accumulator_limbs, loader, plonk_svp)) } } @@ -625,6 +706,7 @@ pub mod test { let aggregation = TestAggregationCircuit::>::new( ¶ms, snarks.iter().map(SnarkOwned::as_snark), + None, ) .unwrap(); let instances = aggregation.instances(); @@ -645,6 +727,7 @@ pub mod test { let aggregation = TestAggregationCircuit::>::new( ¶ms, snarks.iter().map(SnarkOwned::as_snark), + None, ) .unwrap(); let mut instances = aggregation.instances(); diff --git a/zkevm-circuits/src/root_circuit/dev.rs b/zkevm-circuits/src/root_circuit/dev.rs index a2e82a7a68..3124540523 100644 --- a/zkevm-circuits/src/root_circuit/dev.rs +++ b/zkevm-circuits/src/root_circuit/dev.rs @@ -1,13 +1,15 @@ -use super::{aggregate, AggregationConfig, Halo2Loader, KzgSvk, Snark, SnarkWitness, LIMBS}; +use super::{ + aggregate, AggregationConfig, Halo2Loader, KzgSvk, Snark, SnarkWitness, UserChallenge, LIMBS, +}; use eth_types::Field; use halo2_proofs::{ - circuit::{Layouter, SimpleFloorPlanner}, + circuit::{Layouter, SimpleFloorPlanner, Value}, halo2curves::{ff::Field as Halo2Field, serde::SerdeObject, CurveAffine, CurveExt}, - plonk::{Circuit, ConstraintSystem, Error}, + plonk::{Circuit, ConstraintSystem, Error, Error::InvalidInstances}, poly::{commitment::ParamsProver, kzg::commitment::ParamsKZG}, }; use itertools::Itertools; -use maingate::MainGateInstructions; +use maingate::{MainGateInstructions, RegionCtx}; use snark_verifier::{ loader::native::NativeLoader, pcs::{ @@ -20,12 +22,14 @@ use std::{iter, marker::PhantomData, rc::Rc}; /// Aggregation circuit for testing purpose. #[derive(Clone)] +#[allow(clippy::type_complexity)] pub struct TestAggregationCircuit<'a, M: MultiMillerLoop, As> where M::G1Affine: CurveAffine, { svk: KzgSvk, snarks: Vec>, + user_challenge: Option<(UserChallenge, Vec, Vec)>, instances: Vec, _marker: PhantomData, } @@ -50,9 +54,11 @@ where { /// Create an Aggregation circuit with aggregated accumulator computed. /// Returns `None` if any given snark is invalid. + #[allow(clippy::type_complexity)] pub fn new( params: &ParamsKZG, snarks: impl IntoIterator>, + user_challenge: Option<(UserChallenge, Vec, Vec)>, ) -> Result { let snarks = snarks.into_iter().collect_vec(); @@ -71,6 +77,7 @@ where Ok(Self { svk: KzgSvk::::new(params.get_g()[0]), + user_challenge, snarks: snarks.into_iter().map_into().collect(), instances, _marker: PhantomData, @@ -120,6 +127,7 @@ where fn without_witnesses(&self) -> Self { Self { svk: self.svk, + user_challenge: self.user_challenge.clone(), snarks: self .snarks .iter() @@ -140,18 +148,84 @@ where mut layouter: impl Layouter, ) -> Result<(), Error> { config.load_table(&mut layouter)?; - let (instances, accumulator_limbs) = - config.aggregate::(&mut layouter, &self.svk, self.snarks.clone())?; + + let (instances, accumulator_limbs) = layouter.assign_region( + || "Aggregate snarks", + |mut region| { + config.named_column_in_region(&mut region); + let ctx = RegionCtx::new(region, 0); + let (instances, accumulator_limbs, loader, proofs) = + config.aggregate::(ctx, &self.svk, &self.snarks)?; + + // aggregate user challenge for rwtable permutation challenge + let user_challenge = self + .user_challenge + .as_ref() + .map(|(challenge, _, _)| challenge); + let (challenges, commitments) = config.aggregate_user_challenges::( + loader.clone(), + user_challenge, + proofs, + )?; + if !challenges.is_empty() { + let Some((_, expected_commitments, expected_challenges)) = + self.user_challenge.as_ref() + else { + return Err(InvalidInstances); + }; + // check commitment equality + let expected_commitments_loaded = expected_commitments + .iter() + .map(|expected_commitment| { + loader.ecc_chip().assign_point( + &mut loader.ctx_mut(), + Value::known(*expected_commitment), + ) + }) + .collect::, Error>>()?; + expected_commitments_loaded + .iter() + .zip(commitments.iter()) + .try_for_each(|(expected_commitment, commitment)| { + loader.ecc_chip().assert_equal( + &mut loader.ctx_mut(), + expected_commitment, + &commitment.assigned(), + ) + })?; + + // check challenge equality + let expected_challenges_loaded = expected_challenges + .iter() + .map(|value| loader.assign_scalar(Value::known(*value))) + .collect::>(); + expected_challenges_loaded + .iter() + .zip(challenges.iter()) + .try_for_each(|(expected_challenge, challenge)| { + loader.scalar_chip().assert_equal( + &mut loader.ctx_mut(), + &expected_challenge.assigned(), + &challenge.assigned(), + ) + })?; + } + + let instances = instances + .iter() + .flat_map(|instances| { + instances + .iter() + .map(|instance| instance.assigned().to_owned()) + }) + .collect_vec(); + Ok((instances, accumulator_limbs)) + }, + )?; // Constrain equality to instance values let main_gate = config.main_gate(); - for (row, limb) in instances - .into_iter() - .flatten() - .flatten() - .chain(accumulator_limbs) - .enumerate() - { + for (row, limb) in instances.into_iter().chain(accumulator_limbs).enumerate() { main_gate.expose_public(layouter.namespace(|| ""), limb, row)?; } diff --git a/zkevm-circuits/src/root_circuit/test.rs b/zkevm-circuits/src/root_circuit/test.rs index de87bd58e1..b938c0a9a3 100644 --- a/zkevm-circuits/src/root_circuit/test.rs +++ b/zkevm-circuits/src/root_circuit/test.rs @@ -1,74 +1,362 @@ +use std::iter; + use crate::{ - root_circuit::{compile, Config, Gwc, PoseidonTranscript, RootCircuit}, + root_circuit::{ + aggregation::test::SnarkOwned, compile, Config, Gwc, PoseidonTranscript, RootCircuit, + SnarkWitness, TestAggregationCircuit, UserChallenge, + }, super_circuit::{test::block_1tx, SuperCircuit}, + table::{ + self, rw_table::get_rwtable_cols_commitment, AccountFieldTag, LookupTable, TxLogFieldTag, + }, + witness::Rw, }; use bus_mapping::circuit_input_builder::FixedCParams; +use eth_types::{address, Address, Field, U256}; +use gadgets::util::Expr; use halo2_proofs::{ - circuit::Value, + circuit::{Layouter, SimpleFloorPlanner, Value}, dev::MockProver, - halo2curves::bn256::Bn256, - plonk::{create_proof, keygen_pk, keygen_vk}, - poly::kzg::{ - commitment::{KZGCommitmentScheme, ParamsKZG}, - multiopen::ProverGWC, + halo2curves::{bn256::Bn256, pairing::Engine}, + plonk::{create_proof, keygen_pk, keygen_vk, Circuit, ConstraintSystem, Error, Selector}, + poly::{ + kzg::{ + commitment::{KZGCommitmentScheme, ParamsKZG}, + multiopen::ProverGWC, + }, + Rotation, }, }; use itertools::Itertools; use rand::rngs::OsRng; +use snark_verifier::util::transcript::Transcript; +use table::RwTable; + +struct RwTableCircuit<'a> { + rws: &'a [Rw], + n_rows: usize, + prev_chunk_last_rw: Option, +} + +impl<'a> RwTableCircuit<'a> { + #[allow(dead_code)] + pub(crate) fn new(rws: &'a [Rw], n_rows: usize, prev_chunk_last_rw: Option) -> Self { + Self { + rws, + n_rows, + prev_chunk_last_rw, + } + } +} + +#[derive(Clone)] +pub(crate) struct RwTableCircuitConfig { + pub rw_table: RwTable, + pub enable: Selector, +} + +impl RwTableCircuitConfig {} + +impl<'a, F: Field> Circuit for RwTableCircuit<'a> { + type Config = RwTableCircuitConfig; + + type FloorPlanner = SimpleFloorPlanner; + + type Params = (); + + fn without_witnesses(&self) -> Self { + todo!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + let rw_table = RwTable::construct(meta); + let enable = meta.selector(); + + meta.create_gate("dummy id 1", |meta| { + let dummy = meta.query_advice(rw_table.id, Rotation::cur()); + let enable = meta.query_selector(enable); + + vec![ + enable * dummy.clone() * dummy.clone() * dummy.clone() * (dummy.clone() - 1.expr()), + ] + }); + RwTableCircuitConfig { rw_table, enable } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + layouter.assign_region( + || "XXXX", + |mut region| { + let _ = config.rw_table.load_with_region( + &mut region, + self.rws, + self.n_rows, + self.prev_chunk_last_rw, + ); + config.enable.enable(&mut region, 0)?; + Ok(()) + }, + )?; + Ok(()) + } +} + +#[test] +fn test_user_challenge_aggregation() { + let num_challenges = 1; + let k = 12; + let rows = vec![ + Rw::Stack { + rw_counter: 9, + is_write: true, + call_id: 3, + stack_pointer: 100, + value: U256::MAX - 1, + }, + Rw::Stack { + rw_counter: 13, + is_write: true, + call_id: 3, + stack_pointer: 102, + value: U256::MAX - 1, + }, + Rw::Stack { + rw_counter: 1, + is_write: true, + call_id: 1, + stack_pointer: 1023, + value: U256::MAX - 1, + }, + Rw::TxLog { + rw_counter: 2, + is_write: true, + tx_id: 1, + log_id: 1, + field_tag: TxLogFieldTag::Address, + index: 0usize, + value: U256::MAX - 1, + }, + Rw::TxLog { + rw_counter: 3, + is_write: true, + tx_id: 1, + log_id: 1, + field_tag: TxLogFieldTag::Topic, + index: 0usize, + value: U256::MAX - 1, + }, + Rw::TxLog { + rw_counter: 4, + is_write: true, + tx_id: 1, + log_id: 1, + field_tag: TxLogFieldTag::Topic, + index: 1usize, + value: U256::MAX - 1, + }, + Rw::TxLog { + rw_counter: 5, + is_write: true, + tx_id: 1, + log_id: 1, + field_tag: TxLogFieldTag::Data, + index: 10usize, + value: U256::MAX - 1, + }, + Rw::TxLog { + rw_counter: 6, + is_write: true, + tx_id: 1, + log_id: 1, + field_tag: TxLogFieldTag::Data, + index: 1usize, + value: U256::MAX - 1, + }, + Rw::Account { + rw_counter: 1, + is_write: false, + account_address: address!("0x000000000000000000000000000000000cafe002"), + field_tag: AccountFieldTag::CodeHash, + value: U256::MAX - 1, + value_prev: U256::MAX - 1, + }, + Rw::AccountStorage { + rw_counter: 1, + is_write: false, + account_address: Address::default(), + storage_key: U256::MAX - 1, + value: U256::MAX - 1, + value_prev: U256::MAX - 1, + tx_id: 4, + committed_value: U256::MAX - 1, + }, + ]; + + let mut cs = ConstraintSystem::<::Fr>::default(); + let config = RwTableCircuit::configure(&mut cs); + let rwtable_columns = + ::Fr>>::columns(&config.rw_table); + + let params = ParamsKZG::::setup(k, OsRng); + let advice_commitments = get_rwtable_cols_commitment::>( + k.try_into().unwrap(), + &rows, + rows.len() + 1, + ¶ms, + ); + let mut transcript = PoseidonTranscript::new(Vec::::new()); + advice_commitments.iter().for_each(|commit| { + transcript.common_ec_point(commit).unwrap(); + }); + let expected_challenges = transcript.squeeze_n_challenges(num_challenges); + + let circuits = iter::repeat_with(|| RwTableCircuit::new(&rows, rows.len() + 1, None)) + .take(1) + .collect_vec(); + + let pk = keygen_pk( + ¶ms, + keygen_vk(¶ms, &circuits[0]).unwrap(), + &circuits[0], + ) + .unwrap(); + let protocol = compile( + ¶ms, + pk.get_vk(), + Config::kzg().with_num_instance(vec![0]), + ); + // Create proof + let proofs: Vec> = circuits + .into_iter() + .map(|circuit| { + let mut transcript = PoseidonTranscript::new(Vec::new()); + // Create proof + create_proof::, ProverGWC<_>, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&[]], + OsRng, + &mut transcript, + ) + .unwrap(); + transcript.finalize() + }) + .collect(); + let user_challenge = UserChallenge { + column_indexes: rwtable_columns, + num_challenges, + }; + let snark_witnesses: Vec<_> = proofs + .into_iter() + .map(|proof| SnarkOwned::new(protocol.clone(), vec![vec![]], proof)) + .collect(); + let aggregation = TestAggregationCircuit::>::new( + ¶ms, + snark_witnesses.iter().map(SnarkOwned::as_snark), + Some((user_challenge, advice_commitments, expected_challenges)), + // None, + ) + .unwrap(); + + let instances = aggregation.instances(); + assert_eq!( + MockProver::run(21, &aggregation, instances) + .unwrap() + .verify(), + Ok(()) + ); +} #[ignore = "Due to high memory requirement"] #[test] -fn test_root_circuit() { - let (params, protocol, proof, instance) = { +fn test_root_circuit_multiple_chunk() { + let (params, protocol, proofs, instances, rwtable_columns) = { // Preprocess const TEST_MOCK_RANDOMNESS: u64 = 0x100; let circuits_params = FixedCParams { + total_chunks: 3, max_txs: 1, max_withdrawals: 5, max_calldata: 32, - max_rws: 256, + max_rws: 100, max_copy_rows: 256, max_exp_steps: 256, max_bytecode: 512, - max_evm_rows: 0, + max_evm_rows: 1 << 12, max_keccak_rows: 0, max_vertical_circuit_rows: 0, }; - let (k, circuit, instance, _) = + let (k, circuits, instances, _) = SuperCircuit::<_>::build(block_1tx(), circuits_params, TEST_MOCK_RANDOMNESS.into()) .unwrap(); + assert!(!circuits.is_empty()); + assert!(circuits.len() == instances.len()); + + // get chronological_rwtable and byaddr_rwtable columns index + let mut cs = ConstraintSystem::default(); + let config = SuperCircuit::configure_with_params(&mut cs, circuits[0].params()); + let rwtable_columns = config.get_rwtable_columns(); + let params = ParamsKZG::::setup(k, OsRng); - let pk = keygen_pk(¶ms, keygen_vk(¶ms, &circuit).unwrap(), &circuit).unwrap(); + let pk = keygen_pk( + ¶ms, + keygen_vk(¶ms, &circuits[0]).unwrap(), + &circuits[0], + ) + .unwrap(); let protocol = compile( ¶ms, pk.get_vk(), Config::kzg() - .with_num_instance(instance.iter().map(|instance| instance.len()).collect()), + .with_num_instance(instances[0].iter().map(|instance| instance.len()).collect()), ); - // Create proof - let proof = { - let mut transcript = PoseidonTranscript::new(Vec::new()); - create_proof::, ProverGWC<_>, _, _, _, _>( - ¶ms, - &pk, - &[circuit], - &[&instance.iter().map(Vec::as_slice).collect_vec()], - OsRng, - &mut transcript, - ) - .unwrap(); - transcript.finalize() - }; + let proofs: Vec> = circuits + .into_iter() + .zip(instances.iter()) + .map(|(circuit, instance)| { + // Create proof + let proof = { + let mut transcript = PoseidonTranscript::new(Vec::new()); + create_proof::, ProverGWC<_>, _, _, _, _>( + ¶ms, + &pk, + &[circuit], + &[&instance.iter().map(Vec::as_slice).collect_vec()], + OsRng, + &mut transcript, + ) + .unwrap(); + transcript.finalize() + }; + proof + }) + .collect(); + (params, protocol, proofs, instances, rwtable_columns) + }; - (params, protocol, proof, instance) + let user_challenge = UserChallenge { + column_indexes: rwtable_columns, + num_challenges: 2, // alpha, gamma }; + let snark_witnesses: Vec<_> = proofs + .iter() + .zip(instances.iter()) + .map(|(proof, instance)| { + SnarkWitness::new(&protocol, Value::known(instance), Value::known(proof)) + }) + .collect(); let root_circuit = RootCircuit::>::new( ¶ms, &protocol, - Value::known(&instance), - Value::known(&proof), + snark_witnesses, + Some(&user_challenge), ) .unwrap(); assert_eq!( diff --git a/zkevm-circuits/src/sig_circuit.rs b/zkevm-circuits/src/sig_circuit.rs index 7cb3883815..403d784fde 100644 --- a/zkevm-circuits/src/sig_circuit.rs +++ b/zkevm-circuits/src/sig_circuit.rs @@ -199,8 +199,8 @@ pub struct SigCircuit { impl SubCircuit for SigCircuit { type Config = SigCircuitConfig; - fn new_from_block(block: &crate::witness::Block) -> Self { - assert!(block.circuits_params.max_txs <= MAX_NUM_SIG); + fn new_from_block(block: &crate::witness::Block, chunk: &crate::witness::Chunk) -> Self { + assert!(chunk.fixed_param.max_txs <= MAX_NUM_SIG); SigCircuit { max_verif: MAX_NUM_SIG, @@ -235,11 +235,14 @@ impl SubCircuit for SigCircuit // Since sig circuit / halo2-lib use veticle cell assignment, // so the returned pair is consisted of same values - fn min_num_rows_block(block: &crate::witness::Block) -> (usize, usize) { - let row_num = if block.circuits_params.max_vertical_circuit_rows == 0 { + fn min_num_rows_block( + block: &crate::witness::Block, + chunk: &crate::witness::Chunk, + ) -> (usize, usize) { + let row_num = if chunk.fixed_param.max_vertical_circuit_rows == 0 { Self::min_num_rows() } else { - block.circuits_params.max_vertical_circuit_rows + chunk.fixed_param.max_vertical_circuit_rows }; let ecdsa_verif_count = diff --git a/zkevm-circuits/src/state_circuit.rs b/zkevm-circuits/src/state_circuit.rs index 141eab0e51..74b8f1f4cb 100644 --- a/zkevm-circuits/src/state_circuit.rs +++ b/zkevm-circuits/src/state_circuit.rs @@ -20,19 +20,24 @@ use self::{ use crate::{ table::{AccountFieldTag, LookupTable, MPTProofType, MptTable, RwTable, UXTable}, util::{word::WordLoHi, Challenges, Expr, SubCircuit, SubCircuitConfig}, - witness::{self, MptUpdates, Rw, RwMap}, + witness::{ + self, + rw::{RwFingerprints, ToVec}, + Chunk, MptUpdates, Rw, RwMap, + }, }; use constraint_builder::{ConstraintBuilder, Queries}; use eth_types::{Address, Field, Word}; use gadgets::{ batched_is_zero::{BatchedIsZeroChip, BatchedIsZeroConfig}, binary_number::{BinaryNumberBits, BinaryNumberChip, BinaryNumberConfig}, + permutation::{PermutationChip, PermutationChipConfig}, }; use halo2_proofs::{ circuit::{Layouter, Region, Value}, plonk::{ - Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, SecondPhase, - VirtualCells, + Advice, Column, ConstraintSystem, Error, Expression, FirstPhase, Fixed, Instance, + SecondPhase, VirtualCells, }, poly::Rotation, }; @@ -51,7 +56,8 @@ pub struct StateCircuitConfig { // Figure out why you get errors when this is Selector. selector: Column, // https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/407 - rw_table: RwTable, + /// rw table + pub rw_table: RwTable, sort_keys: SortKeysConfig, // Assigned value at the start of the block. For Rw::Account and // Rw::AccountStorage rows this is the committed value in the MPT, for @@ -69,6 +75,13 @@ pub struct StateCircuitConfig { lookups: LookupsConfig, // External tables mpt_table: MptTable, + + /// rw permutation config + pub rw_permutation_config: PermutationChipConfig, + + // pi for chunk context continuity + pi_chunk_continuity: Column, + _marker: PhantomData, } @@ -150,6 +163,11 @@ impl SubCircuitConfig for StateCircuitConfig { let lexicographic_ordering = LexicographicOrderingConfig::configure(meta, sort_keys, lookups, power_of_randomness); + let rw_permutation_config = PermutationChip::configure( + meta, + >::advice_columns(&rw_table), + ); + // annotate columns rw_table.annotate_columns(meta); mpt_table.annotate_columns(meta); @@ -157,6 +175,9 @@ impl SubCircuitConfig for StateCircuitConfig { u10_table.annotate_columns(meta); u16_table.annotate_columns(meta); + let pi_chunk_continuity = meta.instance_column(); + meta.enable_equality(pi_chunk_continuity); + let config = Self { selector, sort_keys, @@ -169,6 +190,8 @@ impl SubCircuitConfig for StateCircuitConfig { lookups, rw_table, mpt_table, + rw_permutation_config, + pi_chunk_continuity, _marker: PhantomData, }; @@ -178,9 +201,7 @@ impl SubCircuitConfig for StateCircuitConfig { constraint_builder.build(&queries); constraint_builder.gate(queries.selector) }); - for (name, lookup) in constraint_builder.lookups() { - meta.lookup_any(name, |_| lookup); - } + constraint_builder.lookups(meta, config.selector); config } @@ -198,11 +219,14 @@ impl StateCircuitConfig { layouter: &mut impl Layouter, rows: &[Rw], n_rows: usize, // 0 means dynamically calculated from `rows`. + prev_chunk_last_rw: Option, ) -> Result<(), Error> { let updates = MptUpdates::mock_from(rows); layouter.assign_region( || "state circuit", - |mut region| self.assign_with_region(&mut region, rows, &updates, n_rows), + |mut region| { + self.assign_with_region(&mut region, rows, &updates, n_rows, prev_chunk_last_rw) + }, ) } @@ -212,10 +236,12 @@ impl StateCircuitConfig { rows: &[Rw], updates: &MptUpdates, n_rows: usize, // 0 means dynamically calculated from `rows`. + prev_chunk_last_rw: Option, ) -> Result<(), Error> { let tag_chip = BinaryNumberChip::construct(self.sort_keys.tag); - let (rows, padding_length) = RwMap::table_assignments_prepad(rows, n_rows); + let (rows, padding_length) = + RwMap::table_assignments_padding(rows, n_rows, prev_chunk_last_rw); let rows_len = rows.len(); let mut state_root = updates.old_root(); @@ -228,11 +254,12 @@ impl StateCircuitConfig { log::trace!("state circuit assign offset:{} row:{:#?}", offset, row); } + // disable selector on offset 0 since it will be copy constraints by public input region.assign_fixed( || "selector", self.selector, offset, - || Value::known(F::ONE), + || Value::known(if offset == 0 { F::ZERO } else { F::ONE }), )?; tag_chip.assign(region, offset, &row.tag())?; @@ -399,6 +426,7 @@ impl StateCircuitConfig { region.name_column(|| "STATE_mpt_proof_type", self.mpt_proof_type); region.name_column(|| "STATE_state_root lo", self.state_root.lo()); region.name_column(|| "STATE_state_root hi", self.state_root.hi()); + region.name_column(|| "STATE_pi_chunk_continuity", self.pi_chunk_continuity); } } @@ -427,27 +455,43 @@ impl SortKeysConfig { /// State Circuit for proving RwTable is valid #[derive(Default, Clone, Debug)] -pub struct StateCircuit { +pub struct StateCircuit { /// Rw rows pub rows: Vec, + #[cfg(test)] + row_padding_and_overrides: Vec>>, updates: MptUpdates, pub(crate) n_rows: usize, #[cfg(test)] overrides: HashMap<(dev::AdviceColumn, isize), F>, + + /// permutation challenge + permu_alpha: F, + permu_gamma: F, + rw_fingerprints: RwFingerprints, + + prev_chunk_last_rw: Option, + _marker: PhantomData, } impl StateCircuit { /// make a new state circuit from an RwMap - pub fn new(rw_map: RwMap, n_rows: usize) -> Self { - let rows = rw_map.table_assignments(); + pub fn new(chunk: &Chunk) -> Self { + let rows = chunk.by_address_rws.table_assignments(false); // address sorted let updates = MptUpdates::mock_from(&rows); Self { rows, + #[cfg(test)] + row_padding_and_overrides: Default::default(), updates, - n_rows, + n_rows: chunk.fixed_param.max_rws, #[cfg(test)] overrides: HashMap::new(), + permu_alpha: chunk.permu_alpha, + permu_gamma: chunk.permu_gamma, + rw_fingerprints: chunk.by_address_rw_fingerprints.clone(), + prev_chunk_last_rw: chunk.prev_chunk_last_by_address_rw, _marker: PhantomData, } } @@ -456,8 +500,8 @@ impl StateCircuit { impl SubCircuit for StateCircuit { type Config = StateCircuitConfig; - fn new_from_block(block: &witness::Block) -> Self { - Self::new(block.rws.clone(), block.circuits_params.max_rws) + fn new_from_block(_block: &witness::Block, chunk: &Chunk) -> Self { + Self::new(chunk) } fn unusable_rows() -> usize { @@ -467,10 +511,10 @@ impl SubCircuit for StateCircuit { } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(_block: &witness::Block, chunk: &Chunk) -> (usize, usize) { ( - block.rws.0.values().flatten().count() + 1, - block.circuits_params.max_rws, + chunk.by_address_rws.0.values().flatten().count() + 1, + chunk.fixed_param.max_rws, ) } @@ -486,21 +530,67 @@ impl SubCircuit for StateCircuit { // Assigning to same columns in different regions should be avoided. // Here we use one single region to assign `overrides` to both rw table and // other parts. - layouter.assign_region( + let ( + alpha_cell, + gamma_cell, + row_fingerprints_prev_cell, + row_fingerprints_next_cell, + acc_fingerprints_prev_cell, + acc_fingerprints_next_cell, + ) = layouter.assign_region( || "state circuit", |mut region| { - config - .rw_table - .load_with_region(&mut region, &self.rows, self.n_rows)?; + // TODO optimise RwMap::table_assignments_prepad calls from 3 times -> 1 + let padded_rows = config.rw_table.load_with_region( + &mut region, + &self.rows, + self.n_rows, + self.prev_chunk_last_rw, + )?; + + config.assign_with_region( + &mut region, + &self.rows, + &self.updates, + self.n_rows, + self.prev_chunk_last_rw, + )?; - config.assign_with_region(&mut region, &self.rows, &self.updates, self.n_rows)?; + // permu_next_continuous_fingerprint and rows override for negative-test + #[allow(unused_assignments, unused_mut)] + let rows = if cfg!(test) { + let mut row_padding_and_overridess = None; + // NOTE need wrap in cfg(test) block even already under if cfg!(test) to make + // compiler happy + #[cfg(test)] + { + row_padding_and_overridess = if self.row_padding_and_overrides.is_empty() { + debug_assert!( + self.overrides.is_empty(), + "overrides size > 0 but row_padding_and_overridess = 0" + ); + Some(padded_rows.to2dvec()) + } else { + Some(self.row_padding_and_overrides.clone()) + }; + } + row_padding_and_overridess.unwrap() + } else { + padded_rows.to2dvec() + }; + let permutation_cells = config.rw_permutation_config.assign( + &mut region, + Value::known(self.permu_alpha), + Value::known(self.permu_gamma), + Value::known(self.rw_fingerprints.prev_mul_acc), + &rows, + "state_circuit", + )?; #[cfg(test)] { - let first_non_padding_index = if self.rows.len() < self.n_rows { - RwMap::padding_len(self.rows.len(), self.n_rows) - } else { - 1 // at least 1 StartOp padding in idx 0, so idx 1 is first non-padding row - }; + // we already handle rw_table override for negative test + // below is to support override value other than rw_table + let first_non_padding_index = 1; for ((column, row_offset), &f) in &self.overrides { let advice_column = column.value(config); @@ -517,14 +607,35 @@ impl SubCircuit for StateCircuit { } } - Ok(()) + Ok(permutation_cells) }, - ) + )?; + // constrain permutation challenges + [ + alpha_cell, + gamma_cell, + row_fingerprints_prev_cell, + row_fingerprints_next_cell, + acc_fingerprints_prev_cell, + acc_fingerprints_next_cell, + ] + .iter() + .enumerate() + .try_for_each(|(i, cell)| { + layouter.constrain_instance(cell.cell(), config.pi_chunk_continuity, i) + })?; + Ok(()) } - /// powers of randomness for instance columns fn instance(&self) -> Vec> { - vec![] + vec![vec![ + self.permu_alpha, + self.permu_gamma, + self.rw_fingerprints.prev_ending_row, + self.rw_fingerprints.ending_row, + self.rw_fingerprints.prev_mul_acc, + self.rw_fingerprints.mul_acc, + ]] } } diff --git a/zkevm-circuits/src/state_circuit/constraint_builder.rs b/zkevm-circuits/src/state_circuit/constraint_builder.rs index f2abbd6faf..5a35a3ec56 100644 --- a/zkevm-circuits/src/state_circuit/constraint_builder.rs +++ b/zkevm-circuits/src/state_circuit/constraint_builder.rs @@ -9,7 +9,10 @@ use crate::{ use bus_mapping::operation::Target; use eth_types::Field; use gadgets::binary_number::BinaryNumberConfig; -use halo2_proofs::plonk::Expression; +use halo2_proofs::{ + plonk::{Column, ConstraintSystem, Expression, Fixed}, + poly::Rotation, +}; use strum::IntoEnumIterator; use word::WordLoHi; @@ -110,12 +113,23 @@ impl ConstraintBuilder { .collect() } - pub fn lookups(&self) -> Vec> { - self.lookups.clone() + pub fn lookups(&self, meta: &mut ConstraintSystem, selector: Column) { + self.lookups.iter().cloned().for_each(|(name, mut lookup)| { + meta.lookup_any(name, |meta| { + let selector = meta.query_fixed(selector, Rotation::cur()); + for (expression, _) in lookup.iter_mut() { + *expression = expression.clone() * selector.clone(); + } + lookup + }); + }); } pub fn build(&mut self, q: &Queries) { self.build_general_constraints(q); + self.condition(q.tag_matches(Target::Padding), |cb| { + cb.build_padding_constraints(q) + }); self.condition(q.tag_matches(Target::Start), |cb| { cb.build_start_constraints(q) }); @@ -202,6 +216,36 @@ impl ConstraintBuilder { }); } + fn build_padding_constraints(&mut self, q: &Queries) { + // 1.0. Unused keys are 0 + self.require_zero("field_tag is 0 for Start", q.field_tag()); + self.require_zero("address is 0 for Start", q.rw_table.address.clone()); + self.require_zero("id is 0 for Start", q.id()); + self.require_word_zero("storage_key is 0 for Start", q.rw_table.storage_key.clone()); + // 1.1. rw_counter increases by 0 or 1 for every non-first row + // this is to serve multiple chunk usage, for padding rw counter is only local unique + // and not global unique + self.condition(q.not_first_access.clone(), |cb| { + cb.require_boolean( + "if previous row is also Padding. rw counter change is 0 or 1", + q.rw_counter_change(), + ) + }); + // 1.2. Start value is 0 + self.require_word_zero("Start value is 0", q.value()); + // 1.3. Start initial value is 0 + self.require_word_zero("Start initial_value is 0", q.initial_value()); + // 1.4. state_root is unchanged for every non-first row + self.condition(q.lexicographic_ordering_selector.clone(), |cb| { + cb.require_word_equal( + "state_root is unchanged for Start", + q.state_root(), + q.state_root_prev(), + ) + }); + self.require_word_zero("value_prev column is 0 for Start", q.value_prev_column()); + } + fn build_start_constraints(&mut self, q: &Queries) { // 1.0. Unused keys are 0 self.require_zero("field_tag is 0 for Start", q.field_tag()); diff --git a/zkevm-circuits/src/state_circuit/dev.rs b/zkevm-circuits/src/state_circuit/dev.rs index 193cf2e52c..c7012a20d0 100644 --- a/zkevm-circuits/src/state_circuit/dev.rs +++ b/zkevm-circuits/src/state_circuit/dev.rs @@ -61,7 +61,18 @@ where } #[cfg(test)] -use halo2_proofs::plonk::{Advice, Column}; +use crate::util::word::WordLoHi; + +#[cfg(test)] +use crate::state_circuit::HashMap; +#[cfg(test)] +use crate::witness::{Rw, RwRow}; + +#[cfg(test)] +use halo2_proofs::{ + circuit::Value, + plonk::{Advice, Column}, +}; #[cfg(test)] #[derive(Hash, Eq, PartialEq, Clone, Debug)] @@ -134,4 +145,51 @@ impl AdviceColumn { Self::NonEmptyWitness => config.is_non_exist.nonempty_witness, } } + + pub(crate) fn rw_row_overrides(&self, row: &mut RwRow>, value: F) { + match self { + Self::IsWrite => row.is_write = Value::known(value), + Self::_Address => row.address = Value::known(value), + Self::_StorageKeyLo => { + row.storage_key = WordLoHi::new([Value::known(value), row.storage_key.hi()]) + } + Self::_StorageKeyHi => { + row.storage_key = WordLoHi::new([row.storage_key.lo(), Value::known(value)]) + } + Self::ValueLo => row.value = WordLoHi::new([Value::known(value), row.value.hi()]), + Self::ValueHi => row.value = WordLoHi::new([row.value.lo(), Value::known(value)]), + Self::ValuePrevLo => { + row.value_prev = WordLoHi::new([Value::known(value), row.value_prev.hi()]) + } + Self::ValuePrevHi => { + row.value_prev = WordLoHi::new([row.value_prev.lo(), Value::known(value)]) + } + Self::RwCounter => row.rw_counter = Value::known(value), + Self::Tag => row.tag = Value::known(value), + Self::InitialValueLo => { + row.init_val = WordLoHi::new([Value::known(value), row.init_val.hi()]) + } + Self::InitialValueHi => { + row.init_val = WordLoHi::new([row.init_val.lo(), Value::known(value)]) + } + _ => (), + }; + } +} + +#[cfg(test)] +pub(crate) fn rw_overrides_skip_first_padding( + rws: &[Rw], + overrides: &HashMap<(AdviceColumn, isize), F>, +) -> Vec>> { + let first_non_padding_index = 1; + let mut rws: Vec>> = rws.iter().map(|row| row.table_assignment()).collect(); + + for ((column, row_offset), &f) in overrides { + let offset = + usize::try_from(isize::try_from(first_non_padding_index).unwrap() + *row_offset) + .unwrap(); + column.rw_row_overrides(&mut rws[offset], f); + } + rws } diff --git a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs index b8a5f4de45..1d2e448578 100644 --- a/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs +++ b/zkevm-circuits/src/state_circuit/lexicographic_ordering.rs @@ -97,7 +97,6 @@ pub struct Config { pub(crate) selector: Column, pub first_different_limb: BinaryNumberConfig, limb_difference: Column, - limb_difference_inverse: Column, } impl Config { @@ -111,27 +110,17 @@ impl Config { let bits = BinaryNumberBits::construct(meta); let first_different_limb = BinaryNumberChip::configure(meta, bits, selector, None); let limb_difference = meta.advice_column(); - let limb_difference_inverse = meta.advice_column(); let config = Config { selector, first_different_limb, limb_difference, - limb_difference_inverse, }; lookup.range_check_u16(meta, "limb_difference fits into u16", |meta| { meta.query_advice(limb_difference, Rotation::cur()) }); - meta.create_gate("limb_difference is not zero", |meta| { - let selector = meta.query_fixed(selector, Rotation::cur()); - let limb_difference = meta.query_advice(limb_difference, Rotation::cur()); - let limb_difference_inverse = - meta.query_advice(limb_difference_inverse, Rotation::cur()); - vec![selector * (1.expr() - limb_difference * limb_difference_inverse)] - }); - meta.create_gate( "limb differences before first_different_limb are all 0", |meta| { @@ -220,24 +209,15 @@ impl Config { offset, || Value::known(limb_difference), )?; - region.assign_advice( - || "limb_difference_inverse", - self.limb_difference_inverse, - offset, - || Value::known(limb_difference.invert().unwrap()), - )?; Ok(index) } /// Annotates columns of this gadget embedded within a circuit region. pub fn annotate_columns_in_region(&self, region: &mut Region, prefix: &str) { - [ - (self.limb_difference, "LO_limb_difference"), - (self.limb_difference_inverse, "LO_limb_difference_inverse"), - ] - .iter() - .for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), *col)); + [(self.limb_difference, "LO_limb_difference")] + .iter() + .for_each(|(col, ann)| region.name_column(|| format!("{}_{}", prefix, ann), *col)); // fixed column region.name_column( || format!("{}_LO_upper_limb_difference", prefix), diff --git a/zkevm-circuits/src/state_circuit/test.rs b/zkevm-circuits/src/state_circuit/test.rs index e072443f88..97826d0746 100644 --- a/zkevm-circuits/src/state_circuit/test.rs +++ b/zkevm-circuits/src/state_circuit/test.rs @@ -2,7 +2,7 @@ pub use super::{dev::*, *}; use crate::{ table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, util::{unusable_rows, SubCircuit}, - witness::{MptUpdates, Rw, RwMap}, + witness::{chunk::*, MptUpdates, Rw, RwMap}, }; use bus_mapping::operation::{ MemoryOp, Operation, OperationContainer, RWCounter, StackOp, StorageOp, RW, @@ -34,6 +34,26 @@ fn state_circuit_unusable_rows() { ) } +fn new_chunk_from_rw_map(rws: &RwMap, padding_start_rw: Option) -> Chunk { + let (alpha, gamma) = get_permutation_randomness(); + let mut chunk = Chunk { + by_address_rws: rws.clone(), + ..Default::default() + }; + + let rw_fingerprints = get_permutation_fingerprint_of_rwmap( + &chunk.by_address_rws, + chunk.fixed_param.max_rws, + alpha, + gamma, + F::from(1), + false, + padding_start_rw, + ); + chunk.by_address_rw_fingerprints = rw_fingerprints; + chunk +} + fn test_state_circuit_ok( memory_ops: Vec>, stack_ops: Vec>, @@ -45,8 +65,9 @@ fn test_state_circuit_ok( storage: storage_ops, ..Default::default() }); + let chunk = new_chunk_from_rw_map(&rw_map, None); - let circuit = StateCircuit::::new(rw_map, N_ROWS); + let circuit = StateCircuit::::new(&chunk); let instance = circuit.instance(); let prover = MockProver::::run(19, &circuit, instance).unwrap(); @@ -64,19 +85,23 @@ fn degree() { #[test] fn verifying_key_independent_of_rw_length() { let params = ParamsKZG::::setup(17, rand_chacha::ChaCha20Rng::seed_from_u64(2)); + let mut chunk = Chunk::default(); - let no_rows = StateCircuit::::new(RwMap::default(), N_ROWS); - let one_row = StateCircuit::::new( - RwMap::from(&OperationContainer { + let no_rows = StateCircuit::::new(&chunk); + + chunk = new_chunk_from_rw_map( + &RwMap::from(&OperationContainer { memory: vec![Operation::new( + RWCounter::from(1), RWCounter::from(1), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), )], ..Default::default() }), - N_ROWS, + None, ); + let one_row = StateCircuit::::new(&chunk); let vk_no_rows = keygen_vk(¶ms, &no_rows).unwrap(); let vk_one_rows = keygen_vk(¶ms, &one_row).unwrap(); @@ -93,39 +118,46 @@ fn verifying_key_independent_of_rw_length() { #[test] fn state_circuit_simple_2() { let memory_op_0 = Operation::new( + RWCounter::from(12), RWCounter::from(12), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let memory_op_1 = Operation::new( + RWCounter::from(24), RWCounter::from(24), RW::READ, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let memory_op_2 = Operation::new( + RWCounter::from(17), RWCounter::from(17), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(1), 32), ); let memory_op_3 = Operation::new( + RWCounter::from(87), RWCounter::from(87), RW::READ, MemoryOp::new(1, MemoryAddress::from(1), 32), ); let stack_op_0 = Operation::new( + RWCounter::from(17), RWCounter::from(17), RW::WRITE, StackOp::new(1, StackAddress::from(1), Word::from(32)), ); let stack_op_1 = Operation::new( + RWCounter::from(87), RWCounter::from(87), RW::READ, StackOp::new(1, StackAddress::from(1), Word::from(32)), ); let storage_op_0 = Operation::new( + RWCounter::from(0), RWCounter::from(0), RW::WRITE, StorageOp::new( @@ -138,6 +170,7 @@ fn state_circuit_simple_2() { ), ); let storage_op_1 = Operation::new( + RWCounter::from(18), RWCounter::from(18), RW::WRITE, StorageOp::new( @@ -150,6 +183,7 @@ fn state_circuit_simple_2() { ), ); let storage_op_2 = Operation::new( + RWCounter::from(19), RWCounter::from(19), RW::WRITE, StorageOp::new( @@ -172,16 +206,19 @@ fn state_circuit_simple_2() { #[test] fn state_circuit_simple_6() { let memory_op_0 = Operation::new( + RWCounter::from(12), RWCounter::from(12), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let memory_op_1 = Operation::new( + RWCounter::from(13), RWCounter::from(13), RW::READ, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let storage_op_2 = Operation::new( + RWCounter::from(19), RWCounter::from(19), RW::WRITE, StorageOp::new( @@ -199,11 +236,13 @@ fn state_circuit_simple_6() { #[test] fn lexicographic_ordering_test_1() { let memory_op = Operation::new( + RWCounter::from(12), RWCounter::from(12), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let storage_op = Operation::new( + RWCounter::from(19), RWCounter::from(19), RW::WRITE, StorageOp::new( @@ -221,11 +260,13 @@ fn lexicographic_ordering_test_1() { #[test] fn lexicographic_ordering_test_2() { let memory_op_0 = Operation::new( + RWCounter::from(12), RWCounter::from(12), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), ); let memory_op_1 = Operation::new( + RWCounter::from(13), RWCounter::from(13), RW::WRITE, MemoryOp::new(1, MemoryAddress::from(0), 32), @@ -655,18 +696,21 @@ fn all_padding() { } #[test] -fn skipped_start_rw_counter() { +fn invalid_padding_rw_counter_change() { let overrides = HashMap::from([ ( - (AdviceColumn::RwCounter, -1), + (AdviceColumn::RwCounter, 0), // The original assignment is 1 << 16. Fr::from((1 << 16) + 1), ), - ((AdviceColumn::RwCounterLimb0, -1), Fr::ONE), + ((AdviceColumn::RwCounterLimb0, 0), Fr::ONE), ]); - let result = prover(vec![], overrides).verify_at_rows(N_ROWS - 1..N_ROWS, N_ROWS - 1..N_ROWS); - assert_error_matches(result, "rw_counter increases by 1 for every non-first row"); + let result = prover(vec![], overrides).verify_at_rows(2..3, 2..3); + assert_error_matches( + result, + "if previous row is also Padding. rw counter change is 0 or 1", + ); } #[test] @@ -783,7 +827,7 @@ fn invalid_stack_address_change() { #[test] fn invalid_tags() { - let first_row_offset = -isize::try_from(N_ROWS).unwrap(); + let first_row_offset = 0; let tags: BTreeSet = Target::iter().map(|x| x as usize).collect(); for i in 0..16 { if tags.contains(&i) { @@ -798,8 +842,8 @@ fn invalid_tags() { ((AdviceColumn::Tag, first_row_offset), Fr::from(i as u64)), ]); - let result = prover(vec![], overrides).verify_at_rows(0..1, 0..1); - + // offset 0 is padding + let result = prover(vec![], overrides).verify_at_rows(1..2, 1..2); assert_error_matches(result, "binary number value in range"); } } @@ -922,15 +966,8 @@ fn variadic_size_check() { value: U256::from(394500u64), }, ]; - - let updates = MptUpdates::mock_from(&rows); - let circuit = StateCircuit:: { - rows: rows.clone(), - updates, - overrides: HashMap::default(), - n_rows: N_ROWS, - _marker: std::marker::PhantomData, - }; + // let rw_map: RwMap = rows.clone().into(); + let circuit = StateCircuit::new(&new_chunk_from_rw_map(&RwMap::from(rows.clone()), None)); let power_of_randomness = circuit.instance(); let prover1 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -951,14 +988,7 @@ fn variadic_size_check() { }, ]); - let updates = MptUpdates::mock_from(&rows); - let circuit = StateCircuit:: { - rows, - updates, - overrides: HashMap::default(), - n_rows: N_ROWS, - _marker: std::marker::PhantomData, - }; + let circuit = StateCircuit::new(&new_chunk_from_rw_map(&rows.into(), None)); let power_of_randomness = circuit.instance(); let prover2 = MockProver::::run(17, &circuit, power_of_randomness).unwrap(); @@ -991,12 +1021,26 @@ fn bad_initial_tx_receipt_value() { } fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockProver { + // permu_next_continuous_fingerprint and rows override for negative-test + #[allow(unused_assignments, unused_mut)] + let (rw_rows, _) = RwMap::table_assignments_padding(&rows, N_ROWS, None); + let rw_rows: Vec>> = + rw_overrides_skip_first_padding(&rw_rows, &overrides); + let rwtable_fingerprints = + get_permutation_fingerprint_of_rwrowvec(&rw_rows, N_ROWS, Fr::ONE, Fr::ONE, Fr::ONE, None); + let row_padding_and_overridess = rw_rows.to2dvec(); + let updates = MptUpdates::mock_from(&rows); let circuit = StateCircuit:: { rows, + row_padding_and_overrides: row_padding_and_overridess, updates, overrides, n_rows: N_ROWS, + permu_alpha: Fr::from(1), + permu_gamma: Fr::from(1), + rw_fingerprints: rwtable_fingerprints, + prev_chunk_last_rw: None, _marker: std::marker::PhantomData, }; let instance = circuit.instance(); @@ -1006,8 +1050,7 @@ fn prover(rows: Vec, overrides: HashMap<(AdviceColumn, isize), Fr>) -> MockP fn verify(rows: Vec) -> Result<(), Vec> { let used_rows = rows.len(); - prover(rows, HashMap::new()) - .verify_at_rows(N_ROWS - used_rows..N_ROWS, N_ROWS - used_rows..N_ROWS) + prover(rows, HashMap::new()).verify_at_rows(1..used_rows + 1, 1..used_rows + 1) } fn verify_with_overrides( @@ -1018,33 +1061,36 @@ fn verify_with_overrides( assert_eq!(verify(rows.clone()), Ok(())); let n_active_rows = rows.len(); - prover(rows, overrides).verify_at_rows( - N_ROWS - n_active_rows..N_ROWS, - N_ROWS - n_active_rows..N_ROWS, - ) + prover(rows, overrides).verify_at_rows(1..n_active_rows + 1, 1..n_active_rows + 1) } fn assert_error_matches(result: Result<(), Vec>, name: &str) { let errors = result.expect_err("result is not an error"); - assert_eq!(errors.len(), 1, "{:?}", errors); - match &errors[0] { - VerifyFailure::ConstraintNotSatisfied { constraint, .. } => { - // fields of halo2_proofs::dev::metadata::Constraint aren't public, so we have - // to match off of its format string. - let constraint = format!("{}", constraint); - if !constraint.contains(name) { - panic!("{} does not contain {}", constraint, name); + errors + .iter() + .find(|err| match err { + VerifyFailure::ConstraintNotSatisfied { constraint, .. } => { + // fields of halo2_proofs::dev::metadata::Constraint aren't public, so we have + // to match off of its format string. + let constraint = format!("{}", constraint); + constraint.contains(name) } - } - VerifyFailure::Lookup { - name: lookup_name, .. - } => { - assert_eq!(lookup_name, &name) - } - VerifyFailure::CellNotAssigned { .. } => panic!(), - VerifyFailure::ConstraintPoisoned { .. } => panic!(), - VerifyFailure::Permutation { .. } => panic!(), - VerifyFailure::InstanceCellNotAssigned { .. } => panic!(), - VerifyFailure::Shuffle { .. } => panic!(), - } + VerifyFailure::Lookup { + name: lookup_name, .. + } => { + assert_eq!(lookup_name, &name); + true + } + VerifyFailure::CellNotAssigned { .. } => false, + VerifyFailure::ConstraintPoisoned { .. } => false, + VerifyFailure::Permutation { .. } => false, + VerifyFailure::InstanceCellNotAssigned { .. } => false, + VerifyFailure::Shuffle { .. } => false, + }) + .unwrap_or_else(|| { + panic!( + "there is no constraints contain {}; err {:#?}", + name, errors + ) + }); } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 55fabdf08a..5829f85c9e 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -62,22 +62,24 @@ use crate::{ pi_circuit::{PiCircuit, PiCircuitConfig, PiCircuitConfigArgs}, state_circuit::{StateCircuit, StateCircuitConfig, StateCircuitConfigArgs}, table::{ - BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, MptTable, RwTable, SigTable, - TxTable, UXTable, WdTable, + BlockTable, BytecodeTable, CopyTable, ExpTable, KeccakTable, LookupTable, MptTable, + RwTable, SigTable, TxTable, UXTable, WdTable, }, tx_circuit::{TxCircuit, TxCircuitConfig, TxCircuitConfigArgs}, - util::{log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, - witness::{block_convert, Block, MptUpdates}, + util::{chunk_ctx::ChunkContextConfig, log2_ceil, Challenges, SubCircuit, SubCircuitConfig}, + witness::{block_convert, chunk_convert, Block, Chunk, MptUpdates}, }; use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, FeatureConfig, FixedCParams}, mock::BlockData, }; use eth_types::{geth_types::GethData, Field}; +use gadgets::util::Expr; use halo2_proofs::{ circuit::{Layouter, SimpleFloorPlanner, Value}, - plonk::{Circuit, ConstraintSystem, Error, Expression}, + plonk::{Any, Circuit, Column, ConstraintSystem, Error, Expression}, }; +use itertools::Itertools; use std::array; @@ -97,10 +99,33 @@ pub struct SuperCircuitConfig { keccak_circuit: KeccakCircuitConfig, pi_circuit: PiCircuitConfig, exp_circuit: ExpCircuitConfig, + chunk_ctx_config: ChunkContextConfig, #[cfg(not(feature = "mock-challenge"))] challenges: Challenges, } +/// Circuit configuration arguments +pub struct SuperCircuitConfigArgs { + /// Max txs + pub max_txs: usize, + /// Max calldata + pub max_calldata: usize, + /// Mock randomness + pub mock_randomness: F, +} + +impl SuperCircuitConfig { + /// get chronological_rwtable and byaddr_rwtable advice columns + pub fn get_rwtable_columns(&self) -> Vec> { + // concat rw_table columns: [chronological_rwtable] ++ [byaddr_rwtable] + let mut columns = >::columns(&self.evm_circuit.rw_table); + columns.append(&mut >::columns( + &self.state_circuit.rw_table, + )); + columns + } +} + impl SubCircuitConfig for SuperCircuitConfig { type ConfigArgs = SuperCircuitParams; @@ -117,7 +142,10 @@ impl SubCircuitConfig for SuperCircuitConfig { ) -> Self { let tx_table = TxTable::construct(meta); let wd_table = WdTable::construct(meta); - let rw_table = RwTable::construct(meta); + + let chronological_rw_table = RwTable::construct(meta); + let by_address_rw_table = RwTable::construct(meta); + let mpt_table = MptTable::construct(meta); let bytecode_table = BytecodeTable::construct(meta); let block_table = BlockTable::construct(meta); @@ -150,6 +178,8 @@ impl SubCircuitConfig for SuperCircuitConfig { let sig_table = SigTable::construct(meta); + let chunk_ctx_config = ChunkContextConfig::new(meta, &challenges_exprs); + let keccak_circuit = KeccakCircuitConfig::new( meta, KeccakCircuitConfigArgs { @@ -191,7 +221,7 @@ impl SubCircuitConfig for SuperCircuitConfig { meta, CopyCircuitConfigArgs { tx_table: tx_table.clone(), - rw_table, + rw_table: chronological_rw_table, bytecode_table: bytecode_table.clone(), copy_table, q_enable: q_copy_table, @@ -201,7 +231,7 @@ impl SubCircuitConfig for SuperCircuitConfig { let state_circuit = StateCircuitConfig::new( meta, StateCircuitConfigArgs { - rw_table, + rw_table: by_address_rw_table, mpt_table, u8_table, u10_table, @@ -215,7 +245,7 @@ impl SubCircuitConfig for SuperCircuitConfig { EvmCircuitConfigArgs { challenges: challenges_exprs, tx_table, - rw_table, + rw_table: chronological_rw_table, bytecode_table, block_table: block_table.clone(), copy_table, @@ -224,10 +254,68 @@ impl SubCircuitConfig for SuperCircuitConfig { u8_table, u16_table, sig_table, + chunk_ctx_config: chunk_ctx_config.clone(), feature_config, }, ); + // chronological/by address rwtable fingerprint must be the same in last chunk + // last row. + meta.create_gate( + "chronological rwtable fingerprint == by address rwtable fingerprint", + |meta| { + let is_last_chunk = chunk_ctx_config.is_last_chunk.expr(); + let chronological_rwtable_acc_fingerprint = evm_circuit + .rw_permutation_config + .acc_fingerprints_cur_expr(); + let by_address_rwtable_acc_fingerprint = state_circuit + .rw_permutation_config + .acc_fingerprints_cur_expr(); + + let q_row_last = meta.query_selector(evm_circuit.rw_permutation_config.q_row_last); + + vec![ + is_last_chunk + * q_row_last + * (chronological_rwtable_acc_fingerprint + - by_address_rwtable_acc_fingerprint), + ] + }, + ); + + // chronological/by address rwtable `row fingerprint` must be the same in first + // chunk first row. + // `row fingerprint` is not a constant so root circuit can NOT constraint it. + // so we constraints here by gate + // Furthermore, first row in rw_table should be `Rw::Start`, which will be lookup by + // `BeginChunk` at first chunk + meta.create_gate( + "chronological rwtable row fingerprint == by address rwtable row fingerprint", + |meta| { + let is_first_chunk = chunk_ctx_config.is_first_chunk.expr(); + let chronological_rwtable_row_fingerprint = evm_circuit + .rw_permutation_config + .row_fingerprints_cur_expr(); + let by_address_rwtable_row_fingerprint = state_circuit + .rw_permutation_config + .row_fingerprints_cur_expr(); + + let q_row_first = 1.expr() + - meta.query_selector(evm_circuit.rw_permutation_config.q_row_non_first); + + let q_row_enable = + meta.query_selector(evm_circuit.rw_permutation_config.q_row_enable); + + vec![ + is_first_chunk + * q_row_first + * q_row_enable + * (chronological_rwtable_row_fingerprint + - by_address_rwtable_row_fingerprint), + ] + }, + ); + Self { block_table, mpt_table, @@ -242,6 +330,7 @@ impl SubCircuitConfig for SuperCircuitConfig { keccak_circuit, pi_circuit, exp_circuit, + chunk_ctx_config, #[cfg(not(feature = "mock-challenge"))] challenges, } @@ -251,6 +340,8 @@ impl SubCircuitConfig for SuperCircuitConfig { /// The Super Circuit contains all the zkEVM circuits #[derive(Clone, Default, Debug)] pub struct SuperCircuit { + /// Chunk + pub chunk: Option>, /// EVM Circuit pub evm_circuit: EvmCircuit, /// State Circuit @@ -277,10 +368,10 @@ pub struct 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); + pub fn get_num_rows_required(block: &Block, chunk: &Chunk) -> usize { + let num_rows_evm_circuit = EvmCircuit::::get_num_rows_required(block, chunk); let num_rows_tx_circuit = - TxCircuitConfig::::get_num_rows_required(block.circuits_params.max_txs); + TxCircuitConfig::::get_num_rows_required(chunk.fixed_param.max_txs); num_rows_evm_circuit.max(num_rows_tx_circuit) } } @@ -305,17 +396,18 @@ impl SubCircuit for SuperCircuit { .unwrap() } - fn new_from_block(block: &Block) -> Self { - let evm_circuit = EvmCircuit::new_from_block(block); - let state_circuit = StateCircuit::new_from_block(block); - let tx_circuit = TxCircuit::new_from_block(block); - let pi_circuit = PiCircuit::new_from_block(block); - let bytecode_circuit = BytecodeCircuit::new_from_block(block); - let copy_circuit = CopyCircuit::new_from_block_no_external(block); - let exp_circuit = ExpCircuit::new_from_block(block); - let keccak_circuit = KeccakCircuit::new_from_block(block); + fn new_from_block(block: &Block, chunk: &Chunk) -> Self { + let evm_circuit = EvmCircuit::new_from_block(block, chunk); + let state_circuit = StateCircuit::new_from_block(block, chunk); + let tx_circuit = TxCircuit::new_from_block(block, chunk); + let pi_circuit = PiCircuit::new_from_block(block, chunk); + let bytecode_circuit = BytecodeCircuit::new_from_block(block, chunk); + let copy_circuit = CopyCircuit::new_from_block_no_external(block, chunk); + let exp_circuit = ExpCircuit::new_from_block(block, chunk); + let keccak_circuit = KeccakCircuit::new_from_block(block, chunk); SuperCircuit::<_> { + chunk: Some(chunk.clone()), evm_circuit, state_circuit, tx_circuit, @@ -324,7 +416,7 @@ impl SubCircuit for SuperCircuit { copy_circuit, exp_circuit, keccak_circuit, - circuits_params: block.circuits_params, + circuits_params: chunk.fixed_param, feature_config: block.feature_config, mock_randomness: block.randomness, } @@ -333,6 +425,17 @@ impl SubCircuit for SuperCircuit { /// Returns suitable inputs for the SuperCircuit. fn instance(&self) -> Vec> { let mut instance = Vec::new(); + + let chunk = self.chunk.as_ref().unwrap(); + + instance.extend_from_slice(&[vec![ + F::from(chunk.chunk_context.idx as u64), + F::from(chunk.chunk_context.idx as u64) + F::ONE, + F::from(chunk.chunk_context.total_chunks as u64), + F::from(chunk.chunk_context.initial_rwc as u64), + F::from(chunk.chunk_context.end_rwc as u64), + ]]); + instance.extend_from_slice(&self.keccak_circuit.instance()); instance.extend_from_slice(&self.pi_circuit.instance()); instance.extend_from_slice(&self.tx_circuit.instance()); @@ -340,21 +443,23 @@ impl SubCircuit for SuperCircuit { instance.extend_from_slice(&self.copy_circuit.instance()); instance.extend_from_slice(&self.state_circuit.instance()); instance.extend_from_slice(&self.exp_circuit.instance()); - instance.extend_from_slice(&self.evm_circuit.instance()); + // remove first vector which is chunk_ctx + // which supercircuit already supply globally on top + instance.extend_from_slice(&self.evm_circuit.instance()[1..]); instance } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &Block) -> (usize, usize) { - let evm = EvmCircuit::min_num_rows_block(block); - let state = StateCircuit::min_num_rows_block(block); - let bytecode = BytecodeCircuit::min_num_rows_block(block); - let copy = CopyCircuit::min_num_rows_block(block); - let keccak = KeccakCircuit::min_num_rows_block(block); - let tx = TxCircuit::min_num_rows_block(block); - let exp = ExpCircuit::min_num_rows_block(block); - let pi = PiCircuit::min_num_rows_block(block); + fn min_num_rows_block(block: &Block, chunk: &Chunk) -> (usize, usize) { + let evm = EvmCircuit::min_num_rows_block(block, chunk); + let state = StateCircuit::min_num_rows_block(block, chunk); + let bytecode = BytecodeCircuit::min_num_rows_block(block, chunk); + let copy = CopyCircuit::min_num_rows_block(block, chunk); + let keccak = KeccakCircuit::min_num_rows_block(block, chunk); + let tx = TxCircuit::min_num_rows_block(block, chunk); + let exp = ExpCircuit::min_num_rows_block(block, chunk); + let pi = PiCircuit::min_num_rows_block(block, chunk); let rows: Vec<(usize, usize)> = vec![evm, state, bytecode, copy, keccak, tx, exp, pi]; let (rows_without_padding, rows_with_padding): (Vec, Vec) = @@ -372,6 +477,12 @@ impl SubCircuit for SuperCircuit { challenges: &Challenges>, layouter: &mut impl Layouter, ) -> Result<(), Error> { + // synthesize chunk context + config.chunk_ctx_config.assign_chunk_context( + layouter, + &self.chunk.as_ref().unwrap().chunk_context, + self.chunk.as_ref().unwrap().fixed_param.max_rws - 1, + )?; self.keccak_circuit .synthesize_sub(&config.keccak_circuit, challenges, layouter)?; self.bytecode_circuit @@ -470,12 +581,19 @@ impl SuperCircuit { geth_data: GethData, circuits_params: FixedCParams, mock_randomness: F, - ) -> Result<(u32, Self, Vec>, CircuitInputBuilder), bus_mapping::Error> - { + ) -> Result< + ( + u32, + Vec, + Vec>>, + CircuitInputBuilder, + ), + bus_mapping::Error, + > { let block_data = BlockData::new_from_geth_data_with_params(geth_data.clone(), circuits_params); - let mut builder = block_data.new_circuit_input_builder(); - builder + let builder = block_data + .new_circuit_input_builder() .handle_block(&geth_data.eth_block, &geth_data.geth_traces) .expect("could not handle block tx"); @@ -488,20 +606,43 @@ impl SuperCircuit { /// /// Also, return with it the minimum required SRS degree for the circuit and /// the Public Inputs needed. + #[allow(clippy::type_complexity)] pub fn build_from_circuit_input_builder( builder: &CircuitInputBuilder, mock_randomness: F, - ) -> Result<(u32, Self, Vec>), bus_mapping::Error> { + ) -> Result<(u32, Vec, Vec>>), bus_mapping::Error> { let mut block = block_convert(builder).unwrap(); + let chunks = chunk_convert(&block, builder).unwrap(); block.randomness = mock_randomness; - let (_, rows_needed) = Self::min_num_rows_block(&block); - let k = log2_ceil(Self::unusable_rows() + rows_needed); + let (rows_needed, circuit_instance_pairs): (Vec, Vec<(_, _)>) = chunks + .iter() + .map(|chunk| { + let (_, rows_needed) = Self::min_num_rows_block(&block, chunk); + + let circuit = SuperCircuit::new_from_block(&block, chunk); + let instance = circuit.instance(); + (rows_needed, (circuit, instance)) + }) + .unzip(); + + // assert all rows needed are equal + rows_needed + .iter() + .tuple_windows() + .for_each(|rows_needed: (&usize, &usize)| { + assert!( + rows_needed.0 == rows_needed.1, + "mismatched super_circuit rows_needed {:?} != {:?}", + rows_needed.0, + rows_needed.1 + ) + }); + + let k = log2_ceil(Self::unusable_rows() + rows_needed[0]); log::debug!("super circuit uses k = {}", k); - let circuit = SuperCircuit::new_from_block(&block); - - let instance = circuit.instance(); - Ok((k, circuit, instance)) + let (circuits, instances) = circuit_instance_pairs.into_iter().unzip(); + Ok((k, circuits, instances)) } } diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index 741fbdad8b..062d324aa7 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -1,14 +1,26 @@ +use crate::{table::rw_table::get_rwtable_cols_commitment, witness::RwMap}; + pub use super::*; +use bus_mapping::operation::OperationContainer; +use eth_types::{address, bytecode, geth_types::GethData, Word}; use ethers_signers::{LocalWallet, Signer}; -use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; +use halo2_proofs::{ + dev::MockProver, + halo2curves::{ + bn256::{Bn256, Fr}, + ff::WithSmallOrderMulGroup, + }, + poly::{ + commitment::CommitmentScheme, + kzg::commitment::{KZGCommitmentScheme, ParamsKZG}, + }, +}; use log::error; use mock::{TestContext, MOCK_CHAIN_ID}; use rand::SeedableRng; -use rand_chacha::ChaCha20Rng; +use rand_chacha::{rand_core::OsRng, ChaCha20Rng}; use std::collections::HashMap; -use eth_types::{address, bytecode, geth_types::GethData, Word}; - #[test] fn super_circuit_degree() { let mut cs = ConstraintSystem::::default(); @@ -26,14 +38,20 @@ fn super_circuit_degree() { } fn test_super_circuit(block: GethData, circuits_params: FixedCParams, mock_randomness: Fr) { - let (k, circuit, instance, _) = + let (k, circuits, instances, _) = SuperCircuit::::build(block, circuits_params, mock_randomness).unwrap(); - let prover = MockProver::run(k, &circuit, instance).unwrap(); - let res = prover.verify(); - if let Err(err) = res { - error!("Verification failures: {:#?}", err); - panic!("Failed verification"); - } + circuits + .into_iter() + .zip(instances) + .enumerate() + .for_each(|(i, (circuit, instance))| { + let prover = MockProver::run(k, &circuit, instance).unwrap(); + let res = prover.verify(); + if let Err(err) = res { + error!("{}th supercircuit Verification failures: {:#?}", i, err); + panic!("Failed verification"); + } + }); } pub(crate) fn block_1tx() -> GethData { @@ -131,6 +149,7 @@ const TEST_MOCK_RANDOMNESS: u64 = 0x100; fn serial_test_super_circuit_1tx_1max_tx() { let block = block_1tx(); let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 1, max_withdrawals: 5, max_calldata: 32, @@ -149,6 +168,7 @@ fn serial_test_super_circuit_1tx_1max_tx() { fn serial_test_super_circuit_1tx_2max_tx() { let block = block_1tx(); let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 2, max_withdrawals: 5, max_calldata: 32, @@ -167,6 +187,7 @@ fn serial_test_super_circuit_1tx_2max_tx() { fn serial_test_super_circuit_2tx_2max_tx() { let block = block_2tx(); let circuits_params = FixedCParams { + total_chunks: 1, max_txs: 2, max_withdrawals: 5, max_calldata: 32, @@ -180,3 +201,74 @@ fn serial_test_super_circuit_2tx_2max_tx() { }; test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); } + +#[ignore] +#[test] +fn serial_test_multi_chunk_super_circuit_2tx_2max_tx() { + let block = block_2tx(); + let circuits_params = FixedCParams { + total_chunks: 4, + max_txs: 2, + max_withdrawals: 5, + max_calldata: 32, + max_rws: 90, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + max_vertical_circuit_rows: 0, + }; + test_super_circuit(block, circuits_params, Fr::from(TEST_MOCK_RANDOMNESS)); +} + +#[ignore] +#[test] +fn test_rw_table_commitment() { + let k = 18; + let params = ParamsKZG::::setup(k, OsRng); + rw_table_commitment::>(¶ms); +} + +fn rw_table_commitment(params: &Scheme::ParamsProver) +where + ::Scalar: WithSmallOrderMulGroup<3> + eth_types::Field, +{ + let circuits_params = FixedCParams { + max_txs: 1, + max_withdrawals: 5, + max_calldata: 32, + max_rws: 256, + max_copy_rows: 256, + max_exp_steps: 256, + max_bytecode: 512, + max_evm_rows: 0, + max_keccak_rows: 0, + total_chunks: 1, + max_vertical_circuit_rows: 0, + }; + let rw_map = RwMap::from(&OperationContainer { + ..Default::default() + }); + let rows = rw_map.table_assignments(false); + + const TEST_MOCK_RANDOMNESS: u64 = 0x100; + + // synthesize to get degree + let mut cs = ConstraintSystem::<::Scalar>::default(); + let _config = SuperCircuit::configure_with_params( + &mut cs, + SuperCircuitParams { + max_txs: circuits_params.max_txs, + max_withdrawals: circuits_params.max_withdrawals, + max_calldata: circuits_params.max_calldata, + mock_randomness: TEST_MOCK_RANDOMNESS.into(), + feature_config: FeatureConfig::default(), + }, + ); + let degree = cs.degree(); + + let advice_commitments = + get_rwtable_cols_commitment::(degree, &rows, circuits_params.max_rws, params); + println!("advice_commitments len() {:?}", advice_commitments.len()); +} diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 93c1d0f1e1..5322a0bcb2 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -27,6 +27,8 @@ use strum_macros::{EnumCount, EnumIter}; pub(crate) mod block_table; /// bytecode table pub(crate) mod bytecode_table; +/// chunk context table +pub(crate) mod chunk_ctx_table; /// copy Table pub(crate) mod copy_table; /// exp(exponentiation) table @@ -48,6 +50,7 @@ pub(crate) mod wd_table; pub use block_table::{BlockContextFieldTag, BlockTable}; pub use bytecode_table::{BytecodeFieldTag, BytecodeTable}; +pub use chunk_ctx_table::ChunkCtxTable; pub use copy_table::CopyTable; pub use exp_table::ExpTable; pub use keccak_table::KeccakTable; @@ -190,3 +193,29 @@ pub enum CallContextFieldTag { ReversibleWriteCounter, } impl_expr!(CallContextFieldTag); + +/// Tag for an StepState in RwTable +#[derive(Clone, Copy, Debug, EnumIter, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum StepStateFieldTag { + /// caller id field + CallID = 1, + /// is_root field + IsRoot, + /// is_create field + IsCreate, + /// code_hash field + CodeHash, + /// program_counter field + ProgramCounter, + /// stack_pointer field + StackPointer, + /// gas_left field + GasLeft, + /// memory_word_size field + MemoryWordSize, + /// reversible_write_counter field + ReversibleWriteCounter, + /// log_id field + LogID, +} +impl_expr!(StepStateFieldTag); diff --git a/zkevm-circuits/src/table/chunk_ctx_table.rs b/zkevm-circuits/src/table/chunk_ctx_table.rs new file mode 100644 index 0000000000..1f9bb85957 --- /dev/null +++ b/zkevm-circuits/src/table/chunk_ctx_table.rs @@ -0,0 +1,145 @@ +use bus_mapping::circuit_input_builder::ChunkContext; +use gadgets::util::Expr; +use halo2_proofs::circuit::AssignedCell; + +use super::*; + +/// Tag to identify the field in a Chunk Context row +// Keep the sequence consistent with OpcodeId for scalar +#[derive(Clone, Copy, Debug)] +pub enum ChunkCtxFieldTag { + /// Coinbase field + CurrentChunkIndex = 1, + /// NextChunk field + NextChunkIndex, + /// Total Chunks field + TotalChunks, + /// initial rw counter + InitialRWC, + /// end rw counter + EndRWC, +} +impl_expr!(ChunkCtxFieldTag); + +/// Table with Chunk context fields +#[derive(Clone, Debug)] +pub struct ChunkCtxTable { + q_enable: Selector, + /// Tag + pub tag: Column, + /// Value + pub value: Column, +} + +type ChunkCtxTableAssignedCells = ( + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, + AssignedCell, +); + +impl ChunkCtxTable { + /// Construct a new ChunkCtxTable + pub fn construct(meta: &mut ConstraintSystem) -> Self { + let (q_enable, tag, value) = (meta.selector(), meta.fixed_column(), meta.advice_column()); + + // constrain NextChunkIndex = CurrentChunkIndex + 1 + meta.create_gate("NextChunkIndex = CurrentChunkIndex + 1", |meta| { + let q_enable = meta.query_selector(q_enable); + let value_cur = meta.query_advice(value, Rotation::cur()); + let value_next = meta.query_advice(value, Rotation::next()); + [q_enable * (value_next - value_cur - 1.expr())] + }); + + meta.enable_equality(value); + + Self { + q_enable, + tag, + value, + } + } + + /// Assign the `ChunkCtxTable` from a `BlockContext`. + pub fn load( + &self, + layouter: &mut impl Layouter, + chunk_ctx: &ChunkContext, + ) -> Result, Error> { + layouter.assign_region( + || "chunk_ctx table", + |mut region| { + let mut offset = 0; + + self.q_enable.enable(&mut region, offset)?; + + let assigned_cells = [ + // CurrentChunkIndex + ( + F::from(ChunkCtxFieldTag::CurrentChunkIndex as u64), + F::from(chunk_ctx.idx as u64), + ), + // NextChunkIndex + ( + F::from(ChunkCtxFieldTag::NextChunkIndex as u64), + F::from(chunk_ctx.idx as u64 + 1u64), + ), + // TotalChunks + ( + F::from(ChunkCtxFieldTag::TotalChunks as u64), + F::from(chunk_ctx.total_chunks as u64), + ), + // InitialRWC + ( + F::from(ChunkCtxFieldTag::InitialRWC as u64), + F::from(chunk_ctx.initial_rwc as u64), + ), + // EndRWC + ( + F::from(ChunkCtxFieldTag::EndRWC as u64), + F::from(chunk_ctx.end_rwc as u64), + ), + // Empty row for disable lookup + (F::ZERO, F::ZERO), + ] + .iter() + .map(|(tag, value)| { + region.assign_fixed( + || format!("chunk_ctx table tag {}", offset), + self.tag, + offset, + || Value::known(*tag), + )?; + + let assigned_value = region.assign_advice( + || format!("chunk_ctx table value {}", offset), + self.value, + offset, + || Value::known(*value), + )?; + + offset += 1; + + Ok(assigned_value) + }) + .collect::>, Error>>()?; + + // remove last empty cell + let assigned_cells = assigned_cells.split_last().unwrap().1; + + Ok(assigned_cells.iter().cloned().collect_tuple().unwrap()) + }, + ) + } +} + +impl LookupTable for ChunkCtxTable { + fn columns(&self) -> Vec> { + vec![self.tag.into(), self.value.into()] + } + + fn annotations(&self) -> Vec { + vec![String::from("tag"), String::from("value")] + } +} diff --git a/zkevm-circuits/src/table/copy_table.rs b/zkevm-circuits/src/table/copy_table.rs index 4b93bdf2ba..1fea1d8240 100644 --- a/zkevm-circuits/src/table/copy_table.rs +++ b/zkevm-circuits/src/table/copy_table.rs @@ -1,3 +1,5 @@ +use crate::witness::Chunk; + use super::*; use gadgets::binary_number::AsBits; @@ -218,6 +220,7 @@ impl CopyTable { &self, layouter: &mut impl Layouter, block: &Block, + chunk: &Chunk, challenges: &Challenges>, ) -> Result<(), Error> { layouter.assign_region( @@ -250,7 +253,7 @@ impl CopyTable { } // Enable selector at all rows - let max_copy_rows = block.circuits_params.max_copy_rows; + let max_copy_rows = chunk.fixed_param.max_copy_rows; for offset in 0..max_copy_rows { region.assign_fixed( || "q_enable", diff --git a/zkevm-circuits/src/table/exp_table.rs b/zkevm-circuits/src/table/exp_table.rs index 628a4c30ff..7067182cb4 100644 --- a/zkevm-circuits/src/table/exp_table.rs +++ b/zkevm-circuits/src/table/exp_table.rs @@ -3,7 +3,7 @@ use super::*; use crate::{ exp_circuit::param::{OFFSET_INCREMENT, ROWS_PER_STEP}, table::LookupTable, - witness::Block, + witness::{Block, Chunk}, }; use bus_mapping::circuit_input_builder::ExpEvent; @@ -118,6 +118,7 @@ impl ExpTable { &self, layouter: &mut impl Layouter, block: &Block, + chunk: &Chunk, ) -> Result<(), Error> { layouter.assign_region( || "exponentiation table", @@ -150,7 +151,7 @@ impl ExpTable { } // Enable selector at all rows - let max_exp_steps = block.circuits_params.max_exp_steps; + let max_exp_steps = chunk.fixed_param.max_exp_steps; for offset in 0..max_exp_steps * OFFSET_INCREMENT { let is_step = if offset % OFFSET_INCREMENT == 0 { F::ONE diff --git a/zkevm-circuits/src/table/rw_table.rs b/zkevm-circuits/src/table/rw_table.rs index 6ade04d386..65ef5ea8dc 100644 --- a/zkevm-circuits/src/table/rw_table.rs +++ b/zkevm-circuits/src/table/rw_table.rs @@ -1,3 +1,22 @@ +use halo2_proofs::{ + self, + circuit::{AssignedCell, SimpleFloorPlanner}, + halo2curves::ff::{BatchInvert, WithSmallOrderMulGroup}, +}; + +use halo2_proofs::{ + halo2curves::{ + bn256::Fr, + group::{prime::PrimeCurveAffine, Curve}, + CurveAffine, + }, + plonk::{Advice, Assigned, Assignment, Challenge, Fixed, FloorPlanner, Instance, Selector}, + poly::{ + commitment::{Blind, CommitmentScheme, Params}, + EvaluationDomain, LagrangeCoeff, Polynomial, + }, +}; + use super::*; /// The RwTable shared between EVM Circuit and State Circuit, which contains @@ -65,28 +84,42 @@ impl LookupTable for RwTable { ] } } + 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: WordLoHi::new([meta.advice_column(), meta.advice_column()]), - value: WordLoHi::new([meta.advice_column(), meta.advice_column()]), - value_prev: WordLoHi::new([meta.advice_column(), meta.advice_column()]), - init_val: WordLoHi::new([meta.advice_column(), meta.advice_column()]), + rw_counter: meta.unblinded_advice_column(), + is_write: meta.unblinded_advice_column(), + tag: meta.unblinded_advice_column(), + id: meta.unblinded_advice_column(), + address: meta.unblinded_advice_column(), + field_tag: meta.unblinded_advice_column(), + storage_key: WordLoHi::new([ + meta.unblinded_advice_column(), + meta.unblinded_advice_column(), + ]), + value: WordLoHi::new([ + meta.unblinded_advice_column(), + meta.unblinded_advice_column(), + ]), + value_prev: WordLoHi::new([ + meta.unblinded_advice_column(), + meta.unblinded_advice_column(), + ]), + init_val: WordLoHi::new([ + meta.unblinded_advice_column(), + meta.unblinded_advice_column(), + ]), } } - fn assign( + pub(crate) fn assign( &self, region: &mut Region<'_, F>, offset: usize, row: &RwRow>, - ) -> Result<(), Error> { + ) -> Result>, Error> { + let mut assigned_cells = vec![]; for (column, value) in [ (self.rw_counter, row.rw_counter), (self.is_write, row.is_write), @@ -95,7 +128,12 @@ impl RwTable { (self.address, row.address), (self.field_tag, row.field_tag), ] { - region.assign_advice(|| "assign rw row on rw table", column, offset, || value)?; + assigned_cells.push(region.assign_advice( + || "assign rw row on rw table", + column, + offset, + || value, + )?); } for (column, value) in [ (self.storage_key, row.storage_key), @@ -103,10 +141,15 @@ impl RwTable { (self.value_prev, row.value_prev), (self.init_val, row.init_val), ] { - value.assign_advice(region, || "assign rw row on rw table", column, offset)?; + assigned_cells.extend( + value + .assign_advice(region, || "assign rw row on rw table", column, offset)? + .limbs + .clone(), + ); } - Ok(()) + Ok(assigned_cells) } /// Assign the `RwTable` from a `RwMap`, following the same @@ -116,10 +159,14 @@ impl RwTable { layouter: &mut impl Layouter, rws: &[Rw], n_rows: usize, + prev_chunk_last_rw: Option, ) -> Result<(), Error> { layouter.assign_region( || "rw table", - |mut region| self.load_with_region(&mut region, rws, n_rows), + |mut region| { + self.load_with_region(&mut region, rws, n_rows, prev_chunk_last_rw) + .map(|_| ()) + }, ) } @@ -128,11 +175,300 @@ impl RwTable { region: &mut Region<'_, F>, rws: &[Rw], n_rows: usize, - ) -> Result<(), Error> { - let (rows, _) = RwMap::table_assignments_prepad(rws, n_rows); + prev_chunk_last_rw: Option, + ) -> Result, Error> { + let (rows, _) = RwMap::table_assignments_padding(rws, n_rows, prev_chunk_last_rw); for (offset, row) in rows.iter().enumerate() { self.assign(region, offset, &row.table_assignment())?; } + Ok(rows) + } +} + +/// get rw table column commitment +/// implementation snippet from halo2 `create_proof` https://github.com/privacy-scaling-explorations/halo2/blob/9b33f9ce524dbb9133fc8b9638b2afd0571659a8/halo2_proofs/src/plonk/prover.rs#L37 +#[allow(unused)] +pub fn get_rwtable_cols_commitment( + degree: usize, + rws: &[Rw], + n_rows: usize, + params_prover: &Scheme::ParamsProver, +) -> Vec<::Curve> +where + ::Scalar: WithSmallOrderMulGroup<3> + Field, +{ + struct WitnessCollection { + advice: Vec, LagrangeCoeff>>, + _marker: std::marker::PhantomData, + } + + impl Assignment for WitnessCollection { + fn enter_region(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about regions in this context. + } + + fn exit_region(&mut self) { + // Do nothing; we don't care about regions in this context. + } + + fn enable_selector(&mut self, _: A, _: &Selector, _: usize) -> Result<(), Error> + where + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn annotate_column(&mut self, _annotation: A, _column: Column) + where + A: FnOnce() -> AR, + AR: Into, + { + // Do nothing + } + + fn query_instance( + &self, + _column: Column, + _row: usize, + ) -> Result, Error> { + Err(Error::BoundsFailure) + } + + fn assign_advice( + &mut self, + _: A, + column: Column, + row: usize, + to: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + to().into_field().map(|v| { + *self + .advice + .get_mut(column.index()) + .and_then(|v| v.get_mut(row)) + .ok_or(Error::BoundsFailure) + .unwrap() = v; + }); + Ok(()) + } + + fn assign_fixed( + &mut self, + _: A, + _: Column, + _: usize, + _: V, + ) -> Result<(), Error> + where + V: FnOnce() -> Value, + VR: Into>, + A: FnOnce() -> AR, + AR: Into, + { + // We only care about advice columns here + + Ok(()) + } + + fn copy( + &mut self, + _: Column, + _: usize, + _: Column, + _: usize, + ) -> Result<(), Error> { + // We only care about advice columns here + + Ok(()) + } + + fn fill_from_row( + &mut self, + _: Column, + _: usize, + _: Value>, + ) -> Result<(), Error> { + Ok(()) + } + + fn get_challenge(&self, _challenge: Challenge) -> Value { + Value::unknown() + } + + fn push_namespace(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self, _: Option) { + // Do nothing; we don't care about namespaces in this context. + } + } + + let rwtable_circuit = RwTableCircuit::new(rws, n_rows, None); + + let domain = EvaluationDomain::<::Scalar>::new( + degree as u32, + params_prover.k(), + ); + + let mut cs = ConstraintSystem::<::Scalar>::default(); + let rwtable_circuit_config = RwTableCircuit::configure(&mut cs); + let mut witness = WitnessCollection { + advice: vec![ + domain.empty_lagrange_assigned(); + ::Scalar>>::advice_columns( + &rwtable_circuit_config.rw_table + ) + .len() + ], + _marker: std::marker::PhantomData, + }; + + // Synthesize the circuit to obtain the witness and other information. + as Circuit>::FloorPlanner::synthesize( + &mut witness, + &rwtable_circuit, + rwtable_circuit_config, + cs.constants().clone(), + ) + .unwrap(); + + let len = witness.advice.len(); + let advice_values = + batch_invert_assigned::(domain, witness.advice.into_iter().collect()); + + // Compute commitments to advice column polynomials + let blinds = vec![Blind::default(); len]; + let advice_commitments_projective: Vec<_> = advice_values + .iter() + .zip(blinds.iter()) + .map(|(poly, blind)| params_prover.commit_lagrange(poly, *blind)) + .collect(); + let mut advice_commitments = + vec![Scheme::Curve::identity(); advice_commitments_projective.len()]; + + ::CurveExt::batch_normalize( + &advice_commitments_projective, + &mut advice_commitments, + ); + + advice_commitments +} + +struct RwTableCircuit<'a> { + rws: &'a [Rw], + n_rows: usize, + prev_chunk_last_rw: Option, +} + +impl<'a> RwTableCircuit<'a> { + #[allow(dead_code)] + pub(crate) fn new(rws: &'a [Rw], n_rows: usize, prev_chunk_last_rw: Option) -> Self { + Self { + rws, + n_rows, + prev_chunk_last_rw, + } + } +} + +#[derive(Clone)] +pub(crate) struct RwTableCircuitConfig { + pub rw_table: RwTable, +} + +impl RwTableCircuitConfig {} + +impl<'a, F: Field> Circuit for RwTableCircuit<'a> { + type Config = RwTableCircuitConfig; + + type FloorPlanner = SimpleFloorPlanner; + + type Params = (); + + fn without_witnesses(&self) -> Self { + todo!() + } + + fn configure(meta: &mut ConstraintSystem) -> Self::Config { + RwTableCircuitConfig { + rw_table: RwTable::construct(meta), + } + } + + fn synthesize( + &self, + config: Self::Config, + mut layouter: impl Layouter, + ) -> Result<(), Error> { + layouter.assign_region( + || "XXXX", + |mut region| { + config.rw_table.load_with_region( + &mut region, + self.rws, + self.n_rows, + self.prev_chunk_last_rw, + ) + }, + )?; Ok(()) } } + +// migrate from halo2 library +#[allow(unused)] +fn batch_invert_assigned>( + domain: EvaluationDomain, + assigned: Vec, LagrangeCoeff>>, +) -> Vec> { + let mut assigned_denominators: Vec<_> = assigned + .iter() + .map(|f| { + f.iter() + .map(|value| value.denominator()) + .collect::>() + }) + .collect(); + + assigned_denominators + .iter_mut() + .flat_map(|f| { + f.iter_mut() + // If the denominator is trivial, we can skip it, reducing the + // size of the batch inversion. + .filter_map(|d| d.as_mut()) + }) + .batch_invert(); + + assigned + .iter() + .zip(assigned_denominators) + .map(|(poly, inv_denoms)| { + let inv_denoms = inv_denoms.into_iter().map(|d| d.unwrap_or(F::ONE)); + domain.lagrange_from_vec( + poly.iter() + .zip(inv_denoms) + .map(|(a, inv_den)| a.numerator() * inv_den) + .collect(), + ) + }) + .collect() +} diff --git a/zkevm-circuits/src/test_util.rs b/zkevm-circuits/src/test_util.rs index 9a890b7364..a809631a7b 100644 --- a/zkevm-circuits/src/test_util.rs +++ b/zkevm-circuits/src/test_util.rs @@ -4,14 +4,14 @@ use crate::{ evm_circuit::{cached::EvmCircuitCached, EvmCircuit}, state_circuit::StateCircuit, util::SubCircuit, - witness::{Block, Rw}, + witness::{Block, Chunk, Rw}, }; use bus_mapping::{ circuit_input_builder::{FeatureConfig, FixedCParams}, mock::BlockData, }; use eth_types::geth_types::GethData; -use itertools::all; +use itertools::{all, Itertools}; use std::cmp; use thiserror::Error; @@ -78,15 +78,20 @@ const NUM_BLINDING_ROWS: usize = 64; /// .unwrap(); /// /// CircuitTestBuilder::new_from_test_ctx(ctx) -/// .block_modifier(Box::new(|block| block.circuits_params.max_evm_rows = (1 << 18) - 100)) -/// .run(); +/// .block_modifier(Box::new(|_block, chunk| { +/// chunk +/// .iter_mut() +/// .for_each(|chunk| chunk.fixed_param.max_evm_rows = (1 << 18) - 100); +/// })) +/// .run() /// ``` pub struct CircuitTestBuilder { test_ctx: Option>, circuits_params: Option, feature_config: Option, block: Option>, - block_modifiers: Vec)>>, + chunks: Option>>, + block_modifiers: Vec, &mut Vec>)>>, } impl CircuitTestBuilder { @@ -97,6 +102,7 @@ impl CircuitTestBuilder { circuits_params: None, feature_config: None, block: None, + chunks: None, block_modifiers: vec![], } } @@ -109,8 +115,8 @@ impl CircuitTestBuilder { /// Generates a CTBC from a [`Block`] passed with all the other fields /// set to [`Default`]. - pub fn new_from_block(block: Block) -> Self { - Self::empty().block(block) + pub fn new_from_block(block: Block, chunks: Vec>) -> Self { + Self::empty().set_block_chunk(block, chunks) } /// Allows to produce a [`TestContext`] which will serve as the generator of @@ -138,19 +144,23 @@ impl CircuitTestBuilder { self } - /// Allows to pass a [`Block`] already built to the constructor. - pub fn block(mut self, block: Block) -> Self { + /// Allows to pass a [`Block`], [`Chunk`] vectors already built to the constructor. + pub fn set_block_chunk(mut self, block: Block, chunks: Vec>) -> Self { self.block = Some(block); + self.chunks = Some(chunks); self } #[allow(clippy::type_complexity)] - /// Allows to provide modifier functions for the [`Block`] that will be + /// Allows to provide modifier functions for the [`Block, Chunk`] that will be /// generated within this builder. /// /// That removes the need in a lot of tests to build the block outside of /// the builder because they need to modify something particular. - pub fn block_modifier(mut self, modifier: Box)>) -> Self { + pub fn block_modifier( + mut self, + modifier: Box, &mut Vec>)>, + ) -> Self { self.block_modifiers.push(modifier); self } @@ -158,97 +168,257 @@ impl CircuitTestBuilder { impl CircuitTestBuilder { /// build block - pub fn build_block(&self) -> Result, CircuitTestError> { - if let Some(block) = &self.block { + pub fn build_block( + &self, + total_chunks: Option, + ) -> Result<(Block, Vec>), CircuitTestError> { + if let (Some(block), Some(chunks)) = (&self.block, &self.chunks) { // If a block is specified, no need to modify the block - return Ok(block.clone()); + return Ok((block.clone(), chunks.clone())); } let block = self .test_ctx .as_ref() .ok_or(CircuitTestError::NotEnoughAttributes)?; let block: GethData = block.clone().into(); - let builder = BlockData::new_from_geth_data(block.clone()) - .new_circuit_input_builder_with_feature(self.feature_config.unwrap_or_default()); - let builder = builder - .handle_block(&block.eth_block, &block.geth_traces) - .map_err(|err| CircuitTestError::CannotHandleBlock(err.to_string()))?; + let builder = match self.circuits_params { + Some(fixed_param) => { + if let Some(total_chunks) = total_chunks { + assert!( + fixed_param.total_chunks == total_chunks, + "Total chunks unmatched with fixed param" + ); + } + + BlockData::new_from_geth_data_with_params(block.clone(), fixed_param) + .new_circuit_input_builder_with_feature(self.feature_config.unwrap_or_default()) + .handle_block(&block.eth_block, &block.geth_traces) + .map_err(|err| CircuitTestError::CannotHandleBlock(err.to_string()))? + } + None => BlockData::new_from_geth_data_chunked(block.clone(), total_chunks.unwrap_or(1)) + .new_circuit_input_builder_with_feature(self.feature_config.unwrap_or_default()) + .handle_block(&block.eth_block, &block.geth_traces) + .map_err(|err| CircuitTestError::CannotHandleBlock(err.to_string()))?, + }; // Build a witness block from trace result. let mut block = crate::witness::block_convert(&builder) .map_err(|err| CircuitTestError::CannotConvertBlock(err.to_string()))?; + let mut chunks = crate::witness::chunk_convert(&block, &builder).unwrap(); for modifier_fn in &self.block_modifiers { - modifier_fn.as_ref()(&mut block); + modifier_fn.as_ref()(&mut block, &mut chunks); } - Ok(block) + Ok((block, chunks)) } - fn run_evm_circuit_test(&self, block: Block) -> Result<(), CircuitTestError> { - let k = block.get_test_degree(); + fn run_evm_circuit_test( + &self, + block: Block, + chunks: Vec>, + ) -> Result<(), CircuitTestError> { + if chunks.is_empty() { + return Err(CircuitTestError::SanityCheckChunks( + "empty chunks vector".to_string(), + )); + } - let (active_gate_rows, active_lookup_rows) = EvmCircuit::::get_active_rows(&block); + let k = block.get_test_degree(&chunks[0]); - // Mainnet EVM circuit constraints can be cached for test performance. - // No cache for EVM circuit with customized features - let prover = if block.feature_config.is_mainnet() { - let circuit = EvmCircuitCached::get_test_circuit_from_block(block); - MockProver::::run(k, &circuit, vec![]) - } else { - let circuit = EvmCircuit::get_test_circuit_from_block(block); - MockProver::::run(k, &circuit, vec![]) - }; + let (active_gate_rows, active_lookup_rows) = + EvmCircuit::::get_active_rows(&block, &chunks[0]); + + // check consistency between chunk + chunks + .iter() + .tuple_windows() + .find_map(|(prev_chunk, chunk)| { + // global consistent + if prev_chunk.permu_alpha != chunk.permu_alpha { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch challenge alpha".to_string(), + ))); + } + if prev_chunk.permu_gamma != chunk.permu_gamma { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch challenge gamma".to_string(), + ))); + } - let prover = prover.map_err(|err| CircuitTestError::SynthesisFailure { - circuit: Circuit::EVM, - reason: err, - })?; - - prover - .verify_at_rows( - active_gate_rows.iter().cloned(), - active_lookup_rows.iter().cloned(), - ) - .map_err(|err| CircuitTestError::VerificationFailed { - circuit: Circuit::EVM, - reasons: err, + if prev_chunk.by_address_rw_fingerprints.ending_row + != chunk.by_address_rw_fingerprints.prev_ending_row + { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch by_address_rw_fingerprints ending_row".to_string(), + ))); + } + if prev_chunk.by_address_rw_fingerprints.mul_acc + != chunk.by_address_rw_fingerprints.prev_mul_acc + { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch by_address_rw_fingerprints mul_acc".to_string(), + ))); + } + + if prev_chunk.chrono_rw_fingerprints.ending_row + != chunk.chrono_rw_fingerprints.prev_ending_row + { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch chrono_rw_fingerprints ending_row".to_string(), + ))); + } + if prev_chunk.chrono_rw_fingerprints.mul_acc + != chunk.chrono_rw_fingerprints.prev_mul_acc + { + return Some(Err(CircuitTestError::SanityCheckChunks( + "mismatch chrono_rw_fingerprints mul_acc".to_string(), + ))); + } + None + }) + .unwrap_or_else(|| Ok(()))?; + + // check last chunk fingerprints + chunks + .last() + .map(|last_chunk| { + if last_chunk.by_address_rw_fingerprints.mul_acc + != last_chunk.chrono_rw_fingerprints.mul_acc + { + Err(CircuitTestError::SanityCheckChunks( + "mismatch last rw_fingerprint mul_acc".to_string(), + )) + } else { + Ok(()) + } }) + .unwrap_or_else(|| Ok(()))?; + + // stop on first chunk validation error + chunks + .into_iter() + .enumerate() + // terminate on first error + .find_map(|(i, chunk)| { + // Mainnet EVM circuit constraints can be cached for test performance. + // No cache for EVM circuit with customized features + let prover = if block.feature_config.is_mainnet() { + let circuit = + EvmCircuitCached::get_test_circuit_from_block(block.clone(), chunk); + let instance = circuit.instance(); + MockProver::::run(k, &circuit, instance) + } else { + let circuit = EvmCircuit::get_test_circuit_from_block(block.clone(), chunk); + let instance = circuit.instance(); + MockProver::::run(k, &circuit, instance) + }; + + if let Err(err) = prover { + return Some(Err(CircuitTestError::SynthesisFailure { + circuit: Circuit::EVM, + reason: err, + })); + } + + let prover = prover.unwrap(); + + let res = prover + .verify_at_rows( + active_gate_rows.iter().cloned(), + active_lookup_rows.iter().cloned(), + ) + .map_err(|err| CircuitTestError::VerificationFailed { + circuit: Circuit::EVM, + reasons: err, + }); + if res.is_err() { + println!("failed on chunk index {}", i); + Some(res) + } else { + None + } + }) + .unwrap_or_else(|| Ok(())) } // TODO: use randomness as one of the circuit public input, since randomness in // state circuit and evm circuit must be same - fn run_state_circuit_test(&self, block: Block) -> Result<(), CircuitTestError> { - let rows_needed = StateCircuit::::min_num_rows_block(&block).1; + fn run_state_circuit_test( + &self, + block: Block, + chunks: Vec>, + ) -> Result<(), CircuitTestError> { + // sanity check + assert!(!chunks.is_empty()); + chunks.iter().tuple_windows().for_each(|(chunk1, chunk2)| { + let (rows_needed_1, rows_needed_2) = ( + StateCircuit::::min_num_rows_block(&block, chunk1).1, + StateCircuit::::min_num_rows_block(&block, chunk2).1, + ); + assert!(rows_needed_1 == rows_needed_2); + + assert!(chunk1.fixed_param == chunk2.fixed_param); + }); + + let rows_needed = StateCircuit::::min_num_rows_block(&block, &chunks[0]).1; let k = cmp::max(log2_ceil(rows_needed + NUM_BLINDING_ROWS), 18); - let max_rws = block.circuits_params.max_rws; - let state_circuit = StateCircuit::::new(block.rws, max_rws); - let instance = state_circuit.instance(); - let prover = MockProver::::run(k, &state_circuit, instance).map_err(|err| { - CircuitTestError::SynthesisFailure { - circuit: Circuit::State, - reason: err, - } - })?; - // Skip verification of Start rows to accelerate testing - let non_start_rows_len = state_circuit - .rows + + chunks .iter() - .filter(|rw| !matches!(rw, Rw::Start { .. })) - .count(); - let rows = max_rws - non_start_rows_len..max_rws; - prover.verify_at_rows(rows.clone(), rows).map_err(|err| { - CircuitTestError::VerificationFailed { - circuit: Circuit::EVM, - reasons: err, - } - }) + // terminate on first error + .find_map(|chunk| { + let state_circuit = StateCircuit::::new(chunk); + let instance = state_circuit.instance(); + let prover = MockProver::::run(k, &state_circuit, instance).map_err(|err| { + CircuitTestError::SynthesisFailure { + circuit: Circuit::State, + reason: err, + } + }); + if let Err(err) = prover { + return Some(Err(err)); + } + let prover = prover.unwrap(); + // Skip verification of Start and Padding rows accelerate testing + let non_padding_rows_len = state_circuit + .rows + .iter() + .filter(|rw| { + !matches!(rw, Rw::Start { .. }) && !matches!(rw, Rw::Padding { .. }) + }) + .count(); + let rows = 1..1 + non_padding_rows_len; + let result: Result<(), CircuitTestError> = prover + .verify_at_rows(rows.clone(), rows) + .map_err(|err| CircuitTestError::VerificationFailed { + circuit: Circuit::EVM, + reasons: err, + }); + if result.is_ok() { + None + } else { + Some(result) + } + }) + .unwrap_or_else(|| Ok(())) } + /// Triggers the `CircuitTestBuilder` to convert the [`TestContext`] if any, /// into a [`Block`] and apply the default or provided block_modifiers or /// circuit checks to the provers generated for the State and EVM circuits. pub fn run_with_result(self) -> Result<(), CircuitTestError> { - let block = self.build_block()?; + self.run_multiple_chunks_with_result(None) + } + + /// Triggers the `CircuitTestBuilder` to convert the [`TestContext`] if any, + /// into a [`Block`] and apply the default or provided block_modifiers or + /// circuit checks to the provers generated for the State and EVM circuits. + pub fn run_multiple_chunks_with_result( + self, + total_chunks: Option, + ) -> Result<(), CircuitTestError> { + let (block, chunks) = self.build_block(total_chunks)?; - self.run_evm_circuit_test(block.clone())?; - self.run_state_circuit_test(block) + self.run_evm_circuit_test(block.clone(), chunks.clone())?; + self.run_state_circuit_test(block, chunks) } /// Convenient method to run in test cases that error handling is not required. @@ -278,6 +448,9 @@ pub enum CircuitTestError { /// Something wrong in the block_convert #[error("CannotConvertBlock({0})")] CannotConvertBlock(String), + /// Something worng in the chunk_convert + #[error("SanityCheckChunks({0})")] + SanityCheckChunks(String), /// Problem constructing MockProver #[error("SynthesisFailure({circuit:?}, reason: {reason:?})")] SynthesisFailure { diff --git a/zkevm-circuits/src/tx_circuit.rs b/zkevm-circuits/src/tx_circuit.rs index 4ea61edfcc..3fae849fcb 100644 --- a/zkevm-circuits/src/tx_circuit.rs +++ b/zkevm-circuits/src/tx_circuit.rs @@ -16,7 +16,7 @@ pub use dev::TxCircuit as TestTxCircuit; use crate::{ table::{KeccakTable, TxFieldTag, TxTable}, util::{word::WordLoHi, Challenges, SubCircuit, SubCircuitConfig}, - witness, + witness::{self, Chunk}, }; use eth_types::{geth_types::Transaction, sign_types::SignData, Field}; use halo2_proofs::{ @@ -309,26 +309,23 @@ impl SubCircuit for TxCircuit { 6 } - fn new_from_block(block: &witness::Block) -> Self { + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self { Self::new( - block.circuits_params.max_txs, - block.circuits_params.max_calldata, + chunk.fixed_param.max_txs, + chunk.fixed_param.max_calldata, block.context.chain_id.as_u64(), block.txs.iter().map(|tx| tx.deref().clone()).collect_vec(), ) } /// Return the minimum number of rows required to prove the block - fn min_num_rows_block(block: &witness::Block) -> (usize, usize) { + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize) { ( Self::min_num_rows( block.txs.len(), block.txs.iter().map(|tx| tx.call_data.len()).sum(), ), - Self::min_num_rows( - block.circuits_params.max_txs, - block.circuits_params.max_calldata, - ), + Self::min_num_rows(chunk.fixed_param.max_txs, chunk.fixed_param.max_calldata), ) } diff --git a/zkevm-circuits/src/util.rs b/zkevm-circuits/src/util.rs index b0d8d0449f..cd614d42ca 100644 --- a/zkevm-circuits/src/util.rs +++ b/zkevm-circuits/src/util.rs @@ -10,7 +10,10 @@ use halo2_proofs::{ }, }; -use crate::{table::TxLogFieldTag, witness}; +use crate::{ + table::TxLogFieldTag, + witness::{self, Chunk}, +}; use eth_types::{keccak256, Field, ToAddress, Word}; pub use ethers_core::types::{Address, U256}; pub use gadgets::util::Expr; @@ -20,6 +23,9 @@ pub mod cell_manager; /// Cell Placement strategies pub mod cell_placement_strategy; +/// Chunk context config +pub mod chunk_ctx; + /// Steal the expression from gate pub fn query_expression( meta: &mut ConstraintSystem, @@ -152,7 +158,7 @@ pub trait SubCircuit { fn unusable_rows() -> usize; /// Create a new SubCircuit from a witness Block - fn new_from_block(block: &witness::Block) -> Self; + fn new_from_block(block: &witness::Block, chunk: &Chunk) -> Self; /// Returns the instance columns required for this circuit. fn instance(&self) -> Vec> { @@ -171,7 +177,7 @@ pub trait SubCircuit { /// Return the minimum number of rows required to prove the block. /// Row numbers without/with padding are both returned. - fn min_num_rows_block(block: &witness::Block) -> (usize, usize); + fn min_num_rows_block(block: &witness::Block, chunk: &Chunk) -> (usize, usize); } /// SubCircuit configuration @@ -204,6 +210,14 @@ pub(crate) fn get_push_size(byte: u8) -> u64 { } } +pub(crate) fn unwrap_value(value: Value) -> T { + let mut inner = None; + _ = value.map(|v| { + inner = Some(v); + }); + inner.unwrap() +} + #[cfg(test)] use halo2_proofs::plonk::Circuit; diff --git a/zkevm-circuits/src/util/chunk_ctx.rs b/zkevm-circuits/src/util/chunk_ctx.rs new file mode 100644 index 0000000000..2f5a223ff6 --- /dev/null +++ b/zkevm-circuits/src/util/chunk_ctx.rs @@ -0,0 +1,226 @@ +use bus_mapping::circuit_input_builder::ChunkContext; +use gadgets::{ + is_zero::{IsZeroChip, IsZeroConfig, IsZeroInstruction}, + less_than::{LtChip, LtConfig}, + util::Expr, +}; +use halo2_proofs::{ + circuit::{Layouter, Value}, + plonk::{Advice, Column, ConstraintSystem, Error, Expression, Instance, Selector}, + poly::Rotation, +}; + +use crate::{ + evm_circuit::util::rlc, + table::{ + chunk_ctx_table::{ChunkCtxFieldTag, ChunkCtxTable}, + LookupTable, + }, +}; +use eth_types::Field; +use gadgets::less_than::LtInstruction; + +use super::Challenges; + +/// chunk context config +#[derive(Clone, Debug)] +pub struct ChunkContextConfig { + chunk_index: Column, + chunk_index_next: Column, + total_chunks: Column, + q_chunk_context: Selector, + + /// is_first_chunk config + pub is_first_chunk: IsZeroConfig, + /// is_last_chunk config + pub is_last_chunk: IsZeroConfig, + + /// ChunkCtxTable + pub chunk_ctx_table: ChunkCtxTable, + /// instance column for chunk context + pub pi_chunk_ctx: Column, + + /// Lt chip to check: chunk_index < total_chunks. + /// Assume `total_chunks` < 2**8 = 256 + pub is_chunk_index_lt_total_chunks: LtConfig, +} + +impl ChunkContextConfig { + /// new a chunk context config + pub fn new(meta: &mut ConstraintSystem, challenges: &Challenges>) -> Self { + let q_chunk_context = meta.complex_selector(); + let chunk_index = meta.advice_column(); + let chunk_index_inv = meta.advice_column(); + let chunk_index_next = meta.advice_column(); + let chunk_diff = meta.advice_column(); + let total_chunks = meta.advice_column(); + + let pi_chunk_ctx = meta.instance_column(); + meta.enable_equality(pi_chunk_ctx); + + let chunk_ctx_table = ChunkCtxTable::construct(meta); + chunk_ctx_table.annotate_columns(meta); + + [ + (ChunkCtxFieldTag::CurrentChunkIndex.expr(), chunk_index), + (ChunkCtxFieldTag::NextChunkIndex.expr(), chunk_index_next), + (ChunkCtxFieldTag::TotalChunks.expr(), total_chunks), + ] + .iter() + .for_each(|(tag_expr, value_col)| { + meta.lookup_any("chunk context lookup", |meta| { + let q_chunk_context = meta.query_selector(q_chunk_context); + let value_col_expr = meta.query_advice(*value_col, Rotation::cur()); + + vec![( + q_chunk_context + * rlc::expr( + &[tag_expr.clone(), value_col_expr], + challenges.lookup_input(), + ), + rlc::expr( + &chunk_ctx_table.table_exprs(meta), + challenges.lookup_input(), + ), + )] + }); + }); + + // assume max total_chunks < 2^8 + let is_chunk_index_lt_total_chunks = LtChip::<_, 1>::configure( + meta, + |meta| meta.query_selector(q_chunk_context), + |meta| meta.query_advice(chunk_index, Rotation::cur()), + |meta| meta.query_advice(total_chunks, Rotation::cur()), + ); + + meta.create_gate("chunk_index < total_chunks", |meta| { + [meta.query_selector(q_chunk_context) + * (1.expr() - is_chunk_index_lt_total_chunks.is_lt(meta, None))] + }); + + let is_first_chunk = IsZeroChip::configure( + meta, + |meta| meta.query_selector(q_chunk_context), + |meta| meta.query_advice(chunk_index, Rotation::cur()), + chunk_index_inv, + ); + + let is_last_chunk = IsZeroChip::configure( + meta, + |meta| meta.query_selector(q_chunk_context), + |meta| { + let chunk_index = meta.query_advice(chunk_index, Rotation::cur()); + let total_chunks = meta.query_advice(total_chunks, Rotation::cur()); + + total_chunks - chunk_index - 1.expr() + }, + chunk_diff, + ); + + Self { + q_chunk_context, + chunk_index, + chunk_index_next, + total_chunks, + is_first_chunk, + is_last_chunk, + chunk_ctx_table, + pi_chunk_ctx, + is_chunk_index_lt_total_chunks, + } + } + + /// assign chunk context + pub fn assign_chunk_context( + &self, + layouter: &mut impl Layouter, + chunk_context: &ChunkContext, + max_offset_index: usize, + ) -> Result<(), Error> { + let is_chunk_index_lt_total_chunks = LtChip::construct(self.is_chunk_index_lt_total_chunks); + is_chunk_index_lt_total_chunks.load(layouter)?; + + let ( + chunk_index_cell, + chunk_index_next_cell, + total_chunk_cell, + initial_rwc_cell, + end_rwc_cell, + ) = self.chunk_ctx_table.load(layouter, chunk_context)?; + + let is_first_chunk = IsZeroChip::construct(self.is_first_chunk.clone()); + let is_last_chunk = IsZeroChip::construct(self.is_last_chunk.clone()); + layouter.assign_region( + || "chunk context", + |mut region| { + region.name_column(|| "chunk_index", self.chunk_index); + region.name_column(|| "chunk_index_next", self.chunk_index_next); + region.name_column(|| "total_chunks", self.total_chunks); + region.name_column(|| "pi_chunk_ctx", self.pi_chunk_ctx); + self.is_first_chunk + .annotate_columns_in_region(&mut region, "is_first_chunk"); + self.is_last_chunk + .annotate_columns_in_region(&mut region, "is_last_chunk"); + self.chunk_ctx_table.annotate_columns_in_region(&mut region); + + for offset in 0..max_offset_index + 1 { + self.q_chunk_context.enable(&mut region, offset)?; + + region.assign_advice( + || "chunk_index", + self.chunk_index, + offset, + || Value::known(F::from(chunk_context.idx as u64)), + )?; + + region.assign_advice( + || "chunk_index_next", + self.chunk_index_next, + offset, + || Value::known(F::from(chunk_context.idx as u64 + 1u64)), + )?; + + region.assign_advice( + || "total_chunks", + self.total_chunks, + offset, + || Value::known(F::from(chunk_context.total_chunks as u64)), + )?; + + is_first_chunk.assign( + &mut region, + offset, + Value::known(F::from(chunk_context.idx as u64)), + )?; + is_last_chunk.assign( + &mut region, + offset, + Value::known(F::from( + (chunk_context.total_chunks - chunk_context.idx - 1) as u64, + )), + )?; + is_chunk_index_lt_total_chunks.assign( + &mut region, + offset, + Value::known(F::from(chunk_context.idx as u64)), + Value::known(F::from(chunk_context.total_chunks as u64)), + )?; + } + Ok(()) + }, + )?; + + [ + chunk_index_cell, + chunk_index_next_cell, + total_chunk_cell, + initial_rwc_cell, + end_rwc_cell, + ] + .iter() + .enumerate() + .try_for_each(|(i, cell)| layouter.constrain_instance(cell.cell(), self.pi_chunk_ctx, i))?; + Ok(()) + } +} diff --git a/zkevm-circuits/src/witness.rs b/zkevm-circuits/src/witness.rs index 97b3a44cb1..ab7fdda89a 100644 --- a/zkevm-circuits/src/witness.rs +++ b/zkevm-circuits/src/witness.rs @@ -3,9 +3,12 @@ //! used to generate witnesses for circuits. mod block; +/// +pub mod chunk; pub use block::{block_convert, Block, BlockContext}; +pub use chunk::{chunk_convert, Chunk}; mod mpt; pub use mpt::{MptUpdate, MptUpdateRow, MptUpdates}; -mod rw; +pub mod rw; pub use bus_mapping::circuit_input_builder::{Call, ExecStep, Transaction, Withdrawal}; pub use rw::{Rw, RwMap, RwRow}; diff --git a/zkevm-circuits/src/witness/block.rs b/zkevm-circuits/src/witness/block.rs index 2718c795e9..6d3c4417fb 100644 --- a/zkevm-circuits/src/witness/block.rs +++ b/zkevm-circuits/src/witness/block.rs @@ -1,10 +1,16 @@ -use super::{ExecStep, Rw, RwMap, Transaction}; +use std::collections::BTreeMap; + +use super::{ + rw::{RwFingerprints, ToVec}, + ExecStep, Rw, RwMap, Transaction, +}; use crate::{ evm_circuit::{detect_fixed_table_tags, EvmCircuit}, exp_circuit::param::OFFSET_INCREMENT, instance::public_data_convert, table::BlockContextFieldTag, - util::{log2_ceil, word::WordLoHi, SubCircuit}, + util::{log2_ceil, unwrap_value, word::WordLoHi, SubCircuit}, + witness::Chunk, }; use bus_mapping::{ circuit_input_builder::{ @@ -14,25 +20,27 @@ use bus_mapping::{ Error, }; use eth_types::{sign_types::SignData, Address, Field, ToScalar, Word, H256}; + +use gadgets::permutation::get_permutation_fingerprints; use halo2_proofs::circuit::Value; use itertools::Itertools; // TODO: Remove fields that are duplicated in`eth_block` -/// Block is the struct used by all circuits, which contains all the needed -/// data for witness generation. +/// [`Block`] is the struct used by all circuits, which contains blockwise +/// data for witness generation. Used with [`Chunk`] for the i-th chunck witness. #[derive(Debug, Clone, Default)] pub struct Block { /// The randomness for random linear combination pub randomness: F, /// Transactions in the block pub txs: Vec, - /// EndBlock step that is repeated after the last transaction and before + /// Padding step that is repeated after the last transaction and before /// reaching the last EVM row. - pub end_block_not_last: ExecStep, - /// Last EndBlock step that appears in the last EVM row. - pub end_block_last: ExecStep, + pub end_block: ExecStep, /// Read write events in the RwTable pub rws: RwMap, + /// Read write events in the RwTable, sorted by address + pub by_address_rws: Vec, /// Bytecode used in the block pub bytecodes: CodeDB, /// The block context @@ -57,6 +65,8 @@ pub struct Block { pub precompile_events: PrecompileEvents, /// Original Block from geth pub eth_block: eth_types::Block, + /// rw_table padding meta data + pub rw_padding_meta: BTreeMap, } impl Block { @@ -129,9 +139,9 @@ 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`. - pub fn get_test_degree(&self) -> u32 { + pub fn get_test_degree(&self, chunk: &Chunk) -> u32 { let num_rows_required_for_execution_steps: usize = - EvmCircuit::::get_num_rows_required(self); + EvmCircuit::::get_num_rows_required(self, chunk); let num_rows_required_for_rw_table: usize = self.circuits_params.max_rws; let num_rows_required_for_fixed_table: usize = detect_fixed_table_tags(self) .iter() @@ -300,15 +310,35 @@ pub fn block_convert( let block = &builder.block; let code_db = &builder.code_db; let rws = RwMap::from(&block.container); + let by_address_rws = rws.table_assignments(false); rws.check_value(); + + // get padding statistics data via BtreeMap + // TODO we can implement it in more efficient version via range sum + let rw_padding_meta = builder + .chunks + .iter() + .fold(BTreeMap::new(), |mut map, chunk| { + assert!( + chunk.ctx.rwc.0.saturating_sub(1) <= builder.circuits_params.max_rws, + "max_rws size {} must larger than chunk rws size {}", + builder.circuits_params.max_rws, + chunk.ctx.rwc.0.saturating_sub(1), + ); + // [chunk.ctx.rwc.0, builder.circuits_params.max_rws) + (chunk.ctx.rwc.0..builder.circuits_params.max_rws).for_each(|padding_rw_counter| { + *map.entry(padding_rw_counter).or_insert(0) += 1; + }); + map + }); + let mut block = Block { // randomness: F::from(0x100), // Special value to reveal elements after RLC randomness: F::from(0xcafeu64), context: block.into(), rws, + by_address_rws, txs: block.txs().to_vec(), - 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.clone(), copy_events: block.copy_events.clone(), exp_events: block.exp_events.clone(), @@ -320,8 +350,13 @@ pub fn block_convert( keccak_inputs: circuit_input_builder::keccak_inputs(block, code_db)?, precompile_events: block.precompile_events.clone(), eth_block: block.eth_block.clone(), + end_block: block.end_block.clone(), + rw_padding_meta, }; let public_data = public_data_convert(&block); + + // We can use params from block + // because max_txs and max_calldata are independent from Chunk let rpi_bytes = public_data.get_pi_bytes( block.circuits_params.max_txs, block.circuits_params.max_withdrawals, @@ -329,5 +364,35 @@ pub fn block_convert( ); // PI Circuit block.keccak_inputs.extend_from_slice(&[rpi_bytes]); + Ok(block) } + +#[allow(dead_code)] +fn get_rwtable_fingerprints( + alpha: F, + gamma: F, + prev_continuous_fingerprint: F, + rows: &Vec, +) -> RwFingerprints { + let x = rows.to2dvec(); + let fingerprints = get_permutation_fingerprints( + &x, + Value::known(alpha), + Value::known(gamma), + Value::known(prev_continuous_fingerprint), + ); + + fingerprints + .first() + .zip(fingerprints.last()) + .map(|((first_acc, first_row), (last_acc, last_row))| { + RwFingerprints::new( + unwrap_value(*first_row), + unwrap_value(*last_row), + unwrap_value(*first_acc), + unwrap_value(*last_acc), + ) + }) + .unwrap_or_default() +} diff --git a/zkevm-circuits/src/witness/chunk.rs b/zkevm-circuits/src/witness/chunk.rs new file mode 100755 index 0000000000..92097737a1 --- /dev/null +++ b/zkevm-circuits/src/witness/chunk.rs @@ -0,0 +1,317 @@ +use std::iter; + +/// +use super::{ + rw::{RwFingerprints, ToVec}, + Block, ExecStep, Rw, RwMap, RwRow, +}; +use crate::util::unwrap_value; +use bus_mapping::{ + circuit_input_builder::{self, Call, ChunkContext, FixedCParams}, + operation::Target, + Error, +}; +use eth_types::Field; +use gadgets::permutation::get_permutation_fingerprints; +use halo2_proofs::circuit::Value; +use itertools::Itertools; + +/// [`Chunk`]` is the struct used by all circuits, which contains chunkwise +/// data for witness generation. Used with [`Block`] for blockwise witness. +#[derive(Debug, Clone)] +pub struct Chunk { + /// BeginChunk step to propagate State + pub begin_chunk: Option, + /// EndChunk step that appears in the last EVM row for all the chunks other than the last. + pub end_chunk: Option, + /// Padding step that is repeated before max_rws is reached + pub padding: Option, + /// Chunk context + pub chunk_context: ChunkContext, + /// Read write events in the chronological sorted RwTable + pub chrono_rws: RwMap, + /// Read write events in the by address sorted RwTable + pub by_address_rws: RwMap, + /// Permutation challenge alpha + pub permu_alpha: F, + /// Permutation challenge gamma + pub permu_gamma: F, + + /// Current rw_table permutation fingerprint + pub by_address_rw_fingerprints: RwFingerprints, + /// Current chronological rw_table permutation fingerprint + pub chrono_rw_fingerprints: RwFingerprints, + + /// Fixed param for the chunk + pub fixed_param: FixedCParams, + + /// The last call of previous chunk if any, used for assigning continuation + pub prev_last_call: Option, + /// + pub prev_chunk_last_chrono_rw: Option, + /// + pub prev_chunk_last_by_address_rw: Option, +} + +impl Default for Chunk { + fn default() -> Self { + // One fixed param chunk with randomness = 1 + // RwFingerprints rw acc starts with 0 and fingerprints = 1 + Self { + begin_chunk: None, + end_chunk: None, + padding: None, + chunk_context: ChunkContext::default(), + chrono_rws: RwMap::default(), + by_address_rws: RwMap::default(), + permu_alpha: F::from(1), + permu_gamma: F::from(1), + by_address_rw_fingerprints: RwFingerprints::default(), + chrono_rw_fingerprints: RwFingerprints::default(), + fixed_param: FixedCParams::default(), + prev_last_call: None, + prev_chunk_last_chrono_rw: None, + prev_chunk_last_by_address_rw: None, + } + } +} + +/// Convert the idx-th chunk struct in bus-mapping to a witness chunk used in circuits +pub fn chunk_convert( + block: &Block, + builder: &circuit_input_builder::CircuitInputBuilder, +) -> Result>, Error> { + let (by_address_rws, padding_meta) = (&block.by_address_rws, &block.rw_padding_meta); + + // Todo: poseidon hash to compute alpha/gamma + let alpha = F::from(103); + let gamma = F::from(101); + + let mut chunks: Vec> = Vec::with_capacity(builder.chunks.len()); + for (i, (prev_chunk, chunk)) in iter::once(None) // left append `None` to make iteration easier + .chain(builder.chunks.iter().map(Some)) + .tuple_windows() + .enumerate() + { + let chunk = chunk.unwrap(); // current chunk always there + let prev_chunk_last_chrono_rw = prev_chunk.map(|prev_chunk| { + assert!(builder.circuits_params.max_rws > 0); + let chunk_inner_rwc = prev_chunk.ctx.rwc.0; + if chunk_inner_rwc.saturating_sub(1) == builder.circuits_params.max_rws { + // if prev chunk rws are full, then get the last rwc + RwMap::get_rw(&builder.block.container, prev_chunk.ctx.end_rwc - 1) + .expect("Rw does not exist") + } else { + // last is the padding row + Rw::Padding { + rw_counter: builder.circuits_params.max_rws - 1, + } + } + }); + + // Get the rws in the i-th chunk + let chrono_rws = { + let mut chrono_rws = RwMap::from(&builder.block.container); + // remove paading here since it will be attached later + if let Some(padding_vec) = chrono_rws.0.get_mut(&Target::Padding) { + padding_vec.clear() + } + chrono_rws.take_rw_counter_range(chunk.ctx.initial_rwc, chunk.ctx.end_rwc) + }; + + let (prev_chunk_last_by_address_rw, by_address_rws) = { + // by_address_rws + let start = chunk.ctx.idx * builder.circuits_params.max_rws; + let size = builder.circuits_params.max_rws; + // by_address_rws[start..end].to_vec() + + let skipped = by_address_rws + .iter() + // remove paading here since it will be attached later + .filter(|rw| rw.tag() != Target::Padding) + .cloned() // TODO avoid clone here + .chain(padding_meta.iter().flat_map(|(k, v)| { + vec![ + Rw::Padding { rw_counter: *k }; + >::try_into(*v).unwrap() + ] + })); + // there is no previous chunk + if start == 0 { + (None, RwMap::from(skipped.take(size).collect::>())) + } else { + // here we got `chunk.ctx.idx - 1` because each chunk first row are propagated from + // prev chunk. giving idx>0 th chunk, there will be (idx-1) placeholders. + let mut skipped = skipped.skip(start - 1 - (chunk.ctx.idx - 1)); + let prev_chunk_last_by_address_rw = skipped.next(); + ( + prev_chunk_last_by_address_rw, + RwMap::from(skipped.take(size).collect::>()), + ) + } + }; + + // Compute cur fingerprints from last fingerprints and current Rw rows + let by_address_rw_fingerprints = get_permutation_fingerprint_of_rwmap( + &by_address_rws, + chunk.fixed_param.max_rws, + alpha, + gamma, + if i == 0 { + F::from(1) + } else { + chunks[i - 1].by_address_rw_fingerprints.mul_acc + }, + false, + prev_chunk_last_by_address_rw, + ); + + let chrono_rw_fingerprints = get_permutation_fingerprint_of_rwmap( + &chrono_rws, + chunk.fixed_param.max_rws, + alpha, + gamma, + if i == 0 { + F::from(1) + } else { + chunks[i - 1].chrono_rw_fingerprints.mul_acc + }, + true, + prev_chunk_last_chrono_rw, + ); + chunks.push(Chunk { + permu_alpha: alpha, + permu_gamma: gamma, + by_address_rw_fingerprints, + chrono_rw_fingerprints, + begin_chunk: chunk.begin_chunk.clone(), + end_chunk: chunk.end_chunk.clone(), + padding: chunk.padding.clone(), + chunk_context: chunk.ctx.clone(), + chrono_rws, + by_address_rws, + fixed_param: chunk.fixed_param, + prev_last_call: chunk.prev_last_call.clone(), + prev_chunk_last_chrono_rw, + prev_chunk_last_by_address_rw, + }); + } + + if log::log_enabled!(log::Level::Debug) { + chunks + .iter() + .enumerate() + .for_each(|(i, chunk)| log::debug!("{}th chunk context {:?}", i, chunk,)); + } + + Ok(chunks) +} + +/// +pub fn get_rwtable_fingerprints( + alpha: F, + gamma: F, + prev_continuous_fingerprint: F, + rows: &Vec, +) -> RwFingerprints { + let x = rows.to2dvec(); + let fingerprints = get_permutation_fingerprints( + &x, + Value::known(alpha), + Value::known(gamma), + Value::known(prev_continuous_fingerprint), + ); + + fingerprints + .first() + .zip(fingerprints.last()) + .map(|((first_acc, first_row), (last_acc, last_row))| { + RwFingerprints::new( + unwrap_value(*first_row), + unwrap_value(*last_row), + unwrap_value(*first_acc), + unwrap_value(*last_acc), + ) + }) + .unwrap_or_default() +} + +/// +pub fn get_permutation_fingerprint_of_rwmap( + rwmap: &RwMap, + max_row: usize, + alpha: F, + gamma: F, + prev_continuous_fingerprint: F, + is_chrono: bool, + padding_start_rw: Option, +) -> RwFingerprints { + get_permutation_fingerprint_of_rwvec( + &rwmap.table_assignments(is_chrono), + max_row, + alpha, + gamma, + prev_continuous_fingerprint, + padding_start_rw, + ) +} + +/// +pub fn get_permutation_fingerprint_of_rwvec( + rwvec: &[Rw], + max_row: usize, + alpha: F, + gamma: F, + prev_continuous_fingerprint: F, + padding_start_rw: Option, +) -> RwFingerprints { + get_permutation_fingerprint_of_rwrowvec( + &rwvec + .iter() + .map(|row| row.table_assignment()) + .collect::>>>(), + max_row, + alpha, + gamma, + prev_continuous_fingerprint, + padding_start_rw.map(|r| r.table_assignment()), + ) +} + +/// +pub fn get_permutation_fingerprint_of_rwrowvec( + rwrowvec: &[RwRow>], + max_row: usize, + alpha: F, + gamma: F, + prev_continuous_fingerprint: F, + padding_start_rwrow: Option>>, +) -> RwFingerprints { + let (rows, _) = RwRow::padding(rwrowvec, max_row, padding_start_rwrow); + let x = rows.to2dvec(); + let fingerprints = get_permutation_fingerprints( + &x, + Value::known(alpha), + Value::known(gamma), + Value::known(prev_continuous_fingerprint), + ); + + fingerprints + .first() + .zip(fingerprints.last()) + .map(|((first_acc, first_row), (last_acc, last_row))| { + RwFingerprints::new( + unwrap_value(*first_row), + unwrap_value(*last_row), + unwrap_value(*first_acc), + unwrap_value(*last_acc), + ) + }) + .unwrap_or_default() +} + +/// +pub fn get_permutation_randomness() -> (F, F) { + // Todo + (F::from(1), F::from(1)) +} diff --git a/zkevm-circuits/src/witness/rw.rs b/zkevm-circuits/src/witness/rw.rs index 950a8329b1..a87f819d9a 100644 --- a/zkevm-circuits/src/witness/rw.rs +++ b/zkevm-circuits/src/witness/rw.rs @@ -1,21 +1,30 @@ //! The Read-Write table related structs -use std::collections::HashMap; +use std::{ + collections::{HashMap, HashSet}, + iter, +}; use bus_mapping::{ exec_trace::OperationRef, - operation::{self, AccountField, CallContextField, Target, TxLogField, TxReceiptField}, + operation::{ + self, AccountField, CallContextField, StepStateField, Target, TxLogField, TxReceiptField, + }, }; use eth_types::{Address, Field, ToAddress, ToScalar, Word, U256}; use halo2_proofs::circuit::Value; use itertools::Itertools; use crate::{ - table::{AccountFieldTag, CallContextFieldTag, TxLogFieldTag, TxReceiptFieldTag}, - util::{build_tx_log_address, word::WordLoHi}, + table::{ + AccountFieldTag, CallContextFieldTag, StepStateFieldTag, TxLogFieldTag, TxReceiptFieldTag, + }, + util::{build_tx_log_address, unwrap_value, word::WordLoHi}, }; use super::MptUpdates; +const U64_BYTES: usize = u64::BITS as usize / 8usize; + /// Rw container for a witness block #[derive(Debug, Default, Clone)] pub struct RwMap(pub HashMap>); @@ -37,25 +46,25 @@ impl std::ops::Index for RwMap { } impl RwMap { - /// Check rw_counter is continuous and starting from 1 + /// Check rw_counter is continuous pub fn check_rw_counter_sanity(&self) { - for (idx, rw_counter) in self + for (rw_counter_prev, rw_counter_cur) in self .0 .iter() - .filter(|(tag, _rs)| !matches!(tag, Target::Start)) + .filter(|(tag, _rs)| !matches!(tag, Target::Padding) && !matches!(tag, Target::Start)) .flat_map(|(_tag, rs)| rs) .map(|r| r.rw_counter()) .sorted() - .enumerate() + .tuple_windows() { - debug_assert_eq!(idx, rw_counter - 1); + debug_assert_eq!(rw_counter_cur - rw_counter_prev, 1); } } /// Check value in the same way like StateCircuit pub fn check_value(&self) { let err_msg_first = "first access reads don't change value"; let err_msg_non_first = "non-first access reads don't change value"; - let rows = self.table_assignments(); + let rows = self.table_assignments(false); let updates = MptUpdates::mock_from(&rows); let mut errs = Vec::new(); for idx in 1..rows.len() { @@ -77,12 +86,13 @@ impl RwMap { let value = row.value_assignment(); if is_first { // value == init_value - let init_value = updates - .get(row) - .map(|u| u.value_assignments().1) - .unwrap_or_default(); - if value != init_value { - errs.push((idx, err_msg_first, *row, *prev_row)); + if let Some(init_value) = updates.get(row).map(|u| u.value_assignments().1) { + if value != init_value { + errs.push((idx, err_msg_first, *row, *prev_row)); + } + } + if row.tag() == Target::CallContext { + println!("call context value: {:?}", row); } } else { // value == prev_value @@ -107,11 +117,11 @@ impl RwMap { } } } - /// Calculates the number of Rw::Start rows needed. + /// Calculates the number of Rw::Padding rows needed. /// `target_len` is allowed to be 0 as an "auto" mode, - /// then only 1 Rw::Start row will be prepadded. + /// return padding size also allow to be 0, means no padding pub(crate) fn padding_len(rows_len: usize, target_len: usize) -> usize { - if target_len > rows_len { + if target_len >= rows_len { target_len - rows_len } else { if target_len != 0 { @@ -120,38 +130,95 @@ impl RwMap { target_len, rows_len ); } - 1 + 0 } } - /// Prepad Rw::Start rows to target length - pub fn table_assignments_prepad(rows: &[Rw], target_len: usize) -> (Vec, usize) { - // Remove Start rows as we will add them from scratch. - let rows: Vec = rows + /// padding Rw::Start/Rw::Padding accordingly + pub fn table_assignments_padding( + rows: &[Rw], + target_len: usize, + padding_start_rw: Option, + ) -> (Vec, usize) { + let mut padding_exist = HashSet::new(); + // Remove Start/Padding rows as we will add them from scratch. + let rows_trimmed: Vec = rows .iter() - .skip_while(|rw| matches!(rw, Rw::Start { .. })) + .filter(|rw| { + if let Rw::Padding { rw_counter } = rw { + padding_exist.insert(*rw_counter); + } + + !matches!(rw, Rw::Start { .. }) + }) .cloned() .collect(); - let padding_length = Self::padding_len(rows.len(), target_len); - let padding = (1..=padding_length).map(|rw_counter| Rw::Start { rw_counter }); - (padding.chain(rows).collect(), padding_length) + let padding_length = { + let length = Self::padding_len(rows_trimmed.len(), target_len); + length.saturating_sub(1) + }; + + // padding rw_counter starting from + // +1 for to including padding_start row + let start_padding_rw_counter = rows_trimmed.len() + 1; + + let padding = (start_padding_rw_counter..).flat_map(|rw_counter| { + if padding_exist.contains(&rw_counter) { + None + } else { + Some(Rw::Padding { rw_counter }) + } + }); + ( + iter::empty() + .chain([padding_start_rw.unwrap_or(Rw::Start { rw_counter: 1 })]) + .chain(rows_trimmed) + .chain(padding) + .take(target_len) + .collect(), + padding_length, + ) } /// Build Rws for assignment - pub fn table_assignments(&self) -> Vec { + pub fn table_assignments(&self, keep_chronological_order: bool) -> Vec { let mut rows: Vec = self.0.values().flatten().cloned().collect(); - rows.sort_by_key(|row| { - ( - row.tag() as u64, - row.id().unwrap_or_default(), - row.address().unwrap_or_default(), - row.field_tag().unwrap_or_default(), - row.storage_key().unwrap_or_default(), - row.rw_counter(), - ) - }); + if keep_chronological_order { + rows.sort_by_key(|row| (row.rw_counter(), row.tag() as u64)); + } else { + rows.sort_by_key(|row| { + ( + row.tag() as u64, + row.id().unwrap_or_default(), + row.address().unwrap_or_default(), + row.field_tag().unwrap_or_default(), + row.storage_key().unwrap_or_default(), + row.rw_counter(), + ) + }); + } + rows } -} + /// take only rw_counter within range + pub fn take_rw_counter_range(mut self, start_rwc: usize, end_rwc: usize) -> Self { + for rw in self.0.values_mut() { + rw.retain(|r| r.rw_counter() >= start_rwc && r.rw_counter() < end_rwc) + } + self + } + /// Get one Rw for a chunk specified by index + pub fn get_rw(container: &operation::OperationContainer, counter: usize) -> Option { + let rws: Self = container.into(); + for rwv in rws.0.values() { + for rw in rwv { + if rw.rw_counter() == counter { + return Some(*rw); + } + } + } + None + } +} #[allow( missing_docs, reason = "Some of the docs are tedious and can be found at https://github.com/privacy-scaling-explorations/zkevm-specs/blob/master/specs/tables.md" @@ -256,6 +323,40 @@ pub enum Rw { field_tag: TxReceiptFieldTag, value: u64, }, + + /// StepState + StepState { + rw_counter: usize, + is_write: bool, + field_tag: StepStateFieldTag, + value: Word, + }, + + /// ... + + /// Padding, must be the largest enum + Padding { rw_counter: usize }, +} + +/// general to vector +pub trait ToVec { + /// to 2d vec + fn to2dvec(&self) -> Vec>; +} + +impl ToVec> for Vec { + fn to2dvec(&self) -> Vec>> { + self.iter() + .map(|row| { + row.table_assignment::() + .unwrap() + .values() + .iter() + .map(|f| Value::known(*f)) + .collect::>>() + }) + .collect::>>>() + } } /// Rw table row assignment @@ -309,7 +410,7 @@ impl RwRow> { _ = f.map(|v| { inner = Some(v); }); - inner.unwrap() + inner.unwrap_or_default() }; let unwrap_w = |f: WordLoHi>| { let (lo, hi) = f.into_lo_hi(); @@ -329,6 +430,92 @@ impl RwRow> { init_val: unwrap_w(self.init_val), } } + + /// padding Rw::Start/Rw::Padding accordingly + pub fn padding( + rows: &[RwRow>], + target_len: usize, + padding_start_rwrow: Option>>, + ) -> (Vec>>, usize) { + let mut padding_exist = HashSet::new(); + // Remove Start/Padding rows as we will add them from scratch. + let rows_trimmed = rows + .iter() + .filter(|rw| { + let tag = unwrap_value(rw.tag); + + if tag == F::from(Target::Padding as u64) { + let rw_counter = u64::from_le_bytes( + unwrap_value(rw.rw_counter).to_repr()[..U64_BYTES] + .try_into() + .unwrap(), + ); + padding_exist.insert(rw_counter); + } + tag != F::from(Target::Start as u64) && tag != F::ZERO // 0 is invalid tag + }) + .cloned() + .collect::>>>(); + let padding_length = { + let length = RwMap::padding_len(rows_trimmed.len(), target_len); + length.saturating_sub(1) // first row always got padding + }; + let start_padding_rw_counter = { + let start_padding_rw_counter = F::from(rows_trimmed.len() as u64) + F::ONE; + // Assume root of unity < 2**64 + assert!( + start_padding_rw_counter.to_repr()[U64_BYTES..] + .iter() + .cloned() + .sum::() + == 0, + "rw counter > 2 ^ 64" + ); + u64::from_le_bytes( + start_padding_rw_counter.to_repr()[..U64_BYTES] + .try_into() + .unwrap(), + ) + } as usize; + + let padding = (start_padding_rw_counter..).flat_map(|rw_counter| { + if padding_exist.contains(&rw_counter.try_into().unwrap()) { + None + } else { + Some(RwRow { + rw_counter: Value::known(F::from(rw_counter as u64)), + tag: Value::known(F::from(Target::Padding as u64)), + ..Default::default() + }) + } + }); + ( + iter::once(padding_start_rwrow.unwrap_or(RwRow { + rw_counter: Value::known(F::ONE), + tag: Value::known(F::from(Target::Start as u64)), + ..Default::default() + })) + .chain(rows_trimmed) + .chain(padding) + .take(target_len) + .collect(), + padding_length, + ) + } +} + +impl ToVec> for Vec>> { + fn to2dvec(&self) -> Vec>> { + self.iter() + .map(|row| { + row.unwrap() + .values() + .iter() + .map(|f| Value::known(*f)) + .collect::>>() + }) + .collect::>>>() + } } impl Rw { @@ -474,6 +661,7 @@ impl Rw { pub(crate) fn rw_counter(&self) -> usize { match self { Self::Start { rw_counter } + | Self::Padding { rw_counter } | Self::Memory { rw_counter, .. } | Self::Stack { rw_counter, .. } | Self::AccountStorage { rw_counter, .. } @@ -482,6 +670,7 @@ impl Rw { | Self::TxRefund { rw_counter, .. } | Self::Account { rw_counter, .. } | Self::CallContext { rw_counter, .. } + | Self::StepState { rw_counter, .. } | Self::TxLog { rw_counter, .. } | Self::TxReceipt { rw_counter, .. } => *rw_counter, } @@ -489,7 +678,7 @@ impl Rw { pub(crate) fn is_write(&self) -> bool { match self { - Self::Start { .. } => false, + Self::Padding { .. } | Self::Start { .. } => false, Self::Memory { is_write, .. } | Self::Stack { is_write, .. } | Self::AccountStorage { is_write, .. } @@ -498,6 +687,7 @@ impl Rw { | Self::TxRefund { is_write, .. } | Self::Account { is_write, .. } | Self::CallContext { is_write, .. } + | Self::StepState { is_write, .. } | Self::TxLog { is_write, .. } | Self::TxReceipt { is_write, .. } => *is_write, } @@ -505,6 +695,7 @@ impl Rw { pub(crate) fn tag(&self) -> Target { match self { + Self::Padding { .. } => Target::Padding, Self::Start { .. } => Target::Start, Self::Memory { .. } => Target::Memory, Self::Stack { .. } => Target::Stack, @@ -516,6 +707,7 @@ impl Rw { Self::CallContext { .. } => Target::CallContext, Self::TxLog { .. } => Target::TxLog, Self::TxReceipt { .. } => Target::TxReceipt, + Self::StepState { .. } => Target::StepState, } } @@ -530,7 +722,10 @@ impl Rw { Self::CallContext { call_id, .. } | Self::Stack { call_id, .. } | Self::Memory { call_id, .. } => Some(*call_id), - Self::Start { .. } | Self::Account { .. } => None, + Self::Padding { .. } + | Self::Start { .. } + | Self::Account { .. } + | Self::StepState { .. } => None, } } @@ -561,8 +756,10 @@ impl Rw { // make field_tag fit into one limb (16 bits) Some(build_tx_log_address(*index as u64, *field_tag, *log_id)) } - Self::Start { .. } + Self::Padding { .. } + | Self::Start { .. } | Self::CallContext { .. } + | Self::StepState { .. } | Self::TxRefund { .. } | Self::TxReceipt { .. } => None, } @@ -572,8 +769,10 @@ impl Rw { match self { Self::Account { field_tag, .. } => Some(*field_tag as u64), Self::CallContext { field_tag, .. } => Some(*field_tag as u64), + Self::StepState { field_tag, .. } => Some(*field_tag as u64), Self::TxReceipt { field_tag, .. } => Some(*field_tag as u64), - Self::Start { .. } + Self::Padding { .. } + | Self::Start { .. } | Self::Memory { .. } | Self::Stack { .. } | Self::AccountStorage { .. } @@ -588,8 +787,10 @@ impl Rw { match self { Self::AccountStorage { storage_key, .. } | Self::TxAccessListAccountStorage { storage_key, .. } => Some(*storage_key), - Self::Start { .. } + Self::Padding { .. } + | Self::Start { .. } | Self::CallContext { .. } + | Self::StepState { .. } | Self::Stack { .. } | Self::Memory { .. } | Self::TxRefund { .. } @@ -602,8 +803,9 @@ impl Rw { pub(crate) fn value_assignment(&self) -> Word { match self { - Self::Start { .. } => U256::zero(), + Self::Padding { .. } | Self::Start { .. } => U256::zero(), Self::CallContext { value, .. } + | Self::StepState { value, .. } | Self::Account { value, .. } | Self::AccountStorage { value, .. } | Self::Stack { value, .. } @@ -625,10 +827,12 @@ impl Rw { Some(U256::from(*is_warm_prev as u64)) } Self::TxRefund { value_prev, .. } => Some(U256::from(*value_prev)), - Self::Start { .. } + Self::Padding { .. } + | Self::Start { .. } | Self::Stack { .. } | Self::Memory { .. } | Self::CallContext { .. } + | Self::StepState { .. } | Self::TxLog { .. } | Self::TxReceipt { .. } => None, } @@ -644,10 +848,123 @@ impl Rw { } } +impl From> for RwMap { + fn from(rws: Vec) -> Self { + let mut rw_map = HashMap::>::default(); + for rw in rws { + match rw { + Rw::Account { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Account) { + vrw.push(rw) + } else { + rw_map.insert(Target::Account, vec![rw]); + } + } + Rw::AccountStorage { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Storage) { + vrw.push(rw) + } else { + rw_map.insert(Target::Storage, vec![rw]); + } + } + Rw::TxAccessListAccount { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::TxAccessListAccount) { + vrw.push(rw) + } else { + rw_map.insert(Target::TxAccessListAccount, vec![rw]); + } + } + Rw::TxAccessListAccountStorage { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::TxAccessListAccountStorage) { + vrw.push(rw) + } else { + rw_map.insert(Target::TxAccessListAccountStorage, vec![rw]); + } + } + Rw::Padding { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Padding) { + vrw.push(rw) + } else { + rw_map.insert(Target::Padding, vec![rw]); + } + } + Rw::Start { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Start) { + vrw.push(rw) + } else { + rw_map.insert(Target::Start, vec![rw]); + } + } + Rw::Stack { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Stack) { + vrw.push(rw) + } else { + rw_map.insert(Target::Stack, vec![rw]); + } + } + Rw::Memory { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::Memory) { + vrw.push(rw) + } else { + rw_map.insert(Target::Memory, vec![rw]); + } + } + Rw::CallContext { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::CallContext) { + vrw.push(rw) + } else { + rw_map.insert(Target::CallContext, vec![rw]); + } + } + Rw::TxLog { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::TxLog) { + vrw.push(rw) + } else { + rw_map.insert(Target::TxLog, vec![rw]); + } + } + Rw::TxReceipt { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::TxReceipt) { + vrw.push(rw) + } else { + rw_map.insert(Target::TxReceipt, vec![rw]); + } + } + Rw::TxRefund { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::TxRefund) { + vrw.push(rw) + } else { + rw_map.insert(Target::TxRefund, vec![rw]); + } + } + Rw::StepState { .. } => { + if let Some(vrw) = rw_map.get_mut(&Target::StepState) { + vrw.push(rw) + } else { + rw_map.insert(Target::StepState, vec![rw]); + } + } + }; + } + Self(rw_map) + } +} + impl From<&operation::OperationContainer> for RwMap { fn from(container: &operation::OperationContainer) -> Self { - let mut rws = HashMap::default(); + // Get rws raning all indices from the whole container + let mut rws = HashMap::>::default(); + rws.insert( + Target::Padding, + container + .padding + .iter() + .map(|op| Rw::Padding { + rw_counter: op.rwc().into(), + }) + .collect(), + ); rws.insert( Target::Start, container @@ -811,7 +1128,9 @@ impl From<&operation::OperationContainer> for RwMap { is_write: op.rw().is_write(), call_id: op.op().call_id(), memory_address: u64::from_le_bytes( - op.op().address().to_le_bytes()[..8].try_into().unwrap(), + op.op().address().to_le_bytes()[..U64_BYTES] + .try_into() + .unwrap(), ), byte: op.op().value(), }) @@ -855,7 +1174,63 @@ impl From<&operation::OperationContainer> for RwMap { }) .collect(), ); - + rws.insert( + Target::StepState, + container + .step_state + .iter() + .map(|op| Rw::StepState { + rw_counter: op.rwc().into(), + is_write: op.rw().is_write(), + field_tag: match op.op().field { + StepStateField::CallID => StepStateFieldTag::CallID, + StepStateField::IsRoot => StepStateFieldTag::IsRoot, + StepStateField::IsCreate => StepStateFieldTag::IsCreate, + StepStateField::CodeHash => StepStateFieldTag::CodeHash, + StepStateField::ProgramCounter => StepStateFieldTag::ProgramCounter, + StepStateField::StackPointer => StepStateFieldTag::StackPointer, + StepStateField::GasLeft => StepStateFieldTag::GasLeft, + StepStateField::MemoryWordSize => StepStateFieldTag::MemoryWordSize, + StepStateField::ReversibleWriteCounter => { + StepStateFieldTag::ReversibleWriteCounter + } + StepStateField::LogID => StepStateFieldTag::LogID, + }, + value: op.op().value, + }) + .collect(), + ); Self(rws) } } + +/// RwFingerprints +#[derive(Debug, Clone)] +pub struct RwFingerprints { + /// last chunk fingerprint = row0 * row1 * ... rowi + pub prev_mul_acc: F, + /// cur chunk fingerprint + pub mul_acc: F, + /// last chunk last row = alpha - (gamma^1 x1 + gamma^2 x2 + ...) + pub prev_ending_row: F, + /// cur chunk last row + pub ending_row: F, +} + +impl RwFingerprints { + /// new by value + pub fn new(row_prev: F, row: F, acc_prev: F, acc: F) -> Self { + Self { + prev_mul_acc: acc_prev, + mul_acc: acc, + prev_ending_row: row_prev, + ending_row: row, + } + } +} + +impl Default for RwFingerprints { + fn default() -> Self { + Self::new(F::from(0), F::from(0), F::from(1), F::from(1)) + } +} diff --git a/zkevm-circuits/tests/prover_error.rs b/zkevm-circuits/tests/prover_error.rs index f19375ca1c..3b6ef967df 100644 --- a/zkevm-circuits/tests/prover_error.rs +++ b/zkevm-circuits/tests/prover_error.rs @@ -13,7 +13,11 @@ use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use mock::{test_ctx::LoggerConfig, test_ctx2::gen_geth_traces}; use serde_json::{from_value, Value}; use std::{collections::HashMap, fs::File, io::BufReader}; -use zkevm_circuits::{super_circuit::SuperCircuit, util::SubCircuit, witness::block_convert}; +use zkevm_circuits::{ + super_circuit::SuperCircuit, + util::SubCircuit, + witness::{block_convert, chunk_convert}, +}; #[derive(serde::Deserialize)] struct MyAccount { @@ -91,12 +95,16 @@ fn prover_error() { let builder = builder .handle_block(&geth_data.eth_block, &geth_data.geth_traces) .expect("handle_block"); - let block_witness = { + let (block, chunk) = { let mut block = block_convert(&builder).expect("block_convert"); + let chunk = chunk_convert(&block, &builder) + .expect("chunk_convert") + .remove(0); + block.randomness = Fr::from(MOCK_RANDOMNESS); - block + (block, chunk) }; - let circuit = SuperCircuit::new_from_block(&block_witness); + let circuit = SuperCircuit::new_from_block(&block, &chunk); let res = MockProver::run(k, &circuit, circuit.instance()) .expect("MockProver::run") .verify(); From 55754a9d3e3bbd5f21ad9116a2b6ca93be666df0 Mon Sep 17 00:00:00 2001 From: Chad Nehemiah Date: Thu, 28 Mar 2024 01:47:46 -0500 Subject: [PATCH 15/16] Refactor: use StepRws in evm circuits to replace rw indices (#1786) ### Description Continuing the work originally started in https://github.com/privacy-scaling-explorations/zkevm-circuits/pull/1616 this PR seeks to introduce `StepRws` across the evm circuits to reduce the need for tracking rw indices through code. ### Issue Link Closes https://github.com/privacy-scaling-explorations/zkevm-circuits/issues/1586 ### Type of change Refactor (no updates to logic) ### Contents The change has been made to the following files - error_oog_call.rs - error_oog_memory_copy.rs - error_oog_sload_sstore.rs - error_return_data_oo_bound.rs - error_write_protection.rs - extcodecopy.rs - extcodehash.rs - extcodesize.rs - logs.rs - return_revert.rs - returndatacopy.rs - sha3.rs - sload.rs - sstore.rs --- .../evm_circuit/execution/error_oog_call.rs | 34 +++++++++++-------- .../execution/error_oog_memory_copy.rs | 15 ++++---- .../execution/error_oog_sload_sstore.rs | 15 +++++--- .../execution/error_return_data_oo_bound.rs | 12 ++++--- .../execution/error_write_protection.rs | 7 ++-- .../src/evm_circuit/execution/extcodecopy.rs | 17 +++++++--- .../src/evm_circuit/execution/extcodehash.rs | 12 ++++--- .../src/evm_circuit/execution/extcodesize.rs | 14 +++++--- .../src/evm_circuit/execution/logs.rs | 8 +++-- .../evm_circuit/execution/return_revert.rs | 12 +++++-- .../evm_circuit/execution/returndatacopy.rs | 21 +++++------- .../src/evm_circuit/execution/sha3.rs | 9 ++--- .../src/evm_circuit/execution/sload.rs | 17 +++++++--- .../src/evm_circuit/execution/sstore.rs | 16 ++++++--- 14 files changed, 130 insertions(+), 79 deletions(-) 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 4c2a851d27..dafd542d5a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_oog_call.rs @@ -8,7 +8,7 @@ use crate::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{IsZeroGadget, LtGadget}, memory_gadget::MemoryExpandedAddressGadget, - or, CachedRegion, Cell, + or, CachedRegion, Cell, StepRws, }, }, table::CallContextFieldTag, @@ -127,26 +127,30 @@ impl ExecutionGadget for ErrorOOGCallGadget { let opcode = step.opcode().unwrap(); let is_call_or_callcode = usize::from([OpcodeId::CALL, OpcodeId::CALLCODE].contains(&opcode)); - let [tx_id, is_static] = - [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 mut rws = StepRws::new(block, step); + + let tx_id = rws.next().call_context_value(); + let is_static = rws.next().call_context_value(); + let gas = rws.next().stack_value(); + let callee_address = rws.next().stack_value(); + let value = if is_call_or_callcode == 1 { - block.get_rws(step, 4).stack_value() + rws.next().stack_value() } else { U256::zero() }; - 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_codehash_pair() - .0; - let callee_exists = !callee_code_hash.is_zero(); + let cd_offset = rws.next().stack_value(); + let cd_length = rws.next().stack_value(); + let rd_offset = rws.next().stack_value(); + let rd_length = rws.next().stack_value(); + + rws.offset_add(1); - let (is_warm, is_warm_prev) = block - .get_rws(step, 10 + is_call_or_callcode) - .tx_access_list_value_pair(); + let callee_code_hash = rws.next().account_codehash_pair().0; + let callee_exists = !callee_code_hash.is_zero(); + let (is_warm, is_warm_prev) = rws.next().tx_access_list_value_pair(); let memory_expansion_gas_cost = self.call.assign( region, 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 e7b8087647..bdbf1f5115 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 @@ -11,7 +11,7 @@ use crate::{ CommonMemoryAddressGadget, MemoryCopierGasGadget, MemoryExpandedAddressGadget, MemoryExpansionGadget, }, - or, select, AccountAddress, CachedRegion, Cell, + or, select, AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -157,6 +157,8 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { let opcode = step.opcode().unwrap(); let is_extcodecopy = opcode == OpcodeId::EXTCODECOPY; + let mut rws = StepRws::new(block, step); + log::debug!( "ErrorOutOfGasMemoryCopy: opcode = {}, gas_left = {}, gas_cost = {}", opcode, @@ -165,17 +167,18 @@ impl ExecutionGadget for ErrorOOGMemoryCopyGadget { ); let (is_warm, external_address) = if is_extcodecopy { + rws.next(); ( - block.get_rws(step, 1).tx_access_list_value_pair().0, - block.get_rws(step, 2).stack_value(), + rws.next().tx_access_list_value_pair().0, + rws.next().stack_value(), ) } else { (false, U256::zero()) }; - 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(|index| block.get_rws(step, index).stack_value()); + let dst_offset = rws.next().stack_value(); + let src_offset = rws.next().stack_value(); + let copy_size = rws.next().stack_value(); self.opcode .assign(region, offset, Value::known(F::from(opcode.as_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 c6ed23f111..100f9572cd 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 @@ -11,7 +11,7 @@ use crate::{ }, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{LtGadget, PairSelectGadget}, - or, select, CachedRegion, Cell, + or, select, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -162,12 +162,17 @@ impl ExecutionGadget for ErrorOOGSloadSstoreGadget { ) -> Result<(), Error> { let opcode = step.opcode().unwrap(); let is_sstore = opcode == OpcodeId::SSTORE; - let key = block.get_rws(step, 3).stack_value(); - let (is_warm, _) = block.get_rws(step, 4).tx_access_list_value_pair(); + + let mut rws = StepRws::new(block, step); + + rws.offset_add(3); + + let key = rws.next().stack_value(); + let (is_warm, _) = rws.next().tx_access_list_value_pair(); let (value, value_prev, original_value, gas_cost) = if is_sstore { - let value = block.get_rws(step, 5).stack_value(); - let (_, value_prev, _, original_value) = block.get_rws(step, 6).storage_value_aux(); + let value = rws.next().stack_value(); + let (_, value_prev, _, original_value) = rws.next().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) 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 42234d499a..0a61142342 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 @@ -8,7 +8,7 @@ use crate::{ constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, from_bytes, math_gadget::{AddWordsGadget, IsZeroGadget, LtGadget}, - not, or, sum, CachedRegion, Cell, U64Cell, + not, or, sum, CachedRegion, Cell, StepRws, U64Cell, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -131,8 +131,11 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { self.opcode .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; - let [dest_offset, data_offset, size] = - [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); + let mut rws = StepRws::new(block, step); + + let dest_offset = rws.next().stack_value(); + let data_offset = rws.next().stack_value(); + let size = rws.next().stack_value(); self.memory_offset .assign(region, offset, Some(dest_offset.as_u64().to_le_bytes()))?; @@ -141,7 +144,8 @@ impl ExecutionGadget for ErrorReturnDataOutOfBoundGadget { self.sum .assign(region, offset, [data_offset, size], remainder_end)?; - let return_data_length = block.get_rws(step, 3).call_context_value(); + let return_data_length = rws.next().call_context_value(); + self.return_data_length.assign( region, offset, 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 baf2b244de..0266354989 100644 --- a/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs +++ b/zkevm-circuits/src/evm_circuit/execution/error_write_protection.rs @@ -6,7 +6,7 @@ use crate::{ common_gadget::CommonErrorGadget, constraint_builder::{ConstrainBuilderCommon, EVMConstraintBuilder}, math_gadget::{IsEqualGadget, IsZeroWordGadget}, - AccountAddress, CachedRegion, Cell, + AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -110,9 +110,10 @@ impl ExecutionGadget for ErrorWriteProtectionGadget { .assign(region, offset, Value::known(F::from(opcode.as_u64())))?; let [mut gas, mut code_address, mut value] = [U256::zero(), U256::zero(), U256::zero()]; + let mut rws = StepRws::new(block, step); + if is_call { - [gas, code_address, value] = - [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); + [gas, code_address, value] = [0, 1, 2].map(|_| rws.next().stack_value()); } self.gas.assign_u256(region, offset, gas)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs index ef72eb0a9d..61db9a8df0 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodecopy.rs @@ -13,7 +13,7 @@ use crate::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget, }, - not, select, AccountAddress, CachedRegion, Cell, + not, select, AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -183,8 +183,13 @@ impl ExecutionGadget for ExtcodecopyGadget { ) -> Result<(), Error> { 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.get_rws(step, idx).stack_value()); + let mut rws = StepRws::new(block, step); + + let external_address = rws.next().stack_value(); + let memory_offset = rws.next().stack_value(); + let code_offset = rws.next().stack_value(); + let memory_length = rws.next().stack_value(); + self.external_address_word .assign_u256(region, offset, external_address)?; let memory_address = @@ -200,11 +205,13 @@ impl ExecutionGadget for ExtcodecopyGadget { call.is_persistent, )?; - let (_, is_warm) = block.get_rws(step, 7).tx_access_list_value_pair(); + rws.offset_add(3); + + let (_, is_warm) = rws.next().tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.get_rws(step, 8).account_codehash_pair().0; + let code_hash = rws.next().account_codehash_pair().0; self.code_hash.assign_u256(region, offset, code_hash)?; self.not_exists.assign_u256(region, offset, code_hash)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs index fd9fcf370d..e7c90402ce 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodehash.rs @@ -8,7 +8,7 @@ use crate::{ constraint_builder::{ EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, - select, AccountAddress, CachedRegion, Cell, + select, AccountAddress, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -107,7 +107,9 @@ impl ExecutionGadget for ExtcodehashGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.get_rws(step, 0).stack_value(); + let mut rws = StepRws::new(block, step); + + let address = rws.next().stack_value(); self.address_word.assign_u256(region, offset, address)?; self.tx_id @@ -119,11 +121,13 @@ impl ExecutionGadget for ExtcodehashGadget { call.is_persistent, )?; - let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); + rws.offset_add(3); + + let (_, is_warm) = rws.next().tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.get_rws(step, 5).account_codehash_pair().0; + let code_hash = rws.next().account_codehash_pair().0; self.code_hash.assign_u256(region, offset, code_hash)?; Ok(()) diff --git a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs index b80a2c3532..ca5affef12 100644 --- a/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs +++ b/zkevm-circuits/src/evm_circuit/execution/extcodesize.rs @@ -10,7 +10,7 @@ use crate::{ Transition::Delta, }, math_gadget::IsZeroWordGadget, - not, select, AccountAddress, CachedRegion, Cell, U64Cell, + not, select, AccountAddress, CachedRegion, Cell, StepRws, U64Cell, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -124,7 +124,9 @@ impl ExecutionGadget for ExtcodesizeGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let address = block.get_rws(step, 0).stack_value(); + let mut rws = StepRws::new(block, step); + + let address = rws.next().stack_value(); self.address_word.assign_u256(region, offset, address)?; self.tx_id @@ -137,16 +139,18 @@ impl ExecutionGadget for ExtcodesizeGadget { call.is_persistent, )?; - let (_, is_warm) = block.get_rws(step, 4).tx_access_list_value_pair(); + rws.offset_add(3); + + let (_, is_warm) = rws.next().tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; - let code_hash = block.get_rws(step, 5).account_codehash_pair().0; + let code_hash = rws.next().account_codehash_pair().0; self.code_hash.assign_u256(region, offset, code_hash)?; self.not_exists .assign(region, offset, WordLoHi::from(code_hash))?; - let code_size = block.get_rws(step, 6).stack_value().as_u64(); + let code_size = rws.next().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/logs.rs b/zkevm-circuits/src/evm_circuit/execution/logs.rs index 12b472de3a..68ab4deede 100644 --- a/zkevm-circuits/src/evm_circuit/execution/logs.rs +++ b/zkevm-circuits/src/evm_circuit/execution/logs.rs @@ -12,7 +12,7 @@ use crate::{ memory_gadget::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryExpansionGadget, }, - not, sum, CachedRegion, Cell, + not, sum, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -202,7 +202,11 @@ impl ExecutionGadget for LogGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [memory_start, msize] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); + let mut rws = StepRws::new(block, step); + + let memory_start = rws.next().stack_value(); + let msize = rws.next().stack_value(); + let memory_address = self .memory_address .assign(region, offset, memory_start, msize)?; diff --git a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs index 48f9e352e1..66a363c311 100644 --- a/zkevm-circuits/src/evm_circuit/execution/return_revert.rs +++ b/zkevm-circuits/src/evm_circuit/execution/return_revert.rs @@ -13,7 +13,7 @@ use crate::{ memory_gadget::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryExpansionGadget, }, - not, CachedRegion, Cell, + not, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -293,7 +293,11 @@ impl ExecutionGadget for ReturnRevertGadget { Value::known(F::from(step.opcode().unwrap().as_u64())), )?; - let [memory_offset, length] = [0, 1].map(|index| block.get_rws(step, index).stack_value()); + let mut rws = StepRws::new(block, step); + + let memory_offset = rws.next().stack_value(); + let length = rws.next().stack_value(); + let range = self.range.assign(region, offset, memory_offset, length)?; self.memory_expansion .assign(region, offset, step.memory_word_size(), [range])?; @@ -349,8 +353,10 @@ impl ExecutionGadget for ReturnRevertGadget { let is_contract_deployment = call.is_create() && call.is_success && !length.is_zero(); + rws.next(); + let init_code_first_byte = if is_contract_deployment { - block.get_rws(step, 3).memory_value() + rws.next().memory_value() } else { 0 } diff --git a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs index 1bf7334d4c..8fa7a5be1a 100644 --- a/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs +++ b/zkevm-circuits/src/evm_circuit/execution/returndatacopy.rs @@ -14,7 +14,7 @@ use crate::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget, }, - CachedRegion, Cell, MemoryAddress, + CachedRegion, Cell, MemoryAddress, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -177,21 +177,16 @@ impl ExecutionGadget for ReturnDataCopyGadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [dest_offset, data_offset, size] = - [0, 1, 2].map(|index| block.get_rws(step, index).stack_value()); + let mut rws = StepRws::new(block, step); + + let [dest_offset, data_offset, size] = [0, 1, 2].map(|_| rws.next().stack_value()); self.data_offset.assign_u256(region, offset, data_offset)?; - let [last_callee_id, return_data_offset, return_data_size] = [ - (3, CallContextFieldTag::LastCalleeId), - (4, CallContextFieldTag::LastCalleeReturnDataOffset), - (5, CallContextFieldTag::LastCalleeReturnDataLength), - ] - .map(|(i, tag)| { - let rw = block.get_rws(step, i); - assert_eq!(rw.field_tag(), Some(tag as u64)); - rw.call_context_value() - }); + let last_callee_id = rws.next().call_context_value(); + let return_data_offset = rws.next().call_context_value(); + let return_data_size = rws.next().call_context_value(); + self.last_callee_id.assign( region, offset, diff --git a/zkevm-circuits/src/evm_circuit/execution/sha3.rs b/zkevm-circuits/src/evm_circuit/execution/sha3.rs index 9cff5746c4..a8ba4644bc 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sha3.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sha3.rs @@ -16,7 +16,7 @@ use crate::{ CommonMemoryAddressGadget, MemoryAddressGadget, MemoryCopierGasGadget, MemoryExpansionGadget, }, - rlc, CachedRegion, Cell, + rlc, CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -124,8 +124,9 @@ impl ExecutionGadget for Sha3Gadget { ) -> Result<(), Error> { self.same_context.assign_exec_step(region, offset, step)?; - let [memory_offset, size, sha3_output] = - [0, 1, 2].map(|idx| block.get_rws(step, idx).stack_value()); + let mut rws = StepRws::new(block, step); + + let [memory_offset, size, sha3_output] = [0, 1, 2].map(|_| rws.next().stack_value()); let memory_address = self .memory_address .assign(region, offset, memory_offset, size)?; @@ -141,7 +142,7 @@ impl ExecutionGadget for Sha3Gadget { )?; let values: Vec = (3..3 + (size.low_u64() as usize)) - .map(|i| block.get_rws(step, i).memory_value()) + .map(|_| rws.next().memory_value()) .collect(); let rlc_acc = region diff --git a/zkevm-circuits/src/evm_circuit/execution/sload.rs b/zkevm-circuits/src/evm_circuit/execution/sload.rs index 0065e00ad8..5b3c7ced0b 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sload.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sload.rs @@ -7,7 +7,7 @@ use crate::{ constraint_builder::{ EVMConstraintBuilder, ReversionInfo, StepStateTransition, Transition::Delta, }, - CachedRegion, Cell, + CachedRegion, Cell, StepRws, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -121,16 +121,23 @@ impl ExecutionGadget for SloadGadget { self.callee_address .assign_h160(region, offset, call.address)?; - let key = block.get_rws(step, 4).stack_value(); - let value = block.get_rws(step, 6).stack_value(); + let mut rws = StepRws::new(block, step); + + rws.offset_add(4); + + let key = rws.next().stack_value(); + let (_, committed_value) = rws.next().aux_pair(); + let value = rws.next().stack_value(); + self.key.assign_u256(region, offset, key)?; self.value.assign_u256(region, offset, value)?; - let (_, committed_value) = block.get_rws(step, 5).aux_pair(); self.committed_value .assign_u256(region, offset, committed_value)?; - let (_, is_warm) = block.get_rws(step, 8).tx_access_list_value_pair(); + rws.next(); + + let (_, is_warm) = rws.next().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 bcc946cf8b..8c6a57cf6e 100644 --- a/zkevm-circuits/src/evm_circuit/execution/sstore.rs +++ b/zkevm-circuits/src/evm_circuit/execution/sstore.rs @@ -10,7 +10,7 @@ use crate::{ Transition::Delta, }, math_gadget::{IsEqualWordGadget, IsZeroWordGadget, LtGadget}, - not, CachedRegion, Cell, U64Cell, + not, CachedRegion, Cell, StepRws, U64Cell, }, witness::{Block, Call, Chunk, ExecStep, Transaction}, }, @@ -187,17 +187,23 @@ impl ExecutionGadget for SstoreGadget { self.callee_address .assign_h160(region, offset, call.address)?; - let key = block.get_rws(step, 5).stack_value(); - let value = block.get_rws(step, 6).stack_value(); + let mut rws = StepRws::new(block, step); + + rws.offset_add(5); + + let key = rws.next().stack_value(); + let value = rws.next().stack_value(); self.key.assign_u256(region, offset, key)?; self.value.assign_u256(region, offset, value)?; - let (_, value_prev, _, original_value) = block.get_rws(step, 7).storage_value_aux(); + let (_, value_prev, _, original_value) = rws.next().storage_value_aux(); self.value_prev.assign_u256(region, offset, value_prev)?; self.original_value .assign_u256(region, offset, original_value)?; - let (_, is_warm) = block.get_rws(step, 9).tx_access_list_value_pair(); + rws.next(); // skip the storage write + + let (_, is_warm) = rws.next().tx_access_list_value_pair(); self.is_warm .assign(region, offset, Value::known(F::from(is_warm as u64)))?; From 82e8d8fed3ab1c6ad3f04e3fa3f9b15423b16b5e Mon Sep 17 00:00:00 2001 From: Akase Haruka Date: Wed, 3 Apr 2024 15:57:03 +0800 Subject: [PATCH 16/16] [feat] support EIP 1559/2930 in testool (#1762) ### Description This pr enables support of EIP 1559/2930 in testool ### Issue Link [_link issue here_] ### Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update - [ ] Refactor (no updates to logic) --- eth-types/src/geth_types.rs | 152 ++++++++++++++- eth-types/src/lib.rs | 5 +- external-tracer/src/lib.rs | 7 +- geth-utils/gethutil/trace.go | 37 ++-- mock/src/transaction.rs | 16 +- testool/src/statetest/executor.rs | 47 +++-- testool/src/statetest/json.rs | 92 +++++---- testool/src/statetest/parse.rs | 150 ++++++++++----- testool/src/statetest/spec.rs | 122 +++++++++++- testool/src/statetest/yaml.rs | 297 +++++++++++++++++++++--------- testool/src/utils.rs | 3 + 11 files changed, 695 insertions(+), 233 deletions(-) diff --git a/eth-types/src/geth_types.rs b/eth-types/src/geth_types.rs index 7c81808d29..c95475a5bc 100644 --- a/eth-types/src/geth_types.rs +++ b/eth-types/src/geth_types.rs @@ -5,10 +5,13 @@ use crate::{ keccak256, sign_types::{biguint_to_32bytes_le, ct_option_ok_or, recover_pk, SignData, SECP256K1_Q}, AccessList, Address, Block, Bytecode, Bytes, Error, GethExecTrace, Hash, ToBigEndian, - ToLittleEndian, ToWord, Word, U64, + ToLittleEndian, ToWord, Word, H256, U64, }; use ethers_core::{ - types::{transaction::response, NameOrAddress, TransactionRequest}, + types::{ + transaction::{eip2718::TypedTransaction, response}, + Eip1559TransactionRequest, Eip2930TransactionRequest, NameOrAddress, TransactionRequest, + }, utils::get_contract_address, }; use ethers_signers::{LocalWallet, Signer}; @@ -18,6 +21,117 @@ use num_bigint::BigUint; use serde::{Serialize, Serializer}; use serde_with::serde_as; use std::collections::HashMap; +use strum_macros::EnumIter; + +/// Tx type +#[derive(Default, Debug, Copy, Clone, EnumIter, Serialize, PartialEq, Eq)] +pub enum TxType { + /// EIP 155 tx + #[default] + Eip155 = 0, + /// Pre EIP 155 tx + PreEip155, + /// EIP 1559 tx + Eip1559, + /// EIP 2930 tx + Eip2930, +} + +impl From for usize { + fn from(value: TxType) -> Self { + value as usize + } +} + +impl From for u64 { + fn from(value: TxType) -> Self { + value as u64 + } +} + +impl TxType { + /// If this type is PreEip155 + pub fn is_pre_eip155(&self) -> bool { + matches!(*self, TxType::PreEip155) + } + + /// If this type is EIP155 or not + pub fn is_eip155(&self) -> bool { + matches!(*self, TxType::Eip155) + } + + /// If this type is Eip1559 or not + pub fn is_eip1559(&self) -> bool { + matches!(*self, TxType::Eip1559) + } + + /// If this type is Eip2930 or not + pub fn is_eip2930(&self) -> bool { + matches!(*self, TxType::Eip2930) + } + + /// Get the type of transaction + pub fn get_tx_type(tx: &crate::Transaction) -> Self { + match tx.transaction_type { + Some(x) if x == U64::from(1) => Self::Eip2930, + Some(x) if x == U64::from(2) => Self::Eip1559, + _ => match tx.v.as_u64() { + 0 | 1 | 27 | 28 => Self::PreEip155, + _ => Self::Eip155, + }, + } + } + + /// Return the recovery id of signature for recovering the signing pk + pub fn get_recovery_id(&self, v: u64) -> u8 { + let recovery_id = match *self { + TxType::Eip155 => (v + 1) % 2, + TxType::PreEip155 => { + assert!(v == 0x1b || v == 0x1c, "v: {v}"); + v - 27 + } + TxType::Eip1559 => { + assert!(v <= 1); + v + } + TxType::Eip2930 => { + assert!(v <= 1); + v + } + }; + + recovery_id as u8 + } +} + +/// Get the RLP bytes for signing +pub fn get_rlp_unsigned(tx: &crate::Transaction) -> Vec { + let sig_v = tx.v; + match TxType::get_tx_type(tx) { + TxType::Eip155 => { + let mut tx: TransactionRequest = tx.into(); + tx.chain_id = Some(tx.chain_id.unwrap_or_else(|| { + let recv_v = TxType::Eip155.get_recovery_id(sig_v.as_u64()) as u64; + (sig_v - recv_v - 35) / 2 + })); + tx.rlp().to_vec() + } + TxType::PreEip155 => { + let tx: TransactionRequest = tx.into(); + tx.rlp_unsigned().to_vec() + } + TxType::Eip1559 => { + let tx: Eip1559TransactionRequest = tx.into(); + let typed_tx: TypedTransaction = tx.into(); + typed_tx.rlp().to_vec() + } + TxType::Eip2930 => { + let tx: Eip2930TransactionRequest = tx.into(); + let typed_tx: TypedTransaction = tx.into(); + typed_tx.rlp().to_vec() + } + } +} /// Definition of all of the data related to an account. #[serde_as] @@ -156,6 +270,8 @@ pub struct Withdrawal { /// Definition of all of the constants related to an Ethereum transaction. #[derive(Debug, Default, Clone, Serialize)] pub struct Transaction { + /// Tx type + pub tx_type: TxType, /// Sender address pub from: Address, /// Recipient address (None for contract creation) @@ -172,9 +288,9 @@ pub struct Transaction { /// Gas Price pub gas_price: Word, /// Gas fee cap - pub gas_fee_cap: Word, + pub gas_fee_cap: Option, /// Gas tip cap - pub gas_tip_cap: Word, + pub gas_tip_cap: Option, /// The compiled code of a contract OR the first 4 bytes of the hash of the /// invoked method signature and encoded parameters. For details see /// Ethereum Contract ABI @@ -188,6 +304,14 @@ pub struct Transaction { pub r: Word, /// "s" value of the transaction signature pub s: Word, + + /// RLP bytes + pub rlp_bytes: Vec, + /// RLP unsigned bytes + pub rlp_unsigned_bytes: Vec, + + /// Transaction hash + pub hash: H256, } impl From<&Transaction> for crate::Transaction { @@ -199,8 +323,8 @@ impl From<&Transaction> for crate::Transaction { gas: tx.gas_limit.to_word(), value: tx.value, gas_price: Some(tx.gas_price), - max_priority_fee_per_gas: Some(tx.gas_tip_cap), - max_fee_per_gas: Some(tx.gas_fee_cap), + max_priority_fee_per_gas: tx.gas_tip_cap, + max_fee_per_gas: tx.gas_fee_cap, input: tx.call_data.clone(), access_list: tx.access_list.clone(), v: tx.v.into(), @@ -214,19 +338,23 @@ impl From<&Transaction> for crate::Transaction { impl From<&crate::Transaction> for Transaction { fn from(tx: &crate::Transaction) -> Transaction { Transaction { + tx_type: TxType::get_tx_type(tx), from: tx.from, to: tx.to, 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_tip_cap: tx.max_priority_fee_per_gas.unwrap_or_default(), - gas_fee_cap: tx.max_fee_per_gas.unwrap_or_default(), + gas_tip_cap: tx.max_priority_fee_per_gas, + gas_fee_cap: tx.max_fee_per_gas, call_data: tx.input.clone(), access_list: tx.access_list.clone(), v: tx.v.as_u64(), r: tx.r, s: tx.s, + rlp_bytes: tx.rlp().to_vec(), + rlp_unsigned_bytes: get_rlp_unsigned(tx), + hash: tx.hash, } } } @@ -256,13 +384,14 @@ impl Transaction { gas_limit: U64::zero(), value: Word::zero(), gas_price: Word::zero(), - gas_tip_cap: Word::zero(), - gas_fee_cap: Word::zero(), + gas_tip_cap: Some(Word::zero()), + gas_fee_cap: Some(Word::zero()), call_data: Bytes::new(), access_list: None, v: 0, r: Word::zero(), s: Word::zero(), + ..Default::default() } } /// Return the SignData associated with this Transaction. @@ -355,6 +484,9 @@ impl Transaction { s: self.s, v: U64::from(self.v), block_number: Some(block_number), + transaction_type: Some(U64::from(self.tx_type as u64)), + max_priority_fee_per_gas: self.gas_tip_cap, + max_fee_per_gas: self.gas_fee_cap, chain_id: Some(chain_id), ..response::Transaction::default() } diff --git a/eth-types/src/lib.rs b/eth-types/src/lib.rs index f1f9fb7d5c..9bebdc63d2 100644 --- a/eth-types/src/lib.rs +++ b/eth-types/src/lib.rs @@ -41,7 +41,10 @@ use ethers_core::types; pub use ethers_core::{ abi::ethereum_types::{BigEndianHash, U512}, types::{ - transaction::{eip2930::AccessList, response::Transaction}, + transaction::{ + eip2930::{AccessList, AccessListItem}, + response::Transaction, + }, Address, Block, Bytes, Signature, H160, H256, H64, U256, U64, }, }; diff --git a/external-tracer/src/lib.rs b/external-tracer/src/lib.rs index d144cc515e..7bb5c27db5 100644 --- a/external-tracer/src/lib.rs +++ b/external-tracer/src/lib.rs @@ -5,7 +5,7 @@ use eth_types::{ Address, Error, GethExecTrace, Word, }; use serde::Serialize; -use std::collections::HashMap; +use std::collections::BTreeMap; /// Configuration structure for `geth_utils::trace` #[derive(Debug, Default, Clone, Serialize)] @@ -18,7 +18,7 @@ pub struct TraceConfig { /// block constants pub block_constants: BlockConstants, /// accounts - pub accounts: HashMap, + pub accounts: BTreeMap, /// transaction pub transactions: Vec, /// withdrawal @@ -78,7 +78,8 @@ pub fn trace(config: &TraceConfig) -> Result, Error> { let allowed_cases = error.starts_with("nonce too low") || error.starts_with("nonce too high") || error.starts_with("intrinsic gas too low") - || error.starts_with("insufficient funds for gas * price + value"); + || error.starts_with("insufficient funds for gas * price + value") + || error.starts_with("insufficient funds for transfer"); if trace.invalid && !allowed_cases { return Err(Error::TracingError(error.clone())); } diff --git a/geth-utils/gethutil/trace.go b/geth-utils/gethutil/trace.go index f0cb76cffd..d48e46666c 100644 --- a/geth-utils/gethutil/trace.go +++ b/geth-utils/gethutil/trace.go @@ -99,19 +99,20 @@ type Account struct { } type Transaction struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - Nonce hexutil.Uint64 `json:"nonce"` - Value *hexutil.Big `json:"value"` - GasLimit hexutil.Uint64 `json:"gas_limit"` - GasPrice *hexutil.Big `json:"gas_price"` - GasFeeCap *hexutil.Big `json:"gas_fee_cap"` - GasTipCap *hexutil.Big `json:"gas_tip_cap"` - CallData hexutil.Bytes `json:"call_data"` - AccessList []struct { - Address common.Address `json:"address"` - StorageKeys []common.Hash `json:"storage_keys"` - } `json:"access_list"` + From common.Address `json:"from"` + To *common.Address `json:"to"` + Nonce hexutil.Uint64 `json:"nonce"` + Value *hexutil.Big `json:"value"` + GasLimit hexutil.Uint64 `json:"gas_limit"` + GasPrice *hexutil.Big `json:"gas_price"` + GasFeeCap *hexutil.Big `json:"gas_fee_cap"` + GasTipCap *hexutil.Big `json:"gas_tip_cap"` + CallData hexutil.Bytes `json:"call_data"` + AccessList types.AccessList `json:"access_list"` + Type string `json:"tx_type"` + V int64 `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` } type TraceConfig struct { @@ -160,10 +161,14 @@ func Trace(config TraceConfig) ([]*ExecutionResult, error) { blockGasLimit := toBigInt(config.Block.GasLimit).Uint64() messages := make([]core.Message, len(config.Transactions)) for i, tx := range config.Transactions { - // If gas price is specified directly, the tx is treated as legacy type. if tx.GasPrice != nil { - tx.GasFeeCap = tx.GasPrice - tx.GasTipCap = tx.GasPrice + // Set GasFeeCap and GasTipCap to GasPrice if not exist. + if tx.GasFeeCap == nil { + tx.GasFeeCap = tx.GasPrice + } + if tx.GasTipCap == nil { + tx.GasTipCap = tx.GasPrice + } } txAccessList := make(types.AccessList, len(tx.AccessList)) diff --git a/mock/src/transaction.rs b/mock/src/transaction.rs index 1184d418d4..e960181b82 100644 --- a/mock/src/transaction.rs +++ b/mock/src/transaction.rs @@ -133,8 +133,8 @@ pub struct MockTransaction { pub s: Option, pub transaction_type: U64, pub access_list: AccessList, - pub max_priority_fee_per_gas: Word, - pub max_fee_per_gas: Word, + pub max_priority_fee_per_gas: Option, + pub max_fee_per_gas: Option, pub chain_id: Word, pub invalid: bool, } @@ -158,8 +158,8 @@ impl Default for MockTransaction { s: None, transaction_type: U64::zero(), access_list: AccessList::default(), - max_priority_fee_per_gas: Word::zero(), - max_fee_per_gas: Word::zero(), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, chain_id: *MOCK_CHAIN_ID, invalid: false, } @@ -185,8 +185,8 @@ impl From for Transaction { s: mock.s.unwrap_or_default(), transaction_type: Some(mock.transaction_type), access_list: Some(mock.access_list), - max_priority_fee_per_gas: Some(mock.max_priority_fee_per_gas), - max_fee_per_gas: Some(mock.max_fee_per_gas), + max_priority_fee_per_gas: mock.max_priority_fee_per_gas, + max_fee_per_gas: mock.max_fee_per_gas, chain_id: Some(mock.chain_id), other: OtherFields::default(), } @@ -289,13 +289,13 @@ impl MockTransaction { /// Set max_priority_fee_per_gas field for the MockTransaction. pub fn max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: Word) -> &mut Self { - self.max_priority_fee_per_gas = max_priority_fee_per_gas; + self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); self } /// Set max_fee_per_gas field for the MockTransaction. pub fn max_fee_per_gas(&mut self, max_fee_per_gas: Word) -> &mut Self { - self.max_fee_per_gas = max_fee_per_gas; + self.max_fee_per_gas = Some(max_fee_per_gas); self } diff --git a/testool/src/statetest/executor.rs b/testool/src/statetest/executor.rs index 2282d41eb8..0eff2d1452 100644 --- a/testool/src/statetest/executor.rs +++ b/testool/src/statetest/executor.rs @@ -1,14 +1,11 @@ use super::{AccountMatch, StateTest, StateTestResult}; -use crate::config::TestSuite; +use crate::{config::TestSuite, utils::ETH_CHAIN_ID}; use bus_mapping::{ circuit_input_builder::{CircuitInputBuilder, FixedCParams}, mock::BlockData, }; use eth_types::{geth_types, Address, Bytes, Error, GethExecTrace, U256, U64}; -use ethers_core::{ - k256::ecdsa::SigningKey, - types::{transaction::eip2718::TypedTransaction, TransactionRequest, Withdrawal}, -}; +use ethers_core::{k256::ecdsa::SigningKey, types::Withdrawal, utils::keccak256}; use ethers_signers::{LocalWallet, Signer}; use external_tracer::TraceConfig; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; @@ -138,28 +135,22 @@ fn check_post( } fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { - let chain_id = 1; - let wallet = LocalWallet::from_str(&hex::encode(st.secret_key.0)).unwrap(); - let mut tx = TransactionRequest::new() - .chain_id(chain_id) - .from(st.from) - .nonce(st.nonce) - .value(st.value) - .data(st.data.clone()) - .gas(st.gas_limit) - .gas_price(st.gas_price); - - if let Some(to) = st.to { - tx = tx.to(to); - } - let tx: TypedTransaction = tx.into(); + let tx_type = st.tx_type(); + let tx = st.build_tx(); + + let wallet = LocalWallet::from_str(&hex::encode(&st.secret_key.0)).unwrap(); + let rlp_unsigned = tx.rlp().to_vec(); let sig = wallet.sign_transaction_sync(&tx).unwrap(); + let v = st.normalize_sig_v(sig.v); + let rlp_signed = tx.rlp_signed(&sig).to_vec(); + let tx_hash = keccak256(tx.rlp_signed(&sig)); + let accounts = st.pre; ( st.id, TraceConfig { - chain_id: U256::one(), + chain_id: U256::from(ETH_CHAIN_ID), history_hashes: vec![U256::from_big_endian(st.env.previous_hash.as_bytes())], block_constants: geth_types::BlockConstants { coinbase: st.env.current_coinbase, @@ -171,21 +162,25 @@ fn into_traceconfig(st: StateTest) -> (String, TraceConfig, StateTestResult) { }, transactions: vec![geth_types::Transaction { + tx_type, from: st.from, to: st.to, nonce: U64::from(st.nonce), value: st.value, gas_limit: U64::from(st.gas_limit), gas_price: st.gas_price, - gas_fee_cap: U256::zero(), - gas_tip_cap: U256::zero(), + gas_fee_cap: st.max_fee_per_gas, + gas_tip_cap: st.max_priority_fee_per_gas, call_data: st.data, - access_list: None, - v: sig.v, + access_list: st.access_list, + v, r: sig.r, s: sig.s, + rlp_bytes: rlp_signed, + rlp_unsigned_bytes: rlp_unsigned, + hash: tx_hash.into(), }], - accounts: st.pre.into_iter().collect(), + accounts, ..Default::default() }, st.result, diff --git a/testool/src/statetest/json.rs b/testool/src/statetest/json.rs index b60c5157f1..fc357adb9c 100644 --- a/testool/src/statetest/json.rs +++ b/testool/src/statetest/json.rs @@ -9,8 +9,6 @@ use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use serde::Deserialize; use std::collections::{BTreeMap, HashMap}; -use serde_json::value::Value; - fn default_block_base_fee() -> String { DEFAULT_BASE_FEE.to_string() } @@ -71,8 +69,11 @@ struct JsonStateTest { #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] struct Transaction { + access_list: Option, data: Vec, gas_limit: Vec, + max_priority_fee_per_gas: Option, + max_fee_per_gas: Option, gas_price: String, nonce: String, secret_key: String, @@ -109,8 +110,7 @@ impl<'a> JsonStateTestBuilder<'a> { /// generates `StateTest` vectors from a ethereum josn test specification pub fn load_json(&mut self, path: &str, source: &str) -> Result> { let mut state_tests = Vec::new(); - let tests: HashMap = - serde_json::from_str(&strip_json_comments(source))?; + let tests: HashMap = serde_json::from_str(source)?; for (test_name, test) in tests { let env = Self::parse_env(&test.env)?; @@ -120,13 +120,32 @@ impl<'a> JsonStateTestBuilder<'a> { let secret_key = parse::parse_bytes(&test.transaction.secret_key)?; let from = secret_key_to_address(&SigningKey::from_slice(&secret_key)?); let nonce = parse::parse_u64(&test.transaction.nonce)?; - let gas_price = parse::parse_u256(&test.transaction.gas_price)?; + + let max_priority_fee_per_gas = test + .transaction + .max_priority_fee_per_gas + .map_or(Ok(None), |s| parse::parse_u256(&s).map(Some))?; + let max_fee_per_gas = test + .transaction + .max_fee_per_gas + .map_or(Ok(None), |s| parse::parse_u256(&s).map(Some))?; + + // Set gas price to `min(max_priority_fee_per_gas + base_fee, max_fee_per_gas)` for + // EIP-1559 transaction. + // + let gas_price = parse::parse_u256(&test.transaction.gas_price).unwrap_or_else(|_| { + max_fee_per_gas + .unwrap() + .min(max_priority_fee_per_gas.unwrap() + env.current_base_fee) + }); + + let access_list = &test.transaction.access_list; let data_s: Vec<_> = test .transaction .data .iter() - .map(|item| parse::parse_calldata(self.compiler, item)) + .map(|item| parse::parse_calldata(self.compiler, item, access_list)) .collect::>()?; let gas_limit_s: Vec<_> = test @@ -167,7 +186,7 @@ impl<'a> JsonStateTestBuilder<'a> { } } - for (idx_data, data) in data_s.iter().enumerate() { + for (idx_data, calldata) in data_s.iter().enumerate() { for (idx_gas, gas_limit) in gas_limit_s.iter().enumerate() { for (idx_value, value) in value_s.iter().enumerate() { for (data_refs, gas_refs, value_refs, result) in &expects { @@ -193,10 +212,13 @@ impl<'a> JsonStateTestBuilder<'a> { to, secret_key: secret_key.clone(), nonce, + max_priority_fee_per_gas, + max_fee_per_gas, gas_price, gas_limit: *gas_limit, value: *value, - data: data.0.clone(), + data: calldata.data.clone(), + access_list: calldata.access_list.clone(), exception: false, }); } @@ -313,29 +335,10 @@ impl<'a> JsonStateTestBuilder<'a> { } } -fn strip_json_comments(json: &str) -> String { - fn strip(value: Value) -> Value { - use Value::*; - match value { - Array(vec) => Array(vec.into_iter().map(strip).collect()), - Object(map) => Object( - map.into_iter() - .filter(|(k, _)| !k.starts_with("//")) - .map(|(k, v)| (k, strip(v))) - .collect(), - ), - _ => value, - } - } - - let value: Value = serde_json::from_str(json).unwrap(); - strip(value).to_string() -} - #[cfg(test)] mod test { use super::*; - use eth_types::{Bytes, H256}; + use eth_types::{address, AccessList, AccessListItem, Bytes, H256}; use std::{collections::HashMap, str::FromStr}; const JSON: &str = r#" @@ -381,6 +384,15 @@ mod test { } }, "transaction" : { + "accessList" : [ + { + "address" : "0x009e7baea6a6c7c4c2dfeb977efac326af552d87", + "storageKeys" : [ + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001" + ] + } + ], "data" : [ "0x6001", "0x6002" @@ -430,9 +442,24 @@ mod test { )?), gas_limit: 400000, gas_price: U256::from(10u64), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, nonce: 0, value: U256::from(100000u64), data: Bytes::from(hex::decode("6001")?), + access_list: Some(AccessList(vec![AccessListItem { + address: address!("0x009e7baea6a6c7c4c2dfeb977efac326af552d87"), + storage_keys: vec![ + H256::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + H256::from_str( + "0x0000000000000000000000000000000000000000000000000000000000000001", + ) + .unwrap(), + ], + }])), pre: BTreeMap::from([( acc095e, Account { @@ -460,13 +487,4 @@ mod test { Ok(()) } - - #[test] - fn test_strip() { - let original = r#"{"//a":"a1","b":[{"c":"c1","//d":"d1"}]}"#; - let expected = r#"{"b":[{"c":"c1"}]}"#; - - let stripped = strip_json_comments(original); - assert_eq!(expected, stripped); - } } diff --git a/testool/src/statetest/parse.rs b/testool/src/statetest/parse.rs index 0576cc8d35..7f41973755 100644 --- a/testool/src/statetest/parse.rs +++ b/testool/src/statetest/parse.rs @@ -1,15 +1,52 @@ -use std::collections::HashMap; - use crate::{abi, Compiler}; - use anyhow::{bail, Context, Result}; -use eth_types::{Address, Bytes, H256, U256}; +use eth_types::{address, AccessList, AccessListItem, Address, Bytes, H256, U256}; use log::debug; use once_cell::sync::Lazy; use regex::Regex; +use serde::Deserialize; +use std::{collections::HashMap, str::FromStr}; type Label = String; +/// Raw access list to parse +pub type RawAccessList = Vec; + +/// Raw access list item to parse +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RawAccessListItem { + address: String, + storage_keys: Vec, +} + +impl RawAccessListItem { + pub fn new(address: String, storage_keys: Vec) -> Self { + Self { + address, + storage_keys, + } + } +} + +/// parsed calldata +#[derive(Debug)] +pub struct Calldata { + pub data: Bytes, + pub label: Option

, pub gas_limit: u64, + pub max_priority_fee_per_gas: Option, + pub max_fee_per_gas: Option, pub gas_price: U256, pub nonce: u64, pub value: U256, pub data: Bytes, + pub access_list: Option, pub pre: BTreeMap, pub result: StateTestResult, pub exception: bool, @@ -74,6 +89,7 @@ impl std::fmt::Display for StateTest { format!("{k} :") }; let max_len = max_len - k.len(); + let v = v.chars().collect::>(); for i in 0..=v.len() / max_len { if i == 0 && !k.is_empty() { text.push_str(&k); @@ -81,7 +97,11 @@ impl std::fmt::Display for StateTest { let padding: String = " ".repeat(k.len()); text.push_str(&padding); } - text.push_str(&v[i * max_len..std::cmp::min((i + 1) * max_len, v.len())]); + text.push_str( + &v[i * max_len..std::cmp::min((i + 1) * max_len, v.len())] + .iter() + .collect::(), + ); text.push('\n'); } text @@ -108,10 +128,19 @@ impl std::fmt::Display for StateTest { table.add_row(row!["from", format!("{:?}", self.from)]); table.add_row(row!["to", format!("{:?}", self.to)]); table.add_row(row!["gas_limit", format!("{}", self.gas_limit)]); + table.add_row(row![ + "max_priority_fee_per_gas", + format!("{:?}", self.max_priority_fee_per_gas) + ]); + table.add_row(row![ + "max_fee_per_gas", + format!("{:?}", self.max_fee_per_gas) + ]); table.add_row(row!["gas_price", format!("{}", self.gas_price)]); table.add_row(row!["nonce", format!("{}", self.nonce)]); table.add_row(row!["value", format!("{}", self.value)]); table.add_row(row!["data", format(&hex::encode(&self.data), "")]); + table.add_row(row!["access_list", format!("{:?}", self.access_list)]); table.add_row(row!["exception", self.exception]); let mut addrs: Vec<_> = self.pre.keys().collect(); @@ -273,10 +302,13 @@ impl StateTest { from, to, gas_limit, + max_priority_fee_per_gas: None, + max_fee_per_gas: None, gas_price: U256::one(), nonce: 0, value, data: data.into(), + access_list: None, pre, result: HashMap::new(), exception: false, @@ -284,4 +316,88 @@ impl StateTest { Ok(state_test) } + + /// Parse transaction type. + pub fn tx_type(&self) -> TxType { + if self.max_priority_fee_per_gas.is_some() { + // For EIP-1559, both maxPriorityFeePerGas and maxFeePerGas must + // exist, and accessList should exist but may be empty. + assert!(self.max_fee_per_gas.is_some()); + assert!(self.access_list.is_some()); + + TxType::Eip1559 + } else if self.access_list.is_some() { + TxType::Eip2930 + } else { + // Set transaction type to EIP-155 as default. + TxType::Eip155 + } + } + + /// Normalize the signature back to 0/1. + pub fn normalize_sig_v(&self, v: u64) -> u64 { + match self.tx_type() { + TxType::Eip1559 | TxType::Eip2930 => { + // + if v > 1 { + v - ETH_CHAIN_ID * 2 - 35 + } else { + v + } + } + _ => v, + } + } + + /// Build a transaction from this test case. + pub fn build_tx(&self) -> TypedTransaction { + match self.tx_type() { + TxType::Eip1559 => self.build_eip1559_tx(), + TxType::Eip2930 => self.build_eip2930_tx(), + _ => self.build_normal_tx_request().into(), + } + } + + fn build_eip1559_tx(&self) -> TypedTransaction { + let mut request = Eip1559TransactionRequest::new() + .chain_id(ETH_CHAIN_ID) + .from(self.from) + .nonce(self.nonce) + .value(self.value) + .data(self.data.clone()) + .gas(self.gas_limit) + .access_list(self.access_list.clone().unwrap()) + .max_priority_fee_per_gas(self.max_priority_fee_per_gas.unwrap()) + .max_fee_per_gas(self.max_fee_per_gas.unwrap()); + + if let Some(to) = self.to { + request = request.to(to); + } + + request.into() + } + + fn build_eip2930_tx(&self) -> TypedTransaction { + let request = self.build_normal_tx_request(); + request + .with_access_list(self.access_list.clone().unwrap()) + .into() + } + + fn build_normal_tx_request(&self) -> TransactionRequest { + let mut request = TransactionRequest::new() + .chain_id(ETH_CHAIN_ID) + .from(self.from) + .nonce(self.nonce) + .value(self.value) + .data(self.data.clone()) + .gas(self.gas_limit) + .gas_price(self.gas_price); + + if let Some(to) = self.to { + request = request.to(to); + } + + request + } } diff --git a/testool/src/statetest/yaml.rs b/testool/src/statetest/yaml.rs index 967e4ec8e8..ae4bb6623c 100644 --- a/testool/src/statetest/yaml.rs +++ b/testool/src/statetest/yaml.rs @@ -3,18 +3,16 @@ use super::{ spec::{AccountMatch, Env, StateTest, DEFAULT_BASE_FEE}, }; use crate::{utils::MainnetFork, Compiler}; -use anyhow::{bail, Context, Result}; +use anyhow::{anyhow, bail, Context, Result}; use eth_types::{geth_types::Account, Address, Bytes, H256, U256}; use ethers_core::{k256::ecdsa::SigningKey, utils::secret_key_to_address}; use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, HashMap, HashSet}, convert::TryInto, str::FromStr, }; use yaml_rust::Yaml; -type Label = String; - #[derive(Debug, Clone)] enum Ref { Any, @@ -76,7 +74,7 @@ impl<'a> YamlStateTestBuilder<'a> { // parse pre (account states before executing the transaction) let pre: BTreeMap = self - .parse_accounts(&yaml_test["pre"])? + .parse_accounts(&yaml_test["pre"], None)? .into_iter() .map(|(addr, account)| (addr, account.try_into().expect("unable to parse account"))) .collect(); @@ -104,12 +102,19 @@ impl<'a> YamlStateTestBuilder<'a> { .map(Self::parse_u256) .collect::>()?; - let max_fee_per_gas = Self::parse_u256(&yaml_transaction["maxFeePerGas"]) - .unwrap_or_else(|_| U256::zero()); - let gas_price = - Self::parse_u256(&yaml_transaction["gasPrice"]).unwrap_or(max_fee_per_gas); + let max_priority_fee_per_gas = + Self::parse_u256(&yaml_transaction["maxPriorityFeePerGas"]).ok(); + let max_fee_per_gas = Self::parse_u256(&yaml_transaction["maxFeePerGas"]).ok(); + + // Set gas price to `min(max_priority_fee_per_gas + base_fee, max_fee_per_gas)` for + // EIP-1559 transaction. + // + let gas_price = Self::parse_u256(&yaml_transaction["gasPrice"]).unwrap_or_else(|_| { + max_fee_per_gas + .unwrap() + .min(max_priority_fee_per_gas.unwrap() + env.current_base_fee) + }); - // TODO handle maxPriorityFeePerGas & maxFeePerGas 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"])?; @@ -143,7 +148,10 @@ impl<'a> YamlStateTestBuilder<'a> { let data_refs = Self::parse_refs(&expect["indexes"]["data"])?; let gas_refs = Self::parse_refs(&expect["indexes"]["gas"])?; let value_refs = Self::parse_refs(&expect["indexes"]["value"])?; - let result = self.parse_accounts(&expect["result"])?; + + // Pass the account addresses before transaction as expected for result. + let expected_addresses = pre.keys().collect(); + let result = self.parse_accounts(&expect["result"], Some(&expected_addresses))?; if MainnetFork::in_network_range(&networks)? { expects.push((exception, data_refs, gas_refs, value_refs, result)); @@ -152,14 +160,14 @@ impl<'a> YamlStateTestBuilder<'a> { // generate all the tests defined in the transaction by generating product of // data x gas x value - for (idx_data, data) in data_s.iter().enumerate() { + for (idx_data, calldata) in data_s.iter().enumerate() { for (idx_gas, gas_limit) in gas_limit_s.iter().enumerate() { for (idx_value, value) in value_s.iter().enumerate() { // find the first result that fulfills the pattern for (exception, data_refs, gas_refs, value_refs, result) in &expects { // check if this result can be applied to the current test let mut data_label = String::new(); - if let Some(label) = &data.1 { + if let Some(label) = &calldata.label { if !data_refs.contains_label(label) { continue; } @@ -189,10 +197,13 @@ impl<'a> YamlStateTestBuilder<'a> { secret_key: secret_key.clone(), to, gas_limit: *gas_limit, + max_priority_fee_per_gas, + max_fee_per_gas, gas_price, nonce, value: *value, - data: data.0.clone(), + data: calldata.data.clone(), + access_list: calldata.access_list.clone(), exception: *exception, }); break; @@ -210,7 +221,7 @@ impl<'a> YamlStateTestBuilder<'a> { Ok(Env { current_base_fee: Self::parse_u256(&yaml["currentBaseFee"]) .unwrap_or_else(|_| U256::from(DEFAULT_BASE_FEE)), - current_coinbase: Self::parse_address(&yaml["currentCoinbase"])?, + current_coinbase: Self::parse_address(&yaml["currentCoinbase"], None)?, current_difficulty: Self::parse_u256(&yaml["currentDifficulty"])?, current_gas_limit: Self::parse_u64(&yaml["currentGasLimit"])?, current_number: Self::parse_u64(&yaml["currentNumber"])?, @@ -220,7 +231,11 @@ impl<'a> YamlStateTestBuilder<'a> { } /// parse a vector of address=>(storage,balance,code,nonce) entry - fn parse_accounts(&mut self, yaml: &Yaml) -> Result> { + fn parse_accounts( + &mut self, + yaml: &Yaml, + expected_addresses: Option<&HashSet<&Address>>, + ) -> Result> { let mut accounts = HashMap::new(); for (address, account) in yaml.as_hash().context("parse_hash")?.iter() { let acc_storage = &account["storage"]; @@ -235,7 +250,7 @@ impl<'a> YamlStateTestBuilder<'a> { } } - let address = Self::parse_address(address)?; + let address = Self::parse_address(address, expected_addresses)?; let account = AccountMatch { address, balance: if acc_balance.is_badvalue() { @@ -289,22 +304,43 @@ impl<'a> YamlStateTestBuilder<'a> { tags } /// returns the element as an address - fn parse_address(yaml: &Yaml) -> Result
{ - match yaml { - Yaml::Real(real) => Ok(Address::from_str(real)?), - Yaml::Integer(int) => { - let hex = format!("{int:0>40}"); - Ok(Address::from_slice(&hex::decode(hex)?)) + fn parse_address( + yaml: &Yaml, + expected_addresses: Option<&HashSet<&Address>>, + ) -> Result
{ + if let Some(as_str) = yaml.as_str() { + parse::parse_address(as_str) + } else if let Some(as_i64) = yaml.as_i64() { + // Try to convert the integer to hex if has expected addresses for + // accounts after transaction (result). + // e.g. 10 -> 0xa + if let Some(expected_addresses) = expected_addresses { + let address = Address::from_slice(&hex::decode(format!("{as_i64:040x}"))?); + if expected_addresses.contains(&address) { + return Ok(address); + } } - Yaml::String(str) => parse::parse_address(str), - _ => bail!("cannot parse address {yaml:?}"), + + // Format as a hex string directly for accounts before transaction (pre). + // e.g. 10 -> 0x10 + Ok(Address::from_slice(&hex::decode(format!("{as_i64:0>40}"))?)) + } else if let Yaml::Real(as_real) = yaml { + Ok(Address::from_str(as_real)?) + } else { + bail!("cannot parse address {yaml:?}"); } } /// returns the element as a to address fn parse_to_address(yaml: &Yaml) -> Result> { - let as_str = yaml.as_str().context("to_address_as_str")?; - parse::parse_to_address(as_str) + if let Some(as_str) = yaml.as_str() { + if as_str.trim().is_empty() { + return Ok(None); + } + parse::parse_to_address(as_str) + } else { + bail!("cannot parse to address {:?}", yaml); + } } /// returns the element as an array of bytes @@ -315,25 +351,32 @@ impl<'a> YamlStateTestBuilder<'a> { /// returns the element as calldata bytes, supports 0x, :raw, :abi, :yul and /// { LLL } - fn parse_calldata(&mut self, yaml: &Yaml) -> Result<(Bytes, Option