bitcoin-ruby 0.0.10 → 0.0.11
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 +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
|