ruby-ethereum 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,28 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class PruningTrie < Trie
|
5
|
+
# TODO: pruning trie implementation
|
6
|
+
|
7
|
+
def clear_all(node=nil)
|
8
|
+
if node.nil?
|
9
|
+
node = @root_node
|
10
|
+
delete_node_storage node
|
11
|
+
end
|
12
|
+
|
13
|
+
return if node == BLANK_NODE
|
14
|
+
|
15
|
+
node_type = get_node_type node
|
16
|
+
delete_node_storage node
|
17
|
+
|
18
|
+
if NODE_KV_TYPE.include?(node_type)
|
19
|
+
value_is_node = node_type == :extension
|
20
|
+
clear_all decode_to_node(node[1]) if value_is_node
|
21
|
+
elsif node_type == :branch
|
22
|
+
16.times do |i|
|
23
|
+
clear_all decode_to_node(node[i])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class PublicKey
|
5
|
+
|
6
|
+
attr :raw
|
7
|
+
|
8
|
+
def initialize(raw)
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode(fmt)
|
13
|
+
case fmt
|
14
|
+
when :decimal
|
15
|
+
value
|
16
|
+
when :bin
|
17
|
+
"\x04#{BaseConvert.encode(value[0], 256, 32)}#{BaseConvert.encode(value[1], 256, 32)}"
|
18
|
+
when :bin_compressed
|
19
|
+
"#{(2+(value[1]%2)).chr}#{BaseConvert.encode(value[0], 256, 32)}"
|
20
|
+
when :hex
|
21
|
+
"04#{BaseConvert.encode(value[0], 16, 64)}#{BaseConvert.encode(value[1], 16, 64)}"
|
22
|
+
when :hex_compressed
|
23
|
+
"0#{2+(value[1]%2)}#{BaseConvert.encode(value[0], 16, 64)}"
|
24
|
+
when :bin_electrum
|
25
|
+
"#{BaseConvert.encode(value[0], 256, 32)}#{BaseConvert.encode(value[1], 256, 32)}"
|
26
|
+
when :hex_electrum
|
27
|
+
"#{BaseConvert.encode(value[0], 16, 64)}#{BaseConvert.encode(value[1], 16, 64)}"
|
28
|
+
else
|
29
|
+
raise FormatError, "Invalid format!"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def decode(fmt=nil)
|
34
|
+
fmt ||= format
|
35
|
+
|
36
|
+
case fmt
|
37
|
+
when :decimal
|
38
|
+
raw
|
39
|
+
when :bin
|
40
|
+
[BaseConvert.decode(raw[1,32], 256), BaseConvert.decode(raw[33,32], 256)]
|
41
|
+
when :bin_compressed
|
42
|
+
x = BaseConvert.decode raw[1,32], 256
|
43
|
+
m = x*x*x + Secp256k1::A*x + Secp256k1::B
|
44
|
+
n = Utils.mod_exp(m, (Secp256k1::P+1)/4, Secp256k1::P)
|
45
|
+
q = (n + raw[0].ord) % 2
|
46
|
+
y = q == 1 ? (Secp256k1::P - n) : n
|
47
|
+
[x, y]
|
48
|
+
when :hex
|
49
|
+
[BaseConvert.decode(raw[2,64], 16), BaseConvert.decode(raw[66,64], 16)]
|
50
|
+
when :hex_compressed
|
51
|
+
PublicKey.new(Utils.decode_hex(raw)).decode :bin_compressed
|
52
|
+
when :bin_electrum
|
53
|
+
[BaseConvert.decode(raw[0,32], 256), BaseConvert.decode(raw[32,32], 256)]
|
54
|
+
when :hex_electrum
|
55
|
+
[BaseConvert.decode(raw[0,64], 16), BaseConvert.decode(raw[64,128], 16)]
|
56
|
+
else
|
57
|
+
raise FormatError, "Invalid format!"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def value
|
62
|
+
@value ||= decode
|
63
|
+
end
|
64
|
+
|
65
|
+
def format
|
66
|
+
return :decimal if raw.is_a?(Array)
|
67
|
+
return :bin if raw.size == 65 && raw[0] == "\x04"
|
68
|
+
return :hex if raw.size == 130 && raw[0, 2] == '04'
|
69
|
+
return :bin_compressed if raw.size == 33 && "\x02\x03".include?(raw[0])
|
70
|
+
return :hex_compressed if raw.size == 66 && %w(02 03).include?(raw[0,2])
|
71
|
+
return :bin_electrum if raw.size == 64
|
72
|
+
return :hex_electrum if raw.size == 128
|
73
|
+
|
74
|
+
raise FormatError, "Pubkey is not in recognized format"
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_bitcoin_address(magicbyte=0)
|
78
|
+
bytes = encode(:bin)
|
79
|
+
Utils.bytes_to_base58_check Utils.hash160(bytes), magicbyte
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_address(extended=false)
|
83
|
+
bytes = Utils.keccak256(encode(:bin)[1..-1])[-20..-1]
|
84
|
+
Address.new(bytes).to_bytes(extended)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class Receipt
|
5
|
+
include RLP::Sedes::Serializable
|
6
|
+
|
7
|
+
extend Sedes
|
8
|
+
|
9
|
+
set_serializable_fields(
|
10
|
+
state_root: trie_root,
|
11
|
+
gas_used: big_endian_int,
|
12
|
+
bloom: int256,
|
13
|
+
logs: RLP::Sedes::CountableList.new(Log)
|
14
|
+
)
|
15
|
+
|
16
|
+
# initialize(state_root, gas_used, logs, bloom: nil)
|
17
|
+
def initialize(*args)
|
18
|
+
h = normalize_args args
|
19
|
+
super(h)
|
20
|
+
raise ArgumentError, "Invalid bloom filter" if h[:bloom] && h[:bloom] != self.bloom
|
21
|
+
end
|
22
|
+
|
23
|
+
def bloom
|
24
|
+
bloomables = logs.map {|l| l.bloomables }
|
25
|
+
Bloom.from_array bloomables.flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def normalize_args(args)
|
31
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
32
|
+
field_set = %i(state_root gas_used logs bloom) # different order than serializable fields
|
33
|
+
|
34
|
+
h = {}
|
35
|
+
fields = field_set[0,args.size]
|
36
|
+
fields.zip(args).each do |(field, arg)|
|
37
|
+
h[field] = arg
|
38
|
+
field_set.delete field
|
39
|
+
end
|
40
|
+
|
41
|
+
options.each do |field, value|
|
42
|
+
if field_set.include?(field)
|
43
|
+
h[field] = value
|
44
|
+
field_set.delete field
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
field_set.each {|field| h[field] = nil }
|
49
|
+
h
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'secp256k1'
|
5
|
+
|
6
|
+
module Ethereum
|
7
|
+
module Secp256k1
|
8
|
+
|
9
|
+
# Elliptic curve parameters
|
10
|
+
P = 2**256 - 2**32 - 977
|
11
|
+
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
|
12
|
+
A = 0
|
13
|
+
B = 7
|
14
|
+
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
|
15
|
+
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
|
16
|
+
G = [Gx, Gy].freeze
|
17
|
+
|
18
|
+
class InvalidPrivateKey < StandardError; end
|
19
|
+
|
20
|
+
class <<self # extensions
|
21
|
+
def priv_to_pub(priv)
|
22
|
+
priv = PrivateKey.new(priv)
|
23
|
+
privkey = ::Secp256k1::PrivateKey.new privkey: priv.encode(:bin), raw: true
|
24
|
+
pubkey = privkey.pubkey
|
25
|
+
PublicKey.new(pubkey.serialize).encode(priv.format)
|
26
|
+
end
|
27
|
+
|
28
|
+
def recoverable_sign(msg, privkey)
|
29
|
+
pk = ::Secp256k1::PrivateKey.new privkey: privkey, raw: true
|
30
|
+
signature = pk.ecdsa_recoverable_serialize pk.ecdsa_sign_recoverable(msg, raw: true)
|
31
|
+
|
32
|
+
v = signature[1] + 27
|
33
|
+
r = Utils.big_endian_to_int signature[0][0,32]
|
34
|
+
s = Utils.big_endian_to_int signature[0][32,32]
|
35
|
+
|
36
|
+
[v,r,s]
|
37
|
+
end
|
38
|
+
|
39
|
+
def signature_verify(msg, vrs, pubkey)
|
40
|
+
pk = ::Secp256k1::PublicKey.new(pubkey: pubkey)
|
41
|
+
raw_sig = Utils.zpad_int(vrs[1]) + Utils.zpad_int(vrs[2])
|
42
|
+
|
43
|
+
sig = ::Secp256k1::C::ECDSASignature.new
|
44
|
+
sig[:data].to_ptr.write_bytes(raw_sig)
|
45
|
+
|
46
|
+
pk.ecdsa_verify(msg, sig)
|
47
|
+
end
|
48
|
+
|
49
|
+
def recover_pubkey(msg, vrs, compressed: false)
|
50
|
+
pk = ::Secp256k1::PublicKey.new(flags: ::Secp256k1::ALL_FLAGS)
|
51
|
+
sig = Utils.zpad_int(vrs[1]) + Utils.zpad_int(vrs[2])
|
52
|
+
recsig = pk.ecdsa_recoverable_deserialize(sig, vrs[0]-27)
|
53
|
+
pk.public_key = pk.ecdsa_recover msg, recsig, raw: true
|
54
|
+
pk.serialize compressed: compressed
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
class SecureTrie
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :@trie, :root_hash, :set_root_hash, :root_hash_valid?, :process_epoch, :commit_death_row, :revert_epoch, :has_key?, :include?, :size, :to_h, :db
|
10
|
+
|
11
|
+
def initialize(trie)
|
12
|
+
@trie = trie
|
13
|
+
@db = trie.db
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](k)
|
17
|
+
@trie[Utils.keccak256(k)]
|
18
|
+
end
|
19
|
+
alias :get :[]
|
20
|
+
|
21
|
+
def []=(k, v)
|
22
|
+
h = Utils.keccak256 k
|
23
|
+
@db.put h, k
|
24
|
+
@trie[h] = v
|
25
|
+
end
|
26
|
+
alias :set :[]=
|
27
|
+
|
28
|
+
def delete(k)
|
29
|
+
@trie.delete Utils.keccak256(k)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
o = {}
|
34
|
+
@trie.to_h.each do |h, v|
|
35
|
+
k = @db.get h
|
36
|
+
o[k] = v
|
37
|
+
end
|
38
|
+
o
|
39
|
+
end
|
40
|
+
|
41
|
+
def each(&block)
|
42
|
+
@trie.each do |h, v|
|
43
|
+
k = @db.get h
|
44
|
+
block.call k, v
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module Sedes
|
5
|
+
include RLP::Sedes
|
6
|
+
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def address
|
10
|
+
Binary.fixed_length(20, allow_empty: true)
|
11
|
+
end
|
12
|
+
|
13
|
+
def int20
|
14
|
+
BigEndianInt.new(20)
|
15
|
+
end
|
16
|
+
|
17
|
+
def int32
|
18
|
+
BigEndianInt.new(32)
|
19
|
+
end
|
20
|
+
|
21
|
+
def int256
|
22
|
+
BigEndianInt.new(256)
|
23
|
+
end
|
24
|
+
|
25
|
+
def hash32
|
26
|
+
Binary.fixed_length(32)
|
27
|
+
end
|
28
|
+
|
29
|
+
def trie_root
|
30
|
+
Binary.fixed_length(32, allow_empty: true)
|
31
|
+
end
|
32
|
+
|
33
|
+
def big_endian_int
|
34
|
+
RLP::Sedes.big_endian_int
|
35
|
+
end
|
36
|
+
|
37
|
+
def binary
|
38
|
+
RLP::Sedes.binary
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class SpecialContract
|
5
|
+
|
6
|
+
class ECRecover
|
7
|
+
def call(ext, msg)
|
8
|
+
gas_cost = Opcodes::GECRECOVER
|
9
|
+
return 0, 0, [] if msg.gas < gas_cost
|
10
|
+
|
11
|
+
message_hash_ints = [0] * 32
|
12
|
+
msg.data.extract_copy(message_hash_ints, 0, 0, 32)
|
13
|
+
message_hash = Utils.int_array_to_bytes message_hash_ints
|
14
|
+
|
15
|
+
v = msg.data.extract32(32)
|
16
|
+
r = msg.data.extract32(64)
|
17
|
+
s = msg.data.extract32(96)
|
18
|
+
|
19
|
+
if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28
|
20
|
+
return 1, msg.gas - gas_cost, []
|
21
|
+
end
|
22
|
+
|
23
|
+
signature_ints = [0] * 64
|
24
|
+
msg.data.extract_copy signature_ints, 0, 64, 32 # r
|
25
|
+
msg.data.extract_copy signature_ints, 32, 96, 32 # s
|
26
|
+
signature = Utils.int_array_to_bytes signature_ints
|
27
|
+
|
28
|
+
pub = nil
|
29
|
+
begin
|
30
|
+
pub = Secp256k1.recover_pubkey(message_hash, [v,r,s])
|
31
|
+
rescue
|
32
|
+
return 1, msg.gas - gas_cost, []
|
33
|
+
end
|
34
|
+
|
35
|
+
pubhash = Utils.keccak256(pub[1..-1])[-20..-1]
|
36
|
+
o = Utils.bytes_to_int_array Utils.zpad(pubhash, 32)
|
37
|
+
|
38
|
+
return 1, msg.gas - gas_cost, o
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class SHA256
|
43
|
+
def call(ext, msg)
|
44
|
+
gas_cost = Opcodes::GSHA256BASE +
|
45
|
+
(Utils.ceil32(msg.data.size) / 32) * Opcodes::GSHA256WORD
|
46
|
+
return 0, 0, [] if msg.gas < gas_cost
|
47
|
+
|
48
|
+
d = msg.data.extract_all
|
49
|
+
o = Utils.bytes_to_int_array Utils.sha256(d)
|
50
|
+
|
51
|
+
return 1, msg.gas - gas_cost, o
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class RIPEMD160
|
56
|
+
def call(ext, msg)
|
57
|
+
gas_cost = Opcodes::GRIPEMD160BASE +
|
58
|
+
(Utils.ceil32(msg.data.size) / 32) * Opcodes::GRIPEMD160WORD
|
59
|
+
return 0, 0, [] if msg.gas < gas_cost
|
60
|
+
|
61
|
+
d = msg.data.extract_all
|
62
|
+
o = Utils.bytes_to_int_array Utils.zpad(Utils.ripemd160(d), 32)
|
63
|
+
|
64
|
+
return 1, msg.gas - gas_cost, o
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Identity
|
69
|
+
def call(ext, msg)
|
70
|
+
gas_cost = Opcodes::GIDENTITYBASE +
|
71
|
+
(Utils.ceil32(msg.data.size) / 32) * Opcodes::GIDENTITYWORD
|
72
|
+
return 0, 0, [] if msg.gas < gas_cost
|
73
|
+
|
74
|
+
o = []
|
75
|
+
msg.data.extract_copy(o, 0, 0, msg.data.size)
|
76
|
+
|
77
|
+
return 1, msg.gas - gas_cost, o
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
DEPLOY = {
|
82
|
+
'0000000000000000000000000000000000000001' => ECRecover.new,
|
83
|
+
'0000000000000000000000000000000000000002' => SHA256.new,
|
84
|
+
'0000000000000000000000000000000000000003' => RIPEMD160.new,
|
85
|
+
'0000000000000000000000000000000000000004' => Identity.new
|
86
|
+
}.map {|k,v| [Utils.decode_hex(k), v] }.to_h.freeze
|
87
|
+
|
88
|
+
class <<self
|
89
|
+
def [](address)
|
90
|
+
DEPLOY[address]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
data/lib/ethereum/spv.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'ethereum/spv/proof'
|
4
|
+
require 'ethereum/spv/proof_constructor'
|
5
|
+
require 'ethereum/spv/proof_verifier'
|
6
|
+
|
7
|
+
module Ethereum
|
8
|
+
module SPV
|
9
|
+
|
10
|
+
class <<self
|
11
|
+
|
12
|
+
def proofs
|
13
|
+
@proofs ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def proof
|
17
|
+
proofs.last
|
18
|
+
end
|
19
|
+
|
20
|
+
def grabbing(node)
|
21
|
+
proof.grabbing node if proof
|
22
|
+
end
|
23
|
+
|
24
|
+
def store(node)
|
25
|
+
proof.store node if proof
|
26
|
+
end
|
27
|
+
|
28
|
+
def record
|
29
|
+
proofs.push ProofConstructor.new
|
30
|
+
result = yield
|
31
|
+
nodes = proof.decoded_nodes
|
32
|
+
[result, nodes]
|
33
|
+
ensure
|
34
|
+
proofs.pop
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify(nodes)
|
38
|
+
proofs.push ProofVerifier.new(nodes: nodes)
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
proofs.pop
|
42
|
+
end
|
43
|
+
|
44
|
+
def mode
|
45
|
+
case proof
|
46
|
+
when ProofConstructor
|
47
|
+
:record
|
48
|
+
when ProofVerifier
|
49
|
+
:verify
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def make_transaction_proof(block, tx)
|
56
|
+
result, nodes = record do
|
57
|
+
block.apply_transaction(tx)
|
58
|
+
end
|
59
|
+
|
60
|
+
nodes
|
61
|
+
.map {|x| RLP.encode(x) }
|
62
|
+
.uniq
|
63
|
+
.map {|x| RLP.decode(x) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def verify_transaction_proof(block, tx, nodes)
|
67
|
+
verify do
|
68
|
+
block.apply_transaction(tx)
|
69
|
+
end
|
70
|
+
true
|
71
|
+
rescue
|
72
|
+
puts $!
|
73
|
+
false
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|