Class: BTC::ScriptInterpreter

Inherits:
Object
  • Object
show all
Includes:
Opcodes, ScriptErrors, ScriptFlags
Defined in:
lib/btcruby/script/script_interpreter.rb

Overview

Script is a stack machine (like Forth) that evaluates a predicate returning a bool indicating valid or not. There are no loops.

Constant Summary

Constants included from Opcodes

Opcodes::OPCODE_NAME_TO_VALUE, Opcodes::OPCODE_VALUE_TO_NAME, Opcodes::OP_0, Opcodes::OP_0NOTEQUAL, Opcodes::OP_1, Opcodes::OP_10, Opcodes::OP_11, Opcodes::OP_12, Opcodes::OP_13, Opcodes::OP_14, Opcodes::OP_15, Opcodes::OP_16, Opcodes::OP_1ADD, Opcodes::OP_1NEGATE, Opcodes::OP_1SUB, Opcodes::OP_2, Opcodes::OP_2DIV, Opcodes::OP_2DROP, Opcodes::OP_2DUP, Opcodes::OP_2MUL, Opcodes::OP_2OVER, Opcodes::OP_2ROT, Opcodes::OP_2SWAP, Opcodes::OP_3, Opcodes::OP_3DUP, Opcodes::OP_4, Opcodes::OP_5, Opcodes::OP_6, Opcodes::OP_7, Opcodes::OP_8, Opcodes::OP_9, Opcodes::OP_ABS, Opcodes::OP_ADD, Opcodes::OP_AND, Opcodes::OP_BOOLAND, Opcodes::OP_BOOLOR, Opcodes::OP_CAT, Opcodes::OP_CHECKLOCKTIMEVERIFY, Opcodes::OP_CHECKMULTISIG, Opcodes::OP_CHECKMULTISIGVERIFY, Opcodes::OP_CHECKSIG, Opcodes::OP_CHECKSIGVERIFY, Opcodes::OP_CODESEPARATOR, Opcodes::OP_DEPTH, Opcodes::OP_DIV, Opcodes::OP_DROP, Opcodes::OP_DUP, Opcodes::OP_ELSE, Opcodes::OP_ENDIF, Opcodes::OP_EQUAL, Opcodes::OP_EQUALVERIFY, Opcodes::OP_FALSE, Opcodes::OP_FROMALTSTACK, Opcodes::OP_GREATERTHAN, Opcodes::OP_GREATERTHANOREQUAL, Opcodes::OP_HASH160, Opcodes::OP_HASH256, Opcodes::OP_IF, Opcodes::OP_IFDUP, Opcodes::OP_INVALIDOPCODE, Opcodes::OP_INVERT, Opcodes::OP_LEFT, Opcodes::OP_LESSTHAN, Opcodes::OP_LESSTHANOREQUAL, Opcodes::OP_LSHIFT, Opcodes::OP_MAX, Opcodes::OP_MIN, Opcodes::OP_MOD, Opcodes::OP_MUL, Opcodes::OP_NEGATE, Opcodes::OP_NIP, Opcodes::OP_NOP, Opcodes::OP_NOP1, Opcodes::OP_NOP10, Opcodes::OP_NOP2, Opcodes::OP_NOP3, Opcodes::OP_NOP4, Opcodes::OP_NOP5, Opcodes::OP_NOP6, Opcodes::OP_NOP7, Opcodes::OP_NOP8, Opcodes::OP_NOP9, Opcodes::OP_NOT, Opcodes::OP_NOTIF, Opcodes::OP_NUMEQUAL, Opcodes::OP_NUMEQUALVERIFY, Opcodes::OP_NUMNOTEQUAL, Opcodes::OP_OR, Opcodes::OP_OVER, Opcodes::OP_PICK, Opcodes::OP_PUSHDATA1, Opcodes::OP_PUSHDATA2, Opcodes::OP_PUSHDATA4, Opcodes::OP_RESERVED, Opcodes::OP_RESERVED1, Opcodes::OP_RESERVED2, Opcodes::OP_RETURN, Opcodes::OP_RIGHT, Opcodes::OP_RIPEMD160, Opcodes::OP_ROLL, Opcodes::OP_ROT, Opcodes::OP_RSHIFT, Opcodes::OP_SHA1, Opcodes::OP_SHA256, Opcodes::OP_SIZE, Opcodes::OP_SUB, Opcodes::OP_SUBSTR, Opcodes::OP_SWAP, Opcodes::OP_TOALTSTACK, Opcodes::OP_TRUE, Opcodes::OP_TUCK, Opcodes::OP_VER, Opcodes::OP_VERIF, Opcodes::OP_VERIFY, Opcodes::OP_VERNOTIF, Opcodes::OP_WITHIN, Opcodes::OP_XOR

