Merge pull request 'mesh-grants: stop rewriting the shadow user list every 3 s when nothing changed (issue 1126, group fix/1126-switch-safe)' (#91) from fix/1126-switch-safe into main

This commit was merged in pull request #91.
This commit is contained in:
2026-10-27 23:52:23 +00:00
5 changed files with 34 additions and 1 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ module mesh-grants
go 1.26.0 go 1.26.0
require ( require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027201213-7a8e9f970648 git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027232145-7359d187db8d
github.com/nats-io/nats.go v1.54.0 github.com/nats-io/nats.go v1.54.0
) )
+2
View File
@@ -10,6 +10,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027200317-538ffbaa085b h1:LuBK5dtB
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027200317-538ffbaa085b/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027200317-538ffbaa085b/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027201213-7a8e9f970648 h1:XoMsgg9kSgjQWa86NZM8h8OpxJ2kovxcMS2C0i6WSVU= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027201213-7a8e9f970648 h1:XoMsgg9kSgjQWa86NZM8h8OpxJ2kovxcMS2C0i6WSVU=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027201213-7a8e9f970648/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027201213-7a8e9f970648/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027232145-7359d187db8d h1:DhR7DRKaF0OtA5l/ikIk7gZGeIM1lRz65PUE1DGEkvU=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027232145-7359d187db8d/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
+15
View File
@@ -1302,6 +1302,21 @@ func TestGrants(t *testing.T) {
if e := effect(); !strings.HasPrefix(e, "switched") { if e := effect(); !strings.HasPrefix(e, "switched") {
t.Errorf("effect once the bus node loaded the written list = %s", e) t.Errorf("effect once the bus node loaded the written list = %s", e)
} }
// Steady (issue 1126, 2026-10-28 00:16: the shadow list moved every 3 s, 99270 to 99357, with nothing
// differing): the bus node saying again that it loaded the same list, at a later time and the key's
// later revision, moves neither the shadow list nor the sealed key.
_, sealedRev, _ := p.Sealed()
p.Compose()
for i := 0; i < 3; i++ {
withKey(delivery.UserListWritten{Source: delivery.GrantsUserListSource, Revision: sealedRev + uint64(i+1), Wanted: g.Digest,
Digest: g.Digest, Users: g.Users, Follows: delivery.FollowGrants, At: time.Date(2026, 10, 28, 0, 16, i, 0, time.UTC)})
if res := p.Compose(); res.Written {
t.Errorf("pass %d: the shadow list is written again for the bus node saying the same list again (revision %d)", i, res.Revision)
}
if _, rev, _ := p.Sealed(); rev != sealedRev {
t.Errorf("pass %d: the sealed key is written again with nothing changed: revision %d, was %d", i, rev, sealedRev)
}
}
withKey(loadedBefore) withKey(loadedBefore)
// After take-over a list composed past a failed read, or one dropping a user of the list in force for // After take-over a list composed past a failed read, or one dropping a user of the list in force for
// want of a hash, is not sealed: the list in force stays, and the pass says why. // want of a hash, is not sealed: the list in force stays, and the pass says why.
@@ -77,6 +77,13 @@ type UserListState struct {
func (s UserListState) inputs() string { func (s UserListState) inputs() string {
s.At, s.Inputs = time.Time{}, "" s.At, s.Inputs = time.Time{}, ""
// When the bus node said it loaded a list is no input: the same list said again, at a later time and the key's later
// revision, composes nothing new (issue 1126: written again every 3 s, 2026-10-28).
if s.Loaded != nil {
l := *s.Loaded
l.Revision, l.At = 0, time.Time{}
s.Loaded = &l
}
raw, _ := json.Marshal(s) raw, _ := json.Marshal(s)
sum := sha256.Sum256(raw) sum := sha256.Sum256(raw)
return "sha256:" + hex.EncodeToString(sum[:]) return "sha256:" + hex.EncodeToString(sum[:])
@@ -177,7 +177,16 @@ func (p *Part) sayHeld(why string) {
const HeldCondition = "mesh-grants.user-list-held" const HeldCondition = "mesh-grants.user-list-held"
func sameSealed(a, b delivery.GrantsUserList) bool { func sameSealed(a, b delivery.GrantsUserList) bool {
// Its time is no change, and no user left out is the same written as [] or null: the key read back says [] for the
// nil a pass composes, which read as a change wrote the key again at every pass, and each write moved the bus node's
// report and woke the next pass (issue 1126: the shadow list's revision 99270 to 99357 in 90 s, 2026-10-28).
a.At, b.At = b.At, a.At a.At, b.At = b.At, a.At
if len(a.LeftOut) == 0 {
a.LeftOut = nil
}
if len(b.LeftOut) == 0 {
b.LeftOut = nil
}
x, _ := json.Marshal(a) x, _ := json.Marshal(a)
y, _ := json.Marshal(b) y, _ := json.Marshal(b)
return string(x) == string(y) return string(x) == string(y)