tapyrus 0.2.4 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|