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,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
|