plc-mirror/util/plc/structs.go

150 lines
3.6 KiB
Go
Raw Normal View History

package plc
import (
"bytes"
"encoding/json"
"fmt"
cid "github.com/ipfs/go-cid"
"github.com/multiformats/go-multicodec"
"github.com/multiformats/go-multihash"
cbg "github.com/whyrusleeping/cbor-gen"
)
//go:generate go run ./gen
type Op struct {
Type string `json:"type" cborgen:"type,const=plc_operation"`
RotationKeys []string `json:"rotationKeys" cborgen:"rotationKeys"`
VerificationMethods map[string]string `json:"verificationMethods" cborgen:"verificationMethods"`
AlsoKnownAs []string `json:"alsoKnownAs" cborgen:"alsoKnownAs"`
Services map[string]Service `json:"services" cborgen:"services"`
Prev *string `json:"prev" cborgen:"prev"`
Sig *string `json:"sig" cborgen:"sig,omitempty"`
}
type Service struct {
Type string `json:"type" cborgen:"type"`
Endpoint string `json:"endpoint" cborgen:"endpoint"`
}
type Tombstone struct {
Type string `json:"type" cborgen:"type,const=plc_tombstone"`
Prev string `json:"prev" cborgen:"prev"`
Sig *string `json:"sig" cborgen:"sig,omitempty"`
}
type LegacyCreateOp struct {
Type string `json:"type" cborgen:"type,const=create"`
SigningKey string `json:"signingKey" cborgen:"signingKey"`
RecoveryKey string `json:"recoveryKey" cborgen:"recoveryKey"`
Handle string `json:"handle" cborgen:"handle"`
Service string `json:"service" cborgen:"service"`
Prev *string `json:"prev" cborgen:"prev"`
Sig *string `json:"sig" cborgen:"sig,omitempty"`
}
func (op *LegacyCreateOp) AsUnsignedOp() Op {
return Op{
Type: "plc_operation",
Prev: op.Prev,
AlsoKnownAs: []string{fmt.Sprintf("at://%s", op.Handle)},
RotationKeys: []string{op.RecoveryKey},
Services: map[string]Service{
"atproto_pds": {
Type: "AtprotoPersonalDataServer",
Endpoint: op.Service,
}},
VerificationMethods: map[string]string{
"atproto": op.SigningKey,
},
}
}
type OperationKind interface {
CID() (cid.Cid, error)
}
type Operation struct {
Value OperationKind
}
type OperationLogEntry struct {
DID string `json:"did"`
Operation Operation `json:"operation"`
CID string `json:"cid"`
Nullified bool `json:"nullified"`
CreatedAt string `json:"createdAt"`
}
func unmarshal[T any](b []byte) (T, error) {
var out T
if err := json.Unmarshal(b, &out); err != nil {
return out, err
}
return out, nil
}
func (o *Operation) UnmarshalJSON(b []byte) error {
var partial struct {
Type string `json:"type"`
}
if err := json.Unmarshal(b, &partial); err != nil {
return err
}
switch partial.Type {
case "create":
v, err := unmarshal[LegacyCreateOp](b)
if err != nil {
return err
}
o.Value = v
return nil
case "plc_operation":
v, err := unmarshal[Op](b)
if err != nil {
return err
}
o.Value = v
return nil
case "plc_tombstone":
v, err := unmarshal[Tombstone](b)
if err != nil {
return err
}
o.Value = v
return nil
default:
return fmt.Errorf("unsupported 'type' value: %q", partial.Type)
}
}
func (o Operation) MarshalJSON() ([]byte, error) {
return json.Marshal(o.Value)
}
func calculateCid(v cbg.CBORMarshaler) (cid.Cid, error) {
b := bytes.NewBuffer(nil)
if err := v.MarshalCBOR(b); err != nil {
return cid.Cid{}, fmt.Errorf("marshaling as CBOR: %w", err)
}
return cid.V1Builder{
Codec: uint64(multicodec.DagCbor),
MhType: multihash.SHA2_256,
}.Sum(b.Bytes())
}
func (o Op) CID() (cid.Cid, error) {
return calculateCid(&o)
}
func (o Tombstone) CID() (cid.Cid, error) {
return calculateCid(&o)
}
func (o LegacyCreateOp) CID() (cid.Cid, error) {
return calculateCid(&o)
}