money-tree 0.11.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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"