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