money-tree 0.0.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd86557ea3684c83ffae3308afb125c6bcea3af7
4
- data.tar.gz: 3d74748c57a91fff5b027f11a9f8eaa16f0740e4
3
+ metadata.gz: ba45ee086a2a9e370b5bf2957530821a29739ea9
4
+ data.tar.gz: c9b4151f42445dd6199cb1a6eb36e6976ff3e0b0
5
5
  SHA512:
6
- metadata.gz: 5d996d51da2dc16fe070bb5d68f192c56fb21709d8da2f80a6f952346869641cddc84f74dde1fe9735436a952be175284875a5995bfb1adfe372e7090eb9f50d
7
- data.tar.gz: a7510386042a6a1367773cea6948a4a5a0cf455e5434d9e0446f1b106a783092c21a52c95a7606f0762bbf6086d6d09596565035b45d02b1bf528bbf80a000fc
6
+ metadata.gz: 50bb95999d9e348c34a6048198668b2b9d743295d8aa55779472f701cafb4764971a8d1a3ec4cc2995038a69884bbdb028ccfdf3368a2e90560d6166abb2b8f0
7
+ data.tar.gz: de5f45295af74489febf5e903c430d549b7768a0a98db823a2d5d1f05e662ae24e714441afe22a7d05c3149ffe86f8ae82fd5d69b4e69b2aebcc1a9f1554cb60
@@ -38,7 +38,6 @@ module MoneyTree
38
38
 
39
39
  def initialize(opts = {})
40
40
  @options = opts
41
- # @ec_key = EC_KEY_new_by_curve_name(NID_secp256k1)
42
41
  @ec_key = PKey::EC.new GROUP_NAME
43
42
  if @options[:key]
44
43
  @raw_key = @options[:key]
@@ -167,7 +166,6 @@ module MoneyTree
167
166
  @raw_key = p_key
168
167
  @group = PKey::EC::Group.new GROUP_NAME
169
168
  @key = parse_raw_key
170
- # set_point
171
169
  end
172
170
  raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil?
173
171
  end
@@ -10,6 +10,17 @@ module MoneyTree
10
10
  compressed_wif_chars: %w(K L),
11
11
  uncompressed_wif_char: '5',
12
12
  protocol_version: 70001
13
+ },
14
+ bitcoin_testnet: {
15
+ address_version: '6f',
16
+ p2sh_version: '05',
17
+ privkey_version: '80',
18
+ privkey_compression_flag: '01',
19
+ extended_privkey_version: "04358394",
20
+ extended_pubkey_version: "043587cf",
21
+ compressed_wif_chars: %w(K L),
22
+ uncompressed_wif_char: '5',
23
+ protocol_version: 70001
13
24
  }
14
25
  }
15
26
  end
@@ -1,7 +1,7 @@
1
1
  module MoneyTree
2
2
  class Node
3
3
  include Support
4
- attr_reader :private_key, :public_key, :chain_code, :is_private, :depth, :index, :parent
4
+ attr_reader :private_key, :public_key, :chain_code, :is_private, :depth, :index, :parent, :is_test
5
5
 
6
6
  class PublicDerivationFailure < Exception; end
7
7
  class InvalidKeyForIndex < Exception; end
@@ -9,17 +9,49 @@ module MoneyTree
9
9
  class PrivatePublicMismatch < Exception; end
10
10
 
11
11
  def initialize(opts = {})
