Skip to content

Commit

Permalink
WASM gas schedule (openethereum#6638)
Browse files Browse the repository at this point in the history
* some failing tests

* finalize

* fallable -> fallible

* alter mul/div/static_i64

* Update schedule.rs

* to u32

* balance charge also

* review fixes

* remove redundant line
  • Loading branch information
NikVolf authored and gavofyork committed Oct 9, 2017
1 parent 8d1964b commit 1601030
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 46 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions ethcore/vm/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,49 @@ pub struct Schedule {
pub kill_dust: CleanDustMode,
/// Enable EIP-86 rules
pub eip86: bool,
/// Wasm extra schedule settings
pub wasm: WasmCosts,
}

/// Wasm cost table
pub struct WasmCosts {
/// Arena allocator cost, per byte
pub alloc: u32,
/// Div operations multiplier.
pub div: u32,
/// Div operations multiplier.
pub mul: u32,
/// Memory (load/store) operations multiplier.
pub mem: u32,
/// Memory copy operation.
pub mem_copy: u32,
/// Static region charge, per byte.
pub static_region: u32,
/// General static query of u64 value from env-info
pub static_u64: u32,
/// General static query of U256 value from env-info
pub static_u256: u32,
/// General static query of Address value from env-info
pub static_address: u32,
}

impl Default for WasmCosts {
fn default() -> Self {
WasmCosts {
alloc: 2,
div: 16,
mul: 4,
mem: 2,
mem_copy: 1,
static_region: 1,

// due to runtime issues, this can be slow
static_u64: 32,

static_u256: 64,
static_address: 40,
}
}
}

/// Dust accounts cleanup mode.
Expand Down Expand Up @@ -187,6 +230,7 @@ impl Schedule {
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip86: false,
wasm: Default::default(),
}
}

Expand Down Expand Up @@ -249,6 +293,7 @@ impl Schedule {
have_static_call: false,
kill_dust: CleanDustMode::Off,
eip86: false,
wasm: Default::default(),
}
}
}
Expand Down
11 changes: 10 additions & 1 deletion ethcore/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,18 @@ impl vm::Vm for WasmInterpreter {
&mut cursor
).map_err(|err| {
vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err))
})?
})?,
runtime.gas_rules(),
);

let data_section_length = contract_module.data_section()
.map(|section| section.entries().iter().fold(0, |sum, entry| sum + entry.value().len()))
.unwrap_or(0)
as u64;

let static_segment_cost = data_section_length * runtime.ext().schedule().wasm.static_region as u64;
runtime.charge(|_| static_segment_cost).map_err(Error)?;

let d_ptr = runtime.write_descriptor(&params.data.unwrap_or_default())
.map_err(Error)?;

Expand Down
143 changes: 120 additions & 23 deletions ethcore/wasm/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use byteorder::{LittleEndian, ByteOrder};

use vm;
use parity_wasm::interpreter;
use wasm_utils::rules;
use bigint::prelude::U256;
use bigint::hash::H256;
use util::Address;
Expand Down Expand Up @@ -111,6 +112,7 @@ pub struct Runtime<'a, 'b> {
memory: Arc<InterpreterMemoryInstance>,
context: RuntimeContext,
instance: &'b InterpreterProgramInstance,
gas_rules: rules::Set,
}

