Commit 0929ad70da5abe328b5aa05863618844797fc7ae

Authored by Marius Hanne
1 parent a68f81da4a

store only addrs/pubkeys in keystore and add flags for 'mine' and 'hidden'

Showing 2 changed files with 125 additions and 18 deletions Side-by-side Diff

lib/bitcoin/wallet/keystore.rb
... ... @@ -14,8 +14,22 @@
14 14 end
15 15  
16 16 # List all stored keys.
17   - def keys
18   - @keys
  17 + def keys(need = nil)
  18 + @keys.select do |key|
  19 + next !key[:hidden] unless need
  20 + case need
  21 + when :label
  22 + !!key[:label]
  23 + when :pub
  24 + !!key[:key].pub
  25 + when :priv
  26 + !!key[:key].priv
  27 + when :hidden
  28 + !!key[:hidden]
  29 + when :mine
  30 + !!key[:mine]
  31 + end
  32 + end
19 33 end
20 34  
21 35 # Get key for given +label+, +addr+ or +pubkey+.
22 36  
... ... @@ -25,12 +39,31 @@
25 39  
26 40 # Generate and store a new key.
27 41 def new_key(label = nil)
  42 + raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
28 43 key = Bitcoin::Key.generate
29 44 @keys << {:label => label, :addr => key.addr, :key => key}
30 45 save_keys
31 46 key
32 47 end
33 48  
  49 + # Add a key which can consist only of +addr+ and +label+.
  50 + def add_key key
  51 + label = key[:label]
  52 + raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
  53 + addr = key[:addr]
  54 + raise ArgumentError, "Address #{addr} is invalid" if addr && !Bitcoin.valid_address?(addr)
  55 + @keys << key
  56 + save_keys
  57 + key
  58 + end
  59 +
  60 + def flag_key(name, flag, value)
  61 + find_key(name) do |key|
  62 + key[flag.to_sym] = value
  63 + end
  64 + save_keys
  65 + end
  66 +
34 67 # Delete key for given +label+, +addr+ or +pubkey+.
35 68 def delete(name)
36 69 key = find_key(name)
... ... @@ -47,6 +80,7 @@
47 80 # Import key from given +base58+ string.
48 81 # (See Bitcoin::Key.from_base58)
49 82 def import(base58, label = nil)
  83 + raise ArgumentError, "Label #{label} already in use" if label && find_key(label)
50 84 key = Bitcoin::Key.from_base58(base58)
51 85 @keys << {:label => label, :addr => key.addr, :key => key}
52 86 save_keys
... ... @@ -75,9 +109,11 @@
75 109 File.open(@config[:file], 'w') do |file|
76 110 keys = @keys.map do |key|
77 111 key = key.dup
78   - key[:priv] = key[:key].priv
79   - key[:pub] = key[:key].pub
80   - key[:key] = nil
  112 + if key[:key]
  113 + key[:priv] = key[:key].priv
  114 + key[:pub] = key[:key].pub
  115 + key.delete(:key)
  116 + end
81 117 key
82 118 end
83 119 file.write(JSON.pretty_generate(keys))
... ... @@ -87,13 +123,14 @@
87 123 private
88 124  
89 125 def find_key(name)
90   - if Bitcoin.valid_address?(name)
91   - @keys.find{|k| k[:addr] == name }
92   - elsif name.size == 130
93   - @keys.find{|k| k[:key].pub == name }
94   - else
95   - @keys.find{|k| k[:label] == name }
96   - end
  126 + key = if Bitcoin.valid_address?(name)
  127 + @keys.find{|k| k[:addr] == name }
  128 + elsif name.size == 130
  129 + @keys.find{|k| k[:key].pub == name }
  130 + else
  131 + @keys.find{|k| k[:label] == name }
  132 + end
  133 + block_given? ? yield(key) : key
97 134 end
98 135  
99 136 end
spec/bitcoin/wallet/keystore_spec.rb
... ... @@ -5,14 +5,31 @@
5 5  
6 6 describe "Bitcoin::Wallet::SimpleKeyStore" do
7 7  
8   - @test1 = [{:label => "test1", :addr => "174xCfTggAovtDezgswTgfUeCp1hWJ1i7F", :pub => "040795786162a1a2fb5bb82310fc1b0da3ced5ed8fc3495bbf848b0156eca465688b0cf08d5389c026556213b7e5ccf471d259575e1756e3352ded2a3eec6a59c8", :priv => "c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb"}]
  8 + @test1 = [
  9 + {:label => "test1", :addr => "174xCfTggAovtDezgswTgfUeCp1hWJ1i7F", :pub => "040795786162a1a2fb5bb82310fc1b0da3ced5ed8fc3495bbf848b0156eca465688b0cf08d5389c026556213b7e5ccf471d259575e1756e3352ded2a3eec6a59c8", :priv => "c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb", :mine => true, :hidden => false},
  10 + # 5J4iJt8Co9uzmAK7SnLLkvP6dY9s6882kiF4ZCJCNBpZf8QHjVf
  11 + {:addr => "135o74rH4r7vxEuDdozehLeTuzBG7ABdCA", :pub => "04608f68aafef3f216dcb0851bbda7834097a43a0b25794611ebea1177d60c52b25d944ee8c1974f4c9de3d2069cb7ebff803b75487f1f725a6c36a68c2a5ec4ad", :priv => "20c1bb60d9242db6240ae125baa0c2eea838e1e33085ff23e36b7dc4e76bb869", :mine => true, :hidden => false},
  12 + # 5JPbwuNwBWDAsKHzSCUWjvZUkwMFooSXEZrDmnQo5wpEGXcfjJY
  13 + {:label => "test3", :addr => "1Esx52p3MXsjkWWvUM8Pwm2NP14Rj5GkDF",
  14 + :mine => false, :hidden => false},
  15 + # 5Hz9HLAm4t8Mgh8i5mGQm7dgqb2R4V88yVUX6RUf2o77uZus7NP
  16 + {:label => "test4", :addr => "1F17yu83Rhtg78f8ZoEseXo6aprC1D9fwi",
  17 + :mine => false, :hidden => true},
  18 + # 5JQnYo4DNdUKKwMiMwQQxova9NExAgPjZybipjx73RxzTTwARch
  19 + {:label => "test5", :addr => "17NgKZgaDphrfvdBxmX1EssLX7Jyq4ZA22",
  20 + :mine => true, :hidden => false},
  21 + # 5KD3KboVn9a31FWZKZ7NxbbvWcbc5f32D3MkU8kfBzFkw7abRZL
  22 + {:addr => "1MnSMHjyVSEJE8eC4GUHtuDbvzHbnDBGP7", :pub => "04d4aa8b12642e533a8c3c63a8d99d03b77e642b23134cc4dde11065845a24bca86dd3fa2d4d8801bbc2c032597f9f780e72940a90081be743c0051f9cd286b935", :mine => false, :hidden => false},
  23 + ]
