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
@@ -5,9 +5,7 @@ require_relative '../spec_helper'
5
5
  include Bitcoin::Builder
6
6
 
7
7
  Bitcoin.network = :testnet
8
-
9
- Bitcoin::Validation::Block::RETARGET = 10
10
-
8
+
11
9
  [
12
10
  [:utxo, :sqlite, index_all_addrs: true],
13
11
  [:sequel, :sqlite], # [:sequel, :postgres],
@@ -28,10 +26,15 @@ Bitcoin::Validation::Block::RETARGET = 10
28
26
  @store = storage
29
27
  @store.reset
30
28
  def @store.in_sync?; true; end
29
+
30
+ Bitcoin.network = :testnet
31
+ Bitcoin.network[:retarget_interval] = 10
31
32
  Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
33
+
32
34
  @key = Bitcoin::Key.generate
33
35
  @block0 = create_block "00"*32, false, [], @key
34
36
  Bitcoin.network[:genesis_hash] = @block0.hash
37
+
35
38
  @store.store_block(@block0)
36
39
  @store.get_head.should == @block0
37
40
  end
@@ -99,6 +102,39 @@ Bitcoin::Validation::Block::RETARGET = 10
99
102
  @store.get_head.hash.should == block_b.hash
100
103
  end
101
104
 
105
+ it "should validate duplicate tx in a side chain" do
106
+ block1 = create_block @block0.hash, true, [], @key
107
+
108
+ block_2_0 = create_block block1.hash, false, [ ->(t) {
109
+ t.input {|i| i.prev_out block1.tx[0], 0; i.signature_key @key }
110
+ t.output {|o| o.value 50e8; o.script {|s| s.recipient @key.addr } }
111
+ }], @key
112
+
113
+ @store.store_block(block_2_0).should == [2, 0]
114
+
115
+ block_3_0 = create_block block_2_0.hash, false, [->(t) {
116
+ t.input {|i| i.prev_out block_2_0.tx[1], 0; i.signature_key @key }
117
+ t.output {|o| o.value 50e8; o.script {|s| s.recipient @key.addr } }
118
+ }], @key
119
+ @store.store_block(block_3_0).should == [3, 0]
120
+
121
+ block_2_1 = create_block block1.hash, false
122
+ block_2_1.tx << block_2_0.tx[1]
123
+ block_2_1.recalc_mrkl_root
124
+ block_2_1.recalc_block_hash
125
+ @store.store_block(block_2_1).should == [2, 1]
126
+
127
+ block_3_1 = create_block block_2_1.hash, false
128
+ block_3_1.tx << block_3_0.tx[1]
129
+ block_3_1.recalc_mrkl_root
130
+ block_3_1.recalc_block_hash
131
+
132
+ @store.store_block(block_3_1).should == [3, 1]
133
+
134
+ block_4 = create_block block_3_1.hash, false
135
+ @store.store_block(block_4).should == [4, 0]
136
+ end
137
+
102
138
  it "should reorg a single side block" do
103
139
  @store.get_head.should == @block0
104
140
 
@@ -163,7 +199,6 @@ Bitcoin::Validation::Block::RETARGET = 10
163
199
  end
164
200
 
165
201
  it "should handle existing blocks" do
166
- Bitcoin.network = :testnet
167
202
  blocks = [@block0]
168
203
  3.times { blocks << create_block(blocks.last.hash, false) }
169
204
  blocks[1..-1].each.with_index {|b, idx| @store.store_block(b).should == [idx+1, 0] }
@@ -173,8 +208,9 @@ Bitcoin::Validation::Block::RETARGET = 10
173
208
 
174
209
  # see https://bitcointalk.org/index.php?topic=46370.0
175
210
  it "should pass reorg unit tests" do
176
- class Bitcoin::Validation::Block; def difficulty; true; end; end
177
211
  Bitcoin.network = :bitcoin
212
+ # Disable difficulty check
213
+ Bitcoin.network[:no_difficulty] = true
178
214
  @store.import "./spec/bitcoin/fixtures/reorg/blk_0_to_4.dat"
179
215
  @store.get_depth.should == 4
180
216
  @store.get_head.hash.should =~ /000000002f264d65040/
@@ -192,13 +228,8 @@ Bitcoin::Validation::Block::RETARGET = 10
192
228
  balance("1NiEGXeURREqqMjCvjCeZn6SwEBZ9AdVet").should == 1000000000
193
229
  balance("1KXFNhNtrRMfgbdiQeuJqnfD7dR4PhniyJ").should == 0
194
230
  balance("1JyMKvPHkrCQd8jQrqTR1rBsAd1VpRhTiE").should == 14000000000
231
+ Bitcoin.network.delete :no_difficulty
195
232
  Bitcoin.network = :testnet
196
- class Bitcoin::Validation::Block
197
- def difficulty
198
- return true if Bitcoin.network_name == :testnet3
199
- block.bits == next_bits_required || [block.bits, next_bits_required]
200
- end
201
- end
202
233
  end
203
234
 
204
235
  end
@@ -24,7 +24,7 @@ Bitcoin::network = :testnet
24
24
  describe "Storage::Backends::#{options[0].to_s.capitalize}Store (#{options[1]})" do
25
25
 
26
26
  before do
27
- class Bitcoin::Validation::Block; def difficulty; true; end; end
27
+ Bitcoin.network[:no_difficulty] = true
28
28
  Bitcoin.network[:proof_of_work_limit] = Bitcoin.encode_compact_bits("ff"*32)
29
29
 
30
30
  @store = storage
@@ -46,12 +46,7 @@ Bitcoin::network = :testnet
46
46
  end
47
47
 
48
48
  after do
49
- class Bitcoin::Validation::Block
50
- def difficulty
51
- return true if Bitcoin.network_name == :testnet3
52
- block.bits == next_bits_required || [block.bits, next_bits_required]
53
- end
54
- end
49
+ Bitcoin.network.delete :no_difficulty
55
50
  end
56
51
 
57
52
  it "should get backend name" do
@@ -154,14 +149,19 @@ Bitcoin::network = :testnet
154
149
  @store.get_block_by_tx(@blk.tx[0].hash).should == @blk
155
150
  end
156
151
 
157
- # TODO calling .is_a? on @store breaks it (?!?)
158
- if @store.class.name =~ /SequelStore/
152
+ it "should get block id for tx id" do
153
+ @store.store_block(@blk)
154
+ tx = @store.get_tx(@blk.tx[0].hash)
155
+ @store.get_block_id_for_tx_id(tx.id).should == @store.get_block(@blk.hash).id
156
+ end
157
+
158
+ unless @store.backend_name == 'utxo'
159
159
  describe :transactions do
160
160
 
161
161
  it "should store tx" do
162
162
  @store.store_tx(@tx, false).should != false
163
163
  end
164
-
164
+
165
165
  it "should not store tx if already stored and return existing id" do
166
166
  id = @store.store_tx(@tx, false)
167
167
  @store.store_tx(@tx, false).should == id
@@ -221,13 +221,14 @@ Bitcoin::network = :testnet
221
221
  end
222
222
 
223
223
  it "should store multisig tx and index hash160's" do
224
- *keys = Bitcoin::Key.generate, Bitcoin::Key.generate
224
+ keys = Array.new(2) { Bitcoin::Key.generate }
225
225
  pk_script = Bitcoin::Script.to_multisig_script(1, keys[0].pub, keys[1].pub)
226
226
  txout = P::TxOut.new(1000, pk_script)
227
- @store.store_txout(0, txout, 0)
227
+ @tx.out[0] = txout
228
+ @store.store_tx(@tx, false)
228
229
  keys.each do |key|
229
230
  hash160 = Bitcoin.hash160(key.pub)
230
- txouts = @store.get_txouts_for_hash160(hash160, true)
231
+ txouts = @store.get_txouts_for_hash160(hash160, :hash160, true)
231
232
  txouts.size.should == 1
232
233
  txouts[0].pk_script.should == txout.pk_script
233
234
  end
@@ -264,7 +265,7 @@ Bitcoin::network = :testnet
264
265
  end
265
266
 
266
267
  it "should get txouts for hash160" do
267
- @store.get_txouts_for_hash160(@key2.hash160, true)
268
+ @store.get_txouts_for_hash160(@key2.hash160, :hash160, true)
268
269
  .should == [@block.tx[1].out[0]]
269
270
  end
270
271
 
@@ -273,11 +274,53 @@ Bitcoin::network = :testnet
273
274
  .should == [@block.tx[1].out[0]]
274
275
  end
275
276
 
277
+ it "should get txouts for txin" do
278
+ prev_tx = @block.tx[0]
279
+ tx = build_tx { |t| create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, Bitcoin::Key.generate]], @key) }
280
+ @store.get_txout_for_txin(tx.in[0]).should == prev_tx.out[0]
281
+ end
282
+
283
+ it "should get unspent txouts for address" do
284
+ @store.get_unspent_txouts_for_address(@key2.addr, true)
285
+ .should == [@block.tx[1].out[0]]
286
+ @block2 = create_block @block.hash, true, [->(t) {
287
+ create_tx(t, @block.tx[1], 0, [[20, @key2]], @key2) }], @key
288
+ @store.get_unspent_txouts_for_address(@key2.addr, true)
289
+ .should == [@block2.tx[1].out[0]]
290
+ end
291
+
276
292
  it "should get balance for address" do
