Skip to content

Commit bc43b72

Browse files
committed
handle precheck failed call
1 parent 1002dfd commit bc43b72

File tree

3 files changed

+89
-44
lines changed

3 files changed

+89
-44
lines changed

bus-mapping/src/circuit_input_builder/input_state_ref.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -962,13 +962,8 @@ impl<'a> CircuitInputStateRef<'a> {
962962
address.0[0..19] == [0u8; 19] && (1..=9).contains(&address.0[19])
963963
}
964964

965-
/// Parse [`Call`] from a *CALL*/CREATE* step.
966-
pub fn parse_call(&mut self, step: &GethExecStep) -> Result<Call, Error> {
967-
let is_success = *self
968-
.tx_ctx
969-
.call_is_success
970-
.get(self.tx.calls().len() - self.tx_ctx.call_is_success_offset)
971-
.unwrap();
965+
/// Parse [`Call`] from a *CALL*/CREATE* step without information about success and persistent.
966+
pub fn parse_call_partial(&mut self, step: &GethExecStep) -> Result<Call, Error> {
972967
let kind = CallKind::try_from(step.op)?;
973968
let caller = self.call()?;
974969
let caller_ctx = self.call_ctx()?;
@@ -1043,8 +1038,8 @@ impl<'a> CircuitInputStateRef<'a> {
10431038
kind,
10441039
is_static: kind == CallKind::StaticCall || caller.is_static,
10451040
is_root: false,
1046-
is_persistent: caller.is_persistent && is_success,
1047-
is_success,
1041+
is_persistent: caller.is_persistent,
1042+
is_success: false,
10481043
rw_counter_end_of_reversion: 0,
10491044
caller_address,
10501045
address,
@@ -1064,6 +1059,19 @@ impl<'a> CircuitInputStateRef<'a> {
10641059
Ok(call)
10651060
}
10661061

1062+
/// Parse [`Call`] from a *CALL*/CREATE* step
1063+
pub fn parse_call(&mut self, step: &GethExecStep) -> Result<Call, Error> {
1064+
let is_success = *self
1065+
.tx_ctx
1066+
.call_is_success
1067+
.get(self.tx.calls().len() - self.tx_ctx.call_is_success_offset)
1068+
.unwrap();
1069+
let mut call = self.parse_call_partial(step)?;
1070+
call.is_success = is_success;
1071+
call.is_persistent = self.call()?.is_persistent && is_success;
1072+
Ok(call)
1073+
}
1074+
10671075
/// Return the reverted version of an op by op_ref only if the original op
10681076
/// was reversible.
10691077
fn get_rev_op_by_ref(&self, op_ref: &OperationRef) -> Option<OpEnum> {

bus-mapping/src/evm/opcodes/callop.rs

+39-18
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,43 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
4747
state.call_expand_memory(args_offset, args_length, ret_offset, ret_length)?;
4848

4949
let tx_id = state.tx_ctx.id();
50-
let callee_call = state.parse_call(geth_step)?;
50+
let callee_kind = CallKind::try_from(geth_step.op)?;
5151
let caller_call = state.call()?.clone();
52+
// we need those information but we haven't parse callee's call yet
53+
let caller_address = match callee_kind {
54+
CallKind::Call | CallKind::CallCode | CallKind::StaticCall => caller_call.address,
55+
CallKind::DelegateCall => caller_call.caller_address,
56+
CallKind::Create | CallKind::Create2 => {
57+
unreachable!("CREATE opcode handled in create.rs")
58+
}
59+
};
60+
let (found, sender_account) = state.sdb.get_account(&caller_address);
61+
debug_assert!(found);
62+
let caller_balance = sender_account.balance;
63+
let call_value = match callee_kind {
64+
CallKind::Call | CallKind::CallCode => geth_step.stack.nth_last(2)?,
65+
CallKind::DelegateCall => caller_call.value,
66+
CallKind::StaticCall => Word::zero(),
67+
CallKind::Create | CallKind::Create2 => {
68+
unreachable!("CREATE opcode handled in create.rs")
69+
}
70+
};
71+
// Precheck is OK when depth is in range and caller balance is sufficient.
72+
let is_call_or_callcode = matches!(callee_kind, CallKind::Call | CallKind::CallCode);
73+
let is_precheck_ok =
74+
geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= call_value);
75+
76+
let callee_call = if is_precheck_ok {
77+
state.parse_call(geth_step)?
78+
} else {
79+
// if precheck not ok, the call won't appear in call trace since it never happens
80+
// we need to increase the offset and mannually set the is_success
81+
state.tx_ctx.call_is_success_offset += 1;
82+
let mut call = state.parse_call_partial(geth_step)?;
83+
call.is_success = false;
84+
call.is_persistent = false;
85+
call
86+
};
5287

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

149-
let (found, sender_account) = state.sdb.get_account(&callee_call.caller_address);
150-
debug_assert!(found);
151-
152-
let caller_balance = sender_account.balance;
153-
let is_call_or_callcode =
154-
callee_call.kind == CallKind::Call || callee_call.kind == CallKind::CallCode;
155-
156-
// Precheck is OK when depth is in range and caller balance is sufficient.
157-
let is_precheck_ok =
158-
geth_step.depth < 1025 && (!is_call_or_callcode || caller_balance >= callee_call.value);
159-
160184
// read balance of caller to compare to value for insufficient_balance checking
161185
// in circuit, also use for callcode successful case check balance is
162186
// indeed larger than transfer value. for call opcode, it does in
@@ -465,11 +489,8 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
465489
callee_gas_left_with_stipend,
466490
);
467491

468-
let mut oog_step = ErrorOOGPrecompile::gen_associated_ops(
469-
state,
470-
&geth_steps[1],
471-
callee_call.clone(),
472-
)?;
492+
let mut oog_step =
493+
ErrorOOGPrecompile::gen_associated_ops(state, &geth_steps[1], callee_call)?;
473494

474495
oog_step.gas_left = Gas(callee_gas_left_with_stipend);
475496
oog_step.gas_cost = GasCost(precompile_call_gas_cost);
@@ -482,7 +503,7 @@ impl<const N_ARGS: usize> Opcode for CallOpcode<N_ARGS> {
482503
let mut precompile_step = precompile_associated_ops(
483504
state,
484505
geth_steps[1].clone(),
485-
callee_call.clone(),
506+
callee_call,
486507
precompile_call,
487508
&input_bytes.unwrap_or_default(),
488509
&output_bytes.unwrap_or_default(),

bus-mapping/src/evm/opcodes/create.rs

+33-17
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,40 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
2828
let mut exec_step = state.new_step(geth_step)?;
2929

3030
let tx_id = state.tx_ctx.id();
31-
let callee = state.parse_call(geth_step)?;
3231
let caller = state.call()?.clone();
32+
let address = if IS_CREATE2 {
33+
state.create2_address(&geth_steps[0])?
34+
} else {
35+
state.create_address()?
36+
};
37+
let callee_account = &state.sdb.get_account(&address).1.clone();
38+
let callee_exists = !callee_account.is_empty();
39+
let callee_value = geth_step.stack.last()?;
40+
if !callee_exists && callee_value.is_zero() {
41+
state.sdb.get_account_mut(&address).1.storage.clear();
42+
}
43+
44+
let is_address_collision = callee_account.code_hash != CodeDB::empty_code_hash()
45+
|| callee_account.nonce > Word::zero();
46+
// Get caller's balance and nonce
47+
let caller_balance = state.sdb.get_balance(&caller.address);
48+
let caller_nonce = state.sdb.get_nonce(&caller.address);
49+
// Check if an error of ErrDepth, ErrInsufficientBalance or
50+
// ErrNonceUintOverflow occurred.
51+
let depth = caller.depth;
52+
let is_precheck_ok =
53+
depth < 1025 && caller_balance >= callee_value && caller_nonce < u64::MAX;
54+
let callee = if is_precheck_ok && !is_address_collision {
55+
state.parse_call(geth_step)?
56+
} else {
57+
// if precheck not ok, the call won't appear in call trace since it never happens
58+
// we need to increase the offset and mannually set the is_success
59+
state.tx_ctx.call_is_success_offset += 1;
60+
let mut call = state.parse_call_partial(geth_step)?;
61+
call.is_success = false;
62+
call.is_persistent = false;
63+
call
64+
};
3365

3466
state.call_context_read(
3567
&mut exec_step,
@@ -84,14 +116,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
84116
state.create_address()?
85117
};
86118

87-
let callee_account = &state.sdb.get_account(&address).1.clone();
88-
let callee_exists = !callee_account.is_empty();
89-
let is_address_collision = callee_account.code_hash != CodeDB::empty_code_hash()
90-
|| callee_account.nonce > Word::zero();
91-
if !callee_exists && callee.value.is_zero() {
92-
state.sdb.get_account_mut(&address).1.storage.clear();
93-
}
94-
95119
state.stack_write(
96120
&mut exec_step,
97121
geth_step.stack.nth_last_filled(n_pop - 1),
@@ -103,10 +127,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
103127
)?;
104128
// stack end
105129

106-
// Get caller's balance and nonce
107-
let caller_balance = state.sdb.get_balance(&caller.address);
108-
let caller_nonce = state.sdb.get_nonce(&caller.address);
109-
110130
state.call_context_read(
111131
&mut exec_step,
112132
caller.call_id,
@@ -128,10 +148,6 @@ impl<const IS_CREATE2: bool> Opcode for Create<IS_CREATE2> {
128148
caller_nonce.into(),
129149
)?;
130150

131-
// Check if an error of ErrDepth, ErrInsufficientBalance or
132-
// ErrNonceUintOverflow occurred.
133-
let is_precheck_ok =
134-
depth < 1025 && caller_balance >= callee.value && caller_nonce < u64::MAX;
135151
if is_precheck_ok {
136152
// Increase caller's nonce
137153
state.push_op_reversible(

0 commit comments

Comments
 (0)