Implement read and write for (txid, recipient) -> ua mapping.

Co-authored-by: Kris Nuttycombe <kris@nutty.land>
Co-authored-by: Jack Grigg <jack@z.cash>
This commit is contained in:
therealyingtong 2022-02-15 09:48:39 +08:00
parent c7346e802e
commit 94ab8e4c77
6 changed files with 142 additions and 0 deletions

View File

@ -13,6 +13,11 @@ UniValue SendTransaction(const CTransaction& tx, const std::vector<SendManyRecip
// Send the transaction
if (!testmode) {
CWalletTx wtx(pwalletMain, tx);
// save the mapping from (receiver, txid) to UA
if (!pwalletMain->SaveRecipientMappings(tx.GetHash(), recipients)) {
// More details in debug.log
throw JSONRPCError(RPC_WALLET_ERROR, "SendTransaction: SaveRecipientMappings failed");
}
if (!pwalletMain->CommitTransaction(wtx, reservekey)) {
// More details in debug.log
throw JSONRPCError(RPC_WALLET_ERROR, "SendTransaction: CommitTransaction failed");

View File

@ -4845,6 +4845,31 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
return true;
}
/**
* @brief
*
*/
bool CWallet::SaveRecipientMappings(const uint256& txid, const std::vector<SendManyRecipient>& recipients)
{
{
LOCK2(cs_main, cs_wallet);
LogPrintf("SaveRecipientMappings:\n%s", txid.ToString());
for (const SendManyRecipient& recipient : recipients)
{
if (recipient.ua.has_value()) {
CWalletDB(strWalletFile).WriteRecipientMapping(
txid,
recipient.address,
recipient.ua.value()
);
}
}
}
return true;
}
/**
* Call after CreateTransaction unless you want to abort
*/

View File

@ -1528,6 +1528,7 @@ public:
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet,
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
bool SaveRecipientMappings(const uint256& txid, const std::vector<SendManyRecipient>& recipients);
bool CommitTransaction(CWalletTx& wtxNew, std::optional<std::reference_wrapper<CReserveKey>> reservekey);
static CFeeRate minTxFee;

View File

@ -12,6 +12,7 @@
#include "proof_verifier.h"
#include "protocol.h"
#include "serialize.h"
#include "script/standard.h"
#include "sync.h"
#include "util.h"
#include "utiltime.h"
@ -313,6 +314,13 @@ bool CWalletDB::WriteMinVersion(int nVersion)
return Write(std::string("minversion"), nVersion);
}
bool CWalletDB::WriteRecipientMapping(const uint256& txid, const libzcash::RecipientAddress& address, const libzcash::UnifiedAddress& ua)
{
std::pair<uint256, CSerializeRecipientAddress> key = std::make_pair(txid, CSerializeRecipientAddress(address));
std::string uaString = KeyIO(Params()).EncodePaymentAddress(ua);
return Write(std::make_pair(std::string("recipientmapping"), key), uaString);
}
class CWalletScanState {
public:
unsigned int nKeys;
@ -847,6 +855,45 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
return false;
}
}
else if (strType == "recipientmapping")
{
uint256 txid;
CSerializeRecipientAddress recipient;
std::string rawUa;
ssKey >> txid;
ssKey >> recipient;
ssValue >> rawUa;
auto pa = keyIO.DecodePaymentAddress(rawUa);
if (!pa.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 {
[&](const CKeyID& key) { recipientReceiver = key; },
[&](const CScriptID& scriptId) { recipientReceiver = scriptId; },
[&](const libzcash::SaplingPaymentAddress& addr) { recipientReceiver = addr; }
}, recipient.recipient);
bool found = false;
for (const auto& receiver : uaPtr->GetReceiversAsParsed()) {
if (receiver == recipientReceiver) {
found = true;
break;
}
}
if (!found) {
strErr = "Error in wallet database: recipientmapping UA does not contain recipient";
return false;
}
}
} catch (...)
{
return false;

View File

@ -314,6 +314,61 @@ public:
}
};
// Serialization wrapper for reading and writing RecipientAddress
// in CompactSize format.
class CSerializeRecipientAddress {
libzcash::ReceiverType typecode;
public:
libzcash::RecipientAddress recipient;
CSerializeRecipientAddress() {} // for serialization only
CSerializeRecipientAddress(libzcash::RecipientAddress recipient): recipient(recipient) {}
template<typename Stream>
void Serialize(Stream& s) const {
std::visit(match {
[&](const CKeyID& keyId) {
ReceiverTypeSer(libzcash::ReceiverType::P2PKH).Serialize(s);
s << keyId;
},
[&](const CScriptID& scriptId) {
ReceiverTypeSer(libzcash::ReceiverType::P2SH).Serialize(s);
s << scriptId;
},
[&](const libzcash::SaplingPaymentAddress& saplingAddr) {
ReceiverTypeSer(libzcash::ReceiverType::Sapling).Serialize(s);
s << saplingAddr;
}
}, recipient);
}
template<typename Stream>
void Unserialize(Stream& s) {
// This cast is fine because ZIP 316 uses CompactSize serialization including the
// size limit, which means it is at most a uint32_t.
typecode = (libzcash::ReceiverType) ReadCompactSize(s);
switch (typecode) {
case libzcash::ReceiverType::P2PKH: {
CKeyID key;
s >> key;
recipient = key;
break;
}
case libzcash::ReceiverType::P2SH: {
CScriptID script;
s >> script;
recipient = script;
break;
}
case libzcash::ReceiverType::Sapling: {
libzcash::SaplingPaymentAddress saplingAddr;
s >> saplingAddr;
recipient = saplingAddr;
break;
}
}
}
};
/** Access to the wallet database */
class CWalletDB : public CDB
@ -356,6 +411,8 @@ public:
bool WriteMinVersion(int nVersion);
bool WriteRecipientMapping(const uint256& txid, const libzcash::RecipientAddress& address, const libzcash::UnifiedAddress& ua);
/// Write destination data key,value tuple to database
bool WriteDestData(const std::string &address, const std::string &key, const std::string &value);
/// Erase destination data tuple from wallet database

View File

@ -66,6 +66,13 @@ class UnifiedAddress {
public:
UnifiedAddress() {}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(receivers);
}
/**
* Adds the given receiver to this unified address.
*