tapyrus 0.1.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -15
  3. data/exe/tapyrusrbd +2 -2
  4. data/lib/openassets/util.rb +2 -4
  5. data/lib/schnorr.rb +83 -0
  6. data/lib/schnorr/signature.rb +38 -0
  7. data/lib/tapyrus.rb +11 -18
  8. data/lib/tapyrus/block.rb +3 -38
  9. data/lib/tapyrus/block_header.rb +70 -41
  10. data/lib/tapyrus/chain_params.rb +13 -36
  11. data/lib/tapyrus/chainparams/{regtest.yml → dev.yml} +10 -19
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -19
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/errors.rb +17 -0
  15. data/lib/tapyrus/ext.rb +5 -0
  16. data/lib/tapyrus/ext/ecdsa.rb +39 -0
  17. data/lib/tapyrus/ext/json_parser.rb +47 -0
  18. data/lib/tapyrus/ext_key.rb +42 -23
  19. data/lib/tapyrus/key.rb +47 -40
  20. data/lib/tapyrus/message.rb +2 -2
  21. data/lib/tapyrus/message/base.rb +1 -0
  22. data/lib/tapyrus/message/block.rb +4 -4
  23. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  24. data/lib/tapyrus/message/header_and_short_ids.rb +1 -1
  25. data/lib/tapyrus/message/headers.rb +1 -1
  26. data/lib/tapyrus/message/merkle_block.rb +1 -1
  27. data/lib/tapyrus/message/tx.rb +2 -2
  28. data/lib/tapyrus/network/peer.rb +1 -15
  29. data/lib/tapyrus/node/cli.rb +15 -11
  30. data/lib/tapyrus/node/configuration.rb +1 -1
  31. data/lib/tapyrus/node/spv.rb +1 -1
  32. data/lib/tapyrus/opcodes.rb +5 -0
  33. data/lib/tapyrus/out_point.rb +1 -1
  34. data/lib/tapyrus/rpc/request_handler.rb +7 -6
  35. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  36. data/lib/tapyrus/script/color.rb +79 -0
  37. data/lib/tapyrus/script/multisig.rb +0 -27
  38. data/lib/tapyrus/script/script.rb +74 -89
  39. data/lib/tapyrus/script/script_error.rb +8 -14
  40. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  41. data/lib/tapyrus/script/tx_checker.rb +21 -5
  42. data/lib/tapyrus/secp256k1.rb +1 -0
  43. data/lib/tapyrus/secp256k1/native.rb +93 -20
  44. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  45. data/lib/tapyrus/secp256k1/ruby.rb +63 -54
  46. data/lib/tapyrus/store/chain_entry.rb +3 -2
  47. data/lib/tapyrus/store/db/level_db.rb +58 -0
  48. data/lib/tapyrus/store/spv_chain.rb +29 -11
  49. data/lib/tapyrus/tx.rb +18 -160
  50. data/lib/tapyrus/tx_in.rb +4 -11
  51. data/lib/tapyrus/tx_out.rb +2 -1
  52. data/lib/tapyrus/util.rb +8 -0
  53. data/lib/tapyrus/validation.rb +1 -6
  54. data/lib/tapyrus/version.rb +1 -1
  55. data/lib/tapyrus/wallet/account.rb +1 -0
  56. data/lib/tapyrus/wallet/master_key.rb +1 -0
  57. data/tapyrusrb.gemspec +3 -3
  58. metadata +44 -39
  59. data/lib/tapyrus/chainparams/testnet.yml +0 -41
  60. data/lib/tapyrus/descriptor.rb +0 -147
  61. data/lib/tapyrus/script_witness.rb +0 -38
@@ -10,7 +10,8 @@ module Tapyrus
10
10
  attr_reader :message_magic
11
11
  attr_reader :address_version
12
12
  attr_reader :p2sh_version
13
- attr_reader :bech32_hrp
13
+ attr_reader :cp2pkh_version
14
+ attr_reader :cp2sh_version
14
15
  attr_reader :privkey_version
15
16
  attr_reader :extended_privkey_version
16
17
  attr_reader :extended_pubkey_version
@@ -23,6 +24,7 @@ module Tapyrus
23
24
  attr_reader :bip84_pubkey_p2wsh_version
24
25
  attr_reader :bip84_privkey_p2wsh_version
25
26
  attr_reader :default_port
27
+ attr_reader :rpc_port
26
28
  attr_reader :protocol_version
27
29
  attr_reader :retarget_interval
28
30
  attr_reader :retarget_time
@@ -31,51 +33,26 @@ module Tapyrus
31
33
  attr_reader :bip34_height
