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