money-tree 0.8.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d626fc55825195bcbd6c57406598ed66ed9ebeaf
4
- data.tar.gz: b1e82a84328bf2fcf4366ccf7995a156f5a09b43
3
+ metadata.gz: b30fcb1b0feac18dde7d94b62a0ff97666209d3f
4
+ data.tar.gz: ffa45ca51da46d1d56d701f24e36d61848e261ea
5
5
  SHA512:
6
- metadata.gz: 56e979b90b695673384164ef2c371160ae8d9988b7c9f7b4dcc4f0822c1619fbee7b0c0698605f3edd5fde8cd7f09f88233e83477e6537bb1e50ae60f19d9f22
7
- data.tar.gz: 04d26f09a34e7b51cd4f42ff33b7b942a19b4d710f07738b29d5e60c02086e2f4d7ea271139a665f1195ef4bce8ce9264da4cc715bdb2dfcfc8c16903a4e1e09
6
+ metadata.gz: 859a1b5a4dfd21fafb73dba60ad6d243c3d8462bba900aed584934e463976fd0075abd5fa83b6317f54ce9801f342e3ba31ac639bb53ee9769068fd1f1cbda13
7
+ data.tar.gz: 3609f386e6fc5276f41a51221898dc0ebb10d314fd4b76a6ea4522ced950738b064167fb0f4d6a7d84c1b12823dd255494043e817b9dd7562f3f3eb4c02e093d
Binary file
Binary file
@@ -8,8 +8,8 @@ module MoneyTree
8
8
  @public_key = MoneyTree::PublicKey.new(@private_key, opts)
9
9
  end
10
10
 
11
- def to_s
12
- public_key.to_s
11
+ def to_s(network: :bitcoin)
12
+ public_key.to_s(network: network)
13
13
  end
14
14
 
15
15
  end
@@ -13,10 +13,10 @@ module MoneyTree
13
13
  class KeyFormatNotFound < Exception; end
14
14
  class InvalidWIFFormat < Exception; end
15
15
  class InvalidBase64Format < Exception; end
16
-
17
- attr_reader :options, :key, :raw_key, :network, :network_key
16
+
17
+ attr_reader :options, :key, :raw_key
18
18
  attr_accessor :ec_key
19
-
19
+
20
20
  GROUP_NAME = 'secp256k1'
21
21
  ORDER = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".to_i(16)
22
22
 
@@ -24,23 +24,21 @@ module MoneyTree
24
24
  eckey ||= ec_key
25
25
  eckey.nil? ? false : eckey.check_key
26
26
  end
27
-
27
+
28
28
  def to_bytes
29
29
  hex_to_bytes to_hex
30
30
  end
31
-
31
+
32
32
  def to_i
33
33
  bytes_to_int to_bytes
34
34
  end
35
35
  end
36
-
36
+
37
37
  class PrivateKey < Key
38
-
38
+
39
39
  def initialize(opts = {})
40
40
  @options = opts
41
41
  @ec_key = PKey::EC.new GROUP_NAME
42
- @network_key = options[:network] || :bitcoin
43
- @network = MoneyTree::NETWORKS[network_key]
44
42
  if @options[:key]
45
43
  @raw_key = @options[:key]
46
44
  @key = parse_raw_key
@@ -50,34 +48,34 @@ module MoneyTree
50
48
  @key = to_hex
51
49
  end
52
50
  end
53
-
51
+
54
52
  def generate
55
53
  ec_key.generate_key
56
54
  end
57
-
55
+
58
56
  def import
59
57
  ec_key.private_key = BN.new(key, 16)
60
58
  set_public_key
61
59
  end
62
-
60
+
63
61
  def calculate_public_key(opts = {})
64
62
  opts[:compressed] = true unless opts[:compressed] == false
65
63
  group = ec_key.group
66
64
  group.point_conversion_form = opts[:compressed] ? :compressed : :uncompressed
67
65
  point = group.generator.mul ec_key.private_key
68
66
  end
69
-
67
+
70
68
  def set_public_key(opts = {})
71
69
  ec_key.public_key = calculate_public_key(opts)
72
70
  end
73
-
71
+
74
72
  def parse_raw_key
75
73
  result = if raw_key.is_a?(Bignum) then from_bignum
76
74
  elsif hex_format? then from_hex
77
75
  elsif base64_format? then from_base64
78
76
  elsif compressed_wif_format? then from_wif
79
77
  elsif uncompressed_wif_format? then from_wif
80
- else
78
+ else
81
79
  raise KeyFormatNotFound
82
80
  end
83
81
  result.downcase
@@ -91,25 +89,15 @@ module MoneyTree
91
89
  def from_hex(hex = raw_key)
92
90
  hex
93
91
  end
94
-
92
+
95
93
  def from_wif(wif = raw_key)
96
94
  compressed = wif.length == 52
97
- parse_network_from_wif(wif, compressed: compressed)
98
95
  validate_wif(wif)
