Skip to content

Commit

Permalink
Merge #2414: Finish address encoding cleanup
Browse files Browse the repository at this point in the history
a470c34 Backport attributes.h and connect it to base58.h functions only (practicalswift)
0247f6f util: Don't allow base58-decoding of std::string:s containing non-base58 characters (practicalswift)
70c480c tests: Add tests for base58-decoding of std::string:s containing non-base58 characters (practicalswift)
9d481be Add bounds checks in key_io before DecodeBase58Check (Pieter Wuille)
eac71b5 Finish Encode/Decode destination functions move from base58 to key_io. (furszy)
2e9376c Pass a maximum output length to DecodeBase58 and DecodeBase58Check (Pieter Wuille)
c93e19f Clean duplicate usage of DecodeSecret & EncodeSecret. (furszy)
4d4160e Stop using CBase58Data for ext keys (furszy)
e861cda Backport string ToUpper and ToLower. (furszy)
f6c2872 util: Add Join helper to join a list of strings (MarcoFalke)
32c1e42 Add tests for util/vector.h's Cat and Vector (Pieter Wuille)
dc42563 Add some general std::vector utility functions (Pieter Wuille)

Pull request description:

  Decoupled from #2411 Tor's v3 addr support, built on top of #2359.

  This PR finishes the address encoding cleanup, removing the `CBase58`, `CBitcoinSecret`, `CBitcoinExtKey`, and `CBitcoinExtPubKey` classes, in favor of using the KeyIO::Encode/Decode functions. Furthermore, all PIVX-specific address logic is moved to key_io.{h,cpp}, leaving base58.{h,cpp} as a pure utility that implements the base58 encoding/decoding logic.
  Plus, includes some general utility functions for std::vector and std::string.

  Adaptation of the following PRs:

  *  bitcoin#11372.
  * bitcoin#16670. (without faebf62)
  *  bitcoin#16889.
  *  bitcoin#17511.
  *  bitcoin#17721.

ACKs for top commit:
  random-zebra:
    rebase utACK a470c34
  Fuzzbawls:
    ACK a470c34

Tree-SHA512: 7a3e1ea0f86c7dab960a5761a666dc7eb291d749e1e9cc24583eec2d6114ca47bc6b9ad50c1c7ff2ecba7f3f60100ce7c0ee8522dc3a2f29d6d79cb052187e0d
  • Loading branch information
furszy committed Jul 1, 2021
2 parents ad5f3e1 + a470c34 commit 170beab
Show file tree
Hide file tree
Showing 54 changed files with 548 additions and 409 deletions.
6 changes: 5 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ BITCOIN_CORE_H = \
activemasternode.h \
addrdb.h \
addrman.h \
attributes.h \
arith_uint256.h \
amount.h \
base58.h \
Expand Down Expand Up @@ -286,10 +287,12 @@ BITCOIN_CORE_H = \
util/memory.h \
util/system.h \
util/macros.h \
util/string.h \
util/threadnames.h \
utilstrencodings.h \
utilmoneystr.h \
utiltime.h \
util/vector.h \
validation.h \
validationinterface.h \
version.h \
Expand Down Expand Up @@ -394,7 +397,6 @@ libbitcoin_wallet_a_SOURCES = \
interfaces/wallet.cpp \
addressbook.cpp \
crypter.cpp \
key_io.cpp \
legacy/stakemodifier.cpp \
kernel.cpp \
wallet/db.cpp \
Expand Down Expand Up @@ -496,6 +498,7 @@ libbitcoin_common_a_SOURCES = \
coins.cpp \
compressor.cpp \
consensus/merkle.cpp \
key_io.cpp \
primitives/block.cpp \
primitives/transaction.cpp \
zpiv/zerocoin.cpp \
Expand Down Expand Up @@ -557,6 +560,7 @@ libbitcoin_util_a_SOURCES = \
utilmoneystr.cpp \
util/threadnames.cpp \
utilstrencodings.cpp \
util/string.cpp \
utiltime.cpp \
$(BITCOIN_CORE_H) \
$(LIBSAPLING_H)
Expand Down
22 changes: 22 additions & 0 deletions src/attributes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_ATTRIBUTES_H
#define BITCOIN_ATTRIBUTES_H

#if defined(__has_cpp_attribute)
# if __has_cpp_attribute(nodiscard)
# define NODISCARD [[nodiscard]]
# endif
#endif
#ifndef NODISCARD
# if defined(_MSC_VER) && _MSC_VER >= 1700
# define NODISCARD _Check_return_
# else
# define NODISCARD __attribute__((warn_unused_result))
# endif
#endif

#endif // BITCOIN_ATTRIBUTES_H
217 changes: 19 additions & 198 deletions src/base58.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@
#include "base58.h"

#include "hash.h"
#include "script/script.h"
#include "uint256.h"
#include "util/string.h"

#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
#include "uint256.h"

#include <algorithm>
#include <assert.h>
#include <sstream>
#include <vector>

#include <limits>

/** 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)
bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len)
{
// Skip leading spaces.
while (*psz && isspace(*psz))
Expand All @@ -30,6 +30,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
int length = 0;
while (*psz == '1') {
zeroes++;
if (zeroes > max_ret_len) return false;
psz++;
}
// Allocate enough space in big-endian base256 representation.
Expand All @@ -51,6 +52,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
}
assert(carry == 0);
length = i;
if (length + zeroes > max_ret_len) return false;
psz++;
}
// Skip trailing spaces.
Expand All @@ -60,8 +62,6 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
return false;
// Skip leading zeroes in b256.
std::vector<unsigned char>::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);
Expand All @@ -70,10 +70,10 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
return true;
}

std::string DecodeBase58(const char* psz)
std::string DecodeBase58(const char* psz, int max_ret_len)
{
std::vector<unsigned char> vch;
DecodeBase58(psz, vch);
DecodeBase58(psz, vch, max_ret_len);
std::stringstream ss;
ss << std::hex;

Expand Down Expand Up @@ -130,9 +130,12 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch)
return EncodeBase58(vch.data(), vch.data() + vch.size());
}

bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len)
{
return DecodeBase58(str.c_str(), vchRet);
if (!ValidAsCString(str)) {
return false;
}
return DecodeBase58(str.c_str(), vchRet, max_ret_len);
}

std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
Expand All @@ -144,9 +147,9 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
return EncodeBase58(vch);
}

bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len)
{
if (!DecodeBase58(psz, vchRet) ||
if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) ||
(vchRet.size() < 4)) {
vchRet.clear();
return false;
Expand All @@ -161,192 +164,10 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
return true;
}

bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret)
{
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.data(), 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();
if (!ValidAsCString(str)) {
return false;
}
vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
vchData.resize(vchTemp.size() - nVersionBytes);
if (!vchData.empty())
memcpy(vchData.data(), vchTemp.data() + nVersionBytes, vchData.size());
memory_cleanse(vchTemp.data(), vchTemp.size());
return true;
}

bool CBase58Data::SetString(const std::string& str)
{
if (str.empty())
return false;
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 DestinationEncoder : public boost::static_visitor<std::string>
{
private:
const CChainParams& m_params;
const CChainParams::Base58Type m_addrType;

public:
DestinationEncoder(const CChainParams& params, const CChainParams::Base58Type _addrType = CChainParams::PUBKEY_ADDRESS) : m_params(params), m_addrType(_addrType) {}

std::string operator()(const CKeyID& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(m_addrType);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}

std::string operator()(const CScriptID& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}

std::string operator()(const CNoDestination& no) const { return ""; }
};

CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, bool& isStaking)
{
std::vector<unsigned char> data;
uint160 hash;
if (DecodeBase58Check(str, data)) {
// base58-encoded PIVX addresses.
// Public-key-hash-addresses have version 30 (or 139 testnet).
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
return CKeyID(hash);
}
// Public-key-hash-coldstaking-addresses have version 63 (or 73 testnet).
const std::vector<unsigned char>& staking_prefix = params.Base58Prefix(CChainParams::STAKING_ADDRESS);
if (data.size() == hash.size() + staking_prefix.size() && std::equal(staking_prefix.begin(), staking_prefix.end(), data.begin())) {
isStaking = true;
std::copy(data.begin() + staking_prefix.size(), data.end(), hash.begin());
return CKeyID(hash);
}
// Script-hash-addresses have version 13 (or 19 testnet).
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
return CScriptID(hash);
}
}
return CNoDestination();
}

} // anon namespace

CKey DecodeSecret(const std::string& str)
{
CKey key;
std::vector<unsigned char> data;
if (DecodeBase58Check(str, data)) {
const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY);
if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) &&
std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) {
bool compressed = data.size() == 33 + privkey_prefix.size();
key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed);
}
}
memory_cleanse(data.data(), data.size());
return key;
}

std::string EncodeSecret(const CKey& key)
{
assert(key.IsValid());
std::vector<unsigned char> data = Params().Base58Prefix(CChainParams::SECRET_KEY);
data.insert(data.end(), key.begin(), key.end());
if (key.IsCompressed()) {
data.push_back(1);
}
std::string ret = EncodeBase58Check(data);
memory_cleanse(data.data(), data.size());
return ret;
}

std::string EncodeDestination(const CTxDestination& dest, bool isStaking)
{
return EncodeDestination(dest, isStaking ? CChainParams::STAKING_ADDRESS : CChainParams::PUBKEY_ADDRESS);
}

std::string EncodeDestination(const CTxDestination& dest, const CChainParams::Base58Type addrType)
{
return boost::apply_visitor(DestinationEncoder(Params(), addrType), dest);
}

CTxDestination DecodeDestination(const std::string& str)
{
bool isStaking;
return DecodeDestination(str, Params(), isStaking);
}

CTxDestination DecodeDestination(const std::string& str, bool& isStaking)
{
return DecodeDestination(str, Params(), isStaking);
}

bool IsValidDestinationString(const std::string& str, bool fStaking, const CChainParams& params)
{
bool isStaking = false;
return IsValidDestination(DecodeDestination(str, params, isStaking)) && (isStaking == fStaking);
}

bool IsValidDestinationString(const std::string& str, bool isStaking)
{
return IsValidDestinationString(str, isStaking, Params());
return DecodeBase58Check(str.c_str(), vchRet, max_ret);
}
Loading

0 comments on commit 170beab

Please sign in to comment.