Constants included from ScriptErrors

BTC::ScriptErrors::SCRIPT_ERR_BAD_OPCODE, BTC::ScriptErrors::SCRIPT_ERR_CHECKMULTISIGVERIFY, BTC::ScriptErrors::SCRIPT_ERR_CHECKSIGVERIFY, BTC::ScriptErrors::SCRIPT_ERR_CLEANSTACK, BTC::ScriptErrors::SCRIPT_ERR_DISABLED_OPCODE, BTC::ScriptErrors::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, BTC::ScriptErrors::SCRIPT_ERR_EQUALVERIFY, BTC::ScriptErrors::SCRIPT_ERR_EVAL_FALSE, BTC::ScriptErrors::SCRIPT_ERR_INVALID_ALTSTACK_OPERATION, BTC::ScriptErrors::SCRIPT_ERR_INVALID_STACK_OPERATION, BTC::ScriptErrors::SCRIPT_ERR_MINIMALDATA, BTC::ScriptErrors::SCRIPT_ERR_NEGATIVE_LOCKTIME, BTC::ScriptErrors::SCRIPT_ERR_NUMEQUALVERIFY, BTC::ScriptErrors::SCRIPT_ERR_OK, BTC::ScriptErrors::SCRIPT_ERR_OP_COUNT, BTC::ScriptErrors::SCRIPT_ERR_OP_RETURN, BTC::ScriptErrors::SCRIPT_ERR_PUBKEYTYPE, BTC::ScriptErrors::SCRIPT_ERR_PUBKEY_COUNT, BTC::ScriptErrors::SCRIPT_ERR_PUSH_SIZE, BTC::ScriptErrors::SCRIPT_ERR_SCRIPT_SIZE, BTC::ScriptErrors::SCRIPT_ERR_SIG_COUNT, BTC::ScriptErrors::SCRIPT_ERR_SIG_DER, BTC::ScriptErrors::SCRIPT_ERR_SIG_HASHTYPE, BTC::ScriptErrors::SCRIPT_ERR_SIG_HIGH_S, BTC::ScriptErrors::SCRIPT_ERR_SIG_NULLDUMMY, BTC::ScriptErrors::SCRIPT_ERR_SIG_PUSHONLY, BTC::ScriptErrors::SCRIPT_ERR_STACK_SIZE, BTC::ScriptErrors::SCRIPT_ERR_UNBALANCED_CONDITIONAL, BTC::ScriptErrors::SCRIPT_ERR_UNKNOWN_ERROR, BTC::ScriptErrors::SCRIPT_ERR_UNSATISFIED_LOCKTIME, BTC::ScriptErrors::SCRIPT_ERR_VERIFY

Constants included from ScriptFlags

BTC::ScriptFlags::SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, BTC::ScriptFlags::SCRIPT_VERIFY_CLEANSTACK, BTC::ScriptFlags::SCRIPT_VERIFY_DERSIG, BTC::ScriptFlags::SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS, BTC::ScriptFlags::SCRIPT_VERIFY_LOW_S, BTC::ScriptFlags::SCRIPT_VERIFY_MINIMALDATA, BTC::ScriptFlags::SCRIPT_VERIFY_NONE, BTC::ScriptFlags::SCRIPT_VERIFY_NULLDUMMY, BTC::ScriptFlags::SCRIPT_VERIFY_P2SH, BTC::ScriptFlags::SCRIPT_VERIFY_SIGPUSHONLY, BTC::ScriptFlags::SCRIPT_VERIFY_STRICTENC

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(flags: SCRIPT_VERIFY_NONE, extensions: nil, signature_checker: nil, raise_on_failure: false, max_pushdata_size: MAX_SCRIPT_ELEMENT_SIZE, max_op_count: MAX_OPS_PER_SCRIPT, max_stack_size: MAX_STACK_SIZE, max_script_size: MAX_SCRIPT_SIZE, integer_max_size: 4, locktime_max_size: 5) ⇒ ScriptInterpreter

Instantiates interpreter with validation flags and an optional checker (required if the scripts use signature-checking opcodes). Checker can be transaction checker or block checker



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/btcruby/script/script_interpreter.rb', line 38