99
96
  hex = decode_base58(wif)
100
97
  last_char = compressed ? -11 : -9
101
98
  hex.slice(2..last_char)
102
99
  end
103
100
 
104
- def parse_network_from_wif(wif, opts = {})
105
- networks = MoneyTree::NETWORKS
106
- chars_key = opts[:compressed] ? :compressed_wif_chars : :uncompressed_wif_chars
107
- @network_key = networks.keys.select do |k|
108
- networks[k][chars_key].include?(wif.slice(0))
109
- end.first
110
- @network = networks[network_key]
111
- end
112
-
113
101
  def from_base64(base64_key = raw_key)
114
102
  raise InvalidBase64Format unless base64_format?(base64_key)
115
103
  decode_base64(base64_key)
@@ -118,7 +106,7 @@ module MoneyTree
118
106
  def compressed_wif_format?
119
107
  wif_format?(:compressed)
120
108
  end
121
-
109
+
122
110
  def uncompressed_wif_format?
123
111
  wif_format?(:uncompressed)
124
112
  end
@@ -132,19 +120,18 @@ module MoneyTree
132
120
  def base64_format?(base64_key = raw_key)
133
121
  base64_key.length == 44 && base64_key =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
134
122
  end
135
-
123
+
136
124
  def hex_format?
137
125
  raw_key.length == 64 && !raw_key[/\H/]
138
126
  end
139
-
127
+
140
128
  def to_hex
141
129
  int_to_hex @ec_key.private_key, 64
142
130
  end
143
-
144
- def to_wif(opts = {})
145
- opts[:compressed] = true unless opts[:compressed] == false
146
- source = network[:privkey_version] + to_hex
147
- source += network[:privkey_compression_flag] if opts[:compressed]
131
+
132
+ def to_wif(compressed: true, network: :bitcoin)
133
+ source = NETWORKS[network][:privkey_version] + to_hex
134
+ source += NETWORKS[network][:privkey_compression_flag] if compressed
148
135
  hash = sha256(source)
149
136
  hash = sha256(hash)
150
137
  checksum = hash.slice(0..7)
@@ -154,7 +141,6 @@ module MoneyTree
154
141
 
155
142
  def wif_valid?(wif)
156
143
  hex = decode_base58(wif)
157
- return false unless hex.slice(0..1) == network[:privkey_version]
158
144
  checksum = hex.chars.to_a.pop(8).join
159
145
  source = hex.slice(0..-9)
160
146
  hash = sha256(source)
@@ -162,68 +148,61 @@ module MoneyTree
162
148
  hash_checksum = hash.slice(0..7)
163
149
  checksum == hash_checksum
164
150
  end
165
-
151
+
166
152
  def validate_wif(wif)
167
153
  raise InvalidWIFFormat unless wif_valid?(wif)
168
154
  end
169
-
155
+
170
156
  def to_base64
171
157
  encode_base64(to_hex)
172
158
  end
173
-
174
- def to_s
175
- to_wif
159
+
160
+ def to_s(network: :bitcoin)
161
+ to_wif(network: network)
176
162
  end
177
-
163
+
178
164
  end
179
-
165
+
180
166
  class PublicKey < Key
181
167
  attr_reader :private_key, :point, :group, :key_int
182
-
168
+
183
169
  def initialize(p_key, opts = {})
184
170
  @options = opts
185
171
  @options[:compressed] = true if @options[:compressed].nil?
186
-
187
172
  if p_key.is_a?(PrivateKey)
188
173
  @private_key = p_key
189
- @network_key = private_key.network_key
190
- @network = MoneyTree::NETWORKS[network_key]
191
174
  @point = @private_key.calculate_public_key(@options)
192
175
  @group = @point.group
193
176
  @key = @raw_key = to_hex
194
177
  else
195
- @network_key = @options[:network] || :bitcoin
196
- @network = MoneyTree::NETWORKS[network_key]
197
178
  @raw_key = p_key
198
179
  @group = PKey::EC::Group.new GROUP_NAME
199
180
  @key = parse_raw_key
200
181
  end
201
182
 
202
- @options[:network] = @network_key # remember for deep clone
203
-
204
183
  raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil?
205
184
  end
206
-
185
+
207
186
  def compression
208
187
  @group.point_conversion_form
209
188
  end
210
-
189
+
211
190
  def compression=(compression_type = :compressed)
212
191
  @group.point_conversion_form = compression_type
213
192
  end
214
-
193
+
215
194
  def compressed
216
195
  compressed_key = self.class.new raw_key, options # deep clone
217
196
  compressed_key.set_point to_i, compressed: true
218
197
  compressed_key
219
198
  end
220
-
199
+
221
200
  def uncompressed
222
201
  uncompressed_key = self.class.new raw_key, options # deep clone
223
202
  uncompressed_key.set_point to_i, compressed: false
224
203
  uncompressed_key
225
204
  end
226
-
205
+
227
206
  def set_point(int = to_i, opts = {})
