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 : }
|