Skip to content

Commit

Permalink
handle precheck failed call
Browse files Browse the repository at this point in the history
  • Loading branch information
lightsing committed Nov 23, 2023
1 parent 1002dfd commit bc43b72
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 44 deletions.
26 changes: 17 additions & 9 deletions bus-mapping/src/circuit_input_builder/input_state_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -962,13 +962,8 @@ impl<'a> CircuitInputStateRef<'a> {
address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19])
}

/// Parse [`Call`] from a *CALL*/CREATE* step.
pub fn parse_call(&mut self, step: &GethExecStep) -> Result<Call, Error> {
let is_success = *self
.tx_ctx
.call_is_success
.get(self.tx.calls().len() - self.tx_ctx.call_is_success_offset)
.unwrap();
/// Parse [`Call`] from a *CALL*/CREATE* step without information about success and persistent.
pub fn parse_call_partial(&mut self, step: &GethExecStep) -> Result<Call, Error> {
let kind = CallKind::try_from(step.op)?;
let caller = self.call()?;
let caller_ctx = self.call_ctx()?;
Expand Down Expand Up @@ -1043,8 +1038,8 @@ impl<'a> CircuitInputStateRef<'a> {
kind,
is_static: kind == CallKind::StaticCall || caller.is_static,
is_root: false,
is_persistent: caller.is_persistent && is_success,
is_success,
is_persistent: caller.is_persistent,
is_success: false,
rw_counter_end_of_reversion: 0,
caller_address,
address,
Expand All @@ -1064,6 +1059,19 @@ impl<'a> CircuitInputStateRef<'a> {
Ok(call)
}

/// Parse [`Call`] from a *CALL*/CREATE* step
pub fn parse_call(&mut self, step: &GethExecStep) -> Result<Call, Error> {
let is_success = *self
.tx_ctx
.call_is_success
.get(self.tx.calls().len() - self.tx_ctx.call_is_success_offset)
.unwrap();
let mut call = self.parse_call_partial(step)?;
call.is_success = is_success;
call.is_persistent = self.call()?.is_persistent && is_success;
Ok(call)
}

/// Return the reverted version of an op by op_ref only if the original op
/// was reversible.
fn get_rev_op_by_ref(&self, op_ref: &OperationRef) -> Option<OpEnum> {
Expand Down
57 changes: 39 additions & 18 deletions bus-mapping/src/evm/opcodes/callop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,43 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
state.call_expand_memory(args_offset, args_length, ret_offset, ret_length)?;

let tx_id = state.tx_ctx.id();
let callee_call = state.parse_call(geth_step)?;
let callee_kind = CallKind::try_from(geth_step.op)?;
let caller_call = state.call()?.clone();
// we need those information but we haven't parse callee's call yet
let caller_address = match callee_kind {
CallKind::Call | CallKind::CallCode | CallKind::StaticCall => caller_call.address,
CallKind::DelegateCall => caller_call.caller_address,
CallKind::Create | CallKind::Create2 => {
unreachable!("CREATE opcode handled in create.rs")
}
};
let (found, sender_account) = state.sdb.get_account(&caller_address);
debug_assert!(found);
let caller_balance = sender_account.balance;
let call_value = match callee_kind {
CallKind::Call | CallKind::CallCode => geth_step.stack.nth_last(2)?,
CallKind::DelegateCall => caller_call.value,
CallKind::StaticCall => Word::zero(),
CallKind::Create | CallKind::Create2 => {
unreachable!("CREATE opcode handled in create.rs")
}
};
// Precheck is OK when depth is in range and caller balance is sufficient.
let is_call_or_callcode = matches!(callee_kind, CallKind::Call | CallKind::CallCode);
let is_precheck_ok =
geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= call_value);

let callee_call = if is_precheck_ok {
state.parse_call(geth_step)?
} else {
// if precheck not ok, the call won't appear in call trace since it never happens
// we need to increase the offset and mannually set the is_success
state.tx_ctx.call_is_success_offset += 1;
let mut call = state.parse_call_partial(geth_step)?;
call.is_success = false;
call.is_persistent = false;
call
};

// For both CALLCODE and DELEGATECALL opcodes, `call.address` is caller
// address which is different from callee_address (code address).
Expand Down Expand Up @@ -146,17 +181,6 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
state.call_context_write(&mut exec_step, callee_call.call_id, field, value)?;
}

let (found, sender_account) = state.sdb.get_account(&callee_call.caller_address);
debug_assert!(found);

let caller_balance = sender_account.balance;
let is_call_or_callcode =
callee_call.kind == CallKind::Call || callee_call.kind == CallKind::CallCode;

// Precheck is OK when depth is in range and caller balance is sufficient.
let is_precheck_ok =
geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= callee_call.value);

// read balance of caller to compare to value for insufficient_balance checking
// in circuit, also use for callcode successful case check balance is
// indeed larger than transfer value. for call opcode, it does in
Expand Down Expand Up @@ -465,11 +489,8 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
callee_gas_left_with_stipend,
);

