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
@@ -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