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
@@ -0,0 +1,41 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require_relative '../spec_helper'
4
+ require_relative '../helpers/fake_blockchain'
5
+ require 'benchmark'
6
+
7
+ [
8
+ [:sequel, :postgres]
9
+ ].compact.each do |options|
10
+
11
+ next unless storage = setup_db(*options)
12
+
13
+ describe "#{storage.backend_name} block storage" do
14
+
15
+ before do
16
+ @store = storage
17
+ @store.reset
18
+ @store.log.level = :error
19
+ @fake_chain = FakeBlockchain.new 10
20
+ end
21
+
22
+ it "block storage" do
23
+ blocks = (0..10).to_a.map{|i| @fake_chain.block(i) }
24
+
25
+ bm = Benchmark.measure do
26
+ bm = Benchmark.bm do |b|
27
+ blocks.each.with_index do |blk,i|
28
+ b.report("storing fake block ##{i}") do
29
+ depth, chain = @store.new_block blk
30
+ chain.should == 0
31
+ end
32
+ end
33
+ end
34
+ end
35
+ puts '-'*80
36
+ puts "TOTAL #{bm.format}"
37
+ end
38
+
39
+
40
+ end
41
+ end
@@ -40,11 +40,13 @@ describe 'Bitcoin::Protocol::Addr' do
40
40
  end
41
41
 
42
42
  it 'initalize time, service and port' do
43
- addr = Bitcoin::Protocol::Addr.new(nil)
44
- t = Time.now.to_i; (t-10..t+10).include?(addr[:time]).should == true
45
- addr[:service] .should == 1
46
- addr[:port] .should == Bitcoin.network[:default_port]
47
- addr[:ip] .should == "127.0.0.1"
43
+ Time.freeze do
44
+ addr = Bitcoin::Protocol::Addr.new(nil)
45
+ addr[:time].should == Time.now.to_i
46
+ addr[:service] .should == 1
47
+ addr[:port] .should == Bitcoin.network[:default_port]
48
+ addr[:ip] .should == "127.0.0.1"
49
+ end
48
50
  end
49
51
 
50
52
  it 'addr payload' do
@@ -7,6 +7,7 @@ include Bitcoin
7
7
  describe Bitcoin::Protocol::AuxPow do
8
8
 
9
9
  before do
10
+ Bitcoin.network = :namecoin
10
11
  @data = fixtures_file("rawblock-auxpow.bin")
11
12
  @blk = P::Block.new(@data)
12
13
  @aux_pow = @blk.aux_pow
@@ -19,6 +19,7 @@ describe 'Bitcoin::Protocol::Block' do
19
19
  '131025' => fixtures_file('rawblock-131025.bin'),
20
20
  # block 26478: 000000000214a3f06ee99a033a7f2252762d6a18d27c3cd8c8fe2278190da9f3
21
21
  'testnet-26478' => fixtures_file('rawblock-testnet-26478.bin'),
22
+ 'testnet-265322' => fixtures_file('rawblock-testnet-265322.bin'),
22
23
  }
23
24
  end
24
25
 
@@ -193,4 +194,9 @@ describe 'Bitcoin::Protocol::Block' do
193
194
  block.bip34_block_height.should == 197657
194
195
  end
195
196
 
197
+ it 'should work with huge block version' do
198
+ Bitcoin::P::Block.new(@blocks['testnet-265322']).hash.should ==
199
+ "0000000000014b351588a177be099e39afd4962cd3d58e9ab5cbe45a9cf83c8a"
200
+ end
201
+
196
202
  end
@@ -49,6 +49,23 @@ describe 'Tx' do
49
49
  tx.binary_hash.should == "\xB4\x02-\x9F\xE5(\xFB\x90pP\x01\x16K\f\xC3\xA8\xF5\xA1\x9C\xB8\xED\x02\xBF\xD4\xFC,\xB6%f\xD1\x9Dn"
50
50
  end
51
51
 
