bitcoin-ruby 0.0.5 → 0.0.6

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.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +2 -2
  4. data/COPYING +1 -1
  5. data/Gemfile +5 -11
  6. data/README.rdoc +11 -5
  7. data/Rakefile +5 -0
  8. data/bin/bitcoin_node +11 -29
  9. data/bin/bitcoin_node_cli +81 -0
  10. data/bin/bitcoin_wallet +9 -6
  11. data/doc/NODE.rdoc +79 -26
  12. data/examples/bbe_verify_tx.rb +1 -1
  13. data/examples/index_nhash.rb +24 -0
  14. data/examples/reindex_p2sh_addrs.rb +44 -0
  15. data/lib/bitcoin.rb +135 -20
  16. data/lib/bitcoin/builder.rb +233 -63
  17. data/lib/bitcoin/key.rb +89 -16
  18. data/lib/bitcoin/litecoin.rb +13 -11
  19. data/lib/bitcoin/namecoin.rb +5 -4
  20. data/lib/bitcoin/network/command_client.rb +23 -13
  21. data/lib/bitcoin/network/command_handler.rb +336 -131
  22. data/lib/bitcoin/network/connection_handler.rb +14 -13
  23. data/lib/bitcoin/network/node.rb +61 -20
  24. data/lib/bitcoin/protocol.rb +5 -1
  25. data/lib/bitcoin/protocol/block.rb +15 -3
  26. data/lib/bitcoin/protocol/parser.rb +3 -3
  27. data/lib/bitcoin/protocol/tx.rb +82 -20
  28. data/lib/bitcoin/protocol/txin.rb +7 -0
  29. data/lib/bitcoin/protocol/txout.rb +12 -9
  30. data/lib/bitcoin/script.rb +329 -75
  31. data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
  32. data/lib/bitcoin/storage/models.rb +6 -11
  33. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
  34. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
  35. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
  36. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
  37. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
  38. data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
  39. data/lib/bitcoin/storage/storage.rb +161 -97
  40. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
  41. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
  42. data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
  43. data/lib/bitcoin/validation.rb +87 -56
  44. data/lib/bitcoin/version.rb +1 -1
  45. data/spec/bitcoin/bitcoin_spec.rb +38 -0
  46. data/spec/bitcoin/builder_spec.rb +177 -0
  47. data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
  48. data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
  49. data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
  50. data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
  51. data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
  52. data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
  53. data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
  54. data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
  55. data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
  56. data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
  57. data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
  58. data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
  59. data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
  60. data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
  61. data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
  62. data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
  63. data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
  64. data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
  65. data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
  66. data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
  67. data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
  68. data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
  69. data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
  70. data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
  71. data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
  72. data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
  73. data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
  74. data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
  75. data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
  76. data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
  77. data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
  78. data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
  79. data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
  80. data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
  81. data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
  82. data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
  83. data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
  84. data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
  85. data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
  86. data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
  87. data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
  88. data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
  89. data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
  90. data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
  91. data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
  92. data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
  93. data/spec/bitcoin/key_spec.rb +79 -8
  94. data/spec/bitcoin/namecoin_spec.rb +1 -1
  95. data/spec/bitcoin/node/command_api_spec.rb +373 -86
  96. data/spec/bitcoin/performance/storage_spec.rb +41 -0
  97. data/spec/bitcoin/protocol/addr_spec.rb +7 -5
  98. data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
  99. data/spec/bitcoin/protocol/block_spec.rb +6 -0
  100. data/spec/bitcoin/protocol/tx_spec.rb +184 -1
  101. data/spec/bitcoin/protocol/txin_spec.rb +27 -0
  102. data/spec/bitcoin/protocol/txout_spec.rb +27 -0
  103. data/spec/bitcoin/script/opcodes_spec.rb +74 -3
  104. data/spec/bitcoin/script/script_spec.rb +271 -0
  105. data/spec/bitcoin/spec_helper.rb +34 -6
  106. data/spec/bitcoin/storage/models_spec.rb +104 -0
  107. data/spec/bitcoin/storage/reorg_spec.rb +42 -11
  108. data/spec/bitcoin/storage/storage_spec.rb +58 -15
  109. data/spec/bitcoin/storage/validation_spec.rb +44 -14
  110. data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
  111. data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
  112. data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
  113. metadata +117 -11
@@ -42,6 +42,13 @@ module Bitcoin
42
42
  @prev_out_index == other.prev_out_index &&
43
43
  @script_sig == other.script_sig &&
44
44
  @sequence == other.sequence
45
+ rescue
46
+ false
47
+ end
48
+
49
+ # returns true if the sequence number is final (DEFAULT_SEQUENCE)
50
+ def is_final?
51
+ self.sequence == DEFAULT_SEQUENCE
45
52
  end
46
53
 
47
54
  # parse raw binary data for transaction input
@@ -11,6 +11,9 @@ module Bitcoin
11
11
  # pk_script output Script
12
12
  attr_accessor :pk_script, :pk_script_length
13
13
 
