Browse Source

Merge pull request #4165

f6b7c64 Move base58.h implementation code to base58.cpp (Pieter Wuille)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
68d5fb3cb3
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 183
      src/base58.cpp
  2. 259
      src/base58.h
  3. 4
      src/test/base58_tests.cpp

183
src/base58.cpp

@ -2,11 +2,18 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "base58.h"
#include "hash.h"
#include "uint256.h"
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
/* All alphanumeric characters except for "0", "I", "O", and "l" */ /* All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
@ -89,3 +96,179 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
str += pszBase58[*(it++)]; str += pszBase58[*(it++)];
return str; 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))
return false;
if (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;
DecodeBase58Check(psz, vchTemp);
if (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; }
};
};
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 {
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;
ret.Set(&vchData[0], &vchData[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());
}

259
src/base58.h

@ -15,17 +15,12 @@
#define BITCOIN_BASE58_H #define BITCOIN_BASE58_H
#include "chainparams.h" #include "chainparams.h"
#include "hash.h"
#include "key.h" #include "key.h"
#include "script.h" #include "script.h"
#include "uint256.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
/** /**
* Encode a byte sequence as a base58-encoded string. * Encode a byte sequence as a base58-encoded string.
* pbegin and pend cannot be NULL, unless both are. * pbegin and pend cannot be NULL, unless both are.
@ -35,10 +30,7 @@ std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
/** /**
* Encode a byte vector as a base58-encoded string * Encode a byte vector as a base58-encoded string
*/ */
inline std::string EncodeBase58(const std::vector<unsigned char>& vch) std::string EncodeBase58(const std::vector<unsigned char>& vch);
{
return EncodeBase58(&vch[0], &vch[0] + vch.size());
}
/** /**
* Decode a base58-encoded string (psz) into a byte vector (vchRet). * Decode a base58-encoded string (psz) into a byte vector (vchRet).
@ -51,55 +43,24 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet);
* Decode a base58-encoded string (str) into a byte vector (vchRet). * Decode a base58-encoded string (str) into a byte vector (vchRet).
* return true if decoding is successful. * return true if decoding is successful.
*/ */
inline bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet) bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet);
{
return DecodeBase58(str.c_str(), vchRet);
}
/** /**
* Encode a byte vector into a base58-encoded string, including checksum * Encode a byte vector into a base58-encoded string, including checksum
*/ */
inline std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) 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);
}
/** /**
* Decode a base58-encoded string (psz) that includes a checksum into a byte * Decode a base58-encoded string (psz) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful * vector (vchRet), return true if decoding is successful
*/ */
inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet);
{
if (!DecodeBase58(psz, vchRet))
return false;
if (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;
}
/** /**
* Decode a base58-encoded string (str) that includes a checksum into a byte * Decode a base58-encoded string (str) that includes a checksum into a byte
* vector (vchRet), return true if decoding is successful * vector (vchRet), return true if decoding is successful
*/ */
inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet) inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet);
{
return DecodeBase58Check(str.c_str(), vchRet);
}
/** /**
* Base class for all base58-encoded data * Base class for all base58-encoded data
@ -114,64 +75,15 @@ protected:
typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar; typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar;
vector_uchar vchData; vector_uchar vchData;
CBase58Data() CBase58Data();
{ void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize);
vchVersion.clear(); void SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend);
vchData.clear();
}
void 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 SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend)
{
SetData(vchVersionIn, (void*)pbegin, pend - pbegin);
}
public: public:
bool SetString(const char* psz, unsigned int nVersionBytes = 1) bool SetString(const char* psz, unsigned int nVersionBytes = 1);
{ bool SetString(const std::string& str);
std::vector<unsigned char> vchTemp; std::string ToString() const;
DecodeBase58Check(psz, vchTemp); int CompareTo(const CBase58Data& b58) const;
if (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 SetString(const std::string& str)
{
return SetString(str.c_str());
}
std::string ToString() const
{
std::vector<unsigned char> vch = vchVersion;
vch.insert(vch.end(), vchData.begin(), vchData.end());
return EncodeBase58Check(vch);
}
int 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;
}
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; }
@ -186,140 +98,37 @@ public:
* Script-hash-addresses have version 5 (or 196 testnet). * Script-hash-addresses have version 5 (or 196 testnet).
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
*/ */
class CBitcoinAddress; class CBitcoinAddress : public CBase58Data {
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
{
private:
CBitcoinAddress *addr;
public: public:
CBitcoinAddressVisitor(CBitcoinAddress *addrIn) : addr(addrIn) { } bool Set(const CKeyID &id);
bool operator()(const CKeyID &id) const; bool Set(const CScriptID &id);
bool operator()(const CScriptID &id) const; bool Set(const CTxDestination &dest);
bool operator()(const CNoDestination &no) const; bool IsValid() const;
CBitcoinAddress() {}
CBitcoinAddress(const CTxDestination &dest) { Set(dest); }
CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); }
CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); }
CTxDestination Get() const;
bool GetKeyID(CKeyID &keyID) const;
bool IsScript() const;
}; };
class CBitcoinAddress : public CBase58Data
{
public:
bool Set(const CKeyID &id) {
SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
return true;
}
bool Set(const CScriptID &id) {
SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
return true;
}
bool Set(const CTxDestination &dest)
{
return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
}
bool IsValid() const
{
bool fCorrectSize = vchData.size() == 20;
bool fKnownVersion = vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
return fCorrectSize && fKnownVersion;
}
CBitcoinAddress()
{
}
CBitcoinAddress(const CTxDestination &dest)
{
Set(dest);
}
CBitcoinAddress(const std::string& strAddress)
{
SetString(strAddress);
}
CBitcoinAddress(const char* pszAddress)
{
SetString(pszAddress);
}
CTxDestination 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 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 IsScript() const {
return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
}
};
bool inline CBitcoinAddressVisitor::operator()(const CKeyID &id) const { return addr->Set(id); }
bool inline CBitcoinAddressVisitor::operator()(const CScriptID &id) const { return addr->Set(id); }
bool inline CBitcoinAddressVisitor::operator()(const CNoDestination &id) const { return false; }
/** /**
* A base58-encoded secret key * A base58-encoded secret key
*/ */
class CBitcoinSecret : public CBase58Data class CBitcoinSecret : public CBase58Data
{ {
public: public:
void SetKey(const CKey& vchSecret) void SetKey(const CKey& vchSecret);
{ CKey GetKey();
assert(vchSecret.IsValid()); bool IsValid() const;
SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size()); bool SetString(const char* pszSecret);
if (vchSecret.IsCompressed()) bool SetString(const std::string& strSecret);
vchData.push_back(1);
} CBitcoinSecret(const CKey& vchSecret) { SetKey(vchSecret); }
CBitcoinSecret() {}
CKey GetKey()
{
CKey ret;
ret.Set(&vchData[0], &vchData[32], vchData.size() > 32 && vchData[32] == 1);
return ret;
}
bool 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 SetString(const char* pszSecret)
{
return CBase58Data::SetString(pszSecret) && IsValid();
}
bool SetString(const std::string& strSecret)
{
return SetString(strSecret.c_str());
}
CBitcoinSecret(const CKey& vchSecret)
{
SetKey(vchSecret);
}
CBitcoinSecret()
{
}
}; };
template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data

4
src/test/base58_tests.cpp

@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
continue; continue;
} }
CBitcoinAddress addrOut; CBitcoinAddress addrOut;
BOOST_CHECK_MESSAGE(boost::apply_visitor(CBitcoinAddressVisitor(&addrOut), dest), "encode dest: " + strTest); BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest);
BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest);
} }
} }
@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
// Visiting a CNoDestination must fail // Visiting a CNoDestination must fail
CBitcoinAddress dummyAddr; CBitcoinAddress dummyAddr;
CTxDestination nodest = CNoDestination(); CTxDestination nodest = CNoDestination();
BOOST_CHECK(!boost::apply_visitor(CBitcoinAddressVisitor(&dummyAddr), nodest)); BOOST_CHECK(!dummyAddr.Set(nodest));
SelectParams(CChainParams::MAIN); SelectParams(CChainParams::MAIN);
} }

Loading…
Cancel
Save