]>
Commit | Line | Data |
---|---|---|
3d31e09c PW |
1 | // Copyright (c) 2014-2016 The Bitcoin Core developers |
2 | // Copyright (c) 2016-2018 The Zcash developers | |
3 | // Distributed under the MIT software license, see the accompanying | |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
3d31e09c PW |
5 | |
6 | #include <key_io.h> | |
7 | ||
8 | #include <base58.h> | |
9 | #include <bech32.h> | |
10 | #include <script/script.h> | |
11 | #include <utilstrencodings.h> | |
12 | ||
13 | #include <boost/variant/apply_visitor.hpp> | |
14 | #include <boost/variant/static_visitor.hpp> | |
2acb304a | 15 | |
2f537fa1 | 16 | #include "pbaas/identity.h" |
2acb304a | 17 | #include "cc/CCinclude.h" |
18 | #include "boost/algorithm/string.hpp" | |
3d31e09c PW |
19 | |
20 | #include <assert.h> | |
21 | #include <string.h> | |
22 | #include <algorithm> | |
23 | ||
082e5784 | 24 | extern uint160 VERUS_CHAINID; |
1e435b54 | 25 | extern std::string VERUS_CHAINNAME; |
082e5784 | 26 | |
56fe75cb | 27 | CIdentityID VERUS_DEFAULTID; |
28 | ||
3d31e09c PW |
29 | namespace |
30 | { | |
31 | class DestinationEncoder : public boost::static_visitor<std::string> | |
32 | { | |
33 | private: | |
34 | const CChainParams& m_params; | |
35 | ||
36 | public: | |
37 | DestinationEncoder(const CChainParams& params) : m_params(params) {} | |
38 | ||
39 | std::string operator()(const CKeyID& id) const | |
40 | { | |
41 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | |
42 | data.insert(data.end(), id.begin(), id.end()); | |
43 | return EncodeBase58Check(data); | |
44 | } | |
45 | ||
b8deecdc | 46 | std::string operator()(const CPubKey& key) const |
47 | { | |
48 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | |
49 | CKeyID id = key.GetID(); | |
50 | data.insert(data.end(), id.begin(), id.end()); | |
51 | return EncodeBase58Check(data); | |
52 | } | |
53 | ||
3d31e09c PW |
54 | std::string operator()(const CScriptID& id) const |
55 | { | |
56 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | |
57 | data.insert(data.end(), id.begin(), id.end()); | |
58 | return EncodeBase58Check(data); | |
59 | } | |
60 | ||
0d7fed99 | 61 | std::string operator()(const CIdentityID& id) const |
62 | { | |
63 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::IDENTITY_ADDRESS); | |
64 | data.insert(data.end(), id.begin(), id.end()); | |
65 | return EncodeBase58Check(data); | |
66 | } | |
67 | ||
56fe75cb | 68 | std::string operator()(const CQuantumID& id) const |
69 | { | |
70 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::QUANTUM_ADDRESS); | |
71 | data.insert(data.end(), id.begin(), id.end()); | |
72 | return EncodeBase58Check(data); | |
73 | } | |
74 | ||
3d31e09c PW |
75 | std::string operator()(const CNoDestination& no) const { return {}; } |
76 | }; | |
77 | ||
b2a98c42 MT |
78 | class DestinationBytes : public boost::static_visitor<std::vector<unsigned char>> |
79 | { | |
80 | public: | |
81 | DestinationBytes() {} | |
82 | ||
83 | std::vector<unsigned char> operator()(const CKeyID& id) const | |
84 | { | |
85 | return std::vector<unsigned char>(id.begin(), id.end()); | |
86 | } | |
87 | ||
88 | std::vector<unsigned char> operator()(const CPubKey& key) const | |
89 | { | |
90 | return std::vector<unsigned char>(key.begin(), key.end()); | |
91 | } | |
92 | ||
93 | std::vector<unsigned char> operator()(const CScriptID& id) const | |
94 | { | |
95 | return std::vector<unsigned char>(id.begin(), id.end()); | |
96 | } | |
97 | ||
0d7fed99 | 98 | std::vector<unsigned char> operator()(const CIdentityID& id) const |
99 | { | |
100 | return std::vector<unsigned char>(id.begin(), id.end()); | |
101 | } | |
102 | ||
56fe75cb | 103 | std::vector<unsigned char> operator()(const CQuantumID& id) const |
104 | { | |
105 | return std::vector<unsigned char>(id.begin(), id.end()); | |
106 | } | |
107 | ||
b2a98c42 MT |
108 | std::vector<unsigned char> operator()(const CNoDestination& no) const { return {}; } |
109 | }; | |
110 | ||
111 | class DestinationID : public boost::static_visitor<uint160> | |
112 | { | |
113 | public: | |
114 | DestinationID() {} | |
115 | ||
116 | uint160 operator()(const CKeyID& id) const | |
117 | { | |
b2a98c42 MT |
118 | return (uint160)id; |
119 | } | |
120 | ||
121 | uint160 operator()(const CPubKey& key) const | |
122 | { | |
123 | return (uint160)key.GetID(); | |
124 | } | |
125 | ||
126 | uint160 operator()(const CScriptID& id) const | |
127 | { | |
128 | return (uint160)id; | |
129 | } | |
130 | ||
0d7fed99 | 131 | uint160 operator()(const CIdentityID& id) const |
132 | { | |
133 | return (uint160)id; | |
134 | } | |
135 | ||
56fe75cb | 136 | uint160 operator()(const CQuantumID& id) const |
137 | { | |
138 | return (uint160)id; | |
139 | } | |
140 | ||
b2a98c42 MT |
141 | uint160 operator()(const CNoDestination& no) const { return CKeyID(); } |
142 | }; | |
143 | ||
3d31e09c PW |
144 | CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) |
145 | { | |
146 | std::vector<unsigned char> data; | |
147 | uint160 hash; | |
148 | if (DecodeBase58Check(str, data)) { | |
149 | // base58-encoded Bitcoin addresses. | |
3d31e09c PW |
150 | // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. |
151 | const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | |
152 | if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { | |
153 | std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); | |
154 | return CKeyID(hash); | |
155 | } | |
0d7fed99 | 156 | |
3d31e09c PW |
157 | // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. |
158 | const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | |
159 | if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { | |
160 | std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); | |
161 | return CScriptID(hash); | |
162 | } | |
0d7fed99 | 163 | |
164 | const std::vector<unsigned char>& identity_prefix = params.Base58Prefix(CChainParams::IDENTITY_ADDRESS); | |
165 | if (data.size() == hash.size() + identity_prefix.size() && std::equal(identity_prefix.begin(), identity_prefix.end(), data.begin())) { | |
166 | std::copy(data.begin() + identity_prefix.size(), data.end(), hash.begin()); | |
167 | return CIdentityID(hash); | |
168 | } | |
56fe75cb | 169 | |
170 | const std::vector<unsigned char>& quantum_prefix = params.Base58Prefix(CChainParams::QUANTUM_ADDRESS); | |
171 | if (data.size() == hash.size() + quantum_prefix.size() && std::equal(quantum_prefix.begin(), quantum_prefix.end(), data.begin())) { | |
172 | std::copy(data.begin() + quantum_prefix.size(), data.end(), hash.begin()); | |
173 | return CQuantumID(hash); | |
174 | } | |
3d31e09c | 175 | } |
2f537fa1 | 176 | else if (std::count(str.begin(), str.end(), '@') == 1) |
177 | { | |
56fe75cb | 178 | uint160 parent; |
179 | return CIdentityID(CIdentity::GetID(str, parent)); | |
2f537fa1 | 180 | } |
56fe75cb | 181 | |
3d31e09c PW |
182 | return CNoDestination(); |
183 | } | |
e5eab182 JG |
184 | |
185 | class PaymentAddressEncoder : public boost::static_visitor<std::string> | |
186 | { | |
187 | private: | |
188 | const CChainParams& m_params; | |
189 | ||
190 | public: | |
191 | PaymentAddressEncoder(const CChainParams& params) : m_params(params) {} | |
192 | ||
193 | std::string operator()(const libzcash::SproutPaymentAddress& zaddr) const | |
194 | { | |
195 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
196 | ss << zaddr; | |
197 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); | |
198 | data.insert(data.end(), ss.begin(), ss.end()); | |
199 | return EncodeBase58Check(data); | |
200 | } | |
201 | ||
bec3e62b JG |
202 | std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const |
203 | { | |
204 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
205 | ss << zaddr; | |
206 | // ConvertBits requires unsigned char, but CDataStream uses char | |
207 | std::vector<unsigned char> seraddr(ss.begin(), ss.end()); | |
208 | std::vector<unsigned char> data; | |
dd7417c8 | 209 | // See calculation comment below |
bec3e62b JG |
210 | data.reserve((seraddr.size() * 8 + 4) / 5); |
211 | ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end()); | |
212 | return bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS), data); | |
213 | } | |
214 | ||
e5eab182 JG |
215 | std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } |
216 | }; | |
217 | ||
218 | class ViewingKeyEncoder : public boost::static_visitor<std::string> | |
219 | { | |
220 | private: | |
221 | const CChainParams& m_params; | |
222 | ||
223 | public: | |
224 | ViewingKeyEncoder(const CChainParams& params) : m_params(params) {} | |
225 | ||
226 | std::string operator()(const libzcash::SproutViewingKey& vk) const | |
227 | { | |
228 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
229 | ss << vk; | |
230 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCVIEWING_KEY); | |
231 | data.insert(data.end(), ss.begin(), ss.end()); | |
232 | std::string ret = EncodeBase58Check(data); | |
233 | memory_cleanse(data.data(), data.size()); | |
234 | return ret; | |
235 | } | |
236 | ||
42b2ccec | 237 | std::string operator()(const libzcash::SaplingIncomingViewingKey& vk) const |
238 | { | |
239 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
240 | ss << vk; | |
241 | std::vector<unsigned char> serkey(ss.begin(), ss.end()); | |
242 | std::vector<unsigned char> data; | |
243 | ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end()); | |
244 | std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY), data); | |
245 | memory_cleanse(serkey.data(), serkey.size()); | |
246 | memory_cleanse(data.data(), data.size()); | |
247 | return ret; | |
248 | } | |
249 | ||
e5eab182 JG |
250 | std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } |
251 | }; | |
252 | ||
253 | class SpendingKeyEncoder : public boost::static_visitor<std::string> | |
254 | { | |
255 | private: | |
256 | const CChainParams& m_params; | |
257 | ||
258 | public: | |
259 | SpendingKeyEncoder(const CChainParams& params) : m_params(params) {} | |
260 | ||
261 | std::string operator()(const libzcash::SproutSpendingKey& zkey) const | |
262 | { | |
263 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
264 | ss << zkey; | |
265 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::ZCSPENDING_KEY); | |
266 | data.insert(data.end(), ss.begin(), ss.end()); | |
267 | std::string ret = EncodeBase58Check(data); | |
268 | memory_cleanse(data.data(), data.size()); | |
269 | return ret; | |
270 | } | |
271 | ||
70b4ad2d | 272 | std::string operator()(const libzcash::SaplingExtendedSpendingKey& zkey) const |
bec3e62b JG |
273 | { |
274 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
275 | ss << zkey; | |
276 | // ConvertBits requires unsigned char, but CDataStream uses char | |
277 | std::vector<unsigned char> serkey(ss.begin(), ss.end()); | |
278 | std::vector<unsigned char> data; | |
dd7417c8 | 279 | // See calculation comment below |
bec3e62b JG |
280 | data.reserve((serkey.size() * 8 + 4) / 5); |
281 | ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end()); | |
70b4ad2d | 282 | std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY), data); |
bec3e62b JG |
283 | memory_cleanse(serkey.data(), serkey.size()); |
284 | memory_cleanse(data.data(), data.size()); | |
285 | return ret; | |
286 | } | |
287 | ||
e5eab182 JG |
288 | std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; } |
289 | }; | |
bec3e62b | 290 | |
dd7417c8 JG |
291 | // Sizes of SaplingPaymentAddress and SaplingSpendingKey after |
292 | // ConvertBits<8, 5, true>(). The calculations below take the | |
293 | // regular serialized size in bytes, convert to bits, and then | |
294 | // perform ceiling division to get the number of 5-bit clusters. | |
bec3e62b | 295 | const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5; |
70b4ad2d | 296 | const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5; |
42b2ccec | 297 | const size_t ConvertedSaplingIncomingViewingKeySize = (32 * 8 + 4) / 5; |
3d31e09c PW |
298 | } // namespace |
299 | ||
300 | CKey DecodeSecret(const std::string& str) | |
301 | { | |
302 | CKey key; | |
303 | std::vector<unsigned char> data; | |
304 | if (DecodeBase58Check(str, data)) { | |
305 | const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); | |
306 | if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && | |
307 | std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { | |
308 | bool compressed = data.size() == 33 + privkey_prefix.size(); | |
309 | key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed); | |
310 | } | |
311 | } | |
312 | memory_cleanse(data.data(), data.size()); | |
313 | return key; | |
314 | } | |
315 | ||
316 | std::string EncodeSecret(const CKey& key) | |
317 | { | |
318 | assert(key.IsValid()); | |
319 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY); | |
320 | data.insert(data.end(), key.begin(), key.end()); | |
321 | if (key.IsCompressed()) { | |
322 | data.push_back(1); | |
323 | } | |
324 | std::string ret = EncodeBase58Check(data); | |
325 | memory_cleanse(data.data(), data.size()); | |
326 | return ret; | |
327 | } | |
328 | ||
329 | CExtPubKey DecodeExtPubKey(const std::string& str) | |
330 | { | |
331 | CExtPubKey key; | |
332 | std::vector<unsigned char> data; | |
333 | if (DecodeBase58Check(str, data)) { | |
334 | const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); | |
335 | if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { | |
336 | key.Decode(data.data() + prefix.size()); | |
337 | } | |
338 | } | |
339 | return key; | |
340 | } | |
341 | ||
342 | std::string EncodeExtPubKey(const CExtPubKey& key) | |
343 | { | |
344 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); | |
345 | size_t size = data.size(); | |
346 | data.resize(size + BIP32_EXTKEY_SIZE); | |
347 | key.Encode(data.data() + size); | |
348 | std::string ret = EncodeBase58Check(data); | |
349 | return ret; | |
350 | } | |
351 | ||
352 | CExtKey DecodeExtKey(const std::string& str) | |
353 | { | |
354 | CExtKey key; | |
355 | std::vector<unsigned char> data; | |
356 | if (DecodeBase58Check(str, data)) { | |
357 | const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); | |
358 | if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { | |
359 | key.Decode(data.data() + prefix.size()); | |
360 | } | |
361 | } | |
362 | return key; | |
363 | } | |
364 | ||
365 | std::string EncodeExtKey(const CExtKey& key) | |
366 | { | |
367 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); | |
368 | size_t size = data.size(); | |
369 | data.resize(size + BIP32_EXTKEY_SIZE); | |
370 | key.Encode(data.data() + size); | |
371 | std::string ret = EncodeBase58Check(data); | |
372 | memory_cleanse(data.data(), data.size()); | |
373 | return ret; | |
374 | } | |
375 | ||
376 | std::string EncodeDestination(const CTxDestination& dest) | |
377 | { | |
378 | return boost::apply_visitor(DestinationEncoder(Params()), dest); | |
379 | } | |
380 | ||
b2a98c42 MT |
381 | std::vector<unsigned char> GetDestinationBytes(const CTxDestination& dest) |
382 | { | |
383 | return boost::apply_visitor(DestinationBytes(), dest); | |
384 | } | |
385 | ||
386 | uint160 GetDestinationID(const CTxDestination dest) | |
387 | { | |
388 | return boost::apply_visitor(DestinationID(), dest); | |
389 | } | |
390 | ||
3d31e09c PW |
391 | CTxDestination DecodeDestination(const std::string& str) |
392 | { | |
393 | return DecodeDestination(str, Params()); | |
394 | } | |
395 | ||
396 | bool IsValidDestinationString(const std::string& str, const CChainParams& params) | |
397 | { | |
398 | return IsValidDestination(DecodeDestination(str, params)); | |
399 | } | |
400 | ||
401 | bool IsValidDestinationString(const std::string& str) | |
402 | { | |
403 | return IsValidDestinationString(str, Params()); | |
404 | } | |
405 | ||
406 | std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) | |
407 | { | |
e5eab182 | 408 | return boost::apply_visitor(PaymentAddressEncoder(Params()), zaddr); |
3d31e09c PW |
409 | } |
410 | ||
e5eab182 | 411 | libzcash::PaymentAddress DecodePaymentAddress(const std::string& str) |
3d31e09c PW |
412 | { |
413 | std::vector<unsigned char> data; | |
414 | if (DecodeBase58Check(str, data)) { | |
415 | const std::vector<unsigned char>& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); | |
74f5b010 | 416 | if ((data.size() == libzcash::SerializedSproutPaymentAddressSize + zaddr_prefix.size()) && |
3d31e09c PW |
417 | std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) { |
418 | CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end()); | |
419 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
e5eab182 | 420 | libzcash::SproutPaymentAddress ret; |
3d31e09c PW |
421 | ss >> ret; |
422 | return ret; | |
423 | } | |
424 | } | |
bec3e62b JG |
425 | data.clear(); |
426 | auto bech = bech32::Decode(str); | |
34e222c1 | 427 | if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) && |
bec3e62b JG |
428 | bech.second.size() == ConvertedSaplingPaymentAddressSize) { |
429 | // Bech32 decoding | |
430 | data.reserve((bech.second.size() * 5) / 8); | |
431 | if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) { | |
432 | CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); | |
433 | libzcash::SaplingPaymentAddress ret; | |
434 | ss >> ret; | |
435 | return ret; | |
436 | } | |
437 | } | |
e5eab182 JG |
438 | return libzcash::InvalidEncoding(); |
439 | } | |
440 | ||
5f63373e | 441 | bool IsValidPaymentAddressString(const std::string& str, uint32_t consensusBranchId) { |
442 | return IsValidPaymentAddress(DecodePaymentAddress(str), consensusBranchId); | |
3d31e09c PW |
443 | } |
444 | ||
445 | std::string EncodeViewingKey(const libzcash::ViewingKey& vk) | |
446 | { | |
e5eab182 | 447 | return boost::apply_visitor(ViewingKeyEncoder(Params()), vk); |
3d31e09c PW |
448 | } |
449 | ||
e5eab182 | 450 | libzcash::ViewingKey DecodeViewingKey(const std::string& str) |
3d31e09c PW |
451 | { |
452 | std::vector<unsigned char> data; | |
453 | if (DecodeBase58Check(str, data)) { | |
454 | const std::vector<unsigned char>& vk_prefix = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY); | |
74f5b010 | 455 | if ((data.size() == libzcash::SerializedSproutViewingKeySize + vk_prefix.size()) && |
3d31e09c PW |
456 | std::equal(vk_prefix.begin(), vk_prefix.end(), data.begin())) { |
457 | CSerializeData serialized(data.begin() + vk_prefix.size(), data.end()); | |
458 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
e5eab182 | 459 | libzcash::SproutViewingKey ret; |
3d31e09c PW |
460 | ss >> ret; |
461 | memory_cleanse(serialized.data(), serialized.size()); | |
462 | memory_cleanse(data.data(), data.size()); | |
463 | return ret; | |
464 | } | |
465 | } | |
42b2ccec | 466 | data.clear(); |
467 | auto bech = bech32::Decode(str); | |
468 | if(bech.first == Params().Bech32HRP(CChainParams::SAPLING_INCOMING_VIEWING_KEY) && | |
469 | bech.second.size() == ConvertedSaplingIncomingViewingKeySize) { | |
470 | // Bech32 decoding | |
471 | data.reserve((bech.second.size() * 5) / 8); | |
472 | if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) { | |
473 | CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); | |
474 | libzcash::SaplingIncomingViewingKey ret; | |
475 | ss >> ret; | |
476 | memory_cleanse(data.data(), data.size()); | |
477 | return ret; | |
478 | } | |
479 | } | |
e5eab182 | 480 | return libzcash::InvalidEncoding(); |
3d31e09c PW |
481 | } |
482 | ||
483 | std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) | |
484 | { | |
e5eab182 | 485 | return boost::apply_visitor(SpendingKeyEncoder(Params()), zkey); |
3d31e09c PW |
486 | } |
487 | ||
e5eab182 | 488 | libzcash::SpendingKey DecodeSpendingKey(const std::string& str) |
3d31e09c PW |
489 | { |
490 | std::vector<unsigned char> data; | |
491 | if (DecodeBase58Check(str, data)) { | |
492 | const std::vector<unsigned char>& zkey_prefix = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY); | |
74f5b010 | 493 | if ((data.size() == libzcash::SerializedSproutSpendingKeySize + zkey_prefix.size()) && |
3d31e09c PW |
494 | std::equal(zkey_prefix.begin(), zkey_prefix.end(), data.begin())) { |
495 | CSerializeData serialized(data.begin() + zkey_prefix.size(), data.end()); | |
496 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
e5eab182 | 497 | libzcash::SproutSpendingKey ret; |
3d31e09c PW |
498 | ss >> ret; |
499 | memory_cleanse(serialized.data(), serialized.size()); | |
500 | memory_cleanse(data.data(), data.size()); | |
501 | return ret; | |
502 | } | |
503 | } | |
bec3e62b JG |
504 | data.clear(); |
505 | auto bech = bech32::Decode(str); | |
34e222c1 | 506 | if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY) && |
70b4ad2d | 507 | bech.second.size() == ConvertedSaplingExtendedSpendingKeySize) { |
bec3e62b JG |
508 | // Bech32 decoding |
509 | data.reserve((bech.second.size() * 5) / 8); | |
510 | if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) { | |
511 | CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION); | |
70b4ad2d | 512 | libzcash::SaplingExtendedSpendingKey ret; |
bec3e62b JG |
513 | ss >> ret; |
514 | memory_cleanse(data.data(), data.size()); | |
515 | return ret; | |
516 | } | |
517 | } | |
3d31e09c | 518 | memory_cleanse(data.data(), data.size()); |
e5eab182 | 519 | return libzcash::InvalidEncoding(); |
3d31e09c | 520 | } |
2acb304a | 521 | |
522 | uint160 CCrossChainRPCData::GetConditionID(uint160 cid, int32_t condition) | |
523 | { | |
524 | CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION); | |
525 | hw << condition; | |
526 | hw << cid; | |
527 | uint256 chainHash = hw.GetHash(); | |
528 | return Hash160(chainHash.begin(), chainHash.end()); | |
529 | } | |
530 | ||
531 | uint160 CCrossChainRPCData::GetConditionID(std::string name, int32_t condition) | |
532 | { | |
9ab5a156 | 533 | uint160 parent; |
534 | uint160 cid = CIdentity::GetID(name, parent); | |
2acb304a | 535 | |
536 | CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION); | |
537 | hw << condition; | |
538 | hw << cid; | |
539 | uint256 chainHash = hw.GetHash(); | |
540 | return Hash160(chainHash.begin(), chainHash.end()); | |
541 | } | |
542 | ||
1e435b54 | 543 | std::string TrimLeading(const std::string &Name, unsigned char ch) |
544 | { | |
545 | std::string nameCopy = Name; | |
546 | int removeSpaces; | |
547 | for (removeSpaces = 0; removeSpaces < nameCopy.size(); removeSpaces++) | |
548 | { | |
549 | if (nameCopy[removeSpaces] != ch) | |
550 | { | |
551 | break; | |
552 | } | |
553 | } | |
554 | if (removeSpaces) | |
555 | { | |
556 | nameCopy.erase(nameCopy.begin(), nameCopy.begin() + removeSpaces); | |
557 | } | |
558 | return nameCopy; | |
559 | } | |
560 | ||
561 | std::string TrimTrailing(const std::string &Name, unsigned char ch) | |
562 | { | |
563 | std::string nameCopy = Name; | |
564 | int removeSpaces; | |
565 | for (removeSpaces = nameCopy.size() - 1; removeSpaces >= 0; removeSpaces--) | |
566 | { | |
567 | if (nameCopy[removeSpaces] != ch) | |
568 | { | |
569 | break; | |
570 | } | |
571 | } | |
c17dbdee | 572 | nameCopy.resize(nameCopy.size() - ((nameCopy.size() - 1) - removeSpaces)); |
1e435b54 | 573 | return nameCopy; |
574 | } | |
575 | ||
56fe75cb | 576 | std::string TrimSpaces(const std::string &Name) |
577 | { | |
578 | return TrimTrailing(TrimLeading(Name, ' '), ' '); | |
579 | } | |
580 | ||
581 | // this will add the current Verus chain name to subnames if it is not present | |
582 | // on both id and chain names | |
583 | std::vector<std::string> ParseSubNames(const std::string &Name, std::string &ChainOut, bool displayfilter, bool addVerus) | |
2acb304a | 584 | { |
585 | std::string nameCopy = Name; | |
b7ce9fe6 | 586 | std::string invalidChars = "\\/:*?\"<>|"; |
587 | if (displayfilter) | |
588 | { | |
589 | invalidChars += "\n\t\r\b\t\v\f\x1B"; | |
590 | } | |
2acb304a | 591 | for (int i = 0; i < nameCopy.size(); i++) |
592 | { | |
b7ce9fe6 | 593 | if (invalidChars.find(nameCopy[i]) != std::string::npos) |
c17dbdee | 594 | { |
595 | return std::vector<std::string>(); | |
596 | } | |
2acb304a | 597 | } |
1e435b54 | 598 | |
2acb304a | 599 | std::vector<std::string> retNames; |
600 | boost::split(retNames, nameCopy, boost::is_any_of("@")); | |
601 | if (!retNames.size() || retNames.size() > 2) | |
602 | { | |
603 | return std::vector<std::string>(); | |
604 | } | |
605 | ||
606 | bool explicitChain = false; | |
607 | if (retNames.size() == 2) | |
608 | { | |
609 | ChainOut = retNames[1]; | |
610 | explicitChain = true; | |
611 | } | |
612 | ||
613 | nameCopy = retNames[0]; | |
614 | boost::split(retNames, nameCopy, boost::is_any_of(".")); | |
615 | ||
56fe75cb | 616 | int numRetNames = retNames.size(); |
617 | ||
618 | if (addVerus) | |
619 | { | |
620 | if (explicitChain) | |
621 | { | |
622 | std::vector<std::string> chainOutNames; | |
623 | boost::split(chainOutNames, ChainOut, boost::is_any_of(".")); | |
624 | if (boost::to_lower_copy(chainOutNames.back()) != boost::to_lower_copy(VERUS_CHAINNAME)) | |
625 | { | |
626 | chainOutNames.push_back(VERUS_CHAINNAME); | |
627 | } | |
628 | } | |
629 | if (boost::to_lower_copy(retNames.back()) != boost::to_lower_copy(VERUS_CHAINNAME)) | |
630 | { | |
631 | retNames.push_back(VERUS_CHAINNAME); | |
632 | } | |
633 | } | |
634 | ||
2acb304a | 635 | for (int i = 0; i < retNames.size(); i++) |
636 | { | |
637 | if (retNames[i].size() > KOMODO_ASSETCHAIN_MAXLEN - 1) | |
638 | { | |
639 | retNames[i] = std::string(retNames[i], 0, (KOMODO_ASSETCHAIN_MAXLEN - 1)); | |
640 | } | |
1e435b54 | 641 | // spaces are allowed, but no sub-name can have leading or trailing spaces |
642 | if (!retNames[i].size() || retNames[i] != TrimTrailing(TrimLeading(retNames[i], ' '), ' ')) | |
643 | { | |
644 | return std::vector<std::string>(); | |
645 | } | |
2acb304a | 646 | } |
647 | ||
56fe75cb | 648 | // if no explicit chain is specified, default to chain of the ID |
2acb304a | 649 | if (!explicitChain && retNames.size()) |
650 | { | |
56fe75cb | 651 | for (int i = 1; i < retNames.size(); i++) |
2acb304a | 652 | { |
56fe75cb | 653 | if (ChainOut.size()) |
2acb304a | 654 | { |
56fe75cb | 655 | ChainOut = ChainOut + "."; |
2acb304a | 656 | } |
56fe75cb | 657 | ChainOut = ChainOut + retNames[i]; |
2acb304a | 658 | } |
659 | } | |
660 | ||
661 | return retNames; | |
662 | } | |
663 | ||
664 | // takes a multipart name, either complete or partially processed with a Parent hash, | |
665 | // hash its parent names into a parent ID and return the parent hash and cleaned, single name | |
b7ce9fe6 | 666 | std::string CleanName(const std::string &Name, uint160 &Parent, bool displayfilter) |
2acb304a | 667 | { |
668 | std::string chainName; | |
669 | std::vector<std::string> subNames = ParseSubNames(Name, chainName); | |
670 | ||
671 | if (!subNames.size()) | |
672 | { | |
673 | return ""; | |
674 | } | |
ee2c56e8 | 675 | |
676 | if (!Parent.IsNull() && | |
677 | boost::to_lower_copy(subNames.back()) == boost::to_lower_copy(VERUS_CHAINNAME)) | |
678 | { | |
679 | subNames.pop_back(); | |
680 | } | |
681 | ||
2acb304a | 682 | for (int i = subNames.size() - 1; i > 0; i--) |
683 | { | |
1713d73a | 684 | std::string parentNameStr = boost::algorithm::to_lower_copy(subNames[i]); |
685 | const char *parentName = parentNameStr.c_str(); | |
9ab5a156 | 686 | uint256 idHash; |
1713d73a | 687 | |
2acb304a | 688 | if (Parent.IsNull()) |
689 | { | |
1713d73a | 690 | idHash = Hash(parentName, parentName + parentNameStr.size()); |
2acb304a | 691 | } |
692 | else | |
693 | { | |
1713d73a | 694 | idHash = Hash(parentName, parentName + strlen(parentName)); |
2acb304a | 695 | idHash = Hash(Parent.begin(), Parent.end(), idHash.begin(), idHash.end()); |
2acb304a | 696 | } |
1713d73a | 697 | Parent = Hash160(idHash.begin(), idHash.end()); |
8496dcf4 | 698 | //printf("uint160 for parent %s: %s\n", parentName, Parent.GetHex().c_str()); |
2acb304a | 699 | } |
700 | return subNames[0]; | |
701 | } | |
702 | ||
a4f9bc97 | 703 | CNameReservation::CNameReservation(const CTransaction &tx, int *pOutNum) |
704 | { | |
705 | for (int i = 0; i < tx.vout.size(); i++) | |
706 | { | |
707 | COptCCParams p; | |
708 | if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p)) | |
709 | { | |
710 | if (p.evalCode == EVAL_IDENTITY_RESERVATION) | |
711 | { | |
712 | FromVector(p.vData[0], *this); | |
713 | return; | |
714 | } | |
715 | } | |
716 | } | |
717 | } | |
718 | ||
719 | CIdentity::CIdentity(const CScript &scriptPubKey) | |
720 | { | |
721 | COptCCParams p; | |
722 | if (IsPayToCryptoCondition(scriptPubKey, p) && p.IsValid() && p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size()) | |
723 | { | |
724 | *this = CIdentity(p.vData[0]); | |
725 | } | |
726 | } | |
727 | ||
9ab5a156 | 728 | CIdentityID CIdentity::GetID(const std::string &Name, uint160 &parent) |
2acb304a | 729 | { |
9ab5a156 | 730 | std::string cleanName = CleanName(Name, parent); |
731 | ||
732 | std::string subName = boost::algorithm::to_lower_copy(cleanName); | |
733 | const char *idName = subName.c_str(); | |
734 | //printf("hashing: %s, %s\n", idName, parent.GetHex().c_str()); | |
2acb304a | 735 | |
2acb304a | 736 | uint256 idHash; |
737 | if (parent.IsNull()) | |
738 | { | |
739 | idHash = Hash(idName, idName + strlen(idName)); | |
740 | } | |
741 | else | |
742 | { | |
743 | idHash = Hash(idName, idName + strlen(idName)); | |
744 | idHash = Hash(parent.begin(), parent.end(), idHash.begin(), idHash.end()); | |
2acb304a | 745 | } |
746 | return Hash160(idHash.begin(), idHash.end()); | |
747 | } | |
748 | ||
749 | CIdentityID CIdentity::GetID(const std::string &Name) const | |
750 | { | |
56fe75cb | 751 | uint160 parent; |
752 | std::string cleanName = CleanName(Name, parent); | |
2acb304a | 753 | |
9ab5a156 | 754 | std::string subName = boost::algorithm::to_lower_copy(cleanName); |
755 | const char *idName = subName.c_str(); | |
756 | //printf("hashing: %s, %s\n", idName, parent.GetHex().c_str()); | |
757 | ||
2acb304a | 758 | uint256 idHash; |
759 | if (parent.IsNull()) | |
760 | { | |
761 | idHash = Hash(idName, idName + strlen(idName)); | |
762 | } | |
763 | else | |
764 | { | |
765 | idHash = Hash(idName, idName + strlen(idName)); | |
766 | idHash = Hash(parent.begin(), parent.end(), idHash.begin(), idHash.end()); | |
767 | ||
768 | } | |
769 | return Hash160(idHash.begin(), idHash.end()); | |
770 | } | |
771 | ||
772 | CIdentityID CIdentity::GetID() const | |
773 | { | |
ee2c56e8 | 774 | uint160 Parent = parent; |
775 | return GetID(name, Parent); | |
2acb304a | 776 | } |
777 | ||
56fe75cb | 778 | uint160 CCrossChainRPCData::GetID(std::string name) |
2acb304a | 779 | { |
9ab5a156 | 780 | uint160 parent; |
8496dcf4 | 781 | //printf("uint160 for name %s: %s\n", name.c_str(), CIdentity::GetID(name, parent).GetHex().c_str()); |
9ab5a156 | 782 | return CIdentity::GetID(name, parent); |
2acb304a | 783 | } |
784 | ||
785 | CScript CIdentity::TransparentOutput() const | |
786 | { | |
787 | CConditionObj<CIdentity> ccObj = CConditionObj<CIdentity>(0, std::vector<CTxDestination>({CTxDestination(CIdentityID(GetID()))}), 1); | |
788 | return MakeMofNCCScript(ccObj); | |
789 | } | |
790 | ||
791 | CScript CIdentity::TransparentOutput(const CIdentityID &destinationID) | |
792 | { | |
793 | CConditionObj<CIdentity> ccObj = CConditionObj<CIdentity>(0, std::vector<CTxDestination>({destinationID}), 1); | |
794 | return MakeMofNCCScript(ccObj); | |
795 | } | |
796 | ||
797 | CScript CIdentity::IdentityUpdateOutputScript() const | |
798 | { | |
799 | CScript ret; | |
800 | ||
801 | if (!IsValid()) | |
802 | { | |
803 | return ret; | |
804 | } | |
805 | ||
806 | std::vector<CTxDestination> dests1({CTxDestination(CIdentityID(GetID()))}); | |
807 | CConditionObj<CIdentity> primary(EVAL_IDENTITY_PRIMARY, dests1, 1, this); | |
808 | std::vector<CTxDestination> dests2({CTxDestination(CIdentityID(revocationAuthority))}); | |
809 | CConditionObj<CIdentity> revocation(EVAL_IDENTITY_REVOKE, dests2, 1); | |
810 | std::vector<CTxDestination> dests3({CTxDestination(CIdentityID(recoveryAuthority))}); | |
811 | CConditionObj<CIdentity> recovery(EVAL_IDENTITY_RECOVER, dests3, 1); | |
812 | ||
76a2e430 | 813 | std::vector<CTxDestination> indexDests({CTxDestination(CKeyID(CCrossChainRPCData::GetConditionID(GetID(), EVAL_IDENTITY_PRIMARY))), |
814 | IsRevoked() ? CTxDestination(CIdentityID(recoveryAuthority)) : CTxDestination(CIdentityID(revocationAuthority)), | |
815 | primaryAddresses.size() ? primaryAddresses[0] : CKeyID()}); | |
2acb304a | 816 | |
817 | ret = MakeMofNCCScript(1, primary, revocation, recovery, &indexDests); | |
818 | return ret; | |
819 | } | |
820 |