def initialize(flags:             SCRIPT_VERIFY_NONE,
               extensions:           nil,
               signature_checker: nil,
               raise_on_failure:  false,
               max_pushdata_size: MAX_SCRIPT_ELEMENT_SIZE,
               max_op_count:      MAX_OPS_PER_SCRIPT,
               max_stack_size:    MAX_STACK_SIZE,
               max_script_size:   MAX_SCRIPT_SIZE,
               integer_max_size:  4,
               locktime_max_size: 5)
  @flags             = flags
  @extensions           = extensions || []
  @signature_checker = signature_checker
  
  @raise_on_failure  = raise_on_failure
  @max_pushdata_size = max_pushdata_size
  @max_op_count      = max_op_count
  @max_stack_size    = max_stack_size
  @max_script_size   = max_script_size
  @integer_max_size  = integer_max_size
  @locktime_max_size = locktime_max_size
end

Instance Attribute Details

#altstackObject (readonly)

Returns the value of attribute altstack.



32
33
34
# File 'lib/btcruby/script/script_interpreter.rb', line 32

def altstack
  @altstack
end

#errorObject

ScriptError instance



33
34
35
# File 'lib/btcruby/script/script_interpreter.rb', line 33

def error
  @error
end

#extensionsObject

Returns the value of attribute extensions.



20
21
22
# File 'lib/btcruby/script/script_interpreter.rb', line 20

def extensions
  @extensions
end

#flagsObject

Flags specified for this interpreter, not including flags added by extensions.



19
20
21
# File 'lib/btcruby/script/script_interpreter.rb', line 19

def flags
  @flags
end

#integer_max_sizeObject

Returns the value of attribute integer_max_size.



27
28
29
# File 'lib/btcruby/script/script_interpreter.rb', line 27

def integer_max_size
  @integer_max_size
end

#locktime_max_sizeObject

Returns the value of attribute locktime_max_size.



28
29
30
# File 'lib/btcruby/script/script_interpreter.rb', line 28

def locktime_max_size
  @locktime_max_size
end

#max_op_countObject

Returns the value of attribute max_op_count.



24
25
26
# File 'lib/btcruby/script/script_interpreter.rb', line 24

def max_op_count
  @max_op_count
end

#max_pushdata_sizeObject

Returns the value of attribute max_pushdata_size.



23
24
25
# File 'lib/btcruby/script/script_interpreter.rb', line 23

def max_pushdata_size
  @max_pushdata_size
end

#max_script_sizeObject

Returns the value of attribute max_script_size.



26
27
28
# File 'lib/btcruby/script/script_interpreter.rb', line 26

def max_script_size
  @max_script_size
end

#max_stack_sizeObject

Returns the value of attribute max_stack_size.



25
26
27
# File 'lib/btcruby/script/script_interpreter.rb', line 25

def max_stack_size
  @max_stack_size
end

#raise_on_failureObject

Returns the value of attribute raise_on_failure.



22
23
24
# File 'lib/btcruby/script/script_interpreter.rb', line 22

def raise_on_failure
  @raise_on_failure
end

#signature_checkerObject

Returns the value of attribute signature_checker.



21
22
23
# File 'lib/btcruby/script/script_interpreter.rb', line 21

def signature_checker
  @signature_checker
end

#stackObject

Execution state



31
32
33
# File 'lib/btcruby/script/script_interpreter.rb', line 31

def stack
  @stack
end

Instance Method Details

#all_flagsObject



898
899
900
901
902
# File 'lib/btcruby/script/script_interpreter.rb', line 898

def all_flags
  @extensions.inject(@flags) do |f, p|
    f | p.extra_flags
  end
end

#cast_to_bool(data) ⇒ Object



910
911
912
913
914
915
916
917
918
919
920
921
# File 'lib/btcruby/script/script_interpreter.rb', line 910

def cast_to_bool(data)
  data.bytes.each_with_index do |byte, i|
    if byte != 0
      # Can be negative zero
      if byte == 0x80 && i == (data.bytesize - 1)
        return false
      end
      return true
    end
  end
  return false
end

#cast_to_number(data, require_minimal: flag?(SCRIPT_VERIFY_MINIMALDATA), max_size: @integer_max_size) ⇒ Object



904
905
906
907
908
# File 'lib/btcruby/script/script_interpreter.rb', line 904

def cast_to_number(data,
                   require_minimal: flag?(SCRIPT_VERIFY_MINIMALDATA),
                   max_size: @integer_max_size)
  ScriptNumber.new(data: data, require_minimal: require_minimal, max_size: max_size)
end

#check_minimal_push(data, opcode) ⇒ Object

aka CheckMinimalPush(const valtype& data, opcodetype opcode)



831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
# File 'lib/btcruby/script/script_interpreter.rb', line 831

