tapyrus 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc2e90197905239fab7d891f05da735cf1fb2df0127abbb1ca38c1299130914d
4
- data.tar.gz: 36a9a028b360a238077bd30c2380b3c722983d5dbd7e5f5d9e6e1d2dbd1408c0
3
+ metadata.gz: bfdff40e4807919098065d5d956383663673f20a264d0eaadaa89124467166f8
4
+ data.tar.gz: 731e768f37f843fc6a03d337e8aea349d57f23449068356348d2bef171d02a2f
5
5
  SHA512:
6
- metadata.gz: e336315f9e68a361edf4b0e2dec290a6e37f666375badc13336391289b773084d57ac324fa5e1b3334faa6400d931abd24dda05345901767a29e9f11251200bc
7
- data.tar.gz: a1a3df8abf276b794eed62cd7b238d57fa909315f116d5f12c4b472e32b24c8da1ff8cc62e656fb985cf5728e24a7821580b1f24f09613535a2be4b23f3bf1fa
6
+ metadata.gz: 0360ede886fe0a3512ea68e212270b5d931b8c085ceb04c6790978e325bc98b1d112d74deaa80232c97109c50ad8b1175566cb04ccd5324a218563677d90fbbd
7
+ data.tar.gz: 3f6f38a994e81c0b1d5157584cf14669ee7f8be612a6e878bf7cc216995ca60255a133b1b318e6210d6328015fdd99434bde021fd124bdc44454da564023ddab
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Tapyrusrb [![Build Status](https://travis-ci.org/chaintope/tapyrusrb.svg?branch=master)](https://travis-ci.org/chaintope/tapyrusrb) [![Gem Version](https://badge.fury.io/rb/tapyrus.svg)](https://badge.fury.io/rb/tapyrus) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
1
+ # Tapyrusrb [![Build Status](https://github.com/chaintope/tapyrusrb/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/chaintope/tapyrusrb/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/tapyrus.svg)](https://badge.fury.io/rb/tapyrus) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
2
2
 
3
3
  Tapyrusrb is a Ruby implementation of [Tapyrus](https://github.com/chaintope/tapyrus-core) Protocol.
4
4
 
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tapyrus'
4
+ require 'terminal-table'
5
+
6
+ class EmptyTxChecker
7
+ def check_sig(script_sig, pubkey, script_code)
8
+ warn "Signature verification failed. You need to enter tx and input index."
9
+ exit
10
+ end
11
+ def verify_sig(sig, pubkey, digest, allow_hybrid: false)
12
+ warn "Signature verification failed. You need to enter tx and input index."
13
+ exit
14
+ end
15
+ end
16
+
17
+ def run_step(interpreter, script, chunk, index, is_redeem_script)
18
+ if chunk.pushdata?
19
+ puts "PUSH #{chunk.pushed_data.bth}"
20
+ else
21
+ puts "APPLY #{Tapyrus::Opcodes.opcode_to_name(chunk.opcode)}"
22
+ end
23
+ result = interpreter.next_step(script, chunk, index, is_redeem_script)
24
+ if result.is_a?(FalseClass)
25
+ warn "script failed. Reason: #{interpreter.error}"
26
+ exit
27
+ end
28
+ rows = interpreter.stack.map{|s|[s]}.reverse
29
+ table = Terminal::Table.new(title: 'Current Stack', rows: rows )
30
+ puts table
31
+ end
32
+
33
+ print "Enter scriptPubkey: "
34
+ script_pubkey_hex = gets.chomp
35
+ script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey_hex.htb)
36
+ puts script_pubkey unless script_pubkey_hex.empty?
37
+
38
+ print "Enter scriptSig: "
39
+ script_sig_hex = gets.chomp
40
+ script_sig = Tapyrus::Script.parse_from_payload(script_sig_hex.htb)
41
+ puts script_sig unless script_sig_hex.empty?
42
+
43
+ unless script_sig.push_only?
44
+ warn "scriptSig has non-push opcode."
45
+ puts
46
+ end
47
+
48
+ if script_pubkey_hex.empty? && script_sig.empty?
49
+ puts "Empty script."
50
+ exit
51
+ end
52
+
53
+ print "Enter tx: "
54
+ tx_hex = gets.chomp
55
+ if tx_hex.length == 0
56
+ tx_checker = EmptyTxChecker.new
57
+ else
58
+ print "Enter index of the input: "
59
+ input_index = gets.chomp
60
+ tx_checker = Tapyrus::TxChecker.new
61
+ begin
62
+ tx_checker.tx = Tapyrus::Tx.parse_from_payload(tx_hex.htb)
63
+ rescue StandardError
64
+ warn "Invalid tx data."
65
+ exit
66
+ end
67
+ if input_index.empty?
68
+ warn "Index of input missing."
69
+ exit
70
+ end
71
+ tx_checker.input_index = input_index.to_i
72
+ if (tx_checker.tx.in.size - 1) < tx_checker.input_index
73
+ warn "Tx does not have #{tx_checker.input_index}-th input."
74
+ exit
75
+ end
76
+ end
77
+
78
+ interpreter = Tapyrus::ScriptInterpreter.new(checker: tx_checker)
79
+
80
+ target_script = script_sig
81
+ is_redeem = false
82
+ interpreter.reset_params
83
+ chunks = target_script.chunks.each
84
+ chunk_index = 0
85
+ chunks_size = target_script.chunks.length
86
+ stack_copy = nil
87
+
88
+ puts "The Script is ready to be executed; you can step execution it by putting the Enter key."
89
+ print "> "
90
+ while cmd = gets.chomp
91
+ if cmd.length == 0
92
+ if chunks_size == chunk_index
93
+ if target_script == script_sig
94
+ stack_copy = interpreter.stack.dup
95
+ target_script = script_pubkey
96
+ interpreter.reset_params
97
+ chunks = target_script.chunks.each
98
+ chunk_index = 0
99
+ chunks_size = target_script.chunks.length
100
+ elsif target_script == script_pubkey
101
+ if interpreter.stack.empty? || !interpreter.cast_to_bool(interpreter.stack.last.htb)
102
+ warn "Script evaluated without error but finished with a false/empty top stack element"
103
+ exit
104
+ end
105
+ if script_pubkey.p2sh?
106
+ interpreter.stack = stack_copy
107
+ redeem_script = Tapyrus::Script.parse_from_payload(interpreter.stack.pop.htb)
108
+ puts "APPLY P2SH: #{redeem_script}"
109
+ rows = interpreter.stack.map{|s|[s]}.reverse
110
+ table = Terminal::Table.new(title: 'Current Stack', rows: rows )
111
+ puts table
112
+ target_script = redeem_script
113
+ chunks = target_script.chunks.each
114
+ chunk_index = 0
115
+ chunks_size = target_script.chunks.length
116
+ else
117
+ puts "Execution finished."
118
+ exit
119
+ end
120
+ else
121
+ puts "Execution finished."
122
+ exit
123
+ end
124
+ end
125
+ run_step(interpreter, target_script, chunks.next, chunk_index, is_redeem)
126
+ chunk_index += 1
127
+ else
128
+ puts "Put enter key to step execution or Ctrl+D to exit."
129
+ end
130
+ print "> "
131
+ end
@@ -104,7 +104,7 @@ module Tapyrus
104
104
  end
105
105
  end
106
106
 
107
- # Check this header contains upgrade aggregated publiec key.
107
+ # Check this header contains upgrade aggregated public key.
108
108
  # @return [Boolean] if contains return true, otherwise false.
109
109
  def upgrade_agg_pubkey?
110
110
  x_field_type == X_FILED_TYPES[:aggregate_pubkey]
@@ -94,8 +94,6 @@ module Tapyrus
94
94
  # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
95
95
  MAX_OP_RETURN_RELAY = 83
96
96
 
97
- SIG_VERSION = [:base]
98
-
99
97
  # for script error
100
98
  SCRIPT_ERR_OK = 0
101
99
  SCRIPT_ERR_UNKNOWN_ERROR = 1
@@ -2,13 +2,19 @@ module Tapyrus
2
2
  class ScriptInterpreter
3
3
  include Tapyrus::Opcodes
4
4
 
5
- attr_reader :stack
5
+ attr_accessor :stack
6
6
  attr_reader :debug
7
7
  attr_reader :flags
8
8
  attr_accessor :error
9
9
  attr_reader :checker
10
10
  attr_reader :require_minimal
11
11
 
12
+ attr_accessor :flow_stack
13
+ attr_accessor :alt_stack
14
+ attr_accessor :last_code_separator_index
15
+ attr_accessor :op_count
16
+ attr_accessor :color_id
17
+
12
18
  DISABLE_OPCODES = [
13
19
  OP_CAT,
14
20
  OP_SUBSTR,
@@ -51,11 +57,11 @@ module Tapyrus
51
57
 
52
58
  stack_copy = nil
53
59
 
54
- return false unless eval_script(script_sig, :base, false)
60
+ return false unless eval_script(script_sig, false)
55
61
 
56
62
  stack_copy = stack.dup if flag?(SCRIPT_VERIFY_P2SH)
57
63
 
58
- return false unless eval_script(script_pubkey, :base, false)
64
+ return false unless eval_script(script_pubkey, false)
59
65
 
60
66
  return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last.htb)
61
67
 
@@ -70,7 +76,7 @@ module Tapyrus
70
76
  rescue Exception => e
71
77
  return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
72
78
  end
73
- return false unless eval_script(redeem_script, :base, true)
79
+ return false unless eval_script(redeem_script, true)
74
80
  return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last)
75
81
  end
76
82
 
@@ -92,457 +98,473 @@ module Tapyrus
92
98
  false
93
99
  end
94
100
 
95
- def eval_script(script, sig_version, is_redeem_script)
101
+ def eval_script(script, is_redeem_script)
96
102
  return set_error(SCRIPT_ERR_SCRIPT_SIZE) if script.size > MAX_SCRIPT_SIZE
97
103
  begin
98
- flow_stack = []
99
- alt_stack = []
100
- last_code_separator_index = 0
101
- op_count = 0
102
- color_id = nil
103
-
104
- script.chunks.each_with_index do |c, index|
105
- need_exec = !flow_stack.include?(false)
104
+ reset_params
105
+ script.chunks.each_with_index do |chunk, index|
106
+ result = next_step(script, chunk, index, is_redeem_script)
107
+ return result if result.is_a?(FalseClass)
108
+ end
109
+ rescue Exception => e
110
+ puts e
111
+ puts e.backtrace
112
+ return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
113
+ end
106
114
 
107
- return set_error(SCRIPT_ERR_PUSH_SIZE) if c.pushdata? && c.pushed_data.bytesize > MAX_SCRIPT_ELEMENT_SIZE
115
+ return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) unless @flow_stack.empty?
108
116
 