32
34
  attr_reader :proof_of_work_limit
33
35
  attr_reader :dns_seeds
34
- attr_reader :genesis
35
36
  attr_reader :bip44_coin_type
36
37
 
37
38
  attr_accessor :dust_relay_fee
38
39
 
39
- # fork coin id.
40
- attr_accessor :fork_id
41
-
42
- # mainnet genesis
43
- def self.mainnet
44
- init('mainnet')
45
- end
46
-
47
- # testnet genesis
48
- def self.testnet
49
- init('testnet')
50
- end
51
-
52
- # regtest genesis
53
- def self.regtest
54
- init('regtest')
55
- end
56
-
57
- def mainnet?
58
- network == 'mainnet'
59
- end
60
-
61
- def testnet?
62
- network == 'testnet'
40
+ # production genesis
41
+ def self.prod
42
+ init('prod')
63
43
  end
64
44
 
65
- def regtest?
66
- network == 'regtest'
45
+ # development genesis
46
+ def self.dev
47
+ init('dev')
67
48
  end
68
49
 
69
- def genesis_block
70
- header = Tapyrus::BlockHeader.new(
71
- genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
72
- genesis['time'], genesis['bits'], genesis['nonce'])
73
- Tapyrus::Block.new(header)
50
+ def prod?
51
+ network == 'prod'
74
52
  end
75
53
 
76
- # whether fork coin.
77
- def fork_chain?
78
- !fork_id.nil?
54
+ def dev?
55
+ network == 'dev'
79
56
  end
80
57
 
81
58
  def self.init(name)
@@ -1,10 +1,11 @@
1
1
  --- !ruby/object:Tapyrus::ChainParams
2
- network: "regtest"
3
- magic_head: "fabfb5da"
4
- message_magic: "Bitcoin Signed Message:\n"
2
+ network: "dev"
3
+ magic_head: "0b110907"
4
+ message_magic: "Tapyrus Signed Message:\n"
5
5
  address_version: "6f"
6
6
  p2sh_version: "c4"
7
- bech32_hrp: 'bcrt'
7
+ cp2pkh_version: "70"
8
+ cp2sh_version: "c5"
8
9
  privkey_version: "ef"
9
10
  extended_privkey_version: "04358394"
10
11
  extended_pubkey_version: "043587cf"
@@ -16,23 +17,13 @@ bip84_pubkey_p2wpkh_version: "045f1cf6"
16
17
  bip84_pubkey_p2wsh_version: "02575483"
17
18
  bip84_privkey_p2wpkh_version: "045f18bc"
18
19
  bip84_privkey_p2wsh_version: "02575048"
19
- default_port: 18444
20
- protocol_version: 70013
20
+ default_port: 12383
21
+ rpc_port: 12381
22
+ protocol_version: 10000
21
23
  retarget_interval: 2016
22
24
  retarget_time: 1209600 # 2 weeks
23
25
  target_spacing: 600 # block interval
24
26
  max_money: 21000000
25
- bip34_height: 0
26
- genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
27
- proof_of_work_limit: 0x207fffff
27
+ bip34_height: 227931
28
28
  dns_seeds:
29
- genesis:
30
- hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
31
- merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
32
- time: 1296688602
33
- nonce: 2
34
- bits: 0x207fffff
35
- version: 1
36
- prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
37
- bip44_coin_type: 1
38
- dust_relay_fee: 3600
29
+ bip44_coin_type: 1
@@ -1,10 +1,11 @@
1
1
  --- !ruby/object:Tapyrus::ChainParams
2
- network: "mainnet"
2
+ network: "prod"
3
3
  magic_head: "f9beb4d9"
4
- message_magic: "Bitcoin Signed Message:\n"
4
+ message_magic: "Tapyrus Signed Message:\n"
5
5
  address_version: "00"
6
6
  p2sh_version: "05"
7
- bech32_hrp: 'bc'
7
+ cp2pkh_version: "01"
8
+ cp2sh_version: "06"
8
9
  privkey_version: "80"
9
10
  extended_privkey_version: "0488ade4"
10
11
  extended_pubkey_version: "0488b21e"
@@ -16,26 +17,13 @@ bip84_pubkey_p2wpkh_version: "04b24746"
16
17
  bip84_pubkey_p2wsh_version: "02aa7ed3"
17
18
  bip84_privkey_p2wpkh_version: "04b2430c"
18
19
  bip84_privkey_p2wsh_version: "02aa7a99"
19
- default_port: 8333
20
- protocol_version: 70013
20
+ default_port: 2357
21
+ rpc_port: 2377
22
+ protocol_version: 10000
21
23
  retarget_interval: 2016
