carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "crypto/sha256"
5 "encoding/hex"
6 "fmt"
7 "time"
8)
9
10type LocalDischarge struct {
11 Qualified bool `json:"qualified"`
12 Records []string `json:"records,omitempty"`
13 Reason string `json:"reason,omitempty"`
14}
15
16// AutomaticLoss is claim §7.3 D4's "Automatic loss persistence": what the
17// transition that consumed a termination fence retains on the claim itself,
18// outside the replaceable operation, until the console materializes the
19// D4 host loss record. It survives CompleteRelocation, free and holder
20// replacement because it lives beside loss_refs, not in op.journal.
21type AutomaticLoss struct {
22 ID string `json:"id"` // the loss identity, also in loss_refs
23 Box string `json:"box"`
24 Enrollment string `json:"enrollment"`
25 Fence FenceRef `json:"fence"`
26 ObservedAt string `json:"observed_at"`
27 Op string `json:"op"`
28 Kind string `json:"kind"`
29 Phase string `json:"phase"`
30 Role string `json:"role"` // "source" | "target"
31 // Incarnation and Stamp bind the loss to the holder it was taken
32 // against; Generation and Session are the box's last writer session,
33 // whether it is still the writer or was superseded, and LastAdmission
34 // is that session's admission record when the claim holds one (the
35 // consequences the console reports: its coverage and declarations).
36 Incarnation string `json:"incarnation"`
37 Stamp uint64 `json:"stamp"`
38 Generation string `json:"generation,omitempty"`
39 Session uint64 `json:"session,omitempty"`
40 LastAdmission *AdmissionRecord `json:"last_admission,omitempty"`
41 Discharge LocalDischarge `json:"discharge"`
42 // Reassigned is the regional work (park-generation, park-replica)
43 // the box owned that the same CAS passed to its region (L3, as
44 // DeclareLost's reassign_regional): the console's D4 record names it.
45 Reassigned []ObligationRef `json:"reassigned,omitempty"`
46 Timers TimerKnowledge `json:"timers"`
47 // Reported is set by the console once the D4 record holds this loss
48 // and its alert work exists. Until then reporting is pending.
49 Reported bool `json:"reported,omitempty"`
50}
51
52// automaticLossID is deterministic for one fence consumed by one
53// operation for one box, so a replayed transition names the same loss and
54// the console's materialization deduplicates on it (D4: "Retries
55// deduplicate by the retained loss identity").
56func automaticLossID(opID, box string, ref FenceRef) string {
57 sum := sha256.Sum256([]byte(opID + "\x00" + box + "\x00" + ref.Enrollment + "\x00" + ref.SHA256))
58 return "auto-" + hex.EncodeToString(sum[:16])
59}
60
61func validateAutomaticLosses(c *Claim, refs map[string]bool) error {
62 seen := map[string]bool{}
63 for _, l := range c.EvidenceRefs {
64 if !refs[l.ID] || seen[l.ID] || l.ID != automaticLossID(l.Op, l.Box, l.Fence) || !validBoxReference(l.Box) || !validFenceRef(l.Fence) || l.Enrollment != l.Fence.Enrollment || l.Op == "" || l.Kind == "" || l.Phase == "" || !isHex64(l.Incarnation) || (l.Role != "source" && l.Role != "target") || (l.Timers != TimersComplete && l.Timers != TimersPartial && l.Timers != TimersUnknown) {
65 return fmt.Errorf("%w: automatic loss evidence", ErrMalformed)
66 }
67 at, err := time.Parse(time.RFC3339Nano, l.ObservedAt)
68 if err != nil || at.IsZero() {
69 return fmt.Errorf("%w: automatic loss observation time", ErrMalformed)
70 }
71 if l.LastAdmission != nil && (l.LastAdmission.Box != l.Box || l.LastAdmission.Session != l.Session || l.LastAdmission.Generation != l.Generation || !validWriteLosses(l.LastAdmission.WritesLost, l.LastAdmission.AdditionalWritesLost)) {
72 return fmt.Errorf("%w: automatic loss admission", ErrMalformed)
73 }
74 if l.Discharge.Qualified && len(l.Discharge.Records) == 0 {
75 return fmt.Errorf("%w: automatic loss discharge", ErrMalformed)
76 }
77 for _, ref := range l.Reassigned {
78 if ref.ID == "" || ref.Op == "" || ref.Retirement == "" {
79 return fmt.Errorf("%w: automatic loss regional work", ErrMalformed)
80 }
81 }
82 seen[l.ID] = true
83 }
84 return nil
85}
86