tapyrus 0.2.1 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba1dec1b7532b15985c9233604dc63ad38b4a11e61d4bbc63d54cebdb291b3d6
|
4
|
+
data.tar.gz: 3b7cb3c9e41df3693ac45b22084108b1ec682fd753a10ff95cf0d50133bff959
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f82c7815830b1eb9aba88b0247acf36cb64f9ab47802e6b05c6dc4e167503baf85fa1696bfc59fe79cd78c8866b0638b2022cb52b3c924e2db34f09c06d01370
|
7
|
+
data.tar.gz: 8cdeec0c475d32f905b245f3b2a8ef9551d4ee55a1e21abe9a5b1f38b1dfe6fb317cfa85387899b5d0d5667bf19278654fee800b0b90234fa1407cd9f1703f8b
|
data/.travis.yml
CHANGED
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
|
data/lib/schnorr.rb
CHANGED
@@ -76,7 +76,7 @@ module Schnorr
|
|
76
76
|
# @return (Integer) digest e.
|
77
77
|
def create_challenge(x, p, message)
|
78
78
|
r_x = ECDSA::Format::IntegerOctetString.encode(x, GROUP.byte_length)
|
79
|
-
p_str=
|
79
|
+
p_str= p.to_hex.htb
|
80
80
|
(ECDSA.normalize_digest(Digest::SHA256.digest(r_x + p_str + message), GROUP.bit_length)) % GROUP.order
|
81
81
|
end
|
82
82
|
|
data/lib/tapyrus.rb
CHANGED
@@ -48,8 +48,10 @@ module Tapyrus
|
|
48
48
|
autoload :KeyPath, 'tapyrus/key_path'
|
49
49
|
autoload :SLIP39, 'tapyrus/slip39'
|
50
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
|
|
@@ -207,11 +209,4 @@ module Tapyrus
|
|
207
209
|
end
|
208
210
|
end
|
209
211
|
|
210
|
-
class ::ECDSA::Signature
|
211
|
-
# convert signature to der string.
|
212
|
-
def to_der
|
213
|
-
ECDSA::Format::SignatureDerString.encode(self)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
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
|
@@ -32,14 +33,9 @@ module Tapyrus
|
|
32
33
|
end
|
33
34
|
|
34
35
|
# return this block height. block height is included in coinbase.
|
35
|
-
#
|
36
|
+
# @return [Integer] block height.
|
36
37
|
def height
|
37
|
-
|
38
|
-
coinbase_tx = transactions[0]
|
39
|
-
return nil unless coinbase_tx.coinbase_tx?
|
40
|
-
buf = StringIO.new(coinbase_tx.inputs[0].script_sig.to_payload)
|
41
|
-
len = Tapyrus.unpack_var_int_from_io(buf)
|
42
|
-
buf.read(len).reverse.bth.to_i(16)
|
38
|
+
transactions.first.in.first.out_point.index
|
43
39
|
end
|
44
40
|
|
45
41
|
end
|
data/lib/tapyrus/block_header.rb
CHANGED
@@ -3,61 +3,69 @@ module Tapyrus
|
|
3
3
|
# Block Header
|
4
4
|
class BlockHeader
|
5
5
|
include Tapyrus::HexConverter
|
6
|
+
extend Tapyrus::Util
|
7
|
+
include Tapyrus::Util
|
8
|
+
|
9
|
+
X_FILED_TYPES = {none: 0, aggregate_pubkey: 1}
|
6
10
|
|
7
11
|
attr_accessor :features
|
8
12
|
attr_accessor :prev_hash
|
9
13
|
attr_accessor :merkle_root
|
10
|
-
attr_accessor :
|
11
|
-
attr_accessor :
|
12
|
-
attr_accessor :
|
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
|
13
19
|
|
14
|
-
def initialize(features, prev_hash, merkle_root, time,
|
20
|
+
def initialize(features, prev_hash, merkle_root, im_merkle_root, time, x_field_type, x_field, proof = nil)
|
15
21
|
@features = features
|
16
22
|
@prev_hash = prev_hash
|
17
23
|
@merkle_root = merkle_root
|
24
|
+
@im_merkle_root = im_merkle_root
|
18
25
|
@time = time
|
19
|
-
@
|
20
|
-
@
|
26
|
+
@x_field_type = x_field_type
|
27
|
+
@x_field = x_field
|
28
|
+
@proof = proof
|
21
29
|
end
|
22
30
|
|
23
31
|
def self.parse_from_payload(payload)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
[features, 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)
|
30
37
|
end
|
31
38
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
(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
|
38
44
|
end
|
39
45
|
|
40
|
-
|
41
|
-
|
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
|
42
50
|
end
|
43
51
|
|
52
|
+
# Calculate block hash
|
53
|
+
# @return [String] hash of block with hex format.
|
44
54
|
def block_hash
|
45
|
-
|
55
|
+
Tapyrus.double_sha256(to_payload).bth
|
46
56
|
end
|
47
57
|
|
48
58
|
# block hash(big endian)
|
59
|
+
# @return [String] block id which is reversed version of block hash.
|
49
60
|
def block_id
|
50
61
|
block_hash.rhex
|
51
62
|
end
|
52
63
|
|
53
64
|
# evaluate block header
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# evaluate valid proof of work.
|
59
|
-
def valid_pow?
|
60
|
-
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?
|
61
69
|
end
|
62
70
|
|
63
71
|
# evaluate valid timestamp.
|
@@ -66,22 +74,42 @@ module Tapyrus
|
|
66
74
|
time <= Time.now.to_i + Tapyrus::MAX_FUTURE_BLOCK_TIME
|
67
75
|
end
|
68
76
|
|
69
|
-
#
|
70
|
-
# @
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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]
|
75
103
|
end
|
76
104
|
|
77
105
|
def ==(other)
|
78
106
|
other && other.to_payload == to_payload
|
79
107
|
end
|
80
108
|
|
81
|
-
|
82
|
-
|
83
|
-
def
|
84
|
-
|
109
|
+
# get bytesize.
|
110
|
+
# @return [Integer] bytesize.
|
111
|
+
def size
|
112
|
+
to_payload.bytesize
|
85
113
|
end
|
86
114
|
|
87
115
|
end
|
data/lib/tapyrus/chain_params.rb
CHANGED
@@ -33,12 +33,10 @@ module Tapyrus
|
|
33
33
|
attr_reader :bip34_height
|
34
34
|
attr_reader :proof_of_work_limit
|
35
35
|
attr_reader :dns_seeds
|
36
|
-
attr_reader :genesis
|
37
36
|
attr_reader :bip44_coin_type
|
38
37
|
|
39
38
|
attr_accessor :dust_relay_fee
|
40
39
|
|
41
|
-
|
42
40
|
# production genesis
|
43
41
|
def self.prod
|
44
42
|
init('prod')
|
@@ -57,14 +55,6 @@ module Tapyrus
|
|
57
55
|
network == 'dev'
|
58
56
|
end
|
59
57
|
|
60
|
-
def genesis_block
|
61
|
-
header = Tapyrus::BlockHeader.new(
|
62
|
-
genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
|
63
|
-
genesis['time'], genesis['bits'], genesis['nonce'])
|
64
|
-
Tapyrus::Block.new(header)
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
58
|
def self.init(name)
|
69
59
|
i = YAML.load(File.open("#{__dir__}/chainparams/#{name}.yml"))
|
70
60
|
i.dust_relay_fee ||= Tapyrus::DUST_RELAY_TX_FEE
|
@@ -25,15 +25,5 @@ retarget_time: 1209600 # 2 weeks
|
|
25
25
|
target_spacing: 600 # block interval
|
26
26
|
max_money: 21000000
|
27
27
|
bip34_height: 227931
|
28
|
-
genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
29
|
-
proof_of_work_limit: 0x1d00ffff
|
30
28
|
dns_seeds:
|
31
|
-
genesis:
|
32
|
-
hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
|
33
|
-
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
34
|
-
time: 1296688602
|
35
|
-
nonce: 414098458
|
36
|
-
bits: 0x1d00ffff
|
37
|
-
version: 1
|
38
|
-
prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
39
29
|
bip44_coin_type: 1
|
@@ -25,14 +25,5 @@ retarget_time: 1209600 # 2 weeks
|
|
25
25
|
target_spacing: 600 # block interval
|
26
26
|
max_money: 21000000
|
27
27
|
bip34_height: 227931
|
28
|
-
proof_of_work_limit: 0x1d00ffff
|
29
28
|
dns_seeds:
|
30
|
-
genesis:
|
31
|
-
hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
|
32
|
-
merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
|
33
|
-
time: 1231006505
|
34
|
-
nonce: 2083236893
|
35
|
-
bits: 0x1d00ffff
|
36
|
-
version: 1
|
37
|
-
prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
|
38
29
|
bip44_coin_type: 0
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tapyrus
|
2
|
+
module Errors
|
3
|
+
module Messages
|
4
|
+
|
5
|
+
INVALID_PUBLIC_KEY = 'Invalid public key.'
|
6
|
+
INVALID_BIP32_PRIV_PREFIX = 'Invalid BIP32 private key prefix. prefix must be 0x00.'
|
7
|
+
INVALID_BIP32_FINGERPRINT = 'Invalid parent fingerprint.'
|
8
|
+
INVALID_BIP32_ZERO_INDEX = 'Invalid index. Depth 0 must have 0 index.'
|
9
|
+
INVALID_BIP32_ZERO_DEPTH = 'Invalid depth. Master key must have 0 depth.'
|
10
|
+
INVALID_BIP32_VERSION = 'An unsupported version byte was specified.'
|
11
|
+
|
12
|
+
INVALID_PRIV_KEY = 'Private key is not in range [1..n-1].'
|
13
|
+
INVALID_CHECKSUM = 'Invalid checksum.'
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class ::ECDSA::Signature
|
2
|
+
# convert signature to der string.
|
3
|
+
def to_der
|
4
|
+
ECDSA::Format::SignatureDerString.encode(self)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class ::ECDSA::Point
|
9
|
+
def to_hex(compression = true)
|
10
|
+
ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ::ECDSA::Format::PointOctetString
|
15
|
+
|
16
|
+
def self.decode(string, group, allow_hybrid: false)
|
17
|
+
string = string.dup.force_encoding('BINARY')
|
18
|
+
|
19
|
+
raise ECDSA::Format::DecodeError, 'Point octet string is empty.' if string.empty?
|
20
|
+
|
21
|
+
case string[0].ord
|
22
|
+
when 0
|
23
|
+
check_length string, 1
|
24
|
+
return group.infinity
|
25
|
+
when 2
|
26
|
+
decode_compressed string, group, 0
|
27
|
+
when 3
|
28
|
+
decode_compressed string, group, 1
|
29
|
+
when 4
|
30
|
+
decode_uncompressed string, group
|
31
|
+
when 6..7
|
32
|
+
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord unless allow_hybrid
|
33
|
+
decode_uncompressed string, group if allow_hybrid
|
34
|
+
else
|
35
|
+
raise ECDSA::Format::DecodeError, 'Unrecognized start byte for point octet string: 0x%x' % string[0].ord
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/tapyrus/ext_key.rb
CHANGED
@@ -7,6 +7,9 @@ module Tapyrus
|
|
7
7
|
class ExtKey
|
8
8
|
include Tapyrus::HexConverter
|
9
9
|
|
10
|
+
MAX_DEPTH = 255
|
11
|
+
MASTER_FINGERPRINT = '00000000'
|
12
|
+
|
10
13
|
attr_accessor :ver
|
11
14
|
attr_accessor :depth
|
12
15
|
attr_accessor :number
|
@@ -20,7 +23,7 @@ module Tapyrus
|
|
20
23
|
ext_key = ExtKey.new
|
21
24
|
ext_key.depth = ext_key.number = 0
|
22
25
|
ext_key.parent_fingerprint = '00000000'
|
23
|
-
l = Tapyrus.hmac_sha512('
|
26
|
+
l = Tapyrus.hmac_sha512('Tapyrus seed', seed.htb)
|
24
27
|
left = l[0..31].bth.to_i(16)
|
25
28
|
raise 'invalid key' if left >= CURVE_ORDER || left == 0
|
26
29
|
ext_key.key = Tapyrus::Key.new(priv_key: l[0..31].bth, key_type: Tapyrus::Key::TYPES[:compressed])
|
@@ -48,9 +51,7 @@ module Tapyrus
|
|
48
51
|
|
49
52
|
# Base58 encoded extended private key
|
50
53
|
def to_base58
|
51
|
-
|
52
|
-
hex = h + Tapyrus.calc_checksum(h)
|
53
|
-
Base58.encode(hex)
|
54
|
+
ExtPubkey.encode_base58(to_hex)
|
54
55
|
end
|
55
56
|
|
56
57
|
# get private key(hex)
|
@@ -141,19 +142,24 @@ module Tapyrus
|
|
141
142
|
buf = StringIO.new(payload)
|
142
143
|
ext_key = ExtKey.new
|
143
144
|
ext_key.ver = buf.read(4).bth # version
|
144
|
-
raise
|
145
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtKey.support_version?(ext_key.ver)
|
145
146
|
ext_key.depth = buf.read(1).unpack('C').first
|
146
147
|
ext_key.parent_fingerprint = buf.read(4).bth
|
147
148
|
ext_key.number = buf.read(4).unpack('N').first
|
149
|
+
if ext_key.depth == 0
|
150
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT unless ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
151
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_key.number > 0
|
152
|
+
end
|
153
|
+
raise ArgumentError, Errors::Messages:: INVALID_BIP32_ZERO_DEPTH if ext_key.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_key.depth > 0
|
148
154
|
ext_key.chain_code = buf.read(32)
|
149
|
-
buf.read(1) # 0x00
|
155
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == '00' # 0x00
|
150
156
|
ext_key.key = Tapyrus::Key.new(priv_key: buf.read(32).bth, key_type: Tapyrus::Key::TYPES[:compressed])
|
151
157
|
ext_key
|
152
158
|
end
|
153
159
|
|
154
160
|
# import private key from Base58 private key address
|
155
161
|
def self.from_base58(address)
|
156
|
-
ExtKey.parse_from_payload(
|
162
|
+
ExtKey.parse_from_payload(ExtPubkey.validate_checksum(address))
|
157
163
|
end
|
158
164
|
|
159
165
|
# get version bytes from purpose' value.
|
@@ -237,9 +243,14 @@ module Tapyrus
|
|
237
243
|
|
238
244
|
# Base58 encoded extended pubkey
|
239
245
|
def to_base58
|
240
|
-
|
241
|
-
|
242
|
-
|
246
|
+
ExtPubkey.encode_base58(to_hex)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Generate Base58 encoded key from BIP32 payload with hex format.
|
250
|
+
# @param [String] hex BIP32 payload with hex format.
|
251
|
+
# @return [String] Base58 encoded extended key.
|
252
|
+
def self.encode_base58(hex)
|
253
|
+
Base58.encode(hex + Tapyrus.calc_checksum(hex))
|
243
254
|
end
|
244
255
|
|
245
256
|
# whether hardened key.
|
@@ -260,7 +271,7 @@ module Tapyrus
|
|
260
271
|
raise 'invalid key' if left >= CURVE_ORDER
|
261
272
|
p1 = Tapyrus::Secp256k1::GROUP.generator.multiply_by_scalar(left)
|
262
273
|
p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
263
|
-
new_key.pubkey =
|
274
|
+
new_key.pubkey = (p1 + p2).to_hex
|
264
275
|
new_key.chain_code = l[32..-1]
|
265
276
|
new_key.ver = version
|
266
277
|
new_key
|
@@ -293,19 +304,32 @@ module Tapyrus
|
|
293
304
|
buf = StringIO.new(payload)
|
294
305
|
ext_pubkey = ExtPubkey.new
|
295
306
|
ext_pubkey.ver = buf.read(4).bth # version
|
296
|
-
raise
|
307
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtPubkey.support_version?(ext_pubkey.ver)
|
297
308
|
ext_pubkey.depth = buf.read(1).unpack('C').first
|
298
309
|
ext_pubkey.parent_fingerprint = buf.read(4).bth
|
299
310
|
ext_pubkey.number = buf.read(4).unpack('N').first
|
311
|
+
if ext_pubkey.depth == 0
|
312
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_FINGERPRINT unless ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT
|
313
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_INDEX if ext_pubkey.number > 0
|
314
|
+
end
|
315
|
+
raise ArgumentError, Errors::Messages::INVALID_BIP32_ZERO_DEPTH if ext_pubkey.parent_fingerprint == ExtKey::MASTER_FINGERPRINT && ext_pubkey.depth > 0
|
300
316
|
ext_pubkey.chain_code = buf.read(32)
|
301
|
-
ext_pubkey.pubkey = buf.read(33).bth
|
317
|
+
ext_pubkey.pubkey = Tapyrus::Key.new(pubkey: buf.read(33).bth).pubkey
|
302
318
|
ext_pubkey
|
303
319
|
end
|
304
320
|
|
305
|
-
|
306
321
|
# import pub key from Base58 private key address
|
307
322
|
def self.from_base58(address)
|
308
|
-
ExtPubkey.parse_from_payload(
|
323
|
+
ExtPubkey.parse_from_payload(ExtPubkey.validate_checksum(address))
|
324
|
+
end
|
325
|
+
|
326
|
+
# Validate address checksum and return payload.
|
327
|
+
# @param [String] BIP32 Base58 address
|
328
|
+
# @return [String] BIP32 payload with binary format
|
329
|
+
def self.validate_checksum(base58)
|
330
|
+
raw = Base58.decode(base58)
|
331
|
+
raise ArgumentError, Errors::Messages::INVALID_CHECKSUM unless Tapyrus.calc_checksum(raw[0...-8]) == raw[-8..-1]
|
332
|
+
raw[0...-8].htb
|
309
333
|
end
|
310
334
|
|
311
335
|
# get version bytes from purpose' value.
|