Skip to content

Commit

Permalink
Auto merge of zcash#3228 - str4d:3058-taddr-encoding-refactor, r=str4d
Browse files Browse the repository at this point in the history
Refactor t-address encoding

Includes code cherry-picked from the following upstream PRs:

- bitcoin/bitcoin#11117
- bitcoin/bitcoin#11259
  - Only the second commit (first is for QT code)
- bitcoin/bitcoin#11167
  - Only the first commit (the rest are not part of the t-address
encoding refactor).

Part of zcash#3058. Precursor to zcash#3202.
  • Loading branch information
zkbot authored and mkjekk committed Aug 7, 2018
2 parents 2d1a3cf + c8f9c87 commit 78344bb
Show file tree
Hide file tree
Showing 23 changed files with 347 additions and 372 deletions.
145 changes: 66 additions & 79 deletions src/base58.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@
#include "base58.h"

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

#include "version.h"
#include "streams.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>

#include <algorithm>
#include <assert.h>
#include <string.h>


/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

Expand Down Expand Up @@ -206,89 +207,55 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const

namespace
{
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
class DestinationEncoder : public boost::static_visitor<std::string>
{
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

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;
}

bool CBitcoinAddress::SetString(const char* pszAddress)
{
return CBase58Data::SetString(pszAddress, 2);
}
DestinationEncoder(const CChainParams& params) : m_params(params) {}

bool CBitcoinAddress::SetString(const std::string& strAddress)
{
return SetString(strAddress.c_str());
}
std::string operator()(const CKeyID& id) const
{
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
data.insert(data.end(), id.begin(), id.end());
return EncodeBase58Check(data);
}

CTxDestination CBitcoinAddress::Get() const
{
if (!IsValid())
return CNoDestination();
uint160 id;
memcpy(&id, vchData.data(), 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();
}
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);
}

bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
{
if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
return false;
uint160 id;
memcpy(&id, vchData.data(), 20);
keyID = CKeyID(id);
return true;
}
std::string operator()(const CNoDestination& no) const { return ""; }
};

bool CBitcoinAddress::IsScript() const
{
return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
{
std::vector<unsigned char> data;
uint160 hash;
if (DecodeBase58Check(str, data)) {
// 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.
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);
}
// Script-hash-addresses have version 5 (or 196 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();
}
} // namespace

void CBitcoinSecret::SetKey(const CKey& vchSecret)
{
Expand Down Expand Up @@ -323,6 +290,26 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
return SetString(strSecret.c_str());
}

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

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

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

bool IsValidDestinationString(const std::string& str)
{
return IsValidDestinationString(str, Params());
}

template<class DATA_TYPE, CChainParams::Base58Type PREFIX, size_t SER_SIZE>
bool CZCEncoding<DATA_TYPE, PREFIX, SER_SIZE>::Set(const DATA_TYPE& addr)
{
Expand Down
32 changes: 5 additions & 27 deletions src/base58.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include "chainparams.h"
#include "key.h"
#include "pubkey.h"
#include "script/script.h"
#include "script/standard.h"
#include "support/allocators/zeroafterfree.h"
#include "zcash/Address.hpp"
Expand Down Expand Up @@ -140,32 +139,6 @@ class CZCSpendingKey : public CZCEncoding<libzcash::SpendingKey, CChainParams::Z
CZCSpendingKey(const libzcash::SpendingKey& addr) { Set(addr); }
};

/** 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 &params) const;
bool SetString(const char* pszSecret);
bool SetString(const std::string& strSecret);

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;
};

/**
* A base58-encoded secret key
*/
Expand Down Expand Up @@ -214,4 +187,9 @@ template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtK
typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> 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& params);

#endif // BITCOIN_BASE58_H
9 changes: 4 additions & 5 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn

// extract and validate ADDRESS
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
CBitcoinAddress addr(strAddr);
if (!addr.IsValid())
CTxDestination destination = DecodeDestination(strAddr);
if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");

// build standard output script via GetScriptForDestination()
CScript scriptPubKey = GetScriptForDestination(addr.Get());
}
CScript scriptPubKey = GetScriptForDestination(destination);

// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
Expand Down
8 changes: 4 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,10 +502,10 @@ std::string CChainParams::GetFoundersRewardAddressAtHeight(int nHeight) const {
CScript CChainParams::GetFoundersRewardScriptAtHeight(int nHeight) const {
assert(nHeight > 0 && nHeight <= consensus.GetLastFoundersRewardBlockHeight());

CBitcoinAddress address(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(address.IsValid());
assert(address.IsScript());
CScriptID scriptID = boost::get<CScriptID>(address.Get()); // Get() returns a boost variant
CTxDestination address = DecodeDestination(GetFoundersRewardAddressAtHeight(nHeight).c_str());
assert(IsValidDestination(address));
assert(boost::get<CScriptID>(&address) != nullptr);
CScriptID scriptID = boost::get<CScriptID>(address); // address is a boost variant
CScript script = CScript() << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL;
return script;
}
Expand Down
5 changes: 3 additions & 2 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("type", GetTxnOutputType(type));

UniValue a(UniValue::VARR);
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr));
}
out.pushKV("addresses", a);
}

Expand Down
2 changes: 1 addition & 1 deletion src/gtest/test_foundersreward.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
pWallet->AddCScript(result);
pWallet->SetAddressBook(innerID, "", "receive");

std::string address = CBitcoinAddress(innerID).ToString();
std::string address = EncodeDestination(innerID);
addresses.push_back(address);
}

Expand Down
9 changes: 4 additions & 5 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,8 +1079,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)

#ifdef ENABLE_MINING
if (mapArgs.count("-mineraddress")) {
CBitcoinAddress addr;
if (!addr.SetString(mapArgs["-mineraddress"])) {
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
if (!IsValidDestination(addr)) {
return InitError(strprintf(
_("Invalid address for -mineraddress=<addr>: '%s' (must be a transparent address)"),
mapArgs["-mineraddress"]));
Expand Down Expand Up @@ -1717,9 +1717,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
bool minerAddressInLocalWallet = false;
if (pwalletMain) {
// Address has alreday been validated
CBitcoinAddress addr(mapArgs["-mineraddress"]);
CKeyID keyID;
addr.GetKeyID(keyID);
CTxDestination addr = DecodeDestination(mapArgs["-mineraddress"]);
CKeyID keyID = boost::get<CKeyID>(addr);
minerAddressInLocalWallet = pwalletMain->HaveKey(keyID);
}
if (GetBoolArg("-minetolocalwallet", true) && !minerAddressInLocalWallet) {
Expand Down
6 changes: 3 additions & 3 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,9 @@ boost::optional<CScript> GetMinerScriptPubKey()
#endif
{
CKeyID keyID;
CBitcoinAddress addr;
if (addr.SetString(GetArg("-mineraddress", ""))) {
addr.GetKeyID(keyID);
CTxDestination addr = DecodeDestination(GetArg("-mineraddress", ""));
if (IsValidDestination(addr)) {
keyID = boost::get<CKeyID>(addr);
} else {
#ifdef ENABLE_WALLET
CPubKey pubkey;
Expand Down
Loading

0 comments on commit 78344bb

Please sign in to comment.