14
+ # p2sh redeem script (optional, not included in the serialized binary format)
15
+ attr_accessor :redeem_script
16
+
14
17
  def initialize *args
15
18
  if args.size == 2
16
19
  @value, @pk_script_length, @pk_script = args[0], args[1].bytesize, args[1]
@@ -20,7 +23,7 @@ module Bitcoin
20
23
  end
21
24
 
22
25
  def ==(other)
23
- @value == other.value && @pk_script == other.pk_script
26
+ @value == other.value && @pk_script == other.pk_script rescue false
24
27
  end
25
28
 
26
29
  # parse raw binary data for transaction output
@@ -43,8 +46,8 @@ module Bitcoin
43
46
 
44
47
  alias :parse_payload :parse_data
45
48
 
46
- def get_script
47
- @script_cache || Bitcoin::Script.new(@pk_script)
49
+ def parsed_script
50
+ @parsed_script ||= Bitcoin::Script.new(pk_script)
48
51
  end
49
52
 
50
53
  def to_payload
@@ -56,10 +59,9 @@ module Bitcoin
56
59
  end
57
60
 
58
61
  def to_hash(options = {})
59
- script = get_script
60
62
  h = { 'value' => "%.8f" % (@value / 100000000.0),
61
- 'scriptPubKey' => script.to_string }
62
- h["address"] = script.get_address if script.is_hash160? && options[:with_address]
63
+ 'scriptPubKey' => parsed_script.to_string }
64
+ h["address"] = parsed_script.get_address if parsed_script.is_hash160? && options[:with_address]
63
65
  h
64
66
  end
65
67
 
@@ -68,17 +70,18 @@ module Bitcoin
68
70
  script = Script.binary_from_string(output['scriptPubKey'])
69
71
  new(amount, script)
70
72
  end
73
+
71
74
  # set pk_script and pk_script_length
72
- def pk_script=(script)
73
- @pk_script_length, @pk_script = script.bytesize, script
75
+ def pk_script=(pk_script)
76
+ @pk_script_length, @pk_script = pk_script.bytesize, pk_script
74
77
  end
75
78
 
76
79
  alias :amount :value
77
80
  alias :amount= :value=
81
+
78
82
  alias :script :pk_script
79
83
  alias :script= :pk_script=
80
84
 
81
-
82
85
  # create output spending +value+ btc (base units) to +address+
83
86
  def self.value_to_address(value, address)
84
87
  pk_script = Bitcoin::Script.to_address_script(address)
@@ -4,10 +4,26 @@ require 'bitcoin'
4
4
 
5
5
  class Bitcoin::Script
6
6
 
7
- OP_1 = 81
8
- OP_TRUE = 81
9
7
  OP_0 = 0
10
8
  OP_FALSE = 0
9
+ OP_1 = 81
10
+ OP_TRUE = 81
11
+ OP_2 = 0x52
12
+ OP_3 = 0x53
13
+ OP_4 = 0x54
14
+ OP_5 = 0x55
15
+ OP_6 = 0x56
16
+ OP_7 = 0x57
17
+ OP_8 = 0x58
18
+ OP_9 = 0x59
19
+ OP_10 = 0x5a
20
+ OP_11 = 0x5b
21
+ OP_12 = 0x5c
22
+ OP_13 = 0x5d
23
+ OP_14 = 0x5e
24
+ OP_15 = 0x5f
25
+ OP_16 = 0x60
26
+
11
27
  OP_PUSHDATA0 = 0
12
28
  OP_PUSHDATA1 = 76
13
29
  OP_PUSHDATA2 = 77
@@ -50,6 +66,7 @@ class Bitcoin::Script
50
66
  OP_MIN = 163
51
67
  OP_MAX = 164
52
68
  OP_2OVER = 112
69
+ OP_2ROT = 113
53
70
  OP_2SWAP = 114
54
71
  OP_IFDUP = 115
55
72
  OP_DEPTH = 116
@@ -133,14 +150,30 @@ class Bitcoin::Script
133
150
  2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] }
