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