bitcoin-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/COPYING +18 -0
- data/Gemfile +4 -0
- data/README.rdoc +189 -0
- data/Rakefile +104 -0
- data/bin/bitcoin_dns_seed +130 -0
- data/bin/bitcoin_gui +80 -0
- data/bin/bitcoin_node +174 -0
- data/bin/bitcoin_shell +12 -0
- data/bin/bitcoin_wallet +323 -0
- data/bitcoin-ruby.gemspec +27 -0
- data/concept-examples/blockchain-pow.rb +151 -0
- data/doc/CONFIG.rdoc +66 -0
- data/doc/EXAMPLES.rdoc +9 -0
- data/doc/NODE.rdoc +35 -0
- data/doc/STORAGE.rdoc +21 -0
- data/doc/WALLET.rdoc +102 -0
- data/examples/balance.rb +60 -0
- data/examples/bbe_verify_tx.rb +55 -0
- data/examples/connect.rb +36 -0
- data/examples/relay_tx.rb +22 -0
- data/examples/verify_tx.rb +57 -0
- data/lib/bitcoin.rb +370 -0
- data/lib/bitcoin/builder.rb +266 -0
- data/lib/bitcoin/config.rb +56 -0
- data/lib/bitcoin/connection.rb +126 -0
- data/lib/bitcoin/ffi/openssl.rb +121 -0
- data/lib/bitcoin/gui/addr_view.rb +42 -0
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
- data/lib/bitcoin/gui/conn_view.rb +36 -0
- data/lib/bitcoin/gui/connection.rb +68 -0
- data/lib/bitcoin/gui/em_gtk.rb +28 -0
- data/lib/bitcoin/gui/gui.builder +1643 -0
- data/lib/bitcoin/gui/gui.rb +290 -0
- data/lib/bitcoin/gui/helpers.rb +113 -0
- data/lib/bitcoin/gui/tree_view.rb +82 -0
- data/lib/bitcoin/gui/tx_view.rb +67 -0
- data/lib/bitcoin/key.rb +125 -0
- data/lib/bitcoin/logger.rb +65 -0
- data/lib/bitcoin/network/command_client.rb +93 -0
- data/lib/bitcoin/network/command_handler.rb +179 -0
- data/lib/bitcoin/network/connection_handler.rb +274 -0
- data/lib/bitcoin/network/node.rb +399 -0
- data/lib/bitcoin/protocol.rb +140 -0
- data/lib/bitcoin/protocol/address.rb +48 -0
- data/lib/bitcoin/protocol/alert.rb +47 -0
- data/lib/bitcoin/protocol/block.rb +154 -0
- data/lib/bitcoin/protocol/handler.rb +38 -0
- data/lib/bitcoin/protocol/parser.rb +148 -0
- data/lib/bitcoin/protocol/tx.rb +205 -0
- data/lib/bitcoin/protocol/txin.rb +97 -0
- data/lib/bitcoin/protocol/txout.rb +73 -0
- data/lib/bitcoin/protocol/version.rb +70 -0
- data/lib/bitcoin/script.rb +634 -0
- data/lib/bitcoin/storage/dummy.rb +164 -0
- data/lib/bitcoin/storage/models.rb +133 -0
- data/lib/bitcoin/storage/sequel.rb +335 -0
- data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
- data/lib/bitcoin/storage/storage.rb +243 -0
- data/lib/bitcoin/version.rb +3 -0
- data/lib/bitcoin/wallet/coinselector.rb +30 -0
- data/lib/bitcoin/wallet/keygenerator.rb +75 -0
- data/lib/bitcoin/wallet/keystore.rb +203 -0
- data/lib/bitcoin/wallet/txdp.rb +116 -0
- data/lib/bitcoin/wallet/wallet.rb +243 -0
- data/spec/bitcoin/bitcoin_spec.rb +472 -0
- data/spec/bitcoin/builder_spec.rb +90 -0
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
- data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
- data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
- data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
- data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
- data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
- data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
- data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
- data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
- data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
- data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
- data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
- data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
- data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
- data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
- data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
- data/spec/bitcoin/key_spec.rb +123 -0
- data/spec/bitcoin/network_spec.rb +48 -0
- data/spec/bitcoin/protocol/addr_spec.rb +68 -0
- data/spec/bitcoin/protocol/alert_spec.rb +20 -0
- data/spec/bitcoin/protocol/block_spec.rb +101 -0
- data/spec/bitcoin/protocol/inv_spec.rb +124 -0
- data/spec/bitcoin/protocol/ping_spec.rb +49 -0
- data/spec/bitcoin/protocol/tx_spec.rb +226 -0
- data/spec/bitcoin/protocol/version_spec.rb +77 -0
- data/spec/bitcoin/reorg_spec.rb +129 -0
- data/spec/bitcoin/script/opcodes_spec.rb +417 -0
- data/spec/bitcoin/script/script_spec.rb +246 -0
- data/spec/bitcoin/spec_helper.rb +36 -0
- data/spec/bitcoin/storage_spec.rb +229 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
- data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
- metadata +295 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
include MiniTest
|
4
|
+
include Bitcoin::Wallet
|
5
|
+
|
6
|
+
describe Bitcoin::Wallet::SimpleCoinSelector do
|
7
|
+
|
8
|
+
def txout_mock(value, next_in = true, in_block = true)
|
9
|
+
tx = Mock.new
|
10
|
+
tx.expect(:get_block, in_block)
|
11
|
+
txout = Mock.new
|
12
|
+
txout.expect(:value, value)
|
13
|
+
txout.expect(:get_next_in, next_in)
|
14
|
+
txout.expect(:get_address, "addr")
|
15
|
+
txout.expect(:get_tx, tx)
|
16
|
+
txout
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should select only txouts which have not been spent" do
|
20
|
+
txouts = [txout_mock(1000, nil), txout_mock(2000, nil),
|
21
|
+
txout_mock(1000), txout_mock(3000, nil)]
|
22
|
+
cs = SimpleCoinSelector.new(txouts)
|
23
|
+
cs.select(2000).should == txouts[0..1]
|
24
|
+
cs.select(4000).should == [txouts[0], txouts[1], txouts[3]]
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should select only txouts which are in a block" do
|
28
|
+
txouts = [txout_mock(1000, nil, false), txout_mock(2000, nil),
|
29
|
+
txout_mock(1000), txout_mock(3000, nil)]
|
30
|
+
cs = SimpleCoinSelector.new(txouts)
|
31
|
+
cs.select(2000).should == txouts[1..1]
|
32
|
+
cs.select(4000).should == [txouts[1], txouts[3]]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
include Bitcoin::Wallet
|
5
|
+
describe "Bitcoin::KeyGenerator" do
|
6
|
+
|
7
|
+
@target = ("\x00\xff" + "\x00"*30).unpack("H*")[0].to_i(16)
|
8
|
+
|
9
|
+
before do
|
10
|
+
Bitcoin.network = :bitcoin
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should use random data if no seed given" do
|
14
|
+
g = KeyGenerator.new(nil, nil, @target)
|
15
|
+
g.seed.size.should == 64
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should find the nonce if not given" do
|
19
|
+
KeyGenerator.new("etd").nonce.should == 622
|
20
|
+
KeyGenerator.new("foo").nonce.should == 2116
|
21
|
+
# KeyGenerator.new("bar").nonce.should == 72353
|
22
|
+
# KeyGenerator.new("baz").nonce.should == 385471
|
23
|
+
# KeyGenerator.new("qux").nonce.should == 29559
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use given nonce" do
|
27
|
+
g = KeyGenerator.new("foo", 2116)
|
28
|
+
g.nonce.should == 2116
|
29
|
+
g.get_key(0).addr.should == '1GjyUrY3XcR4BvfgL8HqoAJbNDEgxSJdm1'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should check nonce if given" do
|
33
|
+
-> { KeyGenerator.new("foo", 42) }.should.raise ArgumentError
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use different target if given" do
|
37
|
+
g = KeyGenerator.new("foo", nil, @target)
|
38
|
+
g.nonce.should == 127
|
39
|
+
g.get_key(0).addr.should == "13E68pPJyGycgQ4ZmV45xV9r9XEeyWqZdp"
|
40
|
+
g = KeyGenerator.new("bar", nil, @target)
|
41
|
+
g.nonce.should == 40
|
42
|
+
g.get_key(0).addr.should == "12iQpWRRQBmWcHYkTZkpDFrykzc9xn5kAU"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should find keys" do
|
46
|
+
g = KeyGenerator.new("foo")
|
47
|
+
[
|
48
|
+
"05221211a9c3edb9bdf0c120770dc58d2359098c6f16f6e269f722f7dda27cc9",
|
49
|
+
"7f27bb0ca02e558c4b4b4e267417437adac01403e0d0bb9b07797d1dbb1adfd1",
|
50
|
+
"da53dec9916406bb9a412bfdc81a3892bbcb1560ab394cb9b9fc3ee2a41101ff",
|
51
|
+
"7d63c88d0ab023de3441ff268548dc5f59623efe38fdf481bdebc8bb5047c2f2",
|
52
|
+
"f582838dcba2a1739307448405905028e330e2c9de2a8ec24eed1648b8bddaa4",
|
53
|
+
"f438a3ff8ea0ee4422f83a456fa6cadf853381c09a4734ae5fbbae616c535a91",
|
54
|
+
"3a7442aa54f66ae1c8a0d352346587492269b7c800a0319c9789a8164054c59e",
|
55
|
+
"523d76467f9c091b0c7240dcc509797c8900d4303b720c6afdc4f218b43a1329",
|
56
|
+
"a11bfa40a0e920bf449ef0ec1d170513c7c82daafd8c4ae3c0e321ddf5fa5cce",
|
57
|
+
"86a60cbbad2aadfba910f63dc558dd87777561297810674bec020f0f9f86f630",
|
58
|
+
"cd1fca7ec2bddddc57fa696aefa1391bf5eeea332b79a1f29cfccfccf082a474",
|
59
|
+
].map{|h| [h].pack("H*")}.each_with_index do |key, i|
|
60
|
+
g.get_key(i).priv.should == key.unpack("H*")[0]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'fileutils'
|
4
|
+
include Bitcoin
|
5
|
+
include Bitcoin::Wallet
|
6
|
+
|
7
|
+
describe "Bitcoin::Wallet::SimpleKeyStore" do
|
8
|
+
|
9
|
+
@test1 = [
|
10
|
+
{:label => "test1", :addr => "174xCfTggAovtDezgswTgfUeCp1hWJ1i7F", :pub => "040795786162a1a2fb5bb82310fc1b0da3ced5ed8fc3495bbf848b0156eca465688b0cf08d5389c026556213b7e5ccf471d259575e1756e3352ded2a3eec6a59c8", :priv => "c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb", :mine => true, :hidden => false},
|
11
|
+
# 5J4iJt8Co9uzmAK7SnLLkvP6dY9s6882kiF4ZCJCNBpZf8QHjVf
|
12
|
+
{:addr => "135o74rH4r7vxEuDdozehLeTuzBG7ABdCA", :pub => "04608f68aafef3f216dcb0851bbda7834097a43a0b25794611ebea1177d60c52b25d944ee8c1974f4c9de3d2069cb7ebff803b75487f1f725a6c36a68c2a5ec4ad", :priv => "20c1bb60d9242db6240ae125baa0c2eea838e1e33085ff23e36b7dc4e76bb869", :mine => true, :hidden => false},
|
13
|
+
# 5JPbwuNwBWDAsKHzSCUWjvZUkwMFooSXEZrDmnQo5wpEGXcfjJY
|
14
|
+
{:label => "test3", :addr => "1Esx52p3MXsjkWWvUM8Pwm2NP14Rj5GkDF",
|
15
|
+
:mine => false, :hidden => false},
|
16
|
+
# 5Hz9HLAm4t8Mgh8i5mGQm7dgqb2R4V88yVUX6RUf2o77uZus7NP
|
17
|
+
{:label => "test4", :addr => "1F17yu83Rhtg78f8ZoEseXo6aprC1D9fwi",
|
18
|
+
:mine => false, :hidden => true},
|
19
|
+
# 5JQnYo4DNdUKKwMiMwQQxova9NExAgPjZybipjx73RxzTTwARch
|
20
|
+
{:label => "test5", :addr => "17NgKZgaDphrfvdBxmX1EssLX7Jyq4ZA22",
|
21
|
+
:mine => true, :hidden => false},
|
22
|
+
# 5KD3KboVn9a31FWZKZ7NxbbvWcbc5f32D3MkU8kfBzFkw7abRZL
|
23
|
+
{:addr => "1MnSMHjyVSEJE8eC4GUHtuDbvzHbnDBGP7", :pub => "04d4aa8b12642e533a8c3c63a8d99d03b77e642b23134cc4dde11065845a24bca86dd3fa2d4d8801bbc2c032597f9f780e72940a90081be743c0051f9cd286b935", :mine => false, :hidden => false},
|
24
|
+
]
|
25
|
+
|
26
|
+
before do
|
27
|
+
Bitcoin.network = :bitcoin
|
28
|
+
file_stub = StringIO.new
|
29
|
+
file_stub.write(@test1.to_json); file_stub.rewind
|
30
|
+
@ks = SimpleKeyStore.new(file: file_stub)
|
31
|
+
@key = Bitcoin::Key.generate
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create new store" do
|
35
|
+
file_stub = StringIO.new
|
36
|
+
file_stub.write(@test1.to_json); file_stub.rewind
|
37
|
+
ks = SimpleKeyStore.new(file: file_stub)
|
38
|
+
ks.keys.size.should == 6
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should load store" do
|
42
|
+
@ks.keys.size.should == 6
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should save store" do
|
46
|
+
file_stub = StringIO.new
|
47
|
+
file_stub.write(@test1.to_json); file_stub.rewind
|
48
|
+
ks = SimpleKeyStore.new(file: file_stub)
|
49
|
+
ks.save_keys
|
50
|
+
ks2 = SimpleKeyStore.new(file: file_stub)
|
51
|
+
ks2.keys.should == ks.keys
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should create new key" do
|
55
|
+
key = @ks.new_key
|
56
|
+
@ks.keys.last.should == {:label => nil, :addr => key.addr, :key => key}
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should delete key" do
|
60
|
+
@ks.delete(@ks.keys.last[:addr])
|
61
|
+
@ks.keys.size.should == 5
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should get key" do
|
65
|
+
k1 = @ks.key('174xCfTggAovtDezgswTgfUeCp1hWJ1i7F')[:key]
|
66
|
+
k2 = @ks.key('test1')[:key]
|
67
|
+
k3 = @ks.key(k2.pub)[:key]
|
68
|
+
[k1,k2,k3].each{|k| k.priv.should ==
|
69
|
+
'c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb'}
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should get keys" do
|
73
|
+
@ks.key('test1')[:key].priv.should ==
|
74
|
+
'c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should export key" do
|
78
|
+
k1 = @ks.export('174xCfTggAovtDezgswTgfUeCp1hWJ1i7F')
|
79
|
+
k2 = @ks.export('test1')
|
80
|
+
k3 = @ks.export(@ks.key('test1')[:key].pub)
|
81
|
+
[k1,k2,k3].uniq.should == ['5KGyp1k36dqprA9zBuzEJzf327vw4bTkJARcW13zAKBhAfVmeT3']
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should import key" do
|
85
|
+
@ks.import('5JUw75N58166KuA4Pb9s2iJARfu6MC7VaQtFZn523VMuXVYUVSm')
|
86
|
+
@ks.key('1JovdwZKSby5q3kHLMCX3cCais5YBKVA9x')[:key].priv.should ==
|
87
|
+
'57c0aea88323c96a75e461499571482ee90d98670a023213f8000047dfa3755c'
|
88
|
+
@ks.delete('1JovdwZKSby5q3kHLMCX3cCais5YBKVA9x')
|
89
|
+
@ks.import('5JUw75N58166KuA4Pb9s2iJARfu6MC7VaQtFZn523VMuXVYUVSm', "test2")
|
90
|
+
@ks.key('test2')[:key].priv.should ==
|
91
|
+
'57c0aea88323c96a75e461499571482ee90d98670a023213f8000047dfa3755c'
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not allow the same label twice" do
|
95
|
+
-> { @ks.new_key("test1") }.should.raise ArgumentError
|
96
|
+
-> { @ks.add_key({:label => "test1", :addr => "12345"}) }.should.raise ArgumentError
|
97
|
+
-> { @ks.import("foobar", "test1") }.should.raise ArgumentError
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should not allow invalid addrs" do
|
101
|
+
-> { @ks.add_key({:addr => "foobar"}) }.should.raise ArgumentError
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should store only address" do
|
105
|
+
k = {:label => 'test6', :addr => @key.addr}
|
106
|
+
@ks.add_key(k)
|
107
|
+
@ks.keys.size.should == 7
|
108
|
+
@ks.key('test6').should == k
|
109
|
+
@ks.key(@key.addr).should == k
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should store only pubkey and addr" do
|
113
|
+
k = {:label => 'test6', :addr => @key.addr, :pub => @key.pub}
|
114
|
+
@ks.add_key(k)
|
115
|
+
@ks.keys.size.should == 7
|
116
|
+
@ks.key('test6').should == k
|
117
|
+
@ks.key(@key.addr).should == k
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should store flags" do
|
121
|
+
@ks.key('test1')[:mine].should == true
|
122
|
+
@ks.key('test1')[:hidden].should == false
|
123
|
+
@ks.flag_key 'test1', :hidden, true
|
124
|
+
@ks.key('test1')[:hidden].should == true
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should list only keys which have a label" do
|
128
|
+
@ks.keys(:label).size.should == 4
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should list only keys which have a pubkey" do
|
132
|
+
@ks.keys(:pub).size.should == 3
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should list only keys which have a privkey" do
|
136
|
+
@ks.keys(:priv).size.should == 2
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should list only hidden keys" do
|
140
|
+
@ks.keys(:hidden).size.should == 1
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should list only keys which are 'mine'" do
|
144
|
+
@ks.keys(:mine).size.should == 3
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
describe "Bitcoin::Wallet::DeterministicKeyStore" do
|
151
|
+
|
152
|
+
before do
|
153
|
+
@ks = DeterministicKeyStore.new(:seed => "foo", :keys => 1, :nonce => 2116)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should create new store" do
|
157
|
+
ks = DeterministicKeyStore.new(:seed => "etd", :keys => 3)
|
158
|
+
ks.keys.size.should == 3
|
159
|
+
ks.generator.nonce.should == 622
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should load store" do
|
163
|
+
@ks.keys.map(&:priv).should ==
|
164
|
+
['7f27bb0ca02e558c4b4b4e267417437adac01403e0d0bb9b07797d1dbb1adfd1']
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should create new key" do
|
168
|
+
key = @ks.new_key
|
169
|
+
key.priv.should == 'da53dec9916406bb9a412bfdc81a3892bbcb1560ab394cb9b9fc3ee2a41101ff'
|
170
|
+
@ks.keys.last.should == key
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should get key" do
|
174
|
+
@ks.key('1KDUUSjPJkKwVEJsfpxEzBAf7iEbmqUwUu').priv.should ==
|
175
|
+
'7f27bb0ca02e558c4b4b4e267417437adac01403e0d0bb9b07797d1dbb1adfd1'
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should get keys" do
|
179
|
+
@ks.keys.map(&:priv).should ==
|
180
|
+
['7f27bb0ca02e558c4b4b4e267417437adac01403e0d0bb9b07797d1dbb1adfd1']
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should export key" do
|
184
|
+
@ks.export('1KDUUSjPJkKwVEJsfpxEzBAf7iEbmqUwUu').should ==
|
185
|
+
'5JnHbCHicVj2Wgd2KgNPU7dQ6te55GzHjc4PH9cQDFUjeepYSHX'
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
include Bitcoin
|
3
|
+
include Bitcoin::Wallet
|
4
|
+
include MiniTest
|
5
|
+
|
6
|
+
describe "Bitcoin::Wallet::TxDP" do
|
7
|
+
|
8
|
+
before do
|
9
|
+
Bitcoin.network = :testnet
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should parse txdp" do
|
13
|
+
txt = fixtures_file("txdp-1.txt")
|
14
|
+
txdp = TxDP.parse(txt)
|
15
|
+
txdp.id.should == "3fX59xPj"
|
16
|
+
txdp.tx.size.should == 3
|
17
|
+
txdp.tx.first.hash.should ==
|
18
|
+
"2aa1938705066d0f9988923000ee75d5fc728b92b9739b71f94c139e5a729527"
|
19
|
+
txdp.inputs.size.should == 2
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should parse unsigned txdp" do
|
23
|
+
txt = fixtures_file("txdp-2-unsigned.txt")
|
24
|
+
txdp = TxDP.parse(txt)
|
25
|
+
txdp.id.should == "7Q74Wkre"
|
26
|
+
txdp.tx.size.should == 2
|
27
|
+
txdp.inputs.size.should == 1
|
28
|
+
txdp.inputs.first[1].should == nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should parse signed txdp" do
|
32
|
+
txt = fixtures_file("txdp-2-signed.txt")
|
33
|
+
txdp = TxDP.parse(txt)
|
34
|
+
txdp.id.should == "7Q74Wkre"
|
35
|
+
txdp.tx.size.should == 2
|
36
|
+
txdp.inputs.size.should == 1
|
37
|
+
txdp.inputs.first[1].should ==[["mnheKkGdmw8d1fUV15XZbfmLR6AjQjVthy", "49304602210087bc1ff770c6cb3c7e47b9a3acb7dce678c16350f29acaa92e4ab231692256cf0221002da46fc1f39e132e726dea46a6e87e4278e85d36ccd393e39e931b89d55fc3a2014104955ec5646652d1b5bb14b2f867ef8879bcf224f1eab01072147fdfe0992440a234b36792937a23df736e8430613da6f0466bfc5505f2ad41b056131b7af13086"]]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should serialize unsigned txdp" do
|
41
|
+
txt = fixtures_file("txdp-2-unsigned.txt")
|
42
|
+
txdp = TxDP.parse(txt)
|
43
|
+
txdp.serialize.should == txt.strip
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should serialize signed txdp" do
|
47
|
+
txt = fixtures_file("txdp-2-signed.txt")
|
48
|
+
txdp = TxDP.parse(txt)
|
49
|
+
txdp.serialize.should == txt.strip
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should create txdp from tx" do
|
53
|
+
tx1 = Bitcoin::P::Tx.from_json(fixtures_file("rawtx-05.json"))
|
54
|
+
tx2 = Bitcoin::P::Tx.from_json(fixtures_file("rawtx-04.json"))
|
55
|
+
sig = tx2.in[0].script_sig
|
56
|
+
tx2.in[0].script_sig_length = 0
|
57
|
+
tx2.in[0].script_sig = ""
|
58
|
+
txdp = TxDP.new([tx2, tx1])
|
59
|
+
txdp.id.should != nil
|
60
|
+
txdp.inputs.size.should == 1
|
61
|
+
txdp.inputs.first[0].should == 5e9
|
62
|
+
txt = txdp.serialize
|
63
|
+
|
64
|
+
txt.should =~ /--BEGIN-TRANSACTION-#{txdp.id}--/
|
65
|
+
txt.should =~ /^_TXDIST_fabfb5da_#{txdp.id}_00cb$/
|
66
|
+
txt.should =~ /^_TXINPUT_00_50.00000000$/
|
67
|
+
txt.should =~ /--END-TRANSACTION-#{txdp.id}--/
|
68
|
+
|
69
|
+
txdp.add_sig(0, tx1.out[0].value, "mh8YhPYEAYs3E7EVyKtB5xrcfMExkkdEMF", sig)
|
70
|
+
txt = txdp.serialize
|
71
|
+
txt.should =~ /^_SIG_mh8YhPYEAYs3E7EVyKtB5xrcfMExkkdEMF_00_0048$/
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'fileutils'
|
4
|
+
include MiniTest
|
5
|
+
include Bitcoin
|
6
|
+
include Bitcoin::Wallet
|
7
|
+
|
8
|
+
def txout_mock(value, next_in = true, in_block = true)
|
9
|
+
tx = Mock.new
|
10
|
+
tx.expect(:get_block, in_block)
|
11
|
+
txout = Mock.new
|
12
|
+
txout.expect(:value, value)
|
13
|
+
txout.expect(:get_next_in, next_in)
|
14
|
+
txout.expect(:hash, [value, next_in].hash)
|
15
|
+
txout.expect(:eql?, false, [1])
|
16
|
+
txout.expect(:==, false, [1])
|
17
|
+
txout.expect(:get_tx, tx)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Bitcoin::Wallet::Wallet do
|
21
|
+
|
22
|
+
class DummyKeyStore
|
23
|
+
|
24
|
+
def initialize keys
|
25
|
+
@keys = keys.map{|k|{:key => k}}
|
26
|
+
end
|
27
|
+
|
28
|
+
def key(addr)
|
29
|
+
@keys.select{|k|k[:key].addr==addr}.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def keys
|
33
|
+
@keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def new_key
|
37
|
+
k=Bitcoin::Key.generate
|
38
|
+
@keys << {:key => k}
|
39
|
+
@keys[-1]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
before do
|
46
|
+
Bitcoin.network = :bitcoin
|
47
|
+
@storage = Mock.new
|
48
|
+
@key = Key.from_base58('5J2hn1E8KEXmQn5gqykzKotyCcHbKrVbe8fjegsfVXRdv6wwon8')
|
49
|
+
@addr = '1M89ZeWtmZmATzE3b6PHTBi8c7tGsg5xpo'
|
50
|
+
@key2 = Key.from_base58('5KK9Lw8gtNd4kcaXQJmkwcmNy8Y5rLGm49RqhcYAb7qRhWxaWMJ')
|
51
|
+
@addr2 = '134A4Bi8jN5V2KjkwmXUHjokDqdyqZ778J'
|
52
|
+
@key3 = Key.from_base58('5JFcJByQvwYnWjQ2RHTTu6LLGiBj9oPQYsHqKWuKLDVAvv4cQ7E')
|
53
|
+
@addr3 = '1EnrPVaRiRgrs1D7pujYZNN1N6iD9unZV6'
|
54
|
+
keystore_data = [{:addr => @key.addr, :priv => @key.priv, :pub => @key.pub}]
|
55
|
+
file_stub = StringIO.new
|
56
|
+
file_stub.write(keystore_data.to_json); file_stub.rewind
|
57
|
+
@keystore = SimpleKeyStore.new(file: file_stub)
|
58
|
+
@selector = MiniTest::Mock.new
|
59
|
+
@wallet = Wallet.new(@storage, @keystore, @selector)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should get total balance" do
|
63
|
+
@storage.expect(:get_txouts_for_address, [], [@addr])
|
64
|
+
@wallet.get_balance.should == 0
|
65
|
+
|
66
|
+
@storage.expect(:get_txouts_for_address, [txout_mock(5000, nil)], [@addr])
|
67
|
+
@wallet.get_balance.should == 5000
|
68
|
+
|
69
|
+
@storage.expect(:get_txouts_for_address, [txout_mock(5000, true), txout_mock(1000, nil)],
|
70
|
+
[@addr])
|
71
|
+
@wallet.get_balance.should == 1000
|
72
|
+
@storage.verify
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should get all addrs" do
|
76
|
+
@wallet.addrs.should == [@addr]
|
77
|
+
@wallet.addrs.size.should == 1
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should list all addrs with balances" do
|
81
|
+
@storage.expect(:get_balance, 0, ['dcbc93494b38ae96b14b1cc080d2acb514b7e955'])
|
82
|
+
list = @wallet.list
|
83
|
+
list.size.should == 1
|
84
|
+
list = list[0]
|
85
|
+
list.size.should == 2
|
86
|
+
list[0][:addr].should == "1M89ZeWtmZmATzE3b6PHTBi8c7tGsg5xpo"
|
87
|
+
list[1].should == 0
|
88
|
+
|
89
|
+
@storage.expect(:get_balance, 5000, ['dcbc93494b38ae96b14b1cc080d2acb514b7e955'])
|
90
|
+
list = @wallet.list
|
91
|
+
list.size.should == 1
|
92
|
+
list = list[0]
|
93
|
+
list.size.should == 2
|
94
|
+
list[0][:addr].should == @addr
|
95
|
+
list[1].should == 5000
|
96
|
+
@storage.verify
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should create new addr" do
|
100
|
+
@wallet.addrs.size.should == 1
|
101
|
+
|
102
|
+
key = Key.generate
|
103
|
+
a = @wallet.get_new_addr
|
104
|
+
@wallet.addrs.size.should == 2
|
105
|
+
@wallet.addrs[1].should == a
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "Bitcoin::Wallet::Wallet#tx" do
|
109
|
+
|
110
|
+
before do
|
111
|
+
txout = txout_mock(5000, nil)
|
112
|
+
tx = Mock.new
|
113
|
+
tx.expect(:binary_hash, "foo")
|
114
|
+
tx.expect(:out, [txout])
|
115
|
+
tx.expect(:get_block, true)
|
116
|
+
txout.expect(:get_tx, tx)
|
117
|
+
txout.expect(:get_address, @addr)
|
118
|
+
txout.expect(:pk_script,
|
119
|
+
Script.to_address_script(@addr))
|
120
|
+
@storage.expect(:get_txouts_for_address, [txout], [@addr])
|
121
|
+
selector = Mock.new
|
122
|
+
selector.expect(:select, [txout], [[txout]])
|
123
|
+
@selector.expect(:new, selector, [[txout]])
|
124
|
+
@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]])
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
it "should have hash" do
|
129
|
+
@tx.hash.size.should == 64
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should have correct inputs" do
|
133
|
+
@tx.in.size.should == 1
|
134
|
+
@tx.in.first.prev_out.should == ("foo" + "\x00"*29)
|
135
|
+
@tx.in.first.prev_out_index.should == 0
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should have correct outputs" do
|
139
|
+
@tx.out.size.should == 2
|
140
|
+
@tx.out.first.value.should == 1000
|
141
|
+
s = Script.new(@tx.out.first.pk_script)
|
142
|
+
s.get_address.should == '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should have change output" do
|
146
|
+
@tx.out.last.value.should == 4000
|
147
|
+
s = Script.new(@tx.out.last.pk_script)
|
148
|
+
s.get_address.should == @addr
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should leave tx fee" do
|
152
|
+
@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50)
|
153
|
+
@tx.out.last.value.should == 3950
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should send change to specified address" do
|
157
|
+
@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50,
|
158
|
+
'1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p')
|
159
|
+
Script.new(@tx.out.last.pk_script).get_address.should ==
|
160
|
+
'1EAntvSjkNeaJJTBQeQcN1ieU2mYf4wU9p'
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should send change to new address" do
|
164
|
+
@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 1000]], 50, :new)
|
165
|
+
@wallet.addrs.size.should == 2
|
166
|
+
Script.new(@tx.out.last.pk_script).get_address.should == @wallet.addrs.last
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should return nil if insufficient balance" do
|
170
|
+
@tx = @wallet.new_tx([[:address, '1M2JjkX7KAgwMyyF5xc2sPSfE7mL1jqkE7', 7000]])
|
171
|
+
@tx.should == nil
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "Bitcoin::Wallet::Wallet#tx (multisig)" do
|
177
|
+
|
178
|
+
|
179
|
+
before do
|
180
|
+
txout = txout_mock(5000, nil)
|
181
|
+
tx = Mock.new
|
182
|
+
tx.expect(:binary_hash, "foo")
|
183
|
+
tx.expect(:out, [txout])
|
184
|
+
tx.expect(:get_block, true)
|
185
|
+
txout.expect(:get_tx, tx)
|
186
|
+
txout.expect(:get_address, @addr)
|
187
|
+
txout.expect(:pk_script, Script.to_address_script(@addr))
|
188
|
+
@storage.expect(:get_txouts_for_address, [txout], [@addr])
|
189
|
+
@keystore = DummyKeyStore.new([@key, @key2, @key3])
|
190
|
+
|
191
|
+
selector = Mock.new
|
192
|
+
selector.expect(:select, [txout], [1000])
|
193
|
+
@selector.expect(:new, selector, [[txout]])
|
194
|
+
@wallet = Wallet.new(@storage, @keystore, @selector)
|
195
|
+
@tx = @wallet.new_tx([[:multisig, 1, @key2.pub, @key3.pub, 1000]])
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should have correct outputs" do
|
199
|
+
@tx.out.size.should == 2
|
200
|
+
@tx.out.first.value.should == 1000
|
201
|
+
s = Script.new(@tx.out.first.pk_script)
|
202
|
+
s.get_addresses.should == [@addr2, @addr3]
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|