22
24
  retarget_time: 1209600 # 2 weeks
23
25
  target_spacing: 600 # block interval
24
26
  max_money: 21000000
25
27
  bip34_height: 227931
26
- proof_of_work_limit: 0x1d00ffff
27
28
  dns_seeds:
28
- - "seed.bitcoin.sipa.be"
29
- - "dnsseed.bluematt.me"
30
- - "dnsseed.bitcoin.dashjr.org"
31
- - "seed.bitcoinstats.com"
32
- - "seed.bitcoin.jonasschnelli.ch"
33
- genesis:
34
- hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
35
- merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
36
- time: 1231006505
37
- nonce: 2083236893
38
- bits: 0x1d00ffff
39
- version: 1
40
- prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
41
29
  bip44_coin_type: 0
@@ -38,11 +38,8 @@ module Tapyrus
38
38
  SCRIPT_VERIFY_CLEANSTACK = (1 << 8)
39
39
  SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9) # Verify CHECKLOCKTIMEVERIFY (BIP-65)
40
40
  SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1 << 10) # support CHECKSEQUENCEVERIFY opcode (BIP-112)
41
- SCRIPT_VERIFY_WITNESS = (1 << 11) # Support segregated witness
42
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1 << 12) # Making v1-v16 witness program non-standard
43
41
  SCRIPT_VERIFY_MINIMALIF = (1 << 13) # Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
44
42
  SCRIPT_VERIFY_NULLFAIL = (1 << 14) # Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
45
- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1 << 15) # Public keys in segregated witness scripts must be compressed
46
43
  SCRIPT_VERIFY_CONST_SCRIPTCODE = (1 << 16) # Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
47
44
 
48
45
  MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
@@ -60,16 +57,10 @@ module Tapyrus
60
57
  SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
61
58
  SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
62
59
  SCRIPT_VERIFY_LOW_S,
63
- SCRIPT_VERIFY_WITNESS,
64
- SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
65
- SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
66
60
  SCRIPT_VERIFY_CONST_SCRIPTCODE].inject(SCRIPT_VERIFY_NONE){|flags, f| flags |= f}
67
61
 
68
62
  # for script
69
63
 
70
- # witness version
71
- WITNESS_VERSION = 0x00
72
-
73
64
  # Maximum script length in bytes
74
65
  MAX_SCRIPT_SIZE = 10000
75
66
 
@@ -91,20 +82,13 @@ module Tapyrus
91
82
  # Signature hash types/flags
92
83
  SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
93
84
 
94
- # SIGHASH_FORK_ID for replay protection of the fork coin
95
- SIGHASH_FORK_ID = 0x40
96
-
97
- # fork coin id.
98
- FORK_ID_CASH = 0
99
- FORK_ID_GOLD = 79
100
-
101
85
  # Maximum number length in bytes
102
86
  DEFAULT_MAX_NUM_SIZE = 4
103
87
 
104
88
  # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
105
89
  MAX_OP_RETURN_RELAY = 83
106
90
 
107
- SIG_VERSION = [:base, :witness_v0]
91
+ SIG_VERSION = [:base]
108
92
 
109
93
  # for script error
110
94
  SCRIPT_ERR_OK = 0
@@ -126,6 +110,7 @@ module Tapyrus
126
110
  SCRIPT_ERR_CHECKMULTISIGVERIFY = 22
127
111
  SCRIPT_ERR_CHECKSIGVERIFY = 23
128
112
  SCRIPT_ERR_NUMEQUALVERIFY = 24
113
+ SCRIPT_ERR_CHECKDATASIGVERIFY = 25
129
114
 
130
115
  # Logical/Format/Canonical errors
131
116
  SCRIPT_ERR_BAD_OPCODE = 30
@@ -133,6 +118,7 @@ module Tapyrus
133
118
  SCRIPT_ERR_INVALID_STACK_OPERATION = 32
134
119
  SCRIPT_ERR_INVALID_ALTSTACK_OPERATION = 33
135
120
  SCRIPT_ERR_UNBALANCED_CONDITIONAL = 34
121
+ SCRIPT_ERR_MIXED_SCHEME_MULTISIG = 35
136
122
 
137
123
  # CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
138
124
  SCRIPT_ERR_NEGATIVE_LOCKTIME = 40
@@ -146,35 +132,27 @@ module Tapyrus
146
132
  SCRIPT_ERR_SIG_HIGH_S = 54
147
133
  SCRIPT_ERR_SIG_NULLDUMMY = 55
148
134
  SCRIPT_ERR_PUBKEYTYPE = 56
