Move parsing of unified addresses to UnifiedAddress.

In cases where we want to be able to decode a string that
is known to be a unified address, it doesn't make sense to have
to route through KeyIO::DecodePaymentAddress and then return
an error depending upon the result type, when it's possible to
provide unified address parsing more directly.

KeyIO::DecodePaymentAddress has been modified to delegate
to UnifiedAddress::Parse
This commit is contained in:
Kris Nuttycombe 2022-02-17 18:27:15 -07:00
parent 73a75a37fe
commit d08c992b5e
5 changed files with 109 additions and 102 deletions

View File

@ -247,7 +247,7 @@ const size_t ConvertedSaplingExtendedFullViewingKeySize = (ZIP32_XFVK_SIZE * 8 +
const size_t ConvertedSaplingExtendedSpendingKeySize = (ZIP32_XSK_SIZE * 8 + 4) / 5;
} // namespace
CTxDestination KeyIO::DecodeDestination(const std::string& str)
CTxDestination KeyIO::DecodeDestination(const std::string& str) const
{
std::vector<unsigned char> data;
uint160 hash;
@ -271,7 +271,7 @@ CTxDestination KeyIO::DecodeDestination(const std::string& str)
return CNoDestination();
};
CKey KeyIO::DecodeSecret(const std::string& str)
CKey KeyIO::DecodeSecret(const std::string& str) const
{
CKey key;
std::vector<unsigned char> data;
@ -287,7 +287,7 @@ CKey KeyIO::DecodeSecret(const std::string& str)
return key;
}
std::string KeyIO::EncodeSecret(const CKey& key)
std::string KeyIO::EncodeSecret(const CKey& key) const
{
assert(key.IsValid());
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::SECRET_KEY);
@ -300,7 +300,7 @@ std::string KeyIO::EncodeSecret(const CKey& key)
return ret;
}
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str) const
{
CExtPubKey key;
std::vector<unsigned char> data;
@ -313,7 +313,7 @@ CExtPubKey KeyIO::DecodeExtPubKey(const std::string& str)
return key;
}
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key) const
{
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_PUBLIC_KEY);
size_t size = data.size();
@ -323,7 +323,7 @@ std::string KeyIO::EncodeExtPubKey(const CExtPubKey& key)
return ret;
}
CExtKey KeyIO::DecodeExtKey(const std::string& str)
CExtKey KeyIO::DecodeExtKey(const std::string& str) const
{
CExtKey key;
std::vector<unsigned char> data;
@ -336,7 +336,7 @@ CExtKey KeyIO::DecodeExtKey(const std::string& str)
return key;
}
std::string KeyIO::EncodeExtKey(const CExtKey& key)
std::string KeyIO::EncodeExtKey(const CExtKey& key) const
{
std::vector<unsigned char> data = keyConstants.Base58Prefix(KeyConstants::EXT_SECRET_KEY);
size_t size = data.size();
@ -347,17 +347,17 @@ std::string KeyIO::EncodeExtKey(const CExtKey& key)
return ret;
}
std::string KeyIO::EncodeDestination(const CTxDestination& dest)
std::string KeyIO::EncodeDestination(const CTxDestination& dest) const
{
return std::visit(DestinationEncoder(keyConstants), dest);
}
bool KeyIO::IsValidDestinationString(const std::string& str)
bool KeyIO::IsValidDestinationString(const std::string& str) const
{
return IsValidDestination(DecodeDestination(str));
}
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr)
std::string KeyIO::EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const
{
return std::visit(PaymentAddressEncoder(keyConstants), zaddr);
}
@ -433,71 +433,12 @@ std::optional<T1> DecodeAny(
return std::nullopt;
}
/**
* `raw` MUST be 43 bytes.
*/
static bool AddSaplingReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 43),
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::SaplingPaymentAddress receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CScriptID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CKeyID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len)
{
libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len));
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str)
std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::string& str) const
{
// Try parsing as a Unified Address.
libzcash::UnifiedAddress ua;
if (zcash_address_parse_unified(
str.c_str(),
keyConstants.NetworkIDString().c_str(),
&ua,
AddSaplingReceiver,
AddP2SHReceiver,
AddP2PKHReceiver,
AddUnknownReceiver)
) {
return ua;
auto ua = libzcash::UnifiedAddress::Parse(keyConstants, str);
if (ua.has_value()) {
return ua.value();
}
// Try parsing as a Sapling address
@ -535,16 +476,17 @@ std::optional<libzcash::PaymentAddress> KeyIO::DecodePaymentAddress(const std::s
}, DecodeDestination(str));
}
bool KeyIO::IsValidPaymentAddressString(const std::string& str) {
bool KeyIO::IsValidPaymentAddressString(const std::string& str) const
{
return DecodePaymentAddress(str).has_value();
}
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk)
std::string KeyIO::EncodeViewingKey(const libzcash::ViewingKey& vk) const
{
return std::visit(ViewingKeyEncoder(keyConstants), vk);
}
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str)
std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& str) const
{
// Try parsing as a Unified full viewing key
auto ufvk = libzcash::UnifiedFullViewingKey::Decode(str, keyConstants);
@ -563,12 +505,12 @@ std::optional<libzcash::ViewingKey> KeyIO::DecodeViewingKey(const std::string& s
);
}
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey)
std::string KeyIO::EncodeSpendingKey(const libzcash::SpendingKey& zkey) const
{
return std::visit(SpendingKeyEncoder(keyConstants), zkey);
}
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str)
std::optional<libzcash::SpendingKey> KeyIO::DecodeSpendingKey(const std::string& str) const
{
return DecodeAny<libzcash::SpendingKey,

View File

@ -23,28 +23,28 @@ private:
public:
KeyIO(const KeyConstants& keyConstants): keyConstants(keyConstants) { }
CKey DecodeSecret(const std::string& str);
std::string EncodeSecret(const CKey& key);
CKey DecodeSecret(const std::string& str) const;
std::string EncodeSecret(const CKey& key) const;
CExtKey DecodeExtKey(const std::string& str);
std::string EncodeExtKey(const CExtKey& extkey);
CExtPubKey DecodeExtPubKey(const std::string& str);
std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
CExtKey DecodeExtKey(const std::string& str) const;
std::string EncodeExtKey(const CExtKey& extkey) const;
CExtPubKey DecodeExtPubKey(const std::string& str) const;
std::string EncodeExtPubKey(const CExtPubKey& extpubkey) const;
std::string EncodeDestination(const CTxDestination& dest);
CTxDestination DecodeDestination(const std::string& str);
std::string EncodeDestination(const CTxDestination& dest) const;
CTxDestination DecodeDestination(const std::string& str) const;
bool IsValidDestinationString(const std::string& str);
bool IsValidDestinationString(const std::string& str) const;
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr);
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str);
bool IsValidPaymentAddressString(const std::string& str);
std::string EncodePaymentAddress(const libzcash::PaymentAddress& zaddr) const;
std::optional<libzcash::PaymentAddress> DecodePaymentAddress(const std::string& str) const;
bool IsValidPaymentAddressString(const std::string& str) const;
std::string EncodeViewingKey(const libzcash::ViewingKey& vk);
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str);
std::string EncodeViewingKey(const libzcash::ViewingKey& vk) const;
std::optional<libzcash::ViewingKey> DecodeViewingKey(const std::string& str) const;
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey);
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str);
std::string EncodeSpendingKey(const libzcash::SpendingKey& zkey) const;
std::optional<libzcash::SpendingKey> DecodeSpendingKey(const std::string& str) const;
};
#endif // BITCOIN_KEY_IO_H

