class Ethereum::Tester::State

Constants

TMP_DIR_PREFIX

Attributes

env[RW]

Public Class Methods

new(env: nil, privkeys: Fixture.keys, start_alloc: nil, gas_price: Fixture.gas_price, gas_limit: Fixture.gas_limit) click to toggle source
# File lib/ethereum/tester/state.rb, line 12
def initialize(env: nil, privkeys: Fixture.keys, start_alloc: nil, gas_price: Fixture.gas_price, gas_limit: Fixture.gas_limit)
  if env
    @db = env.db
    @env = env
  else
    @db = DB::EphemDB.new
    @env = Env.new @db
  end

  @temp_data_dir = Dir.mktmpdir TMP_DIR_PREFIX

  @privkeys = privkeys
  @accounts = @privkeys.map {|k| PrivateKey.new(k).to_address }
  @gas_price = gas_price
  @gas_limit = gas_limit

  start_alloc ||= get_start_alloc()
  @block = Block.genesis @env, start_alloc: start_alloc
  @block.timestamp = 1410973349
  @block.coinbase = @accounts[0]
  @block.gas_limit = 10**9

  @blocks = [@block]
  @last_tx = nil

  ObjectSpace.define_finalizer(self) {|id| FileUtils.rm_rf(@temp_data_dir) }
end

Public Instance Methods

_send_tx(sender, to, value, evmdata: '', funid: nil, abi: nil, profiling: 0) click to toggle source
# File lib/ethereum/tester/state.rb, line 99
def _send_tx(sender, to, value, evmdata: '', funid: nil, abi: nil, profiling: 0)
  if funid || abi
    raise ArgumentError, "Send with funid+abi is deprecated. Please use the abi_contract mechanism."
  end

  t1, g1 = Time.now, @block.gas_used
  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
  tx = Transaction.new(sendnonce, @gas_price, @gas_limit, to, value, evmdata)
  @last_tx = tx
  tx.sign(sender)

  recorder = profiling > 1 ? LogRecorder.new : nil

  success, output = @block.apply_transaction(tx)
  raise TransactionFailed if success.false?
  out = {output: output}

  if profiling > 0
    zero_bytes = tx.data.count Constant::BYTE_ZERO
    none_zero_bytes = tx.data.size - zero_bytes
    intrinsic_gas_used = Opcodes::GTXCOST +
      Opcodes::GTXDATAZERO * zero_bytes +
      Opcodes::GTXDATANONZERO * none_zero_bytes
    t2, g2 = Time.now, @block.gas_used
    out[:time] = t2 - t1
    out[:gas] = g2 - g1 - intrinsic_gas_used
  end

  if profiling > 1
    # TODO: collect all traced ops use LogRecorder
  end

  out
end
abi_contract(code, sender: @privkeys[0], endowment: 0, language: :serpent, libraries: nil, path: nil, constructor_parameters: nil, log_listener: nil, listen: true, **kwargs) click to toggle source
# File lib/ethereum/tester/state.rb, line 58
def abi_contract(code, sender: @privkeys[0], endowment: 0, language: :serpent,
                 libraries: nil, path: nil, constructor_parameters: nil,
                 log_listener: nil, listen: true, **kwargs)
  code = Language.format_spaces code
  compiler = Language.get language

  contract_interface = compiler.mk_full_signature code, path: path, **kwargs
  translator = ABI::ContractTranslator.new contract_interface

  encoded_parameters = constructor_parameters ?
    translator.encode_constructor_arguments(constructor_parameters) :
    nil

  address = contract(code, sender: sender, endowment: endowment, language: language,
                     libraries: libraries, path: path, constructor_call: encoded_parameters,
                     **kwargs)

  ABIContract.new(self, translator, address, listen: listen, log_listener: log_listener, default_key: sender)
end
call(*args, **kwargs) click to toggle source
# File lib/ethereum/tester/state.rb, line 91
def call(*args, **kwargs)
  raise DeprecatedError, "Call deprecated. Please use the abi_contract mechanism or message(sender, to, value, data) directly, using the ABI module to generate data if needed."
