pub fn load_snapshot(path: &Path) -> Result<MaterializedState>Expand description
Load a snapshot, refusing anything this build cannot read (§5.5, D-043).
The header is checked before the payload is decompressed, and a mismatch
is DbError::SnapshotIncompatible rather than a corruption error, because
the two want opposite responses: corruption is a fault to report, an
incompatible snapshot is an ordinary consequence of upgrading and the right
answer is to discard it and cold-fold. Distinguishing them is the whole
point of the header — bincode is not self-describing, so without one an
old file does not reliably fail to parse, it parses into wrong values.
Headerless files written by 0.5.4 and earlier are rejected by the same path:
their first four bytes are zstd’s magic, which is not MACR.
§Damage is a third answer, and it is bounded (0.13.12, W8.2, D-185)
DbError::SnapshotCorrupt is not DbError::SnapshotIncompatible and
not DbError::ReplayCorrupt: the file is damaged, the ledger is not, and
the repair is to delete the file. Every failure below used to be
ReplayCorrupt { seq: 0 }, which said the log was damaged and carried a
sequence number that cannot exist.
The checks run in the order that lets the cheapest one fire first, and each is a named error rather than a symptom further down:
- Declared payload length against the bytes actually present. Catches truncation and trailing junk without hashing anything.
- Checksum over the header and the payload, before zstd is handed a single byte. This is the check that closes §3.3: a corrupt stream is refused as a corrupt stream, rather than being walked to exhaustion by a deserializer trying to make sense of it.
- Declared plaintext length, enforced during decompression rather than
checked after it — the reader is bounded to
plain_len + 1bytes, so a frame that expands further stops at the bound instead of allocating. - A bincode limit equal to the buffer’s own size, replacing the
Infinitelimitbincode::deserializecarries.
Steps 3 and 4 are redundant with step 2 for every file this crate wrote, and that is the point of having them: they hold when the checksum has already been satisfied by something that computed it deliberately.
§This blocks (0.13.11, W8.1)
Read, decompress, deserialize, all synchronous — see save_snapshot for
the argument. The crate’s one async reader is snapshot_anchor, which
offloads the whole scan rather than each file.