aboutsummaryrefslogtreecommitdiff
path: root/internal/types/operation.go
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-26 14:46:02 -0800
committerFuwn <[email protected]>2026-02-26 14:48:52 -0800
commit0099d621e97b6048971fadb5c71918cc9f2b5a09 (patch)
treea38ba31585200bacd61f453ef7158de7f0aaf7a3 /internal/types/operation.go
parentInitial commit (diff)
downloadplutia-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.go77
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[:])
+}