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
@@ -12,10 +12,11 @@ module Tapyrus
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# check signature
|
15
|
-
# @param [String] script_sig
|
16
|
-
# @param [String] pubkey
|
15
|
+
# @param [String] script_sig a signature with hex format.
|
16
|
+
# @param [String] pubkey a public key with hex format.
|
17
17
|
# @param [Tapyrus::Script] script_code
|
18
18
|
# @param [Integer] sig_version
|
19
|
+
# @return [Boolean] if check is passed return true, otherwise false.
|
19
20
|
def check_sig(script_sig, pubkey, script_code, sig_version)
|
20
21
|
return false if script_sig.empty?
|
21
22
|
script_sig = script_sig.htb
|
@@ -23,9 +24,24 @@ module Tapyrus
|
|
23
24
|
sig = script_sig[0..-2]
|
24
25
|
sighash = tx.sighash_for_input(input_index, script_code, hash_type: hash_type,
|
25
26
|
amount: amount, sig_version: sig_version)
|
27
|
+
verify_sig(sig.bth, pubkey, sighash)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check data signature.
|
31
|
+
# @param [String] sig a signature with hex format.
|
32
|
+
# @param [String] pubkey a public key with hex format.
|
33
|
+
# @param [String] digest a message digest with binary format to be verified.
|
34
|
+
# @return [Boolean] if check is passed return true, otherwise false.
|
35
|
+
def verify_sig(sig, pubkey, digest, allow_hybrid: false)
|
26
36
|
key_type = pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed]
|
27
|
-
|
28
|
-
|
37
|
+
sig = sig.htb
|
38
|
+
algo = sig.bytesize == 64 ? :schnorr : :ecdsa
|
39
|
+
begin
|
40
|
+
key = Key.new(pubkey: pubkey, key_type: key_type, allow_hybrid: allow_hybrid)
|
41
|
+
key.verify(sig, digest, algo: algo)
|
42
|
+
rescue Exception
|
43
|
+
false
|
44
|
+
end
|
29
45
|
end
|
30
46
|
|
31
47
|
def check_locktime(locktime)
|
@@ -53,7 +69,7 @@ module Tapyrus
|
|
53
69
|
def check_sequence(sequence)
|
54
70
|
tx_sequence = tx.inputs[input_index].sequence
|
55
71
|
# Fail if the transaction's version number is not set high enough to trigger BIP 68 rules.
|
56
|
-
return false if tx.
|
72
|
+
return false if tx.features < 2
|
57
73
|
|
58
74
|
# Sequence numbers with their most significant bit set are not consensus constrained.
|
59
75
|
# Testing that the transaction's sequence number do not have this bit set prevents using this property to get around a CHECKSEQUENCEVERIFY check.
|
data/lib/tapyrus/secp256k1.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
module Tapyrus
|
5
5
|
module Secp256k1
|
6
6
|
|
7
|
-
# binding for secp256k1 (https://github.com/
|
8
|
-
# tag: v0.
|
7
|
+
# binding for secp256k1 (https://github.com/chaintope/tapyrus-core/tree/v0.4.0/src/secp256k1)
|
8
|
+
# tag: v0.4.0
|
9
9
|
# this is not included by default, to enable set shared object path to ENV['SECP256K1_LIB_PATH']
|
10
10
|
# for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so'
|
11
11
|
# for mac,
|
@@ -50,6 +50,8 @@ module Tapyrus
|
|
50
50
|
attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
|
51
51
|
attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
|
52
52
|
attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
|
53
|
+
attach_function(:secp256k1_schnorr_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
|
54
|
+
attach_function(:secp256k1_schnorr_verify, [:pointer, :pointer, :pointer, :pointer], :int)
|
53
55
|
end
|
54
56
|
|
55
57
|
def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
|
@@ -100,7 +102,68 @@ module Tapyrus
|
|
100
102
|
# @param [String] privkey a private key using sign
|
101
103
|
# @param [String] extra_entropy a extra entropy for rfc6979
|
102
104
|
# @return [String] signature data with binary format
|
103
|
-
def sign_data(data, privkey, extra_entropy)
|
105
|
+
def sign_data(data, privkey, extra_entropy, algo: :ecdsa)
|
106
|
+
case algo
|
107
|
+
when :ecdsa
|
108
|
+
sign_ecdsa(data, privkey, extra_entropy)
|
109
|
+
when :schnorr
|
110
|
+
sign_schnorr(data, privkey)
|
111
|
+
else
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# verify signature.
|
117
|
+
# @param[String] data a data.
|
118
|
+
# @param [String] sig signature data with binary format
|
119
|
+
# @param [String] pub_key a public key using verify.
|
120
|
+
def verify_sig(data, sig, pub_key, algo: :ecdsa)
|
121
|
+
case algo
|
122
|
+
when :ecdsa
|
123
|
+
verify_ecdsa(data, sig, pub_key)
|
124
|
+
when :schnorr
|
125
|
+
verify_schnorr(data, sig, pub_key)
|
126
|
+
else
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# # validate whether this is a valid public key (more expensive than IsValid())
|
132
|
+
# @param [String] pub_key public key with hex format.
|
133
|
+
# @param [Boolean] allow_hybrid whether support hybrid public key.
|
134
|
+
# @return [Boolean] If valid public key return true, otherwise false.
|
135
|
+
def parse_ec_pubkey?(pub_key, allow_hybrid = false)
|
136
|
+
pub_key = pub_key.htb
|
137
|
+
return false if !allow_hybrid && ![0x02, 0x03, 0x04].include?(pub_key[0].ord)
|
138
|
+
with_context do |context|
|
139
|
+
pubkey = FFI::MemoryPointer.new(:uchar, pub_key.bytesize).put_bytes(0, pub_key)
|
140
|
+
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
141
|
+
result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pub_key.bytesize)
|
142
|
+
result == 1
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def generate_pubkey_in_context(context, privkey, compressed: true)
|
149
|
+
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
150
|
+
result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
|
151
|
+
raise 'error creating pubkey' unless result
|
152
|
+
|
153
|
+
pubkey = FFI::MemoryPointer.new(:uchar, 65)
|
154
|
+
pubkey_len = FFI::MemoryPointer.new(:uint64)
|
155
|
+
result = if compressed
|
156
|
+
pubkey_len.put_uint64(0, 33)
|
157
|
+
secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_COMPRESSED)
|
158
|
+
else
|
159
|
+
pubkey_len.put_uint64(0, 65)
|
160
|
+
secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
|
161
|
+
end
|
162
|
+
raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
|
163
|
+
pubkey.read_string(pubkey_len.read_uint64).bth
|
164
|
+
end
|
165
|
+
|
166
|
+
def sign_ecdsa(data, privkey, extra_entropy)
|
104
167
|
with_context do |context|
|
105
168
|
secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
|
106
169
|
raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
|
@@ -126,7 +189,19 @@ module Tapyrus
|
|
126
189
|
end
|
127
190
|
end
|
128
191
|
|
129
|
-
def
|
192
|
+
def sign_schnorr(data, privkey)
|
193
|
+
with_context do |context|
|
194
|
+
secret = FFI::MemoryPointer.new(:uchar, privkey.htb.bytesize).put_bytes(0, privkey.htb)
|
195
|
+
raise 'priv_key invalid' unless secp256k1_ec_seckey_verify(context, secret)
|
196
|
+
|
197
|
+
signature = FFI::MemoryPointer.new(:uchar, 64)
|
198
|
+
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
199
|
+
raise 'Failed to generate schnorr signature.' unless secp256k1_schnorr_sign(context, signature, msg32, secret, nil, nil) == 1
|
200
|
+
signature.read_string(64)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def verify_ecdsa(data, sig, pub_key)
|
130
205
|
with_context do |context|
|
131
206
|
return false if data.bytesize == 0
|
132
207
|
|
@@ -150,25 +225,23 @@ module Tapyrus
|
|
150
225
|
end
|
151
226
|
end
|
152
227
|
|
153
|
-
|
228
|
+
def verify_schnorr(data, sig, pub_key)
|
229
|
+
with_context do |context|
|
230
|
+
return false if data.bytesize == 0
|
231
|
+
pubkey = FFI::MemoryPointer.new(:uchar, pub_key.htb.bytesize).put_bytes(0, pub_key.htb)
|
232
|
+
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
233
|
+
result = secp256k1_ec_pubkey_parse(context, internal_pubkey, pubkey, pubkey.size)
|
234
|
+
return false unless result
|
154
235
|
|
155
|
-
|
156
|
-
internal_pubkey = FFI::MemoryPointer.new(:uchar, 64)
|
157
|
-
result = secp256k1_ec_pubkey_create(context, internal_pubkey, privkey.htb)
|
158
|
-
raise 'error creating pubkey' unless result
|
236
|
+
signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
|
159
237
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
else
|
166
|
-
pubkey_len.put_uint64(0, 65)
|
167
|
-
secp256k1_ec_pubkey_serialize(context, pubkey, pubkey_len, internal_pubkey, SECP256K1_EC_UNCOMPRESSED)
|
168
|
-
end
|
169
|
-
raise 'error serialize pubkey' unless result || pubkey_len.read_uint64 > 0
|
170
|
-
pubkey.read_string(pubkey_len.read_uint64).bth
|
238
|
+
msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
|
239
|
+
result = secp256k1_schnorr_verify(context, signature, msg32, internal_pubkey)
|
240
|
+
|
241
|
+
result == 1
|
242
|
+
end
|
171
243
|
end
|
244
|
+
|
172
245
|
end
|
173
246
|
end
|
174
247
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tapyrus
|
2
|
+
module Secp256k1
|
3
|
+
module RFC6979
|
4
|
+
|
5
|
+
INITIAL_V = '0101010101010101010101010101010101010101010101010101010101010101'.htb
|
6
|
+
INITIAL_K = '0000000000000000000000000000000000000000000000000000000000000000'.htb
|
7
|
+
ZERO_B = '00'.htb
|
8
|
+
ONE_B = '01'.htb
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# generate temporary key k to be used when ECDSA sign.
|
13
|
+
# https://tools.ietf.org/html/rfc6979#section-3.2
|
14
|
+
# @param [String] key_data a data contains private key and message.
|
15
|
+
# @param [String] extra_entropy extra entropy with binary format.
|
16
|
+
# @return [Integer] a nonce.
|
17
|
+
def generate_rfc6979_nonce(key_data, extra_entropy)
|
18
|
+
v = INITIAL_V # 3.2.b
|
19
|
+
k = INITIAL_K # 3.2.c
|
20
|
+
# 3.2.d
|
21
|
+
k = Tapyrus.hmac_sha256(k, v + ZERO_B + key_data + extra_entropy)
|
22
|
+
# 3.2.e
|
23
|
+
v = Tapyrus.hmac_sha256(k, v)
|
24
|
+
# 3.2.f
|
25
|
+
k = Tapyrus.hmac_sha256(k, v + ONE_B + key_data + extra_entropy)
|
26
|
+
# 3.2.g
|
27
|
+
v = Tapyrus.hmac_sha256(k, v)
|
28
|
+
# 3.2.h
|
29
|
+
t = ''
|
30
|
+
10000.times do
|
31
|
+
v = Tapyrus.hmac_sha256(k, v)
|
32
|
+
t = (t + v)
|
33
|
+
t_num = t.bth.to_i(16)
|
34
|
+
return t_num if 1 <= t_num && t_num < Tapyrus::Secp256k1::GROUP.order
|
35
|
+
k = Tapyrus.hmac_sha256(k, v + '00'.htb)
|
36
|
+
v = Tapyrus.hmac_sha256(k, v)
|
37
|
+
end
|
38
|
+
raise 'A valid nonce was not found.'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -12,8 +12,8 @@ module Tapyrus
|
|
12
12
|
private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
|
13
13
|
public_key = GROUP.generator.multiply_by_scalar(private_key)
|
14
14
|
privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
|
15
|
-
pubkey =
|
16
|
-
[privkey.bth, pubkey
|
15
|
+
pubkey = public_key.to_hex(compressed)
|
16
|
+
[privkey.bth, pubkey]
|
17
17
|
end
|
18
18
|
|
19
19
|
# generate tapyrus key object
|
@@ -24,18 +24,75 @@ module Tapyrus
|
|
24
24
|
|
25
25
|
def generate_pubkey(privkey, compressed: true)
|
26
26
|
public_key = ECDSA::Group::Secp256k1.generator.multiply_by_scalar(privkey.to_i(16))
|
27
|
-
|
27
|
+
public_key.to_hex(compressed)
|
28
28
|
end
|
29
29
|
|
30
30
|
# sign data.
|
31
31
|
# @param [String] data a data to be signed with binary format
|
32
32
|
# @param [String] privkey a private key using sign
|
33
33
|
# @return [String] signature data with binary format
|
34
|
-
def sign_data(data, privkey, extra_entropy)
|
34
|
+
def sign_data(data, privkey, extra_entropy, algo: :ecdsa)
|
35
|
+
case algo
|
36
|
+
when :ecdsa
|
37
|
+
sign_ecdsa(data, privkey, extra_entropy)
|
38
|
+
when :schnorr
|
39
|
+
Schnorr.sign(data, privkey.to_i(16)).encode
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# verify signature using public key
|
46
|
+
# @param [String] digest a SHA-256 message digest with binary format
|
47
|
+
# @param [String] sig a signature for +data+ with binary format
|
48
|
+
# @param [String] pubkey a public key corresponding to the private key used for sign
|
49
|
+
# @return [Boolean] verify result
|
50
|
+
def verify_sig(digest, sig, pubkey, algo: :ecdsa)
|
51
|
+
case algo
|
52
|
+
when :ecdsa
|
53
|
+
verify_ecdsa(digest, sig, pubkey)
|
54
|
+
when :schnorr
|
55
|
+
Schnorr.valid_sig?(digest, sig, pubkey.htb)
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
alias :valid_sig? :verify_sig
|
62
|
+
|
63
|
+
module_function :valid_sig?
|
64
|
+
|
65
|
+
# validate whether this is a valid public key (more expensive than IsValid())
|
66
|
+
# @param [String] pubkey public key with hex format.
|
67
|
+
# @param [Boolean] allow_hybrid whether support hybrid public key.
|
68
|
+
# @return [Boolean] If valid public key return true, otherwise false.
|
69
|
+
def parse_ec_pubkey?(pubkey, allow_hybrid = false)
|
70
|
+
begin
|
71
|
+
point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1, allow_hybrid: allow_hybrid)
|
72
|
+
ECDSA::Group::Secp256k1.valid_public_key?(point)
|
73
|
+
rescue ECDSA::Format::DecodeError
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# if +pubkey+ is hybrid public key format, it convert uncompressed format.
|
79
|
+
# https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
|
80
|
+
def repack_pubkey(pubkey)
|
81
|
+
p = pubkey.htb
|
82
|
+
case p[0]
|
83
|
+
when "\x06", "\x07"
|
84
|
+
p[0] = "\x04"
|
85
|
+
p
|
86
|
+
else
|
87
|
+
pubkey.htb
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def sign_ecdsa(data, privkey, extra_entropy)
|
35
92
|
privkey = privkey.htb
|
36
93
|
private_key = ECDSA::Format::IntegerOctetString.decode(privkey)
|
37
94
|
extra_entropy ||= ''
|
38
|
-
nonce = generate_rfc6979_nonce(data,
|
95
|
+
nonce = RFC6979.generate_rfc6979_nonce(privkey + data, extra_entropy)
|
39
96
|
|
40
97
|
# port form ecdsa gem.
|
41
98
|
r_point = GROUP.new_point(nonce)
|
@@ -59,12 +116,7 @@ module Tapyrus
|
|
59
116
|
signature
|
60
117
|
end
|
61
118
|
|
62
|
-
|
63
|
-
# @param [String] digest a SHA-256 message digest with binary format
|
64
|
-
# @param [String] sig a signature for +data+ with binary format
|
65
|
-
# @param [String] pubkey a public key corresponding to the private key used for sign
|
66
|
-
# @return [Boolean] verify result
|
67
|
-
def verify_sig(digest, sig, pubkey)
|
119
|
+
def verify_ecdsa(digest, sig, pubkey)
|
68
120
|
begin
|
69
121
|
k = ECDSA::Format::PointOctetString.decode(repack_pubkey(pubkey), GROUP)
|
70
122
|
signature = ECDSA::Format::SignatureDerString.decode(sig)
|
@@ -74,49 +126,6 @@ module Tapyrus
|
|
74
126
|
end
|
75
127
|
end
|
76
128
|
|
77
|
-
# if +pubkey+ is hybrid public key format, it convert uncompressed format.
|
78
|
-
# https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
|
79
|
-
def repack_pubkey(pubkey)
|
80
|
-
p = pubkey.htb
|
81
|
-
case p[0]
|
82
|
-
when "\x06", "\x07"
|
83
|
-
p[0] = "\x04"
|
84
|
-
p
|
85
|
-
else
|
86
|
-
pubkey.htb
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
INITIAL_V = '0101010101010101010101010101010101010101010101010101010101010101'.htb
|
91
|
-
INITIAL_K = '0000000000000000000000000000000000000000000000000000000000000000'.htb
|
92
|
-
ZERO_B = '00'.htb
|
93
|
-
ONE_B = '01'.htb
|
94
|
-
|
95
|
-
# generate temporary key k to be used when ECDSA sign.
|
96
|
-
# https://tools.ietf.org/html/rfc6979#section-3.2
|
97
|
-
def generate_rfc6979_nonce(data, privkey, extra_entropy)
|
98
|
-
v = INITIAL_V # 3.2.b
|
99
|
-
k = INITIAL_K # 3.2.c
|
100
|
-
# 3.2.d
|
101
|
-
k = Tapyrus.hmac_sha256(k, v + ZERO_B + privkey + data + extra_entropy)
|
102
|
-
# 3.2.e
|
103
|
-
v = Tapyrus.hmac_sha256(k, v)
|
104
|
-
# 3.2.f
|
105
|
-
k = Tapyrus.hmac_sha256(k, v + ONE_B + privkey + data + extra_entropy)
|
106
|
-
# 3.2.g
|
107
|
-
v = Tapyrus.hmac_sha256(k, v)
|
108
|
-
# 3.2.h
|
109
|
-
t = ''
|
110
|
-
10000.times do
|
111
|
-
v = Tapyrus.hmac_sha256(k, v)
|
112
|
-
t = (t + v)
|
113
|
-
t_num = t.bth.to_i(16)
|
114
|
-
return t_num if 1 <= t_num && t_num < GROUP.order
|
115
|
-
k = Tapyrus.hmac_sha256(k, v + '00'.htb)
|
116
|
-
v = Tapyrus.hmac_sha256(k, v)
|
117
|
-
end
|
118
|
-
raise 'A valid nonce was not found.'
|
119
|
-
end
|
120
129
|
end
|
121
130
|
|
122
131
|
end
|
@@ -3,6 +3,7 @@ module Tapyrus
|
|
3
3
|
|
4
4
|
# wrap a block header object with extra data.
|
5
5
|
class ChainEntry
|
6
|
+
include Tapyrus::HexConverter
|
6
7
|
|
7
8
|
attr_reader :header
|
8
9
|
attr_reader :height
|
@@ -35,7 +36,7 @@ module Tapyrus
|
|
35
36
|
|
36
37
|
# whether genesis block
|
37
38
|
def genesis?
|
38
|
-
|
39
|
+
height == 0
|
39
40
|
end
|
40
41
|
|
41
42
|
# @param [String] payload a payload with binary format.
|
@@ -43,7 +44,7 @@ module Tapyrus
|
|
43
44
|
buf = StringIO.new(payload)
|
44
45
|
len = Tapyrus.unpack_var_int_from_io(buf)
|
45
46
|
height = buf.read(len).reverse.bth.to_i(16)
|
46
|
-
new(Tapyrus::BlockHeader.parse_from_payload(buf
|
47
|
+
new(Tapyrus::BlockHeader.parse_from_payload(buf), height)
|
47
48
|
end
|
48
49
|
|
49
50
|
# build next block +StoredBlock+ instance.
|
@@ -58,14 +58,64 @@ module Tapyrus
|
|
58
58
|
db.get(KEY_PREFIX[:entry] + hash)
|
59
59
|
end
|
60
60
|
|
61
|
+
# Save entry.
|
62
|
+
# @param [Tapyrus::Store::ChainEntry]
|
61
63
|
def save_entry(entry)
|
62
64
|
db.batch do
|
63
65
|
db.put(entry.key ,entry.to_payload)
|
64
66
|
db.put(height_key(entry.height), entry.block_hash)
|
67
|
+
add_agg_pubkey(entry.height == 0 ? 0 : entry.height + 1, entry.header.x_field) if entry.header.upgrade_agg_pubkey?
|
65
68
|
connect_entry(entry)
|
66
69
|
end
|
67
70
|
end
|
68
71
|
|
72
|
+
# Add aggregated public key.
|
73
|
+
# @param [Integer] activate_height
|
74
|
+
# @param [String] agg_pubkey aggregated public key with hex format.
|
75
|
+
def add_agg_pubkey(activate_height, agg_pubkey)
|
76
|
+
payload = activate_height.to_even_length_hex + agg_pubkey
|
77
|
+
index = latest_agg_pubkey_index
|
78
|
+
next_index = (index.nil? ? 0 : index + 1).to_even_length_hex
|
79
|
+
db.batch do
|
80
|
+
db.put(KEY_PREFIX[:agg_pubkey] + next_index, payload)
|
81
|
+
db.put(KEY_PREFIX[:latest_agg_pubkey], next_index)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get aggregated public key by specifying +index+.
|
86
|
+
# @param [Integer] index
|
87
|
+
# @return [Array] tupple of activate height and aggregated public key.
|
88
|
+
def agg_pubkey(index)
|
89
|
+
payload = db.get(KEY_PREFIX[:agg_pubkey] + index.to_even_length_hex)
|
90
|
+
[payload[0...(payload.length - 66)].to_i(16), payload[(payload.length - 66)..-1]]
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get aggregated public key by specifying block +height+.
|
94
|
+
# @param [Integer] height block height.
|
95
|
+
# @return [String] aggregated public key with hex format.
|
96
|
+
def agg_pubkey_with_height(height)
|
97
|
+
index = latest_agg_pubkey_index
|
98
|
+
index ||= 0
|
99
|
+
(index + 1).times do |i|
|
100
|
+
target = index - i
|
101
|
+
active_height, pubkey = agg_pubkey(target)
|
102
|
+
return pubkey unless active_height > height
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get latest aggregated public key.
|
107
|
+
# @return [Array] aggregated public key with hex format.
|
108
|
+
def latest_agg_pubkey
|
109
|
+
agg_pubkey(latest_agg_pubkey_index)[1]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get aggregated public key list.
|
113
|
+
# @return [Array[Array]] list of public key and index
|
114
|
+
def agg_pubkeys
|
115
|
+
index = latest_agg_pubkey_index
|
116
|
+
(index + 1).times.map { |i| agg_pubkey(i) }
|
117
|
+
end
|
118
|
+
|
69
119
|
def close
|
70
120
|
db.close
|
71
121
|
end
|
@@ -91,6 +141,14 @@ module Tapyrus
|
|
91
141
|
db.put(KEY_PREFIX[:best], entry.block_hash)
|
92
142
|
db.put(KEY_PREFIX[:next] + entry.prev_hash, entry.block_hash)
|
93
143
|
end
|
144
|
+
|
145
|
+
# Get latest aggregated public key index.
|
146
|
+
# @return [Integer] key index
|
147
|
+
def latest_agg_pubkey_index
|
148
|
+
index = db.get(KEY_PREFIX[:latest_agg_pubkey])
|
149
|
+
index&.to_i(16)
|
150
|
+
end
|
151
|
+
|
94
152
|
end
|
95
153
|
|
96
154
|
end
|