package release import ( "os" "slices" "strings" "github.com/carlosframework/platform/internal/rootstate" ) // Recovery uses usable denials without asserting that the inventory is complete. // Minting continues to require Refusals and its durable completeness checks. func (h *RootHistory) recoveryDenials(b HistoryBinding) []Refusal { if h == nil || h.Store == nil || !b.valid() { return nil } var refs []Refusal raw, err := h.Store.Read(historyKey(b)) var record historyRecord if err == nil && len(raw) <= rootstate.MaxRecord && strictCanonicalJSON(raw, &record) == nil && record.Schema == 1 && record.Binding == b { for _, ref := range record.Refusals.Values { if ref.Schema == 1 && ref.Host == b.Host && ref.App == b.App && ref.Version != "" && !ref.RefusedAt.IsZero() && ref.MoveOp == "" && (ref.Kind == RefusalNotUp || ref.Kind == RefusalWrongProgram || ref.Kind == RefusalNotProven) { refs = append(refs, ref) } } } names, _ := h.Store.Names() var outcomes []HistoryRecovery for _, name := range names { if !strings.HasSuffix(name, ".outcome") { continue } raw, err := h.Store.Read(name) var r historyRecoveryRecord if err != nil || len(raw) > rootstate.MaxRecord || strictCanonicalJSON(raw, &r) != nil || r.Schema != 1 || !validHistoryRecovery(r.HistoryRecovery) || historyRecoveryKey(r.HistoryRecovery) != name { continue } outcomes = append(outcomes, r.HistoryRecovery) } for _, r := range outcomes { if r.Binding != b || (!r.Failed && !r.Recovering) { continue } retired, err := h.recoveryRetired(r, outcomes) if err == nil && retired { continue } refs = append(refs, Refusal{Schema: 1, Host: b.Host, App: b.App, Version: r.Attempt.Version, SHA256: r.Attempt.SHA256, Platform: r.Attempt.Platform, PointerPromotedAt: r.Attempt.PromotedAt, RefusedAt: r.Attempt.Started, Kind: RefusalNotUp}) } return refs } // RetireD1 accepts only an independently verified later promotion of this // version. Program and proof refusals keep their separate authorization rules. func (h *RootHistory) RetireD1(b HistoryBinding, p *ChannelPointer) error { if p == nil || p.Version == "" || p.PromotedAt.IsZero() { return os.ErrPermission } r, err := h.read(b) if err != nil { return err } refs := slices.DeleteFunc(slices.Clone(r.Refusals.Values), func(v Refusal) bool { return v.Kind == RefusalNotUp && v.Version == p.Version && p.PromotedAt.After(v.PointerPromotedAt) }) if len(refs) == len(r.Refusals.Values) { return nil } if r.Revision == ^uint64(0) { return os.ErrPermission } r.Revision++ r.Refusals.Values, r.Refusals.Revision = refs, r.Revision return h.write(r) }