From e31957fddd5af3b2a84c2d3497113b5409cf40f2 Mon Sep 17 00:00:00 2001 From: jaimegem Date: Mon, 8 Dec 2014 00:43:56 -0800 Subject: [PATCH 01/23] clear and free EC_GROUP object --- lib/openssl_extensions.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/openssl_extensions.rb b/lib/openssl_extensions.rb index b942e39..f18aa61 100644 --- a/lib/openssl_extensions.rb +++ b/lib/openssl_extensions.rb @@ -15,6 +15,7 @@ module OpenSSLExtensions attach_function :EC_KEY_free, [:pointer], :int attach_function :EC_KEY_get0_group, [:pointer], :pointer attach_function :EC_KEY_new_by_curve_name, [:int], :pointer + attach_function :EC_GROUP_clear_free, [:pointer], :int attach_function :EC_POINT_clear_free, [:pointer], :int attach_function :EC_POINT_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int attach_function :EC_POINT_point2hex, [:pointer, :pointer, :int, :pointer], :string @@ -39,6 +40,7 @@ def self.add(point_0, point_1) EC_POINT_clear_free(sum_point) EC_POINT_clear_free(point_0_pt) EC_POINT_clear_free(point_1_pt) + EC_GROUP_clear_free(group) hex end @@ -64,4 +66,4 @@ def add(point) self.class.new group, OpenSSL::BN.new(sum_point_hex, 16) end -end \ No newline at end of file +end From c648b6b87e5ca2b847cebeab944d98739b01f11f Mon Sep 17 00:00:00 2001 From: Micah Winkelspecht Date: Mon, 8 Dec 2014 02:31:29 -0800 Subject: [PATCH 02/23] Group is already removed by EC_KEY_free --- lib/openssl_extensions.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/openssl_extensions.rb b/lib/openssl_extensions.rb index f18aa61..7a16bfc 100644 --- a/lib/openssl_extensions.rb +++ b/lib/openssl_extensions.rb @@ -15,7 +15,6 @@ module OpenSSLExtensions attach_function :EC_KEY_free, [:pointer], :int attach_function :EC_KEY_get0_group, [:pointer], :pointer attach_function :EC_KEY_new_by_curve_name, [:int], :pointer - attach_function :EC_GROUP_clear_free, [:pointer], :int attach_function :EC_POINT_clear_free, [:pointer], :int attach_function :EC_POINT_add, [:pointer, :pointer, :pointer, :pointer, :pointer], :int attach_function :EC_POINT_point2hex, [:pointer, :pointer, :int, :pointer], :string @@ -40,7 +39,6 @@ def self.add(point_0, point_1) EC_POINT_clear_free(sum_point) EC_POINT_clear_free(point_0_pt) EC_POINT_clear_free(point_1_pt) - EC_GROUP_clear_free(group) hex end From ae064cd07663f02db3e7c996551da50515082ead Mon Sep 17 00:00:00 2001 From: James Hudon Date: Tue, 9 Dec 2014 13:10:11 -0800 Subject: [PATCH 03/23] ensure subnodes are on same bitcoin network --- lib/money-tree/key.rb | 3 +++ spec/lib/money-tree/public_key_spec.rb | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/lib/money-tree/key.rb b/lib/money-tree/key.rb index 630b6ec..61be6ad 100644 --- a/lib/money-tree/key.rb +++ b/lib/money-tree/key.rb @@ -198,6 +198,9 @@ def initialize(p_key, opts = {}) @group = PKey::EC::Group.new GROUP_NAME @key = parse_raw_key end + + @options[:network] = @network_key # remember for deep clone + raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil? end diff --git a/spec/lib/money-tree/public_key_spec.rb b/spec/lib/money-tree/public_key_spec.rb index 3d26572..25cb27d 100644 --- a/spec/lib/money-tree/public_key_spec.rb +++ b/spec/lib/money-tree/public_key_spec.rb @@ -168,6 +168,10 @@ it "should have an address starting with m or n" do expect(%w(m n)).to include(@key.to_s[0]) end + + it "should have an uncompressed address starting with m or n" do + expect(%w(m n)).to include(@key.uncompressed.to_s[0]) + end end context 'without private key' do From 01aaa3e8c400f4484ed92f7456c3d20cdc3cf512 Mon Sep 17 00:00:00 2001 From: James Hudon Date: Tue, 9 Dec 2014 16:21:27 -0800 Subject: [PATCH 04/23] support uncompressed addresses --- lib/money-tree/node.rb | 11 ++++++----- spec/lib/money-tree/node_spec.rb | 8 +++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 78d5748..17512ac 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -129,9 +129,10 @@ def to_serialized_address(type = :public) raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil? to_serialized_base58 to_serialized_hex(type) end - - def to_identifier - public_key.compressed.to_ripemd160 + + def to_identifier(compressed=true) + key = compressed ? public_key.compressed : public_key.uncompressed + key.to_ripemd160 end def to_fingerprint @@ -146,8 +147,8 @@ def parent_fingerprint end end - def to_address - address = network[:address_version] + to_identifier + def to_address(compressed=true) + address = network[:address_version] + to_identifier(compressed) to_serialized_base58 address end diff --git a/spec/lib/money-tree/node_spec.rb b/spec/lib/money-tree/node_spec.rb index 715fb46..25806c1 100644 --- a/spec/lib/money-tree/node_spec.rb +++ b/spec/lib/money-tree/node_spec.rb @@ -423,7 +423,13 @@ expect(@master.to_fingerprint).to eql("bd16bee5") expect(@master.to_address).to eql("1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg") end - + + it "generates compressed and uncompressed addresses" do + expect(@master.to_address).to eql("1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg") + expect(@master.to_address(true)).to eql("1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg") + expect(@master.to_address(false)).to eql("1AEg9dFEw29kMgaN4BNHALu7AzX5XUfzSU") + end + it "generates a secret key" do expect(@master.private_key.to_hex).to eql("4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e") expect(@master.private_key.to_wif).to eql("KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy") From f40772dd121e7181fe1ba0005e96c4fb9a373c49 Mon Sep 17 00:00:00 2001 From: JOSHUA LEDERMAN Date: Mon, 22 Dec 2014 10:07:25 -0800 Subject: [PATCH 05/23] nil'd after freeing --- lib/openssl_extensions.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/openssl_extensions.rb b/lib/openssl_extensions.rb index 7a16bfc..aacc322 100644 --- a/lib/openssl_extensions.rb +++ b/lib/openssl_extensions.rb @@ -40,6 +40,12 @@ def self.add(point_0, point_1) EC_POINT_clear_free(point_0_pt) EC_POINT_clear_free(point_1_pt) + eckey = nil + group = nil + sum_point = nil + point_0_pt = nil + point_1_pt = nil + hex end From a80b9e3e6bc25347e9ac71312a80023ec176d780 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Fri, 2 Jan 2015 14:51:59 -0800 Subject: [PATCH 06/23] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa425bb..d2d1cc3 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ In HD wallets, chain codes are the mathematical glue that binds a parent node to You don't need to worry about chain codes if you are creating or importing from a Master key (it's always the same for all HD wallet master keys), however if you are trying to import a derived child key at some lower depth in the tree, you'll need the chain code. Luckily, whenever we export a node to a wallet file, we encode it in a special format that includes all of the relevant info (including chain code) that we need to reconstruct the node in a single convenient serialized address. #### Serialized Addresses -Beacause we need multiple pieces of info to reconstruct nodes in a tree, when we're dealing with HD wallets, we pass around a serialized address format that encodes both the key and the chain code. It looks like this: +Because we need multiple pieces of info to reconstruct nodes in a tree, when we're dealing with HD wallets, we pass around a serialized address format that encodes both the key and the chain code. It looks like this: ```ruby # private key From 3606d6ff78c94a1caedb78b2a35470a12cd99bd0 Mon Sep 17 00:00:00 2001 From: fedeb Date: Sat, 17 Jan 2015 20:10:55 +0100 Subject: [PATCH 07/23] force chaincode_hex value to avoid int to bytes conversion error --- lib/money-tree/node.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 17512ac..8bd8156 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -84,7 +84,7 @@ def i_as_bytes(i) def derive_private_key(i = 0) message = i >= 0x80000000 || i < 0 ? private_derivation_message(i) : public_derivation_message(i) - hash = hmac_sha512 int_to_bytes(chain_code), message + hash = hmac_sha512 hex_to_bytes(chain_code_hex), message left_int = left_from_hash(hash) raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability child_private_key = (left_int + private_key.to_i) % MoneyTree::Key::ORDER @@ -96,7 +96,7 @@ def derive_private_key(i = 0) def derive_public_key(i = 0) raise PrivatePublicMismatch if i >= 0x80000000 message = public_derivation_message(i) - hash = hmac_sha512 int_to_bytes(chain_code), message + hash = hmac_sha512 hex_to_bytes(chain_code_hex), message left_int = left_from_hash(hash) raise InvalidKeyForIndex, 'greater than or equal to order' if left_int >= MoneyTree::Key::ORDER # very low probability factor = BN.new left_int.to_s From d841beb38f056f25a5bc1da19b5ee31d100ec119 Mon Sep 17 00:00:00 2001 From: Julian Vergel de Dios Date: Tue, 20 Jan 2015 16:20:09 -0800 Subject: [PATCH 08/23] adding test for child derivation using hex_to_bytes --- spec/lib/money-tree/node_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/lib/money-tree/node_spec.rb b/spec/lib/money-tree/node_spec.rb index 25806c1..a37dc97 100644 --- a/spec/lib/money-tree/node_spec.rb +++ b/spec/lib/money-tree/node_spec.rb @@ -793,5 +793,15 @@ end end end + + describe "deriving a child node" do + describe "#node_for_path" do + it "correctly derives from a node with a chain code represented in 31 bytes" do + @node = MoneyTree::Node.from_serialized_address "tpubD6NzVbkrYhZ4WM42MZZmUZ7LjxyjBf5bGjEeLf9nJnMZqocGJWu94drvpqWsE9jE7k3h22v6gjpPGnqgBrqwGsRYwDXVRfQ2M9dfHbXP5zA" + @subnode = @node.node_for_path('m/1') + expect(@subnode.to_serialized_address).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY") + end + end + end end end From 67d52160d40bdae1e11e4355f98edc95f7af7dca Mon Sep 17 00:00:00 2001 From: Julian Vergel de Dios Date: Tue, 20 Jan 2015 16:25:31 -0800 Subject: [PATCH 09/23] version bump --- lib/money-tree/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/money-tree/version.rb b/lib/money-tree/version.rb index 12e7322..89ed434 100644 --- a/lib/money-tree/version.rb +++ b/lib/money-tree/version.rb @@ -1,3 +1,3 @@ module MoneyTree - VERSION = "0.8.8" + VERSION = "0.8.9" end From ed318fc129e87418fa2cd34251a6b0c002b44678 Mon Sep 17 00:00:00 2001 From: James Larisch Date: Wed, 22 Apr 2015 16:42:43 -0700 Subject: [PATCH 10/23] Remove the notion of 'network' entirely Money-Tree, in essence is a key-derivation tool. The keys it derives are not aware of the 'coin', or 'version'. So there is now no notion of network held as state on anything. Money-Tree derives keys, and in the various forms of serialization you can specify which type of coin (version bytes) you'd like to use. Although it may seem tedious, it allows trees to be coin-agnostic until serialization (and theoretically the usage of the same key-pairs for different coin addresses.) --- lib/money-tree/address.rb | 4 +- lib/money-tree/key.rb | 37 +++--------- lib/money-tree/node.rb | 80 ++++++++++--------------- spec/lib/money-tree/address_spec.rb | 2 +- spec/lib/money-tree/node_spec.rb | 28 ++++----- spec/lib/money-tree/private_key_spec.rb | 4 +- spec/lib/money-tree/public_key_spec.rb | 10 ++-- 7 files changed, 65 insertions(+), 100 deletions(-) diff --git a/lib/money-tree/address.rb b/lib/money-tree/address.rb index cf7ad53..d0de722 100644 --- a/lib/money-tree/address.rb +++ b/lib/money-tree/address.rb @@ -8,8 +8,8 @@ def initialize(opts = {}) @public_key = MoneyTree::PublicKey.new(@private_key, opts) end - def to_s - public_key.to_s + def to_s(network: :bitcoin) + public_key.to_s(network: network) end end diff --git a/lib/money-tree/key.rb b/lib/money-tree/key.rb index 61be6ad..5cfb840 100644 --- a/lib/money-tree/key.rb +++ b/lib/money-tree/key.rb @@ -14,7 +14,7 @@ class KeyFormatNotFound < Exception; end class InvalidWIFFormat < Exception; end class InvalidBase64Format < Exception; end - attr_reader :options, :key, :raw_key, :network, :network_key + attr_reader :options, :key, :raw_key attr_accessor :ec_key GROUP_NAME = 'secp256k1' @@ -39,8 +39,6 @@ class PrivateKey < Key def initialize(opts = {}) @options = opts @ec_key = PKey::EC.new GROUP_NAME - @network_key = options[:network] || :bitcoin - @network = MoneyTree::NETWORKS[network_key] if @options[:key] @raw_key = @options[:key] @key = parse_raw_key @@ -94,22 +92,12 @@ def from_hex(hex = raw_key) def from_wif(wif = raw_key) compressed = wif.length == 52 - parse_network_from_wif(wif, compressed: compressed) validate_wif(wif) hex = decode_base58(wif) last_char = compressed ? -11 : -9 hex.slice(2..last_char) end - def parse_network_from_wif(wif, opts = {}) - networks = MoneyTree::NETWORKS - chars_key = opts[:compressed] ? :compressed_wif_chars : :uncompressed_wif_chars - @network_key = networks.keys.select do |k| - networks[k][chars_key].include?(wif.slice(0)) - end.first - @network = networks[network_key] - end - def from_base64(base64_key = raw_key) raise InvalidBase64Format unless base64_format?(base64_key) decode_base64(base64_key) @@ -141,10 +129,9 @@ def to_hex int_to_hex @ec_key.private_key, 64 end - def to_wif(opts = {}) - opts[:compressed] = true unless opts[:compressed] == false - source = network[:privkey_version] + to_hex - source += network[:privkey_compression_flag] if opts[:compressed] + def to_wif(compressed: true, network: :bitcoin) + source = NETWORKS[network][:privkey_version] + to_hex + source += NETWORKS[network][:privkey_compression_flag] if compressed hash = sha256(source) hash = sha256(hash) checksum = hash.slice(0..7) @@ -154,7 +141,6 @@ def to_wif(opts = {}) def wif_valid?(wif) hex = decode_base58(wif) - return false unless hex.slice(0..1) == network[:privkey_version] checksum = hex.chars.to_a.pop(8).join source = hex.slice(0..-9) hash = sha256(source) @@ -171,8 +157,8 @@ def to_base64 encode_base64(to_hex) end - def to_s - to_wif + def to_s(network: :bitcoin) + to_wif(network: network) end end @@ -183,24 +169,17 @@ class PublicKey < Key def initialize(p_key, opts = {}) @options = opts @options[:compressed] = true if @options[:compressed].nil? - if p_key.is_a?(PrivateKey) @private_key = p_key - @network_key = private_key.network_key - @network = MoneyTree::NETWORKS[network_key] @point = @private_key.calculate_public_key(@options) @group = @point.group @key = @raw_key = to_hex else - @network_key = @options[:network] || :bitcoin - @network = MoneyTree::NETWORKS[network_key] @raw_key = p_key @group = PKey::EC::Group.new GROUP_NAME @key = parse_raw_key end - @options[:network] = @network_key # remember for deep clone - raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil? end @@ -267,9 +246,9 @@ def to_ripemd160 ripemd160 hash end - def to_address + def to_address(network: :bitcoin) hash = to_ripemd160 - address = network[:address_version] + hash + address = NETWORKS[network][:address_version] + hash to_serialized_base58 address end alias :to_s :to_address diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 8bd8156..7fcabfd 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -3,7 +3,7 @@ class Node include Support extend Support attr_reader :private_key, :public_key, :chain_code, - :is_private, :depth, :index, :parent, :network, :network_key + :is_private, :depth, :index, :parent class PublicDerivationFailure < Exception; end class InvalidKeyForIndex < Exception; end @@ -11,49 +11,40 @@ class ImportError < Exception; end class PrivatePublicMismatch < Exception; end def initialize(opts = {}) - @network_key = opts.delete(:network) || :bitcoin - @network = MoneyTree::NETWORKS[network_key] opts.each { |k, v| instance_variable_set "@#{k}", v } end - - def self.from_serialized_address(address) + + def self.from_wif(address, has_version: true) hex = from_serialized_base58 address - version = from_version_hex hex.slice!(0..7) + hex.slice!(0..7) if has_version self.new({ depth: hex.slice!(0..1).to_i(16), parent_fingerprint: hex.slice!(0..7), index: hex.slice!(0..7).to_i(16), chain_code: hex.slice!(0..63).to_i(16) - }.merge(key_options(hex, version))) + }.merge(parse_out_key(hex))) end - - def self.key_options(hex, version) - k_opts = { network: version[:network] } - if version[:private_key] && hex.slice(0..1) == '00' - private_key = MoneyTree::PrivateKey.new({ key: hex.slice(2..-1) }.merge(k_opts)) - k_opts.merge private_key: private_key, public_key: MoneyTree::PublicKey.new(private_key) + + def self.from_serialized_address(address) + puts "Node.from_serialized_address is DEPRECATED.\n + Please use .from_wif instead." + from_wif(address) + end + + def self.parse_out_key(hex) + if hex.slice(0..1) == '00' + private_key = MoneyTree::PrivateKey.new(key: hex.slice(2..-1)) + { + private_key: private_key, + public_key: MoneyTree::PublicKey.new(private_key) + } elsif %w(02 03).include? hex.slice(0..1) - k_opts.merge public_key: MoneyTree::PublicKey.new(hex, k_opts) + { public_key: MoneyTree::PublicKey.new(hex) } else raise ImportError, 'Public or private key data does not match version type' end end - - def self.from_version_hex(hex) - case hex - when MoneyTree::NETWORKS[:bitcoin][:extended_privkey_version] - { private_key: true, network: :bitcoin } - when MoneyTree::NETWORKS[:bitcoin][:extended_pubkey_version] - { private_key: false, network: :bitcoin } - when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_privkey_version] - { private_key: true, network: :bitcoin_testnet } - when MoneyTree::NETWORKS[:bitcoin_testnet][:extended_pubkey_version] - { private_key: false, network: :bitcoin_testnet } - else - raise ImportError, 'invalid version bytes' - end - end - + def is_private? index >= 0x80000000 || index < 0 end @@ -114,10 +105,10 @@ def right_from_hash(hash) bytes_to_int hash.bytes.to_a[32..-1] end - def to_serialized_hex(type = :public) + def to_serialized_hex(type = :public, network: :bitcoin) raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil? version_key = type.to_sym == :private ? :extended_privkey_version : :extended_pubkey_version - hex = network[version_key] # version (4 bytes) + hex = NETWORKS[network][version_key] # version (4 bytes) hex += depth_hex(depth) # depth (1 byte) hex += parent_fingerprint # fingerprint of key (4 bytes) hex += index_hex(index) # child number i (4 bytes) @@ -125,9 +116,9 @@ def to_serialized_hex(type = :public) hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.compressed.to_hex end - def to_serialized_address(type = :public) + def to_serialized_address(type = :public, network: :bitcoin) raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil? - to_serialized_base58 to_serialized_hex(type) + to_serialized_base58 to_serialized_hex(type, network: network) end def to_identifier(compressed=true) @@ -147,28 +138,27 @@ def parent_fingerprint end end - def to_address(compressed=true) - address = network[:address_version] + to_identifier(compressed) + def to_address(compressed=true, network: :bitcoin) + address = NETWORKS[network][:address_version] + to_identifier(compressed) to_serialized_base58 address end def subnode(i = 0, opts = {}) if private_key.nil? child_public_key, child_chain_code = derive_public_key(i) - child_public_key = MoneyTree::PublicKey.new child_public_key, network: network_key + child_public_key = MoneyTree::PublicKey.new child_public_key else child_private_key, child_chain_code = derive_private_key(i) - child_private_key = MoneyTree::PrivateKey.new key: child_private_key, network: network_key + child_private_key = MoneyTree::PrivateKey.new key: child_private_key child_public_key = MoneyTree::PublicKey.new child_private_key end - MoneyTree::Node.new network: network_key, - depth: depth+1, + MoneyTree::Node.new( depth: depth+1, index: i, private_key: private_key.nil? ? nil : child_private_key, public_key: child_public_key, chain_code: child_chain_code, - parent: self + parent: self) end # path: a path of subkeys denoted by numbers and slashes. Use @@ -246,8 +236,6 @@ def initialize(opts = {}) @depth = 0 @index = 0 opts[:seed] = [opts[:seed_hex]].pack("H*") if opts[:seed_hex] - @network_key = opts[:network] || :bitcoin - @network = MoneyTree::NETWORKS[network_key] if opts[:seed] @seed = opts[:seed] @seed_hash = generate_seed_hash(@seed) @@ -258,14 +246,12 @@ def initialize(opts = {}) @chain_code = opts[:chain_code] if opts[:private_key] @private_key = opts[:private_key] - @network_key = @private_key.network_key - @network = MoneyTree::NETWORKS[network_key] @public_key = MoneyTree::PublicKey.new @private_key else opts[:public_key] @public_key = if opts[:public_key].is_a?(MoneyTree::PublicKey) opts[:public_key] else - MoneyTree::PublicKey.new(opts[:public_key], network: network_key) + MoneyTree::PublicKey.new(opts[:public_key]) end end else @@ -295,7 +281,7 @@ def seed_valid?(seed_hash) end def set_seeded_keys - @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash), network: network_key + @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash) @chain_code = right_from_hash(seed_hash) @public_key = MoneyTree::PublicKey.new @private_key end diff --git a/spec/lib/money-tree/address_spec.rb b/spec/lib/money-tree/address_spec.rb index 7d42bc8..e48fc60 100644 --- a/spec/lib/money-tree/address_spec.rb +++ b/spec/lib/money-tree/address_spec.rb @@ -56,7 +56,7 @@ end it "returns a testnet address" do - expect(%w(m n)).to include(@address.to_s[0]) + expect(%w(m n)).to include(@address.to_s(network: :bitcoin_testnet)[0]) end end end diff --git a/spec/lib/money-tree/node_spec.rb b/spec/lib/money-tree/node_spec.rb index a37dc97..9dff685 100644 --- a/spec/lib/money-tree/node_spec.rb +++ b/spec/lib/money-tree/node_spec.rb @@ -24,49 +24,49 @@ end it "generates testnet address" do - expect(%w(m n)).to include(@master.to_address[0]) + expect(%w(m n)).to include(@master.to_address(network: :bitcoin_testnet)[0]) end it "generates testnet compressed wif" do - expect(@master.private_key.to_wif[0]).to eql('c') + expect(@master.private_key.to_wif(network: :bitcoin_testnet)[0]).to eql('c') end it "generates testnet uncompressed wif" do - expect(@master.private_key.to_wif(compressed: false)[0]).to eql('9') + expect(@master.private_key.to_wif(compressed: false, network: :bitcoin_testnet)[0]).to eql('9') end it "generates testnet serialized private address" do - expect(@master.to_serialized_address(:private).slice(0, 4)).to eql("tprv") + expect(@master.to_serialized_address(:private, network: :bitcoin_testnet).slice(0, 4)).to eql("tprv") end it "generates testnet serialized public address" do - expect(@master.to_serialized_address.slice(0, 4)).to eql("tpub") + expect(@master.to_serialized_address(network: :bitcoin_testnet).slice(0, 4)).to eql("tpub") end it "imports from testnet serialized private address" do node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' - expect(node.to_serialized_address(:private)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE') + expect(node.to_serialized_address(:private, network: :bitcoin_testnet)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE') end it "imports from testnet serialized public address" do node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' - expect(%w(m n)).to include(node.public_key.to_s[0]) - expect(node.to_serialized_address).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT') + expect(%w(m n)).to include(node.public_key.to_s(network: :bitcoin_testnet)[0]) + expect(node.to_serialized_address(network: :bitcoin_testnet)).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT') end it "generates testnet subnodes from serialized private address" do node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' subnode = node.node_for_path('1/1/1') - expect(%w(m n)).to include(subnode.public_key.to_s[0]) - expect(subnode.to_serialized_address(:private).slice(0,4)).to eql('tprv') - expect(subnode.to_serialized_address.slice(0,4)).to eql('tpub') + expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0]) + expect(subnode.to_serialized_address(:private, network: :bitcoin_testnet).slice(0,4)).to eql('tprv') + expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') end it "generates testnet subnodes from serialized public address" do node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' subnode = node.node_for_path('1/1/1') - expect(%w(m n)).to include(subnode.public_key.to_s[0]) - expect(subnode.to_serialized_address.slice(0,4)).to eql('tpub') + expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0]) + expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') end end @@ -799,7 +799,7 @@ it "correctly derives from a node with a chain code represented in 31 bytes" do @node = MoneyTree::Node.from_serialized_address "tpubD6NzVbkrYhZ4WM42MZZmUZ7LjxyjBf5bGjEeLf9nJnMZqocGJWu94drvpqWsE9jE7k3h22v6gjpPGnqgBrqwGsRYwDXVRfQ2M9dfHbXP5zA" @subnode = @node.node_for_path('m/1') - expect(@subnode.to_serialized_address).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY") + expect(@subnode.to_serialized_address(network: :bitcoin_testnet)).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY") end end end diff --git a/spec/lib/money-tree/private_key_spec.rb b/spec/lib/money-tree/private_key_spec.rb index cc75765..d792333 100644 --- a/spec/lib/money-tree/private_key_spec.rb +++ b/spec/lib/money-tree/private_key_spec.rb @@ -45,7 +45,7 @@ end it "is valid" do - expect(@key.to_wif(compressed: false)).to eql('5JXz5ZyFk31oHVTQxqce7yitCmTAPxBqeGQ4b7H3Aj3L45wUhoa' ) + expect(@key.to_wif(compressed: false)).to eql('5JXz5ZyFk31oHVTQxqce7yitCmTAPxBqeGQ4b7H3Aj3L45wUhoa') end end @@ -104,7 +104,7 @@ describe "to_wif" do it "returns same wif" do - expect(@key.to_wif).to eql('cRhes8SBnsF6WizphaRKQKZZfDniDa9Bxcw31yKeEC1KDExhxFgD') + expect(@key.to_wif(network: :bitcoin_testnet)).to eql('cRhes8SBnsF6WizphaRKQKZZfDniDa9Bxcw31yKeEC1KDExhxFgD') end end end diff --git a/spec/lib/money-tree/public_key_spec.rb b/spec/lib/money-tree/public_key_spec.rb index 25cb27d..4aabe61 100644 --- a/spec/lib/money-tree/public_key_spec.rb +++ b/spec/lib/money-tree/public_key_spec.rb @@ -161,26 +161,26 @@ context "testnet" do context 'with private key' do before do - @private_key = MoneyTree::PrivateKey.new network: :bitcoin_testnet + @private_key = MoneyTree::PrivateKey.new @key = MoneyTree::PublicKey.new(@private_key) end it "should have an address starting with m or n" do - expect(%w(m n)).to include(@key.to_s[0]) + expect(%w(m n)).to include(@key.to_s(network: :bitcoin_testnet)[0]) end it "should have an uncompressed address starting with m or n" do - expect(%w(m n)).to include(@key.uncompressed.to_s[0]) + expect(%w(m n)).to include(@key.uncompressed.to_s(network: :bitcoin_testnet)[0]) end end context 'without private key' do before do - @key = MoneyTree::PublicKey.new('0297b033ba894611345a0e777861237ef1632370fbd58ebe644eb9f3714e8fe2bc', network: :bitcoin_testnet) + @key = MoneyTree::PublicKey.new('0297b033ba894611345a0e777861237ef1632370fbd58ebe644eb9f3714e8fe2bc') end it "should have an address starting with m or n" do - expect(%w(m n)).to include(@key.to_s[0]) + expect(%w(m n)).to include(@key.to_s(network: :bitcoin_testnet)[0]) end end end From b9ef187a4be587c2a972250550e14bffd8d79496 Mon Sep 17 00:00:00 2001 From: James Larisch Date: Wed, 22 Apr 2015 16:48:38 -0700 Subject: [PATCH 11/23] Networks hash throws error by default If the user provides a NETWORK that is not recognized, we throw an error. --- lib/money-tree/networks.rb | 57 +++++++++++++++++++++----------------- spec/spec_helper.rb | 1 + 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/money-tree/networks.rb b/lib/money-tree/networks.rb index 0df0410..ffdf37f 100644 --- a/lib/money-tree/networks.rb +++ b/lib/money-tree/networks.rb @@ -1,28 +1,33 @@ module MoneyTree - NETWORKS = { - bitcoin: { - address_version: '00', - p2sh_version: '05', - p2sh_char: '3', - privkey_version: '80', - privkey_compression_flag: '01', - extended_privkey_version: "0488ade4", - extended_pubkey_version: "0488b21e", - compressed_wif_chars: %w(K L), - uncompressed_wif_chars: %w(5), - protocol_version: 70001 - }, - bitcoin_testnet: { - address_version: '6f', - p2sh_version: 'c4', - p2sh_char: '2', - privkey_version: 'ef', - privkey_compression_flag: '01', - extended_privkey_version: "04358394", - extended_pubkey_version: "043587cf", - compressed_wif_chars: %w(c), - uncompressed_wif_chars: %w(9), - protocol_version: 70001 - } - } + NETWORKS = + begin + Hash.new do |_, key| + raise "#{key} is not a valid network!" + end.merge({ + bitcoin: { + address_version: '00', + p2sh_version: '05', + p2sh_char: '3', + privkey_version: '80', + privkey_compression_flag: '01', + extended_privkey_version: "0488ade4", + extended_pubkey_version: "0488b21e", + compressed_wif_chars: %w(K L), + uncompressed_wif_chars: %w(5), + protocol_version: 70001 + }, + bitcoin_testnet: { + address_version: '6f', + p2sh_version: 'c4', + p2sh_char: '2', + privkey_version: 'ef', + privkey_compression_flag: '01', + extended_privkey_version: "04358394", + extended_pubkey_version: "043587cf", + compressed_wif_chars: %w(c), + uncompressed_wif_chars: %w(9), + protocol_version: 70001 + } + }) + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d21871c..c74937c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,3 @@ require 'simplecov' require 'money-tree' +require 'pry' From d72b971afca783befa6943660ed205b0423fd194 Mon Sep 17 00:00:00 2001 From: James Larisch Date: Fri, 24 Apr 2015 14:36:40 -0700 Subject: [PATCH 12/23] .***_serialized_address -> .***_bip32 Rename to_serialized_address to to_bip32 same for from_serialized_address. Deprecate old methods. --- lib/money-tree/node.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 7fcabfd..0a9ac07 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -14,7 +14,7 @@ def initialize(opts = {}) opts.each { |k, v| instance_variable_set "@#{k}", v } end - def self.from_wif(address, has_version: true) + def self.from_bip32(address, has_version: true) hex = from_serialized_base58 address hex.slice!(0..7) if has_version self.new({ @@ -26,9 +26,8 @@ def self.from_wif(address, has_version: true) end def self.from_serialized_address(address) - puts "Node.from_serialized_address is DEPRECATED.\n - Please use .from_wif instead." - from_wif(address) + puts 'Node.from_serialized_address is DEPRECATED. Please use .from_bip32 instead.' + from_bip32(address) end def self.parse_out_key(hex) @@ -116,11 +115,16 @@ def to_serialized_hex(type = :public, network: :bitcoin) hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.compressed.to_hex end - def to_serialized_address(type = :public, network: :bitcoin) + def to_bip32(type = :public, network: :bitcoin) raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil? to_serialized_base58 to_serialized_hex(type, network: network) end + def to_serialized_address(type = :public, network: :bitcoin) + puts 'Node.to_serialized_address is DEPRECATED. Please use .to_bip32.' + to_bip32(type, network: network) + end + def to_identifier(compressed=true) key = compressed ? public_key.compressed : public_key.uncompressed key.to_ripemd160 From 8371ac3992823f751a43caeb7170a8ebaf96da57 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Thu, 14 May 2015 16:17:22 -0700 Subject: [PATCH 13/23] support 'testnet3' as network --- lib/money-tree/networks.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/money-tree/networks.rb b/lib/money-tree/networks.rb index ffdf37f..291d613 100644 --- a/lib/money-tree/networks.rb +++ b/lib/money-tree/networks.rb @@ -1,9 +1,9 @@ module MoneyTree - NETWORKS = - begin - Hash.new do |_, key| + NETWORKS = + begin + hsh = Hash.new do |_, key| raise "#{key} is not a valid network!" - end.merge({ + end.merge( bitcoin: { address_version: '00', p2sh_version: '05', @@ -28,6 +28,8 @@ module MoneyTree uncompressed_wif_chars: %w(9), protocol_version: 70001 } - }) + ) + hsh[:testnet3] = hsh[:bitcoin_testnet] + hsh end end From e17fad70eab8ba8858f4290b60e28485af50773d Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Thu, 14 May 2015 16:17:29 -0700 Subject: [PATCH 14/23] trailing whitespace --- lib/money-tree/key.rb | 76 +++++++++++++++++++++--------------------- lib/money-tree/node.rb | 76 +++++++++++++++++++++--------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/lib/money-tree/key.rb b/lib/money-tree/key.rb index 5cfb840..0289736 100644 --- a/lib/money-tree/key.rb +++ b/lib/money-tree/key.rb @@ -13,10 +13,10 @@ class KeyImportFailure < Exception; end class KeyFormatNotFound < Exception; end class InvalidWIFFormat < Exception; end class InvalidBase64Format < Exception; end - + attr_reader :options, :key, :raw_key attr_accessor :ec_key - + GROUP_NAME = 'secp256k1' ORDER = "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".to_i(16) @@ -24,18 +24,18 @@ def valid?(eckey = nil) eckey ||= ec_key eckey.nil? ? false : eckey.check_key end - + def to_bytes hex_to_bytes to_hex end - + def to_i bytes_to_int to_bytes end end - + class PrivateKey < Key - + def initialize(opts = {}) @options = opts @ec_key = PKey::EC.new GROUP_NAME @@ -48,34 +48,34 @@ def initialize(opts = {}) @key = to_hex end end - + def generate ec_key.generate_key end - + def import ec_key.private_key = BN.new(key, 16) set_public_key end - + def calculate_public_key(opts = {}) opts[:compressed] = true unless opts[:compressed] == false group = ec_key.group group.point_conversion_form = opts[:compressed] ? :compressed : :uncompressed point = group.generator.mul ec_key.private_key end - + def set_public_key(opts = {}) ec_key.public_key = calculate_public_key(opts) end - + def parse_raw_key result = if raw_key.is_a?(Bignum) then from_bignum 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 + else raise KeyFormatNotFound end result.downcase @@ -89,7 +89,7 @@ def from_bignum(bignum = raw_key) def from_hex(hex = raw_key) hex end - + def from_wif(wif = raw_key) compressed = wif.length == 52 validate_wif(wif) @@ -106,7 +106,7 @@ def from_base64(base64_key = raw_key) def compressed_wif_format? wif_format?(:compressed) end - + def uncompressed_wif_format? wif_format?(:uncompressed) end @@ -120,15 +120,15 @@ def wif_format?(compression) def base64_format?(base64_key = raw_key) base64_key.length == 44 && base64_key =~ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/ end - + def hex_format? raw_key.length == 64 && !raw_key[/\H/] end - + def to_hex int_to_hex @ec_key.private_key, 64 end - + def to_wif(compressed: true, network: :bitcoin) source = NETWORKS[network][:privkey_version] + to_hex source += NETWORKS[network][:privkey_compression_flag] if compressed @@ -148,24 +148,24 @@ def wif_valid?(wif) hash_checksum = hash.slice(0..7) checksum == hash_checksum end - + def validate_wif(wif) raise InvalidWIFFormat unless wif_valid?(wif) end - + def to_base64 encode_base64(to_hex) end - + def to_s(network: :bitcoin) to_wif(network: network) end - + end - + class PublicKey < Key attr_reader :private_key, :point, :group, :key_int - + def initialize(p_key, opts = {}) @options = opts @options[:compressed] = true if @options[:compressed].nil? @@ -182,27 +182,27 @@ def initialize(p_key, opts = {}) raise ArgumentError, "Must initialize with a MoneyTree::PrivateKey or a public key value" if @key.nil? end - + def compression @group.point_conversion_form end - + def compression=(compression_type = :compressed) @group.point_conversion_form = compression_type end - + def compressed compressed_key = self.class.new raw_key, options # deep clone compressed_key.set_point to_i, compressed: true compressed_key end - + def uncompressed uncompressed_key = self.class.new raw_key, options # deep clone uncompressed_key.set_point to_i, compressed: false uncompressed_key end - + def set_point(int = to_i, opts = {}) opts = options.merge(opts) opts[:compressed] = true if opts[:compressed].nil? @@ -211,7 +211,7 @@ def set_point(int = to_i, opts = {}) @point = PKey::EC::Point.new group, bn raise KeyInvalid, 'point is not on the curve' unless @point.on_curve? end - + def parse_raw_key result = if raw_key.is_a?(Bignum) set_point raw_key @@ -219,45 +219,45 @@ def parse_raw_key set_point hex_to_int(raw_key), compressed: false elsif compressed_hex_format? set_point hex_to_int(raw_key), compressed: true - else + else raise KeyFormatNotFound end to_hex end - + def hex_format? raw_key.length == 130 && !raw_key[/\H/] end - + def compressed_hex_format? raw_key.length == 66 && !raw_key[/\H/] end - + def to_hex int_to_hex to_i, 66 end - + def to_i point.to_bn.to_i end - + def to_ripemd160 hash = sha256 to_hex ripemd160 hash end - + def to_address(network: :bitcoin) hash = to_ripemd160 address = NETWORKS[network][:address_version] + hash to_serialized_base58 address end alias :to_s :to_address - + def to_fingerprint hash = to_ripemd160 hash.slice(0..7) end - + def to_bytes int_to_bytes to_i end diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index 0a9ac07..e361c08 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -2,19 +2,19 @@ module MoneyTree class Node include Support extend Support - attr_reader :private_key, :public_key, :chain_code, - :is_private, :depth, :index, :parent - + attr_reader :private_key, :public_key, :chain_code, + :is_private, :depth, :index, :parent + class PublicDerivationFailure < Exception; end class InvalidKeyForIndex < Exception; end class ImportError < Exception; end class PrivatePublicMismatch < Exception; end - + def initialize(opts = {}) opts.each { |k, v| instance_variable_set "@#{k}", v } end - def self.from_bip32(address, has_version: true) + def self.from_bip32(address, has_version: true) hex = from_serialized_base58 address hex.slice!(0..7) if has_version self.new({ @@ -33,9 +33,9 @@ def self.from_serialized_address(address) def self.parse_out_key(hex) if hex.slice(0..1) == '00' private_key = MoneyTree::PrivateKey.new(key: hex.slice(2..-1)) - { + { private_key: private_key, - public_key: MoneyTree::PublicKey.new(private_key) + public_key: MoneyTree::PublicKey.new(private_key) } elsif %w(02 03).include? hex.slice(0..1) { public_key: MoneyTree::PublicKey.new(hex) } @@ -47,7 +47,7 @@ def self.parse_out_key(hex) def is_private? index >= 0x80000000 || index < 0 end - + def index_hex(i = index) if i < 0 [i].pack('l>').unpack('H*').first @@ -55,15 +55,15 @@ def index_hex(i = index) i.to_s(16).rjust(8, "0") end end - + def depth_hex(depth) depth.to_s(16).rjust(2, "0") end - + def private_derivation_message(i) "\x00" + private_key.to_bytes + i_as_bytes(i) end - + def public_derivation_message(i) public_key.to_bytes << i_as_bytes(i) end @@ -71,7 +71,7 @@ def public_derivation_message(i) def i_as_bytes(i) [i].pack('N') end - + def derive_private_key(i = 0) message = i >= 0x80000000 || i < 0 ? private_derivation_message(i) : public_derivation_message(i) hash = hmac_sha512 hex_to_bytes(chain_code_hex), message @@ -82,7 +82,7 @@ def derive_private_key(i = 0) child_chain_code = right_from_hash(hash) return child_private_key, child_chain_code end - + def derive_public_key(i = 0) raise PrivatePublicMismatch if i >= 0x80000000 message = public_derivation_message(i) @@ -95,11 +95,11 @@ def derive_public_key(i = 0) child_chain_code = right_from_hash(hash) return child_public_key, child_chain_code end - + def left_from_hash(hash) bytes_to_int hash.bytes.to_a[0..31] end - + def right_from_hash(hash) bytes_to_int hash.bytes.to_a[32..-1] end @@ -114,7 +114,7 @@ def to_serialized_hex(type = :public, network: :bitcoin) hex += chain_code_hex hex += type.to_sym == :private ? "00#{private_key.to_hex}" : public_key.compressed.to_hex end - + def to_bip32(type = :public, network: :bitcoin) raise PrivatePublicMismatch if type.to_sym == :private && private_key.nil? to_serialized_base58 to_serialized_hex(type, network: network) @@ -129,11 +129,11 @@ def to_identifier(compressed=true) key = compressed ? public_key.compressed : public_key.uncompressed key.to_ripemd160 end - + def to_fingerprint public_key.compressed.to_fingerprint end - + def parent_fingerprint if @parent_fingerprint @parent_fingerprint @@ -146,7 +146,7 @@ def to_address(compressed=true, network: :bitcoin) address = NETWORKS[network][:address_version] + to_identifier(compressed) to_serialized_base58 address end - + def subnode(i = 0, opts = {}) if private_key.nil? child_public_key, child_chain_code = derive_public_key(i) @@ -156,25 +156,25 @@ def subnode(i = 0, opts = {}) child_private_key = MoneyTree::PrivateKey.new key: child_private_key child_public_key = MoneyTree::PublicKey.new child_private_key end - - MoneyTree::Node.new( depth: depth+1, - index: i, + + MoneyTree::Node.new( depth: depth+1, + index: i, private_key: private_key.nil? ? nil : child_private_key, public_key: child_public_key, chain_code: child_chain_code, parent: self) end - + # path: a path of subkeys denoted by numbers and slashes. Use # p or i<0 for private key derivation. End with .pub to force # the key public. - # + # # Examples: # 1p/-5/2/1 would call subkey(i=1, is_prime=True).subkey(i=-5). # subkey(i=2).subkey(i=1) and then yield the private key # 0/0/458.pub would call subkey(i=0).subkey(i=0).subkey(i=458) and # then yield the public key - # + # # You should choose either the p or the negative number convention for private key derivation. def node_for_path(path) force_public = path[-4..-1] == '.pub' @@ -198,11 +198,11 @@ def node_for_path(path) nodes.last end end - + def parse_index(path_part) is_prime = %w(p ').include? path_part[-1] i = path_part.to_i - + i = if i < 0 i elsif is_prime @@ -211,16 +211,16 @@ def parse_index(path_part) i & 0x7fffffff end end - + def strip_private_info! @private_key = nil end - + def chain_code_hex int_to_hex chain_code, 64 end end - + class Master < Node module SeedGeneration class Failure < Exception; end @@ -230,12 +230,12 @@ class ValidityError < Failure; end class ImportError < Failure; end class TooManyAttempts < Failure; end end - + HD_WALLET_BASE_KEY = "Bitcoin seed" RANDOM_SEED_SIZE = 32 - + attr_reader :seed, :seed_hash - + def initialize(opts = {}) @depth = 0 @index = 0 @@ -263,27 +263,27 @@ def initialize(opts = {}) set_seeded_keys end end - + def is_private? true end - + def generate_seed @seed = OpenSSL::Random.random_bytes(32) @seed_hash = generate_seed_hash(@seed) raise SeedGeneration::ValidityError unless seed_valid?(@seed_hash) end - + def generate_seed_hash(seed) hmac_sha512 HD_WALLET_BASE_KEY, seed end - + def seed_valid?(seed_hash) return false unless seed_hash.bytesize == 64 master_key = left_from_hash(seed_hash) !master_key.zero? && master_key < MoneyTree::Key::ORDER end - + def set_seeded_keys @private_key = MoneyTree::PrivateKey.new key: left_from_hash(seed_hash) @chain_code = right_from_hash(seed_hash) From 286282ed70891e32819cd87f12f4338b0faed23c Mon Sep 17 00:00:00 2001 From: Julian Vergel de Dios Date: Tue, 19 May 2015 11:52:38 -0700 Subject: [PATCH 15/23] 0.9.0 release --- certs/jvergeldedios.pem | 20 ++++++++++++++++++++ checksum/money-tree-0.9.0.gem.sha512 | 1 + lib/money-tree/version.rb | 2 +- money-tree.gemspec | 9 +++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 certs/jvergeldedios.pem create mode 100644 checksum/money-tree-0.9.0.gem.sha512 diff --git a/certs/jvergeldedios.pem b/certs/jvergeldedios.pem new file mode 100644 index 0000000..825fa01 --- /dev/null +++ b/certs/jvergeldedios.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDXDCCAkSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA6MQ8wDQYDVQQDDAZqdWxp +YW4xEzARBgoJkiaJk/IsZAEZFgNnZW0xEjAQBgoJkiaJk/IsZAEZFgJjbzAeFw0x +NTA0MDMwODAwMTdaFw0xNjA0MDIwODAwMTdaMDoxDzANBgNVBAMMBmp1bGlhbjET +MBEGCgmSJomT8ixkARkWA2dlbTESMBAGCgmSJomT8ixkARkWAmNvMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0qCsOUQyTRA4f7WxoU2ctpUO5+eLZw8 +ILvHJy/0dC2nnIgib+FaFA8TRIGw6fjX2hQ43QJyO36zkjUhAwNu/+TBCfG+Grut +2dI9XmqU5Z6PvvXRj6Gu5IkDeIVDKILZv3bDugHJalre4BUKnwPYv5WpZ/e/c6+z +E4fwe4ZQzqslSXZo0o/wFvs5dGuIoP93bazSeqddre0JKFFiEP/SNGP9e/lXEd2V +rLFYAY409no9J+VQOHP0Nu9ShlCZp8M45abKd2ykuSDaT6jH9YcUHBr3/IEsA4+f +DypeS1ySVvad+o8iTnfz1Hyohz4ORm3spf0BOtGI/Swbv3LObZJqkwIDAQABo20w +azAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUERhBDW/kkq7tz2hN +hPNHtounnkowGAYDVR0RBBEwD4ENanVsaWFuQGdlbS5jbzAYBgNVHRIEETAPgQ1q +dWxpYW5AZ2VtLmNvMA0GCSqGSIb3DQEBBQUAA4IBAQAgnumhg8ST8JohYWcoDoQt +3BUG5rbfJ/qE0utOt6esi9d6Vz6YHpiT08woaj68OWl9U9N4vjox+ckkTRs93KBd +y3thnK9cIEAzoEZs3BBguXYOoFLughGD7hEuLlRYbwZzyIdzx/XdLgsy5Di8Gqaa +RKurfXP+dERQww34CUhmhOLO4/rYGqaD88so0MzCImgS+OX+G4ppqd38iQpaxCHL +tdc4VS7IlSRxlZ3dBOgiigy9GXpJ+7F831AqjxL39EPwdr7RguTNz+pi//RKaT/U +IlpVB+Xfk0vQdP7iYfjGxDzUf0FACMjsR95waJmadKW1Iy6STw2hwPhYIQz1Hu1A +-----END CERTIFICATE----- diff --git a/checksum/money-tree-0.9.0.gem.sha512 b/checksum/money-tree-0.9.0.gem.sha512 new file mode 100644 index 0000000..7d2a8af --- /dev/null +++ b/checksum/money-tree-0.9.0.gem.sha512 @@ -0,0 +1 @@ +ce3c7dc0fe6817aee65f990c7a97f89fd36c94380ac804c7579554d665a934df99d4e72ee9b2e467efcc76799a20c4d1de4c950bbba3512d42260c38a46e54b9 \ No newline at end of file diff --git a/lib/money-tree/version.rb b/lib/money-tree/version.rb index 89ed434..c96ac35 100644 --- a/lib/money-tree/version.rb +++ b/lib/money-tree/version.rb @@ -1,3 +1,3 @@ module MoneyTree - VERSION = "0.8.9" + VERSION = "0.9.0" end diff --git a/money-tree.gemspec b/money-tree.gemspec index f174e3b..f635c20 100644 --- a/money-tree.gemspec +++ b/money-tree.gemspec @@ -18,6 +18,15 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] + # used with gem i coin-op -P HighSecurity + spec.cert_chain = ["certs/jvergeldedios.pem"] + # Sign gem when evaluating spec with `gem` command + # unless ENV has set a SKIP_GEM_SIGNING + if ($0 =~ /gem\z/) and not ENV.include?("SKIP_GEM_SIGNING") + spec.signing_key = File.join(Gem.user_home, ".ssh", "gem-private_key.pem") + end + + spec.add_dependency "ffi" spec.add_development_dependency "bundler", "~> 1.3" From 7dc0420dcafe785eca922f3cfc1665d894107370 Mon Sep 17 00:00:00 2001 From: Kasper Holbek Jensen Date: Fri, 26 Feb 2016 13:06:29 +0200 Subject: [PATCH 16/23] update deprecated calls in specs --- .simplecov | 4 +- spec/lib/money-tree/node_spec.rb | 380 +++++++++++++++---------------- 2 files changed, 192 insertions(+), 192 deletions(-) diff --git a/.simplecov b/.simplecov index 3ce85da..af7f103 100644 --- a/.simplecov +++ b/.simplecov @@ -1,7 +1,7 @@ require 'coveralls' -SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter -] +]) SimpleCov.start diff --git a/spec/lib/money-tree/node_spec.rb b/spec/lib/money-tree/node_spec.rb index 9dff685..24a2928 100644 --- a/spec/lib/money-tree/node_spec.rb +++ b/spec/lib/money-tree/node_spec.rb @@ -7,7 +7,7 @@ before do @master = MoneyTree::Master.new end - + it "generates a random seed 32 bytes long" do expect(@master.seed.bytesize).to eql(32) end @@ -36,37 +36,37 @@ end it "generates testnet serialized private address" do - expect(@master.to_serialized_address(:private, network: :bitcoin_testnet).slice(0, 4)).to eql("tprv") + expect(@master.to_bip32(:private, network: :bitcoin_testnet).slice(0, 4)).to eql("tprv") end it "generates testnet serialized public address" do - expect(@master.to_serialized_address(network: :bitcoin_testnet).slice(0, 4)).to eql("tpub") + expect(@master.to_bip32(network: :bitcoin_testnet).slice(0, 4)).to eql("tpub") end it "imports from testnet serialized private address" do - node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' - expect(node.to_serialized_address(:private, network: :bitcoin_testnet)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE') + node = MoneyTree::Node.from_bip32 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' + expect(node.to_bip32(:private, network: :bitcoin_testnet)).to eql('tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE') end it "imports from testnet serialized public address" do - node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' + node = MoneyTree::Node.from_bip32 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' expect(%w(m n)).to include(node.public_key.to_s(network: :bitcoin_testnet)[0]) - expect(node.to_serialized_address(network: :bitcoin_testnet)).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT') + expect(node.to_bip32(network: :bitcoin_testnet)).to eql('tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT') end it "generates testnet subnodes from serialized private address" do - node = MoneyTree::Node.from_serialized_address 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' + node = MoneyTree::Node.from_bip32 'tprv8ZgxMBicQKsPcuN7bfUZqq78UEYapr3Tzmc9NcDXw8BnBJ47dZYr6SusnfYj7vbAYP9CP8ZiD5aVBTUo1yU5QP56mepKVvuEbu8KZQXMKNE' subnode = node.node_for_path('1/1/1') expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0]) - expect(subnode.to_serialized_address(:private, network: :bitcoin_testnet).slice(0,4)).to eql('tprv') - expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') + expect(subnode.to_bip32(:private, network: :bitcoin_testnet).slice(0,4)).to eql('tprv') + expect(subnode.to_bip32(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') end it "generates testnet subnodes from serialized public address" do - node = MoneyTree::Node.from_serialized_address 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' + node = MoneyTree::Node.from_bip32 'tpubD6NzVbkrYhZ4YA8aUE9bBZTSyHJibBqwDny5urfwDdJc4W8od3y3Ebzy6CqsYn9CCC5P5VQ7CeZYpnT1kX3RPVPysU2rFRvYSj8BCoYYNqT' subnode = node.node_for_path('1/1/1') expect(%w(m n)).to include(subnode.public_key.to_s(network: :bitcoin_testnet)[0]) - expect(subnode.to_serialized_address(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') + expect(subnode.to_bip32(network: :bitcoin_testnet).slice(0,4)).to eql('tpub') end end @@ -75,349 +75,349 @@ before do @master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f" end - + describe "m" do it "has an index of 0" do expect(@master.index).to eql(0) end - + it "is private" do expect(@master.is_private?).to eql(true) end - + it "has a depth of 0" do expect(@master.depth).to eql(0) end - + it "generates master node (Master)" do expect(@master.to_identifier).to eql("3442193e1bb70916e914552172cd4e2dbc9df811") expect(@master.to_fingerprint).to eql("3442193e") expect(@master.to_address).to eql("15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma") end - + it "generates a secret key" do expect(@master.private_key.to_hex).to eql("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") expect(@master.private_key.to_wif).to eql("L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW") end - + it "generates a public key" do expect(@master.public_key.to_hex).to eql("0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2") end - + it "generates a chain code" do expect(@master.chain_code_hex).to eql("873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508") end - + it "generates a serialized private key" do expect(@master.to_serialized_hex(:private)).to eql("0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") - expect(@master.to_serialized_address(:private)).to eql("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi") + expect(@master.to_bip32(:private)).to eql("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi") end - + it "generates a serialized public_key" do expect(@master.to_serialized_hex).to eql("0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2") - expect(@master.to_serialized_address).to eql("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8") + expect(@master.to_bip32).to eql("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8") end end - + describe "m/0p" do before do @node = @master.node_for_path "m/0p" end - + it "has an index of 2147483648" do expect(@node.index).to eql(2147483648) end - + it "is private" do expect(@node.is_private?).to eql(true) end - + it "has a depth of 1" do expect(@node.depth).to eql(1) end - + it "generates subnode" do expect(@node.to_identifier).to eql("5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7") expect(@node.to_fingerprint).to eql("5c1bd648") expect(@node.to_address).to eql("19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") expect(@node.private_key.to_wif).to eql("L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") - expect(@node.to_serialized_address(:private)).to eql("xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7") + expect(@node.to_bip32(:private)).to eql("xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") - expect(@node.to_serialized_address).to eql("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") + expect(@node.to_bip32).to eql("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") end end - + describe "m/0p.pub" do before do @node = @master.node_for_path "m/0p.pub" end - + it "has an index of 2147483648" do expect(@node.index).to eql(2147483648) end - + it "is private" do expect(@node.is_private?).to eql(true) end - + it "has a depth of 1" do expect(@node.depth).to eql(1) end - + it "generates subnode" do expect(@node.to_identifier).to eql("5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7") expect(@node.to_fingerprint).to eql("5c1bd648") expect(@node.to_address).to eql("19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh") end - + it "does not generate a private key" do expect(@node.private_key).to be_nil end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141") end - + it "does not generate a serialized private key" do expect { @node.to_serialized_hex(:private) }.to raise_error(MoneyTree::Node::PrivatePublicMismatch) end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") - expect(@node.to_serialized_address).to eql("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") + expect(@node.to_bip32).to eql("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") end end - + describe "m/0'/1" do before do @node = @master.node_for_path "m/0'/1" end - + it "has an index of 1" do expect(@node.index).to eql(1) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "has a depth of 2" do expect(@node.depth).to eql(2) end - + it "generates subnode" do expect(@node.to_identifier).to eql("bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe") expect(@node.to_fingerprint).to eql("bef5a2f9") expect(@node.to_address).to eql("1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") expect(@node.private_key.to_wif).to eql("KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") - expect(@node.to_serialized_address(:private)).to eql("xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs") + expect(@node.to_bip32(:private)).to eql("xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c") - expect(@node.to_serialized_address).to eql("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ") + expect(@node.to_bip32).to eql("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ") end end - + describe "M/0'/1" do before do @node = @master.node_for_path "M/0'/1" end - + it "has an index of 1" do expect(@node.index).to eql(1) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "has a depth of 2" do expect(@node.depth).to eql(2) end - + it "generates subnode" do expect(@node.to_identifier).to eql("bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe") expect(@node.to_fingerprint).to eql("bef5a2f9") expect(@node.to_address).to eql("1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj") end - + it "does not generate a private key" do expect(@node.private_key).to be_nil end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19") end - + it "generates a serialized private key" do expect { @node.to_serialized_hex(:private) }.to raise_error(MoneyTree::Node::PrivatePublicMismatch) end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c") - expect(@node.to_serialized_address).to eql("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ") + expect(@node.to_bip32).to eql("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ") end end - + describe "m/0'/1/2p/2" do before do @node = @master.node_for_path "m/0'/1/2p/2" end - + it "has an index of 2" do expect(@node.index).to eql(2) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "has a depth of 4" do expect(@node.depth).to eql(4) end - + it "generates subnode" do expect(@node.to_identifier).to eql("d880d7d893848509a62d8fb74e32148dac68412f") expect(@node.to_fingerprint).to eql("d880d7d8") expect(@node.to_address).to eql("1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4") expect(@node.private_key.to_wif).to eql("KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4") - expect(@node.to_serialized_address(:private)).to eql("xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334") + expect(@node.to_bip32(:private)).to eql("xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29") - expect(@node.to_serialized_address).to eql("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV") + expect(@node.to_bip32).to eql("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV") end end - + describe "m/0'/1/2'/2/1000000000" do before do @node = @master.node_for_path "m/0'/1/2'/2/1000000000" end - + it "has an index of 1000000000" do expect(@node.index).to eql(1000000000) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "has a depth of 2" do expect(@node.depth).to eql(5) end - + it "generates subnode" do expect(@node.to_identifier).to eql("d69aa102255fed74378278c7812701ea641fdf32") expect(@node.to_fingerprint).to eql("d69aa102") expect(@node.to_address).to eql("1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8") expect(@node.private_key.to_wif).to eql("Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8") - expect(@node.to_serialized_address(:private)).to eql("xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76") + expect(@node.to_bip32(:private)).to eql("xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011") - expect(@node.to_serialized_address).to eql("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy") + expect(@node.to_bip32).to eql("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy") end end end end - + describe "Test vector 2" do describe "from a seed" do before do @master = MoneyTree::Master.new seed_hex: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" end - + describe "m" do it "has an index of 0" do expect(@master.index).to eql(0) end - + it "has a depth of 0" do expect(@master.depth).to eql(0) end - + it "is private" do expect(@master.is_private?).to eql(true) end - + it "generates master node (Master)" do expect(@master.to_identifier).to eql("bd16bee53961a47d6ad888e29545434a89bdfe95") expect(@master.to_fingerprint).to eql("bd16bee5") @@ -434,71 +434,71 @@ expect(@master.private_key.to_hex).to eql("4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e") expect(@master.private_key.to_wif).to eql("KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy") end - + it "generates a public key" do expect(@master.public_key.to_hex).to eql("03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7") end - + it "generates a chain code" do expect(@master.chain_code_hex).to eql("60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689") end - + it "generates a serialized private key" do expect(@master.to_serialized_hex(:private)).to eql("0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e") - expect(@master.to_serialized_address(:private)).to eql("xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U") + expect(@master.to_bip32(:private)).to eql("xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U") end - + it "generates a serialized public_key" do expect(@master.to_serialized_hex).to eql("0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7") - expect(@master.to_serialized_address).to eql("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") + expect(@master.to_bip32).to eql("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") end end - + describe "m/0 (testing imported private key)" do before do @master = MoneyTree::Master.new private_key: @master.private_key, chain_code: @master.chain_code @node = @master.node_for_path "m/0" end - + it "has an index of 0" do expect(@node.index).to eql(0) end - + it "has a depth of 1" do expect(@node.depth).to eql(1) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "generates subnode" do expect(@node.to_identifier).to eql("5a61ff8eb7aaca3010db97ebda76121610b78096") expect(@node.to_fingerprint).to eql("5a61ff8e") expect(@node.to_address).to eql("19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e") expect(@node.private_key.to_wif).to eql("L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e") - expect(@node.to_serialized_address(:private)).to eql("xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt") + expect(@node.to_bip32(:private)).to eql("xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea") - expect(@node.to_serialized_address).to eql("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH") + expect(@node.to_bip32).to eql("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH") end end @@ -507,271 +507,271 @@ @master = MoneyTree::Master.new public_key: "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", chain_code: @master.chain_code @node = @master.node_for_path "M/0" end - + it "has an index of 0" do expect(@node.index).to eql(0) end - + it "has a depth of 1" do expect(@node.depth).to eql(1) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "generates subnode" do expect(@node.to_identifier).to eql("5a61ff8eb7aaca3010db97ebda76121610b78096") expect(@node.to_fingerprint).to eql("5a61ff8e") expect(@node.to_address).to eql("19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ") end - + it "does not generate a private key" do expect(@node.private_key).to be_nil end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c") end - + it "does not generate a serialized private key" do expect { @node.to_serialized_hex(:private) }.to raise_error(MoneyTree::Node::PrivatePublicMismatch) - expect { @node.to_serialized_address(:private) }.to raise_error(MoneyTree::Node::PrivatePublicMismatch) + expect { @node.to_bip32(:private) }.to raise_error(MoneyTree::Node::PrivatePublicMismatch) end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea") - expect(@node.to_serialized_address).to eql("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH") + expect(@node.to_bip32).to eql("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH") end end - + describe "m/0/2147483647'" do before do @node = @master.node_for_path "m/0/2147483647'" end - + it "has an index of 2147483647" do expect(@node.index).to eql(4294967295) end - + it "has a depth of 2" do expect(@node.depth).to eql(2) end - + it "is private" do expect(@node.is_private?).to eql(true) end - + it "generates subnode" do expect(@node.to_identifier).to eql("d8ab493736da02f11ed682f88339e720fb0379d1") expect(@node.to_fingerprint).to eql("d8ab4937") expect(@node.to_address).to eql("1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93") expect(@node.private_key.to_wif).to eql("L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93") - expect(@node.to_serialized_address(:private)).to eql("xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9") + expect(@node.to_bip32(:private)).to eql("xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b") - expect(@node.to_serialized_address).to eql("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a") + expect(@node.to_bip32).to eql("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a") end end - + describe "m/0/2147483647'/1" do before do @node = @master.node_for_path "m/0/2147483647'/1" end - + it "has an index of 1" do expect(@node.index).to eql(1) end - + it "has a depth of 3" do expect(@node.depth).to eql(3) end - + it "is private" do expect(@node.is_private?).to eql(false) end - + it "generates subnode" do expect(@node.to_identifier).to eql("78412e3a2296a40de124307b6485bd19833e2e34") expect(@node.to_fingerprint).to eql("78412e3a") expect(@node.to_address).to eql("1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7") expect(@node.private_key.to_wif).to eql("KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7") - expect(@node.to_serialized_address(:private)).to eql("xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef") + expect(@node.to_bip32(:private)).to eql("xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9") - expect(@node.to_serialized_address).to eql("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon") + expect(@node.to_bip32).to eql("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon") end end - + describe "m/0/2147483647p/1/2147483646p" do before do @node = @master.node_for_path "m/0/2147483647p/1/2147483646p" end - + it "has an index of 4294967294" do expect(@node.index).to eql(4294967294) end - + it "has a depth of 4" do expect(@node.depth).to eql(4) end - + it "is private" do expect(@node.is_private?).to eql(true) end - + it "generates subnode" do expect(@node.to_identifier).to eql("31a507b815593dfc51ffc7245ae7e5aee304246e") expect(@node.to_fingerprint).to eql("31a507b8") expect(@node.to_address).to eql("15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d") expect(@node.private_key.to_wif).to eql("L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d") - expect(@node.to_serialized_address(:private)).to eql("xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc") + expect(@node.to_bip32(:private)).to eql("xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0") - expect(@node.to_serialized_address).to eql("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL") + expect(@node.to_bip32).to eql("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL") end end - + describe "m/0/2147483647p/1/2147483646p/2" do before do @node = @master.node_for_path "m/0/2147483647p/1/2147483646p/2" end - + it "has an index of 2" do expect(@node.index).to eql(2) end - + it "has a depth of 4" do expect(@node.depth).to eql(5) end - + it "is public" do expect(@node.is_private?).to eql(false) end - + it "generates subnode" do expect(@node.to_identifier).to eql("26132fdbe7bf89cbc64cf8dafa3f9f88b8666220") expect(@node.to_fingerprint).to eql("26132fdb") expect(@node.to_address).to eql("14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt") end - + it "generates a private key" do expect(@node.private_key.to_hex).to eql("bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23") expect(@node.private_key.to_wif).to eql("L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK") end - + it "generates a public key" do expect(@node.public_key.to_hex).to eql("024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c") end - + it "generates a chain code" do expect(@node.chain_code_hex).to eql("9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271") end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23") - expect(@node.to_serialized_address(:private)).to eql("xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j") + expect(@node.to_bip32(:private)).to eql("xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c") - expect(@node.to_serialized_address).to eql("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt") + expect(@node.to_bip32).to eql("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt") end end end end - + describe "negative index" do before do @master = MoneyTree::Master.new seed_hex: "000102030405060708090a0b0c0d0e0f" @node = @master.node_for_path "m/0'/-1" end - + it "has an index of 1" do expect(@node.index).to eql(-1) end - + it "is public" do expect(@node.is_private?).to eql(true) end - + it "has a depth of 2" do expect(@node.depth).to eql(2) end - + it "generates a serialized private key" do expect(@node.to_serialized_hex(:private)).to eql("0488ade4025c1bd648ffffffff0f9ca680ee23c81a305d96b86f811947e65590200b6f74d66ecf83936313a9c900235893db08ad0efc6ae4a1eac5b31a90a7d0906403d139d4d7f3c6796fb42c4e") - expect(@node.to_serialized_address(:private)).to eql("xprv9wTYmMFvAM7JHf3RuUidc24a4y2t4gN7aNP5ABreWAqt6BUBcf6xE8RNQxj2vUssYWM8iAZiZi5H1fmKkkpXjtwDCDv1pg8fSfQMk9rhHYt") + expect(@node.to_bip32(:private)).to eql("xprv9wTYmMFvAM7JHf3RuUidc24a4y2t4gN7aNP5ABreWAqt6BUBcf6xE8RNQxj2vUssYWM8iAZiZi5H1fmKkkpXjtwDCDv1pg8fSfQMk9rhHYt") end - + it "generates a serialized public_key" do expect(@node.to_serialized_hex).to eql("0488b21e025c1bd648ffffffff0f9ca680ee23c81a305d96b86f811947e65590200b6f74d66ecf83936313a9c902adb7979a5e99bf8acdfec3680bf482feac9898b28808c22d47db62e98de5d3fa") - expect(@node.to_serialized_address).to eql("xpub6ASuArnozifbW97u1WFdyA1JczsNU95xwbJfxaGG4WNrxyoLACRCmvjrGEojsRsoZULf5FyZXv6AWAtce2UErsshvkpjNaT1fP6sMgTZdc1") + expect(@node.to_bip32).to eql("xpub6ASuArnozifbW97u1WFdyA1JczsNU95xwbJfxaGG4WNrxyoLACRCmvjrGEojsRsoZULf5FyZXv6AWAtce2UErsshvkpjNaT1fP6sMgTZdc1") end end - + describe "importing node" do - describe ".from_serialized_address(address)" do + describe ".from_bip32(address)" do it "imports a valid private node address" do - @node = MoneyTree::Node.from_serialized_address "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7" + @node = MoneyTree::Node.from_bip32 "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7" expect(@node.private_key.to_hex).to eql("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") expect(@node.index).to eql(2147483648) expect(@node.is_private?).to eql(true) @@ -780,9 +780,9 @@ expect(@node.chain_code_hex).to eql("47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141") expect(@node.parent_fingerprint).to eql("3442193e") end - + it "imports a valid public node address" do - @node = MoneyTree::Node.from_serialized_address "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw" + @node = MoneyTree::Node.from_bip32 "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw" expect(@node.private_key).to be_nil expect(@node.index).to eql(2147483648) expect(@node.is_private?).to eql(true) @@ -797,9 +797,9 @@ describe "deriving a child node" do describe "#node_for_path" do it "correctly derives from a node with a chain code represented in 31 bytes" do - @node = MoneyTree::Node.from_serialized_address "tpubD6NzVbkrYhZ4WM42MZZmUZ7LjxyjBf5bGjEeLf9nJnMZqocGJWu94drvpqWsE9jE7k3h22v6gjpPGnqgBrqwGsRYwDXVRfQ2M9dfHbXP5zA" + @node = MoneyTree::Node.from_bip32 "tpubD6NzVbkrYhZ4WM42MZZmUZ7LjxyjBf5bGjEeLf9nJnMZqocGJWu94drvpqWsE9jE7k3h22v6gjpPGnqgBrqwGsRYwDXVRfQ2M9dfHbXP5zA" @subnode = @node.node_for_path('m/1') - expect(@subnode.to_serialized_address(network: :bitcoin_testnet)).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY") + expect(@subnode.to_bip32(network: :bitcoin_testnet)).to eql("tpubDA7bCxb3Nrcz2ChXyPqXxbG4q5oiAZUHR7wD3LAiXukuxmT65weWw84XYmjhkJTkJEM6LhNWioWTpKEkQp7j2fgVccj3PPc271xHDeMsaTY") end end end From 4a2b902d71576ec9726fc0b82c611282d208c241 Mon Sep 17 00:00:00 2001 From: Kasper Holbek Jensen Date: Fri, 26 Feb 2016 13:10:11 +0200 Subject: [PATCH 17/23] make readme use to/from_bip32 --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index d2d1cc3..d29b0c3 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ These instructions assume you have a decent understanding of how Bitcoin wallets ### Create a Master Node (seed) -To create a new HD Wallet, we're going to create a tree structure of private/public keypairs (nodes). You'll first want to start with a master node. This master node should be seeded with at least 16 random bytes but preferably 32 random bytes from a cryptographically secure PRNG (pseudo-random number generator). +To create a new HD Wallet, we're going to create a tree structure of private/public keypairs (nodes). You'll first want to start with a master node. This master node should be seeded with at least 16 random bytes but preferably 32 random bytes from a cryptographically secure PRNG (pseudo-random number generator). DO NOT use a user generated password. Keep in mind that whoever controls the seed controls ALL coins in the entire tree, so it should not be left up to a human brain, because humans tend to follow patterns and patterns are subject to brute force attacks. Luckily, MoneyTree includes the seed generation by default so you don't need to create this on your own. @@ -109,19 +109,19 @@ DO NOT use a user generated password. Keep in mind that whoever controls the see @master.public_key.to_hex => "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2" -@master.chain_code_hex +@master.chain_code_hex => "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508" # Look up chain codes in the BIP0032 spec @master.to_serialized_hex(:private) => "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35" -@master.to_serialized_address(:private) +@master.to_bip32(:private) => "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" @master.to_serialized_hex => "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2" -@master.to_serialized_address +@master.to_bip32 => "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" ``` @@ -140,10 +140,10 @@ To generate a child node from a given path: @node.depth => 2 -@node.to_serialized_address(:private) +@node.to_bip32(:private) => "xprv9ww7sMFLzJN15m7zX5JEBXQrQq8h4fU8PVqd929Hjy3xNSMzeBf163idMNBSq47DdCakyZTK7KcC2nbz3jqUkpJj8ZR4FqrijcFcFmcoBAe" -@node.to_serialized_address +@node.to_bip32 => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr" ``` @@ -164,22 +164,22 @@ Because we need multiple pieces of info to reconstruct nodes in a tree, when we' ``` 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). - + 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. To export a node to a serialized address, you can do: ```ruby -@node.to_serialized_address(:private) # for private keys +@node.to_bip32(:private) # for private keys => "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" -@node.to_serialized_address +@node.to_bip32 => "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" ``` - + To import from a serialized address: (either public or private) ```ruby -@node = MoneyTree::Node.from_serialized_address "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" +@node = MoneyTree::Node.from_bip32 "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" => MoneyTree::Node instance ``` @@ -198,10 +198,10 @@ For example: ```ruby @node = @master.node_for_path("M/0/3") # or "m/0/3.pub" or "M/0/3.pub"...these are equivalent -@node.to_serialized_address +@node.to_bip32 => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr" -@node.to_serialized_address(:private) +@node.to_bip32(:private) -> raises MoneyTree::Node::PrivatePublicMismatch error ``` @@ -209,13 +209,13 @@ For example: You can also import a node using only a public key. Keep in mind that this node will only be able to generate other public-key only nodes. You will not be able to derive child private keys using this node. ```ruby -@node = MoneyTree::Node.from_serialized_address("xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr") +@node = MoneyTree::Node.from_bip32("xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr") => MoneyTree::Node instance -@node.to_serialized_address +@node.to_bip32 => "xpub6AvUGrnEpfvJJFCTd6qEYfMaxryBU8BykimDwQYuJJawFEh9BiyFdr37Cc4wEKCWWv7TsFQRUMdezXVqV9cfBUbeUEgNYCCP4omxULbNaRr" -@node.to_serialized_address(:private) +@node.to_bip32(:private) -> raises MoneyTree::Node::PrivatePublicMismatch error ``` From 70ac4569c227eea2c81cae932d60a65c6b90a958 Mon Sep 17 00:00:00 2001 From: Eric Scrivner Date: Thu, 11 May 2017 19:00:47 -0700 Subject: [PATCH 18/23] [Fix] Descend From StandardError Not Exception **What changed?** * Have all internal exceptions descend from `StandardError` rather than `Exception`. **Why?** * This is a Ruby gotcha (see link below). Essentially using `rescue => e` will not catch any of these exceptions as this only catches subclasses of `StandardError`. This can cause users of the library to get unexpected results when implementing error handling this way. **References** [Rescue StandardError, Not Exception](https://robots.thoughtbot.com/rescue-standarderror-not-exception) --- lib/money-tree/key.rb | 12 ++++++------ lib/money-tree/node.rb | 8 ++++---- spec/lib/money-tree/private_key_spec.rb | 12 +++++++++++- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/money-tree/key.rb b/lib/money-tree/key.rb index 0289736..37feef5 100644 --- a/lib/money-tree/key.rb +++ b/lib/money-tree/key.rb @@ -7,12 +7,12 @@ class Key include OpenSSL include Support extend Support - class KeyInvalid < Exception; end - class KeyGenerationFailure < Exception; end - class KeyImportFailure < Exception; end - class KeyFormatNotFound < Exception; end - class InvalidWIFFormat < Exception; end - class InvalidBase64Format < Exception; end + class KeyInvalid < StandardError; end + class KeyGenerationFailure < StandardError; end + class KeyImportFailure < StandardError; end + class KeyFormatNotFound < StandardError; end + class InvalidWIFFormat < StandardError; end + class InvalidBase64Format < StandardError; end attr_reader :options, :key, :raw_key attr_accessor :ec_key diff --git a/lib/money-tree/node.rb b/lib/money-tree/node.rb index e361c08..9def6bf 100644 --- a/lib/money-tree/node.rb +++ b/lib/money-tree/node.rb @@ -5,10 +5,10 @@ class Node attr_reader :private_key, :public_key, :chain_code, :is_private, :depth, :index, :parent - class PublicDerivationFailure < Exception; end - class InvalidKeyForIndex < Exception; end - class ImportError < Exception; end - class PrivatePublicMismatch < Exception; end + class PublicDerivationFailure < StandardError; end + class InvalidKeyForIndex < StandardError; end + class ImportError < StandardError; end + class PrivatePublicMismatch < StandardError; end def initialize(opts = {}) opts.each { |k, v| instance_variable_set "@#{k}", v } diff --git a/spec/lib/money-tree/private_key_spec.rb b/spec/lib/money-tree/private_key_spec.rb index d792333..7541838 100644 --- a/spec/lib/money-tree/private_key_spec.rb +++ b/spec/lib/money-tree/private_key_spec.rb @@ -93,7 +93,17 @@ describe "parse_raw_key" do it "returns error if key is not Bignum, hex, base64, or wif formatted" do expect { @key = MoneyTree::PrivateKey.new(key: "Thisisnotakey") }.to raise_error(MoneyTree::Key::KeyFormatNotFound) - + end + + it "raises an error that can be caught using a standard exception block" do + exception_raised = false + + begin + MoneyTree::PrivateKey.new(key: "Thisisnotakey") + rescue => ex + exception_raised = true + end + fail unless exception_raised end end From 7ae404b695e1424f62545f0d25004937bbebe3a6 Mon Sep 17 00:00:00 2001 From: Darren Rush Date: Wed, 16 Aug 2017 17:31:12 -0700 Subject: [PATCH 19/23] Update key.rb Get rid of Bignum deprecation for ruby 2.4+ --- lib/money-tree/key.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/money-tree/key.rb b/lib/money-tree/key.rb index 0289736..9e29536 100644 --- a/lib/money-tree/key.rb +++ b/lib/money-tree/key.rb @@ -70,7 +70,7 @@ def set_public_key(opts = {}) end def parse_raw_key - result = if raw_key.is_a?(Bignum) then from_bignum + 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 @@ -81,7 +81,7 @@ def parse_raw_key result.downcase end - def from_bignum(bignum = raw_key) + def from_integer(bignum = raw_key) # TODO: does this need a byte size specification? int_to_hex(bignum) end @@ -213,7 +213,7 @@ def set_point(int = to_i, opts = {}) end def parse_raw_key - result = if raw_key.is_a?(Bignum) + result = if raw_key.is_a?(Integer) set_point raw_key elsif hex_format? set_point hex_to_int(raw_key), compressed: false From 1538ccf773c48822378c6e06d1c0ff3038968b1c Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Tue, 31 Oct 2017 17:27:35 -0700 Subject: [PATCH 20/23] Support more libssl library names so this will work on heroku --- lib/openssl_extensions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openssl_extensions.rb b/lib/openssl_extensions.rb index aacc322..ad5e97d 100644 --- a/lib/openssl_extensions.rb +++ b/lib/openssl_extensions.rb @@ -6,7 +6,7 @@ module MoneyTree module OpenSSLExtensions extend FFI::Library - ffi_lib 'ssl' + ffi_lib ['libssl.so.1.0.0', 'libssl.so.10', 'ssl'] NID_secp256k1 = 714 POINT_CONVERSION_COMPRESSED = 2 From d98b642a5af65360517cc30b940d42f740317f15 Mon Sep 17 00:00:00 2001 From: hank Date: Tue, 13 Mar 2018 23:13:00 -0700 Subject: [PATCH 21/23] adds extra libssl location --- lib/openssl_extensions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openssl_extensions.rb b/lib/openssl_extensions.rb index ad5e97d..44b7f5e 100644 --- a/lib/openssl_extensions.rb +++ b/lib/openssl_extensions.rb @@ -6,7 +6,7 @@ module MoneyTree module OpenSSLExtensions extend FFI::Library - ffi_lib ['libssl.so.1.0.0', 'libssl.so.10', 'ssl'] + ffi_lib ['libssl.so.1.0.0', 'libssl.so.10', 'libssl1.0.0', 'ssl'] NID_secp256k1 = 714 POINT_CONVERSION_COMPRESSED = 2 From 2686577092d3d4e2af8de8552782ff633e61e652 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Thu, 24 May 2018 09:01:24 -0700 Subject: [PATCH 22/23] 0.10.0 Release --- README.md | 7 ++++++- certs/jvergeldedios.pem | 20 -------------------- certs/mattatgemco.pem | 24 ++++++++++++++++++++++++ lib/money-tree/version.rb | 2 +- money-tree.gemspec | 6 +++--- 5 files changed, 34 insertions(+), 25 deletions(-) delete mode 100644 certs/jvergeldedios.pem create mode 100644 certs/mattatgemco.pem diff --git a/README.md b/README.md index d29b0c3..70a85db 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ For instance: They are all equivalent ways of saying the same thing, but the first two are just a more human readable shorthand notation. You are free to use whichever notation you prefer. This gem will parse it. -To add just a little more confusion to the mix, some wallets will use negative `i` values to also denote private derivation. Any `i` value that is negative will be processed using private derivation by this library. e.g. `"m/-1/1"`. +To add just a little more confusion to the mix, some wallets will use negative `i` values to also denote private derivation. Any `i` value that is negative will be processed using private derivation by this library. e.g. `"m/-1/1"`. (NOTE: known issue, see below) ## Contributing @@ -251,3 +251,8 @@ To add just a little more confusion to the mix, some wallets will use negative ` 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request + +## Known Issues (PRs welcome) + +- Segwit ([BIP49](https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki)) address generation is not supported +- Use of negative integers in paths does _not_ produce the correct hardened derivation. diff --git a/certs/jvergeldedios.pem b/certs/jvergeldedios.pem deleted file mode 100644 index 825fa01..0000000 --- a/certs/jvergeldedios.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXDCCAkSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA6MQ8wDQYDVQQDDAZqdWxp -YW4xEzARBgoJkiaJk/IsZAEZFgNnZW0xEjAQBgoJkiaJk/IsZAEZFgJjbzAeFw0x -NTA0MDMwODAwMTdaFw0xNjA0MDIwODAwMTdaMDoxDzANBgNVBAMMBmp1bGlhbjET -MBEGCgmSJomT8ixkARkWA2dlbTESMBAGCgmSJomT8ixkARkWAmNvMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0qCsOUQyTRA4f7WxoU2ctpUO5+eLZw8 -ILvHJy/0dC2nnIgib+FaFA8TRIGw6fjX2hQ43QJyO36zkjUhAwNu/+TBCfG+Grut -2dI9XmqU5Z6PvvXRj6Gu5IkDeIVDKILZv3bDugHJalre4BUKnwPYv5WpZ/e/c6+z -E4fwe4ZQzqslSXZo0o/wFvs5dGuIoP93bazSeqddre0JKFFiEP/SNGP9e/lXEd2V -rLFYAY409no9J+VQOHP0Nu9ShlCZp8M45abKd2ykuSDaT6jH9YcUHBr3/IEsA4+f -DypeS1ySVvad+o8iTnfz1Hyohz4ORm3spf0BOtGI/Swbv3LObZJqkwIDAQABo20w -azAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUERhBDW/kkq7tz2hN -hPNHtounnkowGAYDVR0RBBEwD4ENanVsaWFuQGdlbS5jbzAYBgNVHRIEETAPgQ1q -dWxpYW5AZ2VtLmNvMA0GCSqGSIb3DQEBBQUAA4IBAQAgnumhg8ST8JohYWcoDoQt -3BUG5rbfJ/qE0utOt6esi9d6Vz6YHpiT08woaj68OWl9U9N4vjox+ckkTRs93KBd -y3thnK9cIEAzoEZs3BBguXYOoFLughGD7hEuLlRYbwZzyIdzx/XdLgsy5Di8Gqaa -RKurfXP+dERQww34CUhmhOLO4/rYGqaD88so0MzCImgS+OX+G4ppqd38iQpaxCHL -tdc4VS7IlSRxlZ3dBOgiigy9GXpJ+7F831AqjxL39EPwdr7RguTNz+pi//RKaT/U -IlpVB+Xfk0vQdP7iYfjGxDzUf0FACMjsR95waJmadKW1Iy6STw2hwPhYIQz1Hu1A ------END CERTIFICATE----- diff --git a/certs/mattatgemco.pem b/certs/mattatgemco.pem new file mode 100644 index 0000000..afcbcd5 --- /dev/null +++ b/certs/mattatgemco.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEHDCCAoSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFtYXR0 +L0RDPWdlbS9EQz1jbzAeFw0xODA1MzAyMTQwNDZaFw0xOTA1MzAyMTQwNDZaMBwx +GjAYBgNVBAMMEW1hdHQvREM9Z2VtL0RDPWNvMIIBojANBgkqhkiG9w0BAQEFAAOC +AY8AMIIBigKCAYEAxfbjMHFlxA2P+4YWPagKoGAMi4078imgXdFbD3Rloe6cGfYp +IMUQitiHrKi6fhSE0UjXmoP3qnYFddm1enN9zUAFRhHWv7xpINqSqss4PYAb5Anl +RYZu3jromop5aVodi15HUfu5z27MvBm4rAaN/dDRfh/rT2hDbTTh0HmvEaPUDfX6 +TyflAttfabFvtY4qsD+ao8tks0DytqyuEWZ0tvQ6upOgHRNNuYDwDZB1T9v2dq2w +3goJFmOKBMMn7UH8WMjD3HiOuRD4tWhq5xWLjBqjzFlVPlZPgdCNyXeMMnLXER98 +NY35cVWFFuqG+kZwy4MFKdE9WFTocLZxLFo0VVTNSpPara9HirbHtIo9jZNuop4S +g4JTf1F8dIWYii3sXoAYZfkl6rHVRP0G/OV5LcTfSS3QkmI5hNltz5FZzc+qI6S1 +rTR1ZwTy1rRI3coFY7vDRaFWBoMbbo/DytgCE3+rfbVDxQrJa4aZ0iYDhu8LXEA1 +VTtpf1EWYCOsYE1TAgMBAAGjaTBnMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0G +A1UdDgQWBBQ6QoDNre7LFgOukH2Cv+RqZyfUzjAWBgNVHREEDzANgQttYXR0QGdl +bS5jbzAWBgNVHRIEDzANgQttYXR0QGdlbS5jbzANBgkqhkiG9w0BAQsFAAOCAYEA +kOxYnOsB+NwHwLc2lHEZ8ubxanq2qIZDhvVQ4M31gwmba43xO7vq0ktFxYRvozs4 +74dQ6bmY2e7njoFgeutyJwxulA+BC71mDQA1s4WsZo7Z2TRgB0GViVqHrzq+jY+M +p9mTHQqKH+2j0P9T4DXSzq4qOaBA3YROAwAzYI9N8MObeWkRt2pZ4zYQrAniP2nd +wzXs/G5lWbbntVcvQOfAAXBipSJ3X5P2EGpUytP9ZpGdezY5HZzuiJFcmCf1CM3t +VX4NZjbJak9gOY0AFD0Aw497sYenm0VBExclOmeRuZLffpWteTTL//utpG3bbFPl +jQ78uzsrexYTYW5IshjfSIf3TZxm50Z45pyOTow5EOP1Nd7OmKOcI8hrLGv5+AlD +hCnomUTUNsM4Rjwl5rzQiIn3ezv6+0tlg4rWJmVTuOGwcHk/oj1In2sPjCqm0pgx +TLnMa8gr6aUpuHR5s2N4ZH0Q2YIsaD6cv7DYXt+G4MRut3njOYHfkqsSVykO6hvr +-----END CERTIFICATE----- diff --git a/lib/money-tree/version.rb b/lib/money-tree/version.rb index c96ac35..612eaa6 100644 --- a/lib/money-tree/version.rb +++ b/lib/money-tree/version.rb @@ -1,3 +1,3 @@ module MoneyTree - VERSION = "0.9.0" + VERSION = "0.10.0" end diff --git a/money-tree.gemspec b/money-tree.gemspec index f635c20..43c1f72 100644 --- a/money-tree.gemspec +++ b/money-tree.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |spec| spec.email = ["winkelspecht@gmail.com"] spec.description = %q{A Ruby Gem implementation of Bitcoin HD Wallets} spec.summary = %q{Bitcoin Hierarchical Deterministic Wallets in Ruby! (Bitcoin standard BIP0032)} - spec.homepage = "" + spec.homepage = "https://github.com/gemhq/money-tree" spec.license = "MIT" spec.files = `git ls-files`.split($/) @@ -18,8 +18,8 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - # used with gem i coin-op -P HighSecurity - spec.cert_chain = ["certs/jvergeldedios.pem"] + # used with gem install ... -P HighSecurity + spec.cert_chain = ["certs/mattatgemco.pem"] # Sign gem when evaluating spec with `gem` command # unless ENV has set a SKIP_GEM_SIGNING if ($0 =~ /gem\z/) and not ENV.include?("SKIP_GEM_SIGNING") From 250ec7c87b226045658377246fb94ee5d8a5be32 Mon Sep 17 00:00:00 2001 From: Mark Hudson Date: Thu, 6 Sep 2018 14:04:41 -0700 Subject: [PATCH 23/23] adds Gemfile.lock manifest file for integration with external security scanner services, i.e. https://snyk.io/ --- Gemfile.lock | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..5cc4965 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,62 @@ +PATH + remote: . + specs: + money-tree (0.10.0) + ffi + +GEM + remote: https://rubygems.org/ + specs: + coderay (1.1.2) + coveralls (0.8.22) + json (>= 1.8, < 3) + simplecov (~> 0.16.1) + term-ansicolor (~> 1.3) + thor (~> 0.19.4) + tins (~> 1.6) + diff-lcs (1.3) + docile (1.3.1) + ffi (1.9.25) + json (2.1.0) + method_source (0.9.0) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + rake (12.3.1) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + simplecov (0.16.1) + docile (~> 1.1) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.2) + term-ansicolor (1.6.0) + tins (~> 1.0) + thor (0.19.4) + tins (1.16.3) + +PLATFORMS + ruby + +DEPENDENCIES + bundler (~> 1.3) + coveralls + money-tree! + pry + rake + rspec + simplecov + +BUNDLED WITH + 1.16.1