228
207
  opts = options.merge(opts)
229
208
  opts[:compressed] = true if opts[:compressed].nil?
@@ -232,7 +211,7 @@ module MoneyTree
232
211
  @point = PKey::EC::Point.new group, bn
233
212
  raise KeyInvalid, 'point is not on the curve' unless @point.on_curve?
234
213
  end
235
-
214
+
236
215
  def parse_raw_key
237
216
  result = if raw_key.is_a?(Bignum)
238
217
  set_point raw_key
@@ -240,45 +219,45 @@ module MoneyTree
240
219
  set_point hex_to_int(raw_key), compressed: false
241
220
  elsif compressed_hex_format?
242
221
  set_point hex_to_int(raw_key), compressed: true
243
- else
222
+ else
244
223
  raise KeyFormatNotFound
245
224
  end
246
225
  to_hex
247
226
  end
248
-
227
+
249
228
  def hex_format?
250
229
  raw_key.length == 130 && !raw_key[/\H/]
251
230
  end
252
-
231
+
253
232
  def compressed_hex_format?
254
233
  raw_key.length == 66 && !raw_key[/\H/]
255
234
  end
256
-
235
+
257
236
  def to_hex
258
237
  int_to_hex to_i, 66
259
238
  end
260
-
239
+
261
240
  def to_i
262
241
  point.to_bn.to_i
263
242
  end
264
-
243
+
265
244
  def to_ripemd160
266
245
  hash = sha256 to_hex
267
246
  ripemd160 hash
268
247
  end
269
-
270
- def to_address
248
+
249
+ def to_address(network: :bitcoin)
271
250
  hash = to_ripemd160
272
- address = network[:address_version] + hash
251
+ address = NETWORKS[network][:address_version] + hash
273
252
  to_serialized_base58 address
274
253
  end
275
254
  alias :to_s :to_address
276
-
255
+
277
256
  def to_fingerprint
278
257
  hash = to_ripemd160
279
258
  hash.slice(0..7)
280
259
  end
281
-
260
+
282
261
  def to_bytes
283
262
  int_to_bytes to_i
284
263
  end
@@ -1,28 +1,35 @@
1
1
  module MoneyTree
2
- NETWORKS = {
3
- bitcoin: {
4
- address_version: '00',
5
- p2sh_version: '05',
6
- p2sh_char: '3',
7
- privkey_version: '80',
8
- privkey_compression_flag: '01',
9
- extended_privkey_version: "0488ade4",
10
- extended_pubkey_version: "0488b21e",
11
- compressed_wif_chars: %w(K L),
12
- uncompressed_wif_chars: %w(5),
13
- protocol_version: 70001
14
- },
15
- bitcoin_testnet: {
16
- address_version: '6f',
17
- p2sh_version: 'c4',
18
- p2sh_char: '2',
19
- privkey_version: 'ef',
20
- privkey_compression_flag: '01',
21
- extended_privkey_version: "04358394",
22
- extended_pubkey_version: "043587cf",
23
- compressed_wif_chars: %w(c),
24
- uncompressed_wif_chars: %w(9),
25
- protocol_version: 70001
26
- }
27
- }
2
+ NETWORKS =
3
+ begin
4
+ hsh = Hash.new do |_, key|
5
+ raise "#{key} is not a valid network!"
6
+ end.merge(
7
+ bitcoin: {
8
+ address_version: '00',
9
+ p2sh_version: '05',
10
+ p2sh_char: '3',
11
+ privkey_version: '80',
12
+ privkey_compression_flag: '01',
13
+ extended_privkey_version: "0488ade4",
14
+ extended_pubkey_version: "0488b21e",
15
+ compressed_wif_chars: %w(K L),
16
+ uncompressed_wif_chars: %w(5),
17
+ protocol_version: 70001
18
+ },
19
+ bitcoin_testnet: {
20
+ address_version: '6f',
21
+ p2sh_version: 'c4',
22
+ p2sh_char: '2',
23
+ privkey_version: 'ef',
24
+ privkey_compression_flag: '01',
25
+ extended_privkey_version: "04358394",
26
+ extended_pubkey_version: "043587cf",
27
+ compressed_wif_chars: %w(c),
28
+ uncompressed_wif_chars: %w(9),
29
+ protocol_version: 70001
30
+ }
31
+ )
32
+ hsh[:testnet3] = hsh[:bitcoin_testnet]
33
+ hsh
34
+ end
28
35
  end
@@ -2,62 +2,52 @@ module MoneyTree
2
2
  class Node
3
3
  include Support
4
4
  extend Support
5
- attr_reader :private_key, :public_key, :chain_code,
6
- :is_private, :depth, :index, :parent, :network, :network_key
7
-
5
+ attr_reader :private_key, :public_key, :chain_code,
6
+ :is_private, :depth, :index, :parent
7
+
8
8
  class PublicDerivationFailure < Exception; end
9
9
  class InvalidKeyForIndex < Exception; end
