money-tree 0.11.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 040b86e807e6dea0ebf4dfc4b5e508e3f9ef673c94b95a210a18c125d8857974
4
- data.tar.gz: '01800e0fb32058488fd01f1a8f2be3539788c7d0bebc0021fd49776fdde947a4'
3
+ metadata.gz: a817c56d0f39bc09f84b1dd02d9e828a9b3fd4d93500d7f6e9cd4ead322c7355
4
+ data.tar.gz: 1e8de14764b5ddf911769e537b99eb4b9f3ce2e91e6dff9ffc2cef923214d156
5
5
  SHA512:
6
- metadata.gz: 70a6465799b1f7140a2e45c2ded416b41e4b64a7801b57325ed20cea470c92cad96e7644596d523a192254e7284eb772406e90af0a797f5b367304cbe2e9b725
7
- data.tar.gz: 2d0c6bea3a33f161cf30a3400d3f75b6119098f035eca6cdac076c11c03596a2e0e205456b1e3ea2453f2ad1e58fd7edec91ff21ec1a8fd515c2d7836169762c
6
+ metadata.gz: d2d89e5ffa65f7781a7ff35c830da95614b22e743a24ce5662f4f952dd18685ba9b5a18078fb5bc0a45c1527bb66e32473a5d8453a0a87026e7d5625dbd940d0
7
+ data.tar.gz: 1355fe75f77593dec9c191e9c1d91f4a1f5205fe8e6f01600298311a17c070f25002c8ed0889166125b5879ce9b03675e476f2cf64b5a6f03e19b01886a4506f
@@ -29,3 +29,5 @@ jobs:
29
29
  - name: Run Tests
30
30
  run: |
31
31
  bundle exec rspec
