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
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.
|