277
- @store.get_balance(@key.addr).should == 0
293
+ @store.get_balance(@key2.addr).should == 50
278
294
  @store.get_balance(@key2.hash160).should == 50
279
295
  end
280
296
 
297
+ it "should get txouts for p2sh address (and not confuse regular and p2sh-type hash160" do
298
+ block = create_block(@block.hash, false, [], @key)
299
+
300
+ tx = build_tx do |t|
301
+ t.input {|i| i.prev_out(@block.tx[1], 0); i.signature_key(@key2) }
302
+ t.output do |o|
303
+ o.value 10
304
+ o.to @key2.hash160, :p2sh
305
+ end
306
+ t.output do |o|
307
+ o.value 10
308
+ o.to @key2.addr
309
+ end
310
+ end
311
+ block.tx << tx; block.recalc_mrkl_root; block.recalc_block_hash
312
+
313
+ @store.store_block(block)
314
+
315
+ p2sh_address = Bitcoin.hash160_to_p2sh_address(@key2.hash160)
316
+ o1 = @store.get_unspent_txouts_for_address(@key2.addr)
317
+ o2 = @store.get_unspent_txouts_for_address(p2sh_address)
318
+
319
+ o1.size.should == 1
320
+ o2.size.should == 1
321
+ o1.should != o2
322
+ end
323
+
281
324
  end
