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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/COPYING +1 -1
- data/Gemfile +5 -11
- data/README.rdoc +11 -5
- data/Rakefile +5 -0
- data/bin/bitcoin_node +11 -29
- data/bin/bitcoin_node_cli +81 -0
- data/bin/bitcoin_wallet +9 -6
- data/doc/NODE.rdoc +79 -26
- data/examples/bbe_verify_tx.rb +1 -1
- data/examples/index_nhash.rb +24 -0
- data/examples/reindex_p2sh_addrs.rb +44 -0
- data/lib/bitcoin.rb +135 -20
- data/lib/bitcoin/builder.rb +233 -63
- data/lib/bitcoin/key.rb +89 -16
- data/lib/bitcoin/litecoin.rb +13 -11
- data/lib/bitcoin/namecoin.rb +5 -4
- data/lib/bitcoin/network/command_client.rb +23 -13
- data/lib/bitcoin/network/command_handler.rb +336 -131
- data/lib/bitcoin/network/connection_handler.rb +14 -13
- data/lib/bitcoin/network/node.rb +61 -20
- data/lib/bitcoin/protocol.rb +5 -1
- data/lib/bitcoin/protocol/block.rb +15 -3
- data/lib/bitcoin/protocol/parser.rb +3 -3
- data/lib/bitcoin/protocol/tx.rb +82 -20
- data/lib/bitcoin/protocol/txin.rb +7 -0
- data/lib/bitcoin/protocol/txout.rb +12 -9
- data/lib/bitcoin/script.rb +329 -75
- data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
- data/lib/bitcoin/storage/models.rb +6 -11
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
- data/lib/bitcoin/storage/storage.rb +161 -97
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
- data/lib/bitcoin/validation.rb +87 -56
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +38 -0
- data/spec/bitcoin/builder_spec.rb +177 -0
- data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
- data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
- data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
- data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
- data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
- data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
- data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
- data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
- data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
- data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
- data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
- data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
- data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
- data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
- data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
- data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
- data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
- data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
- data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
- data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
- data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
- data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
- data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
- data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
- data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
- data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
- data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
- data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
- data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
- data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
- data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
- data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
- data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
- data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
- data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
- data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
- data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
- data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
- data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
- data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
- data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
- data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
- data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
- data/spec/bitcoin/key_spec.rb +79 -8
- data/spec/bitcoin/namecoin_spec.rb +1 -1
- data/spec/bitcoin/node/command_api_spec.rb +373 -86
- data/spec/bitcoin/performance/storage_spec.rb +41 -0
- data/spec/bitcoin/protocol/addr_spec.rb +7 -5
- data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
- data/spec/bitcoin/protocol/block_spec.rb +6 -0
- data/spec/bitcoin/protocol/tx_spec.rb +184 -1
- data/spec/bitcoin/protocol/txin_spec.rb +27 -0
- data/spec/bitcoin/protocol/txout_spec.rb +27 -0
- data/spec/bitcoin/script/opcodes_spec.rb +74 -3
- data/spec/bitcoin/script/script_spec.rb +271 -0
- data/spec/bitcoin/spec_helper.rb +34 -6
- data/spec/bitcoin/storage/models_spec.rb +104 -0
- data/spec/bitcoin/storage/reorg_spec.rb +42 -11
- data/spec/bitcoin/storage/storage_spec.rb +58 -15
- data/spec/bitcoin/storage/validation_spec.rb +44 -14
- data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
- data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
- data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
- 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
|
-
|
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
|
-
|
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
|
-
|
158
|
-
|
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
|
-
|
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
|
-
@
|
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(@
|
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
|
-
|
76
|
-
|
77
|
-
|
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 =
|
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 =
|
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::
|
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::
|
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::
|
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::
|
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 <=
|
229
|
-
check_tx(@tx, [:lock_time, [
|
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
|
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, [:
|
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)
|
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 == "
|
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 == "
|
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('
|
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('
|
187
|
-
'
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
104
|
-
|
101
|
+
it "should create new addr" do
|
102
|
+
@wallet.addrs.size.should == 1
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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
|
-
|
186
|
-
|
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
|
-
|
183
|
+
# @keystore = SimpleKeyStore.new(file: StringIO.new("[]"))
|
184
|
+
# @wallet = Wallet.new(@store, @keystore, SimpleCoinSelector)
|
189
185
|
|
190
|
-
|
186
|
+
# @wallet.keystore.add_key(addr: @key.addr)
|
191
187
|
|
192
|
-
|
193
|
-
create_block(@genesis.hash, true, [], @key, 50e8)
|
188
|
+
# @genesis = Bitcoin::P::Block.new("0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000".htb)
|
194
189
|
|
195
|
-
|
196
|
-
|
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
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
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
|
|