10
10
  class ImportError < Exception; end
11
11
  class PrivatePublicMismatch < Exception; end
12
-
12
+
13
13
  def initialize(opts = {})
14
- @network_key = opts.delete(:network) || :bitcoin
15
- @network = MoneyTree::NETWORKS[network_key]
16
14
  opts.each { |k, v| instance_variable_set "@#{k}", v }
17
15
  end
18
-
19
- def self.from_serialized_address(address)
16
+
17
+ def self.from_bip32(address, has_version: true)
20
18
  hex = from_serialized_base58 address
21
- version = from_version_hex hex.slice!(0..7)
19
+ hex.slice!(0..7) if has_version
22
20
  self.new({
23
21
  depth: hex.slice!(0..1).to_i(16),
24
22
  parent_fingerprint: hex.slice!(0..7),
25
23
  index: hex.slice!(0..7).to_i(16),
26
24
  chain_code: hex.slice!(0..63).to_i(16)
27
- }.merge(key_options(hex, version)))
28
- end
29
-
30
- def self.key_options(hex, version)
31
- k_opts = { network: version[:network] }
32
- if version[:private_key] && hex.slice(0..1) == '00'
33
- private_key = MoneyTree::PrivateKey.new({ key: hex.slice(2..-1) }.merge(k_opts))
34
- k_opts.merge private_key: private_key, public_key: MoneyTree::PublicKey.new(private_key)
25
+ }.merge(parse_out_key(hex)))
26
+ end
27
+
28
+ def self.from_serialized_address(address)
29
+ puts 'Node.from_serialized_address is DEPRECATED. Please use .from_bip32 instead.'
30
+ from_bip32(address)
31
+ end
32
+
33
+ def self.parse_out_key(hex)
34
+ if hex.slice(0..1) == '00'
35
+ private_key = MoneyTree::PrivateKey.new(key: hex.slice(2..-1))
36
+ {
37
+ private_key: private_key,
38
+ public_key: MoneyTree::PublicKey.new(private_key)
39
+ }
35
40
  elsif %w(02 03).include? hex.slice(0..1)
36
- k_opts.merge public_key: MoneyTree::PublicKey.new(hex, k_opts)
41
+ { public_key: MoneyTree::PublicKey.new(hex) }
37
42
  else
38
43
  raise ImportError, 'Public or private key data does not match version type'
39
44
  end
40
45
  end
41
-
42
- def self.from_version_hex(hex)
43
- case hex
44
- when MoneyTree::NETWORKS[:bitcoin][:extended_privkey_version]
45
- { private_key: true, network: :bitcoin }
46
- when MoneyTree::NETWORKS[:bitcoin][:extended_pubkey_version]
47
- { private_key: false, network: :bitcoin }
48
- when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_privkey_version]
49
- { private_key: true, network: :bitcoin_testnet }
50
- when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_pubkey_version]
51
- { private_key: false, network: :bitcoin_testnet }
52
- else
53
- raise ImportError, 'invalid version bytes'
54
- end
55
- end
56
-
46
+
57
47
  def is_private?
58
48
  index >= 0x80000000 || index < 0
59
49
  end
60
-
50
+
61
51
  def index_hex(i = index)
62
52
  if i < 0
63
53
  [i].pack('l>').unpack('H*').first
@@ -65,15 +55,15 @@ module MoneyTree
65
55
  i.to_s(16).rjust(8, "0")
66
56
  end
67
57
  end
68
-
58
+
69
59
  def depth_hex(depth)
70
60
  depth.to_s(16).rjust(2, "0")
71
61
  end
72
-
62
+
73
63
  def private_derivation_message(i)
74
64
  "\x00" + private_key.to_bytes + i_as_bytes(i)
75
65
  end
76
-
66
+
77
67
  def public_derivation_message(i)
78
68
  public_key.to_bytes << i_as_bytes(i)
79
69
  end
@@ -81,7 +71,7 @@ module MoneyTree
81
71
  def i_as_bytes(i)
82
72
  [i].pack('N')
83
73
  end
84
-
74
+
85
75
  def derive_private_key(i = 0)
86
76
  message = i >= 0x80000000 || i < 0 ? private_derivation_message(i) : public_derivation_message(i)
87
77
  hash = hmac_sha512 hex_to_bytes(chain_code_hex), message
@@ -92,7 +82,7 @@ module MoneyTree
92
82
  child_chain_code = right_from_hash(hash)
93
83
  return child_private_key, child_chain_code
94
84
  end
95
-
85
+
96
86
  def derive_public_key(i = 0)
97
87
  raise PrivatePublicMismatch if i >= 0x80000000
98
88
  message = public_derivation_message(i)
@@ -105,40 +95,45 @@ module MoneyTree
105
95
  child_chain_code = right_from_hash(hash)
106
96
  return child_public_key, child_chain_code
107
97
  end
108
-
98
+
109
99
  def left_from_hash(hash)
110
100
  bytes_to_int hash.bytes.to_a[0..31]
