diff options
| author | Fuwn <[email protected]> | 2026-02-26 14:46:02 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-26 14:48:52 -0800 |
| commit | 0099d621e97b6048971fadb5c71918cc9f2b5a09 (patch) | |
| tree | a38ba31585200bacd61f453ef7158de7f0aaf7a3 /internal/types/operation.go | |
| parent | Initial commit (diff) | |
| download | plutia-test-0099d621e97b6048971fadb5c71918cc9f2b5a09.tar.xz plutia-test-0099d621e97b6048971fadb5c71918cc9f2b5a09.zip | |
feat: initial Plutia release — verifiable high-performance PLC mirror (mirror + resolver modes)
Diffstat (limited to 'internal/types/operation.go')
| -rw-r--r-- | internal/types/operation.go | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/internal/types/operation.go b/internal/types/operation.go new file mode 100644 index 0000000..2facb82 --- /dev/null +++ b/internal/types/operation.go @@ -0,0 +1,77 @@ +package types + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" +) + +// ExportRecord mirrors a line/event from plc.directory export stream. +type ExportRecord struct { + Seq uint64 `json:"seq"` + DID string `json:"did"` + CreatedAt string `json:"createdAt,omitempty"` + CID string `json:"cid,omitempty"` + Nullified bool `json:"nullified,omitempty"` + Operation json.RawMessage `json:"operation"` +} + +type ParsedOperation struct { + DID string + CanonicalBytes []byte + Payload map[string]any + Sequence uint64 + CID string + Prev string + RawRecord ExportRecord +} + +func CanonicalizeJSON(raw []byte) ([]byte, error) { + trimmed := bytes.TrimSpace(raw) + if len(trimmed) == 0 { + return nil, fmt.Errorf("empty json payload") + } + if !json.Valid(trimmed) { + return nil, fmt.Errorf("invalid json payload") + } + var out bytes.Buffer + if err := json.Compact(&out, trimmed); err != nil { + return nil, fmt.Errorf("compact json: %w", err) + } + return out.Bytes(), nil +} + +func ParseOperation(rec ExportRecord) (ParsedOperation, error) { + if rec.DID == "" { + return ParsedOperation{}, fmt.Errorf("missing did") + } + canonical, err := CanonicalizeJSON(rec.Operation) + if err != nil { + return ParsedOperation{}, err + } + payload := map[string]any{} + if err := json.Unmarshal(canonical, &payload); err != nil { + return ParsedOperation{}, fmt.Errorf("decode operation: %w", err) + } + prev, _ := payload["prev"].(string) + cid := rec.CID + if cid == "" { + cid = ComputeDigestCID(canonical) + } + return ParsedOperation{ + DID: rec.DID, + CanonicalBytes: canonical, + Payload: payload, + Sequence: rec.Seq, + CID: cid, + Prev: prev, + RawRecord: rec, + }, nil +} + +func ComputeDigestCID(payload []byte) string { + sum := sha256.Sum256(payload) + return "sha256:" + hex.EncodeToString(sum[:]) +} |