bitcoin-ruby 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/COPYING +1 -1
- data/Gemfile +5 -11
- data/README.rdoc +11 -5
- data/Rakefile +5 -0
- data/bin/bitcoin_node +11 -29
- data/bin/bitcoin_node_cli +81 -0
- data/bin/bitcoin_wallet +9 -6
- data/doc/NODE.rdoc +79 -26
- data/examples/bbe_verify_tx.rb +1 -1
- data/examples/index_nhash.rb +24 -0
- data/examples/reindex_p2sh_addrs.rb +44 -0
- data/lib/bitcoin.rb +135 -20
- data/lib/bitcoin/builder.rb +233 -63
- data/lib/bitcoin/key.rb +89 -16
- data/lib/bitcoin/litecoin.rb +13 -11
- data/lib/bitcoin/namecoin.rb +5 -4
- data/lib/bitcoin/network/command_client.rb +23 -13
- data/lib/bitcoin/network/command_handler.rb +336 -131
- data/lib/bitcoin/network/connection_handler.rb +14 -13
- data/lib/bitcoin/network/node.rb +61 -20
- data/lib/bitcoin/protocol.rb +5 -1
- data/lib/bitcoin/protocol/block.rb +15 -3
- data/lib/bitcoin/protocol/parser.rb +3 -3
- data/lib/bitcoin/protocol/tx.rb +82 -20
- data/lib/bitcoin/protocol/txin.rb +7 -0
- data/lib/bitcoin/protocol/txout.rb +12 -9
- data/lib/bitcoin/script.rb +329 -75
- data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
- data/lib/bitcoin/storage/models.rb +6 -11
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
- data/lib/bitcoin/storage/storage.rb +161 -97
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
- data/lib/bitcoin/validation.rb +87 -56
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +38 -0
- data/spec/bitcoin/builder_spec.rb +177 -0
- data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
- data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
- data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
- data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
- data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
- data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
- data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
- data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
- data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
- data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
- data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
- data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
- data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
- data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
- data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
- data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
- data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
- data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
- data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
- data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
- data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
- data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
- data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
- data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
- data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
- data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
- data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
- data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
- data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
- data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
- data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
- data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
- data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
- data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
- data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
- data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
- data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
- data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
- data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
- data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
- data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
- data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
- data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
- data/spec/bitcoin/key_spec.rb +79 -8
- data/spec/bitcoin/namecoin_spec.rb +1 -1
- data/spec/bitcoin/node/command_api_spec.rb +373 -86
- data/spec/bitcoin/performance/storage_spec.rb +41 -0
- data/spec/bitcoin/protocol/addr_spec.rb +7 -5
- data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
- data/spec/bitcoin/protocol/block_spec.rb +6 -0
- data/spec/bitcoin/protocol/tx_spec.rb +184 -1
- data/spec/bitcoin/protocol/txin_spec.rb +27 -0
- data/spec/bitcoin/protocol/txout_spec.rb +27 -0
- data/spec/bitcoin/script/opcodes_spec.rb +74 -3
- data/spec/bitcoin/script/script_spec.rb +271 -0
- data/spec/bitcoin/spec_helper.rb +34 -6
- data/spec/bitcoin/storage/models_spec.rb +104 -0
- data/spec/bitcoin/storage/reorg_spec.rb +42 -11
- data/spec/bitcoin/storage/storage_spec.rb +58 -15
- data/spec/bitcoin/storage/validation_spec.rb +44 -14
- data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
- data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
- data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
- metadata +117 -11
@@ -42,6 +42,13 @@ module Bitcoin
|
|
42
42
|
@prev_out_index == other.prev_out_index &&
|
43
43
|
@script_sig == other.script_sig &&
|
44
44
|
@sequence == other.sequence
|
45
|
+
rescue
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# returns true if the sequence number is final (DEFAULT_SEQUENCE)
|
50
|
+
def is_final?
|
51
|
+
self.sequence == DEFAULT_SEQUENCE
|
45
52
|
end
|
46
53
|
|
47
54
|
# parse raw binary data for transaction input
|
@@ -11,6 +11,9 @@ module Bitcoin
|
|
11
11
|
# pk_script output Script
|
12
12
|
attr_accessor :pk_script, :pk_script_length
|
13
13
|
|
14
|
+
# p2sh redeem script (optional, not included in the serialized binary format)
|
15
|
+
attr_accessor :redeem_script
|
16
|
+
|
14
17
|
def initialize *args
|
15
18
|
if args.size == 2
|
16
19
|
@value, @pk_script_length, @pk_script = args[0], args[1].bytesize, args[1]
|
@@ -20,7 +23,7 @@ module Bitcoin
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def ==(other)
|
23
|
-
@value == other.value && @pk_script == other.pk_script
|
26
|
+
@value == other.value && @pk_script == other.pk_script rescue false
|
24
27
|
end
|
25
28
|
|
26
29
|
# parse raw binary data for transaction output
|
@@ -43,8 +46,8 @@ module Bitcoin
|
|
43
46
|
|
44
47
|
alias :parse_payload :parse_data
|
45
48
|
|
46
|
-
def
|
47
|
-
@
|
49
|
+
def parsed_script
|
50
|
+
@parsed_script ||= Bitcoin::Script.new(pk_script)
|
48
51
|
end
|
49
52
|
|
50
53
|
def to_payload
|
@@ -56,10 +59,9 @@ module Bitcoin
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def to_hash(options = {})
|
59
|
-
script = get_script
|
60
62
|
h = { 'value' => "%.8f" % (@value / 100000000.0),
|
61
|
-
'scriptPubKey' =>
|
62
|
-
h["address"] =
|
63
|
+
'scriptPubKey' => parsed_script.to_string }
|
64
|
+
h["address"] = parsed_script.get_address if parsed_script.is_hash160? && options[:with_address]
|
63
65
|
h
|
64
66
|
end
|
65
67
|
|
@@ -68,17 +70,18 @@ module Bitcoin
|
|
68
70
|
script = Script.binary_from_string(output['scriptPubKey'])
|
69
71
|
new(amount, script)
|
70
72
|
end
|
73
|
+
|
71
74
|
# set pk_script and pk_script_length
|
72
|
-
def pk_script=(
|
73
|
-
@pk_script_length, @pk_script =
|
75
|
+
def pk_script=(pk_script)
|
76
|
+
@pk_script_length, @pk_script = pk_script.bytesize, pk_script
|
74
77
|
end
|
75
78
|
|
76
79
|
alias :amount :value
|
77
80
|
alias :amount= :value=
|
81
|
+
|
78
82
|
alias :script :pk_script
|
79
83
|
alias :script= :pk_script=
|
80
84
|
|
81
|
-
|
82
85
|
# create output spending +value+ btc (base units) to +address+
|
83
86
|
def self.value_to_address(value, address)
|
84
87
|
pk_script = Bitcoin::Script.to_address_script(address)
|
data/lib/bitcoin/script.rb
CHANGED
@@ -4,10 +4,26 @@ require 'bitcoin'
|
|
4
4
|
|
5
5
|
class Bitcoin::Script
|
6
6
|
|
7
|
-
OP_1 = 81
|
8
|
-
OP_TRUE = 81
|
9
7
|
OP_0 = 0
|
10
8
|
OP_FALSE = 0
|
9
|
+
OP_1 = 81
|
10
|
+
OP_TRUE = 81
|
11
|
+
OP_2 = 0x52
|
12
|
+
OP_3 = 0x53
|
13
|
+
OP_4 = 0x54
|
14
|
+
OP_5 = 0x55
|
15
|
+
OP_6 = 0x56
|
16
|
+
OP_7 = 0x57
|
17
|
+
OP_8 = 0x58
|
18
|
+
OP_9 = 0x59
|
19
|
+
OP_10 = 0x5a
|
20
|
+
OP_11 = 0x5b
|
21
|
+
OP_12 = 0x5c
|
22
|
+
OP_13 = 0x5d
|
23
|
+
OP_14 = 0x5e
|
24
|
+
OP_15 = 0x5f
|
25
|
+
OP_16 = 0x60
|
26
|
+
|
11
27
|
OP_PUSHDATA0 = 0
|
12
28
|
OP_PUSHDATA1 = 76
|
13
29
|
OP_PUSHDATA2 = 77
|
@@ -50,6 +66,7 @@ class Bitcoin::Script
|
|
50
66
|
OP_MIN = 163
|
51
67
|
OP_MAX = 164
|
52
68
|
OP_2OVER = 112
|
69
|
+
OP_2ROT = 113
|
53
70
|
OP_2SWAP = 114
|
54
71
|
OP_IFDUP = 115
|
55
72
|
OP_DEPTH = 116
|
@@ -133,14 +150,30 @@ class Bitcoin::Script
|
|
133
150
|
2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] }
|
134
151
|
[1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") }
|
135
152
|
|
153
|
+
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
136
154
|
|
137
155
|
attr_reader :raw, :chunks, :debug
|
138
156
|
|
139
157
|
# create a new script. +bytes+ is typically input_script + output_script
|
140
|
-
def initialize(
|
141
|
-
@
|
158
|
+
def initialize(input_script, previous_output_script=nil)
|
159
|
+
@raw_byte_sizes = [input_script.bytesize, previous_output_script ? previous_output_script.bytesize : 0]
|
160
|
+
|
161
|
+
@raw = if previous_output_script
|
162
|
+
input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + previous_output_script
|
163
|
+
else
|
164
|
+
input_script
|
165
|
+
end
|
166
|
+
|
167
|
+
@chunks = parse(input_script)
|
168
|
+
|
169
|
+
if previous_output_script
|
170
|
+
@script_codeseparator_index = @chunks.size
|
171
|
+
@chunks << Bitcoin::Script::OP_CODESEPARATOR
|
172
|
+
@chunks += parse(previous_output_script)
|
173
|
+
end
|
174
|
+
|
142
175
|
@stack, @stack_alt, @exec_stack = [], [], []
|
143
|
-
@
|
176
|
+
@last_codeseparator_index = 0
|
144
177
|
@do_exec = true
|
145
178
|
end
|
146
179
|
|
@@ -154,14 +187,14 @@ class Bitcoin::Script
|
|
154
187
|
program = bytes.unpack("C*")
|
155
188
|
chunks = []
|
156
189
|
until program.empty?
|
157
|
-
opcode = program.shift
|
190
|
+
opcode = program.shift
|
158
191
|
|
159
192
|
if (opcode > 0) && (opcode < OP_PUSHDATA1)
|
160
193
|
len, tmp = opcode, program[0]
|
161
194
|
chunks << program.shift(len).pack("C*")
|
162
195
|
|
163
196
|
# 0x16 = 22 due to OP_2_16 from_string parsing
|
164
|
-
if len == 1 && tmp <= 22
|
197
|
+
if len == 1 && tmp && tmp <= 22
|
165
198
|
chunks.last.bitcoin_pushdata = OP_PUSHDATA0
|
166
199
|
chunks.last.bitcoin_pushdata_length = len
|
167
200
|
else
|
@@ -202,13 +235,14 @@ class Bitcoin::Script
|
|
202
235
|
end
|
203
236
|
end
|
204
237
|
chunks
|
205
|
-
rescue
|
238
|
+
rescue => ex
|
206
239
|
# bail out! #run returns false but serialization roundtrips still create the right payload.
|
240
|
+
chunks.pop if ex.message.include?("invalid OP_PUSHDATA")
|
207
241
|
@parse_invalid = true
|
208
242
|
c = bytes.unpack("C*").pack("C*")
|
209
243
|
c.bitcoin_pushdata = OP_PUSHDATA_INVALID
|
210
244
|
c.bitcoin_pushdata_length = c.bytesize
|
211
|
-
chunks
|
245
|
+
chunks << c
|
212
246
|
end
|
213
247
|
|
214
248
|
# string representation of the script
|
@@ -244,11 +278,44 @@ class Bitcoin::Script
|
|
244
278
|
end
|
245
279
|
alias :to_payload :to_binary
|
246
280
|
|
247
|
-
|
248
281
|
def to_binary_without_signatures(drop_signatures, chunks=nil)
|
249
|
-
|
282
|
+
buf = []
|
283
|
+
(chunks || @chunks).each.with_index{|chunk,idx|
|
284
|
+
if chunk == OP_CODESEPARATOR and idx <= @last_codeseparator_index
|
285
|
+
buf.clear
|
286
|
+
elsif chunk == OP_CODESEPARATOR
|
287
|
+
if idx == @script_codeseparator_index
|
288
|
+
break
|
289
|
+
else
|
290
|
+
# skip
|
291
|
+
end
|
292
|
+
elsif drop_signatures.none?{|e| e == chunk }
|
293
|
+
buf << chunk
|
294
|
+
end
|
295
|
+
}
|
296
|
+
to_binary(buf)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Adds opcode (OP_0, OP_1, ... OP_CHECKSIG etc.)
|
300
|
+
# Returns self.
|
301
|
+
def append_opcode(opcode)
|
302
|
+
raise "Opcode should be a Fixnum" if !opcode.is_a?(Fixnum)
|
303
|
+
if opcode >= OP_0 && opcode <= 0xff
|
304
|
+
@chunks << opcode
|
305
|
+
else
|
306
|
+
raise "Opcode should be within [0x00, 0xff]"
|
307
|
+
end
|
308
|
+
self
|
250
309
|
end
|
251
310
|
|
311
|
+
# Adds binary string as pushdata. Pushdata will be encoded in the most compact form
|
312
|
+
# (unless the string contains internal info about serialization that's added by Script class)
|
313
|
+
# Returns self.
|
314
|
+
def append_pushdata(pushdata_string)
|
315
|
+
raise "Pushdata should be a string" if !pushdata_string.is_a?(String)
|
316
|
+
@chunks << pushdata_string
|
317
|
+
self
|
318
|
+
end
|
252
319
|
|
253
320
|
def self.pack_pushdata(data)
|
254
321
|
size = data.bytesize
|
@@ -333,15 +400,17 @@ class Bitcoin::Script
|
|
333
400
|
return false if @parse_invalid
|
334
401
|
|
335
402
|
#p [to_string, block_timestamp, is_p2sh?]
|
336
|
-
@script_invalid = true if @
|
403
|
+
@script_invalid = true if @raw_byte_sizes.any?{|size| size > 10_000 }
|
404
|
+
@last_codeseparator_index = 0
|
337
405
|
|
338
406
|
if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
|
339
407
|
return pay_to_script_hash(check_callback) if is_p2sh?
|
340
408
|
end
|
341
409
|
|
342
410
|
@debug = []
|
343
|
-
@chunks.each{|chunk|
|
411
|
+
@chunks.each.with_index{|chunk,idx|
|
344
412
|
break if invalid?
|
413
|
+
@chunk_last_index = idx
|
345
414
|
|
346
415
|
@debug << @stack.map{|i| i.unpack("H*") rescue i}
|
347
416
|
@do_exec = @exec_stack.count(false) == 0 ? true : false
|
@@ -355,7 +424,7 @@ class Bitcoin::Script
|
|
355
424
|
break
|
356
425
|
end
|
357
426
|
|
358
|
-
next unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
|
427
|
+
next @debug.pop unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
|
359
428
|
|
360
429
|
case chunk
|
361
430
|
when *OPCODES_METHOD.keys
|
@@ -374,6 +443,8 @@ class Bitcoin::Script
|
|
374
443
|
if @do_exec
|
375
444
|
@debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
|
376
445
|
@stack << chunk
|
446
|
+
else
|
447
|
+
@debug.pop
|
377
448
|
end
|
378
449
|
end
|
379
450
|
}
|
@@ -386,7 +457,7 @@ class Bitcoin::Script
|
|
386
457
|
|
387
458
|
@debug << "RESULT"
|
388
459
|
return false if @stack.empty?
|
389
|
-
return false if
|
460
|
+
return false if cast_to_bool(@stack.pop) == false
|
390
461
|
true
|
391
462
|
end
|
392
463
|
|
@@ -405,10 +476,10 @@ class Bitcoin::Script
|
|
405
476
|
def pay_to_script_hash(check_callback)
|
406
477
|
return false if @chunks.size < 4
|
407
478
|
*rest, script, _, script_hash, _ = @chunks
|
479
|
+
script = rest.pop if script == OP_CODESEPARATOR
|
408
480
|
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
409
481
|
|
410
482
|
return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
411
|
-
rest.delete_at(0) if rest[0] && cast_to_bignum(rest[0]) == 0
|
412
483
|
|
413
484
|
script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
|
414
485
|
result = script.run(&check_callback)
|
@@ -419,7 +490,19 @@ class Bitcoin::Script
|
|
419
490
|
def inner_p2sh!(script=nil); @inner_p2sh = true; @inner_script_code = script; self; end
|
420
491
|
def inner_p2sh?; @inner_p2sh; end
|
421
492
|
|
493
|
+
# get the inner p2sh script
|
494
|
+
def inner_p2sh_script
|
495
|
+
return nil if @chunks.size < 4
|
496
|
+
*rest, script, _, script_hash, _ = @chunks
|
497
|
+
script = rest.pop if script == OP_CODESEPARATOR
|
498
|
+
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
499
|
+
|
500
|
+
return nil unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
501
|
+
script
|
502
|
+
end
|
503
|
+
|
422
504
|
def is_pay_to_script_hash?
|
505
|
+
return false if @inner_p2sh
|
423
506
|
return false unless @chunks[-2].is_a?(String)
|
424
507
|
@chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
|
425
508
|
@chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
|
@@ -428,17 +511,17 @@ class Bitcoin::Script
|
|
428
511
|
|
429
512
|
# check if script is in one of the recognized standard formats
|
430
513
|
def is_standard?
|
431
|
-
is_pubkey? || is_hash160? || is_multisig? || is_p2sh?
|
514
|
+
is_pubkey? || is_hash160? || is_multisig? || is_p2sh? || is_op_return?
|
432
515
|
end
|
433
516
|
|
434
|
-
# is this a pubkey
|
517
|
+
# is this a pubkey script
|
435
518
|
def is_pubkey?
|
436
519
|
return false if @chunks.size != 2
|
437
|
-
(@chunks[1] == OP_CHECKSIG) && @chunks[0].
|
520
|
+
(@chunks[1] == OP_CHECKSIG) && @chunks[0] && (@chunks[0].is_a?(String)) && @chunks[0] != OP_RETURN
|
438
521
|
end
|
439
522
|
alias :is_send_to_ip? :is_pubkey?
|
440
523
|
|
441
|
-
# is this a hash160 (address)
|
524
|
+
# is this a hash160 (address) script
|
442
525
|
def is_hash160?
|
443
526
|
return false if @chunks.size != 5
|
444
527
|
(@chunks[0..1] + @chunks[-2..-1]) ==
|
@@ -446,18 +529,24 @@ class Bitcoin::Script
|
|
446
529
|
@chunks[2].is_a?(String) && @chunks[2].bytesize == 20
|
447
530
|
end
|
448
531
|
|
449
|
-
# is this a multisig
|
532
|
+
# is this a multisig script
|
450
533
|
def is_multisig?
|
451
|
-
return false if @chunks.size
|
534
|
+
return false if @chunks.size < 4 || !@chunks[-2].is_a?(Fixnum)
|
452
535
|
@chunks[-1] == OP_CHECKMULTISIG and get_multisig_pubkeys.all?{|c| c.is_a?(String) }
|
453
536
|
end
|
454
537
|
|
538
|
+
# is this an op_return script
|
539
|
+
def is_op_return?
|
540
|
+
@chunks[0] == OP_RETURN && @chunks.size <= 2
|
541
|
+
end
|
542
|
+
|
455
543
|
# get type of this tx
|
456
544
|
def type
|
457
545
|
if is_hash160?; :hash160
|
458
546
|
elsif is_pubkey?; :pubkey
|
459
547
|
elsif is_multisig?; :multisig
|
460
548
|
elsif is_p2sh?; :p2sh
|
549
|
+
elsif is_op_return?;:op_return
|
461
550
|
else; :unknown
|
462
551
|
end
|
463
552
|
end
|
@@ -504,6 +593,12 @@ class Bitcoin::Script
|
|
504
593
|
Bitcoin.hash160_to_p2sh_address(get_hash160)
|
505
594
|
end
|
506
595
|
|
596
|
+
# get the data possibly included in an OP_RETURN script
|
597
|
+
def get_op_return_data
|
598
|
+
return nil unless is_op_return?
|
599
|
+
cast_to_string(@chunks[1]).unpack("H*")[0] if @chunks[1]
|
600
|
+
end
|
601
|
+
|
507
602
|
# get all addresses this script corresponds to (if possible)
|
508
603
|
def get_addresses
|
509
604
|
return [get_pubkey_address] if is_pubkey?
|
@@ -519,25 +614,30 @@ class Bitcoin::Script
|
|
519
614
|
addrs.is_a?(Array) ? addrs[0] : addrs
|
520
615
|
end
|
521
616
|
|
522
|
-
# generate pubkey tx script for given +pubkey
|
617
|
+
# generate pubkey tx script for given +pubkey+. returns a raw binary script of the form:
|
618
|
+
# <pubkey> OP_CHECKSIG
|
523
619
|
def self.to_pubkey_script(pubkey)
|
524
|
-
|
525
|
-
[[pk.bytesize].pack("C"), pk, "\xAC"].join
|
620
|
+
pack_pushdata([pubkey].pack("H*")) + [ OP_CHECKSIG ].pack("C")
|
526
621
|
end
|
527
622
|
|
528
|
-
# generate hash160 tx for given +address
|
623
|
+
# generate hash160 tx for given +address+. returns a raw binary script of the form:
|
624
|
+
# OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG
|
529
625
|
def self.to_hash160_script(hash160)
|
530
626
|
return nil unless hash160
|
531
627
|
# DUP HASH160 length hash160 EQUALVERIFY CHECKSIG
|
532
628
|
[ ["76", "a9", "14", hash160, "88", "ac"].join ].pack("H*")
|
533
629
|
end
|
534
630
|
|
631
|
+
# generate p2sh output script for given +p2sh+ hash160. returns a raw binary script of the form:
|
632
|
+
# OP_HASH160 <p2sh> OP_EQUAL
|
535
633
|
def self.to_p2sh_script(p2sh)
|
536
634
|
return nil unless p2sh
|
537
635
|
# HASH160 length hash EQUAL
|
538
636
|
[ ["a9", "14", p2sh, "87"].join ].pack("H*")
|
539
637
|
end
|
540
638
|
|
639
|
+
# generate hash160 or p2sh output script, depending on the type of the given +address+.
|
640
|
+
# see #to_hash160_script and #to_p2sh_script.
|
541
641
|
def self.to_address_script(address)
|
542
642
|
hash160 = Bitcoin.hash160_from_address(address)
|
543
643
|
case Bitcoin.address_type(address)
|
@@ -546,30 +646,53 @@ class Bitcoin::Script
|
|
546
646
|
end
|
547
647
|
end
|
548
648
|
|
549
|
-
# generate multisig
|
649
|
+
# generate multisig output script for given +pubkeys+, expecting +m+ signatures.
|
650
|
+
# returns a raw binary script of the form:
|
651
|
+
# <m> <pubkey> [<pubkey> ...] <n_pubkeys> OP_CHECKMULTISIG
|
550
652
|
def self.to_multisig_script(m, *pubkeys)
|
551
|
-
|
552
|
-
|
653
|
+
raise "invalid m-of-n number" unless [m, pubkeys.size].all?{|i| (0..20).include?(i) }
|
654
|
+
raise "invalid m-of-n number" if pubkeys.size < m
|
655
|
+
pubs = pubkeys.map{|pk| pack_pushdata([pk].pack("H*")) }
|
656
|
+
|
657
|
+
m = m > 16 ? pack_pushdata([m].pack("C")) : [80 + m.to_i].pack("C")
|
658
|
+
n = pubkeys.size > 16 ? pack_pushdata([pubkeys.size].pack("C")) : [80 + pubs.size].pack("C")
|
659
|
+
|
660
|
+
[ m, *pubs, n, [OP_CHECKMULTISIG].pack("C")].join
|
661
|
+
end
|
662
|
+
|
663
|
+
# generate OP_RETURN output script with given data. returns a raw binary script of the form:
|
664
|
+
# OP_RETURN <data>
|
665
|
+
def self.to_op_return_script(data = nil)
|
666
|
+
buf = [ OP_RETURN ].pack("C")
|
667
|
+
return buf unless data
|
668
|
+
return buf + pack_pushdata( [data].pack("H*") )
|
553
669
|
end
|
554
670
|
|
555
|
-
# generate
|
671
|
+
# generate input script sig spending a pubkey output with given +signature+ and +pubkey+.
|
672
|
+
# returns a raw binary script sig of the form:
|
673
|
+
# <signature> [<pubkey>]
|
556
674
|
def self.to_pubkey_script_sig(signature, pubkey)
|
557
|
-
hash_type = "
|
558
|
-
|
559
|
-
return
|
675
|
+
hash_type = [ SIGHASH_TYPE[:all] ].pack("C")
|
676
|
+
buf = pack_pushdata(signature + hash_type)
|
677
|
+
return buf unless pubkey
|
560
678
|
|
561
|
-
case pubkey[0]
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
expected_size = 33
|
566
|
-
end
|
679
|
+
expected_size = case pubkey[0]
|
680
|
+
when "\x04"; 65
|
681
|
+
when "\x02", "\x03"; 33
|
682
|
+
end
|
567
683
|
|
568
|
-
if !expected_size || pubkey.bytesize != expected_size
|
569
|
-
|
570
|
-
|
684
|
+
raise "pubkey is not in binary form" if !expected_size || pubkey.bytesize != expected_size
|
685
|
+
|
686
|
+
return buf + pack_pushdata(pubkey)
|
687
|
+
end
|
571
688
|
|
572
|
-
|
689
|
+
# generate p2sh multisig output script for given +args+.
|
690
|
+
# returns the p2sh output script, and the redeem script needed to spend it.
|
691
|
+
# see #to_multisig_script for the redeem script, and #to_p2sh_script for the p2sh script.
|
692
|
+
def self.to_p2sh_multisig_script(*args)
|
693
|
+
redeem_script = to_multisig_script(*args)
|
694
|
+
p2sh_script = to_p2sh_script(Bitcoin.hash160(redeem_script.hth))
|
695
|
+
return p2sh_script, redeem_script
|
573
696
|
end
|
574
697
|
|
575
698
|
# alias for #to_pubkey_script_sig
|
@@ -577,8 +700,30 @@ class Bitcoin::Script
|
|
577
700
|
to_pubkey_script_sig(*a)
|
578
701
|
end
|
579
702
|
|
703
|
+
# generate input script sig spending a multisig output script.
|
704
|
+
# returns a raw binary script sig of the form:
|
705
|
+
# OP_0 <sig> [<sig> ...]
|
580
706
|
def self.to_multisig_script_sig(*sigs)
|
581
|
-
|
707
|
+
partial_script = [OP_0].pack("C*")
|
708
|
+
sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script) }
|
709
|
+
partial_script
|
710
|
+
end
|
711
|
+
|
712
|
+
# take a multisig script sig (or p2sh multisig script sig) and add
|
713
|
+
# another signature to it after the OP_0. Used to sign a tx by
|
714
|
+
# multiple parties. Signatures must be in the same order as the
|
715
|
+
# pubkeys in the output script being redeemed.
|
716
|
+
def self.add_sig_to_multisig_script_sig(sig, script_sig)
|
717
|
+
signature = sig + [SIGHASH_TYPE[:all]].pack("C*")
|
718
|
+
offset = script_sig.empty? ? 0 : 1
|
719
|
+
script_sig.insert(offset, pack_pushdata(signature))
|
720
|
+
end
|
721
|
+
|
722
|
+
# generate input script sig spending a p2sh-multisig output script.
|
723
|
+
# returns a raw binary script sig of the form:
|
724
|
+
# OP_0 <sig> [<sig> ...] <redeem_script>
|
725
|
+
def self.to_p2sh_multisig_script_sig(redeem_script, *sigs)
|
726
|
+
to_multisig_script_sig(*sigs.flatten) + pack_pushdata(redeem_script)
|
582
727
|
end
|
583
728
|
|
584
729
|
def get_signatures_required
|
@@ -586,6 +731,72 @@ class Bitcoin::Script
|
|
586
731
|
@chunks[0] - 80
|
587
732
|
end
|
588
733
|
|
734
|
+
# This matches CScript::GetSigOpCount(bool fAccurate)
|
735
|
+
# Note: this does not cover P2SH script which is to be unserialized
|
736
|
+
# and checked explicitly when validating blocks.
|
737
|
+
def sigops_count_accurate(is_accurate)
|
738
|
+
count = 0
|
739
|
+
last_opcode = nil
|
740
|
+
@chunks.each do |chunk| # pushdate or opcode
|
741
|
+
if chunk == OP_CHECKSIG || chunk == OP_CHECKSIGVERIFY
|
742
|
+
count += 1
|
743
|
+
elsif chunk == OP_CHECKMULTISIG || chunk == OP_CHECKMULTISIGVERIFY
|
744
|
+
# Accurate mode counts exact number of pubkeys required (not signatures, but pubkeys!). Only used in P2SH scripts.
|
745
|
+
# Inaccurate mode counts every multisig as 20 signatures.
|
746
|
+
if is_accurate && last_opcode && last_opcode.is_a?(Fixnum) && last_opcode >= OP_1 && last_opcode <= OP_16
|
747
|
+
count += ::Bitcoin::Script.decode_OP_N(last_opcode)
|
748
|
+
else
|
749
|
+
count += 20
|
750
|
+
end
|
751
|
+
end
|
752
|
+
last_opcode = chunk
|
753
|
+
end
|
754
|
+
count
|
755
|
+
end
|
756
|
+
|
757
|
+
# This method applies to script_sig that is an input for p2sh output.
|
758
|
+
# Bitcoind has somewhat special way to return count for invalid input scripts:
|
759
|
+
# it returns 0 when the opcode can't be parsed or when it's over OP_16.
|
760
|
+
# Also, if the OP_{N} is used anywhere it's treated as 0-length data.
|
761
|
+
# See CScript::GetSigOpCount(const CScript& scriptSig) in bitcoind.
|
762
|
+
def sigops_count_for_p2sh
|
763
|
+
# This is a pay-to-script-hash scriptPubKey;
|
764
|
+
# get the last item that the scriptSig
|
765
|
+
# pushes onto the stack:
|
766
|
+
|
767
|
+
return 0 if @chunks.size == 0
|
768
|
+
|
769
|
+
data = nil
|
770
|
+
@chunks.each do |chunk|
|
771
|
+
case chunk
|
772
|
+
when Fixnum
|
773
|
+
data = ""
|
774
|
+
return 0 if chunk > OP_16
|
775
|
+
when String
|
776
|
+
data = chunk
|
777
|
+
end
|
778
|
+
end
|
779
|
+
return 0 if data == ""
|
780
|
+
|
781
|
+
::Bitcoin::Script.new(data).sigops_count_accurate(true)
|
782
|
+
end
|
783
|
+
|
784
|
+
# Converts OP_{0,1,2,...,16} into 0, 1, 2, ..., 16.
|
785
|
+
# Returns nil for other opcodes.
|
786
|
+
def self.decode_OP_N(opcode)
|
787
|
+
if opcode == OP_0
|
788
|
+
return 0
|
789
|
+
end
|
790
|
+
if opcode.is_a?(Fixnum) && opcode >= OP_1 && opcode <= OP_16
|
791
|
+
return opcode - (OP_1 - 1);
|
792
|
+
else
|
793
|
+
nil
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
|
798
|
+
|
799
|
+
|
589
800
|
## OPCODES
|
590
801
|
|
591
802
|
# Does nothing
|
@@ -739,6 +950,7 @@ class Bitcoin::Script
|
|
739
950
|
@stack << (a + 1)
|
740
951
|
end
|
741
952
|
|
953
|
+
# 1 is subtracted from the input.
|
742
954
|
def op_1sub
|
743
955
|
a = pop_int
|
744
956
|
@stack << (a - 1)
|
@@ -757,15 +969,14 @@ class Bitcoin::Script
|
|
757
969
|
|
758
970
|
# Returns 1 if the inputs are exactly equal, 0 otherwise.
|
759
971
|
def op_equal
|
760
|
-
|
761
|
-
a, b = pop_int(2)
|
972
|
+
a, b = pop_string(2)
|
762
973
|
@stack << (a == b ? 1 : 0)
|
763
974
|
end
|
764
975
|
|
765
976
|
# Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
|
766
977
|
def op_verify
|
767
978
|
res = pop_int
|
768
|
-
if res ==
|
979
|
+
if cast_to_bool(res) == false
|
769
980
|
@stack << res
|
770
981
|
@script_invalid = true # raise 'transaction invalid' ?
|
771
982
|
else
|
@@ -813,7 +1024,7 @@ class Bitcoin::Script
|
|
813
1024
|
|
814
1025
|
# If the input is true, duplicate it.
|
815
1026
|
def op_ifdup
|
816
|
-
if
|
1027
|
+
if cast_to_bool(@stack.last) == true
|
817
1028
|
@stack << @stack.last
|
818
1029
|
end
|
819
1030
|
end
|
@@ -861,8 +1072,8 @@ class Bitcoin::Script
|
|
861
1072
|
def op_if
|
862
1073
|
value = false
|
863
1074
|
if @do_exec
|
864
|
-
return if @stack.size < 1
|
865
|
-
value =
|
1075
|
+
(invalid; return) if @stack.size < 1
|
1076
|
+
value = cast_to_bool(pop_string) == false ? false : true
|
866
1077
|
end
|
867
1078
|
@exec_stack << value
|
868
1079
|
end
|
@@ -871,8 +1082,8 @@ class Bitcoin::Script
|
|
871
1082
|
def op_notif
|
872
1083
|
value = false
|
873
1084
|
if @do_exec
|
874
|
-
return if @stack.size < 1
|
875
|
-
value =
|
1085
|
+
(invalid; return) if @stack.size < 1
|
1086
|
+
value = cast_to_bool(pop_string) == false ? true : false
|
876
1087
|
end
|
877
1088
|
@exec_stack << value
|
878
1089
|
end
|
@@ -891,14 +1102,24 @@ class Bitcoin::Script
|
|
891
1102
|
|
892
1103
|
# The item n back in the stack is copied to the top.
|
893
1104
|
def op_pick
|
1105
|
+
return invalid if @stack.size < 2
|
894
1106
|
pos = pop_int
|
1107
|
+
return invalid if (pos < 0) || (pos >= @stack.size)
|
895
1108
|
item = @stack[-(pos+1)]
|
896
1109
|
@stack << item if item
|
897
1110
|
end
|
898
1111
|
|
1112
|
+
# The fifth and sixth items back are moved to the top of the stack.
|
1113
|
+
def op_2rot
|
1114
|
+
return invalid if @stack.size < 6
|
1115
|
+
@stack[-6..-1] = [ *@stack[-4..-1], *@stack[-6..-5] ]
|
1116
|
+
end
|
1117
|
+
|
899
1118
|
# The item n back in the stack is moved to the top.
|
900
1119
|
def op_roll
|
1120
|
+
return invalid if @stack.size < 2
|
901
1121
|
pos = pop_int
|
1122
|
+
return invalid if (pos < 0) || (pos >= @stack.size)
|
902
1123
|
idx = -(pos+1)
|
903
1124
|
item = @stack[idx]
|
904
1125
|
if item
|
@@ -959,21 +1180,43 @@ class Bitcoin::Script
|
|
959
1180
|
end
|
960
1181
|
|
961
1182
|
def cast_to_bignum(buf)
|
1183
|
+
return (invalid; 0) unless buf
|
962
1184
|
case buf
|
963
|
-
when Numeric
|
964
|
-
|
1185
|
+
when Numeric
|
1186
|
+
invalid if OpenSSL::BN.new(buf.to_s).to_s(0).unpack("N")[0] > 4
|
1187
|
+
buf
|
1188
|
+
when String
|
1189
|
+
invalid if buf.bytesize > 4
|
1190
|
+
OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
|
965
1191
|
else; raise TypeError, 'cast_to_bignum: failed to cast: %s (%s)' % [buf, buf.class]
|
966
1192
|
end
|
967
1193
|
end
|
968
1194
|
|
969
1195
|
def cast_to_string(buf)
|
1196
|
+
return (invalid; "") unless buf
|
970
1197
|
case buf
|
971
|
-
when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1]
|
1198
|
+
when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1].reverse
|
972
1199
|
when String; buf;
|
973
1200
|
else; raise TypeError, 'cast_to_string: failed to cast: %s (%s)' % [buf, buf.class]
|
974
1201
|
end
|
975
1202
|
end
|
976
1203
|
|
1204
|
+
def cast_to_bool(buf)
|
1205
|
+
buf = cast_to_string(buf).unpack("C*")
|
1206
|
+
size = buf.size
|
1207
|
+
buf.each.with_index{|byte,index|
|
1208
|
+
if byte != 0
|
1209
|
+
# Can be negative zero
|
1210
|
+
if (index == (size-1)) && byte == 0x80
|
1211
|
+
return false
|
1212
|
+
else
|
1213
|
+
return true
|
1214
|
+
end
|
1215
|
+
end
|
1216
|
+
}
|
1217
|
+
return false
|
1218
|
+
end
|
1219
|
+
|
977
1220
|
# Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
978
1221
|
def op_numequalverify
|
979
1222
|
op_numequal; op_verify
|
@@ -983,6 +1226,7 @@ class Bitcoin::Script
|
|
983
1226
|
# to the data after the most recently-executed OP_CODESEPARATOR.
|
984
1227
|
def op_codeseparator
|
985
1228
|
@codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
|
1229
|
+
@last_codeseparator_index = @chunk_last_index
|
986
1230
|
end
|
987
1231
|
|
988
1232
|
def codehash_script(opcode)
|
@@ -998,9 +1242,9 @@ class Bitcoin::Script
|
|
998
1242
|
# This is used by Protocol::Tx#verify_input_signature
|
999
1243
|
def op_checksig(check_callback)
|
1000
1244
|
return invalid if @stack.size < 2
|
1001
|
-
pubkey = @stack.pop
|
1245
|
+
pubkey = cast_to_string(@stack.pop)
|
1002
1246
|
#return (@stack << 0) unless Bitcoin::Script.is_canonical_pubkey?(pubkey) # only for isStandard
|
1003
|
-
drop_sigs = [ @stack[-1] ]
|
1247
|
+
drop_sigs = [ cast_to_string(@stack[-1]) ]
|
1004
1248
|
|
1005
1249
|
signature = cast_to_string(@stack.pop)
|
1006
1250
|
#return (@stack << 0) unless Bitcoin::Script.is_canonical_signature?(signature) # only for isStandard
|
@@ -1008,21 +1252,25 @@ class Bitcoin::Script
|
|
1008
1252
|
|
1009
1253
|
sig, hash_type = parse_sig(signature)
|
1010
1254
|
|
1011
|
-
|
1012
|
-
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
1013
|
-
drop_sigs = nil
|
1014
|
-
else
|
1015
|
-
script_code, drop_sigs = nil, nil
|
1016
|
-
end
|
1255
|
+
subscript = sighash_subscript(drop_sigs)
|
1017
1256
|
|
1018
1257
|
if check_callback == nil # for tests
|
1019
1258
|
@stack << 1
|
1020
1259
|
else # real signature check callback
|
1021
1260
|
@stack <<
|
1022
|
-
((check_callback.call(pubkey, sig, hash_type,
|
1261
|
+
((check_callback.call(pubkey, sig, hash_type, subscript) == true) ? 1 : 0)
|
1023
1262
|
end
|
1024
1263
|
end
|
1025
1264
|
|
1265
|
+
def sighash_subscript(drop_sigs)
|
1266
|
+
if inner_p2sh? && @inner_script_code
|
1267
|
+
::Bitcoin::Script.new(@inner_script_code).to_binary_without_signatures(drop_sigs)
|
1268
|
+
else
|
1269
|
+
to_binary_without_signatures(drop_sigs)
|
1270
|
+
end
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
# Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
|
1026
1274
|
def op_checksigverify(check_callback)
|
1027
1275
|
op_checksig(check_callback)
|
1028
1276
|
op_verify
|
@@ -1053,22 +1301,23 @@ class Bitcoin::Script
|
|
1053
1301
|
n_sigs = pop_int
|
1054
1302
|
return invalid if n_sigs < 0 || n_sigs > n_pubkeys
|
1055
1303
|
return invalid if @stack.size < n_sigs
|
1056
|
-
sigs =
|
1304
|
+
sigs = pop_string(n_sigs)
|
1305
|
+
drop_sigs = sigs.dup
|
1057
1306
|
|
1058
|
-
|
1307
|
+
# Bitcoin-core removes an extra item from the stack
|
1308
|
+
@stack.pop
|
1059
1309
|
|
1060
|
-
|
1061
|
-
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
1062
|
-
drop_sigs = nil
|
1063
|
-
else
|
1064
|
-
script_code, drop_sigs = nil, nil
|
1065
|
-
end
|
1310
|
+
subscript = sighash_subscript(drop_sigs)
|
1066
1311
|
|
1067
1312
|
success = true
|
1068
1313
|
while success && n_sigs > 0
|
1069
1314
|
sig, pub = sigs.pop, pubkeys.pop
|
1315
|
+
unless sig && sig.size > 0
|
1316
|
+
success = false
|
1317
|
+
break
|
1318
|
+
end
|
1070
1319
|
signature, hash_type = parse_sig(sig)
|
1071
|
-
if check_callback.call(pub, signature, hash_type,
|
1320
|
+
if pub.size > 0 && check_callback.call(pub, signature, hash_type, subscript)
|
1072
1321
|
n_sigs -= 1
|
1073
1322
|
else
|
1074
1323
|
sigs << sig
|
@@ -1077,7 +1326,13 @@ class Bitcoin::Script
|
|
1077
1326
|
success = false if n_sigs > n_pubkeys
|
1078
1327
|
end
|
1079
1328
|
|
1080
|
-
@stack << (success ? 1 :
|
1329
|
+
@stack << (success ? 1 : 0)
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
# Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
|
1333
|
+
def op_checkmultisigverify(check_callback)
|
1334
|
+
op_checkmultisig(check_callback)
|
1335
|
+
op_verify
|
1081
1336
|
end
|
1082
1337
|
|
1083
1338
|
# op_eval: https://en.bitcoin.it/wiki/BIP_0012
|
@@ -1105,7 +1360,6 @@ class Bitcoin::Script
|
|
1105
1360
|
end
|
1106
1361
|
|
1107
1362
|
|
1108
|
-
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
1109
1363
|
|
1110
1364
|
def self.is_canonical_signature?(sig)
|
1111
1365
|
return false if sig.bytesize < 9 # Non-canonical signature: too short
|