111
101
  end
112
-
102
+
113
103
  def right_from_hash(hash)
114
104
  bytes_to_int hash.bytes.to_a[32..-1]
115
105
  end
116
106
 
117
- def to_serialized_hex(type = :public)
107
+ def to_serialized_hex(type = :public, network: :bitcoin)
118
108
  raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil?
119
109
  version_key = type.to_sym == :private ? :extended_privkey_version : :extended_pubkey_version
120
- hex = network[version_key] # version (4 bytes)
110
+ hex = NETWORKS[network][version_key] # version (4 bytes)
121
111
  hex += depth_hex(depth) # depth (1 byte)
122
112
  hex += parent_fingerprint # fingerprint of key (4 bytes)
123
113
  hex += index_hex(index) # child number i (4 bytes)
124
114
  hex += chain_code_hex
125
115
  hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.compressed.to_hex
126
116
  end
127
-
128
- def to_serialized_address(type = :public)
117
+
118
+ def to_bip32(type = :public, network: :bitcoin)
129
119
  raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil?
130
- to_serialized_base58 to_serialized_hex(type)
120
+ to_serialized_base58 to_serialized_hex(type, network: network)
121
+ end
122
+
123
+ def to_serialized_address(type = :public, network: :bitcoin)
124
+ puts 'Node.to_serialized_address is DEPRECATED. Please use .to_bip32.'
125
+ to_bip32(type, network: network)
131
126
  end
132
127
 
133
128
  def to_identifier(compressed=true)
134
129
  key = compressed ? public_key.compressed : public_key.uncompressed
135
130
  key.to_ripemd160
136
131
  end
137
-
132
+
138
133
  def to_fingerprint
139
134
  public_key.compressed.to_fingerprint
140
135
  end
141
-
136
+
142
137
  def parent_fingerprint
143
138
  if @parent_fingerprint
144
139
  @parent_fingerprint
@@ -147,40 +142,39 @@ module MoneyTree
147
142
  end
148
143
  end
149
144
 
150
- def to_address(compressed=true)
151
- address = network[:address_version] + to_identifier(compressed)
145
+ def to_address(compressed=true, network: :bitcoin)
146
+ address = NETWORKS[network][:address_version] + to_identifier(compressed)
152
147
  to_serialized_base58 address
153
148
  end
154
-
149
+
155
150
  def subnode(i = 0, opts = {})
156
151
  if private_key.nil?
157
152
  child_public_key, child_chain_code = derive_public_key(i)
158
- child_public_key = MoneyTree::PublicKey.new child_public_key, network: network_key
153
+ child_public_key = MoneyTree::PublicKey.new child_public_key
159
154
  else
160
155
  child_private_key, child_chain_code = derive_private_key(i)
161
- child_private_key = MoneyTree::PrivateKey.new key: child_private_key, network: network_key
156
+ child_private_key = MoneyTree::PrivateKey.new key: child_private_key
162
157
  child_public_key = MoneyTree::PublicKey.new child_private_key
163
158
  end
164
-
165
- MoneyTree::Node.new network: network_key,
166
- depth: depth+1,
167
- index: i,
159
+
160
+ MoneyTree::Node.new( depth: depth+1,
161
+ index: i,
168
162
  private_key: private_key.nil? ? nil : child_private_key,
169
163
  public_key: child_public_key,
170
164
  chain_code: child_chain_code,
171
- parent: self
165
+ parent: self)
172
166
  end
173
-
167
+
174
168
  # path: a path of subkeys denoted by numbers and slashes. Use
175
169
  # p or i<0 for private key derivation. End with .pub to force
176
170
  # the key public.
177
- #
171
+ #
178
172
  # Examples:
179
173
  # 1p/-5/2/1 would call subkey(i=1, is_prime=True).subkey(i=-5).
180
174
  # subkey(i=2).subkey(i=1) and then yield the private key
181
175
  # 0/0/458.pub would call subkey(i=0).subkey(i=0).subkey(i=458) and
182
176
  # then yield the public key
183
- #
177
+ #
184
178
  # You should choose either the p or the negative number convention for private key derivation.
185
179
  def node_for_path(path)
186
180
  force_public = path[-4..-1] == '.pub'
@@ -204,11 +198,11 @@ module MoneyTree
204
198
  nodes.last
205
199
  end
206
200
  end
207
-
201
+
208
202
  def parse_index(path_part)