def check_minimal_push(data, opcode)
  if data.bytesize == 0
    # Could have used OP_0.
    return opcode == OP_0
  elsif data.bytesize == 1 && data.bytes[0] >= 1 && data.bytes[0] <= 16
    # Could have used OP_1 .. OP_16.
    return opcode == OP_1 + (data.bytes[0] - 1)
  elsif data.bytesize == 1 && data.bytes[0] == 0x81
    # Could have used OP_1NEGATE.
    return opcode == OP_1NEGATE
  elsif data.bytesize <= 75
    # Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
    return opcode == data.bytesize
  elsif data.bytesize <= 255
    # Could have used OP_PUSHDATA.
    return opcode == OP_PUSHDATA1
  elsif (data.bytesize <= 65535)
    # Could have used OP_PUSHDATA2.
    return opcode == OP_PUSHDATA2
  end
  return true
end

#check_pubkey_encoding(pubkey) ⇒ Object



870
871
872
873
874
875
# File 'lib/btcruby/script/script_interpreter.rb', line 870

def check_pubkey_encoding(pubkey)
  if flag?(SCRIPT_VERIFY_STRICTENC) && !compressed_or_uncompressed_pubkey(pubkey)
    return set_error(SCRIPT_ERR_PUBKEYTYPE)
  end
  return true
end

#check_signature_encoding(sig) ⇒ Object



854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
# File 'lib/btcruby/script/script_interpreter.rb', line 854

def check_signature_encoding(sig)
  # Empty signature. Not strictly DER encoded, but allowed to provide a
  # compact way to provide an invalid signature for use with CHECK(MULTI)SIG
  if sig.size == 0
    return true
  end
  if flag?(SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC) && !valid_signature_encoding(sig)
    return set_error(SCRIPT_ERR_SIG_DER)
  elsif flag?(SCRIPT_VERIFY_LOW_S) && !low_der_signature(sig)
    return set_error(SCRIPT_ERR_SIG_HIGH_S)
  elsif flag?(SCRIPT_VERIFY_STRICTENC) && !defined_hashtype_signature(sig)
    return set_error(SCRIPT_ERR_SIG_HASHTYPE)
  end
  return true
end

#compressed_or_uncompressed_pubkey(pubkey) ⇒ Object



889
890
891
# File 'lib/btcruby/script/script_interpreter.rb', line 889

def compressed_or_uncompressed_pubkey(pubkey)
  BTC::Key.validate_public_key(pubkey)
end

#defined_hashtype_signature(sig) ⇒ Object



885
886
887
# File 'lib/btcruby/script/script_interpreter.rb', line 885

def defined_hashtype_signature(sig)
  BTC::Key.validate_script_signature(sig, verify_lower_s: false, verify_hashtype: true)
end

#flag?(flags) ⇒ Boolean

If multiple flags are provided, returns true if any of them are present

Returns:

  • (Boolean)


894
895
896
# File 'lib/btcruby/script/script_interpreter.rb', line 894

def flag?(flags)
  (all_flags & flags) != 0
end

#low_der_signature(sig) ⇒ Object



881
882
883
# File 'lib/btcruby/script/script_interpreter.rb', line 881

def low_der_signature(sig)
  BTC::Key.validate_script_signature(sig, verify_lower_s: true, verify_hashtype: false)
end

#run_script(script) ⇒ Object

