|
1 | | -# |
2 | | -# Ruby port of https://github.com/Blockstream/contracthashtool |
3 | | -# |
4 | | - |
5 | 1 | module Bitcoin |
| 2 | + # Ruby port of https://github.com/Blockstream/contracthashtool |
6 | 3 | module ContractHash |
7 | | - |
8 | | - HMAC_DIGEST = OpenSSL::Digest.new("SHA256") |
9 | | - EC_GROUP = OpenSSL::PKey::EC::Group.new("secp256k1") |
| 4 | + HMAC_DIGEST = OpenSSL::Digest.new('SHA256') |
| 5 | + EC_GROUP = OpenSSL::PKey::EC::Group.new('secp256k1') |
10 | 6 |
|
11 | 7 | def self.hmac(pubkey, data) |
12 | 8 | OpenSSL::HMAC.hexdigest(HMAC_DIGEST, pubkey, data) |
13 | 9 | end |
14 | 10 |
|
15 | 11 | # generate a contract address |
16 | | - def self.generate(redeem_script_hex, payee_address_or_ascii, nonce_hex=nil) |
17 | | - redeem_script = Bitcoin::Script.new([redeem_script_hex].pack("H*")) |
18 | | - raise "only multisig redeem scripts are currently supported" unless redeem_script.is_multisig? |
| 12 | + def self.generate(redeem_script_hex, payee_address_or_ascii, nonce_hex = nil) |
| 13 | + redeem_script = Bitcoin::Script.new([redeem_script_hex].pack('H*')) |
| 14 | + raise 'only multisig redeem scripts are currently supported' unless redeem_script.is_multisig? |
19 | 15 | nonce_hex, data = compute_data(payee_address_or_ascii, nonce_hex) |
20 | 16 |
|
21 | 17 | derived_keys = [] |
22 | 18 | redeem_script.get_multisig_pubkeys.each do |pubkey| |
23 | 19 | tweak = hmac(pubkey, data).to_i(16) |
24 | | - raise "order exceeded, pick a new nonce" if tweak >= EC_GROUP.order.to_i |
| 20 | + raise 'order exceeded, pick a new nonce' if tweak >= EC_GROUP.order.to_i |
25 | 21 | tweak = OpenSSL::BN.new(tweak.to_s) |
26 | 22 |
|
27 | | - key = Bitcoin::Key.new(nil, pubkey.unpack("H*")[0]) |
| 23 | + key = Bitcoin::Key.new(nil, pubkey.unpack('H*')[0]) |
28 | 24 | key = key.instance_variable_get(:@key) |
29 | 25 | point = EC_GROUP.generator.mul(tweak).ec_add(key.public_key).to_bn.to_i |
30 | | - raise "infinity" if point == 1/0.0 |
| 26 | + raise 'infinity' if point == 1 / 0.0 |
31 | 27 |
|
32 | 28 | key = Bitcoin::Key.new(nil, point.to_s(16)) |
33 | | - key.instance_eval{ @pubkey_compressed = true } |
| 29 | + key.instance_eval { @pubkey_compressed = true } |
34 | 30 | derived_keys << key.pub |
35 | 31 | end |
36 | 32 |
|
37 | 33 | m = redeem_script.get_signatures_required |
38 | 34 | p2sh_script, redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *derived_keys) |
39 | 35 |
|
40 | | - [ nonce_hex, redeem_script.unpack("H*")[0], Bitcoin::Script.new(p2sh_script).get_p2sh_address ] |
| 36 | + [nonce_hex, redeem_script.unpack('H*')[0], Bitcoin::Script.new(p2sh_script).get_p2sh_address] |
41 | 37 | end |
42 | 38 |
|
43 | 39 | # claim a contract |
44 | 40 | def self.claim(private_key_wif, payee_address_or_ascii, nonce_hex) |
45 | 41 | key = Bitcoin::Key.from_base58(private_key_wif) |
46 | 42 | data = compute_data(payee_address_or_ascii, nonce_hex)[1] |
47 | 43 |
|
48 | | - pubkey = [key.pub].pack("H*") |
| 44 | + pubkey = [key.pub].pack('H*') |
49 | 45 | tweak = hmac(pubkey, data).to_i(16) |
50 | | - raise "order exceeded, verify parameters" if tweak >= EC_GROUP.order.to_i |
| 46 | + raise 'order exceeded, verify parameters' if tweak >= EC_GROUP.order.to_i |
51 | 47 |
|
52 | 48 | derived_key = (tweak + key.priv.to_i(16)) % EC_GROUP.order.to_i |
53 | | - raise "zero" if derived_key == 0 |
| 49 | + raise 'zero' if derived_key.zero? |
54 | 50 |
|
55 | 51 | Bitcoin::Key.new(derived_key.to_s(16)) |
56 | 52 | end |
57 | 53 |
|
58 | 54 | # compute HMAC data |
59 | 55 | def self.compute_data(address_or_ascii, nonce_hex) |
60 | | - nonce = nonce_hex ? [nonce_hex].pack("H32") : SecureRandom.random_bytes(16) |
| 56 | + nonce = nonce_hex ? [nonce_hex].pack('H32') : SecureRandom.random_bytes(16) |
61 | 57 | if Bitcoin.valid_address?(address_or_ascii) |
62 | 58 | address_type = case Bitcoin.address_type(address_or_ascii) |
63 | | - when :hash160; 'P2PH' |
64 | | - when :p2sh; 'P2SH' |
| 59 | + when :hash160 then 'P2PH' |
| 60 | + when :p2sh then 'P2SH' |
65 | 61 | else |
66 | 62 | raise "unsupported address type #{address_type}" |
67 | 63 | end |
68 | | - contract_bytes = [ Bitcoin.hash160_from_address(address_or_ascii) ].pack("H*") |
| 64 | + contract_bytes = [Bitcoin.hash160_from_address(address_or_ascii)].pack('H*') |
69 | 65 | else |
70 | | - address_type = "TEXT" |
| 66 | + address_type = 'TEXT' |
71 | 67 | contract_bytes = address_or_ascii |
72 | 68 | end |
73 | | - [ nonce.unpack("H*")[0], address_type + nonce + contract_bytes ] |
| 69 | + [nonce.unpack('H*')[0], address_type + nonce + contract_bytes] |
74 | 70 | end |
75 | 71 | end |
76 | 72 | end |
0 commit comments