12
- @depth = opts[:depth]
13
- @index = opts[:index]
14
- @is_private = opts[:is_private]
15
- @private_key = opts[:private_key]
16
- @public_key = opts[:public_key]
17
- @chain_code = opts[:chain_code]
18
- @parent = opts[:parent]
12
+ opts.each { |k, v| instance_variable_set "@#{k}", v }
13
+ end
14
+
15
+ def self.from_serialized_address(address)
16
+ hex = from_serialized_base58 address
17
+ version = from_version_hex hex.slice!(0..7)
18
+ self.new({
19
+ is_test: version[:test],
20
+ depth: hex.slice!(0..1).to_i(16),
21
+ fingerprint: hex.slice!(0..7),
22
+ index: hex.slice!(0..7).to_i(16),
23
+ chain_code: hex.slice!(0..63).to_i(16)
24
+ }.merge(key_options(hex, version)))
25
+ end
26
+
27
+ def self.key_options(hex, version)
28
+ if version[:private_key] && hex.slice(0..1) == '00'
29
+ private_key = MoneyTree::PrivateKey.new key: hex.slice(2..-1)
30
+ { private_key: private_key, public_key: MoneyTree::PublicKey.new(private_key) }
31
+ elsif %w(02 03).include? hex.slice(0..1)
32
+ { public_key: MoneyTree::PublicKey.new(hex) }
33
+ else
34
+ raise ImportError, 'Public or private key data does not match version type'
35
+ end
36
+ end
37
+
38
+ def self.from_version_hex(hex)
39
+ case hex
40
+ when MoneyTree::NETWORKS[:bitcoin][:extended_privkey_version]
41
+ { private_key: true, test: false }
42
+ when MoneyTree::NETWORKS[:bitcoin][:extended_pubkey_version]
43
+ { private_key: false, test: false }
44
+ when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_privkey_version]
45
+ { private_key: true, test: true }
46
+ when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_pubkey_version]
47
+ { private_key: false, test: true }
48
+ else
49
+ raise ImportError, 'invalid version bytes'
50
+ end
19
51
  end
20
52
 
21
53
  def is_private?
22
- is_private == true
54
+ index >= 0x80000000 || index < 0
23
55
  end
24
56
 
25
57
  def index_hex(i = index)
@@ -47,7 +79,7 @@ module MoneyTree
47
79
  end
48
80
 
49
81
  def derive_private_key(i = 0)
50
- message = i >= 0x80000000 ? private_derivation_message(i) : public_derivation_message(i)
82
+ message = i >= 0x80000000 || i < 0 ? private_derivation_message(i) : public_derivation_message(i)
51
83
  hash = hmac_sha512 int_to_bytes(chain_code), message
52
84
  left_int = left_from_hash(hash)
53
85
  raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability
@@ -78,17 +110,6 @@ module MoneyTree
78
110
  bytes_to_int hash.bytes.to_a[32..-1]
79
111
  end
80
112
 
81
- # TODO: Complete public key derivation
82
- # def derive_public_key(i = 0, opts = {})
83
- # raise PublicDerivationFailure unless i < 0x80000000
84
- # hash = hmac_sha512([chain_code].pack("H*"), public_key.to_hex + index_hex(i))
85
- # temp_key = MoneyTree::PrivateKey.new key: hash[0..63]
86
- # temp_pub_key = MoneyTree::PublicKey.new temp_key
87
- # child_public_key = (temp_key.to_hex.to_i(16) + temp_pub_key.to_hex.to_i(16))
88
- # child_chain_code = hash[64..-1]
89
- # return child_public_key, child_chain_code
90
- # end
91
-
92
113
  def to_serialized_hex(type = :public)
93
114
  raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil?
94
115
  version_key = type.to_sym == :private ? :extended_privkey_version : :extended_pubkey_version
@@ -97,11 +118,7 @@ module MoneyTree
97
118
  hex += depth.zero? ? '00000000' : parent.to_fingerprint# fingerprint of key (4 bytes)
98
119
  hex += index_hex(index) # child number i (4 bytes)
99
120
  hex += chain_code_hex
100
- hex += if type.to_sym == :private
101
- "00#{private_key.to_hex}"
102
- else
103
- public_key.compressed.to_hex
104
- end
121
+ hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.compressed.to_hex
105
122
  end
106
123
 
107
124
  def to_serialized_address(type = :public)
@@ -123,7 +140,6 @@ module MoneyTree
123
140
  end
124
141
 
125
142
  def subnode(i = 0, opts = {})
126
- # opts[:as_private] = is_private? unless opts[:as_private] == false
127
143
  if private_key.nil?
