Artifact 685695a7ab003a1734ea1f8c7bdc8738dfa4ace9d4e11761650b1d5752d57308:
- File
internal/sync/sparse.go
— part of check-in
[dc33bca3a6]
at
2026-10-22 16:12:54
on branch feat/horizon-cones
— feat(sync): serve and take the structural horizon
The origin now answers pragma horizon-structural (draft-fossil-horizon-00 §6.1, §6.2): it keeps only the manifests and nodes the held set reaches, labels every boundary hash at the head of each reply (§6.2.2), mints the seed for C and sends it, pragma ahead of its file card, when the client offers another name (§6.2.3), and advertises the structural level only then; a request it cannot place or seed is served as content. It refuses a seed offered to it and withholds a gimme'd manifest below the horizon.
A structural store's requests carry horizon-structural and the seed it holds. It routes the announced card to seed.Hold after checking its name and shape, never storing or announcing it, records the labels, and applies the seed after each reply and after the closing link. A first reply that does not serve structural with a seed, or a seed failing provenance, makes it a content-horizon store for good, reported in the result, and the manifests below the horizon are fetched (§5.1, §5.2.4, §6.3). (user: [email protected], size: 6584) [annotate] [blame] [check-ins using] [more...]
package sync import ( "bytes" "fmt" "github.com/danmestas/go-libfossil/db" "github.com/danmestas/go-libfossil/internal/cone" "github.com/danmestas/go-libfossil/internal/deck" "github.com/danmestas/go-libfossil/internal/hash" "github.com/danmestas/go-libfossil/internal/horizon/seed" "github.com/danmestas/go-libfossil/internal/xfer" ) // The draft defines request pragmas, but no capability advertisement spelling. // Unknown pragmas remain ignored by peers that predate sparse transfer. // coneServedPragma acknowledges a reply that applied the request's whole cone // (draft-fossil-sparse-cone-00 §6.3). It travels only from server to client. const coneServedPragma = "cone-served" // horizonPragma carries a request's horizon, `pragma horizon HASH RULE`, and // horizonServedPragma the level an origin serves it at, `pragma // horizon-served LEVEL` (draft-fossil-horizon-00 §6.1, §6.2.5). A structural // request adds horizonStructuralPragma and offers its held seed with // horizonSeedPragma, `pragma horizon-seed HASH|-`; a reply names a seed it // sends with the same pragma, and each boundary hash with horizonBelowPragma, // `pragma horizon-below HASH` (§6.2.2, §6.2.3). const ( horizonPragma = "horizon" horizonServedPragma = "horizon-served" horizonStructuralPragma = "horizon-structural" horizonSeedPragma = "horizon-seed" horizonBelowPragma = "horizon-below" ) // noSeed is the horizon-seed value of a client holding no seed. const noSeed = "-" // conePragmas spells c for a request: its path members, then its horizon, // and for a structural horizon horizon-structural and the offered seed. func conePragmas(c *cone.Cone, offer string) []xfer.Card { if c == nil { return nil } cards := make([]xfer.Card, 0, len(c.Include())+len(c.Exclude())+3) for _, m := range c.Include() { cards = append(cards, &xfer.PragmaCard{Name: "cone", Values: []string{deck.FossilEncode(m)}}) } for _, m := range c.Exclude() { cards = append(cards, &xfer.PragmaCard{Name: "cone-not", Values: []string{deck.FossilEncode(m)}}) } if h, ok := c.Horizon(); ok { cards = append(cards, &xfer.PragmaCard{Name: horizonPragma, Values: []string{h.Hash(), h.Rule().String()}}) if h.Depth() == cone.DepthStructural { if offer == "" { panic("sync.conePragmas: a structural horizon needs a seed offer") } cards = append(cards, &xfer.PragmaCard{Name: horizonStructuralPragma}, &xfer.PragmaCard{Name: horizonSeedPragma, Values: []string{offer}}) } } return cards } // seedOffer is the horizon-seed value naming the seed q holds (§6.2.3): the // SHA3-256 name of its bytes, or noSeed. func seedOffer(q db.Querier) (string, error) { held, err := seed.Held(q) if err != nil || held == nil { return noSeed, err } return hash.SHA3(held), nil } // requestCone reads a request's cone: its path members and its horizon, at // the structural depth when the request carries horizon-structural, in // either order. A malformed horizon pragma is ignored, as zit ignores one, // and the last well-formed one stands. func requestCone(cards []xfer.Card) (*cone.Cone, error) { var include, exclude []string var horizon *cone.Horizon structural := false for _, card := range cards { p, ok := card.(*xfer.PragmaCard) if !ok { continue } if p.Name == horizonStructuralPragma { structural = true continue } if p.Name == horizonPragma { if h, ok := requestHorizon(p); ok { horizon = &h } continue } if p.Name != "cone" && p.Name != "cone-not" { continue } if len(p.Values) != 1 || len(p.Values[0]) > 2*cone.MaxMemberLen { return nil, fmt.Errorf("sparse cone: invalid member") } if len(include)+len(exclude) >= cone.MaxMembers { return nil, fmt.Errorf("sparse cone: too many members") } m := deck.FossilDecode(p.Values[0]) if p.Name == "cone" { include = append(include, m) } else { exclude = append(exclude, m) } } if len(include)+len(exclude) == 0 && horizon == nil { return nil, nil } var c *cone.Cone var err error if horizon != nil { if structural { h, herr := cone.NewHorizon(horizon.Hash(), horizon.Rule(), cone.DepthStructural) if herr != nil { panic("sync.requestCone: a valid horizon failed to rebuild: " + herr.Error()) } horizon = &h } c, err = cone.NewWithHorizon(include, exclude, *horizon) } else { c, err = cone.New(include, exclude) } if err != nil { return nil, fmt.Errorf("sparse cone: %w", err) } return c, nil } func requestHorizon(p *xfer.PragmaCard) (cone.Horizon, bool) { if len(p.Values) != 2 { return cone.Horizon{}, false } rule, err := cone.ParseHorizonRule(p.Values[1]) if err != nil { return cone.Horizon{}, false } h, err := cone.NewHorizon(p.Values[0], rule, cone.DepthContent) return h, err == nil } // servedLevel reads a horizon-served pragma's level (§6.2.5); ok is false // for a level this implementation does not know. func servedLevel(p *xfer.PragmaCard) (cone.HorizonDepth, bool) { if len(p.Values) < 1 { return 0, false } d, err := cone.ParseHorizonDepth(p.Values[0]) return d, err == nil } // requestSeedOffer is the seed a structural request says its client holds // (§6.2.3), "" when it names none: the last well-formed horizon-seed wins. func requestSeedOffer(cards []xfer.Card) string { offer := "" for _, card := range cards { p, ok := card.(*xfer.PragmaCard) if ok && p.Name == horizonSeedPragma && len(p.Values) == 1 && hash.IsValidHash(p.Values[0]) { offer = p.Values[0] } } return offer } // withDepth is c with its horizon cut at depth d. func withDepth(c *cone.Cone, d cone.HorizonDepth) *cone.Cone { h, ok := c.Horizon() if !ok { panic("sync.withDepth: the cone has no horizon") } nh, err := cone.NewHorizon(h.Hash(), h.Rule(), d) if err != nil { panic("sync.withDepth: " + err.Error()) } out, err := cone.NewWithHorizon(c.Include(), c.Exclude(), nh) if err != nil { panic("sync.withDepth: " + err.Error()) } return out } // servedCluster reports a cluster artifact sent to a horizon store by an // origin that serves its horizon (draft-fossil-horizon-00 §4.5). The store // holds no cluster, and against such an origin it wants no name a cluster // alone gives, so the card is dropped unstored. Only a full-form card is // tested: a cluster's first card is an M card. func servedCluster(served bool, deltaSrc string, payload []byte) bool { if !served || deltaSrc != "" || !bytes.HasPrefix(payload, []byte("M ")) { return false } d, err := deck.Parse(payload) return err == nil && d.Type == deck.Cluster }