bitcoin-ruby 0.0.5 → 0.0.6

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