216 lines
8.2 KiB
Go
216 lines
8.2 KiB
Go
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[:])
|
|
}
|