2019-01-07 11:33:07 +01:00
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.
2018-03-29 14:48:57 +02:00
2019-01-07 11:33:07 +01:00
// Parity Ethereum is free software: you can redistribute it and/or modify
2018-03-29 14:48:57 +02:00
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2019-01-07 11:33:07 +01:00
// Parity Ethereum is distributed in the hope that it will be useful,
2018-03-29 14:48:57 +02:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
2019-01-07 11:33:07 +01:00
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
2018-03-29 14:48:57 +02:00
2018-08-13 23:27:13 +02:00
use std ::cmp ;
2019-06-03 15:36:21 +02:00
use ethereum_types ::{ BigEndianHash , U256 , H256 , Address } ;
2018-02-05 20:59:27 +01:00
use vm ::{ self , CallType } ;
2018-03-12 12:37:32 +01:00
use wasmi ::{ self , MemoryRef , RuntimeArgs , RuntimeValue , Error as InterpreterError , Trap , TrapKind } ;
2018-02-05 20:59:27 +01:00
use super ::panic_payload ;
pub struct RuntimeContext {
pub address : Address ,
pub sender : Address ,
pub origin : Address ,
pub code_address : Address ,
2019-07-08 12:03:27 +02:00
pub code_version : U256 ,
2018-02-05 20:59:27 +01:00
pub value : U256 ,
}
2017-07-10 17:42:10 +02:00
2018-02-05 20:59:27 +01:00
pub struct Runtime < ' a > {
gas_counter : u64 ,
gas_limit : u64 ,
ext : & ' a mut vm ::Ext ,
context : RuntimeContext ,
memory : MemoryRef ,
args : Vec < u8 > ,
result : Vec < u8 > ,
}
2017-07-10 17:42:10 +02:00
2017-09-10 18:02:31 +02:00
/// User trap in native code
#[ derive(Debug, Clone, PartialEq) ]
2018-02-05 20:59:27 +01:00
pub enum Error {
2017-09-10 18:02:31 +02:00
/// Storage read error
StorageReadError ,
/// Storage update error
StorageUpdateError ,
2017-07-10 17:42:10 +02:00
/// Memory access violation
2017-09-10 18:02:31 +02:00
MemoryAccessViolation ,
/// Native code resulted in suicide
Suicide ,
2018-03-14 13:27:56 +01:00
/// Native code requested execution to finish
Return ,
2017-09-10 18:02:31 +02:00
/// Suicide was requested but coudn't complete
SuicideAbort ,
/// Invalid gas state inside interpreter
InvalidGasState ,
/// Query of the balance resulted in an error
BalanceQueryError ,
/// Failed allocation
AllocationFailed ,
/// Gas limit reached
GasLimit ,
/// Unknown runtime function
Unknown ,
/// Passed string had invalid utf-8 encoding
BadUtf8 ,
2017-11-05 16:54:35 +01:00
/// Log event error
Log ,
2017-09-10 18:02:31 +02:00
/// Other error in native code
Other ,
2018-02-05 20:59:27 +01:00
/// Syscall signature mismatch
InvalidSyscall ,
2018-03-12 12:37:32 +01:00
/// Unreachable instruction encountered
Unreachable ,
/// Invalid virtual call
InvalidVirtualCall ,
/// Division by zero
DivisionByZero ,
/// Invalid conversion to integer
InvalidConversionToInt ,
/// Stack overflow
StackOverflow ,
2017-09-10 18:02:31 +02:00
/// Panic with message
Panic ( String ) ,
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
impl wasmi ::HostError for Error { }
2018-03-12 12:37:32 +01:00
impl From < Trap > for Error {
fn from ( trap : Trap ) -> Self {
match * trap . kind ( ) {
TrapKind ::Unreachable = > Error ::Unreachable ,
TrapKind ::MemoryAccessOutOfBounds = > Error ::MemoryAccessViolation ,
TrapKind ::TableAccessOutOfBounds | TrapKind ::ElemUninitialized = > Error ::InvalidVirtualCall ,
TrapKind ::DivisionByZero = > Error ::DivisionByZero ,
TrapKind ::InvalidConversionToInt = > Error ::InvalidConversionToInt ,
TrapKind ::UnexpectedSignature = > Error ::InvalidVirtualCall ,
TrapKind ::StackOverflow = > Error ::StackOverflow ,
TrapKind ::Host ( _ ) = > Error ::Other ,
}
}
}
2018-02-05 20:59:27 +01:00
impl From < InterpreterError > for Error {
2018-03-12 12:37:32 +01:00
fn from ( err : InterpreterError ) -> Self {
match err {
2018-02-05 20:59:27 +01:00
InterpreterError ::Value ( _ ) = > Error ::InvalidSyscall ,
InterpreterError ::Memory ( _ ) = > Error ::MemoryAccessViolation ,
_ = > Error ::Other ,
2017-09-10 18:02:31 +02:00
}
2017-07-10 17:42:10 +02:00
}
}
2018-02-05 20:59:27 +01:00
impl ::std ::fmt ::Display for Error {
fn fmt ( & self , f : & mut ::std ::fmt ::Formatter ) -> ::std ::result ::Result < ( ) , ::std ::fmt ::Error > {
match * self {
Error ::StorageReadError = > write! ( f , " Storage read error " ) ,
Error ::StorageUpdateError = > write! ( f , " Storage update error " ) ,
Error ::MemoryAccessViolation = > write! ( f , " Memory access violation " ) ,
Error ::SuicideAbort = > write! ( f , " Attempt to suicide resulted in an error " ) ,
Error ::InvalidGasState = > write! ( f , " Invalid gas state " ) ,
Error ::BalanceQueryError = > write! ( f , " Balance query resulted in an error " ) ,
Error ::Suicide = > write! ( f , " Suicide result " ) ,
2018-03-14 13:27:56 +01:00
Error ::Return = > write! ( f , " Return result " ) ,
2018-02-05 20:59:27 +01:00
Error ::Unknown = > write! ( f , " Unknown runtime function invoked " ) ,
Error ::AllocationFailed = > write! ( f , " Memory allocation failed (OOM) " ) ,
Error ::BadUtf8 = > write! ( f , " String encoding is bad utf-8 sequence " ) ,
Error ::GasLimit = > write! ( f , " Invocation resulted in gas limit violated " ) ,
Error ::Log = > write! ( f , " Error occured while logging an event " ) ,
Error ::InvalidSyscall = > write! ( f , " Invalid syscall signature encountered at runtime " ) ,
Error ::Other = > write! ( f , " Other unspecified error " ) ,
2018-03-12 12:37:32 +01:00
Error ::Unreachable = > write! ( f , " Unreachable instruction encountered " ) ,
Error ::InvalidVirtualCall = > write! ( f , " Invalid virtual call " ) ,
Error ::DivisionByZero = > write! ( f , " Division by zero " ) ,
Error ::StackOverflow = > write! ( f , " Stack overflow " ) ,
Error ::InvalidConversionToInt = > write! ( f , " Invalid conversion to integer " ) ,
2018-02-05 20:59:27 +01:00
Error ::Panic ( ref msg ) = > write! ( f , " Panic: {} " , msg ) ,
2017-07-10 17:42:10 +02:00
}
}
}
2018-02-05 20:59:27 +01:00
type Result < T > = ::std ::result ::Result < T , Error > ;
2017-07-24 16:45:15 +02:00
2018-02-05 20:59:27 +01:00
impl < ' a > Runtime < ' a > {
2017-07-10 17:42:10 +02:00
/// New runtime for wasm contract with specified params
2018-02-05 20:59:27 +01:00
pub fn with_params (
ext : & mut vm ::Ext ,
memory : MemoryRef ,
2017-07-10 17:42:10 +02:00
gas_limit : u64 ,
2018-02-05 20:59:27 +01:00
args : Vec < u8 > ,
2017-07-24 16:45:15 +02:00
context : RuntimeContext ,
2018-02-05 20:59:27 +01:00
) -> Runtime {
2017-07-10 17:42:10 +02:00
Runtime {
gas_counter : 0 ,
gas_limit : gas_limit ,
memory : memory ,
ext : ext ,
2017-07-24 16:45:15 +02:00
context : context ,
2018-02-05 20:59:27 +01:00
args : args ,
result : Vec ::new ( ) ,
2017-07-10 17:42:10 +02:00
}
}
2018-02-05 20:59:27 +01:00
/// Loads 256-bit hash from the specifed sandboxed memory pointer
fn h256_at ( & self , ptr : u32 ) -> Result < H256 > {
let mut buf = [ 0 u8 ; 32 ] ;
self . memory . get_into ( ptr , & mut buf [ .. ] ) ? ;
2017-07-10 17:42:10 +02:00
2019-06-03 15:36:21 +02:00
Ok ( H256 ::from_slice ( & buf [ .. ] ) )
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
/// Loads 160-bit hash (Ethereum address) from the specified sandboxed memory pointer
fn address_at ( & self , ptr : u32 ) -> Result < Address > {
let mut buf = [ 0 u8 ; 20 ] ;
self . memory . get_into ( ptr , & mut buf [ .. ] ) ? ;
2017-10-09 13:12:58 +02:00
2019-06-03 15:36:21 +02:00
Ok ( Address ::from_slice ( & buf [ .. ] ) )
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
/// Loads 256-bit integer represented with bigendian from the specified sandboxed memory pointer
fn u256_at ( & self , ptr : u32 ) -> Result < U256 > {
let mut buf = [ 0 u8 ; 32 ] ;
self . memory . get_into ( ptr , & mut buf [ .. ] ) ? ;
2017-10-09 13:12:58 +02:00
2018-02-05 20:59:27 +01:00
Ok ( U256 ::from_big_endian ( & buf [ .. ] ) )
2017-10-04 18:09:18 +02:00
}
2018-02-12 17:59:41 +01:00
/// Charge specified amount of gas
///
/// Returns false if gas limit exceeded and true if not.
2018-02-05 20:59:27 +01:00
/// Intuition about the return value sense is to aswer the question 'are we allowed to continue?'
fn charge_gas ( & mut self , amount : u64 ) -> bool {
let prev = self . gas_counter ;
2018-03-12 13:55:28 +01:00
match prev . checked_add ( amount ) {
// gas charge overflow protection
None = > false ,
Some ( val ) if val > self . gas_limit = > false ,
Some ( _ ) = > {
self . gas_counter = prev + amount ;
true
}
2017-10-09 13:12:58 +02:00
}
2017-07-10 17:42:10 +02:00
}
2017-10-09 13:12:58 +02:00
/// Charge gas according to closure
2018-02-05 20:59:27 +01:00
pub fn charge < F > ( & mut self , f : F ) -> Result < ( ) >
2017-10-25 11:27:18 +02:00
where F : FnOnce ( & vm ::Schedule ) -> u64
2017-10-09 13:12:58 +02:00
{
let amount = f ( self . ext . schedule ( ) ) ;
if ! self . charge_gas ( amount as u64 ) {
2018-02-05 20:59:27 +01:00
Err ( Error ::GasLimit )
2017-10-09 13:12:58 +02:00
} else {
Ok ( ( ) )
}
}
2018-02-05 20:59:27 +01:00
/// Adjusted charge of gas which scales actual charge according to the wasm opcode counting coefficient
pub fn adjusted_charge < F > ( & mut self , f : F ) -> Result < ( ) >
where F : FnOnce ( & vm ::Schedule ) -> u64
{
2018-02-19 12:27:42 +01:00
self . charge ( | schedule | f ( schedule ) * schedule . wasm ( ) . opcodes_div as u64 / schedule . wasm ( ) . opcodes_mul as u64 )
2018-02-05 20:59:27 +01:00
}
2018-02-12 17:59:41 +01:00
/// Charge gas provided by the closure
///
/// Closure also can return overflowing flag as None in gas cost.
2018-02-05 20:59:27 +01:00
pub fn overflow_charge < F > ( & mut self , f : F ) -> Result < ( ) >
2017-11-05 16:54:35 +01:00
where F : FnOnce ( & vm ::Schedule ) -> Option < u64 >
{
let amount = match f ( self . ext . schedule ( ) ) {
Some ( amount ) = > amount ,
2018-02-05 20:59:27 +01:00
None = > { return Err ( Error ::GasLimit . into ( ) ) ; }
2017-11-05 16:54:35 +01:00
} ;
if ! self . charge_gas ( amount as u64 ) {
2018-02-05 20:59:27 +01:00
Err ( Error ::GasLimit . into ( ) )
2017-11-05 16:54:35 +01:00
} else {
Ok ( ( ) )
}
}
2018-02-05 20:59:27 +01:00
/// Same as overflow_charge, but with amount adjusted by wasm opcodes coeff
pub fn adjusted_overflow_charge < F > ( & mut self , f : F ) -> Result < ( ) >
where F : FnOnce ( & vm ::Schedule ) -> Option < u64 >
2017-07-10 17:42:10 +02:00
{
2018-02-05 20:59:27 +01:00
self . overflow_charge ( | schedule |
f ( schedule )
2018-02-19 12:27:42 +01:00
. and_then ( | x | x . checked_mul ( schedule . wasm ( ) . opcodes_div as u64 ) )
. map ( | x | x / schedule . wasm ( ) . opcodes_mul as u64 )
2018-02-05 20:59:27 +01:00
)
}
2017-07-10 17:42:10 +02:00
2018-02-12 17:59:41 +01:00
/// Read from the storage to wasm memory
2018-02-05 20:59:27 +01:00
pub fn storage_read ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
{
2018-03-12 12:37:32 +01:00
let key = self . h256_at ( args . nth_checked ( 0 ) ? ) ? ;
let val_ptr : u32 = args . nth_checked ( 1 ) ? ;
2017-07-10 17:42:10 +02:00
2018-02-05 20:59:27 +01:00
let val = self . ext . storage_at ( & key ) . map_err ( | _ | Error ::StorageReadError ) ? ;
2017-07-10 17:42:10 +02:00
2018-02-05 20:59:27 +01:00
self . adjusted_charge ( | schedule | schedule . sload_gas as u64 ) ? ;
2017-10-09 13:12:58 +02:00
2019-06-03 15:36:21 +02:00
self . memory . set ( val_ptr as u32 , val . as_bytes ( ) ) ? ;
2017-07-12 13:09:17 +02:00
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
2017-07-10 17:42:10 +02:00
}
2018-02-12 17:59:41 +01:00
/// Write to storage from wasm memory
2018-02-05 20:59:27 +01:00
pub fn storage_write ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
2017-07-24 16:45:15 +02:00
{
2018-03-12 12:37:32 +01:00
let key = self . h256_at ( args . nth_checked ( 0 ) ? ) ? ;
let val_ptr : u32 = args . nth_checked ( 1 ) ? ;
2018-02-05 20:59:27 +01:00
let val = self . h256_at ( val_ptr ) ? ;
2018-02-06 11:57:29 +01:00
let former_val = self . ext . storage_at ( & key ) . map_err ( | _ | Error ::StorageUpdateError ) ? ;
if former_val = = H256 ::zero ( ) & & val ! = H256 ::zero ( ) {
self . adjusted_charge ( | schedule | schedule . sstore_set_gas as u64 ) ? ;
} else {
self . adjusted_charge ( | schedule | schedule . sstore_reset_gas as u64 ) ? ;
}
2018-02-05 20:59:27 +01:00
self . ext . set_storage ( key , val ) . map_err ( | _ | Error ::StorageUpdateError ) ? ;
2018-02-06 11:57:29 +01:00
if former_val ! = H256 ::zero ( ) & & val = = H256 ::zero ( ) {
2018-10-15 11:09:55 +02:00
let sstore_clears_schedule = self . schedule ( ) . sstore_refund_gas ;
2018-09-07 12:51:08 +02:00
self . ext . add_sstore_refund ( sstore_clears_schedule ) ;
2018-02-06 11:57:29 +01:00
}
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
}
2018-02-12 17:59:41 +01:00
/// Return currently used schedule
2018-02-05 20:59:27 +01:00
pub fn schedule ( & self ) -> & vm ::Schedule {
self . ext . schedule ( )
}
2018-02-12 17:59:41 +01:00
/// Sets a return value for the call
///
/// Syscall takes 2 arguments:
/// * pointer in sandboxed memory where result is
/// * the length of the result
2018-02-05 20:59:27 +01:00
pub fn ret ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
2018-03-12 12:37:32 +01:00
let ptr : u32 = args . nth_checked ( 0 ) ? ;
let len : u32 = args . nth_checked ( 1 ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " Contract ret: {} bytes @ {} " , len , ptr ) ;
2017-07-24 16:45:15 +02:00
2018-02-05 20:59:27 +01:00
self . result = self . memory . get ( ptr , len as usize ) ? ;
2018-03-14 13:27:56 +01:00
Err ( Error ::Return )
2018-02-05 20:59:27 +01:00
}
2018-02-12 17:59:41 +01:00
/// Destroy the runtime, returning currently recorded result of the execution
2018-02-05 20:59:27 +01:00
pub fn into_result ( self ) -> Vec < u8 > {
self . result
2017-07-24 16:45:15 +02:00
}
2018-02-05 20:59:27 +01:00
/// Query current gas left for execution
pub fn gas_left ( & self ) -> Result < u64 > {
if self . gas_counter > self . gas_limit { return Err ( Error ::InvalidGasState ) ; }
Ok ( self . gas_limit - self . gas_counter )
}
2018-08-06 17:15:52 +02:00
2018-03-15 13:34:11 +01:00
/// General gas charging extern.
2018-02-05 20:59:27 +01:00
fn gas ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
2018-03-12 12:37:32 +01:00
let amount : u32 = args . nth_checked ( 0 ) ? ;
2018-02-05 20:59:27 +01:00
if self . charge_gas ( amount as u64 ) {
Ok ( ( ) )
} else {
Err ( Error ::GasLimit . into ( ) )
}
}
/// Query the length of the input bytes
fn input_legnth ( & mut self ) -> RuntimeValue {
RuntimeValue ::I32 ( self . args . len ( ) as i32 )
}
/// Write input bytes to the memory location using the passed pointer
fn fetch_input ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
2018-03-12 12:37:32 +01:00
let ptr : u32 = args . nth_checked ( 0 ) ? ;
2018-03-14 13:27:56 +01:00
let args_len = self . args . len ( ) as u64 ;
self . charge ( | s | args_len * s . wasm ( ) . memcpy as u64 ) ? ;
2018-02-05 20:59:27 +01:00
self . memory . set ( ptr , & self . args [ .. ] ) ? ;
Ok ( ( ) )
}
2018-02-12 17:59:41 +01:00
/// User panic
///
/// Contract can invoke this when he encounters unrecoverable error.
2018-02-05 20:59:27 +01:00
fn panic ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
2017-07-24 16:45:15 +02:00
{
2018-03-12 12:37:32 +01:00
let payload_ptr : u32 = args . nth_checked ( 0 ) ? ;
let payload_len : u32 = args . nth_checked ( 1 ) ? ;
2017-07-24 16:45:15 +02:00
2018-02-05 20:59:27 +01:00
let raw_payload = self . memory . get ( payload_ptr , payload_len as usize ) ? ;
let payload = panic_payload ::decode ( & raw_payload ) ;
let msg = format! (
" {msg}, {file}:{line}:{col} " ,
msg = payload
. msg
. as_ref ( )
. map ( String ::as_ref )
. unwrap_or ( " <msg was stripped> " ) ,
file = payload
. file
. as_ref ( )
. map ( String ::as_ref )
. unwrap_or ( " <unknown> " ) ,
line = payload . line . unwrap_or ( 0 ) ,
col = payload . col . unwrap_or ( 0 )
) ;
trace! ( target : " wasm " , " Contract custom panic message: {} " , msg ) ;
Err ( Error ::Panic ( msg ) . into ( ) )
2017-07-24 16:45:15 +02:00
}
fn do_call (
& mut self ,
use_val : bool ,
call_type : CallType ,
2018-02-05 20:59:27 +01:00
args : RuntimeArgs ,
2017-07-24 16:45:15 +02:00
)
2018-02-05 20:59:27 +01:00
-> Result < RuntimeValue >
2017-07-24 16:45:15 +02:00
{
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " runtime: CALL({:?}) " , call_type ) ;
2017-07-24 16:45:15 +02:00
2018-03-12 12:37:32 +01:00
let gas : u64 = args . nth_checked ( 0 ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " gas: {:?} " , gas ) ;
2017-07-24 16:45:15 +02:00
2018-03-12 12:37:32 +01:00
let address = self . address_at ( args . nth_checked ( 1 ) ? ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " address: {:?} " , address ) ;
2017-07-24 16:45:15 +02:00
2018-02-05 20:59:27 +01:00
let vofs = if use_val { 1 } else { 0 } ;
2018-03-12 12:37:32 +01:00
let val = if use_val { Some ( self . u256_at ( args . nth_checked ( 2 ) ? ) ? ) } else { None } ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " val: {:?} " , val ) ;
2017-07-24 16:45:15 +02:00
2018-03-12 12:37:32 +01:00
let input_ptr : u32 = args . nth_checked ( 2 + vofs ) ? ;
2017-07-24 16:45:15 +02:00
trace! ( target : " wasm " , " input_ptr: {:?} " , input_ptr ) ;
2018-03-12 12:37:32 +01:00
let input_len : u32 = args . nth_checked ( 3 + vofs ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " input_len: {:?} " , input_len ) ;
2017-07-24 16:45:15 +02:00
2018-03-12 12:37:32 +01:00
let result_ptr : u32 = args . nth_checked ( 4 + vofs ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " result_ptr: {:?} " , result_ptr ) ;
2017-07-24 16:45:15 +02:00
2018-03-12 12:37:32 +01:00
let result_alloc_len : u32 = args . nth_checked ( 5 + vofs ) ? ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " result_len: {:?} " , result_alloc_len ) ;
2017-12-22 19:54:35 +01:00
2017-07-24 16:45:15 +02:00
if let Some ( ref val ) = val {
let address_balance = self . ext . balance ( & self . context . address )
2018-02-05 20:59:27 +01:00
. map_err ( | _ | Error ::BalanceQueryError ) ? ;
2017-07-24 16:45:15 +02:00
if & address_balance < val {
trace! ( target : " wasm " , " runtime: call failed due to balance check " ) ;
2018-02-05 20:59:27 +01:00
return Ok ( ( - 1 i32 ) . into ( ) ) ;
2017-07-24 16:45:15 +02:00
}
}
2018-02-05 20:59:27 +01:00
self . adjusted_charge ( | schedule | schedule . call_gas as u64 ) ? ;
2017-10-09 13:12:58 +02:00
2017-07-24 16:45:15 +02:00
let mut result = Vec ::with_capacity ( result_alloc_len as usize ) ;
result . resize ( result_alloc_len as usize , 0 ) ;
2017-12-22 19:54:35 +01:00
2017-07-24 16:45:15 +02:00
// todo: optimize to use memory views once it's in
let payload = self . memory . get ( input_ptr , input_len as usize ) ? ;
2018-02-19 12:27:42 +01:00
let adjusted_gas = match gas . checked_mul ( self . ext . schedule ( ) . wasm ( ) . opcodes_div as u64 )
. map ( | x | x / self . ext . schedule ( ) . wasm ( ) . opcodes_mul as u64 )
2018-02-05 20:59:27 +01:00
{
Some ( x ) = > x ,
None = > {
trace! ( " CALL overflowed gas, call aborted with error returned " ) ;
return Ok ( RuntimeValue ::I32 ( - 1 ) )
} ,
} ;
self . charge ( | _ | adjusted_gas ) ? ;
2017-12-22 19:54:35 +01:00
2017-07-24 16:45:15 +02:00
let call_result = self . ext . call (
2017-12-22 19:54:35 +01:00
& gas . into ( ) ,
match call_type { CallType ::DelegateCall = > & self . context . sender , _ = > & self . context . address } ,
match call_type { CallType ::Call | CallType ::StaticCall = > & address , _ = > & self . context . address } ,
2017-07-24 16:45:15 +02:00
val ,
& payload ,
& address ,
call_type ,
2018-10-02 16:33:19 +02:00
false
) . ok ( ) . expect ( " Trap is false; trap error will not happen; qed " ) ;
2017-07-24 16:45:15 +02:00
match call_result {
2018-08-13 23:27:13 +02:00
vm ::MessageCallResult ::Success ( gas_left , data ) = > {
let len = cmp ::min ( result . len ( ) , data . len ( ) ) ;
( & mut result [ .. len ] ) . copy_from_slice ( & data [ .. len ] ) ;
2017-12-22 19:54:35 +01:00
// cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas
2018-02-05 20:59:27 +01:00
self . gas_counter = self . gas_counter -
2018-02-19 12:27:42 +01:00
gas_left . low_u64 ( ) * self . ext . schedule ( ) . wasm ( ) . opcodes_div as u64
/ self . ext . schedule ( ) . wasm ( ) . opcodes_mul as u64 ;
2017-12-22 19:54:35 +01:00
2017-07-24 16:45:15 +02:00
self . memory . set ( result_ptr , & result ) ? ;
2018-02-05 20:59:27 +01:00
Ok ( 0 i32 . into ( ) )
2017-07-24 16:45:15 +02:00
} ,
2018-08-13 23:27:13 +02:00
vm ::MessageCallResult ::Reverted ( gas_left , data ) = > {
let len = cmp ::min ( result . len ( ) , data . len ( ) ) ;
( & mut result [ .. len ] ) . copy_from_slice ( & data [ .. len ] ) ;
2017-12-22 19:54:35 +01:00
// cannot overflow, before making call gas_counter was incremented with gas, and gas_left < gas
2018-02-05 20:59:27 +01:00
self . gas_counter = self . gas_counter -
2018-02-19 12:27:42 +01:00
gas_left . low_u64 ( ) * self . ext . schedule ( ) . wasm ( ) . opcodes_div as u64
/ self . ext . schedule ( ) . wasm ( ) . opcodes_mul as u64 ;
2017-12-22 19:54:35 +01:00
2017-09-15 21:07:54 +02:00
self . memory . set ( result_ptr , & result ) ? ;
2018-02-05 20:59:27 +01:00
Ok ( ( - 1 i32 ) . into ( ) )
2017-09-15 21:07:54 +02:00
} ,
2017-08-01 13:33:49 +02:00
vm ::MessageCallResult ::Failed = > {
2018-02-05 20:59:27 +01:00
Ok ( ( - 1 i32 ) . into ( ) )
2017-07-24 16:45:15 +02:00
}
}
}
2018-02-12 17:59:41 +01:00
/// Message call
2018-02-05 20:59:27 +01:00
fn ccall ( & mut self , args : RuntimeArgs ) -> Result < RuntimeValue > {
self . do_call ( true , CallType ::Call , args )
2017-07-10 17:42:10 +02:00
}
2018-02-12 17:59:41 +01:00
/// Delegate call
2018-02-05 20:59:27 +01:00
fn dcall ( & mut self , args : RuntimeArgs ) -> Result < RuntimeValue > {
self . do_call ( false , CallType ::DelegateCall , args )
2017-07-10 17:42:10 +02:00
}
2018-02-12 17:59:41 +01:00
/// Static call
2018-02-05 20:59:27 +01:00
fn scall ( & mut self , args : RuntimeArgs ) -> Result < RuntimeValue > {
self . do_call ( false , CallType ::StaticCall , args )
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
fn return_address_ptr ( & mut self , ptr : u32 , val : Address ) -> Result < ( ) >
2017-07-10 17:42:10 +02:00
{
2018-02-19 12:27:42 +01:00
self . charge ( | schedule | schedule . wasm ( ) . static_address as u64 ) ? ;
2019-06-03 15:36:21 +02:00
self . memory . set ( ptr , val . as_bytes ( ) ) ? ;
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
fn return_u256_ptr ( & mut self , ptr : u32 , val : U256 ) -> Result < ( ) > {
2019-06-03 15:36:21 +02:00
let value : H256 = BigEndianHash ::from_uint ( & val ) ;
2018-02-19 12:27:42 +01:00
self . charge ( | schedule | schedule . wasm ( ) . static_u256 as u64 ) ? ;
2019-06-03 15:36:21 +02:00
self . memory . set ( ptr , value . as_bytes ( ) ) ? ;
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
2018-01-15 15:24:24 +01:00
}
2018-02-12 17:59:41 +01:00
/// Returns value (in Wei) passed to contract
2018-02-05 20:59:27 +01:00
pub fn value ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
let val = self . context . value ;
2018-03-12 12:37:32 +01:00
self . return_u256_ptr ( args . nth_checked ( 0 ) ? , val )
2017-07-10 17:42:10 +02:00
}
2017-08-20 06:02:59 +02:00
2018-08-06 17:15:52 +02:00
fn do_create ( & mut self , endowment : U256 , code_ptr : u32 , code_len : u32 , result_ptr : u32 , scheme : vm ::CreateContractAddress ) -> Result < RuntimeValue > {
2018-02-05 20:59:27 +01:00
let code = self . memory . get ( code_ptr , code_len as usize ) ? ;
2017-11-12 22:21:15 +01:00
2018-02-05 20:59:27 +01:00
self . adjusted_charge ( | schedule | schedule . create_gas as u64 ) ? ;
self . adjusted_charge ( | schedule | schedule . create_data_gas as u64 * code . len ( ) as u64 ) ? ;
2017-11-12 22:21:15 +01:00
2018-02-05 20:59:27 +01:00
let gas_left : U256 = U256 ::from ( self . gas_left ( ) ? )
2018-02-19 12:27:42 +01:00
* U256 ::from ( self . ext . schedule ( ) . wasm ( ) . opcodes_mul )
/ U256 ::from ( self . ext . schedule ( ) . wasm ( ) . opcodes_div ) ;
2017-11-12 22:21:15 +01:00
2019-07-08 12:03:27 +02:00
match self . ext . create ( & gas_left , & endowment , & code , & self . context . code_version , scheme , false ) . ok ( ) . expect ( " Trap is false; trap error will not happen; qed " ) {
2018-02-05 20:59:27 +01:00
vm ::ContractCreateResult ::Created ( address , gas_left ) = > {
2019-06-03 15:36:21 +02:00
self . memory . set ( result_ptr , address . as_bytes ( ) ) ? ;
2018-02-05 20:59:27 +01:00
self . gas_counter = self . gas_limit -
// this cannot overflow, since initial gas is in [0..u64::max) range,
// and gas_left cannot be bigger
2018-02-19 12:27:42 +01:00
gas_left . low_u64 ( ) * self . ext . schedule ( ) . wasm ( ) . opcodes_div as u64
/ self . ext . schedule ( ) . wasm ( ) . opcodes_mul as u64 ;
2018-02-05 20:59:27 +01:00
trace! ( target : " wasm " , " runtime: create contract success (@{:?}) " , address ) ;
Ok ( 0 i32 . into ( ) )
} ,
vm ::ContractCreateResult ::Failed = > {
trace! ( target : " wasm " , " runtime: create contract fail " ) ;
Ok ( ( - 1 i32 ) . into ( ) )
} ,
vm ::ContractCreateResult ::Reverted ( gas_left , _ ) = > {
trace! ( target : " wasm " , " runtime: create contract reverted " ) ;
self . gas_counter = self . gas_limit -
// this cannot overflow, since initial gas is in [0..u64::max) range,
// and gas_left cannot be bigger
2018-02-19 12:27:42 +01:00
gas_left . low_u64 ( ) * self . ext . schedule ( ) . wasm ( ) . opcodes_div as u64
/ self . ext . schedule ( ) . wasm ( ) . opcodes_mul as u64 ;
2017-08-20 06:02:59 +02:00
2018-02-05 20:59:27 +01:00
Ok ( ( - 1 i32 ) . into ( ) )
} ,
}
2017-11-12 22:21:15 +01:00
}
2018-08-06 17:15:52 +02:00
/// 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 ) ;
2019-06-03 15:36:21 +02:00
let salt : H256 = BigEndianHash ::from_uint ( & self . u256_at ( args . nth_checked ( 1 ) ? ) ? ) ;
2018-08-06 17:15:52 +02:00
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 ) )
}
2018-02-05 20:59:27 +01:00
fn debug ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
2017-11-12 22:21:15 +01:00
{
2018-03-12 13:55:28 +01:00
trace! ( target : " wasm " , " Contract debug message: {} " , {
let msg_ptr : u32 = args . nth_checked ( 0 ) ? ;
let msg_len : u32 = args . nth_checked ( 1 ) ? ;
2017-11-21 13:39:34 +01:00
2018-03-12 13:55:28 +01:00
String ::from_utf8 ( self . memory . get ( msg_ptr , msg_len as usize ) ? )
. map_err ( | _ | Error ::BadUtf8 ) ?
} ) ;
2017-11-21 13:39:34 +01:00
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
2017-11-21 13:39:34 +01:00
}
2017-09-10 18:02:31 +02:00
2018-02-05 20:59:27 +01:00
/// Pass suicide to state runtime
pub fn suicide ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
2017-09-10 18:02:31 +02:00
{
2018-03-12 12:37:32 +01:00
let refund_address = self . address_at ( args . nth_checked ( 0 ) ? ) ? ;
2017-10-09 13:12:58 +02:00
2018-02-05 20:59:27 +01:00
if self . ext . exists ( & refund_address ) . map_err ( | _ | Error ::SuicideAbort ) ? {
trace! ( target : " wasm " , " Suicide: refund to existing address {} " , refund_address ) ;
self . adjusted_charge ( | schedule | schedule . suicide_gas as u64 ) ? ;
} else {
trace! ( target : " wasm " , " Suicide: refund to new address {} " , refund_address ) ;
self . adjusted_charge ( | schedule | schedule . suicide_to_new_account_cost as u64 ) ? ;
}
2017-09-10 18:02:31 +02:00
2018-02-05 20:59:27 +01:00
self . ext . suicide ( & refund_address ) . map_err ( | _ | Error ::SuicideAbort ) ? ;
2017-09-10 18:02:31 +02:00
2018-02-05 20:59:27 +01:00
// We send trap to interpreter so it should abort further execution
Err ( Error ::Suicide . into ( ) )
2017-09-10 18:02:31 +02:00
}
2018-02-12 17:59:41 +01:00
/// Signature: `fn blockhash(number: i64, dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn blockhash ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
self . adjusted_charge ( | schedule | schedule . blockhash_gas as u64 ) ? ;
2018-03-12 12:37:32 +01:00
let hash = self . ext . blockhash ( & U256 ::from ( args . nth_checked ::< u64 > ( 0 ) ? ) ) ;
2019-06-03 15:36:21 +02:00
self . memory . set ( args . nth_checked ( 1 ) ? , hash . as_bytes ( ) ) ? ;
2017-10-09 13:12:58 +02:00
Ok ( ( ) )
2017-10-25 11:27:18 +02:00
}
2017-10-09 13:12:58 +02:00
2018-02-12 17:59:41 +01:00
/// Signature: `fn blocknumber() -> i64`
2018-02-05 20:59:27 +01:00
pub fn blocknumber ( & mut self ) -> Result < RuntimeValue > {
Ok ( RuntimeValue ::from ( self . ext . env_info ( ) . number ) )
2017-10-04 13:15:59 +02:00
}
2018-02-12 17:59:41 +01:00
/// Signature: `fn coinbase(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn coinbase ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
let coinbase = self . ext . env_info ( ) . author ;
2018-03-12 12:37:32 +01:00
self . return_address_ptr ( args . nth_checked ( 0 ) ? , coinbase )
2017-10-04 13:15:59 +02:00
}
2018-02-12 17:59:41 +01:00
/// Signature: `fn difficulty(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn difficulty ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
2017-10-09 13:12:58 +02:00
let difficulty = self . ext . env_info ( ) . difficulty ;
2018-03-12 12:37:32 +01:00
self . return_u256_ptr ( args . nth_checked ( 0 ) ? , difficulty )
2017-09-10 18:02:31 +02:00
}
2018-08-24 18:03:46 +02:00
/// 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
)
)
}
2018-02-12 17:59:41 +01:00
/// Signature: `fn gaslimit(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn gaslimit ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
2017-10-09 13:12:58 +02:00
let gas_limit = self . ext . env_info ( ) . gas_limit ;
2018-03-12 12:37:32 +01:00
self . return_u256_ptr ( args . nth_checked ( 0 ) ? , gas_limit )
2017-09-10 18:02:31 +02:00
}
2018-02-12 17:59:41 +01:00
/// Signature: `fn address(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn address ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
let address = self . context . address ;
2018-03-12 12:37:32 +01:00
self . return_address_ptr ( args . nth_checked ( 0 ) ? , address )
2018-02-05 20:59:27 +01:00
}
2017-08-20 06:02:59 +02:00
2018-02-12 17:59:41 +01:00
/// Signature: `sender(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn sender ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
let sender = self . context . sender ;
2018-03-12 12:37:32 +01:00
self . return_address_ptr ( args . nth_checked ( 0 ) ? , sender )
2017-08-20 06:02:59 +02:00
}
2017-10-09 13:12:58 +02:00
2018-02-12 17:59:41 +01:00
/// Signature: `origin(dest: *mut u8)`
2018-02-05 20:59:27 +01:00
pub fn origin ( & mut self , args : RuntimeArgs ) -> Result < ( ) > {
let origin = self . context . origin ;
2018-03-12 12:37:32 +01:00
self . return_address_ptr ( args . nth_checked ( 0 ) ? , origin )
2017-10-09 13:12:58 +02:00
}
2018-02-12 17:59:41 +01:00
/// Signature: `timestamp() -> i64`
2018-02-05 20:59:27 +01:00
pub fn timestamp ( & mut self ) -> Result < RuntimeValue > {
let timestamp = self . ext . env_info ( ) . timestamp ;
Ok ( RuntimeValue ::from ( timestamp ) )
2017-10-09 13:12:58 +02:00
}
2017-11-05 16:54:35 +01:00
2018-02-12 17:59:41 +01:00
/// Signature: `fn elog(topic_ptr: *const u8, topic_count: u32, data_ptr: *const u8, data_len: u32)`
2018-02-05 20:59:27 +01:00
pub fn elog ( & mut self , args : RuntimeArgs ) -> Result < ( ) >
2017-11-05 16:54:35 +01:00
{
2018-03-12 12:37:32 +01:00
let topic_ptr : u32 = args . nth_checked ( 0 ) ? ;
let topic_count : u32 = args . nth_checked ( 1 ) ? ;
let data_ptr : u32 = args . nth_checked ( 2 ) ? ;
let data_len : u32 = args . nth_checked ( 3 ) ? ;
2017-11-05 16:54:35 +01:00
if topic_count > 4 {
2018-02-05 20:59:27 +01:00
return Err ( Error ::Log . into ( ) ) ;
2017-11-05 16:54:35 +01:00
}
2018-02-05 20:59:27 +01:00
self . adjusted_overflow_charge ( | schedule |
2017-11-05 16:54:35 +01:00
{
let topics_gas = schedule . log_gas as u64 + schedule . log_topic_gas as u64 * topic_count as u64 ;
( schedule . log_data_gas as u64 )
. checked_mul ( schedule . log_data_gas as u64 )
. and_then ( | data_gas | data_gas . checked_add ( topics_gas ) )
}
) ? ;
let mut topics : Vec < H256 > = Vec ::with_capacity ( topic_count as usize ) ;
topics . resize ( topic_count as usize , H256 ::zero ( ) ) ;
for i in 0 .. topic_count {
2018-02-05 20:59:27 +01:00
let offset = i . checked_mul ( 32 ) . ok_or ( Error ::MemoryAccessViolation ) ?
. checked_add ( topic_ptr ) . ok_or ( Error ::MemoryAccessViolation ) ? ;
2017-11-05 16:54:35 +01:00
* topics . get_mut ( i as usize )
. expect ( " topics is resized to `topic_count`, i is in 0..topic count iterator, get_mut uses i as an indexer, get_mut cannot fail; qed " )
2019-06-03 15:36:21 +02:00
= H256 ::from_slice ( & self . memory . get ( offset , 32 ) ? [ .. ] ) ;
2017-11-05 16:54:35 +01:00
}
2018-02-05 20:59:27 +01:00
self . ext . log ( topics , & self . memory . get ( data_ptr , data_len as usize ) ? ) . map_err ( | _ | Error ::Log ) ? ;
2017-11-05 16:54:35 +01:00
2018-02-05 20:59:27 +01:00
Ok ( ( ) )
2017-11-05 16:54:35 +01:00
}
2017-07-10 17:42:10 +02:00
}
2018-02-05 20:59:27 +01:00
mod ext_impl {
2018-03-12 12:37:32 +01:00
use wasmi ::{ Externals , RuntimeArgs , RuntimeValue , Trap } ;
2018-02-05 20:59:27 +01:00
use env ::ids ::* ;
macro_rules ! void {
{ $e : expr } = > { { $e ? ; Ok ( None ) } }
}
macro_rules ! some {
{ $e : expr } = > { { Ok ( Some ( $e ? ) ) } }
}
macro_rules ! cast {
{ $e : expr } = > { { Ok ( Some ( $e ) ) } }
}
impl < ' a > Externals for super ::Runtime < ' a > {
fn invoke_index (
& mut self ,
index : usize ,
args : RuntimeArgs ,
2018-03-12 12:37:32 +01:00
) -> Result < Option < RuntimeValue > , Trap > {
2018-02-05 20:59:27 +01:00
match index {
STORAGE_WRITE_FUNC = > void! ( self . storage_write ( args ) ) ,
STORAGE_READ_FUNC = > void! ( self . storage_read ( args ) ) ,
RET_FUNC = > void! ( self . ret ( args ) ) ,
GAS_FUNC = > void! ( self . gas ( args ) ) ,
INPUT_LENGTH_FUNC = > cast! ( self . input_legnth ( ) ) ,
FETCH_INPUT_FUNC = > void! ( self . fetch_input ( args ) ) ,
PANIC_FUNC = > void! ( self . panic ( args ) ) ,
DEBUG_FUNC = > void! ( self . debug ( args ) ) ,
CCALL_FUNC = > some! ( self . ccall ( args ) ) ,
DCALL_FUNC = > some! ( self . dcall ( args ) ) ,
SCALL_FUNC = > some! ( self . scall ( args ) ) ,
VALUE_FUNC = > void! ( self . value ( args ) ) ,
CREATE_FUNC = > some! ( self . create ( args ) ) ,
SUICIDE_FUNC = > void! ( self . suicide ( args ) ) ,
BLOCKHASH_FUNC = > void! ( self . blockhash ( args ) ) ,
BLOCKNUMBER_FUNC = > some! ( self . blocknumber ( ) ) ,
COINBASE_FUNC = > void! ( self . coinbase ( args ) ) ,
DIFFICULTY_FUNC = > void! ( self . difficulty ( args ) ) ,
GASLIMIT_FUNC = > void! ( self . gaslimit ( args ) ) ,
TIMESTAMP_FUNC = > some! ( self . timestamp ( ) ) ,
ADDRESS_FUNC = > void! ( self . address ( args ) ) ,
SENDER_FUNC = > void! ( self . sender ( args ) ) ,
ORIGIN_FUNC = > void! ( self . origin ( args ) ) ,
ELOG_FUNC = > void! ( self . elog ( args ) ) ,
2018-08-06 17:15:52 +02:00
CREATE2_FUNC = > some! ( self . create2 ( args ) ) ,
2018-08-24 18:03:46 +02:00
GASLEFT_FUNC = > some! ( self . gasleft ( ) ) ,
2018-02-05 20:59:27 +01:00
_ = > panic! ( " env module doesn't provide function at index {} " , index ) ,
}
2017-07-10 17:42:10 +02:00
}
}
2018-02-12 17:59:41 +01:00
}