128
144
  child_public_key, child_chain_code = derive_public_key(i)
129
145
  child_public_key = MoneyTree::PublicKey.new child_public_key
@@ -132,12 +148,9 @@ module MoneyTree
132
148
  child_private_key = MoneyTree::PrivateKey.new key: child_private_key
133
149
  child_public_key = MoneyTree::PublicKey.new child_private_key
134
150
  end
135
-
136
- index = i
137
-
151
+
138
152
  MoneyTree::Node.new depth: depth+1,
139
153
  index: i,
140
- is_private: i >= 0x80000000 || i < 0,
141
154
  private_key: private_key.nil? ? nil : child_private_key,
142
155
  public_key: child_public_key,
143
156
  chain_code: child_chain_code,
@@ -157,7 +170,7 @@ module MoneyTree
157
170
  # You should choose either the p or the negative number convention for private key derivation.
158
171
  def node_for_path(path)
159
172
  force_public = path[-4..-1] == '.pub'
160
- path = path[0..-4] if force_public
173
+ path = path[0..-5] if force_public
161
174
  parts = path.split('/')
162
175
  nodes = []
163
176
  parts.each_with_index do |part, depth|
@@ -168,7 +181,13 @@ module MoneyTree
168
181
  nodes << nodes.last.subnode(i)
169
182
  end
170
183
  end
171
- nodes.last
184
+ if force_public or parts.first == 'M'
185
+ node = nodes.last
186
+ node.strip_private_info!
187
+ node
188
+ else
189
+ nodes.last
190
+ end
172
191
  end
173
192
 
174
193
  def parse_index(path_part)
@@ -184,6 +203,10 @@ module MoneyTree
184
203
  end
185
204
  end
186
205
 
206
+ def strip_private_info!
207
+ @private_key = nil
208
+ end
209
+
187
210
  def chain_code_hex
188
211
  int_to_hex chain_code
189
212
  end
@@ -207,7 +230,6 @@ module MoneyTree
207
230
  def initialize(opts = {})
208
231
  @depth = 0
209
232
  @index = 0
210
- @is_private = true
211
233
  opts[:seed] = [opts[:seed_hex]].pack("H*") if opts[:seed_hex]
212
234
  if opts[:seed]
213
235
  @seed = opts[:seed]
@@ -222,7 +244,6 @@ module MoneyTree
222
244
  @public_key = MoneyTree::PublicKey.new @private_key
223
245
  else opts[:public_key]
224
246
  @public_key = opts[:public_key].is_a?(MoneyTree::PublicKey) ? opts[:public_key] : MoneyTree::PublicKey.new(opts[:public_key])
225
- @is_private = false
226
247
  end
227
248
  else
228
249
  generate_seed
@@ -230,6 +251,10 @@ module MoneyTree
230
251
  end
231
252
  end
232
253
 
254
+ def is_private?
255
+ true
256
+ end
257
+
233
258
  def generate_seed
234
259
  @seed = OpenSSL::Random.random_bytes(32)
235
260
  @seed_hash = generate_seed_hash(@seed)
@@ -49,6 +49,14 @@ module MoneyTree
49
49
  encode_base58 address
50
50
  end
51
51
 
52
+ def from_serialized_base58(base58)
53
+ hex = decode_base58 base58
54
+ checksum = hex.slice!(-8..-1)
55
+ compare_checksum = sha256(sha256(hex)).slice(0..7)
56
+ raise EncodingError unless checksum == compare_checksum
57
+ hex
58
+ end
59
+
52
60
  def digestify(digest_type, source, opts = {})
53
61
  source = [source].pack("H*") unless opts[:ascii]
54
62
  bytes_to_hex Digest.digest(digest_type, source)
@@ -81,8 +89,6 @@ module MoneyTree
81
89
  end
82
90
 
83
91
  def bytes_to_int(bytes, base = 16)
84
- # bytes = bytes.bytes unless bytes.respond_to?(:inject)
85
- # bytes.inject {|a, b| (a << 8) + b }
86
92
  if bytes.is_a?(Array)
