A very experimental PLC implementation which uses BFT consensus for decentralization
1package abciapp
2
3import (
4 "context"
5 "encoding/json"
6
7 "github.com/did-method-plc/go-didplc"
8 cbornode "github.com/ipfs/go-ipld-cbor"
9 "github.com/palantir/stacktrace"
10 "tangled.org/gbl08ma.com/didplcbft/plc"
11)
12
13var TransactionActionCreatePlcOp = registerTransactionAction[CreatePlcOpArguments]("CreatePlcOp", processCreatePlcOpTx)
14
15type CreatePlcOpArguments struct {
16 DID string `json:"did" refmt:"did"`
17 Operation *didplc.OpEnum `refmt:"operation"`
18}
19
20func (CreatePlcOpArguments) ForAction() TransactionAction {
21 return TransactionActionCreatePlcOp
22}
23
24func init() {
25 cbornode.RegisterCborType(CreatePlcOpArguments{})
26 cbornode.RegisterCborType(Transaction[CreatePlcOpArguments]{})
27}
28
29func processCreatePlcOpTx(ctx context.Context, deps TransactionProcessorDependencies, txBytes []byte) (*processResult, error) {
30 tx, err := UnmarshalTransaction[CreatePlcOpArguments](txBytes)
31 if err != nil {
32 return &processResult{
33 Code: 4000,
34 Info: err.Error(),
35 }, nil
36 }
37
38 // sadly didplc is really designed to unmarshal JSON, not CBOR
39 // so JSON ends up being the lingua franca for operations inside our PLC implementation too
40 // we also can't instance didplc.Operations directly from the CBOR unmarshaller (the MakeUnmarshalTransformFunc thing)
41 // because the interface makes us lose data (it is not powerful enough to detect the type of a transaction, for instance)
42 // so our PLC internals end up depending on OpEnum, too
43 // the decision to use CBOR for the entire thing at the blockchain transaction level is:
44 // - to make transactions more compact
45 // - to have more of a canonical format for them (we specifically use the stable CBOR format already used by the PLC for signing)
46
47 // there is one advantage to this approach: by ensuring we first unmarshal the operations into strongly defined types
48 // (e.g. the OpEnum struct of the didplc package)
49 // we avoid accepting malformed data like what happened in https://github.com/did-method-plc/did-method-plc/issues/71
50 opBytes, err := json.Marshal(tx.Arguments.Operation)
51 if err != nil {
52 return nil, stacktrace.Propagate(err, "internal error")
53 }
54
55 if writeTx, ok := deps.writeTx.Get(); ok {
56 err = deps.plc.ExecuteOperation(ctx, writeTx, tx.Arguments.DID, opBytes)
57 } else {
58 err = deps.plc.ValidateOperation(ctx, deps.readTx, tx.Arguments.DID, opBytes)
59 }
60 if err != nil {
61 if code, ok := plc.InvalidOperationErrorCode(err); ok {
62 return &processResult{
63 Code: code,
64 Info: err.Error(),
65 }, nil
66 }
67 return nil, stacktrace.Propagate(err, "internal error")
68 }
69
70 return &processResult{
71 Code: 0,
72 }, nil
73}