class Ethereum::VM

Public Class Methods

code_cache() click to toggle source
# File lib/ethereum/vm.rb, line 10
def code_cache
  @code_cache ||= {}
end
execute(*args) click to toggle source
# File lib/ethereum/vm.rb, line 14
def execute(*args)
  new.execute(*args)
end

Public Instance Methods

execute(ext, msg, code) click to toggle source
# File lib/ethereum/vm.rb, line 21
def execute(ext, msg, code)
  s = State.new gas: msg.gas

  if VM.code_cache.has_key?(code)
    processed_code = VM.code_cache[code]
  else
    processed_code = preprocess_code code
    VM.code_cache[code] = processed_code
  end

  # for trace only
  steps = 0
  _prevop = nil

  loop do
    return peaceful_exit('CODE OUT OF RANGE', s.gas, []) if s.pc >= processed_code.size

    op, in_args, out_args, fee, opcode, pushval = processed_code[s.pc]

    return vm_exception('OUT OF GAS') if fee > s.gas
    return vm_exception('INSUFFICIENT STACK', op: op, needed: in_args, available: s.stack.size) if in_args > s.stack.size
    return vm_exception('STACK SIZE LIMIT EXCEEDED', op: op, pre_height: s.stack.size) if (s.stack.size - in_args + out_args) > 1024

    s.gas -= fee
    s.pc += 1

    # This diverges from normal logging, as we use the logging namespace
    # only to decide which features get logged in 'eth.vm.op', i.e.
    # tracing can not be activated by activating a sub like
    # 'eth.vm.op.stack'.
    if log_vm_exit.trace?
      trace_data = {
        stack: s.stack.map(&:to_s),
        gas: s.gas + fee,
        inst: opcode,
        pc: s.pc-1,
        op: op,
        steps: steps
      }

      if %i(MLOAD MSTORE MSTORE8 SHA3 CALL CALLCODE CREATE CALLDATACOPY CODECOPY EXTCODECOPY).include?(_prevop)
        if s.memory.size < 1024
          trace_data[:memory] = Utils.encode_hex(Utils.int_array_to_bytes(s.memory))
        else
          trace_data[:sha3memory] = Utils.encode_hex(Utils.keccak256(Utils.int_array_to_bytes(s.memory)))
        end
      end

      if %i(SSTORE SLOAD).include?(_prevop) || steps == 0
        trace_data[:storage] = ext.log_storage(msg.to)
      end

      if steps == 0
        trace_data[:depth] = msg.depth
        trace_data[:address] = msg.to
      end

      if op[0,4] == 'PUSH'
        trace_data[:pushvalue] = pushval
      end

      log_vm_op.trace('vm', **trace_data)

      steps += 1
      _prevop = op
    end

    # Invalid operation
    return vm_exception('INVALID OP', opcode: opcode) if op == :INVALID

    # Valid operations
    stk = s.stack
    mem = s.memory
    if opcode < 0x10 # Stop & Arithmetic Operations
      case op
      when :STOP
        return peaceful_exit('STOP', s.gas, [])
      when :ADD
        r = (stk.pop + stk.pop) & UINT_MAX
        stk.push r
      when :SUB
        r = (stk.pop - stk.pop) & UINT_MAX
        stk.push r
      when :MUL
        r = (stk.pop * stk.pop) & UINT_MAX
        stk.push r
      when :DIV
        s0, s1 = stk.pop, stk.pop
        stk.push(s1 == 0 ? 0 : s0 / s1)
      when :MOD
        s0, s1 = stk.pop, stk.pop
        stk.push(s1 == 0 ? 0 : s0 % s1)
      when :SDIV
        s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
        r = s1 == 0 ? 0 : ((s0.abs / s1.abs * (s0*s1 < 0 ? -1 : 1)) & UINT_MAX)
        stk.push r
      when :SMOD
        s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
        r = s1 == 0 ? 0 : ((s0.abs % s1.abs * (s0 < 0 ? -1 : 1)) & UINT_MAX)
        stk.push r
      when :ADDMOD
        s0, s1, s2 = stk.pop, stk.pop, stk.pop
        r = s2 == 0 ? 0 : (s0+s1) % s2
        stk.push r
      when :MULMOD
        s0, s1, s2 = stk.pop, stk.pop, stk.pop
        r = s2 == 0 ? 0 : Utils.mod_mul(s0, s1, s2)
        stk.push r
      when :EXP
        base, exponent = stk.pop, stk.pop

        # fee for exponent is dependent on its bytes
        # calc n bytes to represent exponent
        nbytes = Utils.encode_int(exponent).size
        expfee = nbytes * Opcodes::GEXPONENTBYTE
        if s.gas < expfee
          s.gas = 0
          return vm_exception('OOG EXPONENT')
        end

        s.gas -= expfee
        stk.push Utils.mod_exp(base, exponent, TT256)
      when :SIGNEXTEND # extend sign from bytes at s0 to left
        s0, s1 = stk.pop, stk.pop
        if s0 < 32
          testbit = s0*8 + 7
          mask = 1 << testbit
          if s1 & mask == 0 # extend 0s
            stk.push(s1 & (mask - 1))
          else # extend 1s
            stk.push(s1 | (TT256 - mask))
          end
        else
          stk.push s1
        end
      end
    elsif opcode < 0x20 # Comparison & Bitwise Logic Operations
      case op
      when :LT
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 < s1 ? 1 : 0)
      when :GT
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 > s1 ? 1 : 0)
      when :SLT
        s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
        stk.push(s0 < s1 ? 1 : 0)
      when :SGT
        s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
        stk.push(s0 > s1 ? 1 : 0)
      when :EQ
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 == s1 ? 1 : 0)
      when :ISZERO
        s0 = stk.pop
        stk.push(s0 == 0 ? 1 : 0)
      when :AND
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 & s1)
      when :OR
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 | s1)
      when :XOR
        s0, s1 = stk.pop, stk.pop
        stk.push(s0 ^ s1)
      when :NOT
        s0 = stk.pop
        stk.push(UINT_MAX - s0)
      when :BYTE
        s0, s1 = stk.pop, stk.pop
        if s0 < 32
          stk.push((s1 / 256**(31-s0)) % 256)
        else
          stk.push(0)
        end
      end
    elsif opcode < 0x40 # SHA3 & Environmental Information
      case op
      when :SHA3
        s0, s1 = stk.pop, stk.pop

        s.gas -= Opcodes::GSHA3WORD * (Utils.ceil32(s1) / 32)
        return vm_exception('OOG PAYING FOR SHA3') if s.gas < 0

        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)

        data = Utils.int_array_to_bytes mem.safe_slice(s0,s1)
        stk.push Utils.big_endian_to_int(Utils.keccak256(data))
      when :ADDRESS
        stk.push Utils.coerce_to_int(msg.to)
      when :BALANCE
        if ext.post_anti_dos_hardfork
          return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::BALANCE_SUPPLEMENTAL_GAS)
        end
        s0 = stk.pop
        addr = Utils.coerce_addr_to_hex(s0 % 2**160)
        stk.push ext.get_balance(addr)
      when :ORIGIN
        stk.push Utils.coerce_to_int(ext.tx_origin)
      when :CALLER
        stk.push Utils.coerce_to_int(msg.sender)
      when :CALLVALUE
        stk.push msg.value
      when :CALLDATALOAD
        stk.push msg.data.extract32(stk.pop)
      when :CALLDATASIZE
        stk.push msg.data.size
      when :CALLDATACOPY
        mstart, dstart, size = stk.pop, stk.pop, stk.pop

        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
        return vm_exception('OOG COPY DATA') unless data_copy(s, size)

        msg.data.extract_copy(mem, mstart, dstart, size)
      when :CODESIZE
        stk.push processed_code.size
      when :CODECOPY
        mstart, cstart, size = stk.pop, stk.pop, stk.pop

        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
        return vm_exception('OOG COPY CODE') unless data_copy(s, size)

        size.times do |i|
          if cstart + i < processed_code.size
            mem[mstart+i] = processed_code[cstart+i][4] # copy opcode
          else
            mem[mstart+i] = 0
          end
        end
      when :GASPRICE
        stk.push ext.tx_gasprice
      when :EXTCODESIZE
        if ext.post_anti_dos_hardfork
          return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::EXTCODELOAD_SUPPLEMENTAL_GAS)
        end
        addr = stk.pop
        addr = Utils.coerce_addr_to_hex(addr % 2**160)
        stk.push (ext.get_code(addr) || Constant::BYTE_EMPTY).size
      when :EXTCODECOPY
        if ext.post_anti_dos_hardfork
          return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::EXTCODELOAD_SUPPLEMENTAL_GAS)
        end
        addr, mstart, cstart, size = stk.pop, stk.pop, stk.pop, stk.pop
        addr = Utils.coerce_addr_to_hex(addr % 2**160)
        extcode = ext.get_code(addr) || Constant::BYTE_EMPTY
        raise ValueError, "extcode must be string" unless extcode.is_a?(String)

        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
        return vm_exception('OOG COPY CODE') unless data_copy(s, size)

        size.times do |i|
          if cstart + i < extcode.size
            mem[mstart+i] = extcode[cstart+i].ord
          else
            mem[mstart+i] = 0
          end
        end
      end
    elsif opcode < 0x50 # Block Information
      case op
      when :BLOCKHASH
        s0 = stk.pop
        stk.push ext.block_hash(s0)
      when :COINBASE
        stk.push Utils.big_endian_to_int(ext.block_coinbase)
      when :TIMESTAMP
        stk.push ext.block_timestamp
      when :NUMBER
        stk.push ext.block_number
      when :DIFFICULTY
        stk.push ext.block_difficulty
      when :GASLIMIT
        stk.push ext.block_gas_limit
      end
    elsif opcode < 0x60 # Stack, Memory, Storage and Flow Operations
      case op
      when :POP
        stk.pop
      when :MLOAD
        s0 = stk.pop
        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)

        data = Utils.int_array_to_bytes mem.safe_slice(s0, 32)
        stk.push Utils.big_endian_to_int(data)
      when :MSTORE
        s0, s1 = stk.pop, stk.pop
        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)

        32.times.to_a.reverse.each do |i|
          mem[s0+i] = s1 % 256
          s1 /= 256
        end
      when :MSTORE8
        s0, s1 = stk.pop, stk.pop
        return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 1)
        mem[s0] = s1 % 256
      when :SLOAD
        if ext.post_anti_dos_hardfork
          return vm_exception('OUT OF GAS') unless eat_gas(s, Opcodes::SLOAD_SUPPLEMENTAL_GAS)
        end
        s0 = stk.pop
        stk.push ext.get_storage_data(msg.to, s0)
      when :SSTORE
        s0, s1 = stk.pop, stk.pop

        if ext.get_storage_data(msg.to, s0) != 0
          gascost = s1 == 0 ? Opcodes::GSTORAGEKILL : Opcodes::GSTORAGEMOD
          refund = s1 == 0 ? Opcodes::GSTORAGEREFUND : 0
        else
          gascost = s1 == 0 ? Opcodes::GSTORAGEMOD : Opcodes::GSTORAGEADD
          refund = 0
        end

        return vm_exception('OUT OF GAS') if s.gas < gascost

        s.gas -= gascost
        ext.add_refund refund
        ext.set_storage_data msg.to, s0, s1
      when :JUMP
        s0 = stk.pop
        s.pc = s0

        op_new = s.pc < processed_code.size ? processed_code[s.pc][0] : :STOP
        return vm_exception('BAD JUMPDEST') if op_new != :JUMPDEST
      when :JUMPI
        s0, s1 = stk.pop, stk.pop
        if s1 != 0
          s.pc = s0
          op_new = s.pc < processed_code.size ? processed_code[s.pc][0] : :STOP
          return vm_exception('BAD JUMPDEST') if op_new != :JUMPDEST
        end
      when :PC
        stk.push(s.pc - 1)
      when :MSIZE
        stk.push mem.size
      when :GAS
        stk.push s.gas # AFTER subtracting cost 1
      end
    elsif op[0,Opcodes::PREFIX_PUSH.size] == Opcodes::PREFIX_PUSH
      pushnum = op[Opcodes::PREFIX_PUSH.size..-1].to_i
      s.pc += pushnum
      stk.push pushval
    elsif op[0,Opcodes::PREFIX_DUP.size] == Opcodes::PREFIX_DUP
      depth = op[Opcodes::PREFIX_DUP.size..-1].to_i
      stk.push stk[-depth]
    elsif op[0,Opcodes::PREFIX_SWAP.size] == Opcodes::PREFIX_SWAP
      depth = op[Opcodes::PREFIX_SWAP.size..-1].to_i
      temp = stk[-depth - 1]
      stk[-depth - 1] = stk[-1]
      stk[-1] = temp
    elsif op[0,Opcodes::PREFIX_LOG.size] == Opcodes::PREFIX_LOG
      # 0xa0 ... 0xa4, 32/64/96/128/160 + data.size gas
      #
      # a. Opcodes LOG0...LOG4 are added, takes 2-6 stake arguments:
      #      MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
      #
      # b. Logs are kept track of during tx execution exactly the same way
      #    as suicides (except as an ordered list, not a set).
      #
      #    Each log is in the form [address, [topic1, ... ], data] where:
      #    * address is what the ADDRESS opcode would output
      #    * data is mem[MEMSTART, MEMSZ]
      #    * topics are as provided by the opcode
      #
      # c. The ordered list of logs in the transation are expreseed as
      #    [log0, log1, ..., logN].
      #
      depth = op[Opcodes::PREFIX_LOG.size..-1].to_i
      mstart, msz = stk.pop, stk.pop
      topics = depth.times.map {|i| stk.pop }

      s.gas -= msz * Opcodes::GLOGBYTE

      return vm_exception("OOG EXTENDING MEMORY") unless mem_extend(mem, s, mstart, msz)

      data = mem.safe_slice(mstart, msz)
      ext.log(msg.to, topics, Utils.int_array_to_bytes(data))
      if log_log.trace?
        log_log.trace('LOG', to: msg.to, topics: topics, data: data)
      end
    elsif op == :CREATE
      value, mstart, msz = stk.pop, stk.pop, stk.pop

      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, msz)

      if ext.get_balance(msg.to) >= value && msg.depth < 1024
        cd = CallData.new mem, mstart, msz

        ingas = s.gas
        if ext.post_anti_dos_hardfork
          ingas = max_call_gas(ingas)
        end
        create_msg = Message.new(msg.to, Constant::BYTE_EMPTY, value, ingas, cd, depth: msg.depth+1)

        o, gas, addr = ext.create create_msg
        if o.true?
          stk.push Utils.coerce_to_int(addr)
          s.gas -= (ingas - gas)
        else
          stk.push 0
          s.gas -= ingas
        end
      else
        stk.push(0)
      end
    elsif op == :CALL
      gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
        stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop

      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)

      to = Utils.zpad_int(to)[12..-1] # last 20 bytes
      extra_gas = (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT +
        (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER +
        (ext.post_anti_dos_hardfork ? 1 : 0) * Opcodes::CALL_SUPPLEMENTAL_GAS
      if ext.post_anti_dos_hardfork
        return vm_exception('OUT OF GAS', needed: extra_gas) if s.gas < extra_gas
        gas = [gas, max_call_gas(s.gas-extra_gas)].min
      else
        return vm_exception('OUT OF GAS', needed: gas+extra_gas) if s.gas < gas+extra_gas
      end

      submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
      total_gas = gas + extra_gas
      if ext.get_balance(msg.to) >= value && msg.depth < 1024
        s.gas -= total_gas

        cd = CallData.new mem, memin_start, memin_sz
        call_msg = Message.new(msg.to, to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)

        result, gas, data = ext.apply_msg call_msg
        if result == 0
          stk.push 0
        else
          stk.push 1
          s.gas += gas
          [data.size, memout_sz].min.times do |i|
            mem[memout_start+i] = data[i]
          end
        end
      else
        s.gas -= (total_gas - submsg_gas)
        stk.push(0)
      end
    elsif op == :CALLCODE || op == :DELEGATECALL
      if op == :CALLCODE
        gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
          stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
      else
        gas, to, memin_start, memin_sz, memout_start, memout_sz = \
          stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
        value = 0
      end

      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)

      extra_gas = (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER +
        (ext.post_anti_dos_hardfork ? 1 : 0) * Opcodes::CALL_SUPPLEMENTAL_GAS
      if ext.post_anti_dos_hardfork
        return vm_exception('OUT OF GAS', needed: extra_gas) if s.gas < extra_gas
        gas = [gas, max_call_gas(s.gas-extra_gas)].min
      else
        return vm_exception('OUT OF GAS', needed: gas+extra_gas) if s.gas < gas+extra_gas
      end

      submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
      total_gas = gas + extra_gas
      if ext.get_balance(msg.to) >= value && msg.depth < 1024
        s.gas -= total_gas

        to = Utils.zpad_int(to)[12..-1] # last 20 bytes
        cd = CallData.new mem, memin_start, memin_sz

        if ext.post_homestead_hardfork && op == :DELEGATECALL
          call_msg = Message.new(msg.sender, msg.to, msg.value, submsg_gas, cd, depth: msg.depth+1, code_address: to, transfers_value: false)
        elsif op == :DELEGATECALL
          return vm_exception('OPCODE INACTIVE')
        else
          call_msg = Message.new(msg.to, msg.to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
        end

        result, gas, data = ext.apply_msg call_msg
        if result == 0
          stk.push 0
        else
          stk.push 1
          s.gas += gas
          [data.size, memout_sz].min.times do |i|
            mem[memout_start+i] = data[i]
          end
        end
      else
        s.gas -= (total_gas - submsg_gas)
        stk.push(0)
      end
    elsif op == :RETURN
      s0, s1 = stk.pop, stk.pop
      return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
      return peaceful_exit('RETURN', s.gas, mem.safe_slice(s0, s1))
    elsif op == :SUICIDE
      s0 = stk.pop
      to = Utils.zpad_int(s0)[12..-1] # last 20 bytes

      if ext.post_anti_dos_hardfork
        extra_gas = Opcodes::SUICIDE_SUPPLEMENTAL_GAS +
          (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT
        return vm_exception('OUT OF GAS') unless eat_gas(s, extra_gas)
      end

      xfer = ext.get_balance(msg.to)
      ext.set_balance(to, ext.get_balance(to)+xfer)
      ext.set_balance(msg.to, 0)
      ext.add_suicide(msg.to)

      return 1, s.gas, []
    end
  end
end
preprocess_code(code) click to toggle source

Preprocesses code, and determines which locations are in the middle of pushdata and thus invalid

# File lib/ethereum/vm.rb, line 544
def preprocess_code(code)
  code = Utils.bytes_to_int_array code
  ops = []

  i = 0
  while i < code.size
    o = Opcodes::TABLE.fetch(code[i], [:INVALID, 0, 0, 0]) + [code[i], 0]
    ops.push o

    if o[0][0,Opcodes::PREFIX_PUSH.size] == Opcodes::PREFIX_PUSH
      n = o[0][Opcodes::PREFIX_PUSH.size..-1].to_i
      n.times do |j|
        i += 1
        byte = i < code.size ? code[i] : 0
        o[-1] = (o[-1] << 8) + byte

        # polyfill, these INVALID ops will be skipped in execution
        ops.push [:INVALID, 0, 0, 0, byte, 0] if i < code.size
      end
    end

    i += 1
  end

  ops
end

Private Instance Methods

data_copy(s, sz) click to toggle source
# File lib/ethereum/vm.rb, line 624
def data_copy(s, sz)
  if sz > 0
    copyfee = Opcodes::GCOPY * Utils.ceil32(sz) / 32

    if s.gas < copyfee
      s.gas = 0
      return false
    end
    s.gas -= copyfee
  end

  true
end
eat_gas(s, amount) click to toggle source
# File lib/ethereum/vm.rb, line 642
def eat_gas(s, amount)
  if s.gas < amount
    s.gas = 0
    false
  else
    s.gas -= amount
    true
  end
end
log_log() click to toggle source
# File lib/ethereum/vm.rb, line 581
def log_log
  @log_log ||= Logger.new 'eth.vm.log'
end
log_vm_exit() click to toggle source
# File lib/ethereum/vm.rb, line 573
def log_vm_exit
  @log_vm_exit ||= Logger.new 'eth.vm.exit'
end
log_vm_op() click to toggle source
# File lib/ethereum/vm.rb, line 577
def log_vm_op
  @log_vm_op ||= Logger.new 'eth.vm.op'
end
max_call_gas(gas) click to toggle source
# File lib/ethereum/vm.rb, line 652
def max_call_gas(gas)
  gas - (gas / Opcodes::CALL_CHILD_LIMIT_DENOM)
end
mem_extend(mem, s, start, sz) click to toggle source
# File lib/ethereum/vm.rb, line 599
def mem_extend(mem, s, start, sz)
  if sz > 0
    oldsize = mem.size / 32
    old_totalfee = mem_fee oldsize

    newsize = Utils.ceil32(start + sz) / 32
    new_totalfee = mem_fee newsize

    if old_totalfee < new_totalfee
      memfee = new_totalfee - old_totalfee

      if s.gas < memfee
        s.gas = 0
        return false
      end
      s.gas -= memfee

      m_extend = (newsize - oldsize) * 32
      mem.concat([0]*m_extend)
    end
  end

  true
end
mem_fee(sz) click to toggle source
# File lib/ethereum/vm.rb, line 638
def mem_fee(sz)
  sz * Opcodes::GMEMORY + sz**2 / Opcodes::GQUADRATICMEMDENOM
end
peaceful_exit(cause, gas, data, **kwargs) click to toggle source
# File lib/ethereum/vm.rb, line 592
def peaceful_exit(cause, gas, data, **kwargs)
  if log_vm_exit.trace?
    log_vm_exit.trace('EXIT', cause: cause, **kwargs)
  end
  return 1, gas, data
end
vm_exception(error, **kwargs) click to toggle source
# File lib/ethereum/vm.rb, line 585
def vm_exception(error, **kwargs)
  if log_vm_exit.trace?
    log_vm_exit.trace('EXCEPTION', cause: error, **kwargs)
  end
  return 0, 0, []
end