impl<'a, 'b> Runtime<'a, 'b> {
Expand All @@ -123,6 +125,20 @@ impl<'a, 'b> Runtime<'a, 'b> {
context: RuntimeContext,
program_instance: &'d InterpreterProgramInstance,
) -> Runtime<'c, 'd> {

let rules = {
let schedule = ext.schedule();

rules::Set::new({
let mut vals = ::std::collections::HashMap::with_capacity(4);
vals.insert(rules::InstructionType::Load, schedule.wasm.mem as u32);
vals.insert(rules::InstructionType::Store, schedule.wasm.mem as u32);
vals.insert(rules::InstructionType::Div, schedule.wasm.div as u32);
vals.insert(rules::InstructionType::Mul, schedule.wasm.mul as u32);
vals
})
};

Runtime {
gas_counter: 0,
gas_limit: gas_limit,
Expand All @@ -131,6 +147,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
ext: ext,
context: context,
instance: program_instance,
gas_rules: rules,
}
}

Expand All @@ -143,6 +160,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
let key = self.pop_h256(&mut context)?;
trace!(target: "wasm", "storage_write: value {} at @{}", &val, &key);

self.charge(|schedule| schedule.sstore_set_gas as u64)?;

self.ext.set_storage(key, val).map_err(|_| UserTrap::StorageUpdateError)?;

Ok(Some(0i32.into()))
Expand All @@ -155,9 +174,10 @@ impl<'a, 'b> Runtime<'a, 'b> {
let mut context = context;
let val_ptr = context.value_stack.pop_as::<i32>()?;
let key = self.pop_h256(&mut context)?;

let val = self.ext.storage_at(&key).map_err(|_| UserTrap::StorageReadError)?;

self.charge(|schedule| schedule.sload_gas as u64)?;

self.memory.set(val_ptr as u32, &*val)?;

Ok(Some(0.into()))
Expand All @@ -170,6 +190,9 @@ impl<'a, 'b> Runtime<'a, 'b> {
let mut context = context;
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
let address = self.pop_address(&mut context)?;

self.charge(|schedule| schedule.balance_gas as u64)?;

let balance = self.ext.balance(&address).map_err(|_| UserTrap::BalanceQueryError)?;
let value: H256 = balance.into();
self.memory.set(return_ptr, &*value)?;
Expand All @@ -183,12 +206,32 @@ impl<'a, 'b> Runtime<'a, 'b> {
let mut context = context;
let refund_address = self.pop_address(&mut context)?;

if self.ext.exists(&refund_address).map_err(|_| UserTrap::SuicideAbort)? {
trace!(target: "wasm", "Suicide: refund to existing address {}", refund_address);
self.charge(|schedule| schedule.suicide_gas as u64)?;
} else {
trace!(target: "wasm", "Suicide: refund to new address {}", refund_address);
self.charge(|schedule| schedule.suicide_to_new_account_cost as u64)?;
}

self.ext.suicide(&refund_address).map_err(|_| UserTrap::SuicideAbort)?;

// We send trap to interpreter so it should abort further execution
Err(UserTrap::Suicide.into())
}

/// Charge gas according to closure
pub fn charge<F>(&mut self, f: F) -> Result<(), InterpreterError>
where F: FnOnce(&vm::Schedule) -> u64
{
let amount = f(self.ext.schedule());
if !self.charge_gas(amount as u64) {
Err(UserTrap::GasLimit.into())
} else {
Ok(())
}
}

/// Invoke create in the state runtime
pub fn create(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
Expand All @@ -211,6 +254,9 @@ impl<'a, 'b> Runtime<'a, 'b> {

let code = self.memory.get(code_ptr, code_len as usize)?;

self.charge(|schedule| schedule.create_gas as u64)?;
self.charge(|schedule| schedule.create_data_gas as u64 * code.len() as u64)?;

let gas_left = self.gas_left()
.map_err(|_| UserTrap::InvalidGasState)?
.into();
Expand Down Expand Up @@ -312,6 +358,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
}
}

self.charge(|schedule| schedule.call_gas as u64)?;

let mut result = Vec::with_capacity(result_alloc_len as usize);
result.resize(result_alloc_len as usize, 0);
let gas = self.gas_left()
Expand Down Expand Up @@ -369,6 +417,9 @@ impl<'a, 'b> Runtime<'a, 'b> {
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let amount = context.value_stack.pop_as::<i32>()? as u32;

self.charge(|schedule| schedule.wasm.alloc as u64 * amount as u64)?;

let previous_top = self.dynamic_top;
self.dynamic_top = previous_top + amount;
Ok(Some((previous_top as i32).into()))
Expand Down Expand Up @@ -492,13 +543,15 @@ impl<'a, 'b> Runtime<'a, 'b> {
&*self.memory
}

fn mem_copy(&self, context: InterpreterCallerContext)
fn mem_copy(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let len = context.value_stack.pop_as::<i32>()? as u32;
let dst = context.value_stack.pop_as::<i32>()? as u32;
let src = context.value_stack.pop_as::<i32>()? as u32;

self.charge(|schedule| schedule.wasm.mem_copy as u64 * len as u64)?;

let mem = self.memory().get(src, len as usize)?;
self.memory().set(dst, &mem)?;

Expand Down Expand Up @@ -542,6 +595,8 @@ impl<'a, 'b> Runtime<'a, 'b> {
let block_hi = context.value_stack.pop_as::<i32>()? as u32;
let block_lo = context.value_stack.pop_as::<i32>()? as u32;

self.charge(|schedule| schedule.blockhash_gas as u64)?;

let block_num = (block_hi as u64) << 32 | block_lo as u64;

trace!("Requesting block hash for block #{}", block_num);
Expand All @@ -552,44 +607,72 @@ impl<'a, 'b> Runtime<'a, 'b> {
Ok(Some(0i32.into()))
}

fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<(), InterpreterError>
{
self.charge(|schedule| schedule.wasm.static_address as u64)?;
self.memory.set(ptr, &*val)?;
Ok(())
}

fn return_u256_ptr(&mut self, ptr: u32, val: U256) -> Result<(), InterpreterError> {
let value: H256 = val.into();
self.charge(|schedule| schedule.wasm.static_u256 as u64)?;
self.memory.set(ptr, &*value)?;
Ok(())
}

fn coinbase(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
self.memory.set(return_ptr, &*self.ext.env_info().author)?;
let author = self.ext.env_info().author;
self.return_address_ptr(
context.value_stack.pop_as::<i32>()? as u32,
author,
)?;
Ok(None)
}

fn sender(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
self.memory.set(return_ptr, &*self.context.sender)?;
Ok(None)
let sender = self.context.sender;
self.return_address_ptr(
context.value_stack.pop_as::<i32>()? as u32,
sender,
)?;
Ok(None)
}

fn address(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
self.memory.set(return_ptr, &*self.context.address)?;
Ok(None)
let addr = self.context.address;
self.return_address_ptr(
context.value_stack.pop_as::<i32>()? as u32,
addr,
)?;
Ok(None)
}

fn origin(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
self.memory.set(return_ptr, &*self.context.origin)?;
Ok(None)
let origin = self.context.origin;
self.return_address_ptr(
context.value_stack.pop_as::<i32>()? as u32,
origin,
)?;
Ok(None)
}

fn value(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
let value: H256 = self.context.value.clone().into();
self.memory.set(return_ptr, &*value)?;
let value = self.context.value;
self.return_u256_ptr(
context.value_stack.pop_as::<i32>()? as u32,
value,
)?;
Ok(None)
}

Expand All @@ -610,22 +693,28 @@ impl<'a, 'b> Runtime<'a, 'b> {
fn difficulty(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
let difficulty: H256 = self.ext.env_info().difficulty.into();
self.memory.set(return_ptr, &*difficulty)?;
let difficulty = self.ext.env_info().difficulty;
self.return_u256_ptr(
context.value_stack.pop_as::<i32>()? as u32,
difficulty,
)?;
Ok(None)
}

fn ext_gas_limit(&mut self, context: InterpreterCallerContext)
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
{
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
let gas_limit: H256 = self.ext.env_info().gas_limit.into();
self.memory.set(return_ptr, &*gas_limit)?;
Ok(None)
let gas_limit = self.ext.env_info().gas_limit;
self.return_u256_ptr(
context.value_stack.pop_as::<i32>()? as u32,
gas_limit,
)?;
Ok(None)
}

fn return_i64(&mut self, val: i64) -> Result<Option<interpreter::RuntimeValue>, InterpreterError> {
self.charge(|schedule| schedule.wasm.static_u64 as u64)?;

let uval = val as u64;
let hi = (uval >> 32) as i32;
let lo = (uval << 32 >> 32) as i32;
Expand Down Expand Up @@ -656,6 +745,14 @@ impl<'a, 'b> Runtime<'a, 'b> {
)
)
}

pub fn gas_rules(&self) -> &rules::Set {
&self.gas_rules
}

pub fn ext(&mut self) -> &mut vm::Ext {
self.ext
}
}

impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
Expand Down
Loading

0 comments on commit 1601030

Please sign in to comment.