btcruby 1.1.1 → 1.1.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
  SHA1:
3
- metadata.gz: b2ed39f95cdd06b5950c9164180c4086592519ef
4
- data.tar.gz: 826463f6e12c74f02e122f696251d9c212cf9077
3
+ metadata.gz: e26eccf4e25b07bfb7c0a20d4757c73112ef36ac
4
+ data.tar.gz: 44f3ca44eb7f7c36e666a7a0987055c5a16efeb7
5
5
  SHA512:
6
- metadata.gz: 26f331d4b44fc40c0e211693baca89866176b101600b6dd70860eef7a05bf4db2047c54f7e2a676cb4154683da2d17764b9bfd1c652de805af259f5ab33e9623
7
- data.tar.gz: 96817a00e44da817f3a83d91890c24b6ebde7d5079499e1e759303a99df4002067439761b12f34203f76b26d1afc361d3cb2ff873abde04937fa7ff4a7706858
6
+ metadata.gz: 6c891533a5c79e2091f095ae8a9c103cf326cb5d6f532e62497dfc677e887ddf3b718cee7fc16f5b86c53b45b7f9a08320942b088cb55d92ec246aadf84c0f7b
7
+ data.tar.gz: 8c0dda6751152a16cf585c5472e5f9bb6ee0c0f2bca7fc64ff3e204ea85b9a93924c2d70e88ed70c20e04f36dfec04bc4032633c4ba0c99854fb6fee56c4b823
data/README.md CHANGED
@@ -28,6 +28,7 @@ Please see [BTCRuby Reference](documentation/index.md) for API documentation and
28
28
  and [BIP62](https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki).
29
29
  * Automatic normalization of existing ECDSA signatures (see `BTC::Key#normalized_signature`).
30
30
  * Rich script analysis and compositing support (see `BTC::Script`).
31
+ * Full script interpreter with [P2SH](https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki) and [CLTV](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki) features.
31
32
  * Powerful diagnostics API covering the entire library (see `BTC::Diagnostics`).
32
33
  * Canonicality checks for transactions, public keys and script elements.
33
34
  * Fee computation and signature script simulation for building transactions without signing them.
@@ -36,7 +37,7 @@ Please see [BTCRuby Reference](documentation/index.md) for API documentation and
36
37
  ## Philosophy
37
38
 
38
39
  * We use clear, expressive names for all methods and classes.
39
- * Self-contained implementation. Only external dependency is `ffi` gem that helps linking directly with OpenSSL.
40
+ * Self-contained implementation. Only external dependency is `ffi` gem that helps linking directly with OpenSSL and libsecp256k1.
40
41
  * For efficiency and consistency we use binary strings throughout the library (not the hex strings as in other libraries).
41
42
  * We do not pollute standard classes with our methods. To use utility extensions like `String#to_hex` you should explicitly `require 'btcruby/extensions'`.
42
43
  * We use OpenSSL `BIGNUM` implementation where compatibility is critical (instead of the built-in Ruby Bignum).
@@ -49,6 +50,7 @@ The goal is to provide a complete Bitcoin toolkit in Ruby.
49
50
 
50
51
  ```
51
52
  $ bundle install
53
+ $ brew install ./vendor/homebrew/secp256k1.rb
52
54
  $ rake
53
55
  ```
54
56
 
@@ -2,6 +2,14 @@
2
2
  BTCRuby Release Notes
3
3
  =====================
4
4
 
5
+ 1.1.2 (August 17, 2015)
6
+ ---------------------
7
+
8
+ * Added scripts test suite from Bitcoin Core.
9
+ * Added transactions test suite from Bitcoin Core.
10
+ * As a result, fixed a few consensus bugs.
11
+ * Renamed `TransactionOutpoint` to `Outpoint` (previous name kept for backwards compatibility).
12
+
5
13
  1.1.1 (July 30, 2015)
6
14
  ---------------------
7
15
 
@@ -23,10 +23,11 @@ require_relative 'btcruby/key.rb'
23
23
  require_relative 'btcruby/keychain.rb'
24
24
  require_relative 'btcruby/wire_format.rb'
25
25
  require_relative 'btcruby/hash_id.rb'
26
+ require_relative 'btcruby/outpoint.rb'
26
27
  require_relative 'btcruby/transaction.rb'
27
28
  require_relative 'btcruby/transaction_input.rb'
28
29
  require_relative 'btcruby/transaction_output.rb'
29
- require_relative 'btcruby/transaction_outpoint.rb'
30
+ require_relative 'btcruby/validation.rb'
30
31
 
31
32
  require_relative 'btcruby/script/script_error.rb'
32
33
  require_relative 'btcruby/script/script_flags.rb'
