Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
documentation and generalize word lo/hi assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
Wu Sung-Ming committed May 17, 2023
1 parent f749c6d commit d89eef8
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 50 deletions.
20 changes: 14 additions & 6 deletions zkevm-circuits/src/evm_circuit/execution/address.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::N_BYTES_HALF_WORD,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta},
AccountAddress, CachedRegion,
CachedRegion,
},
witness::{Block, Call, ExecStep, Transaction},
},
table::CallContextFieldTag,
util::{word::WordExpr, Expr},
util::{
word::{WordCell, WordExpr},
Expr,
},
};
use bus_mapping::evm::OpcodeId;
use eth_types::{Field, ToAddress, ToLittleEndian};
Expand All @@ -19,7 +23,7 @@ use halo2_proofs::plonk::Error;
#[derive(Clone, Debug)]
pub(crate) struct AddressGadget<F> {
same_context: SameContextGadget<F>,
address: AccountAddress<F>,
address: WordCell<F>,
}

impl<F: Field> ExecutionGadget<F> for AddressGadget<F> {
Expand All @@ -28,7 +32,7 @@ impl<F: Field> ExecutionGadget<F> for AddressGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::ADDRESS;

fn configure(cb: &mut EVMConstraintBuilder<F>) -> Self {
let address = cb.query_account_address();
let address = cb.query_word_unchecked();

// Lookup callee address in call context.
cb.call_context_lookup_read(None, CallContextFieldTag::CalleeAddress, address.to_word());
Expand Down Expand Up @@ -66,8 +70,12 @@ impl<F: Field> ExecutionGadget<F> for AddressGadget<F> {
let address = block.rws[step.rw_indices[1]].stack_value();
debug_assert_eq!(call.address, address.to_address());

self.address
.assign(region, offset, Some(address.to_le_bytes()))?;
self.address.assign_lo_hi(
region,
offset,
address.to_le_bytes()[0..N_BYTES_HALF_WORD].try_into().ok(),
address.to_le_bytes()[N_BYTES_HALF_WORD..].try_into().ok(),
)?;

Ok(())
}
Expand Down
29 changes: 14 additions & 15 deletions zkevm-circuits/src/evm_circuit/execution/caller.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use crate::{
evm_circuit::{
execution::ExecutionGadget,
param::N_BYTES_ACCOUNT_ADDRESS,
param::N_BYTES_HALF_WORD,
step::ExecutionState,
util::{
common_gadget::SameContextGadget,
constraint_builder::{EVMConstraintBuilder, StepStateTransition, Transition::Delta},
from_bytes, CachedRegion, RandomLinearCombination,
CachedRegion,
},
witness::{Block, Call, ExecStep, Transaction},
},
table::CallContextFieldTag,
util::Expr,
util::{
word::{WordCell, WordExpr},
Expr,
},
};
use bus_mapping::evm::OpcodeId;
use eth_types::{Field, ToLittleEndian};
Expand All @@ -21,7 +24,7 @@ use halo2_proofs::plonk::Error;
pub(crate) struct CallerGadget<F> {
same_context: SameContextGadget<F>,
// Using RLC to match against rw_table->stack_op value
caller_address: RandomLinearCombination<F, N_BYTES_ACCOUNT_ADDRESS>,
caller_address: WordCell<F>,
}

impl<F: Field> ExecutionGadget<F> for CallerGadget<F> {
Expand All @@ -30,18 +33,17 @@ impl<F: Field> ExecutionGadget<F> for CallerGadget<F> {
const EXECUTION_STATE: ExecutionState = ExecutionState::CALLER;

fn configure(cb: &mut EVMConstraintBuilder<F>) -> Self {
let caller_address = cb.query_word_rlc();
let caller_address = cb.query_word_unchecked();

// Lookup rw_table -> call_context with caller address
cb.call_context_lookup(
false.expr(),
cb.call_context_lookup_read(
None, // cb.curr.state.call_id
CallContextFieldTag::CallerAddress,
from_bytes::expr(&caller_address.cells),
caller_address.to_word(),
);

// Push the value to the stack
cb.stack_push(caller_address.expr());
cb.stack_push(caller_address.to_word());

// State transition
let opcode = cb.query_cell();
Expand Down Expand Up @@ -73,14 +75,11 @@ impl<F: Field> ExecutionGadget<F> for CallerGadget<F> {

let caller = block.rws[step.rw_indices[1]].stack_value();

self.caller_address.assign(
self.caller_address.assign_lo_hi(
region,
offset,
Some(
caller.to_le_bytes()[..N_BYTES_ACCOUNT_ADDRESS]
.try_into()
.unwrap(),
),
caller.to_le_bytes()[..N_BYTES_HALF_WORD].try_into().ok(),
caller.to_le_bytes()[N_BYTES_HALF_WORD..].try_into().ok(),
)?;

Ok(())
Expand Down
3 changes: 3 additions & 0 deletions zkevm-circuits/src/evm_circuit/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ pub(crate) const MAX_N_BYTES_INTEGER: usize = 31;
// Number of bytes an EVM word has.
pub(crate) const N_BYTES_WORD: usize = 32;

// Number of bytes an half EVM word has.
pub(crate) const N_BYTES_HALF_WORD: usize = 16;

// Number of bytes an u64 has.
pub(crate) const N_BYTES_U64: usize = 8;

Expand Down
36 changes: 7 additions & 29 deletions zkevm-circuits/src/evm_circuit/util/math_gadget/min_max_word.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
use std::marker::PhantomData;

use crate::{
evm_circuit::util::{
constraint_builder::EVMConstraintBuilder, math_gadget::*, transpose_val_ret, CachedRegion,
},
evm_circuit::util::{constraint_builder::EVMConstraintBuilder, math_gadget::*, CachedRegion},
util::word::{Word, WordExpr},
};
use eth_types::Field;
use halo2_proofs::{
circuit::Value,
plonk::{Error, Expression},
};
use halo2_proofs::plonk::{Error, Expression};
/// Returns `rhs` when `lhs < rhs`, and returns `lhs` otherwise.
/// lhs and rhs `< 256**N_BYTES`
/// `N_BYTES` is required to be `<= MAX_N_BYTES_INTEGER`.
Expand Down Expand Up @@ -48,28 +43,11 @@ impl<F: Field, T: WordExpr<F> + Clone> MinMaxWordGadget<F, T> {
&self,
region: &mut CachedRegion<'_, '_, F>,
offset: usize,
lhs: Word<F>,
rhs: Word<F>,
) -> Result<(F, F), Error> {
let (lt, _) = self.lt.assign(region, offset, lhs, rhs)?;
Ok(if lt.is_zero_vartime() {
(rhs, lhs)
} else {
(lhs, rhs)
})
}

pub(crate) fn assign_value(
&self,
region: &mut CachedRegion<'_, '_, F>,
offset: usize,
lhs: Value<F>,
rhs: Value<F>,
) -> Result<Value<(F, F)>, Error> {
transpose_val_ret(
lhs.zip(rhs)
.map(|(lhs, rhs)| self.assign(region, offset, lhs, rhs)),
)
lhs: eth_types::Word,
rhs: eth_types::Word,
) -> Result<(), Error> {
self.lt.assign(region, offset, lhs, rhs)?;
Ok(())
}
}

Expand Down
57 changes: 57 additions & 0 deletions zkevm-circuits/src/util/word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ pub(crate) trait WordExpr<F> {
}

impl<F: Field, const N: usize> WordLimbs<Cell<F>, N> {
// N1 is number of bytes to assign, while N is number of limbs.
// N1 % N = 0 (also implies N1 >= N)
// If N1 > N, then N1 will be chunk into N1 / N size then aggregate to single expression
// then assign to N limbs respectively.
// e.g. N1 = 4 bytes, [b1, b2, b3, b4], and N = 2 limbs [l1, l2]
// It equivalent `l1.assign(b1.expr() + b2.expr * F(256))`, `l2.assign(b3.expr() + b4.expr *
// F(256))`
pub fn assign<const N1: usize>(
&self,
region: &mut CachedRegion<'_, '_, F>,
Expand All @@ -70,6 +77,56 @@ impl<F: Field, const N: usize> WordLimbs<Cell<F>, N> {
})
}

// N_LO, N_HI are number of bytes to assign to first half and second half of size N limbs,
// respectively N_LO and N_HI can be different size, the only requirement is N_LO % (N/2)
// and N_HI % (N/2) [N/2] limbs will be assigned separately.
// E.g. N_LO = 4 => [nl1, nl2, nl3, nl4]
// N_HI = 2 => [nh1, nh2]
// N = 2 => [l1, l2]
// it equivalent l1.assign(nl1.expr() + nl2.expr() * 256 + nl3.expr() * 256^2 + nl3.expr() *
// 256^3) and l2.assign(nh1.expr() + nh2.expr() * 256)
pub fn assign_lo_hi<const N_LO: usize, const N_HI: usize>(
&self,
region: &mut CachedRegion<'_, '_, F>,
offset: usize,
bytes_lo: Option<[u8; N_LO]>,
bytes_hi: Option<[u8; N_HI]>,
) -> Result<Vec<AssignedCell<F, F>>, Error> {
assert_eq!(N % 2, 0); // TODO use static_assertion instead
assert_eq!(N_LO % (N / 2), 0);
assert_eq!(N_HI % (N / 2), 0);
// assign lo
let bytes_lo_assigned: Result<Vec<AssignedCell<F, F>>, Error> =
bytes_lo.map_or(Err(Error::Synthesis), |bytes| {
bytes
.chunks(N_LO / (N / 2)) // chunk in little endian
.map(|chunk| from_bytes::value(chunk))
.zip(self.limbs[0..(N / 2)].iter())
.map(|(value, cell)| cell.assign(region, offset, Value::known(value)))
.collect()
});

// assign hi
let bytes_hi_assigned: Result<Vec<AssignedCell<F, F>>, Error> =
bytes_hi.map_or(Err(Error::Synthesis), |bytes| {
bytes
.chunks(N_HI / (N / 2)) // chunk in little endian
.map(|chunk| from_bytes::value(chunk))
.zip(self.limbs[N / 2..].iter())
.map(|(value, cell)| cell.assign(region, offset, Value::known(value)))
.collect()
});

match (bytes_lo_assigned, bytes_hi_assigned) {
(Ok(bytes_lo_assignedcells), Ok(bytes_hi_assignedcells)) => Ok([
bytes_lo_assignedcells.to_vec(),
bytes_hi_assignedcells.to_vec(),
]
.concat()),
_ => Err(Error::Synthesis),
}
}

pub fn word_expr(&self) -> WordLimbs<Expression<F>, N> {
return WordLimbs::new(self.limbs.map(|cell| cell.expr()));
}
Expand Down

0 comments on commit d89eef8

Please sign in to comment.