tapyrus 0.2.4 → 0.2.9
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/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets.rb +0 -2
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/schnorr.rb +14 -9
- data/lib/schnorr/sign_to_contract.rb +51 -0
- data/lib/schnorr/signature.rb +3 -6
- data/lib/tapyrus.rb +8 -30
- data/lib/tapyrus/base58.rb +7 -6
- data/lib/tapyrus/bip175.rb +67 -0
- data/lib/tapyrus/block.rb +1 -2
- data/lib/tapyrus/block_header.rb +15 -9
- data/lib/tapyrus/bloom_filter.rb +5 -3
- data/lib/tapyrus/chain_params.rb +1 -4
- data/lib/tapyrus/chainparams/dev.yml +3 -2
- data/lib/tapyrus/chainparams/prod.yml +3 -2
- data/lib/tapyrus/constants.rb +29 -23
- data/lib/tapyrus/errors.rb +1 -3
- data/lib/tapyrus/ext.rb +1 -1
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext_key.rb +44 -32
- data/lib/tapyrus/key.rb +31 -35
- data/lib/tapyrus/key_path.rb +15 -12
- data/lib/tapyrus/logger.rb +20 -16
- data/lib/tapyrus/merkle_tree.rb +19 -20
- data/lib/tapyrus/message.rb +14 -16
- data/lib/tapyrus/message/addr.rb +1 -7
- data/lib/tapyrus/message/base.rb +0 -3
- data/lib/tapyrus/message/block.rb +2 -9
- data/lib/tapyrus/message/block_transaction_request.rb +3 -6
- data/lib/tapyrus/message/block_transactions.rb +2 -6
- data/lib/tapyrus/message/block_txn.rb +0 -4
- data/lib/tapyrus/message/cmpct_block.rb +1 -7
- data/lib/tapyrus/message/error.rb +1 -4
- data/lib/tapyrus/message/fee_filter.rb +1 -4
- data/lib/tapyrus/message/filter_add.rb +0 -4
- data/lib/tapyrus/message/filter_clear.rb +0 -4
- data/lib/tapyrus/message/filter_load.rb +2 -5
- data/lib/tapyrus/message/get_addr.rb +0 -4
- data/lib/tapyrus/message/get_block_txn.rb +0 -4
- data/lib/tapyrus/message/get_blocks.rb +0 -3
- data/lib/tapyrus/message/get_data.rb +1 -4
- data/lib/tapyrus/message/get_headers.rb +1 -3
- data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
- data/lib/tapyrus/message/headers.rb +0 -4
- data/lib/tapyrus/message/headers_parser.rb +3 -8
- data/lib/tapyrus/message/inv.rb +1 -4
- data/lib/tapyrus/message/inventories_parser.rb +2 -7
- data/lib/tapyrus/message/inventory.rb +12 -5
- data/lib/tapyrus/message/mem_pool.rb +0 -4
- data/lib/tapyrus/message/merkle_block.rb +4 -9
- data/lib/tapyrus/message/network_addr.rb +7 -6
- data/lib/tapyrus/message/not_found.rb +0 -3
- data/lib/tapyrus/message/ping.rb +0 -3
- data/lib/tapyrus/message/pong.rb +0 -3
- data/lib/tapyrus/message/prefilled_tx.rb +0 -4
- data/lib/tapyrus/message/reject.rb +0 -3
- data/lib/tapyrus/message/send_cmpct.rb +1 -3
- data/lib/tapyrus/message/send_headers.rb +0 -3
- data/lib/tapyrus/message/tx.rb +0 -4
- data/lib/tapyrus/message/ver_ack.rb +1 -5
- data/lib/tapyrus/message/version.rb +2 -5
- data/lib/tapyrus/mnemonic.rb +17 -15
- data/lib/tapyrus/network.rb +0 -2
- data/lib/tapyrus/network/connection.rb +0 -3
- data/lib/tapyrus/network/message_handler.rb +61 -60
- data/lib/tapyrus/network/peer.rb +13 -12
- data/lib/tapyrus/network/peer_discovery.rb +3 -5
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/node.rb +1 -1
- data/lib/tapyrus/node/cli.rb +12 -14
- data/lib/tapyrus/node/configuration.rb +1 -3
- data/lib/tapyrus/node/spv.rb +2 -3
- data/lib/tapyrus/opcodes.rb +9 -7
- data/lib/tapyrus/out_point.rb +5 -5
- data/lib/tapyrus/rpc.rb +1 -0
- data/lib/tapyrus/rpc/http_server.rb +21 -22
- data/lib/tapyrus/rpc/request_handler.rb +42 -44
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
- data/lib/tapyrus/script/color.rb +20 -2
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +104 -67
- data/lib/tapyrus/script/script_error.rb +1 -4
- data/lib/tapyrus/script/script_interpreter.rb +439 -399
- data/lib/tapyrus/script/tx_checker.rb +20 -10
- data/lib/tapyrus/secp256k1.rb +0 -4
- data/lib/tapyrus/secp256k1/native.rb +14 -15
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
- data/lib/tapyrus/secp256k1/ruby.rb +10 -12
- data/lib/tapyrus/slip39.rb +20 -5
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +101 -57
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- data/lib/tapyrus/tx.rb +45 -37
- data/lib/tapyrus/tx_builder.rb +158 -0
- data/lib/tapyrus/tx_in.rb +1 -6
- data/lib/tapyrus/tx_out.rb +2 -7
- data/lib/tapyrus/util.rb +20 -7
- data/lib/tapyrus/validation.rb +12 -11
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +22 -18
- data/lib/tapyrus/wallet/base.rb +12 -9
- data/lib/tapyrus/wallet/db.rb +6 -9
- data/lib/tapyrus/wallet/master_key.rb +2 -4
- data/tapyrusrb.gemspec +13 -16
- metadata +22 -31
- data/.travis.yml +0 -12
@@ -1,11 +1,10 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# utility for multisig
|
4
3
|
module Multisig
|
5
4
|
include Tapyrus::Opcodes
|
6
5
|
|
7
6
|
def self.prefix
|
8
|
-
[OP_0].pack(
|
7
|
+
[OP_0].pack('C*')
|
9
8
|
end
|
10
9
|
|
11
10
|
# generate input script sig spending a multisig output script.
|
@@ -23,7 +22,7 @@ module Tapyrus
|
|
23
22
|
# multiple parties. Signatures must be in the same order as the
|
24
23
|
# pubkeys in the output script being redeemed.
|
25
24
|
def self.add_sig_to_multisig_script_sig(sig_to_add, script_sig, hash_type = SIGHASH_TYPE[:all])
|
26
|
-
signature = sig_to_add + [hash_type].pack(
|
25
|
+
signature = sig_to_add + [hash_type].pack('C*')
|
27
26
|
offset = script_sig.empty? ? 0 : 1
|
28
27
|
script_sig.insert(offset, Tapyrus::Script.pack_pushdata(signature))
|
29
28
|
end
|
@@ -49,17 +48,19 @@ module Tapyrus
|
|
49
48
|
pubkeys = redeem_script.get_multisig_pubkeys
|
50
49
|
|
51
50
|
# find the pubkey for each signature by trying to verify it
|
52
|
-
sigs =
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
51
|
+
sigs =
|
52
|
+
Hash[
|
53
|
+
script.chunks[1...-1].map.with_index do |sig, idx|
|
54
|
+
sig = sig.pushed_data
|
55
|
+
pubkey =
|
56
|
+
pubkeys.map { |key| Tapyrus::Key.new(pubkey: key.bth).verify(sig, sig_hash) ? key : nil }.compact.first
|
57
|
+
raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
|
58
|
+
[pubkey, sig]
|
59
|
+
end
|
60
|
+
]
|
60
61
|
|
61
62
|
prefix + pubkeys.map { |k| sigs[k] ? Tapyrus::Script.pack_pushdata(sigs[k]) : nil }.join +
|
62
63
|
Tapyrus::Script.pack_pushdata(script.chunks[-1].pushed_data)
|
63
64
|
end
|
64
65
|
end
|
65
|
-
end
|
66
|
+
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
# https://github.com/lian/bitcoin-ruby/blob/master/COPYING
|
3
3
|
|
4
4
|
module Tapyrus
|
5
|
-
|
6
5
|
# tapyrus script
|
7
6
|
class Script
|
8
7
|
include Tapyrus::HexConverter
|
@@ -75,9 +74,19 @@ module Tapyrus
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
77
|
+
# Remove color identifier from cp2pkh or cp2sh
|
78
|
+
# @param [ColorIdentifier] color identifier
|
79
|
+
# @return [Script] P2PKH or P2SH script
|
80
|
+
# @raise [RuntimeError] if script is neither cp2pkh nor cp2sh
|
81
|
+
def remove_color
|
82
|
+
raise RuntimeError, 'Only cp2pkh and cp2sh can remove color' unless cp2pkh? or cp2sh?
|
83
|
+
|
84
|
+
Tapyrus::Script.new.tap { |s| s.chunks = self.chunks[2..-1] }
|
85
|
+
end
|
86
|
+
|
78
87
|
def get_multisig_pubkeys
|
79
88
|
num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
|
80
|
-
(1..num).map{ |i| chunks[i].pushed_data }
|
89
|
+
(1..num).map { |i| chunks[i].pushed_data }
|
81
90
|
end
|
82
91
|
|
83
92
|
# generate m of n multisig script
|
@@ -92,14 +101,16 @@ module Tapyrus
|
|
92
101
|
# generate script from string.
|
93
102
|
def self.from_string(string)
|
94
103
|
script = new
|
95
|
-
string
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
104
|
+
string
|
105
|
+
.split(' ')
|
106
|
+
.each do |v|
|
107
|
+
opcode = Opcodes.name_to_opcode(v)
|
108
|
+
if opcode
|
109
|
+
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
110
|
+
else
|
111
|
+
script << (v =~ /^[0-9]+$/ ? v.to_i : v)
|
112
|
+
end
|
101
113
|
end
|
102
|
-
end
|
103
114
|
script
|
104
115
|
end
|
105
116
|
|
@@ -118,6 +129,12 @@ module Tapyrus
|
|
118
129
|
Tapyrus::Script.to_p2pkh(hex)
|
119
130
|
when Tapyrus.chain_params.p2sh_version
|
120
131
|
Tapyrus::Script.to_p2sh(hex)
|
132
|
+
when Tapyrus.chain_params.cp2pkh_version
|
133
|
+
color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
|
134
|
+
Tapyrus::Script.to_cp2pkh(color, hex[66..-1])
|
135
|
+
when Tapyrus.chain_params.cp2sh_version
|
136
|
+
color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
|
137
|
+
Tapyrus::Script.to_cp2sh(color, hex[66..-1])
|
121
138
|
else
|
122
139
|
throw e
|
123
140
|
end
|
@@ -132,19 +149,20 @@ module Tapyrus
|
|
132
149
|
if opcode.pushdata?
|
133
150
|
pushcode = opcode.ord
|
134
151
|
packed_size = nil
|
135
|
-
len =
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
152
|
+
len =
|
153
|
+
case pushcode
|
154
|
+
when OP_PUSHDATA1
|
155
|
+
packed_size = buf.read(1)
|
156
|
+
packed_size.unpack('C').first
|
157
|
+
when OP_PUSHDATA2
|
158
|
+
packed_size = buf.read(2)
|
159
|
+
packed_size.unpack('v').first
|
160
|
+
when OP_PUSHDATA4
|
161
|
+
packed_size = buf.read(4)
|
162
|
+
packed_size.unpack('V').first
|
163
|
+
else
|
164
|
+
pushcode if pushcode < OP_PUSHDATA1
|
165
|
+
end
|
148
166
|
if len
|
149
167
|
s.chunks << [len].pack('C') if buf.eof?
|
150
168
|
unless buf.eof?
|
@@ -176,7 +194,7 @@ module Tapyrus
|
|
176
194
|
return [p2sh_addr] if p2sh?
|
177
195
|
return [cp2pkh_addr] if cp2pkh?
|
178
196
|
return [cp2sh_addr] if cp2sh?
|
179
|
-
return get_multisig_pubkeys.map{|pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh} if multisig?
|
197
|
+
return get_multisig_pubkeys.map { |pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh } if multisig?
|
180
198
|
[]
|
181
199
|
end
|
182
200
|
|
@@ -188,8 +206,8 @@ module Tapyrus
|
|
188
206
|
# whether this script is a P2PKH format script.
|
189
207
|
def p2pkh?
|
190
208
|
return false unless chunks.size == 5
|
191
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
192
|
-
|
209
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[0..1] + chunks[3..4]).map(&:ord) &&
|
210
|
+
chunks[2].bytesize == 21
|
193
211
|
end
|
194
212
|
|
195
213
|
def p2sh?
|
@@ -210,19 +228,22 @@ module Tapyrus
|
|
210
228
|
end
|
211
229
|
|
212
230
|
def standard_op_return?
|
213
|
-
op_return? && size <= MAX_OP_RETURN_RELAY &&
|
214
|
-
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
231
|
+
op_return? && size <= MAX_OP_RETURN_RELAY && (chunks.size == 1 || chunks[1].opcode <= OP_16)
|
215
232
|
end
|
216
233
|
|
234
|
+
# Return whether this script is a CP2PKH format script or not.
|
235
|
+
# @return [Boolean] true if this script is cp2pkh, otherwise false.
|
217
236
|
def cp2pkh?
|
218
237
|
return false unless chunks.size == 7
|
219
238
|
return false unless chunks[0].bytesize == 34
|
220
239
|
return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
|
221
240
|
return false unless chunks[1].ord == OP_COLOR
|
222
|
-
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
223
|
-
|
241
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[2..3] + chunks[5..6]).map(&:ord) &&
|
242
|
+
chunks[4].bytesize == 21
|
224
243
|
end
|
225
244
|
|
245
|
+
# Return whether this script is a CP2SH format script or not.
|
246
|
+
# @return [Boolean] true if this script is cp2pkh, otherwise false.
|
226
247
|
def cp2sh?
|
227
248
|
return false unless chunks.size == 5
|
228
249
|
return false unless chunks[0].bytesize == 34
|
@@ -231,6 +252,20 @@ module Tapyrus
|
|
231
252
|
OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
|
232
253
|
end
|
233
254
|
|
255
|
+
# Return whether this script represents colored coin.
|
256
|
+
# @return [Boolean] true if this script is colored, otherwise false.
|
257
|
+
def colored?
|
258
|
+
cp2pkh? || cp2sh?
|
259
|
+
end
|
260
|
+
|
261
|
+
# Return color identifier for this script.
|
262
|
+
# @return [ColorIdentifer] color identifier for this script if this script is colored. return nil if this script is not colored.
|
263
|
+
def color_id
|
264
|
+
return nil unless colored?
|
265
|
+
|
266
|
+
Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)
|
267
|
+
end
|
268
|
+
|
234
269
|
def op_return_data
|
235
270
|
return nil unless op_return?
|
236
271
|
return nil if chunks.size == 1
|
@@ -239,24 +274,23 @@ module Tapyrus
|
|
239
274
|
|
240
275
|
# whether data push only script which dose not include other opcode
|
241
276
|
def push_only?
|
242
|
-
chunks.each
|
243
|
-
return false if !c.opcode.nil? && c.opcode > OP_16
|
244
|
-
end
|
277
|
+
chunks.each { |c| return false if !c.opcode.nil? && c.opcode > OP_16 }
|
245
278
|
true
|
246
279
|
end
|
247
280
|
|
248
281
|
# get public keys in the stack.
|
249
282
|
# @return[Array[String]] an array of the pubkeys with hex format.
|
250
283
|
def get_pubkeys
|
251
|
-
chunks.select
|
284
|
+
chunks.select do |c|
|
285
|
+
c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) &&
|
286
|
+
[2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))
|
287
|
+
end.map { |c| c.pushed_data.bth }
|
252
288
|
end
|
253
289
|
|
254
290
|
# returns the self payload. ScriptInterpreter does not use this.
|
255
291
|
def to_script_code(skip_separator_index = 0)
|
256
292
|
payload = to_payload
|
257
|
-
if skip_separator_index > 0
|
258
|
-
payload = subscript_codeseparator(skip_separator_index)
|
259
|
-
end
|
293
|
+
payload = subscript_codeseparator(skip_separator_index) if skip_separator_index > 0
|
260
294
|
Tapyrus.pack_var_string(payload)
|
261
295
|
end
|
262
296
|
|
@@ -267,7 +301,7 @@ module Tapyrus
|
|
267
301
|
elsif obj.is_a?(String)
|
268
302
|
append_data(obj)
|
269
303
|
elsif obj.is_a?(Array)
|
270
|
-
obj.each { |o| self.<< o}
|
304
|
+
obj.each { |o| self.<< o }
|
271
305
|
self
|
272
306
|
end
|
273
307
|
end
|
@@ -303,18 +337,19 @@ module Tapyrus
|
|
303
337
|
|
304
338
|
# Check the item is in the chunk of the script.
|
305
339
|
def include?(item)
|
306
|
-
chunk_item =
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
340
|
+
chunk_item =
|
341
|
+
if item.is_a?(Integer)
|
342
|
+
item.chr
|
343
|
+
elsif item.is_a?(String)
|
344
|
+
data = Encoding::ASCII_8BIT == item.encoding ? item : item.htb
|
345
|
+
Tapyrus::Script.pack_pushdata(data)
|
346
|
+
end
|
312
347
|
return false unless chunk_item
|
313
348
|
chunks.include?(chunk_item)
|
314
349
|
end
|
315
350
|
|
316
351
|
def to_s
|
317
|
-
chunks.map
|
352
|
+
chunks.map do |c|
|
318
353
|
case c
|
319
354
|
when Integer
|
320
355
|
opcode_to_name(c)
|
@@ -337,7 +372,7 @@ module Tapyrus
|
|
337
372
|
opcode ? opcode : 'OP_UNKNOWN [error]'
|
338
373
|
end
|
339
374
|
end
|
340
|
-
|
375
|
+
end.join(' ')
|
341
376
|
end
|
342
377
|
|
343
378
|
# generate sha-256 hash for payload
|
@@ -395,17 +430,18 @@ module Tapyrus
|
|
395
430
|
# binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
|
396
431
|
def self.pack_pushdata(data)
|
397
432
|
size = data.bytesize
|
398
|
-
header =
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
433
|
+
header =
|
434
|
+
if size < OP_PUSHDATA1
|
435
|
+
[size].pack('C')
|
436
|
+
elsif size < 0xff
|
437
|
+
[OP_PUSHDATA1, size].pack('CC')
|
438
|
+
elsif size < 0xffff
|
439
|
+
[OP_PUSHDATA2, size].pack('Cv')
|
440
|
+
elsif size < 0xffffffff
|
441
|
+
[OP_PUSHDATA4, size].pack('CV')
|
442
|
+
else
|
443
|
+
raise ArgumentError, 'data size is too big.'
|
444
|
+
end
|
409
445
|
header + data
|
410
446
|
end
|
411
447
|
|
@@ -429,8 +465,12 @@ module Tapyrus
|
|
429
465
|
if chunk == sub_chunk
|
430
466
|
buf << chunk
|
431
467
|
i += 1
|
432
|
-
(
|
433
|
-
|
468
|
+
(
|
469
|
+
i = 0
|
470
|
+
buf.clear
|
471
|
+
) if i == subscript.chunks.size # matched the whole subscript
|
472
|
+
else
|
473
|
+
# matched the part of head
|
434
474
|
i = 0
|
435
475
|
tmp = chunk.dup
|
436
476
|
tmp.slice!(sub_chunk)
|
@@ -452,7 +492,7 @@ module Tapyrus
|
|
452
492
|
|
453
493
|
# remove all occurences of opcode. Typically it's OP_CODESEPARATOR.
|
454
494
|
def delete_opcode(opcode)
|
455
|
-
@chunks = chunks.select{|chunk| chunk.ord != opcode}
|
495
|
+
@chunks = chunks.select { |chunk| chunk.ord != opcode }
|
456
496
|
self
|
457
497
|
end
|
458
498
|
|
@@ -460,12 +500,10 @@ module Tapyrus
|
|
460
500
|
def subscript_codeseparator(separator_index)
|
461
501
|
buf = []
|
462
502
|
process_separator_index = 0
|
463
|
-
chunks.each
|
503
|
+
chunks.each do |chunk|
|
464
504
|
buf << chunk if process_separator_index == separator_index
|
465
|
-
if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
466
|
-
|
467
|
-
end
|
468
|
-
}
|
505
|
+
process_separator_index += 1 if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
506
|
+
end
|
469
507
|
buf.join
|
470
508
|
end
|
471
509
|
|
@@ -482,10 +520,10 @@ module Tapyrus
|
|
482
520
|
end
|
483
521
|
|
484
522
|
def to_h
|
485
|
-
h = {asm: to_s, hex: to_hex, type: type}
|
523
|
+
h = { asm: to_s, hex: to_hex, type: type }
|
486
524
|
addrs = addresses
|
487
525
|
unless addrs.empty?
|
488
|
-
h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
|
526
|
+
h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) : addrs.size
|
489
527
|
h[:addresses] = addrs
|
490
528
|
end
|
491
529
|
h
|
@@ -532,5 +570,4 @@ module Tapyrus
|
|
532
570
|
Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
|
533
571
|
end
|
534
572
|
end
|
535
|
-
|
536
573
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# tapyrus script error
|
4
3
|
class ScriptError < Exception
|
5
|
-
|
6
4
|
attr_accessor :code
|
7
5
|
attr_accessor :extra_msg
|
8
6
|
|
@@ -100,6 +98,5 @@ module Tapyrus
|
|
100
98
|
def self.name_to_code(name)
|
101
99
|
NAME_MAP[name]
|
102
100
|
end
|
103
|
-
|
104
101
|
end
|
105
|
-
end
|
102
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
class ScriptInterpreter
|
4
|
-
|
5
3
|
include Tapyrus::Opcodes
|
6
4
|
|
7
5
|
attr_reader :stack
|
@@ -11,8 +9,23 @@ module Tapyrus
|
|
11
9
|
attr_reader :checker
|
12
10
|
attr_reader :require_minimal
|
13
11
|
|
14
|
-
DISABLE_OPCODES = [
|
15
|
-
|
12
|
+
DISABLE_OPCODES = [
|
13
|
+
OP_CAT,
|
14
|
+
OP_SUBSTR,
|
15
|
+
OP_LEFT,
|
16
|
+
OP_RIGHT,
|
17
|
+
OP_INVERT,
|
18
|
+
OP_AND,
|
19
|
+
OP_OR,
|
20
|
+
OP_XOR,
|
21
|
+
OP_2MUL,
|
22
|
+
OP_2DIV,
|
23
|
+
OP_DIV,
|
24
|
+
OP_MUL,
|
25
|
+
OP_MOD,
|
26
|
+
OP_LSHIFT,
|
27
|
+
OP_RSHIFT
|
28
|
+
]
|
16
29
|
|
17
30
|
# syntax sugar for simple evaluation for script.
|
18
31
|
# @param [Tapyrus::Script] script_sig a scriptSig.
|
@@ -34,7 +47,6 @@ module Tapyrus
|
|
34
47
|
# @param [Tapyrus::Script] script_pubkey a script pubkey (locking script)
|
35
48
|
# @return [Boolean] result
|
36
49
|
def verify_script(script_sig, script_pubkey)
|
37
|
-
|
38
50
|
return set_error(SCRIPT_ERR_SIG_PUSHONLY) if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !script_sig.push_only?
|
39
51
|
|
40
52
|
stack_copy = nil
|
@@ -97,9 +109,7 @@ module Tapyrus
|
|
97
109
|
opcode = c.opcode
|
98
110
|
|
99
111
|
if need_exec && c.pushdata?
|
100
|
-
if require_minimal && !minimal_push?(c.pushed_data, opcode)
|
101
|
-
return set_error(SCRIPT_ERR_MINIMALDATA)
|
102
|
-
end
|
112
|
+
return set_error(SCRIPT_ERR_MINIMALDATA) if require_minimal && !minimal_push?(c.pushed_data, opcode)
|
103
113
|
return set_error(SCRIPT_ERR_BAD_OPCODE) unless verify_pushdata_length(c)
|
104
114
|
stack << c.pushed_data.bth
|
105
115
|
else
|
@@ -107,385 +117,415 @@ module Tapyrus
|
|
107
117
|
return set_error(SCRIPT_ERR_OP_COUNT)
|
108
118
|
end
|
109
119
|
return set_error(SCRIPT_ERR_DISABLED_OPCODE) if DISABLE_OPCODES.include?(opcode)
|
110
|
-
|
120
|
+
if opcode == OP_CODESEPARATOR && sig_version == :base && flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE)
|
121
|
+
return set_error(SCRIPT_ERR_OP_CODESEPARATOR)
|
122
|
+
end
|
111
123
|
next unless (need_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
|
112
124
|
small_int = Opcodes.opcode_to_small_int(opcode)
|
113
125
|
if small_int && opcode != OP_0
|
114
126
|
push_int(small_int)
|
115
127
|
else
|
116
128
|
case opcode
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
129
|
+
when OP_0
|
130
|
+
stack << ''
|
131
|
+
when OP_DEPTH
|
132
|
+
push_int(stack.size)
|
133
|
+
when OP_EQUAL, OP_EQUALVERIFY
|
134
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
135
|
+
a, b = pop_string(2)
|
136
|
+
result = a == b
|
137
|
+
push_int(result ? 1 : 0)
|
138
|
+
if opcode == OP_EQUALVERIFY
|
139
|
+
if result
|
140
|
+
stack.pop
|
141
|
+
else
|
142
|
+
return set_error(SCRIPT_ERR_EQUALVERIFY)
|
132
143
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
144
|
+
end
|
145
|
+
when OP_0NOTEQUAL
|
146
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
147
|
+
push_int(pop_int == 0 ? 0 : 1)
|
148
|
+
when OP_ADD
|
149
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
150
|
+
a, b = pop_int(2)
|
151
|
+
push_int(a + b)
|
152
|
+
when OP_1ADD
|
153
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
154
|
+
push_int(pop_int + 1)
|
155
|
+
when OP_SUB
|
156
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
157
|
+
a, b = pop_int(2)
|
158
|
+
push_int(a - b)
|
159
|
+
when OP_1SUB
|
160
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
161
|
+
push_int(pop_int - 1)
|
162
|
+
when OP_IF, OP_NOTIF
|
163
|
+
result = false
|
164
|
+
if need_exec
|
165
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if stack.size < 1
|
166
|
+
value = pop_string.htb
|
167
|
+
if flag?(SCRIPT_VERIFY_MINIMALIF)
|
168
|
+
if value.bytesize > 1 || (value.bytesize == 1 && value[0].unpack('C').first != 1)
|
169
|
+
return set_error(SCRIPT_ERR_MINIMALIF)
|
159
170
|
end
|
160
|
-
result = cast_to_bool(value)
|
161
|
-
result = !result if opcode == OP_NOTIF
|
162
171
|
end
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
172
|
+
result = cast_to_bool(value)
|
173
|
+
result = !result if opcode == OP_NOTIF
|
174
|
+
end
|
175
|
+
flow_stack << result
|
176
|
+
when OP_ELSE
|
177
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if flow_stack.size < 1
|
178
|
+
flow_stack << !flow_stack.pop
|
179
|
+
when OP_ENDIF
|
180
|
+
return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if flow_stack.empty?
|
181
|
+
flow_stack.pop
|
182
|
+
when OP_NOP
|
183
|
+
when OP_NOP1, OP_NOP4..OP_NOP10
|
184
|
+
if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
|
185
|
+
return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
|
186
|
+
end
|
187
|
+
when OP_CHECKLOCKTIMEVERIFY
|
188
|
+
next unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
|
189
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
190
|
+
|
191
|
+
# Note that elsewhere numeric opcodes are limited to operands in the range -2**31+1 to 2**31-1,
|
192
|
+
# however it is legal for opcodes to produce results exceeding that range.
|
193
|
+
# This limitation is implemented by CScriptNum's default 4-byte limit.
|
194
|
+
# If we kept to that limit we'd have a year 2038 problem,
|
195
|
+
# even though the nLockTime field in transactions themselves is uint32 which only becomes meaningless after the year 2106.
|
196
|
+
# Thus as a special case we tell CScriptNum to accept up to 5-byte bignums,
|
197
|
+
# which are good until 2**39-1, well beyond the 2**32-1 limit of the nLockTime field itself.
|
198
|
+
locktime = cast_to_int(stack.last, 5)
|
199
|
+
return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if locktime < 0
|
200
|
+
return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_locktime(locktime)
|
201
|
+
when OP_CHECKSEQUENCEVERIFY
|
202
|
+
next unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
|
203
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
204
|
+
|
205
|
+
# nSequence, like nLockTime, is a 32-bit unsigned integer field.
|
206
|
+
# See the comment in CHECKLOCKTIMEVERIFY regarding 5-byte numeric operands.
|
207
|
+
sequence = cast_to_int(stack.last, 5)
|
208
|
+
|
209
|
+
# In the rare event that the argument may be < 0 due to some arithmetic being done first,
|
210
|
+
# you can always use 0 MAX CHECKSEQUENCEVERIFY.
|
211
|
+
return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if sequence < 0
|
212
|
+
|
213
|
+
# To provide for future soft-fork extensibility,
|
214
|
+
# if the operand has the disabled lock-time flag set, CHECKSEQUENCEVERIFY behaves as a NOP.
|
215
|
+
next if (sequence & Tapyrus::TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0
|
216
|
+
|
217
|
+
# Compare the specified sequence number with the input.
|
218
|
+
return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_sequence(sequence)
|
219
|
+
when OP_DUP
|
220
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
221
|
+
stack << stack.last
|
222
|
+
when OP_2DUP
|
223
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
224
|
+
2.times { stack << stack[-2] }
|
225
|
+
when OP_3DUP
|
226
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
227
|
+
3.times { stack << stack[-3] }
|
228
|
+
when OP_IFDUP
|
229
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
230
|
+
stack << stack.last if cast_to_bool(stack.last)
|
231
|
+
when OP_RIPEMD160
|
232
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
233
|
+
stack << Digest::RMD160.hexdigest(pop_string.htb)
|
234
|
+
when OP_SHA1
|
235
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
236
|
+
stack << Digest::SHA1.hexdigest(pop_string.htb)
|
237
|
+
when OP_SHA256
|
238
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
239
|
+
stack << Digest::SHA256.hexdigest(pop_string.htb)
|
240
|
+
when OP_HASH160
|
241
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
242
|
+
stack << Tapyrus.hash160(pop_string)
|
243
|
+
when OP_HASH256
|
244
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
245
|
+
stack << Tapyrus.double_sha256(pop_string.htb).bth
|
246
|
+
when OP_VERIFY
|
247
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
248
|
+
return set_error(SCRIPT_ERR_VERIFY) unless pop_bool
|
249
|
+
when OP_TOALTSTACK
|
250
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
251
|
+
alt_stack << stack.pop
|
252
|
+
when OP_FROMALTSTACK
|
253
|
+
return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION) if alt_stack.size < 1
|
254
|
+
stack << alt_stack.pop
|
255
|
+
when OP_DROP
|
256
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
257
|
+
stack.pop
|
258
|
+
when OP_2DROP
|
259
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
260
|
+
2.times { stack.pop }
|
261
|
+
when OP_NIP
|
262
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
263
|
+
stack.delete_at(-2)
|
264
|
+
when OP_OVER
|
265
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
266
|
+
stack << stack[-2]
|
267
|
+
when OP_2OVER
|
268
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
269
|
+
2.times { stack << stack[-4] }
|
270
|
+
when OP_PICK, OP_ROLL
|
271
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
272
|
+
pos = pop_int
|
273
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if pos < 0 || pos >= stack.size
|
274
|
+
stack << stack[-pos - 1]
|
275
|
+
stack.delete_at(-pos - 2) if opcode == OP_ROLL
|
276
|
+
when OP_ROT
|
277
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
278
|
+
stack << stack[-3]
|
279
|
+
stack.delete_at(-4)
|
280
|
+
when OP_2ROT
|
281
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 6
|
282
|
+
2.times { stack << stack[-6] }
|
283
|
+
2.times { stack.delete_at(-7) }
|
284
|
+
when OP_SWAP
|
285
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
286
|
+
tmp = stack.last
|
287
|
+
stack[-1] = stack[-2]
|
288
|
+
stack[-2] = tmp
|
289
|
+
when OP_2SWAP
|
290
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
291
|
+
2.times { stack << stack[-4] }
|
292
|
+
2.times { stack.delete_at(-5) }
|
293
|
+
when OP_TUCK
|
294
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
295
|
+
stack.insert(-3, stack.last)
|
296
|
+
when OP_ABS
|
297
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
298
|
+
v = pop_int
|
299
|
+
push_int(v.abs)
|
300
|
+
when OP_BOOLAND
|
301
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
302
|
+
a, b = pop_int(2)
|
303
|
+
push_int((!a.zero? && !b.zero?) ? 1 : 0)
|
304
|
+
when OP_BOOLOR
|
305
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
306
|
+
a, b = pop_int(2)
|
307
|
+
push_int((!a.zero? || !b.zero?) ? 1 : 0)
|
308
|
+
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
309
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
310
|
+
a, b = pop_int(2)
|
311
|
+
result = a == b
|
312
|
+
push_int(result ? 1 : 0)
|
313
|
+
if opcode == OP_NUMEQUALVERIFY
|
314
|
+
if result
|
315
|
+
stack.pop
|
316
|
+
else
|
317
|
+
return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
|
205
318
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
when OP_2OVER
|
255
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
256
|
-
2.times { stack << stack[-4]}
|
257
|
-
when OP_PICK, OP_ROLL
|
258
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
259
|
-
pos = pop_int
|
260
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if pos < 0 || pos >= stack.size
|
261
|
-
stack << stack[-pos - 1]
|
262
|
-
stack.delete_at(-pos - 2) if opcode == OP_ROLL
|
263
|
-
when OP_ROT
|
264
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
265
|
-
stack << stack[-3]
|
266
|
-
stack.delete_at(-4)
|
267
|
-
when OP_2ROT
|
268
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 6
|
269
|
-
2.times { stack << stack[-6] }
|
270
|
-
2.times { stack.delete_at(-7) }
|
271
|
-
when OP_SWAP
|
272
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
273
|
-
tmp = stack.last
|
274
|
-
stack[-1] = stack[-2]
|
275
|
-
stack[-2] = tmp
|
276
|
-
when OP_2SWAP
|
277
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
|
278
|
-
2.times {stack << stack[-4]}
|
279
|
-
2.times {stack.delete_at(-5)}
|
280
|
-
when OP_TUCK
|
281
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
282
|
-
stack.insert(-3, stack.last)
|
283
|
-
when OP_ABS
|
284
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
285
|
-
v = pop_int
|
286
|
-
push_int(v.abs)
|
287
|
-
when OP_BOOLAND
|
288
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
289
|
-
a, b = pop_int(2)
|
290
|
-
push_int((!a.zero? && !b.zero?) ? 1 : 0)
|
291
|
-
when OP_BOOLOR
|
292
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
293
|
-
a, b = pop_int(2)
|
294
|
-
push_int((!a.zero? || !b.zero?) ? 1 : 0)
|
295
|
-
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
296
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
297
|
-
a, b = pop_int(2)
|
298
|
-
result = a == b
|
299
|
-
push_int(result ? 1 : 0)
|
300
|
-
if opcode == OP_NUMEQUALVERIFY
|
301
|
-
if result
|
302
|
-
stack.pop
|
303
|
-
else
|
304
|
-
return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
|
305
|
-
end
|
319
|
+
end
|
320
|
+
when OP_LESSTHAN, OP_LESSTHANOREQUAL
|
321
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
322
|
+
a, b = pop_int(2)
|
323
|
+
push_int(a < b ? 1 : 0) if opcode == OP_LESSTHAN
|
324
|
+
push_int(a <= b ? 1 : 0) if opcode == OP_LESSTHANOREQUAL
|
325
|
+
when OP_GREATERTHAN, OP_GREATERTHANOREQUAL
|
326
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
327
|
+
a, b = pop_int(2)
|
328
|
+
push_int(a > b ? 1 : 0) if opcode == OP_GREATERTHAN
|
329
|
+
push_int(a >= b ? 1 : 0) if opcode == OP_GREATERTHANOREQUAL
|
330
|
+
when OP_MIN
|
331
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
332
|
+
push_int(pop_int(2).min)
|
333
|
+
when OP_MAX
|
334
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
335
|
+
push_int(pop_int(2).max)
|
336
|
+
when OP_WITHIN
|
337
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
338
|
+
x, a, b = pop_int(3)
|
339
|
+
push_int((a <= x && x < b) ? 1 : 0)
|
340
|
+
when OP_NOT
|
341
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
342
|
+
push_int(pop_int == 0 ? 1 : 0)
|
343
|
+
when OP_SIZE
|
344
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
345
|
+
item = stack.last
|
346
|
+
item = Tapyrus::Script.encode_number(item) if item.is_a?(Numeric)
|
347
|
+
size = item.htb.bytesize
|
348
|
+
push_int(size)
|
349
|
+
when OP_NEGATE
|
350
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
351
|
+
push_int(-pop_int)
|
352
|
+
when OP_NUMNOTEQUAL
|
353
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
354
|
+
a, b = pop_int(2)
|
355
|
+
push_int(a == b ? 0 : 1)
|
356
|
+
when OP_CODESEPARATOR
|
357
|
+
last_code_separator_index = index + 1
|
358
|
+
when OP_CHECKSIG, OP_CHECKSIGVERIFY
|
359
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
360
|
+
sig, pubkey = pop_string(2)
|
361
|
+
|
362
|
+
subscript = script.subscript(last_code_separator_index..-1)
|
363
|
+
if sig_version == :base
|
364
|
+
tmp = subscript.find_and_delete(Script.new << sig)
|
365
|
+
if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
366
|
+
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE)
|
306
367
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
item = Tapyrus::Script.encode_number(item) if item.is_a?(Numeric)
|
334
|
-
size = item.htb.bytesize
|
335
|
-
push_int(size)
|
336
|
-
when OP_NEGATE
|
337
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
338
|
-
push_int(-pop_int)
|
339
|
-
when OP_NUMNOTEQUAL
|
340
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
341
|
-
a, b = pop_int(2)
|
342
|
-
push_int(a == b ? 0 : 1)
|
343
|
-
when OP_CODESEPARATOR
|
344
|
-
last_code_separator_index = index + 1
|
345
|
-
when OP_CHECKSIG, OP_CHECKSIGVERIFY
|
346
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
|
347
|
-
sig, pubkey = pop_string(2)
|
348
|
-
|
349
|
-
subscript = script.subscript(last_code_separator_index..-1)
|
350
|
-
if sig_version == :base
|
351
|
-
tmp = subscript.find_and_delete(Script.new << sig)
|
352
|
-
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
353
|
-
subscript = tmp
|
368
|
+
subscript = tmp
|
369
|
+
end
|
370
|
+
if (
|
371
|
+
if sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE
|
372
|
+
!check_schnorr_signature_encoding(sig)
|
373
|
+
else
|
374
|
+
!check_ecdsa_signature_encoding(sig)
|
375
|
+
end
|
376
|
+
) || !check_pubkey_encoding(pubkey)
|
377
|
+
return false
|
378
|
+
end
|
379
|
+
|
380
|
+
success = checker.check_sig(sig, pubkey, subscript, sig_version)
|
381
|
+
|
382
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL
|
383
|
+
if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
|
384
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL)
|
385
|
+
end
|
386
|
+
|
387
|
+
push_int(success ? 1 : 0)
|
388
|
+
|
389
|
+
if opcode == OP_CHECKSIGVERIFY
|
390
|
+
if success
|
391
|
+
stack.pop
|
392
|
+
else
|
393
|
+
return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
|
354
394
|
end
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
return set_error(
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < (sig_count)
|
407
|
-
|
408
|
-
sigs = pop_string(sig_count)
|
409
|
-
sigs = [sigs] if sigs.is_a?(String)
|
410
|
-
|
411
|
-
subscript = script.subscript(last_code_separator_index..-1)
|
412
|
-
|
413
|
-
if sig_version == :base
|
414
|
-
sigs.each do |sig|
|
415
|
-
tmp = subscript.find_and_delete(Script.new << sig)
|
416
|
-
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
417
|
-
subscript = tmp
|
395
|
+
end
|
396
|
+
when OP_CHECKDATASIG, OP_CHECKDATASIGVERIFY
|
397
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
|
398
|
+
sig, msg, pubkey = pop_string(3)
|
399
|
+
|
400
|
+
# check signature encoding without hashtype byte
|
401
|
+
if (
|
402
|
+
sig.htb.bytesize != (Tapyrus::Key::COMPACT_SIGNATURE_SIZE - 1) &&
|
403
|
+
!check_ecdsa_signature_encoding(sig, true)
|
404
|
+
) || !check_pubkey_encoding(pubkey)
|
405
|
+
return false
|
406
|
+
end
|
407
|
+
digest = Tapyrus.sha256(msg)
|
408
|
+
success = checker.verify_sig(sig, pubkey, digest)
|
409
|
+
if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
|
410
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL)
|
411
|
+
end
|
412
|
+
push_int(success ? 1 : 0)
|
413
|
+
if opcode == OP_CHECKDATASIGVERIFY
|
414
|
+
stack.pop if success
|
415
|
+
return set_error(SCRIPT_ERR_CHECKDATASIGVERIFY) unless success
|
416
|
+
end
|
417
|
+
when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
|
418
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
419
|
+
pubkey_count = pop_int
|
420
|
+
return set_error(SCRIPT_ERR_PUBKEY_COUNT) unless (0..MAX_PUBKEYS_PER_MULTISIG).include?(pubkey_count)
|
421
|
+
|
422
|
+
op_count += pubkey_count
|
423
|
+
return set_error(SCRIPT_ERR_OP_COUNT) if op_count > MAX_OPS_PER_SCRIPT
|
424
|
+
|
425
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < pubkey_count
|
426
|
+
|
427
|
+
pubkeys = pop_string(pubkey_count)
|
428
|
+
pubkeys = [pubkeys] if pubkeys.is_a?(String)
|
429
|
+
|
430
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
431
|
+
|
432
|
+
sig_count = pop_int
|
433
|
+
return set_error(SCRIPT_ERR_SIG_COUNT) if sig_count < 0 || sig_count > pubkey_count
|
434
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < (sig_count)
|
435
|
+
|
436
|
+
sigs = pop_string(sig_count)
|
437
|
+
sigs = [sigs] if sigs.is_a?(String)
|
438
|
+
|
439
|
+
subscript = script.subscript(last_code_separator_index..-1)
|
440
|
+
|
441
|
+
if sig_version == :base
|
442
|
+
sigs.each do |sig|
|
443
|
+
tmp = subscript.find_and_delete(Script.new << sig)
|
444
|
+
if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
|
445
|
+
return set_error(SCRIPT_ERR_SIG_FINDANDDELETE)
|
418
446
|
end
|
447
|
+
subscript = tmp
|
419
448
|
end
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
449
|
+
end
|
450
|
+
|
451
|
+
success = true
|
452
|
+
current_sig_scheme = nil
|
453
|
+
while success && sig_count > 0
|
454
|
+
sig = sigs.pop
|
455
|
+
pubkey = pubkeys.pop
|
456
|
+
sig_scheme = sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE ? :schnorr : :ecdsa
|
457
|
+
current_sig_scheme = sig_scheme if current_sig_scheme.nil?
|
458
|
+
|
459
|
+
if (
|
460
|
+
if sig_scheme == :schnorr
|
461
|
+
!check_schnorr_signature_encoding(sig)
|
462
|
+
else
|
463
|
+
!check_ecdsa_signature_encoding(sig)
|
464
|
+
end
|
465
|
+
) || !check_pubkey_encoding(pubkey)
|
466
|
+
return false
|
467
|
+
end # error already set.
|
468
|
+
|
469
|
+
return set_error(SCRIPT_ERR_MIXED_SCHEME_MULTISIG) unless sig_scheme == current_sig_scheme
|
470
|
+
|
471
|
+
ok = checker.check_sig(sig, pubkey, subscript, sig_version)
|
472
|
+
if ok
|
473
|
+
sig_count -= 1
|
474
|
+
else
|
475
|
+
sigs << sig
|
441
476
|
end
|
442
|
-
|
443
|
-
if
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
477
|
+
pubkey_count -= 1
|
478
|
+
success = false if sig_count > pubkey_count
|
479
|
+
end
|
480
|
+
|
481
|
+
if !success && flag?(SCRIPT_VERIFY_NULLFAIL)
|
482
|
+
sigs.each do |sig|
|
483
|
+
# If the operation failed, we require that all signatures must be empty vector
|
484
|
+
return set_error(SCRIPT_ERR_SIG_NULLFAIL) if sig.bytesize > 0
|
448
485
|
end
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
486
|
+
end
|
487
|
+
|
488
|
+
# A bug causes CHECKMULTISIG to consume one extra argument whose contents were not checked in any way.
|
489
|
+
# Unfortunately this is a potential source of mutability,
|
490
|
+
# so optionally verify it is exactly equal to zero prior to removing it from the stack.
|
491
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
492
|
+
return set_error(SCRIPT_ERR_SIG_NULLDUMMY) if stack[-1].size > 0
|
493
|
+
|
494
|
+
stack.pop
|
495
|
+
|
496
|
+
push_int(success ? 1 : 0)
|
497
|
+
if opcode == OP_CHECKMULTISIGVERIFY
|
498
|
+
if success
|
499
|
+
stack.pop
|
500
|
+
else
|
501
|
+
return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
|
465
502
|
end
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
503
|
+
end
|
504
|
+
when OP_RETURN
|
505
|
+
return set_error(SCRIPT_ERR_OP_RETURN)
|
506
|
+
when OP_COLOR
|
507
|
+
# Color id is not permitted in p2sh redeem script
|
508
|
+
return set_error(SCRIPT_ERR_OP_COLOR_UNEXPECTED) if is_redeem_script
|
471
509
|
|
472
|
-
|
473
|
-
|
510
|
+
# if Color id is already initialized this must be an extra
|
511
|
+
if color_id && color_id.type != Tapyrus::Color::TokenTypes::NONE
|
512
|
+
return set_error(SCRIPT_ERR_OP_COLOR_MULTIPLE)
|
513
|
+
end
|
474
514
|
|
475
|
-
|
476
|
-
|
515
|
+
# color id is not allowed inside OP_IF
|
516
|
+
return set_error(SCRIPT_ERR_OP_COLOR_IN_BRANCH) unless flow_stack.empty?
|
477
517
|
|
478
|
-
|
479
|
-
|
518
|
+
# pop one stack element and verify that it exists
|
519
|
+
return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
|
480
520
|
|
481
|
-
|
521
|
+
color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(stack.last.htb)
|
482
522
|
|
483
|
-
|
484
|
-
|
523
|
+
# check ColorIdentifier is valid
|
524
|
+
return set_error(SCRIPT_ERR_OP_COLOR_ID_INVALID) unless color_id.valid?
|
485
525
|
|
486
|
-
|
487
|
-
|
488
|
-
|
526
|
+
stack.pop
|
527
|
+
else
|
528
|
+
return set_error(SCRIPT_ERR_BAD_OPCODE)
|
489
529
|
end
|
490
530
|
end
|
491
531
|
end
|
@@ -513,7 +553,7 @@ module Tapyrus
|
|
513
553
|
|
514
554
|
# pop the item with the int value for the number specified by +count+ from the stack.
|
515
555
|
def pop_int(count = 1)
|
516
|
-
i = stack.pop(count).map{ |s| cast_to_int(s) }
|
556
|
+
i = stack.pop(count).map { |s| cast_to_int(s) }
|
517
557
|
count == 1 ? i.first : i
|
518
558
|
end
|
519
559
|
|
@@ -536,14 +576,17 @@ module Tapyrus
|
|
536
576
|
|
537
577
|
# pop the item with the string(hex) value for the number specified by +count+ from the stack.
|
538
578
|
def pop_string(count = 1)
|
539
|
-
s =
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
579
|
+
s =
|
580
|
+
stack
|
581
|
+
.pop(count)
|
582
|
+
.map do |s|
|
583
|
+
case s
|
584
|
+
when Numeric
|
585
|
+
Script.encode_number(s)
|
586
|
+
else
|
587
|
+
s
|
588
|
+
end
|
589
|
+
end
|
547
590
|
count == 1 ? s.first : s
|
548
591
|
end
|
549
592
|
|
@@ -555,15 +598,13 @@ module Tapyrus
|
|
555
598
|
# see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L36-L49
|
556
599
|
def cast_to_bool(v)
|
557
600
|
case v
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
else
|
566
|
-
false
|
601
|
+
when Numeric
|
602
|
+
return v != 0
|
603
|
+
when String
|
604
|
+
v.each_byte.with_index { |b, i| return !(i == (v.bytesize - 1) && b == 0x80) unless b == 0 }
|
605
|
+
false
|
606
|
+
else
|
607
|
+
false
|
567
608
|
end
|
568
609
|
end
|
569
610
|
|
@@ -616,7 +657,7 @@ module Tapyrus
|
|
616
657
|
return opcode == data.bytesize
|
617
658
|
elsif data.bytesize <= 255
|
618
659
|
return opcode == OP_PUSHDATA1
|
619
|
-
elsif data.bytesize <=
|
660
|
+
elsif data.bytesize <= 65_535
|
620
661
|
return opcode == OP_PUSHDATA2
|
621
662
|
end
|
622
663
|
true
|
@@ -626,22 +667,21 @@ module Tapyrus
|
|
626
667
|
buf = StringIO.new(chunk)
|
627
668
|
opcode = buf.read(1).ord
|
628
669
|
offset = 1
|
629
|
-
len =
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
670
|
+
len =
|
671
|
+
case opcode
|
672
|
+
when OP_PUSHDATA1
|
673
|
+
offset += 1
|
674
|
+
buf.read(1).unpack('C').first
|
675
|
+
when OP_PUSHDATA2
|
676
|
+
offset += 2
|
677
|
+
buf.read(2).unpack('v').first
|
678
|
+
when OP_PUSHDATA4
|
679
|
+
offset += 4
|
680
|
+
buf.read(4).unpack('V').first
|
681
|
+
else
|
682
|
+
opcode
|
683
|
+
end
|
642
684
|
chunk.bytesize == len + offset
|
643
685
|
end
|
644
|
-
|
645
686
|
end
|
646
|
-
|
647
|
-
end
|
687
|
+
end
|