tapyrus 0.2.1 → 0.2.6
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/.travis.yml +5 -3
- data/README.md +1 -1
- data/lib/schnorr.rb +1 -1
- data/lib/tapyrus.rb +2 -7
- data/lib/tapyrus/block.rb +3 -7
- data/lib/tapyrus/block_header.rb +66 -38
- data/lib/tapyrus/chain_params.rb +0 -10
- data/lib/tapyrus/chainparams/dev.yml +0 -10
- data/lib/tapyrus/chainparams/prod.yml +0 -9
- data/lib/tapyrus/errors.rb +17 -0
- data/lib/tapyrus/ext/ecdsa.rb +39 -0
- data/lib/tapyrus/ext_key.rb +39 -15
- data/lib/tapyrus/key.rb +15 -36
- data/lib/tapyrus/message/block.rb +1 -1
- 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/rpc/request_handler.rb +5 -4
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +1 -1
- data/lib/tapyrus/script/color.rb +10 -2
- data/lib/tapyrus/script/script.rb +30 -0
- data/lib/tapyrus/script/tx_checker.rb +7 -3
- data/lib/tapyrus/secp256k1/native.rb +93 -20
- data/lib/tapyrus/secp256k1/ruby.rb +62 -27
- data/lib/tapyrus/store/chain_entry.rb +2 -2
- data/lib/tapyrus/store/db/level_db.rb +58 -0
- data/lib/tapyrus/store/spv_chain.rb +29 -11
- data/lib/tapyrus/version.rb +1 -1
- data/tapyrusrb.gemspec +2 -4
- metadata +7 -33
data/lib/tapyrus/key.rb
CHANGED
@@ -30,7 +30,7 @@ module Tapyrus
|
|
30
30
|
# @param [Integer] key_type a key type which determine address type.
|
31
31
|
# @param [Boolean] compressed [Deprecated] whether public key is compressed.
|
32
32
|
# @return [Tapyrus::Key] a key object.
|
33
|
-
def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true)
|
33
|
+
def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
|
34
34
|
puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
|
35
35
|
if key_type
|
36
36
|
@key_type = key_type
|
@@ -41,13 +41,14 @@ module Tapyrus
|
|
41
41
|
@secp256k1_module = Tapyrus.secp_impl
|
42
42
|
@priv_key = priv_key
|
43
43
|
if @priv_key
|
44
|
-
raise ArgumentError,
|
44
|
+
raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
|
45
45
|
end
|
46
46
|
if pubkey
|
47
47
|
@pubkey = pubkey
|
48
48
|
else
|
49
49
|
@pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key
|
50
50
|
end
|
51
|
+
raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless fully_valid_pubkey?(allow_hybrid)
|
51
52
|
end
|
52
53
|
|
53
54
|
# generate key pair
|
@@ -65,7 +66,7 @@ module Tapyrus
|
|
65
66
|
data = hex[2...-8].htb
|
66
67
|
checksum = hex[-8..-1]
|
67
68
|
raise ArgumentError, 'invalid version' unless version == Tapyrus.chain_params.privkey_version
|
68
|
-
raise ArgumentError,
|
69
|
+
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Tapyrus.calc_checksum(version + data.bth) == checksum
|
69
70
|
key_len = data.bytesize
|
70
71
|
if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack('C').first == 1
|
71
72
|
key_type = TYPES[:compressed]
|
@@ -95,10 +96,13 @@ module Tapyrus
|
|
95
96
|
# @return [String] signature data with binary format
|
96
97
|
def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa)
|
97
98
|
raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
|
98
|
-
|
99
|
+
case algo
|
100
|
+
when :ecdsa
|
99
101
|
sign_ecdsa(data, low_r, extra_entropy)
|
102
|
+
when :schnorr
|
103
|
+
secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: algo)
|
100
104
|
else
|
101
|
-
|
105
|
+
false
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
@@ -111,11 +115,8 @@ module Tapyrus
|
|
111
115
|
return false unless valid_pubkey?
|
112
116
|
begin
|
113
117
|
raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
|
114
|
-
if algo == :ecdsa
|
115
|
-
|
116
|
-
else
|
117
|
-
verify_schnorr_sig(sig, origin)
|
118
|
-
end
|
118
|
+
sig = ecdsa_signature_parse_der_lax(sig) if algo == :ecdsa
|
119
|
+
secp256k1_module.verify_sig(origin, sig, pubkey, algo: algo)
|
119
120
|
rescue Exception
|
120
121
|
false
|
121
122
|
end
|
@@ -223,14 +224,8 @@ module Tapyrus
|
|
223
224
|
end
|
224
225
|
|
225
226
|
# fully validate whether this is a valid public key (more expensive than IsValid())
|
226
|
-
def fully_valid_pubkey?
|
227
|
-
|
228
|
-
begin
|
229
|
-
point = ECDSA::Format::PointOctetString.decode(pubkey.htb, ECDSA::Group::Secp256k1)
|
230
|
-
ECDSA::Group::Secp256k1.valid_public_key?(point)
|
231
|
-
rescue ECDSA::Format::DecodeError
|
232
|
-
false
|
233
|
-
end
|
227
|
+
def fully_valid_pubkey?(allow_hybrid = false)
|
228
|
+
valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid)
|
234
229
|
end
|
235
230
|
|
236
231
|
private
|
@@ -291,34 +286,18 @@ module Tapyrus
|
|
291
286
|
|
292
287
|
# generate ecdsa signature
|
293
288
|
def sign_ecdsa(data, low_r, extra_entropy)
|
294
|
-
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
|
289
|
+
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
|
295
290
|
if low_r && !sig_has_low_r?(sig)
|
296
291
|
counter = 1
|
297
292
|
until sig_has_low_r?(sig)
|
298
293
|
extra_entropy = [counter].pack('I*').bth.ljust(64, '0').htb
|
299
|
-
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy)
|
294
|
+
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
|
300
295
|
counter += 1
|
301
296
|
end
|
302
297
|
end
|
303
298
|
sig
|
304
299
|
end
|
305
300
|
|
306
|
-
# generate schnorr signature
|
307
|
-
def sign_schnorr(msg)
|
308
|
-
Schnorr.sign(msg, priv_key.to_i(16)).encode
|
309
|
-
end
|
310
|
-
|
311
|
-
# verify ecdsa signature
|
312
|
-
def verify_ecdsa_sig(sig, message)
|
313
|
-
sig = ecdsa_signature_parse_der_lax(sig)
|
314
|
-
secp256k1_module.verify_sig(message, sig, pubkey)
|
315
|
-
end
|
316
|
-
|
317
|
-
# verify schnorr signature
|
318
|
-
def verify_schnorr_sig(sig, message)
|
319
|
-
Schnorr.valid_sig?(message, sig, pubkey.htb)
|
320
|
-
end
|
321
|
-
|
322
301
|
end
|
323
302
|
|
324
303
|
end
|
@@ -19,7 +19,7 @@ module Tapyrus
|
|
19
19
|
|
20
20
|
def self.parse_from_payload(payload)
|
21
21
|
buf = StringIO.new(payload)
|
22
|
-
header = Tapyrus::BlockHeader.parse_from_payload(buf
|
22
|
+
header = Tapyrus::BlockHeader.parse_from_payload(buf)
|
23
23
|
transactions = []
|
24
24
|
unless buf.eof?
|
25
25
|
tx_count = Tapyrus.unpack_var_int_from_io(buf)
|
@@ -21,7 +21,7 @@ module Tapyrus
|
|
21
21
|
|
22
22
|
def self.parse_from_payload(payload)
|
23
23
|
buf = StringIO.new(payload)
|
24
|
-
header = Tapyrus::BlockHeader.parse_from_payload(buf
|
24
|
+
header = Tapyrus::BlockHeader.parse_from_payload(buf)
|
25
25
|
nonce = buf.read(8).unpack('q*').first
|
26
26
|
short_ids_len = Tapyrus.unpack_var_int_from_io(buf)
|
27
27
|
short_ids = short_ids_len.times.map do
|
@@ -19,7 +19,7 @@ module Tapyrus
|
|
19
19
|
header_count = Tapyrus.unpack_var_int_from_io(buf)
|
20
20
|
h = new
|
21
21
|
header_count.times do
|
22
|
-
h.headers << Tapyrus::BlockHeader.parse_from_payload(buf
|
22
|
+
h.headers << Tapyrus::BlockHeader.parse_from_payload(buf)
|
23
23
|
buf.read(1) # read tx count 0x00 (headers message doesn't include any tx.)
|
24
24
|
end
|
25
25
|
h
|
@@ -19,7 +19,7 @@ module Tapyrus
|
|
19
19
|
def self.parse_from_payload(payload)
|
20
20
|
m = new
|
21
21
|
buf = StringIO.new(payload)
|
22
|
-
m.header = Tapyrus::BlockHeader.parse_from_payload(buf
|
22
|
+
m.header = Tapyrus::BlockHeader.parse_from_payload(buf)
|
23
23
|
m.tx_count = buf.read(4).unpack('V').first
|
24
24
|
hash_count = Tapyrus.unpack_var_int_from_io(buf)
|
25
25
|
hash_count.times do
|
@@ -11,7 +11,6 @@ module Tapyrus
|
|
11
11
|
best_block = node.chain.latest_block
|
12
12
|
h[:headers] = best_block.height
|
13
13
|
h[:bestblockhash] = best_block.header.block_id
|
14
|
-
h[:chainwork] = best_block.header.work
|
15
14
|
h[:mediantime] = node.chain.mtp(best_block.block_hash)
|
16
15
|
h
|
17
16
|
end
|
@@ -32,12 +31,14 @@ module Tapyrus
|
|
32
31
|
hash: block_id,
|
33
32
|
height: entry.height,
|
34
33
|
features: entry.header.features,
|
35
|
-
featuresHex: entry.header.features.to_even_length_hex,
|
34
|
+
featuresHex: entry.header.features.to_even_length_hex.ljust(8, '0'),
|
36
35
|
merkleroot: entry.header.merkle_root.rhex,
|
36
|
+
immutablemerkleroot: entry.header.im_merkle_root.rhex,
|
37
37
|
time: entry.header.time,
|
38
38
|
mediantime: node.chain.mtp(block_hash),
|
39
|
-
|
40
|
-
|
39
|
+
xfield_type: entry.header.x_field_type,
|
40
|
+
xfield: entry.header.x_field,
|
41
|
+
proof: entry.header.proof,
|
41
42
|
previousblockhash: entry.prev_hash.rhex,
|
42
43
|
nextblockhash: node.chain.next_hash(block_hash).rhex
|
43
44
|
}
|
@@ -64,7 +64,7 @@ module Tapyrus
|
|
64
64
|
response = http.request(request)
|
65
65
|
body = response.body
|
66
66
|
response = Tapyrus::Ext::JsonParser.new(body.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') }).parse
|
67
|
-
raise response['error'].
|
67
|
+
raise response['error'].to_json if response['error']
|
68
68
|
response['result']
|
69
69
|
end
|
70
70
|
|
data/lib/tapyrus/script/color.rb
CHANGED
@@ -43,6 +43,14 @@ module Tapyrus
|
|
43
43
|
true
|
44
44
|
end
|
45
45
|
|
46
|
+
def hash
|
47
|
+
to_payload.hash
|
48
|
+
end
|
49
|
+
|
50
|
+
def eql?(other)
|
51
|
+
to_payload.eql?(other.to_payload)
|
52
|
+
end
|
53
|
+
|
46
54
|
private
|
47
55
|
|
48
56
|
def initialize(type, payload)
|
@@ -53,11 +61,11 @@ module Tapyrus
|
|
53
61
|
|
54
62
|
module ColoredOutput
|
55
63
|
def colored?
|
56
|
-
script_pubkey.
|
64
|
+
script_pubkey.colored?
|
57
65
|
end
|
58
66
|
|
59
67
|
def color_id
|
60
|
-
@color_id ||=
|
68
|
+
@color_id ||= script_pubkey.color_id
|
61
69
|
end
|
62
70
|
|
63
71
|
def reissuable?
|
@@ -75,6 +75,18 @@ module Tapyrus
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
# Remove color identifier from cp2pkh or cp2sh
|
79
|
+
# @param [ColorIdentifier] color identifier
|
80
|
+
# @return [Script] P2PKH or P2SH script
|
81
|
+
# @raise [RuntimeError] if script is neither cp2pkh nor cp2sh
|
82
|
+
def remove_color
|
83
|
+
raise RuntimeError, 'Only cp2pkh and cp2sh can remove color' unless cp2pkh? or cp2sh?
|
84
|
+
|
85
|
+
Tapyrus::Script.new.tap do |s|
|
86
|
+
s.chunks = self.chunks[2..-1]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
78
90
|
def get_multisig_pubkeys
|
79
91
|
num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
|
80
92
|
(1..num).map{ |i| chunks[i].pushed_data }
|
@@ -214,6 +226,8 @@ module Tapyrus
|
|
214
226
|
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
215
227
|
end
|
216
228
|
|
229
|
+
# Return whether this script is a CP2PKH format script or not.
|
230
|
+
# @return [Boolean] true if this script is cp2pkh, otherwise false.
|
217
231
|
def cp2pkh?
|
218
232
|
return false unless chunks.size == 7
|
219
233
|
return false unless chunks[0].bytesize == 34
|
@@ -223,6 +237,8 @@ module Tapyrus
|
|
223
237
|
(chunks[2..3]+ chunks[5..6]).map(&:ord) && chunks[4].bytesize == 21
|
224
238
|
end
|
225
239
|
|
240
|
+
# Return whether this script is a CP2SH format script or not.
|
241
|
+
# @return [Boolean] true if this script is cp2pkh, otherwise false.
|
226
242
|
def cp2sh?
|
227
243
|
return false unless chunks.size == 5
|
228
244
|
return false unless chunks[0].bytesize == 34
|
@@ -230,6 +246,20 @@ module Tapyrus
|
|
230
246
|
return false unless chunks[1].ord == OP_COLOR
|
231
247
|
OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
|
232
248
|
end
|
249
|
+
|
250
|
+
# Return whether this script represents colored coin.
|
251
|
+
# @return [Boolean] true if this script is colored, otherwise false.
|
252
|
+
def colored?
|
253
|
+
cp2pkh? || cp2sh?
|
254
|
+
end
|
255
|
+
|
256
|
+
# Return color identifier for this script.
|
257
|
+
# @return [ColorIdentifer] color identifier for this script if this script is colored. return nil if this script is not colored.
|
258
|
+
def color_id
|
259
|
+
return nil unless colored?
|
260
|
+
|
261
|
+
Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)
|
262
|
+
end
|
233
263
|
|
234
264
|
def op_return_data
|
235
265
|
return nil unless op_return?
|
@@ -32,12 +32,16 @@ module Tapyrus
|
|
32
32
|
# @param [String] pubkey a public key with hex format.
|
33
33
|
# @param [String] digest a message digest with binary format to be verified.
|
34
34
|
# @return [Boolean] if check is passed return true, otherwise false.
|
35
|
-
def verify_sig(sig, pubkey, digest)
|
35
|
+
def verify_sig(sig, pubkey, digest, allow_hybrid: false)
|
36
36
|
key_type = pubkey.start_with?('02') || pubkey.start_with?('03') ? Key::TYPES[:compressed] : Key::TYPES[:uncompressed]
|
37
37
|
sig = sig.htb
|
38
38
|
algo = sig.bytesize == 64 ? :schnorr : :ecdsa
|
39
|
-
|
40
|
-
|
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
|
41
45
|
end
|
42
46
|
|
43
47
|
def check_locktime(locktime)
|
@@ -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
|
@@ -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,14 +24,71 @@ 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 ||= ''
|
@@ -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,23 +126,6 @@ module Tapyrus
|
|
74
126
|
end
|
75
127
|
end
|
76
128
|
|
77
|
-
alias :valid_sig? :verify_sig
|
78
|
-
|
79
|
-
module_function :valid_sig?
|
80
|
-
|
81
|
-
# if +pubkey+ is hybrid public key format, it convert uncompressed format.
|
82
|
-
# https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
|
83
|
-
def repack_pubkey(pubkey)
|
84
|
-
p = pubkey.htb
|
85
|
-
case p[0]
|
86
|
-
when "\x06", "\x07"
|
87
|
-
p[0] = "\x04"
|
88
|
-
p
|
89
|
-
else
|
90
|
-
pubkey.htb
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
129
|
end
|
95
130
|
|
96
131
|
end
|