|
|
@ -70,7 +70,6 @@ using namespace libzcash; |
|
|
|
|
|
|
|
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
|
|
|
extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; |
|
|
|
const std::string ADDR_TYPE_SPROUT = "sprout"; |
|
|
|
const std::string ADDR_TYPE_SAPLING = "sapling"; |
|
|
|
extern UniValue TxJoinSplitToJSON(const CTransaction& tx); |
|
|
|
extern int32_t KOMODO_INSYNC; |
|
|
@ -3787,9 +3786,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
|
UniValue results(UniValue::VARR); |
|
|
|
|
|
|
|
if (zaddrs.size() > 0) { |
|
|
|
std::vector<CSproutNotePlaintextEntry> sproutEntries; |
|
|
|
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); |
|
|
|
|
|
|
|
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 balance = 0; |
|
|
|
std::vector<CSproutNotePlaintextEntry> sproutEntries; |
|
|
|
std::vector<SaplingNoteEntry> saplingEntries; |
|
|
|
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) { |
|
|
|
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."); |
|
|
|
} |
|
|
|
|
|
|
|
// Visitor to support Sprout and Sapling addrs
|
|
|
|
if (!boost::apply_visitor(PaymentAddressBelongsToWallet(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."); |
|
|
|
} |
|
|
|
|
|
|
|
UniValue result(UniValue::VARR); |
|
|
|
std::vector<CSproutNotePlaintextEntry> sproutEntries; |
|
|
|
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; |
|
|
|
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" |
|
|
|
"\nArguments:\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" |
|
|
|
"{\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" |
|
|
|
"{\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" |
|
|
|
"}\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."); |
|
|
|
} |
|
|
|
|
|
|
|
// Remember whether this is a Sprout or Sapling address
|
|
|
|
// Remember whether this is a Sapling address
|
|
|
|
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(); |
|
|
|
|
|
|
@ -4667,15 +4657,11 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) |
|
|
|
// Keep track of addresses to spot duplicates
|
|
|
|
set<std::string> setAddress; |
|
|
|
|
|
|
|
// Track whether we see any Sprout addresses
|
|
|
|
bool noSproutAddrs = !fromSprout; |
|
|
|
|
|
|
|
// Recipients
|
|
|
|
std::vector<SendManyRecipient> taddrRecipients; |
|
|
|
std::vector<SendManyRecipient> zaddrRecipients; |
|
|
|
CAmount nTotalOut = 0; |
|
|
|
|
|
|
|
bool containsSproutOutput = false; |
|
|
|
bool containsSaplingOutput = false; |
|
|
|
|
|
|
|
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); |
|
|
|
if (IsValidPaymentAddress(res, branchId)) { |
|
|
|
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 { |
|
|
|
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)
|
|
|
|
boost::optional<TransactionBuilder> builder; |
|
|
|
if (noSproutAddrs) { |
|
|
|
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); |
|
|
|
} |
|
|
|
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); |
|
|
|
|
|
|
|
// Contextual transaction we will build on
|
|
|
|
// (used if no Sapling addresses are involved)
|
|
|
@ -5190,11 +5145,11 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
return NullUniValue; |
|
|
|
|
|
|
|
string enableArg = "zmergetoaddress"; |
|
|
|
auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true); |
|
|
|
std::string strDisabledMsg = ""; |
|
|
|
if (!fEnableMergeToAddress) { |
|
|
|
strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); |
|
|
|
} |
|
|
|
//auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-" + enableArg, true);
|
|
|
|
//std::string strDisabledMsg = "";
|
|
|
|
//if (!fEnableMergeToAddress) {
|
|
|
|
// strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg);
|
|
|
|
//}
|
|
|
|
|
|
|
|
if (fHelp || params.size() < 2 || params.size() > 7) |
|
|
|
throw runtime_error( |
|
|
@ -5256,7 +5211,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
THROW_IF_SYNCING(KOMODO_INSYNC); |
|
|
|
|
|
|
|
bool useAnyUTXO = false; |
|
|
|
bool useAnySprout = false; |
|
|
|
bool useAnySapling = false; |
|
|
|
std::set<CTxDestination> taddrs = {}; |
|
|
|
std::set<libzcash::PaymentAddress> zaddrs = {}; |
|
|
@ -5277,8 +5231,6 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
|
|
|
|
if (address == "ANY_TADDR") { |
|
|
|
useAnyUTXO = true; |
|
|
|
} else if (address == "ANY_SPROUT") { |
|
|
|
useAnySprout = true; |
|
|
|
} else if (address == "ANY_SAPLING") { |
|
|
|
useAnySapling = true; |
|
|
|
} else { |
|
|
@ -5303,8 +5255,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
if (useAnyUTXO && taddrs.size() > 0) { |
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific t-addrs when using \"ANY_TADDR\""); |
|
|
|
} |
|
|
|
if ((useAnySprout || useAnySapling) && zaddrs.size() > 0) { |
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); |
|
|
|
if ((useAnySapling) && zaddrs.size() > 0) { |
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify specific z-addrs when using \"ANY_SAPLING\""); |
|
|
|
} |
|
|
|
|
|
|
|
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
|
|
|
|
auto destaddress = params[1].get_str(); |
|
|
|
bool isToSproutZaddr = false; |
|
|
|
bool isToSaplingZaddr = false; |
|
|
|
CTxDestination taddr = DecodeDestination(destaddress); |
|
|
|
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"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
isToSproutZaddr = true; |
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Only Sapling zaddrs allowed!"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
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; |
|
|
|
if (params.size() > 4) { |
|
|
|
int nNoteLimit = params[4].get_int(); |
|
|
|
if (nNoteLimit < 0) { |
|
|
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); |
|
|
|
} |
|
|
|
sproutNoteLimit = nNoteLimit; |
|
|
|
saplingNoteLimit = nNoteLimit; |
|
|
|
} |
|
|
|
|
|
|
@ -5375,7 +5324,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
std::string memo; |
|
|
|
if (params.size() > 6) { |
|
|
|
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."); |
|
|
|
} else if (!IsHex(memo)) { |
|
|
|
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
|
|
|
|
std::vector<MergeToAddressInputUTXO> utxoInputs; |
|
|
|
std::vector<MergeToAddressInputSproutNote> sproutNoteInputs; |
|
|
|
std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs; |
|
|
|
CAmount mergedUTXOValue = 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; |
|
|
|
size_t estimatedTxSize = 200; // tx overhead + wiggle room
|
|
|
|
if (isToSproutZaddr) { |
|
|
|
estimatedTxSize += JOINSPLIT_SIZE; |
|
|
|
} else if (isToSaplingZaddr) { |
|
|
|
|
|
|
|
if (isToSaplingZaddr) { |
|
|
|
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
|
|
|
|
std::vector<CSproutNotePlaintextEntry> sproutEntries; |
|
|
|
//std::vector<SaplingNoteEntry> saplingEntries;
|
|
|
|
//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"); |
|
|
|
} |
|
|
|
std::vector<SaplingNoteEntry> saplingEntries; |
|
|
|
pwalletMain->GetFilteredNotes(saplingEntries, zaddrs); |
|
|
|
|
|
|
|
for (const SaplingNoteEntry& entry : saplingEntries) { |
|
|
|
noteCounter++; |
|
|
@ -5514,7 +5442,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
} |
|
|
|
|
|
|
|
size_t numUtxos = utxoInputs.size(); |
|
|
|
size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); |
|
|
|
size_t numNotes = saplingNoteInputs.size(); |
|
|
|
|
|
|
|
//fprintf(stderr, "num utxos.%li\n", numUtxos);
|
|
|
|
if (numUtxos < 2 && numNotes == 0) { |
|
|
@ -5552,22 +5480,19 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp, const CPubKey& myp |
|
|
|
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( |
|
|
|
Params().GetConsensus(), |
|
|
|
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)
|
|
|
|
boost::optional<TransactionBuilder> builder; |
|
|
|
if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { |
|
|
|
builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); |
|
|
|
} else |
|
|
|
} else { |
|
|
|
contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height.
|
|
|
|
} |
|
|
|
|
|
|
|
// Create operation and add to global queue
|
|
|
|
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
|
|
|
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); |
|
|
|
AsyncRPCOperationId operationId = operation->getId(); |
|
|
|
|
|
|
|