ruby-ethereum 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/lib/ethereum.rb +53 -0
- data/lib/ethereum/abi.rb +333 -0
- data/lib/ethereum/abi/contract_translator.rb +174 -0
- data/lib/ethereum/abi/type.rb +117 -0
- data/lib/ethereum/account.rb +72 -0
- data/lib/ethereum/address.rb +60 -0
- data/lib/ethereum/base_convert.rb +53 -0
- data/lib/ethereum/block.rb +1311 -0
- data/lib/ethereum/block_header.rb +211 -0
- data/lib/ethereum/bloom.rb +83 -0
- data/lib/ethereum/cached_block.rb +36 -0
- data/lib/ethereum/chain.rb +400 -0
- data/lib/ethereum/constant.rb +26 -0
- data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
- data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
- data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
- data/lib/ethereum/core_ext/object/truth.rb +47 -0
- data/lib/ethereum/db.rb +6 -0
- data/lib/ethereum/db/base_db.rb +9 -0
- data/lib/ethereum/db/ephem_db.rb +63 -0
- data/lib/ethereum/db/overlay_db.rb +72 -0
- data/lib/ethereum/db/refcount_db.rb +188 -0
- data/lib/ethereum/env.rb +64 -0
- data/lib/ethereum/ethash.rb +78 -0
- data/lib/ethereum/ethash_ruby.rb +38 -0
- data/lib/ethereum/ethash_ruby/cache.rb +47 -0
- data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
- data/lib/ethereum/ethash_ruby/utils.rb +53 -0
- data/lib/ethereum/exceptions.rb +28 -0
- data/lib/ethereum/external_call.rb +173 -0
- data/lib/ethereum/fast_rlp.rb +81 -0
- data/lib/ethereum/fast_vm.rb +625 -0
- data/lib/ethereum/fast_vm/call_data.rb +44 -0
- data/lib/ethereum/fast_vm/message.rb +29 -0
- data/lib/ethereum/fast_vm/state.rb +25 -0
- data/lib/ethereum/ffi/openssl.rb +390 -0
- data/lib/ethereum/index.rb +97 -0
- data/lib/ethereum/log.rb +43 -0
- data/lib/ethereum/miner.rb +84 -0
- data/lib/ethereum/opcodes.rb +131 -0
- data/lib/ethereum/private_key.rb +92 -0
- data/lib/ethereum/pruning_trie.rb +28 -0
- data/lib/ethereum/public_key.rb +88 -0
- data/lib/ethereum/receipt.rb +53 -0
- data/lib/ethereum/secp256k1.rb +58 -0
- data/lib/ethereum/secure_trie.rb +49 -0
- data/lib/ethereum/sedes.rb +42 -0
- data/lib/ethereum/special_contract.rb +95 -0
- data/lib/ethereum/spv.rb +79 -0
- data/lib/ethereum/spv/proof.rb +31 -0
- data/lib/ethereum/spv/proof_constructor.rb +19 -0
- data/lib/ethereum/spv/proof_verifier.rb +24 -0
- data/lib/ethereum/tester.rb +14 -0
- data/lib/ethereum/tester/abi_contract.rb +65 -0
- data/lib/ethereum/tester/fixture.rb +31 -0
- data/lib/ethereum/tester/language.rb +30 -0
- data/lib/ethereum/tester/log_recorder.rb +13 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
- data/lib/ethereum/tester/state.rb +194 -0
- data/lib/ethereum/transaction.rb +196 -0
- data/lib/ethereum/transient_trie.rb +28 -0
- data/lib/ethereum/trie.rb +549 -0
- data/lib/ethereum/trie/nibble_key.rb +184 -0
- data/lib/ethereum/utils.rb +191 -0
- data/lib/ethereum/version.rb +5 -0
- data/lib/ethereum/vm.rb +606 -0
- data/lib/ethereum/vm/call_data.rb +40 -0
- data/lib/ethereum/vm/message.rb +30 -0
- data/lib/ethereum/vm/state.rb +25 -0
- metadata +284 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
module SPV
|
7
|
+
class Proof
|
8
|
+
|
9
|
+
attr :nodes, :exempts
|
10
|
+
|
11
|
+
def initialize(nodes: Set.new, exempts: [])
|
12
|
+
@nodes = nodes
|
13
|
+
@exempts = exempts
|
14
|
+
end
|
15
|
+
|
16
|
+
def decoded_nodes
|
17
|
+
nodes.map {|n| RLP.decode(n) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_node(node)
|
21
|
+
node = FastRLP.encode node
|
22
|
+
nodes.add(node) unless exempts.include?(node)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_exempt(node)
|
26
|
+
exempts.add FastRLP.encode(node)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
module SPV
|
7
|
+
class ProofVerifier < Proof
|
8
|
+
|
9
|
+
def initialize(nodes, exempts: [])
|
10
|
+
nodes = nodes.map {|n| RLP.encode(n) }.to_set
|
11
|
+
super(nodes: nodes, exempts: exempts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def grabbing(node)
|
15
|
+
raise InvalidSPVProof unless nodes.include?(FastRLP.encode(node))
|
16
|
+
end
|
17
|
+
|
18
|
+
def store(node)
|
19
|
+
add_node node.dup
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'ethereum/tester/abi_contract'
|
4
|
+
require 'ethereum/tester/fixture'
|
5
|
+
require 'ethereum/tester/solidity_wrapper'
|
6
|
+
require 'ethereum/tester/language'
|
7
|
+
require 'ethereum/tester/log_recorder'
|
8
|
+
require 'ethereum/tester/state'
|
9
|
+
|
10
|
+
module Ethereum
|
11
|
+
module Tester
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module Tester
|
5
|
+
class ABIContract
|
6
|
+
|
7
|
+
attr :address, :abi
|
8
|
+
|
9
|
+
def initialize(state, abi, address, listen: true, log_listener: nil)
|
10
|
+
@state = state
|
11
|
+
@abi = abi
|
12
|
+
@address = address
|
13
|
+
|
14
|
+
@translator = ABI::ContractTranslator.new abi
|
15
|
+
|
16
|
+
if listen
|
17
|
+
if log_listener
|
18
|
+
listener = lambda do |log|
|
19
|
+
r = @translator.listen(log, noprint: true)
|
20
|
+
log_listener.call r if r.true?
|
21
|
+
end
|
22
|
+
else
|
23
|
+
listener = ->(log) { @translator.listen(log, noprint: false) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@translator.function_data.each do |f, _|
|
28
|
+
generate_function f
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def listen(x)
|
33
|
+
@translator.listen x
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def generate_function(f)
|
39
|
+
singleton_class.class_eval <<-EOF
|
40
|
+
def #{f}(*args, **kwargs)
|
41
|
+
sender = kwargs.delete(:sender) || Fixture.keys[0]
|
42
|
+
to = @address
|
43
|
+
value = kwargs.delete(:value) || 0
|
44
|
+
evmdata = @translator.encode('#{f}', args)
|
45
|
+
output = kwargs.delete(:output)
|
46
|
+
|
47
|
+
o = @state._send_tx(sender, to, value, **kwargs.merge(evmdata: evmdata))
|
48
|
+
|
49
|
+
if output == :raw
|
50
|
+
outdata = o[:output]
|
51
|
+
elsif o[:output].false?
|
52
|
+
outdata = nil
|
53
|
+
else
|
54
|
+
outdata = @translator.decode '#{f}', o[:output]
|
55
|
+
outdata = outdata.size == 1 ? outdata[0] : outdata
|
56
|
+
end
|
57
|
+
|
58
|
+
kwargs[:profiling].true? ? o.merge(outdata: outdata) : outdata
|
59
|
+
end
|
60
|
+
EOF
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module Tester
|
5
|
+
module Fixture
|
6
|
+
|
7
|
+
NUM_ACCOUNTS = 10
|
8
|
+
|
9
|
+
class <<self
|
10
|
+
def keys
|
11
|
+
@keys ||= NUM_ACCOUNTS.times.map {|i| Utils.keccak256(i.to_s) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def accounts
|
15
|
+
@accounts ||= NUM_ACCOUNTS.times.map {|i| PrivateKey.new(keys[i]).to_address }
|
16
|
+
end
|
17
|
+
|
18
|
+
def gas_limit
|
19
|
+
@gas_limit ||= 3141592
|
20
|
+
end
|
21
|
+
attr_writer :gas_limit
|
22
|
+
|
23
|
+
def gas_price
|
24
|
+
@gas_price ||= 1
|
25
|
+
end
|
26
|
+
attr_writer :gas_price
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module Tester
|
5
|
+
module Language
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
def all
|
9
|
+
return @all if @all
|
10
|
+
|
11
|
+
@all = {
|
12
|
+
serpent: Serpent,
|
13
|
+
solidity: SolidityWrapper.solc_path && SolidityWrapper
|
14
|
+
}
|
15
|
+
|
16
|
+
@all
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(name)
|
20
|
+
all[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_spaces(code)
|
24
|
+
code =~ /\A(\s+)/ ? code.gsub(/^#{$1}/, '') : code
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module Tester
|
5
|
+
|
6
|
+
module SolidityWrapper
|
7
|
+
|
8
|
+
class CompileError < StandardError; end
|
9
|
+
|
10
|
+
class <<self
|
11
|
+
def split_contracts(code)
|
12
|
+
contracts = []
|
13
|
+
contract = nil
|
14
|
+
|
15
|
+
code.split("\n").each do |line|
|
16
|
+
if line =~ /\Acontract /
|
17
|
+
contracts.push(contract.join("\n")) if contract
|
18
|
+
contract = [line]
|
19
|
+
elsif contract
|
20
|
+
contract.push line
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
contracts.push(contract.join("\n")) if contract
|
25
|
+
|
26
|
+
contracts
|
27
|
+
end
|
28
|
+
|
29
|
+
def contract_names(code)
|
30
|
+
names = []
|
31
|
+
|
32
|
+
split_contracts(code).each do |contract|
|
33
|
+
keyword, name, _ = contract.split(/\s+/, 3)
|
34
|
+
raise AssertError, 'keyword must be contract' unless keyword == 'contract' && !name.empty?
|
35
|
+
names.push name
|
36
|
+
end
|
37
|
+
|
38
|
+
names
|
39
|
+
end
|
40
|
+
|
41
|
+
def combined(code)
|
42
|
+
out = Tempfile.new 'solc_output_'
|
43
|
+
|
44
|
+
pipe = IO.popen([solc_path, '--add-std', '--optimize', '--combined-json', 'abi,bin,devdoc,userdoc'], 'w', [:out, :err] => out)
|
45
|
+
pipe.write code
|
46
|
+
pipe.close_write
|
47
|
+
raise CompileError, 'compilation failed' unless $?.success?
|
48
|
+
|
49
|
+
out.rewind
|
50
|
+
contracts = JSON.parse(out.read)['contracts']
|
51
|
+
|
52
|
+
contracts.each do |name, data|
|
53
|
+
data['abi'] = JSON.parse data['abi']
|
54
|
+
data['devdoc'] = JSON.parse data['devdoc']
|
55
|
+
data['userdoc'] = JSON.parse data['userdoc']
|
56
|
+
end
|
57
|
+
|
58
|
+
names = contract_names code
|
59
|
+
raise AssertError unless names.size <= contracts.size
|
60
|
+
|
61
|
+
names.map {|n| [n, contracts[n]] }
|
62
|
+
ensure
|
63
|
+
out.close
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Returns binary of last contract in code.
|
68
|
+
#
|
69
|
+
def compile(code, contract_name='')
|
70
|
+
sorted_contracts = combined code
|
71
|
+
if contract_name.true?
|
72
|
+
idx = sorted_contracts.map(&:first).index(contract_name)
|
73
|
+
else
|
74
|
+
idx = -1
|
75
|
+
end
|
76
|
+
|
77
|
+
Utils.decode_hex sorted_contracts[idx][1]['bin']
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Returns signature of last contract in code.
|
82
|
+
#
|
83
|
+
def mk_full_signature(code, contract_name='')
|
84
|
+
sorted_contracts = combined code
|
85
|
+
if contract_name.true?
|
86
|
+
idx = sorted_contracts.map(&:first).index(contract_name)
|
87
|
+
else
|
88
|
+
idx = -1
|
89
|
+
end
|
90
|
+
|
91
|
+
sorted_contracts[idx][1]['abi']
|
92
|
+
end
|
93
|
+
|
94
|
+
def compiler_version
|
95
|
+
output = `#{solc_path} --version`.strip
|
96
|
+
output =~ /^Version: ([0-9a-z.-]+)\// ? $1 : nil
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Full format as returned by jsonrpc.
|
101
|
+
#
|
102
|
+
def compile_rich(code)
|
103
|
+
combined(code).map do |(name, contract)|
|
104
|
+
[
|
105
|
+
name,
|
106
|
+
{
|
107
|
+
'code' => "0x#{contract['bin']}",
|
108
|
+
'info' => {
|
109
|
+
'abiDefinition' => contract['abi'],
|
110
|
+
'compilerVersion' => compiler_version,
|
111
|
+
'developerDoc' => contract['devdoc'],
|
112
|
+
'language' => 'Solidity',
|
113
|
+
'languageVersion' => '0',
|
114
|
+
'source' => code,
|
115
|
+
'userDoc' => contract['userdoc']
|
116
|
+
}
|
117
|
+
}
|
118
|
+
]
|
119
|
+
end.to_h
|
120
|
+
end
|
121
|
+
|
122
|
+
def solc_path
|
123
|
+
@solc_path ||= which('solc')
|
124
|
+
end
|
125
|
+
|
126
|
+
def solc_path=(v)
|
127
|
+
@solc_path = v
|
128
|
+
end
|
129
|
+
|
130
|
+
def which(cmd)
|
131
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
132
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
133
|
+
exts.each { |ext|
|
134
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
135
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
136
|
+
}
|
137
|
+
end
|
138
|
+
return nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
module Tester
|
7
|
+
class State
|
8
|
+
|
9
|
+
TMP_DIR_PREFIX = 'eth-tester-'.freeze
|
10
|
+
|
11
|
+
attr :block, :blocks
|
12
|
+
|
13
|
+
def initialize(num_accounts=Fixture::NUM_ACCOUNTS)
|
14
|
+
@temp_data_dir = Dir.mktmpdir TMP_DIR_PREFIX
|
15
|
+
|
16
|
+
@db = DB::EphemDB.new
|
17
|
+
@env = Env.new @db
|
18
|
+
|
19
|
+
@block = Block.genesis @env, start_alloc: get_start_alloc(num_accounts)
|
20
|
+
@block.timestamp = 1410973349
|
21
|
+
@block.coinbase = Fixture.accounts[0]
|
22
|
+
@block.gas_limit = 10**9
|
23
|
+
|
24
|
+
@blocks = [@block]
|
25
|
+
@last_tx = nil
|
26
|
+
|
27
|
+
ObjectSpace.define_finalizer(self) {|id| FileUtils.rm_rf(@temp_data_dir) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def contract(code, sender: Fixture.keys[0], endowment: 0, language: :serpent, gas: nil)
|
31
|
+
code = Language.format_spaces code
|
32
|
+
opcodes = Language.get(language).compile(code)
|
33
|
+
addr = evm(opcodes, sender: sender, endowment: endowment)
|
34
|
+
raise AssertError, "Contract code empty" if @block.get_code(addr).empty?
|
35
|
+
addr
|
36
|
+
end
|
37
|
+
|
38
|
+
def abi_contract(code, **kwargs)
|
39
|
+
sender = kwargs.delete(:sender) || Fixture.keys[0]
|
40
|
+
endowment = kwargs.delete(:endowment) || 0
|
41
|
+
language = kwargs.delete(:language) || :serpent
|
42
|
+
contract_name = kwargs.delete(:contract_name) || ''
|
43
|
+
gas = kwargs.delete(:gas) || nil
|
44
|
+
log_listener = kwargs.delete(:log_listener) || nil
|
45
|
+
listen = kwargs.delete(:listen) || true
|
46
|
+
|
47
|
+
code = Language.format_spaces code
|
48
|
+
|
49
|
+
if contract_name.true?
|
50
|
+
raise ArgumentError, "language must be solidity when contract name is given" unless language == :solidity
|
51
|
+
cn_args = {contract_name: contract_name}
|
52
|
+
else
|
53
|
+
cn_args = kwargs
|
54
|
+
end
|
55
|
+
|
56
|
+
lang = Language.get language
|
57
|
+
opcodes = lang.compile code, **cn_args
|
58
|
+
addr = evm(opcodes, sender: sender, endowment: endowment, gas: gas)
|
59
|
+
raise AssertError, "Contract code empty" if @block.get_code(addr).empty?
|
60
|
+
|
61
|
+
abi = lang.mk_full_signature(code, **cn_args)
|
62
|
+
ABIContract.new(self, abi, addr, listen: listen, log_listener: log_listener)
|
63
|
+
end
|
64
|
+
|
65
|
+
def evm(opcodes, sender: Fixture.keys[0], endowment: 0, gas: nil)
|
66
|
+
sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
|
67
|
+
|
68
|
+
tx = Transaction.contract sendnonce, Fixture.gas_price, Fixture.gas_limit, endowment, opcodes
|
69
|
+
tx.sign sender
|
70
|
+
tx.startgas = gas if gas
|
71
|
+
|
72
|
+
success, output = @block.apply_transaction tx
|
73
|
+
raise ContractCreationFailed if success.false?
|
74
|
+
|
75
|
+
output
|
76
|
+
end
|
77
|
+
|
78
|
+
def call(*args, **kwargs)
|
79
|
+
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."
|
80
|
+
end
|
81
|
+
|
82
|
+
def send_tx(*args, **kwargs)
|
83
|
+
_send_tx(*args, **kwargs)[:output]
|
84
|
+
end
|
85
|
+
|
86
|
+
def _send_tx(sender, to, value, evmdata: '', output: nil, funid: nil, abi: nil, profiling: 0)
|
87
|
+
if funid || abi
|
88
|
+
raise ArgumentError, "Send with funid+abi is deprecated. Please use the abi_contract mechanism."
|
89
|
+
end
|
90
|
+
|
91
|
+
t1, g1 = Time.now, @block.gas_used
|
92
|
+
sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
|
93
|
+
tx = Transaction.new(sendnonce, Fixture.gas_price, Fixture.gas_limit, to, value, evmdata)
|
94
|
+
@last_tx = tx
|
95
|
+
tx.sign(sender)
|
96
|
+
|
97
|
+
recorder = profiling > 1 ? LogRecorder.new : nil
|
98
|
+
|
99
|
+
success, output = @block.apply_transaction(tx)
|
100
|
+
raise TransactionFailed if success.false?
|
101
|
+
out = {output: output}
|
102
|
+
|
103
|
+
if profiling > 0
|
104
|
+
zero_bytes = tx.data.count Constant::BYTE_ZERO
|
105
|
+
none_zero_bytes = tx.data.size - zero_bytes
|
106
|
+
intrinsic_gas_used = Opcodes::GTXCOST +
|
107
|
+
Opcodes::GTXDATAZERO * zero_bytes +
|
108
|
+
Opcodes::GTXDATANONZERO * none_zero_bytes
|
109
|
+
t2, g2 = Time.now, @block.gas_used
|
110
|
+
output[:time] = t2 - t1
|
111
|
+
output[:gas] = g2 - g1 - intrinsic_gas_used
|
112
|
+
end
|
113
|
+
|
114
|
+
if profiling > 1
|
115
|
+
# TODO: collect all traced ops use LogRecorder
|
116
|
+
end
|
117
|
+
|
118
|
+
out
|
119
|
+
end
|
120
|
+
|
121
|
+
def profile(*args, **kwargs)
|
122
|
+
kwargs[:profiling] = true
|
123
|
+
_send_tx(*args, **kwargs)
|
124
|
+
end
|
125
|
+
|
126
|
+
def mkspv(sender, to, value, data: [], funid: nil, abi: nil)
|
127
|
+
sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
|
128
|
+
evmdata = funid ? Serpent.encode_abi(funid, *abi) : Serpent.encode_datalist(*data)
|
129
|
+
|
130
|
+
tx = Transaction.new(sendnonce, Fixture.gas_price, Fixture.gas_limit, to, value, evmdata)
|
131
|
+
@last_tx = tx
|
132
|
+
tx.sign(sender)
|
133
|
+
|
134
|
+
SPV.make_transaction_proof(@block, tx)
|
135
|
+
end
|
136
|
+
|
137
|
+
def verifyspv(sender, to, value, data: [], funid: nil, abi: nil, proof: [])
|
138
|
+
sendnonce = @block.get_nonce PrivateKey.new(sender).to_address
|
139
|
+
evmdata = funid ? Serpent.encode_abi(funid, *abi) : Serpent.encode_datalist(*data)
|
140
|
+
|
141
|
+
tx = Transaction.new(sendnonce, Fixture.gas_price, Fixture.gas_limit, to, value, evmdata)
|
142
|
+
@last_tx = tx
|
143
|
+
tx.sign(sender)
|
144
|
+
|
145
|
+
SPV.verify_transaction_proof(@block, tx, proof)
|
146
|
+
end
|
147
|
+
|
148
|
+
def trace(sender, to, value, data=[])
|
149
|
+
recorder = LogRecorder.new
|
150
|
+
send_tx sender, to, value, data
|
151
|
+
recorder.pop_records # TODO: implement recorder
|
152
|
+
end
|
153
|
+
|
154
|
+
def mine(n=1, coinbase: Fixture.accounts[0])
|
155
|
+
n.times do |i|
|
156
|
+
@block.finalize
|
157
|
+
@block.commit_state
|
158
|
+
|
159
|
+
@db.put @block.full_hash, RLP.encode(@block)
|
160
|
+
|
161
|
+
t = @block.timestamp + 6 + rand(12)
|
162
|
+
x = Block.build_from_parent @block, coinbase, timestamp: t
|
163
|
+
@block = x
|
164
|
+
|
165
|
+
@blocks.push @block
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def snapshot
|
170
|
+
RLP.encode @block
|
171
|
+
end
|
172
|
+
|
173
|
+
def revert(data)
|
174
|
+
@block = RLP.decode data, sedes: Block, env: @env
|
175
|
+
|
176
|
+
@block.make_mutable!
|
177
|
+
@block._cached_rlp = nil
|
178
|
+
|
179
|
+
@block.header.make_mutable!
|
180
|
+
@block.header._cached_rlp = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
private
|
184
|
+
|
185
|
+
def get_start_alloc(num_accounts)
|
186
|
+
o = {}
|
187
|
+
num_accounts.times {|i| o[Fixture.accounts[i]] = {wei: 10**24} }
|
188
|
+
(1...5).each {|i| o[Utils.int_to_addr(i)] = {wei: 1} }
|
189
|
+
o
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|