end
contract(code, sender: @privkeys[0], endowment: 0, language: :serpent, libraries: nil, path: nil, constructor_call: nil, **kwargs) click to toggle source
# File lib/ethereum/tester/state.rb, line 44
def contract(code, sender: @privkeys[0], endowment: 0, language: :serpent,
             libraries: nil, path: nil, constructor_call: nil, **kwargs)
  code = Language.format_spaces code
  compiler = Language.get language

  bytecode = compiler.compile code, path: path, libraries: libraries, **kwargs
  bytecode += constructor_call if constructor_call

  address = evm bytecode, sender: sender, endowment: endowment
  raise AssertError, "Contract code empty" if @block.get_code(address).empty?

  address
end
evm(bytecode, sender: @privkeys[0], endowment: 0, gas: nil) click to toggle source
# File lib/ethereum/tester/state.rb, line 78
def evm(bytecode, sender: @privkeys[0], endowment: 0, gas: nil)
  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address

  tx = Transaction.contract sendnonce, @gas_price, @gas_limit, endowment, bytecode
  tx.sign sender
  tx.startgas = gas if gas

  success, output = @block.apply_transaction tx
  raise ContractCreationFailed if success.false?

  output
end
head() click to toggle source
# File lib/ethereum/tester/state.rb, line 40
def head
  @blocks.last
end
mine(n=1, coinbase: @accounts[0]) click to toggle source
# File lib/ethereum/tester/state.rb, line 177
def mine(n=1, coinbase: @accounts[0])
  n.times do |i|
    @block.finalize
    @block.commit_state

    @db.put @block.full_hash, RLP.encode(@block)

    t = @block.timestamp + 6 + rand(12)
    x = Block.build_from_parent @block, coinbase, timestamp: t
    @block = x

    @blocks.push @block
  end
end
mkspv(sender, to, value, data: [], funid: nil, abi: nil) click to toggle source
# File lib/ethereum/tester/state.rb, line 139
def mkspv(sender, to, value, data: [], funid: nil, abi: nil)
  raise NotImplemented

  serpent = Language.get :serpent
  raise "ruby-serpent not installed" unless serpent

  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
  evmdata = funid ? serpent.encode_abi(funid, *abi) : serpent.encode_datalist(*data)

  tx = Transaction.new(sendnonce, @gas_price, @gas_limit, to, value, evmdata)
  @last_tx = tx
  tx.sign(sender)

  SPV.make_transaction_proof(@block, tx)
end
profile(*args, **kwargs) click to toggle source
# File lib/ethereum/tester/state.rb, line 134
def profile(*args, **kwargs)
  kwargs[:profiling] = true
  _send_tx(*args, **kwargs)
end
revert(data) click to toggle source
# File lib/ethereum/tester/state.rb, line 196
def revert(data)
  @block = RLP.decode data, sedes: Block, env: @env

  @block.make_mutable!
  @block._cached_rlp = nil

  @block.header.make_mutable!
  @block.header._cached_rlp = nil
end
send_tx(*args, **kwargs) click to toggle source
# File lib/ethereum/tester/state.rb, line 95
def send_tx(*args, **kwargs)
  _send_tx(*args, **kwargs)[:output]
end
snapshot() click to toggle source
# File lib/ethereum/tester/state.rb, line 192
def snapshot
  RLP.encode @block
end
trace(sender, to, value, data=[]) click to toggle source
# File lib/ethereum/tester/state.rb, line 171
def trace(sender, to, value, data=[])
  recorder = LogRecorder.new
  send_tx sender, to, value, data
  recorder.pop_records # TODO: implement recorder
end
verifyspv(sender, to, value, data: [], funid: nil, abi: nil, proof: []) click to toggle source
# File lib/ethereum/tester/state.rb, line 155
def verifyspv(sender, to, value, data: [], funid: nil, abi: nil, proof: [])
  raise NotImplemented

  serpent = Language.get(:serpent)
  raise "ruby-serpent not installed" unless serpent

  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
  evmdata = funid ? serpent.encode_abi(funid, *abi) : serpent.encode_datalist(*data)

  tx = Transaction.new(sendnonce, @gas_price, @gas_limit, to, value, evmdata)
  @last_tx = tx
  tx.sign(sender)

  SPV.verify_transaction_proof(@block, tx, proof)
end

Private Instance Methods

get_start_alloc() click to toggle source
# File lib/ethereum/tester/state.rb, line 208
def get_start_alloc
  o = {}
  (1...5).each {|i| o[Utils.int_to_addr(i)] = {wei: 1} }
  @accounts.each {|addr| o[addr] = {wei: 10**24} }
  o
end