|
4 | 4 |
|
5 | 5 | #include "base58.h"
|
6 | 6 |
|
| 7 | +#include "bech32.h" |
7 | 8 | #include "hash.h"
|
| 9 | +#include "script/script.h" |
8 | 10 | #include "uint256.h"
|
| 11 | +#include "utilstrencodings.h" |
9 | 12 |
|
10 |
| -#include <assert.h> |
11 |
| -#include <stdint.h> |
12 |
| -#include <string.h> |
13 |
| -#include <vector> |
14 |
| -#include <string> |
15 | 13 | #include <boost/variant/apply_visitor.hpp>
|
16 | 14 | #include <boost/variant/static_visitor.hpp>
|
17 | 15 |
|
| 16 | +#include <algorithm> |
| 17 | +#include <assert.h> |
| 18 | +#include <string.h> |
| 19 | + |
| 20 | + |
18 | 21 | /** All alphanumeric characters except for "0", "I", "O", and "l" */
|
19 | 22 | static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
20 | 23 |
|
@@ -212,86 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
|
212 | 215 |
|
213 | 216 | namespace
|
214 | 217 | {
|
215 |
| -/** base58-encoded Bitcoin addresses. |
216 |
| - * Public-key-hash-addresses have version 0 (or 111 testnet). |
217 |
| - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. |
218 |
| - * Script-hash-addresses have version 5 (or 196 testnet). |
219 |
| - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. |
220 |
| - */ |
221 |
| -class CBitcoinAddress : public CBase58Data { |
222 |
| -public: |
223 |
| - bool Set(const CKeyID &id); |
224 |
| - bool Set(const CScriptID &id); |
225 |
| - bool Set(const CTxDestination &dest); |
226 |
| - bool IsValid() const; |
227 |
| - bool IsValid(const CChainParams ¶ms) const; |
228 |
| - |
229 |
| - CBitcoinAddress() {} |
230 |
| - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } |
231 |
| - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } |
232 |
| - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } |
233 |
| - |
234 |
| - CTxDestination Get() const; |
235 |
| -}; |
236 |
| - |
237 |
| -class CBitcoinAddressVisitor : public boost::static_visitor<bool> |
| 218 | +class DestinationEncoder : public boost::static_visitor<std::string> |
238 | 219 | {
|
239 | 220 | private:
|
240 |
| - CBitcoinAddress* addr; |
| 221 | + const CChainParams& m_params; |
241 | 222 |
|
242 | 223 | public:
|
243 |
| - explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} |
| 224 | + DestinationEncoder(const CChainParams& params) : m_params(params) {} |
244 | 225 |
|
245 |
| - bool operator()(const CKeyID& id) const { return addr->Set(id); } |
246 |
| - bool operator()(const CScriptID& id) const { return addr->Set(id); } |
247 |
| - bool operator()(const CNoDestination& no) const { return false; } |
248 |
| -}; |
249 |
| - |
250 |
| -} // namespace |
| 226 | + std::string operator()(const CKeyID& id) const |
| 227 | + { |
| 228 | + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); |
| 229 | + data.insert(data.end(), id.begin(), id.end()); |
| 230 | + return EncodeBase58Check(data); |
| 231 | + } |
251 | 232 |
|
252 |
| -bool CBitcoinAddress::Set(const CKeyID& id) |
253 |
| -{ |
254 |
| - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); |
255 |
| - return true; |
256 |
| -} |
| 233 | + std::string operator()(const CScriptID& id) const |
| 234 | + { |
| 235 | + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); |
| 236 | + data.insert(data.end(), id.begin(), id.end()); |
| 237 | + return EncodeBase58Check(data); |
| 238 | + } |
257 | 239 |
|
258 |
| -bool CBitcoinAddress::Set(const CScriptID& id) |
259 |
| -{ |
260 |
| - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); |
261 |
| - return true; |
262 |
| -} |
| 240 | + std::string operator()(const WitnessV0KeyHash& id) const |
| 241 | + { |
| 242 | + std::vector<unsigned char> data = {0}; |
| 243 | + ConvertBits<8, 5, true>(data, id.begin(), id.end()); |
| 244 | + return bech32::Encode(m_params.Bech32HRP(), data); |
| 245 | + } |
263 | 246 |
|
264 |
| -bool CBitcoinAddress::Set(const CTxDestination& dest) |
265 |
| -{ |
266 |
| - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); |
267 |
| -} |
| 247 | + std::string operator()(const WitnessV0ScriptHash& id) const |
| 248 | + { |
| 249 | + std::vector<unsigned char> data = {0}; |
| 250 | + ConvertBits<8, 5, true>(data, id.begin(), id.end()); |
| 251 | + return bech32::Encode(m_params.Bech32HRP(), data); |
| 252 | + } |
268 | 253 |
|
269 |
| -bool CBitcoinAddress::IsValid() const |
270 |
| -{ |
271 |
| - return IsValid(Params()); |
272 |
| -} |
| 254 | + std::string operator()(const WitnessUnknown& id) const |
| 255 | + { |
| 256 | + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { |
| 257 | + return {}; |
| 258 | + } |
| 259 | + std::vector<unsigned char> data = {(unsigned char)id.version}; |
| 260 | + ConvertBits<8, 5, true>(data, id.program, id.program + id.length); |
| 261 | + return bech32::Encode(m_params.Bech32HRP(), data); |
| 262 | + } |
273 | 263 |
|
274 |
| -bool CBitcoinAddress::IsValid(const CChainParams& params) const |
275 |
| -{ |
276 |
| - bool fCorrectSize = vchData.size() == 20; |
277 |
| - bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || |
278 |
| - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); |
279 |
| - return fCorrectSize && fKnownVersion; |
280 |
| -} |
| 264 | + std::string operator()(const CNoDestination& no) const { return {}; } |
| 265 | +}; |
281 | 266 |
|
282 |
| -CTxDestination CBitcoinAddress::Get() const |
| 267 | +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) |
283 | 268 | {
|
284 |
| - if (!IsValid()) |
285 |
| - return CNoDestination(); |
286 |
| - uint160 id; |
287 |
| - memcpy(&id, vchData.data(), 20); |
288 |
| - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) |
289 |
| - return CKeyID(id); |
290 |
| - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) |
291 |
| - return CScriptID(id); |
292 |
| - else |
293 |
| - return CNoDestination(); |
| 269 | + std::vector<unsigned char> data; |
| 270 | + uint160 hash; |
| 271 | + if (DecodeBase58Check(str, data)) { |
| 272 | + // base58-encoded Bitcoin addresses. |
| 273 | + // Public-key-hash-addresses have version 0 (or 111 testnet). |
| 274 | + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. |
| 275 | + const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); |
| 276 | + if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { |
| 277 | + std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); |
| 278 | + return CKeyID(hash); |
| 279 | + } |
| 280 | + // Script-hash-addresses have version 5 (or 196 testnet). |
| 281 | + // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. |
| 282 | + const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); |
| 283 | + if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { |
| 284 | + std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); |
| 285 | + return CScriptID(hash); |
| 286 | + } |
| 287 | + } |
| 288 | + data.clear(); |
| 289 | + auto bech = bech32::Decode(str); |
| 290 | + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { |
| 291 | + // Bech32 decoding |
| 292 | + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) |
| 293 | + // The rest of the symbols are converted witness program bytes. |
| 294 | + if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { |
| 295 | + if (version == 0) { |
| 296 | + { |
| 297 | + WitnessV0KeyHash keyid; |
| 298 | + if (data.size() == keyid.size()) { |
| 299 | + std::copy(data.begin(), data.end(), keyid.begin()); |
| 300 | + return keyid; |
| 301 | + } |
| 302 | + } |
| 303 | + { |
| 304 | + WitnessV0ScriptHash scriptid; |
| 305 | + if (data.size() == scriptid.size()) { |
| 306 | + std::copy(data.begin(), data.end(), scriptid.begin()); |
| 307 | + return scriptid; |
| 308 | + } |
| 309 | + } |
| 310 | + return CNoDestination(); |
| 311 | + } |
| 312 | + if (version > 16 || data.size() < 2 || data.size() > 40) { |
| 313 | + return CNoDestination(); |
| 314 | + } |
| 315 | + WitnessUnknown unk; |
| 316 | + unk.version = version; |
| 317 | + std::copy(data.begin(), data.end(), unk.program); |
| 318 | + unk.length = data.size(); |
| 319 | + return unk; |
| 320 | + } |
| 321 | + } |
| 322 | + return CNoDestination(); |
294 | 323 | }
|
| 324 | +} // namespace |
295 | 325 |
|
296 | 326 | void CBitcoinSecret::SetKey(const CKey& vchSecret)
|
297 | 327 | {
|
@@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
|
328 | 358 |
|
329 | 359 | std::string EncodeDestination(const CTxDestination& dest)
|
330 | 360 | {
|
331 |
| - CBitcoinAddress addr(dest); |
332 |
| - if (!addr.IsValid()) return ""; |
333 |
| - return addr.ToString(); |
| 361 | + return boost::apply_visitor(DestinationEncoder(Params()), dest); |
334 | 362 | }
|
335 | 363 |
|
336 | 364 | CTxDestination DecodeDestination(const std::string& str)
|
337 | 365 | {
|
338 |
| - return CBitcoinAddress(str).Get(); |
| 366 | + return DecodeDestination(str, Params()); |
339 | 367 | }
|
340 | 368 |
|
341 | 369 | bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
342 | 370 | {
|
343 |
| - return CBitcoinAddress(str).IsValid(params); |
| 371 | + return IsValidDestination(DecodeDestination(str, params)); |
344 | 372 | }
|
345 | 373 |
|
346 | 374 | bool IsValidDestinationString(const std::string& str)
|
347 | 375 | {
|
348 |
| - return CBitcoinAddress(str).IsValid(); |
| 376 | + return IsValidDestinationString(str, Params()); |
349 | 377 | }
|
0 commit comments