Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: inject call to CREATE2 factory through custom revm handler #7653

Merged
merged 10 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
add docs
  • Loading branch information
klkvr committed Apr 17, 2024
commit bfee0f80f92f3b6d5f183d54325c70a92bb9aab0
5 changes: 3 additions & 2 deletions crates/cheatcodes/src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1328,10 +1328,10 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
ecx.env.tx.caller = broadcast.new_origin;

if ecx.journaled_state.depth() == broadcast.depth {
call.caller = broadcast.new_origin;
let is_fixed_gas_limit = check_if_fixed_gas_limit(ecx, call.gas_limit);

let account =
ecx.journaled_state.state().get_mut(&broadcast.new_origin).unwrap();
let account = ecx.journaled_state.state().get(&broadcast.new_origin).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let account = ecx.journaled_state.state().get(&broadcast.new_origin).unwrap();
let account = ecx.journaled_state.state()[&broadcast.new_origin];


self.broadcastable_transactions.push_back(BroadcastableTransaction {
rpc: ecx.db.active_fork_url(),
Expand All @@ -1349,6 +1349,7 @@ impl<DB: DatabaseExt> Inspector<DB> for Cheatcodes {
..Default::default()
},
});

let kind = match call.scheme {
CreateScheme::Create => "create",
CreateScheme::Create2 { .. } => "create2",
Expand Down
80 changes: 48 additions & 32 deletions crates/evm/core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use revm::{
db::WrapDatabaseRef,
handler::register::EvmHandler,
interpreter::{
CallContext, CallInputs, CallScheme, CreateOutcome, Gas, InstructionResult,
InterpreterResult, Transfer,
return_ok, CallContext, CallInputs, CallScheme, CreateInputs, CreateOutcome, Gas,
InstructionResult, InterpreterResult, Transfer,
},
primitives::{CreateScheme, EVMError, SpecId, TransactTo},
primitives::{CreateScheme, EVMError, SpecId, TransactTo, KECCAK_EMPTY},
FrameOrResult, FrameResult,
};

Expand Down Expand Up @@ -108,8 +108,31 @@ pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 {
spent - (refunded).min(spent / refund_quotient)
}

pub fn create2_handler_register<'a, DB: revm::Database, I: InspectorExt<DB>>(
handler: &mut EvmHandler<'a, I, DB>,
pub fn get_create2_factory_call_inputs(salt: U256, inputs: Box<CreateInputs>) -> CallInputs {
let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
CallInputs {
contract: DEFAULT_CREATE2_DEPLOYER,
transfer: Transfer {
source: inputs.caller,
target: DEFAULT_CREATE2_DEPLOYER,
value: inputs.value,
},
input: calldata.into(),
gas_limit: inputs.gas_limit,
context: CallContext {
caller: inputs.caller,
address: DEFAULT_CREATE2_DEPLOYER,
code_address: DEFAULT_CREATE2_DEPLOYER,
apparent_value: inputs.value,
scheme: CallScheme::Call,
},
is_static: false,
return_memory_offset: 0..0,
}
}

pub fn create2_handler_register<DB: revm::Database, I: InspectorExt<DB>>(
DaniPopes marked this conversation as resolved.
Show resolved Hide resolved
handler: &mut EvmHandler<'_, I, DB>,
) {
let create2_overrides = Rc::<RefCell<Vec<_>>>::new(RefCell::new(Vec::new()));

Expand All @@ -124,9 +147,9 @@ pub fn create2_handler_register<'a, DB: revm::Database, I: InspectorExt<DB>>(
return old_handle(ctx, inputs);
}

// Sanity checks for our CREATE2 deployer
// Sanity check that CREATE2 deployer exists.
let code_hash = ctx.evm.load_account(DEFAULT_CREATE2_DEPLOYER)?.0.info.code_hash;
if ctx.evm.db.code_by_hash(code_hash).map_err(EVMError::Database)?.is_empty() {
if code_hash == KECCAK_EMPTY {
return Ok(FrameOrResult::Result(FrameResult::Create(CreateOutcome {
result: InterpreterResult {
result: InstructionResult::Revert,
Expand All @@ -137,35 +160,18 @@ pub fn create2_handler_register<'a, DB: revm::Database, I: InspectorExt<DB>>(
})))
}

let calldata = [&salt.to_be_bytes::<32>()[..], &inputs.init_code[..]].concat();
let mut call_inputs = CallInputs {
contract: DEFAULT_CREATE2_DEPLOYER,
transfer: Transfer {
source: inputs.caller,
target: DEFAULT_CREATE2_DEPLOYER,
value: inputs.value.clone(),
},
input: calldata.into(),
gas_limit: inputs.gas_limit,
context: CallContext {
caller: inputs.caller,
address: DEFAULT_CREATE2_DEPLOYER,
code_address: DEFAULT_CREATE2_DEPLOYER,
apparent_value: inputs.value.clone(),
scheme: CallScheme::Call,
},
is_static: false,
return_memory_offset: 0..0,
};
// Generate call inputs for CREATE2 factory.
let mut call_inputs = get_create2_factory_call_inputs(salt, inputs);

// call inspector to change input or return outcome.
// Call inspector to change input or return outcome.
if let Some(outcome) = ctx.external.call(&mut ctx.evm, &mut call_inputs) {
create2_overrides_inner
.borrow_mut()
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
return Ok(FrameOrResult::Result(FrameResult::Call(outcome)));
}

// Push data about current override to the stack.
create2_overrides_inner
.borrow_mut()
.push((ctx.evm.journaled_state.depth(), call_inputs.clone()));
Expand All @@ -184,18 +190,28 @@ pub fn create2_handler_register<'a, DB: revm::Database, I: InspectorExt<DB>>(

handler.execution.insert_call_outcome =
Arc::new(move |ctx, frame, shared_memory, mut outcome| {
// If we are on the depth of the latest override, handle the outcome.
if create2_overrides_inner
.borrow()
.last()
.map_or(false, |(depth, _)| *depth == ctx.evm.journaled_state.depth())
{
let (_, call_inputs) = create2_overrides_inner.borrow_mut().pop().unwrap();
outcome = ctx.external.call_end(&mut ctx.evm, &call_inputs, outcome);
let create_outcome = CreateOutcome {
address: Some(Address::try_from(outcome.result.output.as_ref()).unwrap()),
result: outcome.result,

// Decode address from output.
let address = match outcome.instruction_result() {
return_ok!() => {
// SAFETY: we assume here that successful CREATE2 factory call will always
klkvr marked this conversation as resolved.
Show resolved Hide resolved
// return a valid address.
Some(Address::try_from(outcome.output().as_ref()).unwrap())
klkvr marked this conversation as resolved.
Show resolved Hide resolved
}
_ => None,
};
frame.frame_data_mut().interpreter.insert_create_outcome(create_outcome);
frame
.frame_data_mut()
.interpreter
.insert_create_outcome(CreateOutcome { address, result: outcome.result });

Ok(())
} else {
Expand Down