Skip to content

Commit

Permalink
EcMul gadget (scroll-tech#600)
Browse files Browse the repository at this point in the history
* precompile io table definition

* chore: ecc circuit (pre-req)

* chore: more witness data structs

* wip: assignment to the ecc circuit

* wip: pairing ops

* wip

* wip: assigned values to ecadd, ecmul

* wip: assigned ec ops

* wip: expose ecc table

* chore: refactor

* chore: docs

* ecc table dev load

* pairing op bytes

* chore: refactor to_bytes for pairing op

* add ecc table to evm circuit

* wip: test structure for ecc circuit

* add unequal or double

* tests for ecc circuit

* random values in tests (ecc circuit)

* fix: compilation

* fix: minor fixes

* fix: pairing op assert success value

* chore: remove part not required

* fix: compilation

* fix: docs for ecc table args

* chore: revert changes added by mistake

* feat: EcMul execution gadget and associated bus-mapping

* refactor: conform to new precompile gadget pattern

* test: add eip-196 specified test cases for ecmul

* fix: 32-byte scalar s needs to be reduced to Fr

* fix: handle point at infinity for pairing op

* fix: accommodate valid large scalar values

* fix: variadic size OK | ignore infinity points

* feat: add verifying constraints on scalar mod order of Fr

* fix: handling ecadd/ecdouble using ecc_chip::sum

* chore: refactor, clean code

* fix: add gadget conditions for infinity points and zero scalar

* fix: resolve merge conflict on ecc circuit

* fix: remove invalid flag on ecmul gadget

* fix: resolve merge conflict ecc circuit test

* fix: add padding gadget for bus-mapping

* fix: modulo copy constraints and infinity/empty inputs

* fix: doc

* chore: remove unnecessary parts | fmt

* feat: handle edge cases for ecAdd within ecc ciruit

* chore: clippy fix

* fix: copy lookups for precompile from callop

* chore: revision from PR review

* clippy fix

* fix: scalar s doesn't need to be RLC in the ecc table lookup

* fix: mod gadget can accept both EVM/Keccak randomness

* chore: one less phase2 cell

* chore: review comments

---------

Co-authored-by: Rohit Narurkar <[email protected]>
  • Loading branch information
darth-cy and roynalnaruto authored Jul 20, 2023
1 parent ac5f73e commit b348d3f
Show file tree
Hide file tree
Showing 11 changed files with 693 additions and 12 deletions.
49 changes: 48 additions & 1 deletion bus-mapping/src/circuit_input_builder/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,13 +996,60 @@ pub struct EcMulOp {
impl Default for EcMulOp {
fn default() -> Self {
let p = G1Affine::generator();
let s = Fr::from_raw([2, 0, 0, 0]);
let s = Fr::one();
let r = p.mul(s).into();
Self { p, s, r }
}
}

impl EcMulOp {
/// Creates a new EcMul op given the inputs and output.
pub fn new(p: G1Affine, s: Fr, r: G1Affine) -> Self {
assert_eq!(p.mul(s), r.into());
Self { p, s, r }
}

/// Creates a new EcMul op given input and output bytes from a precompile call.
pub fn new_from_bytes(input: &[u8], output: &[u8]) -> Self {
let copy_bytes = |buf: &mut [u8; 32], bytes: &[u8]| {
buf.copy_from_slice(bytes);
buf.reverse();
};

assert_eq!(input.len(), 96);
assert_eq!(output.len(), 64);

let mut buf = [0u8; 32];

let p: G1Affine = {
copy_bytes(&mut buf, &input[0x00..0x20]);
Fq::from_bytes(&buf).and_then(|x| {
copy_bytes(&mut buf, &input[0x20..0x40]);
Fq::from_bytes(&buf).and_then(|y| G1Affine::from_xy(x, y))
})
}
.unwrap();

let s = Fr::from_raw(Word::from_big_endian(&input[0x40..0x60]).0);

let r_specified: G1Affine = {
copy_bytes(&mut buf, &output[0x00..0x20]);
Fq::from_bytes(&buf).and_then(|x| {
copy_bytes(&mut buf, &output[0x20..0x40]);
Fq::from_bytes(&buf).and_then(|y| G1Affine::from_xy(x, y))
})
}
.unwrap();

assert_eq!(G1Affine::from(p.mul(s)), r_specified);

Self {
p,
s,
r: r_specified,
}
}

/// A check on the op to tell the ECC Circuit whether or not to skip the op.
pub fn skip_by_ecc_circuit(&self) -> bool {
self.p.is_identity().into() || self.s.is_zero().into()
Expand Down
26 changes: 26 additions & 0 deletions bus-mapping/src/evm/opcodes/precompiles/ec_mul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::{
circuit_input_builder::{CircuitInputStateRef, EcMulOp, ExecStep, PrecompileEvent},
precompile::{EcMulAuxData, PrecompileAuxData},
};

pub(crate) fn handle(
input_bytes: Option<Vec<u8>>,
output_bytes: Option<Vec<u8>>,
state: &mut CircuitInputStateRef,
exec_step: &mut ExecStep,
) {
let input_bytes = input_bytes.map_or(vec![0u8; 96], |mut bytes| {
bytes.resize(96, 0u8);
bytes
});
let output_bytes = output_bytes.map_or(vec![0u8; 64], |mut bytes| {
bytes.resize(64, 0u8);
bytes
});

let aux_data = EcMulAuxData::new(&input_bytes, &output_bytes);
exec_step.aux_data = Some(PrecompileAuxData::EcMul(aux_data));

let ec_mul_op = EcMulOp::new_from_bytes(&input_bytes, &output_bytes);
state.push_precompile_event(PrecompileEvent::EcMul(ec_mul_op));
}
5 changes: 5 additions & 0 deletions bus-mapping/src/evm/opcodes/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ use crate::{
};

mod ec_add;
mod ec_mul;
mod ecrecover;

use ec_add::handle as handle_ec_add;
use ec_mul::handle as handle_ec_mul;
use ecrecover::handle as handle_ecrecover;

type InOutRetData = (Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>);
Expand All @@ -35,6 +37,9 @@ pub fn gen_associated_ops(
PrecompileCalls::Bn128Add => {
handle_ec_add(input_bytes, output_bytes, state, &mut exec_step)
}
PrecompileCalls::Bn128Mul => {
handle_ec_mul(input_bytes, output_bytes, state, &mut exec_step)
}
_ => {}
}

Expand Down
39 changes: 39 additions & 0 deletions bus-mapping/src/precompile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use eth_types::{evm_types::GasCost, Address, ToBigEndian, Word};
use revm_precompile::{Precompile, Precompiles};
use strum::EnumIter;

use crate::circuit_input_builder::EcMulOp;

/// Check if address is a precompiled or not.
pub fn is_precompiled(address: &Address) -> bool {
Precompiles::berlin()
Expand Down Expand Up @@ -199,13 +201,50 @@ impl EcAddAuxData {
}
}

/// Auxiliary data for EcMul, i.e. s * P = R
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct EcMulAuxData {
/// x co-ordinate of the point.
pub p_x: Word,
/// y co-ordinate of the point.
pub p_y: Word,
/// scalar.
pub s: Word,
/// unmodulated scalar
pub s_raw: Word,
/// x co-ordinate of the result point.
pub r_x: Word,
/// y co-ordinate of the result point.
pub r_y: Word,
}

impl EcMulAuxData {
/// Create a new instance of EcMul auxiliary data.
pub fn new(input: &[u8], output: &[u8]) -> Self {
assert_eq!(input.len(), 96);
assert_eq!(output.len(), 64);
let ec_mul_op = EcMulOp::new_from_bytes(input, output);

Self {
p_x: Word::from_big_endian(&input[0x00..0x20]),
p_y: Word::from_big_endian(&input[0x20..0x40]),
s: Word::from_little_endian(&ec_mul_op.s.to_bytes()),
s_raw: Word::from_big_endian(&input[0x40..0x60]),
r_x: Word::from_big_endian(&output[0x00..0x20]),
r_y: Word::from_big_endian(&output[0x20..0x40]),
}
}
}

/// Auxiliary data attached to an internal state for precompile verification.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PrecompileAuxData {
/// Ecrecover.
Ecrecover(EcrecoverAuxData),
/// EcAdd.
EcAdd(EcAddAuxData),
/// EcMul.
EcMul(EcMulAuxData),
}

impl Default for PrecompileAuxData {
Expand Down
5 changes: 2 additions & 3 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ use opcode_not::NotGadget;
use origin::OriginGadget;
use pc::PcGadget;
use pop::PopGadget;
use precompiles::{EcAddGadget, EcrecoverGadget, IdentityGadget};
use precompiles::{EcAddGadget, EcMulGadget, EcrecoverGadget, IdentityGadget};
use push::PushGadget;
use return_revert::ReturnRevertGadget;
use returndatacopy::ReturnDataCopyGadget;
Expand Down Expand Up @@ -354,8 +354,7 @@ pub(crate) struct ExecutionConfig<F> {
precompile_identity_gadget: Box<IdentityGadget<F>>,
precompile_modexp_gadget: Box<BasePrecompileGadget<F, { ExecutionState::PrecompileBigModExp }>>,
precompile_bn128add_gadget: Box<EcAddGadget<F>>,
precompile_bn128mul_gadget:
Box<BasePrecompileGadget<F, { ExecutionState::PrecompileBn256ScalarMul }>>,
precompile_bn128mul_gadget: Box<EcMulGadget<F>>,
precompile_bn128pairing_gadget:
Box<BasePrecompileGadget<F, { ExecutionState::PrecompileBn256Pairing }>>,
precompile_blake2f_gadget: Box<BasePrecompileGadget<F, { ExecutionState::PrecompileBlake2f }>>,
Expand Down
2 changes: 1 addition & 1 deletion zkevm-circuits/src/evm_circuit/execution/mulmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub(crate) struct MulModGadget<F> {
a_reduced: util::Word<F>,
d: util::Word<F>,
e: util::Word<F>,
modword: ModGadget<F>,
modword: ModGadget<F, true>,
mul512_left: MulAddWords512Gadget<F>,
mul512_right: MulAddWords512Gadget<F>,
n_is_zero: IsZeroGadget<F>,
Expand Down
Loading

0 comments on commit b348d3f

Please sign in to comment.