Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Implement KIP4: create2 for wasm #9277

Merged
merged 10 commits into from
Aug 6, 2018
Merged
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
2 changes: 2 additions & 0 deletions ethcore/evm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ fn test_calls(factory: super::Factory) {

assert_set_contains(&ext.calls, &FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: U256::from(2556),
sender_address: Some(address.clone()),
receive_address: Some(code_address.clone()),
Expand All @@ -755,6 +756,7 @@ fn test_calls(factory: super::Factory) {
});
assert_set_contains(&ext.calls, &FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: U256::from(2556),
sender_address: Some(address.clone()),
receive_address: Some(address.clone()),
Expand Down
2 changes: 1 addition & 1 deletion ethcore/res/wasm-tests
12 changes: 11 additions & 1 deletion ethcore/src/spec/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub struct CommonParams {
pub remove_dust_contracts: bool,
/// Wasm activation blocknumber, if any disabled initially.
pub wasm_activation_transition: BlockNumber,
/// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated.
pub kip4_transition: BlockNumber,
/// Gas limit bound divisor (how much gas limit can change per block)
pub gas_limit_bound_divisor: U256,
/// Registrar contract address.
Expand Down Expand Up @@ -187,7 +189,11 @@ impl CommonParams {
};
}
if block_number >= self.wasm_activation_transition {
schedule.wasm = Some(Default::default());
let mut wasm = ::vm::WasmCosts::default();
if block_number >= self.kip4_transition {
wasm.have_create2 = true;
}
schedule.wasm = Some(wasm);
}
}

Expand Down Expand Up @@ -294,6 +300,10 @@ impl From<ethjson::spec::Params> for CommonParams {
BlockNumber::max_value,
Into::into
),
kip4_transition: p.kip4_transition.map_or_else(
BlockNumber::max_value,
Into::into
),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion ethcore/vm/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub enum MessageCallResult {
}

/// Specifies how an address is calculated for a new contract.
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum CreateContractAddress {
/// Address is calculated from sender and nonce. Pre EIP-86 (Metropolis)
FromSenderAndNonce,
Expand Down
3 changes: 3 additions & 0 deletions ethcore/vm/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ pub struct WasmCosts {
pub opcodes_mul: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_div: u32,
/// Whether create2 extern function is activated.
pub have_create2: bool,
}

impl Default for WasmCosts {
Expand All @@ -166,6 +168,7 @@ impl Default for WasmCosts {
max_stack_height: 64*1024,
opcodes_mul: 3,
opcodes_div: 8,
have_create2: false,
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion ethcore/vm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub enum FakeCallType {
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct FakeCall {
pub call_type: FakeCallType,
pub create_scheme: Option<CreateContractAddress>,
pub gas: U256,
pub sender_address: Option<Address>,
pub receive_address: Option<Address>,
Expand Down Expand Up @@ -133,9 +134,10 @@ impl Ext for FakeExt {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
}

fn create(&mut self, gas: &U256, value: &U256, code: &[u8], _address: CreateContractAddress) -> ContractCreateResult {
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult {
self.calls.insert(FakeCall {
call_type: FakeCallType::Create,
create_scheme: Some(address),
gas: *gas,
sender_address: None,
receive_address: None,
Expand All @@ -159,6 +161,7 @@ impl Ext for FakeExt {

self.calls.insert(FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: *gas,
sender_address: Some(sender_address.clone()),
receive_address: Some(receive_address.clone()),
Expand Down
15 changes: 13 additions & 2 deletions ethcore/wasm/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! Env module glue for wasmi interpreter

use std::cell::RefCell;
use vm::WasmCosts;
use wasmi::{
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
MemoryRef, MemoryInstance, memory_units,
Expand Down Expand Up @@ -47,6 +48,7 @@ pub mod ids {
pub const SENDER_FUNC: usize = 190;
pub const ORIGIN_FUNC: usize = 200;
pub const ELOG_FUNC: usize = 210;
pub const CREATE2_FUNC: usize = 220;

pub const PANIC_FUNC: usize = 1000;
pub const DEBUG_FUNC: usize = 1010;
Expand Down Expand Up @@ -125,6 +127,11 @@ pub mod signatures {
Some(I32),
);

pub const CREATE2: StaticSignature = StaticSignature(
&[I32, I32, I32, I32, I32],
Some(I32),
);

pub const SUICIDE: StaticSignature = StaticSignature(
&[I32],
None,
Expand Down Expand Up @@ -195,18 +202,21 @@ fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef {
/// Maps all functions that runtime support to the corresponding contract import
/// entries.
/// Also manages initial memory request from the runtime.
#[derive(Default)]
pub struct ImportResolver {
max_memory: u32,
memory: RefCell<Option<MemoryRef>>,

have_create2: bool,
}

impl ImportResolver {
/// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb)
pub fn with_limit(max_memory: u32) -> ImportResolver {
pub fn with_limit(max_memory: u32, schedule: &WasmCosts) -> ImportResolver {
ImportResolver {
max_memory: max_memory,
memory: RefCell::new(None),

have_create2: schedule.have_create2,
}
}

Expand Down Expand Up @@ -263,6 +273,7 @@ impl wasmi::ModuleImportResolver for ImportResolver {
"sender" => host(signatures::SENDER, ids::SENDER_FUNC),
"origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC),
"elog" => host(signatures::ELOG, ids::ELOG_FUNC),
"create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC),
_ => {
return Err(wasmi::Error::Instantiation(
format!("Export {} not found", field_name),
Expand Down
2 changes: 1 addition & 1 deletion ethcore/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl vm::Vm for WasmInterpreter {

let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;

let instantiation_resolver = env::ImportResolver::with_limit(16);
let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm());

let module_instance = wasmi::ModuleInstance::new(
&loaded_module,
Expand Down
82 changes: 57 additions & 25 deletions ethcore/wasm/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl<'a> Runtime<'a> {
if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); }
Ok(self.gas_limit - self.gas_counter)
}

/// General gas charging extern.
fn gas(&mut self, args: RuntimeArgs) -> Result<()> {
let amount: u32 = args.nth_checked(0)?;
Expand Down Expand Up @@ -511,29 +511,7 @@ impl<'a> Runtime<'a> {
self.return_u256_ptr(args.nth_checked(0)?, val)
}

/// Creates a new contract
///
/// Arguments:
/// * endowment - how much value (in Wei) transfer to the newly created contract
/// * code_ptr - pointer to the code data
/// * code_len - lenght of the code data
/// * result_ptr - pointer to write an address of the newly created contract
pub fn create(&mut self, args: RuntimeArgs) -> Result<RuntimeValue>
{
//
// method signature:
// fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
//
trace!(target: "wasm", "runtime: CREATE");
let endowment = self.u256_at(args.nth_checked(0)?)?;
trace!(target: "wasm", " val: {:?}", endowment);
let code_ptr: u32 = args.nth_checked(1)?;
trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
let code_len: u32 = args.nth_checked(2)?;
trace!(target: "wasm", " code_len: {:?}", code_len);
let result_ptr: u32 = args.nth_checked(3)?;
trace!(target: "wasm", "result_ptr: {:?}", result_ptr);

fn do_create(&mut self, endowment: U256, code_ptr: u32, code_len: u32, result_ptr: u32, scheme: vm::CreateContractAddress) -> Result<RuntimeValue> {
let code = self.memory.get(code_ptr, code_len as usize)?;

self.adjusted_charge(|schedule| schedule.create_gas as u64)?;
Expand All @@ -543,7 +521,7 @@ impl<'a> Runtime<'a> {
* U256::from(self.ext.schedule().wasm().opcodes_mul)
/ U256::from(self.ext.schedule().wasm().opcodes_div);

match self.ext.create(&gas_left, &endowment, &code, vm::CreateContractAddress::FromSenderAndCodeHash) {
match self.ext.create(&gas_left, &endowment, &code, scheme) {
vm::ContractCreateResult::Created(address, gas_left) => {
self.memory.set(result_ptr, &*address)?;
self.gas_counter = self.gas_limit -
Expand Down Expand Up @@ -571,6 +549,59 @@ impl<'a> Runtime<'a> {
}
}

/// Creates a new contract
///
/// Arguments:
/// * endowment - how much value (in Wei) transfer to the newly created contract
/// * code_ptr - pointer to the code data
/// * code_len - lenght of the code data
/// * result_ptr - pointer to write an address of the newly created contract
pub fn create(&mut self, args: RuntimeArgs) -> Result<RuntimeValue> {
//
// method signature:
// fn create(endowment: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
//
trace!(target: "wasm", "runtime: CREATE");
let endowment = self.u256_at(args.nth_checked(0)?)?;
trace!(target: "wasm", " val: {:?}", endowment);
let code_ptr: u32 = args.nth_checked(1)?;
trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
let code_len: u32 = args.nth_checked(2)?;
trace!(target: "wasm", " code_len: {:?}", code_len);
let result_ptr: u32 = args.nth_checked(3)?;
trace!(target: "wasm", "result_ptr: {:?}", result_ptr);

self.do_create(endowment, code_ptr, code_len, result_ptr, vm::CreateContractAddress::FromSenderAndCodeHash)
}

/// Creates a new contract using FromSenderSaltAndCodeHash scheme
///
/// Arguments:
/// * endowment - how much value (in Wei) transfer to the newly created contract
/// * salt - salt to be used in contract creation address
/// * code_ptr - pointer to the code data
/// * code_len - lenght of the code data
/// * result_ptr - pointer to write an address of the newly created contract
pub fn create2(&mut self, args: RuntimeArgs) -> Result<RuntimeValue> {
//
// method signature:
// fn create2(endowment: *const u8, salt: *const u8, code_ptr: *const u8, code_len: u32, result_ptr: *mut u8) -> i32;
//
trace!(target: "wasm", "runtime: CREATE2");
let endowment = self.u256_at(args.nth_checked(0)?)?;
trace!(target: "wasm", " val: {:?}", endowment);
let salt: H256 = self.u256_at(args.nth_checked(1)?)?.into();
trace!(target: "wasm", " salt: {:?}", salt);
let code_ptr: u32 = args.nth_checked(2)?;
trace!(target: "wasm", " code_ptr: {:?}", code_ptr);
let code_len: u32 = args.nth_checked(3)?;
trace!(target: "wasm", " code_len: {:?}", code_len);
let result_ptr: u32 = args.nth_checked(4)?;
trace!(target: "wasm", "result_ptr: {:?}", result_ptr);

self.do_create(endowment, code_ptr, code_len, result_ptr, vm::CreateContractAddress::FromSenderSaltAndCodeHash(salt))
}

fn debug(&mut self, args: RuntimeArgs) -> Result<()>
{
trace!(target: "wasm", "Contract debug message: {}", {
Expand Down Expand Up @@ -744,6 +775,7 @@ mod ext_impl {
SENDER_FUNC => void!(self.sender(args)),
ORIGIN_FUNC => void!(self.origin(args)),
ELOG_FUNC => void!(self.elog(args)),
CREATE2_FUNC => some!(self.create2(args)),
_ => panic!("env module doesn't provide function at index {}", index),
}
}
Expand Down
Loading