Skip to content

Commit

Permalink
Exponentiation circuit and EXP opcode (privacy-scaling-explorations#700)
Browse files Browse the repository at this point in the history
* feat: simple muladd gadget

* feat: muladd gadget fewer columns, more rows

* feat: basic structure of EXP and exp_table/circuit

* feat: bus-mapping for EXP with exponentiation by squaring steps

* fix: interface for exp table lookup

* feat: basic constraints for exp circuit, refactor circuit layout

* feat: additional constraints for exponentiation circuit

* feat: assignments for exponentiation table

* fix: minor fixes in exp circuit and exp gadget. NOT TESTED

* fix: handle exponent cases 0,1 and fix constraints for tests

* chore: comments and minor refactoring

* fix: constraints for exponentiation circuit, first tests

* test: more test cases for exp circuit

* chore: add exp circuit/table to super circuit

* use array of 33 boolean cells to determine byte size of exponent

* make clippy happy

* fix: fetching most significant byte fixed, tests passing

* chore: refactor into byte size gadget

* chore: remove unnecessary LoC, some doc

* fix: minor constraint fix, add constraints for multiplicand value

* chore: add a TODO

* fix: constraints to verify division by 2 was correct

* fix: updates based on recent upstream changes

* fix: add missing constraints for a and b (exp by squaring updates)

* fix: intermediate steps generated in exp by squaring

* fix: ensure last step is included (verification through idx transition)

* feat: enforce is_last exists (additional lookup) and some TODOs

* chore: refactor constraint into the same condition

* feat: add identifier (some todos)

* feat: padding row at the end of exp trace

* feat: validate odd/even parity with conditional fixed lookup

* fix: exponent hi was not taken into account for is_zero and is_one comparison

* fix: identifier | is_pad constraints, 2*n + k parity check using mul gadget

* tests: add the previously failing test, verify that parity check is OK

* fix: remove redundant column (not used anymore)

* chore: rename field to concise

* fix: assignment to exp table matches exp circuit block assignment

* fix: is_pad added to lookup, no need for mul gadget in exp gadget

* fix: no overflow in parity check gadget

* fix: exp circuit layout

* fix: compilation after rebase

* fix negative tests

* remove redundant addition

* fix: c == 0 and is_last bool check

* fix: comment on the correct line

Co-authored-by: adria0.eth <[email protected]>

* fix: formatting

* fix: uncomment tests

* refactoring for better encapsulation

* fix: avoid conditional assignment

* chore: rebase and fmt

* fix: more efficient check for boolean formula

Co-authored-by: z2trillion <[email protected]>
Co-authored-by: Zhang Zhuo <[email protected]>
Co-authored-by: adria0.eth <[email protected]>
  • Loading branch information
4 people authored Oct 31, 2022
1 parent 103ddd7 commit 247bcff
Show file tree
Hide file tree
Showing 23 changed files with 1,905 additions and 23 deletions.
4 changes: 3 additions & 1 deletion bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ use core::fmt::Debug;
use eth_types::sign_types::{pk_bytes_le, pk_bytes_swap_endianness, SignData};
use eth_types::{self, geth_types, Address, GethExecStep, GethExecTrace, Word};
use ethers_providers::JsonRpcClient;
pub use execution::{CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, NumberOrHash};
pub use execution::{
CopyDataType, CopyEvent, CopyStep, ExecState, ExecStep, ExpEvent, ExpStep, NumberOrHash,
};
pub use input_state_ref::CircuitInputStateRef;
use itertools::Itertools;
use std::collections::HashMap;
Expand Down
15 changes: 12 additions & 3 deletions bus-mapping/src/circuit_input_builder/block.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Block-related utility module
use super::{execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep};
use super::{
execution::ExecState, transaction::Transaction, CircuitsParams, CopyEvent, ExecStep, ExpEvent,
};
use crate::{
operation::{OperationContainer, RWCounter},
Error,
Expand Down Expand Up @@ -79,6 +81,8 @@ pub struct Block {
pub copy_events: Vec<CopyEvent>,
/// Inputs to the SHA3 opcode
pub sha3_inputs: Vec<Vec<u8>>,
/// Exponentiation events in the block.
pub exp_events: Vec<ExpEvent>,
code: HashMap<Hash, Vec<u8>>,
/// Circuits Setup Paramteres
pub circuits_params: CircuitsParams,
Expand Down Expand Up @@ -127,6 +131,7 @@ impl Block {
},
},
copy_events: Vec::new(),
exp_events: Vec::new(),
code: HashMap::new(),
sha3_inputs: Vec::new(),
circuits_params,
Expand All @@ -146,7 +151,11 @@ impl Block {

impl Block {
/// Push a copy event to the block.
pub fn add_copy_event(&mut self, copy: CopyEvent) {
self.copy_events.push(copy);
pub fn add_copy_event(&mut self, event: CopyEvent) {
self.copy_events.push(event);
}
/// Push an exponentiation event to the block.
pub fn add_exp_event(&mut self, event: ExpEvent) {
self.exp_events.push(event);
}
}
38 changes: 37 additions & 1 deletion bus-mapping/src/circuit_input_builder/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
};
use eth_types::{
evm_types::{Gas, GasCost, OpcodeId, ProgramCounter},
GethExecStep, H256,
GethExecStep, Word, H256,
};
use gadgets::impl_expr;
use halo2_proofs::plonk::Expression;
Expand Down Expand Up @@ -262,3 +262,39 @@ impl CopyEvent {
source_rw_increase + destination_rw_increase
}
}

/// Intermediary multiplication step, representing `a * b == d (mod 2^256)`
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExpStep {
/// First multiplicand.
pub a: Word,
/// Second multiplicand.
pub b: Word,
/// Multiplication result.
pub d: Word,
}

impl From<(Word, Word, Word)> for ExpStep {
fn from(values: (Word, Word, Word)) -> Self {
Self {
a: values.0,
b: values.1,
d: values.2,
}
}
}

/// Event representating an exponentiation `a ^ b == d (mod 2^256)`.
#[derive(Clone, Debug)]
pub struct ExpEvent {
/// Identifier for the exponentiation trace.
pub identifier: usize,
/// Base `a` for the exponentiation.
pub base: Word,
/// Exponent `b` for the exponentiation.
pub exponent: Word,
/// Exponentiation result.
pub exponentiation: Word,
/// Intermediate multiplication results.
pub steps: Vec<ExpStep>,
}
12 changes: 9 additions & 3 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
use super::{
get_call_memory_offset_length, get_create_init_code, Block, BlockContext, Call, CallContext,
CallKind, CodeSource, CopyEvent, ExecState, ExecStep, Transaction, TransactionContext,
CallKind, CodeSource, CopyEvent, ExecState, ExecStep, ExpEvent, Transaction,
TransactionContext,
};
use crate::{
error::{get_step_reported_error, ExecError},
Expand Down Expand Up @@ -924,8 +925,13 @@ impl<'a> CircuitInputStateRef<'a> {
}

/// Push a copy event to the state.
pub fn push_copy(&mut self, copy: CopyEvent) {
self.block.add_copy_event(copy);
pub fn push_copy(&mut self, event: CopyEvent) {
self.block.add_copy_event(event);
}

/// Push a exponentiation event to the state.
pub fn push_exponentiation(&mut self, event: ExpEvent) {
self.block.add_exp_event(event)
}

pub(crate) fn get_step_err(
Expand Down
4 changes: 3 additions & 1 deletion bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod codecopy;
mod codesize;
mod create;
mod dup;
mod exp;
mod extcodecopy;
mod extcodehash;
mod extcodesize;
Expand Down Expand Up @@ -67,6 +68,7 @@ use codecopy::Codecopy;
use codesize::Codesize;
use create::DummyCreate;
use dup::Dup;
use exp::Exponentiation;
use extcodecopy::Extcodecopy;
use extcodehash::Extcodehash;
use extcodesize::Extcodesize;
Expand Down Expand Up @@ -133,7 +135,6 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
OpcodeId::SMOD => StackOnlyOpcode::<2, 1>::gen_associated_ops,
OpcodeId::ADDMOD => StackOnlyOpcode::<3, 1>::gen_associated_ops,
OpcodeId::MULMOD => StackOnlyOpcode::<3, 1>::gen_associated_ops,
OpcodeId::EXP => StackOnlyOpcode::<2, 1>::gen_associated_ops,
OpcodeId::SIGNEXTEND => StackOnlyOpcode::<2, 1>::gen_associated_ops,
OpcodeId::LT => StackOnlyOpcode::<2, 1>::gen_associated_ops,
OpcodeId::GT => StackOnlyOpcode::<2, 1>::gen_associated_ops,
Expand Down Expand Up @@ -161,6 +162,7 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
OpcodeId::GASPRICE => GasPrice::gen_associated_ops,
OpcodeId::CODECOPY => Codecopy::gen_associated_ops,
OpcodeId::CODESIZE => Codesize::gen_associated_ops,
OpcodeId::EXP => Exponentiation::gen_associated_ops,
OpcodeId::EXTCODESIZE => Extcodesize::gen_associated_ops,
OpcodeId::EXTCODECOPY => Extcodecopy::gen_associated_ops,
OpcodeId::RETURNDATASIZE => Returndatasize::gen_associated_ops,
Expand Down
103 changes: 103 additions & 0 deletions bus-mapping/src/evm/opcodes/exp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep, ExpEvent, ExpStep},
Error,
};
use eth_types::{GethExecStep, U256};

use super::Opcode;

#[derive(Clone, Copy, Debug)]
pub(crate) struct Exponentiation;

fn exp_by_squaring(base: U256, exponent: U256, steps: &mut Vec<ExpStep>) -> U256 {
if exponent.is_zero() {
return U256::one();
}
if exponent == U256::one() {
return base;
}

let (exponent_div2, odd) = exponent.div_mod(U256::from(2));
let exp1 = exp_by_squaring(base, exponent_div2, steps);
let (exp2, _) = exp1.overflowing_mul(exp1);
steps.push((exp1, exp1, exp2).into());

if odd.is_zero() {
// exponent is even
exp2
} else {
// exponent is odd
let (exp, _) = base.overflowing_mul(exp2);
steps.push((exp2, base, exp).into());
exp
}
}

impl Opcode for Exponentiation {
fn gen_associated_ops(
state: &mut CircuitInputStateRef,
geth_steps: &[GethExecStep],
) -> Result<Vec<ExecStep>, Error> {
let geth_step = &geth_steps[0];
let mut exec_step = state.new_step(geth_step)?;

let base = geth_step.stack.nth_last(0)?;
state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(0), base)?;
let exponent = geth_step.stack.nth_last(1)?;
state.stack_read(&mut exec_step, geth_step.stack.nth_last_filled(1), exponent)?;

let (exponentiation, _) = base.overflowing_pow(exponent);
state.stack_write(
&mut exec_step,
geth_steps[1].stack.last_filled(),
exponentiation,
)?;

let mut steps = Vec::new();
let exponentiation_calc = exp_by_squaring(base, exponent, &mut steps);
debug_assert_eq!(exponentiation, exponentiation_calc);
state.push_exponentiation(ExpEvent {
identifier: state.block_ctx.rwc.0,
base,
exponent,
exponentiation,
steps,
});

Ok(vec![exec_step])
}
}

#[cfg(test)]
mod tests {
use eth_types::U256;

use super::exp_by_squaring;

#[test]
fn test_exp_by_squaring() {
let mut steps = Vec::new();
let exp = exp_by_squaring(23u64.into(), 123u64.into(), &mut steps);
assert_eq!(
exp,
U256::from_dec_str(
"87180413255890732361416772728849128389641993872302935967571352892955279939527"
)
.unwrap()
);

let mut steps = Vec::new();
let exp = exp_by_squaring(3u64.into(), 13u64.into(), &mut steps);
assert_eq!(exp, 1594323u64.into());
assert_eq!(
steps,
vec![
(3.into(), 3.into(), 9.into()).into(),
(9.into(), 3.into(), 27.into()).into(),
(27.into(), 27.into(), 729.into()).into(),
(729.into(), 729.into(), 531441.into()).into(),
(531441.into(), 3.into(), 1594323.into()).into(),
]
);
}
}
2 changes: 2 additions & 0 deletions circuit-benchmarks/src/evm_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ impl<F: Field> Circuit<F> for TestCircuit<F> {
let block_table = BlockTable::construct(meta);
let copy_table = [(); 11].map(|_| meta.advice_column());
let keccak_table = [(); 4].map(|_| meta.advice_column());
let exp_table = [(); 9].map(|_| meta.advice_column());
// Use constant expression to mock constant instance column for a more
// reasonable benchmark.
let power_of_randomness = [(); 31].map(|_| Expression::Constant(F::one()));
Expand All @@ -41,6 +42,7 @@ impl<F: Field> Circuit<F> for TestCircuit<F> {
&block_table,
&copy_table,
&keccak_table,
&exp_table,
)
}

Expand Down
1 change: 1 addition & 0 deletions gadgets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod evm_word;
pub mod is_zero;
pub mod less_than;
pub mod monotone;
pub mod mul_add;
pub mod util;

use eth_types::Field;
Expand Down
Loading

0 comments on commit 247bcff

Please sign in to comment.