Use libzcash::PaymentAddress instead of std::string in mergetoaddress

This commit is contained in:
Kris Nuttycombe 2021-12-29 17:00:27 -07:00
parent eb91c7869a
commit 9702b47e2a
4 changed files with 72 additions and 77 deletions

View File

@ -68,7 +68,7 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
CAmount fee,
UniValue contextInfo) :
tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs),
saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo)
saplingNoteInputs_(saplingNoteInputs), memo_(recipient.second), fee_(fee), contextinfo_(contextInfo)
{
if (fee < 0 || fee > MAX_MONEY) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
@ -78,10 +78,6 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs");
}
if (std::get<0>(recipient).size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing");
}
if (sproutNoteInputs.size() > 0 && saplingNoteInputs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
}
@ -100,34 +96,29 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
isToTaddr_ = false;
isToZaddr_ = false;
auto address = keyIO.DecodePaymentAddress(std::get<0>(recipient));
if (address.has_value()) {
std::visit(match {
[&](const CKeyID& keyId) {
toTaddr_ = keyId;
isToTaddr_ = true;
},
[&](const CScriptID& scriptId) {
toTaddr_ = scriptId;
isToTaddr_ = true;
},
[&](const libzcash::SproutPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::SaplingPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::UnifiedAddress& addr) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"z_mergetoaddress does not yet support sending to unified addresses");
},
}, address.value());
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid recipient address");
}
std::visit(match {
[&](const CKeyID& keyId) {
toTaddr_ = keyId;
isToTaddr_ = true;
},
[&](const CScriptID& scriptId) {
toTaddr_ = scriptId;
isToTaddr_ = true;
},
[&](const libzcash::SproutPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::SaplingPaymentAddress& addr) {
toPaymentAddress_ = addr;
isToZaddr_ = true;
},
[&](const libzcash::UnifiedAddress& addr) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"z_mergetoaddress does not yet support sending to unified addresses");
},
}, recipient.first);
// Log the context info i.e. the call parameters to z_mergetoaddress
if (LogAcceptCategory("zrpcunsafe")) {
@ -350,9 +341,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToTaddr_) {
builder_.AddTransparentOutput(toTaddr_, sendAmount);
} else {
std::string zaddr = std::get<0>(recipient_);
std::string memo = std::get<1>(recipient_);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo);
std::array<unsigned char, ZC_MEMO_SIZE> hexMemo = get_memo_from_hex_string(memo_);
auto saplingPaymentAddress = std::get_if<libzcash::SaplingPaymentAddress>(&toPaymentAddress_);
if (saplingPaymentAddress == nullptr) {
// This should never happen as we have already determined that the payment is to sapling
@ -404,14 +393,11 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
* END SCENARIO #1
*/
// Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx(tx_);
ed25519_generate_keypair(&joinSplitPrivKey_, &joinSplitPubKey_);
mtx.joinSplitPubKey = joinSplitPubKey_;
tx_ = CTransaction(mtx);
std::string hexMemo = std::get<1>(recipient_);
/**
* SCENARIO #2
@ -427,8 +413,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
info.vpub_new = 0;
JSOutput jso = JSOutput(std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount);
if (hexMemo.size() > 0) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (memo_.size() > 0) {
jso.memo = get_memo_from_hex_string(memo_);
}
info.vjsout.push_back(jso);
@ -716,8 +702,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if (isToZaddr_ && vpubNewProcessed) {
outputType = "target";
jso.addr = std::get<libzcash::SproutPaymentAddress>(toPaymentAddress_);
if (!hexMemo.empty()) {
jso.memo = get_memo_from_hex_string(hexMemo);
if (!memo_.empty()) {
jso.memo = get_memo_from_hex_string(memo_);
}
}
info.vjsout.push_back(jso);

View File

@ -34,7 +34,7 @@ typedef std::tuple<JSOutPoint, SproutNote, CAmount, SproutSpendingKey> MergeToAd
typedef std::tuple<SaplingOutPoint, SaplingNote, CAmount, SaplingExpandedSpendingKey> MergeToAddressInputSaplingNote;
// A recipient is a tuple of address, memo (optional if zaddr)
typedef std::tuple<std::string, std::string> MergeToAddressRecipient;
typedef std::pair<libzcash::PaymentAddress, std::string> MergeToAddressRecipient;
// Package of info which is passed to perform_joinsplit methods.
struct MergeToAddressJSInfo {
@ -89,11 +89,11 @@ private:
uint32_t consensusBranchId_;
CAmount fee_;
int mindepth_;
MergeToAddressRecipient recipient_;
bool isToTaddr_;
bool isToZaddr_;
CTxDestination toTaddr_;
PaymentAddress toPaymentAddress_;
std::string memo_;
Ed25519VerificationKey joinSplitPubKey_;
Ed25519SigningKey joinSplitPrivKey_;

View File

@ -4487,20 +4487,32 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
useAnySapling = true;
isFromNonSprout = true;
} else {
CTxDestination taddr = keyIO.DecodeDestination(address);
if (IsValidDestination(taddr)) {
taddrs.insert(taddr);
isFromNonSprout = true;
} else {
auto zaddr = keyIO.DecodePaymentAddress(address);
if (zaddr.has_value()) {
zaddrs.push_back(zaddr.value());
if (std::holds_alternative<libzcash::SaplingPaymentAddress>(zaddr.value())) {
auto addr = keyIO.DecodePaymentAddress(address);
if (addr.has_value()) {
std::visit(match {
[&](const CKeyID& taddr) {
taddrs.insert(taddr);
isFromNonSprout = true;
},
[&](const CScriptID& taddr) {
taddrs.insert(taddr);
isFromNonSprout = true;
},
[&](const libzcash::SaplingPaymentAddress& zaddr) {
zaddrs.push_back(zaddr);
isFromNonSprout = true;
},
[&](const libzcash::SproutPaymentAddress& zaddr) {
zaddrs.push_back(zaddr);
},
[&](libzcash::UnifiedAddress) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Unified addresses are not supported in z_mergetoaddress");
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address);
}
}, addr.value());
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Unknown address format: ") + address);
}
}
@ -4522,12 +4534,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
const bool canopyActive = Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_CANOPY);
// Validate the destination address
auto destaddress = params[1].get_str();
auto destStr = params[1].get_str();
auto destaddress = keyIO.DecodePaymentAddress(destStr);
bool isToTaddr = false;
bool isToSproutZaddr = false;
bool isToSaplingZaddr = false;
auto paymentAddress = keyIO.DecodePaymentAddress(destaddress);
if (paymentAddress.has_value()) {
if (destaddress.has_value()) {
std::visit(match {
[&](CKeyID addr) {
isToTaddr = true;
@ -4546,11 +4558,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
isToSproutZaddr = true;
},
[&](libzcash::UnifiedAddress) {
// TODO UNIFIED
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Invalid parameter, unified addresses are not yet supported.");
}
}, paymentAddress.value());
}, destaddress.value());
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress );
throw JSONRPCError(
RPC_INVALID_PARAMETER,
string("Invalid parameter, unknown address format: ") + destStr);
}
if (canopyActive && isFromNonSprout && isToSproutZaddr) {
@ -4600,7 +4616,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
}
}
MergeToAddressRecipient recipient(destaddress, memo);
MergeToAddressRecipient recipient(destaddress.value(), memo);
// Prepare to get UTXOs and notes
std::vector<MergeToAddressInputUTXO> utxoInputs;
@ -4769,7 +4785,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
// - We only have one from address
// - It's equal to toaddress
// - The address only contains a single UTXO or note
if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) {
if (setAddress.size() == 1 && setAddress.count(destStr) && (numUtxos + numNotes) == 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged.");
}

View File

@ -1926,8 +1926,9 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1);
// Test constructor of AsyncRPCOperation_mergetoaddress
KeyIO keyIO(Params());
MergeToAddressRecipient testnetzaddr(
"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP",
keyIO.DecodePaymentAddress("ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP").value(),
"testnet memo");
try {
@ -1946,14 +1947,6 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters)
std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} };
try {
MergeToAddressRecipient badaddr("", "memo");
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(std::nullopt, mtx, inputs, {}, {}, badaddr, 1));
BOOST_FAIL("Should have caused an error");
} catch (const UniValue& objError) {
BOOST_CHECK( find_error(objError, "Recipient parameter missing"));
}
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs =
{MergeToAddressInputSproutNote{JSOutPoint(), SproutNote(), 0, SproutSpendingKey()}};
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs =
@ -1981,6 +1974,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
{
SelectParams(CBaseChainParams::TESTNET);
const Consensus::Params& consensusParams = Params().GetConsensus();
KeyIO keyIO(Params());
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -1994,10 +1988,9 @@ BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals)
// Add keys manually
BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress"));
MergeToAddressRecipient taddr1(retValue.get_str(), "");
MergeToAddressRecipient taddr1(keyIO.DecodePaymentAddress(retValue.get_str()).value(), "");
auto pa = pwalletMain->GenerateNewSproutZKey();
KeyIO keyIO(Params());
MergeToAddressRecipient zaddr1(keyIO.EncodePaymentAddress(pa), "DEADBEEF");
MergeToAddressRecipient zaddr1(pa, "DEADBEEF");
// Insufficient funds
{