LCOV - code coverage report
Current view: top level - src/sapling - sapling_operation.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 244 308 79.2 %
Date: 2025-02-23 09:33:43 Functions: 17 18 94.4 %

          Line data    Source code
       1             : // Copyright (c) 2021 The PIVX Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include "sapling/sapling_operation.h"
       6             : 
       7             : #include "coincontrol.h"
       8             : #include "net.h" // for g_connman
       9             : #include "policy/policy.h" // for GetDustThreshold
      10             : #include "sapling/key_io_sapling.h"
      11             : #include "script/standard.h"
      12             : #include "utilmoneystr.h"        // for FormatMoney
      13             : 
      14             : struct TxValues
      15             : {
      16             :     CAmount transInTotal{0};
      17             :     CAmount shieldedInTotal{0};
      18             :     CAmount transOutTotal{0};
      19             :     CAmount shieldedOutTotal{0};
      20             :     CAmount target{0};
      21             : };
      22             : 
      23        1097 : SaplingOperation::SaplingOperation(const Consensus::Params& consensusParams, CWallet* _wallet) :
      24             :     wallet(_wallet),
      25        1097 :     txBuilder(consensusParams, _wallet)
      26             : {
      27        1097 :     assert (wallet != nullptr);
      28        1097 : };
      29             : 
      30        4329 : SaplingOperation::~SaplingOperation()
      31             : {
      32        2146 :     delete tkeyChange;
      33        1097 : }
      34             : 
      35        2142 : OperationResult SaplingOperation::checkTxValues(TxValues& txValues, bool isFromtAddress, bool isFromShielded)
      36             : {
      37        2142 :     assert(!isFromtAddress || txValues.shieldedInTotal == 0);
      38        2142 :     assert(!isFromShielded || txValues.transInTotal == 0);
      39             : 
      40        2142 :     if (isFromtAddress && (txValues.transInTotal < txValues.target)) {
      41           0 :         return errorOut(strprintf("Insufficient transparent funds, have %s, need %s",
      42           0 :                                   FormatMoney(txValues.transInTotal), FormatMoney(txValues.target)));
      43             :     }
      44             : 
      45        2142 :     if (isFromShielded && (txValues.shieldedInTotal < txValues.target)) {
      46           0 :         return errorOut(strprintf("Insufficient shielded funds, have %s, need %s",
      47           0 :                                   FormatMoney(txValues.shieldedInTotal), FormatMoney(txValues.target)));
      48             :     }
      49        2142 :     return OperationResult(true);
      50             : }
      51             : 
      52          79 : OperationResult loadKeysFromShieldedFrom(const CWallet* pwallet,
      53             :                                          const libzcash::SaplingPaymentAddress &addr,
      54             :                                          libzcash::SaplingExpandedSpendingKey& expskOut,
      55             :                                          uint256& ovkOut)
      56             : {
      57             :     // Get spending key for address
      58          79 :     libzcash::SaplingExtendedSpendingKey sk;
      59          79 :     if (!pwallet->GetSaplingExtendedSpendingKey(addr, sk)) {
      60           0 :         return errorOut("Spending key not in the wallet");
      61             :     }
      62          79 :     expskOut = sk.expsk;
      63          79 :     ovkOut = expskOut.full_viewing_key().ovk;
      64          79 :     return OperationResult(true);
      65             : }
      66             : 
      67        2146 : TxValues calculateTarget(const std::vector<SendManyRecipient>& recipients, const CAmount& fee)
      68             : {
      69        2146 :     TxValues txValues;
      70        4364 :     for (const SendManyRecipient &t : recipients) {
      71        2218 :         if (t.IsTransparent()) {
      72          39 :             txValues.transOutTotal += t.getAmount();
      73             :         } else {
      74        2179 :             txValues.shieldedOutTotal += t.getAmount();
      75             :         }
      76             :     }
      77        2146 :     txValues.target = txValues.shieldedOutTotal + txValues.transOutTotal + fee;
      78        2146 :     return txValues;
      79             : }
      80             : 
      81        1093 : OperationResult SaplingOperation::build()
      82             : {
      83        1093 :     bool isFromtAddress = false;
      84        1093 :     bool isFromShielded = false;
      85             : 
      86        1093 :     if (coinControl && coinControl->HasSelected()) {
      87             :         // if coin control was selected it overrides any other defined configuration
      88           0 :         std::vector<OutPointWrapper> coins;
      89           0 :         coinControl->ListSelected(coins);
      90             :         // first check that every selected input is from the same type, cannot be mixed for clear privacy reasons.
      91             :         // error is thrown below if it happens, not here.
      92           0 :         for (const auto& coin : coins) {
      93           0 :             if (coin.outPoint.isTransparent) {
      94             :                 isFromtAddress = true;
      95             :             } else {
      96           0 :                 isFromShielded = true;
      97             :             }
      98             :         }
      99             :     } else {
     100             :         // Regular flow
     101        1093 :         isFromtAddress = fromAddress.isFromTAddress();
     102        1093 :         isFromShielded = fromAddress.isFromSapAddress();
     103             : 
     104        1093 :         if (!isFromtAddress && !isFromShielded) {
     105        1051 :             isFromtAddress = selectFromtaddrs;
     106        1051 :             isFromShielded = selectFromShield;
     107             :         }
     108             :     }
     109             : 
     110             :     // It needs to have a from.
     111        1093 :     if (!isFromtAddress && !isFromShielded) {
     112           0 :         return errorOut("From address parameter missing");
     113             :     }
     114             : 
     115             :     // Cannot be from both
     116        1093 :     if (isFromtAddress && isFromShielded) {
     117           0 :         return errorOut("From address type cannot be shielded and transparent");
     118             :     }
     119             : 
     120        1093 :     if (recipients.empty()) {
     121           0 :         return errorOut("No recipients");
     122             :     }
     123             : 
     124        1093 :     if (isFromShielded && mindepth == 0) {
     125           3 :         return errorOut("Minconf cannot be zero when sending from shielded address");
     126             :     }
     127             : 
     128             :     // Check outputs to subtract fee from
     129        1092 :     unsigned int nSubtractFeeFromAmount = 0;
     130        2252 :     for (const SendManyRecipient& rec : recipients) {
     131        1160 :         if (rec.IsSubtractFee()) nSubtractFeeFromAmount++;
     132             :     }
     133             : 
     134        1092 :     CAmount nFeeRet = (fee > 0 ? fee : minRelayTxFee.GetFeePerK());
     135        1092 :     int tries = 0;
     136        2146 :     while (true) {
     137             :         // First calculate target values
     138        2146 :         TxValues txValues = calculateTarget(recipients, nSubtractFeeFromAmount == 0 ? nFeeRet : 0);
     139        3200 :         OperationResult result(false);
     140        2146 :         uint256 ovk;
     141        2146 :         if (isFromShielded) {
     142             :             // Load and select notes to spend, then return the ovk of the first note input of the transaction
     143         130 :             if (!(result = loadUnspentNotes(txValues, ovk))) {
     144           8 :                 return result;
     145             :             }
     146             :         } else {
     147             :             // Get the common OVK for recovering t->shield outputs.
     148             :             // If not already databased, a new one will be generated from the HD seed.
     149             :             // It is safe to do it here, as the wallet is unlocked.
     150        2082 :             ovk = wallet->GetSaplingScriptPubKeyMan()->getCommonOVK();
     151             :         }
     152             : 
     153             :         // Add outputs
     154        2144 :         bool fFirst = true;
     155        4359 :         for (const SendManyRecipient &t : recipients) {
     156        2216 :             CAmount amount = t.getAmount();
     157             :             // Subtract from fee calculation
     158        2216 :             if (t.IsSubtractFee()) {
     159             :                 // Subtract fee equally from each selected recipient
     160           8 :                 amount -= nFeeRet / nSubtractFeeFromAmount;
     161           8 :                 if (fFirst) {
     162             :                     // first receiver pays the remainder not divisible by output count
     163           6 :                     fFirst = false;
     164           6 :                     amount -= nFeeRet % nSubtractFeeFromAmount;
     165             :                 }
     166             :             }
     167             :             // Append output
     168        2216 :             if (t.IsTransparent()) {
     169          78 :                 txBuilder.AddTransparentOutput(CTxOut(amount, t.getScript()));
     170             :             } else {
     171        2178 :                 const auto& address = t.getSapPaymentAddr();
     172        2178 :                 assert(IsValidPaymentAddress(address));
     173        2178 :                 std::array<unsigned char, ZC_MEMO_SIZE> vMemo = {};
     174        4360 :                 if (!(result = GetMemoFromString(t.getMemo(), vMemo)))
     175           2 :                     return result;
     176        2177 :                 txBuilder.AddSaplingOutput(ovk, address, amount, vMemo);
     177             :             }
     178             :         }
     179             : 
     180             :         // If from address is a taddr, select UTXOs to spend
     181             :         // note: when spending coinbase utxos, you can only specify a single shielded addr as the change must go somewhere
     182             :         // and if there are multiple shielded addrs, we don't know where to send it.
     183        4224 :         if (isFromtAddress && !(result = loadUtxos(txValues))) {
     184           1 :             return result;
     185             :         }
     186             : 
     187        3196 :         const auto& retCalc = checkTxValues(txValues, isFromtAddress, isFromShielded);
     188        2150 :         if (!retCalc) return retCalc;
     189             : 
     190             :         // By default look for a shield change address
     191        2142 :         if (coinControl && coinControl->destShieldChange) {
     192           0 :             txBuilder.SendChangeTo(*coinControl->destShieldChange, ovk);
     193             : 
     194             :             // If not found, and the transaction is transparent, set transparent change address
     195        2142 :         } else if (isFromtAddress) {
     196             :             // Try to use coin control first
     197        2080 :             if (coinControl && IsValidDestination(coinControl->destChange)) {
     198           0 :                 txBuilder.SendChangeTo(coinControl->destChange);
     199             :                 // No Coin control! Then we can just use a random key from the keypool
     200             :             } else {
     201        2080 :                 if (!tkeyChange) {
     202        1049 :                     tkeyChange = new CReserveKey(wallet);
     203             :                 }
     204        2080 :                 CPubKey vchPubKey;
     205        2080 :                 if (!tkeyChange->GetReservedKey(vchPubKey, true)) {
     206           0 :                     return errorOut("Could not generate a taddr to use as a change address");
     207             :                 }
     208        4160 :                 CTxDestination changeAddr = vchPubKey.GetID();
     209        2080 :                 txBuilder.SendChangeTo(changeAddr);
     210             :             }
     211             :         }
     212             : 
     213             :         // Build the transaction
     214        2142 :         txBuilder.SetFee(nFeeRet);
     215        3196 :         TransactionBuilderResult txResult = txBuilder.Build(true);
     216        3196 :         auto opTx = txResult.GetTx();
     217             : 
     218             :         // Check existent tx
     219        2142 :         if (!opTx) {
     220           0 :             return errorOut("Failed to build transaction: " + txResult.GetError());
     221             :         }
     222             : 
     223             :         // Now check fee
     224        2144 :         bool isShielded = opTx->IsShieldedTx();
     225        2140 :         const CAmount& nFeeNeeded = isShielded ? GetShieldedTxMinFee(*opTx) :
     226           2 :                                                  GetMinRelayFee(opTx->GetTotalSize());
     227        2142 :         if (nFeeNeeded <= nFeeRet) {
     228             :             // Check that the fee is not too high.
     229        1087 :             CAmount nMaxFee = nFeeNeeded * (isShielded ? 100 : 10000);
     230        1087 :             if (nFeeRet > nMaxFee) {
     231           3 :                 return errorOut(strprintf("The transaction fee is too high: %s > %s", FormatMoney(nFeeRet), FormatMoney(100 * nFeeNeeded)));
     232             :             }
     233             :             // Done, enough fee included
     234        2167 :             LogPrint(BCLog::SAPLING, "%s: spending %s to send %s with fee %s (min required %s)\n", __func__ , FormatMoney(txValues.target),
     235             :                     FormatMoney(txValues.shieldedOutTotal + txValues.transOutTotal), FormatMoney(nFeeRet), FormatMoney(nFeeNeeded));
     236        2167 :             LogPrint(BCLog::SAPLING, "%s: transparent input: %s (to choose from)\n", __func__ , FormatMoney(txValues.transInTotal));
     237        2167 :             LogPrint(BCLog::SAPLING, "%s: private input: %s (to choose from)\n", __func__ , FormatMoney(txValues.shieldedInTotal));
     238        2167 :             LogPrint(BCLog::SAPLING, "%s: transparent output: %s\n", __func__ , FormatMoney(txValues.transOutTotal));
     239        2167 :             LogPrint(BCLog::SAPLING, "%s: private output: %s\n", __func__ , FormatMoney(txValues.shieldedOutTotal));
     240        2172 :             break;
     241             :         }
     242        1055 :         if (fee > 0 && nFeeNeeded > fee) {
     243             :             // User selected fee is not enough
     244           3 :             return errorOut(strprintf("Fee set (%s) too low. Must be at least %s", FormatMoney(fee), FormatMoney(nFeeNeeded)));
     245             :         }
     246             :         // If we can't get the optimal fee after 100 tries, give up.
     247        1054 :         if (++tries > 100) {
     248           0 :             return errorOut("Unable to compute optimal fee. Set manually.");
     249             :         }
     250             :         // include more fee and try again
     251        2103 :         LogPrint(BCLog::SAPLING, "%s: incrementing fee: %s --> %s\n", __func__ , FormatMoney(nFeeRet), FormatMoney(nFeeNeeded));
     252        1054 :         clearTx();
     253        1054 :         nFeeRet = nFeeNeeded;
     254             :     }
     255             :     // Done
     256        1086 :     fee = nFeeRet;
     257             : 
     258             :     // Clear dummy signatures/proofs and add real ones
     259        1086 :     txBuilder.ClearProofsAndSignatures();
     260        1086 :     TransactionBuilderResult txResult = txBuilder.ProveAndSign();
     261        2172 :     auto opTx = txResult.GetTx();
     262             :     // Check existent tx
     263        1086 :     if (!opTx) {
     264           0 :         return errorOut("Failed to build transaction: " + txResult.GetError());
     265             :     }
     266        1086 :     finalTx = MakeTransactionRef(*opTx);
     267        1086 :     return OperationResult(true);
     268             : }
     269             : 
     270        1067 : OperationResult SaplingOperation::send(std::string& retTxHash)
     271             : {
     272        2134 :     const CWallet::CommitResult& res = wallet->CommitTransaction(finalTx, tkeyChange, g_connman.get());
     273        1067 :     if (res.status != CWallet::CommitStatus::OK) {
     274           6 :         return errorOut(res.ToString());
     275             :     }
     276             : 
     277        1065 :     retTxHash = finalTx->GetHash().ToString();
     278        1067 :     return OperationResult(true);
     279             : }
     280             : 
     281           3 : OperationResult SaplingOperation::buildAndSend(std::string& retTxHash)
     282             : {
     283           3 :     OperationResult res = build();
     284           9 :     return (res) ? send(retTxHash) : res;
     285             : }
     286             : 
     287          16 : void SaplingOperation::setFromAddress(const CTxDestination& _dest)
     288             : {
     289          16 :     fromAddress = FromAddress(_dest);
     290          16 : }
     291             : 
     292          27 : void SaplingOperation::setFromAddress(const libzcash::SaplingPaymentAddress& _payment)
     293             : {
     294          27 :     fromAddress = FromAddress(_payment);
     295          27 : }
     296             : 
     297        1039 : SaplingOperation* SaplingOperation::setSelectTransparentCoins(const bool select, const bool _fIncludeDelegated)
     298             : {
     299        1039 :     selectFromtaddrs = select;
     300        1039 :     if (selectFromtaddrs) fIncludeDelegated = _fIncludeDelegated;
     301        1039 :     return this;
     302             : };
     303             : 
     304        2081 : OperationResult SaplingOperation::loadUtxos(TxValues& txValues)
     305             : {
     306             :     // If the user has selected coins to spend then, directly load them.
     307             :     // The spendability, depth and other checks should have been done on the user selection side,
     308             :     // no need to do them again.
     309        2081 :     if (coinControl && coinControl->HasSelected()) {
     310           0 :         std::vector<OutPointWrapper> vCoins;
     311           0 :         coinControl->ListSelected(vCoins);
     312             : 
     313           0 :         std::vector<COutput> selectedUTXOInputs;
     314           0 :         CAmount nSelectedValue = 0;
     315           0 :         for (const auto& outpoint : vCoins) {
     316           0 :             const auto* tx = wallet->GetWalletTx(outpoint.outPoint.hash);
     317           0 :             if (!tx) continue;
     318           0 :             nSelectedValue += tx->tx->vout[outpoint.outPoint.n].nValue;
     319           0 :             selectedUTXOInputs.emplace_back(tx, outpoint.outPoint.n, 0, true, true, true);
     320             :         }
     321           0 :         return loadUtxos(txValues, selectedUTXOInputs, nSelectedValue);
     322             :     }
     323             : 
     324             :     // No coin control selected, let's find the utxo by our own.
     325        4162 :     std::set<CTxDestination> destinations;
     326        2081 :     if (fromAddress.isFromTAddress()) destinations.insert(fromAddress.fromTaddr);
     327        2081 :     CWallet::AvailableCoinsFilter coinsFilter(fIncludeDelegated,
     328             :                                               false,
     329             :                                               true,
     330             :                                               true,
     331             :                                               &destinations,
     332        2081 :                                               mindepth);
     333        2081 :     if (!wallet->AvailableCoins(&transInputs, nullptr, coinsFilter)) {
     334           3 :         return errorOut("Insufficient funds, no available UTXO to spend");
     335             :     }
     336             : 
     337             :     // sort in descending order, so higher utxos appear first
     338        2080 :     std::sort(transInputs.begin(), transInputs.end(), [](const COutput& i, const COutput& j) -> bool {
     339     6795226 :         return i.Value() > j.Value();
     340             :     });
     341             : 
     342             :     // Final step, append utxo to the transaction
     343             : 
     344             :     // Get dust threshold
     345        2080 :     CAmount dustThreshold = GetDustThreshold(dustRelayFee);
     346        2080 :     CAmount dustChange = -1;
     347             : 
     348        2080 :     CAmount selectedUTXOAmount = 0;
     349        4161 :     std::vector<COutput> selectedTInputs;
     350        2124 :     for (const COutput& t : transInputs) {
     351        2124 :         const auto& outPoint = t.tx->tx->vout[t.i];
     352        2124 :         selectedUTXOAmount += outPoint.nValue;
     353        2124 :         selectedTInputs.emplace_back(t);
     354        2124 :         if (selectedUTXOAmount >= txValues.target) {
     355             :             // Select another utxo if there is change less than the dust threshold.
     356        2080 :             dustChange = selectedUTXOAmount - txValues.target;
     357        2080 :             if (dustChange == 0 || dustChange >= dustThreshold) {
     358             :                 break;
     359             :             }
     360             :         }
     361             :     }
     362             : 
     363             :     // Not enough funds
     364        2080 :     if (selectedUTXOAmount < txValues.target) {
     365           0 :                 return errorOut(strprintf("Insufficient transparent funds, have %s, need %s",
     366           0 :                                   FormatMoney(selectedUTXOAmount), FormatMoney(txValues.target)));
     367             :     }
     368             : 
     369             :     // If there is transparent change, is it valid or is it dust?
     370        2080 :     if (dustChange < dustThreshold && dustChange != 0) {
     371           0 :         return errorOut(strprintf("Insufficient transparent funds, have %s, need %s more to avoid creating invalid change output %s (dust threshold is %s)",
     372           0 :                                   FormatMoney(selectedUTXOAmount), FormatMoney(dustThreshold - dustChange), FormatMoney(dustChange), FormatMoney(dustThreshold)));
     373             :     }
     374             : 
     375        2080 :     return loadUtxos(txValues, selectedTInputs, selectedUTXOAmount);
     376             : }
     377             : 
     378        2080 : OperationResult SaplingOperation::loadUtxos(TxValues& txValues, const std::vector<COutput>& selectedUTXO, const CAmount selectedUTXOAmount)
     379             : {
     380        2080 :     transInputs = selectedUTXO;
     381        2080 :     txValues.transInTotal = selectedUTXOAmount;
     382             : 
     383             :     // update the transaction with these inputs
     384        4204 :     for (const auto& t : transInputs) {
     385        2124 :         const auto& outPoint = t.tx->tx->vout[t.i];
     386        2124 :         txBuilder.AddTransparentInput(COutPoint(t.tx->GetHash(), t.i), outPoint.scriptPubKey, outPoint.nValue);
     387             :     }
     388        2080 :     return OperationResult(true);
     389             : }
     390             : 
     391             : /*
     392             :  * Check that the witness and nullifier of a sapling note (about to be spent) have been
     393             :  * correctly cached. If the witness is missing, return an error. If the nullifier is missing,
     394             :  * recover it from the note (now that we have the spending key).
     395             :  */
     396             : enum CacheCheckResult {OK, SPENT, INVALID};
     397          79 : static CacheCheckResult CheckCachedNote(CWallet* pwallet,
     398             :                                         const SaplingNoteEntry& t,
     399             :                                         const libzcash::SaplingExpandedSpendingKey& expsk)
     400             : {
     401          79 :     auto sspkm = pwallet->GetSaplingScriptPubKeyMan();
     402          79 :     CWalletTx& prevTx = pwallet->mapWallet.at(t.op.hash);
     403          79 :     SaplingNoteData& nd = prevTx.mapSaplingNoteData.at(t.op);
     404          79 :     if (nd.witnesses.empty()) {
     405             :         return CacheCheckResult::INVALID;
     406             :     }
     407          79 :     if (nd.nullifier == nullopt) {
     408           0 :         const std::string& noteStr = t.op.ToString();
     409           0 :         LogPrintf("WARNING: nullifier not cached for note %s. Updating...\n", noteStr);
     410             :         // get the nullifier from the note and update the cache
     411           0 :         const auto& witness = nd.witnesses.front();
     412           0 :         const Optional<uint256> nf = t.note.nullifier(expsk.full_viewing_key(), witness.position());
     413             :         // check that it's valid
     414           0 :         if (nf == nullopt) {
     415           0 :             LogPrintf("ERROR: Unable to recover nullifier for note %s.\n", noteStr);
     416           0 :             return CacheCheckResult::INVALID;
     417             :         }
     418           0 :         WITH_LOCK(pwallet->cs_wallet, sspkm->UpdateSaplingNullifierNoteMap(nd, t.op, nf));
     419             :         // re-check the spent status
     420           0 :         if (sspkm->IsSaplingSpent(*(nd.nullifier))) {
     421           0 :             LogPrintf("Removed note %s as it appears to be already spent.\n", noteStr);
     422           0 :             prevTx.MarkDirty();
     423           0 :             WalletBatch(pwallet->GetDBHandle(), "r+").WriteTx(prevTx);
     424           0 :             pwallet->NotifyTransactionChanged(pwallet, t.op.hash, CT_UPDATED);
     425             :             return CacheCheckResult::SPENT;
     426             :         }
     427             :     }
     428             :     return CacheCheckResult::OK;
     429             : }
     430             : 
     431          64 : OperationResult SaplingOperation::loadUnspentNotes(TxValues& txValues, uint256& ovk)
     432             : {
     433          64 :     shieldedInputs.clear();
     434          64 :     auto sspkm = wallet->GetSaplingScriptPubKeyMan();
     435             :     // if we already have selected the notes, let's directly set them.
     436          64 :     bool hasCoinControl = coinControl && coinControl->HasSelected();
     437          64 :     if (hasCoinControl) {
     438           0 :         std::vector<OutPointWrapper> vCoins;
     439           0 :         coinControl->ListSelected(vCoins);
     440             : 
     441             :         // Converting outpoint wrapper to sapling outpoints
     442           0 :         std::vector<SaplingOutPoint> vSaplingOutpoints;
     443           0 :         vSaplingOutpoints.reserve(vCoins.size());
     444           0 :         for (const auto& outpoint : vCoins) {
     445           0 :             vSaplingOutpoints.emplace_back(outpoint.outPoint.hash, outpoint.outPoint.n);
     446             :         }
     447             : 
     448           0 :         sspkm->GetNotes(vSaplingOutpoints, shieldedInputs);
     449             : 
     450           0 :         if (shieldedInputs.empty()) {
     451           0 :             return errorOut("Insufficient funds, no available notes to spend");
     452             :         }
     453             :     } else {
     454             :         // If we don't have coinControl then let's find the notes
     455          64 :         sspkm->GetFilteredNotes(shieldedInputs, fromAddress.fromSapAddr, mindepth);
     456          64 :         if (shieldedInputs.empty()) {
     457             :             // Just to notify the user properly, check if the wallet has notes with less than the min depth
     458           4 :             std::vector<SaplingNoteEntry> _shieldedInputs;
     459           2 :             sspkm->GetFilteredNotes(_shieldedInputs, fromAddress.fromSapAddr, 0);
     460           2 :             return errorOut(_shieldedInputs.empty() ?
     461             :                     "Insufficient funds, no available notes to spend" :
     462           6 :                     "Insufficient funds, shielded PIV need at least 5 confirmations");
     463             :         }
     464             :     }
     465             : 
     466             :     // sort in descending order, so big notes appear first
     467          62 :     std::sort(shieldedInputs.begin(), shieldedInputs.end(),
     468        1671 :               [](const SaplingNoteEntry& i, const SaplingNoteEntry& j) -> bool {
     469        1625 :                   return i.note.value() > j.note.value();
     470             :               });
     471             : 
     472             :     // Now select the notes that we are going to use.
     473         126 :     std::vector<SaplingOutPoint> ops;
     474          62 :     std::vector<libzcash::SaplingNote> notes;
     475          62 :     std::vector<libzcash::SaplingExpandedSpendingKey> spendingKeys;
     476          62 :     txValues.shieldedInTotal = 0;
     477          62 :     CAmount dustThreshold = GetShieldedDustThreshold(dustRelayFee);
     478          62 :     CAmount dustChange = -1;
     479          79 :     for (const auto& t : shieldedInputs) {
     480             :         // Get the spending key for the address.
     481          79 :         libzcash::SaplingExpandedSpendingKey expsk;
     482          79 :         uint256 ovkIn;
     483          96 :         auto resLoadKeys = loadKeysFromShieldedFrom(wallet, t.address, expsk, ovkIn);
     484          79 :         if (!resLoadKeys) return resLoadKeys;
     485             : 
     486             :         // If the noteData is not properly cached, for whatever reason,
     487             :         // try to update it here, now that we have the spending key.
     488          79 :         CacheCheckResult res = CheckCachedNote(wallet, t, expsk);
     489          79 :         if (res == CacheCheckResult::INVALID) {
     490             :             // This should never happen. User would be forced to zap.
     491           0 :             LogPrintf("ERROR: Witness/Nullifier invalid for note %s. Restart with --zapwallettxes\n", t.op.ToString());
     492           0 :             return errorOut("Note cache corrupt. Try \"Recover transactions\" (Settings-->Debug-->\"wallet repair\")");
     493          79 :         } else if (res == CacheCheckResult::SPENT) {
     494             :             // note was already spent, don't include it in the inputs
     495           0 :             continue;
     496             :         }
     497             : 
     498             :         // Return ovk to be used in the outputs
     499        2080 :         if (ovk.IsNull()) {
     500          62 :             ovk = ovkIn;
     501             :         }
     502             : 
     503             :         // Load data
     504          79 :         spendingKeys.emplace_back(expsk);
     505          79 :         ops.emplace_back(t.op);
     506          79 :         notes.emplace_back(t.note);
     507          79 :         txValues.shieldedInTotal += t.note.value();
     508          79 :         if (!hasCoinControl && txValues.shieldedInTotal >= txValues.target) {
     509             :             // coin control selection by pass this check, uses all the selected notes.
     510             :             // Select another note if there is change less than the dust threshold.
     511          62 :             dustChange = txValues.shieldedInTotal - txValues.target;
     512          62 :             if (dustChange == 0 || dustChange >= dustThreshold) {
     513             :                 break;
     514             :             }
     515             :         }
     516             :     }
     517             : 
     518             :     // Not enough funds
     519          62 :     if (txValues.shieldedInTotal < txValues.target) {
     520           0 :                 return errorOut(strprintf("Insufficient shielded funds, have %s, need %s",
     521           0 :                                   FormatMoney(txValues.shieldedInTotal), FormatMoney(txValues.target)));
     522             :     }
     523             : 
     524             :     // Fetch Sapling anchor and witnesses
     525          62 :     uint256 anchor;
     526          62 :     std::vector<Optional<SaplingWitness>> witnesses;
     527          62 :     wallet->GetSaplingScriptPubKeyMan()->GetSaplingNoteWitnesses(ops, witnesses, anchor);
     528             : 
     529             :     // Add Sapling spends
     530         141 :     for (size_t i = 0; i < notes.size(); i++) {
     531          79 :         if (!witnesses[i]) {
     532           0 :             return errorOut("Missing witness for Sapling note");
     533             :         }
     534          79 :         txBuilder.AddSaplingSpend(spendingKeys[i], notes[i], anchor, witnesses[i].get());
     535             :     }
     536             : 
     537          62 :     return OperationResult(true);
     538             : }
     539             : 
     540        2180 : OperationResult GetMemoFromString(const std::string& s, std::array<unsigned char, ZC_MEMO_SIZE>& memoRet)
     541             : {
     542        2180 :     memoRet.fill(0x00);
     543             :     // default memo (no_memo), see section 5.5 of the protocol spec
     544        2180 :     if (s.empty()) {
     545        2169 :         memoRet[0] = 0xF6;
     546        2169 :         return OperationResult(true);
     547             :     }
     548             :     // non-empty memo
     549        2191 :     std::vector<unsigned char> rawMemo(s.begin(), s.end());
     550          11 :     const size_t sizeMemo = rawMemo.size();
     551          11 :     if (sizeMemo > ZC_MEMO_SIZE) {
     552           6 :         return errorOut(strprintf("Memo size of %d is too big, maximum allowed is %d", sizeMemo, ZC_MEMO_SIZE));
     553             :     }
     554             :     // copy vector into array
     555         196 :     for (unsigned int i = 0; i < sizeMemo; i++) {
     556         187 :         memoRet[i] = rawMemo[i];
     557             :     }
     558          11 :     return OperationResult(true);
     559             : }
     560             : 
     561        1084 : OperationResult CheckTransactionSize(std::vector<SendManyRecipient>& recipients, bool fromTaddr)
     562             : {
     563        2168 :     CMutableTransaction mtx;
     564        1084 :     mtx.nVersion = CTransaction::TxVersion::SAPLING;
     565        1084 :     unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING;
     566             : 
     567             :     // As a sanity check, estimate and verify that the size of the transaction will be valid.
     568             :     // Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid.
     569        1084 :     size_t nTransparentOuts = 0;
     570        2236 :     for (const auto& t : recipients) {
     571        1152 :         if (t.IsTransparent()) {
     572          22 :             nTransparentOuts++;
     573          22 :             continue;
     574             :         }
     575        1130 :         if (IsValidPaymentAddress(t.getSapPaymentAddr())) {
     576        1130 :             mtx.sapData->vShieldedOutput.emplace_back();
     577             :         } else {
     578           0 :             return errorOut(strprintf("invalid recipient shielded address %s",
     579           0 :                     KeyIO::EncodePaymentAddress(t.getSapPaymentAddr())));
     580             :         }
     581             :     }
     582        1084 :     CTransaction tx(mtx);
     583        1084 :     size_t txsize = tx.GetTotalSize() + CTXOUT_REGULAR_SIZE * nTransparentOuts;
     584        1084 :     if (fromTaddr) {
     585        1047 :         txsize += CTXIN_SPEND_DUST_SIZE;
     586        1047 :         txsize += CTXOUT_REGULAR_SIZE;      // There will probably be taddr change
     587             :     }
     588        1084 :     if (txsize > max_tx_size) {
     589           0 :         return errorOut(strprintf("Too many outputs, size of raw transaction would be larger than limit of %d bytes", max_tx_size));
     590             :     }
     591        1084 :     return OperationResult(true);
     592             : }

Generated by: LCOV version 1.14