tapyrus 0.2.8 → 0.2.9
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/lib/schnorr.rb +12 -6
- data/lib/schnorr/sign_to_contract.rb +51 -0
- data/lib/tapyrus.rb +1 -0
- data/lib/tapyrus/bip175.rb +67 -0
- data/lib/tapyrus/ext_key.rb +8 -0
- data/lib/tapyrus/rpc.rb +1 -0
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +26 -12
- data/lib/tapyrus/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44f097dd931371e9cce458e44ff030d9ebedcd9372632339d274457bb3839a4c
|
4
|
+
data.tar.gz: c72dddb87aacb3f649cc4a542b0988eb54347da6203e0bcc2d430065fc3398b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc7fae61da930e3af35d17039dcd9bda45b6a9317d132dc5096e585ed3b00309a7fc4a3f3552928a57b5b46289bf3fe5be28000cffe666f0d0990480e88b31f3
|
7
|
+
data.tar.gz: b7d9c26524bda4fe26f4522b51b3328145a6e590a0099f2f72b0d9cbfb43c9a4e9b7ee4eb23907d8d8c8cf3daa239b0b7ad76caee82b5b00edffef63cb69d178
|
data/lib/schnorr.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Schnorr
|
2
2
|
autoload :Signature, 'schnorr/signature'
|
3
|
+
autoload :SignToContract, 'schnorr/sign_to_contract'
|
3
4
|
|
4
5
|
module_function
|
5
6
|
|
@@ -16,12 +17,7 @@ module Schnorr
|
|
16
17
|
raise 'private_key is zero or over the curve order.' if private_key == 0 || private_key >= GROUP.order
|
17
18
|
|
18
19
|
p = GROUP.new_point(private_key)
|
19
|
-
|
20
|
-
secret = secret + message + ALGO16
|
21
|
-
nonce = Tapyrus::Secp256k1::RFC6979.generate_rfc6979_nonce(secret, '')
|
22
|
-
|
23
|
-
k0 = nonce % GROUP.order
|
24
|
-
raise 'Creation of signature failed. k is zero' if k0.zero?
|
20
|
+
k0 = deterministic_nonce(message, private_key)
|
25
21
|
|
26
22
|
r = GROUP.new_point(k0)
|
27
23
|
k = ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) == 1 ? k0 : GROUP.order - k0
|
@@ -31,6 +27,16 @@ module Schnorr
|
|
31
27
|
Schnorr::Signature.new(r.x, (k + e * private_key) % GROUP.order)
|
32
28
|
end
|
33
29
|
|
30
|
+
def deterministic_nonce(message, private_key)
|
31
|
+
secret = ECDSA::Format::IntegerOctetString.encode(private_key, GROUP.byte_length)
|
32
|
+
secret = secret + message + ALGO16
|
33
|
+
nonce = Tapyrus::Secp256k1::RFC6979.generate_rfc6979_nonce(secret, '')
|
34
|
+
|
35
|
+
k0 = nonce % GROUP.order
|
36
|
+
raise 'Creation of signature failed. k is zero' if k0.zero?
|
37
|
+
k0
|
38
|
+
end
|
39
|
+
|
34
40
|
# Verifies the given {Signature} and returns true if it is valid.
|
35
41
|
# @param message (String) A message to be signed with binary format.
|
36
42
|
# @param public_key (String) The public key with binary format.
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Schnorr
|
2
|
+
module SignToContract
|
3
|
+
module_function
|
4
|
+
|
5
|
+
GROUP = ECDSA::Group::Secp256k1
|
6
|
+
|
7
|
+
# Generate schnorr signature for sign-to-signature.
|
8
|
+
# @param message [String] A message to be signed with binary format.
|
9
|
+
# @param private_key [Integer] The private key.
|
10
|
+
# @param contract [String] A contract information with 32-bytes binary format.
|
11
|
+
# @return [(Schnorr::Signature, ECDSA::Point)] signature and point to prove the commitment to contract.
|
12
|
+
def sign(message, private_key, contract)
|
13
|
+
raise 'The message must be a 32-byte array.' unless message.bytesize == 32
|
14
|
+
raise 'private_key is zero or over the curve order.' if private_key == 0 || private_key >= GROUP.order
|
15
|
+
raise 'The contract must be a 32-byte binary string.' unless contract.bytesize == 32
|
16
|
+
|
17
|
+
p = GROUP.new_point(private_key)
|
18
|
+
k0 = Schnorr.deterministic_nonce(message, private_key)
|
19
|
+
|
20
|
+
k1, r = tweak(k0, contract)
|
21
|
+
|
22
|
+
q = GROUP.new_point(k1)
|
23
|
+
k = ECDSA::PrimeField.jacobi(q.y, GROUP.field.prime) == 1 ? k1 : GROUP.order - k1
|
24
|
+
|
25
|
+
e = Schnorr.create_challenge(q.x, p, message)
|
26
|
+
|
27
|
+
[Schnorr::Signature.new(q.x, (k + e * private_key) % GROUP.order), r]
|
28
|
+
end
|
29
|
+
|
30
|
+
def tweak(k, contract)
|
31
|
+
r = GROUP.new_point(k)
|
32
|
+
rx = ECDSA::Format::IntegerOctetString.encode(r.x, GROUP.byte_length)
|
33
|
+
h = Tapyrus.sha256(rx + contract)
|
34
|
+
k1 = (k + h.bth.to_i(16)) % GROUP.order
|
35
|
+
raise 'Creation of signature failed. k + h(R || c) is zero' if k1.zero?
|
36
|
+
[k1, r]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate contract
|
40
|
+
# @param r [ECDSA::Point] point to prove the commitment.
|
41
|
+
# @param signature [Schnorr::Signature] signature.
|
42
|
+
# @param contract [String] A contract information with 32-bytes binary format.
|
43
|
+
# @return true if commitment for contract is valid, otherwise false
|
44
|
+
def valid_contract?(r, signature, contract)
|
45
|
+
rx = ECDSA::Format::IntegerOctetString.encode(r.x, GROUP.byte_length)
|
46
|
+
commitment = Tapyrus.sha256(rx + contract).bth.to_i(16) % GROUP.order
|
47
|
+
point = r + GROUP.generator.multiply_by_scalar(commitment)
|
48
|
+
signature.r == point.x
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/tapyrus.rb
CHANGED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Tapyrus
|
2
|
+
# Key generation based on BIP-175
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
#
|
6
|
+
# master = Tapyrus::ExtKey.from_base58('xprv9s21ZrQH143K2JF8RafpqtKiTbsbaxEeUaMnNHsm5o6wCW3z8ySyH4UxFVSfZ8n7ESu7fgir8imbZKLYVBxFPND1pniTZ81vKfd45EHKX73')
|
7
|
+
# bip175 = Tapyrus::BIP175.from_private_key(master)
|
8
|
+
# bip175 << "foo"
|
9
|
+
# bip175 << "bar"
|
10
|
+
# bip175.addr
|
11
|
+
# > 1C7f322izqMqLzZzfzkPAjxBzprxDi47Yf
|
12
|
+
#
|
13
|
+
# @see https://github.com/bitcoin/bips/blob/master/bip-0175.mediawiki
|
14
|
+
class BIP175
|
15
|
+
PURPOSE_TYPE = 175
|
16
|
+
|
17
|
+
attr_accessor :payment_base
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@contracts = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param key [Tapyrus::ExtKey] master private extended key
|
24
|
+
def self.from_ext_key(key)
|
25
|
+
raise ArgumentError, 'key should be Tapyrus::ExtKey' unless key.is_a?(Tapyrus::ExtKey)
|
26
|
+
raise ArgumentError, 'key should be master private extended key' unless key.master?
|
27
|
+
new.tap do |bip175|
|
28
|
+
bip175.payment_base =
|
29
|
+
key.derive(PURPOSE_TYPE, true).derive(Tapyrus.chain_params.bip44_coin_type, true).ext_pubkey
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param key [Tapyrus::ExtPubkey] contract base public key
|
34
|
+
def self.from_ext_pubkey(key)
|
35
|
+
raise ArgumentError, 'key should be Tapyrus::ExtPubkey' unless key.is_a?(Tapyrus::ExtPubkey)
|
36
|
+
new.tap { |bip175| bip175.payment_base = key }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add value of hashed contract
|
40
|
+
# @param contract [String] contract information
|
41
|
+
def add(contract)
|
42
|
+
@contracts << Tapyrus.sha256(contract)
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias << add
|
46
|
+
|
47
|
+
# Return combined hash consist of payment_base and contract hashes
|
48
|
+
# @return [String] contract_hash
|
49
|
+
def combined_hash
|
50
|
+
hashes = @contracts.map { |c| c.bth }.sort
|
51
|
+
concatenated_hash = [payment_base.to_base58].concat(hashes).join
|
52
|
+
Tapyrus.sha256(concatenated_hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return pay-to-contract extended public key
|
56
|
+
# @return [Tapyrus::ExtPubkey] extended public key
|
57
|
+
def pubkey
|
58
|
+
# Split every 2 bytes
|
59
|
+
paths = combined_hash.unpack('S>*')
|
60
|
+
paths.inject(payment_base) { |key, p| key.derive(p) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def addr
|
64
|
+
pubkey.addr
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/tapyrus/ext_key.rb
CHANGED
@@ -196,6 +196,10 @@ module Tapyrus
|
|
196
196
|
Tapyrus.chain_params.extended_pubkey_version
|
197
197
|
end
|
198
198
|
end
|
199
|
+
|
200
|
+
def master?
|
201
|
+
depth == 0 && number == 0 && parent_fingerprint == '00000000'
|
202
|
+
end
|
199
203
|
end
|
200
204
|
|
201
205
|
# BIP-32 Extended public key
|
@@ -356,5 +360,9 @@ module Tapyrus
|
|
356
360
|
p = Tapyrus.chain_params
|
357
361
|
[p.bip49_pubkey_p2wpkh_p2sh_version, p.bip84_pubkey_p2wpkh_version, p.extended_pubkey_version].include?(version)
|
358
362
|
end
|
363
|
+
|
364
|
+
def master?
|
365
|
+
depth == 0 && number == 0 && parent_fingerprint == '00000000'
|
366
|
+
end
|
359
367
|
end
|
360
368
|
end
|
data/lib/tapyrus/rpc.rb
CHANGED
@@ -16,22 +16,25 @@ module Tapyrus
|
|
16
16
|
# end
|
17
17
|
# end
|
18
18
|
class Error < StandardError
|
19
|
-
attr_reader :
|
19
|
+
attr_reader :response_code, :response_msg, :rpc_error
|
20
20
|
|
21
|
-
def initialize(
|
22
|
-
|
21
|
+
def initialize(response_code, response_msg, rpc_error)
|
22
|
+
@response_code = response_code
|
23
|
+
@response_msg = response_msg
|
24
|
+
@rpc_error = rpc_error
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
@message
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def message
|
28
|
+
@message ||=
|
29
|
+
begin
|
30
|
+
m = { response_code: response_code, response_msg: response_msg }
|
31
|
+
m.merge!(rpc_error: rpc_error) if rpc_error
|
32
|
+
m
|
33
|
+
end
|
31
34
|
end
|
32
35
|
|
33
36
|
def to_s
|
34
|
-
|
37
|
+
message.to_s
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
@@ -83,10 +86,21 @@ module Tapyrus
|
|
83
86
|
request.content_type = 'application/json'
|
84
87
|
request.body = data.to_json
|
85
88
|
response = http.request(request)
|
86
|
-
raise
|
89
|
+
raise error!(response) unless response.is_a? Net::HTTPOK
|
87
90
|
response = Tapyrus::RPC.response_body2json(response.body)
|
88
91
|
response['result']
|
89
92
|
end
|
93
|
+
|
94
|
+
def error!(response)
|
95
|
+
rpc_error =
|
96
|
+
begin
|
97
|
+
Tapyrus::RPC.response_body2json(response.body)['error']
|
98
|
+
rescue JSON::ParserError => _
|
99
|
+
# if RPC server don't send error message.
|
100
|
+
end
|
101
|
+
|
102
|
+
raise Error.new(response.code, response.msg, rpc_error)
|
103
|
+
end
|
90
104
|
end
|
91
105
|
|
92
106
|
def response_body2json(body)
|
data/lib/tapyrus/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tapyrus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- azuchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ecdsa
|
@@ -306,9 +306,11 @@ files:
|
|
306
306
|
- lib/openassets/payload.rb
|
307
307
|
- lib/openassets/util.rb
|
308
308
|
- lib/schnorr.rb
|
309
|
+
- lib/schnorr/sign_to_contract.rb
|
309
310
|
- lib/schnorr/signature.rb
|
310
311
|
- lib/tapyrus.rb
|
311
312
|
- lib/tapyrus/base58.rb
|
313
|
+
- lib/tapyrus/bip175.rb
|
312
314
|
- lib/tapyrus/block.rb
|
313
315
|
- lib/tapyrus/block_header.rb
|
314
316
|
- lib/tapyrus/bloom_filter.rb
|