@@ -39,6 +40,10 @@ require_relative 'btcruby/script/signature_checker.rb'
39
40
  require_relative 'btcruby/script/test_signature_checker.rb'
40
41
  require_relative 'btcruby/script/transaction_signature_checker.rb'
41
42
 
43
+ require_relative 'btcruby/script/script_interpreter_plugin.rb'
44
+ require_relative 'btcruby/script/p2sh_plugin.rb'
45
+ require_relative 'btcruby/script/cltv_plugin.rb'
46
+
42
47
  require_relative 'btcruby/transaction_builder.rb'
43
48
  require_relative 'btcruby/proof_of_work.rb'
44
49
  require_relative 'btcruby/block_header.rb'
@@ -269,7 +269,7 @@ module BTC
269
269
  # Nonce k is equal to HMAC-SHA256(data: hash, key: privkey)
270
270
  def ecdsa_signature(hash, privkey, normalized: true)
271
271
  raise ArgumentError, "Hash is missing" if !hash
272
- raise ArgumentError, "Cannot make a ECDSA signature without the private key" if !privkey
272
+ raise ArgumentError, "Private key is missing" if !privkey
273
273
 
274
274
  # ECDSA signature is a pair of numbers: (Kx, s)
275
275
  # Where Kx = x coordinate of k*G mod n (n is the order of secp256k1).
@@ -345,6 +345,10 @@ module BTC
345
345
  # Normalizes S value of the signature and returns normalized signature.
346
346
  # Returns nil if signature is completely invalid.
347
347
  def ecdsa_normalized_signature(signature)
348
+ ecdsa_reserialize_signature(signature, normalize_s: true)
349
+ end
350
+
351
+ def ecdsa_reserialize_signature(signature, normalize_s: false)
348
352
  raise ArgumentError, "Signature is missing" if !signature
349
353
 
350
354
  autorelease do |pool|
@@ -358,15 +362,19 @@ module BTC
358
362
  buf = FFI::MemoryPointer.from_string(signature)
359
363
  psig = d2i_ECDSA_SIG(nil, pointer_to_pointer(buf), buf.size-1)
360
364
  if psig.null?
361
- raise BTCError, "OpenSSL failed to read ECDSA signature with DER: #{BTC.to_hex(signature).inspect}"
365
+ Diagnostics.current.add_message("OpenSSL failed to read ECDSA signature with DER during reserialize: #{BTC.to_hex(signature).inspect}")
366
+ return signature
367
+ #raise BTCError, "OpenSSL failed to read ECDSA signature with DER: #{BTC.to_hex(signature).inspect}"
362
368
  end
363
369
 
364
370
  sig = ECDSA_SIG.new(psig) # read sig from its pointer
365
- s = sig[:s]
366
-
367
- # Enforce low S values, by negating the value (modulo the order) if above order/2.
368
- if BN_cmp(s, halfn) > 0
369
- BN_sub(s, n, s)
371
+
372
+ if normalize_s
373
+ # Enforce low S values, by negating the value (modulo the order) if above order/2.
374
+ s = sig[:s]
375
+ if BN_cmp(s, halfn) > 0
376
+ BN_sub(s, n, s)
377
+ end
370
378
  end
371
379
 
372
380
  # Note: we'll place new s value back to s bignum,
@@ -393,6 +401,9 @@ module BTC
393
401
  raise ArgumentError, "Signature is missing" if !signature
394
402
  raise ArgumentError, "Hash is missing" if !hash
395
403
  raise ArgumentError, "Public key is missing" if !public_key
404
+
405
+ # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first.
406
+ signature = ecdsa_reserialize_signature(signature, normalize_s: false)
396
407
 
397
408
  autorelease do |pool|
398
409
  eckey = pool.new_ec_key
@@ -400,13 +411,14 @@ module BTC
400
411
  buf = FFI::MemoryPointer.from_string(public_key)
401
412
  eckey = o2i_ECPublicKey(pointer_to_pointer(eckey), pointer_to_pointer(buf), buf.size - 1)
402
413
  if eckey.null?
414
+ Diagnostics.current.add_message("OpenSSL failed to create EC_KEY with public key: #{BTC.to_hex(public_key).inspect}")
403
415
  raise BTCError, "OpenSSL failed to create EC_KEY with public key: #{BTC.to_hex(public_key).inspect}"
404
416
  end
405
417
 
406
418
  # -1 = error, 0 = bad sig, 1 = good
407
419
  hash_buf = FFI::MemoryPointer.from_string(hash)
408
420
  sig_buf = FFI::MemoryPointer.from_string(signature)
409
- result = ECDSA_verify(0, hash_buf, hash_buf.size-1, sig_buf, sig_buf.size-1, eckey)
421
+ result = ECDSA_verify(0, hash_buf, hash.bytesize, sig_buf, signature.bytesize, eckey)
410
422
 
