money-tree 0.0.1

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