tapyrus 0.1.0 → 0.2.4

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