52
+ it '#normalized_hash' do
53
+ tx = Tx.new( @payload[0] )
54
+ tx.normalized_hash.size.should == 64
55
+ tx.normalized_hash.should == "402e30100b6937cc13828ca096377c93afc0ff227ad2f249245e5b1db9123a39"
56
+
57
+ new_tx = JSON.parse(tx.to_json)
58
+ script = Bitcoin::Script.from_string(new_tx['in'][0]['scriptSig'])
59
+ script.chunks[0].bitcoin_pushdata = Bitcoin::Script::OP_PUSHDATA2
60
+ script.chunks[0].bitcoin_pushdata_length = script.chunks[0].bytesize
61
+ new_tx['in'][0]['scriptSig'] = script.to_string
62
+ new_tx = Bitcoin::P::Tx.from_hash(new_tx)
63
+
64
+ new_tx.hash.should != tx.hash
65
+ new_tx.normalized_hash.size.should == 64
66
+ new_tx.normalized_hash.should == "402e30100b6937cc13828ca096377c93afc0ff227ad2f249245e5b1db9123a39"
67
+ end
68
+
52
69
  it '#to_payload' do
53
70
  tx = Tx.new( @payload[0] )
54
71
  tx.to_payload.size.should == @payload[0].size
@@ -183,6 +200,53 @@ describe 'Tx' do
183
200
  outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-a6ce7081addade7676cd2af75c4129eba6bf5e179a19c40c7d4cf6a5fe595954.json'))
184
201
  outpoint_tx.hash.should == "a6ce7081addade7676cd2af75c4129eba6bf5e179a19c40c7d4cf6a5fe595954"
185
202
  tx.verify_input_signature(0, outpoint_tx).should == true
203
+
204
+ # drop OP_CODESEPARATOR in subscript for signature_hash_for_input
205
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json'))
206
+ tx.hash.should == "46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa"
207
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json'))
208
+ outpoint_tx.hash.should == "bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224"
209
+ tx.verify_input_signature(0, outpoint_tx).should == true
210
+
211
+ # drop OP_CODESEPARATOR in subscript for signature_hash_for_input
212
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json'))
213
+ tx.hash.should == "aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8"
214
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json'))
215
+ outpoint_tx.hash.should == "326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e"
216
+ tx.verify_input_signature(0, outpoint_tx).should == true
217
+
218
+ # drop multisig OP_CODESEPARATOR in subscript for signature_hash_for_input
219
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json'))
220
+ tx.hash.should == "6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190"
221
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json'))
222
+ outpoint_tx.hash.should == "a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944"
223
+ tx.verify_input_signature(0, outpoint_tx).should == true
224
+
225
+ # drop multisig OP_CODESEPARATOR in subscript for signature_hash_for_input when used in ScriptSig
226
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json'))
227
+ tx.hash.should == "eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb"
228
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json'))
229
+ outpoint_tx.hash.should == "b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d"
230
+ tx.verify_input_signature(1, outpoint_tx).should == true
231
+
232
+ # OP_DUP OP_HASH160
233
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json'))
234
+ tx.hash.should == "5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f"
235
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json'))
236
+ outpoint_tx.hash.should == "b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9"
237
+ tx.verify_input_signature(0, outpoint_tx).should == true
238
+ outpoint_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json'))
239
+ outpoint_tx.hash.should == "ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742"
240
+ tx.verify_input_signature(1, outpoint_tx).should == true
241
+
242
+ # testnet3 e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009
243
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json'))
244
+ tx.hash.should == "e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009"
245
+ prev_txs = {}
246
+ tx.in.map{|i| i.previous_output }.uniq.each{|i| prev_txs[i] = Bitcoin::P::Tx.from_json(fixtures_file("tx-#{i}.json")) }
247
+ tx.in.each.with_index{|i,idx|
248
+ tx.verify_input_signature(idx, prev_txs[i.previous_output]).should == true
249
+ }
186
250
  end
187
251
 
188
252
  it '#sign_input_signature' do
@@ -244,6 +308,95 @@ describe 'Tx' do
244
308
  prev_tx.hash.should == "52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2"
245
309
  #File.open("rawtx-#{prev_tx.hash}.json",'wb'){|f| f.print prev_tx.to_json }
246
310
  end