87
93
  bytes = bytes.pack("C*")
88
94
  end
@@ -1,3 +1,3 @@
1
1
  module MoneyTree
2
- VERSION = "0.0.3"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -26,7 +26,7 @@ describe MoneyTree::Master do
26
26
  end
27
27
 
28
28
  it "is private" do
29
- @master.is_private.should == true
29
+ @master.is_private?.should == true
30
30
  end
31
31
 
32
32
  it "has a depth of 0" do
@@ -73,7 +73,7 @@ describe MoneyTree::Master do
73
73
  end
74
74
 
75
75
  it "is private" do
76
- @node.is_private.should == true
76
+ @node.is_private?.should == true
77
77
  end
78
78
 
79
79
  it "has a depth of 1" do
@@ -109,6 +109,51 @@ describe MoneyTree::Master do
109
109
  @node.to_serialized_address.should == "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
110
110
  end
111
111
  end
112
+
113
+ describe "m/0p.pub" do
114
+ before do
115
+ @node = @master.node_for_path "m/0p.pub"
116
+ end
117
+
118
+ it "has an index of 2147483648" do
119
+ @node.index.should == 2147483648
120
+ end
121
+
122
+ it "is private" do
123
+ @node.is_private?.should == true
124
+ end
125
+
126
+ it "has a depth of 1" do
127
+ @node.depth.should == 1
128
+ end
129
+
130
+ it "generates subnode" do
131
+ @node.to_identifier.should == "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"
132
+ @node.to_fingerprint.should == "5c1bd648"
133
+ @node.to_address.should == "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"
134
+ end
135
+
136
+ it "does not generate a private key" do
137
+ @node.private_key.should be_nil
138
+ end
139
+
140
+ it "generates a public key" do
141
+ @node.public_key.to_hex.should == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
142
+ end
143
+
144
+ it "generates a chain code" do
145
+ @node.chain_code_hex.should == "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"
146
+ end
147
+
148
+ it "does not generate a serialized private key" do
149
+ lambda { @node.to_serialized_hex(:private) }.should raise_error(MoneyTree::Node::PrivatePublicMismatch)
150
+ end
151
+
152
+ it "generates a serialized public_key" do
153
+ @node.to_serialized_hex.should == "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
154
+ @node.to_serialized_address.should == "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
155
+ end
156
+ end
112
157
 
113
158
  describe "m/0'/1" do
114
159
  before do
@@ -120,7 +165,7 @@ describe MoneyTree::Master do
120
165
  end
121
166
 
122
167
  it "is public" do
123
- @node.is_private.should == false
168
+ @node.is_private?.should == false
124
169
  end
125
170
 
126
171
  it "has a depth of 2" do
@@ -157,6 +202,51 @@ describe MoneyTree::Master do
157
202
  end
158
203
  end
159
204
 
205
+ describe "M/0'/1" do
206
+ before do
207
+ @node = @master.node_for_path "M/0'/1"
208
+ end
209
+
210
+ it "has an index of 1" do
211
+ @node.index.should == 1
212
+ end
213
+
214
+ it "is public" do
215
+ @node.is_private?.should == false
216
+ end
217
+
218
+ it "has a depth of 2" do
219
+ @node.depth.should == 2
220
+ end
221
+
222
+ it "generates subnode" do
223
+ @node.to_identifier.should == "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"
224
+ @node.to_fingerprint.should == "bef5a2f9"
225
+ @node.to_address.should == "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"
226
+ end
227
+
228
+ it "does not generate a private key" do
229
+ @node.private_key.should be_nil
230
+ end
231
+
232
+ it "generates a public key" do
233
+ @node.public_key.to_hex.should == "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"
234
+ end
235
+
236
+ it "generates a chain code" do
237
+ @node.chain_code_hex.should == "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"
238
+ end
239
+
240
+ it "generates a serialized private key" do
241
+ lambda { @node.to_serialized_hex(:private) }.should raise_error(MoneyTree::Node::PrivatePublicMismatch)
242
+ end
243
+
244
+ it "generates a serialized public_key" do
245
+ @node.to_serialized_hex.should == "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"
246
+ @node.to_serialized_address.should == "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
247
+ end
248
+ end
249
+
160
250
  describe "m/0'/1/2p/2" do