209
203
  is_prime = %w(p ').include? path_part[-1]
210
204
  i = path_part.to_i
211
-
205
+
212
206
  i = if i < 0
213
207
  i
214
208
  elsif is_prime
@@ -217,16 +211,16 @@ module MoneyTree
217
211
  i & 0x7fffffff
218
212
  end
219
213
  end
220
-
214
+
221
215
  def strip_private_info!
222
216
  @private_key = nil
223
217
  end
224
-
218
+
225
219
  def chain_code_hex
226
220
  int_to_hex chain_code, 64
227
221
  end
228
222
  end
229
-
223
+
230
224
  class Master < Node
231
225
  module SeedGeneration
232
226
  class Failure < Exception; end
@@ -236,18 +230,16 @@ module MoneyTree
236
230
  class ImportError < Failure; end
237
231
  class TooManyAttempts < Failure; end
238
232
  end
239
-
233
+
240
234
  HD_WALLET_BASE_KEY = "Bitcoin seed"
241
235
  RANDOM_SEED_SIZE = 32
242
-
236
+
243
237
  attr_reader :seed, :seed_hash
244
-
238
+
245
239
  def initialize(opts = {})
246
240
  @depth = 0
247
241
  @index = 0
248
242
  opts[:seed] = [opts[:seed_hex]].pack("H*") if opts[:seed_hex]
249
- @network_key = opts[:network] || :bitcoin
250
- @network = MoneyTree::NETWORKS[network_key]
251
243
  if opts[:seed]
252
244
  @seed = opts[:seed]
253
245
  @seed_hash = generate_seed_hash(@seed)
@@ -258,14 +250,12 @@ module MoneyTree
258
250
  @chain_code = opts[:chain_code]
259
251
  if opts[:private_key]
260
252
  @private_key = opts[:private_key]
261
- @network_key = @private_key.network_key
262
- @network = MoneyTree::NETWORKS[network_key]
263
253
  @public_key = MoneyTree::PublicKey.new @private_key
264
254
  else opts[:public_key]
265
255
  @public_key = if opts[:public_key].is_a?(MoneyTree::PublicKey)
266
256
  opts[:public_key]
267
257
  else
268
- MoneyTree::PublicKey.new(opts[:public_key], network: network_key)
258
+ MoneyTree::PublicKey.new(opts[:public_key])
269
259
  end
270
260
  end
271
261
  else
@@ -273,29 +263,29 @@ module MoneyTree
273
263
  set_seeded_keys
274
264
  end
275
265
  end
276
-
266
+
277
267
  def is_private?
278
268
  true
279
269
  end
280
-
270
+
281
271
  def generate_seed
282
272
  @seed = OpenSSL::Random.random_bytes(32)
283
273
  @seed_hash = generate_seed_hash(@seed)
284
274
  raise SeedGeneration::ValidityError unless seed_valid?(@seed_hash)
285
275
  end
286
-
276
+
287
277
  def generate_seed_hash(seed)
288
278
  hmac_sha512 HD_WALLET_BASE_KEY, seed
289
279
  end
290
-
280
+
291
281
  def seed_valid?(seed_hash)
292
282
  return false unless seed_hash.bytesize == 64
293
283
  master_key = left_from_hash(seed_hash)
294
284
  !master_key.zero? && master_key < MoneyTree::Key::ORDER
295
285
  end
296
-
286
+
297
287
  def set_seeded_keys
298
- @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash), network: network_key
288
+ @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash)
299
289
  @chain_code = right_from_hash(seed_hash)
300
290
  @public_key = MoneyTree::PublicKey.new @private_key
301
291
  end
@@ -1,3 +1,3 @@
1
1
  module MoneyTree
2
- VERSION = "0.8.9"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -40,6 +40,12 @@ module MoneyTree
40
40
  EC_POINT_clear_free(point_0_pt)
41
41
  EC_POINT_clear_free(point_1_pt)
42
42
 
43
+ eckey = nil
44
+ group = nil
45
+ sum_point = nil
46
+ point_0_pt = nil
47
+ point_1_pt = nil
48
+
43
49
  hex
44
50
  end
45
51
 
@@ -18,6 +18,15 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ # used with gem i coin-op -P HighSecurity
22
+ spec.cert_chain = ["certs/jvergeldedios.pem"]
23
+ # Sign gem when evaluating spec with `gem` command
24
+ # unless ENV has set a SKIP_GEM_SIGNING
25
+ if ($0 =~ /gem\z/) and not ENV.include?("SKIP_GEM_SIGNING")
26
+ spec.signing_key = File.join(Gem.user_home, ".ssh", "gem-private_key.pem")
27
+ end
28
+
29
+
21
30
  spec.add_dependency "ffi"
22
31
 
23
32
  spec.add_development_dependency "bundler", "~> 1.3"
@@ -56,7 +56,7 @@ describe MoneyTree::Address do
56
56
  end
57
57
 
58
58
  it "returns a testnet address" do
59
- expect(%w(m n)).to include(@address.to_s[0])
59
+ expect(%w(m n)).to include(@address.to_s(network: :bitcoin_testnet)[0])
60
60
  end
61
61
  end
62
62
  end
@@ -24,49 +24,49 @@ describe MoneyTree::Master do
24
24
  end
25
25
 
26
26
  it "generates testnet address" do
27
- expect(%w(m n)).to include(@master.to_address[0])
27
+ expect(%w(m n)).to include(@master.to_address(network: :bitcoin_testnet)[0])
28
28
  end
