money-tree 0.0.1

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.
@@ -0,0 +1,109 @@
1
+ require 'openssl'
2
+
3
+ module MoneyTree
4
+ module Support
5
+ INT32_MAX = 256 ** [1].pack("L*").size
6
+ INT64_MAX = 256 ** [1].pack("Q*").size
7
+
8
+ def int_to_base58(int_val, leading_zero_bytes=0)
9
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
10
+ base58_val, base = '', alpha.size
11
+ while int_val > 0
12
+ int_val, remainder = int_val.divmod(base)
13
+ base58_val = alpha[remainder] + base58_val
14
+ end
15
+ base58_val
16
+ end
17
+
18
+ def base58_to_int(base58_val)
19
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
20
+ int_val, base = 0, alpha.size
21
+ base58_val.reverse.each_char.with_index do |char,index|
22
+ raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
23
+ int_val += char_index*(base**index)
24
+ end
25
+ int_val
26
+ end
27
+
28
+ def encode_base58(hex)
29
+ leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
30
+ ("1"*leading_zero_bytes) + int_to_base58( hex.to_i(16) )
31
+ end
32
+
33
+ def decode_base58(base58_val)
34
+ s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
35
+ s = '' if s == '00'
36
+ leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
37
+ s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
38
+ s
39
+ end
40
+ alias_method :base58_to_hex, :decode_base58
41
+
42
+ def to_serialized_base58(hex)
43
+ hash = sha256 hex
44
+ hash = sha256 hash
45
+ checksum = hash.slice(0..7)
46
+ address = hex + checksum
47
+ encode_base58 address
48
+ end
49
+
50
+ def sha256(source, opts = {})
51
+ source = [source].pack("H*") unless opts[:ascii]
52
+ bytes_to_hex OpenSSL::Digest::SHA256.digest(source)
53
+ end
54
+
55
+ def ripemd160(source, opts = {})
56
+ source = [source].pack("H*") unless opts[:ascii]
57
+ bytes_to_hex OpenSSL::Digest::RIPEMD160.digest(source)
58
+ end
59
+
60
+ def encode_base64(hex)
61
+ [[hex].pack("H*")].pack("m0")
62
+ end
63
+
64
+ def decode_base64(base64)
65
+ base64.unpack("m0").unpack("H*")
66
+ end
67
+
68
+ def hmac_sha512(key, message)
69
+ digest = OpenSSL::Digest::SHA512.new
70
+ OpenSSL::HMAC.digest digest, key, message
71
+ end
72
+
73
+ def hmac_sha512_hex(key, message)
74
+ md = hmac_sha512(key, message)
75
+ md.unpack("H*").first.rjust(64, '0')
76
+ end
77
+
78
+ def bytes_to_int(bytes, base = 16)
79
+ # bytes = bytes.bytes unless bytes.respond_to?(:inject)
80
+ # bytes.inject {|a, b| (a << 8) + b }
81
+ if bytes.is_a?(Array)
82
+ bytes = bytes.pack("C*")
83
+ end
84
+ bytes.unpack("H*")[0].to_i(16)
85
+ end
86
+
87
+ def int_to_hex(i)
88
+ hex = i.to_s(16)
89
+ hex = '0' + hex unless (hex.length % 2).zero?
90
+ hex.downcase
91
+ end
92
+
93
+ def int_to_bytes(i)
94
+ [int_to_hex(i)].pack("H*")
95
+ end
96
+
97
+ def bytes_to_hex(i)
98
+ i.unpack("H*")[0].downcase
99
+ end
100
+
101
+ def hex_to_bytes(i)
102
+ [i].pack("H*")
103
+ end
104
+
105
+ def hex_to_int(i)
106
+ bytes_to_int(hex_to_bytes(i))
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module MoneyTree
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'money-tree/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "money-tree"
8
+ spec.version = MoneyTree::VERSION
9
+ spec.authors = ["Micah Winkelspecht"]
10
+ spec.email = ["winkelspecht@gmail.com"]
11
+ spec.description = %q{A Ruby Gem implementation of Bitcoin HD Wallets}
12
+ spec.summary = %q{Bitcoin Hierarchical Deterministic Wallets in Ruby! (Bitcoin standard BIP0032)}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry"
25
+ end
@@ -0,0 +1,52 @@
1
+ require 'money-tree'
2
+
3
+ describe MoneyTree::Address do
4
+ describe "initialize" do
5
+ it "generates a private key by default" do
6
+ address = MoneyTree::Address.new
7
+ address.private_key.key.length.should == 64
8
+ end
9
+
10
+ it "generates a public key by default" do
11
+ address = MoneyTree::Address.new
12
+ address.public_key.key.length.should == 66
13
+ end
14
+
15
+ it "imports a private key in hex form" do
16
+ address = MoneyTree::Address.new private_key: "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
17
+ address.private_key.key.should == "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
18
+ address.public_key.key.should == "022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b"
19
+ address.to_s.should == "13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe"
20
+ address.private_key.to_s.should == "KzPkwAXJ4wtXHnbamTaJqoMrzwCUUJaqhUxnqYhnZvZH6KhgmDPK"
21
+ address.public_key.to_s.should == "13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe"
22
+ end
23
+
24
+ it "imports a private key in compressed wif format" do
25
+ address = MoneyTree::Address.new private_key: "KzPkwAXJ4wtXHnbamTaJqoMrzwCUUJaqhUxnqYhnZvZH6KhgmDPK"
26
+ address.private_key.key.should == "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
27
+ address.public_key.key.should == "022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b"
28
+ address.to_s.should == "13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe"
29
+ end
30
+
31
+ it "imports a private key in uncompressed wif format" do
32
+ address = MoneyTree::Address.new private_key: "5JXz5ZyFk31oHVTQxqce7yitCmTAPxBqeGQ4b7H3Aj3L45wUhoa"
33
+ address.private_key.key.should == "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
34
+ address.public_key.key.should == "022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b"
35
+ end
36
+ end
37
+
38
+ describe "to_s" do
39
+ before do
40
+ @address = MoneyTree::Address.new private_key: "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
41
+ end
42
+
43
+ it "returns compressed base58 public key" do
44
+ @address.to_s.should == "13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe"
45
+ @address.public_key.to_s.should == "13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe"
46
+ end
47
+
48
+ it "returns compressed WIF private key" do
49
+ @address.private_key.to_s.should == "KzPkwAXJ4wtXHnbamTaJqoMrzwCUUJaqhUxnqYhnZvZH6KhgmDPK"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,542 @@
1
+ require 'money-tree'
2
+
3
+ # Test vectors from https://en.bitcoin.it/wiki/BIP_0032_TestVectors
4
+ describe MoneyTree::Master do
5
+ describe "initialize" do
6
+ describe "without a seed" do
7
+ before do
8
+ @master = MoneyTree::Master.new
9
+ end
10
+
11
+ it "generates a random seed 32 bytes long" do
12
+ @master.seed.bytesize.should == 32
13
+ end
14
+ end
15
+
16
+ describe "Test vector 1" do
17
+ describe "from a seed" do
18
+ before do
19
+ @master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f"
20
+ end
21
+
22
+ describe "m" do
23
+ it "has an index of 0" do
24
+ @master.index.should == 0
25
+ end
26
+
27
+ it "is private" do
28
+ @master.is_private.should == true
29
+ end
30
+
31
+ it "has a depth of 0" do
32
+ @master.depth.should == 0
33
+ end
34
+
35
+ it "generates master node (Master)" do
36
+ @master.to_identifier.should == "3442193e1bb70916e914552172cd4e2dbc9df811"
37
+ @master.to_fingerprint.should == "3442193e"
38
+ @master.to_address.should == "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"
39
+ end
40
+
41
+ it "generates a secret key" do
42
+ @master.private_key.to_hex.should == "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"
43
+ @master.private_key.to_wif.should == "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW"
44
+ end
45
+
46
+ it "generates a public key" do
47
+ @master.public_key.to_hex.should == "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"
48
+ end
49
+
50
+ it "generates a chain code" do
51
+ @master.chain_code_hex.should == "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"
52
+ end
53
+
54
+ it "generates a serialized private key" do
55
+ @master.to_serialized_hex(:private).should == "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"
56
+ @master.to_serialized_address(:private).should == "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
57
+ end
58
+
59
+ it "generates a serialized public_key" do
60
+ @master.to_serialized_hex.should == "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"
61
+ @master.to_serialized_address.should == "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
62
+ end
63
+ end
64
+
65
+ describe "m/0p" do
66
+ before do
67
+ @node = @master.node_for_path "m/0p"
68
+ end
69
+
70
+ it "has an index of 2147483648" do
71
+ @node.index.should == 2147483648
72
+ end
73
+
74
+ it "is private" do
75
+ @node.is_private.should == true
76
+ end
77
+
78
+ it "has a depth of 1" do
79
+ @node.depth.should == 1
80
+ end
81
+
82
+ it "generates subnode" do
83
+ @node.to_identifier.should == "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"
84
+ @node.to_fingerprint.should == "5c1bd648"
85
+ @node.to_address.should == "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"
86
+ end
87
+
88
+ it "generates a private key" do
89
+ @node.private_key.to_hex.should == "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"
90
+ @node.private_key.to_wif.should == "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT"
91
+ end
92
+
93
+ it "generates a public key" do
94
+ @node.public_key.to_hex.should == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
95
+ end
96
+
97
+ it "generates a chain code" do
98
+ @node.chain_code_hex.should == "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"
99
+ end
100
+
101
+ it "generates a serialized private key" do
102
+ @node.to_serialized_hex(:private).should == "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"
103
+ @node.to_serialized_address(:private).should == "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"
104
+ end
105
+
106
+ it "generates a serialized public_key" do
107
+ @node.to_serialized_hex.should == "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"
108
+ @node.to_serialized_address.should == "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
109
+ end
110
+ end
111
+
112
+ describe "m/0'/1" do
113
+ before do
114
+ @node = @master.node_for_path "m/0'/1"
115
+ end
116
+
117
+ it "has an index of 1" do
118
+ @node.index.should == 1
119
+ end
120
+
121
+ it "is public" do
122
+ @node.is_private.should == false
123
+ end
124
+
125
+ it "has a depth of 2" do
126
+ @node.depth.should == 2
127
+ end
128
+
129
+ it "generates subnode" do
130
+ @node.to_identifier.should == "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"
131
+ @node.to_fingerprint.should == "bef5a2f9"
132
+ @node.to_address.should == "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"
133
+ end
134
+
135
+ it "generates a private key" do
136
+ @node.private_key.to_hex.should == "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"
137
+ @node.private_key.to_wif.should == "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM"
138
+ end
139
+
140
+ it "generates a public key" do
141
+ @node.public_key.to_hex.should == "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"
142
+ end
143
+
144
+ it "generates a chain code" do
145
+ @node.chain_code_hex.should == "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"
146
+ end
147
+
148
+ it "generates a serialized private key" do
149
+ @node.to_serialized_hex(:private).should == "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"
150
+ @node.to_serialized_address(:private).should == "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"
151
+ end
152
+
153
+ it "generates a serialized public_key" do
154
+ @node.to_serialized_hex.should == "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"
155
+ @node.to_serialized_address.should == "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
156
+ end
157
+ end
158
+
159
+ describe "m/0'/1/2p/2" do
160
+ before do
161
+ @node = @master.node_for_path "m/0'/1/2p/2"
162
+ end
163
+
164
+ it "has an index of 2" do
165
+ @node.index.should == 2
166
+ end
167
+
168
+ it "is public" do
169
+ @node.is_private.should == false
170
+ end
171
+
172
+ it "has a depth of 4" do
173
+ @node.depth.should == 4
174
+ end
175
+
176
+ it "generates subnode" do
177
+ @node.to_identifier.should == "d880d7d893848509a62d8fb74e32148dac68412f"
178
+ @node.to_fingerprint.should == "d880d7d8"
179
+ @node.to_address.should == "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt"
180
+ end
181
+
182
+ it "generates a private key" do
183
+ @node.private_key.to_hex.should == "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"
184
+ @node.private_key.to_wif.should == "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR"
185
+ end
186
+
187
+ it "generates a public key" do
188
+ @node.public_key.to_hex.should == "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"
189
+ end
190
+
191
+ it "generates a chain code" do
192
+ @node.chain_code_hex.should == "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"
193
+ end
194
+
195
+ it "generates a serialized private key" do
196
+ @node.to_serialized_hex(:private).should == "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"
197
+ @node.to_serialized_address(:private).should == "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"
198
+ end
199
+
200
+ it "generates a serialized public_key" do
201
+ @node.to_serialized_hex.should == "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"
202
+ @node.to_serialized_address.should == "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"
203
+ end
204
+ end
205
+
206
+ describe "m/0'/1/2'/2/1000000000" do
207
+ before do
208
+ @node = @master.node_for_path "m/0'/1/2'/2/1000000000"
209
+ end
210
+
211
+ it "has an index of 1000000000" do
212
+ @node.index.should == 1000000000
213
+ end
214
+
215
+ it "is public" do
216
+ @node.is_private.should == false
217
+ end
218
+
219
+ it "has a depth of 2" do
220
+ @node.depth.should == 5
221
+ end
222
+
223
+ it "generates subnode" do
224
+ @node.to_identifier.should == "d69aa102255fed74378278c7812701ea641fdf32"
225
+ @node.to_fingerprint.should == "d69aa102"
226
+ @node.to_address.should == "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam"
227
+ end
228
+
229
+ it "generates a private key" do
230
+ @node.private_key.to_hex.should == "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"
231
+ @node.private_key.to_wif.should == "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs"
232
+ end
233
+
234
+ it "generates a public key" do
235
+ @node.public_key.to_hex.should == "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"
236
+ end
237
+
238
+ it "generates a chain code" do
239
+ @node.chain_code_hex.should == "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"
240
+ end
241
+
242
+ it "generates a serialized private key" do
243
+ @node.to_serialized_hex(:private).should == "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"
244
+ @node.to_serialized_address(:private).should == "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"
245
+ end
246
+
247
+ it "generates a serialized public_key" do
248
+ @node.to_serialized_hex.should == "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"
249
+ @node.to_serialized_address.should == "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ describe "Test vector 2" do
256
+ describe "from a seed" do
257
+ before do
258
+ @master = MoneyTree::Master.new seed_hex: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
259
+ end
260
+
261
+ describe "m" do
262
+ it "has an index of 0" do
263
+ @master.index.should == 0
264
+ end
265
+
266
+ it "has a depth of 0" do
267
+ @master.depth.should == 0
268
+ end
269
+
270
+ it "is private" do
271
+ @master.is_private.should == true
272
+ end
273
+
274
+ it "generates master node (Master)" do
275
+ @master.to_identifier.should == "bd16bee53961a47d6ad888e29545434a89bdfe95"
276
+ @master.to_fingerprint.should == "bd16bee5"
277
+ @master.to_address.should == "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg"
278
+ end
279
+
280
+ it "generates a secret key" do
281
+ @master.private_key.to_hex.should == "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"
282
+ @master.private_key.to_wif.should == "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy"
283
+ end
284
+
285
+ it "generates a public key" do
286
+ @master.public_key.to_hex.should == "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"
287
+ end
288
+
289
+ it "generates a chain code" do
290
+ @master.chain_code_hex.should == "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"
291
+ end
292
+
293
+ it "generates a serialized private key" do
294
+ @master.to_serialized_hex(:private).should == "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"
295
+ @master.to_serialized_address(:private).should == "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"
296
+ end
297
+
298
+ it "generates a serialized public_key" do
299
+ @master.to_serialized_hex.should == "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"
300
+ @master.to_serialized_address.should == "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"
301
+ end
302
+ end
303
+
304
+ describe "m/0" do
305
+ before do
306
+ @node = @master.node_for_path "m/0"
307
+ end
308
+
309
+ it "has an index of 0" do
310
+ @node.index.should == 0
311
+ end
312
+
313
+ it "has a depth of 1" do
314
+ @node.depth.should == 1
315
+ end
316
+
317
+ it "is public" do
318
+ @node.is_private.should == false
319
+ end
320
+
321
+ it "generates subnode" do
322
+ @node.to_identifier.should == "5a61ff8eb7aaca3010db97ebda76121610b78096"
323
+ @node.to_fingerprint.should == "5a61ff8e"
324
+ @node.to_address.should == "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"
325
+ end
326
+
327
+ it "generates a private key" do
328
+ @node.private_key.to_hex.should == "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"
329
+ @node.private_key.to_wif.should == "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj"
330
+ end
331
+
332
+ it "generates a public key" do
333
+ @node.public_key.to_hex.should == "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"
334
+ end
335
+
336
+ it "generates a chain code" do
337
+ @node.chain_code_hex.should == "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"
338
+ end
339
+
340
+ it "generates a serialized private key" do
341
+ @node.to_serialized_hex(:private).should == "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"
342
+ @node.to_serialized_address(:private).should == "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"
343
+ end
344
+
345
+ it "generates a serialized public_key" do
346
+ @node.to_serialized_hex.should == "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"
347
+ @node.to_serialized_address.should == "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
348
+ end
349
+ end
350
+
351
+ describe "m/0/2147483647'" do
352
+ before do
353
+ @node = @master.node_for_path "m/0/2147483647'"
354
+ end
355
+
356
+ it "has an index of 2147483647" do
357
+ @node.index.should == 4294967295
358
+ end
359
+
360
+ it "has a depth of 2" do
361
+ @node.depth.should == 2
362
+ end
363
+
364
+ it "is private" do
365
+ @node.is_private.should == true
366
+ end
367
+
368
+ it "generates subnode" do
369
+ @node.to_identifier.should == "d8ab493736da02f11ed682f88339e720fb0379d1"
370
+ @node.to_fingerprint.should == "d8ab4937"
371
+ @node.to_address.should == "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk"
372
+ end
373
+
374
+ it "generates a private key" do
375
+ @node.private_key.to_hex.should == "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"
376
+ @node.private_key.to_wif.should == "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr"
377
+ end
378
+
379
+ it "generates a public key" do
380
+ @node.public_key.to_hex.should == "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"
381
+ end
382
+
383
+ it "generates a chain code" do
384
+ @node.chain_code_hex.should == "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"
385
+ end
386
+
387
+ it "generates a serialized private key" do
388
+ @node.to_serialized_hex(:private).should == "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"
389
+ @node.to_serialized_address(:private).should == "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"
390
+ end
391
+
392
+ it "generates a serialized public_key" do
393
+ @node.to_serialized_hex.should == "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"
394
+ @node.to_serialized_address.should == "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"
395
+ end
396
+ end
397
+
398
+ describe "m/0/2147483647'/1" do
399
+ before do
400
+ @node = @master.node_for_path "m/0/2147483647'/1"
401
+ end
402
+
403
+ it "has an index of 1" do
404
+ @node.index.should == 1
405
+ end
406
+
407
+ it "has a depth of 3" do
408
+ @node.depth.should == 3
409
+ end
410
+
411
+ it "is private" do
412
+ @node.is_private.should == false
413
+ end
414
+
415
+ it "generates subnode" do
416
+ @node.to_identifier.should == "78412e3a2296a40de124307b6485bd19833e2e34"
417
+ @node.to_fingerprint.should == "78412e3a"
418
+ @node.to_address.should == "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW"
419
+ end
420
+
421
+ it "generates a private key" do
422
+ @node.private_key.to_hex.should == "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"
423
+ @node.private_key.to_wif.should == "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2"
424
+ end
425
+
426
+ it "generates a public key" do
427
+ @node.public_key.to_hex.should == "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"
428
+ end
429
+
430
+ it "generates a chain code" do
431
+ @node.chain_code_hex.should == "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"
432
+ end
433
+
434
+ it "generates a serialized private key" do
435
+ @node.to_serialized_hex(:private).should == "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"
436
+ @node.to_serialized_address(:private).should == "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"
437
+ end
438
+
439
+ it "generates a serialized public_key" do
440
+ @node.to_serialized_hex.should == "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"
441
+ @node.to_serialized_address.should == "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"
442
+ end
443
+ end
444
+
445
+ describe "m/0/2147483647p/1/2147483646p" do
446
+ before do
447
+ @node = @master.node_for_path "m/0/2147483647p/1/2147483646p"
448
+ end
449
+
450
+ it "has an index of 4294967294" do
451
+ @node.index.should == 4294967294
452
+ end
453
+
454
+ it "has a depth of 4" do
455
+ @node.depth.should == 4
456
+ end
457
+
458
+ it "is private" do
459
+ @node.is_private.should == true
460
+ end
461
+
462
+ it "generates subnode" do
463
+ @node.to_identifier.should == "31a507b815593dfc51ffc7245ae7e5aee304246e"
464
+ @node.to_fingerprint.should == "31a507b8"
465
+ @node.to_address.should == "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R"
466
+ end
467
+
468
+ it "generates a private key" do
469
+ @node.private_key.to_hex.should == "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"
470
+ @node.private_key.to_wif.should == "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF"
471
+ end
472
+
473
+ it "generates a public key" do
474
+ @node.public_key.to_hex.should == "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"
475
+ end
476
+
477
+ it "generates a chain code" do
478
+ @node.chain_code_hex.should == "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"
479
+ end
480
+
481
+ it "generates a serialized private key" do
482
+ @node.to_serialized_hex(:private).should == "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"
483
+ @node.to_serialized_address(:private).should == "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"
484
+ end
485
+
486
+ it "generates a serialized public_key" do
487
+ @node.to_serialized_hex.should == "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"
488
+ @node.to_serialized_address.should == "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"
489
+ end
490
+ end
491
+
492
+ describe "m/0/2147483647p/1/2147483646p/2" do
493
+ before do
494
+ @node = @master.node_for_path "m/0/2147483647p/1/2147483646p/2"
495
+ end
496
+
497
+ it "has an index of 2" do
498
+ @node.index.should == 2
499
+ end
500
+
501
+ it "has a depth of 4" do
502
+ @node.depth.should == 5
503
+ end
504
+
505
+ it "is public" do
506
+ @node.is_private.should == false
507
+ end
508
+
509
+ it "generates subnode" do
510
+ @node.to_identifier.should == "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220"
511
+ @node.to_fingerprint.should == "26132fdb"
512
+ @node.to_address.should == "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt"
513
+ end
514
+
515
+ it "generates a private key" do
516
+ @node.private_key.to_hex.should == "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"
517
+ @node.private_key.to_wif.should == "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK"
518
+ end
519
+
520
+ it "generates a public key" do
521
+ @node.public_key.to_hex.should == "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"
522
+ end
523
+
524
+ it "generates a chain code" do
525
+ @node.chain_code_hex.should == "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"
526
+ end
527
+
528
+ it "generates a serialized private key" do
529
+ @node.to_serialized_hex(:private).should == "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"
530
+ @node.to_serialized_address(:private).should == "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"
531
+ end
532
+
533
+ it "generates a serialized public_key" do
534
+ @node.to_serialized_hex.should == "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"
535
+ @node.to_serialized_address.should == "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"
536
+ end
537
+ end
538
+ end
539
+ end
540
+
541
+ end
542
+ end