161
251
  before do
162
252
  @node = @master.node_for_path "m/0'/1/2p/2"
@@ -167,7 +257,7 @@ describe MoneyTree::Master do
167
257
  end
168
258
 
169
259
  it "is public" do
170
- @node.is_private.should == false
260
+ @node.is_private?.should == false
171
261
  end
172
262
 
173
263
  it "has a depth of 4" do
@@ -214,7 +304,7 @@ describe MoneyTree::Master do
214
304
  end
215
305
 
216
306
  it "is public" do
217
- @node.is_private.should == false
307
+ @node.is_private?.should == false
218
308
  end
219
309
 
220
310
  it "has a depth of 2" do
@@ -269,7 +359,7 @@ describe MoneyTree::Master do
269
359
  end
270
360
 
271
361
  it "is private" do
272
- @master.is_private.should == true
362
+ @master.is_private?.should == true
273
363
  end
274
364
 
275
365
  it "generates master node (Master)" do
@@ -317,7 +407,7 @@ describe MoneyTree::Master do
317
407
  end
318
408
 
319
409
  it "is public" do
320
- @node.is_private.should == false
410
+ @node.is_private?.should == false
321
411
  end
322
412
 
323
413
  it "generates subnode" do
@@ -365,7 +455,7 @@ describe MoneyTree::Master do
365
455
  end
366
456
 
367
457
  it "is public" do
368
- @node.is_private.should == false
458
+ @node.is_private?.should == false
369
459
  end
370
460
 
371
461
  it "generates subnode" do
@@ -411,7 +501,7 @@ describe MoneyTree::Master do
411
501
  end
412
502
 
413
503
  it "is private" do
414
- @node.is_private.should == true
504
+ @node.is_private?.should == true
415
505
  end
416
506
 
417
507
  it "generates subnode" do
@@ -458,7 +548,7 @@ describe MoneyTree::Master do
458
548
  end
459
549
 
460
550
  it "is private" do
461
- @node.is_private.should == false
551
+ @node.is_private?.should == false
462
552
  end
463
553
 
464
554
  it "generates subnode" do
@@ -505,7 +595,7 @@ describe MoneyTree::Master do
505
595
  end
506
596
 
507
597
  it "is private" do
508
- @node.is_private.should == true
598
+ @node.is_private?.should == true
509
599
  end
510
600
 
511
601
  it "generates subnode" do
@@ -552,7 +642,7 @@ describe MoneyTree::Master do
552
642
  end
553
643
 
554
644
  it "is public" do
555
- @node.is_private.should == false
645
+ @node.is_private?.should == false
556
646
  end
557
647
 
558
648
  it "generates subnode" do
@@ -598,7 +688,7 @@ describe MoneyTree::Master do
598
688
  end
599
689
 
600
690
  it "is public" do
601
- @node.is_private.should == true
691
+ @node.is_private?.should == true
602
692
  end
603
693
 
604
694
  it "has a depth of 2" do
@@ -606,13 +696,37 @@ describe MoneyTree::Master do
606
696
  end
607
697
 
608
698
  it "generates a serialized private key" do
