From 0bed5976e368e948566f5a445f5fa17088674f3d Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 27 Jun 2018 19:33:32 +0800 Subject: [PATCH] Refactor evm Instruction to be a c-like enum (#8914) * Add a basic instruction c-like enum * Fix all compiling errors * Fix tests * Access instruction info as a Instruction impl * Use macro to avoid duplication in from_u8 * Use single space instead of multiple tabs to avoid formatting issue * Fix evmbin compile * typo: indentation * Use if let to remove an expect * Address grumbles --- ethcore/evm/src/instructions.rs | 1063 ++++++++++--------- ethcore/evm/src/interpreter/gasometer.rs | 6 +- ethcore/evm/src/interpreter/mod.rs | 177 +-- ethcore/evm/src/interpreter/shared_cache.rs | 14 +- ethcore/evm/src/lib.rs | 2 +- evmbin/src/display/json.rs | 7 +- evmbin/src/display/std_json.rs | 9 +- 7 files changed, 662 insertions(+), 616 deletions(-) diff --git a/ethcore/evm/src/instructions.rs b/ethcore/evm/src/instructions.rs index 76f99a933..7a41ed33c 100644 --- a/ethcore/evm/src/instructions.rs +++ b/ethcore/evm/src/instructions.rs @@ -16,83 +16,378 @@ //! VM Instructions list and utility functions -pub type Instruction = u8; +pub use self::Instruction::*; -/// Returns true if given instruction is `PUSHN` instruction. -pub fn is_push(i: Instruction) -> bool { - i >= PUSH1 && i <= PUSH32 +macro_rules! enum_with_from_u8 { + ( + $( #[$enum_attr:meta] )* + pub enum $name:ident { + $( $( #[$variant_attr:meta] )* $variant:ident = $discriminator:expr ),+, + } + ) => { + $( #[$enum_attr] )* + pub enum $name { + $( $( #[$variant_attr] )* $variant = $discriminator ),+, + } + + impl $name { + #[doc = "Convert from u8 to the given enum"] + pub fn from_u8(value: u8) -> Option { + match value { + $( $discriminator => Some($variant) ),+, + _ => None, + } + } + } + }; } -#[test] -fn test_is_push() { - assert!(is_push(PUSH1)); - assert!(is_push(PUSH32)); - assert!(!is_push(DUP1)); -} +enum_with_from_u8! { + #[doc = "Virtual machine bytecode instruction."] + #[repr(u8)] + #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] + pub enum Instruction { + #[doc = "halts execution"] + STOP = 0x00, + #[doc = "addition operation"] + ADD = 0x01, + #[doc = "mulitplication operation"] + MUL = 0x02, + #[doc = "subtraction operation"] + SUB = 0x03, + #[doc = "integer division operation"] + DIV = 0x04, + #[doc = "signed integer division operation"] + SDIV = 0x05, + #[doc = "modulo remainder operation"] + MOD = 0x06, + #[doc = "signed modulo remainder operation"] + SMOD = 0x07, + #[doc = "unsigned modular addition"] + ADDMOD = 0x08, + #[doc = "unsigned modular multiplication"] + MULMOD = 0x09, + #[doc = "exponential operation"] + EXP = 0x0a, + #[doc = "extend length of signed integer"] + SIGNEXTEND = 0x0b, -/// Returns number of bytes to read for `PUSHN` instruction -/// PUSH1 -> 1 -pub fn get_push_bytes(i: Instruction) -> usize { - assert!(is_push(i), "Only for PUSH instructions."); - (i - PUSH1 + 1) as usize -} + #[doc = "less-than comparision"] + LT = 0x10, + #[doc = "greater-than comparision"] + GT = 0x11, + #[doc = "signed less-than comparision"] + SLT = 0x12, + #[doc = "signed greater-than comparision"] + SGT = 0x13, + #[doc = "equality comparision"] + EQ = 0x14, + #[doc = "simple not operator"] + ISZERO = 0x15, + #[doc = "bitwise AND operation"] + AND = 0x16, + #[doc = "bitwise OR operation"] + OR = 0x17, + #[doc = "bitwise XOR operation"] + XOR = 0x18, + #[doc = "bitwise NOT opertation"] + NOT = 0x19, + #[doc = "retrieve single byte from word"] + BYTE = 0x1a, + #[doc = "shift left operation"] + SHL = 0x1b, + #[doc = "logical shift right operation"] + SHR = 0x1c, + #[doc = "arithmetic shift right operation"] + SAR = 0x1d, -/// Returns number of bytes to read for `PUSHN` instruction or 0. -pub fn push_bytes(i: Instruction) -> usize { - if is_push(i) { - get_push_bytes(i) - } else { - 0 + #[doc = "compute SHA3-256 hash"] + SHA3 = 0x20, + + #[doc = "get address of currently executing account"] + ADDRESS = 0x30, + #[doc = "get balance of the given account"] + BALANCE = 0x31, + #[doc = "get execution origination address"] + ORIGIN = 0x32, + #[doc = "get caller address"] + CALLER = 0x33, + #[doc = "get deposited value by the instruction/transaction responsible for this execution"] + CALLVALUE = 0x34, + #[doc = "get input data of current environment"] + CALLDATALOAD = 0x35, + #[doc = "get size of input data in current environment"] + CALLDATASIZE = 0x36, + #[doc = "copy input data in current environment to memory"] + CALLDATACOPY = 0x37, + #[doc = "get size of code running in current environment"] + CODESIZE = 0x38, + #[doc = "copy code running in current environment to memory"] + CODECOPY = 0x39, + #[doc = "get price of gas in current environment"] + GASPRICE = 0x3a, + #[doc = "get external code size (from another contract)"] + EXTCODESIZE = 0x3b, + #[doc = "copy external code (from another contract)"] + EXTCODECOPY = 0x3c, + #[doc = "get the size of the return data buffer for the last call"] + RETURNDATASIZE = 0x3d, + #[doc = "copy return data buffer to memory"] + RETURNDATACOPY = 0x3e, + + #[doc = "get hash of most recent complete block"] + BLOCKHASH = 0x40, + #[doc = "get the block's coinbase address"] + COINBASE = 0x41, + #[doc = "get the block's timestamp"] + TIMESTAMP = 0x42, + #[doc = "get the block's number"] + NUMBER = 0x43, + #[doc = "get the block's difficulty"] + DIFFICULTY = 0x44, + #[doc = "get the block's gas limit"] + GASLIMIT = 0x45, + + #[doc = "remove item from stack"] + POP = 0x50, + #[doc = "load word from memory"] + MLOAD = 0x51, + #[doc = "save word to memory"] + MSTORE = 0x52, + #[doc = "save byte to memory"] + MSTORE8 = 0x53, + #[doc = "load word from storage"] + SLOAD = 0x54, + #[doc = "save word to storage"] + SSTORE = 0x55, + #[doc = "alter the program counter"] + JUMP = 0x56, + #[doc = "conditionally alter the program counter"] + JUMPI = 0x57, + #[doc = "get the program counter"] + PC = 0x58, + #[doc = "get the size of active memory"] + MSIZE = 0x59, + #[doc = "get the amount of available gas"] + GAS = 0x5a, + #[doc = "set a potential jump destination"] + JUMPDEST = 0x5b, + + #[doc = "place 1 byte item on stack"] + PUSH1 = 0x60, + #[doc = "place 2 byte item on stack"] + PUSH2 = 0x61, + #[doc = "place 3 byte item on stack"] + PUSH3 = 0x62, + #[doc = "place 4 byte item on stack"] + PUSH4 = 0x63, + #[doc = "place 5 byte item on stack"] + PUSH5 = 0x64, + #[doc = "place 6 byte item on stack"] + PUSH6 = 0x65, + #[doc = "place 7 byte item on stack"] + PUSH7 = 0x66, + #[doc = "place 8 byte item on stack"] + PUSH8 = 0x67, + #[doc = "place 9 byte item on stack"] + PUSH9 = 0x68, + #[doc = "place 10 byte item on stack"] + PUSH10 = 0x69, + #[doc = "place 11 byte item on stack"] + PUSH11 = 0x6a, + #[doc = "place 12 byte item on stack"] + PUSH12 = 0x6b, + #[doc = "place 13 byte item on stack"] + PUSH13 = 0x6c, + #[doc = "place 14 byte item on stack"] + PUSH14 = 0x6d, + #[doc = "place 15 byte item on stack"] + PUSH15 = 0x6e, + #[doc = "place 16 byte item on stack"] + PUSH16 = 0x6f, + #[doc = "place 17 byte item on stack"] + PUSH17 = 0x70, + #[doc = "place 18 byte item on stack"] + PUSH18 = 0x71, + #[doc = "place 19 byte item on stack"] + PUSH19 = 0x72, + #[doc = "place 20 byte item on stack"] + PUSH20 = 0x73, + #[doc = "place 21 byte item on stack"] + PUSH21 = 0x74, + #[doc = "place 22 byte item on stack"] + PUSH22 = 0x75, + #[doc = "place 23 byte item on stack"] + PUSH23 = 0x76, + #[doc = "place 24 byte item on stack"] + PUSH24 = 0x77, + #[doc = "place 25 byte item on stack"] + PUSH25 = 0x78, + #[doc = "place 26 byte item on stack"] + PUSH26 = 0x79, + #[doc = "place 27 byte item on stack"] + PUSH27 = 0x7a, + #[doc = "place 28 byte item on stack"] + PUSH28 = 0x7b, + #[doc = "place 29 byte item on stack"] + PUSH29 = 0x7c, + #[doc = "place 30 byte item on stack"] + PUSH30 = 0x7d, + #[doc = "place 31 byte item on stack"] + PUSH31 = 0x7e, + #[doc = "place 32 byte item on stack"] + PUSH32 = 0x7f, + + #[doc = "copies the highest item in the stack to the top of the stack"] + DUP1 = 0x80, + #[doc = "copies the second highest item in the stack to the top of the stack"] + DUP2 = 0x81, + #[doc = "copies the third highest item in the stack to the top of the stack"] + DUP3 = 0x82, + #[doc = "copies the 4th highest item in the stack to the top of the stack"] + DUP4 = 0x83, + #[doc = "copies the 5th highest item in the stack to the top of the stack"] + DUP5 = 0x84, + #[doc = "copies the 6th highest item in the stack to the top of the stack"] + DUP6 = 0x85, + #[doc = "copies the 7th highest item in the stack to the top of the stack"] + DUP7 = 0x86, + #[doc = "copies the 8th highest item in the stack to the top of the stack"] + DUP8 = 0x87, + #[doc = "copies the 9th highest item in the stack to the top of the stack"] + DUP9 = 0x88, + #[doc = "copies the 10th highest item in the stack to the top of the stack"] + DUP10 = 0x89, + #[doc = "copies the 11th highest item in the stack to the top of the stack"] + DUP11 = 0x8a, + #[doc = "copies the 12th highest item in the stack to the top of the stack"] + DUP12 = 0x8b, + #[doc = "copies the 13th highest item in the stack to the top of the stack"] + DUP13 = 0x8c, + #[doc = "copies the 14th highest item in the stack to the top of the stack"] + DUP14 = 0x8d, + #[doc = "copies the 15th highest item in the stack to the top of the stack"] + DUP15 = 0x8e, + #[doc = "copies the 16th highest item in the stack to the top of the stack"] + DUP16 = 0x8f, + + #[doc = "swaps the highest and second highest value on the stack"] + SWAP1 = 0x90, + #[doc = "swaps the highest and third highest value on the stack"] + SWAP2 = 0x91, + #[doc = "swaps the highest and 4th highest value on the stack"] + SWAP3 = 0x92, + #[doc = "swaps the highest and 5th highest value on the stack"] + SWAP4 = 0x93, + #[doc = "swaps the highest and 6th highest value on the stack"] + SWAP5 = 0x94, + #[doc = "swaps the highest and 7th highest value on the stack"] + SWAP6 = 0x95, + #[doc = "swaps the highest and 8th highest value on the stack"] + SWAP7 = 0x96, + #[doc = "swaps the highest and 9th highest value on the stack"] + SWAP8 = 0x97, + #[doc = "swaps the highest and 10th highest value on the stack"] + SWAP9 = 0x98, + #[doc = "swaps the highest and 11th highest value on the stack"] + SWAP10 = 0x99, + #[doc = "swaps the highest and 12th highest value on the stack"] + SWAP11 = 0x9a, + #[doc = "swaps the highest and 13th highest value on the stack"] + SWAP12 = 0x9b, + #[doc = "swaps the highest and 14th highest value on the stack"] + SWAP13 = 0x9c, + #[doc = "swaps the highest and 15th highest value on the stack"] + SWAP14 = 0x9d, + #[doc = "swaps the highest and 16th highest value on the stack"] + SWAP15 = 0x9e, + #[doc = "swaps the highest and 17th highest value on the stack"] + SWAP16 = 0x9f, + + #[doc = "Makes a log entry, no topics."] + LOG0 = 0xa0, + #[doc = "Makes a log entry, 1 topic."] + LOG1 = 0xa1, + #[doc = "Makes a log entry, 2 topics."] + LOG2 = 0xa2, + #[doc = "Makes a log entry, 3 topics."] + LOG3 = 0xa3, + #[doc = "Makes a log entry, 4 topics."] + LOG4 = 0xa4, + + #[doc = "create a new account with associated code"] + CREATE = 0xf0, + #[doc = "message-call into an account"] + CALL = 0xf1, + #[doc = "message-call with another account's code only"] + CALLCODE = 0xf2, + #[doc = "halt execution returning output data"] + RETURN = 0xf3, + #[doc = "like CALLCODE but keeps caller's value and sender"] + DELEGATECALL = 0xf4, + #[doc = "create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160"] + CREATE2 = 0xfb, + #[doc = "stop execution and revert state changes. Return output data."] + REVERT = 0xfd, + #[doc = "like CALL but it does not take value, nor modify the state"] + STATICCALL = 0xfa, + #[doc = "halt execution and register account for later deletion"] + SUICIDE = 0xff, } } -#[test] -fn test_get_push_bytes() { - assert_eq!(get_push_bytes(PUSH1), 1); - assert_eq!(get_push_bytes(PUSH3), 3); - assert_eq!(get_push_bytes(PUSH32), 32); -} +impl Instruction { + /// Returns true if given instruction is `PUSHN` instruction. + pub fn is_push(&self) -> bool { + *self >= PUSH1 && *self <= PUSH32 + } -/// Returns stack position of item to duplicate -/// DUP1 -> 0 -pub fn get_dup_position(i: Instruction) -> usize { - assert!(i >= DUP1 && i <= DUP16); - (i - DUP1) as usize -} + /// Returns number of bytes to read for `PUSHN` instruction + /// PUSH1 -> 1 + pub fn push_bytes(&self) -> Option { + if self.is_push() { + Some(((*self as u8) - (PUSH1 as u8) + 1) as usize) + } else { + None + } + } -#[test] -fn test_get_dup_position() { - assert_eq!(get_dup_position(DUP1), 0); - assert_eq!(get_dup_position(DUP5), 4); - assert_eq!(get_dup_position(DUP10), 9); -} -/// Returns stack position of item to SWAP top with -/// SWAP1 -> 1 -pub fn get_swap_position(i: Instruction) -> usize { - assert!(i >= SWAP1 && i <= SWAP16); - (i - SWAP1 + 1) as usize -} + /// Returns stack position of item to duplicate + /// DUP1 -> 0 + pub fn dup_position(&self) -> Option { + if *self >= DUP1 && *self <= DUP16 { + Some(((*self as u8) - (DUP1 as u8)) as usize) + } else { + None + } + } -#[test] -fn test_get_swap_position() { - assert_eq!(get_swap_position(SWAP1), 1); - assert_eq!(get_swap_position(SWAP5), 5); - assert_eq!(get_swap_position(SWAP10), 10); -} -/// Returns number of topics to take from stack -/// LOG0 -> 0 -pub fn get_log_topics (i: Instruction) -> usize { - assert!(i >= LOG0 && i <= LOG4); - (i - LOG0) as usize -} + /// Returns stack position of item to SWAP top with + /// SWAP1 -> 1 + pub fn swap_position(&self) -> Option { + if *self >= SWAP1 && *self <= SWAP16 { + Some(((*self as u8) - (SWAP1 as u8) + 1) as usize) + } else { + None + } + } -#[test] -fn test_get_log_topics() { - assert_eq!(get_log_topics(LOG0), 0); - assert_eq!(get_log_topics(LOG2), 2); - assert_eq!(get_log_topics(LOG4), 4); + /// Returns number of topics to take from stack + /// LOG0 -> 0 + pub fn log_topics(&self) -> Option { + if *self >= LOG0 && *self <= LOG4 { + Some(((*self as u8) - (LOG0 as u8)) as usize) + } else { + None + } + } + + /// Returns the instruction info. + pub fn info(&self) -> &'static InstructionInfo { + INSTRUCTIONS[*self as usize].as_ref().expect("A instruction is defined in Instruction enum, but it is not found in InstructionInfo struct; this indicates a logic failure in the code.") + } } #[derive(PartialEq, Clone, Copy)] @@ -113,33 +408,26 @@ pub enum GasPriceTier { Ext, /// Multiparam or otherwise special Special, - /// Invalid - Invalid } -impl Default for GasPriceTier { - fn default() -> Self { - GasPriceTier::Invalid - } -} - -/// Returns the index in schedule for specific `GasPriceTier` -pub fn get_tier_idx (tier: GasPriceTier) -> usize { - match tier { - GasPriceTier::Zero => 0, - GasPriceTier::Base => 1, - GasPriceTier::VeryLow => 2, - GasPriceTier::Low => 3, - GasPriceTier::Mid => 4, - GasPriceTier::High => 5, - GasPriceTier::Ext => 6, - GasPriceTier::Special => 7, - GasPriceTier::Invalid => 8 +impl GasPriceTier { + /// Returns the index in schedule for specific `GasPriceTier` + pub fn idx(&self) -> usize { + match self { + &GasPriceTier::Zero => 0, + &GasPriceTier::Base => 1, + &GasPriceTier::VeryLow => 2, + &GasPriceTier::Low => 3, + &GasPriceTier::Mid => 4, + &GasPriceTier::High => 5, + &GasPriceTier::Ext => 6, + &GasPriceTier::Special => 7, + } } } /// EVM instruction information. -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone)] pub struct InstructionInfo { /// Mnemonic name. pub name: &'static str, @@ -165,436 +453,189 @@ impl InstructionInfo { lazy_static! { /// Static instruction table. - pub static ref INSTRUCTIONS: [InstructionInfo; 0x100] = { - let mut arr = [InstructionInfo::default(); 0x100]; - arr[STOP as usize] = InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero); - arr[ADD as usize] = InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow); - arr[SUB as usize] = InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow); - arr[MUL as usize] = InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low); - arr[DIV as usize] = InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low); - arr[SDIV as usize] = InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low); - arr[MOD as usize] = InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low); - arr[SMOD as usize] = InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low); - arr[EXP as usize] = InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special); - arr[NOT as usize] = InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow); - arr[LT as usize] = InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow); - arr[GT as usize] = InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow); - arr[SLT as usize] = InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow); - arr[SGT as usize] = InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow); - arr[EQ as usize] = InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow); - arr[ISZERO as usize] = InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow); - arr[AND as usize] = InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow); - arr[OR as usize] = InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow); - arr[XOR as usize] = InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow); - arr[BYTE as usize] = InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow); - arr[SHL as usize] = InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow); - arr[SHR as usize] = InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow); - arr[SAR as usize] = InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow); - arr[ADDMOD as usize] = InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid); - arr[MULMOD as usize] = InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid); - arr[SIGNEXTEND as usize] = InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low); - arr[RETURNDATASIZE as usize] = InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base); - arr[RETURNDATACOPY as usize] = InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow); - arr[SHA3 as usize] = InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special); - arr[ADDRESS as usize] = InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base); - arr[BALANCE as usize] = InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special); - arr[ORIGIN as usize] = InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base); - arr[CALLER as usize] = InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base); - arr[CALLVALUE as usize] = InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base); - arr[CALLDATALOAD as usize] = InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow); - arr[CALLDATASIZE as usize] = InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base); - arr[CALLDATACOPY as usize] = InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow); - arr[CODESIZE as usize] = InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base); - arr[CODECOPY as usize] = InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow); - arr[GASPRICE as usize] = InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base); - arr[EXTCODESIZE as usize] = InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special); - arr[EXTCODECOPY as usize] = InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special); - arr[BLOCKHASH as usize] = InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext); - arr[COINBASE as usize] = InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base); - arr[TIMESTAMP as usize] = InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base); - arr[NUMBER as usize] = InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base); - arr[DIFFICULTY as usize] = InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base); - arr[GASLIMIT as usize] = InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base); - arr[POP as usize] = InstructionInfo::new("POP", 1, 0, GasPriceTier::Base); - arr[MLOAD as usize] = InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow); - arr[MSTORE as usize] = InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow); - arr[MSTORE8 as usize] = InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow); - arr[SLOAD as usize] = InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special); - arr[SSTORE as usize] = InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special); - arr[JUMP as usize] = InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid); - arr[JUMPI as usize] = InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High); - arr[PC as usize] = InstructionInfo::new("PC", 0, 1, GasPriceTier::Base); - arr[MSIZE as usize] = InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base); - arr[GAS as usize] = InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base); - arr[JUMPDEST as usize] = InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special); - arr[PUSH1 as usize] = InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow); - arr[PUSH2 as usize] = InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow); - arr[PUSH3 as usize] = InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow); - arr[PUSH4 as usize] = InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow); - arr[PUSH5 as usize] = InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow); - arr[PUSH6 as usize] = InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow); - arr[PUSH7 as usize] = InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow); - arr[PUSH8 as usize] = InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow); - arr[PUSH9 as usize] = InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow); - arr[PUSH10 as usize] = InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow); - arr[PUSH11 as usize] = InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow); - arr[PUSH12 as usize] = InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow); - arr[PUSH13 as usize] = InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow); - arr[PUSH14 as usize] = InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow); - arr[PUSH15 as usize] = InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow); - arr[PUSH16 as usize] = InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow); - arr[PUSH17 as usize] = InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow); - arr[PUSH18 as usize] = InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow); - arr[PUSH19 as usize] = InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow); - arr[PUSH20 as usize] = InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow); - arr[PUSH21 as usize] = InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow); - arr[PUSH22 as usize] = InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow); - arr[PUSH23 as usize] = InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow); - arr[PUSH24 as usize] = InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow); - arr[PUSH25 as usize] = InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow); - arr[PUSH26 as usize] = InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow); - arr[PUSH27 as usize] = InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow); - arr[PUSH28 as usize] = InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow); - arr[PUSH29 as usize] = InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow); - arr[PUSH30 as usize] = InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow); - arr[PUSH31 as usize] = InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow); - arr[PUSH32 as usize] = InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow); - arr[DUP1 as usize] = InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow); - arr[DUP2 as usize] = InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow); - arr[DUP3 as usize] = InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow); - arr[DUP4 as usize] = InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow); - arr[DUP5 as usize] = InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow); - arr[DUP6 as usize] = InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow); - arr[DUP7 as usize] = InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow); - arr[DUP8 as usize] = InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow); - arr[DUP9 as usize] = InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow); - arr[DUP10 as usize] = InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow); - arr[DUP11 as usize] = InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow); - arr[DUP12 as usize] = InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow); - arr[DUP13 as usize] = InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow); - arr[DUP14 as usize] = InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow); - arr[DUP15 as usize] = InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow); - arr[DUP16 as usize] = InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow); - arr[SWAP1 as usize] = InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow); - arr[SWAP2 as usize] = InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow); - arr[SWAP3 as usize] = InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow); - arr[SWAP4 as usize] = InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow); - arr[SWAP5 as usize] = InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow); - arr[SWAP6 as usize] = InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow); - arr[SWAP7 as usize] = InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow); - arr[SWAP8 as usize] = InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow); - arr[SWAP9 as usize] = InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow); - arr[SWAP10 as usize] = InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow); - arr[SWAP11 as usize] = InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow); - arr[SWAP12 as usize] = InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow); - arr[SWAP13 as usize] = InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow); - arr[SWAP14 as usize] = InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow); - arr[SWAP15 as usize] = InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow); - arr[SWAP16 as usize] = InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow); - arr[LOG0 as usize] = InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special); - arr[LOG1 as usize] = InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special); - arr[LOG2 as usize] = InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special); - arr[LOG3 as usize] = InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special); - arr[LOG4 as usize] = InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special); - arr[CREATE as usize] = InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special); - arr[CALL as usize] = InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special); - arr[CALLCODE as usize] = InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special); - arr[RETURN as usize] = InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero); - arr[DELEGATECALL as usize] = InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special); - arr[STATICCALL as usize] = InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special); - arr[SUICIDE as usize] = InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special); - arr[CREATE2 as usize] = InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special); - arr[REVERT as usize] = InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero); + static ref INSTRUCTIONS: [Option; 0x100] = { + let mut arr = [None; 0x100]; + arr[STOP as usize] = Some(InstructionInfo::new("STOP", 0, 0, GasPriceTier::Zero)); + arr[ADD as usize] = Some(InstructionInfo::new("ADD", 2, 1, GasPriceTier::VeryLow)); + arr[SUB as usize] = Some(InstructionInfo::new("SUB", 2, 1, GasPriceTier::VeryLow)); + arr[MUL as usize] = Some(InstructionInfo::new("MUL", 2, 1, GasPriceTier::Low)); + arr[DIV as usize] = Some(InstructionInfo::new("DIV", 2, 1, GasPriceTier::Low)); + arr[SDIV as usize] = Some(InstructionInfo::new("SDIV", 2, 1, GasPriceTier::Low)); + arr[MOD as usize] = Some(InstructionInfo::new("MOD", 2, 1, GasPriceTier::Low)); + arr[SMOD as usize] = Some(InstructionInfo::new("SMOD", 2, 1, GasPriceTier::Low)); + arr[EXP as usize] = Some(InstructionInfo::new("EXP", 2, 1, GasPriceTier::Special)); + arr[NOT as usize] = Some(InstructionInfo::new("NOT", 1, 1, GasPriceTier::VeryLow)); + arr[LT as usize] = Some(InstructionInfo::new("LT", 2, 1, GasPriceTier::VeryLow)); + arr[GT as usize] = Some(InstructionInfo::new("GT", 2, 1, GasPriceTier::VeryLow)); + arr[SLT as usize] = Some(InstructionInfo::new("SLT", 2, 1, GasPriceTier::VeryLow)); + arr[SGT as usize] = Some(InstructionInfo::new("SGT", 2, 1, GasPriceTier::VeryLow)); + arr[EQ as usize] = Some(InstructionInfo::new("EQ", 2, 1, GasPriceTier::VeryLow)); + arr[ISZERO as usize] = Some(InstructionInfo::new("ISZERO", 1, 1, GasPriceTier::VeryLow)); + arr[AND as usize] = Some(InstructionInfo::new("AND", 2, 1, GasPriceTier::VeryLow)); + arr[OR as usize] = Some(InstructionInfo::new("OR", 2, 1, GasPriceTier::VeryLow)); + arr[XOR as usize] = Some(InstructionInfo::new("XOR", 2, 1, GasPriceTier::VeryLow)); + arr[BYTE as usize] = Some(InstructionInfo::new("BYTE", 2, 1, GasPriceTier::VeryLow)); + arr[SHL as usize] = Some(InstructionInfo::new("SHL", 2, 1, GasPriceTier::VeryLow)); + arr[SHR as usize] = Some(InstructionInfo::new("SHR", 2, 1, GasPriceTier::VeryLow)); + arr[SAR as usize] = Some(InstructionInfo::new("SAR", 2, 1, GasPriceTier::VeryLow)); + arr[ADDMOD as usize] = Some(InstructionInfo::new("ADDMOD", 3, 1, GasPriceTier::Mid)); + arr[MULMOD as usize] = Some(InstructionInfo::new("MULMOD", 3, 1, GasPriceTier::Mid)); + arr[SIGNEXTEND as usize] = Some(InstructionInfo::new("SIGNEXTEND", 2, 1, GasPriceTier::Low)); + arr[RETURNDATASIZE as usize] = Some(InstructionInfo::new("RETURNDATASIZE", 0, 1, GasPriceTier::Base)); + arr[RETURNDATACOPY as usize] = Some(InstructionInfo::new("RETURNDATACOPY", 3, 0, GasPriceTier::VeryLow)); + arr[SHA3 as usize] = Some(InstructionInfo::new("SHA3", 2, 1, GasPriceTier::Special)); + arr[ADDRESS as usize] = Some(InstructionInfo::new("ADDRESS", 0, 1, GasPriceTier::Base)); + arr[BALANCE as usize] = Some(InstructionInfo::new("BALANCE", 1, 1, GasPriceTier::Special)); + arr[ORIGIN as usize] = Some(InstructionInfo::new("ORIGIN", 0, 1, GasPriceTier::Base)); + arr[CALLER as usize] = Some(InstructionInfo::new("CALLER", 0, 1, GasPriceTier::Base)); + arr[CALLVALUE as usize] = Some(InstructionInfo::new("CALLVALUE", 0, 1, GasPriceTier::Base)); + arr[CALLDATALOAD as usize] = Some(InstructionInfo::new("CALLDATALOAD", 1, 1, GasPriceTier::VeryLow)); + arr[CALLDATASIZE as usize] = Some(InstructionInfo::new("CALLDATASIZE", 0, 1, GasPriceTier::Base)); + arr[CALLDATACOPY as usize] = Some(InstructionInfo::new("CALLDATACOPY", 3, 0, GasPriceTier::VeryLow)); + arr[CODESIZE as usize] = Some(InstructionInfo::new("CODESIZE", 0, 1, GasPriceTier::Base)); + arr[CODECOPY as usize] = Some(InstructionInfo::new("CODECOPY", 3, 0, GasPriceTier::VeryLow)); + arr[GASPRICE as usize] = Some(InstructionInfo::new("GASPRICE", 0, 1, GasPriceTier::Base)); + arr[EXTCODESIZE as usize] = Some(InstructionInfo::new("EXTCODESIZE", 1, 1, GasPriceTier::Special)); + arr[EXTCODECOPY as usize] = Some(InstructionInfo::new("EXTCODECOPY", 4, 0, GasPriceTier::Special)); + arr[BLOCKHASH as usize] = Some(InstructionInfo::new("BLOCKHASH", 1, 1, GasPriceTier::Ext)); + arr[COINBASE as usize] = Some(InstructionInfo::new("COINBASE", 0, 1, GasPriceTier::Base)); + arr[TIMESTAMP as usize] = Some(InstructionInfo::new("TIMESTAMP", 0, 1, GasPriceTier::Base)); + arr[NUMBER as usize] = Some(InstructionInfo::new("NUMBER", 0, 1, GasPriceTier::Base)); + arr[DIFFICULTY as usize] = Some(InstructionInfo::new("DIFFICULTY", 0, 1, GasPriceTier::Base)); + arr[GASLIMIT as usize] = Some(InstructionInfo::new("GASLIMIT", 0, 1, GasPriceTier::Base)); + arr[POP as usize] = Some(InstructionInfo::new("POP", 1, 0, GasPriceTier::Base)); + arr[MLOAD as usize] = Some(InstructionInfo::new("MLOAD", 1, 1, GasPriceTier::VeryLow)); + arr[MSTORE as usize] = Some(InstructionInfo::new("MSTORE", 2, 0, GasPriceTier::VeryLow)); + arr[MSTORE8 as usize] = Some(InstructionInfo::new("MSTORE8", 2, 0, GasPriceTier::VeryLow)); + arr[SLOAD as usize] = Some(InstructionInfo::new("SLOAD", 1, 1, GasPriceTier::Special)); + arr[SSTORE as usize] = Some(InstructionInfo::new("SSTORE", 2, 0, GasPriceTier::Special)); + arr[JUMP as usize] = Some(InstructionInfo::new("JUMP", 1, 0, GasPriceTier::Mid)); + arr[JUMPI as usize] = Some(InstructionInfo::new("JUMPI", 2, 0, GasPriceTier::High)); + arr[PC as usize] = Some(InstructionInfo::new("PC", 0, 1, GasPriceTier::Base)); + arr[MSIZE as usize] = Some(InstructionInfo::new("MSIZE", 0, 1, GasPriceTier::Base)); + arr[GAS as usize] = Some(InstructionInfo::new("GAS", 0, 1, GasPriceTier::Base)); + arr[JUMPDEST as usize] = Some(InstructionInfo::new("JUMPDEST", 0, 0, GasPriceTier::Special)); + arr[PUSH1 as usize] = Some(InstructionInfo::new("PUSH1", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH2 as usize] = Some(InstructionInfo::new("PUSH2", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH3 as usize] = Some(InstructionInfo::new("PUSH3", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH4 as usize] = Some(InstructionInfo::new("PUSH4", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH5 as usize] = Some(InstructionInfo::new("PUSH5", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH6 as usize] = Some(InstructionInfo::new("PUSH6", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH7 as usize] = Some(InstructionInfo::new("PUSH7", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH8 as usize] = Some(InstructionInfo::new("PUSH8", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH9 as usize] = Some(InstructionInfo::new("PUSH9", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH10 as usize] = Some(InstructionInfo::new("PUSH10", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH11 as usize] = Some(InstructionInfo::new("PUSH11", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH12 as usize] = Some(InstructionInfo::new("PUSH12", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH13 as usize] = Some(InstructionInfo::new("PUSH13", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH14 as usize] = Some(InstructionInfo::new("PUSH14", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH15 as usize] = Some(InstructionInfo::new("PUSH15", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH16 as usize] = Some(InstructionInfo::new("PUSH16", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH17 as usize] = Some(InstructionInfo::new("PUSH17", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH18 as usize] = Some(InstructionInfo::new("PUSH18", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH19 as usize] = Some(InstructionInfo::new("PUSH19", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH20 as usize] = Some(InstructionInfo::new("PUSH20", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH21 as usize] = Some(InstructionInfo::new("PUSH21", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH22 as usize] = Some(InstructionInfo::new("PUSH22", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH23 as usize] = Some(InstructionInfo::new("PUSH23", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH24 as usize] = Some(InstructionInfo::new("PUSH24", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH25 as usize] = Some(InstructionInfo::new("PUSH25", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH26 as usize] = Some(InstructionInfo::new("PUSH26", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH27 as usize] = Some(InstructionInfo::new("PUSH27", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH28 as usize] = Some(InstructionInfo::new("PUSH28", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH29 as usize] = Some(InstructionInfo::new("PUSH29", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH30 as usize] = Some(InstructionInfo::new("PUSH30", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH31 as usize] = Some(InstructionInfo::new("PUSH31", 0, 1, GasPriceTier::VeryLow)); + arr[PUSH32 as usize] = Some(InstructionInfo::new("PUSH32", 0, 1, GasPriceTier::VeryLow)); + arr[DUP1 as usize] = Some(InstructionInfo::new("DUP1", 1, 2, GasPriceTier::VeryLow)); + arr[DUP2 as usize] = Some(InstructionInfo::new("DUP2", 2, 3, GasPriceTier::VeryLow)); + arr[DUP3 as usize] = Some(InstructionInfo::new("DUP3", 3, 4, GasPriceTier::VeryLow)); + arr[DUP4 as usize] = Some(InstructionInfo::new("DUP4", 4, 5, GasPriceTier::VeryLow)); + arr[DUP5 as usize] = Some(InstructionInfo::new("DUP5", 5, 6, GasPriceTier::VeryLow)); + arr[DUP6 as usize] = Some(InstructionInfo::new("DUP6", 6, 7, GasPriceTier::VeryLow)); + arr[DUP7 as usize] = Some(InstructionInfo::new("DUP7", 7, 8, GasPriceTier::VeryLow)); + arr[DUP8 as usize] = Some(InstructionInfo::new("DUP8", 8, 9, GasPriceTier::VeryLow)); + arr[DUP9 as usize] = Some(InstructionInfo::new("DUP9", 9, 10, GasPriceTier::VeryLow)); + arr[DUP10 as usize] = Some(InstructionInfo::new("DUP10", 10, 11, GasPriceTier::VeryLow)); + arr[DUP11 as usize] = Some(InstructionInfo::new("DUP11", 11, 12, GasPriceTier::VeryLow)); + arr[DUP12 as usize] = Some(InstructionInfo::new("DUP12", 12, 13, GasPriceTier::VeryLow)); + arr[DUP13 as usize] = Some(InstructionInfo::new("DUP13", 13, 14, GasPriceTier::VeryLow)); + arr[DUP14 as usize] = Some(InstructionInfo::new("DUP14", 14, 15, GasPriceTier::VeryLow)); + arr[DUP15 as usize] = Some(InstructionInfo::new("DUP15", 15, 16, GasPriceTier::VeryLow)); + arr[DUP16 as usize] = Some(InstructionInfo::new("DUP16", 16, 17, GasPriceTier::VeryLow)); + arr[SWAP1 as usize] = Some(InstructionInfo::new("SWAP1", 2, 2, GasPriceTier::VeryLow)); + arr[SWAP2 as usize] = Some(InstructionInfo::new("SWAP2", 3, 3, GasPriceTier::VeryLow)); + arr[SWAP3 as usize] = Some(InstructionInfo::new("SWAP3", 4, 4, GasPriceTier::VeryLow)); + arr[SWAP4 as usize] = Some(InstructionInfo::new("SWAP4", 5, 5, GasPriceTier::VeryLow)); + arr[SWAP5 as usize] = Some(InstructionInfo::new("SWAP5", 6, 6, GasPriceTier::VeryLow)); + arr[SWAP6 as usize] = Some(InstructionInfo::new("SWAP6", 7, 7, GasPriceTier::VeryLow)); + arr[SWAP7 as usize] = Some(InstructionInfo::new("SWAP7", 8, 8, GasPriceTier::VeryLow)); + arr[SWAP8 as usize] = Some(InstructionInfo::new("SWAP8", 9, 9, GasPriceTier::VeryLow)); + arr[SWAP9 as usize] = Some(InstructionInfo::new("SWAP9", 10, 10, GasPriceTier::VeryLow)); + arr[SWAP10 as usize] = Some(InstructionInfo::new("SWAP10", 11, 11, GasPriceTier::VeryLow)); + arr[SWAP11 as usize] = Some(InstructionInfo::new("SWAP11", 12, 12, GasPriceTier::VeryLow)); + arr[SWAP12 as usize] = Some(InstructionInfo::new("SWAP12", 13, 13, GasPriceTier::VeryLow)); + arr[SWAP13 as usize] = Some(InstructionInfo::new("SWAP13", 14, 14, GasPriceTier::VeryLow)); + arr[SWAP14 as usize] = Some(InstructionInfo::new("SWAP14", 15, 15, GasPriceTier::VeryLow)); + arr[SWAP15 as usize] = Some(InstructionInfo::new("SWAP15", 16, 16, GasPriceTier::VeryLow)); + arr[SWAP16 as usize] = Some(InstructionInfo::new("SWAP16", 17, 17, GasPriceTier::VeryLow)); + arr[LOG0 as usize] = Some(InstructionInfo::new("LOG0", 2, 0, GasPriceTier::Special)); + arr[LOG1 as usize] = Some(InstructionInfo::new("LOG1", 3, 0, GasPriceTier::Special)); + arr[LOG2 as usize] = Some(InstructionInfo::new("LOG2", 4, 0, GasPriceTier::Special)); + arr[LOG3 as usize] = Some(InstructionInfo::new("LOG3", 5, 0, GasPriceTier::Special)); + arr[LOG4 as usize] = Some(InstructionInfo::new("LOG4", 6, 0, GasPriceTier::Special)); + arr[CREATE as usize] = Some(InstructionInfo::new("CREATE", 3, 1, GasPriceTier::Special)); + arr[CALL as usize] = Some(InstructionInfo::new("CALL", 7, 1, GasPriceTier::Special)); + arr[CALLCODE as usize] = Some(InstructionInfo::new("CALLCODE", 7, 1, GasPriceTier::Special)); + arr[RETURN as usize] = Some(InstructionInfo::new("RETURN", 2, 0, GasPriceTier::Zero)); + arr[DELEGATECALL as usize] = Some(InstructionInfo::new("DELEGATECALL", 6, 1, GasPriceTier::Special)); + arr[STATICCALL as usize] = Some(InstructionInfo::new("STATICCALL", 6, 1, GasPriceTier::Special)); + arr[SUICIDE as usize] = Some(InstructionInfo::new("SUICIDE", 1, 0, GasPriceTier::Special)); + arr[CREATE2 as usize] = Some(InstructionInfo::new("CREATE2", 3, 1, GasPriceTier::Special)); + arr[REVERT as usize] = Some(InstructionInfo::new("REVERT", 2, 0, GasPriceTier::Zero)); arr }; } -/// Virtual machine bytecode instruction. -/// halts execution -pub const STOP: Instruction = 0x00; -/// addition operation -pub const ADD: Instruction = 0x01; -/// mulitplication operation -pub const MUL: Instruction = 0x02; -/// subtraction operation -pub const SUB: Instruction = 0x03; -/// integer division operation -pub const DIV: Instruction = 0x04; -/// signed integer division operation -pub const SDIV: Instruction = 0x05; -/// modulo remainder operation -pub const MOD: Instruction = 0x06; -/// signed modulo remainder operation -pub const SMOD: Instruction = 0x07; -/// unsigned modular addition -pub const ADDMOD: Instruction = 0x08; -/// unsigned modular multiplication -pub const MULMOD: Instruction = 0x09; -/// exponential operation -pub const EXP: Instruction = 0x0a; -/// extend length of signed integer -pub const SIGNEXTEND: Instruction = 0x0b; - -/// less-than comparision -pub const LT: Instruction = 0x10; -/// greater-than comparision -pub const GT: Instruction = 0x11; -/// signed less-than comparision -pub const SLT: Instruction = 0x12; -/// signed greater-than comparision -pub const SGT: Instruction = 0x13; -/// equality comparision -pub const EQ: Instruction = 0x14; -/// simple not operator -pub const ISZERO: Instruction = 0x15; -/// bitwise AND operation -pub const AND: Instruction = 0x16; -/// bitwise OR operation -pub const OR: Instruction = 0x17; -/// bitwise XOR operation -pub const XOR: Instruction = 0x18; -/// bitwise NOT opertation -pub const NOT: Instruction = 0x19; -/// retrieve single byte from word -pub const BYTE: Instruction = 0x1a; -/// shift left operation -pub const SHL: Instruction = 0x1b; -/// logical shift right operation -pub const SHR: Instruction = 0x1c; -/// arithmetic shift right operation -pub const SAR: Instruction = 0x1d; - -/// compute SHA3-256 hash -pub const SHA3: Instruction = 0x20; - -/// get address of currently executing account -pub const ADDRESS: Instruction = 0x30; -/// get balance of the given account -pub const BALANCE: Instruction = 0x31; -/// get execution origination address -pub const ORIGIN: Instruction = 0x32; -/// get caller address -pub const CALLER: Instruction = 0x33; -/// get deposited value by the instruction/transaction responsible for this execution -pub const CALLVALUE: Instruction = 0x34; -/// get input data of current environment -pub const CALLDATALOAD: Instruction = 0x35; -/// get size of input data in current environment -pub const CALLDATASIZE: Instruction = 0x36; -/// copy input data in current environment to memory -pub const CALLDATACOPY: Instruction = 0x37; -/// get size of code running in current environment -pub const CODESIZE: Instruction = 0x38; -/// copy code running in current environment to memory -pub const CODECOPY: Instruction = 0x39; -/// get price of gas in current environment -pub const GASPRICE: Instruction = 0x3a; -/// get external code size (from another contract) -pub const EXTCODESIZE: Instruction = 0x3b; -/// copy external code (from another contract) -pub const EXTCODECOPY: Instruction = 0x3c; -/// get the size of the return data buffer for the last call -pub const RETURNDATASIZE: Instruction = 0x3d; -/// copy return data buffer to memory -pub const RETURNDATACOPY: Instruction = 0x3e; - -/// get hash of most recent complete block -pub const BLOCKHASH: Instruction = 0x40; -/// get the block's coinbase address -pub const COINBASE: Instruction = 0x41; -/// get the block's timestamp -pub const TIMESTAMP: Instruction = 0x42; -/// get the block's number -pub const NUMBER: Instruction = 0x43; -/// get the block's difficulty -pub const DIFFICULTY: Instruction = 0x44; -/// get the block's gas limit -pub const GASLIMIT: Instruction = 0x45; - -/// remove item from stack -pub const POP: Instruction = 0x50; -/// load word from memory -pub const MLOAD: Instruction = 0x51; -/// save word to memory -pub const MSTORE: Instruction = 0x52; -/// save byte to memory -pub const MSTORE8: Instruction = 0x53; -/// load word from storage -pub const SLOAD: Instruction = 0x54; -/// save word to storage -pub const SSTORE: Instruction = 0x55; -/// alter the program counter -pub const JUMP: Instruction = 0x56; -/// conditionally alter the program counter -pub const JUMPI: Instruction = 0x57; -/// get the program counter -pub const PC: Instruction = 0x58; -/// get the size of active memory -pub const MSIZE: Instruction = 0x59; -/// get the amount of available gas -pub const GAS: Instruction = 0x5a; -/// set a potential jump destination -pub const JUMPDEST: Instruction = 0x5b; - -/// place 1 byte item on stack -pub const PUSH1: Instruction = 0x60; -/// place 2 byte item on stack -pub const PUSH2: Instruction = 0x61; -/// place 3 byte item on stack -pub const PUSH3: Instruction = 0x62; -/// place 4 byte item on stack -pub const PUSH4: Instruction = 0x63; -/// place 5 byte item on stack -pub const PUSH5: Instruction = 0x64; -/// place 6 byte item on stack -pub const PUSH6: Instruction = 0x65; -/// place 7 byte item on stack -pub const PUSH7: Instruction = 0x66; -/// place 8 byte item on stack -pub const PUSH8: Instruction = 0x67; -/// place 9 byte item on stack -pub const PUSH9: Instruction = 0x68; -/// place 10 byte item on stack -pub const PUSH10: Instruction = 0x69; -/// place 11 byte item on stack -pub const PUSH11: Instruction = 0x6a; -/// place 12 byte item on stack -pub const PUSH12: Instruction = 0x6b; -/// place 13 byte item on stack -pub const PUSH13: Instruction = 0x6c; -/// place 14 byte item on stack -pub const PUSH14: Instruction = 0x6d; -/// place 15 byte item on stack -pub const PUSH15: Instruction = 0x6e; -/// place 16 byte item on stack -pub const PUSH16: Instruction = 0x6f; -/// place 17 byte item on stack -pub const PUSH17: Instruction = 0x70; -/// place 18 byte item on stack -pub const PUSH18: Instruction = 0x71; -/// place 19 byte item on stack -pub const PUSH19: Instruction = 0x72; -/// place 20 byte item on stack -pub const PUSH20: Instruction = 0x73; -/// place 21 byte item on stack -pub const PUSH21: Instruction = 0x74; -/// place 22 byte item on stack -pub const PUSH22: Instruction = 0x75; -/// place 23 byte item on stack -pub const PUSH23: Instruction = 0x76; -/// place 24 byte item on stack -pub const PUSH24: Instruction = 0x77; -/// place 25 byte item on stack -pub const PUSH25: Instruction = 0x78; -/// place 26 byte item on stack -pub const PUSH26: Instruction = 0x79; -/// place 27 byte item on stack -pub const PUSH27: Instruction = 0x7a; -/// place 28 byte item on stack -pub const PUSH28: Instruction = 0x7b; -/// place 29 byte item on stack -pub const PUSH29: Instruction = 0x7c; -/// place 30 byte item on stack -pub const PUSH30: Instruction = 0x7d; -/// place 31 byte item on stack -pub const PUSH31: Instruction = 0x7e; -/// place 32 byte item on stack -pub const PUSH32: Instruction = 0x7f; - -/// copies the highest item in the stack to the top of the stack -pub const DUP1: Instruction = 0x80; -/// copies the second highest item in the stack to the top of the stack -pub const DUP2: Instruction = 0x81; -/// copies the third highest item in the stack to the top of the stack -pub const DUP3: Instruction = 0x82; -/// copies the 4th highest item in the stack to the top of the stack -pub const DUP4: Instruction = 0x83; -/// copies the 5th highest item in the stack to the top of the stack -pub const DUP5: Instruction = 0x84; -/// copies the 6th highest item in the stack to the top of the stack -pub const DUP6: Instruction = 0x85; -/// copies the 7th highest item in the stack to the top of the stack -pub const DUP7: Instruction = 0x86; -/// copies the 8th highest item in the stack to the top of the stack -pub const DUP8: Instruction = 0x87; -/// copies the 9th highest item in the stack to the top of the stack -pub const DUP9: Instruction = 0x88; -/// copies the 10th highest item in the stack to the top of the stack -pub const DUP10: Instruction = 0x89; -/// copies the 11th highest item in the stack to the top of the stack -pub const DUP11: Instruction = 0x8a; -/// copies the 12th highest item in the stack to the top of the stack -pub const DUP12: Instruction = 0x8b; -/// copies the 13th highest item in the stack to the top of the stack -pub const DUP13: Instruction = 0x8c; -/// copies the 14th highest item in the stack to the top of the stack -pub const DUP14: Instruction = 0x8d; -/// copies the 15th highest item in the stack to the top of the stack -pub const DUP15: Instruction = 0x8e; -/// copies the 16th highest item in the stack to the top of the stack -pub const DUP16: Instruction = 0x8f; - -/// swaps the highest and second highest value on the stack -pub const SWAP1: Instruction = 0x90; -/// swaps the highest and third highest value on the stack -pub const SWAP2: Instruction = 0x91; -/// swaps the highest and 4th highest value on the stack -pub const SWAP3: Instruction = 0x92; -/// swaps the highest and 5th highest value on the stack -pub const SWAP4: Instruction = 0x93; -/// swaps the highest and 6th highest value on the stack -pub const SWAP5: Instruction = 0x94; -/// swaps the highest and 7th highest value on the stack -pub const SWAP6: Instruction = 0x95; -/// swaps the highest and 8th highest value on the stack -pub const SWAP7: Instruction = 0x96; -/// swaps the highest and 9th highest value on the stack -pub const SWAP8: Instruction = 0x97; -/// swaps the highest and 10th highest value on the stack -pub const SWAP9: Instruction = 0x98; -/// swaps the highest and 11th highest value on the stack -pub const SWAP10: Instruction = 0x99; -/// swaps the highest and 12th highest value on the stack -pub const SWAP11: Instruction = 0x9a; -/// swaps the highest and 13th highest value on the stack -pub const SWAP12: Instruction = 0x9b; -/// swaps the highest and 14th highest value on the stack -pub const SWAP13: Instruction = 0x9c; -/// swaps the highest and 15th highest value on the stack -pub const SWAP14: Instruction = 0x9d; -/// swaps the highest and 16th highest value on the stack -pub const SWAP15: Instruction = 0x9e; -/// swaps the highest and 17th highest value on the stack -pub const SWAP16: Instruction = 0x9f; - -/// Makes a log entry; no topics. -pub const LOG0: Instruction = 0xa0; -/// Makes a log entry; 1 topic. -pub const LOG1: Instruction = 0xa1; -/// Makes a log entry; 2 topics. -pub const LOG2: Instruction = 0xa2; -/// Makes a log entry; 3 topics. -pub const LOG3: Instruction = 0xa3; -/// Makes a log entry; 4 topics. -pub const LOG4: Instruction = 0xa4; /// Maximal number of topics for log instructions -pub const MAX_NO_OF_TOPICS : usize = 4; +pub const MAX_NO_OF_TOPICS: usize = 4; -/// create a new account with associated code -pub const CREATE: Instruction = 0xf0; -/// message-call into an account -pub const CALL: Instruction = 0xf1; -/// message-call with another account's code only -pub const CALLCODE: Instruction = 0xf2; -/// halt execution returning output data -pub const RETURN: Instruction = 0xf3; -/// like CALLCODE but keeps caller's value and sender -pub const DELEGATECALL: Instruction = 0xf4; -/// create a new account and set creation address to sha3(sender + sha3(init code)) % 2**160 -pub const CREATE2: Instruction = 0xfb; -/// stop execution and revert state changes. Return output data. -pub const REVERT: Instruction = 0xfd; -/// like CALL but it does not take value, nor modify the state -pub const STATICCALL: Instruction = 0xfa; -/// halt execution and register account for later deletion -pub const SUICIDE: Instruction = 0xff; +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_push() { + assert!(PUSH1.is_push()); + assert!(PUSH32.is_push()); + assert!(!DUP1.is_push()); + } + + #[test] + fn test_get_push_bytes() { + assert_eq!(PUSH1.push_bytes(), Some(1)); + assert_eq!(PUSH3.push_bytes(), Some(3)); + assert_eq!(PUSH32.push_bytes(), Some(32)); + } + + #[test] + fn test_get_dup_position() { + assert_eq!(DUP1.dup_position(), Some(0)); + assert_eq!(DUP5.dup_position(), Some(4)); + assert_eq!(DUP10.dup_position(), Some(9)); + } + + #[test] + fn test_get_swap_position() { + assert_eq!(SWAP1.swap_position(), Some(1)); + assert_eq!(SWAP5.swap_position(), Some(5)); + assert_eq!(SWAP10.swap_position(), Some(10)); + } + + #[test] + fn test_get_log_topics() { + assert_eq!(LOG0.log_topics(), Some(0)); + assert_eq!(LOG2.log_topics(), Some(2)); + assert_eq!(LOG4.log_topics(), Some(4)); + } +} diff --git a/ethcore/evm/src/interpreter/gasometer.rs b/ethcore/evm/src/interpreter/gasometer.rs index 85ea8ee48..dbb333837 100644 --- a/ethcore/evm/src/interpreter/gasometer.rs +++ b/ethcore/evm/src/interpreter/gasometer.rs @@ -113,7 +113,7 @@ impl Gasometer { current_mem_size: usize, ) -> vm::Result> { let schedule = ext.schedule(); - let tier = instructions::get_tier_idx(info.tier); + let tier = info.tier.idx(); let default_gas = Gas::from(schedule.tier_step_gas[tier]); let cost = match instruction { @@ -179,8 +179,8 @@ impl Gasometer { instructions::EXTCODECOPY => { Request::GasMemCopy(schedule.extcodecopy_base_gas.into(), mem_needed(stack.peek(1), stack.peek(3))?, Gas::from_u256(*stack.peek(3))?) }, - instructions::LOG0...instructions::LOG4 => { - let no_of_topics = instructions::get_log_topics(instruction); + instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => { + let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed"); let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; let data_gas = overflowing!(Gas::from_u256(*stack.peek(1))?.overflow_mul(Gas::from(schedule.log_data_gas))); diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index ef9b3fb97..111986913 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -64,7 +64,6 @@ struct CodeReader<'a> { } impl<'a> CodeReader<'a> { - /// Create new code reader - starting at position 0. fn new(code: &'a [u8]) -> Self { CodeReader { @@ -81,7 +80,7 @@ impl<'a> CodeReader<'a> { U256::from(&self.code[pos..max]) } - fn len (&self) -> usize { + fn len(&self) -> usize { self.code.len() } } @@ -124,24 +123,31 @@ impl vm::Vm for Interpreter { let mut gasometer = Gasometer::::new(Cost::from_u256(params.gas)?); let mut stack = VecStack::with_capacity(ext.schedule().stack_limit, U256::zero()); let mut reader = CodeReader::new(code); - let infos = &*instructions::INSTRUCTIONS; while reader.position < code.len() { - let instruction = code[reader.position]; + let opcode = code[reader.position]; + let instruction = Instruction::from_u8(opcode); reader.position += 1; // TODO: make compile-time removable if too much of a performance hit. do_trace = do_trace && ext.trace_next_instruction( - reader.position - 1, instruction, gasometer.current_gas.as_u256(), + reader.position - 1, opcode, gasometer.current_gas.as_u256(), ); - let info = &infos[instruction as usize]; + if instruction.is_none() { + return Err(vm::Error::BadInstruction { + instruction: opcode + }); + } + let instruction = instruction.expect("None case is checked above; qed"); + + let info = instruction.info(); self.verify_instruction(ext, instruction, info, &stack)?; // Calculate gas cost let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?; if do_trace { - ext.trace_prepare_execute(reader.position - 1, instruction, requirements.gas_cost.as_u256()); + ext.trace_prepare_execute(reader.position - 1, opcode, requirements.gas_cost.as_u256()); } gasometer.verify_gas(&requirements.gas_cost)?; @@ -227,13 +233,7 @@ impl Interpreter { ((instruction == instructions::SHL || instruction == instructions::SHR || instruction == instructions::SAR) && !schedule.have_bitwise_shifting) { return Err(vm::Error::BadInstruction { - instruction: instruction - }); - } - - if info.tier == instructions::GasPriceTier::Invalid { - return Err(vm::Error::BadInstruction { - instruction: instruction + instruction: instruction as u8 }); } @@ -396,7 +396,7 @@ impl Interpreter { }, instructions::DELEGATECALL => (¶ms.sender, ¶ms.address, true, CallType::DelegateCall), instructions::STATICCALL => (¶ms.address, &code_address, true, CallType::StaticCall), - _ => panic!(format!("Unexpected instruction {} in CALL branch.", instruction)) + _ => panic!(format!("Unexpected instruction {:?} in CALL branch.", instruction)) }; // clear return data buffer before creating new call frame. @@ -453,8 +453,8 @@ impl Interpreter { ext.suicide(&u256_to_address(&address))?; return Ok(InstructionResult::StopExecution); }, - instructions::LOG0...instructions::LOG4 => { - let no_of_topics = instructions::get_log_topics(instruction); + instructions::LOG0 | instructions::LOG1 | instructions::LOG2 | instructions::LOG3 | instructions::LOG4 => { + let no_of_topics = instruction.log_topics().expect("log_topics always return some for LOG* instructions; qed"); let offset = stack.pop_back(); let size = stack.pop_back(); @@ -464,8 +464,15 @@ impl Interpreter { .collect(); ext.log(topics, self.mem.read_slice(offset, size))?; }, - instructions::PUSH1...instructions::PUSH32 => { - let bytes = instructions::get_push_bytes(instruction); + instructions::PUSH1 | instructions::PUSH2 | instructions::PUSH3 | instructions::PUSH4 | + instructions::PUSH5 | instructions::PUSH6 | instructions::PUSH7 | instructions::PUSH8 | + instructions::PUSH9 | instructions::PUSH10 | instructions::PUSH11 | instructions::PUSH12 | + instructions::PUSH13 | instructions::PUSH14 | instructions::PUSH15 | instructions::PUSH16 | + instructions::PUSH17 | instructions::PUSH18 | instructions::PUSH19 | instructions::PUSH20 | + instructions::PUSH21 | instructions::PUSH22 | instructions::PUSH23 | instructions::PUSH24 | + instructions::PUSH25 | instructions::PUSH26 | instructions::PUSH27 | instructions::PUSH28 | + instructions::PUSH29 | instructions::PUSH30 | instructions::PUSH31 | instructions::PUSH32 => { + let bytes = instruction.push_bytes().expect("push_bytes always return some for PUSH* instructions"); let val = code.read(bytes); stack.push(val); }, @@ -609,73 +616,22 @@ impl Interpreter { instructions::GASLIMIT => { stack.push(ext.env_info().gas_limit.clone()); }, - _ => { - self.exec_stack_instruction(instruction, stack)?; - } - }; - Ok(InstructionResult::Ok) - } - fn copy_data_to_memory(mem: &mut Vec, stack: &mut Stack, source: &[u8]) { - let dest_offset = stack.pop_back(); - let source_offset = stack.pop_back(); - let size = stack.pop_back(); - let source_size = U256::from(source.len()); + // Stack instructions - let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size { - true => { - let zero_slice = if source_offset > source_size { - mem.writeable_slice(dest_offset, size) - } else { - mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size) - }; - for i in zero_slice.iter_mut() { - *i = 0; - } - source.len() - }, - false => (size.low_u64() + source_offset.low_u64()) as usize - }; - - if source_offset < source_size { - let output_begin = source_offset.low_u64() as usize; - mem.write_slice(dest_offset, &source[output_begin..output_end]); - } - } - - fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> vm::Result { - let jump = jump_u.low_u64() as usize; - - if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u { - Ok(jump) - } else { - Err(vm::Error::BadJumpDestination { - destination: jump - }) - } - } - - fn is_zero(&self, val: &U256) -> bool { - val.is_zero() - } - - fn bool_to_u256(&self, val: bool) -> U256 { - if val { - U256::one() - } else { - U256::zero() - } - } - - fn exec_stack_instruction(&self, instruction: Instruction, stack: &mut Stack) -> vm::Result<()> { - match instruction { - instructions::DUP1...instructions::DUP16 => { - let position = instructions::get_dup_position(instruction); + instructions::DUP1 | instructions::DUP2 | instructions::DUP3 | instructions::DUP4 | + instructions::DUP5 | instructions::DUP6 | instructions::DUP7 | instructions::DUP8 | + instructions::DUP9 | instructions::DUP10 | instructions::DUP11 | instructions::DUP12 | + instructions::DUP13 | instructions::DUP14 | instructions::DUP15 | instructions::DUP16 => { + let position = instruction.dup_position().expect("dup_position always return some for DUP* instructions"); let val = stack.peek(position).clone(); stack.push(val); }, - instructions::SWAP1...instructions::SWAP16 => { - let position = instructions::get_swap_position(instruction); + instructions::SWAP1 | instructions::SWAP2 | instructions::SWAP3 | instructions::SWAP4 | + instructions::SWAP5 | instructions::SWAP6 | instructions::SWAP7 | instructions::SWAP8 | + instructions::SWAP9 | instructions::SWAP10 | instructions::SWAP11 | instructions::SWAP12 | + instructions::SWAP13 | instructions::SWAP14 | instructions::SWAP15 | instructions::SWAP16 => { + let position = instruction.swap_position().expect("swap_position always return some for SWAP* instructions"); stack.swap_with_top(position) }, instructions::POP => { @@ -923,15 +879,60 @@ impl Interpreter { }; stack.push(result); }, - _ => { - return Err(vm::Error::BadInstruction { - instruction: instruction - }); - } - } - Ok(()) + }; + Ok(InstructionResult::Ok) } + fn copy_data_to_memory(mem: &mut Vec, stack: &mut Stack, source: &[u8]) { + let dest_offset = stack.pop_back(); + let source_offset = stack.pop_back(); + let size = stack.pop_back(); + let source_size = U256::from(source.len()); + + let output_end = match source_offset > source_size || size > source_size || source_offset + size > source_size { + true => { + let zero_slice = if source_offset > source_size { + mem.writeable_slice(dest_offset, size) + } else { + mem.writeable_slice(dest_offset + source_size - source_offset, source_offset + size - source_size) + }; + for i in zero_slice.iter_mut() { + *i = 0; + } + source.len() + }, + false => (size.low_u64() + source_offset.low_u64()) as usize + }; + + if source_offset < source_size { + let output_begin = source_offset.low_u64() as usize; + mem.write_slice(dest_offset, &source[output_begin..output_end]); + } + } + + fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> vm::Result { + let jump = jump_u.low_u64() as usize; + + if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u { + Ok(jump) + } else { + Err(vm::Error::BadJumpDestination { + destination: jump + }) + } + } + + fn is_zero(&self, val: &U256) -> bool { + val.is_zero() + } + + fn bool_to_u256(&self, val: bool) -> U256 { + if val { + U256::one() + } else { + U256::zero() + } + } } fn get_and_reset_sign(value: U256) -> (U256, bool) { diff --git a/ethcore/evm/src/interpreter/shared_cache.rs b/ethcore/evm/src/interpreter/shared_cache.rs index d4e992c90..f4a7f47f9 100644 --- a/ethcore/evm/src/interpreter/shared_cache.rs +++ b/ethcore/evm/src/interpreter/shared_cache.rs @@ -21,7 +21,7 @@ use ethereum_types::H256; use parking_lot::Mutex; use memory_cache::MemoryLruCache; use bit_set::BitSet; -use super::super::instructions; +use super::super::instructions::{self, Instruction}; const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; @@ -70,12 +70,14 @@ impl SharedCache { let mut position = 0; while position < code.len() { - let instruction = code[position]; + let instruction = Instruction::from_u8(code[position]); - if instruction == instructions::JUMPDEST { - jump_dests.insert(position); - } else if instructions::is_push(instruction) { - position += instructions::get_push_bytes(instruction); + if let Some(instruction) = instruction { + if instruction == instructions::JUMPDEST { + jump_dests.insert(position); + } else if let Some(push_bytes) = instruction.push_bytes() { + position += push_bytes; + } } position += 1; } diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index 6eca25f42..cd326a317 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -52,6 +52,6 @@ pub use vm::{ GasLeft, ReturnData }; pub use self::evm::{Finalize, FinalizationResult, CostType}; -pub use self::instructions::{InstructionInfo, INSTRUCTIONS, push_bytes}; +pub use self::instructions::{InstructionInfo, Instruction}; pub use self::vmtype::VMType; pub use self::factory::Factory; diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index ccee9c371..613be1d22 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -121,13 +121,13 @@ impl trace::VMTracer for Informant { } fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { - let info = ::evm::INSTRUCTIONS[self.instruction as usize]; + let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); let trace = format!( "{{\"pc\":{pc},\"op\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"gasCost\":\"0x{gas_cost:x}\",\"memory\":{memory},\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}", pc = self.pc, op = self.instruction, - name = info.name, + name = info.map(|i| i.name).unwrap_or(""), gas = gas_used.saturating_add(self.gas_cost), gas_cost = self.gas_cost, memory = self.memory(), @@ -141,7 +141,8 @@ impl trace::VMTracer for Informant { self.gas_used = gas_used; let len = self.stack.len(); - self.stack.truncate(if len > info.args { len - info.args } else { 0 }); + let info_args = info.map(|i| i.args).unwrap_or(0); + self.stack.truncate(if len > info_args { len - info_args } else { 0 }); self.stack.extend_from_slice(stack_push); // TODO [ToDr] Align memory? diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index 6c4dac162..b3533ea2c 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -118,7 +118,7 @@ impl trace::VMTracer for Informant { type Output = (); fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { - let info = ::evm::INSTRUCTIONS[instruction as usize]; + let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); self.instruction = instruction; let storage = self.storage(); let stack = self.stack(); @@ -128,7 +128,7 @@ impl trace::VMTracer for Informant { "{{\"pc\":{pc},\"op\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}", pc = pc, op = instruction, - name = info.name, + name = info.map(|i| i.name).unwrap_or(""), gas = current_gas, stack = stack, storage = storage, @@ -142,10 +142,11 @@ impl trace::VMTracer for Informant { } fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { - let info = ::evm::INSTRUCTIONS[self.instruction as usize]; + let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); let len = self.stack.len(); - self.stack.truncate(if len > info.args { len - info.args } else { 0 }); + let info_args = info.map(|i| i.args).unwrap_or(0); + self.stack.truncate(if len > info_args { len - info_args } else { 0 }); self.stack.extend_from_slice(stack_push); if let Some((pos, val)) = store_diff {