109
- opcode = c.opcode
117
+ set_error(SCRIPT_ERR_OK)
118
+ true
119
+ end
110
120
 
111
- if need_exec && c.pushdata?
112
- return set_error(SCRIPT_ERR_MINIMALDATA) if require_minimal && !minimal_push?(c.pushed_data, opcode)
113
- return set_error(SCRIPT_ERR_BAD_OPCODE) unless verify_pushdata_length(c)
114
- stack << c.pushed_data.bth
115
- else
116
- if opcode > OP_16 && (op_count += 1) > MAX_OPS_PER_SCRIPT
117
- return set_error(SCRIPT_ERR_OP_COUNT)
118
- end
119
- return set_error(SCRIPT_ERR_DISABLED_OPCODE) if DISABLE_OPCODES.include?(opcode)
120
- if opcode == OP_CODESEPARATOR && sig_version == :base && flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE)
121
- return set_error(SCRIPT_ERR_OP_CODESEPARATOR)
122
- end
123
- next unless (need_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
124
- small_int = Opcodes.opcode_to_small_int(opcode)
125
- if small_int && opcode != OP_0
126
- push_int(small_int)
127
- else
128
- case opcode
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)
143
- end
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)
170
- end
171
- end
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)
318
- 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)
367
- end
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
121
+ def next_step(script, c, index, is_redeem_script)
122
+ need_exec = !@flow_stack.include?(false)
379
123
 
