The assigner's state and verb placement are assignments, with its events, tools, views and every caller; the assigner copies the retired state at its start (issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
package assignment
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
seat "git.novox.be/novox/mesh-contracts/go/seats/meshmoduleassigner"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
)
|
||||
|
||||
// The digests here are of values a proposal's ask and a seed were bound to before this package: the fields are written in
|
||||
// the order the first writer wrote them (the generated contract types order theirs by name), so a digest an earlier holder
|
||||
// bound an ask to is the one this computes. Golden values in the tests hold it.
|
||||
|
||||
type providerForm struct {
|
||||
Node string `json:"node"`
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
type placedForm struct {
|
||||
Module string `json:"module"`
|
||||
Ports map[string]int64 `json:"ports,omitempty"`
|
||||
Bindings map[string]providerForm `json:"bindings,omitempty"`
|
||||
MeshWide map[string]any `json:"mesh-wide,omitempty"`
|
||||
HasMesh bool `json:"has-mesh-wide,omitempty"`
|
||||
Layer map[string]any `json:"layer,omitempty"`
|
||||
HasLayer bool `json:"has-layer,omitempty"`
|
||||
MeshValues map[string]any `json:"mesh-wide-values,omitempty"`
|
||||
Values map[string]any `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
type holdingForm struct {
|
||||
Claim string `json:"claim"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Module string `json:"module"`
|
||||
Site string `json:"site,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
}
|
||||
|
||||
type decidedForm struct {
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
Site string `json:"site,omitempty"`
|
||||
Hub bool `json:"hub,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
PublicDomain string `json:"public-domain,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
AccountHome string `json:"account-home,omitempty"`
|
||||
AgentAccount string `json:"agent-account,omitempty"`
|
||||
AgentHome string `json:"agent-account-home,omitempty"`
|
||||
Adopted bool `json:"adopted,omitempty"`
|
||||
Taken []string `json:"taken,omitempty"`
|
||||
Tunnel *tunnelForm `json:"tunnel,omitempty"`
|
||||
PeerNames map[string]string `json:"peer-names,omitempty"`
|
||||
}
|
||||
|
||||
type tunnelForm struct {
|
||||
Interface string `json:"interface"`
|
||||
Unit string `json:"unit,omitempty"`
|
||||
Config string `json:"config,omitempty"`
|
||||
Port int64 `json:"port,omitempty"`
|
||||
MTU int64 `json:"mtu,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Range string `json:"range,omitempty"`
|
||||
PublicKey string `json:"public-key,omitempty"`
|
||||
Peers []tunnelPeerForm `json:"peers,omitempty"`
|
||||
}
|
||||
|
||||
type tunnelPeerForm struct {
|
||||
PublicKey string `json:"public-key"`
|
||||
Address string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
type assignmentsForm struct {
|
||||
V int `json:"v"`
|
||||
Node string `json:"node"`
|
||||
Revision string `json:"revision,omitempty"`
|
||||
Decided decidedForm `json:"decided"`
|
||||
Modules []placedForm `json:"modules"`
|
||||
Pins map[string]providerForm `json:"pins,omitempty"`
|
||||
Holds []holdingForm `json:"holds,omitempty"`
|
||||
}
|
||||
|
||||
type joiningForm struct {
|
||||
Identity string `json:"identity"`
|
||||
Overlay string `json:"overlay,omitempty"`
|
||||
Sealing string `json:"sealing"`
|
||||
Serving string `json:"serving,omitempty"`
|
||||
}
|
||||
|
||||
type changeForm struct {
|
||||
Op string `json:"op"`
|
||||
Node string `json:"node"`
|
||||
Module string `json:"module,omitempty"`
|
||||
Ports map[string]int64 `json:"ports,omitempty"`
|
||||
Bindings map[string]providerForm `json:"bindings,omitempty"`
|
||||
Provision string `json:"provision,omitempty"`
|
||||
Provider *providerForm `json:"provider,omitempty"`
|
||||
Claim string `json:"claim,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Site string `json:"site,omitempty"`
|
||||
Field string `json:"field,omitempty"`
|
||||
Value json.RawMessage `json:"value,omitempty"`
|
||||
MeshWide map[string]any `json:"mesh-wide,omitempty"`
|
||||
Layer map[string]any `json:"layer,omitempty"`
|
||||
MeshValues map[string]any `json:"mesh-wide-values,omitempty"`
|
||||
Values map[string]any `json:"values,omitempty"`
|
||||
Whole *assignmentsForm `json:"whole,omitempty"`
|
||||
Decided *decidedForm `json:"decided,omitempty"`
|
||||
Keys *joiningForm `json:"keys,omitempty"`
|
||||
}
|
||||
|
||||
func providerOf(p seat.Provider) providerForm { return providerForm{Node: p.Node, Module: p.Module} }
|
||||
|
||||
func providersOf(m map[string]seat.Provider) map[string]providerForm {
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]providerForm, len(m))
|
||||
for k, v := range m {
|
||||
out[k] = providerOf(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tunnelOf(t *seat.FoundTunnel) *tunnelForm {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
out := &tunnelForm{Interface: t.Interface, Unit: t.Unit, Config: t.Config, Port: t.Port, MTU: t.Mtu, Address: t.Address, Range: t.Range, PublicKey: t.PublicKey}
|
||||
for _, p := range t.Peers {
|
||||
out.Peers = append(out.Peers, tunnelPeerForm{PublicKey: p.PublicKey, Address: p.Address})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func decidedOf(d seat.NodeDecided) decidedForm {
|
||||
return decidedForm{Endpoint: d.Endpoint, Site: d.Site, Hub: d.Hub, Address: d.Address, PublicDomain: d.PublicDomain,
|
||||
Account: d.Account, AccountHome: d.AccountHome, AgentAccount: d.AgentAccount, AgentHome: d.AgentAccountHome,
|
||||
Adopted: d.Adopted, Taken: d.Taken, Tunnel: tunnelOf(d.Tunnel), PeerNames: d.PeerNames}
|
||||
}
|
||||
|
||||
func assignmentsOf(p seat.Assignments) assignmentsForm {
|
||||
out := assignmentsForm{V: int(p.V), Node: p.Node, Revision: p.Revision, Decided: decidedOf(p.Decided),
|
||||
Pins: providersOf(p.Pins)}
|
||||
if p.Modules != nil {
|
||||
out.Modules = []placedForm{}
|
||||
}
|
||||
for _, m := range p.Modules {
|
||||
out.Modules = append(out.Modules, placedForm{Module: m.Module, Ports: m.Ports, Bindings: providersOf(m.Bindings),
|
||||
MeshWide: m.MeshWide, HasMesh: m.HasMeshWide, Layer: m.Layer, HasLayer: m.HasLayer, MeshValues: m.MeshWideValues, Values: m.Values})
|
||||
}
|
||||
for _, h := range p.Holds {
|
||||
out.Holds = append(out.Holds, holdingForm{Claim: h.Claim, Scope: h.Scope, Module: h.Module, Site: h.Site, Kind: h.Kind})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func changeOf(c seat.ProposalChange) changeForm {
|
||||
out := changeForm{Op: c.Op, Node: c.Node, Module: c.Module, Ports: c.Ports, Bindings: providersOf(c.Bindings), Provision: c.Provision,
|
||||
Claim: c.Claim, Scope: c.Scope, Kind: c.Kind, Site: c.Site, Field: c.Field, MeshWide: c.MeshWide, Layer: c.Layer,
|
||||
MeshValues: c.MeshWideValues, Values: c.Values}
|
||||
if c.Provider != nil {
|
||||
p := providerOf(*c.Provider)
|
||||
out.Provider = &p
|
||||
}
|
||||
if c.Value != nil {
|
||||
out.Value, _ = json.Marshal(c.Value)
|
||||
}
|
||||
if c.Whole != nil {
|
||||
w := assignmentsOf(*c.Whole)
|
||||
out.Whole = &w
|
||||
}
|
||||
if c.Decided != nil {
|
||||
d := decidedOf(*c.Decided)
|
||||
out.Decided = &d
|
||||
}
|
||||
if c.Keys != nil {
|
||||
out.Keys = &joiningForm{Identity: c.Keys.Identity, Overlay: c.Keys.Overlay, Sealing: c.Keys.Sealing, Serving: c.Keys.Serving}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ExportDigest is the digest of an export's answer, every node's assignments, as the seed proposes it: the same
|
||||
// assignments give the same digest, whatever order they came in.
|
||||
func ExportDigest(assignments map[string]seat.Assignments) string {
|
||||
h := sha256.New()
|
||||
for _, node := range sortedKeys(assignments) {
|
||||
p := assignments[node]
|
||||
p.V = keys.Version // the export was made with the assignments encoded at the keys' version
|
||||
raw, _ := json.Marshal(assignmentsOf(p))
|
||||
fmt.Fprintf(h, "%s\n%s\n", node, raw)
|
||||
}
|
||||
return "sha256:" + hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
// ChangesDigest is the digest of a proposal's changes, which its authorising ask is bound to (§5).
|
||||
func ChangesDigest(p seat.Proposal) string {
|
||||
var changes []changeForm
|
||||
if p.Changes != nil {
|
||||
changes = make([]changeForm, 0, len(p.Changes))
|
||||
}
|
||||
for _, c := range p.Changes {
|
||||
changes = append(changes, changeOf(c))
|
||||
}
|
||||
raw, _ := json.Marshal(struct {
|
||||
ID string `json:"id"`
|
||||
Changes []changeForm `json:"changes"`
|
||||
Base map[string]int64 `json:"base"`
|
||||
}{p.ID, changes, p.Base})
|
||||
sum := sha256.Sum256(raw)
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
}
|
||||
Reference in New Issue
Block a user