134
151
  [1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") }
135
152
 
153
+ SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
136
154
 
137
155
  attr_reader :raw, :chunks, :debug
138
156
 
139
157
  # create a new script. +bytes+ is typically input_script + output_script
140
- def initialize(bytes, offset=0)
141
- @raw = bytes
158
+ def initialize(input_script, previous_output_script=nil)
159
+ @raw_byte_sizes = [input_script.bytesize, previous_output_script ? previous_output_script.bytesize : 0]
160
+
161
+ @raw = if previous_output_script
162
+ input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + previous_output_script
163
+ else
164
+ input_script
165
+ end
166
+
167
+ @chunks = parse(input_script)
168
+
169
+ if previous_output_script
170
+ @script_codeseparator_index = @chunks.size
171
+ @chunks << Bitcoin::Script::OP_CODESEPARATOR
172
+ @chunks += parse(previous_output_script)
173
+ end
174
+
142
175
  @stack, @stack_alt, @exec_stack = [], [], []
143
- @chunks = parse(bytes, offset)
176
+ @last_codeseparator_index = 0
144
177
  @do_exec = true
145
178
  end
146
179
 
@@ -154,14 +187,14 @@ class Bitcoin::Script
154
187
  program = bytes.unpack("C*")
155
188
  chunks = []
156
189
  until program.empty?
157
- opcode = program.shift(1)[0]
190
+ opcode = program.shift
158
191
 
159
192
  if (opcode > 0) && (opcode < OP_PUSHDATA1)
160
193
  len, tmp = opcode, program[0]
161
194
  chunks << program.shift(len).pack("C*")
162
195
 
163
196
  # 0x16 = 22 due to OP_2_16 from_string parsing
164
- if len == 1 && tmp <= 22
197
+ if len == 1 && tmp && tmp <= 22
165
198
  chunks.last.bitcoin_pushdata = OP_PUSHDATA0
166
199
  chunks.last.bitcoin_pushdata_length = len
167
200
  else
@@ -202,13 +235,14 @@ class Bitcoin::Script
202
235
  end
203
236
  end
204
237
  chunks
205
- rescue Exception => ex
238
+ rescue => ex
206
239
  # bail out! #run returns false but serialization roundtrips still create the right payload.
240
+ chunks.pop if ex.message.include?("invalid OP_PUSHDATA")
207
241
  @parse_invalid = true
208
242
  c = bytes.unpack("C*").pack("C*")
209
243
  c.bitcoin_pushdata = OP_PUSHDATA_INVALID
210
244
  c.bitcoin_pushdata_length = c.bytesize
211
- chunks = [ c ]
245
+ chunks << c
212
246
  end
213
247
 
214
248
  # string representation of the script
@@ -244,11 +278,44 @@ class Bitcoin::Script
244
278
  end
245
279
  alias :to_payload :to_binary
246
280
 
247
-
248
281
  def to_binary_without_signatures(drop_signatures, chunks=nil)
249
- to_binary( (chunks || @chunks).select{|i| drop_signatures.none?{|e| e == i } } )
282
+ buf = []
283
+ (chunks || @chunks).each.with_index{|chunk,idx|
284
+ if chunk == OP_CODESEPARATOR and idx <= @last_codeseparator_index
285
+ buf.clear
286
+ elsif chunk == OP_CODESEPARATOR
287
+ if idx == @script_codeseparator_index
288
+ break
289
+ else
290
+ # skip
291
+ end
292
+ elsif drop_signatures.none?{|e| e == chunk }
293
+ buf << chunk
294
+ end
295
+ }
296
+ to_binary(buf)
297
+ end
298
+
299
+ # Adds opcode (OP_0, OP_1, ... OP_CHECKSIG etc.)
300
+ # Returns self.
301
+ def append_opcode(opcode)
302
+ raise "Opcode should be a Fixnum" if !opcode.is_a?(Fixnum)
303
+ if opcode >= OP_0 && opcode <= 0xff
304
+ @chunks << opcode
305
+ else
306
+ raise "Opcode should be within [0x00, 0xff]"
307
+ end
308
+ self
250
309
  end
251
310
 
311
+ # Adds binary string as pushdata. Pushdata will be encoded in the most compact form
312
+ # (unless the string contains internal info about serialization that's added by Script class)
313
+ # Returns self.
314
+ def append_pushdata(pushdata_string)
315
+ raise "Pushdata should be a string" if !pushdata_string.is_a?(String)
316
+ @chunks << pushdata_string
317
+ self
318
+ end
252
319
 
253
320
  def self.pack_pushdata(data)
254
321
  size = data.bytesize
@@ -333,15 +400,17 @@ class Bitcoin::Script
333
400
  return false if @parse_invalid
334
401
 
335
402
  #p [to_string, block_timestamp, is_p2sh?]
336
- @script_invalid = true if @raw.bytesize > 10_000
403
+ @script_invalid = true if @raw_byte_sizes.any?{|size| size > 10_000 }
404
+ @last_codeseparator_index = 0
337
405
 
338
406
  if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
339
407
  return pay_to_script_hash(check_callback) if is_p2sh?
340
408
  end
341
409
 
342
410
  @debug = []
343
- @chunks.each{|chunk|
411
+ @chunks.each.with_index{|chunk,idx|
344
412
  break if invalid?
413
+ @chunk_last_index = idx
345
414
 
346
415
  @debug << @stack.map{|i| i.unpack("H*") rescue i}
347
416
  @do_exec = @exec_stack.count(false) == 0 ? true : false
@@ -355,7 +424,7 @@ class Bitcoin::Script
355
424
  break
356
425
  end
357
426
 
358
- next unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
427
+ next @debug.pop unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
359
428
 
360
429
  case chunk
361
430
  when *OPCODES_METHOD.keys
@@ -374,6 +443,8 @@ class Bitcoin::Script
374
443
  if @do_exec
375
444
  @debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
376
445
  @stack << chunk
446
+ else
447
+ @debug.pop
377
448
  end
378
449
  end
379
450
  }
@@ -386,7 +457,7 @@ class Bitcoin::Script
386
457
 
387
458
  @debug << "RESULT"
388
459
  return false if @stack.empty?
389
- return false if [0, ''].include?(@stack.pop)
460
+ return false if cast_to_bool(@stack.pop) == false
390
461
  true
391
462
  end
392
463
 
@@ -405,10 +476,10 @@ class Bitcoin::Script
405
476
  def pay_to_script_hash(check_callback)
406
477
  return false if @chunks.size < 4
407
478
  *rest, script, _, script_hash, _ = @chunks
479
+ script = rest.pop if script == OP_CODESEPARATOR
408
480
  script, script_hash = cast_to_string(script), cast_to_string(script_hash)
409
481
 
410
482
  return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
411
- rest.delete_at(0) if rest[0] && cast_to_bignum(rest[0]) == 0
412
483
 
413
484
  script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
414
485
  result = script.run(&check_callback)
@@ -419,7 +490,19 @@ class Bitcoin::Script
419
490
  def inner_p2sh!(script=nil); @inner_p2sh = true; @inner_script_code = script; self; end
420
491
  def inner_p2sh?; @inner_p2sh; end
421
492
 
493
+ # get the inner p2sh script
494
+ def inner_p2sh_script
495
+ return nil if @chunks.size < 4
496
+ *rest, script, _, script_hash, _ = @chunks
497
+ script = rest.pop if script == OP_CODESEPARATOR
498
+ script, script_hash = cast_to_string(script), cast_to_string(script_hash)
499
+
500
+ return nil unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
501
+ script
502
+ end
503
+
422
504
  def is_pay_to_script_hash?
505
+ return false if @inner_p2sh
423
506
  return false unless @chunks[-2].is_a?(String)
424
507
  @chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
425
508
  @chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
@@ -428,17 +511,17 @@ class Bitcoin::Script
428
511
 
429
512
  # check if script is in one of the recognized standard formats
430
513
  def is_standard?
431
- is_pubkey? || is_hash160? || is_multisig? || is_p2sh?
514
+ is_pubkey? || is_hash160? || is_multisig? || is_p2sh? || is_op_return?
432
515
  end
433
516
 
434
- # is this a pubkey tx
517
+ # is this a pubkey script
435
518
  def is_pubkey?
436
519
  return false if @chunks.size != 2
437
- (@chunks[1] == OP_CHECKSIG) && @chunks[0].size > 1
520
+ (@chunks[1] == OP_CHECKSIG) && @chunks[0] && (@chunks[0].is_a?(String)) && @chunks[0] != OP_RETURN
438
521
  end
439
522
  alias :is_send_to_ip? :is_pubkey?
440
523
 
441
- # is this a hash160 (address) tx
524
+ # is this a hash160 (address) script
442
525
  def is_hash160?
443
526
  return false if @chunks.size != 5
444
527
  (@chunks[0..1] + @chunks[-2..-1]) ==
@@ -446,18 +529,24 @@ class Bitcoin::Script
446
529
  @chunks[2].is_a?(String) && @chunks[2].bytesize == 20
447
530
  end
448
531
 
449
- # is this a multisig tx
532
+ # is this a multisig script
450
533
  def is_multisig?
451
- return false if @chunks.size > 6 || @chunks.size < 4 || !@chunks[-2].is_a?(Fixnum)
534
+ return false if @chunks.size < 4 || !@chunks[-2].is_a?(Fixnum)
452
535
  @chunks[-1] == OP_CHECKMULTISIG and get_multisig_pubkeys.all?{|c| c.is_a?(String) }
453
536
  end
454
537
 
538
+ # is this an op_return script
539
+ def is_op_return?
540
+ @chunks[0] == OP_RETURN && @chunks.size <= 2
541
+ end
542
+
455
543
  # get type of this tx
456
544
  def type
457
545
  if is_hash160?; :hash160
458
546
  elsif is_pubkey?; :pubkey
459
547
  elsif is_multisig?; :multisig
460
548
  elsif is_p2sh?; :p2sh
549
+ elsif is_op_return?;:op_return
461
550
  else; :unknown
462
551
  end
463
552
  end
@@ -504,6 +593,12 @@ class Bitcoin::Script
504
593
  Bitcoin.hash160_to_p2sh_address(get_hash160)
505
594
  end
506
595
 
596
+ # get the data possibly included in an OP_RETURN script
597
+ def get_op_return_data
598
+ return nil unless is_op_return?
599
+ cast_to_string(@chunks[1]).unpack("H*")[0] if @chunks[1]
600
+ end
601
+
507
602
  # get all addresses this script corresponds to (if possible)
508
603
  def get_addresses
509
604
  return [get_pubkey_address] if is_pubkey?
@@ -519,25 +614,30 @@ class Bitcoin::Script
519
614
  addrs.is_a?(Array) ? addrs[0] : addrs
520
615
  end
521
616
 
522
- # generate pubkey tx script for given +pubkey+
617
+ # generate pubkey tx script for given +pubkey+. returns a raw binary script of the form:
618
+ # <pubkey> OP_CHECKSIG
523
619
  def self.to_pubkey_script(pubkey)
524
- pk = [pubkey].pack("H*")
525
- [[pk.bytesize].pack("C"), pk, "\xAC"].join
620
+ pack_pushdata([pubkey].pack("H*")) + [ OP_CHECKSIG ].pack("C")
526
621
  end
527
622
 
528
- # generate hash160 tx for given +address+
623
+ # generate hash160 tx for given +address+. returns a raw binary script of the form:
624
+ # OP_DUP OP_HASH160 <hash160> OP_EQUALVERIFY OP_CHECKSIG
529
625
  def self.to_hash160_script(hash160)
530
626
  return nil unless hash160
531
627
  # DUP HASH160 length hash160 EQUALVERIFY CHECKSIG
532
628
  [ ["76", "a9", "14", hash160, "88", "ac"].join ].pack("H*")
533
629
  end
534
630
 
631
+ # generate p2sh output script for given +p2sh+ hash160. returns a raw binary script of the form:
632
+ # OP_HASH160 <p2sh> OP_EQUAL
535
633
  def self.to_p2sh_script(p2sh)
536
634
  return nil unless p2sh
537
635
  # HASH160 length hash EQUAL
538
636
  [ ["a9", "14", p2sh, "87"].join ].pack("H*")
539
637
  end
540
638
 
639
+ # generate hash160 or p2sh output script, depending on the type of the given +address+.
640
+ # see #to_hash160_script and #to_p2sh_script.
541
641
  def self.to_address_script(address)
542
642
  hash160 = Bitcoin.hash160_from_address(address)
543
643
  case Bitcoin.address_type(address)
@@ -546,30 +646,53 @@ class Bitcoin::Script
546
646
  end
547
647
  end
548
648
 
549
- # generate multisig tx for given +pubkeys+, expecting +m+ signatures
649
+ # generate multisig output script for given +pubkeys+, expecting +m+ signatures.
650
+ # returns a raw binary script of the form:
651
+ # <m> <pubkey> [<pubkey> ...] <n_pubkeys> OP_CHECKMULTISIG
550
652
  def self.to_multisig_script(m, *pubkeys)
551
- pubs = pubkeys.map{|pk|p=[pk].pack("H*"); [p.bytesize].pack("C") + p}
552
- [ [80 + m.to_i].pack("C"), *pubs, [80 + pubs.size].pack("C"), "\xAE"].join
653
+ raise "invalid m-of-n number" unless [m, pubkeys.size].all?{|i| (0..20).include?(i) }
654
+ raise "invalid m-of-n number" if pubkeys.size < m
655
+ pubs = pubkeys.map{|pk| pack_pushdata([pk].pack("H*")) }
656
+
657
+ m = m > 16 ? pack_pushdata([m].pack("C")) : [80 + m.to_i].pack("C")
658
+ n = pubkeys.size > 16 ? pack_pushdata([pubkeys.size].pack("C")) : [80 + pubs.size].pack("C")
659
+
660
+ [ m, *pubs, n, [OP_CHECKMULTISIG].pack("C")].join
661
+ end
662
+
663
+ # generate OP_RETURN output script with given data. returns a raw binary script of the form:
664
+ # OP_RETURN <data>
665
+ def self.to_op_return_script(data = nil)
666
+ buf = [ OP_RETURN ].pack("C")
667
+ return buf unless data
668
+ return buf + pack_pushdata( [data].pack("H*") )
553
669
  end
554
670
 
555
- # generate pubkey script sig for given +signature+ and +pubkey+
671
+ # generate input script sig spending a pubkey output with given +signature+ and +pubkey+.
672
+ # returns a raw binary script sig of the form:
673
+ # <signature> [<pubkey>]
556
674
  def self.to_pubkey_script_sig(signature, pubkey)
557
- hash_type = "\x01"
558
- #pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
559
- return [ [signature.bytesize+1].pack("C"), signature, hash_type ].join unless pubkey
675
+ hash_type = [ SIGHASH_TYPE[:all] ].pack("C")
676
+ buf = pack_pushdata(signature + hash_type)
677
+ return buf unless pubkey
560
678
 
561
- case pubkey[0]
562
- when "\x04"
563
- expected_size = 65
564
- when "\x02", "\x03"
565
- expected_size = 33
566
- end
679
+ expected_size = case pubkey[0]
680
+ when "\x04"; 65
681
+ when "\x02", "\x03"; 33
682
+ end
567
683
 
568
- if !expected_size || pubkey.bytesize != expected_size
569
- raise "pubkey is not in binary form"
570
- end
684
+ raise "pubkey is not in binary form" if !expected_size || pubkey.bytesize != expected_size
685
+
686
+ return buf + pack_pushdata(pubkey)
687
+ end
571
688
 
572
- [ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
689
+ # generate p2sh multisig output script for given +args+.
690
+ # returns the p2sh output script, and the redeem script needed to spend it.
691
+ # see #to_multisig_script for the redeem script, and #to_p2sh_script for the p2sh script.
692
+ def self.to_p2sh_multisig_script(*args)
693
+ redeem_script = to_multisig_script(*args)
694
+ p2sh_script = to_p2sh_script(Bitcoin.hash160(redeem_script.hth))
695
+ return p2sh_script, redeem_script
573
696
  end
574
697
 
575
698
  # alias for #to_pubkey_script_sig
@@ -577,8 +700,30 @@ class Bitcoin::Script
577
700
  to_pubkey_script_sig(*a)
578
701
  end
579
702
 
703
+ # generate input script sig spending a multisig output script.
704
+ # returns a raw binary script sig of the form:
705
+ # OP_0 <sig> [<sig> ...]
580
706
  def self.to_multisig_script_sig(*sigs)
581
- from_string("0 #{sigs.map{|s|s.unpack('H*')[0]}.join(' ')}").raw
707
+ partial_script = [OP_0].pack("C*")
708
+ sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script) }
709
+ partial_script
710
+ end
711
+
712
+ # take a multisig script sig (or p2sh multisig script sig) and add
713
+ # another signature to it after the OP_0. Used to sign a tx by
714
+ # multiple parties. Signatures must be in the same order as the
715
+ # pubkeys in the output script being redeemed.
716
+ def self.add_sig_to_multisig_script_sig(sig, script_sig)
717
+ signature = sig + [SIGHASH_TYPE[:all]].pack("C*")
718
+ offset = script_sig.empty? ? 0 : 1
719
+ script_sig.insert(offset, pack_pushdata(signature))
720
+ end
721
+
722
+ # generate input script sig spending a p2sh-multisig output script.
723
+ # returns a raw binary script sig of the form:
724
+ # OP_0 <sig> [<sig> ...] <redeem_script>
725
+ def self.to_p2sh_multisig_script_sig(redeem_script, *sigs)
726
+ to_multisig_script_sig(*sigs.flatten) + pack_pushdata(redeem_script)
582
727
  end
583
728
 
584
729
  def get_signatures_required
@@ -586,6 +731,72 @@ class Bitcoin::Script
586
731
  @chunks[0] - 80
587
732
  end
588
733
 
734
+ # This matches CScript::GetSigOpCount(bool fAccurate)
735
+ # Note: this does not cover P2SH script which is to be unserialized
736
+ # and checked explicitly when validating blocks.
737
+ def sigops_count_accurate(is_accurate)
738
+ count = 0
739
+ last_opcode = nil
740
+ @chunks.each do |chunk| # pushdate or opcode
741
+ if chunk == OP_CHECKSIG || chunk == OP_CHECKSIGVERIFY
742
+ count += 1
743
+ elsif chunk == OP_CHECKMULTISIG || chunk == OP_CHECKMULTISIGVERIFY
744
+ # Accurate mode counts exact number of pubkeys required (not signatures, but pubkeys!). Only used in P2SH scripts.
745
+ # Inaccurate mode counts every multisig as 20 signatures.
746
+ if is_accurate && last_opcode && last_opcode.is_a?(Fixnum) && last_opcode >= OP_1 && last_opcode <= OP_16
747
+ count += ::Bitcoin::Script.decode_OP_N(last_opcode)
748
+ else
749
+ count += 20
750
+ end
751
+ end
752
+ last_opcode = chunk
753
+ end
754
+ count
755
+ end
756
+
757
+ # This method applies to script_sig that is an input for p2sh output.
758
+ # Bitcoind has somewhat special way to return count for invalid input scripts:
759
+ # it returns 0 when the opcode can't be parsed or when it's over OP_16.
760
+ # Also, if the OP_{N} is used anywhere it's treated as 0-length data.
761
+ # See CScript::GetSigOpCount(const CScript& scriptSig) in bitcoind.
762
+ def sigops_count_for_p2sh
763
+ # This is a pay-to-script-hash scriptPubKey;
764
+ # get the last item that the scriptSig
765
+ # pushes onto the stack:
766
+
767
+ return 0 if @chunks.size == 0
768
+
769
+ data = nil
770
+ @chunks.each do |chunk|
771
+ case chunk
772
+ when Fixnum
773
+ data = ""
774
+ return 0 if chunk > OP_16
775
+ when String
776
+ data = chunk
777
+ end
778
+ end
779
+ return 0 if data == ""
780
+
781
+ ::Bitcoin::Script.new(data).sigops_count_accurate(true)
782
+ end
783
+
784
+ # Converts OP_{0,1,2,...,16} into 0, 1, 2, ..., 16.
785
+ # Returns nil for other opcodes.
786
+ def self.decode_OP_N(opcode)
787
+ if opcode == OP_0
788
+ return 0
789
+ end
790
+ if opcode.is_a?(Fixnum) && opcode >= OP_1 && opcode <= OP_16
791
+ return opcode - (OP_1 - 1);
792
+ else
793
+ nil
794
+ end
795
+ end
796
+
797
+
798
+
799
+
589
800
  ## OPCODES
590
801
 
591
802
  # Does nothing
@@ -739,6 +950,7 @@ class Bitcoin::Script
739
950
  @stack << (a + 1)
740
951
  end
741
952
 
953
+ # 1 is subtracted from the input.
742
954
  def op_1sub
743
955
  a = pop_int
744
956
  @stack << (a - 1)
@@ -757,15 +969,14 @@ class Bitcoin::Script
757
969
 
758
970
  # Returns 1 if the inputs are exactly equal, 0 otherwise.
759
971
  def op_equal
760
- #a, b = @stack.pop(2)
761
- a, b = pop_int(2)
972
+ a, b = pop_string(2)
762
973
  @stack << (a == b ? 1 : 0)
763
974
  end
764
975
 
765
976
  # Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
766
977
  def op_verify
767
978
  res = pop_int
768
- if res == 0
979
+ if cast_to_bool(res) == false
769
980
  @stack << res
770
981
  @script_invalid = true # raise 'transaction invalid' ?
771
982
  else
@@ -813,7 +1024,7 @@ class Bitcoin::Script
813
1024
 
814
1025
  # If the input is true, duplicate it.
815
1026
  def op_ifdup
816
- if cast_to_bignum(@stack.last) != 0
1027
+ if cast_to_bool(@stack.last) == true
817
1028
  @stack << @stack.last
818
1029
  end
819
1030
  end
@@ -861,8 +1072,8 @@ class Bitcoin::Script
861
1072
  def op_if
862
1073
  value = false
863
1074
  if @do_exec
864
- return if @stack.size < 1
865
- value = pop_int == 1 ? true : false
1075
+ (invalid; return) if @stack.size < 1
1076
+ value = cast_to_bool(pop_string) == false ? false : true
866
1077
  end
867
1078
  @exec_stack << value
868
1079
  end
@@ -871,8 +1082,8 @@ class Bitcoin::Script
871
1082
  def op_notif
872
1083
  value = false
873
1084
  if @do_exec
874
- return if @stack.size < 1
875
- value = pop_int == 1 ? false : true
1085
+ (invalid; return) if @stack.size < 1
1086
+ value = cast_to_bool(pop_string) == false ? true : false
876
1087
  end
877
1088
  @exec_stack << value
878
1089
  end
@@ -891,14 +1102,24 @@ class Bitcoin::Script
891
1102
 
892
1103
  # The item n back in the stack is copied to the top.
893
1104
  def op_pick
1105
+ return invalid if @stack.size < 2
894
1106
  pos = pop_int
1107
+ return invalid if (pos < 0) || (pos >= @stack.size)
895
1108
  item = @stack[-(pos+1)]
896
1109
  @stack << item if item
897
1110
  end
898
1111
 
1112
+ # The fifth and sixth items back are moved to the top of the stack.
1113
+ def op_2rot
1114
+ return invalid if @stack.size < 6
1115
+ @stack[-6..-1] = [ *@stack[-4..-1], *@stack[-6..-5] ]
1116
+ end
1117
+
899
1118
  # The item n back in the stack is moved to the top.
900
1119
  def op_roll
1120
+ return invalid if @stack.size < 2
901
1121
  pos = pop_int
1122
+ return invalid if (pos < 0) || (pos >= @stack.size)
902
1123
  idx = -(pos+1)
903
1124
  item = @stack[idx]
904
1125
  if item
@@ -959,21 +1180,43 @@ class Bitcoin::Script
959
1180
  end
960
1181
 
961
1182
  def cast_to_bignum(buf)
1183
+ return (invalid; 0) unless buf
962
1184
  case buf
963
- when Numeric; buf
964
- when String; OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
1185
+ when Numeric
1186
+ invalid if OpenSSL::BN.new(buf.to_s).to_s(0).unpack("N")[0] > 4
1187
+ buf
1188
+ when String
1189
+ invalid if buf.bytesize > 4
1190
+ OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
965
1191
  else; raise TypeError, 'cast_to_bignum: failed to cast: %s (%s)' % [buf, buf.class]
966
1192
  end
967
1193
  end
968
1194
 
969
1195
  def cast_to_string(buf)
1196
+ return (invalid; "") unless buf
970
1197
  case buf
971
- when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1]
1198
+ when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1].reverse
972
1199
  when String; buf;
