bitcoin-ruby 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/COPYING +1 -1
- data/Gemfile.lock +9 -10
- data/README.rdoc +1 -1
- data/Rakefile +4 -2
- data/lib/bitcoin.rb +4 -2
- data/lib/bitcoin/bloom_filter.rb +125 -0
- data/lib/bitcoin/builder.rb +34 -9
- data/lib/bitcoin/ext_key.rb +191 -0
- data/lib/bitcoin/ffi/openssl.rb +1 -1
- data/lib/bitcoin/key.rb +6 -4
- data/lib/bitcoin/protocol.rb +13 -11
- data/lib/bitcoin/protocol/block.rb +38 -2
- data/lib/bitcoin/protocol/parser.rb +8 -0
- data/lib/bitcoin/protocol/partial_merkle_tree.rb +61 -0
- data/lib/bitcoin/protocol/script_witness.rb +31 -0
- data/lib/bitcoin/protocol/tx.rb +170 -10
- data/lib/bitcoin/protocol/txin.rb +8 -0
- data/lib/bitcoin/protocol/version.rb +2 -1
- data/lib/bitcoin/script.rb +58 -8
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bloom_filter_spec.rb +23 -0
- data/spec/bitcoin/builder_spec.rb +12 -0
- data/spec/bitcoin/ext_key_spec.rb +180 -0
- data/spec/bitcoin/fixtures/filteredblock-0.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-1151351.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-p2wpkh.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-p2wpkh.json +67 -0
- data/spec/bitcoin/fixtures/tx-0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3.json +139 -0
- data/spec/bitcoin/fixtures/tx-28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f.json +34 -0
- data/spec/bitcoin/protocol/bip143_spec.rb +116 -0
- data/spec/bitcoin/protocol/block_spec.rb +27 -0
- data/spec/bitcoin/protocol/partial_merkle_tree_spec.rb +38 -0
- data/spec/bitcoin/protocol/tx_spec.rb +134 -1
- data/spec/bitcoin/script/script_spec.rb +53 -2
- metadata +27 -3
data/lib/bitcoin/ffi/openssl.rb
CHANGED
data/lib/bitcoin/key.rb
CHANGED
@@ -69,13 +69,15 @@ module Bitcoin
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def pub_compressed
|
72
|
-
@key.public_key
|
73
|
-
|
72
|
+
public_key = @key.public_key
|
73
|
+
public_key.group.point_conversion_form = :compressed
|
74
|
+
public_key.to_hex.rjust(66, '0')
|
74
75
|
end
|
75
76
|
|
76
77
|
def pub_uncompressed
|
77
|
-
@key.public_key
|
78
|
-
|
78
|
+
public_key = @key.public_key
|
79
|
+
public_key.group.point_conversion_form = :uncompressed
|
80
|
+
public_key.to_hex.rjust(130, '0')
|
79
81
|
end
|
80
82
|
|
81
83
|
def compressed
|
data/lib/bitcoin/protocol.rb
CHANGED
@@ -13,15 +13,17 @@ module Bitcoin
|
|
13
13
|
# BIP 0031, pong message, is enabled for all versions AFTER this one
|
14
14
|
BIP0031_VERSION = 60000
|
15
15
|
|
16
|
-
autoload :
|
17
|
-
autoload :
|
18
|
-
autoload :
|
19
|
-
autoload :
|
20
|
-
autoload :
|
21
|
-
autoload :
|
22
|
-
autoload :
|
23
|
-
autoload :
|
24
|
-
autoload :
|
16
|
+
autoload :ScriptWitness, 'bitcoin/protocol/script_witness'
|
17
|
+
autoload :TxIn, 'bitcoin/protocol/txin'
|
18
|
+
autoload :TxOut, 'bitcoin/protocol/txout'
|
19
|
+
autoload :Tx, 'bitcoin/protocol/tx'
|
20
|
+
autoload :Block, 'bitcoin/protocol/block'
|
21
|
+
autoload :Addr, 'bitcoin/protocol/address'
|
22
|
+
autoload :Alert, 'bitcoin/protocol/alert'
|
23
|
+
autoload :Reject, 'bitcoin/protocol/reject'
|
24
|
+
autoload :Version, 'bitcoin/protocol/version'
|
25
|
+
autoload :AuxPow, 'bitcoin/protocol/aux_pow'
|
26
|
+
autoload :PartialMerkleTree, 'bitcoin/protocol/partial_merkle_tree'
|
25
27
|
|
26
28
|
autoload :Handler, 'bitcoin/protocol/handler'
|
27
29
|
autoload :Parser, 'bitcoin/protocol/parser'
|
@@ -141,7 +143,7 @@ module Bitcoin
|
|
141
143
|
pkt("verack", "")
|
142
144
|
end
|
143
145
|
|
144
|
-
TypeLookup = Hash[:tx, 1, :block, 2, nil, 0]
|
146
|
+
TypeLookup = Hash[:tx, 1, :block, 2, :filtered_block, 3, nil, 0]
|
145
147
|
|
146
148
|
def self.getdata_pkt(type, hashes)
|
147
149
|
return if hashes.size > MAX_INV_SZ
|
@@ -158,7 +160,7 @@ module Bitcoin
|
|
158
160
|
DEFAULT_STOP_HASH = "00"*32
|
159
161
|
|
160
162
|
def self.locator_payload(version, locator_hashes, stop_hash)
|
161
|
-
|
163
|
+
[
|
162
164
|
[version].pack("V"),
|
163
165
|
pack_var_int(locator_hashes.size),
|
164
166
|
locator_hashes.map{|l| l.htb_reverse }.join,
|
@@ -42,6 +42,8 @@ module Bitcoin
|
|
42
42
|
# AuxPow linking the block to a merge-mined chain
|
43
43
|
attr_accessor :aux_pow
|
44
44
|
|
45
|
+
attr_reader :partial_merkle_tree
|
46
|
+
|
45
47
|
alias :transactions :tx
|
46
48
|
|
47
49
|
# compare to another block
|
@@ -82,6 +84,24 @@ module Bitcoin
|
|
82
84
|
|
83
85
|
return buf if buf.eof?
|
84
86
|
|
87
|
+
if header_only == :filtered
|
88
|
+
@tx_count = buf.read(4).unpack("V")[0]
|
89
|
+
|
90
|
+
nhashes = Protocol.unpack_var_int_from_io(buf)
|
91
|
+
hashes = []
|
92
|
+
nhashes.times do
|
93
|
+
hashes << buf.read(256 / 8)
|
94
|
+
end
|
95
|
+
|
96
|
+
nflags = Protocol.unpack_var_int_from_io(buf)
|
97
|
+
flags = buf.read(nflags)
|
98
|
+
|
99
|
+
@partial_merkle_tree = PartialMerkleTree.new(@tx_count, hashes, flags)
|
100
|
+
@partial_merkle_tree.set_value
|
101
|
+
|
102
|
+
return buf
|
103
|
+
end
|
104
|
+
|
85
105
|
tx_size = Protocol.unpack_var_int_from_io(buf)
|
86
106
|
@tx_count = tx_size
|
87
107
|
return buf if header_only
|
@@ -109,12 +129,28 @@ module Bitcoin
|
|
109
129
|
end
|
110
130
|
|
111
131
|
def recalc_mrkl_root
|
112
|
-
@mrkl_root =
|
132
|
+
@mrkl_root = if partial_merkle_tree
|
133
|
+
partial_merkle_tree.root.value.htb_reverse
|
134
|
+
else
|
135
|
+
Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
|
136
|
+
end
|
113
137
|
end
|
114
138
|
|
115
139
|
# verify mrkl tree
|
116
140
|
def verify_mrkl_root
|
117
|
-
|
141
|
+
if partial_merkle_tree
|
142
|
+
partial_merkle_tree.valid_tree?(@mrkl_root.reverse_hth)
|
143
|
+
else
|
144
|
+
@mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def tx_hashes
|
149
|
+
if partial_merkle_tree
|
150
|
+
partial_merkle_tree.tx_hashes
|
151
|
+
else
|
152
|
+
@tx.map(&:hash)
|
153
|
+
end
|
118
154
|
end
|
119
155
|
|
120
156
|
# get the block header info
|
@@ -56,6 +56,13 @@ module Bitcoin
|
|
56
56
|
@h.on_headers(headers)
|
57
57
|
end
|
58
58
|
|
59
|
+
def parse_mrkle_block(payload)
|
60
|
+
return unless @h.respond_to?(:on_mrkle_block)
|
61
|
+
b = Block.new
|
62
|
+
b.parse_data_from_io(payload, header_only= :filtered)
|
63
|
+
@h.on_mrkle_block(b)
|
64
|
+
end
|
65
|
+
|
59
66
|
def parse_getblocks(payload)
|
60
67
|
version, payload = payload.unpack('Va*')
|
61
68
|
count, payload = Protocol.unpack_var_int(payload)
|
@@ -86,6 +93,7 @@ module Bitcoin
|
|
86
93
|
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
|
87
94
|
when 'mempool'; handle_mempool_request(payload)
|
88
95
|
when 'notfound'; handle_notfound_reply(payload)
|
96
|
+
when 'merkleblock'; parse_mrkle_block(payload)
|
89
97
|
when 'reject'; handle_reject(payload)
|
90
98
|
else
|
91
99
|
parse_error :unknown_packet, [command, payload.hth]
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Bitcoin::Protocol::PartialMerkleTree
|
2
|
+
Node = Struct.new(:value, :left, :right, :width_idx)
|
3
|
+
|
4
|
+
BIT_MASK = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
|
5
|
+
|
6
|
+
def initialize(total_txs, hashes, flags)
|
7
|
+
@total_txs, @flags = total_txs, flags
|
8
|
+
@hashes = hashes.map{|h| h.reverse_hth }
|
9
|
+
@visit_idx = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def tx_hashes
|
13
|
+
@leaves.reject{|n| n.value.nil? }.map{|n| n.value }
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_tree
|
17
|
+
lay = @leaves = @total_txs.times.map{ Node.new(nil, nil, nil) }
|
18
|
+
while lay.size > 1
|
19
|
+
lay = lay.each_slice(2).map do |left, right|
|
20
|
+
Node.new(nil, left, right)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return lay[0]
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_flag
|
27
|
+
@flags[@visit_idx / 8].ord & BIT_MASK[@visit_idx % 8] == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def root
|
31
|
+
@root ||= build_tree
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_value(node = root)
|
35
|
+
if current_flag || (node.left.nil? && node.right.nil?)
|
36
|
+
node.value = @hashes.shift
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
if node.left
|
41
|
+
@visit_idx += 1
|
42
|
+
set_value(node.left)
|
43
|
+
end
|
44
|
+
if node.right
|
45
|
+
@visit_idx += 1
|
46
|
+
set_value(node.right)
|
47
|
+
end
|
48
|
+
|
49
|
+
right = node.right || node.left
|
50
|
+
node.value = Bitcoin.bitcoin_mrkl(node.left.value, right.value)
|
51
|
+
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_tree?(mrkl_root_hash)
|
56
|
+
return false unless @hashes.empty?
|
57
|
+
return false if ((@visit_idx + 1)/8.0).ceil != @flags.length
|
58
|
+
return false if mrkl_root_hash != root.value
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
|
5
|
+
module Protocol
|
6
|
+
|
7
|
+
class ScriptWitness
|
8
|
+
|
9
|
+
# witness stack
|
10
|
+
attr_reader :stack
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@stack = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# check empty
|
17
|
+
def empty?
|
18
|
+
stack.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
# output script in raw binary format
|
22
|
+
def to_payload
|
23
|
+
payload = Bitcoin::Protocol.pack_var_int(stack.size)
|
24
|
+
payload << stack.map{|e| Bitcoin::Protocol.pack_var_int(e.bytesize) << e }.join
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/bitcoin/protocol/tx.rb
CHANGED
@@ -7,6 +7,9 @@ module Bitcoin
|
|
7
7
|
|
8
8
|
class Tx
|
9
9
|
|
10
|
+
MARKER = 0
|
11
|
+
FLAG = 1
|
12
|
+
|
10
13
|
# transaction hash
|
11
14
|
attr_reader :hash
|
12
15
|
|
@@ -28,6 +31,9 @@ module Bitcoin
|
|
28
31
|
# parsed / evaluated input scripts cached for later use
|
29
32
|
attr_reader :scripts
|
30
33
|
|
34
|
+
attr_accessor :marker
|
35
|
+
attr_accessor :flag
|
36
|
+
|
31
37
|
alias :inputs :in
|
32
38
|
alias :outputs :out
|
33
39
|
|
@@ -63,13 +69,22 @@ module Bitcoin
|
|
63
69
|
# parse raw binary data
|
64
70
|
def parse_data_from_io(data)
|
65
71
|
buf = data.is_a?(String) ? StringIO.new(data) : data
|
66
|
-
payload_start = buf.pos
|
67
72
|
|
68
73
|
@ver = buf.read(4).unpack("V")[0]
|
69
74
|
|
70
75
|
return false if buf.eof?
|
71
76
|
|
72
77
|
in_size = Protocol.unpack_var_int_from_io(buf)
|
78
|
+
|
79
|
+
# segwit serialization format is defined by https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
|
80
|
+
witness = false
|
81
|
+
if in_size.zero?
|
82
|
+
@marker = 0
|
83
|
+
@flag = buf.read(1).unpack('c').first
|
84
|
+
in_size = Protocol.unpack_var_int_from_io(buf)
|
85
|
+
witness = true
|
86
|
+
end
|
87
|
+
|
73
88
|
@in = []
|
74
89
|
in_size.times{
|
75
90
|
break if buf.eof?
|
@@ -87,12 +102,19 @@ module Bitcoin
|
|
87
102
|
|
88
103
|
return false if buf.eof?
|
89
104
|
|
105
|
+
if witness
|
106
|
+
in_size.times do |i|
|
107
|
+
witness_count = Protocol.unpack_var_int_from_io(buf)
|
108
|
+
witness_count.times do
|
109
|
+
size = Protocol.unpack_var_int_from_io(buf)
|
110
|
+
@in[i].script_witness.stack << buf.read(size)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
90
115
|
@lock_time = buf.read(4).unpack("V")[0]
|
91
116
|
|
92
|
-
|
93
|
-
buf.seek(payload_start)
|
94
|
-
@payload = buf.read( payload_end-payload_start )
|
95
|
-
@hash = hash_from_payload(@payload)
|
117
|
+
@hash = hash_from_payload(to_old_payload)
|
96
118
|
|
97
119
|
if buf.eof?
|
98
120
|
true
|
@@ -105,6 +127,10 @@ module Bitcoin
|
|
105
127
|
|
106
128
|
# output transaction in raw binary format
|
107
129
|
def to_payload
|
130
|
+
witness? ? to_witness_payload : to_old_payload
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_old_payload
|
108
134
|
pin = ""
|
109
135
|
@in.each{|input| pin << input.to_payload }
|
110
136
|
pout = ""
|
@@ -113,6 +139,22 @@ module Bitcoin
|
|
113
139
|
[@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V")
|
114
140
|
end
|
115
141
|
|
142
|
+
# output transaction in raw binary format with witness
|
143
|
+
def to_witness_payload
|
144
|
+
buf = [@ver, MARKER, FLAG].pack('Vcc')
|
145
|
+
buf << Protocol.pack_var_int(@in.length) << @in.map(&:to_payload).join
|
146
|
+
buf << Protocol.pack_var_int(@out.length) << @out.map(&:to_payload).join
|
147
|
+
buf << witness_payload << [@lock_time].pack('V')
|
148
|
+
buf
|
149
|
+
end
|
150
|
+
|
151
|
+
def witness_payload
|
152
|
+
@in.map { |i| i.script_witness.to_payload }.join
|
153
|
+
end
|
154
|
+
|
155
|
+
def witness?
|
156
|
+
!@in.find { |i| !i.script_witness.empty? }.nil?
|
157
|
+
end
|
116
158
|
|
117
159
|
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
118
160
|
|
@@ -170,6 +212,50 @@ module Bitcoin
|
|
170
212
|
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
171
213
|
end
|
172
214
|
|
215
|
+
# generate a witness signature hash for input +input_idx+.
|
216
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
|
217
|
+
def signature_hash_for_witness_input(input_idx, witness_program, prev_out_value, witness_script = nil, hash_type=nil, skip_separator_index = 0)
|
218
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
|
219
|
+
|
220
|
+
hash_type ||= SIGHASH_TYPE[:all]
|
221
|
+
|
222
|
+
script = Bitcoin::Script.new(witness_program)
|
223
|
+
raise "ScriptPubkey does not contain witness program." unless script.is_witness?
|
224
|
+
|
225
|
+
hash_prevouts = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i| [i.prev_out_hash, i.prev_out_index].pack("a32V")}.join))
|
226
|
+
hash_sequence = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i|i.sequence}.join))
|
227
|
+
outpoint = [@in[input_idx].prev_out_hash, @in[input_idx].prev_out_index].pack("a32V")
|
228
|
+
amount = [prev_out_value].pack("Q")
|
229
|
+
nsequence = @in[input_idx].sequence
|
230
|
+
|
231
|
+
if script.is_witness_v0_keyhash?
|
232
|
+
script_code = [["1976a914", script.get_hash160, "88ac"].join].pack("H*")
|
233
|
+
elsif script.is_witness_v0_scripthash?
|
234
|
+
raise "witness script does not match script pubkey" unless Bitcoin::Script.to_witness_p2sh_script(Digest::SHA256.digest(witness_script).bth) == witness_program
|
235
|
+
script = skip_separator_index > 0 ? Bitcoin::Script.new(witness_script).subscript_codeseparator(skip_separator_index) : witness_script
|
236
|
+
script_code = Bitcoin::Protocol.pack_var_string(script)
|
237
|
+
end
|
238
|
+
|
239
|
+
hash_outputs = Digest::SHA256.digest(Digest::SHA256.digest(@out.map{|o|o.to_payload}.join))
|
240
|
+
|
241
|
+
case (hash_type & 0x1f)
|
242
|
+
when SIGHASH_TYPE[:single]
|
243
|
+
hash_outputs = input_idx >= @out.size ? "\x00".ljust(32, "\x00") : Digest::SHA256.digest(Digest::SHA256.digest(@out[input_idx].to_payload))
|
244
|
+
hash_sequence = "\x00".ljust(32, "\x00")
|
245
|
+
when SIGHASH_TYPE[:none]
|
246
|
+
hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
|
247
|
+
end
|
248
|
+
|
249
|
+
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
250
|
+
hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
|
251
|
+
end
|
252
|
+
|
253
|
+
buf = [ [@ver].pack("V"), hash_prevouts, hash_sequence, outpoint,
|
254
|
+
script_code, amount, nsequence, hash_outputs, [@lock_time, hash_type].pack("VV")].join
|
255
|
+
|
256
|
+
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
257
|
+
end
|
258
|
+
|
173
259
|
# verify input signature +in_idx+ against the corresponding
|
174
260
|
# output in +outpoint_tx+
|
175
261
|
# outpoint
|
@@ -204,6 +290,63 @@ module Bitcoin
|
|
204
290
|
return sig_valid
|
205
291
|
end
|
206
292
|
|
293
|
+
# verify witness input signature +in_idx+ against the corresponding
|
294
|
+
# output in +outpoint_tx+
|
295
|
+
# outpoint
|
296
|
+
#
|
297
|
+
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
|
298
|
+
def verify_witness_input_signature(in_idx, outpoint_tx_or_script, prev_out_amount, block_timestamp=Time.now.to_i, opts={})
|
299
|
+
if @enable_bitcoinconsensus
|
300
|
+
return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts)
|
301
|
+
end
|
302
|
+
|
303
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
304
|
+
script_sig = ''
|
305
|
+
|
306
|
+
# If given an entire previous transaction, take the script from it
|
307
|
+
script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
|
308
|
+
Bitcoin::Script.new(outpoint_tx_or_script.out[outpoint_idx].pk_script)
|
309
|
+
else
|
310
|
+
# Otherwise, it's already a script.
|
311
|
+
Bitcoin::Script.new(outpoint_tx_or_script)
|
312
|
+
end
|
313
|
+
|
314
|
+
if script_pubkey.is_p2sh?
|
315
|
+
redeem_script = Bitcoin::Script.new(@in[in_idx].script_sig).get_pubkey
|
316
|
+
script_pubkey = Bitcoin::Script.new(redeem_script.htb) if Bitcoin.hash160(redeem_script) == script_pubkey.get_hash160 # P2SH-P2WPKH or P2SH-P2WSH
|
317
|
+
end
|
318
|
+
|
319
|
+
@in[in_idx].script_witness.stack.each{|s|script_sig << Bitcoin::Script.pack_pushdata(s)}
|
320
|
+
code_separator_index = 0
|
321
|
+
|
322
|
+
if script_pubkey.is_witness_v0_keyhash? # P2WPKH
|
323
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_hash160_script(script_pubkey.get_hash160))
|
324
|
+
elsif script_pubkey.is_witness_v0_scripthash? # P2WSH
|
325
|
+
witness_hex = @in[in_idx].script_witness.stack.last.bth
|
326
|
+
witness_script = Bitcoin::Script.new(witness_hex.htb)
|
327
|
+
return false unless Bitcoin.sha256(witness_hex) == script_pubkey.get_hash160
|
328
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_p2sh_script(Bitcoin.hash160(witness_hex)))
|
329
|
+
else
|
330
|
+
return false
|
331
|
+
end
|
332
|
+
|
333
|
+
return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
|
334
|
+
return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
|
335
|
+
sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
|
336
|
+
if script_pubkey.is_witness_v0_keyhash?
|
337
|
+
hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, nil, hash_type)
|
338
|
+
elsif script_pubkey.is_witness_v0_scripthash?
|
339
|
+
hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, witness_hex.htb, hash_type, code_separator_index)
|
340
|
+
code_separator_index += 1 if witness_script.codeseparator_count > code_separator_index
|
341
|
+
end
|
342
|
+
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
343
|
+
end
|
344
|
+
# BIP62 rule #6
|
345
|
+
return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
|
346
|
+
|
347
|
+
return sig_valid
|
348
|
+
end
|
349
|
+
|
207
350
|
def bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
|
208
351
|
raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
|
209
352
|
|
@@ -229,12 +372,12 @@ module Bitcoin
|
|
229
372
|
|
230
373
|
# convert to ruby hash (see also #from_hash)
|
231
374
|
def to_hash(options = {})
|
232
|
-
@hash ||= hash_from_payload(
|
375
|
+
@hash ||= hash_from_payload(to_old_payload)
|
233
376
|
h = {
|
234
377
|
'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash,
|
235
378
|
'vin_sz' => @in.size, 'vout_sz' => @out.size,
|
236
379
|
'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
|
237
|
-
'in' => @in.map{|i|
|
380
|
+
'in' => @in.map{|i|i.to_hash(options)},
|
238
381
|
'out' => @out.map{|o| o.to_hash(options) }
|
239
382
|
}
|
240
383
|
h['nid'] = normalized_hash if options[:with_nid]
|
@@ -258,9 +401,11 @@ module Bitcoin
|
|
258
401
|
tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time']
|
259
402
|
ins = h['in'] || h['inputs']
|
260
403
|
outs = h['out'] || h['outputs']
|
261
|
-
ins .each{|input|
|
404
|
+
ins .each{|input|
|
405
|
+
tx.add_in(TxIn.from_hash(input))
|
406
|
+
}
|
262
407
|
outs.each{|output| tx.add_out TxOut.from_hash(output) }
|
263
|
-
tx.instance_eval{ @hash = hash_from_payload(@payload =
|
408
|
+
tx.instance_eval{ @hash = hash_from_payload(@payload = to_old_payload) }
|
264
409
|
if h['hash'] && (h['hash'] != tx.hash)
|
265
410
|
raise "Tx hash mismatch! Claimed: #{h['hash']}, Actual: #{tx.hash}" if do_raise
|
266
411
|
end
|
@@ -268,7 +413,10 @@ module Bitcoin
|
|
268
413
|
end
|
269
414
|
|
270
415
|
# convert ruby hash to raw binary
|
271
|
-
def self.binary_from_hash(h)
|
416
|
+
def self.binary_from_hash(h)
|
417
|
+
tx = from_hash(h)
|
418
|
+
tx.to_payload
|
419
|
+
end
|
272
420
|
|
273
421
|
# parse json representation
|
274
422
|
def self.from_json(json_string); from_hash( JSON.load(json_string) ); end
|
@@ -367,6 +515,18 @@ module Bitcoin
|
|
367
515
|
end
|
368
516
|
alias :nhash :normalized_hash
|
369
517
|
|
518
|
+
# get witness hash
|
519
|
+
def witness_hash
|
520
|
+
hash_from_payload(to_witness_payload)
|
521
|
+
end
|
522
|
+
|
523
|
+
# sort transaction inputs and outputs under BIP 69
|
524
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
|
525
|
+
def lexicographical_sort!
|
526
|
+
inputs.sort_by!{|i| [i.previous_output, i.prev_out_index]}
|
527
|
+
outputs.sort_by!{|o| [o.amount, o.pk_script.bth]}
|
528
|
+
end
|
529
|
+
|
370
530
|
end
|
371
531
|
end
|
372
532
|
end
|