380
- success = checker.check_sig(sig, pubkey, subscript, sig_version)
124
+ return set_error(SCRIPT_ERR_PUSH_SIZE) if c.pushdata? && c.pushed_data.bytesize > MAX_SCRIPT_ELEMENT_SIZE
381
125
 
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
126
+ opcode = c.opcode
386
127
 
387
- push_int(success ? 1 : 0)
128
+ if need_exec && c.pushdata?
129
+ return set_error(SCRIPT_ERR_MINIMALDATA) if require_minimal && !minimal_push?(c.pushed_data, opcode)
130
+ return set_error(SCRIPT_ERR_BAD_OPCODE) unless verify_pushdata_length(c)
131
+ stack << c.pushed_data.bth
132
+ else
133
+ if opcode > OP_16 && (@op_count += 1) > MAX_OPS_PER_SCRIPT
134
+ return set_error(SCRIPT_ERR_OP_COUNT)
135
+ end
136
+ return set_error(SCRIPT_ERR_DISABLED_OPCODE) if DISABLE_OPCODES.include?(opcode)
137
+ if opcode == OP_CODESEPARATOR && flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE)
138
+ return set_error(SCRIPT_ERR_OP_CODESEPARATOR)
139
+ end
388
140
 
389
- if opcode == OP_CHECKSIGVERIFY
390
- if success
391
- stack.pop
392
- else
393
- return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
394
- end
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
141
+ # next unless (need_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
142
+ return unless (need_exec || (OP_IF <= opcode && opcode <= OP_ENDIF))
143
+ small_int = Opcodes.opcode_to_small_int(opcode)
144
+ if small_int && opcode != OP_0
145
+ push_int(small_int)
146
+ else
147
+ case opcode
148
+ when OP_0
149
+ stack << ''
150
+ when OP_DEPTH
151
+ push_int(stack.size)
152
+ when OP_EQUAL, OP_EQUALVERIFY
153
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
154
+ a, b = pop_string(2)
155
+ result = a == b
156
+ push_int(result ? 1 : 0)
157
+ if opcode == OP_EQUALVERIFY
158
+ if result
159
+ stack.pop
160
+ else
161
+ return set_error(SCRIPT_ERR_EQUALVERIFY)
162
+ end
163
+ end
164
+ when OP_0NOTEQUAL
165
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
166
+ push_int(pop_int == 0 ? 0 : 1)
167
+ when OP_ADD
168
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
169
+ a, b = pop_int(2)
170
+ push_int(a + b)
171
+ when OP_1ADD
172
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
173
+ push_int(pop_int + 1)
174
+ when OP_SUB
175
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
176
+ a, b = pop_int(2)
177
+ push_int(a - b)
178
+ when OP_1SUB
179
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
180
+ push_int(pop_int - 1)
181
+ when OP_IF, OP_NOTIF
182
+ result = false
183
+ if need_exec
184
+ return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if stack.size < 1
185
+ value = pop_string.htb
186
+ if flag?(SCRIPT_VERIFY_MINIMALIF)
187
+ if value.bytesize > 1 || (value.bytesize == 1 && value[0].unpack('C').first != 1)
188
+ return set_error(SCRIPT_ERR_MINIMALIF)
416
189
  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
