Skip to content
Open
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
12 changes: 6 additions & 6 deletions crates/interpreter/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,14 +287,14 @@ const fn instruction_table_impl<WIRE: InterpreterTypes, H: Host>() -> Instructio
table[LOG3 as usize] = Instruction::new(host::log::<3, _>);
table[LOG4 as usize] = Instruction::new(host::log::<4, _>);

table[CREATE as usize] = Instruction::new(contract::create::<_, false, _>);
table[CALL as usize] = Instruction::new(contract::call);
table[CALLCODE as usize] = Instruction::new(contract::call_code);
table[CREATE as usize] = Instruction::new(contract::create::<false, _, _>);
table[CALL as usize] = Instruction::new(contract::call::<CALL, _, _>);
table[CALLCODE as usize] = Instruction::new(contract::call::<CALLCODE, _, _>);
table[RETURN as usize] = Instruction::new(control::ret);
table[DELEGATECALL as usize] = Instruction::new(contract::delegate_call);
table[CREATE2 as usize] = Instruction::new(contract::create::<_, true, _>);
table[DELEGATECALL as usize] = Instruction::new(contract::call::<DELEGATECALL, _, _>);
table[CREATE2 as usize] = Instruction::new(contract::create::<true, _, _>);

table[STATICCALL as usize] = Instruction::new(contract::static_call);
table[STATICCALL as usize] = Instruction::new(contract::call::<STATICCALL, _, _>);
table[REVERT as usize] = Instruction::new(control::revert);
table[INVALID as usize] = Instruction::new(control::invalid);
table[SELFDESTRUCT as usize] = Instruction::new(host::selfdestruct);
Expand Down
179 changes: 53 additions & 126 deletions crates/interpreter/src/instructions/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use crate::{
InstructionExecResult as Result, InstructionResult, InterpreterAction,
};
use context_interface::CreateScheme;
use primitives::{hardfork::SpecId, Address, Bytes, B256, U256};
use primitives::{hardfork::SpecId, Bytes, U256};
use std::boxed::Box;

use crate::InstructionContext as Ictx;

/// Implements the CREATE/CREATE2 instruction.
///
/// Creates a new contract with provided bytecode.
pub fn create<IT: ITy, const IS_CREATE2: bool, H: Host + ?Sized>(
pub fn create<const IS_CREATE2: bool, IT: ITy, H: Host + ?Sized>(
context: Ictx<'_, H, IT>,
) -> Result {
// Static call check is before gas charging (unlike execution-specs where it's
Expand Down Expand Up @@ -126,139 +126,66 @@ pub fn create<IT: ITy, const IS_CREATE2: bool, H: Host + ?Sized>(
Err(InstructionResult::Suspend)
}

/// Implements the CALL instruction.
///
/// Message call with value transfer to another account.
pub fn call<IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
popn!([local_gas_limit, to, value], context.interpreter);
let to = to.into_address();
// Max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let has_transfer = !value.is_zero();
/// Implements the CALL, CALLCODE, DELEGATECALL, and STATICCALL instructions.
pub fn call<const KIND: u8, IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
use bytecode::opcode::{CALL, CALLCODE, DELEGATECALL, STATICCALL};

if context.interpreter.runtime_flag.is_static() && has_transfer {
return Err(InstructionResult::CallNotAllowedInsideStatic);
if !matches!(KIND, CALL | CALLCODE | DELEGATECALL | STATICCALL) {
unreachable!("invalid call kind")
}

let (input, return_memory_offset) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;

let (gas_limit, bytecode, bytecode_hash) =
load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit)?;

// Call host to interact with target contract
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: to,
caller: context.interpreter.input.target_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(value),
scheme: CallScheme::Call,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
Err(InstructionResult::Suspend)
}
if KIND == DELEGATECALL {
check!(context.interpreter, HOMESTEAD);
} else if KIND == STATICCALL {
check!(context.interpreter, BYZANTIUM);
}

/// Implements the CALLCODE instruction.
///
/// Message call with alternative account's code.
pub fn call_code<IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
popn!([local_gas_limit, to, value], context.interpreter);
let to = Address::from_word(B256::from(to));
let (local_gas_limit, to, value) = if matches!(KIND, CALL | CALLCODE) {
popn!([local_gas_limit, to, value], context.interpreter);
(local_gas_limit, to, value)
} else {
popn!([local_gas_limit, to], context.interpreter);
(local_gas_limit, to, U256::ZERO)
};
let to = to.into_address();
// Max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
let has_transfer = !value.is_zero();

let (input, return_memory_offset) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;

let (gas_limit, bytecode, bytecode_hash) =
load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit)?;

