`gasleft` extern implemented for WASM runtime (kip-6) (#9357)

* Wasm gasleft extern added

* wasm_gasleft_activation_transition -> kip4_transition

* use kip-6 switch

* gasleft_panic -> gasleft_fail rename

* call_msg_gasleft test added and gas_left agustments because this https://github.com/paritytech/wasm-tests/pull/52

* change .. to _

* fix comment for the have_gasleft param

* update tests (0edbf860ff)
This commit is contained in:
Alexey 2018-08-24 20:03:46 +04:00 committed by Afri Schoedon
parent b87c7cac54
commit 5ed2527663
7 changed files with 134 additions and 11 deletions

@ -1 +1 @@
Subproject commit 242b8d8a89ecb3e11277f0beb8180c95792aac6b
Subproject commit 0edbf860ff7ed4b6b6336097ba44836e8c6482dd

View File

@ -127,6 +127,8 @@ pub struct CommonParams {
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,
/// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated.
pub kip6_transition: BlockNumber,
/// Gas limit bound divisor (how much gas limit can change per block)
pub gas_limit_bound_divisor: U256,
/// Registrar contract address.
@ -195,6 +197,9 @@ impl CommonParams {
if block_number >= self.kip4_transition {
wasm.have_create2 = true;
}
if block_number >= self.kip6_transition {
wasm.have_gasleft = true;
}
schedule.wasm = Some(wasm);
}
}
@ -308,6 +313,10 @@ impl From<ethjson::spec::Params> for CommonParams {
BlockNumber::max_value,
Into::into
),
kip6_transition: p.kip6_transition.map_or_else(
BlockNumber::max_value,
Into::into
),
}
}
}

View File

@ -151,6 +151,8 @@ pub struct WasmCosts {
pub opcodes_div: u32,
/// Whether create2 extern function is activated.
pub have_create2: bool,
/// Whether gasleft extern function is activated.
pub have_gasleft: bool,
}
impl Default for WasmCosts {
@ -169,6 +171,7 @@ impl Default for WasmCosts {
opcodes_mul: 3,
opcodes_div: 8,
have_create2: false,
have_gasleft: false,
}
}
}

View File

