Skip to content
This repository was archived by the owner on Jul 5, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
TransactionContext,
};
use crate::{
error::{get_step_reported_error, ExecError},
error::{get_step_reported_error, ExecError, InsufficientBalanceError, NonceUintOverflowError},
exec_trace::OperationRef,
operation::{
AccountField, AccountOp, CallContextField, CallContextOp, MemoryOp, Op, OpEnum, Operation,
Expand Down Expand Up @@ -434,6 +434,24 @@ impl<'a> CircuitInputStateRef<'a> {
Ok(())
}

/// Add address to access list for the current transaction.
pub fn tx_access_list_write(
&mut self,
step: &mut ExecStep,
address: Address,
) -> Result<(), Error> {
let is_warm = self.sdb.check_account_in_access_list(&address);
self.push_op_reversible(
step,
TxAccessListAccountOp {
tx_id: self.tx_ctx.id(),
address,
is_warm: true,
is_warm_prev: is_warm,
},
)
}

/// Push a write type [`TxAccessListAccountOp`] into the
/// [`OperationContainer`](crate::operation::OperationContainer) with the
/// next [`RWCounter`](crate::operation::RWCounter), and then
Expand Down Expand Up @@ -688,6 +706,18 @@ impl<'a> CircuitInputStateRef<'a> {
))
}

pub(crate) fn reversion_info_read(&mut self, step: &mut ExecStep, call: &Call) {
for (field, value) in [
(
CallContextField::RwCounterEndOfReversion,
call.rw_counter_end_of_reversion.to_word(),
),
(CallContextField::IsPersistent, call.is_persistent.to_word()),
] {
self.call_context_read(step, call.call_id, field, value);
}
}

/// Check if address is a precompiled or not.
pub fn is_precompiled(&self, address: &Address) -> bool {
address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19])
Expand Down Expand Up @@ -1299,7 +1329,23 @@ impl<'a> CircuitInputStateRef<'a> {
return Err(Error::AccountNotFound(sender));
}
if account.balance < value {
return Ok(Some(ExecError::InsufficientBalance));
return Ok(Some(ExecError::InsufficientBalance(match step.op {
OpcodeId::CALL | OpcodeId::CALLCODE => InsufficientBalanceError::Call,
OpcodeId::CREATE => InsufficientBalanceError::Create,
OpcodeId::CREATE2 => InsufficientBalanceError::Create2,
op => {
unreachable!("insufficient balance error unexpected for opcode: {:?}", op)
}
})));
}

// Nonce Uint overflow
if account.nonce >= u64::MAX.into() {
return Ok(Some(ExecError::NonceUintOverflow(match step.op {
OpcodeId::CREATE => NonceUintOverflowError::Create,
OpcodeId::CREATE2 => NonceUintOverflowError::Create2,
op => unreachable!("Nonce Uint overflow error unexpected for opcode: {:?}", op),
})));
}

// Address collision
Expand Down
6 changes: 4 additions & 2 deletions bus-mapping/src/circuit_input_builder/tracer_tests.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::*;
use crate::{
circuit_input_builder::access::gen_state_access_trace,
error::{ExecError, OogError},
error::{ExecError, InsufficientBalanceError, OogError},
geth_errors::{
GETH_ERR_GAS_UINT_OVERFLOW, GETH_ERR_OUT_OF_GAS, GETH_ERR_STACK_OVERFLOW,
GETH_ERR_STACK_UNDERFLOW,
Expand Down Expand Up @@ -288,7 +288,9 @@ fn tracer_err_insufficient_balance() {
let mut builder = CircuitInputBuilderTx::new(&block, step);
assert_eq!(
builder.state_ref().get_step_err(step, next_step).unwrap(),
Some(ExecError::InsufficientBalance)
Some(ExecError::InsufficientBalance(
InsufficientBalanceError::Call
))
);
}

Expand Down
28 changes: 25 additions & 3 deletions bus-mapping/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ pub enum OogError {
SelfDestruct,
}

/// Insufficient balance errors by opcode/state.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InsufficientBalanceError {
/// Insufficient balance during CALL/CALLCODE opcode.
Call,
/// Insufficient balance during CREATE opcode.
Create,
/// Insufficient balance during CREATE2 opcode.
Create2,
}

/// Nonce uint overflow errors by opcode/state.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NonceUintOverflowError {
/// Nonce uint overflow during CREATE opcode.
Create,
/// Nonce uint overflow during CREATE2 opcode.
Create2,
}

