wallet: note-cache review follow-ups
- UpdatedNoteData now keeps the incoming plaintext when it keeps the
wallet's own entries. When it returns "unchanged" (equal note data,
or SaplingNoteDataIdentical), any entry lacking a plaintext takes the
incoming one, for the same outpoint and ivk only. The startup rescan
replays loaded txs with FindMySaplingNotes' freshly decrypted data;
that plaintext was thrown away, so the warm-up decrypted those notes a
second time. The cache is outside every comparison, so the function
still returns false and nothing is rewritten.
- -checknotecache is read once at init into fCheckNoteCache, instead of
a mapArgs lookup on every GetFilteredNotes call. Its help text and
comment now say what it checks: every note the walk considers, before
the address filter, so a corrupt cached address can't drop a note
unchecked.
- GetFilteredNotes pre-sizes its result only when there's no address
filter, so a one-address call no longer reserves room for the whole
wallet.
- WarmSaplingNoteCache returns its counts (notes, filled, no such
output, undecryptable), and FillSaplingNoteCache records the threads
it used, so tests can tell the cases apart.
- WarmSaplingNoteCache's comment no longer claims nothing can wait on
cs_wallet while it runs: the RPCs allowed during warmup can.
Tests:
- ReplayWithPlaintextIsNoUpdate pins the rule that the plaintext cache
stays out of the "did this tx change?" decision. A rescan replays a
witnessed note loaded without plaintext, using FindMySaplingNotes'
data that carries one. That must be no update: no rewrite, and the
witness and nullifier are kept. The stored entry then gains the
plaintext. Checked by mutation: with cache presence added to
SaplingNoteDataIdentical, this test fails and every other test still
passes.
- ParallelFillMatchesSerial uses 460 notes and asserts the thread count.
So the 7-thread case really runs 7 threads, with uneven shards
(460 % 7 != 0). Before, it was capped at 4 and repeated the 4-thread
case.
- ColdParallelFillMatchesLegacy: a cold GetFilteredNotes over ~270
candidates fills on 4 threads and matches the 69bb035e7 loop entry
for entry.
- WarmUpSkipsBadNotes asserts the warm-up's separate counts (1 no such
output, 2 undecryptable), so removing the out-of-range guard is now
caught.
- CachedPlaintextMatchesFreshDecrypt checks exact counts.
CheckNoteCacheRepairsADifference also tampers with a memo, and looks
entries up by outpoint. The meaningless GetHash check is dropped.
- A ScopedArg / ScopedCheckNoteCache RAII helper restores -args and
fCheckNoteCache even when an assertion returns early.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+14
-4
@@ -62,6 +62,8 @@ extern bool bSpendZeroConfChange;
|
||||
extern bool fSendFreeTransactions;
|
||||
extern bool fPayAtLeastCustomFee;
|
||||
extern bool fTxDeleteEnabled;
|
||||
//! -checknotecache: re-decrypt cached Sapling notes in GetFilteredNotes and repair any difference
|
||||
extern bool fCheckNoteCache;
|
||||
extern bool fTxConflictDeleteEnabled;
|
||||
extern int fDeleteInterval;
|
||||
extern unsigned int fDeleteTransactionsAfterNBlocks;
|
||||
@@ -1494,12 +1496,20 @@ public:
|
||||
/** Decrypts each note into nd->cachedPlaintext and returns how many it filled. A note that doesn't
|
||||
* decrypt under its ivk is left unfilled. Caller holds cs_wallet. */
|
||||
unsigned int FillSaplingNoteCache(const std::vector<SaplingNoteToDecrypt>& notes);
|
||||
/** Notes FillSaplingNoteCache has decrypted since startup (FindMySaplingNotes' fills not counted).
|
||||
* Guarded by cs_wallet. */
|
||||
/** Notes FillSaplingNoteCache has decrypted since startup (FindMySaplingNotes' fills not counted), and
|
||||
* the threads its last call used. Guarded by cs_wallet. */
|
||||
uint64_t nSaplingNoteCacheFills = 0;
|
||||
int nSaplingNoteCacheFillThreads = 0;
|
||||
|
||||
struct SaplingNoteCacheWarmup {
|
||||
unsigned int notes = 0; //!< notes in the wallet
|
||||
unsigned int filled = 0; //!< decrypted by this call
|
||||
unsigned int noSuchOutput = 0; //!< naming an output their tx doesn't have
|
||||
unsigned int undecryptable = 0; //!< not decrypting under their own ivk
|
||||
};
|
||||
/** Decrypts every wallet note whose plaintext isn't cached yet (all of them after wallet.dat is loaded).
|
||||
* Never asserts: a note that won't decrypt is logged and left unfilled. Run at startup. */
|
||||
void WarmSaplingNoteCache();
|
||||
* Never asserts: a note that can't be decrypted is counted, logged and left unfilled. Run at startup. */
|
||||
SaplingNoteCacheWarmup WarmSaplingNoteCache();
|
||||
|
||||
void GetFilteredNotes(std::vector<SaplingNoteEntry>& saplingEntries,
|
||||
std::string address,
|
||||
|
||||
Reference in New Issue
Block a user