609
- @node.to_serialized_hex(:private).should == "0488ade4025c1bd648ffffffffeb8291afea1716eb01a1ce7e00d1843072cbd227309df728ba4015f73d5344aa00d9bd1df2ae56b5be763ddd393573497a8075352bda4aad9ea9083e4c0b1081ac"
610
- @node.to_serialized_address(:private).should == "xprv9wTYmMFvAM7JKr2H3xMXcBNzPsqKgffzsM2XnrvKQLJQo2qRftY4uspJCAqjvPypq6Rgvkpoen8MGGnNBxYdeYR5jt6VFdL8cGp2qKnukbW"
699
+ @node.to_serialized_hex(:private).should == "0488ade4025c1bd648ffffffff0f9ca680ee23c81a305d96b86f811947e65590200b6f74d66ecf83936313a9c900235893db08ad0efc6ae4a1eac5b31a90a7d0906403d139d4d7f3c6796fb42c4e"
700
+ @node.to_serialized_address(:private).should == "xprv9wTYmMFvAM7JHf3RuUidc24a4y2t4gN7aNP5ABreWAqt6BUBcf6xE8RNQxj2vUssYWM8iAZiZi5H1fmKkkpXjtwDCDv1pg8fSfQMk9rhHYt"
611
701
  end
612
702
 
613
703
  it "generates a serialized public_key" do
614
- @node.to_serialized_hex.should == "0488b21e025c1bd648ffffffffeb8291afea1716eb01a1ce7e00d1843072cbd227309df728ba4015f73d5344aa02bcd68031f49f6c921e24ed8c1774221433de23eb309709f571d5449909405cfc"
615
- @node.to_serialized_address.should == "xpub6ASuArnozifbYL6k9ytXyKKiwufp68PrEZx8bFKvxfqPfqAaDRrKTg8n3Ren8WFSpAbbWLGTimcfYkUB2JbyYFToRBdKT6pEhmpyVe3AqZ9"
704
+ @node.to_serialized_hex.should == "0488b21e025c1bd648ffffffff0f9ca680ee23c81a305d96b86f811947e65590200b6f74d66ecf83936313a9c902adb7979a5e99bf8acdfec3680bf482feac9898b28808c22d47db62e98de5d3fa"
705
+ @node.to_serialized_address.should == "xpub6ASuArnozifbW97u1WFdyA1JczsNU95xwbJfxaGG4WNrxyoLACRCmvjrGEojsRsoZULf5FyZXv6AWAtce2UErsshvkpjNaT1fP6sMgTZdc1"
706
+ end
707
+ end
708
+
709
+ describe "importing node" do
710
+ describe ".from_serialized_address(address)" do
711
+ it "imports a valid private node address" do
712
+ @node = MoneyTree::Node.from_serialized_address "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"
713
+ @node.private_key.to_hex.should == "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"
714
+ @node.index.should == 2147483648
715
+ @node.is_private?.should == true
716
+ @node.depth.should == 1
717
+ @node.public_key.to_hex.should == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
718
+ @node.chain_code_hex.should == "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"
719
+ end
720
+
721
+ it "imports a valid public node address" do
722
+ @node = MoneyTree::Node.from_serialized_address "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
723
+ @node.private_key.should be_nil
724
+ @node.index.should == 2147483648
725
+ @node.is_private?.should == true
726
+ @node.depth.should == 1
727
+ @node.public_key.to_hex.should == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
728
+ @node.chain_code_hex.should == "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"
729
+ end
616
730
  end
617
731
  end
618
732
  end
@@ -7,8 +7,9 @@ describe MoneyTree::PrivateKey do
7
7
  end
8
8
 
9
9
  describe "to_hex" do
10
- it "has 64 characters" do
11
- @key.to_hex.length.should == 64
10
+ it "has 62 - 64 characters" do
11
+ # could be only 31 bytes if key has leading zeros
12
+ [62, 64].should include(@key.to_hex.length)
12
13
  end
13
14
 
14
15
  it "is a valid hex" do
@@ -22,7 +23,7 @@ describe MoneyTree::PrivateKey do
22
23
  end
23
24
 
24
25
  it "starts with K or L" do
25
- @key.to_wif[0].should == 'K'
26
+ %w(K L).should include(@key.to_wif[0])
26
27
  end
27
28
 
28
29
  it "is a valid compressed wif" do
@@ -9,15 +9,15 @@ describe MoneyTree::PublicKey do
9
9
  @key = MoneyTree::PublicKey.new @private_key
10
10
  end
11
11
 