190
+ end
191
+ result = cast_to_bool(value)
192
+ result = !result if opcode == OP_NOTIF
193
+ end
194
+ @flow_stack << result
195
+ when OP_ELSE
196
+ return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if @flow_stack.size < 1
197
+ @flow_stack << !@flow_stack.pop
198
+ when OP_ENDIF
199
+ return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if @flow_stack.empty?
200
+ @flow_stack.pop
201
+ when OP_NOP
202
+ when OP_NOP1, OP_NOP4..OP_NOP10
203
+ return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS) if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
204
+ when OP_CHECKLOCKTIMEVERIFY
205
+ # next unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
206
+ return unless flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
207
+
208
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
209
+
210
+ # Note that elsewhere numeric opcodes are limited to operands in the range -2**31+1 to 2**31-1,
211
+ # however it is legal for opcodes to produce results exceeding that range.
212
+ # This limitation is implemented by CScriptNum's default 4-byte limit.
213
+ # If we kept to that limit we'd have a year 2038 problem,
214
+ # even though the nLockTime field in transactions themselves is uint32 which only becomes meaningless after the year 2106.
215
+ # Thus as a special case we tell CScriptNum to accept up to 5-byte bignums,
216
+ # which are good until 2**39-1, well beyond the 2**32-1 limit of the nLockTime field itself.
217
+ locktime = cast_to_int(stack.last, 5)
218
+ return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if locktime < 0
219
+ return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_locktime(locktime)
220
+ when OP_CHECKSEQUENCEVERIFY
221
+ # next unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
222
+ return unless flag?(SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
223
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
224
+
225
+ # nSequence, like nLockTime, is a 32-bit unsigned integer field.
226
+ # See the comment in CHECKLOCKTIMEVERIFY regarding 5-byte numeric operands.
227
+ sequence = cast_to_int(stack.last, 5)
228
+
229
+ # In the rare event that the argument may be < 0 due to some arithmetic being done first,
230
+ # you can always use 0 MAX CHECKSEQUENCEVERIFY.
231
+ return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME) if sequence < 0
232
+
233
+ # To provide for future soft-fork extensibility,
234
+ # if the operand has the disabled lock-time flag set, CHECKSEQUENCEVERIFY behaves as a NOP.
235
+ # next if (sequence & Tapyrus::TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0
236
+ return if (sequence & Tapyrus::TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0
237
+
238
+ # Compare the specified sequence number with the input.
239
+ return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME) unless checker.check_sequence(sequence)
240
+ when OP_DUP
241
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
242
+ stack << stack.last
243
+ when OP_2DUP
244
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
245
+ 2.times { stack << stack[-2] }
246
+ when OP_3DUP
247
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
248
+ 3.times { stack << stack[-3] }
249
+ when OP_IFDUP
250
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
251
+ stack << stack.last if cast_to_bool(stack.last)
252
+ when OP_RIPEMD160
253
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
254
+ stack << Digest::RMD160.hexdigest(pop_string.htb)
255
+ when OP_SHA1
256
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
257
+ stack << Digest::SHA1.hexdigest(pop_string.htb)
258
+ when OP_SHA256
259
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
260
+ stack << Digest::SHA256.hexdigest(pop_string.htb)
261
+ when OP_HASH160
262
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
263
+ stack << Tapyrus.hash160(pop_string)
264
+ when OP_HASH256
265
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
266
+ stack << Tapyrus.double_sha256(pop_string.htb).bth
267
+ when OP_VERIFY
268
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
269
+ return set_error(SCRIPT_ERR_VERIFY) unless pop_bool
270
+ when OP_TOALTSTACK
271
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
272
+ @alt_stack << stack.pop
273
+ when OP_FROMALTSTACK
274
+ return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION) if @alt_stack.size < 1
275
+ stack << @alt_stack.pop
276
+ when OP_DROP
277
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
278
+ stack.pop
279
+ when OP_2DROP
280
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
281
+ 2.times { stack.pop }
282
+ when OP_NIP
283
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
284
+ stack.delete_at(-2)
285
+ when OP_OVER
286
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
287
+ stack << stack[-2]
288
+ when OP_2OVER
289
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
290
+ 2.times { stack << stack[-4] }
291
+ when OP_PICK, OP_ROLL
292
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
293
+ pos = pop_int
294
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if pos < 0 || pos >= stack.size
295
+ stack << stack[-pos - 1]
296
+ stack.delete_at(-pos - 2) if opcode == OP_ROLL
297
+ when OP_ROT
298
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
299
+ stack << stack[-3]
300
+ stack.delete_at(-4)
301
+ when OP_2ROT
302
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 6
303
+ 2.times { stack << stack[-6] }
304
+ 2.times { stack.delete_at(-7) }
305
+ when OP_SWAP
306
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
307
+ tmp = stack.last
308
+ stack[-1] = stack[-2]
309
+ stack[-2] = tmp
310
+ when OP_2SWAP
311
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 4
312
+ 2.times { stack << stack[-4] }
313
+ 2.times { stack.delete_at(-5) }
314
+ when OP_TUCK
315
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
316
+ stack.insert(-3, stack.last)
317
+ when OP_ABS
318
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
319
+ v = pop_int
320
+ push_int(v.abs)
321
+ when OP_BOOLAND
322
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
323
+ a, b = pop_int(2)
324
+ push_int((!a.zero? && !b.zero?) ? 1 : 0)
325
+ when OP_BOOLOR
326
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
327
+ a, b = pop_int(2)
328
+ push_int((!a.zero? || !b.zero?) ? 1 : 0)
329
+ when OP_NUMEQUAL, OP_NUMEQUALVERIFY
330
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
331
+ a, b = pop_int(2)
332
+ result = a == b
333
+ push_int(result ? 1 : 0)
334
+ if opcode == OP_NUMEQUALVERIFY
335
+ if result
336
+ stack.pop
337
+ else
338
+ return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
339
+ end
340
+ end
341
+ when OP_LESSTHAN, OP_LESSTHANOREQUAL
342
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
343
+ a, b = pop_int(2)
344
+ push_int(a < b ? 1 : 0) if opcode == OP_LESSTHAN
345
+ push_int(a <= b ? 1 : 0) if opcode == OP_LESSTHANOREQUAL
346
+ when OP_GREATERTHAN, OP_GREATERTHANOREQUAL
347
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
348
+ a, b = pop_int(2)
349
+ push_int(a > b ? 1 : 0) if opcode == OP_GREATERTHAN
350
+ push_int(a >= b ? 1 : 0) if opcode == OP_GREATERTHANOREQUAL
351
+ when OP_MIN
352
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
353
+ push_int(pop_int(2).min)
354
+ when OP_MAX
355
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
356
+ push_int(pop_int(2).max)
357
+ when OP_WITHIN
358
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
359
+ x, a, b = pop_int(3)
360
+ push_int((a <= x && x < b) ? 1 : 0)
361
+ when OP_NOT
362
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
363
+ push_int(pop_int == 0 ? 1 : 0)
364
+ when OP_SIZE
365
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
366
+ item = stack.last
367
+ item = Tapyrus::Script.encode_number(item) if item.is_a?(Numeric)
368
+ size = item.htb.bytesize
369
+ push_int(size)
370
+ when OP_NEGATE
371
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
372
+ push_int(-pop_int)
373
+ when OP_NUMNOTEQUAL
374
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
375
+ a, b = pop_int(2)
376
+ push_int(a == b ? 0 : 1)
377
+ when OP_CODESEPARATOR
378
+ @last_code_separator_index = index + 1
379
+ when OP_CHECKSIG, OP_CHECKSIGVERIFY
380
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 2
381
+ sig, pubkey = pop_string(2)
382
+
383
+ subscript = script.subscript(@last_code_separator_index..-1)
384
+ tmp = subscript.find_and_delete(Script.new << sig)
385
+ return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
386
+ subscript = tmp
387
+
388
+ if (
389
+ if sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE
390
+ !check_schnorr_signature_encoding(sig)
391
+ else
392
+ !check_ecdsa_signature_encoding(sig)
393
+ end
394
+ ) || !check_pubkey_encoding(pubkey)
395
+ return false
396
+ end
424
397
 