311
+
312
+ it "#legacy_sigops_count" do
313
+ Tx.new(@payload[0]).legacy_sigops_count.should == 2
314
+ Tx.new(@payload[1]).legacy_sigops_count.should == 2
315
+ Tx.new(@payload[2]).legacy_sigops_count.should == 2
316
+
317
+ # Test sig ops count in inputs too.
318
+ tx = Tx.new
319
+ txin = TxIn.new
320
+ txin.script_sig = Bitcoin::Script.from_string("10 OP_CHECKMULTISIGVERIFY OP_CHECKSIGVERIFY").to_binary
321
+ tx.add_in(txin)
322
+ txout = TxOut.new
323
+ txout.pk_script = Bitcoin::Script.from_string("5 OP_CHECKMULTISIG OP_CHECKSIG").to_binary
324
+ tx.add_out(txout)
325
+ tx.legacy_sigops_count.should == (20 + 1 + 20 + 1)
326
+
327
+ end
328
+
329
+ describe "Tx - is_final?" do
330
+ it "should be final if lock_time == 0" do
331
+ tx = Tx.new
332
+ tx.lock_time = 0
333
+ tx.is_final?(0,0).should == true
334
+
335
+ # even if has non-final input:
336
+ txin = TxIn.new
337
+ txin.sequence = "\x01\x00\x00\x00"
338
+ tx.add_in(txin)
339
+ tx.is_final?(0,0).should == true
340
+ end
341
+
342
+ it "should be final if lock_time is below block_height" do
343
+ tx = Tx.new
344
+ txin = TxIn.new
345
+ txin.sequence = "\x01\x00\x00\x00"
346
+ tx.add_in(txin)
347
+ tx.lock_time = 6543
348
+ tx.is_final?(6000,0).should == false
349
+ tx.is_final?(6543,0).should == false # when equal to block height, still not final
350
+ tx.is_final?(6544,0).should == true
351
+ tx.is_final?(9999,0).should == true
352
+ end
353
+
354
+ it "should be final if lock_time is below timestamp" do
355
+ tx = Tx.new
356
+ txin = TxIn.new
357
+ txin.sequence = "\xff\xff\xff\xff"
358
+ tx.add_in(txin)
359
+ txin = TxIn.new
360
+ txin.sequence = "\x01\x00\x00\x00"
361
+ tx.add_in(txin)
362
+ tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD # when equal, interpreted as threshold
363
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD - 1).should == false
364
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD).should == false # when equal to timestamp, still not final
365
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true
366
+
367
+ tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD + 666
368
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == false
369
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 666).should == false # when equal to timestamp, still not final
370
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 667).should == true
371
+ end
372
+
373
+ it "should be final if all inputs are finalized regardless of lock_time" do
374
+ tx = Tx.new
375
+ txin = TxIn.new
376
+ txin.sequence = "\xff\xff\xff\xff"
377
+ tx.add_in(txin)
378
+ txin = TxIn.new
379
+ txin.sequence = "\xff\xff\xff\xff"
380
+ tx.add_in(txin)
381
+
382
+ tx.lock_time = 6543
383
+ tx.is_final?(6000,0).should == true
384
+ tx.is_final?(6543,0).should == true
385
+ tx.is_final?(6544,0).should == true
386
+ tx.is_final?(9999,0).should == true
387
+
388
+ tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD
389
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD - 1).should == true
390
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD).should == true
391
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true
392
+
393
+ tx.lock_time = Bitcoin::LOCKTIME_THRESHOLD + 666
394
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 1).should == true
395
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 666).should == true
396
+ tx.is_final?(0,Bitcoin::LOCKTIME_THRESHOLD + 667).should == true
397
+ end
398
+
399
+ end
247
400
 
248
401
  it '#calculate_minimum_fee' do
249
402
  tx = Tx.new( fixtures_file('rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin') )
@@ -254,6 +407,23 @@ describe 'Tx' do
254
407
  tx.minimum_block_fee.should == 10_000
255
408
  end
256
409
 
410
+ it '#calculate_minimum_fee for litecoin' do
411
+ tx = Tx.from_json(fixtures_file('litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json'))
412
+ tx.minimum_relay_fee.should == 0
413
+ tx.minimum_block_fee.should == 30_000
414
+ Bitcoin.network = :litecoin # change to litecoin
415
+ tx.minimum_relay_fee.should == 0
416
+ tx.minimum_block_fee.should == 5_900_000
417
+ end
418
+
419
+ it "should compare transactions" do
420
+ tx1 = Tx.new( @payload[0] )
421
+ tx2 = Tx.new( @payload[1] )
422
+ (tx1 == Bitcoin::P::Tx.from_json(tx1.to_json)).should == true
423
+ (tx1 == tx2).should == false
424
+ (tx1 == nil).should == false
425
+ end
426
+
257
427
  describe "Tx - BIP Scripts" do
258
428
 
259
429
  it "should do OP_CHECKMULTISIG" do
@@ -266,6 +436,11 @@ describe 'Tx' do
266
436
  tx = Tx.from_json( fixtures_file('rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json') )
