diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 11:48:26 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 11:48:26 +0000 |
| commit | c7ada00eeed8ef689b976c7201db2d9c076c17d1 (patch) | |
| tree | c3abe9a9d71f076c02927fb111fe19d79663e780 | |
| parent | 9e5dc16941ac34e77157d85692d1774d8698b1b4 (diff) | |
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.
| -rw-r--r-- | src/rpc.cpp | 8 |
1 files changed, 8 insertions, 0 deletions
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, | |||
| 644 | std::mutex& fileMutex, uint64& publishedSeq, | 644 | std::mutex& fileMutex, uint64& publishedSeq, |
| 645 | const char* what) | 645 | const char* what) |
| 646 | { | 646 | { |
| 647 | // A default-constructed blob is not a snapshot of anything; it is a | ||
| 648 | // VersionedBlob nobody filled in. Today they are already dropped, because | ||
| 649 | // every serialiser stamps ++gSeq and so never produces 0, and 0 fails the | ||
| 650 | // seq <= publishedSeq test below -- but only for as long as publishedSeq | ||
| 651 | // starts at 0 and nobody writes `seq = gSeq++`. One character, and the | ||
| 652 | // ledger file becomes "[]". Say it out loud instead of relying on it. | ||
| 653 | if (!v.seq) | ||
| 654 | return; | ||
| 647 | // A file whose loader threw is not a file this process knows the contents | 655 | // A file whose loader threw is not a file this process knows the contents |
| 648 | // of. Writing it would replace a full record of what was owed with | 656 | // of. Writing it would replace a full record of what was owed with |
| 649 | // whatever happened to parse before the error. | 657 | // whatever happened to parse before the error. |