29
29
 
30
30
  it "generates testnet compressed wif" do
31
- expect(@master.private_key.to_wif[0]).to eql('c')
31
+ expect(@master.private_key.to_wif(network: :bitcoin_testnet)[0]).to eql('c')
32
32
  end
33
33
 
34
34
  it "generates testnet uncompressed wif" do
35
- expect(@master.private_key.to_wif(compressed: false)[0]).to eql('9')
35
+ expect(@master.private_key.to_wif(compressed: false, network: :bitcoin_testnet)[0]).to eql('9')
36
36
  end
37
37
 
38
38
  it "generates testnet serialized private address" do
39
- expect(@master.to_serialized_address(:private).slice(0, 4)).to eql("tprv")
39
+ expect(@master.to_serialized_address(:private, network: :bitcoin_testnet).slice(0, 4)).to eql("tprv")
40
40
  end
41
41
 
42
42
  it "generates testnet serialized public address" do
43
- expect(@master.to_serialized_address.slice(0, 4)).to eql("tpub")
43
+ expect(@master.to_serialized_address(network: :bitcoin_testnet).slice(0, 4)).to eql("tpub")
44
44
  end
45
45
 
46
46
  it "imports from testnet serialized private address" do
47
47
  node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE'
48
- expect(node.to_serialized_address(:private)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE')
48
+ expect(node.to_serialized_address(:private, network: :bitcoin_testnet)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE')
49
49
  end
50
50
 
51
51
  it "imports from testnet serialized public address" do
52
52
  node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT'
53
- expect(%w(m n)).to include(node.public_key.to_s[0])
54
- expect(node.to_serialized_address).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT')
53
+ expect(%w(m n)).to include(node.public_key.to_s(network: :bitcoin_testnet)[0])
54
+ expect(node.to_serialized_address(network: :bitcoin_testnet)).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT')
55
55
  end
56
56
 
57
57
  it "generates testnet subnodes from serialized private address" do
58
58
  node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE'
59
59
  subnode = node.node_for_path('1/1/1')
60
- expect(%w(m n)).to include(subnode.public_key.to_s[0])
61
- expect(subnode.to_serialized_address(:private).slice(0,4)).to eql('tprv')
62
- expect(subnode.to_serialized_address.slice(0,4)).to eql('tpub')
60
+ expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0])
61
+ expect(subnode.to_serialized_address(:private, network: :bitcoin_testnet).slice(0,4)).to eql('tprv')
62
+ expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub')
63
63
  end
64
64
 
65
65
  it "generates testnet subnodes from serialized public address" do
66
66
  node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT'
67
67
  subnode = node.node_for_path('1/1/1')
68
- expect(%w(m n)).to include(subnode.public_key.to_s[0])
69
- expect(subnode.to_serialized_address.slice(0,4)).to eql('tpub')
68
+ expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0])
69
+ expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub')
70
70
  end
71
71
  end
72
72
 
@@ -799,7 +799,7 @@ describe MoneyTree::Master do
799
799
  it "correctly derives from a node with a chain code represented in 31 bytes" do
800
800
  @node = MoneyTree::Node.from_serialized_address "tpubD6NzVbkrYhZ4WM42MZZmUZ7LjxyjBf5bGjEeLf9nJnMZqocGJWu94drvpqWsE9jE7k3h22v6gjpPGnqgBrqwGsRYwDXVRfQ2M9dfHbXP5zA"
801
801
  @subnode = @node.node_for_path('m/1')
802
- expect(@subnode.to_serialized_address).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY")
802
+ expect(@subnode.to_serialized_address(network: :bitcoin_testnet)).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY")
803
803
  end
804
804
  end
805
805
  end
@@ -45,7 +45,7 @@ describe MoneyTree::PrivateKey do
45
45
  end
46
46
 
47
47
  it "is valid" do
48
- expect(@key.to_wif(compressed: false)).to eql('5JXz5ZyFk31oHVTQxqce7yitCmTAPxBqeGQ4b7H3Aj3L45wUhoa' )
48
+ expect(@key.to_wif(compressed: false)).to eql('5JXz5ZyFk31oHVTQxqce7yitCmTAPxBqeGQ4b7H3Aj3L45wUhoa')
49
49
  end
50
50
  end
51
51
 
@@ -104,7 +104,7 @@ describe MoneyTree::PrivateKey do
104
104
 
105
105
  describe "to_wif" do
106
106
  it "returns same wif" do
107
- expect(@key.to_wif).to eql('cRhes8SBnsF6WizphaRKQKZZfDniDa9Bxcw31yKeEC1KDExhxFgD')
107
+ expect(@key.to_wif(network: :bitcoin_testnet)).to eql('cRhes8SBnsF6WizphaRKQKZZfDniDa9Bxcw31yKeEC1KDExhxFgD')
108
108
  end
109
109
  end
110
110
  end
@@ -161,26 +161,26 @@ describe MoneyTree::PublicKey do
161
161
  context "testnet" do