// Call host to interact with target contract
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: context.interpreter.input.target_address(),
caller: context.interpreter.input.target_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(value),
scheme: CallScheme::CallCode,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
Err(InstructionResult::Suspend)
}

/// Implements the DELEGATECALL instruction.
///
/// Message call with alternative account's code but same sender and value.
pub fn delegate_call<IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
check!(context.interpreter, HOMESTEAD);
popn!([local_gas_limit, to], context.interpreter);
let to = Address::from_word(B256::from(to));
// Max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);
if KIND == CALL && context.interpreter.runtime_flag.is_static() && has_transfer {
return Err(InstructionResult::CallNotAllowedInsideStatic);
}

let (input, return_memory_offset) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;

let is_call = KIND == CALL;
let (gas_limit, bytecode, bytecode_hash) =
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)?;
load_acc_and_calc_gas(&mut context, to, has_transfer, is_call, local_gas_limit)?;

// Call host to interact with target contract
context
.interpreter
.bytecode
.set_action(InterpreterAction::NewFrame(FrameInput::Call(Box::new(
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: context.interpreter.input.target_address(),
caller: context.interpreter.input.caller_address(),
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Apparent(context.interpreter.input.call_value()),
scheme: CallScheme::DelegateCall,
is_static: context.interpreter.runtime_flag.is_static(),
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
))));
Err(InstructionResult::Suspend)
}

/// Implements the STATICCALL instruction.
///
/// Static message call (cannot modify state).
pub fn static_call<IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> Result {
check!(context.interpreter, BYZANTIUM);
popn!([local_gas_limit, to], context.interpreter);
let to = Address::from_word(B256::from(to));
// Max gas limit is not possible in real ethereum situation.
let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX);

let (input, return_memory_offset) =
get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params())?;

let (gas_limit, bytecode, bytecode_hash) =
load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit)?;
let target_address = if matches!(KIND, CALLCODE | DELEGATECALL) {
context.interpreter.input.target_address()
} else {
to
};
let caller = if KIND == DELEGATECALL {
context.interpreter.input.caller_address()
} else {
context.interpreter.input.target_address()
};
let value = if KIND == DELEGATECALL {
CallValue::Apparent(context.interpreter.input.call_value())
} else {
CallValue::Transfer(value)
};
let scheme = match KIND {
CALL => CallScheme::Call,
CALLCODE => CallScheme::CallCode,
DELEGATECALL => CallScheme::DelegateCall,
STATICCALL => CallScheme::StaticCall,
_ => unreachable!(),
};
let is_static = context.interpreter.runtime_flag.is_static() || KIND == STATICCALL;

// Call host to interact with target contract
context
Expand All @@ -268,13 +195,13 @@ pub fn static_call<IT: ITy, H: Host + ?Sized>(mut context: Ictx<'_, H, IT>) -> R
CallInputs {
input: CallInput::SharedBuffer(input),
gas_limit,
target_address: to,
caller: context.interpreter.input.target_address(),
target_address,
caller,
bytecode_address: to,
known_bytecode: (bytecode_hash, bytecode),
value: CallValue::Transfer(U256::ZERO),
scheme: CallScheme::StaticCall,
is_static: true,
value,
scheme,
is_static,
return_memory_offset,
reservoir: context.interpreter.gas.reservoir(),
},
Expand Down
Loading