tapyrus 0.1.0 → 0.2.4
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 +7 -15
- data/exe/tapyrusrbd +2 -2
- data/lib/openassets/util.rb +2 -4
- data/lib/schnorr.rb +83 -0
- data/lib/schnorr/signature.rb +38 -0
- data/lib/tapyrus.rb +11 -18
- data/lib/tapyrus/block.rb +3 -38
- data/lib/tapyrus/block_header.rb +70 -41
- data/lib/tapyrus/chain_params.rb +13 -36
- data/lib/tapyrus/chainparams/{regtest.yml → dev.yml} +10 -19
- data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -19
- data/lib/tapyrus/constants.rb +12 -34
- data/lib/tapyrus/errors.rb +17 -0
- data/lib/tapyrus/ext.rb +5 -0
- data/lib/tapyrus/ext/ecdsa.rb +39 -0
- data/lib/tapyrus/ext/json_parser.rb +47 -0
- data/lib/tapyrus/ext_key.rb +42 -23
- data/lib/tapyrus/key.rb +47 -40
- data/lib/tapyrus/message.rb +2 -2
- data/lib/tapyrus/message/base.rb +1 -0
- data/lib/tapyrus/message/block.rb +4 -4
- data/lib/tapyrus/message/cmpct_block.rb +3 -5
- data/lib/tapyrus/message/header_and_short_ids.rb +1 -1
- data/lib/tapyrus/message/headers.rb +1 -1
- data/lib/tapyrus/message/merkle_block.rb +1 -1
- data/lib/tapyrus/message/tx.rb +2 -2
- data/lib/tapyrus/network/peer.rb +1 -15
- data/lib/tapyrus/node/cli.rb +15 -11
- data/lib/tapyrus/node/configuration.rb +1 -1
- data/lib/tapyrus/node/spv.rb +1 -1
- data/lib/tapyrus/opcodes.rb +5 -0
- data/lib/tapyrus/out_point.rb +1 -1
- data/lib/tapyrus/rpc/request_handler.rb +7 -6
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
- data/lib/tapyrus/script/color.rb +79 -0
- data/lib/tapyrus/script/multisig.rb +0 -27
- data/lib/tapyrus/script/script.rb +74 -89
- data/lib/tapyrus/script/script_error.rb +8 -14
- data/lib/tapyrus/script/script_interpreter.rb +65 -86
- data/lib/tapyrus/script/tx_checker.rb +21 -5
- data/lib/tapyrus/secp256k1.rb +1 -0
- data/lib/tapyrus/secp256k1/native.rb +93 -20
- data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
- data/lib/tapyrus/secp256k1/ruby.rb +63 -54
- data/lib/tapyrus/store/chain_entry.rb +3 -2
- data/lib/tapyrus/store/db/level_db.rb +58 -0
- data/lib/tapyrus/store/spv_chain.rb +29 -11
- data/lib/tapyrus/tx.rb +18 -160
- data/lib/tapyrus/tx_in.rb +4 -11
- data/lib/tapyrus/tx_out.rb +2 -1
- data/lib/tapyrus/util.rb +8 -0
- data/lib/tapyrus/validation.rb +1 -6
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +1 -0
- data/lib/tapyrus/wallet/master_key.rb +1 -0
- data/tapyrusrb.gemspec +3 -3
- metadata +44 -39
- data/lib/tapyrus/chainparams/testnet.yml +0 -41
- data/lib/tapyrus/descriptor.rb +0 -147
- data/lib/tapyrus/script_witness.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e7f821324dfe56eb103457c17b585792e2cc250c3738f59f1665aa4a831a917
|
4
|
+
data.tar.gz: 1faa2850c7dfe07d9be35c75e3c9500e10ef39725db64fc01d88de5548894068
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3299a042ff5f3fa919d4732c1c7c9288cef3bdb213267171d7fa733b53929479b83abb333be1820304fbb593cda742fd8240d8589424536a044901e92cf96823
|
7
|
+
data.tar.gz: c579e85d346cb81290e5854c303147f32dbe1df3049f4190f05af1fded387aaf7cf7cc4170761fa9eaf409c174b7b37f9f3031193c442567bc95e5d8b1b02e2f
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Tapyrusrb supports following feature:
|
|
12
12
|
* Tapyrus script interpreter
|
13
13
|
* De/serialization of Tapyrus protocol network messages
|
14
14
|
* De/serialization of blocks and transactions
|
15
|
-
* Key generation and verification for ECDSA
|
15
|
+
* Key generation and verification for Schnorr and ECDSA (including [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) supports).
|
16
16
|
* ECDSA signature(RFC6979 -Deterministic ECDSA, LOW-S, LOW-R support)
|
17
17
|
* [WIP] SPV node
|
18
18
|
* [WIP] 0ff-chain protocol
|
@@ -65,29 +65,21 @@ And then add to your .rb file:
|
|
65
65
|
|
66
66
|
The parameters of the blockchain are managed by `Tapyrus::ChainParams`. Switch chain parameters as follows:
|
67
67
|
|
68
|
-
*
|
68
|
+
* prod
|
69
69
|
|
70
70
|
```ruby
|
71
|
-
Tapyrus.chain_params = :
|
71
|
+
Tapyrus.chain_params = :prod
|
72
72
|
```
|
73
73
|
|
74
|
-
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/
|
74
|
+
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/prod.yml.
|
75
75
|
|
76
|
-
*
|
76
|
+
* dev
|
77
77
|
|
78
78
|
```ruby
|
79
|
-
Tapyrus.chain_params = :
|
79
|
+
Tapyrus.chain_params = :dev
|
80
80
|
```
|
81
81
|
|
82
|
-
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/
|
83
|
-
|
84
|
-
* regtest
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
Tapyrus.chain_params = :regtest
|
88
|
-
```
|
89
|
-
|
90
|
-
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/regtest.yml.
|
82
|
+
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/dev.yml.
|
91
83
|
|
92
84
|
## Contributing
|
93
85
|
|
data/exe/tapyrusrbd
CHANGED
@@ -16,11 +16,11 @@ end
|
|
16
16
|
|
17
17
|
class Tapyrusrbd < Thor
|
18
18
|
|
19
|
-
class_option :network, aliases: '-n', default: :
|
19
|
+
class_option :network, aliases: '-n', default: :prod
|
20
20
|
|
21
21
|
desc 'start', 'start tapyrusrb daemon.'
|
22
22
|
def start
|
23
|
-
network = options['network'] ? options['network'].to_sym : :
|
23
|
+
network = options['network'] ? options['network'].to_sym : :prod
|
24
24
|
Tapyrus.chain_params = network
|
25
25
|
FileUtils.mkdir_p(Tapyrus.base_dir)
|
26
26
|
execute_daemon(['start', network: network])
|
data/lib/openassets/util.rb
CHANGED
@@ -18,10 +18,8 @@ module OpenAssets
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def oa_version_byte
|
21
|
-
|
22
|
-
|
23
|
-
when 'testnet', 'regtest' then OA_VERSION_BYTE_TESTNET
|
24
|
-
end
|
21
|
+
return OA_VERSION_BYTE if Tapyrus.chain_params.prod?
|
22
|
+
return OA_VERSION_BYTE_TESTNET if Tapyrus.chain_params.dev?
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
data/lib/schnorr.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Schnorr
|
2
|
+
autoload :Signature, 'schnorr/signature'
|
3
|
+
|
4
|
+
module_function
|
5
|
+
|
6
|
+
GROUP = ECDSA::Group::Secp256k1
|
7
|
+
ALGO16 = 'SCHNORR + SHA256'
|
8
|
+
|
9
|
+
# Generate schnorr signature.
|
10
|
+
# @param message (String) A message to be signed with binary format.
|
11
|
+
# @param private_key (Integer) The private key.
|
12
|
+
# (The number of times to add the generator point to itself to get the public key.)
|
13
|
+
# @return (Schnorr::Signature)
|
14
|
+
def sign(message, private_key)
|
15
|
+
raise 'The message must be a 32-byte array.' unless message.bytesize == 32
|
16
|
+
raise 'private_key is zero or over the curve order.' if private_key == 0 || private_key >= GROUP.order
|
17
|
+
|
18
|
+
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?
|
25
|
+
|
26
|
+
r = GROUP.new_point(k0)
|
27
|
+
k = ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) == 1 ? k0 : GROUP.order - k0
|
28
|
+
|
29
|
+
e = create_challenge(r.x, p, message)
|
30
|
+
|
31
|
+
Schnorr::Signature.new(r.x, (k + e * private_key) % GROUP.order)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Verifies the given {Signature} and returns true if it is valid.
|
35
|
+
# @param message (String) A message to be signed with binary format.
|
36
|
+
# @param public_key (String) The public key with binary format.
|
37
|
+
# @param signature (String) The signature with binary format.
|
38
|
+
# @return (Boolean) whether signature is valid.
|
39
|
+
def valid_sig?(message, signature, public_key)
|
40
|
+
check_sig!(message, signature, public_key)
|
41
|
+
rescue InvalidSignatureError, ECDSA::Format::DecodeError
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Verifies the given {Signature} and raises an {InvalidSignatureError} if it is invalid.
|
46
|
+
# @param message (String) A message to be signed with binary format.
|
47
|
+
# @param public_key (String) The public key with binary format.
|
48
|
+
# @param signature (String) The signature with binary format.
|
49
|
+
# @return (Boolean)
|
50
|
+
def check_sig!(message, signature, public_key)
|
51
|
+
sig = Schnorr::Signature.decode(signature)
|
52
|
+
pubkey = ECDSA::Format::PointOctetString.decode(public_key, GROUP)
|
53
|
+
field = GROUP.field
|
54
|
+
|
55
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is not in the field.' unless field.include?(sig.r)
|
56
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is not in the field.' unless field.include?(sig.s)
|
57
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is zero.' if sig.r.zero?
|
58
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is zero.' if sig.s.zero?
|
59
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: r is larger than field size.' if sig.r >= field.prime
|
60
|
+
raise Schnorr::InvalidSignatureError, 'Invalid signature: s is larger than group order.' if sig.s >= GROUP.order
|
61
|
+
|
62
|
+
e = create_challenge(sig.r, pubkey, message)
|
63
|
+
|
64
|
+
r = GROUP.new_point(sig.s) + pubkey.multiply_by_scalar(e).negate
|
65
|
+
|
66
|
+
if r.infinity? || r.x != sig.r || ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) != 1
|
67
|
+
raise Schnorr::InvalidSignatureError, 'signature verification failed.'
|
68
|
+
end
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
# create signature digest.
|
74
|
+
# @param (Integer) x a x coordinate for R.
|
75
|
+
# @param (ECDSA::Point) p a public key.
|
76
|
+
# @return (Integer) digest e.
|
77
|
+
def create_challenge(x, p, message)
|
78
|
+
r_x = ECDSA::Format::IntegerOctetString.encode(x, GROUP.byte_length)
|
79
|
+
p_str= p.to_hex.htb
|
80
|
+
(ECDSA.normalize_digest(Digest::SHA256.digest(r_x + p_str + message), GROUP.bit_length)) % GROUP.order
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Schnorr
|
2
|
+
|
3
|
+
class InvalidSignatureError < StandardError; end
|
4
|
+
|
5
|
+
# Instances of this class represents Schnorr signatures,
|
6
|
+
# which are simply a pair of integers named `r` and `s`.
|
7
|
+
class Signature
|
8
|
+
|
9
|
+
attr_reader :r
|
10
|
+
attr_reader :s
|
11
|
+
|
12
|
+
# @param r (Integer) the value of r.
|
13
|
+
# @param s (Integer) the value of s.
|
14
|
+
def initialize(r, s)
|
15
|
+
@r, @s = r, s
|
16
|
+
r.is_a?(Integer) or raise ArgumentError, 'r is not an integer.'
|
17
|
+
s.is_a?(Integer) or raise ArgumentError, 's is not an integer.'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Parse a string to a {Signature}.
|
21
|
+
# @param string (String) signature string with binary format.
|
22
|
+
# @return (Signature) signature instance.
|
23
|
+
def self.decode(string)
|
24
|
+
raise InvalidSignatureError, 'Invalid schnorr signature length.' unless string.bytesize == 64
|
25
|
+
r = string[0...32].unpack('H*').first.to_i(16)
|
26
|
+
s = string[32..-1].unpack('H*').first.to_i(16)
|
27
|
+
new(r, s)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Encode signature to string.
|
31
|
+
# @return (String) encoded signature.
|
32
|
+
def encode
|
33
|
+
ECDSA::Format::IntegerOctetString.encode(r, 32) + ECDSA::Format::IntegerOctetString.encode(s, 32)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/tapyrus.rb
CHANGED
@@ -6,14 +6,15 @@ require 'eventmachine'
|
|
6
6
|
require 'ecdsa'
|
7
7
|
require 'securerandom'
|
8
8
|
require 'json'
|
9
|
-
require 'bech32'
|
10
9
|
require 'ffi'
|
11
10
|
require 'observer'
|
12
11
|
require 'tmpdir'
|
13
12
|
require_relative 'openassets'
|
13
|
+
require_relative 'schnorr'
|
14
14
|
|
15
15
|
module Tapyrus
|
16
16
|
|
17
|
+
autoload :Ext, 'tapyrus/ext'
|
17
18
|
autoload :Util, 'tapyrus/util'
|
18
19
|
autoload :ChainParams, 'tapyrus/chain_params'
|
19
20
|
autoload :Message, 'tapyrus/message'
|
@@ -29,7 +30,6 @@ module Tapyrus
|
|
29
30
|
autoload :TxIn, 'tapyrus/tx_in'
|
30
31
|
autoload :TxOut, 'tapyrus/tx_out'
|
31
32
|
autoload :OutPoint, 'tapyrus/out_point'
|
32
|
-
autoload :ScriptWitness, 'tapyrus/script_witness'
|
33
33
|
autoload :MerkleTree, 'tapyrus/merkle_tree'
|
34
34
|
autoload :Key, 'tapyrus/key'
|
35
35
|
autoload :ExtKey, 'tapyrus/ext_key'
|
@@ -46,18 +46,20 @@ module Tapyrus
|
|
46
46
|
autoload :Wallet, 'tapyrus/wallet'
|
47
47
|
autoload :BloomFilter, 'tapyrus/bloom_filter'
|
48
48
|
autoload :KeyPath, 'tapyrus/key_path'
|
49
|
-
autoload :Descriptor, 'tapyrus/descriptor'
|
50
49
|
autoload :SLIP39, 'tapyrus/slip39'
|
50
|
+
autoload :Color, 'tapyrus/script/color'
|
51
|
+
autoload :Errors, 'tapyrus/errors'
|
51
52
|
|
52
53
|
require_relative 'tapyrus/constants'
|
54
|
+
require_relative 'tapyrus/ext/ecdsa'
|
53
55
|
|
54
56
|
extend Util
|
55
57
|
|
56
|
-
@chain_param = :
|
58
|
+
@chain_param = :prod
|
57
59
|
|
58
60
|
# set tapyrus network chain params
|
59
61
|
def self.chain_params=(name)
|
60
|
-
raise "chain params for #{name} is not defined." unless %i(
|
62
|
+
raise "chain params for #{name} is not defined." unless %i(prod dev).include?(name.to_sym)
|
61
63
|
@current_chain = nil
|
62
64
|
@chain_param = name.to_sym
|
63
65
|
end
|
@@ -66,12 +68,10 @@ module Tapyrus
|
|
66
68
|
def self.chain_params
|
67
69
|
return @current_chain if @current_chain
|
68
70
|
case @chain_param
|
69
|
-
when :
|
70
|
-
@current_chain = Tapyrus::ChainParams.
|
71
|
-
when :
|
72
|
-
@current_chain = Tapyrus::ChainParams.
|
73
|
-
when :regtest
|
74
|
-
@current_chain = Tapyrus::ChainParams.regtest
|
71
|
+
when :prod
|
72
|
+
@current_chain = Tapyrus::ChainParams.prod
|
73
|
+
when :dev
|
74
|
+
@current_chain = Tapyrus::ChainParams.dev
|
75
75
|
end
|
76
76
|
@current_chain
|
77
77
|
end
|
@@ -209,11 +209,4 @@ module Tapyrus
|
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
212
|
-
class ::ECDSA::Signature
|
213
|
-
# convert signature to der string.
|
214
|
-
def to_der
|
215
|
-
ECDSA::Format::SignatureDerString.encode(self)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
212
|
end
|
data/lib/tapyrus/block.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
class Block
|
3
|
+
include Tapyrus::Util
|
3
4
|
|
4
5
|
attr_accessor :header
|
5
6
|
attr_accessor :transactions
|
@@ -21,23 +22,6 @@ module Tapyrus
|
|
21
22
|
header.block_hash
|
22
23
|
end
|
23
24
|
|
24
|
-
# calculate block weight
|
25
|
-
def weight
|
26
|
-
stripped_size * (WITNESS_SCALE_FACTOR - 1) + size
|
27
|
-
end
|
28
|
-
|
29
|
-
# calculate total size (include witness data.)
|
30
|
-
def size
|
31
|
-
80 + Tapyrus.pack_var_int(transactions.size).bytesize +
|
32
|
-
transactions.inject(0){|sum, tx| sum + (tx.witness? ? tx.serialize_witness_format.bytesize : tx.serialize_old_format.bytesize)}
|
33
|
-
end
|
34
|
-
|
35
|
-
# calculate base size (not include witness data.)
|
36
|
-
def stripped_size
|
37
|
-
80 + Tapyrus.pack_var_int(transactions.size).bytesize +
|
38
|
-
transactions.inject(0){|sum, tx| sum + tx.serialize_old_format.bytesize}
|
39
|
-
end
|
40
|
-
|
41
25
|
# check the merkle root in the block header matches merkle root calculated from tx list.
|
42
26
|
def valid_merkle_root?
|
43
27
|
calculate_merkle_root == header.merkle_root
|
@@ -48,29 +32,10 @@ module Tapyrus
|
|
48
32
|
Tapyrus::MerkleTree.build_from_leaf(transactions.map(&:tx_hash)).merkle_root
|
49
33
|
end
|
50
34
|
|
51
|
-
# check the witness commitment in coinbase tx matches witness commitment calculated from tx list.
|
52
|
-
def valid_witness_commitment?
|
53
|
-
transactions[0].witness_commitment == calculate_witness_commitment
|
54
|
-
end
|
55
|
-
|
56
|
-
# calculate witness commitment from tx list.
|
57
|
-
def calculate_witness_commitment
|
58
|
-
witness_hashes = [COINBASE_WTXID]
|
59
|
-
witness_hashes += (transactions[1..-1].map(&:witness_hash))
|
60
|
-
reserved_value = transactions[0].inputs[0].script_witness.stack.map(&:bth).join
|
61
|
-
root_hash = Tapyrus::MerkleTree.build_from_leaf(witness_hashes).merkle_root
|
62
|
-
Tapyrus.double_sha256([root_hash + reserved_value].pack('H*')).bth
|
63
|
-
end
|
64
|
-
|
65
35
|
# return this block height. block height is included in coinbase.
|
66
|
-
#
|
36
|
+
# @return [Integer] block height.
|
67
37
|
def height
|
68
|
-
|
69
|
-
coinbase_tx = transactions[0]
|
70
|
-
return nil unless coinbase_tx.coinbase_tx?
|
71
|
-
buf = StringIO.new(coinbase_tx.inputs[0].script_sig.to_payload)
|
72
|
-
len = Tapyrus.unpack_var_int_from_io(buf)
|
73
|
-
buf.read(len).reverse.bth.to_i(16)
|
38
|
+
transactions.first.in.first.out_point.index
|
74
39
|
end
|
75
40
|
|
76
41
|
end
|
data/lib/tapyrus/block_header.rb
CHANGED
@@ -2,61 +2,70 @@ module Tapyrus
|
|
2
2
|
|
3
3
|
# Block Header
|
4
4
|
class BlockHeader
|
5
|
+
include Tapyrus::HexConverter
|
6
|
+
extend Tapyrus::Util
|
7
|
+
include Tapyrus::Util
|
5
8
|
|
6
|
-
|
9
|
+
X_FILED_TYPES = {none: 0, aggregate_pubkey: 1}
|
10
|
+
|
11
|
+
attr_accessor :features
|
7
12
|
attr_accessor :prev_hash
|
8
13
|
attr_accessor :merkle_root
|
9
|
-
attr_accessor :
|
10
|
-
attr_accessor :
|
11
|
-
attr_accessor :
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
attr_accessor :im_merkle_root # merkel root of immulable merkle tree which consist of immutable txid.
|
15
|
+
attr_accessor :time # unix timestamp
|
16
|
+
attr_accessor :x_field_type
|
17
|
+
attr_accessor :x_field
|
18
|
+
attr_accessor :proof
|
19
|
+
|
20
|
+
def initialize(features, prev_hash, merkle_root, im_merkle_root, time, x_field_type, x_field, proof = nil)
|
21
|
+
@features = features
|
15
22
|
@prev_hash = prev_hash
|
16
23
|
@merkle_root = merkle_root
|
24
|
+
@im_merkle_root = im_merkle_root
|
17
25
|
@time = time
|
18
|
-
@
|
19
|
-
@
|
26
|
+
@x_field_type = x_field_type
|
27
|
+
@x_field = x_field
|
28
|
+
@proof = proof
|
20
29
|
end
|
21
30
|
|
22
31
|
def self.parse_from_payload(payload)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
[version, prev_hash.htb, merkle_root.htb, time, bits, nonce].pack('Va32a32VVV')
|
32
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
33
|
+
features, prev_hash, merkle_root, im_merkle_root, time, x_filed_type = buf.read(105).unpack('Va32a32a32Vc')
|
34
|
+
x_field = buf.read(unpack_var_int_from_io(buf)) unless x_filed_type == X_FILED_TYPES[:none]
|
35
|
+
proof = buf.read(unpack_var_int_from_io(buf))
|
36
|
+
new(features, prev_hash.bth, merkle_root.bth, im_merkle_root.bth, time, x_filed_type, x_field ? x_field.bth : x_field, proof.bth)
|
29
37
|
end
|
30
38
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
(mantissa * 2 ** (8 * (exponent - 3)))
|
39
|
+
def to_payload(skip_proof = false)
|
40
|
+
payload = [features, prev_hash.htb, merkle_root.htb, im_merkle_root.htb, time, x_field_type].pack('Va32a32a32Vc')
|
41
|
+
payload << pack_var_string(x_field.htb) unless x_field_type == X_FILED_TYPES[:none]
|
42
|
+
payload << pack_var_string(proof.htb) if proof && !skip_proof
|
43
|
+
payload
|
37
44
|
end
|
38
45
|
|
39
|
-
|
40
|
-
|
46
|
+
# Calculate hash using sign which does not contains proof.
|
47
|
+
# @return [String] hash of block without proof.
|
48
|
+
def hash_for_sign
|
49
|
+
Tapyrus.double_sha256(to_payload(true)).bth
|
41
50
|
end
|
42
51
|
|
52
|
+
# Calculate block hash
|
53
|
+
# @return [String] hash of block with hex format.
|
43
54
|
def block_hash
|
44
|
-
|
55
|
+
Tapyrus.double_sha256(to_payload).bth
|
45
56
|
end
|
46
57
|
|
47
58
|
# block hash(big endian)
|
59
|
+
# @return [String] block id which is reversed version of block hash.
|
48
60
|
def block_id
|
49
61
|
block_hash.rhex
|
50
62
|
end
|
51
63
|
|
52
64
|
# evaluate block header
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
# evaluate valid proof of work.
|
58
|
-
def valid_pow?
|
59
|
-
block_id.hex < difficulty_target
|
65
|
+
# @param [String] agg_pubkey aggregated public key for signers with hex format.
|
66
|
+
# @return [Boolean] result.
|
67
|
+
def valid?(agg_pubkey)
|
68
|
+
valid_timestamp? && valid_proof?(agg_pubkey) && valid_x_field?
|
60
69
|
end
|
61
70
|
|
62
71
|
# evaluate valid timestamp.
|
@@ -65,22 +74,42 @@ module Tapyrus
|
|
65
74
|
time <= Time.now.to_i + Tapyrus::MAX_FUTURE_BLOCK_TIME
|
66
75
|
end
|
67
76
|
|
68
|
-
#
|
69
|
-
# @
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
77
|
+
# Check whether proof is valid.
|
78
|
+
# @param [String] agg_pubkey aggregated public key for signers with hex format.
|
79
|
+
# @return [Boolean] Return true if proof is valid, otherwise return false.
|
80
|
+
def valid_proof?(agg_pubkey)
|
81
|
+
pubkey = Tapyrus::Key.new(pubkey: agg_pubkey)
|
82
|
+
msg = hash_for_sign.htb
|
83
|
+
pubkey.verify(proof.htb, msg, algo: :schnorr)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Check whether x_field is valid.
|
87
|
+
# @return [Boolean] if valid return true, otherwise false
|
88
|
+
def valid_x_field?
|
89
|
+
case x_field_type
|
90
|
+
when X_FILED_TYPES[:none] then
|
91
|
+
x_field.nil?
|
92
|
+
when X_FILED_TYPES[:aggregate_pubkey] then
|
93
|
+
Tapyrus::Key.new(pubkey: x_field).fully_valid_pubkey?
|
94
|
+
else
|
95
|
+
false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Check this header contains upgrade aggregated publiec key.
|
100
|
+
# @return [Boolean] if contains return true, otherwise false.
|
101
|
+
def upgrade_agg_pubkey?
|
102
|
+
x_field_type == X_FILED_TYPES[:aggregate_pubkey]
|
74
103
|
end
|
75
104
|
|
76
105
|
def ==(other)
|
77
106
|
other && other.to_payload == to_payload
|
78
107
|
end
|
79
108
|
|
80
|
-
|
81
|
-
|
82
|
-
def
|
83
|
-
|
109
|
+
# get bytesize.
|
110
|
+
# @return [Integer] bytesize.
|
111
|
+
def size
|
112
|
+
to_payload.bytesize
|
84
113
|
end
|
85
114
|
|
86
115
|
end
|