282
325
 
283
326
  describe "validation" do
@@ -67,14 +67,16 @@ Bitcoin.network = :spec
67
67
  it "4. Block hash must satisfy claimed nBits proof of work" do
68
68
  @block.bits = Bitcoin.encode_compact_bits("0000#{"ff" * 30}")
69
69
  @block.recalc_block_hash
70
- target = Bitcoin.decode_compact_bits(block.bits).to_i(16)
70
+ target = Bitcoin.decode_compact_bits(@block.bits).to_i(16)
71
71
  check_block(@block, [:bits, [@block.hash.to_i(16), target]])
72
72
  end
73
73
 
74
74
  it "5. Block timestamp must not be more than two hours in the future" do
75
- fake_time = (Time.now + 3 * 60 * 60).to_i
76
- check_block(@block, [:max_timestamp, [fake_time, Time.now.to_i + 7200]]) {|b|
77
- b.time = fake_time }
75
+ Time.freeze do
76
+ fake_time = (Time.now + 3 * 60 * 60).to_i
77
+ check_block(@block, [:max_timestamp, [fake_time, Time.now.to_i + 7200]]) {|b|
78
+ b.time = fake_time }
79
+ end
78
80
  end
79
81
 
80
82
  it "6. First transaction must be coinbase (i.e. only 1 input, with hash=0, n=-1), the rest must not be" do