149
- SCRIPT_ERR_CLEANSTACK = 56
150
- SCRIPT_ERR_MINIMALIF = 57
151
- SCRIPT_ERR_SIG_NULLFAIL = 58
135
+ SCRIPT_ERR_CLEANSTACK = 57
136
+ SCRIPT_ERR_MINIMALIF = 58
137
+ SCRIPT_ERR_SIG_NULLFAIL = 59
152
138
 
153
139
  # softfork safeness
154
140
  SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
155
- SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 61
156
-
157
- # segregated witness
158
- SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
159
- SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY = 71
160
- SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH = 72
161
- SCRIPT_ERR_WITNESS_MALLEATED = 73
162
- SCRIPT_ERR_WITNESS_MALLEATED_P2SH = 74
163
- SCRIPT_ERR_WITNESS_UNEXPECTED = 75
164
- SCRIPT_ERR_WITNESS_PUBKEYTYPE = 76
165
141
 
166
142
  # Constant scriptCode
167
143
  SCRIPT_ERR_OP_CODESEPARATOR = 77
168
144
  SCRIPT_ERR_SIG_FINDANDDELETE = 78
169
145
 
170
- SCRIPT_ERR_ERROR_COUNT = 80
146
+ SCRIPT_ERR_OP_COLOR_UNEXPECTED = 79
147
+ SCRIPT_ERR_OP_COLOR_ID_INVALID = 80
148
+ SCRIPT_ERR_OP_COLOR_MULTIPLE = 81
149
+ SCRIPT_ERR_OP_COLOR_IN_BRANCH = 82
150
+
151
+ SCRIPT_ERR_ERROR_COUNT = 83
171
152
 
172
153
  ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
173
154
  NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
174
155
 
175
- # witness commitment
176
- WITNESS_COMMITMENT_HEADER = 'aa21a9ed'
177
-
178
156
  COINBASE_WTXID = '00'* 32
179
157
 
180
158
  # for message
@@ -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,5 @@
1
+ module Tapyrus
2
+ module Ext
3
+ autoload :JsonParser, 'tapyrus/ext/json_parser'
4
+ end
5
+ 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 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
@@ -0,0 +1,47 @@
1
+ require 'json/pure'
2
+
3
+ module Tapyrus
4
+ module Ext
5
+
6
+ # Extension of JSON::Pure::Parser.
7
+ # This class convert Float value to String value.
8
+ class JsonParser < JSON::Pure::Parser
9
+
10
+ def parse_value
11
+ case
12
+ when scan(FLOAT)
13
+ self[1].to_s
14
+ when scan(INTEGER)
15
+ Integer(self[1])
16
+ when scan(TRUE)
17
+ true
18
+ when scan(FALSE)
19
+ false
20
+ when scan(NULL)
21
+ nil
22
+ when !UNPARSED.equal?(string = parse_string)
23
+ string
24
+ when scan(ARRAY_OPEN)
25
+ @current_nesting += 1
26
+ ary = parse_array
27
+ @current_nesting -= 1
28
+ ary
29
+ when scan(OBJECT_OPEN)
30
+ @current_nesting += 1
31
+ obj = parse_object
32
+ @current_nesting -= 1
33
+ obj
34
+ when @allow_nan && scan(NAN)
35
+ NaN
36
+ when @allow_nan && scan(INFINITY)
37
+ Infinity
38
+ when @allow_nan && scan(MINUS_INFINITY)
39
+ MinusInfinity
40
+ else
41
+ UNPARSED
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -5,6 +5,10 @@ module Tapyrus
5
5
 
6
6
  # BIP32 Extended private key
7
7
  class ExtKey
8
+ include Tapyrus::HexConverter
9
+
10
+ MAX_DEPTH = 255
11
+ MASTER_FINGERPRINT = '00000000'
8
12
 
9
13
  attr_accessor :ver
10
14
  attr_accessor :depth
@@ -19,7 +23,7 @@ module Tapyrus
19
23
  ext_key = ExtKey.new
20
24
  ext_key.depth = ext_key.number = 0
21
25
  ext_key.parent_fingerprint = '00000000'
22
- l = Tapyrus.hmac_sha512('Bitcoin seed', seed.htb)
26
+ l = Tapyrus.hmac_sha512('Tapyrus seed', seed.htb)
23
27
  left = l[0..31].bth.to_i(16)
24
28
  raise 'invalid key' if left >= CURVE_ORDER || left == 0
25
29
  ext_key.key = Tapyrus::Key.new(priv_key: l[0..31].bth, key_type: Tapyrus::Key::TYPES[:compressed])
@@ -47,9 +51,7 @@ module Tapyrus
47
51
 