267
437
  prev_tx = Tx.from_json( fixtures_file("rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json") )
268
438
  tx.verify_input_signature(0, prev_tx).should == true
439
+
440
+ # checkmultisig for testnet3 tx: 2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1 input index 1
441
+ tx = Tx.from_json( fixtures_file('tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json') )
442
+ prev_tx = Tx.from_json( fixtures_file("tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json") )
443
+ tx.verify_input_signature(1, prev_tx).should == true
269
444
  end
270
445
 
271
446
  it "should do P2SH with inner OP_CHECKMULTISIG (BIP 0016)" do
@@ -294,6 +469,14 @@ describe 'Tx' do
294
469
  tx.verify_input_signature(0, prev_tx).should == true
295
470
  end
296
471
 
297
- end
472
+ it "should do OP_CHECKMULTISIG with OP_0 used as a pubkey" do
473
+ tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json'))
474
+ tx.hash.should == "6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba"
475
+ prev_tx = Bitcoin::P::Tx.from_json(fixtures_file('tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json'))
476
+ prev_tx.hash.should == "4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9"
477
+ tx.verify_input_signature(0, prev_tx).should == true
478
+ end
298
479
 
480
+ end
481
+
299
482
  end
@@ -14,5 +14,32 @@ describe 'TxIn' do
14
14
  end
15
15
  end
16
16
 
17
+ it "should compare txins" do
18
+ i1 = Tx.new(fixtures_file('rawtx-01.bin')).in[0]
19
+ i1_1 = TxIn.new(i1.prev_out, i1.prev_out_index, i1.script_sig_length, i1.script_sig)
20
+ i2 = Tx.new(fixtures_file('rawtx-02.bin')).in[0]
21
+
22
+ (i1 == i1).should == true
23
+ (i1 == i1_1).should == true
24
+ (i1 == i2).should == false
25
+ (i1 == nil).should == false
26
+ end
27
+
28
+ it "should be final only when sequence == 0xffffffff" do
29
+ txin = TxIn.new
30
+ txin.is_final?.should == true
31
+ txin.sequence.should == TxIn::DEFAULT_SEQUENCE
32
+
33
+ txin.sequence = "\x01\x00\x00\x00"
34
+ txin.is_final?.should == false
35
+
36
+ txin.sequence = "\x00\x00\x00\x00"
37
+ txin.is_final?.should == false
38
+
39
+ txin.sequence = "\xff\xff\xff\xff"
40
+ txin.is_final?.should == true
41
+ end
42
+
43
+
17
44
  end
18
45
 
@@ -0,0 +1,27 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require_relative '../spec_helper.rb'
4
+
5
+ include Bitcoin::Protocol
6
+
7
+ describe 'TxOut' do
8
+
9
+ it '#initialize without specifying script_sig_length' do
10
+ key = Bitcoin::Key.generate
11
+ tx_out = TxOut.new(12345, Bitcoin::Script.from_string("OP_DUP OP_HASH160 #{key.hash160} OP_EQUALVERIFY OP_CHECKSIG").to_payload)
12
+ lambda { tx_out.to_payload }.should.not.raise
13
+ end
14
+
15
+ it "should compare txouts" do
16
+ o1 = Tx.new(fixtures_file('rawtx-01.bin')).out[0]
17
+ o1_1 = TxOut.new(o1.value, o1.pk_script)
18
+ o2 = Tx.new(fixtures_file('rawtx-02.bin')).out[0]
19
+
20
+ (o1 == o1).should == true
21
+ (o1 == o1_1).should == true
22
+ (o1 == o2).should == false
23
+ (o1 == nil).should == false
24
+ end
25
+
26
+ end
27
+
@@ -358,6 +358,15 @@ describe "Bitcoin::Script OPCODES" do
358
358
  [[1, 0], [1,1]],
359
359
  ].each{|stack, expected|
360
360
  op(:pick, stack).should == expected
361
+ @script.invalid?.should == false
362
+ }
363
+
364
+ [
365
+ [[0], [0]],
366
+ [[-1], [-1]],
367
+ ].each{|stack, expected|
368
+ op(:pick, stack).should == expected
369
+ @script.invalid?.should == true
361
370
  }
362
371
  end
363
372
 
@@ -367,9 +376,26 @@ describe "Bitcoin::Script OPCODES" do
367
376
  [[1, 0], [1]],
