From c7ada00eeed8ef689b976c7201db2d9c076c17d1 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Sun, 23 Aug 2026 11:48:26 +0000 Subject: payout: refuse to publish a snapshot that was never stamped PublishVersioned drops a default-constructed VersionedBlob only by accident. Every serialiser stamps `++gSeq`, so it never produces 0, and 0 then loses the `seq <= publishedSeq` comparison because publishedSeq starts at 0. Both halves of that are incidental. A serialiser written as `seq = gSeq++` -- one character different, and the natural way to write it -- publishes seq 0 for its first snapshot, which would sail past the comparison on a fresh process and write an empty ledger over a real one. There is also a live path that produces an unstamped blob today: FlushMaturePayouts declares `VersionedBlob ledgerSnap` and only fills it if something was voided, then publishes unconditionally. It is harmless for exactly the reasons above, which is to say for no reason that is written down anywhere. One line, checked explicitly, so the guarantee does not depend on how a future serialiser spells its increment. Property: a blob that was never stamped with a version is never written to disk. --- src/rpc.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/rpc.cpp') diff --git a/src/rpc.cpp b/src/rpc.cpp index 7d7cc8f..c382926 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -644,6 +644,14 @@ static void PublishVersioned(const std::string& path, const VersionedBlob& v, std::mutex& fileMutex, uint64& publishedSeq, const char* what) { + // A default-constructed blob is not a snapshot of anything; it is a + // VersionedBlob nobody filled in. Today they are already dropped, because + // every serialiser stamps ++gSeq and so never produces 0, and 0 fails the + // seq <= publishedSeq test below -- but only for as long as publishedSeq + // starts at 0 and nobody writes `seq = gSeq++`. One character, and the + // ledger file becomes "[]". Say it out loud instead of relying on it. + if (!v.seq) + return; // A file whose loader threw is not a file this process knows the contents // of. Writing it would replace a full record of what was owed with // whatever happened to parse before the error. -- cgit v1.2.3