carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "bytes"
5 "context"
6 "crypto/ed25519"
7 "errors"
8 "os"
9)
10
11// SystemPublication retains the exact CAS and signed bytes across retries.
12type SystemPublication struct {
13 Component string `json:"component"`
14 Channel string `json:"channel"`
15 ID string `json:"id"`
16 ExpectedETag string `json:"expected_etag"`
17 Before []byte `json:"before,omitempty"`
18 After []byte `json:"after"`
19}
20
21func (p SystemPublication) Verify(pub ed25519.PublicKey) (*SystemPointer, error) {
22 next, err := verifySystemPointer(pub, p.After, p.Component, p.Channel)
23 if err != nil {
24 return nil, err
25 }
26 if next.ActivationID != p.ID || next.CancellationID != "" {
27 return nil, ErrBadSystemPointer
28 }
29 return next, nil
30}
31
32func PublishSystemPublication(ctx context.Context, s Store, pub ed25519.PublicKey, p SystemPublication) (*SystemPointer, error) {
33 next, err := p.Verify(pub)
34 if err != nil {
35 return nil, err
36 }
37 key := SystemPointerKey(p.Component, p.Channel)
38 _, err = s.Put(ctx, key, p.After, p.ExpectedETag)
39 if err == nil {
40 return next, nil
41 }
42 // A failed response can follow a committed write. Only these exact signed
43 // bytes prove completion; a different pointer never authorizes another CAS.
44 raw, _, readErr := s.Get(ctx, key)
45 if readErr == nil && bytes.Equal(raw, p.After) {
46 return next, nil
47 }
48 return nil, err
49}
50
51// CancelSystemPublication competes with publication on its original pointer
52// ETag. The fence must be durable before the actor reservation is removed.
53func CancelSystemPublication(ctx context.Context, s Store, sg Signer, p SystemPublication) error {
54 if p.ID == "" {
55 return ErrBadSystemPointer
56 }
57 if _, err := p.Verify(sg.verifyPub()); err != nil {
58 return err
59 }
60 before := &SystemPointer{Schema: 1, Component: p.Component, Channel: p.Channel}
61 if len(p.Before) != 0 {
62 var err error
63 before, err = verifySystemPointer(sg.verifyPub(), p.Before, p.Component, p.Channel)
64 if err != nil {
65 return err
66 }
67 }
68 before.CancellationID = p.ID
69 fence, err := SignRootEnvelope(sg, before)
70 if err != nil {
71 return err
72 }
73 key := SystemPointerKey(p.Component, p.Channel)
74 raw, etag, err := s.Get(ctx, key)
75 if err == nil && bytes.Equal(raw, fence) {
76 return nil
77 }
78 if err != nil && !errors.Is(err, os.ErrNotExist) {
79 return err
80 }
81 if etag != p.ExpectedETag || (err == nil && bytes.Equal(raw, p.After)) {
82 return ErrCASConflict
83 }
84 _, err = s.Put(ctx, key, fence, p.ExpectedETag)
85 if err == nil {
86 return nil
87 }
88 raw, _, readErr := s.Get(ctx, key)
89 if readErr == nil && bytes.Equal(raw, fence) {
90 return nil
91 }
92 return err
93}
94