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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ad31bf4aa539aa394eeb5002fe11fc9b8c6a43aab73c9e36a63220159c2fb30
4
- data.tar.gz: 8b7aaacfd238de278b334b7b26249045facf14843b8fb32985045c0a52b9cdc9
3
+ metadata.gz: 44f097dd931371e9cce458e44ff030d9ebedcd9372632339d274457bb3839a4c
4
+ data.tar.gz: c72dddb87aacb3f649cc4a542b0988eb54347da6203e0bcc2d430065fc3398b8
5
5
  SHA512:
6
- metadata.gz: ad3bc4d2fbaaf0e228cd7c07ee78e878f6b913e971645b8a6e188df70c9e363fbc210456833436f1e7ea780881bd10accdd7a069365e26fc09195744fe606766
7
- data.tar.gz: b267bb2ff699df8822feffc75573db5e506dc5490f5f79146cfaf16d20abfd14660ddbfa4f2f01401d54b5bb9ecdb646613de8c7a28b01d48260aebb862834d3
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
- secret = ECDSA::Format::IntegerOctetString.encode(private_key, GROUP.byte_length)
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
@@ -49,6 +49,7 @@ module Tapyrus
49
49
  autoload :Color, 'tapyrus/script/color'
50
50
  autoload :Errors, 'tapyrus/errors'
51
51
  autoload :TxBuilder, 'tapyrus/tx_builder'
52
+ autoload :BIP175, 'tapyrus/bip175'
52
53
 
53
54
  require_relative 'tapyrus/constants'
54
55
  require_relative 'tapyrus/ext/ecdsa'
@@ -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
@@ -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
@@ -3,5 +3,6 @@ module Tapyrus
3
3
  autoload :HttpServer, 'tapyrus/rpc/http_server'
4
4
  autoload :RequestHandler, 'tapyrus/rpc/request_handler'
5
5
  autoload :TapyrusCoreClient, 'tapyrus/rpc/tapyrus_core_client'
6
+ autoload :Error, 'tapyrus/rpc/tapyrus_core_client'
6
7
  end
7
8
  end
@@ -16,22 +16,25 @@ module Tapyrus
16
16
  # end
17
17
  # end
18
18
  class Error < StandardError
19
- attr_reader :message
19
+ attr_reader :response_code, :response_msg, :rpc_error
20
20
 
21
- def initialize(response)
22
- raise ArgumentError, 'Must set response as cause.' unless response
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
- # set message from response body or status code.
25
- @message = { response_code: response&.code, response_msg: response&.msg }
26
- begin
27
- @message.merge!({ rpc_error: Tapyrus::RPC.response_body2json(response.body)['error'] })
28
- rescue JSON::ParserError => _
29
- # if RPC server don't send error message.
30
- end
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
- @message.to_s
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 Error.new(response) unless response.is_a? Net::HTTPOK
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)
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = '0.2.8'
2
+ VERSION = '0.2.9'
3
3
  end
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.8
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-14 00:00:00.000000000 Z
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