@@ -108,20 +110,27 @@ Bitcoin.network = :spec
108
110
  prev_block = @block1
109
111
  12.times do |i|
110
112
  prev_block = create_block(prev_block.hash, false, [])
111
- prev_block.time = Time.now.to_i - (12-i)
113
+ prev_block.time = i
112
114
  prev_block.recalc_block_hash
113
115
  @store.store_block(prev_block).should == [i+2, 0]
114
116
  end
117
+
115
118
  block = create_block(prev_block.hash, false, [], @key)
116
119
 
117
- fake_time = Time.now.to_i - 100
120
+ fake_time = @store.get_block_by_depth(8).time - 1
118
121
  times = @store.db[:blk].where("depth > 2").map{|b|b[:time]}.sort
119
122
  m, r = times.size.divmod(2)
120
123
  min_time = (r == 0 ? times[m-1, 2].inject(:+) / 2.0 : times[m])
124
+
125
+ # reject before median time
121
126
  check_block(block, [:min_timestamp, [fake_time, min_time]]) {|b| b.time = fake_time }
122
127
 
128
+ # reject at exactly median time
123
129
  fake_time = @store.get_block_by_depth(8).time
124
130
  check_block(block, [:min_timestamp, [fake_time, fake_time]]) {|b| b.time = fake_time }
131
+
132
+ # accept after median time
133
+ block.time = @store.get_block_by_depth(8).time + 1; block.recalc_block_hash
125
134
  @store.store_block(block).should == [14, 0]
126
135
  end
127
136
 
@@ -145,13 +154,13 @@ Bitcoin.network = :spec
145
154
  block3 = create_block(block2.hash, false, [], @key, 60e8)
146
155
  -> { @store.store_block(block3) }.should.raise(ValidationError)
147
156
 
148
- Bitcoin::Validation::REWARD_DROP = 2
157
+ Bitcoin::REWARD_DROP = 2
149
158
  block4 = create_block(block2.hash, false, [], @key, 50e8)
150
159
  -> { @store.store_block(block4) }.should.raise(ValidationError)
151
160
 
152
161
  block5 = create_block(block2.hash, false, [], @key, 25e8)
153
162
  @store.store_block(block5).should == [3, 0]
154
- Bitcoin::Validation::REWARD_DROP = 210_000
163
+ Bitcoin::REWARD_DROP = 210_000
155
164
  end
156
165
 
157
166
  end
@@ -207,10 +216,10 @@ describe "transaction rules (#{options[0]} - #{options[1]})" do
207
216
  end
208
217
 
209
218
  it "3. Size in bytes < MAX_BLOCK_SIZE" do
210
- max = Bitcoin::Validation::MAX_BLOCK_SIZE; Bitcoin::Validation::MAX_BLOCK_SIZE = 1000
219
+ max = Bitcoin::MAX_BLOCK_SIZE; Bitcoin::MAX_BLOCK_SIZE = 1000
211
220
  check_tx(@tx, [:max_size, [@tx.payload.bytesize+978, 1000]]) {|tx|
212
221
  tx.out[0].pk_script = "\x00" * 1001 }
213
- Bitcoin::Validation::MAX_BLOCK_SIZE = max
222
+ Bitcoin::MAX_BLOCK_SIZE = max
214
223
  end
215
224
 
216
225
  it "4. Each output value, as well as the total, must be in legal money range" do
@@ -225,8 +234,8 @@ describe "transaction rules (#{options[0]} - #{options[1]})" do
225
234
  end
226
235
  end
227
236
 
228
- it "6. Check that nLockTime <= INT_MAX, size in bytes >= 100, and sig opcount <= 2" do
229
- check_tx(@tx, [:lock_time, [INT_MAX + 1, INT_MAX]]) {|tx| tx.lock_time = INT_MAX + 1 }
237
+ it "6. Check that nLockTime <= UINT32_MAX, size in bytes >= 100, and sig opcount <= 2" do
238
+ check_tx(@tx, [:lock_time, [Bitcoin::UINT32_MAX + 1, Bitcoin::UINT32_MAX]]) {|tx| tx.lock_time = Bitcoin::UINT32_MAX + 1 }
230
239
  # TODO: validate sig opcount
