Artifact 868bd822f00a64789203aa1445a77cc8f76b08c666835bf289fcc6fb380f1789:
- File
internal/sync/seed_basis.go
— part of check-in
[f8446cdfcc]
at
2026-10-28 13:59:48
on branch main
— seedcheck: count, privacy and savepoint follow-ups
A delta on a refused seed now counts as a new phantom only when it makes one, so a peer resending the same delta no longer holds a pull open to the round cap. The phantom it leaves is private when the card was, so its name is never asked for in a public sync.
seedcheck's drop reuses db's savepoint instead of its own copy: db.Tx.WithSavepoint exposes what WithTx already does on a bound DB. (user: [email protected], size: 3441) [annotate] [blame] [check-ins using] [more...]
package sync import ( "fmt" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/blob" "github.com/danmestas/go-libfossil/internal/content" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/manifest" "github.com/danmestas/go-libfossil/internal/repo" "github.com/danmestas/go-libfossil/internal/seedcheck" "github.com/danmestas/go-libfossil/internal/shun" ) // A delta and a dropped seed it is built on (horizon §5.2.3). The seed's // bytes are the only way such a delta can be read, so a store either reads // the delta while it has them (storeSeedBasis) or does not keep the delta as // one at all (onRefusedBasis). // onRefusedBasis reports whether deltaSrc names a seed this store refused, // whose content is gone (taken). A delta on it could never be read, so it is // not stored: uuid becomes a phantom instead, private when the card was // (§7.4), and is asked for again, which a peer answers with its full text. // created reports whether this call made that phantom, so a peer resending // the delta adds nothing new. A shunned uuid is left to the caller, which // drops it. func onRefusedBasis(q db.Querier, uuid, deltaSrc string, vis visibility) (taken, created bool, err error) { if q == nil || uuid == "" { panic("sync.onRefusedBasis: q must not be nil and uuid must not be empty") } if deltaSrc == "" { return false, false, nil } refused, err := seedcheck.IsRefusedUUID(q, deltaSrc) if err != nil || !refused { return false, false, err } if shunned, err := shun.IsShunned(q, uuid); err != nil || shunned { return false, false, err } if _, held := blob.Exists(q, uuid); held { return true, false, nil } rid, err := blob.StorePhantom(q, uuid) if err != nil { return false, false, fmt.Errorf("sync: delta %s on a refused seed: %w", uuid, err) } if vis == visibilityPrivate { if err := content.MakePrivate(q, int64(rid)); err != nil { return false, false, fmt.Errorf("sync: delta %s on a refused seed: %w", uuid, err) } } return true, true, nil } // storeSeedBasis is what storeResolvedContent does with a seed's full text // before refusing it (errSeed). When deltas already stored are built on the // seed's phantom row, the seed is stored just long enough to check those // deltas against their names (the pass), judge any pending on it, and // rewrite them as full text, and is then dropped. Otherwise nothing is // stored. func storeSeedBasis(r *repo.Repo, pass *receivePass, uuid string, full []byte, linker *manifest.ReceiveLinker) error { if r == nil || pass == nil || uuid == "" || full == nil { panic("sync.storeSeedBasis: r, pass, uuid and full must be set") } return withLinkerTx(r, linker, func(tx *db.Tx) error { rid, held := blob.Exists(tx, uuid) if !held { return nil } var size int64 var deps int err := tx.QueryRow("SELECT size, (SELECT count(*) FROM delta WHERE srcid=?1) FROM blob WHERE rid=?1", rid).Scan(&size, &deps) if err != nil { return fmt.Errorf("sync: seed basis %s: %w", uuid, err) } if size >= 0 || deps == 0 { return nil } if _, _, _, err := writeResolved(tx, uuid, full, nil); err != nil { return err } if err := pass.verifyFrom(tx, rid); err != nil { return err } if _, err := seedcheck.JudgeFilled(tx, []libfossil.FslID{rid}); err != nil { return err } return seedcheck.Drop(tx, rid, uuid) }) }