tapyrus 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|