12
- # describe "to_hex(compressed: false)" do
13
- # it "has 65 bytes" do
14
- # @key.uncompressed.to_hex.length.should == 130
15
- # end
16
- #
17
- # it "is a valid hex" do
18
- # @key.uncompressed.to_hex.should == '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
19
- # end
20
- # end
12
+ describe "to_hex(compressed: false)" do
13
+ it "has 65 bytes" do
14
+ @key.uncompressed.to_hex.length.should == 130
15
+ end
16
+
17
+ it "is a valid hex" do
18
+ @key.uncompressed.to_hex.should == '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
19
+ end
20
+ end
21
21
 
22
22
  describe "to_hex" do
23
23
  it "has 33 bytes" do
@@ -35,15 +35,15 @@ describe MoneyTree::PublicKey do
35
35
  end
36
36
  end
37
37
 
38
- # describe "to_address(compressed: false)" do
39
- # it "has 34 characters" do
40
- # @key.uncompressed.to_address.length.should == 34
41
- # end
42
- #
43
- # it "is a valid bitcoin address" do
44
- # @key.uncompressed.to_address.should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
45
- # end
46
- # end
38
+ describe "to_address(compressed: false)" do
39
+ it "has 34 characters" do
40
+ @key.uncompressed.to_address.length.should == 34
41
+ end
42
+
43
+ it "is a valid bitcoin address" do
44
+ @key.uncompressed.to_address.should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
45
+ end
46
+ end
47
47
 
48
48
  describe "to_compressed_address" do
49
49
  it "has 34 characters" do
@@ -61,15 +61,15 @@ describe MoneyTree::PublicKey do
61
61
  @key = MoneyTree::PublicKey.new '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
62
62
  end
63
63
 
64
- # describe "to_hex(compressed: false)" do
65
- # it "has 65 bytes" do
66
- # @key.uncompressed.to_hex.length.should == 130
67
- # end
68
- #
69
- # it "is a valid hex" do
70
- # @key.uncompressed.to_hex.should == '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
71
- # end
72
- # end
64
+ describe "to_hex(compressed: false)" do
65
+ it "has 65 bytes" do
66
+ @key.uncompressed.to_hex.length.should == 130
67
+ end
68
+
69
+ it "is a valid hex" do
70
+ @key.uncompressed.to_hex.should == '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
71
+ end
72
+ end
73
73
 
74
74
  describe "to_hex" do
75
75
  it "has 33 bytes" do
@@ -87,15 +87,15 @@ describe MoneyTree::PublicKey do
87
87
  end
88
88
  end
89
89
 
90
- # describe "to_address(compressed: false)" do
91
- # it "has 34 characters" do
92
- # @key.uncompressed.to_address.length.should == 34
93
- # end
94
- #
95
- # it "is a valid bitcoin address" do
96
- # @key.uncompressed.to_address.should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
97
- # end
98
- # end
90
+ describe "to_address(compressed: false)" do
91
+ it "has 34 characters" do
92
+ @key.uncompressed.to_address.length.should == 34
93
+ end
94
+
95
+ it "is a valid bitcoin address" do
96
+ @key.uncompressed.to_address.should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
97
+ end
98
+ end
99
99
 
100
100
  describe "to_compressed_address" do
101
101
  it "has 34 characters" do
@@ -106,5 +106,20 @@ describe MoneyTree::PublicKey do
106
106
  @key.compressed.to_address.should == '13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe'
107
107
  end
108
108
  end
109
+
110
+ describe "#compression" do
111
+ it "returns current compression setting" do
112
+ @key.compression = :uncompressed
113
+ @key.compression.should == :uncompressed
114
+ @key.compression = :compressed
115
+ @key.compression.should == :compressed
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "with a bad key" do
121
+ it "raises KeyFormatNotFound" do
122
+ lambda { @key = MoneyTree::PublicKey.new 'THISISNOTAVALIDKEY' }.should raise_error(MoneyTree::Key::KeyFormatNotFound)
123
+ end
109
124
  end
110
125
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Winkelspecht
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-15 00:00:00.000000000 Z
11
+ date: 2013-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi