tapyrus 0.3.4 → 0.3.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/.github/workflows/ruby.yml +2 -2
- data/{.prettierrc.yaml → .prettierrc.yml} +0 -1
- data/.ruby-version +1 -1
- data/Gemfile +2 -2
- data/README.md +1 -1
- data/Rakefile +2 -2
- data/exe/tapyrus-script-debugger +5 -5
- data/exe/tapyrusrb-cli +1 -1
- data/exe/tapyrusrbd +18 -17
- data/lib/openassets/payload.rb +2 -2
- data/lib/openassets/util.rb +2 -2
- data/lib/openassets.rb +4 -4
- data/lib/schnorr/sign_to_contract.rb +4 -4
- data/lib/schnorr/signature.rb +5 -5
- data/lib/schnorr.rb +14 -14
- data/lib/tapyrus/base58.rb +8 -8
- data/lib/tapyrus/bip175.rb +5 -5
- data/lib/tapyrus/block_header.rb +2 -2
- data/lib/tapyrus/bloom_filter.rb +1 -1
- data/lib/tapyrus/chain_params.rb +5 -5
- data/lib/tapyrus/constants.rb +1 -1
- data/lib/tapyrus/errors.rb +9 -9
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -1
- data/lib/tapyrus/ext.rb +3 -3
- data/lib/tapyrus/ext_key.rb +21 -21
- data/lib/tapyrus/jws.rb +76 -0
- data/lib/tapyrus/key.rb +48 -20
- data/lib/tapyrus/key_path.rb +3 -3
- data/lib/tapyrus/logger.rb +4 -11
- data/lib/tapyrus/merkle_tree.rb +1 -1
- data/lib/tapyrus/message/addr.rb +2 -2
- data/lib/tapyrus/message/base.rb +2 -2
- data/lib/tapyrus/message/block.rb +1 -1
- data/lib/tapyrus/message/block_txn.rb +1 -1
- data/lib/tapyrus/message/cmpct_block.rb +1 -1
- data/lib/tapyrus/message/fee_filter.rb +3 -3
- data/lib/tapyrus/message/filter_add.rb +1 -1
- data/lib/tapyrus/message/filter_clear.rb +2 -2
- data/lib/tapyrus/message/filter_load.rb +7 -7
- data/lib/tapyrus/message/get_addr.rb +2 -2
- data/lib/tapyrus/message/get_block_txn.rb +1 -1
- data/lib/tapyrus/message/get_blocks.rb +1 -1
- data/lib/tapyrus/message/get_data.rb +1 -1
- data/lib/tapyrus/message/get_headers.rb +1 -1
- data/lib/tapyrus/message/header_and_short_ids.rb +6 -6
- data/lib/tapyrus/message/headers.rb +1 -1
- data/lib/tapyrus/message/headers_parser.rb +2 -2
- data/lib/tapyrus/message/inv.rb +1 -1
- data/lib/tapyrus/message/inventory.rb +3 -3
- data/lib/tapyrus/message/mem_pool.rb +2 -2
- data/lib/tapyrus/message/merkle_block.rb +3 -3
- data/lib/tapyrus/message/network_addr.rb +9 -9
- data/lib/tapyrus/message/not_found.rb +1 -1
- data/lib/tapyrus/message/ping.rb +3 -3
- data/lib/tapyrus/message/pong.rb +3 -3
- data/lib/tapyrus/message/reject.rb +6 -6
- data/lib/tapyrus/message/send_cmpct.rb +4 -4
- data/lib/tapyrus/message/send_headers.rb +2 -2
- data/lib/tapyrus/message/tx.rb +1 -1
- data/lib/tapyrus/message/ver_ack.rb +2 -2
- data/lib/tapyrus/message/version.rb +7 -7
- data/lib/tapyrus/message.rb +37 -37
- data/lib/tapyrus/mnemonic.rb +18 -16
- data/lib/tapyrus/network/connection.rb +2 -2
- data/lib/tapyrus/network/message_handler.rb +11 -11
- data/lib/tapyrus/network/peer.rb +1 -1
- data/lib/tapyrus/network/peer_discovery.rb +1 -1
- data/lib/tapyrus/network/pool.rb +4 -4
- data/lib/tapyrus/network.rb +6 -6
- data/lib/tapyrus/node/cli.rb +34 -34
- data/lib/tapyrus/node/configuration.rb +3 -3
- data/lib/tapyrus/node/spv.rb +2 -2
- data/lib/tapyrus/node.rb +3 -3
- data/lib/tapyrus/opcodes.rb +7 -7
- data/lib/tapyrus/out_point.rb +2 -2
- data/lib/tapyrus/rpc/http_server.rb +6 -6
- data/lib/tapyrus/rpc/request_handler.rb +5 -5
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +10 -10
- data/lib/tapyrus/rpc.rb +4 -4
- data/lib/tapyrus/script/color.rb +3 -3
- data/lib/tapyrus/script/debugger.rb +9 -9
- data/lib/tapyrus/script/multisig.rb +2 -2
- data/lib/tapyrus/script/script.rb +28 -28
- data/lib/tapyrus/script/script_error.rb +42 -42
- data/lib/tapyrus/script/script_interpreter.rb +9 -9
- data/lib/tapyrus/script/tx_checker.rb +2 -2
- data/lib/tapyrus/secp256k1/native.rb +23 -23
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -7
- data/lib/tapyrus/secp256k1/ruby.rb +2 -2
- data/lib/tapyrus/secp256k1.rb +3 -3
- data/lib/tapyrus/slip39/share.rb +7 -7
- data/lib/tapyrus/slip39/sss.rb +24 -24
- data/lib/tapyrus/slip39.rb +514 -37
- data/lib/tapyrus/store/db/level_db.rb +2 -2
- data/lib/tapyrus/store/db.rb +1 -1
- data/lib/tapyrus/store/spv_chain.rb +7 -7
- data/lib/tapyrus/store.rb +3 -3
- data/lib/tapyrus/tip0137.rb +201 -0
- data/lib/tapyrus/tx.rb +12 -12
- data/lib/tapyrus/tx_builder.rb +3 -3
- data/lib/tapyrus/tx_in.rb +3 -3
- data/lib/tapyrus/tx_out.rb +3 -3
- data/lib/tapyrus/util.rb +21 -21
- data/lib/tapyrus/validation.rb +9 -9
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +12 -12
- data/lib/tapyrus/wallet/base.rb +9 -8
- data/lib/tapyrus/wallet/db.rb +11 -11
- data/lib/tapyrus/wallet/master_key.rb +13 -13
- data/lib/tapyrus/wallet.rb +4 -4
- data/lib/tapyrus.rb +62 -59
- data/tapyrusrb.gemspec +34 -31
- metadata +45 -12
data/lib/tapyrus/ext_key.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Tapyrus
|
|
|
7
7
|
include Tapyrus::HexConverter
|
|
8
8
|
|
|
9
9
|
MAX_DEPTH = 255
|
|
10
|
-
MASTER_FINGERPRINT =
|
|
10
|
+
MASTER_FINGERPRINT = "00000000"
|
|
11
11
|
|
|
12
12
|
attr_accessor :ver
|
|
13
13
|
attr_accessor :depth
|
|
@@ -21,10 +21,10 @@ module Tapyrus
|
|
|
21
21
|
def self.generate_master(seed)
|
|
22
22
|
ext_key = ExtKey.new
|
|
23
23
|
ext_key.depth = ext_key.number = 0
|
|
24
|
-
ext_key.parent_fingerprint =
|
|
25
|
-
l = Tapyrus.hmac_sha512(
|
|
24
|
+
ext_key.parent_fingerprint = "00000000"
|
|
25
|
+
l = Tapyrus.hmac_sha512("Tapyrus seed", seed.htb)
|
|
26
26
|
left = l[0..31].bth.to_i(16)
|
|
27
|
-
raise
|
|
27
|
+
raise "invalid key" if left >= CURVE_ORDER || left == 0
|
|
28
28
|
l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
|
|
29
29
|
ext_key.key = Tapyrus::Key.new(priv_key: l_priv.bth, key_type: Tapyrus::Key::TYPES[:compressed])
|
|
30
30
|
ext_key.chain_code = l[32..-1]
|
|
@@ -45,8 +45,8 @@ module Tapyrus
|
|
|
45
45
|
|
|
46
46
|
# serialize extended private key
|
|
47
47
|
def to_payload
|
|
48
|
-
version.htb << [depth].pack(
|
|
49
|
-
[0x00].pack(
|
|
48
|
+
version.htb << [depth].pack("C") << parent_fingerprint.htb << [number].pack("N") << chain_code <<
|
|
49
|
+
[0x00].pack("C") << key.priv_key.htb
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Base58 encoded extended private key
|
|
@@ -99,15 +99,15 @@ module Tapyrus
|
|
|
99
99
|
new_key.number = number
|
|
100
100
|
new_key.parent_fingerprint = fingerprint
|
|
101
101
|
if number > (Tapyrus::HARDENED_THRESHOLD - 1)
|
|
102
|
-
data = [0x00].pack(
|
|
102
|
+
data = [0x00].pack("C") << key.priv_key.htb << [number].pack("N")
|
|
103
103
|
else
|
|
104
|
-
data = key.pubkey.htb << [number].pack(
|
|
104
|
+
data = key.pubkey.htb << [number].pack("N")
|
|
105
105
|
end
|
|
106
106
|
l = Tapyrus.hmac_sha512(chain_code, data)
|
|
107
107
|
left = l[0..31].bth.to_i(16)
|
|
108
|
-
raise
|
|
108
|
+
raise "invalid key" if left >= CURVE_ORDER
|
|
109
109
|
child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
|
|
110
|
-
raise
|
|
110
|
+
raise "invalid key " if child_priv >= CURVE_ORDER
|
|
111
111
|
child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
|
|
112
112
|
new_key.key = Tapyrus::Key.new(priv_key: child_priv.bth, key_type: key_type)
|
|
113
113
|
new_key.chain_code = l[32..-1]
|
|
@@ -143,9 +143,9 @@ module Tapyrus
|
|
|
143
143
|
ext_key = ExtKey.new
|
|
144
144
|
ext_key.ver = buf.read(4).bth # version
|
|
145
145
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtKey.support_version?(ext_key.ver)
|
|
146
|
-
ext_key.depth = buf.read(1).unpack(
|
|
146
|
+
ext_key.depth = buf.read(1).unpack("C").first
|
|
147
147
|
ext_key.parent_fingerprint = buf.read(4).bth
|
|
148
|
-
ext_key.number = buf.read(4).unpack(
|
|
148
|
+
ext_key.number = buf.read(4).unpack("N").first
|
|
149
149
|
if ext_key.depth == 0
|
|
150
150
|
unless ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
|
151
151
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT
|
|
@@ -156,7 +156,7 @@ module Tapyrus
|
|
|
156
156
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH
|
|
157
157
|
end
|
|
158
158
|
ext_key.chain_code = buf.read(32)
|
|
159
|
-
raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth ==
|
|
159
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == "00" # 0x00
|
|
160
160
|
ext_key.key = Tapyrus::Key.new(priv_key: buf.read(32).bth, key_type: Tapyrus::Key::TYPES[:compressed])
|
|
161
161
|
ext_key
|
|
162
162
|
end
|
|
@@ -200,7 +200,7 @@ module Tapyrus
|
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def master?
|
|
203
|
-
depth == 0 && number == 0 && parent_fingerprint ==
|
|
203
|
+
depth == 0 && number == 0 && parent_fingerprint == "00000000"
|
|
204
204
|
end
|
|
205
205
|
end
|
|
206
206
|
|
|
@@ -217,7 +217,7 @@ module Tapyrus
|
|
|
217
217
|
|
|
218
218
|
# serialize extended pubkey
|
|
219
219
|
def to_payload
|
|
220
|
-
version.htb << [depth].pack(
|
|
220
|
+
version.htb << [depth].pack("C") << parent_fingerprint.htb << [number].pack("N") << chain_code << pub.htb
|
|
221
221
|
end
|
|
222
222
|
|
|
223
223
|
def pub
|
|
@@ -272,11 +272,11 @@ module Tapyrus
|
|
|
272
272
|
new_key.depth = depth + 1
|
|
273
273
|
new_key.number = number
|
|
274
274
|
new_key.parent_fingerprint = fingerprint
|
|
275
|
-
raise
|
|
276
|
-
data = pub.htb << [number].pack(
|
|
275
|
+
raise "hardened key is not support" if number > (Tapyrus::HARDENED_THRESHOLD - 1)
|
|
276
|
+
data = pub.htb << [number].pack("N")
|
|
277
277
|
l = Tapyrus.hmac_sha512(chain_code, data)
|
|
278
278
|
left = l[0..31].bth.to_i(16)
|
|
279
|
-
raise
|
|
279
|
+
raise "invalid key" if left >= CURVE_ORDER
|
|
280
280
|
l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
|
|
281
281
|
p1 = Tapyrus::Key.new(priv_key: l_priv.bth, key_type: Tapyrus::Key::TYPES[:uncompressed]).to_point
|
|
282
282
|
p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
|
@@ -314,9 +314,9 @@ module Tapyrus
|
|
|
314
314
|
ext_pubkey = ExtPubkey.new
|
|
315
315
|
ext_pubkey.ver = buf.read(4).bth # version
|
|
316
316
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtPubkey.support_version?(ext_pubkey.ver)
|
|
317
|
-
ext_pubkey.depth = buf.read(1).unpack(
|
|
317
|
+
ext_pubkey.depth = buf.read(1).unpack("C").first
|
|
318
318
|
ext_pubkey.parent_fingerprint = buf.read(4).bth
|
|
319
|
-
ext_pubkey.number = buf.read(4).unpack(
|
|
319
|
+
ext_pubkey.number = buf.read(4).unpack("N").first
|
|
320
320
|
if ext_pubkey.depth == 0
|
|
321
321
|
unless ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
|
322
322
|
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT
|
|
@@ -365,7 +365,7 @@ module Tapyrus
|
|
|
365
365
|
end
|
|
366
366
|
|
|
367
367
|
def master?
|
|
368
|
-
depth == 0 && number == 0 && parent_fingerprint ==
|
|
368
|
+
depth == 0 && number == 0 && parent_fingerprint == "00000000"
|
|
369
369
|
end
|
|
370
370
|
end
|
|
371
371
|
end
|
data/lib/tapyrus/jws.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
module JWS
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
class DecodeError < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
ALGO = "ES256K"
|
|
9
|
+
CURVE_NAME = "secp256k1"
|
|
10
|
+
|
|
11
|
+
# Encode data as JWS format.
|
|
12
|
+
#
|
|
13
|
+
# @param payload [Object] The data to be encoded as JWS.
|
|
14
|
+
# @param private_key_hex [String] The private key as hex string
|
|
15
|
+
# @return [String] JWS signed with the specified private key
|
|
16
|
+
def encode(payload, private_key_hex)
|
|
17
|
+
parameters = { use: "sig", alg: ALGO }
|
|
18
|
+
|
|
19
|
+
# see https://github.com/nov/json-jwt/blob/413848c/lib/json/jwk.rb#L162-L172
|
|
20
|
+
# see https://www.rfc-editor.org/rfc/rfc5915.html#page-6
|
|
21
|
+
sequence =
|
|
22
|
+
OpenSSL::ASN1.Sequence(
|
|
23
|
+
[
|
|
24
|
+
OpenSSL::ASN1.Integer(1),
|
|
25
|
+
OpenSSL::ASN1.OctetString(OpenSSL::BN.new(private_key_hex, 16).to_s(2)),
|
|
26
|
+
OpenSSL::ASN1.ObjectId(CURVE_NAME, 0, :EXPLICIT),
|
|
27
|
+
OpenSSL::ASN1.BitString(
|
|
28
|
+
ECDSA::Format::PointOctetString.encode(point_for(private_key_hex), compression: false),
|
|
29
|
+
1,
|
|
30
|
+
:EXPLICIT
|
|
31
|
+
)
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
ec_key = OpenSSL::PKey::EC.new(sequence.to_der)
|
|
35
|
+
jwk = JWT::JWK.new(ec_key, parameters)
|
|
36
|
+
jwks_hash = JWT::JWK::Set.new(jwk).export(include_private: false)
|
|
37
|
+
JWT.encode(payload, jwk.signing_key, jwk[:alg], { typ: "JWT", algo: ALGO, jwk: jwks_hash })
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Decode JWS to JSON object
|
|
41
|
+
#
|
|
42
|
+
# @param jws [String] The JWS formatted data to be decoded
|
|
43
|
+
# @return [Array[JSON]] JSON objects representing JWS header and payload.
|
|
44
|
+
# @raise [JWT::VerificationError] If the verification of the signature fails
|
|
45
|
+
# @raise [Tapyrus::JWS::DecodeError] If no jwk key found in header
|
|
46
|
+
# @raise [Tapyrus::JWS::DecodeError] If jwk kty header is not EC
|
|
47
|
+
# @raise [Tapyrus::JWS::DecodeError] If jwk crv header is not P-256K
|
|
48
|
+
# @raise [Tapyrus::JWS::DecodeError] If jwk use header is not sig
|
|
49
|
+
# @raise [Tapyrus::JWS::DecodeError] If jwk alg header is not ES256K
|
|
50
|
+
def decode(jws)
|
|
51
|
+
jwt_claims, header = JWT.decode(jws, nil, false, { algorithm: ALGO })
|
|
52
|
+
jwks_hash = header.dig("jwk", "keys")
|
|
53
|
+
raise Tapyrus::JWS::DecodeError, "No jwk key found in header" unless jwks_hash
|
|
54
|
+
validate_header!(jwks_hash)
|
|
55
|
+
jwks = JWT::JWK::Set.new(jwks_hash)
|
|
56
|
+
JWT.decode(jws, nil, true, { algorithm: ALGO, jwks: jwks, allow_nil_kid: true })
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def validate_header!(jwks)
|
|
60
|
+
jwk = jwks.first
|
|
61
|
+
raise Tapyrus::JWS::DecodeError, "No jwk key found in header" unless jwk
|
|
62
|
+
raise Tapyrus::JWS::DecodeError, 'kty must be "EC"' if jwk["kty"] && jwk["kty"] != "EC"
|
|
63
|
+
raise Tapyrus::JWS::DecodeError, 'crv must be "P-256K"' if jwk["crv"] && jwk["crv"] != "P-256K"
|
|
64
|
+
raise Tapyrus::JWS::DecodeError, 'use must be "sig"' if jwk["use"] && jwk["use"] != "sig"
|
|
65
|
+
raise Tapyrus::JWS::DecodeError, 'alg must be "ES256K"' if jwk["alg"] && jwk["alg"] != "ES256K"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Return point object that represents rG
|
|
69
|
+
#
|
|
70
|
+
# @param r_hex [String] r value as hex string
|
|
71
|
+
# @return [ECDSA::Point] The point that represents r * G
|
|
72
|
+
def point_for(r_hex)
|
|
73
|
+
Tapyrus::Key.new(priv_key: r_hex).to_point
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/tapyrus/key.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Tapyrus
|
|
|
9
9
|
SIGNATURE_SIZE = 72
|
|
10
10
|
COMPACT_SIGNATURE_SIZE = 65
|
|
11
11
|
|
|
12
|
-
SIG_ALGO = [
|
|
12
|
+
SIG_ALGO = %i[ecdsa schnorr]
|
|
13
13
|
|
|
14
14
|
attr_accessor :priv_key
|
|
15
15
|
attr_accessor :pubkey
|
|
@@ -31,7 +31,7 @@ module Tapyrus
|
|
|
31
31
|
# @return [Tapyrus::Key] a key object.
|
|
32
32
|
def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false)
|
|
33
33
|
if key_type.nil? && !compressed.nil? && pubkey.nil?
|
|
34
|
-
warn(
|
|
34
|
+
warn("Use key_type parameter instead of compressed. compressed parameter removed in the future.")
|
|
35
35
|
end
|
|
36
36
|
if key_type
|
|
37
37
|
@key_type = key_type
|
|
@@ -56,7 +56,7 @@ module Tapyrus
|
|
|
56
56
|
|
|
57
57
|
# generate key pair
|
|
58
58
|
def self.generate(key_type = TYPES[:compressed])
|
|
59
|
-
priv_key, pubkey = Tapyrus.secp_impl.generate_key_pair
|
|
59
|
+
priv_key, pubkey = Tapyrus.secp_impl.generate_key_pair(compressed: key_type != TYPES[:uncompressed])
|
|
60
60
|
new(priv_key: priv_key, pubkey: pubkey, key_type: key_type)
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -64,22 +64,22 @@ module Tapyrus
|
|
|
64
64
|
# https://en.bitcoin.it/wiki/Wallet_import_format
|
|
65
65
|
def self.from_wif(wif)
|
|
66
66
|
hex = Base58.decode(wif)
|
|
67
|
-
raise ArgumentError,
|
|
67
|
+
raise ArgumentError, "data is too short" if hex.htb.bytesize < 4
|
|
68
68
|
version = hex[0..1]
|
|
69
69
|
data = hex[2...-8].htb
|
|
70
70
|
checksum = hex[-8..-1]
|
|
71
|
-
raise ArgumentError,
|
|
71
|
+
raise ArgumentError, "invalid version" unless version == Tapyrus.chain_params.privkey_version
|
|
72
72
|
unless Tapyrus.calc_checksum(version + data.bth) == checksum
|
|
73
73
|
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM
|
|
74
74
|
end
|
|
75
75
|
key_len = data.bytesize
|
|
76
|
-
if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack(
|
|
76
|
+
if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack("C").first == 1
|
|
77
77
|
key_type = TYPES[:compressed]
|
|
78
78
|
data = data[0..-2]
|
|
79
79
|
elsif key_len == 32
|
|
80
80
|
key_type = TYPES[:uncompressed]
|
|
81
81
|
else
|
|
82
|
-
raise ArgumentError,
|
|
82
|
+
raise ArgumentError, "Wrong number of bytes for a private key, not 32 or 33"
|
|
83
83
|
end
|
|
84
84
|
new(priv_key: data.bth, key_type: key_type)
|
|
85
85
|
end
|
|
@@ -88,7 +88,7 @@ module Tapyrus
|
|
|
88
88
|
def to_wif
|
|
89
89
|
version = Tapyrus.chain_params.privkey_version
|
|
90
90
|
hex = version + priv_key
|
|
91
|
-
hex +=
|
|
91
|
+
hex += "01" if compressed?
|
|
92
92
|
hex += Tapyrus.calc_checksum(hex)
|
|
93
93
|
Base58.encode(hex)
|
|
94
94
|
end
|
|
@@ -100,7 +100,7 @@ module Tapyrus
|
|
|
100
100
|
# @param [Symbol] algo Algorithms used for verification. Either :ecdsa or :schnorr is supported. default value is :ecdsa.
|
|
101
101
|
# @return [String] signature data with binary format
|
|
102
102
|
def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa)
|
|
103
|
-
raise ArgumentError,
|
|
103
|
+
raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
|
|
104
104
|
case algo
|
|
105
105
|
when :ecdsa
|
|
106
106
|
sign_ecdsa(data, low_r, extra_entropy)
|
|
@@ -119,7 +119,7 @@ module Tapyrus
|
|
|
119
119
|
def verify(sig, origin, algo: :ecdsa)
|
|
120
120
|
return false unless valid_pubkey?
|
|
121
121
|
begin
|
|
122
|
-
raise ArgumentError,
|
|
122
|
+
raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo)
|
|
123
123
|
sig = ecdsa_signature_parse_der_lax(sig) if algo == :ecdsa
|
|
124
124
|
secp256k1_module.verify_sig(origin, sig, pubkey, algo: algo)
|
|
125
125
|
rescue Exception
|
|
@@ -173,17 +173,45 @@ module Tapyrus
|
|
|
173
173
|
|
|
174
174
|
# check +sig+ is low.
|
|
175
175
|
def self.low_signature?(sig)
|
|
176
|
-
s = sig.unpack(
|
|
176
|
+
s = sig.unpack("C*")
|
|
177
177
|
len_r = s[3]
|
|
178
178
|
len_s = s[5 + len_r]
|
|
179
179
|
val_s = s.slice(6 + len_r, len_s)
|
|
180
180
|
|
|
181
181
|
# prettier-ignore
|
|
182
182
|
max_mod_half_order = [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
183
|
+
0x7f,
|
|
184
|
+
0xff,
|
|
185
|
+
0xff,
|
|
186
|
+
0xff,
|
|
187
|
+
0xff,
|
|
188
|
+
0xff,
|
|
189
|
+
0xff,
|
|
190
|
+
0xff,
|
|
191
|
+
0xff,
|
|
192
|
+
0xff,
|
|
193
|
+
0xff,
|
|
194
|
+
0xff,
|
|
195
|
+
0xff,
|
|
196
|
+
0xff,
|
|
197
|
+
0xff,
|
|
198
|
+
0xff,
|
|
199
|
+
0x5d,
|
|
200
|
+
0x57,
|
|
201
|
+
0x6e,
|
|
202
|
+
0x73,
|
|
203
|
+
0x57,
|
|
204
|
+
0xa4,
|
|
205
|
+
0x50,
|
|
206
|
+
0x1d,
|
|
207
|
+
0xdf,
|
|
208
|
+
0xe9,
|
|
209
|
+
0x2f,
|
|
210
|
+
0x46,
|
|
211
|
+
0x68,
|
|
212
|
+
0x1b,
|
|
213
|
+
0x20,
|
|
214
|
+
0xa0
|
|
187
215
|
]
|
|
188
216
|
compare_big_endian(val_s, [0]) > 0 && compare_big_endian(val_s, max_mod_half_order) <= 0
|
|
189
217
|
end
|
|
@@ -199,7 +227,7 @@ module Tapyrus
|
|
|
199
227
|
|
|
200
228
|
return false if sig.bytesize < num_parts || sig.bytesize > size # Minimum and maximum size check
|
|
201
229
|
|
|
202
|
-
s = sig.unpack(
|
|
230
|
+
s = sig.unpack("C*")
|
|
203
231
|
|
|
204
232
|
return false if s[0] != 0x30 || s[1] != s.size - (data_sig ? 2 : 3) # A signature is of type 0x30 (compound). Make sure the length covers the entire signature.
|
|
205
233
|
|
|
@@ -267,11 +295,11 @@ module Tapyrus
|
|
|
267
295
|
# strict DER before being passed to this module, and we know it supports all
|
|
268
296
|
# violations present in the blockchain before that point.
|
|
269
297
|
def ecdsa_signature_parse_der_lax(sig)
|
|
270
|
-
sig_array = sig.unpack(
|
|
298
|
+
sig_array = sig.unpack("C*")
|
|
271
299
|
len_r = sig_array[3]
|
|
272
|
-
r = sig_array[4...(len_r + 4)].pack(
|
|
300
|
+
r = sig_array[4...(len_r + 4)].pack("C*").bth
|
|
273
301
|
len_s = sig_array[len_r + 5]
|
|
274
|
-
s = sig_array[(len_r + 6)...(len_r + 6 + len_s)].pack(
|
|
302
|
+
s = sig_array[(len_r + 6)...(len_r + 6 + len_s)].pack("C*").bth
|
|
275
303
|
ECDSA::Signature.new(r.to_i(16), s.to_i(16)).to_der
|
|
276
304
|
end
|
|
277
305
|
|
|
@@ -292,7 +320,7 @@ module Tapyrus
|
|
|
292
320
|
if low_r && !sig_has_low_r?(sig)
|
|
293
321
|
counter = 1
|
|
294
322
|
until sig_has_low_r?(sig)
|
|
295
|
-
extra_entropy = [counter].pack(
|
|
323
|
+
extra_entropy = [counter].pack("I*").bth.ljust(64, "0").htb
|
|
296
324
|
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
|
|
297
325
|
counter += 1
|
|
298
326
|
end
|
data/lib/tapyrus/key_path.rb
CHANGED
|
@@ -5,11 +5,11 @@ module Tapyrus
|
|
|
5
5
|
# @return [Array[Integer]] key path numbers.
|
|
6
6
|
def parse_key_path(path_string)
|
|
7
7
|
path_string
|
|
8
|
-
.split(
|
|
8
|
+
.split("/")
|
|
9
9
|
.map
|
|
10
10
|
.with_index do |p, index|
|
|
11
11
|
if index == 0
|
|
12
|
-
raise ArgumentError.new("#{path_string} is invalid format.") unless p ==
|
|
12
|
+
raise ArgumentError.new("#{path_string} is invalid format.") unless p == "m"
|
|
13
13
|
next
|
|
14
14
|
end
|
|
15
15
|
raise ArgumentError.new("#{path_string} is invalid format.") unless p.delete("'") =~ /^[0-9]+$/
|
|
@@ -23,7 +23,7 @@ module Tapyrus
|
|
|
23
23
|
# @param [Array[Integer]] key path numbers.
|
|
24
24
|
# @return [String] path string.
|
|
25
25
|
def to_key_path(numbers)
|
|
26
|
-
"m/#{numbers.map { |p| p >= Tapyrus::HARDENED_THRESHOLD ? "#{p - Tapyrus::HARDENED_THRESHOLD}'" : p.to_s }.join(
|
|
26
|
+
"m/#{numbers.map { |p| p >= Tapyrus::HARDENED_THRESHOLD ? "#{p - Tapyrus::HARDENED_THRESHOLD}'" : p.to_s }.join("/")}"
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/tapyrus/logger.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "logger"
|
|
2
2
|
|
|
3
3
|
module Tapyrus
|
|
4
4
|
# Simple Logger module
|
|
@@ -15,15 +15,8 @@ module Tapyrus
|
|
|
15
15
|
logger.level = level
|
|
16
16
|
logger.formatter =
|
|
17
17
|
proc do |severity, datetime, progname, msg|
|
|
18
|
-
Format %
|
|
19
|
-
severity[0..0],
|
|
20
|
-
format_datetime(datetime),
|
|
21
|
-
$$,
|
|
22
|
-
Thread.current.object_id,
|
|
23
|
-
severity,
|
|
24
|
-
progname,
|
|
25
|
-
msg2str(msg)
|
|
26
|
-
]
|
|
18
|
+
Format %
|
|
19
|
+
[severity[0..0], format_datetime(datetime), $$, Thread.current.object_id, severity, progname, msg2str(msg)]
|
|
27
20
|
end
|
|
28
21
|
logger
|
|
29
22
|
end
|
|
@@ -40,7 +33,7 @@ module Tapyrus
|
|
|
40
33
|
end
|
|
41
34
|
|
|
42
35
|
def format_datetime(time)
|
|
43
|
-
time.strftime(@datetime_format ||
|
|
36
|
+
time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%S.%6N ".freeze)
|
|
44
37
|
end
|
|
45
38
|
end
|
|
46
39
|
end
|
data/lib/tapyrus/merkle_tree.rb
CHANGED
data/lib/tapyrus/message/addr.rb
CHANGED
data/lib/tapyrus/message/base.rb
CHANGED
|
@@ -12,14 +12,14 @@ module Tapyrus
|
|
|
12
12
|
payload = to_payload
|
|
13
13
|
magic = Tapyrus.chain_params.magic_head.htb
|
|
14
14
|
command_name = self.class.const_get(:COMMAND, false).ljust(12, "\x00")
|
|
15
|
-
payload_size = [payload.bytesize].pack(
|
|
15
|
+
payload_size = [payload.bytesize].pack("V")
|
|
16
16
|
checksum = Tapyrus.double_sha256(payload)[0...4]
|
|
17
17
|
magic << command_name << payload_size << checksum << payload
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
# abstract method
|
|
21
21
|
def to_payload
|
|
22
|
-
raise
|
|
22
|
+
raise "to_payload must be implemented in a child class."
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -3,7 +3,7 @@ module Tapyrus
|
|
|
3
3
|
# feefilter message
|
|
4
4
|
# https://bitcoin.org/en/developer-reference#feefilter
|
|
5
5
|
class FeeFilter < Base
|
|
6
|
-
COMMAND =
|
|
6
|
+
COMMAND = "feefilter"
|
|
7
7
|
|
|
8
8
|
# The fee rate (in satoshis per kilobyte)
|
|
9
9
|
attr_accessor :fee_rate
|
|
@@ -13,11 +13,11 @@ module Tapyrus
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.parse_from_payload(payload)
|
|
16
|
-
new(payload.unpack(
|
|
16
|
+
new(payload.unpack("Q").first)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def to_payload
|
|
20
|
-
[fee_rate].pack(
|
|
20
|
+
[fee_rate].pack("Q")
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -3,7 +3,7 @@ module Tapyrus
|
|
|
3
3
|
# filteradd message
|
|
4
4
|
# https://bitcoin.org/en/developer-reference#filteradd
|
|
5
5
|
class FilterAdd < Base
|
|
6
|
-
COMMAND =
|
|
6
|
+
COMMAND = "filteradd"
|
|
7
7
|
|
|
8
8
|
# element must be sent in the byte order they would use when appearing in a raw transaction;
|
|
9
9
|
attr_accessor :element
|
|
@@ -3,7 +3,7 @@ module Tapyrus
|
|
|
3
3
|
# filterload message
|
|
4
4
|
# https://bitcoin.org/en/developer-reference#filterload
|
|
5
5
|
class FilterLoad < Base
|
|
6
|
-
COMMAND =
|
|
6
|
+
COMMAND = "filterload"
|
|
7
7
|
|
|
8
8
|
BLOOM_UPDATE_NONE = 0
|
|
9
9
|
BLOOM_UPDATE_ALL = 1
|
|
@@ -20,16 +20,16 @@ module Tapyrus
|
|
|
20
20
|
def self.parse_from_payload(payload)
|
|
21
21
|
buf = StringIO.new(payload)
|
|
22
22
|
filter_count = Tapyrus.unpack_var_int_from_io(buf)
|
|
23
|
-
filter = buf.read(filter_count).unpack(
|
|
24
|
-
func_count = buf.read(4).unpack(
|
|
25
|
-
tweak = buf.read(4).unpack(
|
|
26
|
-
flag = buf.read(1).unpack(
|
|
23
|
+
filter = buf.read(filter_count).unpack("C*")
|
|
24
|
+
func_count = buf.read(4).unpack("V").first
|
|
25
|
+
tweak = buf.read(4).unpack("V").first
|
|
26
|
+
flag = buf.read(1).unpack("C").first
|
|
27
27
|
FilterLoad.new(Tapyrus::BloomFilter.new(filter, func_count, tweak), flag)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def to_payload
|
|
31
|
-
Tapyrus.pack_var_int(filter.filter.size) << filter.filter.pack(
|
|
32
|
-
[filter.hash_funcs, filter.tweak, flag].pack(
|
|
31
|
+
Tapyrus.pack_var_int(filter.filter.size) << filter.filter.pack("C*") <<
|
|
32
|
+
[filter.hash_funcs, filter.tweak, flag].pack("VVC")
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|