class ExoCrypto::Bip44WalletProvider
Constants
- TESTNET_TICKER
Public Class Methods
address_details(sub_wallet, is_testnet, include_private)
click to toggle source
see: github.com/citizen010/bitcoin-prefixes-address-list
# File lib/exocrypto/bip44_wallet_provider.rb, line 60 def self.address_details(sub_wallet, is_testnet, include_private) if include_private { :bitcoin_address => sub_wallet.bitcoin_address(testnet: is_testnet), :ethereum_address => sub_wallet.ethereum_address, :public_hex => sub_wallet.public_key, :private_hex => sub_wallet.private_key, :private_wif => sub_wallet.wif(testnet: is_testnet) } else { :bitcoin_address => sub_wallet.bitcoin_address(testnet: is_testnet), :ethereum_address => sub_wallet.ethereum_address, :public_hex => sub_wallet.public_key } end end
btc_address_details_of(seed, path, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 126 def self.btc_address_details_of(seed, path, is_testnet=false) wallet = Bip44::Wallet.from_seed(seed, path) details = Bip44WalletProvider.address_details(wallet, is_testnet, true) details[:address] = details[:bitcoin_address] details.delete(:bitcoin_address) details.delete(:ethereum_address) details end
btc_base58_to_int(base58_val)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 189 def self.btc_base58_to_int(base58_val) alpha = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' int_val, base = 0, alpha.size base58_val.reverse.split(//).each_with_index do |char, index| char_index = alpha.index(char) int_val += char_index * base**index end int_val end
btc_decode_base58(base58_val)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 200 def self.btc_decode_base58(base58_val) nzeroes = base58_val.chars.find_index { |c| c != '1' } || base58_val.length - 1 prefix = nzeroes < 0 ? '' : '00' * nzeroes decoded = Bip44WalletProvider.int_to_hex(Bip44WalletProvider.btc_base58_to_int(base58_val)) [prefix + decoded].pack('H*') end
btc_encode_base58(hex)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 184 def self.btc_encode_base58(hex) leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2 ('1' * leading_zero_bytes) + Bip44WalletProvider.btc_int_to_base58(hex.to_i(16)) end
btc_int_to_base58(int_val, leading_zero_bytes=0)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 173 def self.btc_int_to_base58(int_val, leading_zero_bytes=0) alpha = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' base58_val, base = '', alpha.size while int_val > 0 int_val, remainder = int_val.divmod(base) base58_val = alpha[remainder] + base58_val end base58_val end
btc_privkey_of_wif(wif, is_compressed=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 218 def self.btc_privkey_of_wif(wif, is_compressed=false) result = Bip44WalletProvider.btc_decode_base58(wif)[1..-5].unpack('H*').first if is_compressed result = result[0...-2] end result end
btc_pubkey_of_privkey(hex, is_compressed=false, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 310 def self.btc_pubkey_of_privkey(hex, is_compressed=false, is_testnet=false) priv = Bip44WalletProvider.btc_wif_of_privkey(hex, is_compressed, is_testnet) Bip44WalletProvider.btc_pubkey_of_wif(priv, is_compressed, is_testnet) end
btc_pubkey_of_wif(wif, is_compressed=false, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 226 def self.btc_pubkey_of_wif(wif, is_compressed=false, is_testnet=false) priv_key = Bip44WalletProvider.btc_privkey_of_wif(wif, is_compressed) group = OpenSSL::PKey::EC::Group.new('secp256k1') public_key = group.generator.mul(OpenSSL::BN.new(priv_key, 16)) decompressed_public_key_hex = public_key.to_bn.to_s(16) # NOTE: for compression public_key_hex = decompressed_public_key_hex if is_compressed x = decompressed_public_key_hex[2..-1][0..63] y = decompressed_public_key_hex[2..-1][64..-1] leader = y.to_i(16) % 2 == 0 ? '02' : '03' public_key_hex = leader + x end public_key_hash = Bip44WalletProvider.ripemd160(Bip44WalletProvider.sha256(public_key_hex)) network = is_testnet ? '6f' : '00' data = network + public_key_hash public_address = Bip44WalletProvider.btc_encode_base58(data + Bip44WalletProvider.checksum(data)) { :decompressed_public_key => decompressed_public_key_hex, :public_key => public_key_hex, :bitcoin_address => public_address } end
btc_pubkey_of_wif_ecdsa(wif, is_compressed=false, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 254 def self.btc_pubkey_of_wif_ecdsa(wif, is_compressed=false, is_testnet=false) priv_key = Bip44WalletProvider.btc_privkey_of_wif(wif, is_compressed) # kpub = kpriv * G curve = ECDSA::Group::Secp256k1 pub_key_point = curve.generator.multiply_by_scalar(priv_key.to_i(16)) # # uncompressed, pub key is in form 0x04 + x + y # compressed, pub key is in form # 0x02 + x if y is even # 0x03 + x if y is odd # Getting encodings right is difficult...pub.x is a Bignum. Bignum + Bignum is # a biggernum, which isn't really what we want. We want string concatenation. To do that, # we translate to hex, concatenate the hex, and pack it back into a string to # concatenate with our leading byte # # pub.x is a Bignum, # so we must concatenate our compression byte with the hex representation of pub_key.x # decompressed_pub_key = "\x04" + [pub_key_point.x.to_s(16)].pack('H*') + [pub_key_point.y.to_s(16)].pack('H*') pub_key = decompressed_pub_key if is_compressed leader = pub_key_point.y % 2 == 0 ? "\x02" : "\x03" pub_key = leader + [pub_key_point.x.to_s(16)].pack('H*') end # ripe160(sha256(pub_key)) pub_key_hash = Digest::RMD160.digest(Digest::SHA256.digest(pub_key)) # # prepend version to our double hashed pub key, append checksum # Bitcoin: 0x00 # Testnet: 0x6f # network = is_testnet ? "\x6f" : "\x00" pub_key_hash_and_version = network + pub_key_hash # add checksum pub_key_hash_and_version_str = pub_key_hash_and_version.unpack('H*').first pub_key_hash_and_version_and_checksum = pub_key_hash_and_version_str + Bip44WalletProvider.checksum( pub_key_hash_and_version_str) # base58 encode version, hash, checksum pub_addr = Bip44WalletProvider.btc_encode_base58(pub_key_hash_and_version_and_checksum) { :decompressed_public_key => decompressed_pub_key.unpack("H*").first, :public_key => pub_key.unpack("H*").first, :bitcoin_address => pub_addr } end
btc_wif_of_privkey(hex, is_compressed=false, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 208 def self.btc_wif_of_privkey(hex, is_compressed=false, is_testnet=false) priv_key_version = is_testnet ? 'ef' : '80' data = priv_key_version + hex if is_compressed data += '01' end Bip44WalletProvider.btc_encode_base58(data + Bip44WalletProvider.checksum(data)) end
checksum(hex)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 160 def self.checksum(hex) Bip44WalletProvider.sha256(Bip44WalletProvider.sha256(hex))[0...8] end
coins()
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 51 def self.coins Bip44WalletProvider.ticker_map.keys.to_set.delete(Bip44WalletProvider::TESTNET_TICKER) end
create_subwallet(xpub, sub_path, is_testnet=false)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 105 def self.create_subwallet(xpub, sub_path, is_testnet=false) wallet = Bip44::Wallet.from_xpub(xpub) sub_wallet = wallet.sub_wallet(sub_path) { :obj => sub_wallet, :path => sub_path, :details => Bip44WalletProvider.address_details(sub_wallet, is_testnet, false) } end
create_wallet(coin_ticker='BTC')
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 88 def self.create_wallet(coin_ticker='BTC') words = BipMnemonic.to_mnemonic(bits: 128) seed = BipMnemonic.to_seed(mnemonic: words) coin = ticker_map[coin_ticker][:slip] path = "m/44'/#{coin}'/0'" wallet = Bip44::Wallet.from_seed(seed, path) testnet = coin_ticker == Bip44WalletProvider::TESTNET_TICKER { :obj => wallet, :seed_hex => seed, :path => path, :details => Bip44WalletProvider.master_details(words, wallet, testnet) } end
from_absolute_path(path)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 135 def self.from_absolute_path(path) elements = path.split('/') derivation = "m/#{elements.take(4).drop(1).join('/')}" relative = "m/#{elements.drop(4).join('/')}" { :absolute_path => path, :derivation => derivation, :relative_path => relative } end
int_to_hex(int)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 164 def self.int_to_hex(int) hex = int.to_s(16) # # The hex string must always consist of an even number of characters, # otherwise the pack() parsing will be misaligned. # (hex.length % 2 == 0) ? hex : ('0' + hex) end
master_details(mnemonic, wallet, is_testnet)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 78 def self.master_details(mnemonic, wallet, is_testnet) { :mnemonic => mnemonic, :seed_hex => BipMnemonic.to_seed(mnemonic: mnemonic), :xpub => wallet.xpub(testnet: is_testnet), :xprv => wallet.xprv(testnet: is_testnet), :details => Bip44WalletProvider.address_details(wallet, is_testnet, true) } end
precision_of(ticker)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 55 def self.precision_of(ticker) Bip44WalletProvider.ticker_map[ticker][:precision] end
ripemd160(hex)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 156 def self.ripemd160(hex) Digest::RMD160.hexdigest([hex].pack('H*')) end
seed_of(mnemonic, password=nil)
click to toggle source
# File lib/exocrypto/bip44_wallet_provider.rb, line 116 def self.seed_of(mnemonic, password=nil) #BipMnemonic.to_seed(mnemonic: mnemonic) OpenSSL::PKCS5.pbkdf2_hmac(mnemonic, "mnemonic#{password}", 2048, 64, OpenSSL::Digest::SHA512.new) .unpack('H*').first end
sha256(hex)
click to toggle source
see: gobittest.appspot.com/PrivateKey
https://github.com/dougal/base58/blob/master/lib/base58.rb http://royalforkblog.github.io/2014/07/31/address-gen
# File lib/exocrypto/bip44_wallet_provider.rb, line 152 def self.sha256(hex) Digest::SHA256.hexdigest([hex].pack('H*')) end
ticker_map()
click to toggle source
see: github.com/satoshilabs/slips/blob/master/slip-0044.md
# File lib/exocrypto/bip44_wallet_provider.rb, line 22 def self.ticker_map { 'TESTNET' => { :slip => 1, :precision => 8 }, 'BTC' => { :slip => 0, :precision => 8 }, 'ETH' => { :slip => 60, :precision => 18 }, 'DASH' => { :slip => 5, :precision => 8 }, 'DOGE' => { :slip => 3, :precision => 8 }, 'LTC' => { :slip => 2, :precision => 8 } } end