411
423
  if result == 1
412
424
  return true
@@ -415,7 +427,9 @@ module BTC
415
427
  if result == 0
416
428
  Diagnostics.current.add_message("OpenSSL detected invalid ECDSA signature. Signature: #{BTC.to_hex(signature).inspect}; Hash: #{BTC.to_hex(hash).inspect}; Pubkey: #{BTC.to_hex(public_key).inspect}")
417
429
  else
418
- raise BTCError, "OpenSSL failed with error while verifying ECDSA signature. Signature: #{BTC.to_hex(signature).inspect}; Hash: #{BTC.to_hex(hash).inspect}; Pubkey: #{BTC.to_hex(public_key).inspect}"
430
+ Diagnostics.current.add_message("OpenSSL failed with error while verifying ECDSA signature. Signature: #{BTC.to_hex(signature).inspect}; Hash: #{BTC.to_hex(hash).inspect}; Pubkey: #{BTC.to_hex(public_key).inspect}; Result: #{result}")
431
+ return false
432
+ # raise BTCError, "OpenSSL failed with error while verifying ECDSA signature. Signature: #{BTC.to_hex(signature).inspect}; Hash: #{BTC.to_hex(hash).inspect}; Pubkey: #{BTC.to_hex(public_key).inspect}; Result: #{result}"
419
433
  end
420
434
  return false
421
435
  end
@@ -1,6 +1,9 @@
1
1
  module BTC
2
2
  # Represents a reference to a previous transaction.
3
- class TransactionOutpoint
3
+ class Outpoint
4
+ INVALID_INDEX = 0xFFFFFFFF # aka "(unsigned int) -1" in BitcoinQT.
5
+ ZERO_HASH256 = ("\x00".b*32).freeze
6
+
4
7
  attr_accessor :transaction_hash
5
8
  attr_accessor :transaction_id
6
9
  attr_accessor :index
@@ -8,6 +11,9 @@ module BTC
8
11
  def initialize(transaction_hash: nil, transaction_id: nil, index: 0)
9
12
  @transaction_hash = transaction_hash
10
13
  self.transaction_id = transaction_id if transaction_id
14
+ while index < 0
15
+ index += 2**32
16
+ end
11
17
  @index = index
12
18
  end
13
19
 
@@ -22,6 +28,10 @@ module BTC
22
28
  def outpoint_id
