Skip to content

Commit

Permalink
Circuit for oog account access error (scroll-tech#426)
Browse files Browse the repository at this point in the history
* add buss mapping

* implement circuit gadget

* fix oog account root test

* test EXTCODEHASH

* refactor root tests

* misc update

* buss mapping test
  • Loading branch information
DreamWuGit authored Apr 4, 2023
1 parent c95f4ae commit 3d406bf
Show file tree
Hide file tree
Showing 4 changed files with 496 additions and 2 deletions.
5 changes: 5 additions & 0 deletions bus-mapping/src/evm/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ mod error_codestore;
mod error_contract_address_collision;
mod error_invalid_creation_code;
mod error_invalid_jump;
mod error_oog_account_access;
mod error_oog_call;
mod error_oog_dynamic_memory;
mod error_oog_log;
Expand Down Expand Up @@ -89,6 +90,7 @@ use dup::Dup;
use error_codestore::ErrorCodeStore;
use error_invalid_creation_code::ErrorCreationCode;
use error_invalid_jump::InvalidJump;
use error_oog_account_access::ErrorOOGAccountAccess;
use error_oog_call::OOGCall;
use error_oog_dynamic_memory::OOGDynamicMemory;
use error_oog_log::ErrorOOGLog;
Expand Down Expand Up @@ -306,6 +308,9 @@ fn fn_gen_error_state_associated_ops(
Some(StackOnlyOpcode::<2, 0, true>::gen_associated_ops)
}
ExecError::OutOfGas(OogError::SloadSstore) => Some(OOGSloadSstore::gen_associated_ops),
ExecError::OutOfGas(OogError::AccountAccess) => {
Some(ErrorOOGAccountAccess::gen_associated_ops)
}
// ExecError::
ExecError::StackOverflow => Some(StackOnlyOpcode::<0, 0, true>::gen_associated_ops),
ExecError::StackUnderflow => Some(StackOnlyOpcode::<0, 0, true>::gen_associated_ops),
Expand Down
176 changes: 176 additions & 0 deletions bus-mapping/src/evm/opcodes/error_oog_account_access.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use crate::{
circuit_input_builder::{CircuitInputStateRef, ExecStep},
error::{ExecError, OogError},
evm::{Opcode, OpcodeId},
operation::{CallContextField, TxAccessListAccountOp, RW},
Error,
};
use eth_types::{GethExecStep, ToAddress, U256};

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

impl Opcode for ErrorOOGAccountAccess {
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)?;
exec_step.error = Some(ExecError::OutOfGas(OogError::AccountAccess));

// assert op code is BALANCE | EXTCODESIZE | EXTCODEHASH
assert!([
OpcodeId::BALANCE,
OpcodeId::EXTCODESIZE,
OpcodeId::EXTCODEHASH
]
.contains(&geth_step.op));
// Read account address from stack.
let address_word = geth_step.stack.last()?;
let address = address_word.to_address();
state.stack_read(&mut exec_step, geth_step.stack.last_filled(), address_word)?;

// Read transaction ID from call context.
state.call_context_read(
&mut exec_step,
state.call()?.call_id,
CallContextField::TxId,
U256::from(state.tx_ctx.id()),
);

// transaction access list for account address.
let is_warm = state.sdb.check_account_in_access_list(&address);
// read `is_warm` state
state.push_op(
&mut exec_step,
RW::READ,
TxAccessListAccountOp {
tx_id: state.tx_ctx.id(),
address,
is_warm,
is_warm_prev: is_warm,
},
);

// common error handling
state.gen_restore_context_ops(&mut exec_step, geth_steps)?;
state.handle_return(geth_step)?;
Ok(vec![exec_step])
}
}

