diff --git a/bus-mapping/src/circuit_input_builder/input_state_ref.rs b/bus-mapping/src/circuit_input_builder/input_state_ref.rs index 250cb9a5c1..11cad28a2f 100644 --- a/bus-mapping/src/circuit_input_builder/input_state_ref.rs +++ b/bus-mapping/src/circuit_input_builder/input_state_ref.rs @@ -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 { - 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 { let kind = CallKind::try_from(step.op)?; let caller = self.call()?; let caller_ctx = self.call_ctx()?; @@ -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, @@ -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 { + 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 { @@ -1285,7 +1293,7 @@ impl<'a> CircuitInputStateRef<'a> { caller.last_callee_memory = callee_memory; } - self.tx_ctx.pop_call_ctx(); + self.tx_ctx.pop_call_ctx(call.is_success); Ok(()) } diff --git a/bus-mapping/src/circuit_input_builder/transaction.rs b/bus-mapping/src/circuit_input_builder/transaction.rs index 169dc624fa..d43d54ea36 100644 --- a/bus-mapping/src/circuit_input_builder/transaction.rs +++ b/bus-mapping/src/circuit_input_builder/transaction.rs @@ -162,10 +162,10 @@ impl TransactionContext { } /// Pop the last entry in the call stack. - pub(crate) fn pop_call_ctx(&mut self) { + pub(crate) fn pop_call_ctx(&mut self, is_success: bool) { let call = self.calls.pop().expect("calls should not be empty"); // Accumulate reversible_write_counter if call is success - if self.call_is_success[call.index] { + if is_success { if let Some(caller) = self.calls.last_mut() { caller.reversible_write_counter += call.reversible_write_counter; } diff --git a/bus-mapping/src/evm/opcodes/callop.rs b/bus-mapping/src/evm/opcodes/callop.rs index 642ec094cd..5e3448bba2 100644 --- a/bus-mapping/src/evm/opcodes/callop.rs +++ b/bus-mapping/src/evm/opcodes/callop.rs @@ -47,8 +47,43 @@ impl Opcode for CallOpcode { 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). @@ -146,17 +181,6 @@ impl Opcode for CallOpcode { 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 @@ -465,11 +489,8 @@ impl Opcode for CallOpcode { 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); @@ -482,7 +503,7 @@ impl Opcode for CallOpcode { 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(), diff --git a/bus-mapping/src/evm/opcodes/create.rs b/bus-mapping/src/evm/opcodes/create.rs index 7167dad7c7..9eb310dc94 100644 --- a/bus-mapping/src/evm/opcodes/create.rs +++ b/bus-mapping/src/evm/opcodes/create.rs @@ -28,8 +28,40 @@ impl Opcode for Create { 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, @@ -84,14 +116,6 @@ impl Opcode for Create { 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), @@ -103,10 +127,6 @@ impl Opcode for Create { )?; // 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, @@ -128,10 +148,6 @@ impl Opcode for Create { 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(