23
29
  %{#{transaction_id}:#{index}}
24
30
  end
31
+
32
+ def null?
33
+ @index == INVALID_INDEX && @transaction_hash == ZERO_HASH256
34
+ end
25
35
 
26
36
  def ==(other)
27
37
  index == other.index &&
@@ -37,4 +47,7 @@ module BTC
37
47
  outpoint_id
38
48
  end
39
49
  end
50
+
51
+ # For backwards compatibility keep the longer name.
52
+ TransactionOutpoint = Outpoint
40
53
  end
@@ -0,0 +1,63 @@
1
+ module BTC
2
+ # Performs CHECKLOCKTIMEVERIFY (BIP65) evaluation
3
+ class CLTVPlugin
4
+ include ScriptInterpreterPlugin
5
+
6
+ # Default `locktime_max_size` is 5.
7
+ # Default `lock_time_checker` equals current interpreter's signature checker.
8
+ def initialize(locktime_max_size: nil, lock_time_checker: nil)
9
+ @locktime_max_size = locktime_max_size || 5
10
+ @lock_time_checker = lock_time_checker
11
+ end
12
+
13
+ def extra_flags
14
+ SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY
15
+ end
16
+
17
+ def should_handle_opcode(interpreter: nil, opcode: nil)
18
+ opcode == OP_CHECKLOCKTIMEVERIFY
19
+ end
20
+
21
+ # Returns `false` if failed to execute the opcode.
22
+ def handle_opcode(interpreter: nil, opcode: nil)
23
+ # We are not supposed to handle any other opcodes here.
24
+ return false if opcode != OP_CHECKLOCKTIMEVERIFY
25
+
26
+ if interpreter.stack.size < 1
27
+ return interpreter.set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
28
+ end
29
+
30
+ # Note that elsewhere numeric opcodes are limited to
31
+ # operands in the range -2**31+1 to 2**31-1, however it is
32
+ # legal for opcodes to produce results exceeding that
33
+ # range. This limitation is implemented by CScriptNum's
34
+ # default 4-byte limit.
35
+ #
36
+ # If we kept to that limit we'd have a year 2038 problem,
37
+ # even though the nLockTime field in transactions
38
+ # themselves is uint32 which only becomes meaningless
39
+ # after the year 2106.
40
+ #
41
+ # Thus as a special case we tell CScriptNum to accept up
42
+ # to 5-byte bignums, which are good until 2**39-1, well
43
+ # beyond the 2**32-1 limit of the nLockTime field itself.
44
+ locktime = interpreter.cast_to_number(interpreter.stack.last, max_size: @locktime_max_size)
45
+
46
+ # In the rare event that the argument may be < 0 due to
47
+ # some arithmetic being done first, you can always use
48
+ # 0 MAX CHECKLOCKTIMEVERIFY.
49
+ if locktime < 0
50
+ return interpreter.set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
51
+ end
52
+
53
+ # Actually compare the specified lock time with the transaction.
54
+ checker = @lock_time_checker || interpreter.signature_checker
55
+ if !checker.check_lock_time(locktime)
56
+ return interpreter.set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
57
+ end
58
+
59
+ return true
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,71 @@
1
+ module BTC
2
+ # Performs Pay-to-Script-Hash (BIP16) evaluation
3
+ class P2SHPlugin
4
+ include ScriptInterpreterPlugin
5
+
6
+ # Returns additional flags to be available to #flag? checks during script execution.
7
+ # This way one plugin can affect evaluation of another.
8
+ def extra_flags
9
+ SCRIPT_VERIFY_P2SH
10
+ end
11
+
12
+ # Every plugin gets this callback. If plugin return `false`, execution is stopped and interpreter returns `false`.
13
+ # Default value is `true`.
14
+ def did_execute_signature_script(interpreter: nil, signature_script: nil)
15
+ @signature_script = signature_script
16
+ @stack_copy = interpreter.stack.dup
17
+ true
18
+ end
19
+
20
+ def did_execute_output_script(interpreter: nil, output_script: nil)
21
+ # Cleanup ivars
22
+ stack_copy = @stack_copy
23
+ @stack_copy = nil
24
+
25
+ signature_script = @signature_script
26
+ @signature_script = nil
27
+
28
+ # If output script is not P2SH, do nothing.
29
+ return true if !output_script.p2sh?
30
+
31
+ # Additional validation for pay-to-script-hash (P2SH) transactions:
32
+
33
+ # scriptSig must be literals-only or validation fails
34
+ if !signature_script.data_only?
35
+ return interpreter.set_error(SCRIPT_ERR_SIG_PUSHONLY)
36
+ end
37
+
38
+ # Restore stack.
39
+ interpreter.stack = stack_copy
40
+
41
+ # stack cannot be empty here, because if it was the
42
+ # P2SH HASH <> EQUAL scriptPubKey would be evaluated with
43
+ # an empty stack and the EvalScript above would return false.
44
+ raise "Stack cannot be empty" if stack_copy.empty?
45
+
46
+ serialized_redeem_script = interpreter.stack.pop
47
+ begin
48
+ redeem_script = BTC::Script.new(data: serialized_redeem_script)
49
+ rescue => e
50
+ return interpreter.set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
51
+ end
52
+
53
+ # Actually execute the script.
54
+ if !interpreter.run_script(redeem_script)
55
+ # error is set in run_script
56
+ return false
57
+ end
58
+
59
+ if interpreter.stack.empty?
60
+ return interpreter.set_error(SCRIPT_ERR_EVAL_FALSE)
61
+ end
62
+
63
+ if interpreter.cast_to_bool(interpreter.stack.last) == false
64
+ return interpreter.set_error(SCRIPT_ERR_EVAL_FALSE)
65
+ end
66
+
67
+ return true
68
+ end
69
+
70
+ end
71
+ end
@@ -421,9 +421,11 @@ module BTC
421
421
 
422
422
  # Appends an opcode (Integer), pushdata (String) or Script and returns self.
423
423
  # If Array is passed, this method is recursively called for each element in the array.
424
- def <<(object)
424
+ def append(object)
425
425
  if object.is_a?(BTC::Script)
426
426
  append_script(object)
427
+ elsif object.is_a?(BTC::ScriptNumber)
428
+ append_pushdata(object.data)
427
429
  elsif object.is_a?(Integer)
428
430
  append_opcode(object)
429
431
  elsif object.is_a?(String)
@@ -439,6 +441,10 @@ module BTC
439
441
  end
440
442
  return self
441
443
  end
444
+
445
+ def <<(object)
446
+ append(object)
447
+ end
442
448
 
443
449
  # Returns a new instance with concatenation of two scripts.
444
450
  def +(other)
@@ -134,6 +134,6 @@ module BTC
134
134
  end
135
135
 
136
136
  if $0 == __FILE__
137
- puts BTC::ScriptError.new(BTC::ScriptError::SCRIPT_ERR_SIG_HIGH_S).inspect
137
+ puts BTC::ScriptError.new(BTC::SCRIPT_ERR_SIG_HIGH_S).inspect
138
138
  end
139
139
 
@@ -13,51 +13,88 @@ module BTC
13
13
  class ScriptInterpreter
14
14
  include ScriptFlags
15
15
 
16
+ # Flags specified for this interpreter, not including flags added by plugins.
16
17
  attr_accessor :flags
18
+ attr_accessor :plugins
17
19
  attr_accessor :signature_checker
20
+ attr_accessor :raise_on_failure
21
+ attr_accessor :max_pushdata_size
22
+ attr_accessor :max_op_count
23
+ attr_accessor :max_stack_size
24
+ attr_accessor :max_script_size
25
+ attr_accessor :integer_max_size
26
+ attr_accessor :locktime_max_size
27
+
28
+ # Execution state
18
29
  attr_accessor :stack
19
- attr_accessor :altstack
30
+ attr_reader :altstack
20
31
  attr_accessor :error # ScriptError instance
21
32
 
22
33
  # Instantiates interpreter with validation flags and an optional checker
23
34
  # (required if the scripts use signature-checking opcodes).
24
35
  # Checker can be transaction checker or block checker
25
36
  def initialize(flags: SCRIPT_VERIFY_NONE,
37
+ plugins: nil,
26
38
  signature_checker: nil,
27
39
  raise_on_failure: false,
28
40
  max_pushdata_size: MAX_SCRIPT_ELEMENT_SIZE,
29
41
  max_op_count: MAX_OPS_PER_SCRIPT,
30
42
  max_stack_size: MAX_STACK_SIZE,
43
+ max_script_size: MAX_SCRIPT_SIZE,
31
44
  integer_max_size: 4,
32
45
  locktime_max_size: 5)
33
46
  @flags = flags
47
+ @plugins = plugins || []
34
48
  @signature_checker = signature_checker
49
+
35
50
  @raise_on_failure = raise_on_failure
36
51
  @max_pushdata_size = max_pushdata_size
37
52
  @max_op_count = max_op_count
38
53
  @max_stack_size = max_stack_size
54
+ @max_script_size = max_script_size
39
55
  @integer_max_size = integer_max_size
40
56
  @locktime_max_size = locktime_max_size
41
-
42
- @stack = []
43
- @altstack = []
44
57
  end
45
58
 
46
59
  # Returns true if succeeded or false in case of failure.
47
60
  # If fails, sets the error attribute.
48
61
  def verify_script(signature_script: nil, output_script: nil)
49
62
 
63
+ @stack = []
64
+ @altstack = []
65
+
50
66
  if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !signature_script.data_only?
51
67
  return set_error(SCRIPT_ERR_SIG_PUSHONLY)
52
68
  end
53
69
 
70
+ if plugin = plugin_to_handle_scripts(signature_script, output_script)
71
+ return plugin.handle_scripts(
72
+ interpreter: self,
73
+ signature_script: signature_script,
74
+ output_script: output_script
75
+ ) && verify_clean_stack_if_needed
76
+ end
77
+
54
78
  if !run_script(signature_script)
55
79
  # error is set in run_script
56
80
  return false
57
81
  end
58
82
 
59
- stack_copy = if flag?(SCRIPT_VERIFY_P2SH)
60
- @stack.dup
83
+ if !did_execute_signature_script(signature_script)
84
+ # error is set already
85
+ return false
86
+ end
87
+
88
+ # This is implemented in P2SHPlugin
89
+ # stack_copy = if flag?(SCRIPT_VERIFY_P2SH)
90
+ # @stack.dup
91
+ # end
92
+
93
+ if plugin = plugin_to_handle_output_script(output_script)
94
+ return plugin.handle_output_script(
95
+ interpreter: self,
96
+ output_script: output_script
97
+ ) && verify_clean_stack_if_needed
61
98
  end
62
99
 
63
100
  if !run_script(output_script)
@@ -73,58 +110,15 @@ module BTC
73
110
  return set_error(SCRIPT_ERR_EVAL_FALSE)
74
111
  end
75
112
 
76
- # Additional validation for pay-to-script-hash (P2SH) transactions:
77
- if flag?(SCRIPT_VERIFY_P2SH) && output_script.p2sh?
78
-
79
- # scriptSig must be literals-only or validation fails
80
- if !signature_script.data_only?
81
- return set_error(SCRIPT_ERR_SIG_PUSHONLY)
82
- end
83
-
84
- # Restore stack.
85
- @stack = stack_copy
86
-
87
- # stack cannot be empty here, because if it was the
88
- # P2SH HASH <> EQUAL scriptPubKey would be evaluated with
89
- # an empty stack and the EvalScript above would return false.
90
- raise "Stack cannot be empty" if @stack.empty?
91
-
92
- serialized_redeem_script = stack_pop
93
- begin
94
- redeem_script = BTC::Script.new(data: serialized_redeem_script)
95
- rescue => e
96
- return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
97
- end
98
-
99
- if !run_script(redeem_script)
100
- # error is set in run_script
101
- return false
102
- end
103
-
104
- if @stack.empty?
105
- return set_error(SCRIPT_ERR_EVAL_FALSE)
106
- end
107
-
108
- if cast_to_bool(@stack.last) == false
109
- return set_error(SCRIPT_ERR_EVAL_FALSE)
110
- end
113
+ if !did_execute_output_script(output_script)
114
+ # error is set already
115
+ return false
111
116
  end
112
117
 
113
- # The CLEANSTACK check is only performed after potential P2SH evaluation,
114
- # as the non-P2SH evaluation of a P2SH script will obviously not result in
115
- # a clean stack (the P2SH inputs remain).
116
- if flag?(SCRIPT_VERIFY_CLEANSTACK)
117
- # Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK
118
- # would be possible, which is not a softfork (and P2SH should be one).
119
- if !flag?(SCRIPT_VERIFY_P2SH)
120
- raise ArgumentError, "CLEANSTACK without P2SH is disallowed"
121
- end
122
- if @stack.size != 1
123
- return set_error(SCRIPT_ERR_CLEANSTACK, "Stack must be clean (should contain one item 'true')")
124
- end
125
- end
126
-
127
- return true
118
+ # Additional validation for pay-to-script-hash (P2SH) transactions:
119
+ # See P2SHPlugin#did_execute_output_script.
120
+
121
+ return verify_clean_stack_if_needed
128
122
  end
129
123
 
130
124
 
@@ -140,6 +134,13 @@ module BTC
140
134
  # Used internally in `verify_script` and also in unit tests.
141
135
  def run_script(script)
142
136
 
137
+ if script.data.bytesize > @max_script_size
138
+ return set_error(SCRIPT_ERR_SCRIPT_SIZE)
139
+ end
140
+
141
+ # Altstack is not shared between individual runs, but we still store it in ivar to make available to the plugins.
142
+ @altstack = []
143
+
143
144
  number_zero = ScriptNumber.new(integer: 0)
144
145
  number_one = ScriptNumber.new(integer: 1)
145
146
  zero_value = "".b
@@ -165,32 +166,45 @@ module BTC
165
166
  return set_error(SCRIPT_ERR_OP_COUNT)
166
167
  end
167
168
  end
168
-
169
- if opcode == OP_CAT ||
170
- opcode == OP_SUBSTR ||
171
- opcode == OP_LEFT ||
172
- opcode == OP_RIGHT ||
173
- opcode == OP_INVERT ||
174
- opcode == OP_AND ||
175
- opcode == OP_OR ||
176
- opcode == OP_XOR ||
177
- opcode == OP_2MUL ||
178
- opcode == OP_2DIV ||
179
- opcode == OP_MUL ||
180
- opcode == OP_DIV ||
181
- opcode == OP_MOD ||
182
- opcode == OP_LSHIFT ||
183
- opcode == OP_RSHIFT
184
-
185
- return set_error(SCRIPT_ERR_DISABLED_OPCODE)
169
+
170
+ # Check if there is a plugin for this opcode before we check for disabled opcodes.
171
+ opcode_plugin = plugin_to_handle_opcode(opcode)
172
+
173
+ if !opcode_plugin
174
+ if opcode == OP_CAT ||
175
+ opcode == OP_SUBSTR ||
176
+ opcode == OP_LEFT ||
177
+ opcode == OP_RIGHT ||
178
+ opcode == OP_INVERT ||
179
+ opcode == OP_AND ||
180
+ opcode == OP_OR ||
181
+ opcode == OP_XOR ||
182
+ opcode == OP_2MUL ||
183
+ opcode == OP_2DIV ||
184
+ opcode == OP_MUL ||
185
+ opcode == OP_DIV ||
186
+ opcode == OP_MOD ||
187
+ opcode == OP_LSHIFT ||
188
+ opcode == OP_RSHIFT
189
+
190
+ return set_error(SCRIPT_ERR_DISABLED_OPCODE)
191
+ end
186
192
  end
187
193
 
188
194
  if should_execute && 0 <= opcode && opcode <= OP_PUSHDATA4
189
195
  # Pushdata (including OP_0).
190
- if require_minimal && !chunk.canonical?
196
+ if require_minimal && !check_minimal_push(chunk.pushdata, opcode)
191
197
  return set_error(SCRIPT_ERR_MINIMALDATA)
192
198
  end
193
199
  stack_push(chunk.pushdata)
200
+
201
+ elsif should_execute && opcode_plugin
202
+
203
+ if !opcode_plugin.handle_opcode(interpreter: self, opcode: opcode)
204
+ # error is set already
205
+ return false
206
+ end
207
+
194
208
  elsif should_execute || (OP_IF <= opcode && opcode <= OP_ENDIF)
195
209
 
196
210
  case opcode
@@ -207,47 +221,50 @@ module BTC
207
221
 
208
222
  when OP_NOP
209
223
  # nothing
210
-
211
- when OP_CHECKLOCKTIMEVERIFY
212
- if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
213
- # not enabled; treat as a NOP2
214
- if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
215
- return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
216
- end
217
- break
218
- end
219
-
220
- if @stack.size < 1
221
- return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
222
- end
223
-
224
- # Note that elsewhere numeric opcodes are limited to
225
- # operands in the range -2**31+1 to 2**31-1, however it is
226
- # legal for opcodes to produce results exceeding that
227
- # range. This limitation is implemented by CScriptNum's
228
- # default 4-byte limit.
229
- #
230
- # If we kept to that limit we'd have a year 2038 problem,
231
- # even though the nLockTime field in transactions
232
- # themselves is uint32 which only becomes meaningless
233
- # after the year 2106.
234
- #
235
- # Thus as a special case we tell CScriptNum to accept up
236
- # to 5-byte bignums, which are good until 2**39-1, well
237
- # beyond the 2**32-1 limit of the nLockTime field itself.
238
- locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
239
-
240
- # In the rare event that the argument may be < 0 due to
241
- # some arithmetic being done first, you can always use
242
- # 0 MAX CHECKLOCKTIMEVERIFY.
243
- if locktime < 0
244
- return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
245
- end
246
-
247
- # Actually compare the specified lock time with the transaction.
248
- if !signature_checker.check_lock_time(locktime)
249
- return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
250
- end
224
+
225
+ # See CLTVPlugin
226
+ # when OP_CHECKLOCKTIMEVERIFY
227
+ # begin
228
+ # if !flag?(SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
229
+ # # not enabled; treat as a NOP2
230
+ # if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
231
+ # return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS)
232
+ # end
233
+ # break # breaks out of begin ... end while false
234
+ # end
235
+ #
236
+ # if @stack.size < 1
237
+ # return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION)
238
+ # end
239
+ #
240
+ # # Note that elsewhere numeric opcodes are limited to
241
+ # # operands in the range -2**31+1 to 2**31-1, however it is
242
+ # # legal for opcodes to produce results exceeding that
243
+ # # range. This limitation is implemented by CScriptNum's
244
+ # # default 4-byte limit.
245
+ # #
246
+ # # If we kept to that limit we'd have a year 2038 problem,
247
+ # # even though the nLockTime field in transactions
248
+ # # themselves is uint32 which only becomes meaningless
249
+ # # after the year 2106.
250
+ # #
251
+ # # Thus as a special case we tell CScriptNum to accept up
252
+ # # to 5-byte bignums, which are good until 2**39-1, well
253
+ # # beyond the 2**32-1 limit of the nLockTime field itself.
254
+ # locktime = cast_to_number(@stack.last, max_size: @locktime_max_size)
255
+ #
256
+ # # In the rare event that the argument may be < 0 due to
257
+ # # some arithmetic being done first, you can always use
258
+ # # 0 MAX CHECKLOCKTIMEVERIFY.
259
+ # if locktime < 0
260
+ # return set_error(SCRIPT_ERR_NEGATIVE_LOCKTIME)
261
+ # end
262
+ #
263
+ # # Actually compare the specified lock time with the transaction.
264
+ # if !signature_checker.check_lock_time(locktime)
265
+ # return set_error(SCRIPT_ERR_UNSATISFIED_LOCKTIME)
266
+ # end
267
+ # end while false
251
268
 
252
269
  when OP_NOP1..OP_NOP10
253
270
  if flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
@@ -567,9 +584,9 @@ module BTC
567
584
  when OP_GREATERTHANOREQUAL
568
585
  bn = ScriptNumber.new(boolean: (bn1 >= bn2))
569
586
  when OP_MIN
570
- bn = ScriptNumber.new(boolean: (bn1 < bn2 ? bn1 : bn2))
587
+ bn = (bn1 < bn2 ? bn1 : bn2)
571
588
  when OP_MAX
572
- bn = ScriptNumber.new(boolean: (bn1 > bn2 ? bn1 : bn2))
589
+ bn = (bn1 > bn2 ? bn1 : bn2)
573
590
  else
574
591
  raise "Invalid opcode"
575
592
  end
@@ -775,7 +792,7 @@ module BTC
775
792
  end
776
793
 
777
794
  else # unknown opcode
778
- return set_error(SCRIPT_ERR_BAD_OPCODE)
795
+ return set_error(SCRIPT_ERR_BAD_OPCODE, "Unknown opcode 0x#{opcode.to_s(16)}")
779
796
 
780
797
  end # case opcode
781
798
  end # within IF scope
@@ -792,6 +809,8 @@ module BTC
792
809
  end
793
810
 
794
811
  return true
812
+ rescue ScriptNumberError => e
813
+ return set_error(SCRIPT_ERR_UNKNOWN_ERROR, e.message)
795
814
  end # run_script
796
815
 
797
816
 
@@ -806,6 +825,30 @@ module BTC
806
825
  #puts "PUSHED TO STACK: #{@stack.map{|s|s.to_hex}.join(' ')}"
807
826
  end
808
827
 
828
+ # aka CheckMinimalPush(const valtype& data, opcodetype opcode)
829
+ def check_minimal_push(data, opcode)
830
+ if data.bytesize == 0
831
+ # Could have used OP_0.
832
+ return opcode == OP_0
833
+ elsif data.bytesize == 1 && data.bytes[0] >= 1 && data.bytes[0] <= 16
834
+ # Could have used OP_1 .. OP_16.
835
+ return opcode == OP_1 + (data.bytes[0] - 1)
836
+ elsif data.bytesize == 1 && data.bytes[0] == 0x81
837
+ # Could have used OP_1NEGATE.
838
+ return opcode == OP_1NEGATE
839
+ elsif data.bytesize <= 75
840
+ # Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
841
+ return opcode == data.bytesize
842
+ elsif data.bytesize <= 255
843
+ # Could have used OP_PUSHDATA.
844
+ return opcode == OP_PUSHDATA1
845
+ elsif (data.bytesize <= 65535)
846
+ # Could have used OP_PUSHDATA2.
847
+ return opcode == OP_PUSHDATA2
848
+ end
849
+ return true
850
+ end
851
+
809
852
  def check_signature_encoding(sig)
810
853
  # Empty signature. Not strictly DER encoded, but allowed to provide a
811
854
  # compact way to provide an invalid signature for use with CHECK(MULTI)SIG
@@ -847,7 +890,13 @@ module BTC
847
890
 
848
891
  # If multiple flags are provided, returns true if any of them are present
849
892
  def flag?(flags)
850
- (@flags & flags) != 0
893
+ (all_flags & flags) != 0
894
+ end
895
+
896
+ def all_flags
897
+ @plugins.inject(@flags) do |f, p|
898
+ f | p.extra_flags
899
+ end
851
900
  end
852
901
 
853
902
  def cast_to_number(data,
@@ -876,6 +925,67 @@ module BTC
876
925
  false
877
926
  end
878
927
 
928
+ private
929
+
930
+ def plugin_to_handle_scripts(signature_script, output_script)
931
+ @plugins.each do |plugin|
932
+ if plugin.should_handle_scripts(interpreter: self, signature_script: signature_script, output_script: output_script)
933
+ return plugin
934
+ end
935
+ end
936
+ nil
937
+ end
938
+
939
+ def plugin_to_handle_output_script(output_script)
940
+ @plugins.each do |plugin|
941
+ if plugin.should_handle_output_script(interpreter: self, output_script: output_script)
942
+ return plugin
943
+ end
944
+ end
945
+ nil
946
+ end
947
+
948
+ def plugin_to_handle_opcode(opcode)
949
+ @plugins.each do |plugin|
950
+ if plugin.should_handle_opcode(interpreter: self, opcode: opcode)
951
+ return plugin
952
+ end
953
+ end
954
+ nil
955
+ end
956
+
957
+ def did_execute_signature_script(signature_script)
958
+ @plugins.each do |plugin|
959
+ if !plugin.did_execute_signature_script(interpreter: self, signature_script: signature_script)
960
+ return false
961
+ end
962
+ end
963
+ end
964
+
965
+ def did_execute_output_script(output_script)
966
+ @plugins.each do |plugin|
967
+ if !plugin.did_execute_output_script(interpreter: self, output_script: output_script)
968
+ return false
969
+ end
970
+ end
971
+ end
972
+
973
+ def verify_clean_stack_if_needed
974
+ # The CLEANSTACK check is only performed after potential P2SH evaluation,
975
+ # as the non-P2SH evaluation of a P2SH script will obviously not result in
976
+ # a clean stack (the P2SH inputs remain).
977
+ if flag?(SCRIPT_VERIFY_CLEANSTACK)
978
+ # Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK
979
+ # would be possible, which is not a softfork (and P2SH should be one).
980
+ if !flag?(SCRIPT_VERIFY_P2SH)
981
+ raise ArgumentError, "CLEANSTACK without P2SH is disallowed"
982
+ end
983
+ if @stack.size != 1
984
+ return set_error(SCRIPT_ERR_CLEANSTACK, "Stack must be clean (should contain one item 'true')")
985
+ end
986
+ end
987
+ return true
988
+ end
879
989
 
880
990
  end
881
991
  end