money-tree 0.0.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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