Commit | Line | Data |
---|---|---|
f914f1a7 | 1 | // Copyright (c) 2014 The Bitcoin Core developers |
fa94b9d5 | 2 | // Distributed under the MIT software license, see the accompanying |
b58be132 PW |
3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | ||
f6b7c644 PW |
5 | #include "base58.h" |
6 | ||
7 | #include "hash.h" | |
c8f9c87b | 8 | #include "script/script.h" |
f6b7c644 PW |
9 | #include "uint256.h" |
10 | ||
e104fcdd SB |
11 | #include "version.h" |
12 | #include "streams.h" | |
13 | ||
f6b7c644 PW |
14 | #include <boost/variant/apply_visitor.hpp> |
15 | #include <boost/variant/static_visitor.hpp> | |
b58be132 | 16 | |
c8f9c87b PW |
17 | #include <algorithm> |
18 | #include <assert.h> | |
19 | #include <string.h> | |
20 | ||
21 | ||
fa94b9d5 | 22 | /** All alphanumeric characters except for "0", "I", "O", and "l" */ |
b58be132 PW |
23 | static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; |
24 | ||
20e01b1a PW |
25 | bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) |
26 | { | |
b58be132 PW |
27 | // Skip leading spaces. |
28 | while (*psz && isspace(*psz)) | |
29 | psz++; | |
30 | // Skip and count leading '1's. | |
31 | int zeroes = 0; | |
32 | while (*psz == '1') { | |
33 | zeroes++; | |
34 | psz++; | |
35 | } | |
36 | // Allocate enough space in big-endian base256 representation. | |
37 | std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up. | |
38 | // Process the characters. | |
39 | while (*psz && !isspace(*psz)) { | |
40 | // Decode base58 character | |
20e01b1a | 41 | const char* ch = strchr(pszBase58, *psz); |
b58be132 PW |
42 | if (ch == NULL) |
43 | return false; | |
44 | // Apply "b256 = b256 * 58 + ch". | |
45 | int carry = ch - pszBase58; | |
46 | for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) { | |
47 | carry += 58 * (*it); | |
48 | *it = carry % 256; | |
49 | carry /= 256; | |
50 | } | |
51 | assert(carry == 0); | |
52 | psz++; | |
53 | } | |
54 | // Skip trailing spaces. | |
55 | while (isspace(*psz)) | |
56 | psz++; | |
57 | if (*psz != 0) | |
58 | return false; | |
59 | // Skip leading zeroes in b256. | |
60 | std::vector<unsigned char>::iterator it = b256.begin(); | |
61 | while (it != b256.end() && *it == 0) | |
62 | it++; | |
63 | // Copy result into output vector. | |
64 | vch.reserve(zeroes + (b256.end() - it)); | |
65 | vch.assign(zeroes, 0x00); | |
66 | while (it != b256.end()) | |
20e01b1a | 67 | vch.push_back(*(it++)); |
b58be132 PW |
68 | return true; |
69 | } | |
70 | ||
20e01b1a PW |
71 | std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) |
72 | { | |
b58be132 PW |
73 | // Skip & count leading zeroes. |
74 | int zeroes = 0; | |
75 | while (pbegin != pend && *pbegin == 0) { | |
76 | pbegin++; | |
77 | zeroes++; | |
78 | } | |
79 | // Allocate enough space in big-endian base58 representation. | |
80 | std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up. | |
81 | // Process the bytes. | |
82 | while (pbegin != pend) { | |
83 | int carry = *pbegin; | |
84 | // Apply "b58 = b58 * 256 + ch". | |
85 | for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) { | |
86 | carry += 256 * (*it); | |
87 | *it = carry % 58; | |
88 | carry /= 58; | |
89 | } | |
90 | assert(carry == 0); | |
91 | pbegin++; | |
92 | } | |
93 | // Skip leading zeroes in base58 result. | |
94 | std::vector<unsigned char>::iterator it = b58.begin(); | |
95 | while (it != b58.end() && *it == 0) | |
96 | it++; | |
97 | // Translate the result into a string. | |
98 | std::string str; | |
99 | str.reserve(zeroes + (b58.end() - it)); | |
100 | str.assign(zeroes, '1'); | |
101 | while (it != b58.end()) | |
102 | str += pszBase58[*(it++)]; | |
103 | return str; | |
104 | } | |
f6b7c644 | 105 | |
20e01b1a PW |
106 | std::string EncodeBase58(const std::vector<unsigned char>& vch) |
107 | { | |
1b34996b | 108 | return EncodeBase58(vch.data(), vch.data() + vch.size()); |
f6b7c644 PW |
109 | } |
110 | ||
20e01b1a PW |
111 | bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet) |
112 | { | |
f6b7c644 PW |
113 | return DecodeBase58(str.c_str(), vchRet); |
114 | } | |
115 | ||
20e01b1a PW |
116 | std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) |
117 | { | |
f6b7c644 PW |
118 | // add 4-byte hash check to the end |
119 | std::vector<unsigned char> vch(vchIn); | |
120 | uint256 hash = Hash(vch.begin(), vch.end()); | |
121 | vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); | |
122 | return EncodeBase58(vch); | |
123 | } | |
124 | ||
20e01b1a PW |
125 | bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) |
126 | { | |
88df548d | 127 | if (!DecodeBase58(psz, vchRet) || |
20e01b1a | 128 | (vchRet.size() < 4)) { |
f6b7c644 PW |
129 | vchRet.clear(); |
130 | return false; | |
131 | } | |
132 | // re-calculate the checksum, insure it matches the included 4-byte checksum | |
20e01b1a PW |
133 | uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); |
134 | if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) { | |
f6b7c644 PW |
135 | vchRet.clear(); |
136 | return false; | |
137 | } | |
20e01b1a | 138 | vchRet.resize(vchRet.size() - 4); |
f6b7c644 PW |
139 | return true; |
140 | } | |
141 | ||
20e01b1a PW |
142 | bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet) |
143 | { | |
f6b7c644 PW |
144 | return DecodeBase58Check(str.c_str(), vchRet); |
145 | } | |
146 | ||
20e01b1a PW |
147 | CBase58Data::CBase58Data() |
148 | { | |
f6b7c644 PW |
149 | vchVersion.clear(); |
150 | vchData.clear(); | |
151 | } | |
152 | ||
20e01b1a PW |
153 | void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize) |
154 | { | |
f6b7c644 PW |
155 | vchVersion = vchVersionIn; |
156 | vchData.resize(nSize); | |
157 | if (!vchData.empty()) | |
1b34996b | 158 | memcpy(vchData.data(), pdata, nSize); |
f6b7c644 PW |
159 | } |
160 | ||
20e01b1a PW |
161 | void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend) |
162 | { | |
f6b7c644 PW |
163 | SetData(vchVersionIn, (void*)pbegin, pend - pbegin); |
164 | } | |
165 | ||
20e01b1a PW |
166 | bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) |
167 | { | |
f6b7c644 | 168 | std::vector<unsigned char> vchTemp; |
88df548d JG |
169 | bool rc58 = DecodeBase58Check(psz, vchTemp); |
170 | if ((!rc58) || (vchTemp.size() < nVersionBytes)) { | |
f6b7c644 PW |
171 | vchData.clear(); |
172 | vchVersion.clear(); | |
173 | return false; | |
174 | } | |
175 | vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes); | |
176 | vchData.resize(vchTemp.size() - nVersionBytes); | |
177 | if (!vchData.empty()) | |
1b34996b JR |
178 | memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size()); |
179 | memory_cleanse(vchTemp.data(), vchTemp.size()); | |
f6b7c644 PW |
180 | return true; |
181 | } | |
182 | ||
00e56272 | 183 | bool CBase58Data::SetString(const std::string& str, unsigned int nVersionBytes) |
20e01b1a | 184 | { |
00e56272 | 185 | return SetString(str.c_str(), nVersionBytes); |
f6b7c644 PW |
186 | } |
187 | ||
20e01b1a PW |
188 | std::string CBase58Data::ToString() const |
189 | { | |
f6b7c644 PW |
190 | std::vector<unsigned char> vch = vchVersion; |
191 | vch.insert(vch.end(), vchData.begin(), vchData.end()); | |
192 | return EncodeBase58Check(vch); | |
193 | } | |
194 | ||
20e01b1a PW |
195 | int CBase58Data::CompareTo(const CBase58Data& b58) const |
196 | { | |
197 | if (vchVersion < b58.vchVersion) | |
198 | return -1; | |
199 | if (vchVersion > b58.vchVersion) | |
200 | return 1; | |
201 | if (vchData < b58.vchData) | |
202 | return -1; | |
203 | if (vchData > b58.vchData) | |
204 | return 1; | |
f6b7c644 PW |
205 | return 0; |
206 | } | |
207 | ||
20e01b1a PW |
208 | namespace |
209 | { | |
c8f9c87b | 210 | class DestinationEncoder : public boost::static_visitor<std::string> |
20e01b1a PW |
211 | { |
212 | private: | |
c8f9c87b | 213 | const CChainParams& m_params; |
e10dcf27 | 214 | |
20e01b1a | 215 | public: |
c8f9c87b | 216 | DestinationEncoder(const CChainParams& params) : m_params(params) {} |
f6b7c644 | 217 | |
c8f9c87b PW |
218 | std::string operator()(const CKeyID& id) const |
219 | { | |
220 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | |
221 | data.insert(data.end(), id.begin(), id.end()); | |
222 | return EncodeBase58Check(data); | |
223 | } | |
f6b7c644 | 224 | |
c8f9c87b PW |
225 | std::string operator()(const CScriptID& id) const |
226 | { | |
227 | std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | |
228 | data.insert(data.end(), id.begin(), id.end()); | |
229 | return EncodeBase58Check(data); | |
230 | } | |
00e56272 | 231 | |
c8f9c87b PW |
232 | std::string operator()(const CNoDestination& no) const { return ""; } |
233 | }; | |
00e56272 | 234 | |
c8f9c87b PW |
235 | CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) |
236 | { | |
237 | std::vector<unsigned char> data; | |
238 | uint160 hash; | |
239 | if (DecodeBase58Check(str, data)) { | |
240 | // base58-encoded Bitcoin addresses. | |
241 | // Public-key-hash-addresses have version 0 (or 111 testnet). | |
242 | // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. | |
243 | const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); | |
244 | if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { | |
245 | std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); | |
246 | return CKeyID(hash); | |
247 | } | |
248 | // Script-hash-addresses have version 5 (or 196 testnet). | |
249 | // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. | |
250 | const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); | |
251 | if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { | |
252 | std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); | |
253 | return CScriptID(hash); | |
254 | } | |
255 | } | |
256 | return CNoDestination(); | |
f6b7c644 | 257 | } |
c8f9c87b | 258 | } // namespace |
f6b7c644 | 259 | |
f146029b | 260 | CKey DecodeSecret(const std::string& str) |
20e01b1a | 261 | { |
f146029b PW |
262 | CKey key; |
263 | std::vector<unsigned char> data; | |
264 | if (DecodeBase58Check(str, data)) { | |
265 | const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); | |
266 | if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && | |
267 | std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { | |
268 | bool compressed = data.size() == 33 + privkey_prefix.size(); | |
269 | key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed); | |
270 | } | |
271 | } | |
272 | memory_cleanse(data.data(), data.size()); | |
273 | return key; | |
f6b7c644 PW |
274 | } |
275 | ||
f146029b | 276 | std::string EncodeSecret(const CKey& key) |
20e01b1a | 277 | { |
f146029b PW |
278 | assert(key.IsValid()); |
279 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY); | |
280 | data.insert(data.end(), key.begin(), key.end()); | |
281 | if (key.IsCompressed()) { | |
282 | data.push_back(1); | |
283 | } | |
284 | std::string ret = EncodeBase58Check(data); | |
285 | memory_cleanse(data.data(), data.size()); | |
f6b7c644 PW |
286 | return ret; |
287 | } | |
288 | ||
07444da1 PW |
289 | std::string EncodeDestination(const CTxDestination& dest) |
290 | { | |
c8f9c87b | 291 | return boost::apply_visitor(DestinationEncoder(Params()), dest); |
07444da1 PW |
292 | } |
293 | ||
294 | CTxDestination DecodeDestination(const std::string& str) | |
295 | { | |
c8f9c87b | 296 | return DecodeDestination(str, Params()); |
07444da1 PW |
297 | } |
298 | ||
299 | bool IsValidDestinationString(const std::string& str, const CChainParams& params) | |
300 | { | |
c8f9c87b | 301 | return IsValidDestination(DecodeDestination(str, params)); |
07444da1 PW |
302 | } |
303 | ||
304 | bool IsValidDestinationString(const std::string& str) | |
305 | { | |
c8f9c87b | 306 | return IsValidDestinationString(str, Params()); |
07444da1 PW |
307 | } |
308 | ||
80ed13d5 JG |
309 | std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) |
310 | { | |
aa333ee8 JG |
311 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
312 | ss << zaddr; | |
313 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); | |
314 | data.insert(data.end(), ss.begin(), ss.end()); | |
315 | return EncodeBase58Check(data); | |
80ed13d5 JG |
316 | } |
317 | ||
318 | boost::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str) | |
319 | { | |
aa333ee8 JG |
320 | std::vector<unsigned char> data; |
321 | if (DecodeBase58Check(str, data)) { | |
322 | const std::vector<unsigned char>& zaddr_prefix = Params().Base58Prefix(CChainParams::ZCPAYMENT_ADDRRESS); | |
323 | if ((data.size() == libzcash::SerializedPaymentAddressSize + zaddr_prefix.size()) && | |
324 | std::equal(zaddr_prefix.begin(), zaddr_prefix.end(), data.begin())) { | |
325 | CSerializeData serialized(data.begin() + zaddr_prefix.size(), data.end()); | |
326 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
327 | libzcash::PaymentAddress ret; | |
328 | ss >> ret; | |
329 | return ret; | |
330 | } | |
80ed13d5 | 331 | } |
aa333ee8 | 332 | return boost::none; |
80ed13d5 | 333 | } |
472f75bc | 334 | |
8bf3a3d7 JG |
335 | std::string EncodeViewingKey(const libzcash::ViewingKey& vk) |
336 | { | |
aa333ee8 JG |
337 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
338 | ss << vk; | |
339 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY); | |
340 | data.insert(data.end(), ss.begin(), ss.end()); | |
341 | std::string ret = EncodeBase58Check(data); | |
342 | memory_cleanse(data.data(), data.size()); | |
343 | return ret; | |
8bf3a3d7 JG |
344 | } |
345 | ||
346 | boost::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str) | |
347 | { | |
aa333ee8 JG |
348 | std::vector<unsigned char> data; |
349 | if (DecodeBase58Check(str, data)) { | |
350 | const std::vector<unsigned char>& vk_prefix = Params().Base58Prefix(CChainParams::ZCVIEWING_KEY); | |
351 | if ((data.size() == libzcash::SerializedViewingKeySize + vk_prefix.size()) && | |
352 | std::equal(vk_prefix.begin(), vk_prefix.end(), data.begin())) { | |
353 | CSerializeData serialized(data.begin() + vk_prefix.size(), data.end()); | |
354 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
355 | libzcash::ViewingKey ret; | |
356 | ss >> ret; | |
357 | memory_cleanse(serialized.data(), serialized.size()); | |
358 | memory_cleanse(data.data(), data.size()); | |
359 | return ret; | |
360 | } | |
8bf3a3d7 | 361 | } |
aa333ee8 JG |
362 | memory_cleanse(data.data(), data.size()); |
363 | return boost::none; | |
8bf3a3d7 JG |
364 | } |
365 | ||
472f75bc JG |
366 | std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) |
367 | { | |
aa333ee8 JG |
368 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
369 | ss << zkey; | |
370 | std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY); | |
371 | data.insert(data.end(), ss.begin(), ss.end()); | |
372 | std::string ret = EncodeBase58Check(data); | |
373 | memory_cleanse(data.data(), data.size()); | |
374 | return ret; | |
472f75bc JG |
375 | } |
376 | ||
377 | boost::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str) | |
378 | { | |
aa333ee8 JG |
379 | std::vector<unsigned char> data; |
380 | if (DecodeBase58Check(str, data)) { | |
381 | const std::vector<unsigned char>& zkey_prefix = Params().Base58Prefix(CChainParams::ZCSPENDING_KEY); | |
382 | if ((data.size() == libzcash::SerializedSpendingKeySize + zkey_prefix.size()) && | |
383 | std::equal(zkey_prefix.begin(), zkey_prefix.end(), data.begin())) { | |
384 | CSerializeData serialized(data.begin() + zkey_prefix.size(), data.end()); | |
385 | CDataStream ss(serialized, SER_NETWORK, PROTOCOL_VERSION); | |
386 | libzcash::SpendingKey ret; | |
387 | ss >> ret; | |
388 | memory_cleanse(serialized.data(), serialized.size()); | |
389 | memory_cleanse(data.data(), data.size()); | |
390 | return ret; | |
391 | } | |
472f75bc | 392 | } |
aa333ee8 JG |
393 | memory_cleanse(data.data(), data.size()); |
394 | return boost::none; | |
472f75bc | 395 | } |