@ -49,6 +49,7 @@ pub mod ids {
pub const ORIGIN_FUNC: usize = 200;
pub const ELOG_FUNC: usize = 210;
pub const CREATE2_FUNC: usize = 220;
pub const GASLEFT_FUNC: usize = 230;
pub const PANIC_FUNC: usize = 1000;
pub const DEBUG_FUNC: usize = 1010;
@ -157,6 +158,11 @@ pub mod signatures {
None,
);
pub const GASLEFT: StaticSignature = StaticSignature(
&[],
Some(I64),
);
pub const GASLIMIT: StaticSignature = StaticSignature(
&[I32],
None,
@ -207,6 +213,7 @@ pub struct ImportResolver {
memory: RefCell<Option<MemoryRef>>,
have_create2: bool,
have_gasleft: bool,
}
impl ImportResolver {
@ -217,6 +224,7 @@ impl ImportResolver {
memory: RefCell::new(None),
have_create2: schedule.have_create2,
have_gasleft: schedule.have_gasleft,
}
}
@ -274,6 +282,7 @@ impl wasmi::ModuleImportResolver for ImportResolver {
"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),
"gasleft" if self.have_gasleft => host(signatures::GASLEFT, ids::GASLEFT_FUNC),
_ => {
return Err(wasmi::Error::Instantiation(
format!("Export {} not found", field_name),

View File

@ -666,6 +666,15 @@ impl<'a> Runtime<'a> {
self.return_u256_ptr(args.nth_checked(0)?, difficulty)
}
/// Signature: `fn gasleft() -> i64`
pub fn gasleft(&mut self) -> Result<RuntimeValue> {
Ok(RuntimeValue::from(
self.gas_left()? * self.ext.schedule().wasm().opcodes_mul as u64
/ self.ext.schedule().wasm().opcodes_div as u64
)
)
}
/// Signature: `fn gaslimit(dest: *mut u8)`
pub fn gaslimit(&mut self, args: RuntimeArgs) -> Result<()> {
let gas_limit = self.ext.env_info().gas_limit;
@ -782,6 +791,7 @@ mod ext_impl {
ORIGIN_FUNC => void!(self.origin(args)),
ELOG_FUNC => void!(self.elog(args)),
CREATE2_FUNC => some!(self.create2(args)),
GASLEFT_FUNC => some!(self.gasleft()),
_ => panic!("env module doesn't provide function at index {}", index),
}
}

View File

@ -303,7 +303,7 @@ fn create() {
&FakeCall {
call_type: FakeCallType::Create,
create_scheme: Some(CreateContractAddress::FromSenderAndCodeHash),
gas: U256::from(52_017),
gas: U256::from(49_674),
sender_address: None,
receive_address: None,
value: Some((1_000_000_000 / 2).into()),
@ -315,7 +315,7 @@ fn create() {
&FakeCall {
call_type: FakeCallType::Create,
create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash(H256::from([5u8].as_ref()))),
gas: U256::from(10_740),
gas: U256::from(6039),
sender_address: None,
receive_address: None,
value: Some((1_000_000_000 / 2).into()),
@ -323,7 +323,7 @@ fn create() {
code_address: None,
}
));
assert_eq!(gas_left, U256::from(10_675));
assert_eq!(gas_left, U256::from(5974));
}
#[test]
@ -371,6 +371,54 @@ fn call_msg() {
assert_eq!(gas_left, U256::from(91_672));
}
// The same as `call_msg`, but send a `pwasm_ethereum::gasleft`
// value as `gas` argument to the inner pwasm_ethereum::call
#[test]
fn call_msg_gasleft() {
::ethcore_logger::init_log();
let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap();
let contract_address: Address = "0d461d4174b4ae35775c4a342f1e5e1e4e6c4db5".parse().unwrap();
let mut params = ActionParams::default();
params.sender = sender.clone();
params.address = receiver.clone();
params.code_address = contract_address.clone();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("call_gasleft.wasm")));
params.data = Some(Vec::new());
let mut ext = FakeExt::new().with_wasm();
ext.schedule.wasm.as_mut().unwrap().have_gasleft = true;
ext.balances.insert(receiver.clone(), U256::from(10000000000u64));
let gas_left = {
let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(gas_left) => gas_left,
GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); },
}
};
trace!(target: "wasm", "fake_calls: {:?}", &ext.calls);
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
create_scheme: None,
gas: U256::from(91_165),
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])),
value: Some(1000000000.into()),
data: vec![129u8, 123, 113, 107, 101, 97],
code_address: Some(Address::from([99, 88, 77, 66, 55, 44, 33, 22, 11, 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 0])),
}
));
assert_eq!(gas_left, U256::from(91_671));
}
#[test]
fn call_code() {
::ethcore_logger::init_log();
@ -591,7 +639,7 @@ fn math_add() {
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(92_095));
assert_eq!(gas_left, U256::from(92_072));
}
// multiplication
@ -613,7 +661,7 @@ fn math_mul() {
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(91_423));
assert_eq!(gas_left, U256::from(91_400));
}
// subtraction
@ -635,7 +683,7 @@ fn math_sub() {
U256::from_dec_str("111111111111111111111111111111").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(92_095));
assert_eq!(gas_left, U256::from(92_072));
}
// subtraction with overflow
@ -677,7 +725,7 @@ fn math_div() {
U256::from_dec_str("1125000").unwrap(),
(&result[..]).into()
);
assert_eq!(gas_left, U256::from(87_379));
assert_eq!(gas_left, U256::from(85_700));
}
#[test]
@ -705,7 +753,7 @@ fn storage_metering() {
};
// 0 -> not 0
assert_eq!(gas_left, U256::from(72_395));
assert_eq!(gas_left, U256::from(72_164));
// #2
@ -724,7 +772,7 @@ fn storage_metering() {
};
// not 0 -> not 0
assert_eq!(gas_left, U256::from(87_395));
assert_eq!(gas_left, U256::from(87_164));
}
// This test checks the ability of wasm contract to invoke
@ -815,6 +863,47 @@ fn externs() {
assert_eq!(gas_left, U256::from(90_428));
}
// This test checks the ability of wasm contract to invoke gasleft
#[test]
fn gasleft() {
::ethcore_logger::init_log();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("gasleft.wasm")));
let mut ext = FakeExt::new().with_wasm();
ext.schedule.wasm.as_mut().unwrap().have_gasleft = true;
let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => {},
GasLeft::NeedsReturn { gas_left, data, .. } => {
let gas = LittleEndian::read_u64(data.as_ref());
assert_eq!(gas, 93_423);
assert_eq!(gas_left, U256::from(93_349));
},
}
}
// This test should fail because
// ext.schedule.wasm.as_mut().unwrap().have_gasleft = false;
#[test]
fn gasleft_fail() {
::ethcore_logger::init_log();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("gasleft.wasm")));
let mut ext = FakeExt::new().with_wasm();
let mut interpreter = wasm_interpreter(params);
match interpreter.exec(&mut ext) {
Err(_) => {},
Ok(_) => panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false")
}
}
#[test]
fn embedded_keccak() {
::ethcore_logger::init_log();
@ -873,7 +962,7 @@ fn events() {
assert_eq!(&log_entry.data, b"gnihtemos");
assert_eq!(&result, b"gnihtemos");
assert_eq!(gas_left, U256::from(83_158));
assert_eq!(gas_left, U256::from(83_161));
}
#[test]

View File

@ -152,6 +152,9 @@ pub struct Params {
/// KIP4 activiation block height.
#[serde(rename="kip4Transition")]
pub kip4_transition: Option<Uint>,
/// KIP6 activiation block height.
#[serde(rename="kip6Transition")]
pub kip6_transition: Option<Uint>,
}
#[cfg(test)]