Skip to content
go-libfossil Check-in [8d3b1ef28a]
Login

Check-in [8d3b1ef28a]

Overview
Comment:Merge pull request #477 from danmestas/fix/bound-chain-walks

fix(sync): bound basis-chain walks at the read depth

Timelines: family | ancestors | descendants | both | main | trunk
Files: files | file ages | folders
SHA3-256: 8d3b1ef28ac93cca7981d990b95e4e9790094d3926fce2ecc30fbf851785cf19
User & Date: [email protected] on 2026-10-24 13:16:06.000
Other Links: branch diff | manifest | tags
Context
2026-10-24
13:29
Merge pull request #478 from danmestas/feat/horizon-seed-binding

feat(horizon): bind each seed to its horizon and sign seeds apart check-in: b35d577a54 user: [email protected] tags: main, trunk

13:16
Merge branch 'main' into feat/horizon-seed-binding check-in: 707b42538c user: [email protected] tags: main, trunk
13:16
Merge pull request #477 from danmestas/fix/bound-chain-walks

fix(sync): bound basis-chain walks at the read depth check-in: 8d3b1ef28a user: [email protected] tags: main, trunk

13:08
fix(sync): bound basis-chain walks at the read depth

Any pusher could hold the write lock about 30s per push with a two-delta cycle. The receive pass's basis-chain walks, and the horizon seed check's pending-row walk, ran to 1<<24 steps before giving up.

All of them now stop at content.MaxDeltaChainDepth, the depth at which content.Expand stops, so no walk outlasts what a read could ever use. What crossing the bound means at each site:

- chainEnd (rootOf, for a dangling delta being stored) keeps a visited set. A chain that loops is refused, as it was, but at the revisit rather than after 1<<24 steps. A chain longer than the bound returns errChainTooDeep. storeDeltaContent then stores the delta untracked: the push succeeds, as a push onto a chain between 16k and 16M deep always did, and since no basis can make the delta readable there is nothing to check against its name later. Storing matches the current behaviour, and an honest transfer never fails for a deep chain it did not build. - verifyFrom's cascade carries each row's depth from the basis that arrived, its own chain included. It does not check, or descend past, a row deeper than the bound: no read can produce those bytes, so the pass owes them no check. A delta that does not hash to its name past the bound no longer aborts the pass. - pathRows loses its step counter. Its visited marks already stop it at a row on a path, so it visits each row once, and the cascade's depth check bounds what it verifies. - seedcheck's chainRoot gives a chain that loops or runs past the bound no root, consistent with ErrChainUnresolvable: the row stays pending, withheld, and is never judged.

Tests: - Closing a two-delta cycle is refused in under 5s (it took 27s). - A push onto a chain one past the bound is stored. - chainEnd stops exactly at its limit. - A dangling delta with a wrong name one node past the bound does not abort the push, whether the basis that arrives is full text or a delta deep on a held chain, while one within the bound is still checked and readable. - MarkPending gives an over-deep chain no root, so filling its far end examines nothing, and returns at once on a cycle. - TestHorizonOverDeepPendingChainDoesNotBlockFill still passes. - Removing each bound, the cycle check, the untracked store or the root depth fails a test. - chainDepth's own early stop is an equivalent mutant: the cascade's root has just been expanded, so its chain is within the bound unless the store is damaged. check-in: eac16cf7ed user: [email protected] tags: main, trunk

12:32
Merge pull request #476 from danmestas/feat/horizon-names-seed-refusal

feat(horizon): named horizons with depth, and origin seed refusal check-in: 5a7c2ba12e user: [email protected] tags: main, trunk

Changes