162
162
  context 'with private key' do
163
163
  before do
164
- @private_key = MoneyTree::PrivateKey.new network: :bitcoin_testnet
164
+ @private_key = MoneyTree::PrivateKey.new
165
165
  @key = MoneyTree::PublicKey.new(@private_key)
166
166
  end
167
167
 
168
168
  it "should have an address starting with m or n" do
169
- expect(%w(m n)).to include(@key.to_s[0])
169
+ expect(%w(m n)).to include(@key.to_s(network: :bitcoin_testnet)[0])
170
170
  end
171
171
 
172
172
  it "should have an uncompressed address starting with m or n" do
173
- expect(%w(m n)).to include(@key.uncompressed.to_s[0])
173
+ expect(%w(m n)).to include(@key.uncompressed.to_s(network: :bitcoin_testnet)[0])
174
174
  end
175
175
  end
176
176
 
177
177
  context 'without private key' do
178
178
  before do
179
- @key = MoneyTree::PublicKey.new('0297b033ba894611345a0e777861237ef1632370fbd58ebe644eb9f3714e8fe2bc', network: :bitcoin_testnet)
179
+ @key = MoneyTree::PublicKey.new('0297b033ba894611345a0e777861237ef1632370fbd58ebe644eb9f3714e8fe2bc')
180
180
  end
181
181
 
182
182
  it "should have an address starting with m or n" do
183
- expect(%w(m n)).to include(@key.to_s[0])
183
+ expect(%w(m n)).to include(@key.to_s(network: :bitcoin_testnet)[0])
184
184
  end
185
185
  end
186
186
  end
@@ -1,2 +1,3 @@
1
1
  require 'simplecov'
2
2
  require 'money-tree'
3
+ require 'pry'
metadata CHANGED
@@ -1,14 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.9
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Winkelspecht
8
8
  autorequire:
9
9
  bindir: bin
10
- cert_chain: []
11
- date: 2015-01-21 00:00:00.000000000 Z
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDXDCCAkSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA6MQ8wDQYDVQQDDAZqdWxp
14
+ YW4xEzARBgoJkiaJk/IsZAEZFgNnZW0xEjAQBgoJkiaJk/IsZAEZFgJjbzAeFw0x
15
+ NTA0MDMwODAwMTdaFw0xNjA0MDIwODAwMTdaMDoxDzANBgNVBAMMBmp1bGlhbjET
16
+ MBEGCgmSJomT8ixkARkWA2dlbTESMBAGCgmSJomT8ixkARkWAmNvMIIBIjANBgkq
17
+ hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0qCsOUQyTRA4f7WxoU2ctpUO5+eLZw8
18
+ ILvHJy/0dC2nnIgib+FaFA8TRIGw6fjX2hQ43QJyO36zkjUhAwNu/+TBCfG+Grut
19
+ 2dI9XmqU5Z6PvvXRj6Gu5IkDeIVDKILZv3bDugHJalre4BUKnwPYv5WpZ/e/c6+z
20
+ E4fwe4ZQzqslSXZo0o/wFvs5dGuIoP93bazSeqddre0JKFFiEP/SNGP9e/lXEd2V
21
+ rLFYAY409no9J+VQOHP0Nu9ShlCZp8M45abKd2ykuSDaT6jH9YcUHBr3/IEsA4+f
22
+ DypeS1ySVvad+o8iTnfz1Hyohz4ORm3spf0BOtGI/Swbv3LObZJqkwIDAQABo20w
23
+ azAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUERhBDW/kkq7tz2hN
24
+ hPNHtounnkowGAYDVR0RBBEwD4ENanVsaWFuQGdlbS5jbzAYBgNVHRIEETAPgQ1q
25
+ dWxpYW5AZ2VtLmNvMA0GCSqGSIb3DQEBBQUAA4IBAQAgnumhg8ST8JohYWcoDoQt
26
+ 3BUG5rbfJ/qE0utOt6esi9d6Vz6YHpiT08woaj68OWl9U9N4vjox+ckkTRs93KBd
27
+ y3thnK9cIEAzoEZs3BBguXYOoFLughGD7hEuLlRYbwZzyIdzx/XdLgsy5Di8Gqaa
28
+ RKurfXP+dERQww34CUhmhOLO4/rYGqaD88so0MzCImgS+OX+G4ppqd38iQpaxCHL
29
+ tdc4VS7IlSRxlZ3dBOgiigy9GXpJ+7F831AqjxL39EPwdr7RguTNz+pi//RKaT/U
30
+ IlpVB+Xfk0vQdP7iYfjGxDzUf0FACMjsR95waJmadKW1Iy6STw2hwPhYIQz1Hu1A
31
+ -----END CERTIFICATE-----
32
+ date: 2015-05-19 00:00:00.000000000 Z
12
33
  dependencies:
13
34
  - !ruby/object:Gem::Dependency
14
35
  name: ffi
Binary file