Implement KIP4: create2 for wasm (#9277)
* 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
This commit is contained in:
parent
3f2fd610d9
commit
e8b13cb77e
@ -746,6 +746,7 @@ fn test_calls(factory: super::Factory) {
|
|||||||
|
|
||||||
assert_set_contains(&ext.calls, &FakeCall {
|
assert_set_contains(&ext.calls, &FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: U256::from(2556),
|
gas: U256::from(2556),
|
||||||
sender_address: Some(address.clone()),
|
sender_address: Some(address.clone()),
|
||||||
receive_address: Some(code_address.clone()),
|
receive_address: Some(code_address.clone()),
|
||||||
@ -755,6 +756,7 @@ fn test_calls(factory: super::Factory) {
|
|||||||
});
|
});
|
||||||
assert_set_contains(&ext.calls, &FakeCall {
|
assert_set_contains(&ext.calls, &FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: U256::from(2556),
|
gas: U256::from(2556),
|
||||||
sender_address: Some(address.clone()),
|
sender_address: Some(address.clone()),
|
||||||
receive_address: Some(address.clone()),
|
receive_address: Some(address.clone()),
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 474110de59a0f632b20615256c913b144c49354c
|
Subproject commit 986a6fb94673ba270f8f7ef1fff521ba33d427c2
|
@ -125,6 +125,8 @@ pub struct CommonParams {
|
|||||||
pub remove_dust_contracts: bool,
|
pub remove_dust_contracts: bool,
|
||||||
/// Wasm activation blocknumber, if any disabled initially.
|
/// Wasm activation blocknumber, if any disabled initially.
|
||||||
pub wasm_activation_transition: BlockNumber,
|
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)
|
/// Gas limit bound divisor (how much gas limit can change per block)
|
||||||
pub gas_limit_bound_divisor: U256,
|
pub gas_limit_bound_divisor: U256,
|
||||||
/// Registrar contract address.
|
/// Registrar contract address.
|
||||||
@ -187,7 +189,11 @@ impl CommonParams {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
if block_number >= self.wasm_activation_transition {
|
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<ethjson::spec::Params> for CommonParams {
|
|||||||
BlockNumber::max_value,
|
BlockNumber::max_value,
|
||||||
Into::into
|
Into::into
|
||||||
),
|
),
|
||||||
|
kip4_transition: p.kip4_transition.map_or_else(
|
||||||
|
BlockNumber::max_value,
|
||||||
|
Into::into
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ pub enum MessageCallResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies how an address is calculated for a new contract.
|
/// 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 {
|
pub enum CreateContractAddress {
|
||||||
/// Address is calculated from sender and nonce. Pre EIP-86 (Metropolis)
|
/// Address is calculated from sender and nonce. Pre EIP-86 (Metropolis)
|
||||||
FromSenderAndNonce,
|
FromSenderAndNonce,
|
||||||
|
@ -149,6 +149,8 @@ pub struct WasmCosts {
|
|||||||
pub opcodes_mul: u32,
|
pub opcodes_mul: u32,
|
||||||
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
|
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
|
||||||
pub opcodes_div: u32,
|
pub opcodes_div: u32,
|
||||||
|
/// Whether create2 extern function is activated.
|
||||||
|
pub have_create2: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WasmCosts {
|
impl Default for WasmCosts {
|
||||||
@ -166,6 +168,7 @@ impl Default for WasmCosts {
|
|||||||
max_stack_height: 64*1024,
|
max_stack_height: 64*1024,
|
||||||
opcodes_mul: 3,
|
opcodes_mul: 3,
|
||||||
opcodes_div: 8,
|
opcodes_div: 8,
|
||||||
|
have_create2: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ pub enum FakeCallType {
|
|||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct FakeCall {
|
pub struct FakeCall {
|
||||||
pub call_type: FakeCallType,
|
pub call_type: FakeCallType,
|
||||||
|
pub create_scheme: Option<CreateContractAddress>,
|
||||||
pub gas: U256,
|
pub gas: U256,
|
||||||
pub sender_address: Option<Address>,
|
pub sender_address: Option<Address>,
|
||||||
pub receive_address: Option<Address>,
|
pub receive_address: Option<Address>,
|
||||||
@ -133,9 +134,10 @@ impl Ext for FakeExt {
|
|||||||
self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
|
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 {
|
self.calls.insert(FakeCall {
|
||||||
call_type: FakeCallType::Create,
|
call_type: FakeCallType::Create,
|
||||||
|
create_scheme: Some(address),
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
sender_address: None,
|
sender_address: None,
|
||||||
receive_address: None,
|
receive_address: None,
|
||||||
@ -159,6 +161,7 @@ impl Ext for FakeExt {
|
|||||||
|
|
||||||
self.calls.insert(FakeCall {
|
self.calls.insert(FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: *gas,
|
gas: *gas,
|
||||||
sender_address: Some(sender_address.clone()),
|
sender_address: Some(sender_address.clone()),
|
||||||
receive_address: Some(receive_address.clone()),
|
receive_address: Some(receive_address.clone()),
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
//! Env module glue for wasmi interpreter
|
//! Env module glue for wasmi interpreter
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use vm::WasmCosts;
|
||||||
use wasmi::{
|
use wasmi::{
|
||||||
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
|
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
|
||||||
MemoryRef, MemoryInstance, memory_units,
|
MemoryRef, MemoryInstance, memory_units,
|
||||||
@ -47,6 +48,7 @@ pub mod ids {
|
|||||||
pub const SENDER_FUNC: usize = 190;
|
pub const SENDER_FUNC: usize = 190;
|
||||||
pub const ORIGIN_FUNC: usize = 200;
|
pub const ORIGIN_FUNC: usize = 200;
|
||||||
pub const ELOG_FUNC: usize = 210;
|
pub const ELOG_FUNC: usize = 210;
|
||||||
|
pub const CREATE2_FUNC: usize = 220;
|
||||||
|
|
||||||
pub const PANIC_FUNC: usize = 1000;
|
pub const PANIC_FUNC: usize = 1000;
|
||||||
pub const DEBUG_FUNC: usize = 1010;
|
pub const DEBUG_FUNC: usize = 1010;
|
||||||
@ -125,6 +127,11 @@ pub mod signatures {
|
|||||||
Some(I32),
|
Some(I32),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
pub const CREATE2: StaticSignature = StaticSignature(
|
||||||
|
&[I32, I32, I32, I32, I32],
|
||||||
|
Some(I32),
|
||||||
|
);
|
||||||
|
|
||||||
pub const SUICIDE: StaticSignature = StaticSignature(
|
pub const SUICIDE: StaticSignature = StaticSignature(
|
||||||
&[I32],
|
&[I32],
|
||||||
None,
|
None,
|
||||||
@ -195,18 +202,21 @@ fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef {
|
|||||||
/// Maps all functions that runtime support to the corresponding contract import
|
/// Maps all functions that runtime support to the corresponding contract import
|
||||||
/// entries.
|
/// entries.
|
||||||
/// Also manages initial memory request from the runtime.
|
/// Also manages initial memory request from the runtime.
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ImportResolver {
|
pub struct ImportResolver {
|
||||||
max_memory: u32,
|
max_memory: u32,
|
||||||
memory: RefCell<Option<MemoryRef>>,
|
memory: RefCell<Option<MemoryRef>>,
|
||||||
|
|
||||||
|
have_create2: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportResolver {
|
impl ImportResolver {
|
||||||
/// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb)
|
/// 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 {
|
ImportResolver {
|
||||||
max_memory: max_memory,
|
max_memory: max_memory,
|
||||||
memory: RefCell::new(None),
|
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),
|
"sender" => host(signatures::SENDER, ids::SENDER_FUNC),
|
||||||
"origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC),
|
"origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC),
|
||||||
"elog" => host(signatures::ELOG, ids::ELOG_FUNC),
|
"elog" => host(signatures::ELOG, ids::ELOG_FUNC),
|
||||||
|
"create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(wasmi::Error::Instantiation(
|
return Err(wasmi::Error::Instantiation(
|
||||||
format!("Export {} not found", field_name),
|
format!("Export {} not found", field_name),
|
||||||
|
@ -90,7 +90,7 @@ impl vm::Vm for WasmInterpreter {
|
|||||||
|
|
||||||
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;
|
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(
|
let module_instance = wasmi::ModuleInstance::new(
|
||||||
&loaded_module,
|
&loaded_module,
|
||||||
|
@ -321,7 +321,7 @@ impl<'a> Runtime<'a> {
|
|||||||
if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); }
|
if self.gas_counter > self.gas_limit { return Err(Error::InvalidGasState); }
|
||||||
Ok(self.gas_limit - self.gas_counter)
|
Ok(self.gas_limit - self.gas_counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// General gas charging extern.
|
/// General gas charging extern.
|
||||||
fn gas(&mut self, args: RuntimeArgs) -> Result<()> {
|
fn gas(&mut self, args: RuntimeArgs) -> Result<()> {
|
||||||
let amount: u32 = args.nth_checked(0)?;
|
let amount: u32 = args.nth_checked(0)?;
|
||||||
@ -511,29 +511,7 @@ impl<'a> Runtime<'a> {
|
|||||||
self.return_u256_ptr(args.nth_checked(0)?, val)
|
self.return_u256_ptr(args.nth_checked(0)?, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new contract
|
fn do_create(&mut self, endowment: U256, code_ptr: u32, code_len: u32, result_ptr: u32, scheme: vm::CreateContractAddress) -> Result<RuntimeValue> {
|
||||||
///
|
|
||||||
/// 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);
|
|
||||||
|
|
||||||
let code = self.memory.get(code_ptr, code_len as usize)?;
|
let code = self.memory.get(code_ptr, code_len as usize)?;
|
||||||
|
|
||||||
self.adjusted_charge(|schedule| schedule.create_gas as u64)?;
|
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_mul)
|
||||||
/ U256::from(self.ext.schedule().wasm().opcodes_div);
|
/ 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) => {
|
vm::ContractCreateResult::Created(address, gas_left) => {
|
||||||
self.memory.set(result_ptr, &*address)?;
|
self.memory.set(result_ptr, &*address)?;
|
||||||
self.gas_counter = self.gas_limit -
|
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<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<()>
|
fn debug(&mut self, args: RuntimeArgs) -> Result<()>
|
||||||
{
|
{
|
||||||
trace!(target: "wasm", "Contract debug message: {}", {
|
trace!(target: "wasm", "Contract debug message: {}", {
|
||||||
@ -744,6 +775,7 @@ mod ext_impl {
|
|||||||
SENDER_FUNC => void!(self.sender(args)),
|
SENDER_FUNC => void!(self.sender(args)),
|
||||||
ORIGIN_FUNC => void!(self.origin(args)),
|
ORIGIN_FUNC => void!(self.origin(args)),
|
||||||
ELOG_FUNC => void!(self.elog(args)),
|
ELOG_FUNC => void!(self.elog(args)),
|
||||||
|
CREATE2_FUNC => some!(self.create2(args)),
|
||||||
_ => panic!("env module doesn't provide function at index {}", index),
|
_ => panic!("env module doesn't provide function at index {}", index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use byteorder::{LittleEndian, ByteOrder};
|
|||||||
use ethereum_types::{H256, U256, Address};
|
use ethereum_types::{H256, U256, Address};
|
||||||
|
|
||||||
use super::WasmInterpreter;
|
use super::WasmInterpreter;
|
||||||
use vm::{self, Vm, GasLeft, ActionParams, ActionValue};
|
use vm::{self, Vm, GasLeft, ActionParams, ActionValue, CreateContractAddress};
|
||||||
use vm::tests::{FakeCall, FakeExt, FakeCallType};
|
use vm::tests::{FakeCall, FakeExt, FakeCallType};
|
||||||
|
|
||||||
macro_rules! load_sample {
|
macro_rules! load_sample {
|
||||||
@ -138,7 +138,7 @@ fn logger() {
|
|||||||
U256::from(1_000_000_000),
|
U256::from(1_000_000_000),
|
||||||
"Logger sets 0x04 key to the trasferred value"
|
"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.
|
// This test checks if the contract can allocate memory and pass pointer to the result stream properly.
|
||||||
@ -173,7 +173,7 @@ fn identity() {
|
|||||||
sender,
|
sender,
|
||||||
"Idenity test contract does not return the sender passed"
|
"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
|
// Dispersion test sends byte array and expect the contract to 'disperse' the original elements with
|
||||||
@ -207,7 +207,7 @@ fn dispersion() {
|
|||||||
result,
|
result,
|
||||||
vec![0u8, 0, 125, 11, 197, 7, 255, 8, 19, 0]
|
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]
|
#[test]
|
||||||
@ -267,7 +267,7 @@ fn suicide() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert!(ext.suicides.contains(&refund));
|
assert!(ext.suicides.contains(&refund));
|
||||||
assert_eq!(gas_left, U256::from(93_348));
|
assert_eq!(gas_left, U256::from(93_346));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -281,14 +281,19 @@ fn create() {
|
|||||||
params.value = ActionValue::transfer(1_000_000_000);
|
params.value = ActionValue::transfer(1_000_000_000);
|
||||||
|
|
||||||
let mut ext = FakeExt::new().with_wasm();
|
let mut ext = FakeExt::new().with_wasm();
|
||||||
|
ext.schedule.wasm.as_mut().unwrap().have_create2 = true;
|
||||||
|
|
||||||
let gas_left = {
|
let gas_left = {
|
||||||
let mut interpreter = wasm_interpreter();
|
let mut interpreter = wasm_interpreter();
|
||||||
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
|
||||||
match result {
|
match result {
|
||||||
GasLeft::Known(gas) => gas,
|
GasLeft::Known(_) => {
|
||||||
GasLeft::NeedsReturn { .. } => {
|
panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero.");
|
||||||
panic!("Create contract should not return anthing because ext always fails on creation");
|
},
|
||||||
|
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(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Create,
|
call_type: FakeCallType::Create,
|
||||||
gas: U256::from(59_269),
|
create_scheme: Some(CreateContractAddress::FromSenderAndCodeHash),
|
||||||
|
gas: U256::from(52_017),
|
||||||
sender_address: None,
|
sender_address: None,
|
||||||
receive_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],
|
data: vec![0u8, 2, 4, 8, 16, 32, 64, 128],
|
||||||
code_address: None,
|
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]
|
#[test]
|
||||||
@ -340,6 +358,7 @@ fn call_msg() {
|
|||||||
assert!(ext.calls.contains(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: U256::from(33_000),
|
gas: U256::from(33_000),
|
||||||
sender_address: Some(receiver),
|
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])),
|
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(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: U256::from(20_000),
|
gas: U256::from(20_000),
|
||||||
sender_address: Some(sender),
|
sender_address: Some(sender),
|
||||||
receive_address: Some(receiver),
|
receive_address: Some(receiver),
|
||||||
@ -394,7 +414,7 @@ fn call_code() {
|
|||||||
// siphash result
|
// siphash result
|
||||||
let res = LittleEndian::read_u32(&result[..]);
|
let res = LittleEndian::read_u32(&result[..]);
|
||||||
assert_eq!(res, 4198595614);
|
assert_eq!(res, 4198595614);
|
||||||
assert_eq!(gas_left, U256::from(90_038));
|
assert_eq!(gas_left, U256::from(90_037));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -429,6 +449,7 @@ fn call_static() {
|
|||||||
assert!(ext.calls.contains(
|
assert!(ext.calls.contains(
|
||||||
&FakeCall {
|
&FakeCall {
|
||||||
call_type: FakeCallType::Call,
|
call_type: FakeCallType::Call,
|
||||||
|
create_scheme: None,
|
||||||
gas: U256::from(20_000),
|
gas: U256::from(20_000),
|
||||||
sender_address: Some(receiver),
|
sender_address: Some(receiver),
|
||||||
receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
|
receive_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
|
||||||
@ -442,7 +463,7 @@ fn call_static() {
|
|||||||
let res = LittleEndian::read_u32(&result[..]);
|
let res = LittleEndian::read_u32(&result[..]);
|
||||||
assert_eq!(res, 317632590);
|
assert_eq!(res, 317632590);
|
||||||
|
|
||||||
assert_eq!(gas_left, U256::from(90_043));
|
assert_eq!(gas_left, U256::from(90_042));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Realloc test
|
// Realloc test
|
||||||
@ -465,7 +486,7 @@ fn realloc() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert_eq!(result, vec![0u8; 2]);
|
assert_eq!(result, vec![0u8; 2]);
|
||||||
assert_eq!(gas_left, U256::from(92_842));
|
assert_eq!(gas_left, U256::from(92_848));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -487,7 +508,7 @@ fn alloc() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert_eq!(result, vec![5u8; 1024*400]);
|
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
|
// 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!(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
|
// Tests keccak calculation
|
||||||
@ -541,7 +562,7 @@ fn keccak() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
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
|
// 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(),
|
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
);
|
);
|
||||||
assert_eq!(gas_left, U256::from(92_086));
|
assert_eq!(gas_left, U256::from(92_095));
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiplication
|
// multiplication
|
||||||
@ -592,7 +613,7 @@ fn math_mul() {
|
|||||||
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
);
|
);
|
||||||
assert_eq!(gas_left, U256::from(91_414));
|
assert_eq!(gas_left, U256::from(91_423));
|
||||||
}
|
}
|
||||||
|
|
||||||
// subtraction
|
// subtraction
|
||||||
@ -614,7 +635,7 @@ fn math_sub() {
|
|||||||
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
);
|
);
|
||||||
assert_eq!(gas_left, U256::from(92_086));
|
assert_eq!(gas_left, U256::from(92_095));
|
||||||
}
|
}
|
||||||
|
|
||||||
// subtraction with overflow
|
// subtraction with overflow
|
||||||
@ -656,7 +677,7 @@ fn math_div() {
|
|||||||
U256::from_dec_str("1125000").unwrap(),
|
U256::from_dec_str("1125000").unwrap(),
|
||||||
(&result[..]).into()
|
(&result[..]).into()
|
||||||
);
|
);
|
||||||
assert_eq!(gas_left, U256::from(87_376));
|
assert_eq!(gas_left, U256::from(87_379));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -684,7 +705,7 @@ fn storage_metering() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 0 -> not 0
|
// 0 -> not 0
|
||||||
assert_eq!(gas_left, U256::from(72_399));
|
assert_eq!(gas_left, U256::from(72_395));
|
||||||
|
|
||||||
// #2
|
// #2
|
||||||
|
|
||||||
@ -703,7 +724,7 @@ fn storage_metering() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// not 0 -> not 0
|
// 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
|
// This test checks the ability of wasm contract to invoke
|
||||||
@ -791,7 +812,7 @@ fn externs() {
|
|||||||
"Gas limit requested and returned does not match"
|
"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]
|
#[test]
|
||||||
@ -817,7 +838,7 @@ fn embedded_keccak() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
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
|
/// This test checks the correctness of log extern
|
||||||
@ -852,7 +873,7 @@ fn events() {
|
|||||||
assert_eq!(&log_entry.data, b"gnihtemos");
|
assert_eq!(&log_entry.data, b"gnihtemos");
|
||||||
|
|
||||||
assert_eq!(&result, b"gnihtemos");
|
assert_eq!(&result, b"gnihtemos");
|
||||||
assert_eq!(gas_left, U256::from(81_351));
|
assert_eq!(gas_left, U256::from(83_158));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -146,6 +146,9 @@ pub struct Params {
|
|||||||
/// Wasm activation block height, if not activated from start
|
/// Wasm activation block height, if not activated from start
|
||||||
#[serde(rename="wasmActivationTransition")]
|
#[serde(rename="wasmActivationTransition")]
|
||||||
pub wasm_activation_transition: Option<Uint>,
|
pub wasm_activation_transition: Option<Uint>,
|
||||||
|
/// KIP4 activiation block height.
|
||||||
|
#[serde(rename="kip4Transition")]
|
||||||
|
pub kip4_transition: Option<Uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user