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 +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
|