View File

@ -864,16 +864,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
ssKey >> recipient;
ssValue >> rawUa;
auto pa = keyIO.DecodePaymentAddress(rawUa);
if (!pa.has_value()) {
auto ua = libzcash::UnifiedAddress::Parse(Params(), rawUa);
if (!ua.has_value()) {
strErr = "Error in wallet database: non-UnifiedAddress in recipientmapping";
return false;
}
auto uaPtr = std::get_if<libzcash::UnifiedAddress>(&pa.value());
if (uaPtr == nullptr) {
strErr = "Error in wallet database: failed to deserialize unified address";
return false;
}
libzcash::Receiver recipientReceiver;
std::visit(match {
@ -883,7 +878,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
}, recipient.recipient);
bool found = false;
for (const auto& receiver : uaPtr->GetReceiversAsParsed()) {
for (const auto& receiver : ua.value().GetReceiversAsParsed()) {
if (receiver == recipientReceiver) {
found = true;
break;

View File

@ -16,6 +16,74 @@ namespace libzcash {
// Unified Addresses
//
/**
* `raw` MUST be 43 bytes.
*/
static bool AddSaplingReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 43),
SER_NETWORK,
PROTOCOL_VERSION);
libzcash::SaplingPaymentAddress receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2SHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CScriptID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
/**
* `raw` MUST be 20 bytes.
*/
static bool AddP2PKHReceiver(void* ua, const unsigned char* raw)
{
CDataStream ss(
reinterpret_cast<const char*>(raw),
reinterpret_cast<const char*>(raw + 20),
SER_NETWORK,
PROTOCOL_VERSION);
CKeyID receiver;
ss >> receiver;
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
static bool AddUnknownReceiver(void* ua, uint32_t typecode, const unsigned char* data, size_t len)
{
libzcash::UnknownReceiver receiver(typecode, std::vector(data, data + len));
return reinterpret_cast<libzcash::UnifiedAddress*>(ua)->AddReceiver(receiver);
}
std::optional<UnifiedAddress> UnifiedAddress::Parse(const KeyConstants& keyConstants, const std::string& str) {
libzcash::UnifiedAddress ua;
if (zcash_address_parse_unified(
str.c_str(),
keyConstants.NetworkIDString().c_str(),
&ua,
AddSaplingReceiver,
AddP2SHReceiver,
AddP2PKHReceiver,
AddUnknownReceiver)
) {
return ua;
} else {
return std::nullopt;
}
}
std::vector<const Receiver*> UnifiedAddress::GetSorted() const {
std::vector<const libzcash::Receiver*> sorted;
for (const auto& receiver : receivers) {

View File

@ -66,6 +66,8 @@ class UnifiedAddress {
public:
UnifiedAddress() {}
static std::optional<UnifiedAddress> Parse(const KeyConstants& keyConstants, const std::string& str);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>