973
1200
  else; raise TypeError, 'cast_to_string: failed to cast: %s (%s)' % [buf, buf.class]
974
1201
  end
975
1202
  end
976
1203
 
1204
+ def cast_to_bool(buf)
1205
+ buf = cast_to_string(buf).unpack("C*")
1206
+ size = buf.size
1207
+ buf.each.with_index{|byte,index|
1208
+ if byte != 0
1209
+ # Can be negative zero
1210
+ if (index == (size-1)) && byte == 0x80
1211
+ return false
1212
+ else
1213
+ return true
1214
+ end
1215
+ end
1216
+ }
1217
+ return false
1218
+ end
1219
+
977
1220
  # Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
978
1221
  def op_numequalverify
979
1222
  op_numequal; op_verify
@@ -983,6 +1226,7 @@ class Bitcoin::Script
983
1226
  # to the data after the most recently-executed OP_CODESEPARATOR.
984
1227
  def op_codeseparator
985
1228
  @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
1229
+ @last_codeseparator_index = @chunk_last_index
986
1230
  end
987
1231
 
988
1232
  def codehash_script(opcode)
@@ -998,9 +1242,9 @@ class Bitcoin::Script
998
1242
  # This is used by Protocol::Tx#verify_input_signature
999
1243
  def op_checksig(check_callback)
