Line data Source code
1 : // Copyright (c) 2016-2020 The ZCash developers
2 : // Copyright (c) 2021 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "sapling/saplingscriptpubkeyman.h"
7 :
8 : #include "chain.h" // for CBlockIndex
9 : #include "primitives/transaction.h"
10 : #include "consensus/params.h"
11 : #include "primitives/block.h"
12 : #include "sapling/incrementalmerkletree.h"
13 : #include "uint256.h"
14 : #include "validation.h" // for ReadBlockFromDisk()
15 : #include "wallet/wallet.h"
16 : #include <algorithm>
17 : #include <map>
18 : #include <string>
19 : #include <vector>
20 :
21 144 : void SaplingScriptPubKeyMan::AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid)
22 : {
23 144 : AssertLockHeld(wallet->cs_wallet);
24 144 : mapTxSaplingNullifiers.emplace(nullifier, wtxid);
25 :
26 144 : std::pair<TxNullifiers::iterator, TxNullifiers::iterator> range;
27 144 : range = mapTxSaplingNullifiers.equal_range(nullifier);
28 144 : wallet->SyncMetaDataN(range);
29 144 : }
30 :
31 2 : bool SaplingScriptPubKeyMan::IsSaplingSpent(const SaplingOutPoint& op) const
32 : {
33 2 : for (auto& i : mapSaplingNullifiersToNotes) {
34 2 : SaplingOutPoint iOp = i.second;
35 2 : if (iOp == op) {
36 2 : return IsSaplingSpent(i.first);
37 : }
38 : }
39 0 : return false;
40 : }
41 :
42 1215 : bool SaplingScriptPubKeyMan::IsSaplingSpent(const uint256& nullifier) const {
43 2430 : LOCK(wallet->cs_wallet); // future: move to AssertLockHeld()
44 1215 : std::pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range;
45 1215 : range = mapTxSaplingNullifiers.equal_range(nullifier);
46 :
47 1215 : for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) {
48 273 : const uint256& wtxid = it->second;
49 273 : std::map<uint256, CWalletTx>::const_iterator mit = wallet->mapWallet.find(wtxid);
50 273 : if (mit != wallet->mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) {
51 273 : return true; // Spent
52 : }
53 : }
54 942 : return false;
55 : }
56 :
57 42319 : void SaplingScriptPubKeyMan::UpdateSaplingNullifierNoteMapForBlock(const CBlock *pblock) {
58 42319 : LOCK(wallet->cs_wallet);
59 :
60 344035 : for (const auto& tx : pblock->vtx) {
61 301716 : auto it = wallet->mapWallet.find(tx->GetHash());
62 301716 : if (it != wallet->mapWallet.end()) {
63 169767 : UpdateSaplingNullifierNoteMapWithTx(it->second);
64 : }
65 : }
66 42319 : }
67 :
68 : // Updates noteData and mapSaplingNullifiersToNotes directly
69 897 : void SaplingScriptPubKeyMan::UpdateSaplingNullifierNoteMap(SaplingNoteData& nd, const SaplingOutPoint& op, const Optional<uint256>& nullifier)
70 : {
71 1794 : AssertLockHeld(wallet->cs_wallet);
72 897 : nd.nullifier = nullifier;
73 897 : if (nullifier) mapSaplingNullifiersToNotes[*nullifier] = op;
74 897 : }
75 :
76 : /**
77 : * Update mapSaplingNullifiersToNotes, computing the nullifier from a cached witness if necessary.
78 : */
79 169769 : void SaplingScriptPubKeyMan::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) {
80 169769 : LOCK(wallet->cs_wallet);
81 :
82 170720 : for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) {
83 951 : const SaplingOutPoint& op = item.first;
84 951 : SaplingNoteData& nd = item.second;
85 :
86 1848 : if (nd.witnesses.empty() || !nd.IsMyNote()) {
87 : // If there are no witnesses, erase the nullifier and associated mapping.
88 54 : if (nd.nullifier) {
89 0 : mapSaplingNullifiersToNotes.erase(item.second.nullifier.get());
90 : }
91 54 : nd.nullifier = nullopt;
92 : } else {
93 897 : const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk);
94 897 : uint64_t position = nd.witnesses.front().position();
95 897 : auto extfvk = wallet->mapSaplingFullViewingKeys.at(ivk);
96 897 : OutputDescription output = wtx.tx->sapData->vShieldedOutput[op.n];
97 1794 : auto optPlaintext = libzcash::SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cmu);
98 897 : if (!optPlaintext) {
99 : // An item in mapSaplingNoteData must have already been successfully decrypted,
100 : // otherwise the item would not exist in the first place.
101 0 : assert(false);
102 : }
103 1794 : auto optNote = optPlaintext.get().note(ivk);
104 897 : if (!optNote) {
105 0 : assert(false);
106 : }
107 897 : auto optNullifier = optNote.get().nullifier(extfvk.fvk, position);
108 897 : if (!optNullifier) {
109 : // This should not happen. If it does, maybe the position has been corrupted or miscalculated?
110 0 : assert(false);
111 : }
112 897 : UpdateSaplingNullifierNoteMap(nd, op, optNullifier.get());
113 : }
114 : }
115 169769 : }
116 :
117 : /**
118 : * Update mapSaplingNullifiersToNotes with the cached nullifiers in this tx.
119 : */
120 403237 : void SaplingScriptPubKeyMan::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx)
121 : {
122 403237 : {
123 403237 : LOCK(wallet->cs_wallet);
124 404381 : for (const mapSaplingNoteData_t::value_type& item : wtx.mapSaplingNoteData) {
125 1144 : if (item.second.nullifier) {
126 21 : mapSaplingNullifiersToNotes[*item.second.nullifier] = item.first;
127 : }
128 : }
129 : }
130 403237 : }
131 :
132 : template<typename NoteDataMap>
133 24249 : void CopyPreviousWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize)
134 : {
135 50767 : for (auto& item : noteDataMap) {
136 26518 : auto* nd = &(item.second);
137 : // skip externally sent notes
138 26518 : if (!nd->IsMyNote()) continue;
139 : // Only increment witnesses that are behind the current height
140 26207 : if (nd->witnessHeight < indexHeight) {
141 : // Check the validity of the cache
142 : // The only time a note witnessed above the current height
143 : // would be invalid here is during a reindex when blocks
144 : // have been decremented, and we are incrementing the blocks
145 : // immediately after.
146 12475 : assert(nWitnessCacheSize >= (int64_t) nd->witnesses.size());
147 : // Witnesses being incremented should always be either -1
148 : // (never incremented or decremented) or one below indexHeight
149 12475 : assert((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight - 1));
150 : // Copy the witness for the previous block if we have one
151 12475 : if (nd->witnesses.size() > 0) {
152 9668 : nd->witnesses.push_front(nd->witnesses.front());
153 : }
154 12475 : if (nd->witnesses.size() > WITNESS_CACHE_SIZE) {
155 26856 : nd->witnesses.pop_back();
156 : }
157 : }
158 : }
159 24249 : }
160 :
161 831966 : void AppendNoteCommitment(SaplingNoteData* nd, int indexHeight, int64_t nWitnessCacheSize, const uint256& note_commitment)
162 : {
163 : // skip externally sent notes
164 831966 : if (!nd->IsMyNote()) return;
165 : // No empty witnesses can reach here. Before any append, the note must be already witnessed.
166 831606 : if (nd->witnessHeight < indexHeight && nd->witnesses.size() > 0) {
167 : // Check the validity of the cache
168 : // See comment in CopyPreviousWitnesses about validity.
169 199525 : assert(nWitnessCacheSize >= (int64_t) nd->witnesses.size());
170 199525 : nd->witnesses.front().append(note_commitment);
171 : }
172 : }
173 :
174 : template<typename Witness>
175 1177 : void WitnessNoteIfMine(SaplingNoteData* nd,
176 : int indexHeight,
177 : int64_t nWitnessCacheSize,
178 : const Witness& witness)
179 : {
180 1177 : assert(nd);
181 : // skip externally sent and already witnessed notes
182 1177 : if (!nd->IsMyNote() || nd->witnessHeight >= indexHeight) return;
183 996 : if (!nd->witnesses.empty()) {
184 : // We think this can happen because we write out the
185 : // witness cache state after every block increment or
186 : // decrement, but the block index itself is written in
187 : // batches. So if the node crashes in between these two
188 : // operations, it is possible for IncrementNoteWitnesses
189 : // to be called again on previously-cached blocks. This
190 : // doesn't affect existing cached notes because of the
191 : // NoteData::witnessHeight checks. See #1378 for details.
192 0 : LogPrintf("Inconsistent witness cache state found\n- Cache size: %d\n- Top (height %d): %s\n- New (height %d): %s\n",
193 0 : nd->witnesses.size(), nd->witnessHeight,
194 0 : nd->witnesses.front().root().GetHex(),
195 : indexHeight,
196 0 : witness.root().GetHex());
197 0 : nd->witnesses.clear();
198 : }
199 996 : nd->witnesses.push_front(witness);
200 : // Set height to one less than pindex so it gets incremented
201 996 : nd->witnessHeight = indexHeight - 1;
202 : // Check the validity of the cache
203 996 : assert(nWitnessCacheSize >= (int64_t) nd->witnesses.size());
204 : }
205 :
206 : template <typename NoteDataMap>
207 25426 : void UpdateWitnessHeights(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize)
208 : {
209 54977 : for (auto& item : noteDataMap) {
210 29551 : auto* nd = &(item.second);
211 : // skip externally sent notes
212 29551 : if (!nd->IsMyNote()) continue;
213 29159 : if (nd->witnessHeight < indexHeight) {
214 13471 : nd->witnessHeight = indexHeight;
215 : // Check the validity of the cache
216 : // See comment in CopyPreviousWitnesses about validity.
217 13471 : assert(nWitnessCacheSize >= (int64_t) nd->witnesses.size());
218 : }
219 : }
220 25426 : }
221 :
222 5 : bool SaplingScriptPubKeyMan::BuildWitnessChain(const CBlockIndex* pTargetBlock, const Consensus::Params& params, std::string& errorStr)
223 : {
224 : // If V5 is not enforced building the witness cache is useless
225 5 : if (!params.NetworkUpgradeActive(chainActive.Height(), Consensus::UPGRADE_V5_0)) {
226 : return true;
227 : }
228 :
229 7 : LOCK2(cs_main, wallet->cs_wallet);
230 : // Target is the last block we want to invalidate
231 1 : rollbackTargetHeight = pTargetBlock->nHeight;
232 1 : cachedWitnessMap.clear();
233 :
234 : // Find the oldest sapling note
235 1 : int minHeight = INT_MAX;
236 227 : for (auto& it : wallet->mapWallet) {
237 226 : CWalletTx& wtx = it.second;
238 226 : if (wtx.mapSaplingNoteData.empty()) continue;
239 : // Skip abandoned and conflicted txs for which the block_height is not defined (more precisely it it set to 0 by default)
240 0 : if (wtx.m_confirm.status != CWalletTx::CONFIRMED) continue;
241 0 : minHeight = std::min(wtx.m_confirm.block_height, minHeight);
242 : }
243 :
244 : // Read blocks from the disk from chaintip to the minimum found height
245 2 : std::vector<CBlock> cblocks;
246 1 : const CBlockIndex* pIndex = GetChainTip();
247 1 : int currentHeight = GetChainTip()->nHeight;
248 1 : while (currentHeight >= minHeight) {
249 0 : CBlock cblock;
250 0 : ReadBlockFromDisk(cblock, pIndex);
251 0 : cblocks.insert(cblocks.begin(), cblock);
252 0 : pIndex = pIndex->pprev;
253 0 : currentHeight = pIndex->nHeight;
254 : }
255 :
256 2 : SaplingMerkleTree initialSaplingTree = SaplingMerkleTree();
257 : // Load the SaplingMerkleTree for the block before the oldest note (if the hash is zero then continue with an empty merkle tree)
258 1 : if (!(pIndex->hashFinalSaplingRoot == UINT256_ZERO) && !pcoinsTip->GetSaplingAnchorAt(pIndex->hashFinalSaplingRoot, initialSaplingTree)) {
259 1 : errorStr = "Cannot fetch the sapling anchor!";
260 : return false;
261 : }
262 : // Finally build the witness cache for each sapling note of your wallet
263 1 : int height = minHeight;
264 1 : for (CBlock& block : cblocks) {
265 : // Finally build the witness cache for each sapling note
266 0 : std::vector<uint256> noteCommitments;
267 0 : std::vector<SaplingNoteData*> inBlockArrivingNotes;
268 0 : for (const auto& tx : block.vtx) {
269 0 : const auto& hash = tx->GetHash();
270 0 : auto it = wallet->mapWallet.find(hash);
271 0 : bool txIsOurs = it != wallet->mapWallet.end();
272 :
273 0 : if (!tx->IsShieldedTx()) continue;
274 0 : for (uint32_t i = 0; i < tx->sapData->vShieldedOutput.size(); i++) {
275 0 : const auto& cmu = tx->sapData->vShieldedOutput[i].cmu;
276 0 : noteCommitments.emplace_back(cmu);
277 0 : for (auto& item : inBlockArrivingNotes) {
278 0 : item->witnesses.front().append(cmu);
279 : }
280 0 : initialSaplingTree.append(cmu);
281 0 : if (txIsOurs) {
282 0 : CWalletTx* wtx = &it->second;
283 0 : auto ndIt = wtx->mapSaplingNoteData.find({hash, i});
284 0 : if (ndIt != wtx->mapSaplingNoteData.end()) {
285 0 : SaplingNoteData* nd = &ndIt->second;
286 0 : nd->witnesses.push_front(initialSaplingTree.witness());
287 0 : inBlockArrivingNotes.emplace_back(nd);
288 : }
289 : }
290 : }
291 : }
292 0 : for (auto& it2 : cachedWitnessMap) {
293 : // Don't duplicate if the block is too old
294 0 : if (height >= rollbackTargetHeight) {
295 0 : it2.second.emplace_front(it2.second.front());
296 : }
297 0 : for (auto& noteComm : noteCommitments) {
298 0 : it2.second.front().append(noteComm);
299 : }
300 : }
301 0 : for (auto nd : inBlockArrivingNotes) {
302 0 : if (nd->nullifier) {
303 0 : std::list<SaplingWitness> witnesses;
304 0 : witnesses.push_front(nd->witnesses.front());
305 0 : cachedWitnessMap.emplace(*(nd->nullifier), witnesses);
306 : }
307 : }
308 0 : height++;
309 : }
310 1 : return true;
311 : }
312 :
313 42368 : void SaplingScriptPubKeyMan::IncrementNoteWitnesses(const CBlockIndex* pindex,
314 : const CBlock* pblock,
315 : SaplingMerkleTree& saplingTreeRes)
316 : {
317 42368 : LOCK(wallet->cs_wallet);
318 42368 : int chainHeight = pindex->nHeight;
319 :
320 : // Set the update cache flag.
321 42368 : int64_t prevWitCacheSize = nWitnessCacheSize;
322 42368 : if (nWitnessCacheSize < WITNESS_CACHE_SIZE) {
323 18755 : nWitnessCacheSize += 1;
324 18755 : nWitnessCacheNeedsUpdate = true;
325 : }
326 :
327 : // 1) Loop over the block txs and gather the note commitments ordered.
328 : // If the wtx is from this wallet, witness it and append the following block note commitments on top.
329 84736 : std::vector<uint256> noteCommitments;
330 42368 : std::vector<std::pair<CWalletTx*, SaplingNoteData*>> inBlockArrivingNotes;
331 344114 : for (const auto& tx : pblock->vtx) {
332 304379 : if (!tx->IsShieldedTx()) continue;
333 :
334 1807 : const auto& hash = tx->GetHash();
335 1807 : auto it = wallet->mapWallet.find(hash);
336 1807 : bool txIsOurs = it != wallet->mapWallet.end();
337 :
338 3853 : for (uint32_t i = 0; i < tx->sapData->vShieldedOutput.size(); i++) {
339 2046 : const auto& cmu = tx->sapData->vShieldedOutput[i].cmu;
340 2046 : noteCommitments.emplace_back(cmu);
341 :
342 : // Append note commitment to the in-block wallet's notes.
343 : // This is processed here because we already looked for the wtx on
344 : // the WitnessNoteIfMine call and only need to append the follow-up block notes,
345 : // not every block note (check below).
346 193053 : for (auto& item : inBlockArrivingNotes) {
347 191007 : ::AppendNoteCommitment(item.second, chainHeight, nWitnessCacheSize, cmu);
348 : }
349 :
350 : // If tx is from this wallet, try to witness the note for the first time (if exists).
351 : // And add it to the in-block arriving txs.
352 2046 : saplingTreeRes.append(cmu);
353 2046 : if (txIsOurs) {
354 1226 : CWalletTx* wtx = &it->second;
355 1226 : auto ndIt = wtx->mapSaplingNoteData.find({hash, i});
356 1226 : if (ndIt != wtx->mapSaplingNoteData.end()) {
357 1177 : SaplingNoteData* nd = &ndIt->second;
358 1177 : ::WitnessNoteIfMine(nd, chainHeight, nWitnessCacheSize, saplingTreeRes.witness());
359 1177 : inBlockArrivingNotes.emplace_back(std::make_pair(wtx, nd));
360 : }
361 : }
362 : }
363 : }
364 :
365 : // 2) Mark already sync wtx, so we don't process them again.
366 43545 : for (auto& item : inBlockArrivingNotes) {
367 1177 : ::UpdateWitnessHeights(item.first->mapSaplingNoteData, chainHeight, nWitnessCacheSize);
368 : }
369 :
370 : // 3) Loop over the shield txs in the wallet's map (excluding the wtx arriving in this block) and for each tx:
371 : // a) Copy the previous witness.
372 : // b) Append all new notes commitments
373 : // c) Update witness last processed height
374 11236833 : for (auto& it : wallet->mapWallet) {
375 11194424 : CWalletTx& wtx = it.second;
376 11194424 : if (!wtx.mapSaplingNoteData.empty()) {
377 : // Create copy of the previous witness (verifying pre-arriving block witness cache size)
378 24249 : ::CopyPreviousWitnesses(wtx.mapSaplingNoteData, chainHeight, prevWitCacheSize);
379 :
380 : // Append new notes commitments.
381 661656 : for (auto& noteComm : noteCommitments) {
382 1278364 : for (auto& item : wtx.mapSaplingNoteData) {
383 640959 : AppendNoteCommitment(&(item.second), chainHeight, nWitnessCacheSize, noteComm);
384 : }
385 : }
386 :
387 : // Set last processed height.
388 24249 : ::UpdateWitnessHeights(wtx.mapSaplingNoteData, chainHeight, nWitnessCacheSize);
389 : }
390 : }
391 :
392 : // For performance reasons, we write out the witness cache in
393 : // CWallet::SetBestChain() (which also ensures that overall consistency
394 : // of the wallet.dat is maintained).
395 42368 : }
396 : /*
397 : * Clear and eventually reset each witness of noteDataMap with the corresponding front-value of cachedWitnessMap, indexHeight is the blockHeight being invalidated
398 : */
399 0 : void ResetNoteWitnesses(std::map<SaplingOutPoint, SaplingNoteData>& noteDataMap, std::map<uint256, std::list<SaplingWitness>>& cachedWitnessMap, int indexHeight)
400 : {
401 : // For each note that you own:
402 0 : for (auto& item : noteDataMap) {
403 0 : auto& nd = (item.second);
404 : // skip externally sent notes
405 0 : if (!nd.IsMyNote()) continue;
406 : // Clear the cache
407 0 : nd.witnesses.clear();
408 : // The withnessHeight must be EITHER -1 or equal to the block indexHeight
409 : // The case in which indexHeight > witnessHeight is due to conflicted notes, which are irrelevant
410 : // TODO: Allow invalidating blocks only if there are not conflicted txs?
411 0 : if (nd.witnessHeight <= indexHeight) {
412 0 : assert((nd.witnessHeight == -1) || (nd.witnessHeight == indexHeight));
413 : }
414 : // Decrease the witnessHeight
415 0 : nd.witnessHeight = indexHeight - 1;
416 0 : if (nd.nullifier && cachedWitnessMap.at(*nd.nullifier).size() > 0) {
417 : // Update the witness value with the cached one
418 0 : nd.witnesses.push_front(cachedWitnessMap.at(*nd.nullifier).front());
419 0 : cachedWitnessMap.at(*nd.nullifier).pop_front();
420 : }
421 : }
422 0 : }
423 :
424 :
425 : template<typename NoteDataMap>
426 3893 : void DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize)
427 : {
428 4130 : for (auto& item : noteDataMap) {
429 237 : auto* nd = &(item.second);
430 : // skip externally sent notes
431 237 : if (!nd->IsMyNote()) continue;
432 : // Only decrement witnesses that are not above the current height
433 235 : if (nd->witnessHeight <= indexHeight) {
434 : // Check the validity of the cache
435 : // See comment below (this would be invalid if there were a
436 : // prior decrement).
437 13 : assert(nWitnessCacheSize >= (int64_t) nd->witnesses.size());
438 : // Witnesses being decremented should always be either -1
439 : // (never incremented or decremented) or equal to the height
440 : // of the block being removed (indexHeight)
441 13 : assert((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight));
442 13 : if (nd->witnesses.size() > 0) {
443 12 : nd->witnesses.pop_front();
444 : }
445 : // indexHeight is the height of the block being removed, so
446 : // the new witness cache height is one below it.
447 13 : nd->witnessHeight = indexHeight - 1;
448 : }
449 : // Check the validity of the cache
450 : // Technically if there are notes witnessed above the current
451 : // height, their cache will now be invalid (relative to the new
452 : // value of nWitnessCacheSize). However, this would only occur
453 : // during a reindex, and by the time the reindex reaches the tip
454 : // of the chain again, the existing witness caches will be valid
455 : // again.
456 : // We don't set nWitnessCacheSize to zero at the start of the
457 : // reindex because the on-disk blocks had already resulted in a
458 : // chain that didn't trigger the assertion below.
459 235 : if (nd->witnessHeight < indexHeight) {
460 : // Subtract 1 to compare to what nWitnessCacheSize will be after
461 : // decrementing.
462 13 : assert((nWitnessCacheSize - 1) >= (int64_t) nd->witnesses.size());
463 : }
464 : }
465 3893 : }
466 :
467 188 : void SaplingScriptPubKeyMan::DecrementNoteWitnesses(const CBlockIndex* pindex)
468 : {
469 188 : assert(pindex);
470 235 : LOCK(wallet->cs_wallet);
471 188 : int nChainHeight = pindex->nHeight;
472 : // if the targetHeight is different from -1 we have a cache to use
473 188 : if (rollbackTargetHeight != -1) {
474 31780 : for (std::pair<const uint256, CWalletTx>& wtxItem : wallet->mapWallet) {
475 31640 : if (!wtxItem.second.mapSaplingNoteData.empty()) {
476 : // For each sapling note that you own reset the current witness with the cached one
477 0 : ResetNoteWitnesses(wtxItem.second.mapSaplingNoteData, cachedWitnessMap, nChainHeight);
478 : }
479 : }
480 140 : nWitnessCacheSize = 1;
481 140 : nWitnessCacheNeedsUpdate = true;
482 : // If we reached the target height empty the cache and reset the target height to -1
483 : // Remember that the targetHeight is indeed the last block we want to invalidate
484 140 : if (rollbackTargetHeight == pindex->nHeight) {
485 1 : cachedWitnessMap.clear();
486 1 : rollbackTargetHeight = -1;
487 : }
488 280 : return;
489 : }
490 :
491 3941 : for (std::pair<const uint256, CWalletTx>& wtxItem : wallet->mapWallet) {
492 3893 : ::DecrementNoteWitnesses(wtxItem.second.mapSaplingNoteData, nChainHeight, nWitnessCacheSize);
493 : }
494 48 : nWitnessCacheSize -= 1;
495 48 : nWitnessCacheNeedsUpdate = true;
496 : // TODO: If nWitnessCache is zero, we need to regenerate the caches (#1302)
497 48 : if (Params().IsRegTestNet()) { // throw an error in regtest to be able to catch it from the sapling_wallet_tests.cpp unit test.
498 48 : if (nWitnessCacheSize <= 0) throw std::runtime_error("nWitnessCacheSize > 0");
499 0 : } else assert(nWitnessCacheSize > 0);
500 :
501 : // For performance reasons, we write out the witness cache in
502 : // CWallet::SetBestChain() (which also ensures that overall consistency
503 : // of the wallet.dat is maintained).
504 : }
505 :
506 : /**
507 : * Finds all output notes in the given transaction that have been sent to
508 : * SaplingPaymentAddresses in this wallet.
509 : *
510 : * It should never be necessary to call this method with a CWalletTx, because
511 : * the result of FindMySaplingNotes (for the addresses available at the time) will
512 : * already have been cached in CWalletTx.mapSaplingNoteData.
513 : */
514 647505 : std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> SaplingScriptPubKeyMan::FindMySaplingNotes(const CTransaction &tx) const
515 : {
516 : // First check that this tx is a Shielded tx.
517 647505 : if (!tx.IsShieldedTx()) {
518 643739 : return {};
519 : }
520 :
521 651271 : LOCK(wallet->cs_KeyStore);
522 3766 : const uint256& hash = tx.GetHash();
523 :
524 7532 : mapSaplingNoteData_t noteData;
525 3766 : SaplingIncomingViewingKeyMap viewingKeysToAdd;
526 :
527 : // Protocol Spec: 4.19 Block Chain Scanning (Sapling)
528 7944 : for (uint32_t i = 0; i < tx.sapData->vShieldedOutput.size(); ++i) {
529 4178 : const OutputDescription output = tx.sapData->vShieldedOutput[i];
530 8319 : for (auto it = wallet->mapSaplingFullViewingKeys.begin(); it != wallet->mapSaplingFullViewingKeys.end(); ++it) {
531 6197 : libzcash::SaplingIncomingViewingKey ivk = it->first;
532 6197 : auto result = libzcash::SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cmu);
533 6197 : if (!result) {
534 4141 : continue;
535 : }
536 :
537 : // Check if we already have it.
538 4112 : Optional<libzcash::SaplingPaymentAddress> address = ivk.address(result.get().d);
539 4112 : if (address && wallet->mapSaplingIncomingViewingKeys.count(address.get()) == 0) {
540 0 : viewingKeysToAdd[address.get()] = ivk;
541 : }
542 : // We don't cache the nullifier here as computing it requires knowledge of the note position
543 : // in the commitment tree, which can only be determined when the transaction has been mined.
544 2056 : SaplingOutPoint op {hash, i};
545 4112 : SaplingNoteData nd;
546 2056 : nd.ivk = ivk;
547 2056 : nd.amount = result->value();
548 2056 : nd.address = address;
549 2056 : const auto& memo = result->memo();
550 : // don't save empty memo (starting with 0xF6)
551 2056 : if (memo[0] < 0xF6) {
552 23 : nd.memo = memo;
553 : }
554 4112 : noteData.insert(std::make_pair(op, nd));
555 2056 : break;
556 : }
557 : }
558 :
559 3766 : return std::make_pair(noteData, viewingKeysToAdd);
560 : }
561 :
562 19 : std::vector<libzcash::SaplingPaymentAddress> SaplingScriptPubKeyMan::FindMySaplingAddresses(const CTransaction& tx) const
563 : {
564 38 : LOCK(wallet->cs_KeyStore);
565 19 : std::vector<libzcash::SaplingPaymentAddress> ret;
566 19 : if (!tx.sapData) return ret;
567 :
568 : // Protocol Spec: 4.19 Block Chain Scanning (Sapling)
569 39 : for (const OutputDescription& output : tx.sapData->vShieldedOutput) {
570 649 : for (auto it = wallet->mapSaplingFullViewingKeys.begin(); it != wallet->mapSaplingFullViewingKeys.end(); ++it) {
571 629 : libzcash::SaplingIncomingViewingKey ivk = it->first;
572 638 : auto result = libzcash::SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cmu);
573 629 : if (!result) {
574 620 : continue;
575 : }
576 18 : Optional<libzcash::SaplingPaymentAddress> address = ivk.address(result.get().d);
577 9 : if (address && wallet->mapSaplingIncomingViewingKeys.count(address.get()) != 0) {
578 9 : ret.emplace_back(address.get());
579 : }
580 : }
581 : }
582 19 : return ret;
583 : }
584 :
585 1 : void SaplingScriptPubKeyMan::GetNotes(const std::vector<SaplingOutPoint>& saplingOutpoints,
586 : std::vector<SaplingNoteEntry>& saplingEntriesRet) const
587 : {
588 6 : for (const auto& outpoint : saplingOutpoints) {
589 5 : const auto* wtx = wallet->GetWalletTx(outpoint.hash);
590 5 : if (!wtx) throw std::runtime_error("No transaction available for hash " + outpoint.hash.GetHex());
591 15 : const int depth = WITH_LOCK(wallet->cs_wallet, return wtx->GetDepthInMainChain(); );
592 5 : const auto& it = wtx->mapSaplingNoteData.find(outpoint);
593 5 : if (it != wtx->mapSaplingNoteData.end()) {
594 5 : const SaplingOutPoint& op = it->first;
595 5 : const SaplingNoteData& nd = it->second;
596 :
597 : // skip sent notes
598 5 : if (!nd.IsMyNote()) continue;
599 :
600 : // recover plaintext and address
601 10 : auto optNotePtAndAddress = wtx->DecryptSaplingNote(op);
602 5 : assert(static_cast<bool>(optNotePtAndAddress));
603 :
604 5 : const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk);
605 5 : const libzcash::SaplingNotePlaintext& notePt = optNotePtAndAddress->first;
606 5 : const libzcash::SaplingPaymentAddress& pa = optNotePtAndAddress->second;
607 10 : auto note = notePt.note(ivk).get();
608 :
609 5 : saplingEntriesRet.emplace_back(op, pa, note, notePt.memo(), depth);
610 : }
611 : }
612 1 : }
613 :
614 : /**
615 : * Find notes in the wallet filtered by payment address, min depth and ability to spend and if the notes are locked.
616 : * These notes are decrypted and added to the output parameter vector, saplingEntries.
617 : */
618 131 : void SaplingScriptPubKeyMan::GetFilteredNotes(
619 : std::vector<SaplingNoteEntry>& saplingEntries,
620 : Optional<libzcash::SaplingPaymentAddress>& address,
621 : int minDepth,
622 : bool ignoreSpent,
623 : bool requireSpendingKey,
624 : bool ignoreLocked) const
625 : {
626 131 : std::set<libzcash::PaymentAddress> filterAddresses;
627 :
628 231 : if (address && IsValidPaymentAddress(*address)) {
629 100 : filterAddresses.insert(*address);
630 : }
631 :
632 131 : GetFilteredNotes(saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey, ignoreLocked);
633 131 : }
634 :
635 : /**
636 : * Find notes in the wallet filtered by payment addresses, min depth, max depth,
637 : * if the note is spent, if a spending key is required, and if the notes are locked.
638 : * These notes are decrypted and added to the output parameter vector, saplingEntries.
639 : */
640 142 : void SaplingScriptPubKeyMan::GetFilteredNotes(
641 : std::vector<SaplingNoteEntry>& saplingEntries,
642 : std::set<libzcash::PaymentAddress>& filterAddresses,
643 : int minDepth,
644 : int maxDepth,
645 : bool ignoreSpent,
646 : bool requireSpendingKey,
647 : bool ignoreLocked) const
648 : {
649 142 : LOCK(wallet->cs_wallet);
650 :
651 10725 : for (auto& p : wallet->mapWallet) {
652 10583 : const CWalletTx& wtx = p.second;
653 :
654 : // Filter coinbase/coinstakes transactions that don't have Sapling outputs
655 14043 : if ((wtx.IsCoinBase() || wtx.IsCoinStake()) && wtx.mapSaplingNoteData.empty()) {
656 9842 : continue;
657 : }
658 :
659 : // Filter the transactions before checking for notes
660 785 : const int depth = wtx.GetDepthInMainChain();
661 785 : if (!IsFinalTx(wtx.tx, wallet->GetLastBlockHeight() + 1, GetAdjustedTime()) ||
662 785 : depth < minDepth || depth > maxDepth) {
663 44 : continue;
664 : }
665 :
666 2054 : for (const auto& it : wtx.mapSaplingNoteData) {
667 1313 : const SaplingOutPoint& op = it.first;
668 1313 : const SaplingNoteData& nd = it.second;
669 : // skip sent notes
670 1608 : if (!nd.IsMyNote()) continue;
671 :
672 : // recover plaintext and address
673 2045 : auto optNotePtAndAddress = wtx.DecryptSaplingNote(op);
674 1170 : assert(static_cast<bool>(optNotePtAndAddress));
675 :
676 1170 : const libzcash::SaplingIncomingViewingKey& ivk = *(nd.ivk);
677 1170 : const libzcash::SaplingNotePlaintext& notePt = optNotePtAndAddress->first;
678 1170 : const libzcash::SaplingPaymentAddress& pa = optNotePtAndAddress->second;
679 3215 : auto note = notePt.note(ivk).get();
680 :
681 : // skip notes which belong to a different payment address in the wallet
682 2630 : if (!(filterAddresses.empty() || filterAddresses.count(pa))) {
683 295 : continue;
684 : }
685 :
686 1122 : if (ignoreSpent && nd.nullifier && IsSaplingSpent(*nd.nullifier)) {
687 240 : continue;
688 : }
689 :
690 : // skip notes which cannot be spent
691 882 : if (requireSpendingKey && !HaveSpendingKeyForPaymentAddress(pa)) {
692 5 : continue;
693 : }
694 :
695 : // skip locked notes.
696 877 : if (ignoreLocked && wallet->IsLockedNote(op)) {
697 2 : continue;
698 : }
699 :
700 875 : saplingEntries.emplace_back(op, pa, note, notePt.memo(), depth);
701 : }
702 : }
703 142 : }
704 :
705 : /* Return list of available notes and locked notes grouped by sapling address. */
706 0 : std::map<libzcash::SaplingPaymentAddress, std::vector<SaplingNoteEntry>> SaplingScriptPubKeyMan::ListNotes() const
707 : {
708 0 : std::vector<SaplingNoteEntry> notes;
709 0 : Optional<libzcash::SaplingPaymentAddress> dummy = nullopt;
710 0 : GetFilteredNotes(notes, dummy, 1, true, true, false);
711 :
712 0 : std::map<libzcash::SaplingPaymentAddress, std::vector<SaplingNoteEntry>> result;
713 0 : for (const auto& note : notes) {
714 0 : result[note.address].emplace_back(std::move(note));
715 : }
716 0 : return result;
717 : }
718 :
719 : Optional<libzcash::SaplingPaymentAddress>
720 0 : SaplingScriptPubKeyMan::GetAddressFromInputIfPossible(const uint256& txHash, int index) const
721 : {
722 0 : const CWalletTx* wtx = wallet->GetWalletTx(txHash);
723 0 : if (!wtx) return nullopt;
724 0 : return GetAddressFromInputIfPossible(wtx, index);
725 : }
726 :
727 : Optional<libzcash::SaplingPaymentAddress>
728 0 : SaplingScriptPubKeyMan::GetAddressFromInputIfPossible(const CWalletTx* wtx, int index) const
729 : {
730 0 : if (!wtx->tx->sapData || wtx->tx->sapData->vShieldedSpend.empty()) return nullopt;
731 :
732 0 : SpendDescription spendDesc = wtx->tx->sapData->vShieldedSpend.at(index);
733 0 : if (!IsSaplingNullifierFromMe(spendDesc.nullifier)) return nullopt;
734 :
735 : // Knowing that the spent note is from us, we can get the address from
736 0 : const SaplingOutPoint& outPoint = mapSaplingNullifiersToNotes.at(spendDesc.nullifier);
737 0 : const CWalletTx& txPrev = wallet->mapWallet.at(outPoint.hash);
738 0 : return txPrev.mapSaplingNoteData.at(outPoint).address;
739 : }
740 :
741 191 : bool SaplingScriptPubKeyMan::IsSaplingNullifierFromMe(const uint256& nullifier) const
742 : {
743 191 : LOCK(wallet->cs_wallet);
744 191 : auto it = mapSaplingNullifiersToNotes.find(nullifier);
745 191 : return it != mapSaplingNullifiersToNotes.end() && wallet->mapWallet.count(it->second.hash);
746 : }
747 :
748 29 : std::set<std::pair<libzcash::PaymentAddress, uint256>> SaplingScriptPubKeyMan::GetNullifiersForAddresses(
749 : const std::set<libzcash::PaymentAddress> & addresses) const
750 : {
751 29 : AssertLockHeld(wallet->cs_wallet);
752 29 : std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet;
753 : // Sapling ivk -> list of addrs map
754 : // (There may be more than one diversified address for a given ivk.)
755 29 : std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap;
756 422 : for (const auto& addr : addresses) {
757 393 : auto saplingAddr = boost::get<libzcash::SaplingPaymentAddress>(&addr);
758 393 : if (saplingAddr != nullptr) {
759 393 : libzcash::SaplingIncomingViewingKey ivk;
760 393 : if (wallet->GetSaplingIncomingViewingKey(*saplingAddr, ivk))
761 392 : ivkMap[ivk].push_back(*saplingAddr);
762 : }
763 : }
764 1059 : for (const auto& txPair : wallet->mapWallet) {
765 1491 : for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) {
766 461 : const auto& noteData = noteDataPair.second;
767 :
768 : // Skip sent notes
769 461 : if (!noteData.IsMyNote()) continue;
770 428 : const libzcash::SaplingIncomingViewingKey& ivk = *(noteData.ivk);
771 :
772 428 : const auto& nullifier = noteData.nullifier;
773 431 : if (nullifier && ivkMap.count(ivk)) {
774 836 : for (const auto & addr : ivkMap[ivk]) {
775 418 : nullifierSet.insert(std::make_pair(addr, nullifier.get()));
776 : }
777 : }
778 : }
779 : }
780 29 : return nullifierSet;
781 : }
782 :
783 0 : Optional<libzcash::SaplingPaymentAddress> SaplingScriptPubKeyMan::GetOutPointAddress(const CWalletTx& tx, const SaplingOutPoint& op) const
784 : {
785 0 : auto it = tx.mapSaplingNoteData.find(op);
786 0 : if (it == tx.mapSaplingNoteData.end()) {
787 0 : return nullopt;
788 : }
789 0 : return it->second.address;
790 : }
791 :
792 0 : CAmount SaplingScriptPubKeyMan::GetOutPointValue(const CWalletTx& tx, const SaplingOutPoint& op) const
793 : {
794 0 : auto it = tx.mapSaplingNoteData.find(op);
795 0 : if (it == tx.mapSaplingNoteData.end()) {
796 : return 0;
797 : }
798 0 : return it->second.amount ? *(it->second.amount) : 0;
799 : }
800 :
801 0 : Optional<std::string> SaplingScriptPubKeyMan::GetOutPointMemo(const CWalletTx& tx, const SaplingOutPoint& op) const
802 : {
803 0 : auto it = tx.mapSaplingNoteData.find(op);
804 0 : if (it == tx.mapSaplingNoteData.end() || !static_cast<bool>(it->second.memo))
805 0 : return nullopt;
806 0 : auto& memo = *(it->second.memo);
807 0 : auto end = FindFirstNonZero(memo.rbegin(), memo.rend());
808 0 : if (memo[0] <= 0xf4) {
809 0 : std::string memoStr(memo.begin(), end.base());
810 0 : if (IsValidUTF8(memoStr)) return memoStr;
811 : }
812 : // non UTF-8 memo. Return as hex encoded raw memo.
813 0 : return HexStr(std::vector<unsigned char>(memo.begin(), end.base()));
814 : }
815 :
816 : Optional<std::pair<
817 : libzcash::SaplingNotePlaintext,
818 : libzcash::SaplingPaymentAddress>>
819 99 : SaplingScriptPubKeyMan::TryToRecoverNote(const CWalletTx& tx, const SaplingOutPoint& op)
820 : {
821 99 : const uint256& txId = tx.GetHash();
822 99 : assert(txId == op.hash);
823 : // Try to recover it with the ovks (either the common one, if t->shield tx, or the ones from the spends)
824 99 : std::set<uint256> ovks;
825 : // Get the common OVK for recovering t->shield outputs.
826 : // If not already databased, a new one will be generated from the HD seed (this throws an error if the
827 : // wallet is currently locked). As the ovk is created when the wallet is unlocked for sending a t->shield
828 : // tx for the first time, a failure to decode can happen only if this note was sent (from a t-addr)
829 : // using this wallet.dat on another computer (and never sent t->shield txes from this computer).
830 99 : if (!tx.tx->vin.empty()) {
831 46 : try {
832 92 : ovks.emplace(getCommonOVK());
833 0 : } catch (...) {
834 0 : LogPrintf("WARNING: No CommonOVK found. Some notes might not be correctly recovered. " /* Continued */
835 0 : "Unlock the wallet and call 'viewshieldtransaction %s' to fix.\n", txId.ToString());
836 : }
837 : } else {
838 134 : for (const auto& spend : tx.tx->sapData->vShieldedSpend) {
839 81 : const auto& it = mapSaplingNullifiersToNotes.find(spend.nullifier);
840 81 : if (it != mapSaplingNullifiersToNotes.end()) {
841 77 : const SaplingOutPoint& prevOut = it->second;
842 77 : const CWalletTx* txPrev = wallet->GetWalletTx(prevOut.hash);
843 77 : if (!txPrev) continue;
844 77 : const auto& itPrev = txPrev->mapSaplingNoteData.find(prevOut);
845 77 : if (itPrev != txPrev->mapSaplingNoteData.end()) {
846 77 : const SaplingNoteData& noteData = itPrev->second;
847 77 : if (!noteData.IsMyNote()) continue;
848 77 : libzcash::SaplingExtendedFullViewingKey extfvk;
849 77 : if (wallet->GetSaplingFullViewingKey(*(noteData.ivk), extfvk)) {
850 154 : ovks.emplace(extfvk.fvk.ovk);
851 : }
852 : }
853 : }
854 : }
855 : }
856 198 : return tx.RecoverSaplingNote(op, ovks);
857 : }
858 :
859 0 : isminetype SaplingScriptPubKeyMan::IsMine(const CWalletTx& wtx, const SaplingOutPoint& op) const
860 : {
861 0 : auto ndIt = wtx.mapSaplingNoteData.find(op);
862 0 : if (ndIt != wtx.mapSaplingNoteData.end() && ndIt->second.IsMyNote()) {
863 0 : const auto addr = ndIt->second.address;
864 0 : return (static_cast<bool>(addr) && HaveSpendingKeyForPaymentAddress(*addr)) ?
865 0 : ISMINE_SPENDABLE_SHIELDED : ISMINE_WATCH_ONLY_SHIELDED;
866 : }
867 : return ISMINE_NO;
868 : }
869 :
870 21370 : CAmount SaplingScriptPubKeyMan::GetCredit(const CWalletTx& tx, const isminefilter& filter, const bool fUnspent) const
871 : {
872 : // If we are not filtering shield data, return
873 21370 : if (!(filter & ISMINE_WATCH_ONLY_SHIELDED || filter & ISMINE_SPENDABLE_SHIELDED)) {
874 : return 0;
875 : }
876 :
877 21521 : if (!tx.tx->IsShieldedTx() || tx.tx->sapData->vShieldedOutput.empty()) {
878 21219 : return 0;
879 : }
880 : CAmount nCredit = 0;
881 330 : for (int i = 0; i < (int) tx.tx->sapData->vShieldedOutput.size(); ++i) {
882 179 : SaplingOutPoint op(tx.GetHash(), i);
883 179 : if (tx.mapSaplingNoteData.find(op) == tx.mapSaplingNoteData.end()) {
884 18 : continue;
885 : }
886 : // Obtain the noteData and check if the nullifier has being spent or not
887 268 : SaplingNoteData noteData = tx.mapSaplingNoteData.at(op);
888 :
889 : // Skip externally sent notes
890 233 : if (!noteData.IsMyNote()) continue;
891 :
892 : // The nullifier could be null if the wallet was locked when the noteData was created.
893 137 : if (noteData.nullifier &&
894 264 : (fUnspent && IsSaplingSpent(*noteData.nullifier))) {
895 30 : continue; // only unspent
896 : }
897 : // If we are filtering watch only or we have spend authority add the amount
898 107 : if ((filter & ISMINE_WATCH_ONLY_SHIELDED) || (noteData.address && HaveSpendingKeyForPaymentAddress(*noteData.address))) {
899 107 : nCredit += noteData.amount ? *noteData.amount : 0;
900 : }
901 : }
902 : return nCredit;
903 : }
904 :
905 4786 : CAmount SaplingScriptPubKeyMan::GetDebit(const CTransaction& tx, const isminefilter& filter) const
906 : {
907 : // If we are not filtering shield data, return
908 4786 : if (!(filter & ISMINE_WATCH_ONLY_SHIELDED || filter & ISMINE_SPENDABLE_SHIELDED)) {
909 : return 0;
910 : }
911 :
912 9572 : if (!tx.IsShieldedTx() || tx.sapData->vShieldedSpend.empty()) {
913 4537 : return 0;
914 : }
915 249 : CAmount nDebit = 0;
916 595 : for (const SpendDescription& spend : tx.sapData->vShieldedSpend) {
917 346 : const auto &it = mapSaplingNullifiersToNotes.find(spend.nullifier);
918 346 : if (it != mapSaplingNullifiersToNotes.end()) {
919 : // If we have the spend nullifier, it means that this input is ours.
920 : // The transaction (and decrypted note data) has been added to the wallet.
921 151 : const SaplingOutPoint& op = it->second;
922 151 : auto wit = wallet->mapWallet.find(op.hash);
923 151 : assert(wit != wallet->mapWallet.end());
924 151 : const auto& wtx = wit->second;
925 151 : auto nit = wtx.mapSaplingNoteData.find(op);
926 151 : assert(nit != wtx.mapSaplingNoteData.end());
927 151 : const auto& nd = nit->second;
928 151 : assert(nd.IsMyNote());
929 151 : assert(static_cast<bool>(nd.amount));
930 : // If we are filtering watch only or we have spend authority add the amount
931 151 : if ((filter & ISMINE_WATCH_ONLY_SHIELDED) || (nd.address && HaveSpendingKeyForPaymentAddress(*nd.address))) {
932 151 : nDebit += *(nd.amount);
933 : }
934 302 : if (!Params().GetConsensus().MoneyRange(nDebit))
935 0 : throw std::runtime_error("SaplingScriptPubKeyMan::GetDebit() : value out of range");
936 : }
937 : }
938 249 : return nDebit;
939 : }
940 :
941 3 : CAmount SaplingScriptPubKeyMan::GetShieldedChange(const CWalletTx& wtx) const
942 : {
943 6 : if (!wtx.tx->IsShieldedTx() || wtx.tx->sapData->vShieldedOutput.empty()) {
944 0 : return 0;
945 : }
946 3 : const uint256& txHash = wtx.GetHash();
947 3 : CAmount nChange = 0;
948 3 : SaplingOutPoint op{txHash, 0};
949 9 : for (uint32_t pos = 0; pos < (uint32_t) wtx.tx->sapData->vShieldedOutput.size(); ++pos) {
950 6 : op.n = pos;
951 6 : auto it = wtx.mapSaplingNoteData.find(op);
952 6 : if (it == wtx.mapSaplingNoteData.end()) continue;
953 4 : const auto& nd = it->second;
954 4 : if (!nd.IsMyNote() || !static_cast<bool>(nd.address) || !static_cast<bool>(nd.amount)) continue;
955 4 : if (IsNoteSaplingChange(op, *(nd.address))) {
956 2 : nChange += *(nd.amount);
957 6 : if (!Params().GetConsensus().MoneyRange(nChange))
958 0 : throw std::runtime_error("GetShieldedChange() : value out of range");
959 : }
960 : }
961 : return nChange;
962 : }
963 :
964 5 : bool SaplingScriptPubKeyMan::IsNoteSaplingChange(const SaplingOutPoint& op, libzcash::SaplingPaymentAddress address) const
965 : {
966 10 : LOCK2(wallet->cs_wallet, wallet->cs_KeyStore);
967 15 : std::set<libzcash::PaymentAddress> shieldedAddresses = {address};
968 10 : std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet = GetNullifiersForAddresses(shieldedAddresses);
969 10 : return IsNoteSaplingChange(nullifierSet, address, op);
970 : }
971 :
972 381 : bool SaplingScriptPubKeyMan::IsNoteSaplingChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet,
973 : const libzcash::PaymentAddress & address,
974 : const SaplingOutPoint & op) const
975 : {
976 : // A Note is marked as "change" if the address that received it
977 : // also spent Notes in the same transaction. This will catch,
978 : // for instance:
979 : // - Change created by spending fractions of Notes (because
980 : // shieldsendmany sends change to the originating shielded address).
981 : // - Notes sent from one address to itself.
982 381 : const auto& tx = wallet->mapWallet.at(op.hash);
983 381 : if (tx.tx->sapData) {
984 437 : for (const SpendDescription& spend : tx.tx->sapData->vShieldedSpend) {
985 124 : if (nullifierSet.count(std::make_pair(address, spend.nullifier))) {
986 6 : return true;
987 : }
988 : }
989 : }
990 : return false;
991 : }
992 :
993 304 : void SaplingScriptPubKeyMan::GetSaplingNoteWitnesses(const std::vector<SaplingOutPoint>& notes,
994 : std::vector<Optional<SaplingWitness>>& witnesses,
995 : uint256& final_anchor) const
996 : {
997 304 : LOCK(wallet->cs_wallet);
998 304 : witnesses.resize(notes.size());
999 608 : Optional<uint256> rt;
1000 304 : int i = 0;
1001 19382 : for (SaplingOutPoint note : notes) {
1002 19078 : auto it = wallet->mapWallet.find(note.hash);
1003 19078 : if (it != wallet->mapWallet.end()) {
1004 19077 : auto nit = it->second.mapSaplingNoteData.find(note);
1005 19077 : if (nit != it->second.mapSaplingNoteData.end() &&
1006 19075 : nit->second.witnesses.size() > 0) {
1007 19068 : witnesses[i] = nit->second.witnesses.front();
1008 19068 : if (!rt) {
1009 592 : rt = witnesses[i]->root();
1010 : } else {
1011 37544 : assert(*rt == witnesses[i]->root());
1012 : }
1013 : }
1014 : }
1015 19078 : i++;
1016 : }
1017 : // All returned witnesses have the same anchor
1018 304 : if (rt) {
1019 296 : final_anchor = *rt;
1020 : }
1021 304 : }
1022 :
1023 277356 : bool SaplingScriptPubKeyMan::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx)
1024 : {
1025 277356 : bool unchangedSaplingFlag = (wtxIn.mapSaplingNoteData.empty() || wtxIn.mapSaplingNoteData == wtx.mapSaplingNoteData);
1026 1112 : if (!unchangedSaplingFlag) {
1027 2224 : auto tmp = wtxIn.mapSaplingNoteData;
1028 : // Ensure we keep any cached witnesses we may already have
1029 :
1030 1166 : for (const std::pair <SaplingOutPoint, SaplingNoteData> nd : wtx.mapSaplingNoteData) {
1031 108 : if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) {
1032 21 : tmp.at(nd.first).witnesses.assign(
1033 : nd.second.witnesses.cbegin(), nd.second.witnesses.cend());
1034 : }
1035 54 : tmp.at(nd.first).witnessHeight = nd.second.witnessHeight;
1036 : }
1037 :
1038 : // Now copy over the updated note data
1039 1112 : wtx.mapSaplingNoteData = tmp;
1040 : }
1041 :
1042 277356 : return !unchangedSaplingFlag;
1043 : }
1044 :
1045 12 : void SaplingScriptPubKeyMan::ClearNoteWitnessCache()
1046 : {
1047 12 : LOCK(wallet->cs_wallet);
1048 166 : for (std::pair<const uint256, CWalletTx>& wtxItem : wallet->mapWallet) {
1049 165 : for (mapSaplingNoteData_t::value_type& item : wtxItem.second.mapSaplingNoteData) {
1050 11 : item.second.witnesses.clear();
1051 11 : item.second.witnessHeight = -1;
1052 : }
1053 : }
1054 12 : nWitnessCacheSize = 0;
1055 12 : nWitnessCacheNeedsUpdate = true;
1056 12 : }
1057 :
1058 1004 : Optional<libzcash::SaplingExtendedSpendingKey> SaplingScriptPubKeyMan::GetSpendingKeyForPaymentAddress(const libzcash::SaplingPaymentAddress &addr) const
1059 : {
1060 1004 : libzcash::SaplingExtendedSpendingKey extsk;
1061 1004 : if (wallet->GetSaplingExtendedSpendingKey(addr, extsk)) {
1062 1004 : return extsk;
1063 : } else {
1064 0 : return nullopt;
1065 : }
1066 : }
1067 :
1068 3 : Optional<libzcash::SaplingExtendedFullViewingKey> SaplingScriptPubKeyMan::GetViewingKeyForPaymentAddress(
1069 : const libzcash::SaplingPaymentAddress &addr) const
1070 : {
1071 3 : libzcash::SaplingIncomingViewingKey ivk;
1072 3 : libzcash::SaplingExtendedFullViewingKey extfvk;
1073 :
1074 6 : if (wallet->GetSaplingIncomingViewingKey(addr, ivk) &&
1075 3 : wallet->GetSaplingFullViewingKey(ivk, extfvk))
1076 : {
1077 3 : return extfvk;
1078 : } else {
1079 0 : return nullopt;
1080 : }
1081 : }
1082 :
1083 : //! TODO: Should be Sapling address format, SaplingPaymentAddress
1084 : // Generate a new Sapling spending key and return its public payment address
1085 1245 : libzcash::SaplingPaymentAddress SaplingScriptPubKeyMan::GenerateNewSaplingZKey()
1086 : {
1087 1245 : LOCK(wallet->cs_wallet); // mapSaplingZKeyMetadata
1088 :
1089 : // Try to get the seed
1090 2490 : CKey seedKey;
1091 1245 : if (!wallet->GetKey(hdChain.GetID(), seedKey))
1092 2 : throw std::runtime_error(std::string(__func__) + ": HD seed not found");
1093 :
1094 3733 : HDSeed seed(seedKey.GetPrivKey());
1095 1244 : auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
1096 :
1097 : // We use a fixed keypath scheme of m/32'/coin_type'/account'
1098 : // Derive m/32'
1099 1244 : auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
1100 : // Derive m/32'/coin_type'
1101 1244 : auto m_32h_cth = m_32h.Derive(119 | ZIP32_HARDENED_KEY_LIMIT);
1102 :
1103 : // Derive account key at next index, skip keys already known to the wallet
1104 1244 : libzcash::SaplingExtendedSpendingKey xsk;
1105 1244 : do {
1106 1244 : xsk = m_32h_cth.Derive(hdChain.nExternalChainCounter | ZIP32_HARDENED_KEY_LIMIT);
1107 1244 : hdChain.nExternalChainCounter++; // Increment childkey index
1108 1244 : } while (wallet->HaveSaplingSpendingKey(xsk.ToXFVK()));
1109 :
1110 : // Update the chain model in the database
1111 1244 : if (!WalletBatch(wallet->GetDBHandle()).WriteHDChain(hdChain))
1112 0 : throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");
1113 :
1114 : // Create new metadata
1115 1244 : int64_t nCreationTime = GetTime();
1116 1244 : auto ivk = xsk.expsk.full_viewing_key().in_viewing_key();
1117 2488 : CKeyMetadata metadata(nCreationTime);
1118 1244 : metadata.key_origin.path.push_back(32 | BIP32_HARDENED_KEY_LIMIT);
1119 1244 : metadata.key_origin.path.push_back(119 | BIP32_HARDENED_KEY_LIMIT);
1120 1244 : metadata.key_origin.path.push_back(hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);
1121 1244 : metadata.hd_seed_id = hdChain.GetID();
1122 1244 : mapSaplingZKeyMetadata[ivk] = metadata;
1123 :
1124 1244 : if (!AddSaplingZKey(xsk)) {
1125 0 : throw std::runtime_error(std::string(__func__) + ": AddSaplingZKey failed");
1126 : }
1127 : // return default sapling payment address.
1128 2488 : return xsk.DefaultAddress();
1129 : }
1130 :
1131 0 : int64_t SaplingScriptPubKeyMan::GetKeyCreationTime(const libzcash::SaplingIncomingViewingKey& ivk)
1132 : {
1133 0 : auto it = mapSaplingZKeyMetadata.find(ivk);
1134 0 : return it != mapSaplingZKeyMetadata.end() ? it->second.nCreateTime : 0;
1135 : }
1136 :
1137 395 : void SaplingScriptPubKeyMan::GetConflicts(const CWalletTx& wtx, std::set<uint256>& result) const
1138 : {
1139 395 : AssertLockHeld(wallet->cs_wallet);
1140 395 : std::pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range_o;
1141 :
1142 395 : if (wtx.tx->IsShieldedTx()) {
1143 8 : for (const SpendDescription& spend : wtx.tx->sapData->vShieldedSpend) {
1144 2 : const uint256& nullifier = spend.nullifier;
1145 2 : if (mapTxSaplingNullifiers.count(nullifier) <= 1) {
1146 1 : continue; // No conflict if zero or one spends
1147 : }
1148 1 : range_o = mapTxSaplingNullifiers.equal_range(nullifier);
1149 3 : for (TxNullifiers::const_iterator it = range_o.first; it != range_o.second; ++it) {
1150 2 : result.insert(it->second);
1151 : }
1152 : }
1153 : }
1154 395 : }
1155 :
1156 3 : KeyAddResult SaplingScriptPubKeyMan::AddViewingKeyToWallet(const libzcash::SaplingExtendedFullViewingKey &extfvk) const {
1157 3 : if (wallet->HaveSaplingSpendingKey(extfvk)) {
1158 : return SpendingKeyExists;
1159 3 : } else if (wallet->HaveSaplingFullViewingKey(extfvk.fvk.in_viewing_key())) {
1160 : return KeyAlreadyExists;
1161 3 : } else if (wallet->AddSaplingFullViewingKey(extfvk)) {
1162 : return KeyAdded;
1163 : } else {
1164 0 : return KeyNotAdded;
1165 : }
1166 : }
1167 :
1168 1007 : KeyAddResult SaplingScriptPubKeyMan::AddSpendingKeyToWallet(
1169 : const Consensus::Params ¶ms,
1170 : const libzcash::SaplingExtendedSpendingKey &sk,
1171 : int64_t nTime)
1172 : {
1173 1007 : auto extfvk = sk.ToXFVK();
1174 1007 : auto ivk = extfvk.fvk.in_viewing_key();
1175 1007 : {
1176 : //LogPrint(BCLog::SAPLING, "Importing shielded addr %s...\n", KeyIO::EncodePaymentAddress(sk.DefaultAddress()));
1177 : // Don't throw error in case a key is already there
1178 1007 : if (wallet->HaveSaplingSpendingKey(extfvk)) {
1179 : return KeyAlreadyExists;
1180 : } else {
1181 1006 : if (!wallet-> AddSaplingZKey(sk)) {
1182 : return KeyNotAdded;
1183 : }
1184 :
1185 1006 : int64_t nTimeToSet;
1186 : // Sapling addresses can't have been used in transactions prior to activation.
1187 1006 : if (params.vUpgrades[Consensus::UPGRADE_V5_0].nActivationHeight == Consensus::NetworkUpgrade::ALWAYS_ACTIVE) {
1188 0 : nTimeToSet = nTime;
1189 : } else {
1190 : // TODO: Update epoch before release v5.
1191 : // 154051200 seconds from epoch is Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates
1192 1006 : nTimeToSet = std::max((int64_t) 154051200, nTime);
1193 : }
1194 :
1195 2012 : mapSaplingZKeyMetadata[ivk] = CKeyMetadata(nTimeToSet);
1196 1006 : return KeyAdded;
1197 : }
1198 : }
1199 : }
1200 :
1201 : // Add spending key to keystore
1202 2264 : bool SaplingScriptPubKeyMan::AddSaplingZKey(
1203 : const libzcash::SaplingExtendedSpendingKey &sk)
1204 : {
1205 2264 : AssertLockHeld(wallet->cs_wallet); // mapSaplingZKeyMetadata
1206 :
1207 2264 : if (!IsEnabled()) {
1208 0 : return error("%s: Sapling spkm not enabled", __func__ );
1209 : }
1210 :
1211 2264 : if (!AddSaplingSpendingKey(sk)) {
1212 : return false;
1213 : }
1214 :
1215 2264 : if (!wallet->IsCrypted()) {
1216 2262 : auto ivk = sk.expsk.full_viewing_key().in_viewing_key();
1217 2262 : return WalletBatch(wallet->GetDBHandle()).WriteSaplingZKey(ivk, sk, mapSaplingZKeyMetadata[ivk]);
1218 : }
1219 :
1220 : return true;
1221 : }
1222 :
1223 2264 : bool SaplingScriptPubKeyMan::AddSaplingSpendingKey(
1224 : const libzcash::SaplingExtendedSpendingKey &sk)
1225 : {
1226 2264 : {
1227 2264 : LOCK(wallet->cs_KeyStore);
1228 2264 : if (!wallet->IsCrypted()) {
1229 4524 : return wallet->AddSaplingSpendingKey(sk); // keystore
1230 : }
1231 :
1232 2 : if (wallet->IsLocked()) {
1233 : return false;
1234 : }
1235 :
1236 4 : std::vector<unsigned char> vchCryptedSecret;
1237 4 : CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
1238 2 : ss << sk;
1239 4 : CKeyingMaterial vchSecret(ss.begin(), ss.end());
1240 2 : auto extfvk = sk.ToXFVK();
1241 2 : if (!EncryptSecret(wallet->GetEncryptionKey(), vchSecret, extfvk.fvk.GetFingerprint(), vchCryptedSecret)) {
1242 0 : return false;
1243 : }
1244 :
1245 2 : if (!AddCryptedSaplingSpendingKeyDB(extfvk, vchCryptedSecret)) {
1246 : return false;
1247 : }
1248 : }
1249 2 : return true;
1250 : }
1251 :
1252 : // Add payment address -> incoming viewing key map entry
1253 1 : bool SaplingScriptPubKeyMan::AddSaplingIncomingViewingKey(
1254 : const libzcash::SaplingIncomingViewingKey &ivk,
1255 : const libzcash::SaplingPaymentAddress &addr)
1256 : {
1257 1 : AssertLockHeld(wallet->cs_wallet); // mapSaplingZKeyMetadata
1258 :
1259 1 : if (!wallet->AddSaplingIncomingViewingKey(ivk, addr)) {
1260 : return false;
1261 : }
1262 :
1263 1 : if (!wallet->IsCrypted()) {
1264 1 : return WalletBatch(wallet->GetDBHandle()).WriteSaplingPaymentAddress(addr, ivk);
1265 : }
1266 :
1267 : return true;
1268 : }
1269 :
1270 7 : bool SaplingScriptPubKeyMan::EncryptSaplingKeys(CKeyingMaterial& vMasterKeyIn)
1271 : {
1272 7 : AssertLockHeld(wallet->cs_wallet); // mapSaplingSpendingKeys
1273 :
1274 109 : for (SaplingSpendingKeyMap::value_type& mSaplingSpendingKey : wallet->mapSaplingSpendingKeys) {
1275 102 : const libzcash::SaplingExtendedSpendingKey &sk = mSaplingSpendingKey.second;
1276 204 : CSecureDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
1277 102 : ss << sk;
1278 204 : CKeyingMaterial vchSecret(ss.begin(), ss.end());
1279 102 : auto extfvk = sk.ToXFVK();
1280 204 : std::vector<unsigned char> vchCryptedSecret;
1281 102 : if (!EncryptSecret(vMasterKeyIn, vchSecret, extfvk.fvk.GetFingerprint(), vchCryptedSecret)) {
1282 0 : return false;
1283 : }
1284 102 : if (!AddCryptedSaplingSpendingKeyDB(extfvk, vchCryptedSecret)) {
1285 : return false;
1286 : }
1287 : }
1288 7 : wallet->mapSaplingSpendingKeys.clear();
1289 7 : return true;
1290 : }
1291 :
1292 104 : bool SaplingScriptPubKeyMan::AddCryptedSaplingSpendingKeyDB(const libzcash::SaplingExtendedFullViewingKey &extfvk,
1293 : const std::vector<unsigned char> &vchCryptedSecret)
1294 : {
1295 104 : if (!wallet->AddCryptedSaplingSpendingKey(extfvk, vchCryptedSecret))
1296 : return false;
1297 104 : {
1298 208 : LOCK(wallet->cs_wallet);
1299 104 : if (wallet->encrypted_batch) {
1300 102 : return wallet->encrypted_batch->WriteCryptedSaplingZKey(extfvk,
1301 : vchCryptedSecret,
1302 204 : mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]);
1303 : } else {
1304 6 : return WalletBatch(wallet->GetDBHandle()).WriteCryptedSaplingZKey(extfvk,
1305 : vchCryptedSecret,
1306 4 : mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]);
1307 : }
1308 : }
1309 : return false;
1310 : }
1311 :
1312 4630 : bool SaplingScriptPubKeyMan::HaveSpendingKeyForPaymentAddress(const libzcash::SaplingPaymentAddress &zaddr) const
1313 : {
1314 4630 : libzcash::SaplingIncomingViewingKey ivk;
1315 4630 : libzcash::SaplingExtendedFullViewingKey extfvk;
1316 :
1317 9256 : return wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
1318 9256 : wallet->GetSaplingFullViewingKey(ivk, extfvk) &&
1319 4626 : wallet->HaveSaplingSpendingKey(extfvk);
1320 : }
1321 :
1322 15 : bool SaplingScriptPubKeyMan::PaymentAddressBelongsToWallet(const libzcash::SaplingPaymentAddress &zaddr) const
1323 : {
1324 15 : libzcash::SaplingIncomingViewingKey ivk;
1325 :
1326 : // If we have a SaplingExtendedSpendingKey in the wallet, then we will
1327 : // also have the corresponding SaplingExtendedFullViewingKey.
1328 29 : return wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
1329 14 : wallet->HaveSaplingFullViewingKey(ivk);
1330 : }
1331 :
1332 : ///////////////////// Load ////////////////////////////////////////
1333 :
1334 2 : bool SaplingScriptPubKeyMan::LoadCryptedSaplingZKey(
1335 : const libzcash::SaplingExtendedFullViewingKey &extfvk,
1336 : const std::vector<unsigned char> &vchCryptedSecret)
1337 : {
1338 2 : return wallet->AddCryptedSaplingSpendingKey(extfvk, vchCryptedSecret);
1339 : }
1340 :
1341 38 : bool SaplingScriptPubKeyMan::LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta)
1342 : {
1343 38 : AssertLockHeld(wallet->cs_wallet); // mapSaplingZKeyMetadata
1344 38 : mapSaplingZKeyMetadata[ivk] = meta;
1345 38 : return true;
1346 : }
1347 :
1348 36 : bool SaplingScriptPubKeyMan::LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key)
1349 : {
1350 36 : return wallet->AddSaplingSpendingKey(key);
1351 : }
1352 :
1353 1 : bool SaplingScriptPubKeyMan::LoadSaplingPaymentAddress(
1354 : const libzcash::SaplingPaymentAddress &addr,
1355 : const libzcash::SaplingIncomingViewingKey &ivk)
1356 : {
1357 1 : return wallet->AddSaplingIncomingViewingKey(ivk, addr);
1358 : }
1359 :
1360 : ///////////////////// Setup ///////////////////////////////////////
1361 :
1362 237 : bool SaplingScriptPubKeyMan::SetupGeneration(const CKeyID& keyID, bool force, bool memonly)
1363 : {
1364 237 : SetHDSeed(keyID, force, memonly);
1365 237 : return true;
1366 : }
1367 :
1368 4 : void SaplingScriptPubKeyMan::SetHDSeed(const CPubKey& seed, bool force, bool memonly)
1369 : {
1370 4 : SetHDSeed(seed.GetID(), force, memonly);
1371 4 : }
1372 :
1373 241 : void SaplingScriptPubKeyMan::SetHDSeed(const CKeyID& keyID, bool force, bool memonly)
1374 : {
1375 241 : if (!hdChain.IsNull() && !force)
1376 0 : throw std::runtime_error(std::string(__func__) + ": sapling trying to set a hd seed on an already created chain");
1377 :
1378 241 : LOCK(wallet->cs_wallet);
1379 : // store the keyid (hash160) together with
1380 : // the child index counter in the database
1381 : // as a hdChain object
1382 241 : CHDChain newHdChain(HDChain::ChainCounterType::Sapling);
1383 241 : if (!newHdChain.SetSeed(keyID) ) {
1384 0 : throw std::runtime_error(std::string(__func__) + ": set sapling hd seed failed");
1385 : }
1386 :
1387 241 : SetHDChain(newHdChain, memonly);
1388 :
1389 : // Update the commonOVK to recover t->shield notes
1390 241 : commonOVK = getCommonOVKFromSeed();
1391 478 : if (!memonly && !WalletBatch(wallet->GetDBHandle()).WriteSaplingCommonOVK(*commonOVK)) {
1392 0 : throw std::runtime_error(std::string(__func__) + ": writing sapling commonOVK failed");
1393 : }
1394 241 : }
1395 :
1396 400 : void SaplingScriptPubKeyMan::SetHDChain(CHDChain& chain, bool memonly)
1397 : {
1398 400 : LOCK(wallet->cs_wallet);
1399 400 : if (chain.chainType != HDChain::ChainCounterType::Sapling)
1400 0 : throw std::runtime_error(std::string(__func__) + ": trying to store an invalid chain type");
1401 :
1402 637 : if (!memonly && !WalletBatch(wallet->GetDBHandle()).WriteHDChain(chain))
1403 0 : throw std::runtime_error(std::string(__func__) + ": writing sapling chain failed");
1404 :
1405 400 : hdChain = chain;
1406 :
1407 : // Sanity check
1408 400 : if (!wallet->HaveKey(hdChain.GetID()))
1409 0 : throw std::runtime_error(std::string(__func__) + ": Not found sapling seed in wallet");
1410 400 : }
1411 :
1412 2140 : uint256 SaplingScriptPubKeyMan::getCommonOVK()
1413 : {
1414 : // If already loaded, return it
1415 2140 : if (commonOVK) return *commonOVK;
1416 :
1417 : // Else, look for it in the database
1418 0 : uint256 ovk;
1419 0 : if (WalletBatch(wallet->GetDBHandle()).ReadSaplingCommonOVK(ovk)) {
1420 0 : commonOVK = std::move(ovk);
1421 0 : return *commonOVK;
1422 : }
1423 :
1424 : // Otherwise create one. This throws if the wallet is encrypted.
1425 : // So we should always call this after unlocking the wallet during a spend
1426 : // from a transparent address, or when changing/setting the HD seed.
1427 0 : commonOVK = getCommonOVKFromSeed();
1428 0 : if (!WalletBatch(wallet->GetDBHandle()).WriteSaplingCommonOVK(*commonOVK)) {
1429 0 : throw std::runtime_error("Unable to write sapling Common OVK to database");
1430 : }
1431 0 : return *commonOVK;
1432 : }
1433 :
1434 241 : uint256 SaplingScriptPubKeyMan::getCommonOVKFromSeed() const
1435 : {
1436 : // Sending from a t-address, which we don't have an ovk for. Instead,
1437 : // generate a common one from the HD seed. This ensures the data is
1438 : // recoverable, while keeping it logically separate from the ZIP 32
1439 : // Sapling key hierarchy, which the user might not be using.
1440 241 : CKey key;
1441 241 : if (!wallet->GetKey(GetHDChain().GetID(), key)) {
1442 0 : throw std::runtime_error("HD seed not found, wallet must be un-locked");
1443 : }
1444 723 : HDSeed seed{key.GetPrivKey()};
1445 482 : return ovkForShieldingFromTaddr(seed);
1446 : }
|