Module: Contracthashtool

Defined in:
lib/contracthashtool.rb,
lib/contracthashtool/version.rb

Overview

Defined Under Namespace

Modules: EC_ADD

Constant Summary collapse

VERSION =
"0.0.1"

Class Method Summary collapse

Class Method Details

.claim(private_key_wif, payee_address, nonce_hex) ⇒ Object

claim a contract



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/contracthashtool.rb', line 35

def self.claim(private_key_wif, payee_address, nonce_hex)
  key = Bitcoin::Key.from_base58(private_key_wif)
  data = compute_data(payee_address, 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, nonce_hex) ⇒ Object

compute HMAC data



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/contracthashtool.rb', line 49

def self.compute_data(address, nonce_hex)
  nonce = nonce_hex ? [nonce_hex].pack("H32") : SecureRandom.random_bytes(16)
  hash160 = [Bitcoin.hash160_from_address(address)].pack("H*")
  address_type = Bitcoin.address_type(address)
  case address_type
  when :hash160
    address_type = "P2PH"
  when :p2sh
    address_type = "P2SH"
  else
    raise "unsuppoorted address type #{address_type}"
  end
  [ nonce.unpack("H*")[0], address_type + nonce + hash160 ]
end

.generate(redeem_script_hex, payee_address, nonce_hex = nil) ⇒ Object

generate a contract address



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/contracthashtool.rb', line 9

def self.generate(redeem_script_hex, payee_address, 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, 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 = Bitcoin::Script.to_p2sh_multisig_script(m, *derived_keys)[0]

  [ nonce_hex, Bitcoin::Script.new(p2sh_script).get_p2sh_address ]
end