module Contracthashtool
Ruby port of github.com/Blockstream/contracthashtool
Constants
- VERSION
Public Class Methods
claim(private_key_wif, payee_address_or_ascii, nonce_hex)
click to toggle source
claim a contract
# File lib/contracthashtool.rb, line 35 def self.claim(private_key_wif, payee_address_or_ascii, nonce_hex) key = Bitcoin::Key.from_base58(private_key_wif) data = compute_data(payee_address_or_ascii, nonce_hex)[1] pubkey = [key.pub].pack("H*") tweak = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA256"), pubkey, data).to_i(16) group = OpenSSL::PKey::EC::Group.new("secp256k1") raise "order exceeded, verify parameters" if tweak >= group.order derived_key = (tweak + key.priv.to_i(16)) % group.order Bitcoin::Key.new(derived_key.to_s(16)) end
compute_data(address_or_ascii, nonce_hex)
click to toggle source
compute HMAC data
# File lib/contracthashtool.rb, line 49 def self.compute_data(address_or_ascii, nonce_hex) nonce = nonce_hex ? [nonce_hex].pack("H32") : SecureRandom.random_bytes(16) if Bitcoin.valid_address?(address_or_ascii) address_type = Bitcoin.address_type(address_or_ascii) case address_type when :hash160 address_type = "P2PH" when :p2sh address_type = "P2SH" else raise "unsupported address type #{address_type}" end contract_bytes = [Bitcoin.hash160_from_address(address_or_ascii)].pack("H*") else address_type = "TEXT" contract_bytes = address_or_ascii end [ nonce.unpack("H*")[0], address_type + nonce + contract_bytes ] end
generate(redeem_script_hex, payee_address_or_ascii, nonce_hex=nil)
click to toggle source
generate a contract address
# File lib/contracthashtool.rb, line 9 def self.generate(redeem_script_hex, payee_address_or_ascii, nonce_hex=nil) redeem_script = Bitcoin::Script.new([redeem_script_hex].pack("H*")) raise "only multisig redeem scripts are currently supported" unless redeem_script.is_multisig? nonce_hex, data = compute_data(payee_address_or_ascii, nonce_hex) derived_keys = [] group = OpenSSL::PKey::EC::Group.new("secp256k1") redeem_script.get_multisig_pubkeys.each do |pubkey| tweak = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA256"), pubkey, data).to_i(16) raise "order exceeded, pick a new nonce" if tweak >= group.order tweak = OpenSSL::BN.new(tweak.to_s) key = Bitcoin::Key.new(nil, pubkey.unpack("H*")[0]) key = key.instance_variable_get(:@key) point = group.generator.mul(tweak).add(key.public_key).to_bn.to_i key = Bitcoin::Key.new(nil, point.to_s(16)) key.instance_eval{ @pubkey_compressed = true } derived_keys << key.pub end m = redeem_script.get_signatures_required p2sh_script, redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *derived_keys) [ nonce_hex, redeem_script.unpack("H*")[0], Bitcoin::Script.new(p2sh_script).get_p2sh_address ] end