1000
1244
  return invalid if @stack.size < 2
1001
- pubkey = @stack.pop
1245
+ pubkey = cast_to_string(@stack.pop)
1002
1246
  #return (@stack << 0) unless Bitcoin::Script.is_canonical_pubkey?(pubkey) # only for isStandard
1003
- drop_sigs = [ @stack[-1] ]
1247
+ drop_sigs = [ cast_to_string(@stack[-1]) ]
1004
1248
 
1005
1249
  signature = cast_to_string(@stack.pop)
1006
1250
  #return (@stack << 0) unless Bitcoin::Script.is_canonical_signature?(signature) # only for isStandard
@@ -1008,21 +1252,25 @@ class Bitcoin::Script
1008
1252
 
1009
1253
  sig, hash_type = parse_sig(signature)
1010
1254
 
1011
- if inner_p2sh?
1012
- script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
1013
- drop_sigs = nil
1014
- else
1015
- script_code, drop_sigs = nil, nil
1016
- end
1255
+ subscript = sighash_subscript(drop_sigs)
1017
1256
 
1018
1257
  if check_callback == nil # for tests
1019
1258
  @stack << 1
1020
1259
  else # real signature check callback
1021
1260
  @stack <<
1022
- ((check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) == true) ? 1 : 0)
1261
+ ((check_callback.call(pubkey, sig, hash_type, subscript) == true) ? 1 : 0)
1023
1262
  end
