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
15 changes: 11 additions & 4 deletions bus-mapping/src/circuit_input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,17 @@ pub fn get_create_init_code<'a, 'b>(
call_ctx: &'a CallContext,
step: &'b GethExecStep,
) -> Result<&'a [u8], Error> {
let offset = step.stack.nth_last(1)?;
let length = step.stack.nth_last(2)?;
Ok(&call_ctx.memory.0
[offset.low_u64() as usize..(offset.low_u64() + length.low_u64()) as usize])
let offset = step.stack.nth_last(1)?.low_u64() as usize;
let length = step.stack.nth_last(2)?.as_usize();

let mem_len = call_ctx.memory.0.len();
if offset >= mem_len {
return Ok(&[]);
}

let offset_end = offset.checked_add(length).unwrap_or(mem_len);

Ok(&call_ctx.memory.0[offset..offset_end])
}

/// Retrieve the memory offset and length of call.
Expand Down
7 changes: 7 additions & 0 deletions bus-mapping/src/circuit_input_builder/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ pub struct CallContext {
pub return_data: Vec<u8>,
}

impl CallContext {
/// Memory size in words, rounded up
pub fn memory_word_size(&self) -> u64 {
u64::try_from(self.memory.len()).expect("failed to convert usize to u64") / 32
}
}

/// A reversion group is the collection of calls and the operations which are
/// [`Operation::reversible`](crate::operation::Operation::reversible) that
/// happened in them, that will be reverted at once when the call that initiated
Expand Down
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
35 changes: 23 additions & 12 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 @@ -77,7 +77,7 @@ use callop::CallOpcode;
use callvalue::Callvalue;
use codecopy::Codecopy;
use codesize::Codesize;
use create::DummyCreate;
use create::Create;
use dup::Dup;
use error_invalid_jump::InvalidJump;
use error_oog_call::OOGCall;
Expand Down Expand Up @@ -247,19 +247,13 @@ fn fn_gen_associated_ops(opcode_id: &OpcodeId) -> FnGenAssociatedOps {
OpcodeId::LOG4 => Log::gen_associated_ops,
OpcodeId::CALL | OpcodeId::CALLCODE => CallOpcode::<7>::gen_associated_ops,
OpcodeId::DELEGATECALL | OpcodeId::STATICCALL => CallOpcode::<6>::gen_associated_ops,
OpcodeId::CREATE => Create::<false>::gen_associated_ops,
OpcodeId::CREATE2 => Create::<true>::gen_associated_ops,
OpcodeId::RETURN | OpcodeId::REVERT => ReturnRevert::gen_associated_ops,
OpcodeId::SELFDESTRUCT => {
evm_unimplemented!("Using dummy gen_selfdestruct_ops for opcode SELFDESTRUCT");
DummySelfDestruct::gen_associated_ops
}
OpcodeId::CREATE => {
evm_unimplemented!("Using dummy gen_create_ops for opcode {:?}", opcode_id);
DummyCreate::<false>::gen_associated_ops
}
OpcodeId::CREATE2 => {
evm_unimplemented!("Using dummy gen_create_ops for opcode {:?}", opcode_id);
DummyCreate::<true>::gen_associated_ops
}
_ => {
evm_unimplemented!("Using dummy gen_associated_ops for opcode {:?}", opcode_id);
Dummy::gen_associated_ops
Expand All @@ -278,8 +272,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(Create::<false>::gen_associated_ops)
}
ExecError::InsufficientBalance(InsufficientBalanceError::Create2) => {
Some(Create::<true>::gen_associated_ops)
}
// only create2 may cause ContractAddressCollision error, so use Create::<true>.
ExecError::ContractAddressCollision => Some(Create::<true>::gen_associated_ops),
// create & create2 can encounter nonce uint overflow.
ExecError::NonceUintOverflow(NonceUintOverflowError::Create) => {
Some(Create::<false>::gen_associated_ops)
}
ExecError::NonceUintOverflow(NonceUintOverflowError::Create2) => {
Some(Create::<true>::gen_associated_ops)
}
ExecError::WriteProtection => Some(ErrorWriteProtection::gen_associated_ops),
ExecError::ReturnDataOutOfBounds => Some(ErrorReturnDataOutOfBound::gen_associated_ops),

Expand Down
Loading