diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index b62faf87d..b8f0df363 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -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()), @@ -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()), diff --git a/ethcore/res/wasm-tests b/ethcore/res/wasm-tests index 474110de5..986a6fb94 160000 --- a/ethcore/res/wasm-tests +++ b/ethcore/res/wasm-tests @@ -1 +1 @@ -Subproject commit 474110de59a0f632b20615256c913b144c49354c +Subproject commit 986a6fb94673ba270f8f7ef1fff521ba33d427c2 diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index d79775f78..45f9a6c95 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -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. @@ -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); } } @@ -294,6 +300,10 @@ impl From for CommonParams { BlockNumber::max_value, Into::into ), + kip4_transition: p.kip4_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), } } } diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 3e6ee1e02..c1ce1b79f 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -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, diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index 757d8a16d..ec72c4683 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -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 { @@ -166,6 +168,7 @@ impl Default for WasmCosts { max_stack_height: 64*1024, opcodes_mul: 3, opcodes_div: 8, + have_create2: false, } } } diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index d83e6881a..4930e4219 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -39,6 +39,7 @@ pub enum FakeCallType { #[derive(PartialEq, Eq, Hash, Debug)] pub struct FakeCall { pub call_type: FakeCallType, + pub create_scheme: Option, pub gas: U256, pub sender_address: Option
, pub receive_address: Option
, @@ -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, @@ -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()), diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index 9bcbee63f..a9e536f5f 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -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, @@ -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; @@ -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, @@ -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>, + + 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, } } @@ -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), diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index f1290318e..1fcfe9371 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -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, diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index d99ab8645..347023dd9 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -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)?; @@ -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 - { - // - // 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 { let code = self.memory.get(code_ptr, code_len as usize)?; self.adjusted_charge(|schedule| schedule.create_gas as u64)?; @@ -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 - @@ -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 { + // + // 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 { + // + // 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: {}", { @@ -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), } } diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 726b9ebab..e72cc15c8 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -20,7 +20,7 @@ use byteorder::{LittleEndian, ByteOrder}; use ethereum_types::{H256, U256, Address}; use super::WasmInterpreter; -use vm::{self, Vm, GasLeft, ActionParams, ActionValue}; +use vm::{self, Vm, GasLeft, ActionParams, ActionValue, CreateContractAddress}; use vm::tests::{FakeCall, FakeExt, FakeCallType}; macro_rules! load_sample { @@ -138,7 +138,7 @@ fn logger() { U256::from(1_000_000_000), "Logger sets 0x04 key to the trasferred value" ); - assert_eq!(gas_left, U256::from(16_181)); + assert_eq!(gas_left, U256::from(17_716)); } // This test checks if the contract can allocate memory and pass pointer to the result stream properly. @@ -173,7 +173,7 @@ fn identity() { sender, "Idenity test contract does not return the sender passed" ); - assert_eq!(gas_left, U256::from(96_883)); + assert_eq!(gas_left, U256::from(98_419)); } // Dispersion test sends byte array and expect the contract to 'disperse' the original elements with @@ -207,7 +207,7 @@ fn dispersion() { result, vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0] ); - assert_eq!(gas_left, U256::from(92_371)); + assert_eq!(gas_left, U256::from(92_377)); } #[test] @@ -267,7 +267,7 @@ fn suicide() { }; assert!(ext.suicides.contains(&refund)); - assert_eq!(gas_left, U256::from(93_348)); + assert_eq!(gas_left, U256::from(93_346)); } #[test] @@ -281,14 +281,19 @@ fn create() { params.value = ActionValue::transfer(1_000_000_000); let mut ext = FakeExt::new().with_wasm(); + ext.schedule.wasm.as_mut().unwrap().have_create2 = true; let gas_left = { let mut interpreter = wasm_interpreter(); let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors"); match result { - GasLeft::Known(gas) => gas, - GasLeft::NeedsReturn { .. } => { - panic!("Create contract should not return anthing because ext always fails on creation"); + GasLeft::Known(_) => { + panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero."); + }, + GasLeft::NeedsReturn { gas_left, data, apply_state } => { + assert!(apply_state); + assert_eq!(data.as_ref(), [0u8; 40].as_ref()); // FakeExt never succeeds in create. + gas_left }, } }; @@ -297,15 +302,28 @@ fn create() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Create, - gas: U256::from(59_269), + create_scheme: Some(CreateContractAddress::FromSenderAndCodeHash), + gas: U256::from(52_017), sender_address: None, receive_address: None, - value: Some(1_000_000_000.into()), + value: Some((1_000_000_000 / 2).into()), data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], code_address: None, } )); - assert_eq!(gas_left, U256::from(59_212)); + assert!(ext.calls.contains( + &FakeCall { + call_type: FakeCallType::Create, + create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash(H256::from([5u8].as_ref()))), + gas: U256::from(10_740), + sender_address: None, + receive_address: None, + value: Some((1_000_000_000 / 2).into()), + data: vec![0u8, 2, 4, 8, 16, 32, 64, 128], + code_address: None, + } + )); + assert_eq!(gas_left, U256::from(10_675)); } #[test] @@ -340,6 +358,7 @@ fn call_msg() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, + create_scheme: None, gas: U256::from(33_000), sender_address: Some(receiver), receive_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])), @@ -382,6 +401,7 @@ fn call_code() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, + create_scheme: None, gas: U256::from(20_000), sender_address: Some(sender), receive_address: Some(receiver), @@ -394,7 +414,7 @@ fn call_code() { // siphash result let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 4198595614); - assert_eq!(gas_left, U256::from(90_038)); + assert_eq!(gas_left, U256::from(90_037)); } #[test] @@ -429,6 +449,7 @@ fn call_static() { assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Call, + create_scheme: None, gas: U256::from(20_000), sender_address: Some(receiver), receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()), @@ -442,7 +463,7 @@ fn call_static() { let res = LittleEndian::read_u32(&result[..]); assert_eq!(res, 317632590); - assert_eq!(gas_left, U256::from(90_043)); + assert_eq!(gas_left, U256::from(90_042)); } // Realloc test @@ -465,7 +486,7 @@ fn realloc() { } }; assert_eq!(result, vec![0u8; 2]); - assert_eq!(gas_left, U256::from(92_842)); + assert_eq!(gas_left, U256::from(92_848)); } #[test] @@ -487,7 +508,7 @@ fn alloc() { } }; assert_eq!(result, vec![5u8; 1024*400]); - assert_eq!(gas_left, U256::from(6_893_883)); + assert_eq!(gas_left, U256::from(6_893_881)); } // Tests that contract's ability to read from a storage @@ -515,7 +536,7 @@ fn storage_read() { }; assert_eq!(Address::from(&result[12..32]), address); - assert_eq!(gas_left, U256::from(96_833)); + assert_eq!(gas_left, U256::from(98_369)); } // Tests keccak calculation @@ -541,7 +562,7 @@ fn keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(84_134)); + assert_eq!(gas_left, U256::from(85_949)); } // math_* tests check the ability of wasm contract to perform big integer operations @@ -570,7 +591,7 @@ fn math_add() { U256::from_dec_str("1888888888888888888888888888887").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(92_086)); + assert_eq!(gas_left, U256::from(92_095)); } // multiplication @@ -592,7 +613,7 @@ fn math_mul() { U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(91_414)); + assert_eq!(gas_left, U256::from(91_423)); } // subtraction @@ -614,7 +635,7 @@ fn math_sub() { U256::from_dec_str("111111111111111111111111111111").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(92_086)); + assert_eq!(gas_left, U256::from(92_095)); } // subtraction with overflow @@ -656,7 +677,7 @@ fn math_div() { U256::from_dec_str("1125000").unwrap(), (&result[..]).into() ); - assert_eq!(gas_left, U256::from(87_376)); + assert_eq!(gas_left, U256::from(87_379)); } #[test] @@ -684,7 +705,7 @@ fn storage_metering() { }; // 0 -> not 0 - assert_eq!(gas_left, U256::from(72_399)); + assert_eq!(gas_left, U256::from(72_395)); // #2 @@ -703,7 +724,7 @@ fn storage_metering() { }; // not 0 -> not 0 - assert_eq!(gas_left, U256::from(87_399)); + assert_eq!(gas_left, U256::from(87_395)); } // This test checks the ability of wasm contract to invoke @@ -791,7 +812,7 @@ fn externs() { "Gas limit requested and returned does not match" ); - assert_eq!(gas_left, U256::from(90_435)); + assert_eq!(gas_left, U256::from(90_428)); } #[test] @@ -817,7 +838,7 @@ fn embedded_keccak() { }; assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(gas_left, U256::from(84_134)); + assert_eq!(gas_left, U256::from(85_949)); } /// This test checks the correctness of log extern @@ -852,7 +873,7 @@ fn events() { assert_eq!(&log_entry.data, b"gnihtemos"); assert_eq!(&result, b"gnihtemos"); - assert_eq!(gas_left, U256::from(81_351)); + assert_eq!(gas_left, U256::from(83_158)); } #[test] diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index a37e4f23b..0fab68198 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -146,6 +146,9 @@ pub struct Params { /// Wasm activation block height, if not activated from start #[serde(rename="wasmActivationTransition")] pub wasm_activation_transition: Option, + /// KIP4 activiation block height. + #[serde(rename="kip4Transition")] + pub kip4_transition: Option, } #[cfg(test)]