48
52
  # Base58 encoded extended private key
49
53
  def to_base58
50
- h = to_payload.bth
51
- hex = h + Tapyrus.calc_checksum(h)
52
- Base58.encode(hex)
54
+ ExtPubkey.encode_base58(to_hex)
53
55
  end
54
56
 
55
57
  # get private key(hex)
@@ -140,19 +142,24 @@ module Tapyrus
140
142
  buf = StringIO.new(payload)
141
143
  ext_key = ExtKey.new
142
144
  ext_key.ver = buf.read(4).bth # version
143
- raise 'An unsupported version byte was specified.' unless ExtKey.support_version?(ext_key.ver)
145
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtKey.support_version?(ext_key.ver)
144
146
  ext_key.depth = buf.read(1).unpack('C').first
145
147
  ext_key.parent_fingerprint = buf.read(4).bth
146
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
147
154
  ext_key.chain_code = buf.read(32)
148
- buf.read(1) # 0x00
155
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_PRIV_PREFIX unless buf.read(1).bth == '00' # 0x00
149
156
  ext_key.key = Tapyrus::Key.new(priv_key: buf.read(32).bth, key_type: Tapyrus::Key::TYPES[:compressed])
150
157
  ext_key
151
158
  end
152
159
 
153
160
  # import private key from Base58 private key address
154
161
  def self.from_base58(address)
155
- ExtKey.parse_from_payload(Base58.decode(address).htb)
162
+ ExtKey.parse_from_payload(ExtPubkey.validate_checksum(address))
156
163
  end
157
164
 
158
165
  # get version bytes from purpose' value.
@@ -190,6 +197,7 @@ module Tapyrus
190
197
 
191
198
  # BIP-32 Extended public key
192
199
  class ExtPubkey
200
+ include Tapyrus::HexConverter
193
201
 
194
202
  attr_accessor :ver
195
203
  attr_accessor :depth
@@ -214,14 +222,7 @@ module Tapyrus
214
222
 
215
223
  # get address
216
224
  def addr
217
- case version
218
- when Tapyrus.chain_params.bip49_pubkey_p2wpkh_p2sh_version
219
- key.to_nested_p2wpkh
220
- when Tapyrus.chain_params.bip84_pubkey_p2wpkh_version
221
- key.to_p2wpkh
222
- else
223
- key.to_p2pkh
224
- end
225
+ key.to_p2pkh
225
226
  end
226
227
 
227
228
  # get key object
@@ -242,9 +243,14 @@ module Tapyrus
242
243
 
243
244
  # Base58 encoded extended pubkey
244
245
  def to_base58
245
- h = to_payload.bth
246
- hex = h + Tapyrus.calc_checksum(h)
247
- Base58.encode(hex)
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))
248
254
  end
249
255
 
250
256
  # whether hardened key.
@@ -265,7 +271,7 @@ module Tapyrus
265
271
  raise 'invalid key' if left >= CURVE_ORDER
266
272
  p1 = Tapyrus::Secp256k1::GROUP.generator.multiply_by_scalar(left)
267
273
  p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
268
- new_key.pubkey = ECDSA::Format::PointOctetString.encode(p1 + p2, compression: true).bth
274
+ new_key.pubkey = (p1 + p2).to_hex
269
275
  new_key.chain_code = l[32..-1]
270
276
  new_key.ver = version
271
277
  new_key
@@ -298,19 +304,32 @@ module Tapyrus
298
304
  buf = StringIO.new(payload)
299
305
  ext_pubkey = ExtPubkey.new
300
306
  ext_pubkey.ver = buf.read(4).bth # version
301
- raise 'An unsupported version byte was specified.' unless ExtPubkey.support_version?(ext_pubkey.ver)
307
+ raise ArgumentError, Errors::Messages::INVALID_BIP32_VERSION unless ExtPubkey.support_version?(ext_pubkey.ver)
302
308
  ext_pubkey.depth = buf.read(1).unpack('C').first
303
309
  ext_pubkey.parent_fingerprint = buf.read(4).bth
304
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
305
316
  ext_pubkey.chain_code = buf.read(32)
306
- ext_pubkey.pubkey = buf.read(33).bth
317
+ ext_pubkey.pubkey = Tapyrus::Key.new(pubkey: buf.read(33).bth).pubkey
307
318
  ext_pubkey
308
319
  end
309
320
 
310
-
311
321
  # import pub key from Base58 private key address
312
322
  def self.from_base58(address)
313
- ExtPubkey.parse_from_payload(Base58.decode(address).htb)
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
314
333
  end
315
334
 
316
335
  # get version bytes from purpose' value.