diff options
Diffstat (limited to 'src/base58.cpp')
-rw-r--r-- | src/base58.cpp | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/src/base58.cpp b/src/base58.cpp new file mode 100644 index 0000000000..c594993ea0 --- /dev/null +++ b/src/base58.cpp @@ -0,0 +1,311 @@ +// Copyright (c) 2014 The Bitcoin 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 "uint256.h" + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <vector> +#include <string> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> + +/** All alphanumeric characters except for "0", "I", "O", and "l" */ +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) +{ + // Skip leading spaces. + while (*psz && isspace(*psz)) + psz++; + // Skip and count leading '1's. + int zeroes = 0; + while (*psz == '1') { + zeroes++; + psz++; + } + // Allocate enough space in big-endian base256 representation. + std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up. + // Process the characters. + while (*psz && !isspace(*psz)) { + // Decode base58 character + const char* ch = strchr(pszBase58, *psz); + if (ch == NULL) + return false; + // Apply "b256 = b256 * 58 + ch". + int carry = ch - pszBase58; + for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) { + carry += 58 * (*it); + *it = carry % 256; + carry /= 256; + } + assert(carry == 0); + psz++; + } + // Skip trailing spaces. + while (isspace(*psz)) + psz++; + if (*psz != 0) + return false; + // Skip leading zeroes in b256. + std::vector<unsigned char>::iterator it = b256.begin(); + 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 unsigned char* pbegin, const unsigned char* pend) +{ + // Skip & count leading zeroes. + int zeroes = 0; + while (pbegin != pend && *pbegin == 0) { + pbegin++; + zeroes++; + } + // Allocate enough space in big-endian base58 representation. + std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up. + // Process the bytes. + while (pbegin != pend) { + int carry = *pbegin; + // Apply "b58 = b58 * 256 + ch". + for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) { + carry += 256 * (*it); + *it = carry % 58; + carry /= 58; + } + assert(carry == 0); + pbegin++; + } + // Skip leading zeroes in base58 result. + std::vector<unsigned char>::iterator it = b58.begin(); + 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<unsigned char>& vch) +{ + return EncodeBase58(&vch[0], &vch[0] + vch.size()); +} + +bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet) +{ + return DecodeBase58(str.c_str(), vchRet); +} + +std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) +{ + // add 4-byte hash check to the end + std::vector<unsigned char> vch(vchIn); + uint256 hash = Hash(vch.begin(), vch.end()); + vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4); + return EncodeBase58(vch); +} + +bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& 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<unsigned char>& vchRet) +{ + return DecodeBase58Check(str.c_str(), vchRet); +} + +CBase58Data::CBase58Data() +{ + vchVersion.clear(); + vchData.clear(); +} + +void CBase58Data::SetData(const std::vector<unsigned char>& 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<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend) +{ + SetData(vchVersionIn, (void*)pbegin, pend - pbegin); +} + +bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes) +{ + std::vector<unsigned char> 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()); + OPENSSL_cleanse(&vchTemp[0], vchData.size()); + return true; +} + +bool CBase58Data::SetString(const std::string& str) +{ + return SetString(str.c_str()); +} + +std::string CBase58Data::ToString() const +{ + std::vector<unsigned char> 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 +{ +class CBitcoinAddressVisitor : public boost::static_visitor<bool> +{ +private: + CBitcoinAddress* addr; + +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 + +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); +} + +bool CBitcoinAddress::IsValid() const +{ + return IsValid(Params()); +} + +bool CBitcoinAddress::IsValid(const CChainParams& params) const +{ + bool fCorrectSize = vchData.size() == 20; + bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || + vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + return fCorrectSize && fKnownVersion; +} + +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 + return CNoDestination(); +} + +bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const +{ + if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) + return false; + uint160 id; + memcpy(&id, &vchData[0], 20); + keyID = CKeyID(id); + return true; +} + +bool CBitcoinAddress::IsScript() const +{ + return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); +} + +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()); +} |