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 +4 -4
- data/lib/money-tree/key.rb +0 -2
- data/lib/money-tree/networks.rb +11 -0
- data/lib/money-tree/node.rb +60 -35
- data/lib/money-tree/support.rb +8 -2
- data/lib/money-tree/version.rb +1 -1
- data/spec/lib/money-tree/node_spec.rb +131 -17
- data/spec/lib/money-tree/private_key_spec.rb +4 -3
- data/spec/lib/money-tree/public_key_spec.rb +51 -36
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba45ee086a2a9e370b5bf2957530821a29739ea9
|
4
|
+
data.tar.gz: c9b4151f42445dd6199cb1a6eb36e6976ff3e0b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50bb95999d9e348c34a6048198668b2b9d743295d8aa55779472f701cafb4764971a8d1a3ec4cc2995038a69884bbdb028ccfdf3368a2e90560d6166abb2b8f0
|
7
|
+
data.tar.gz: de5f45295af74489febf5e903c430d549b7768a0a98db823a2d5d1f05e662ae24e714441afe22a7d05c3149ffe86f8ae82fd5d69b4e69b2aebcc1a9f1554cb60
|
data/lib/money-tree/key.rb
CHANGED
@@ -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
|
data/lib/money-tree/networks.rb
CHANGED
@@ -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
|
data/lib/money-tree/node.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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 +=
|
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..-
|
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
|
-
|
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)
|
data/lib/money-tree/support.rb
CHANGED
@@ -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
|
data/lib/money-tree/version.rb
CHANGED
@@ -26,7 +26,7 @@ describe MoneyTree::Master do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it "is private" do
|
29
|
-
@master.is_private
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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 == "
|
610
|
-
@node.to_serialized_address(:private).should == "
|
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 == "
|
615
|
-
@node.to_serialized_address.should == "
|
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
|
-
|
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]
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
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-
|
11
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|