425
- return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < pubkey_count
398
+ success = checker.check_sig(sig, pubkey, subscript)
426
399
 
427
- pubkeys = pop_string(pubkey_count)
428
- pubkeys = [pubkeys] if pubkeys.is_a?(String)
400
+ # https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#NULLFAIL
401
+ return set_error(SCRIPT_ERR_SIG_NULLFAIL) if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
429
402
 
430
- return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
403
+ push_int(success ? 1 : 0)
431
404
 
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)
405
+ if opcode == OP_CHECKSIGVERIFY
406
+ if success
407
+ stack.pop
408
+ else
409
+ return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
410
+ end
411
+ end
412
+ when OP_CHECKDATASIG, OP_CHECKDATASIGVERIFY
413
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
414
+ sig, msg, pubkey = pop_string(3)
415
+
416
+ # check signature encoding without hashtype byte
417
+ if (
418
+ sig.htb.bytesize != (Tapyrus::Key::COMPACT_SIGNATURE_SIZE - 1) &&
419
+ !check_ecdsa_signature_encoding(sig, true)
420
+ ) || !check_pubkey_encoding(pubkey)
421
+ return false
422
+ end
423
+ digest = Tapyrus.sha256(msg)
424
+ success = checker.verify_sig(sig, pubkey, digest)
425
+ return set_error(SCRIPT_ERR_SIG_NULLFAIL) if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
426
+ push_int(success ? 1 : 0)
427
+ if opcode == OP_CHECKDATASIGVERIFY
428
+ stack.pop if success
429
+ return set_error(SCRIPT_ERR_CHECKDATASIGVERIFY) unless success
430
+ end
431
+ when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
432
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
433
+ pubkey_count = pop_int
434
+ return set_error(SCRIPT_ERR_PUBKEY_COUNT) unless (0..MAX_PUBKEYS_PER_MULTISIG).include?(pubkey_count)
435
435
 