1024
1263
  end
1025
1264
 
1265
+ def sighash_subscript(drop_sigs)
1266
+ if inner_p2sh? && @inner_script_code
1267
+ ::Bitcoin::Script.new(@inner_script_code).to_binary_without_signatures(drop_sigs)
1268
+ else
1269
+ to_binary_without_signatures(drop_sigs)
1270
+ end
1271
+ end
1272
+
1273
+ # Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
1026
1274
  def op_checksigverify(check_callback)
1027
1275
  op_checksig(check_callback)
1028
1276
  op_verify
@@ -1053,22 +1301,23 @@ class Bitcoin::Script
1053
1301
  n_sigs = pop_int
1054
1302
  return invalid if n_sigs < 0 || n_sigs > n_pubkeys
1055
1303
  return invalid if @stack.size < n_sigs
1056
- sigs = drop_sigs = pop_string(n_sigs)
1304
+ sigs = pop_string(n_sigs)
1305
+ drop_sigs = sigs.dup
1057
1306
 
1058
- @stack.pop if @stack[-1] && cast_to_bignum(@stack[-1]) == 0 # remove OP_0 from stack
1307
+ # Bitcoin-core removes an extra item from the stack
1308
+ @stack.pop
1059
1309
 
1060
- if inner_p2sh?
1061
- script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
1062
- drop_sigs = nil
1063
- else
1064
- script_code, drop_sigs = nil, nil
1065
- end
1310
+ subscript = sighash_subscript(drop_sigs)
1066
1311
 
