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:
parent
c7346e802e
commit
94ab8e4c77
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue