Class: Ethereum::Tester::State

Inherits:
Object
  • Object
show all
Defined in:
lib/ethereum/tester/state.rb

Constant Summary collapse

TMP_DIR_PREFIX =
'eth-tester-'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(num_accounts = Fixture::NUM_ACCOUNTS) ⇒ State

Returns a new instance of State.



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/ethereum/tester/state.rb', line 13

def initialize(num_accounts=Fixture::NUM_ACCOUNTS)
  @temp_data_dir = Dir.mktmpdir TMP_DIR_PREFIX

  @db = DB::EphemDB.new
  @env = Env.new @db

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

  @blocks = [@block]
  @last_tx = nil

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

Instance Attribute Details

#blockObject (readonly)

Returns the value of attribute block.



11
12
13
# File 'lib/ethereum/tester/state.rb', line 11

def block
  @block
end

Instance Method Details

#_send_tx(sender, to, value, evmdata: '', output: nil, funid: nil, abi: nil, profiling: 0) ⇒ Object

Raises:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/ethereum/tester/state.rb', line 86

def _send_tx(sender, to, value, evmdata: '', output: nil, 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, Fixture.gas_price, Fixture.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, **kwargs) ⇒ Object

Raises:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/ethereum/tester/state.rb', line 38

def abi_contract(code, **kwargs)
  sender        = kwargs.delete(:sender) || Fixture.keys[0]
  endowment     = kwargs.delete(:endowment) || 0
  language      = kwargs.delete(:language) || :serpent
  contract_name = kwargs.delete(:contract_name) || ''
  gas           = kwargs.delete(:gas) || nil
  log_listener  = kwargs.delete(:log_listener) || nil
  listen        = kwargs.delete(:listen) || true

  code = Language.format_spaces code

  if contract_name.true?
    raise ArgumentError, "language must be solidity when contract name is given" unless language == :solidity
    cn_args = {contract_name: contract_name}
  else
    cn_args = kwargs
  end

  lang = Language.get language
  opcodes = lang.compile code, **cn_args
  addr = evm(opcodes, sender: sender, endowment: endowment, gas: gas)
  raise AssertError, "Contract code empty" if @block.get_code(addr).empty?

  abi = lang.mk_full_signature(code, **cn_args)
  ABIContract.new(self, abi, addr, listen: listen, log_listener: log_listener)
end

#call(*args, **kwargs) ⇒ Object

Raises:



78
79
80
# File 'lib/ethereum/tester/state.rb', line 78

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: , endowment: 0, language: :serpent, gas: nil) ⇒ Object

Raises:



30
31
32
33
34
35
36
# File 'lib/ethereum/tester/state.rb', line 30

def contract(code, sender: Fixture.keys[0], endowment: 0, language: :serpent, gas: nil)
  code = Language.format_spaces code
  opcodes = Language.get(language).compile(code)
  addr = evm(opcodes, sender: sender, endowment: endowment)
  raise AssertError, "Contract code empty" if @block.get_code(addr).empty?
  addr
end

#evm(opcodes, sender: , endowment: 0, gas: nil) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ethereum/tester/state.rb', line 65

def evm(opcodes, sender: Fixture.keys[0], endowment: 0, gas: nil)
  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address

  tx = Transaction.contract sendnonce, Fixture.gas_price, Fixture.gas_limit, endowment, opcodes
  tx.sign sender
  tx.startgas = gas if gas

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

  output
end

#mine(n = 1, coinbase: ) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/ethereum/tester/state.rb', line 154

def mine(n=1, coinbase: Fixture.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) ⇒ Object



126
127
128
129
130
131
132
133
134
135
# File 'lib/ethereum/tester/state.rb', line 126

def mkspv(sender, to, value, data: [], funid: nil, abi: nil)
  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
  evmdata = funid ? Serpent.encode_abi(funid, *abi) : Serpent.encode_datalist(*data)

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

  SPV.make_transaction_proof(@block, tx)
end

#profile(*args, **kwargs) ⇒ Object



121
122
123
124
# File 'lib/ethereum/tester/state.rb', line 121

def profile(*args, **kwargs)
  kwargs[:profiling] = true
  _send_tx(*args, **kwargs)
end

#revert(data) ⇒ Object



173
174
175
176
177
178
179
180
181
# File 'lib/ethereum/tester/state.rb', line 173

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) ⇒ Object



82
83
84
# File 'lib/ethereum/tester/state.rb', line 82

def send_tx(*args, **kwargs)
  _send_tx(*args, **kwargs)[:output]
end

#snapshotObject



169
170
171
# File 'lib/ethereum/tester/state.rb', line 169

def snapshot
  RLP.encode @block
end

#trace(sender, to, value, data = []) ⇒ Object



148
149
150
151
152
# File 'lib/ethereum/tester/state.rb', line 148

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: []) ⇒ Object



137
138
139
140
141
142
143
144
145
146
# File 'lib/ethereum/tester/state.rb', line 137

def verifyspv(sender, to, value, data: [], funid: nil, abi: nil, proof: [])
  sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
  evmdata = funid ? Serpent.encode_abi(funid, *abi) : Serpent.encode_datalist(*data)

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

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