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

Commit

Permalink
Implement KIP4: create2 for wasm (#9277)
Browse files Browse the repository at this point in the history
* Basic implementation for kip4

* Add KIP-4 config flags

* typo: docs fix

* Fix args offset

* Add tests for create2

* tests: evm

* Update wasm-tests and fix all gas costs

* Update wasm-tests

* Update wasm-tests and fix gas costs
  • Loading branch information
sorpaas authored Aug 6, 2018
1 parent 3f2fd61 commit e8b13cb
Show file tree
Hide file tree
Showing 11 changed files with 143 additions and 58 deletions.
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

0 comments on commit e8b13cb

Please sign in to comment.