231
240
  end
232
241
 
@@ -242,7 +251,7 @@ describe "transaction rules (#{options[0]} - #{options[1]})" do
242
251
  end
243
252
 
244
253
  it "13. Verify crypto signatures for each input; reject if any are bad" do
245
- check_tx(@tx, [:signatures, [0]]) {|tx| @tx.in[0].script_sig[-1] = "\x00" }
254
+ check_tx(@tx, [:signatures, [0]]) {|tx| @tx.in[0].script_sig = "bad sig" }
246
255
  end
247
256
 
248
257
  it "14. For each input, if the referenced output has already been spent by a transaction in the main branch, reject this transaction" do
@@ -251,7 +260,7 @@ describe "transaction rules (#{options[0]} - #{options[1]})" do
251
260
  if @store.class.name =~ /Utxo/
252
261
  check_tx(@tx, [:prev_out, [[@tx.in[0].prev_out.reverse_hth, 0]]])
253
262
  else
254
- check_tx(@tx, [:spent, [0]])
263
+ check_tx(@tx, [:not_spent, [0]])
255
264
  end
256
265
  end
257
266
 
@@ -265,6 +274,27 @@ describe "transaction rules (#{options[0]} - #{options[1]})" do
265
274
  check_tx(tx, [:output_sum, [100e8, 50e8]])
266
275
  end
267
276
 
277
+
278
+ it "should not allow double spend within the same block" do
279
+ # double-spend output from previous block
280
+ prev_tx = @block1.tx[0]
281
+ block = create_block @block1.hash, false, [
282
+ ->(t) { create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, @key]]) },
283
+ ->(t) { create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, @key]]) }
284
+ ]
285
+ -> { @store.store_block(block) }.should.raise(Bitcoin::Validation::ValidationError)
286
+
287
+ # double-spend output from current block
288
+ block = create_block @block1.hash, false, [
289
+ ->(t) { create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, @key]]) }
290
+ ]
291
+ prev_tx = block.tx[1]
292
+ block.tx << build_tx {|t| create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, @key]]) }
293
+ block.tx << build_tx {|t| create_tx(t, prev_tx, 0, [[prev_tx.out[0].value, @key]]) }
294
+ block.recalc_mrkl_root; block.recalc_block_hash
295
+ -> { @store.store_block(block) }.should.raise(Bitcoin::Validation::ValidationError)
296
+ end
297
+
268
298
  end
269
299
 
270
300
  end
@@ -28,7 +28,10 @@ describe "Bitcoin::KeyGenerator" do
28
28
  it "should use given nonce" do
29
29
  g = KeyGenerator.new("foo", 2116)
30
30
  g.nonce.should == 2116
31
- g.get_key(0).addr.should == '1GjyUrY3XcR4BvfgL8HqoAJbNDEgxSJdm1'
31
+ key = g.get_key(0)
32
+ key.addr.should == '1JvRdnShvscPtoP44VxPk5VaFBAo7ozRPb'
33
+ key.instance_eval { @pubkey_compressed = false }
34
+ key.addr.should == '1GjyUrY3XcR4BvfgL8HqoAJbNDEgxSJdm1'
32
35
  end
33
36
 
34
37
  it "should check nonce if given" do
@@ -38,10 +41,10 @@ describe "Bitcoin::KeyGenerator" do
38
41
  it "should use different target if given" do
39
42
  g = KeyGenerator.new("foo", nil, @target)
40
43
  g.nonce.should == 127
41
- g.get_key(0).addr.should == "13E68pPJyGycgQ4ZmV45xV9r9XEeyWqZdp"
44
+ g.get_key(0).addr.should == "1KLBACvBnz9BTdBnuJmNuQpKQrsi55sstj"
42
45
  g = KeyGenerator.new("bar", nil, @target)