9 24  
10 25 before do
  26 + Bitcoin.network = :bitcoin
11 27 spec_dir = File.join(File.dirname(__FILE__), '../fixtures/wallet')
12 28 FileUtils.mkdir_p(spec_dir)
13 29 @filename = File.join(spec_dir, 'test1.json')
14 30 File.open(@filename, 'w') {|f| f.write(@test1.to_json) }
15 31 @ks = SimpleKeyStore.new(file: @filename)
  32 + @key = Bitcoin::Key.generate
16 33 end
17 34  
18 35 after do
19 36  
... ... @@ -23,12 +40,12 @@
23 40 filename = @filename.sub('test1', 'test2')
24 41 File.open(filename, 'w') {|f| f.write(@test1.to_json) }
25 42 ks = SimpleKeyStore.new(file: filename)
26   - ks.keys.size.should == 1
  43 + ks.keys.size.should == 5
27 44 File.delete(filename) rescue nil
28 45 end
29 46  
30 47 it "should load store" do
31   - @ks.keys.size.should == 1
  48 + @ks.keys.size.should == 5
32 49 end
33 50  
34 51 it "should save store" do
... ... @@ -48,7 +65,7 @@
48 65  
49 66 it "should delete key" do
50 67 @ks.delete(@ks.keys.last[:addr])
51   - @ks.keys.size.should == 0
  68 + @ks.keys.size.should == 4
52 69 end
53 70  
54 71 it "should get key" do
... ... @@ -60,8 +77,8 @@
60 77 end
61 78  
62 79 it "should get keys" do
63   - @ks.keys.map{|k|k[:key].priv}.should ==
64   - ['c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb']
  80 + @ks.key('test1')[:key].priv.should ==
  81 + 'c04ea613926036d2782d43eca89512724c9f33f3e8484adb8b952a3837564bcb'
65 82 end
66 83  
67 84 it "should export key" do
... ... @@ -79,6 +96,59 @@
79 96 @ks.import('5JUw75N58166KuA4Pb9s2iJARfu6MC7VaQtFZn523VMuXVYUVSm', "test2")
80 97 @ks.key('test2')[:key].priv.should ==
81 98 '57c0aea88323c96a75e461499571482ee90d98670a023213f8000047dfa3755c'
  99 + end
  100 +
  101 + it "should not allow the same label twice" do
  102 + -> { @ks.new_key("test1") }.should.raise ArgumentError
  103 + -> { @ks.add_key({:label => "test1", :addr => "12345"}) }.should.raise ArgumentError
  104 + -> { @ks.import("foobar", "test1") }.should.raise ArgumentError
  105 + end
  106 +
  107 + it "should not allow invalid addrs" do
  108 + -> { @ks.add_key({:addr => "foobar"}) }.should.raise ArgumentError
  109 + end
  110 +
  111 + it "should store only address" do
  112 + k = {:label => 'test6', :addr => @key.addr}
  113 + @ks.add_key(k)
  114 + @ks.keys.size.should == 6
  115 + @ks.key('test6').should == k
  116 + @ks.key(@key.addr).should == k
  117 + end
  118 +
  119 + it "should store only pubkey and addr" do
  120 + k = {:label => 'test6', :addr => @key.addr, :pub => @key.pub}
  121 + @ks.add_key(k)
  122 + @ks.keys.size.should == 6
  123 + @ks.key('test6').should == k
  124 + @ks.key(@key.addr).should == k
  125 + end
  126 +
  127 + it "should store flags" do
  128 + @ks.key('test1')[:mine].should == true
  129 + @ks.key('test1')[:hidden].should == false
  130 + @ks.flag_key 'test1', :hidden, true
  131 + @ks.key('test1')[:hidden].should == true
  132 + end
  133 +
  134 + it "should list only keys which have a label" do
  135 + @ks.keys(:label).size.should == 4
  136 + end
  137 +
  138 + it "should list only keys which have a pubkey" do
  139 + @ks.keys(:pub).size.should == 3
  140 + end
  141 +
  142 + it "should list only keys which have a privkey" do
  143 + @ks.keys(:priv).size.should == 2
  144 + end
  145 +
  146 + it "should list only hidden keys" do
  147 + @ks.keys(:hidden).size.should == 1
  148 + end
  149 +
  150 + it "should list only keys which are 'mine'" do
  151 + @ks.keys(:mine).size.should == 3
82 152 end
83 153  
84 154 end