diff --git a/src/base58.cpp b/src/base58.cpp index 189f9a7dcb..899bbd29d3 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -1,337 +1,309 @@ // Copyright (c) 2014-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "base58.h" #include "hash.h" +#include "script/script.h" #include "uint256.h" #include #include #include #include #include #include #include /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char *pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; bool DecodeBase58(const char *psz, std::vector &vch) { // Skip leading spaces. while (*psz && isspace(*psz)) { psz++; } // Skip and count leading '1's. int zeroes = 0; int length = 0; while (*psz == '1') { zeroes++; psz++; } // Allocate enough space in big-endian base256 representation. // log(58) / log(256), rounded up. int size = strlen(psz) * 733 / 1000 + 1; std::vector b256(size); // Process the characters. while (*psz && !isspace(*psz)) { // Decode base58 character const char *ch = strchr(pszBase58, *psz); if (ch == nullptr) { return false; } // Apply "b256 = b256 * 58 + ch". int carry = ch - pszBase58; int i = 0; for (std::vector::reverse_iterator it = b256.rbegin(); (carry != 0 || i < length) && (it != b256.rend()); ++it, ++i) { carry += 58 * (*it); *it = carry % 256; carry /= 256; } assert(carry == 0); length = i; psz++; } // Skip trailing spaces. while (isspace(*psz)) { psz++; } if (*psz != 0) { return false; } // Skip leading zeroes in b256. std::vector::iterator it = b256.begin() + (size - length); while (it != b256.end() && *it == 0) it++; // Copy result into output vector. vch.reserve(zeroes + (b256.end() - it)); vch.assign(zeroes, 0x00); while (it != b256.end()) { vch.push_back(*(it++)); } return true; } std::string EncodeBase58(const uint8_t *pbegin, const uint8_t *pend) { // Skip & count leading zeroes. int zeroes = 0; int length = 0; while (pbegin != pend && *pbegin == 0) { pbegin++; zeroes++; } // Allocate enough space in big-endian base58 representation. // log(256) / log(58), rounded up. int size = (pend - pbegin) * 138 / 100 + 1; std::vector b58(size); // Process the bytes. while (pbegin != pend) { int carry = *pbegin; int i = 0; // Apply "b58 = b58 * 256 + ch". for (std::vector::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) { carry += 256 * (*it); *it = carry % 58; carry /= 58; } assert(carry == 0); length = i; pbegin++; } // Skip leading zeroes in base58 result. std::vector::iterator it = b58.begin() + (size - length); while (it != b58.end() && *it == 0) { it++; } // Translate the result into a string. std::string str; str.reserve(zeroes + (b58.end() - it)); str.assign(zeroes, '1'); while (it != b58.end()) { str += pszBase58[*(it++)]; } return str; } std::string EncodeBase58(const std::vector &vch) { return EncodeBase58(&vch[0], &vch[0] + vch.size()); } bool DecodeBase58(const std::string &str, std::vector &vchRet) { return DecodeBase58(str.c_str(), vchRet); } std::string EncodeBase58Check(const std::vector &vchIn) { // add 4-byte hash check to the end std::vector vch(vchIn); uint256 hash = Hash(vch.begin(), vch.end()); vch.insert(vch.end(), (uint8_t *)&hash, (uint8_t *)&hash + 4); return EncodeBase58(vch); } bool DecodeBase58Check(const char *psz, std::vector &vchRet) { if (!DecodeBase58(psz, vchRet) || (vchRet.size() < 4)) { vchRet.clear(); return false; } // re-calculate the checksum, insure it matches the included 4-byte checksum uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) { vchRet.clear(); return false; } vchRet.resize(vchRet.size() - 4); return true; } bool DecodeBase58Check(const std::string &str, std::vector &vchRet) { return DecodeBase58Check(str.c_str(), vchRet); } CBase58Data::CBase58Data() { vchVersion.clear(); vchData.clear(); } void CBase58Data::SetData(const std::vector &vchVersionIn, const void *pdata, size_t nSize) { vchVersion = vchVersionIn; vchData.resize(nSize); if (!vchData.empty()) { memcpy(&vchData[0], pdata, nSize); } } void CBase58Data::SetData(const std::vector &vchVersionIn, const uint8_t *pbegin, const uint8_t *pend) { SetData(vchVersionIn, (void *)pbegin, pend - pbegin); } bool CBase58Data::SetString(const char *psz, unsigned int nVersionBytes) { std::vector vchTemp; bool rc58 = DecodeBase58Check(psz, vchTemp); if ((!rc58) || (vchTemp.size() < nVersionBytes)) { vchData.clear(); vchVersion.clear(); return false; } vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes); vchData.resize(vchTemp.size() - nVersionBytes); if (!vchData.empty()) { memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size()); } memory_cleanse(&vchTemp[0], vchTemp.size()); return true; } bool CBase58Data::SetString(const std::string &str) { return SetString(str.c_str()); } std::string CBase58Data::ToString() const { std::vector vch = vchVersion; vch.insert(vch.end(), vchData.begin(), vchData.end()); return EncodeBase58Check(vch); } int CBase58Data::CompareTo(const CBase58Data &b58) const { if (vchVersion < b58.vchVersion) return -1; if (vchVersion > b58.vchVersion) return 1; if (vchData < b58.vchData) return -1; if (vchData > b58.vchData) return 1; return 0; } namespace { -/** - * base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the - * serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the - * serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string &strAddress) { SetString(strAddress); } - CBitcoinAddress(const char *pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; -}; - -class CBitcoinAddressVisitor : public boost::static_visitor { +class DestinationEncoder : public boost::static_visitor { private: - CBitcoinAddress *addr; + const CChainParams &m_params; public: - CBitcoinAddressVisitor(CBitcoinAddress *addrIn) : addr(addrIn) {} - - bool operator()(const CKeyID &id) const { return addr->Set(id); } - bool operator()(const CScriptID &id) const { return addr->Set(id); } - bool operator()(const CNoDestination &no) const { return false; } -}; - -} // anon namespace + DestinationEncoder(const CChainParams ¶ms) : m_params(params) {} -bool CBitcoinAddress::Set(const CKeyID &id) { - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); - return true; -} - -bool CBitcoinAddress::Set(const CScriptID &id) { - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); - return true; -} - -bool CBitcoinAddress::Set(const CTxDestination &dest) { - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); -} + std::string operator()(const CKeyID &id) const { + std::vector data = + m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::IsValid() const { - return IsValid(Params()); -} + std::string operator()(const CScriptID &id) const { + std::vector data = + m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::IsValid(const CChainParams ¶ms) const { - bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = - vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - return fCorrectSize && fKnownVersion; -} + std::string operator()(const CNoDestination &no) const { return ""; } +}; -CTxDestination CBitcoinAddress::Get() const { - if (!IsValid()) return CNoDestination(); - uint160 id; - memcpy(&id, &vchData[0], 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) { - return CKeyID(id); - } else if (vchVersion == - Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) { - return CScriptID(id); - } else { +CTxDestination DecodeDestination(const std::string &str, + const CChainParams ¶ms) { + std::vector data; + uint160 hash; + if (!DecodeBase58Check(str, data)) { return CNoDestination(); } + // Base58Check decoding + const std::vector &pubkey_prefix = + params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + if (data.size() == 20 + pubkey_prefix.size() && + std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { + memcpy(hash.begin(), &data[pubkey_prefix.size()], 20); + return CKeyID(hash); + } + const std::vector &script_prefix = + params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + if (data.size() == 20 + script_prefix.size() && + std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { + memcpy(hash.begin(), &data[script_prefix.size()], 20); + return CScriptID(hash); + } + return CNoDestination(); } +} // namespace + void CBitcoinSecret::SetKey(const CKey &vchSecret) { assert(vchSecret.IsValid()); SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size()); if (vchSecret.IsCompressed()) vchData.push_back(1); } CKey CBitcoinSecret::GetKey() { CKey ret; assert(vchData.size() >= 32); ret.Set(vchData.begin(), vchData.begin() + 32, vchData.size() > 32 && vchData[32] == 1); return ret; } bool CBitcoinSecret::IsValid() const { bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1); bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY); return fExpectedFormat && fCorrectVersion; } bool CBitcoinSecret::SetString(const char *pszSecret) { return CBase58Data::SetString(pszSecret) && IsValid(); } bool CBitcoinSecret::SetString(const std::string &strSecret) { return SetString(strSecret.c_str()); } std::string EncodeDestination(const CTxDestination &dest) { - CBitcoinAddress addr(dest); - return addr.IsValid() ? addr.ToString() : ""; + return boost::apply_visitor(DestinationEncoder(Params()), dest); } CTxDestination DecodeDestination(const std::string &str) { - return CBitcoinAddress(str).Get(); + return DecodeDestination(str, Params()); } bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms) { - return CBitcoinAddress(str).IsValid(params); + return IsValidDestination(DecodeDestination(str, params)); } bool IsValidDestinationString(const std::string &str) { - return CBitcoinAddress(str).IsValid(); + return IsValidDestination(DecodeDestination(str, Params())); } diff --git a/src/base58.h b/src/base58.h index e24804aa78..24dea0d213 100644 --- a/src/base58.h +++ b/src/base58.h @@ -1,165 +1,164 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** * Why base-58 instead of standard base-64 encoding? * - Don't want 0OIl characters that look the same in some fonts and * could be used to create visually identical looking data. * - A string with non-alphanumeric characters is not as easily accepted as * input. * - E-mail usually won't line-break if there's no punctuation to break at. * - Double-clicking selects the whole string as one word if it's all * alphanumeric. */ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H #include "chainparams.h" #include "key.h" #include "pubkey.h" -#include "script/script.h" #include "script/standard.h" #include "support/allocators/zeroafterfree.h" #include #include /** * Encode a byte sequence as a base58-encoded string. * pbegin and pend cannot be nullptr, unless both are. */ std::string EncodeBase58(const uint8_t *pbegin, const uint8_t *pend); /** * Encode a byte vector as a base58-encoded string */ std::string EncodeBase58(const std::vector &vch); /** * Decode a base58-encoded string (psz) into a byte vector (vchRet). * return true if decoding is successful. * psz cannot be nullptr. */ bool DecodeBase58(const char *psz, std::vector &vchRet); /** * Decode a base58-encoded string (str) into a byte vector (vchRet). * return true if decoding is successful. */ bool DecodeBase58(const std::string &str, std::vector &vchRet); /** * Encode a byte vector into a base58-encoded string, including checksum */ std::string EncodeBase58Check(const std::vector &vchIn); /** * Decode a base58-encoded string (psz) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ inline bool DecodeBase58Check(const char *psz, std::vector &vchRet); /** * Decode a base58-encoded string (str) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ inline bool DecodeBase58Check(const std::string &str, std::vector &vchRet); /** * Base class for all base58-encoded data */ class CBase58Data { protected: //! the version byte(s) std::vector vchVersion; //! the actually encoded data typedef std::vector> vector_uchar; vector_uchar vchData; CBase58Data(); void SetData(const std::vector &vchVersionIn, const void *pdata, size_t nSize); void SetData(const std::vector &vchVersionIn, const uint8_t *pbegin, const uint8_t *pend); public: bool SetString(const char *psz, unsigned int nVersionBytes = 1); bool SetString(const std::string &str); std::string ToString() const; int CompareTo(const CBase58Data &b58) const; bool operator==(const CBase58Data &b58) const { return CompareTo(b58) == 0; } bool operator<=(const CBase58Data &b58) const { return CompareTo(b58) <= 0; } bool operator>=(const CBase58Data &b58) const { return CompareTo(b58) >= 0; } bool operator<(const CBase58Data &b58) const { return CompareTo(b58) < 0; } bool operator>(const CBase58Data &b58) const { return CompareTo(b58) > 0; } }; /** * A base58-encoded secret key */ class CBitcoinSecret : public CBase58Data { public: void SetKey(const CKey &vchSecret); CKey GetKey(); bool IsValid() const; bool SetString(const char *pszSecret); bool SetString(const std::string &strSecret); CBitcoinSecret(const CKey &vchSecret) { SetKey(vchSecret); } CBitcoinSecret() {} }; template class CBitcoinExtKeyBase : public CBase58Data { public: void SetKey(const K &key) { uint8_t vch[Size]; key.Encode(vch); SetData(Params().Base58Prefix(Type), vch, vch + Size); } K GetKey() { K ret; if (vchData.size() == Size) { // If base58 encoded data does not hold an ext key, return a // !IsValid() key ret.Decode(&vchData[0]); } return ret; } CBitcoinExtKeyBase(const K &key) { SetKey(key); } CBitcoinExtKeyBase(const std::string &strBase58c) { SetString(strBase58c.c_str(), Params().Base58Prefix(Type).size()); } CBitcoinExtKeyBase() {} }; typedef CBitcoinExtKeyBase CBitcoinExtKey; typedef CBitcoinExtKeyBase CBitcoinExtPubKey; std::string EncodeDestination(const CTxDestination &dest); CTxDestination DecodeDestination(const std::string &str); bool IsValidDestinationString(const std::string &str); bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms); #endif // BITCOIN_BASE58_H