let mut oog_step = ErrorOOGPrecompile::gen_associated_ops(
state,
&geth_steps[1],
callee_call.clone(),
)?;
let mut oog_step =
ErrorOOGPrecompile::gen_associated_ops(state, &geth_steps[1], callee_call)?;

oog_step.gas_left = Gas(callee_gas_left_with_stipend);
oog_step.gas_cost = GasCost(precompile_call_gas_cost);
Expand All @@ -482,7 +503,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
let mut precompile_step = precompile_associated_ops(
state,
geth_steps[1].clone(),
callee_call.clone(),
callee_call,
precompile_call,
&input_bytes.unwrap_or_default(),
&output_bytes.unwrap_or_default(),
Expand Down
50 changes: 33 additions & 17 deletions bus-mapping/src/evm/opcodes/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,40 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
let mut exec_step = state.new_step(geth_step)?;

let tx_id = state.tx_ctx.id();
let callee = state.parse_call(geth_step)?;
let caller = state.call()?.clone();
let address = if IS_CREATE2 {
state.create2_address(&geth_steps[0])?
} else {
state.create_address()?
};
let callee_account = &state.sdb.get_account(&address).1.clone();
let callee_exists = !callee_account.is_empty();
let callee_value = geth_step.stack.last()?;
if !callee_exists && callee_value.is_zero() {
state.sdb.get_account_mut(&address).1.storage.clear();
}

let is_address_collision = callee_account.code_hash != CodeDB::empty_code_hash()
|| callee_account.nonce > Word::zero();
// Get caller's balance and nonce
let caller_balance = state.sdb.get_balance(&caller.address);
let caller_nonce = state.sdb.get_nonce(&caller.address);
// Check if an error of ErrDepth, ErrInsufficientBalance or
// ErrNonceUintOverflow occurred.
let depth = caller.depth;
let is_precheck_ok =
depth < 1025 && caller_balance >= callee_value && caller_nonce < u64::MAX;
let callee = if is_precheck_ok && !is_address_collision {
state.parse_call(geth_step)?
} else {
// if precheck not ok, the call won't appear in call trace since it never happens
// we need to increase the offset and mannually set the is_success
state.tx_ctx.call_is_success_offset += 1;
let mut call = state.parse_call_partial(geth_step)?;
call.is_success = false;
call.is_persistent = false;
call
};

state.call_context_read(
&mut exec_step,
Expand Down Expand Up @@ -84,14 +116,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
state.create_address()?
};

let callee_account = &state.sdb.get_account(&address).1.clone();
let callee_exists = !callee_account.is_empty();
let is_address_collision = callee_account.code_hash != CodeDB::empty_code_hash()
|| callee_account.nonce > Word::zero();
if !callee_exists && callee.value.is_zero() {
state.sdb.get_account_mut(&address).1.storage.clear();
}

state.stack_write(
&mut exec_step,
geth_step.stack.nth_last_filled(n_pop - 1),
Expand All @@ -103,10 +127,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
)?;
// stack end

// Get caller's balance and nonce
let caller_balance = state.sdb.get_balance(&caller.address);
let caller_nonce = state.sdb.get_nonce(&caller.address);

state.call_context_read(
&mut exec_step,
caller.call_id,
Expand All @@ -128,10 +148,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
caller_nonce.into(),
)?;

// Check if an error of ErrDepth, ErrInsufficientBalance or
// ErrNonceUintOverflow occurred.
let is_precheck_ok =
depth < 1025 && caller_balance >= callee.value && caller_nonce < u64::MAX;
if is_precheck_ok {
// Increase caller's nonce
state.push_op_reversible(
Expand Down

0 comments on commit bc43b72

Please sign in to comment.