bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. 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