#[cfg(test)]
mod oog_account_access_tests {
use crate::{
circuit_input_builder::ExecState,
error::{ExecError, OogError},
mock::BlockData,
operation::{StackOp, RW},
};
use eth_types::{
address, bytecode, evm_types::OpcodeId, geth_types::GethData, Bytecode, ToWord, Word,
};
use mock::TestContext;
use pretty_assertions::assert_eq;

#[test]
fn test_balance_of_warm_address() {
test_ok(true, false);
test_ok(false, false);
test_ok(true, true);
}

// test balance opcode as an example
fn test_ok(exists: bool, is_warm: bool) {
let address = address!("0xaabbccddee000000000000000000000000000000");

// Pop balance first for warm account.
let mut code = Bytecode::default();
if is_warm {
code.append(&bytecode! {
PUSH20(address.to_word())
BALANCE
POP
});
}
code.append(&bytecode! {
PUSH20(address.to_word())
BALANCE
STOP
});

let balance = if exists {
Word::from(800u64)
} else {
Word::zero()
};

// Get the execution steps from the external tracer.
let block: GethData = TestContext::<3, 1>::new(
None,
|accs| {
accs[0]
.address(address!("0x0000000000000000000000000000000000000010"))
.balance(Word::from(1u64 << 20))
.code(code.clone());
if exists {
accs[1].address(address).balance(balance);
} else {
accs[1]
.address(address!("0x0000000000000000000000000000000000000020"))
.balance(Word::from(1u64 << 20));
}
accs[2]
.address(address!("0x0000000000000000000000000000000000cafe01"))
.balance(Word::from(1u64 << 20));
},
|mut txs, accs| {
txs[0]
.to(accs[0].address)
.from(accs[2].address)
.gas(21005.into());
},
|block, _tx| block.number(0xcafeu64),
)
.unwrap()
.into();

let mut builder = BlockData::new_from_geth_data(block.clone()).new_circuit_input_builder();
builder
.handle_block(&block.eth_block, &block.geth_traces)
.unwrap();

// Check if account address is in access list as a result of bus mapping.
assert!(builder.sdb.add_account_to_access_list(address));

let tx_id = 1;
let transaction = &builder.block.txs()[tx_id - 1];
let call_id = transaction.calls()[0].call_id;

let step = transaction
.steps()
.iter()
.filter(|step| step.exec_state == ExecState::Op(OpcodeId::BALANCE))
.last()
.unwrap();

// check expected error occurs
assert_eq!(
step.error,
Some(ExecError::OutOfGas(OogError::AccountAccess))
);

let container = builder.block.container.clone();
let operation = &container.stack[step.bus_mapping_instance[0].as_usize()];
assert_eq!(operation.rw(), RW::READ);
assert_eq!(
operation.op(),
&StackOp {
call_id,
address: 1023.into(),
value: address.to_word(),
}
);
}
}
5 changes: 3 additions & 2 deletions zkevm-circuits/src/evm_circuit/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ mod error_code_store;
mod error_invalid_creation_code;
mod error_invalid_jump;
mod error_invalid_opcode;
mod error_oog_account_access;
mod error_oog_call;
mod error_oog_constant;
mod error_oog_create2;
Expand Down Expand Up @@ -161,6 +162,7 @@ use error_code_store::ErrorCodeStoreGadget;
use error_invalid_creation_code::ErrorInvalidCreationCodeGadget;
use error_invalid_jump::ErrorInvalidJumpGadget;
use error_invalid_opcode::ErrorInvalidOpcodeGadget;
use error_oog_account_access::ErrorOOGAccountAccessGadget;
use error_oog_call::ErrorOOGCallGadget;
use error_oog_constant::ErrorOOGConstantGadget;
use error_oog_create2::ErrorOOGCreate2Gadget;
Expand Down Expand Up @@ -324,8 +326,7 @@ pub(crate) struct ExecutionConfig<F> {
error_write_protection: Box<ErrorWriteProtectionGadget<F>>,
error_oog_dynamic_memory_gadget: Box<ErrorOOGDynamicMemoryGadget<F>>,
error_oog_log: Box<ErrorOOGLogGadget<F>>,
error_oog_account_access:
Box<DummyGadget<F, 0, 0, { ExecutionState::ErrorOutOfGasAccountAccess }>>,
error_oog_account_access: Box<ErrorOOGAccountAccessGadget<F>>,
error_oog_sha3: Box<ErrorOOGSha3Gadget<F>>,
error_oog_create2: Box<ErrorOOGCreate2Gadget<F>>,
error_code_store: Box<ErrorCodeStoreGadget<F>>,
Expand Down
Loading

0 comments on commit 3d406bf

Please sign in to comment.