1067
1312
  success = true
1068
1313
  while success && n_sigs > 0
1069
1314
  sig, pub = sigs.pop, pubkeys.pop
1315
+ unless sig && sig.size > 0
1316
+ success = false
1317
+ break
1318
+ end
1070
1319
  signature, hash_type = parse_sig(sig)
1071
- if check_callback.call(pub, signature, hash_type, drop_sigs, script_code)
1320
+ if pub.size > 0 && check_callback.call(pub, signature, hash_type, subscript)
1072
1321
  n_sigs -= 1
1073
1322
  else
1074
1323
  sigs << sig
@@ -1077,7 +1326,13 @@ class Bitcoin::Script
1077
1326
  success = false if n_sigs > n_pubkeys
1078
1327
  end
1079
1328
 
1080
- @stack << (success ? 1 : (invalid; 0))
1329
+ @stack << (success ? 1 : 0)
1330
+ end
1331
+
1332
+ # Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
1333
+ def op_checkmultisigverify(check_callback)
1334
+ op_checkmultisig(check_callback)
1335
+ op_verify
1081
1336
  end
1082
1337
 
1083
1338
  # op_eval: https://en.bitcoin.it/wiki/BIP_0012
@@ -1105,7 +1360,6 @@ class Bitcoin::Script
1105
1360
  end
1106
1361
 
1107
1362
 
1108
- SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
1109
1363
 
1110
1364
  def self.is_canonical_signature?(sig)
1111
1365
  return false if sig.bytesize < 9 # Non-canonical signature: too short