tapyrus 0.3.1 → 0.3.2

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