Returns true if succeeded or false in case of failure. If fails, sets the error attribute. Used internally in ‘verify_script` and also in unit tests.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
# File 'lib/btcruby/script/script_interpreter.rb', line 137

def run_script(script)

  if script.data.bytesize > @max_script_size
    return set_error(SCRIPT_ERR_SCRIPT_SIZE)
  end

  # Altstack is not shared between individual runs, but we still store it in ivar to make available to the extensions.
  @altstack = []

  number_zero  = ScriptNumber.new(integer: 0)
  number_one   = ScriptNumber.new(integer: 1)
  zero_value = "".b
  false_value = "".b
  true_value = "\x01".b

  opcount = 0
  require_minimal = flag?(SCRIPT_VERIFY_MINIMALDATA)
  condition_flags = []
  index_after_codeseparator = 0
  script.chunks.each_with_index do |chunk, chunk_index|

    opcode = chunk.opcode
    should_execute = !condition_flags.include?(false)

    if chunk.pushdata? && chunk.pushdata.bytesize > @max_pushdata_size
      return set_error(SCRIPT_ERR_PUSH_SIZE)
    end

    # Note how OP_RESERVED does not count towards the opcode limit.
    if opcode > OP_16
      if (opcount += 1) > @max_op_count
        return set_error(SCRIPT_ERR_OP_COUNT)
      end
    end
    
    # Check if there is a extension for this opcode before we check for disabled opcodes.
    opcode_extension = extension_to_handle_opcode(opcode)
    
    if !opcode_extension
      if opcode == OP_CAT ||
         opcode == OP_SUBSTR ||
         opcode == OP_LEFT ||
         opcode == OP_RIGHT ||
         opcode == OP_INVERT ||
         opcode == OP_AND ||
         opcode == OP_OR ||
         opcode == OP_XOR ||
         opcode == OP_2MUL ||
         opcode == OP_2DIV ||
         opcode == OP_MUL ||
         opcode == OP_DIV ||
         opcode == OP_MOD ||
         opcode == OP_LSHIFT ||
         opcode == OP_RSHIFT

        return set_error(SCRIPT_ERR_DISABLED_OPCODE)
      end
    end

    if should_execute && 0 <= opcode && opcode <= OP_PUSHDATA4
      # Pushdata (including OP_0).
      if require_minimal && !check_minimal_push(chunk.pushdata, opcode)
        return set_error(SCRIPT_ERR_MINIMALDATA)
      end
      stack_push(chunk.pushdata)

    elsif should_execute && opcode_extension
      
      if !opcode_extension.handle_opcode(interpreter: self, opcode: opcode)
        # error is set already
        return false
      end

    elsif should_execute || (OP_IF <= opcode && opcode <= OP_ENDIF)

      case opcode
      when OP_1NEGATE, OP_1..OP_16
        # ( -- value)
        num = ScriptNumber.new(integer: opcode - (OP_1 - 1))
        stack_push(num.data)
        # The result of these opcodes should always be the minimal way to push the data
        # they push, so no need for a CheckMinimalPush here.


      # Control Operators
      # -----------------

      when OP_NOP
        # nothing
        
      # See CLTVExtension
      # when OP_CHECKLOCKTIMEVERIFY
      #   begin
      #     if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
      #       # not enabled; treat as a NOP2
      #       if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
      #         return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
      #       end
      #       break # breaks out of begin ... end while false
      #     end
      # 
      #     if @stack.size < 1
      #       return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
      #     end
      # 
      #     # Note that elsewhere numeric opcodes are limited to
      #     # operands in the range -2**31+1 to 2**31-1, however it is
      #     # legal for opcodes to produce results exceeding that
      #     # range. This limitation is implemented by CScriptNum's
      #     # default 4-byte limit.
      #     #
      #     # If we kept to that limit we'd have a year 2038 problem,
      #     # even though the nLockTime field in transactions
      #     # themselves is uint32 which only becomes meaningless
      #     # after the year 2106.
      #     #
      #     # Thus as a special case we tell CScriptNum to accept up
      #     # to 5-byte bignums, which are good until 2**39-1, well
      #     # beyond the 2**32-1 limit of the nLockTime field itself.
      #     locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
      # 
      #     # In the rare event that the argument may be < 0 due to
      #     # some arithmetic being done first, you can always use
      #     # 0 MAX CHECKLOCKTIMEVERIFY.
      #     if locktime < 0
      #       return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
      #     end
      # 
      #     # Actually compare the specified lock time with the transaction.
      #     if !signature_checker.check_lock_time(locktime)
      #       return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
      #     end
      #   end while false

      when OP_NOP1..OP_NOP10
        if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
          return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
        end

      when OP_IF, OP_NOTIF
        # <expression> if [statements] [else [statements]] endif
        flag = false
        if should_execute
          if @stack.size < 1
            return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
          end
          flag = cast_to_bool(@stack.last)
          if opcode == OP_NOTIF
            flag = !flag
          end
          stack_pop
        end
        condition_flags.push(flag)

      when OP_ELSE
        if condition_flags.empty?
          return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
        end
        condition_flags[-1] = !condition_flags.last

      when OP_ENDIF
        if condition_flags.empty?
          return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
        end
        condition_flags.pop

      when OP_VERIFY
        # (true -- ) or
        # (false -- false) and return
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        flag = cast_to_bool(@stack.last)
        if flag
          stack_pop
        else
          return set_error(SCRIPT_ERR_VERIFY)
        end

      when OP_RETURN
        return set_error(SCRIPT_ERR_OP_RETURN)


      # Stack Operations
      # ----------------

      when OP_TOALTSTACK
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        @altstack.push(stack_pop)

      when OP_FROMALTSTACK
        if @altstack.size < 1
          return set_error(SCRIPT_ERR_INVALID_ALTSTACK_OPERATION)
        end
        stack_push(@altstack.pop)

      when OP_2DROP
        # (x1 x2 -- )
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        stack_pop
        stack_pop

      when OP_2DUP
        # (x1 x2 -- x1 x2 x1 x2)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        a = @stack[-2]
        b = @stack[-1]
        stack_push(a)
        stack_push(b)

      when OP_3DUP
        # (x1 x2 x3 -- x1 x2 x3 x1 x2 x3)
        if @stack.size < 3
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        a = @stack[-3]
        b = @stack[-2]
        c = @stack[-1]
        stack_push(a)
        stack_push(b)
        stack_push(c)

      when OP_2OVER
        # (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2)
        if @stack.size < 4
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        a = @stack[-4]
        b = @stack[-3]
        stack_push(a)
        stack_push(b)

      when OP_2ROT
        # (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2)
        if @stack.size < 6
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        a = @stack[-6]
        b = @stack[-5]
        @stack[-6...-4] = []
        stack_push(a)
        stack_push(b)

      when OP_2SWAP
        # (x1 x2 x3 x4 -- x3 x4 x1 x2)
        if @stack.size < 4
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        x1x2 = @stack[-4...-2]
        @stack[-4...-2] = []
        @stack += x1x2

      when OP_IFDUP
        # (x - 0 | x x)
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        item = @stack.last
        if cast_to_bool(item)
          stack_push(item)
        end

      when OP_DEPTH
        # -- stacksize
        sn = ScriptNumber.new(integer: @stack.size)
        stack_push(sn.data)

      when OP_DROP
        # (x -- )
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        stack_pop

      when OP_DUP
        # (x -- x x)
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        stack_push(@stack.last)

      when OP_NIP
        # (x1 x2 -- x2)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        @stack.delete_at(-2)

      when OP_OVER
        # (x1 x2 -- x1 x2 x1)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        stack_push(@stack[-2])

      when OP_PICK, OP_ROLL
        # (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
        # (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        n = cast_to_number(@stack.last).to_i
        stack_pop
        if n < 0 || n >= @stack.size
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        item = @stack[-n-1]
        if opcode == OP_ROLL
          @stack.delete_at(-n-1)
        end
        stack_push(item)

      when OP_ROT
        # (x1 x2 x3 -- x2 x3 x1)
        #  x2 x1 x3  after first swap
        #  x2 x3 x1  after second swap
        if @stack.size < 3
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        x1 = @stack[-3]
        @stack.delete_at(-3)
        stack_push(x1)

      when OP_SWAP
        # (x1 x2 -- x2 x1)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        x1 = @stack[-2]
        @stack.delete_at(-2)
        stack_push(x1)

      when OP_TUCK
        # (x1 x2 -- x2 x1 x2)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        item = @stack[-1]
        @stack.insert(-3, item) # -1 inserts in the end, -2 before the last item.

      when OP_SIZE
        # (in -- in size)
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        sn = ScriptNumber.new(integer: @stack.last.size)
        stack_push(sn.data)


      # Bitwise Logic
      # -------------

      when OP_EQUAL, OP_EQUALVERIFY
      # there is no OP_NOTEQUAL, use OP_NUMNOTEQUAL instead
        # (x1 x2 - bool)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        a = @stack[-2]
        b = @stack[-1]
        equal = (a == b)
        # OP_NOTEQUAL is disabled because it would be too easy to say
        # something like n != 1 and have some wiseguy pass in 1 with extra
        # zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001)
        # if opcode == OP_NOTEQUAL
        #   equal = !equal
        # end
        stack_pop
        stack_pop
        stack_push(equal ? true_value : false_value)
        if opcode == OP_EQUALVERIFY
          if equal
            stack_pop
          else
            return set_error(SCRIPT_ERR_EQUALVERIFY)
          end
        end


      # Numeric
      # -------

      when OP_1ADD,
           OP_1SUB,
           OP_NEGATE,
           OP_ABS,
           OP_NOT,
           OP_0NOTEQUAL
        # (in -- out)
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        bn = cast_to_number(@stack.last)
        case opcode
        when OP_1ADD
          bn += 1
        when OP_1SUB
          bn -= 1
        when OP_NEGATE
          bn = -bn
        when OP_ABS
          bn = -bn if bn < 0
        when OP_NOT
          bn = ScriptNumber.new(boolean: (bn == 0))
        when OP_0NOTEQUAL
          bn = ScriptNumber.new(boolean: (bn != 0))
        else
          raise "invalid opcode"
        end
        stack_pop
        stack_push(bn.data);


      when OP_ADD, OP_SUB, OP_BOOLAND..OP_MAX
        # (x1 x2 -- out)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        bn1 = cast_to_number(@stack[-2])
        bn2 = cast_to_number(@stack[-1])
        bn = ScriptNumber.new(integer: 0)

        case opcode
        when OP_ADD
          bn = bn1 + bn2
        when OP_SUB
          bn = bn1 - bn2
        when OP_BOOLAND
          bn = ScriptNumber.new(boolean: ((bn1 != 0) && (bn2 != 0)))
        when OP_BOOLOR
          bn = ScriptNumber.new(boolean: ((bn1 != 0) || (bn2 != 0)))
        when OP_NUMEQUAL, OP_NUMEQUALVERIFY
          bn = ScriptNumber.new(boolean: (bn1 == bn2))
        when OP_NUMNOTEQUAL
          bn = ScriptNumber.new(boolean: (bn1 != bn2))
        when OP_LESSTHAN
          bn = ScriptNumber.new(boolean: (bn1 < bn2))
        when OP_GREATERTHAN
          bn = ScriptNumber.new(boolean: (bn1 > bn2))
        when OP_LESSTHANOREQUAL
          bn = ScriptNumber.new(boolean: (bn1 <= bn2))
        when OP_GREATERTHANOREQUAL
          bn = ScriptNumber.new(boolean: (bn1 >= bn2))
        when OP_MIN
          bn = (bn1 < bn2 ? bn1 : bn2)
        when OP_MAX
          bn = (bn1 > bn2 ? bn1 : bn2)
        else
          raise "Invalid opcode"
        end
        stack_pop
        stack_pop
        stack_push(bn.data)

        if opcode == OP_NUMEQUALVERIFY
          if cast_to_bool(@stack[-1])
            stack_pop
          else
            return set_error(SCRIPT_ERR_NUMEQUALVERIFY)
          end
        end

      when OP_WITHIN
        # (x min max -- out)
        if @stack.size < 3
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        bn1 = cast_to_number(@stack[-3])
        bn2 = cast_to_number(@stack[-2])
        bn3 = cast_to_number(@stack[-1])
        flag = ((bn2 <= bn1) && (bn1 < bn3))
        stack_pop
        stack_pop
        stack_pop
        stack_push(flag ? true_value : false_value)

      # Crypto
      # ------

      when OP_RIPEMD160..OP_HASH256
        # (in -- hash)
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end
        item = @stack[-1]
        hash = case opcode
        when OP_RIPEMD160
          BTC.ripemd160(item)
        when OP_SHA1
          BTC.sha1(item)
        when OP_SHA256
          BTC.sha256(item)
        when OP_HASH160
          BTC.hash160(item)
        when OP_HASH256
          BTC.hash256(item)
        end
        stack_pop
        stack_push(hash)

      when OP_CODESEPARATOR
        # Hash starts after the code separator
        index_after_codeseparator = chunk_index + 1

      when OP_CHECKSIG, OP_CHECKSIGVERIFY
        # (sig pubkey -- bool)
        if @stack.size < 2
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        sig = @stack[-2]
        pubkey    = @stack[-1]

        # Subset of script starting at the most recent codeseparator
        subscript = script.subscript(index_after_codeseparator..-1)

        # Drop the signature, since there's no way for a signature to sign itself
        # Consensus-critical code: must replace sig with minimal encoding.
        subscript = subscript.find_and_delete(BTC::Script.new << sig)

        if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
          # error is set already
          return false
        end

        success = signature_checker.check_signature(
          script_signature: sig,
          public_key:       pubkey,
          script:           subscript
        )

        stack_pop
        stack_pop
        stack_push(success ? true_value : false_value)

        if opcode == OP_CHECKSIGVERIFY
          if success
            stack_pop
          else
            return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
          end
        end


      when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
        # ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool)

        i = 1
        if @stack.size < i
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        keys_count = cast_to_number(@stack[-i]).to_i
        if keys_count < 0 || keys_count > 20
          return set_error(SCRIPT_ERR_PUBKEY_COUNT)
        end
        opcount += keys_count
        if opcount > @max_op_count
          return set_error(SCRIPT_ERR_OP_COUNT)
        end
        ikey = (i+=1)
        i += keys_count
        if @stack.size < i
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        sigs_count = cast_to_number(@stack[-i]).to_i
        if sigs_count < 0 || sigs_count > keys_count
          return set_error(SCRIPT_ERR_SIG_COUNT)
        end

        isig = (i+=1)
        i += sigs_count

        if @stack.size < i
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        # Subset of script starting at the most recent codeseparator

        # Subset of script starting at the most recent codeseparator
        subscript = script.subscript(index_after_codeseparator..-1)

        # Drop the signatures, since there's no way for a signature to sign itself
        # Consensus-critical code: must replace sig with minimal encoding.
        sigs_count.times do |k|
          sig = @stack[-isig-k]
          subscript = subscript.find_and_delete(BTC::Script.new << sig)
        end

        success = true
        while success && sigs_count > 0
          sig = @stack[-isig]
          pubkey = @stack[-ikey]
          # Note how this makes the exact order of pubkey/signature evaluation
          # distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set.
          # See the script_(in)valid tests for details.
          if !check_signature_encoding(sig) || !check_pubkey_encoding(pubkey)
            # error is set already
            return false
          end

          # Check signature
          ok = signature_checker.check_signature(
            script_signature: sig,
            public_key:       pubkey,
            script:           subscript
          )
          if ok
            isig += 1
            sigs_count -= 1
          end
          ikey += 1
          keys_count -= 1

          # If there are more signatures left than keys left,
          # then too many signatures have failed. Exit early,
          # without checking any further signatures.
          if sigs_count > keys_count
            success = false
          end
        end

        # Clean up stack of actual arguments
        while (i-=1) > 0  # Bitcoin Core: while (i-- > 1)
          stack_pop
        end

        # A bug causes CHECKMULTISIG to consume one extra argument
        # whose contents were not checked in any way.
        #
        # Unfortunately this is a potential source of mutability,
        # so optionally verify it is exactly equal to zero prior
        # to removing it from the stack.
        if @stack.size < 1
          return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
        end

        if flag?(SCRIPT_VERIFY_NULLDUMMY) && @stack[-1].size > 0
          return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
        end
        stack_pop
        stack_push(success ? true_value : false_value)
        if opcode == OP_CHECKMULTISIGVERIFY
          if success
            stack_pop
          else
            return set_error(SCRIPT_ERR_CHECKMULTISIGVERIFY)
          end
        end

      else # unknown opcode
        return set_error(SCRIPT_ERR_BAD_OPCODE, "Unknown opcode 0x#{opcode.to_s(16)}")

      end # case opcode
    end # within IF scope

    # Size limits
    if @stack.size + @altstack.size > @max_stack_size
      return set_error(SCRIPT_ERR_STACK_SIZE)
    end

  end # each chunk

  if !condition_flags.empty?
    return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL)
  end

  return true
rescue ScriptNumberError => e
  return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
end

#set_error(code, message = nil) ⇒ Object



923
924
925
926
927
928
# File 'lib/btcruby/script/script_interpreter.rb', line 923

def set_error(code, message = nil)
  error = ScriptError.new(code, message)
  raise "#{error.description} (#{code.inspect})" if @raise_on_failure
  @error = error
  false
end

#stack_popObject



819
820
821
822
823
# File 'lib/btcruby/script/script_interpreter.rb', line 819

def stack_pop
  r = @stack.pop
  #puts "POPPED FROM STACK: #{@stack.map{|s|s.to_hex}.join(' ')}"
  r
end

#stack_push(x) ⇒ Object



825
826
827
828
# File 'lib/btcruby/script/script_interpreter.rb', line 825

def stack_push(x)
  @stack.push(x)
  #puts "PUSHED TO STACK:   #{@stack.map{|s|s.to_hex}.join(' ')}"
end

#valid_signature_encoding(sig) ⇒ Object



877
878
879
# File 'lib/btcruby/script/script_interpreter.rb', line 877

def valid_signature_encoding(sig)
  BTC::Key.validate_script_signature(sig, verify_lower_s: false, verify_hashtype: false)
end

#verify_script(signature_script: nil, output_script: nil) ⇒ Object

Returns true if succeeded or false in case of failure. If fails, sets the error attribute.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/btcruby/script/script_interpreter.rb', line 63

def verify_script(signature_script: nil, output_script: nil)

  @stack = []
  @altstack = []

  if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !signature_script.data_only?
    return set_error(SCRIPT_ERR_SIG_PUSHONLY)
  end

  if extension = extension_to_handle_scripts(signature_script, output_script)
    return extension.handle_scripts(
      interpreter: self,
      signature_script: signature_script,
      output_script: output_script
    ) && verify_clean_stack_if_needed
  end

  if !run_script(signature_script)
    # error is set in run_script
    return false
  end

  if !did_execute_signature_script(signature_script)
    # error is set already
    return false
  end

  # This is implemented in P2SHExtension
  # stack_copy = if flag?(SCRIPT_VERIFY_P2SH)
  #   @stack.dup
  # end

  if extension = extension_to_handle_output_script(output_script)
    return extension.handle_output_script(
      interpreter: self,
      output_script: output_script
    ) && verify_clean_stack_if_needed
  end

  if !run_script(output_script)
    # error is set in run_script
    return false
  end

  if @stack.empty?
    return set_error(SCRIPT_ERR_EVAL_FALSE)
  end

  if cast_to_bool(@stack.last) == false
    return set_error(SCRIPT_ERR_EVAL_FALSE)
  end

  if !did_execute_output_script(output_script)
    # error is set already
    return false
  end

  # Additional validation for pay-to-script-hash (P2SH) transactions:
  # See P2SHExtension#did_execute_output_script.

  return verify_clean_stack_if_needed
end