From 697893f87ad9c4acbf323cb1ac9cd3f784d2594b Mon Sep 17 00:00:00 2001 From: Rohit Narurkar Date: Fri, 11 Aug 2023 06:18:59 +0100 Subject: [PATCH] `EccCircuit` (related to `EcPairing`) multiple fixes (#756) * fix: several fixes | wip debuging * remove unnecessary part * fix: assert equal for op output and success * fix: G2 coeffs * chore: remove info log --- .../src/circuit_input_builder/execution.rs | 8 +- .../src/evm/opcodes/precompiles/ec_pairing.rs | 8 +- zkevm-circuits/src/ecc_circuit.rs | 33 +-- zkevm-circuits/src/evm_circuit.rs | 4 +- zkevm-circuits/src/super_circuit.rs | 4 +- zkevm-circuits/src/super_circuit/test.rs | 209 +++++++++++++++++- zkevm-circuits/src/table.rs | 2 +- 7 files changed, 237 insertions(+), 31 deletions(-) diff --git a/bus-mapping/src/circuit_input_builder/execution.rs b/bus-mapping/src/circuit_input_builder/execution.rs index 5430f1ddb7..196a131b7d 100644 --- a/bus-mapping/src/circuit_input_builder/execution.rs +++ b/bus-mapping/src/circuit_input_builder/execution.rs @@ -1101,10 +1101,10 @@ impl EcPairingPair { /// Returns the big-endian representation of the G2 point in the pair. pub fn g2_bytes_be(&self) -> Vec { std::iter::empty() - .chain(self.g2_point.x.c0.to_bytes().iter().rev()) .chain(self.g2_point.x.c1.to_bytes().iter().rev()) - .chain(self.g2_point.y.c0.to_bytes().iter().rev()) + .chain(self.g2_point.x.c0.to_bytes().iter().rev()) .chain(self.g2_point.y.c1.to_bytes().iter().rev()) + .chain(self.g2_point.y.c0.to_bytes().iter().rev()) .cloned() .collect() } @@ -1114,10 +1114,10 @@ impl EcPairingPair { std::iter::empty() .chain(self.g1_point.x.to_bytes().iter().rev()) .chain(self.g1_point.y.to_bytes().iter().rev()) - .chain(self.g2_point.x.c0.to_bytes().iter().rev()) .chain(self.g2_point.x.c1.to_bytes().iter().rev()) - .chain(self.g2_point.y.c0.to_bytes().iter().rev()) + .chain(self.g2_point.x.c0.to_bytes().iter().rev()) .chain(self.g2_point.y.c1.to_bytes().iter().rev()) + .chain(self.g2_point.y.c0.to_bytes().iter().rev()) .cloned() .collect() } diff --git a/bus-mapping/src/evm/opcodes/precompiles/ec_pairing.rs b/bus-mapping/src/evm/opcodes/precompiles/ec_pairing.rs index 98ad8fd99c..43aebadd58 100644 --- a/bus-mapping/src/evm/opcodes/precompiles/ec_pairing.rs +++ b/bus-mapping/src/evm/opcodes/precompiles/ec_pairing.rs @@ -64,12 +64,12 @@ pub(crate) fn opt_data( .unwrap(); G2Affine { x: Fq2 { - c0: g2_x1, - c1: g2_x2, + c0: g2_x2, + c1: g2_x1, }, y: Fq2 { - c0: g2_y1, - c1: g2_y2, + c0: g2_y2, + c1: g2_y1, }, } }; diff --git a/zkevm-circuits/src/ecc_circuit.rs b/zkevm-circuits/src/ecc_circuit.rs index a3926ec07f..dee6fdea0f 100644 --- a/zkevm-circuits/src/ecc_circuit.rs +++ b/zkevm-circuits/src/ecc_circuit.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; use bus_mapping::{ - circuit_input_builder::{EcAddOp, EcMulOp, EcPairingOp}, + circuit_input_builder::{EcAddOp, EcMulOp, EcPairingOp, N_BYTES_PER_PAIR, N_PAIRING_PER_OP}, precompile::PrecompileCalls, }; use eth_types::{Field, ToScalar}; @@ -176,7 +176,7 @@ impl EccCircuit { let keccak_powers = std::iter::successors(Some(Value::known(F::one())), |coeff| { Some(challenges.keccak_input() * coeff) }) - .take(4 * 192) + .take(N_PAIRING_PER_OP * N_BYTES_PER_PAIR) .map(|x| QuantumCell::Witness(x)) .collect_vec(); @@ -561,12 +561,12 @@ impl EccCircuit { }; G1Assigned { decomposed, - x_rlc: pairing_chip.fp_chip.range.gate.inner_product( + x_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, x_cells, powers_of_rand.iter().cloned(), ), - y_rlc: pairing_chip.fp_chip.range.gate.inner_product( + y_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, y_cells, powers_of_rand.iter().cloned(), @@ -593,22 +593,22 @@ impl EccCircuit { }; G2Assigned { decomposed, - x_c0_rlc: pairing_chip.fp_chip.range.gate.inner_product( + x_c0_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, x_c0_cells, powers_of_rand.iter().cloned(), ), - x_c1_rlc: pairing_chip.fp_chip.range.gate.inner_product( + x_c1_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, x_c1_cells, powers_of_rand.iter().cloned(), ), - y_c0_rlc: pairing_chip.fp_chip.range.gate.inner_product( + y_c0_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, y_c0_cells, powers_of_rand.iter().cloned(), ), - y_c1_rlc: pairing_chip.fp_chip.range.gate.inner_product( + y_c1_rlc: ecc_chip.field_chip().range().gate().inner_product( ctx, y_c1_cells, powers_of_rand.iter().cloned(), @@ -628,18 +628,17 @@ impl EccCircuit { std::iter::empty() .chain(g1.decomposed.x_cells.iter().rev()) .chain(g1.decomposed.y_cells.iter().rev()) - .chain(g2.decomposed.x_c0_cells.iter().rev()) .chain(g2.decomposed.x_c1_cells.iter().rev()) - .chain(g2.decomposed.y_c0_cells.iter().rev()) + .chain(g2.decomposed.x_c0_cells.iter().rev()) .chain(g2.decomposed.y_c1_cells.iter().rev()) + .chain(g2.decomposed.y_c0_cells.iter().rev()) .cloned() - .rev() .collect::>>() }) .collect::>>(); - let input_rlc = pairing_chip.fp_chip.range.gate.inner_product( + let input_rlc = ecc_chip.field_chip().range().gate().inner_product( ctx, - input_cells, + input_cells.into_iter().rev(), powers_of_rand.iter().cloned(), ); @@ -662,12 +661,14 @@ impl EccCircuit { fp12_chip.is_equal(ctx, >, &one) }; + 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::Witness(Value::known( - op.output.to_scalar().expect("EcPairing output = {0, 1}"), - )), + QuantumCell::Existing(op_output), ); log::trace!("[ECC] EcPairingAssignment END:"); diff --git a/zkevm-circuits/src/evm_circuit.rs b/zkevm-circuits/src/evm_circuit.rs index 85cdc0c16d..c7898d43f7 100644 --- a/zkevm-circuits/src/evm_circuit.rs +++ b/zkevm-circuits/src/evm_circuit.rs @@ -326,6 +326,7 @@ impl SubCircuit for EvmCircuit { config.load_fixed_table(layouter, self.fixed_table_tags.clone())?; config.load_byte_table(layouter)?; + config.pow_of_rand_table.assign(layouter, challenges)?; let export = config.execution.assign_block(layouter, block, challenges)?; self.exports.borrow_mut().replace(export); Ok(()) @@ -516,9 +517,6 @@ impl Circuit for EvmCircuit { &block.get_ec_pairing_ops(), &challenges, )?; - config - .pow_of_rand_table - .dev_load(&mut layouter, &challenges)?; self.synthesize_sub(&config, &challenges, &mut layouter) } diff --git a/zkevm-circuits/src/super_circuit.rs b/zkevm-circuits/src/super_circuit.rs index 68efeea3dd..38d8a6ae01 100644 --- a/zkevm-circuits/src/super_circuit.rs +++ b/zkevm-circuits/src/super_circuit.rs @@ -643,6 +643,8 @@ impl< .synthesize_sub(&config.tx_circuit, challenges, layouter)?; self.sig_circuit .synthesize_sub(&config.sig_circuit, challenges, layouter)?; + self.ecc_circuit + .synthesize_sub(&config.ecc_circuit, challenges, layouter)?; self.modexp_circuit .synthesize_sub(&config.modexp_circuit, challenges, layouter)?; self.state_circuit @@ -804,7 +806,7 @@ impl< log::debug!("super circuit needs k = {}", k); let circuit = - SuperCircuit::::new_from_block(&block); + SuperCircuit::::new_from_block(&block); let instance = circuit.instance(); Ok((k, circuit, instance)) diff --git a/zkevm-circuits/src/super_circuit/test.rs b/zkevm-circuits/src/super_circuit/test.rs index 9e9327c52a..f445dd1f7a 100644 --- a/zkevm-circuits/src/super_circuit/test.rs +++ b/zkevm-circuits/src/super_circuit/test.rs @@ -1,5 +1,9 @@ pub use super::*; -use bus_mapping::{circuit_input_builder::keccak_inputs, evm::OpcodeId}; +use bus_mapping::{ + circuit_input_builder::keccak_inputs, + evm::{OpcodeId, PrecompileCallArgs}, + precompile::PrecompileCalls, +}; use ethers_signers::{LocalWallet, Signer}; use halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use log::error; @@ -9,7 +13,7 @@ use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use std::{collections::HashMap, env::set_var}; -use eth_types::{address, bytecode, geth_types::GethData, Bytecode, Word}; +use eth_types::{address, bytecode, geth_types::GethData, word, Bytecode, ToWord, Word}; #[test] fn super_circuit_degree() { @@ -210,6 +214,178 @@ pub(crate) fn block_2tx() -> GethData { block } +pub(crate) fn block_ec_ops() -> GethData { + let mut rng = ChaCha20Rng::seed_from_u64(2); + + let chain_id = *MOCK_CHAIN_ID; + + let bytecode_ec_add = PrecompileCallArgs { + name: "ecAdd (valid inputs)", + // P = (1, 2) + // Q = (1, 2) + setup_code: bytecode! { + // p_x = 1 + PUSH1(0x01) + PUSH1(0x00) + MSTORE + // p_y = 2 + PUSH1(0x02) + PUSH1(0x20) + MSTORE + // q_x = 1 + PUSH1(0x01) + PUSH1(0x40) + MSTORE + // q_y = 2 + PUSH1(0x02) + PUSH1(0x60) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x80.into(), + ret_offset: 0x80.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Add.address().to_word(), + ..Default::default() + } + .with_call_op(OpcodeId::STATICCALL); + let bytecode_ec_mul = PrecompileCallArgs { + name: "ecMul (valid input)", + // P = (2, 16059845205665218889595687631975406613746683471807856151558479858750240882195) + // s = 7 + setup_code: bytecode! { + // p_x + PUSH1(0x02) + PUSH1(0x00) + MSTORE + // p_y + PUSH32(word!("0x23818CDE28CF4EA953FE59B1C377FAFD461039C17251FF4377313DA64AD07E13")) + PUSH1(0x20) + MSTORE + // s + PUSH1(0x07) + PUSH1(0x40) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x60.into(), + ret_offset: 0x60.into(), + ret_size: 0x40.into(), + address: PrecompileCalls::Bn128Mul.address().to_word(), + ..Default::default() + } + .with_call_op(OpcodeId::CALL); + let bytecode_ec_pairing = PrecompileCallArgs { + name: "ecPairing (pairing true): 2 pairs", + setup_code: bytecode! { + // G1_x1 + PUSH32(word!("0x2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da")) + PUSH1(0x00) + MSTORE + // G1_y1 + PUSH32(word!("0x2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f6")) + PUSH1(0x20) + MSTORE + // G2_x11 + PUSH32(word!("0x1fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc")) + PUSH1(0x40) + MSTORE + // G2_x12 + PUSH32(word!("0x22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d9")) + PUSH1(0x60) + MSTORE + // G2_y11 + PUSH32(word!("0x2bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f90")) + PUSH1(0x80) + MSTORE + // G2_y12 + PUSH32(word!("0x2fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e")) + PUSH1(0xA0) + MSTORE + // G1_x2 + PUSH32(word!("0x0000000000000000000000000000000000000000000000000000000000000001")) + PUSH1(0xC0) + MSTORE + // G1_y2 + PUSH32(word!("0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45")) + PUSH1(0xE0) + MSTORE + // G2_x21 + PUSH32(word!("0x1971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4")) + PUSH2(0x100) + MSTORE + // G2_x22 + PUSH32(word!("0x091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc7")) + PUSH2(0x120) + MSTORE + // G2_y21 + PUSH32(word!("0x2a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea2")) + PUSH2(0x140) + MSTORE + // G2_y22 + PUSH32(word!("0x23a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc")) + PUSH2(0x160) + MSTORE + }, + call_data_offset: 0x00.into(), + call_data_length: 0x180.into(), + ret_offset: 0x180.into(), + ret_size: 0x20.into(), + address: PrecompileCalls::Bn128Pairing.address().to_word(), + ..Default::default() + } + .with_call_op(OpcodeId::DELEGATECALL); + + let wallet_a = LocalWallet::new(&mut rng).with_chain_id(chain_id); + + let addr_a = wallet_a.address(); + let addr_b = address!("0x000000000000000000000000000000000000BBBB"); + let addr_c = address!("0x000000000000000000000000000000000000CCCC"); + let addr_d = address!("0x000000000000000000000000000000000000DDDD"); + + let mut wallets = HashMap::new(); + wallets.insert(wallet_a.address(), wallet_a); + + // 4 accounts and 3 txs. + let mut block: GethData = TestContext::<4, 3>::new( + Some(vec![Word::zero()]), + |accs| { + accs[0].address(addr_a).balance(Word::from(1u64 << 20)); + accs[1] + .address(addr_b) + .balance(Word::from(1u64 << 20)) + .code(bytecode_ec_add); + accs[2] + .address(addr_c) + .balance(Word::from(1u64 << 20)) + .code(bytecode_ec_mul); + accs[3] + .address(addr_d) + .balance(Word::from(1u64 << 20)) + .code(bytecode_ec_pairing); + }, + |mut txs, accs| { + txs[0] + .from(accs[0].address) + .to(accs[1].address) + .gas(Word::from(1_000_000u64)); + txs[1] + .from(accs[0].address) + .to(accs[2].address) + .gas(Word::from(1_000_000u64)); + txs[2] + .from(accs[0].address) + .to(accs[3].address) + .gas(Word::from(1_000_000u64)); + }, + |block, _tx| block.number(0xcafeu64), + ) + .unwrap() + .into(); + block.sign(&wallets); + block +} + const TEST_MOCK_RANDOMNESS: u64 = 0x100; // High memory usage test. Run in serial with: @@ -357,3 +533,32 @@ fn serial_test_super_circuit_2tx_2max_tx() { circuits_params, ); } + +#[cfg(feature = "scroll")] +#[test] +fn test_super_circuit_ec_ops_txs() { + let block = block_ec_ops(); + const MAX_TXS: usize = 3; + const MAX_CALLDATA: usize = 320; + const MAX_INNER_BLOCKS: usize = 1; + const MAX_RWS: usize = 1024; + const MAX_COPY_ROWS: usize = 2048; + let circuits_params = CircuitsParams { + max_txs: MAX_TXS, + max_calldata: MAX_CALLDATA, + max_rws: MAX_RWS, + max_copy_rows: MAX_COPY_ROWS, + max_bytecode: 4096, + max_mpt_rows: 2048, + max_evm_rows: 0, + max_keccak_rows: 0, + max_inner_blocks: MAX_INNER_BLOCKS, + max_exp_steps: 256, + max_rlp_rows: 800, + ..Default::default() + }; + test_super_circuit::( + block, + circuits_params, + ); +} diff --git a/zkevm-circuits/src/table.rs b/zkevm-circuits/src/table.rs index 2cde0f82ca..53d42c6304 100644 --- a/zkevm-circuits/src/table.rs +++ b/zkevm-circuits/src/table.rs @@ -2801,7 +2801,7 @@ impl PowOfRandTable { } /// Assign values to the table. - pub fn dev_load( + pub fn assign( &self, layouter: &mut impl Layouter, challenges: &Challenges>,