/// EVM Execution Error
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExecError {
Expand All @@ -116,9 +136,9 @@ pub enum ExecError {
WriteProtection,
/// For CALL, CALLCODE, DELEGATECALL, STATICCALL
Depth,
/// For CALL, CALLCODE
InsufficientBalance,
/// For CREATE, CREATE2
/// For CALL, CALLCODE, CREATE, CREATE2
InsufficientBalance(InsufficientBalanceError),
/// For CREATE2
ContractAddressCollision,
/// contract must not begin with 0xef due to EIP #3541 EVM Object Format
/// (EOF)
Expand All @@ -131,6 +151,8 @@ pub enum ExecError {
CodeStoreOutOfGas,
/// For RETURN in a CREATE, CREATE2
MaxCodeSizeExceeded,
/// For CREATE, CREATE2
NonceUintOverflow(NonceUintOverflowError),
}

// TODO: Move to impl block.
Expand Down
23 changes: 20 additions & 3 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Definition of each opcode of the EVM.
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep},
error::{ExecError, OogError},
error::{ExecError, InsufficientBalanceError, NonceUintOverflowError, OogError},
evm::OpcodeId,
operation::{
AccountField, AccountOp, CallContextField, TxAccessListAccountOp, TxReceiptField,
Expand Down Expand Up @@ -278,8 +278,25 @@ fn fn_gen_error_state_associated_ops(error: &ExecError) -> Option<FnGenAssociate
ExecError::OutOfGas(OogError::SloadSstore) => Some(OOGSloadSstore::gen_associated_ops),
ExecError::StackOverflow => Some(ErrorSimple::gen_associated_ops),
ExecError::StackUnderflow => Some(ErrorSimple::gen_associated_ops),
// call & callcode can encounter InsufficientBalance error, Use pop-7 generic CallOpcode
ExecError::InsufficientBalance => Some(CallOpcode::<7>::gen_associated_ops),
ExecError::InsufficientBalance(InsufficientBalanceError::Call) => {
Some(CallOpcode::<7>::gen_associated_ops)
}
// create & create2 can encounter insufficient balance.
ExecError::InsufficientBalance(InsufficientBalanceError::Create) => {
Some(DummyCreate::<false>::gen_associated_ops)
}
ExecError::InsufficientBalance(InsufficientBalanceError::Create2) => {
Some(DummyCreate::<true>::gen_associated_ops)
}
// only create2 may cause ContractAddressCollision error, so use DummyCreate::<true>.
ExecError::ContractAddressCollision => Some(DummyCreate::<true>::gen_associated_ops),
// create & create2 can encounter nonce uint overflow.
ExecError::NonceUintOverflow(NonceUintOverflowError::Create) => {
Some(DummyCreate::<false>::gen_associated_ops)
}
ExecError::NonceUintOverflow(NonceUintOverflowError::Create2) => {
Some(DummyCreate::<true>::gen_associated_ops)
}
ExecError::WriteProtection => Some(ErrorWriteProtection::gen_associated_ops),
ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops),

Expand Down
2 changes: 1 addition & 1 deletion mock/src/test_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub use external_tracer::LoggerConfig;
/// // Now we can start generating the traces and items we need to inspect
/// // the behaviour of the generated env.
/// ```
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct TestContext<const NACC: usize, const NTX: usize> {
/// chain id
pub chain_id: Word,
Expand Down
123 changes: 77 additions & 46 deletions zkevm-circuits/src/copy_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub struct CopyCircuitConfig<F> {
pub is_last: Column<Advice>,
/// The value copied in this copy step.
pub value: Column<Advice>,
/// Random linear combination accumulator value.
pub value_acc: Column<Advice>,
/// Whether the row is padding.
pub is_pad: Column<Advice>,
/// In case of a bytecode tag, this denotes whether or not the copied byte
Expand Down Expand Up @@ -105,6 +107,7 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
let q_step = meta.complex_selector();
let is_last = meta.advice_column();
let value = meta.advice_column_in(SecondPhase);
let value_acc = meta.advice_column_in(SecondPhase);
let is_code = meta.advice_column();
let is_pad = meta.advice_column();
let is_first = copy_table.is_first;
Expand Down Expand Up @@ -222,23 +225,48 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
rw_diff,
);
});
cb.condition(
and::expr([
meta.query_advice(is_last, Rotation::cur()),
tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta),
]),
|cb| {
cb.require_equal(
"value == rlc_acc at the last row for RlcAcc",
meta.query_advice(value, Rotation::cur()),
meta.query_advice(rlc_acc, Rotation::cur()),
);
},
);

cb.gate(meta.query_fixed(q_enable, Rotation::cur()))
});