43
46
  g.nonce.should == 40
44
- g.get_key(0).addr.should == "12iQpWRRQBmWcHYkTZkpDFrykzc9xn5kAU"
47
+ g.get_key(0).addr.should == "14T4deW5BGVA7wXpR3eoU9U8xprUJepxcy"
45
48
  end
46
49
 
47
50
  it "should find keys" do
@@ -173,7 +173,7 @@ describe "Bitcoin::Wallet::DeterministicKeyStore" do
173
173
  end
174
174
 
175
175
  it "should get key" do
176
- @ks.key('1KDUUSjPJkKwVEJsfpxEzBAf7iEbmqUwUu').priv.should ==
176
+ @ks.key('1GKjKQemNRhxL1ChTRFJNLZCXeCDxut2d7').priv.should ==
177
177
  '7f27bb0ca02e558c4b4b4e267417437adac01403e0d0bb9b07797d1dbb1adfd1'
178
178
  end
179
179
 
@@ -183,8 +183,8 @@ describe "Bitcoin::Wallet::DeterministicKeyStore" do
183
183
  end
184
184
 
185
185
  it "should export key" do
186
- @ks.export('1KDUUSjPJkKwVEJsfpxEzBAf7iEbmqUwUu').should ==
187
- '5JnHbCHicVj2Wgd2KgNPU7dQ6te55GzHjc4PH9cQDFUjeepYSHX'
186
+ @ks.export('1GKjKQemNRhxL1ChTRFJNLZCXeCDxut2d7').should ==
187
+ 'L1UtDvpnffnVg1szqSmQAgFexzvcysZrs3jwLH1FT4uREpZqcXaR'
188
188
  end
189
189
 
190
190
  end
@@ -46,14 +46,12 @@ describe Bitcoin::Wallet::Wallet do
46
46
  @storage = Mock.new
47
47
  @key = Key.from_base58('5J2hn1E8KEXmQn5gqykzKotyCcHbKrVbe8fjegsfVXRdv6wwon8')
48
48
  @addr = '1M89ZeWtmZmATzE3b6PHTBi8c7tGsg5xpo'
49
- @key2 = Key.from_base58('5KK9Lw8gtNd4kcaXQJmkwcmNy8Y5rLGm49RqhcYAb7qRhWxaWMJ')
50
- @addr2 = '134A4Bi8jN5V2KjkwmXUHjokDqdyqZ778J'
51
- @key3 = Key.from_base58('5JFcJByQvwYnWjQ2RHTTu6LLGiBj9oPQYsHqKWuKLDVAvv4cQ7E')
52
- @addr3 = '1EnrPVaRiRgrs1D7pujYZNN1N6iD9unZV6'
49
+ #@key2 = Key.from_base58('5KK9Lw8gtNd4kcaXQJmkwcmNy8Y5rLGm49RqhcYAb7qRhWxaWMJ')
50
+ #@addr2 = '134A4Bi8jN5V2KjkwmXUHjokDqdyqZ778J'
51
+ #@key3 = Key.from_base58('5JFcJByQvwYnWjQ2RHTTu6LLGiBj9oPQYsHqKWuKLDVAvv4cQ7E')
52
+ #@addr3 = '1EnrPVaRiRgrs1D7pujYZNN1N6iD9unZV6'
53
53
 
54
54
  @storage.expect(:add_watched_address, [], [@addr])
55
- @storage.expect(:add_watched_address, [], [@addr2])
56
- @storage.expect(:add_watched_address, [], [@addr3])
57
55
 
58
56
  keystore_data = [{:addr => @key.addr, :priv => @key.priv, :pub => @key.pub}]
59
57
  file_stub = StringIO.new
@@ -100,110 +98,110 @@ describe Bitcoin::Wallet::Wallet do
100
98
  list[1].should == 5000
101
99
  end
102
100
 
103
- # it "should create new addr" do
104
- # @wallet.addrs.size.should == 1
101
+ it "should create new addr" do
102
+ @wallet.addrs.size.should == 1
105
103
 