436
- sigs = pop_string(sig_count)
437
- sigs = [sigs] if sigs.is_a?(String)
436
+ @op_count += pubkey_count
437
+ return set_error(SCRIPT_ERR_OP_COUNT) if @op_count > MAX_OPS_PER_SCRIPT
438
438
 
439
- subscript = script.subscript(last_code_separator_index..-1)
439
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < pubkey_count
440
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)
446
- end
447
- subscript = tmp
448
- end
449
- end
441
+ pubkeys = pop_string(pubkey_count)
442
+ pubkeys = [pubkeys] if pubkeys.is_a?(String)
450
443
 
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
476
- end
477
- pubkey_count -= 1
478
- success = false if sig_count > pubkey_count
479
- end
444
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
480
445
 
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
485
- end
486
- end
446
+ sig_count = pop_int
447
+ return set_error(SCRIPT_ERR_SIG_COUNT) if sig_count < 0 || sig_count > pubkey_count
448
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < (sig_count)
487
449
 
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
450
+ sigs = pop_string(sig_count)
451
+ sigs = [sigs] if sigs.is_a?(String)
493
452
 
494
- stack.pop
453
+ subscript = script.subscript(@last_code_separator_index..-1)
495
454
 
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)
502
- end
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
509
-
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
455
+ sigs.each do |sig|
456
+ tmp = subscript.find_and_delete(Script.new << sig)
457
+ if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
458
+ return set_error(SCRIPT_ERR_SIG_FINDANDDELETE)
459
+ end
460
+ subscript = tmp
461
+ end
514
462
 
515
- # color id is not allowed inside OP_IF
516
- return set_error(SCRIPT_ERR_OP_COLOR_IN_BRANCH) unless flow_stack.empty?
463
+ success = true
464
+ current_sig_scheme = nil
465
+ while success && sig_count > 0
466
+ sig = sigs.pop
467
+ pubkey = pubkeys.pop
468
+ sig_scheme = sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE ? :schnorr : :ecdsa
469
+ current_sig_scheme = sig_scheme if current_sig_scheme.nil?
470
+
471
+ if (
472
+ if sig_scheme == :schnorr
473
+ !check_schnorr_signature_encoding(sig)
474
+ else
475
+ !check_ecdsa_signature_encoding(sig)
476
+ end
477
+ ) || !check_pubkey_encoding(pubkey)
478
+ return false
479
+ end # error already set.
480
+
481
+ return set_error(SCRIPT_ERR_MIXED_SCHEME_MULTISIG) unless sig_scheme == current_sig_scheme
482
+
483
+ ok = checker.check_sig(sig, pubkey, subscript)
484
+ if ok
485
+ sig_count -= 1
486
+ else
487
+ sigs << sig
488
+ end
489
+ pubkey_count -= 1
490
+ success = false if sig_count > pubkey_count
491
+ end
517
492
 
518
- # pop one stack element and verify that it exists
519
- return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
493
+ if !success && flag?(SCRIPT_VERIFY_NULLFAIL)
494
+ sigs.each do |sig|
495
+ # If the operation failed, we require that all signatures must be empty vector
496
+ return set_error(SCRIPT_ERR_SIG_NULLFAIL) if sig.bytesize > 0
497
+ end
498
+ end
520
499
 
521
- color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(stack.last.htb)
500
+ # A bug causes CHECKMULTISIG to consume one extra argument whose contents were not checked in any way.
501
+ # Unfortunately this is a potential source of mutability,
502
+ # so optionally verify it is exactly equal to zero prior to removing it from the stack.
503
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
504
+ return set_error(SCRIPT_ERR_SIG_NULLDUMMY) if stack[-1].size > 0
522
505
 
523
- # check ColorIdentifier is valid
524
- return set_error(SCRIPT_ERR_OP_COLOR_ID_INVALID) unless color_id.valid?
506
+ stack.pop
525
507
 
508
+ push_int(success ? 1 : 0)
509
+ if opcode == OP_CHECKMULTISIGVERIFY
510
+ if success
526
511
  stack.pop
527
512
  else
528
- return set_error(SCRIPT_ERR_BAD_OPCODE)
513
+ return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
529
514
  end
530
515
  end
531
- end
516
+ when OP_RETURN
517
+ return set_error(SCRIPT_ERR_OP_RETURN)
518
+ when OP_COLOR
519
+ # Color id is not permitted in p2sh redeem script
520
+ return set_error(SCRIPT_ERR_OP_COLOR_UNEXPECTED) if is_redeem_script
521
+
522
+ # if Color id is already initialized this must be an extra
523
+ if @color_id && @color_id.type != Tapyrus::Color::TokenTypes::NONE
524
+ return set_error(SCRIPT_ERR_OP_COLOR_MULTIPLE)
525
+ end
526
+
527
+ # color id is not allowed inside OP_IF
528
+ return set_error(SCRIPT_ERR_OP_COLOR_IN_BRANCH) unless @flow_stack.empty?
532
529
 