32
+ env:
33
+ COVERAGE: true
data/Gemfile CHANGED
@@ -1,4 +1,16 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in money-tree.gemspec
4
- gemspec
3
+ gem "openssl", "~> 3.0"
4
+ gem "bech32", "~> 1.2"
5
+
6
+ group :test, :development do
7
+ gem "bundler", "~> 2.2"
8
+ gem "codecov", "~> 0.6"
9
+ gem "pry", "~> 0.14"
10
+ gem "rake", "~> 13.0"
11
+ gem "rdoc", "~> 6.3"
12
+ gem "rspec", "~> 3.10"
13
+ gem "rufo", "~> 0.13"
14
+ gem "simplecov", "~> 0.21"
15
+ gem "yard", "~> 0.9"
16
+ end
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![GitHub top language](https://img.shields.io/github/languages/top/GemHQ/money-tree?color=red)](https://github.com/GemHQ/money-tree/pulse)
7
7
 
8
8
  [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/GemHQ/money-tree/Spec)](https://github.com/GemHQ/money-tree/actions)
9
- [![Coverage Status](https://img.shields.io/coveralls/GemHQ/money-tree.svg)](https://coveralls.io/r/GemHQ/money-tree?branch=master)
9
+ [![Code Coverage](https://codecov.io/gh/GemHQ/money-tree/branch/master/graph/badge.svg?token=PF4BL36Z9q)](https://codecov.io/gh/GemHQ/money-tree)
10
10
  [![Code Climate](https://codeclimate.com/github/GemHQ/money-tree.png)](https://codeclimate.com/github/GemHQ/money-tree)
11
11
  [![GitHub](https://img.shields.io/github/license/GemHQ/money-tree)](LICENSE)
12
12
 
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
- require 'rspec/core/rake_task'
2
+ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
@@ -0,0 +1 @@
1
+ cc59abeb70c300eb32c0562dea582de4d26da71cb3fac9937fb3b88fd7092177c98ccbdf6eba7517c3dbc14b33f15e95bf799a02adfec5c12583cb9179c68bb7
@@ -1 +1 @@
1
- ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9
1
+ ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9
@@ -1,16 +1,26 @@
1
1
  module MoneyTree
2
2
  class Address
3
- attr_reader :private_key, :public_key
3
+ attr_reader :private_key
4
+ attr_reader :public_key
4
5
 
5
6
  def initialize(opts = {})
6
7
  private_key = opts.delete(:private_key)
7
- @private_key = MoneyTree::PrivateKey.new({ key: private_key }.merge(opts))
8
- @public_key = MoneyTree::PublicKey.new(@private_key, opts)
8
+ @private_key = PrivateKey.new({ key: private_key }.merge(opts))
9
+ @public_key = PublicKey.new(@private_key, opts)
9
10
  end
10
11
 
11
12
  def to_s(network: :bitcoin)
12
- public_key.to_s(network: network)
13
+ @public_key.to_s(network: network)
13
14
  end
14
15
 
16
+ def to_bech32(network: :bitcoin)
17
+ hrp = NETWORKS[network][:human_readable_part]
18
+ witprog = @public_key.to_ripemd160
19
+ Support.to_serialized_bech32(hrp, witprog)
20
+ end
21
+
22
+ def to_p2wpkh_p2sh(network: :bitcoin)
23
+ @public_key.to_p2wpkh_p2sh(network: network)
24
+ end
15
25
  end
16
26
  end
@@ -1,12 +1,11 @@
1
1
  # encoding ascii-8bit
2
2
 
3
- require 'openssl'
3
+ require "openssl"
4
4
 
5
5
  module MoneyTree
6
6
  class Key
7
- include OpenSSL
8
7
  include Support
9
- extend Support
8
+
10
9
  class KeyInvalid < StandardError; end
11
10
  class KeyGenerationFailure < StandardError; end
12
11
  class KeyImportFailure < StandardError; end
@@ -14,10 +13,13 @@ module MoneyTree
14
13
  class InvalidWIFFormat < StandardError; end
15
14
  class InvalidBase64Format < StandardError; end
16
15
 
17
- attr_reader :options, :key, :raw_key
16
+ attr_reader :options
17
+ attr_reader :key
18
+ attr_reader :raw_key
19
+
18
20
  attr_accessor :ec_key
19
21
 
20
- GROUP_NAME = 'secp256k1'
22
+ GROUP_NAME = "secp256k1"
21
23
  ORDER = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".to_i(16)
22
24
 
23
25
  def valid?(eckey = nil)
@@ -35,7 +37,6 @@ module MoneyTree
35
37
  end
36
38
 
37
39
  class PrivateKey < Key
38
-
39
40
  def initialize(opts = {})
40
41
  @options = opts
41
42
  @ec_key = PKey::EC.new GROUP_NAME
@@ -70,14 +71,9 @@ module MoneyTree
70
71
  end
71
72
 
72
73
  def parse_raw_key
73
- result = if raw_key.is_a?(Integer) then from_integer
74
- elsif hex_format? then from_hex
75
- elsif base64_format? then from_base64
76
- elsif compressed_wif_format? then from_wif
77
- elsif uncompressed_wif_format? then from_wif
78
- else
79
- raise KeyFormatNotFound
80
- end
74
+ result = if raw_key.is_a?(Integer) then from_integer elsif hex_format? then from_hex elsif base64_format? then from_base64 elsif compressed_wif_format? then from_wif elsif uncompressed_wif_format? then from_wif else
75
+ raise KeyFormatNotFound
76
+ end
81
77
  result.downcase
82
78
  end
83
79
 
@@ -113,7 +109,7 @@ module MoneyTree
113
109
 
114
110
  def wif_format?(compression)
115
111
  length = compression == :compressed ? 52 : 51
116
- wif_prefixes = MoneyTree::NETWORKS.map {|k, v| v["#{compression}_wif_chars".to_sym]}.flatten
112
+ wif_prefixes = MoneyTree::NETWORKS.map { |k, v| v["#{compression}_wif_chars".to_sym] }.flatten
117
113
  raw_key.length == length && wif_prefixes.include?(raw_key.slice(0))
118
114
  end
119
115
 
@@ -160,7 +156,6 @@ module MoneyTree
160
156
  def to_s(network: :bitcoin)
161
157
  to_wif(network: network)
162
158
  end
163
-
164
159
  end
165
160
 
166
161
  class PublicKey < Key
@@ -209,19 +204,19 @@ module MoneyTree
209
204
  self.compression = opts[:compressed] ? :compressed : :uncompressed
210
205
  bn = BN.new int_to_hex(int), 16
211
206
  @point = PKey::EC::Point.new group, bn
212
- raise KeyInvalid, 'point is not on the curve' unless @point.on_curve?
207
+ raise KeyInvalid, "point is not on the curve" unless @point.on_curve?
213
208
  end
214
209
 
215
210
  def parse_raw_key
216
211
  result = if raw_key.is_a?(Integer)
217
- set_point raw_key
218
- elsif hex_format?
219
- set_point hex_to_int(raw_key), compressed: false
220
- elsif compressed_hex_format?
221
- set_point hex_to_int(raw_key), compressed: true
222
- else
223
- raise KeyFormatNotFound
224
- end
212
+ set_point raw_key
213
+ elsif hex_format?
214
+ set_point hex_to_int(raw_key), compressed: false
215
+ elsif compressed_hex_format?
216
+ set_point hex_to_int(raw_key), compressed: true
217
+ else
218
+ raise KeyFormatNotFound
219
+ end
225
220
  to_hex
226
221
  end
227
222
 
@@ -251,8 +246,20 @@ module MoneyTree
251
246
  address = NETWORKS[network][:address_version] + hash
252
247
  to_serialized_base58 address
253
248
  end
249
+
254
250
  alias :to_s :to_address
255
251
 
252
+ def to_p2wpkh_p2sh(network: :bitcoin)
253
+ prefix = [NETWORKS[network][:p2sh_version]].pack("H*")
254
+ convert_p2wpkh_p2sh(to_hex, prefix)
255
+ end
256
+
257
+ def to_bech32_address(network: :bitcoin)
258
+ hrp = NETWORKS[network][:human_readable_part]
259
+ witprog = to_ripemd160
260
+ to_serialized_bech32(hrp, witprog)
261
+ end
262
+
256
263
  def to_fingerprint
257
264
  hash = to_ripemd160
258
265
  hash.slice(0..7)
@@ -1,33 +1,34 @@
1
1
  module MoneyTree
2
- NETWORKS =
3
- begin
2
+ NETWORKS = begin
4
3
  hsh = Hash.new do |_, key|
5
4
  raise "#{key} is not a valid network!"
6
5
  end.merge(
7
6
  bitcoin: {
8
- address_version: '00',
9
- p2sh_version: '05',
10
- p2sh_char: '3',
11
- privkey_version: '80',
12
- privkey_compression_flag: '01',
7
+ address_version: "00",
8
+ p2sh_version: "05",
9
+ p2sh_char: "3",
10
+ privkey_version: "80",
11
+ privkey_compression_flag: "01",
13
12
  extended_privkey_version: "0488ade4",
14
13
  extended_pubkey_version: "0488b21e",
15
14
  compressed_wif_chars: %w(K L),
16
15
  uncompressed_wif_chars: %w(5),
17
- protocol_version: 70001
16
+ protocol_version: 70001,
17
+ human_readable_part: "bc",
18
18
  },
19
19
  bitcoin_testnet: {
20
- address_version: '6f',
21
- p2sh_version: 'c4',
22
- p2sh_char: '2',
23
- privkey_version: 'ef',
24
- privkey_compression_flag: '01',
20
+ address_version: "6f",
21
+ p2sh_version: "c4",
22
+ p2sh_char: "2",
23
+ privkey_version: "ef",
24
+ privkey_compression_flag: "01",
25
25
  extended_privkey_version: "04358394",
26
26
  extended_pubkey_version: "043587cf",
27
27
  compressed_wif_chars: %w(c),
28
28
  uncompressed_wif_chars: %w(9),
29
- protocol_version: 70001
30
- }
29
+ protocol_version: 70001,
30
+ human_readable_part: "tb",
31
+ },
31
32
  )
32
33
  hsh[:testnet3] = hsh[:bitcoin_testnet]
33
34
  hsh
@@ -2,8 +2,14 @@ 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
5
+
6
+ attr_reader :private_key
7
+ attr_reader :public_key
8
+ attr_reader :chain_code
9
+ attr_reader :is_private
10
+ attr_reader :depth
11
+ attr_reader :index
12
+ attr_reader :parent
7
13
 
8
14
  class PublicDerivationFailure < StandardError; end
9
15
  class InvalidKeyForIndex < StandardError; end
@@ -21,26 +27,21 @@ module MoneyTree
21
27
  depth: hex.slice!(0..1).to_i(16),
22
28
  parent_fingerprint: hex.slice!(0..7),
23
29
  index: hex.slice!(0..7).to_i(16),
24
- chain_code: hex.slice!(0..63).to_i(16)
30
+ chain_code: hex.slice!(0..63).to_i(16),
25
31
  }.merge(parse_out_key(hex)))
26
32
  end
27
33
 
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
34
  def self.parse_out_key(hex)
34
- if hex.slice(0..1) == '00'
35
+ if hex.slice(0..1) == "00"
35
36
  private_key = MoneyTree::PrivateKey.new(key: hex.slice(2..-1))
36
37
  {
37
38
  private_key: private_key,
38
- public_key: MoneyTree::PublicKey.new(private_key)
39
+ public_key: MoneyTree::PublicKey.new(private_key),
39
40
  }
40
41
  elsif %w(02 03).include? hex.slice(0..1)
41
42
  { public_key: MoneyTree::PublicKey.new(hex) }
42
43
  else
43
- raise ImportError, 'Public or private key data does not match version type'
44
+ raise ImportError, "Public or private key data does not match version type"
44
45
  end
45
46
  end
46
47
 
@@ -50,7 +51,7 @@ module MoneyTree
50
51
 
51
52
  def index_hex(i = index)
52
53
  if i < 0
53
- [i].pack('l>').unpack('H*').first
54
+ [i].pack("l>").unpack("H*").first
54
55
  else
55
56
  i.to_s(16).rjust(8, "0")
56
57
  end
@@ -69,16 +70,16 @@ module MoneyTree
69
70
  end
70
71
 
71
72
  def i_as_bytes(i)
72
- [i].pack('N')
73
+ [i].pack("N")
73
74
  end
74
75
 
75
76
  def derive_private_key(i = 0)
76
77
  message = i >= 0x80000000 || i < 0 ? private_derivation_message(i) : public_derivation_message(i)
77
78
  hash = hmac_sha512 hex_to_bytes(chain_code_hex), message
78
79
  left_int = left_from_hash(hash)
79
- raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability
80
+ raise InvalidKeyForIndex, "greater than or equal to order" if left_int >= MoneyTree::Key::ORDER # very low probability
80
81
  child_private_key = (left_int + private_key.to_i) % MoneyTree::Key::ORDER
81
- raise InvalidKeyForIndex, 'equal to zero' if child_private_key == 0 # very low probability
82
+ raise InvalidKeyForIndex, "equal to zero" if child_private_key == 0 # very low probability
82
83
  child_chain_code = right_from_hash(hash)
83
84
  return child_private_key, child_chain_code
84
85
  end
@@ -88,7 +89,7 @@ module MoneyTree
88
89
  message = public_derivation_message(i)
89
90
  hash = hmac_sha512 hex_to_bytes(chain_code_hex), message
90
91
  left_int = left_from_hash(hash)
91
- raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability
92
+ raise InvalidKeyForIndex, "greater than or equal to order" if left_int >= MoneyTree::Key::ORDER # very low probability
92
93
  factor = BN.new left_int.to_s
93
94
 
94
95
  gen_point = public_key.uncompressed.group.generator.mul(factor)
@@ -96,7 +97,7 @@ module MoneyTree
96
97
  sum_point_hex = MoneyTree::OpenSSLExtensions.add(gen_point, public_key.uncompressed.point)
97
98
  child_public_key = OpenSSL::PKey::EC::Point.new(public_key.group, OpenSSL::BN.new(sum_point_hex, 16)).to_bn.to_i
98
99
 
99
- raise InvalidKeyForIndex, 'at infinity' if child_public_key == 1/0.0 # very low probability
100
+ raise InvalidKeyForIndex, "at infinity" if child_public_key == 1 / 0.0 # very low probability
100
101
  child_chain_code = right_from_hash(hash)
101
102
  return child_public_key, child_chain_code
102
103
  end
@@ -125,12 +126,7 @@ module MoneyTree
125
126
  to_serialized_base58 to_serialized_hex(type, network: network)
126
127
  end
127
128
 
128
- def to_serialized_address(type = :public, network: :bitcoin)
129
- puts 'Node.to_serialized_address is DEPRECATED. Please use .to_bip32.'
130
- to_bip32(type, network: network)
131
- end
132
-
133
- def to_identifier(compressed=true)
129
+ def to_identifier(compressed = true)
134
130
  key = compressed ? public_key.compressed : public_key.uncompressed
135
131
  key.to_ripemd160
136
132
  end
@@ -143,15 +139,25 @@ module MoneyTree
143
139
  if @parent_fingerprint
144
140
  @parent_fingerprint
145
141
  else
146
- depth.zero? ? '00000000' : parent.to_fingerprint
142
+ depth.zero? ? "00000000" : parent.to_fingerprint
147
143
  end
148
144
  end
149
145
 
150
- def to_address(compressed=true, network: :bitcoin)
146
+ def to_address(compressed = true, network: :bitcoin)
151
147
  address = NETWORKS[network][:address_version] + to_identifier(compressed)
152
148
  to_serialized_base58 address
153
149
  end
154
150
 
151
+ def to_p2wpkh_p2sh(network: :bitcoin)
152
+ public_key.to_p2wpkh_p2sh(network: network)
153
+ end
154
+
155
+ def to_bech32_address(network: :bitcoin)
156
+ hrp = NETWORKS[network][:human_readable_part]
157
+ witprog = to_identifier
158
+ to_serialized_bech32(hrp, witprog)
159
+ end
160
+
155
161
  def subnode(i = 0, opts = {})
156
162
  if private_key.nil?
157
163
  child_public_key, child_chain_code = derive_public_key(i)
@@ -162,7 +168,7 @@ module MoneyTree
162
168
  child_public_key = MoneyTree::PublicKey.new child_private_key
163
169
  end
164
170
 
165
- MoneyTree::Node.new( depth: depth+1,
171
+ MoneyTree::Node.new(depth: depth + 1,
166
172
  index: i,
167
173
  private_key: private_key.nil? ? nil : child_private_key,
168
174
  public_key: child_public_key,
@@ -182,9 +188,9 @@ module MoneyTree
182
188
  #
183
189
  # You should choose either the p or the negative number convention for private key derivation.
184
190
  def node_for_path(path)
185
- force_public = path[-4..-1] == '.pub'
191
+ force_public = path[-4..-1] == ".pub"
186
192
  path = path[0..-5] if force_public
187
- parts = path.split('/')
193
+ parts = path.split("/")
188
194
  nodes = []
189
195
  parts.each_with_index do |part, depth|
190
196
  if part =~ /m/i
@@ -195,7 +201,7 @@ module MoneyTree
195
201
  nodes << node.subnode(i)
196
202
  end
197
203
  end
198
- if force_public or parts.first == 'M'
204
+ if force_public or parts.first == "M"
199
205
  node = nodes.last
200
206
  node.strip_private_info!
201
207
  node
@@ -209,12 +215,12 @@ module MoneyTree
209
215
  i = path_part.to_i
210
216
 
211
217
  i = if i < 0
212
- i
213
- elsif is_prime
214
- i | 0x80000000
215
- else
216
- i & 0x7fffffff
217
- end
218
+ i
219
+ elsif is_prime
220
+ i | 0x80000000
221
+ else
222
+ i & 0x7fffffff
223
+ end
218
224
  end
219
225
 
220
226
  def strip_private_info!
@@ -251,7 +257,7 @@ module MoneyTree
251
257
  raise SeedGeneration::ImportError unless seed_valid?(@seed_hash)
252
258
  set_seeded_keys
253
259
  elsif opts[:private_key] || opts[:public_key]
254
- raise ImportError, 'chain code required' unless opts[:chain_code]
260
+ raise ImportError, "chain code required" unless opts[:chain_code]
255
261
  @chain_code = opts[:chain_code]
256
262
  if opts[:private_key]
257
263
  @private_key = opts[:private_key]
@@ -261,8 +267,7 @@ module MoneyTree
261
267
  opts[:public_key]
262
268
  else
263
269
  MoneyTree::PublicKey.new(opts[:public_key])
264
- end
265
- end
270
+ end end
266
271
  else
267
272
  generate_seed
268
273
  set_seeded_keys
@@ -1,16 +1,18 @@
1
- require 'openssl'
2
- require 'base64'
1
+ require "base64"
2
+ require "bech32"
3
+ require "openssl"
3
4
 
4
5
  module MoneyTree
5
6
  module Support
6
7
  include OpenSSL
8
+ extend self
7
9
 
8
10
  INT32_MAX = 256 ** [1].pack("L*").size
9
11
  INT64_MAX = 256 ** [1].pack("Q*").size
10
12
  BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
11
13
 
12
- def int_to_base58(int_val, leading_zero_bytes=0)
13
- base58_val, base = '', BASE58_CHARS.size
14
+ def int_to_base58(int_val, leading_zero_bytes = 0)
15
+ base58_val, base = "", BASE58_CHARS.size
14
16
  while int_val > 0
15
17
  int_val, remainder = int_val.divmod(base)
16
18
  base58_val = BASE58_CHARS[remainder] + base58_val
@@ -20,25 +22,26 @@ module MoneyTree
20
22
 
21
23
  def base58_to_int(base58_val)
22
24
  int_val, base = 0, BASE58_CHARS.size
23
- base58_val.reverse.each_char.with_index do |char,index|
24
- raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = BASE58_CHARS.index(char)
25
- int_val += char_index*(base**index)
25
+ base58_val.reverse.each_char.with_index do |char, index|
26
+ raise ArgumentError, "Value not a valid Base58 String." unless char_index = BASE58_CHARS.index(char)
27
+ int_val += char_index * (base ** index)
26
28
  end
27
29
  int_val
28
30
  end
29
31
 
30
32
  def encode_base58(hex)
31
- leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
32
- ("1"*leading_zero_bytes) + int_to_base58( hex.to_i(16) )
33
+ leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : "").size / 2
34
+ ("1" * leading_zero_bytes) + int_to_base58(hex.to_i(16))
33
35
  end
34
36
 
35
37
  def decode_base58(base58_val)
36
- s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
37
- s = '' if s == '00'
38
- leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
39
- s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
38
+ s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? "0" + s : s)
39
+ s = "" if s == "00"
40
+ leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : "").size
41
+ s = ("00" * leading_zero_bytes) + s if leading_zero_bytes > 0
40
42
  s
41
43
  end
44
+
42
45
  alias_method :base58_to_hex, :decode_base58
43
46
 
44
47
  def to_serialized_base58(hex)
@@ -49,6 +52,13 @@ module MoneyTree
49
52
  encode_base58 address
50
53
  end
51
54
 
55
+ def to_serialized_bech32(human_readable_part, hex)
56
+ segwit_addr = Bech32::SegwitAddr.new
57
+ segwit_addr.hrp = human_readable_part
58
+ segwit_addr.script_pubkey = "0000" + hex
59
+ segwit_addr.addr
60
+ end
61
+
52
62
  def from_serialized_base58(base58)
53
63
  hex = decode_base58 base58
54
64
  checksum = hex.slice!(-8..-1)
@@ -63,11 +73,11 @@ module MoneyTree
63
73
  end
64
74
 
65
75
  def sha256(source, opts = {})
66
- digestify('SHA256', source, opts)
76
+ digestify("SHA256", source, opts)
67
77
  end
68
78
 
69
79
  def ripemd160(source, opts = {})
70
- digestify('RIPEMD160', source, opts)
80
+ digestify("RIPEMD160", source, opts)
71
81
  end
72
82
 
73
83
  def encode_base64(hex)
@@ -85,7 +95,7 @@ module MoneyTree
85
95
 
86
96
  def hmac_sha512_hex(key, message)
87
97
  md = hmac_sha512(key, message)
88
- md.unpack("H*").first.rjust(64, '0')
98
+ md.unpack("H*").first.rjust(64, "0")
89
99
  end
90
100
 
91
101
  def bytes_to_int(bytes, base = 16)
@@ -95,7 +105,7 @@ module MoneyTree
95
105
  bytes.unpack("H*")[0].to_i(16)
96
106
  end
97
107
 
98
- def int_to_hex(i, size=nil)
108
+ def int_to_hex(i, size = nil)
99
109
  hex = i.to_s(16).downcase
100
110
  if (hex.size % 2) != 0
101
111
  hex = "#{0}#{hex}"
@@ -123,5 +133,20 @@ module MoneyTree
123
133
  def hex_to_int(hex)
124
134
  hex.to_i(16)
125
135
  end
136
+
137
+ def encode_p2wpkh_p2sh(value)
138
+ chk = [Digest::SHA256.hexdigest(Digest::SHA256.digest(value))].pack("H*")[0...4]
139
+ encode_base58 (value + chk).unpack("H*")[0]
140
+ end
141
+
142
+ def custom_hash_160(value)
143
+ [OpenSSL::Digest::RIPEMD160.hexdigest(Digest::SHA256.digest(value))].pack("H*")
144
+ end
145
+
146
+ def convert_p2wpkh_p2sh(key_hex, prefix)
147
+ push_20 = ["0014"].pack("H*")
148
+ script_sig = push_20 + custom_hash_160([key_hex].pack("H*"))
149
+ encode_p2wpkh_p2sh(prefix + custom_hash_160(script_sig))
150
+ end
126
151
  end
127
152
  end
@@ -1,3 +1,3 @@
1
1
  module MoneyTree
2
- VERSION = "0.11.0"
2
+ VERSION = "0.11.1"
3
3
  end
data/lib/money-tree.rb CHANGED
@@ -1,11 +1,11 @@
1
- require "openssl_extensions"
2
- require "money-tree/version"
3
1
  require "money-tree/support"
4
- require "money-tree/networks"
5
- require "money-tree/key"
2
+
6
3
  require "money-tree/address"
4
+ require "money-tree/key"
7
5
  require "money-tree/networks"
8
6
  require "money-tree/node"
7
+ require "money-tree/version"
8
+
9
+ require "openssl_extensions"
9
10
 
10
- module MoneyTree
11
- end
11
+ module MoneyTree; end
@@ -1,12 +1,14 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- require 'openssl'
3
+ require "openssl"
4
4
 
5
5
  module MoneyTree
6
6
  module OpenSSLExtensions
7
- def self.add(point_0, point_1)
7
+ extend self
8
+
9
+ def add(point_0, point_1)
8
10
  validate_points(point_0, point_1)
9
- group = OpenSSL::PKey::EC::Group.new('secp256k1')
11
+ group = OpenSSL::PKey::EC::Group.new("secp256k1")
10
12
  point_0_hex = point_0.to_bn.to_s(16)
11
13
  point_0_pt = OpenSSL::PKey::EC::Point.new(group, OpenSSL::BN.new(point_0_hex, 16))
12
14
  point_1_hex = point_1.to_bn.to_s(16)
@@ -15,7 +17,7 @@ module MoneyTree
15
17
  sum_point.to_bn.to_s(16)
16
18
  end
17
19
 
18
- def self.validate_points(*points)
20
+ def validate_points(*points)
19
21
  points.each do |point|
20
22
  if !point.is_a?(OpenSSL::PKey::EC::Point)
21
23
  raise ArgumentError, "point must be an OpenSSL::PKey::EC::Point object"