class Ethereum::ExternalCall

External calls that can be made from inside the VM. To use the EVM with a different blockchain system, database, set parameters for testing, just swap out the functions here.

Public Class Methods

new(block, tx) click to toggle source
# File lib/ethereum/external_call.rb, line 19
def initialize(block, tx)
  @block = block
  @tx = tx
end

Public Instance Methods

add_suicide(x) click to toggle source
# File lib/ethereum/external_call.rb, line 28
def add_suicide(x)
  @block.suicides.push x
end
apply_msg(msg, code=nil) click to toggle source
# File lib/ethereum/external_call.rb, line 150
def apply_msg(msg, code=nil)
  code ||= get_code msg.code_address

  if log_msg.trace?
    log_msg.debug "MSG APPLY",  sender: Utils.encode_hex(msg.sender), to: Utils.encode_hex(msg.to), gas: msg.gas, value: msg.value, data: Utils.encode_hex(msg.data.extract_all)
    if log_state.trace?
      log_state.trace "MSG PRE STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
      log_state.trace "MSG PRE STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
    end
  end

  # snapshot before execution
  snapshot = self.snapshot

  # transfer value
  if msg.transfers_value
    unless transfer_value(msg.sender, msg.to, msg.value)
      log_msg.debug "MSG TRANSFER FAILED", have: get_balance(msg.to), want: msg.value
      return [1, msg.gas, []]
    end
  end

  # main loop
  if SpecialContract[msg.code_address]
    res, gas, dat = SpecialContract[msg.code_address].call(self, msg)
  else
    res, gas, dat = VM.execute self, msg, code
  end

  if log_msg.trace?
    log_msg.trace "MSG APPLIED", gas_remained: gas, sender: msg.sender, to: msg.to, data: dat
    if log_state.trace?
      log_state.trace "MSG POST STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
      log_state.trace "MSG POST STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
    end
  end

  if res == 0
    log_msg.debug 'REVERTING'
    revert snapshot
  end

  return res, gas, dat
end
block_coinbase() click to toggle source
# File lib/ethereum/external_call.rb, line 46
def block_coinbase
  @block.coinbase
end
block_difficulty() click to toggle source
# File lib/ethereum/external_call.rb, line 58
def block_difficulty
  @block.difficulty
end
block_gas_limit() click to toggle source
# File lib/ethereum/external_call.rb, line 62
def block_gas_limit
  @block.gas_limit
end
block_hash(x) click to toggle source
# File lib/ethereum/external_call.rb, line 32
def block_hash(x)
  if post_metropolis_hardfork
    get_storage_data @block.config[:metropolis_blockhash_store], x
  else
    d = @block.number - x
    hash = if d > 0 && d <= 256
             @block.get_ancestor_hash d
           else
             Constant::BYTE_EMPTY
           end
    Utils.big_endian_to_int hash
  end
end
block_number() click to toggle source
# File lib/ethereum/external_call.rb, line 54
def block_number
  @block.number
end
block_timestamp() click to toggle source
# File lib/ethereum/external_call.rb, line 50
def block_timestamp
  @block.timestamp
end
create(msg) click to toggle source
# File lib/ethereum/external_call.rb, line 90
def create(msg)
  log_msg.debug 'CONTRACT CREATION'

  sender = Utils.normalize_address(msg.sender, allow_blank: true)

  code = msg.data.extract_all
  if post_metropolis_hardfork
    msg.to = Utils.mk_metropolis_contract_address msg.sender, code
    if get_code(msg.to)
      n1 = get_nonce msg.to
      n2 = n1 >= Constant::TT40 ?
        (n + 1) :
        (Utils.big_endian_to_int(msg.to) + 2)
      set_nonce msg.to, (n2 % Constant::TT160)
      msg.to = Utils.normalize_address((get_nonce(msg.to) - 1) % Constant::TT160)
    end
  else
    increment_nonce msg.sender if tx_origin != msg.sender

    nonce = Utils.encode_int(get_nonce(msg.sender) - 1)
    msg.to = Utils.mk_contract_address sender, nonce
  end

  balance = get_balance(msg.to)
  if balance > 0
    set_balance msg.to, balance
    set_nonce msg.to, 0
    set_code msg.to, Constant::BYTE_EMPTY
    reset_storage msg.to
  end

  msg.is_create = true
  msg.data = VM::CallData.new [], 0, 0

  snapshot = self.snapshot
  res, gas, dat = apply_msg msg, code

  if res.true?
    return 1, gas, msg.to if dat.empty?

    gcost = dat.size * Opcodes::GCONTRACTBYTE
    if gas >= gcost
      gas -= gcost
    else
      dat = []
      log_msg.debug "CONTRACT CREATION OOG", have: gas, want: gcost, block_number: @block.number

      if post_homestead_hardfork
        revert snapshot
        return 0, 0, Constant::BYTE_EMPTY
      end
    end

    set_code msg.to, Utils.int_array_to_bytes(dat)
    return 1, gas, msg.to
  else
    return 0, gas, Constant::BYTE_EMPTY
  end
end
log(addr, topics, data) click to toggle source
# File lib/ethereum/external_call.rb, line 66
def log(addr, topics, data)
  @block.add_log Log.new(addr, topics, data)
end
log_storage(x) click to toggle source
# File lib/ethereum/external_call.rb, line 24
def log_storage(x)
  @block.account_to_dict(x)[:storage]
end
post_anti_dos_hardfork() click to toggle source
# File lib/ethereum/external_call.rb, line 82
def post_anti_dos_hardfork
  @block.number >= @block.config[:anti_dos_fork_blknum]
end
post_homestead_hardfork() click to toggle source
# File lib/ethereum/external_call.rb, line 78
def post_homestead_hardfork
  @block.number >= @block.config[:homestead_fork_blknum]
end
post_metropolis_hardfork() click to toggle source
# File lib/ethereum/external_call.rb, line 86
def post_metropolis_hardfork
  @block.number >= @block.config[:metropolis_fork_blknum]
end
tx_gasprice() click to toggle source
# File lib/ethereum/external_call.rb, line 74
def tx_gasprice
  @tx.gasprice
end
tx_origin() click to toggle source
# File lib/ethereum/external_call.rb, line 70
def tx_origin
  @tx.sender
end

Private Instance Methods

log_msg() click to toggle source
# File lib/ethereum/external_call.rb, line 197
def log_msg
  @log_msg ||= Logger.new 'eth.external_call.msg'
end
log_state() click to toggle source
# File lib/ethereum/external_call.rb, line 201
def log_state
  @log_state ||= Logger.new 'eth.external_call.state'
end