This commit is contained in:
Duke Leto
2020-06-06 04:34:30 -04:00
parent 0cc4772c25
commit 22d4d1a06e
5 changed files with 31 additions and 140 deletions

View File

@@ -74,19 +74,17 @@ AsyncRPCOperation_mergetoaddress::AsyncRPCOperation_mergetoaddress(
boost::optional<TransactionBuilder> builder, boost::optional<TransactionBuilder> builder,
CMutableTransaction contextualTx, CMutableTransaction contextualTx,
std::vector<MergeToAddressInputUTXO> utxoInputs, std::vector<MergeToAddressInputUTXO> utxoInputs,
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs,
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs, std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs,
MergeToAddressRecipient recipient, MergeToAddressRecipient recipient,
CAmount fee, CAmount fee,
UniValue contextInfo) : UniValue contextInfo) :
tx_(contextualTx), utxoInputs_(utxoInputs), sproutNoteInputs_(sproutNoteInputs), tx_(contextualTx), utxoInputs_(utxoInputs), saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo)
saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), contextinfo_(contextInfo)
{ {
if (fee < 0 || fee > MAX_MONEY) { if (fee < 0 || fee > MAX_MONEY) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee is out of range");
} }
if (utxoInputs.empty() && sproutNoteInputs.empty() && saplingNoteInputs.empty()) { if (utxoInputs.empty() && saplingNoteInputs.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs"); throw JSONRPCError(RPC_INVALID_PARAMETER, "No inputs");
} }
@@ -94,14 +92,6 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context
throw JSONRPCError(RPC_INVALID_PARAMETER, "Recipient parameter missing"); 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");
}
if (sproutNoteInputs.size() > 0 && builder) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Sprout notes are not supported by the TransactionBuilder");
}
isUsingBuilder_ = false; isUsingBuilder_ = false;
if (builder) { if (builder) {
isUsingBuilder_ = true; isUsingBuilder_ = true;
@@ -215,7 +205,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
{ {
assert(isToTaddr_ != isToZaddr_); assert(isToTaddr_ != isToZaddr_);
bool isPureTaddrOnlyTx = (sproutNoteInputs_.empty() && saplingNoteInputs_.empty() && isToTaddr_); bool isPureTaddrOnlyTx = (saplingNoteInputs_.empty() && isToTaddr_);
CAmount minersFee = fee_; CAmount minersFee = fee_;
size_t numInputs = utxoInputs_.size(); size_t numInputs = utxoInputs_.size();
@@ -240,9 +230,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} }
CAmount z_inputs_total = 0; CAmount z_inputs_total = 0;
for (const MergeToAddressInputSproutNote& t : sproutNoteInputs_) {
z_inputs_total += std::get<2>(t);
}
for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) { for (const MergeToAddressInputSaplingNote& t : saplingNoteInputs_) {
z_inputs_total += std::get<2>(t); z_inputs_total += std::get<2>(t);
@@ -293,7 +280,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
/** /**
* SCENARIO #0 * SCENARIO #0
* *
* Sprout not involved, so we just use the TransactionBuilder and we're done. * Only sapling involved, so we just use the TransactionBuilder and we're done.
* *
* This is based on code from AsyncRPCOperation_sendmany::main_impl() and should be refactored. * This is based on code from AsyncRPCOperation_sendmany::main_impl() and should be refactored.
*/ */

View File

@@ -57,7 +57,6 @@ public:
boost::optional<TransactionBuilder> builder, boost::optional<TransactionBuilder> builder,
CMutableTransaction contextualTx, CMutableTransaction contextualTx,
std::vector<MergeToAddressInputUTXO> utxoInputs, std::vector<MergeToAddressInputUTXO> utxoInputs,
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs,
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs, std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs,
MergeToAddressRecipient recipient, MergeToAddressRecipient recipient,
CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE, CAmount fee = MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE,
@@ -94,9 +93,6 @@ private:
uint256 joinSplitPubKey_; uint256 joinSplitPubKey_;
unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES]; unsigned char joinSplitPrivKey_[crypto_sign_SECRETKEYBYTES];
// The key is the result string from calling JSOutPoint::ToString()
std::unordered_map<std::string, MergeToAddressWitnessAnchorData> jsopWitnessAnchorMap;
std::vector<MergeToAddressInputUTXO> utxoInputs_; std::vector<MergeToAddressInputUTXO> utxoInputs_;
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs_; std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs_;

View File

@@ -87,7 +87,6 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() {
return status; return status;
} }
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries; std::vector<SaplingNoteEntry> saplingEntries;
std::set<libzcash::SaplingPaymentAddress> addresses; std::set<libzcash::SaplingPaymentAddress> addresses;
{ {
@@ -95,7 +94,7 @@ bool AsyncRPCOperation_saplingconsolidation::main_impl() {
// We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying // We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying
// an anchor at height N-10 for each SpendDescription // an anchor at height N-10 for each SpendDescription
// Consider, should notes be sorted? // Consider, should notes be sorted?
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11); pwalletMain->GetFilteredNotes(saplingEntries, "", 11);
if (fConsolidationMapUsed) { if (fConsolidationMapUsed) {
const vector<string>& v = mapMultiArgs["-consolidatesaplingaddress"]; const vector<string>& v = mapMultiArgs["-consolidatesaplingaddress"];
for(int i = 0; i < v.size(); i++) { for(int i = 0; i < v.size(); i++) {

View File

@@ -248,18 +248,12 @@ bool AsyncRPCOperation_sendmany::main_impl() {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address."); throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address.");
} }
// At least one of z_sprout_inputs_ and z_sapling_inputs_ must be empty by design
assert(z_sprout_inputs_.empty() || z_sapling_inputs_.empty());
CAmount t_inputs_total = 0; CAmount t_inputs_total = 0;
for (SendManyInputUTXO & t : t_inputs_) { for (SendManyInputUTXO & t : t_inputs_) {
t_inputs_total += std::get<2>(t); t_inputs_total += std::get<2>(t);
} }
CAmount z_inputs_total = 0; CAmount z_inputs_total = 0;
for (SendManyInputJSOP & t : z_sprout_inputs_) {
z_inputs_total += std::get<2>(t);
}
for (auto t : z_sapling_inputs_) { for (auto t : z_sapling_inputs_) {
z_inputs_total += t.note.value(); z_inputs_total += t.note.value();
} }
@@ -687,20 +681,10 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
bool AsyncRPCOperation_sendmany::find_unspent_notes() { bool AsyncRPCOperation_sendmany::find_unspent_notes() {
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries; std::vector<SaplingNoteEntry> saplingEntries;
{ {
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress_, mindepth_); pwalletMain->GetFilteredNotes(saplingEntries, fromaddress_, mindepth_);
}
// If using the TransactionBuilder, we only want Sapling notes.
// If not using it, we only want Sprout notes.
// TODO: Refactor `GetFilteredNotes()` so we only fetch what we need.
if (isUsingBuilder_) {
sproutEntries.clear();
} else {
saplingEntries.clear();
} }
for (auto entry : saplingEntries) { for (auto entry : saplingEntries) {

View File

@@ -70,7 +70,6 @@ using namespace libzcash;
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; extern std::string ASSETCHAINS_OVERRIDE_PUBKEY;
const std::string ADDR_TYPE_SPROUT = "sprout";
const std::string ADDR_TYPE_SAPLING = "sapling"; const std::string ADDR_TYPE_SAPLING = "sapling";
extern UniValue TxJoinSplitToJSON(const CTransaction& tx); extern UniValue TxJoinSplitToJSON(const CTransaction& tx);
extern int32_t KOMODO_INSYNC; extern int32_t KOMODO_INSYNC;
@@ -3787,9 +3786,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk)
UniValue results(UniValue::VARR); UniValue results(UniValue::VARR);
if (zaddrs.size() > 0) { if (zaddrs.size() > 0) {
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries; std::vector<SaplingNoteEntry> saplingEntries;
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false); pwalletMain->GetFilteredNotes(saplingEntries, zaddrs, nMinDepth, nMaxDepth, true, !fIncludeWatchonly, false);
std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs); std::set<std::pair<PaymentAddress, uint256>> nullifierSet = pwalletMain->GetNullifiersForAddresses(zaddrs);
for (auto & entry : saplingEntries) { for (auto & entry : saplingEntries) {
@@ -4047,10 +4045,9 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign
CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) {
CAmount balance = 0; CAmount balance = 0;
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries; std::vector<SaplingNoteEntry> saplingEntries;
LOCK2(cs_main, pwalletMain->cs_wallet); LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, address, minDepth, true, ignoreUnspendable); pwalletMain->GetFilteredNotes(saplingEntries, address, minDepth, true, ignoreUnspendable);
for (auto & entry : saplingEntries) { for (auto & entry : saplingEntries) {
balance += CAmount(entry.note.value()); balance += CAmount(entry.note.value());
} }
@@ -4102,16 +4099,14 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp, const CPubK
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr.");
} }
// Visitor to support Sprout and Sapling addrs
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr) && if (!boost::apply_visitor(PaymentAddressBelongsToWallet(pwalletMain), zaddr) &&
!boost::apply_visitor(IncomingViewingKeyBelongsToWallet(pwalletMain), zaddr)) { !boost::apply_visitor(IncomingViewingKeyBelongsToWallet(pwalletMain), zaddr)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found.");
} }
UniValue result(UniValue::VARR); UniValue result(UniValue::VARR);
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries; std::vector<SaplingNoteEntry> saplingEntries;
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, fromaddress, nMinDepth, false, false); pwalletMain->GetFilteredNotes(saplingEntries, fromaddress, nMinDepth, false, false);
std::set<std::pair<PaymentAddress, uint256>> nullifierSet; std::set<std::pair<PaymentAddress, uint256>> nullifierSet;
auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr); auto hasSpendingKey = boost::apply_visitor(HaveSpendingKeyForPaymentAddress(pwalletMain), zaddr);
@@ -4223,7 +4218,7 @@ UniValue z_getnotescount(const UniValue& params, bool fHelp,const CPubKey& mypk)
"z_getnotescount\n" "z_getnotescount\n"
"\nArguments:\n" "\nArguments:\n"
"1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n" "1. minconf (numeric, optional, default=1) Only include notes in transactions confirmed at least this many times.\n"
"\nReturns the number of sprout and sapling notes available in the wallet.\n" "\nReturns the number of sapling notes available in the wallet.\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"sapling\" (numeric) the number of sapling notes in the wallet\n" " \"sapling\" (numeric) the number of sapling notes in the wallet\n"
@@ -4268,7 +4263,7 @@ UniValue z_gettotalbalance(const UniValue& params, bool fHelp, const CPubKey& my
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n"
" \"private\": xxxxx, (numeric) the total balance of private funds (in both Sprout and Sapling addresses)\n" " \"private\": xxxxx, (numeric) the total balance of private funds\n"
" \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n" " \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n"
"}\n" "}\n"
"\nExamples:\n" "\nExamples:\n"
@@ -4650,14 +4645,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found.");
} }
// Remember whether this is a Sprout or Sapling address // Remember whether this is a Sapling address
fromSapling = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr; fromSapling = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr;
} }
// This logic will need to be updated if we add a new shielded pool
bool fromSprout = !(fromTaddr || fromSapling);
if (fromSprout)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot send from a Sprout zaddr, only Sapling zaddrs supported.");
UniValue outputs = params[1].get_array(); UniValue outputs = params[1].get_array();
@@ -4667,15 +4657,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
// Keep track of addresses to spot duplicates // Keep track of addresses to spot duplicates
set<std::string> setAddress; set<std::string> setAddress;
// Track whether we see any Sprout addresses
bool noSproutAddrs = !fromSprout;
// Recipients // Recipients
std::vector<SendManyRecipient> taddrRecipients; std::vector<SendManyRecipient> taddrRecipients;
std::vector<SendManyRecipient> zaddrRecipients; std::vector<SendManyRecipient> zaddrRecipients;
CAmount nTotalOut = 0; CAmount nTotalOut = 0;
bool containsSproutOutput = false;
bool containsSaplingOutput = false; bool containsSaplingOutput = false;
for (const UniValue& o : outputs.getValues()) { for (const UniValue& o : outputs.getValues()) {
@@ -4696,35 +4682,6 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
auto res = DecodePaymentAddress(address); auto res = DecodePaymentAddress(address);
if (IsValidPaymentAddress(res, branchId)) { if (IsValidPaymentAddress(res, branchId)) {
isZaddr = true; isZaddr = true;
bool toSapling = boost::get<libzcash::SaplingPaymentAddress>(&res) != nullptr;
bool toSprout = !toSapling;
noSproutAddrs = noSproutAddrs && toSapling;
containsSproutOutput |= toSprout;
containsSaplingOutput |= toSapling;
// Sending to both Sprout and Sapling is currently unsupported using z_sendmany
if (containsSproutOutput && containsSaplingOutput) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Cannot send to both Sprout and Sapling addresses using z_sendmany");
}
if ( GetTime() > KOMODO_SAPLING_DEADLINE )
{
if ( fromSprout || toSprout )
throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage has expired");
}
if ( toSapling && ASSETCHAINS_SYMBOL[0] == 0 )
throw JSONRPCError(RPC_INVALID_PARAMETER,"Sprout usage will expire soon");
// If we are sending from a shielded address, all recipient
// shielded addresses must be of the same type.
if ((fromSprout && toSapling) || (fromSapling && toSprout)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Cannot send between Sprout and Sapling addresses using z_sendmany");
}
} else { } else {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address );
} }
@@ -4915,9 +4872,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
// Builder (used if Sapling addresses are involved) // Builder (used if Sapling addresses are involved)
boost::optional<TransactionBuilder> builder; boost::optional<TransactionBuilder> builder;
if (noSproutAddrs) { builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain);
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain);
}
// Contextual transaction we will build on // Contextual transaction we will build on
// (used if no Sapling addresses are involved) // (used if no Sapling addresses are involved)
@@ -5190,11 +5145,11 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
return NullUniValue; return NullUniValue;
string enableArg = "zmergetoaddress"; string enableArg = "zmergetoaddress";
auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true); //auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true);
std::string strDisabledMsg = ""; //std::string strDisabledMsg = "";
if (!fEnableMergeToAddress) { //if (!fEnableMergeToAddress) {
strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); // strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg);
} //}
if (fHelp || params.size() < 2 || params.size() > 7) if (fHelp || params.size() < 2 || params.size() > 7)
throw runtime_error( throw runtime_error(
@@ -5256,7 +5211,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
THROW_IF_SYNCING(KOMODO_INSYNC); THROW_IF_SYNCING(KOMODO_INSYNC);
bool useAnyUTXO = false; bool useAnyUTXO = false;
bool useAnySprout = false;
bool useAnySapling = false; bool useAnySapling = false;
std::set<CTxDestination> taddrs = {}; std::set<CTxDestination> taddrs = {};
std::set<libzcash::PaymentAddress> zaddrs = {}; std::set<libzcash::PaymentAddress> zaddrs = {};
@@ -5277,8 +5231,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
if (address == "ANY_TADDR") { if (address == "ANY_TADDR") {
useAnyUTXO = true; useAnyUTXO = true;
} else if (address == "ANY_SPROUT") {
useAnySprout = true;
} else if (address == "ANY_SAPLING") { } else if (address == "ANY_SAPLING") {
useAnySapling = true; useAnySapling = true;
} else { } else {
@@ -5303,8 +5255,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
if (useAnyUTXO && taddrs.size() > 0) { if (useAnyUTXO && taddrs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\""); throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\"");
} }
if ((useAnySprout || useAnySapling) && zaddrs.size() > 0) { if ((useAnySapling) && zaddrs.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SAPLING\"");
} }
const int nextBlockHeight = chainActive.Height() + 1; const int nextBlockHeight = chainActive.Height() + 1;
@@ -5313,7 +5265,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
// Validate the destination address // Validate the destination address
auto destaddress = params[1].get_str(); auto destaddress = params[1].get_str();
bool isToSproutZaddr = false;
bool isToSaplingZaddr = false; bool isToSaplingZaddr = false;
CTxDestination taddr = DecodeDestination(destaddress); CTxDestination taddr = DecodeDestination(destaddress);
if (!IsValidDestination(taddr)) { if (!IsValidDestination(taddr)) {
@@ -5326,7 +5277,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated");
} }
} else { } else {
isToSproutZaddr = true; throw JSONRPCError(RPC_INVALID_PARAMETER, "Only Sapling zaddrs allowed!");
} }
} else { } else {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress );
@@ -5351,14 +5302,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
} }
} }
int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT;
int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT; int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT;
if (params.size() > 4) { if (params.size() > 4) {
int nNoteLimit = params[4].get_int(); int nNoteLimit = params[4].get_int();
if (nNoteLimit < 0) { if (nNoteLimit < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative");
} }
sproutNoteLimit = nNoteLimit;
saplingNoteLimit = nNoteLimit; saplingNoteLimit = nNoteLimit;
} }
@@ -5375,7 +5324,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
std::string memo; std::string memo;
if (params.size() > 6) { if (params.size() > 6) {
memo = params[6].get_str(); memo = params[6].get_str();
if (!(isToSproutZaddr || isToSaplingZaddr)) { if (!isToSaplingZaddr) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr.");
} else if (!IsHex(memo)) { } else if (!IsHex(memo)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format.");
@@ -5389,7 +5338,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
// Prepare to get UTXOs and notes // Prepare to get UTXOs and notes
std::vector<MergeToAddressInputUTXO> utxoInputs; std::vector<MergeToAddressInputUTXO> utxoInputs;
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs;
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs; std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs;
CAmount mergedUTXOValue = 0; CAmount mergedUTXOValue = 0;
CAmount mergedNoteValue = 0; CAmount mergedNoteValue = 0;
@@ -5403,9 +5351,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING; unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING;
size_t estimatedTxSize = 200; // tx overhead + wiggle room size_t estimatedTxSize = 200; // tx overhead + wiggle room
if (isToSproutZaddr) {
estimatedTxSize += JOINSPLIT_SIZE; if (isToSaplingZaddr) {
} else if (isToSaplingZaddr) {
estimatedTxSize += OUTPUTDESCRIPTION_SIZE; estimatedTxSize += OUTPUTDESCRIPTION_SIZE;
} }
@@ -5463,29 +5410,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
} }
} }
if (useAnySprout || useAnySapling || zaddrs.size() > 0) { if (useAnySapling || zaddrs.size() > 0) {
// Get available notes // Get available notes
std::vector<CSproutNotePlaintextEntry> sproutEntries; std::vector<SaplingNoteEntry> saplingEntries;
//std::vector<SaplingNoteEntry> saplingEntries; pwalletMain->GetFilteredNotes(saplingEntries, zaddrs);
//pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs);
std::vector<SaplingNoteEntry> saplingEntries,skipsapling;
pwalletMain->GetFilteredNotes(sproutEntries, useAnySprout == 0 ? saplingEntries : skipsapling, zaddrs);
// If Sapling is not active, do not allow sending from a sapling addresses.
if (!saplingActive && saplingEntries.size() > 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated");
}
// Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress
if (sproutEntries.size() > 0 && saplingEntries.size() > 0) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Cannot send from both Sprout and Sapling addresses using z_mergetoaddress");
}
// If sending between shielded addresses, they must be the same type
if ((saplingEntries.size() > 0 && isToSproutZaddr) || (sproutEntries.size() > 0 && isToSaplingZaddr)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
"Cannot send between Sprout and Sapling addresses using z_mergetoaddress");
}
for (const SaplingNoteEntry& entry : saplingEntries) { for (const SaplingNoteEntry& entry : saplingEntries) {
noteCounter++; noteCounter++;
@@ -5514,7 +5442,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
} }
size_t numUtxos = utxoInputs.size(); size_t numUtxos = utxoInputs.size();
size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); size_t numNotes = saplingNoteInputs.size();
//fprintf(stderr, "num utxos.%li\n", numUtxos); //fprintf(stderr, "num utxos.%li\n", numUtxos);
if (numUtxos < 2 && numNotes == 0) { if (numUtxos < 2 && numNotes == 0) {
@@ -5552,22 +5480,19 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(
Params().GetConsensus(), Params().GetConsensus(),
nextBlockHeight); nextBlockHeight);
bool isSproutShielded = sproutNoteInputs.size() > 0 || isToSproutZaddr;
if (contextualTx.nVersion == 1 && isSproutShielded) {
contextualTx.nVersion = 2; // Tx format should support vjoinsplit
}
// Builder (used if Sapling addresses are involved) // Builder (used if Sapling addresses are involved)
boost::optional<TransactionBuilder> builder; boost::optional<TransactionBuilder> builder;
if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { if (isToSaplingZaddr || saplingNoteInputs.size() > 0) {
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain);
} else } else {
contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height. contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height.
}
// Create operation and add to global queue // Create operation and add to global queue
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
std::shared_ptr<AsyncRPCOperation> operation( std::shared_ptr<AsyncRPCOperation> operation(
new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, sproutNoteInputs, saplingNoteInputs, recipient, nFee, contextInfo) ); new AsyncRPCOperation_mergetoaddress(builder, contextualTx, utxoInputs, saplingNoteInputs, recipient, nFee, contextInfo) );
q->addOperation(operation); q->addOperation(operation);
AsyncRPCOperationId operationId = operation->getId(); AsyncRPCOperationId operationId = operation->getId();