533
- # max stack size check
534
- return set_error(SCRIPT_ERR_STACK_SIZE) if stack.size + alt_stack.size > MAX_STACK_SIZE
530
+ # pop one stack element and verify that it exists
531
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
532
+
533
+ @color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(stack.last.htb)
534
+
535
+ # check ColorIdentifier is valid
536
+ return set_error(SCRIPT_ERR_OP_COLOR_ID_INVALID) unless @color_id.valid?
537
+
538
+ stack.pop
539
+ else
540
+ return set_error(SCRIPT_ERR_BAD_OPCODE)
541
+ end
535
542
  end
536
- rescue Exception => e
537
- puts e
538
- puts e.backtrace
539
- return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
540
543
  end
541
544
 
542
- return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) unless flow_stack.empty?
545
+ # max stack size check
546
+ return set_error(SCRIPT_ERR_STACK_SIZE) if stack.size + @alt_stack.size > MAX_STACK_SIZE
547
+ end
543
548
 
544
- set_error(SCRIPT_ERR_OK)
545
- true
549
+ def reset_params
550
+ @flow_stack = []
551
+ @alt_stack = []
552
+ @last_code_separator_index = 0
553
+ @op_count = 0
554
+ @color_id = nil
555
+ end
556
+
557
+ # see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L36-L49
558
+ def cast_to_bool(v)
559
+ case v
560
+ when Numeric
561
+ return v != 0
562
+ when String
563
+ v.each_byte.with_index { |b, i| return !(i == (v.bytesize - 1) && b == 0x80) unless b == 0 }
564
+ false
565
+ else
566
+ false
567
+ end
546
568
  end
547
569
 
548
570
  private
@@ -595,19 +617,6 @@ module Tapyrus
595
617
  cast_to_bool(pop_string.htb)
596
618
  end
597
619
 
598
- # see https://github.com/bitcoin/bitcoin/blob/master/src/script/interpreter.cpp#L36-L49
599
- def cast_to_bool(v)
600
- case v
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
608
- end
609
- end
610
-
611
620
  def check_ecdsa_signature_encoding(sig, data_sig = false)
612
621
  return true if sig.size.zero?
613
622
  if !Key.valid_signature_encoding?(sig.htb, data_sig)
@@ -1,12 +1,10 @@
1
1
  module Tapyrus
2
2
  class TxChecker
3
- attr_reader :tx
4
- attr_reader :input_index
5
- attr_reader :amount
3
+ attr_accessor :tx
4
+ attr_accessor :input_index
6
5
 
7
- def initialize(tx: nil, amount: 0, input_index: nil)
6
+ def initialize(tx: nil, input_index: nil)
8
7
  @tx = tx
9
- @amount = amount
10
8
  @input_index = input_index
11
9
  end
12
10
 
@@ -14,15 +12,13 @@ module Tapyrus
14
12
  # @param [String] script_sig a signature with hex format.
15
13
  # @param [String] pubkey a public key with hex format.
16
14
  # @param [Tapyrus::Script] script_code
17
- # @param [Integer] sig_version
18
15
  # @return [Boolean] if check is passed return true, otherwise false.
19
- def check_sig(script_sig, pubkey, script_code, sig_version)
16
+ def check_sig(script_sig, pubkey, script_code)
20
17
  return false if script_sig.empty?
21
18
  script_sig = script_sig.htb
22
19
  hash_type = script_sig[-1].unpack('C').first
23
20
  sig = script_sig[0..-2]
24
- sighash =
25
- tx.sighash_for_input(input_index, script_code, hash_type: hash_type, amount: amount, sig_version: sig_version)
21
+ sighash = tx.sighash_for_input(input_index, script_code, hash_type: hash_type)
26
22
  verify_sig(sig.bth, pubkey, sighash)
27
23
  end
28
24
 
data/lib/tapyrus/tx.rb CHANGED
@@ -103,34 +103,23 @@ module Tapyrus
103
103
  to_payload.bytesize
104
104
  end
105
105
 
106
- # get signature hash
106
+ # Generate signature hash
107
107
  # @param [Integer] input_index input index.
108
108
  # @param [Integer] hash_type signature hash type
109
- # @param [Tapyrus::Script] output_script script pubkey or script code. if script pubkey is P2WSH, set witness script to this.
110
- # @param [Integer] amount tapyrus amount locked in input. required for witness input only.
111
- # @param [Integer] skip_separator_index If output_script is P2WSH and output_script contains any OP_CODESEPARATOR,
112
- # the script code needs is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed.
113
- def sighash_for_input(
114
- input_index,
115
- output_script,
116
- hash_type: SIGHASH_TYPE[:all],
117
- sig_version: :base,
118
- amount: nil,
119
- skip_separator_index: 0
120
- )
109
+ # @param [Tapyrus::Script] output_script script pubkey or script code. if script pubkey is P2SH, set redeem script to this.
110
+ # @return [String] sighash
111
+ def sighash_for_input(input_index, output_script, hash_type: SIGHASH_TYPE[:all])
121
112
  raise ArgumentError, 'input_index must be specified.' unless input_index
122
113
  raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
123
114
  raise ArgumentError, 'script_pubkey must be specified.' unless output_script
124
- raise ArgumentError, 'unsupported sig version specified.' unless SIG_VERSION.include?(sig_version)
125
115
  sighash_for_legacy(input_index, output_script, hash_type)
126
116
  end
127
117
 
128
118
  # verify input signature.
129
119
  # @param [Integer] input_index
130
120
  # @param [Tapyrus::Script] script_pubkey the script pubkey for target input.
131
- # @param [Integer] amount the amount of tapyrus, require for witness program only.
132
121
  # @param [Array] flags the flags used when execute script interpreter.
133
- def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
122
+ def verify_input_sig(input_index, script_pubkey, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
134
123
  flags << SCRIPT_VERIFY_P2SH if script_pubkey.p2sh?
135
124
  verify_input_sig_for_legacy(input_index, script_pubkey, flags)
136
125
  end
@@ -207,7 +196,9 @@ module Tapyrus
207
196
  checker = Tapyrus::TxChecker.new(tx: self, input_index: input_index)
208
197
  interpreter = Tapyrus::ScriptInterpreter.new(checker: checker, flags: flags)
209
198
 
210
- interpreter.verify_script(script_sig, script_pubkey)
199
+ result = interpreter.verify_script(script_sig, script_pubkey)
200
+ warn("Verify failed. Cause: #{interpreter.error}") unless result
201
+ result
211
202
  end
212
203
  end
213
204
  end
@@ -89,9 +89,11 @@ module Tapyrus
89
89
  script_pubkey = script_pubkey.add_color(color_id)
90
90
  end
91
91
 
92
+ output = Tapyrus::TxOut.new(script_pubkey: script_pubkey, value: value)
93
+ raise ArgumentError, 'The transaction amount is too small' if color_id.default? && output.dust?
92
94
  @outgoings[color_id] ||= 0
93
95
  @outgoings[color_id] += value
94
- @outputs << Tapyrus::TxOut.new(script_pubkey: script_pubkey, value: value)
96
+ @outputs << output
95
97
  self
96
98
  end
97
99
 
@@ -136,13 +138,16 @@ module Tapyrus
136
138
  def add_change(tx)
137
139
  @incomings.each do |color_id, in_amount|
138
140
  out_amount = @outgoings[color_id] || 0
139
- change, script_pubkey =
141
+ output =
140
142
  if color_id.default?
141
- [in_amount - out_amount - estimated_fee, @change_script_pubkey]
143
+ change = in_amount - out_amount - estimated_fee
144
+ change_output = Tapyrus::TxOut.new(script_pubkey: @change_script_pubkey, value: change)
145
+ change_output unless change_output.dust?
142
146
  else
143
- [in_amount - out_amount, @change_script_pubkey.add_color(color_id)]
147
+ change = in_amount - out_amount
148
+ Tapyrus::TxOut.new(script_pubkey: @change_script_pubkey.add_color(color_id), value: change) if change > 0
144
149
  end
145
- tx.outputs << Tapyrus::TxOut.new(script_pubkey: script_pubkey, value: change) if change > 0
150
+ tx.outputs << output if output
146
151
  end
147
152
  end
148
153
 
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = '0.3.1'
2
+ VERSION = '0.3.2'
3
3
  end
data/tapyrusrb.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_runtime_dependency 'siphash'
32
32
  spec.add_runtime_dependency 'activesupport', '>= 5.2.3'
33
33
  spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
34
+ spec.add_runtime_dependency 'terminal-table', '~> 3.0.2'
34
35
 
35
36
  # for options
36
37
  spec.add_development_dependency 'leveldb-native'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapyrus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-23 00:00:00.000000000 Z
11
+ date: 2022-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: 2.3.1
181
+ - !ruby/object:Gem::Dependency
182
+ name: terminal-table
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: 3.0.2
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: 3.0.2
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: leveldb-native
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -280,6 +294,7 @@ description: The implementation of Tapyrus Protocol for Ruby.
280
294
  email:
281
295
  - azuchi@chaintope.com
282
296
  executables:
297
+ - tapyrus-script-debugger
283
298
  - tapyrusrb-cli
284
299
  - tapyrusrbd
285
300
  extensions: []
@@ -299,6 +314,7 @@ files:
299
314
  - Rakefile
300
315
  - bin/console
301
316
  - bin/setup
317
+ - exe/tapyrus-script-debugger
302
318
  - exe/tapyrusrb-cli
303
319
  - exe/tapyrusrbd
304
320
  - lib/openassets.rb
@@ -440,7 +456,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
440
456
  - !ruby/object:Gem::Version
441
457
  version: '0'
442
458
  requirements: []
443
- rubygems_version: 3.3.7
459
+ rubygems_version: 3.3.21
444
460
  signing_key:
445
461
  specification_version: 4
446
462
  summary: The implementation of Tapyrus Protocol for Ruby.