106
- # key = Key.generate
107
- # a = @wallet.get_new_addr
108
- # @wallet.addrs.size.should == 2
109
- # @wallet.addrs[1].should == a
110
- # end
104
+ @storage.expect(:add_watched_address, [], [String])
105
+ a = @wallet.get_new_addr
106
+ @wallet.addrs.size.should == 2
107
+ @wallet.addrs[1].should == a
108
+ end
111
109
 
112
- describe "Bitcoin::Wallet::Wallet#tx" do
113
-
114
- before do
115
- txout = txout_mock(5000, nil)
116
- tx = Mock.new
117
- 2.times { tx.expect(:binary_hash, "foo") }
118
- 8.times { tx.expect(:out, [txout]) }
119
- 3.times { tx.expect(:get_block, true) }
120
- 5.times { txout.expect(:get_tx, tx) }
121
- 6.times { txout.expect(:get_address, @addr) }
122
- 8.times { txout.expect(:pk_script, Script.to_address_script(@addr)) }
123
- 2.times { @storage.expect(:get_txouts_for_address, [txout], [@addr]) }
124
- 2.times { @storage.expect(:class, Bitcoin::Storage::Backends::SequelStore, []) }
125
- selector = Bitcoin::Wallet::SimpleCoinSelector.new([txout])
126
- 2.times { @selector.expect(:new, selector, [[txout]]) }
127
- @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]])
128
- end
110
+ # describe "Bitcoin::Wallet::Wallet#tx" do
129
111
 
112
+ # before do
113
+ # txout = txout_mock(5000, nil)
114
+ # tx = Mock.new
115
+ # 2.times { tx.expect(:binary_hash, "foo") }
116
+ # 8.times { tx.expect(:out, [txout]) }
117
+ # 3.times { tx.expect(:get_block, true) }
118
+ # 5.times { txout.expect(:get_tx, tx) }
119
+ # 6.times { txout.expect(:get_address, @addr) }
120
+ # 8.times { txout.expect(:pk_script, Script.to_address_script(@addr)) }
121
+ # 2.times { @storage.expect(:get_txouts_for_address, [txout], [@addr]) }
122
+ # 2.times { @storage.expect(:class, Bitcoin::Storage::Backends::SequelStore, []) }
123
+ # selector = Bitcoin::Wallet::SimpleCoinSelector.new([txout])
124
+ # 2.times { @selector.expect(:new, selector, [[txout]]) }
125
+ # @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]])
126
+ # end
130
127
 
131
- it "should have hash" do
132
- @tx.hash.size.should == 64
133
- end
134
128
 
135
- it "should have correct inputs" do
136
- @tx.in.size.should == 1
137
- @tx.in.first.prev_out.should == ("foo" + "\x00"*29)
138
- @tx.in.first.prev_out_index.should == 0
139
- end
129
+ # it "should have hash" do
130
+ # @tx.hash.size.should == 64
131
+ # end
140
132
 
141
- it "should have correct outputs" do
142
- @tx.out.size.should == 2
143
- @tx.out.first.value.should == 1000
144
- s = Script.new(@tx.out.first.pk_script)
145
- s.get_address.should == '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7'
146
- end
133
+ # it "should have correct inputs" do
134
+ # @tx.in.size.should == 1
135
+ # @tx.in.first.prev_out.should == ("foo" + "\x00"*29)
136
+ # @tx.in.first.prev_out_index.should == 0
137
+ # end
147
138
 
148
- it "should have change output" do
149
- @tx.out.last.value.should == 4000
150
- s = Script.new(@tx.out.last.pk_script)
151
- s.get_address.should == @addr
152
- end
139
+ # it "should have correct outputs" do
140
+ # @tx.out.size.should == 2
141
+ # @tx.out.first.value.should == 1000
142
+ # s = Script.new(@tx.out.first.pk_script)
143
+ # s.get_address.should == '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7'
144
+ # end
153
145
 
