bitcoin-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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