368
377
  ].each{|stack, expected|
369
378
  op(:roll, stack).should == expected
379
+ @script.invalid?.should == false
380
+ }
381
+
382
+ [
383
+ [[0], [0]],
384
+ [[-1], [-1]],
385
+ ].each{|stack, expected|
386
+ op(:roll, stack).should == expected
387
+ @script.invalid?.should == true
370
388
  }
371
389
  end
372
390
 
391
+ it "should do op_2rot" do
392
+ op(:"2rot", [-1,0,1,2,3,4,5,6]).should == [-1, 0, 3, 4, 5, 6, 1, 2]
393
+ @script.invalid?.should == false
394
+
395
+ op(:"2rot", [2,3,4,5,6]).should == [2, 3, 4, 5, 6]
396
+ @script.invalid?.should == true
397
+ end
398
+
373
399
  it "should do op_rot" do
374
400
  op(:rot, [22, 21, 20]).should == [21, 20, 22]
375
401
  op(:rot, [21, 20]).should == [21, 20]
@@ -443,6 +469,9 @@ describe "Bitcoin::Script OPCODES" do
443
469
  "1 OP_DUP OP_IF OP_ELSE OP_ENDIF",
444
470
  "1 OP_IF 1 OP_ELSE OP_ENDIF",
445
471
  "0 OP_IF OP_ELSE 1 OP_ENDIF",
472
+ "beef OP_IF 1 OP_ELSE 0 OP_ENDIF",
473
+ "0 OP_NOTIF 1 OP_ELSE 0 OP_ENDIF",
474
+ "beef OP_NOTIF 0 OP_ELSE 1 OP_ENDIF",
446
475
  ].each{|script|
447
476
  Bitcoin::Script.from_string(script).run.should == true
448
477
  }
@@ -487,6 +516,11 @@ describe "Bitcoin::Script OPCODES" do
487
516
  }
488
517
  @script.op_checksig(verify_callback).should == [1]
489
518
 
519
+ @script.stack = [signature + hash_type, intger_pubkey=1]
520
+ verify_callback = proc{|pub,sig,hash_type|
521
+ pub.is_a?(String)
522
+ }
523
+ @script.op_checksig(verify_callback).should == [1]
490
524
 
491
525
  @script.stack = [signature + hash_type, pubkey]
492
526
  verify_callback = proc{|pub,sig,hash_type|
@@ -523,8 +557,8 @@ describe "Bitcoin::Script OPCODES" do
523
557
  def run_script(string, hash)
524
558
  script = Bitcoin::Script.from_string(string)
525
559
  script.run do |pk, sig, hash_type|
526
- k = Bitcoin::Key.new nil, pk.unpack("H*")[0]
527
- k.verify(hash, sig) rescue false
560
+ k = Bitcoin::Key.new(nil, pk.unpack("H*")[0]) rescue false
561
+ k && k.verify(hash, sig) rescue false
528
562
  end == true
529
563
  end
530
564
 
@@ -593,6 +627,12 @@ describe "Bitcoin::Script OPCODES" do
593
627
  script = "0 #{sig1} f0f0f0f0 #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG"
594
628
  run_script(script, "foobar").should == false
595
629
 
630
+ script = "0 #{sig1} f0f0f0f0 #{sig3} 3 #{k1.pub} #{k2.pub} #{k3.pub} 3 OP_CHECKMULTISIG OP_NOT"
631
+ run_script(script, "foobar").should == true
632
+
633
+ script = "1 1 1 1 1 OP_CHECKMULTISIG OP_NOT"
634
+ run_script(script, "foobar").should == true
635
+
596
636
  # mainnet tx output: 514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58
597
637
  script = "0 #{sig1} 1 0 #{k1.pub} OP_SWAP OP_1ADD OP_CHECKMULTISIG"
598
638
  run_script(script, "foobar").should == true
@@ -632,6 +672,12 @@ describe "Bitcoin::Script OPCODES" do
632
672
  address = "3CkxTG25waxsmd13FFgRChPuGYba3ar36B"
633
673
  script = Bitcoin::Script.new(Bitcoin::Script.to_address_script(address))
634
674
  script.type.should == :p2sh
675
+
676
+ inner_script = Bitcoin::Script.from_string("0 OP_NOT").raw.unpack("H*")[0]
677
+ script_hash = Bitcoin.hash160(inner_script)
678
+ script = Bitcoin::Script.from_string("#{inner_script} OP_HASH160 #{script_hash} OP_EQUAL")
679
+ script.is_p2sh?.should == true
680
+ run_script(script.to_string, "foobar").should == true
635
681
  end
636
682
 
637
683
  it "should skip OP_EVAL" do
@@ -681,7 +727,19 @@ describe "Bitcoin::Script OPCODES" do
681
727
  "0 OP_0NOTEQUAL 0 OP_EQUAL",
682
728
  "2 82 OP_ADD 0 OP_EQUAL",
683
729
  ].each{|script|
684
- Bitcoin::Script.from_string(script).run.should == true
730
+ parsed_script = Bitcoin::Script.from_string(script)
731
+ result = parsed_script.run
732
+ #p [script, parsed_script, parsed_script.debug result]
733
+ result.should == true
734
+ }
735
+
736
+ [
737
+ "ffffff7f ffffff7f OP_ADD ffffff7f OP_ADD OP_TRUE"
738
+ ].each{|script|
739
+ parsed_script = Bitcoin::Script.from_string(script)
740
+ result = parsed_script.run
741
+ #p [script, parsed_script, parsed_script.debug result]
742
+ result.should == false
685
743
  }
