money-tree 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 838719a8f745234b8e08d8726d2e4af5b61d93de
4
- data.tar.gz: 9391e5eebd7b8b71dfe98ef61f2ff5e3ad0094ad
3
+ metadata.gz: fd86557ea3684c83ffae3308afb125c6bcea3af7
4
+ data.tar.gz: 3d74748c57a91fff5b027f11a9f8eaa16f0740e4
5
5
  SHA512:
6
- metadata.gz: 73142e1cd77e36e4a4750b0a7f2e4f34f47f9320a27144ea412fd5149073598642624a37d63aca0417136c4acb9bb2e1043ec8d679384fe5a5eb415e8fc033e8
7
- data.tar.gz: 6e0e848c4b968c004ca66acfd5ff1e917978fd9af63fea16264785d36ab6219ba6ef8a493919607c40789335f87d85f02d6a9c444fae3494f8d70b1e9f5ee55c
6
+ metadata.gz: 5d996d51da2dc16fe070bb5d68f192c56fb21709d8da2f80a6f952346869641cddc84f74dde1fe9735436a952be175284875a5995bfb1adfe372e7090eb9f50d
7
+ data.tar.gz: a7510386042a6a1367773cea6948a4a5a0cf455e5434d9e0446f1b106a783092c21a52c95a7606f0762bbf6086d6d09596565035b45d02b1bf528bbf80a000fc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format documentation
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/wink/money-tree.png)](https://travis-ci.org/wink/money-tree) [![Coverage Status](https://coveralls.io/repos/wink/money-tree/badge.png?branch=master)](https://coveralls.io/r/wink/money-tree?branch=master) [![Gem Version](https://badge.fury.io/rb/money-tree.png)](http://badge.fury.io/rb/money-tree)
1
+ [![Build Status](https://travis-ci.org/wink/money-tree.png)](https://travis-ci.org/wink/money-tree) [![Coverage Status](https://coveralls.io/repos/wink/money-tree/badge.png?branch=master)](https://coveralls.io/r/wink/money-tree?branch=master) [![Code Climate](https://codeclimate.com/github/wink/money-tree.png)](https://codeclimate.com/github/wink/money-tree) [![Gem Version](https://badge.fury.io/rb/money-tree.png)](http://badge.fury.io/rb/money-tree)
2
2
  # MoneyTree
3
3
 
4
4
  MoneyTree is a Ruby implementation of Bitcoin Wallets. Specifically, it supports [Hierachical Deterministic wallets](https://en.bitcoin.it/wiki/Deterministic_Wallet) according to the protocol specified in [BIP0032](https://en.bitcoin.it/wiki/BIP_0032).
@@ -14,14 +14,12 @@ module MoneyTree
14
14
  class InvalidWIFFormat < Exception; end
15
15
  class InvalidBase64Format < Exception; end
16
16
 
17
- attr_reader :options, :key
17
+ attr_reader :options, :key, :raw_key
18
18
  attr_accessor :ec_key
19
19
 
20
20
  GROUP_NAME = 'secp256k1'
21
- GROUP_UNCOMPRESSED = "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".to_i(16)
22
- GROUP_COMPRESSED = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798".to_i(16)
23
21
  ORDER = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".to_i(16)
24
-
22
+
25
23
  def valid?(eckey = nil)
26
24
  eckey ||= ec_key
27
25
  eckey.nil? ? false : eckey.check_key
@@ -37,13 +35,11 @@ module MoneyTree
37
35
  end
38
36
 
39
37
  class PrivateKey < Key
40
-
41
- attr_reader :raw_key
42
-
38
+
43
39
  def initialize(opts = {})
44
40
  @options = opts
45
41
  # @ec_key = EC_KEY_new_by_curve_name(NID_secp256k1)
46
- @ec_key = PKey::EC.new 'secp256k1'
42
+ @ec_key = PKey::EC.new GROUP_NAME
47
43
  if @options[:key]
48
44
  @raw_key = @options[:key]
49
45
  @key = parse_raw_key
@@ -156,46 +152,103 @@ module MoneyTree
156
152
  end
157
153
 
158
154
  class PublicKey < Key
159
- attr_reader :private_key
155
+ attr_reader :private_key, :point, :group, :key_int
160
156
 
161
157
  def initialize(p_key, opts = {})
162
- raise "Must initialize with a MoneyTree::PrivateKey" unless p_key.is_a?(PrivateKey)
163
- @private_key = p_key
164
- @ec_key = @private_key.ec_key
165
158
  @options = opts
166
- @key = @options[:key] || to_hex
159
+ @options[:compressed] = true if @options[:compressed].nil?
160
+
161
+ if p_key.is_a?(PrivateKey)
162
+ @private_key = p_key
163
+ @point = @private_key.calculate_public_key(@options)
164
+ @group = @point.group
165
+ @key = @raw_key = to_hex
166
+ else
167
+ @raw_key = p_key
168
+ @group = PKey::EC::Group.new GROUP_NAME
169
+ @key = parse_raw_key
170
+ # set_point
171
+ end
172
+ raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil?
173
+ end
174
+
175
+ def compression
176
+ @group.point_conversion_form
167
177
  end
168
178
 
169
- def to_hex(opts = {})
170
- int_to_hex to_i(opts)
179
+ def compression=(compression_type = :compressed)
180
+ @group.point_conversion_form = compression_type
171
181
  end
172
182
 
173
- def to_i(opts = {})
174
- private_key.calculate_public_key(opts).to_bn.to_i
183
+ def compressed
184
+ compressed_key = self.dup
185
+ compressed_key.set_point to_i, compressed: true
186
+ compressed_key
175
187
  end
176
188
 
177
- def to_ripemd160(opts = {})
178
- hash = sha256 to_hex(opts)
189
+ def uncompressed
190
+ uncompressed_key = self.dup
191
+ uncompressed_key.set_point to_i, compressed: false
192
+ uncompressed_key
193
+ end
194
+
195
+ def set_point(int = to_i, opts = {})
196
+ opts = options.merge(opts)
197
+ opts[:compressed] = true if opts[:compressed].nil?
198
+ self.compression = opts[:compressed] ? :compressed : :uncompressed
199
+ bn = BN.new int_to_hex(int), 16
200
+ @point = PKey::EC::Point.new group, bn
201
+ raise KeyInvalid, 'point is not on the curve' unless @point.on_curve?
202
+ end
203
+
204
+ def parse_raw_key
205
+ result = if raw_key.is_a?(Bignum)
206
+ set_point raw_key
207
+ elsif hex_format?
208
+ set_point hex_to_int(raw_key), compressed: false
209
+ elsif compressed_hex_format?
210
+ set_point hex_to_int(raw_key), compressed: true
211
+ else
212
+ raise KeyFormatNotFound
213
+ end
214
+ to_hex
215
+ end
216
+
217
+ def hex_format?
218
+ raw_key.length == 130 && !raw_key[/\H/]
219
+ end
220
+
221
+ def compressed_hex_format?
222
+ raw_key.length == 66 && !raw_key[/\H/]
223
+ end
224
+
225
+ def to_hex
226
+ int_to_hex to_i
227
+ end
228
+
229
+ def to_i
230
+ point.to_bn.to_i
231
+ end
232
+
233
+ def to_ripemd160
234
+ hash = sha256 to_hex
179
235
  ripemd160 hash
180
236
  end
181
237
 
182
- def to_address(opts = {})
183
- hash = to_ripemd160(opts)
238
+ def to_address
239
+ hash = to_ripemd160
184
240
  address = MoneyTree::NETWORKS[:bitcoin][:address_version] + hash
185
241
  to_serialized_base58 address
186
242
  end
243
+ alias :to_s :to_address
187
244
 
188
- def to_fingerprint(opts = {})
189
- hash = to_ripemd160(opts)
245
+ def to_fingerprint
246
+ hash = to_ripemd160
190
247
  hash.slice(0..7)
191
248
  end
192
-
193
- def to_s(opts = {})
194
- to_address(opts)
195
- end
196
249
 
197
- def to_bytes(opts = {})
198
- int_to_bytes to_i(opts)
250
+ def to_bytes
251
+ int_to_bytes to_i
199
252
  end
200
253
  end
201
254
  end
@@ -5,6 +5,8 @@ module MoneyTree
5
5
 
6
6
  class PublicDerivationFailure < Exception; end
7
7
  class InvalidKeyForIndex < Exception; end
8
+ class ImportError < Exception; end
9
+ class PrivatePublicMismatch < Exception; end
8
10
 
9
11
  def initialize(opts = {})
10
12
  @depth = opts[:depth]
@@ -16,6 +18,10 @@ module MoneyTree
16
18
  @parent = opts[:parent]
17
19
  end
18
20
 
21
+ def is_private?
22
+ is_private == true
23
+ end
24
+
19
25
  def index_hex(i = index)
20
26
  if i < 0
21
27
  [i].pack('l>').unpack('H*').first
@@ -28,34 +34,42 @@ module MoneyTree
28
34
  depth.to_s(16).rjust(2, "0")
29
35
  end
30
36
 
31
- def private_derivation_private_key_message(i)
37
+ def private_derivation_message(i)
32
38
  "\x00" + private_key.to_bytes + i_as_bytes(i)
33
39
  end
34
40
 
35
- def public_derivation_private_key_message(i)
41
+ def public_derivation_message(i)
36
42
  public_key.to_bytes << i_as_bytes(i)
37
43
  end
38
-
39
- # TODO: Complete public key derivation message
40
- # def public_derivation_public_key_message(i)
41
- # public_key.to_bytes + i_as_bytes(i)
42
- # end
43
-
44
+
44
45
  def i_as_bytes(i)
45
46
  [i].pack('N')
46
47
  end
47
48
 
48
49
  def derive_private_key(i = 0)
49
- message = i >= 0x80000000 ? private_derivation_private_key_message(i) : public_derivation_private_key_message(i)
50
+ message = i >= 0x80000000 ? private_derivation_message(i) : public_derivation_message(i)
50
51
  hash = hmac_sha512 int_to_bytes(chain_code), message
51
52
  left_int = left_from_hash(hash)
52
- raise InvalidKeyForIndex if left_int >= MoneyTree::Key::ORDER # very low probability
53
+ raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability
53
54
  child_private_key = (left_int + private_key.to_i) % MoneyTree::Key::ORDER
54
- raise InvalidKeyForIndex if child_private_key == 0 # very low probability
55
+ raise InvalidKeyForIndex, 'equal to zero' if child_private_key == 0 # very low probability
55
56
  child_chain_code = right_from_hash(hash)
56
57
  return child_private_key, child_chain_code
57
58
  end
58
59
 
60
+ def derive_public_key(i = 0)
61
+ raise PrivatePublicMismatch if i >= 0x80000000
62
+ message = public_derivation_message(i)
63
+ hash = hmac_sha512 int_to_bytes(chain_code), message
64
+ left_int = left_from_hash(hash)
65
+ raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability
66
+ factor = BN.new left_int.to_s
67
+ child_public_key = public_key.uncompressed.group.generator.mul(factor).add(public_key.uncompressed.point).to_bn.to_i
68
+ raise InvalidKeyForIndex, 'at infinity' if child_public_key == 1/0.0 # very low probability
69
+ child_chain_code = right_from_hash(hash)
70
+ return child_public_key, child_chain_code
71
+ end
72
+
59
73
  def left_from_hash(hash)
60
74
  bytes_to_int hash.bytes.to_a[0..31]
61
75
  end
@@ -76,25 +90,31 @@ module MoneyTree
76
90
  # end
77
91
 
78
92
  def to_serialized_hex(type = :public)
93
+ raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil?
79
94
  version_key = type.to_sym == :private ? :extended_privkey_version : :extended_pubkey_version
80
95
  hex = MoneyTree::NETWORKS[:bitcoin][version_key] # version (4 bytes)
81
96
  hex += depth_hex(depth) # depth (1 byte)
82
97
  hex += depth.zero? ? '00000000' : parent.to_fingerprint# fingerprint of key (4 bytes)
83
98
  hex += index_hex(index) # child number i (4 bytes)
84
99
  hex += chain_code_hex
85
- hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.to_hex
100
+ hex += if type.to_sym == :private
101
+ "00#{private_key.to_hex}"
102
+ else
103
+ public_key.compressed.to_hex
104
+ end
86
105
  end
87
106
 
88
107
  def to_serialized_address(type = :public)
108
+ raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil?
89
109
  to_serialized_base58 to_serialized_hex(type)
90
110
  end
91
111
 
92
112
  def to_identifier
93
- public_key.to_ripemd160
113
+ public_key.compressed.to_ripemd160
94
114
  end
95
115
 
96
116
  def to_fingerprint
97
- public_key.to_fingerprint
117
+ public_key.compressed.to_fingerprint
98
118
  end
99
119
 
100
120
  def to_address
@@ -104,18 +124,24 @@ module MoneyTree
104
124
 
105
125
  def subnode(i = 0, opts = {})
106
126
  # opts[:as_private] = is_private? unless opts[:as_private] == false
107
- child_private_key, child_chain_code = derive_private_key(i)
108
- child_private_key = MoneyTree::PrivateKey.new key: child_private_key
109
- child_public_key = MoneyTree::PublicKey.new child_private_key
127
+ if private_key.nil?
128
+ child_public_key, child_chain_code = derive_public_key(i)
129
+ child_public_key = MoneyTree::PublicKey.new child_public_key
130
+ else
131
+ child_private_key, child_chain_code = derive_private_key(i)
132
+ child_private_key = MoneyTree::PrivateKey.new key: child_private_key
133
+ child_public_key = MoneyTree::PublicKey.new child_private_key
134
+ end
135
+
110
136
  index = i
111
137
 
112
138
  MoneyTree::Node.new depth: depth+1,
113
- index: i,
114
- is_private: i >= 0x80000000 || i < 0,
115
- private_key: child_private_key,
116
- public_key: child_public_key,
117
- chain_code: child_chain_code,
118
- parent: self
139
+ index: i,
140
+ is_private: i >= 0x80000000 || i < 0,
141
+ private_key: private_key.nil? ? nil : child_private_key,
142
+ public_key: child_public_key,
143
+ chain_code: child_chain_code,
144
+ parent: self
119
145
  end
120
146
 
121
147
  # path: a path of subkeys denoted by numbers and slashes. Use
@@ -182,28 +208,34 @@ module MoneyTree
182
208
  @depth = 0
183
209
  @index = 0
184
210
  @is_private = true
185
- @seed_generation_attempt = 0
186
211
  opts[:seed] = [opts[:seed_hex]].pack("H*") if opts[:seed_hex]
187
212
  if opts[:seed]
188
213
  @seed = opts[:seed]
189
214
  @seed_hash = generate_seed_hash(@seed)
190
215
  raise SeedGeneration::ImportError unless seed_valid?(@seed_hash)
216
+ set_seeded_keys
217
+ elsif opts[:private_key] || opts[:public_key]
218
+ raise ImportError, 'chain code required' unless opts[:chain_code]
219
+ @chain_code = opts[:chain_code]
220
+ if opts[:private_key]
221
+ @private_key = opts[:private_key]
222
+ @public_key = MoneyTree::PublicKey.new @private_key
223
+ else opts[:public_key]
224
+ @public_key = opts[:public_key].is_a?(MoneyTree::PublicKey) ? opts[:public_key] : MoneyTree::PublicKey.new(opts[:public_key])
225
+ @is_private = false
226
+ end
191
227
  else
192
- generate_seed_until_valid
228
+ generate_seed
229
+ set_seeded_keys
193
230
  end
194
- set_master_keys
195
231
  end
196
232
 
197
- def generate_seed_until_valid
198
- @seed = generate_seed
233
+ def generate_seed
234
+ @seed = OpenSSL::Random.random_bytes(32)
199
235
  @seed_hash = generate_seed_hash(@seed)
200
236
  raise SeedGeneration::ValidityError unless seed_valid?(@seed_hash)
201
237
  end
202
238
 
203
- def generate_seed
204
- OpenSSL::Random.random_bytes(32)
205
- end
206
-
207
239
  def generate_seed_hash(seed)
208
240
  hmac_sha512 HD_WALLET_BASE_KEY, seed
209
241
  end
@@ -214,7 +246,7 @@ module MoneyTree
214
246
  !master_key.zero? && master_key < MoneyTree::Key::ORDER
215
247
  end
216
248
 
217
- def set_master_keys
249
+ def set_seeded_keys
218
250
  @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash)
219
251
  @chain_code = right_from_hash(seed_hash)
220
252
  @public_key = MoneyTree::PublicKey.new @private_key
@@ -3,24 +3,25 @@ require 'base64'
3
3
 
4
4
  module MoneyTree
5
5
  module Support
6
+ include OpenSSL
7
+
6
8
  INT32_MAX = 256 ** [1].pack("L*").size
7
9
  INT64_MAX = 256 ** [1].pack("Q*").size
10
+ BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
8
11
 
9
12
  def int_to_base58(int_val, leading_zero_bytes=0)
10
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
11
- base58_val, base = '', alpha.size
13
+ base58_val, base = '', BASE58_CHARS.size
12
14
  while int_val > 0
13
15
  int_val, remainder = int_val.divmod(base)
14
- base58_val = alpha[remainder] + base58_val
16
+ base58_val = BASE58_CHARS[remainder] + base58_val
15
17
  end
16
18
  base58_val
17
19
  end
18
20
 
19
21
  def base58_to_int(base58_val)
20
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
21
- int_val, base = 0, alpha.size
22
+ int_val, base = 0, BASE58_CHARS.size
22
23
  base58_val.reverse.each_char.with_index do |char,index|
23
- raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
24
+ raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = BASE58_CHARS.index(char)
24
25
  int_val += char_index*(base**index)
25
26
  end
26
27
  int_val
@@ -48,14 +49,17 @@ module MoneyTree
48
49
  encode_base58 address
49
50
  end
50
51
 
51
- def sha256(source, opts = {})
52
+ def digestify(digest_type, source, opts = {})
52
53
  source = [source].pack("H*") unless opts[:ascii]
53
- bytes_to_hex OpenSSL::Digest::SHA256.digest(source)
54
+ bytes_to_hex Digest.digest(digest_type, source)
55
+ end
56
+
57
+ def sha256(source, opts = {})
58
+ digestify('SHA256', source, opts)
54
59
  end
55
60
 
56
61
  def ripemd160(source, opts = {})
57
- source = [source].pack("H*") unless opts[:ascii]
58
- bytes_to_hex OpenSSL::Digest::RIPEMD160.digest(source)
62
+ digestify('RIPEMD160', source, opts)
59
63
  end
60
64
 
61
65
  def encode_base64(hex)
@@ -67,8 +71,8 @@ module MoneyTree
67
71
  end
68
72
 
69
73
  def hmac_sha512(key, message)
70
- digest = OpenSSL::Digest::SHA512.new
71
- OpenSSL::HMAC.digest digest, key, message
74
+ digest = Digest::SHA512.new
75
+ HMAC.digest digest, key, message
72
76
  end
73
77
 
74
78
  def hmac_sha512_hex(key, message)
@@ -1,3 +1,3 @@
1
1
  module MoneyTree
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/money-tree.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "openssl_extensions"
1
2
  require "money-tree/version"
2
3
  require "money-tree/support"
3
4
  require "money-tree/networks"
@@ -0,0 +1,52 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'openssl'
4
+ require 'ffi'
5
+
6
+ module MoneyTree
7
+ module OpenSSLExtensions
8
+ extend FFI::Library
9
+ ffi_lib 'ssl'
10
+
11
+ NID_secp256k1 = 714
12
+ POINT_CONVERSION_COMPRESSED = 2
13
+ POINT_CONVERSION_UNCOMPRESSED = 4
14
+
15
+ attach_function :EC_KEY_free, [:pointer], :int
16
+ attach_function :EC_KEY_get0_group, [:pointer], :pointer
17
+ attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
18
+ attach_function :EC_POINT_free, [:pointer], :int
19
+ attach_function :EC_POINT_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int
20
+ attach_function :EC_POINT_point2hex, [:pointer, :pointer, :int, :pointer], :string
21
+ attach_function :EC_POINT_hex2point, [:pointer, :string, :pointer, :pointer], :pointer
22
+ attach_function :EC_POINT_new, [:pointer], :pointer
23
+
24
+ def self.add(point_0, point_1)
25
+ eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
26
+ group = EC_KEY_get0_group(eckey)
27
+
28
+ point_0_hex = point_0.to_bn.to_s(16)
29
+ point_0_pt = EC_POINT_hex2point(group, point_0_hex, nil, nil)
30
+ point_1_hex = point_1.to_bn.to_s(16)
31
+ point_1_pt = EC_POINT_hex2point(group, point_1_hex, nil, nil)
32
+
33
+ sum_point = EC_POINT_new(group)
34
+ success = EC_POINT_add(group, sum_point, point_0_pt, point_1_pt, nil)
35
+ hex = EC_POINT_point2hex(group, sum_point, POINT_CONVERSION_UNCOMPRESSED, nil)
36
+ EC_KEY_free(eckey)
37
+ EC_POINT_free(sum_point)
38
+ hex
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ class OpenSSL::PKey::EC::Point
45
+ include MoneyTree::OpenSSLExtensions
46
+
47
+ def add(point)
48
+ sum_point_hex = MoneyTree::OpenSSLExtensions.add(self, point)
49
+ self.class.new group, OpenSSL::BN.new(sum_point_hex, 16)
50
+ end
51
+
52
+ end
data/money-tree.gemspec CHANGED
@@ -17,6 +17,8 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "ffi"
20
22
 
21
23
  spec.add_development_dependency "bundler", "~> 1.3"
22
24
  spec.add_development_dependency "rake"
@@ -302,8 +302,9 @@ describe MoneyTree::Master do
302
302
  end
303
303
  end
304
304
 
305
- describe "m/0" do
305
+ describe "m/0 (testing imported private key)" do
306
306
  before do
307
+ @master = MoneyTree::Master.new private_key: @master.private_key, chain_code: @master.chain_code
307
308
  @node = @master.node_for_path "m/0"
308
309
  end
309
310
 
@@ -348,6 +349,53 @@ describe MoneyTree::Master do
348
349
  @node.to_serialized_address.should == "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
349
350
  end
350
351
  end
352
+
353
+ describe "M/0 (testing import of public key)" do
354
+ before do
355
+ @master = MoneyTree::Master.new public_key: "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", chain_code: @master.chain_code
356
+ @node = @master.node_for_path "M/0"
357
+ end
358
+
359
+ it "has an index of 0" do
360
+ @node.index.should == 0
361
+ end
362
+
363
+ it "has a depth of 1" do
364
+ @node.depth.should == 1
365
+ end
366
+
367
+ it "is public" do
368
+ @node.is_private.should == false
369
+ end
370
+
371
+ it "generates subnode" do
372
+ @node.to_identifier.should == "5a61ff8eb7aaca3010db97ebda76121610b78096"
373
+ @node.to_fingerprint.should == "5a61ff8e"
374
+ @node.to_address.should == "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"
375
+ end
376
+
377
+ it "does not generate a private key" do
378
+ @node.private_key.should be_nil
379
+ end
380
+
381
+ it "generates a public key" do
382
+ @node.public_key.to_hex.should == "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"
383
+ end
384
+
385
+ it "generates a chain code" do
386
+ @node.chain_code_hex.should == "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"
387
+ end
388
+
389
+ it "does not generate a serialized private key" do
390
+ lambda { @node.to_serialized_hex(:private) }.should raise_error(MoneyTree::Node::PrivatePublicMismatch)
391
+ lambda { @node.to_serialized_address(:private) }.should raise_error(MoneyTree::Node::PrivatePublicMismatch)
392
+ end
393
+
394
+ it "generates a serialized public_key" do
395
+ @node.to_serialized_hex.should == "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"
396
+ @node.to_serialized_address.should == "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
397
+ end
398
+ end
351
399
 
352
400
  describe "m/0/2147483647'" do
353
401
  before do
@@ -2,54 +2,109 @@ require 'spec_helper'
2
2
  require 'money-tree'
3
3
 
4
4
  describe MoneyTree::PublicKey do
5
- before do
6
- @private_key = MoneyTree::PrivateKey.new key: "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
7
- @key = MoneyTree::PublicKey.new @private_key
8
- end
9
5
 
10
- describe "to_hex(compressed: false)" do
11
- it "has 65 bytes" do
12
- @key.to_hex(compressed: false).length.should == 130
13
- end
14
-
15
- it "is a valid hex" do
16
- @key.to_hex(compressed: false).should == '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
6
+ describe "with a private key" do
7
+ before do
8
+ @private_key = MoneyTree::PrivateKey.new key: "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
9
+ @key = MoneyTree::PublicKey.new @private_key
17
10
  end
18
- end
19
11
 
20
- describe "to_hex" do
21
- it "has 33 bytes" do
22
- @key.to_hex.length.should == 66
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
+
22
+ describe "to_hex" do
23
+ it "has 33 bytes" do
24
+ @key.to_hex.length.should == 66
25
+ end
26
+
27
+ it "is a valid compressed hex" do
28
+ @key.to_hex.should == '022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b'
29
+ end
23
30
  end
24
31
 
25
- it "is a valid compressed hex" do
26
- @key.to_hex.should == '022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b'
32
+ describe "to_fingerprint" do
33
+ it "returns a valid fingerprint" do
34
+ @key.to_fingerprint.should == "1fddf42e"
35
+ end
27
36
  end
28
- end
29
37
 
30
- describe "to_fingerprint" do
31
- it "returns a valid fingerprint" do
32
- @key.to_fingerprint.should == "1fddf42e"
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
+
48
+ describe "to_compressed_address" do
49
+ it "has 34 characters" do
50
+ @key.to_address.length.should == 34
51
+ end
52
+
53
+ it "is a valid compressed bitcoin address" do
54
+ @key.to_address.should == '13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe'
55
+ end
33
56
  end
34
57
  end
35
58
 
36
- describe "to_address(compressed: false)" do
37
- it "has 34 characters" do
38
- @key.to_address(compressed: false).length.should == 34
59
+ describe "without a private key" do
60
+ before do
61
+ @key = MoneyTree::PublicKey.new '042dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b11203096f1a1c5276a73f91b9465357004c2103cc42c63d6d330df589080d2e4'
39
62
  end
40
63
 
41
- it "is a valid bitcoin address" do
42
- @key.to_address(compressed: false).should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
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
+
74
+ describe "to_hex" do
75
+ it "has 33 bytes" do
76
+ @key.compressed.to_hex.length.should == 66
77
+ end
78
+
79
+ it "is a valid compressed hex" do
80
+ @key.compressed.to_hex.should == '022dfc2557a007c93092c2915f11e8aa70c4f399a6753e2e908330014091580e4b'
81
+ end
43
82
  end
44
- end
45
83
 
46
- describe "to_compressed_address" do
47
- it "has 34 characters" do
48
- @key.to_address.length.should == 34
84
+ describe "to_fingerprint" do
85
+ it "returns a valid fingerprint" do
86
+ @key.compressed.to_fingerprint.should == "1fddf42e"
87
+ end
49
88
  end
89
+
90
+ # describe "to_address(compressed: false)" do
91
+ # it "has 34 characters" do
92
+ # @key.uncompressed.to_address.length.should == 34
93
+ # end
94
+ #
95
+ # it "is a valid bitcoin address" do
96
+ # @key.uncompressed.to_address.should == '133bJA2xoVqBUsiR3uSkciMo5r15fLAaZg'
97
+ # end
98
+ # end
99
+
100
+ describe "to_compressed_address" do
101
+ it "has 34 characters" do
102
+ @key.compressed.to_address.length.should == 34
103
+ end
50
104
 
51
- it "is a valid compressed bitcoin address" do
52
- @key.to_address.should == '13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe'
105
+ it "is a valid compressed bitcoin address" do
106
+ @key.compressed.to_address.should == '13uVqa35BMo4mYq9LiZrXVzoz9EFZ6aoXe'
107
+ end
53
108
  end
54
109
  end
55
110
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
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-10 00:00:00.000000000 Z
11
+ date: 2013-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +116,7 @@ extensions: []
102
116
  extra_rdoc_files: []
103
117
  files:
104
118
  - .gitignore
119
+ - .rspec
105
120
  - .simplecov
106
121
  - .travis.yml
107
122
  - Gemfile
@@ -115,6 +130,7 @@ files:
115
130
  - lib/money-tree/node.rb
116
131
  - lib/money-tree/support.rb
117
132
  - lib/money-tree/version.rb
133
+ - lib/openssl_extensions.rb
118
134
  - money-tree.gemspec
119
135
  - spec/lib/money-tree/address_spec.rb
120
136
  - spec/lib/money-tree/node_spec.rb