meta.create_gate(
"Last Step (check value accumulator) Memory => Bytecode",
|meta| {
let mut cb = BaseConstraintBuilder::default();

cb.require_equal(
"value_acc == rlc_acc on the last row",
meta.query_advice(value_acc, Rotation::next()),
meta.query_advice(rlc_acc, Rotation::next()),
);

cb.gate(and::expr([
meta.query_fixed(q_enable, Rotation::cur()),
meta.query_advice(is_last, Rotation::next()),
and::expr([
tag.value_equals(CopyDataType::Memory, Rotation::cur())(meta),
tag.value_equals(CopyDataType::Bytecode, Rotation::next())(meta),
]),
]))
},
);

meta.create_gate("Last Step (check value accumulator) RlcAcc", |meta| {
let mut cb = BaseConstraintBuilder::default();

cb.require_equal(
"value_acc == rlc_acc on the last row",
meta.query_advice(value_acc, Rotation::next()),
meta.query_advice(rlc_acc, Rotation::next()),
);

cb.gate(and::expr([
meta.query_fixed(q_enable, Rotation::cur()),
meta.query_advice(is_last, Rotation::next()),
tag.value_equals(CopyDataType::RlcAcc, Rotation::next())(meta),
]))
});

meta.create_gate("verify step (q_step == 1)", |meta| {
let mut cb = BaseConstraintBuilder::default();

Expand All @@ -262,25 +290,30 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
);
},
);
cb.require_equal(
"write value == read value",
meta.query_advice(value, Rotation::cur()),
meta.query_advice(value, Rotation::next()),
);
cb.require_equal(
"value_acc is same for read-write rows",
meta.query_advice(value_acc, Rotation::cur()),
meta.query_advice(value_acc, Rotation::next()),
);
cb.condition(
not::expr(tag.value_equals(CopyDataType::RlcAcc, Rotation::next())(
meta,
)),
and::expr([
not::expr(meta.query_advice(is_last, Rotation::next())),
not::expr(meta.query_advice(is_pad, Rotation::cur())),
]),
|cb| {
cb.require_equal(
"write value == read value (if not rlc acc)",
meta.query_advice(value, Rotation::cur()),
meta.query_advice(value, Rotation::next()),
"value_acc(2) == value_acc(0) * r + value(2)",
meta.query_advice(value_acc, Rotation(2)),
meta.query_advice(value_acc, Rotation::cur()) * challenges.keccak_input()
+ meta.query_advice(value, Rotation(2)),
);
},
);
cb.condition(meta.query_advice(is_first, Rotation::cur()), |cb| {
cb.require_equal(
"write value == read value (is_first == 1)",
meta.query_advice(value, Rotation::cur()),
meta.query_advice(value, Rotation::next()),
);
});
cb.require_zero(
"value == 0 when is_pad == 1 for read",
and::expr([
Expand All @@ -298,25 +331,9 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
meta.query_advice(is_pad, Rotation::next()),
);

cb.gate(meta.query_selector(q_step))
});

meta.create_gate("verify_step (q_step == 0)", |meta| {
let mut cb = BaseConstraintBuilder::default();

cb.require_equal(
"rows[2].value == rows[0].value * r + rows[1].value",
meta.query_advice(value, Rotation(2)),
meta.query_advice(value, Rotation::cur()) * challenges.keccak_input()
+ meta.query_advice(value, Rotation::next()),
);

cb.gate(and::expr([
meta.query_fixed(q_enable, Rotation::cur()),
not::expr(meta.query_selector(q_step)),
not::expr(meta.query_advice(is_last, Rotation::cur())),
tag.value_equals(CopyDataType::RlcAcc, Rotation::cur())(meta),
not::expr(meta.query_advice(is_pad, Rotation::cur())),
meta.query_selector(q_step),
]))
});

Expand Down Expand Up @@ -402,6 +419,7 @@ impl<F: Field> SubCircuitConfig<F> for CopyCircuitConfig<F> {
q_step,
is_last,
value,
value_acc,
is_pad,
is_code,
q_enable,
Expand Down Expand Up @@ -463,9 +481,15 @@ impl<F: Field> CopyCircuitConfig<F> {
)?;

// is_last, value, is_pad, is_code
for (column, &(value, label)) in [self.is_last, self.value, self.is_pad, self.is_code]
.iter()
.zip_eq(circuit_row)
for (column, &(value, label)) in [
self.is_last,
self.value,
self.value_acc,
self.is_pad,
self.is_code,
]
.iter()
.zip_eq(circuit_row)
{
region.assign_advice(
|| format!("{} at row: {}", label, *offset),
Expand Down Expand Up @@ -616,6 +640,13 @@ impl<F: Field> CopyCircuitConfig<F> {
*offset,
|| Value::known(F::zero()),
)?;
// value_acc
region.assign_advice(
|| format!("assign value_acc {}", *offset),
self.value_acc,
*offset,
|| Value::known(F::zero()),
)?;
// rlc_acc
region.assign_advice(
|| format!("assign rlc_acc {}", *offset),
Expand Down
Loading