money-tree 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/wink/money-tree.png)](https://travis-ci.org/wink/money-tree) [![Coverage Status](https://coveralls.io/repos/wink/money-tree/badge.png?branch=master)](https://coveralls.io/r/wink/money-tree?branch=master) [![Gem Version](https://badge.fury.io/rb/money-tree.png)](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
|