package nodeengineclient import ( "encoding/json" "fmt" "time" seat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-sdk/go/keys" ) // A node's own health report (the state health, one key per node, written by that node's engine alone) and the data items // a module declares as the engine placed them (the state data, `.`): their layouts are the seat's contract // (seat.HealthByNodeValue, seat.DataByNodeModuleValue); this file is their codec, shared by the engine and its readers. // RootContract is the statement contract from which an engine judges an account's root: a report whose contract is lower // judged nothing about root, and a reader must not read its empty root verdicts as healthy (HealthByNodeValue.Contract). const RootContract = 3 // EncodeHealth is h as JSON at the current version; it refuses a report naming no node, and a fall back to the old login // that names another node, no login fallen back from, no reason or no time. func EncodeHealth(h seat.HealthByNodeValue) ([]byte, error) { h.V = keys.Version if h.Node == "" { return nil, fmt.Errorf("node-engine: a node's health report names its node") } if f := h.Fallback; f != nil && (f.Node != h.Node || f.From == "" || f.Why == "" || f.At.IsZero()) { return nil, fmt.Errorf("node-engine: a node's fall back to its old login names its node %s, the login fallen back from, why and when", h.Node) } return json.Marshal(h) } // DecodeHealth reads a report; it refuses another version (keys.ErrUnknownVersion) and a field the contract does not know. func DecodeHealth(raw []byte) (seat.HealthByNodeValue, error) { if err := keys.CheckVersion(raw); err != nil { return seat.HealthByNodeValue{}, err } h, err := seat.DecodeHealthByNodeValue(raw) if err != nil { return seat.HealthByNodeValue{}, fmt.Errorf("node-engine: a node's health report is not readable: %w", err) } return h, nil } // SameHealth says two reports say the same, whenever each was written. func SameHealth(a, b seat.HealthByNodeValue) bool { a.At, b.At = time.Time{}, time.Time{} return sameJSON(a, b) } // DataKey is a module's key on a node in the state data, `.`, its module part escaped as a have's is. func DataKey(node, module string) (string, error) { return HaveKey(node, module) } // DataNodeKeys is the pattern of every data key of a node, `.*`. func DataNodeKeys(node string) (string, error) { if err := keys.CheckName("node", node); err != nil { return "", err } return node + ".*", nil } // EncodeData is d as JSON at the current version; it refuses a node or module the key refuses. func EncodeData(d seat.DataByNodeModuleValue) ([]byte, error) { d.V = keys.Version if _, err := DataKey(d.Node, d.Module); err != nil { return nil, err } if d.Items == nil { d.Items = []seat.PlacedDataItem{} } return json.Marshal(d) } // DecodeData reads a module's data items; it refuses another version (keys.ErrUnknownVersion). func DecodeData(raw []byte) (seat.DataByNodeModuleValue, error) { if err := keys.CheckVersion(raw); err != nil { return seat.DataByNodeModuleValue{}, err } d, err := seat.DecodeDataByNodeModuleValue(raw) if err != nil { return seat.DataByNodeModuleValue{}, fmt.Errorf("node-engine: a module's data items are not readable: %w", err) } return d, nil }