money-tree 0.0.1 → 0.0.2
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 +4 -4
- data/.simplecov +7 -0
- data/.travis.yml +3 -0
- data/README.md +8 -2
- data/Rakefile +5 -0
- data/lib/money-tree/key.rb +7 -6
- data/lib/money-tree/node.rb +11 -20
- data/lib/money-tree/support.rb +10 -9
- data/lib/money-tree/version.rb +1 -1
- data/money-tree.gemspec +2 -0
- data/spec/lib/money-tree/address_spec.rb +1 -0
- data/spec/lib/money-tree/node_spec.rb +30 -1
- data/spec/lib/money-tree/private_key_spec.rb +29 -0
- data/spec/lib/money-tree/public_key_spec.rb +7 -0
- data/spec/lib/money-tree/support_spec.rb +7 -0
- data/spec/spec_helper.rb +1 -0
- metadata +34 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 838719a8f745234b8e08d8726d2e4af5b61d93de
|
4
|
+
data.tar.gz: 9391e5eebd7b8b71dfe98ef61f2ff5e3ad0094ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73142e1cd77e36e4a4750b0a7f2e4f34f47f9320a27144ea412fd5149073598642624a37d63aca0417136c4acb9bb2e1043ec8d679384fe5a5eb415e8fc033e8
|
7
|
+
data.tar.gz: 6e0e848c4b968c004ca66acfd5ff1e917978fd9af63fea16264785d36ab6219ba6ef8a493919607c40789335f87d85f02d6a9c444fae3494f8d70b1e9f5ee55c
|
data/.simplecov
ADDED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
[](https://travis-ci.org/wink/money-tree) [](https://coveralls.io/r/wink/money-tree?branch=master) [](http://badge.fury.io/rb/money-tree)
|
1
2
|
# MoneyTree
|
2
3
|
|
3
|
-
MoneyTree is a Ruby implementation of Bitcoin Wallets. Specifically, it supports [Hierachical Deterministic wallets](
|
4
|
+
MoneyTree is a Ruby implementation of Bitcoin Wallets. Specifically, it supports [Hierachical Deterministic wallets](https://en.bitcoin.it/wiki/Deterministic_Wallet) according to the protocol specified in [BIP0032](https://en.bitcoin.it/wiki/BIP_0032).
|
5
|
+
|
6
|
+
If you find this helpful, please consider a small Bitcoin donation to 1nj2kie1hATcFbAaD7dEY53QaxNgt4KBp
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -16,9 +19,12 @@ Or install it yourself as:
|
|
16
19
|
|
17
20
|
$ gem install money-tree
|
18
21
|
|
22
|
+
## Prerequisites
|
23
|
+
MoneyTree will only work with Ruby 2.0.0 and greater. This is because the version of OpenSSL included with previous versions of Ruby did not include an OpenSSL::PKey::EC::Point#mul (point multiplication) method, which is required in order to calculate a Bitcoin public key from a private key.
|
24
|
+
|
19
25
|
## Usage
|
20
26
|
|
21
|
-
|
27
|
+
Documentation coming very soon! For now, please feel free to browse the source and specs.
|
22
28
|
|
23
29
|
## Contributing
|
24
30
|
|
data/Rakefile
CHANGED
data/lib/money-tree/key.rb
CHANGED
@@ -12,6 +12,7 @@ module MoneyTree
|
|
12
12
|
class KeyImportFailure < Exception; end
|
13
13
|
class KeyFormatNotFound < Exception; end
|
14
14
|
class InvalidWIFFormat < Exception; end
|
15
|
+
class InvalidBase64Format < Exception; end
|
15
16
|
|
16
17
|
attr_reader :options, :key
|
17
18
|
attr_accessor :ec_key
|
@@ -76,11 +77,10 @@ module MoneyTree
|
|
76
77
|
def parse_raw_key
|
77
78
|
result = if raw_key.is_a?(Bignum) then int_to_hex(raw_key)
|
78
79
|
elsif hex_format? then raw_key
|
80
|
+
elsif base64_format? then from_base64
|
79
81
|
elsif compressed_wif_format? then from_wif
|
80
82
|
elsif uncompressed_wif_format? then from_wif
|
81
|
-
elsif base64_format? then from_base64
|
82
83
|
else
|
83
|
-
raise raw_key.inspect
|
84
84
|
raise KeyFormatNotFound
|
85
85
|
end
|
86
86
|
result.downcase
|
@@ -94,8 +94,9 @@ module MoneyTree
|
|
94
94
|
hex.slice(2..last_char)
|
95
95
|
end
|
96
96
|
|
97
|
-
def from_base64
|
98
|
-
|
97
|
+
def from_base64(base64_key = raw_key)
|
98
|
+
raise InvalidBase64Format unless base64_format?(base64_key)
|
99
|
+
decode_base64(base64_key)
|
99
100
|
end
|
100
101
|
|
101
102
|
def compressed_wif_format?
|
@@ -106,8 +107,8 @@ module MoneyTree
|
|
106
107
|
raw_key.length == 51 && raw_key.slice(0) == MoneyTree::NETWORKS[:bitcoin][:uncompressed_wif_char]
|
107
108
|
end
|
108
109
|
|
109
|
-
def base64_format?
|
110
|
-
|
110
|
+
def base64_format?(base64_key = raw_key)
|
111
|
+
base64_key.length == 44 && base64_key =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
|
111
112
|
end
|
112
113
|
|
113
114
|
def hex_format?
|
data/lib/money-tree/node.rb
CHANGED
@@ -16,12 +16,12 @@ module MoneyTree
|
|
16
16
|
@parent = opts[:parent]
|
17
17
|
end
|
18
18
|
|
19
|
-
def is_private?
|
20
|
-
is_private == true
|
21
|
-
end
|
22
|
-
|
23
19
|
def index_hex(i = index)
|
24
|
-
i
|
20
|
+
if i < 0
|
21
|
+
[i].pack('l>').unpack('H*').first
|
22
|
+
else
|
23
|
+
i.to_s(16).rjust(8, "0")
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def depth_hex(depth)
|
@@ -94,7 +94,7 @@ module MoneyTree
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def to_fingerprint
|
97
|
-
|
97
|
+
public_key.to_fingerprint
|
98
98
|
end
|
99
99
|
|
100
100
|
def to_address
|
@@ -107,10 +107,11 @@ module MoneyTree
|
|
107
107
|
child_private_key, child_chain_code = derive_private_key(i)
|
108
108
|
child_private_key = MoneyTree::PrivateKey.new key: child_private_key
|
109
109
|
child_public_key = MoneyTree::PublicKey.new child_private_key
|
110
|
+
index = i
|
110
111
|
|
111
112
|
MoneyTree::Node.new depth: depth+1,
|
112
113
|
index: i,
|
113
|
-
is_private: i >= 0x80000000,
|
114
|
+
is_private: i >= 0x80000000 || i < 0,
|
114
115
|
private_key: child_private_key,
|
115
116
|
public_key: child_public_key,
|
116
117
|
chain_code: child_chain_code,
|
@@ -194,15 +195,9 @@ module MoneyTree
|
|
194
195
|
end
|
195
196
|
|
196
197
|
def generate_seed_until_valid
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
raise SeedGeneration::ValidityError unless seed_valid?(@seed_hash)
|
201
|
-
@seed_generation_attempt = 0
|
202
|
-
rescue SeedGeneration::Failure
|
203
|
-
@seed_generation_attempt += 1
|
204
|
-
@seed_generation_attempt < 10 ? generate_seed_until_valid : raise(SeedGeneration::TooManyAttempts)
|
205
|
-
end
|
198
|
+
@seed = generate_seed
|
199
|
+
@seed_hash = generate_seed_hash(@seed)
|
200
|
+
raise SeedGeneration::ValidityError unless seed_valid?(@seed_hash)
|
206
201
|
end
|
207
202
|
|
208
203
|
def generate_seed
|
@@ -219,10 +214,6 @@ module MoneyTree
|
|
219
214
|
!master_key.zero? && master_key < MoneyTree::Key::ORDER
|
220
215
|
end
|
221
216
|
|
222
|
-
def is_hex?(str)
|
223
|
-
str =~ /^[0-9A-F]+$/i && str.length % 2 == 0
|
224
|
-
end
|
225
|
-
|
226
217
|
def set_master_keys
|
227
218
|
@private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash)
|
228
219
|
@chain_code = right_from_hash(seed_hash)
|
data/lib/money-tree/support.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'openssl'
|
2
|
+
require 'base64'
|
2
3
|
|
3
4
|
module MoneyTree
|
4
5
|
module Support
|
@@ -58,11 +59,11 @@ module MoneyTree
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def encode_base64(hex)
|
61
|
-
[
|
62
|
+
Base64.encode64([hex].pack("H*")).chomp
|
62
63
|
end
|
63
64
|
|
64
65
|
def decode_base64(base64)
|
65
|
-
|
66
|
+
Base64.decode64(base64).unpack("H*")[0]
|
66
67
|
end
|
67
68
|
|
68
69
|
def hmac_sha512(key, message)
|
@@ -89,21 +90,21 @@ module MoneyTree
|
|
89
90
|
hex = '0' + hex unless (hex.length % 2).zero?
|
90
91
|
hex.downcase
|
91
92
|
end
|
92
|
-
|
93
|
+
|
93
94
|
def int_to_bytes(i)
|
94
95
|
[int_to_hex(i)].pack("H*")
|
95
96
|
end
|
96
97
|
|
97
|
-
def bytes_to_hex(
|
98
|
-
|
98
|
+
def bytes_to_hex(bytes)
|
99
|
+
bytes.unpack("H*")[0].downcase
|
99
100
|
end
|
100
101
|
|
101
|
-
def hex_to_bytes(
|
102
|
-
[
|
102
|
+
def hex_to_bytes(hex)
|
103
|
+
[hex].pack("H*")
|
103
104
|
end
|
104
105
|
|
105
|
-
def hex_to_int(
|
106
|
-
|
106
|
+
def hex_to_int(hex)
|
107
|
+
hex.to_i(16)
|
107
108
|
end
|
108
109
|
end
|
109
110
|
end
|
data/lib/money-tree/version.rb
CHANGED
data/money-tree.gemspec
CHANGED
@@ -21,5 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "simplecov"
|
25
|
+
spec.add_development_dependency "coveralls"
|
24
26
|
spec.add_development_dependency "pry"
|
25
27
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'money-tree'
|
2
3
|
|
3
4
|
# Test vectors from https://en.bitcoin.it/wiki/BIP_0032_TestVectors
|
@@ -537,6 +538,34 @@ describe MoneyTree::Master do
|
|
537
538
|
end
|
538
539
|
end
|
539
540
|
end
|
540
|
-
|
541
|
+
|
542
|
+
describe "negative index" do
|
543
|
+
before do
|
544
|
+
@master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f"
|
545
|
+
@node = @master.node_for_path "m/0'/-1"
|
546
|
+
end
|
547
|
+
|
548
|
+
it "has an index of 1" do
|
549
|
+
@node.index.should == -1
|
550
|
+
end
|
551
|
+
|
552
|
+
it "is public" do
|
553
|
+
@node.is_private.should == true
|
554
|
+
end
|
555
|
+
|
556
|
+
it "has a depth of 2" do
|
557
|
+
@node.depth.should == 2
|
558
|
+
end
|
559
|
+
|
560
|
+
it "generates a serialized private key" do
|
561
|
+
@node.to_serialized_hex(:private).should == "0488ade4025c1bd648ffffffffeb8291afea1716eb01a1ce7e00d1843072cbd227309df728ba4015f73d5344aa00d9bd1df2ae56b5be763ddd393573497a8075352bda4aad9ea9083e4c0b1081ac"
|
562
|
+
@node.to_serialized_address(:private).should == "xprv9wTYmMFvAM7JKr2H3xMXcBNzPsqKgffzsM2XnrvKQLJQo2qRftY4uspJCAqjvPypq6Rgvkpoen8MGGnNBxYdeYR5jt6VFdL8cGp2qKnukbW"
|
563
|
+
end
|
564
|
+
|
565
|
+
it "generates a serialized public_key" do
|
566
|
+
@node.to_serialized_hex.should == "0488b21e025c1bd648ffffffffeb8291afea1716eb01a1ce7e00d1843072cbd227309df728ba4015f73d5344aa02bcd68031f49f6c921e24ed8c1774221433de23eb309709f571d5449909405cfc"
|
567
|
+
@node.to_serialized_address.should == "xpub6ASuArnozifbYL6k9ytXyKKiwufp68PrEZx8bFKvxfqPfqAaDRrKTg8n3Ren8WFSpAbbWLGTimcfYkUB2JbyYFToRBdKT6pEhmpyVe3AqZ9"
|
568
|
+
end
|
569
|
+
end
|
541
570
|
end
|
542
571
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'money-tree'
|
2
3
|
|
3
4
|
describe MoneyTree::PrivateKey do
|
@@ -62,4 +63,32 @@ describe MoneyTree::PrivateKey do
|
|
62
63
|
@key.to_base64.should == 'Xq5Tdftfeg6mUFZjY776KDDvRBvcsZGYrfMY+u6G1ks='
|
63
64
|
end
|
64
65
|
end
|
66
|
+
|
67
|
+
describe "from_base64(base64_key)" do
|
68
|
+
it "parses base64 key" do
|
69
|
+
@key = MoneyTree::PrivateKey.new(key: "Xq5Tdftfeg6mUFZjY776KDDvRBvcsZGYrfMY+u6G1ks=")
|
70
|
+
@key.to_hex.should == "5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns the key from base64 encoding" do
|
74
|
+
@key.from_base64("Xq5Tdftfeg6mUFZjY776KDDvRBvcsZGYrfMY+u6G1ks=").should == '5eae5375fb5f7a0ea650566363befa2830ef441bdcb19198adf318faee86d64b'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises an error on bad encoding" do
|
78
|
+
lambda { @key.from_base64("Xq5Tdftfeg6mUFZjY776KD&%#BbadBADrfMY+u6G1ks=") }.should raise_error(MoneyTree::Key::InvalidBase64Format)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "valid?(eckey)" do
|
83
|
+
it "checks for a valid key" do
|
84
|
+
@key.valid?.should be_true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "parse_raw_key" do
|
89
|
+
it "returns error if key is not Bignum, hex, base64, or wif formatted" do
|
90
|
+
lambda { @key = MoneyTree::PrivateKey.new(key: "Thisisnotakey") }.should raise_error(MoneyTree::Key::KeyFormatNotFound)
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
65
94
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'money-tree'
|
2
3
|
|
3
4
|
describe MoneyTree::PublicKey do
|
@@ -26,6 +27,12 @@ describe MoneyTree::PublicKey do
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
30
|
+
describe "to_fingerprint" do
|
31
|
+
it "returns a valid fingerprint" do
|
32
|
+
@key.to_fingerprint.should == "1fddf42e"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
29
36
|
describe "to_address(compressed: false)" do
|
30
37
|
it "has 34 characters" do
|
31
38
|
@key.to_address(compressed: false).length.should == 34
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'money-tree'
|
2
3
|
include MoneyTree::Support
|
3
4
|
|
@@ -23,4 +24,10 @@ describe MoneyTree::Support do
|
|
23
24
|
hmac_sha512_hex("Jefe", "what do ya want for nothing?").should == "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
describe "hex_to_int" do
|
29
|
+
it "converts hex to integer" do
|
30
|
+
hex_to_int("abcdef0123456789").should == 12379813738877118345
|
31
|
+
end
|
32
|
+
end
|
26
33
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'simplecov'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money-tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micah Winkelspecht
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: coveralls
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: pry
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +102,8 @@ extensions: []
|
|
74
102
|
extra_rdoc_files: []
|
75
103
|
files:
|
76
104
|
- .gitignore
|
105
|
+
- .simplecov
|
106
|
+
- .travis.yml
|
77
107
|
- Gemfile
|
78
108
|
- LICENSE.txt
|
79
109
|
- README.md
|
@@ -91,6 +121,7 @@ files:
|
|
91
121
|
- spec/lib/money-tree/private_key_spec.rb
|
92
122
|
- spec/lib/money-tree/public_key_spec.rb
|
93
123
|
- spec/lib/money-tree/support_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
94
125
|
homepage: ''
|
95
126
|
licenses:
|
96
127
|
- MIT
|
@@ -121,3 +152,4 @@ test_files:
|
|
121
152
|
- spec/lib/money-tree/private_key_spec.rb
|
122
153
|
- spec/lib/money-tree/public_key_spec.rb
|
123
154
|
- spec/lib/money-tree/support_spec.rb
|
155
|
+
- spec/spec_helper.rb
|