154
- it "should leave tx fee" do
155
- @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50)
156
- @tx.out.last.value.should == 3950
157
- end
146
+ # it "should have change output" do
147
+ # @tx.out.last.value.should == 4000
148
+ # s = Script.new(@tx.out.last.pk_script)
149
+ # s.get_address.should == @addr
150
+ # end
158
151
 
159
- it "should send change to specified address" do
160
- @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50,
161
- '1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p')
162
- Script.new(@tx.out.last.pk_script).get_address.should ==
163
- '1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p'
164
- end
152
+ # it "should leave tx fee" do
153
+ # @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50)
154
+ # @tx.out.last.value.should == 3950
155
+ # end
165
156
 
166
- it "should send change to new address" do
167
- @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50, :new)
168
- @wallet.addrs.size.should == 2
169
- Script.new(@tx.out.last.pk_script).get_address.should == @wallet.addrs.last
170
- end
157
+ # it "should send change to specified address" do
158
+ # @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50,
159
+ # '1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p')
160
+ # Script.new(@tx.out.last.pk_script).get_address.should ==
161
+ # '1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p'
162
+ # end
171
163
 
172
- it "should raise exception if insufficient balance" do
173
- -> {@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 7000]])}
174
- .should.raise(RuntimeError).message.should == "Insufficient funds."
175
- end
164
+ # it "should send change to new address" do
165
+ # @tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50, :new)
166
+ # @wallet.addrs.size.should == 2
167
+ # Script.new(@tx.out.last.pk_script).get_address.should == @wallet.addrs.last
168
+ # end
176
169
 
170
+ # it "should raise exception if insufficient balance" do
171
+ # -> {@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 7000]])}
172
+ # .should.raise(RuntimeError).message.should == "Insufficient funds."
173
+ # end
177
174
 
178
- it "should create unsigned tx" do
179
- Bitcoin.network = :spec
180
- @key = Bitcoin::Key.generate
181
- @key2 = Bitcoin::Key.generate
182
- @store = Storage.sequel(db: "sqlite:/")
183
- @store.log.level = :debug
184
175
 
185
- @keystore = SimpleKeyStore.new(file: StringIO.new("[]"))
186
- @wallet = Wallet.new(@store, @keystore, SimpleCoinSelector)
176
+ # it "should create unsigned tx" do
177
+ # Bitcoin.network = :spec
178
+ # @key = Bitcoin::Key.generate
179
+ # @key2 = Bitcoin::Key.generate
180
+ # @store = Storage.sequel(db: "sqlite:/")
181
+ # @store.log.level = :debug
187
182
 
188
- @wallet.keystore.add_key(addr: @key.addr)
183
+ # @keystore = SimpleKeyStore.new(file: StringIO.new("[]"))
184
+ # @wallet = Wallet.new(@store, @keystore, SimpleCoinSelector)
189
185
 
190
- @genesis = Bitcoin::P::Block.new("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".htb)
186
+ # @wallet.keystore.add_key(addr: @key.addr)
191
187
 
192
- @store.new_block @genesis
193
- create_block(@genesis.hash, true, [], @key, 50e8)
188
+ # @genesis = Bitcoin::P::Block.new("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".htb)
194
189
 
195
- list = @wallet.list
196
- list.size.should == 1
197
- list[0][0].should == {addr: @key.addr}
198
- list[0][1].should == 50e8
190
+ # @store.new_block @genesis
191
+ # create_block(@genesis.hash, true, [], @key, 50e8)
199
192
 
200
- tx = @wallet.new_tx([[:address, @key2.addr, 10e8]])
201
- tx.in[0].sig_hash.should != nil
202
- end
193
+ # list = @wallet.list
194
+ # list.size.should == 1
195
+ # list[0][0].should == {addr: @key.addr}
196
+ # list[0][1].should == 50e8
203
197
 
204
- end
198
+ # tx = @wallet.new_tx([[:address, @key2.addr, 10e8]])
199
+ # tx.in[0].sig_hash.should != nil
200
+ # end
201
+
202
+ # end
205
203
 
206
- # TODO
204
+ # # TODO
207
205
  # describe "Bitcoin::Wallet::Wallet#tx (multisig)" do
208
206
 
209
207