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