money-tree 0.10.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{donation_btc_qr_code.gif → .github/donation_btc_qr_code.gif} +0 -0
- data/.github/workflows/spec.yml +33 -0
- data/Gemfile +15 -3
- data/README.md +14 -4
- data/Rakefile +2 -2
- data/checksum/money-tree-0.11.0.gem.sha512 +1 -0
- data/checksum/money-tree-0.9.0.gem.sha512 +1 -1
- data/lib/money-tree/address.rb +16 -6
- data/lib/money-tree/key.rb +32 -25
- data/lib/money-tree/networks.rb +16 -15
- data/lib/money-tree/node.rb +49 -39
- data/lib/money-tree/support.rb +59 -34
- data/lib/money-tree/version.rb +1 -1
- data/lib/money-tree.rb +6 -7
- data/lib/openssl_extensions.rb +11 -54
- data/money-tree.gemspec +22 -30
- data/spec/{lib/money-tree → money-tree}/address_spec.rb +42 -8
- data/spec/money-tree/money_tree_spec.rb +9 -0
- data/spec/{lib/money-tree → money-tree}/node_spec.rb +425 -15
- data/spec/money-tree/openssl_extensions_spec.rb +71 -0
- data/spec/{lib/money-tree → money-tree}/private_key_spec.rb +27 -27
- data/spec/{lib/money-tree → money-tree}/public_key_spec.rb +81 -40
- data/spec/{lib/money-tree → money-tree}/support_spec.rb +4 -4
- data/spec/spec_helper.rb +15 -3
- metadata +37 -150
- checksums.yaml.gz.sig +0 -0
- data/.simplecov +0 -7
- data/.travis.yml +0 -3
- data/spec/lib/money-tree/openssl_extensions_spec.rb +0 -23
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a817c56d0f39bc09f84b1dd02d9e828a9b3fd4d93500d7f6e9cd4ead322c7355
|
4
|
+
data.tar.gz: 1e8de14764b5ddf911769e537b99eb4b9f3ce2e91e6dff9ffc2cef923214d156
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2d89e5ffa65f7781a7ff35c830da95614b22e743a24ce5662f4f952dd18685ba9b5a18078fb5bc0a45c1527bb66e32473a5d8453a0a87026e7d5625dbd940d0
|
7
|
+
data.tar.gz: 1355fe75f77593dec9c191e9c1d91f4a1f5205fe8e6f01600298311a17c070f25002c8ed0889166125b5879ce9b03675e476f2cf64b5a6f03e19b01886a4506f
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
name: Spec
|
3
|
+
|
4
|
+
on:
|
5
|
+
pull_request:
|
6
|
+
branches:
|
7
|
+
- master
|
8
|
+
push:
|
9
|
+
branches:
|
10
|
+
- master
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
test:
|
14
|
+
runs-on: ${{ matrix.os }}
|
15
|
+
strategy:
|
16
|
+
fail-fast: false
|
17
|
+
matrix:
|
18
|
+
os: [ubuntu-latest, macos-latest]
|
19
|
+
ruby: ['2.7', '3.0']
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v2
|
22
|
+
- uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: false
|
26
|
+
- name: Install Dependencies
|
27
|
+
run: |
|
28
|
+
bundle install
|
29
|
+
- name: Run Tests
|
30
|
+
run: |
|
31
|
+
bundle exec rspec
|
32
|
+
env:
|
33
|
+
COVERAGE: true
|
data/Gemfile
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
source
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
|
-
|
4
|
-
|
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
@@ -1,13 +1,23 @@
|
|
1
|
-
[![Build Status](https://travis-ci.org/GemHQ/money-tree.png)](https://travis-ci.org/GemHQ/money-tree) [![Coverage Status](https://img.shields.io/coveralls/GemHQ/money-tree.svg)](https://coveralls.io/r/GemHQ/money-tree?branch=master) [![Code Climate](https://codeclimate.com/github/GemHQ/money-tree.png)](https://codeclimate.com/github/GemHQ/money-tree) [![Gem Version](https://badge.fury.io/rb/money-tree.png)](http://badge.fury.io/rb/money-tree)
|
2
1
|
# MoneyTree
|
2
|
+
|
3
|
+
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/GemHQ/money-tree)](https://github.com/GemHQ/money-tree/releases)
|
4
|
+
[![Gem](https://img.shields.io/gem/v/money-tree)](https://rubygems.org/gems/money-tree)
|
5
|
+
[![Gem](https://img.shields.io/gem/dt/money-tree)](https://rubygems.org/gems/money-tree)
|
6
|
+
[![GitHub top language](https://img.shields.io/github/languages/top/GemHQ/money-tree?color=red)](https://github.com/GemHQ/money-tree/pulse)
|
7
|
+
|
8
|
+
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/GemHQ/money-tree/Spec)](https://github.com/GemHQ/money-tree/actions)
|
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
|
+
[![Code Climate](https://codeclimate.com/github/GemHQ/money-tree.png)](https://codeclimate.com/github/GemHQ/money-tree)
|
11
|
+
[![GitHub](https://img.shields.io/github/license/GemHQ/money-tree)](LICENSE)
|
12
|
+
|
3
13
|
### RSpec tested. Big Brother removed.
|
4
14
|
|
5
15
|
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://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki).
|
6
16
|
|
7
17
|
___
|
8
|
-
If you find this helpful, please consider a small Bitcoin donation to 1nj2kie1hATcFbAaD7dEY53QaxNgt4KBp
|
18
|
+
If you find this helpful, please consider a small Bitcoin donation to `1nj2kie1hATcFbAaD7dEY53QaxNgt4KBp`.
|
9
19
|
|
10
|
-
![Donate BTC](
|
20
|
+
![Donate BTC](./.github/donation_btc_qr_code.gif)
|
11
21
|
___
|
12
22
|
|
13
23
|
## Why would I want an HD Wallet?
|
@@ -163,7 +173,7 @@ Because we need multiple pieces of info to reconstruct nodes in a tree, when we'
|
|
163
173
|
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
|
164
174
|
```
|
165
175
|
|
166
|
-
In addition to the key and the chain code, this encoding also includes info about the depth and index of the key, along with a fingerprint of its parent key (which I presume is for quickly sorting a big pile of keys into a tree).
|
176
|
+
In addition to the key and the chain code, this encoding also includes info about the depth and index of the key, along with a fingerprint of its parent key (which I presume is for quickly sorting a big pile of keys into a tree).
|
167
177
|
|
168
178
|
These are the addresses that you should use to represent each node in the tree structure, however these are NOT the bitcoin addresses you should pass around for receiving money. These are more for storing inside a wallet file so that you can reconstruct the tree.
|
169
179
|
|
data/Rakefile
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
cc59abeb70c300eb32c0562dea582de4d26da71cb3fac9937fb3b88fd7092177c98ccbdf6eba7517c3dbc14b33f15e95bf799a02adfec5c12583cb9179c68bb7
|
@@ -1 +1 @@
|
|
1
|
-
ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9
|
1
|
+
ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9
|
data/lib/money-tree/address.rb
CHANGED
@@ -1,16 +1,26 @@
|
|
1
1
|
module MoneyTree
|
2
2
|
class Address
|
3
|
-
attr_reader :private_key
|
4
|
-
|
3
|
+
attr_reader :private_key
|
4
|
+
attr_reader :public_key
|
5
|
+
|
5
6
|
def initialize(opts = {})
|
6
7
|
private_key = opts.delete(:private_key)
|
7
|
-
@private_key =
|
8
|
-
@public_key =
|
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
|
data/lib/money-tree/key.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# encoding ascii-8bit
|
2
2
|
|
3
|
-
require
|
3
|
+
require "openssl"
|
4
4
|
|
5
5
|
module MoneyTree
|
6
6
|
class Key
|
7
|
-
include OpenSSL
|
8
7
|
include Support
|
9
|
-
|
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
|
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 =
|
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
|
-
|
75
|
-
|
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,
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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)
|
data/lib/money-tree/networks.rb
CHANGED
@@ -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:
|
9
|
-
p2sh_version:
|
10
|
-
p2sh_char:
|
11
|
-
privkey_version:
|
12
|
-
privkey_compression_flag:
|
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:
|
21
|
-
p2sh_version:
|
22
|
-
p2sh_char:
|
23
|
-
privkey_version:
|
24
|
-
privkey_compression_flag:
|
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
|
data/lib/money-tree/node.rb
CHANGED
@@ -2,8 +2,14 @@ module MoneyTree
|
|
2
2
|
class Node
|
3
3
|
include Support
|
4
4
|
extend Support
|
5
|
-
|
6
|
-
|
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) ==
|
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,
|
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(
|
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(
|
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,
|
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,
|
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,10 +89,15 @@ 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,
|
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
|
+
|
97
|
+
sum_point_hex = MoneyTree::OpenSSLExtensions.add(gen_point, public_key.uncompressed.point)
|
98
|
+
child_public_key = OpenSSL::PKey::EC::Point.new(public_key.group, OpenSSL::BN.new(sum_point_hex, 16)).to_bn.to_i
|
99
|
+
|
100
|
+
raise InvalidKeyForIndex, "at infinity" if child_public_key == 1 / 0.0 # very low probability
|
95
101
|
child_chain_code = right_from_hash(hash)
|
96
102
|
return child_public_key, child_chain_code
|
97
103
|
end
|
@@ -120,12 +126,7 @@ module MoneyTree
|
|
120
126
|
to_serialized_base58 to_serialized_hex(type, network: network)
|
121
127
|
end
|
122
128
|
|
123
|
-
def
|
124
|
-
puts 'Node.to_serialized_address is DEPRECATED. Please use .to_bip32.'
|
125
|
-
to_bip32(type, network: network)
|
126
|
-
end
|
127
|
-
|
128
|
-
def to_identifier(compressed=true)
|
129
|
+
def to_identifier(compressed = true)
|
129
130
|
key = compressed ? public_key.compressed : public_key.uncompressed
|
130
131
|
key.to_ripemd160
|
131
132
|
end
|
@@ -138,15 +139,25 @@ module MoneyTree
|
|
138
139
|
if @parent_fingerprint
|
139
140
|
@parent_fingerprint
|
140
141
|
else
|
141
|
-
depth.zero? ?
|
142
|
+
depth.zero? ? "00000000" : parent.to_fingerprint
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
145
|
-
def to_address(compressed=true, network: :bitcoin)
|
146
|
+
def to_address(compressed = true, network: :bitcoin)
|
146
147
|
address = NETWORKS[network][:address_version] + to_identifier(compressed)
|
147
148
|
to_serialized_base58 address
|
148
149
|
end
|
149
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
|
+
|
150
161
|
def subnode(i = 0, opts = {})
|
151
162
|
if private_key.nil?
|
152
163
|
child_public_key, child_chain_code = derive_public_key(i)
|
@@ -157,7 +168,7 @@ module MoneyTree
|
|
157
168
|
child_public_key = MoneyTree::PublicKey.new child_private_key
|
158
169
|
end
|
159
170
|
|
160
|
-
MoneyTree::Node.new(
|
171
|
+
MoneyTree::Node.new(depth: depth + 1,
|
161
172
|
index: i,
|
162
173
|
private_key: private_key.nil? ? nil : child_private_key,
|
163
174
|
public_key: child_public_key,
|
@@ -177,9 +188,9 @@ module MoneyTree
|
|
177
188
|
#
|
178
189
|
# You should choose either the p or the negative number convention for private key derivation.
|
179
190
|
def node_for_path(path)
|
180
|
-
force_public = path[-4..-1] ==
|
191
|
+
force_public = path[-4..-1] == ".pub"
|
181
192
|
path = path[0..-5] if force_public
|
182
|
-
parts = path.split(
|
193
|
+
parts = path.split("/")
|
183
194
|
nodes = []
|
184
195
|
parts.each_with_index do |part, depth|
|
185
196
|
if part =~ /m/i
|
@@ -190,7 +201,7 @@ module MoneyTree
|
|
190
201
|
nodes << node.subnode(i)
|
191
202
|
end
|
192
203
|
end
|
193
|
-
if force_public or parts.first ==
|
204
|
+
if force_public or parts.first == "M"
|
194
205
|
node = nodes.last
|
195
206
|
node.strip_private_info!
|
196
207
|
node
|
@@ -204,12 +215,12 @@ module MoneyTree
|
|
204
215
|
i = path_part.to_i
|
205
216
|
|
206
217
|
i = if i < 0
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
218
|
+
i
|
219
|
+
elsif is_prime
|
220
|
+
i | 0x80000000
|
221
|
+
else
|
222
|
+
i & 0x7fffffff
|
223
|
+
end
|
213
224
|
end
|
214
225
|
|
215
226
|
def strip_private_info!
|
@@ -246,7 +257,7 @@ module MoneyTree
|
|
246
257
|
raise SeedGeneration::ImportError unless seed_valid?(@seed_hash)
|
247
258
|
set_seeded_keys
|
248
259
|
elsif opts[:private_key] || opts[:public_key]
|
249
|
-
raise ImportError,
|
260
|
+
raise ImportError, "chain code required" unless opts[:chain_code]
|
250
261
|
@chain_code = opts[:chain_code]
|
251
262
|
if opts[:private_key]
|
252
263
|
@private_key = opts[:private_key]
|
@@ -256,8 +267,7 @@ module MoneyTree
|
|
256
267
|
opts[:public_key]
|
257
268
|
else
|
258
269
|
MoneyTree::PublicKey.new(opts[:public_key])
|
259
|
-
end
|
260
|
-
end
|
270
|
+
end end
|
261
271
|
else
|
262
272
|
generate_seed
|
263
273
|
set_seeded_keys
|