lib/bitcoin.rb in bitcoin-ruby-0.0.14 vs lib/bitcoin.rb in bitcoin-ruby-0.0.15

- old
+ new

@@ -6,10 +6,11 @@ require 'openssl' require 'securerandom' module Bitcoin + autoload :Bech32, 'bitcoin/bech32' autoload :Connection, 'bitcoin/connection' autoload :Protocol, 'bitcoin/protocol' autoload :P, 'bitcoin/protocol' autoload :Script, 'bitcoin/script' autoload :VERSION, 'bitcoin/version' @@ -56,14 +57,11 @@ alias :address_checksum? :base58_checksum? # check if given +address+ is valid. # this means having a correct version byte, length and checksum. def valid_address?(address) - hex = decode_base58(address) rescue nil - return false unless hex && hex.bytesize == 50 - return false unless [address_version, p2sh_version].include?(hex[0...2]) - address_checksum?(address) + address_type(address) != nil end # check if given +pubkey+ is valid. def valid_pubkey?(pubkey) ::OpenSSL::PKey::EC::Point.from_hex(bitcoin_elliptic_curve.group, pubkey) @@ -72,21 +70,46 @@ false end # get hash160 for given +address+. returns nil if address is invalid. def hash160_from_address(address) - return nil unless valid_address?(address) - decode_base58(address)[2...42] + case address_type(address) + when :witness_v0_keyhash + _, witness_program_hex = decode_segwit_address(address) + witness_program_hex + when :hash160, :p2sh + decode_base58(address)[2...42] + end end # get type of given +address+. def address_type(address) - return nil unless valid_address?(address) - case decode_base58(address)[0...2] - when address_version; :hash160 - when p2sh_version; :p2sh + segwit_decoded = decode_segwit_address(address) + if segwit_decoded + witness_version, witness_program_hex = segwit_decoded + witness_program = [witness_program_hex].pack("H*") + + if witness_version == 0 && witness_program.bytesize == 20 + return :witness_v0_keyhash + end + + if witness_version == 0 && witness_program.bytesize == 32 + return :witness_v0_scripthash + end end + + hex = decode_base58(address) rescue nil + if hex && hex.bytesize == 50 && address_checksum?(address) + case hex[0...2] + when address_version + return :hash160 + when p2sh_version + return :p2sh + end + end + + nil end def sha256(hex) Digest::SHA256.hexdigest([hex].pack("H*")) end @@ -111,10 +134,53 @@ def pubkeys_to_p2sh_multisig_address(m, *pubkeys) redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *pubkeys).last return Bitcoin.hash160_to_p2sh_address(Bitcoin.hash160(redeem_script.hth)), redeem_script end + + def encode_segwit_address(version, program_hex) + hrp = Bitcoin.network[:bech32_hrp] + raise "Invalid network" if hrp.nil? + + program = [program_hex].pack("H*") + + return nil if version > 16 + length = program.size + return nil if version == 0 && length != 20 && length != 32 + return nil if length < 2 || length > 40 + + data = [ version ] + Bitcoin::Bech32.convert_bits(program.unpack("C*"), from_bits: 8, to_bits: 5, pad: true) + + address = Bitcoin::Bech32.encode(hrp, data) + + return address.nil? ? nil : address + end + + def decode_segwit_address(address) + hrp = Bitcoin.network[:bech32_hrp] + return nil if hrp.nil? + + actual_hrp, data = Bitcoin::Bech32.decode(address) + + return nil if actual_hrp.nil? + length = data.size + return nil if length == 0 || length > 65 + return nil if hrp != actual_hrp + return nil if data[0] > 16 + + + program = Bitcoin::Bech32.convert_bits(data[1..-1], from_bits: 5, to_bits: 8, pad: false) + return nil if program.nil? + + length = program.size + return nil if length < 2 || length > 40 + return nil if data[0] == 0 && length != 20 && length != 32 + + program_hex = program.pack("C*").unpack("H*").first + return [data[0], program_hex] + end + def 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) @@ -437,11 +503,10 @@ end end extend Util - module BinaryExtensions # bin-to-hex def bth; unpack("H*")[0]; end # hex-to-bin def htb; [self].pack("H*"); end @@ -555,10 +620,11 @@ address_version: "00", p2sh_version: "05", privkey_version: "80", extended_privkey_version: "0488ade4", extended_pubkey_version: "0488b21e", + bech32_hrp: "bc", default_port: 8333, protocol_version: 70001, coinbase_maturity: 100, reward_base: 50 * COIN, reward_halving: 210_000, @@ -612,10 +678,11 @@ address_version: "6f", p2sh_version: "c4", privkey_version: "ef", extended_privkey_version: "04358394", extended_pubkey_version: "043587cf", + bech32_hrp: "tb", default_port: 18333, bip34_height: 21111, dns_seeds: [ ], genesis_hash: "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008", proof_of_work_limit: 0x1d07fff8, @@ -623,10 +690,11 @@ known_nodes: [], checkpoints: {}, }) NETWORKS[:regtest] = NETWORKS[:testnet].merge({ + bech32_hrp: "bcrt", default_port: 18444, genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", proof_of_work_limit: 0x207fffff, bip34_height: 0, }) @@ -658,10 +726,11 @@ magic_head: "\xfb\xc0\xb6\xdb", message_magic: "Litecoin Signed Message:\n", address_version: "30", p2sh_version: "32", privkey_version: "b0", + bech32_hrp: "ltc", extended_privkey_version: "019d9cfe", extended_pubkey_version: "019da462", default_port: 9333, protocol_version: 70002, max_money: 84_000_000 * COIN, @@ -708,10 +777,11 @@ NETWORKS[:litecoin_testnet] = NETWORKS[:litecoin].merge({ magic_head: "\xfd\xd2\xc8\xf1", address_version: "6f", p2sh_version: "3a", privkey_version: "ef", + bech32_hrp: "tltc", extended_privkey_version: "0436ef7d", extended_pubkey_version: "0436f6e1", default_port: 19335, dns_seeds: [ "testnet-seed.ltc.xurious.com", @@ -731,10 +801,11 @@ magic_head: "\xc0\xc0\xc0\xc0", message_magic: "Dogecoin Signed Message:\n", address_version: "1e", p2sh_version: "16", privkey_version: "9e", + bech32_hrp: nil, extended_privkey_version: "02fac398", extended_pubkey_version: "02facafd", default_port: 22556, protocol_version: 70003, max_money: 100_000_000_000 * COIN, @@ -833,9 +904,10 @@ NETWORKS[:namecoin] = NETWORKS[:bitcoin].merge({ project: :namecoin, magic_head: "\xF9\xBE\xB4\xFE", address_version: "34", + bech32_hrp: nil, default_port: 8334, protocol_version: 35000, min_tx_fee: 50_000, per_dust_fee: true, dns_seeds: [