contracthashtool 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +48 -16
- data/contracthashtool.gemspec +3 -3
- data/lib/contracthashtool.rb +24 -19
- data/lib/contracthashtool/version.rb +1 -1
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08284e65956a5805962806d2f4ba2748d792655c
|
4
|
+
data.tar.gz: 9a2aa606c96675f60d95ef93329256526ec098cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c330b0fb70042dc4c010e07ce86b21304cb6418fab35f2f096990b051c83ec1c89f05bb895193e2caf765588f97702bb8a9388ed13b6435177bcb08d7195323a
|
7
|
+
data.tar.gz: 615298ef744d2d9dd860e73d9375307cc86ba4cece7eaf8d7eb649076fbf5778851a69b060f94f7962809e2348b5302e77255e6d5fba1d587ab663a7c6c69dfa
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# contracthashtool-ruby
|
2
2
|
|
3
|
-
Ruby port of https://github.com/Blockstream/contracthashtool
|
3
|
+
Ruby port of: https://github.com/Blockstream/contracthashtool
|
4
|
+
|
5
|
+
See also Appendix A of: https://blockstream.com/sidechains.pdf
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -26,22 +28,52 @@ require 'bitcoin'
|
|
26
28
|
|
27
29
|
Bitcoin.network = :testnet3
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
# Example parameters from the original tool's usage().
|
32
|
+
|
33
|
+
redeem_script_template = '5121038695b28f1649c711aedb1fec8df54874334cfb7ddf31ba3132a94d00bdc9715251ae'
|
34
|
+
payee_address = 'mqWkEAFeQdrQvyaWNRn5vijPJeiQAjtxL2'
|
35
|
+
nonce_hex = '3a11be476485a6273fad4a0e09117d42'
|
36
|
+
private_key_wif = 'cMcpaCT6pHkyS4347i4rSmecaQtLiu1eH28NWmBiePn8bi6N4kzh'
|
37
|
+
|
38
|
+
# Someone wanting to send funds to the sidechain would call this
|
39
|
+
# to calculate a P2SH address to send to. They would then send the
|
40
|
+
# MDFs (mutually distrusting functionaries) the target address
|
41
|
+
# and nonce so they are able to locate the subsequent transaction.
|
42
|
+
# The caller would then send the desired amount of coin to the P2SH
|
43
|
+
# address to initiate the peg protocol.
|
44
|
+
|
45
|
+
nonce, redeem_script, p2sh_address =
|
46
|
+
Contracthashtool.generate(redeem_script_template, payee_address, nonce_hex)
|
47
|
+
|
48
|
+
puts "nonce: #{nonce}"
|
49
|
+
puts "P2SH address: #{p2sh_address}"
|
50
|
+
puts "new redeem script: #{redeem_script}"
|
51
|
+
|
52
|
+
# Each MDF would call this to derive a private key to redeem the
|
53
|
+
# cross-chain seed transaction after the confirmation period lapses.
|
54
|
+
# They would then presumably create and sign a transaction on the
|
55
|
+
# sidechain paying the desired amount of sidecoin to the target address.
|
56
|
+
# And then we've succeeded in executing one direction of a federated peg.
|
57
|
+
# Rinse, wash, and repeat to go back.
|
58
|
+
|
59
|
+
key = Contracthashtool.claim(private_key_wif, payee_address, nonce)
|
60
|
+
puts "new privkey: #{key.to_base58}"
|
33
61
|
|
34
|
-
|
35
|
-
puts "nonce: #{nonce}, address: #{p2sh_address}"
|
62
|
+
# Verify homomorphic derivation was successful.
|
36
63
|
|
37
|
-
|
38
|
-
|
64
|
+
signature = key.sign_message("derp")
|
65
|
+
script = Bitcoin::Script.new([redeem_script].pack("H*"))
|
66
|
+
pubkey = Bitcoin::Key.new(nil, script.get_multisig_pubkeys.first.unpack("H*").first)
|
67
|
+
raise "nope" unless pubkey.verify_message(signature, "derp")
|
39
68
|
```
|
40
69
|
|
41
|
-
|
70
|
+
<pre>
|
71
|
+
<code>
|
72
|
+
$ bundle exec ruby test.rb
|
73
|
+
nonce: 3a11be476485a6273fad4a0e09117d42
|
74
|
+
P2SH address: 2MvGPFfDXbJZyH79u187VNZbuCgyRBhcdsw
|
75
|
+
new redeem script: 512102944aba05d40d8df1724f8ab2f5f3a58d052d26aedc93e175534cb782becc8ff751ae
|
76
|
+
new privkey: cSBD8yM62R82RfbugiGK8Lui9gdMB81NtZBckxe5YxRsDSKySwHK
|
77
|
+
</code>
|
78
|
+
</pre>
|
42
79
|
|
43
|
-
1. Fork it ( https://github.com/[my-github-username]/contracthashtool/fork )
|
44
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
-
5. Create a new Pull Request
|
data/contracthashtool.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["andy.alness@gmail.com"]
|
11
11
|
spec.summary = %q{Ruby port of contracthashtool}
|
12
12
|
spec.description = %q{Ruby port of Blockstream's contracthashtool for federated peg support}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/aalness/contracthashtool-ruby"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -21,6 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
23
|
|
24
|
-
spec.add_dependency "bitcoin-ruby"
|
25
|
-
spec.add_dependency "ffi"
|
24
|
+
spec.add_dependency "bitcoin-ruby", "~> 0.0", ">= 0.0.6"
|
25
|
+
spec.add_dependency "ffi", "~> 1.9"
|
26
26
|
end
|
data/lib/contracthashtool.rb
CHANGED
@@ -6,13 +6,13 @@ require "ffi"
|
|
6
6
|
module Contracthashtool
|
7
7
|
|
8
8
|
# generate a contract address
|
9
|
-
def self.generate(redeem_script_hex,
|
9
|
+
def self.generate(redeem_script_hex, payee_address_or_ascii, nonce_hex=nil)
|
10
10
|
redeem_script = Bitcoin::Script.new([redeem_script_hex].pack("H*"))
|
11
11
|
raise "only multisig redeem scripts are currently supported" unless redeem_script.is_multisig?
|
12
|
-
nonce_hex, data = compute_data(
|
12
|
+
nonce_hex, data = compute_data(payee_address_or_ascii, nonce_hex)
|
13
13
|
|
14
14
|
derived_keys = []
|
15
|
-
group = OpenSSL::PKey::EC::Group.new(
|
15
|
+
group = OpenSSL::PKey::EC::Group.new("secp256k1")
|
16
16
|
redeem_script.get_multisig_pubkeys.each do |pubkey|
|
17
17
|
tweak = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA256"), pubkey, data).to_i(16)
|
18
18
|
raise "order exceeded, pick a new nonce" if tweak >= group.order
|
@@ -26,19 +26,19 @@ module Contracthashtool
|
|
26
26
|
end
|
27
27
|
|
28
28
|
m = redeem_script.get_signatures_required
|
29
|
-
p2sh_script = Bitcoin::Script.to_p2sh_multisig_script(m, *derived_keys)
|
29
|
+
p2sh_script, redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *derived_keys)
|
30
30
|
|
31
|
-
[ nonce_hex, Bitcoin::Script.new(p2sh_script).get_p2sh_address ]
|
31
|
+
[ nonce_hex, redeem_script.unpack("H*")[0], Bitcoin::Script.new(p2sh_script).get_p2sh_address ]
|
32
32
|
end
|
33
33
|
|
34
34
|
# claim a contract
|
35
|
-
def self.claim(private_key_wif,
|
35
|
+
def self.claim(private_key_wif, payee_address_or_ascii, nonce_hex)
|
36
36
|
key = Bitcoin::Key.from_base58(private_key_wif)
|
37
|
-
data = compute_data(
|
37
|
+
data = compute_data(payee_address_or_ascii, nonce_hex)[1]
|
38
38
|
|
39
39
|
pubkey = [key.pub].pack("H*")
|
40
40
|
tweak = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("SHA256"), pubkey, data).to_i(16)
|
41
|
-
group = OpenSSL::PKey::EC::Group.new(
|
41
|
+
group = OpenSSL::PKey::EC::Group.new("secp256k1")
|
42
42
|
raise "order exceeded, verify parameters" if tweak >= group.order
|
43
43
|
|
44
44
|
derived_key = (tweak + key.priv.to_i(16)) % group.order
|
@@ -46,25 +46,30 @@ module Contracthashtool
|
|
46
46
|
end
|
47
47
|
|
48
48
|
# compute HMAC data
|
49
|
-
def self.compute_data(
|
49
|
+
def self.compute_data(address_or_ascii, nonce_hex)
|
50
50
|
nonce = nonce_hex ? [nonce_hex].pack("H32") : SecureRandom.random_bytes(16)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
if Bitcoin.valid_address?(address_or_ascii)
|
52
|
+
address_type = Bitcoin.address_type(address_or_ascii)
|
53
|
+
case address_type
|
54
|
+
when :hash160
|
55
|
+
address_type = "P2PH"
|
56
|
+
when :p2sh
|
57
|
+
address_type = "P2SH"
|
58
|
+
else
|
59
|
+
raise "unsupported address type #{address_type}"
|
60
|
+
end
|
61
|
+
contract_bytes = [Bitcoin.hash160_from_address(address_or_ascii)].pack("H*")
|
58
62
|
else
|
59
|
-
|
63
|
+
address_type = "TEXT"
|
64
|
+
contract_bytes = address_or_ascii
|
60
65
|
end
|
61
|
-
[ nonce.unpack("H*")[0], address_type + nonce +
|
66
|
+
[ nonce.unpack("H*")[0], address_type + nonce + contract_bytes ]
|
62
67
|
end
|
63
68
|
|
64
69
|
# lifted from https://github.com/GemHQ/money-tree
|
65
70
|
module EC_ADD
|
66
71
|
extend ::FFI::Library
|
67
|
-
ffi_lib
|
72
|
+
ffi_lib "ssl"
|
68
73
|
|
69
74
|
NID_secp256k1 = 714
|
70
75
|
POINT_CONVERSION_COMPRESSED = 2
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: contracthashtool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Alness
|
@@ -42,30 +42,36 @@ dependencies:
|
|
42
42
|
name: bitcoin-ruby
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.0'
|
45
48
|
- - ">="
|
46
49
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
50
|
+
version: 0.0.6
|
48
51
|
type: :runtime
|
49
52
|
prerelease: false
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
51
54
|
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0.0'
|
52
58
|
- - ">="
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
60
|
+
version: 0.0.6
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: ffi
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
|
-
- - "
|
65
|
+
- - "~>"
|
60
66
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
67
|
+
version: '1.9'
|
62
68
|
type: :runtime
|
63
69
|
prerelease: false
|
64
70
|
version_requirements: !ruby/object:Gem::Requirement
|
65
71
|
requirements:
|
66
|
-
- - "
|
72
|
+
- - "~>"
|
67
73
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
74
|
+
version: '1.9'
|
69
75
|
description: Ruby port of Blockstream's contracthashtool for federated peg support
|
70
76
|
email:
|
71
77
|
- andy.alness@gmail.com
|
@@ -81,7 +87,7 @@ files:
|
|
81
87
|
- contracthashtool.gemspec
|
82
88
|
- lib/contracthashtool.rb
|
83
89
|
- lib/contracthashtool/version.rb
|
84
|
-
homepage:
|
90
|
+
homepage: https://github.com/aalness/contracthashtool-ruby
|
85
91
|
licenses:
|
86
92
|
- MIT
|
87
93
|
metadata: {}
|