Module: Bitcoin::ContractHash
- Defined in:
- lib/bitcoin/contracthash.rb
Overview
Ruby port of github.com/Blockstream/contracthashtool
Constant Summary collapse
Class Method Summary collapse
-
.claim(private_key_wif, payee_address_or_ascii, nonce_hex) ⇒ Object
claim a contract.
-
.compute_data(address_or_ascii, nonce_hex) ⇒ Object
compute HMAC data.
-
.generate(redeem_script_hex, payee_address_or_ascii, nonce_hex = nil) ⇒ Object
generate a contract address.
- .hmac(pubkey, data) ⇒ Object
Class Method Details
.claim(private_key_wif, payee_address_or_ascii, nonce_hex) ⇒ Object
claim a contract
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/bitcoin/contracthash.rb', line 40 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 = hmac(pubkey, data).to_i(16) raise 'order exceeded, verify parameters' if tweak >= EC_GROUP.order.to_i derived_key = (tweak + key.priv.to_i(16)) % EC_GROUP.order.to_i raise 'zero' if derived_key.zero? Bitcoin::Key.new(derived_key.to_s(16)) end |
.compute_data(address_or_ascii, nonce_hex) ⇒ Object
compute HMAC data
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/bitcoin/contracthash.rb', line 55 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 = case Bitcoin.address_type(address_or_ascii) when :hash160 then 'P2PH' when :p2sh then '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) ⇒ Object
generate a contract address
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/bitcoin/contracthash.rb', line 12 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 = [] redeem_script.get_multisig_pubkeys.each do |pubkey| tweak = hmac(pubkey, data).to_i(16) raise 'order exceeded, pick a new nonce' if tweak >= EC_GROUP.order.to_i tweak = OpenSSL::BN.new(tweak.to_s) key = Bitcoin::Key.new(nil, pubkey.unpack('H*')[0]) key = key.instance_variable_get(:@key) point = EC_GROUP.generator.mul(tweak).ec_add(key.public_key).to_bn.to_i raise 'infinity' if point == 1 / 0.0 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 |
.hmac(pubkey, data) ⇒ Object
7 8 9 |
# File 'lib/bitcoin/contracthash.rb', line 7 def self.hmac(pubkey, data) OpenSSL::HMAC.hexdigest(HMAC_DIGEST, pubkey, data) end |