686
744
  end
687
745
 
@@ -699,4 +757,17 @@ describe "Bitcoin::Script OPCODES" do
699
757
  }
700
758
  end
701
759
 
760
+ it "check before casting and mark bad cases invalid" do
761
+ s = Bitcoin::Script.from_string("OP_NOT") # tries to pop off an element from the empty stack here.
762
+ s.run.should == false
763
+ s.invalid?.should == true
764
+ end
765
+
766
+ it "should do OP_CHECKSIGVERIFY and OP_CHECKMULTISIGVERIFY" do
767
+ tx1 = Bitcoin::P::Tx.new("0100000001a3fe4396b575690095bfc088d864aa971c99f65e2d893b48e0b26b1b60a28754000000006a47304402201ddfc8e3f825add9f42c0ce76dc5709cf76871e7ee6c97aae11d7db7f829b3f202201c3043515bfcf3d77845c8740ce4ccb4bda3f431da64f2596ee0ea2dfb727a5c01210328a5915165382c9b119d10d313c5781d98a7de79225f3c58e7fa115660ba90e0ffffffff0270f305000000000017a914ca164de1946bf0146ed1f32413df0efb0e1c730f87005d8806000000001976a91437c1d63690e00845663f3de661fef981c08e8de588ac00000000".htb)
768
+ tx2 = Bitcoin::P::Tx.new("0100000001a1c5263304aa47f8e4e8a8dbca33e525667f7f0d84390c5a92d49eccbe5b970f00000000fde50152483045022100fbc7ccd87ad2384a4d8823d3cf36d839bb6acca3d80a9ed9c51c784b7bdf1e430220305fcb1660219fcc340935000aa92dd02684b763177b8a3c1be094c919af323701473044022008f66d2e31175cdefbd7461afb5f9946e5dcb8173d1a2d3ef837f1c810695d160220250354de77b4a919b87910aa203ecec54bd1006d2dad2fcac06a54f39a9d39a101514d4f0176519c6375522103b124c48bbff7ebe16e7bd2b2f2b561aa53791da678a73d2777cc1ca4619ab6f72103ad6bb76e00d124f07a22680e39debd4dc4bdb1aa4b893720dd05af3c50560fdd52af67529c63552103b124c48bbff7ebe16e7bd2b2f2b561aa53791da678a73d2777cc1ca4619ab6f721025098a1d5a338592bf1e015468ec5a8fafc1fc9217feb5cb33597f3613a2165e9210360cfabc01d52eaaeb3976a5de05ff0cfa76d0af42d3d7e1b4c233ee8a00655ed2103f571540c81fd9dbf9622ca00cfe95762143f2eab6b65150365bb34ac533160432102bc2b4be1bca32b9d97e2d6fb255504f4bc96e01aaca6e29bfa3f8bea65d8865855af672103ad6bb76e00d124f07a22680e39debd4dc4bdb1aa4b893720dd05af3c50560fddada820a4d933888318a23c28fb5fc67aca8530524e2074b1d185dbf5b4db4ddb0642848868685174519c6351670068000000000170f30500000000001976a914bce2fe0e49630a996cb9fe611e6b9b7d1e4dc21188acb4ff6153".htb)
769
+ tx2.verify_input_signature(0, tx1).should == true
770
+ end
771
+
772
+
702
773
  end