A very experimental PLC implementation which uses BFT consensus for decentralization
19
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement entirety of PLC API in ABCI Query method

gbl08ma 59f183f7 3a3acdb4

+138 -85
+98 -45
abciapp/info.go
··· 11 11 "time" 12 12 13 13 abcitypes "github.com/cometbft/cometbft/abci/types" 14 + "github.com/did-method-plc/go-didplc" 14 15 "github.com/gbl08ma/stacktrace" 15 16 "github.com/ucarion/urlpath" 16 17 "tangled.org/gbl08ma.com/didplcbft/plc" ··· 39 40 if err != nil || url.Host != "" || url.Scheme != "" { 40 41 return &abcitypes.ResponseQuery{ 41 42 Code: 6000, 42 - Info: "Invalid path", 43 + Log: "Invalid path", 43 44 }, nil 44 45 } 45 46 ··· 53 54 if err != nil { 54 55 return &abcitypes.ResponseQuery{ 55 56 Code: 6001, 56 - Info: "Unavailable height", 57 + Log: "Unavailable height", 57 58 }, nil 58 59 } 59 60 ··· 66 67 handler: func(match urlpath.Match) (*abcitypes.ResponseQuery, error) { 67 68 did := match.Params["did"] 68 69 doc, err := d.plc.Resolve(ctx, readTx, did) 69 - if err != nil { 70 - switch { 71 - case errors.Is(err, plc.ErrDIDNotFound): 72 - return &abcitypes.ResponseQuery{ 73 - Key: []byte(did), 74 - Code: http.StatusNotFound, 75 - Info: "DID not registered: " + did, 76 - }, nil 77 - case errors.Is(err, plc.ErrDIDGone): 78 - return &abcitypes.ResponseQuery{ 79 - Key: []byte(did), 80 - Code: http.StatusGone, 81 - Info: "DID not available: " + did, 82 - }, nil 83 - default: 84 - return nil, stacktrace.Propagate(err) 85 - } 70 + if resp := handlePLCError(err, did); resp != nil { 71 + return resp, nil 72 + } else if err != nil { 73 + return nil, stacktrace.Propagate(err) 86 74 } 87 75 88 76 docJSON, err := json.Marshal(doc) ··· 100 88 { 101 89 matcher: urlpath.New("/plc/:did/log"), 102 90 handler: func(match urlpath.Match) (*abcitypes.ResponseQuery, error) { 91 + did := match.Params["did"] 92 + ops, err := d.plc.OperationLog(ctx, readTx, did) 93 + if resp := handlePLCError(err, did); resp != nil { 94 + return resp, nil 95 + } else if err != nil { 96 + return nil, stacktrace.Propagate(err) 97 + } 98 + 99 + opsJSON, err := json.Marshal(ops) 100 + if err != nil { 101 + return nil, stacktrace.Propagate(err) 102 + } 103 + 103 104 return &abcitypes.ResponseQuery{ 104 - Code: 1, 105 - Info: "Not implemented", 105 + Key: []byte(did), 106 + Value: []byte(opsJSON), 107 + Code: 0, 106 108 }, nil 107 109 }, 108 110 }, 109 111 { 110 112 matcher: urlpath.New("/plc/:did/log/audit"), 111 113 handler: func(match urlpath.Match) (*abcitypes.ResponseQuery, error) { 114 + did := match.Params["did"] 115 + entries, err := d.plc.AuditLog(ctx, readTx, did) 116 + if resp := handlePLCError(err, did); resp != nil { 117 + return resp, nil 118 + } else if err != nil { 119 + return nil, stacktrace.Propagate(err) 120 + } 121 + 122 + entriesJSON, err := json.Marshal(entries) 123 + if err != nil { 124 + return nil, stacktrace.Propagate(err) 125 + } 126 + 112 127 return &abcitypes.ResponseQuery{ 113 - Code: 1, 114 - Info: "Not implemented", 128 + Key: []byte(did), 129 + Value: []byte(entriesJSON), 130 + Code: 0, 115 131 }, nil 116 132 }, 117 133 }, 118 134 { 119 135 matcher: urlpath.New("/plc/:did/log/last"), 120 136 handler: func(match urlpath.Match) (*abcitypes.ResponseQuery, error) { 137 + did := match.Params["did"] 138 + op, err := d.plc.LastOperation(ctx, readTx, did) 139 + if resp := handlePLCError(err, did); resp != nil { 140 + return resp, nil 141 + } else if err != nil { 142 + return nil, stacktrace.Propagate(err) 143 + } 144 + 145 + opJSON, err := json.Marshal(op) 146 + if err != nil { 147 + return nil, stacktrace.Propagate(err) 148 + } 149 + 121 150 return &abcitypes.ResponseQuery{ 122 - Code: 1, 123 - Info: "Not implemented", 151 + Key: []byte(did), 152 + Value: []byte(opJSON), 153 + Code: 0, 124 154 }, nil 125 155 }, 126 156 }, ··· 129 159 handler: func(match urlpath.Match) (*abcitypes.ResponseQuery, error) { 130 160 did := match.Params["did"] 131 161 data, err := d.plc.Data(ctx, readTx, did) 132 - if err != nil { 133 - switch { 134 - case errors.Is(err, plc.ErrDIDNotFound): 135 - return &abcitypes.ResponseQuery{ 136 - Key: []byte(did), 137 - Code: http.StatusNotFound, 138 - Info: "DID not registered: " + did, 139 - }, nil 140 - case errors.Is(err, plc.ErrDIDGone): 141 - return &abcitypes.ResponseQuery{ 142 - Key: []byte(did), 143 - Code: http.StatusGone, 144 - Info: "DID not available: " + did, 145 - }, nil 146 - default: 147 - return nil, stacktrace.Propagate(err) 148 - } 162 + if resp := handlePLCError(err, did); resp != nil { 163 + return resp, nil 164 + } else if err != nil { 165 + return nil, stacktrace.Propagate(err) 166 + } 167 + 168 + resp := struct { 169 + DID string `json:"did"` 170 + RotationKeys []string `json:"rotationKeys"` 171 + VerificationMethods map[string]string `json:"verificationMethods"` 172 + AlsoKnownAs []string `json:"alsoKnownAs"` 173 + Services map[string]didplc.OpService `json:"services"` 174 + }{ 175 + DID: did, 176 + RotationKeys: data.RotationKeys, 177 + VerificationMethods: data.VerificationMethods, 178 + AlsoKnownAs: data.AlsoKnownAs, 179 + Services: data.Services, 149 180 } 150 181 151 - dataJSON, err := json.Marshal(&data) 182 + respJSON, err := json.Marshal(&resp) 152 183 if err != nil { 153 184 return nil, stacktrace.Propagate(err) 154 185 } 155 186 156 187 return &abcitypes.ResponseQuery{ 157 188 Key: []byte(did), 158 - Value: []byte(dataJSON), 189 + Value: []byte(respJSON), 159 190 Code: 0, 160 191 }, nil 161 192 }, ··· 170 201 171 202 return &abcitypes.ResponseQuery{ 172 203 Code: 6000, 173 - Info: "Invalid path", 204 + Log: "Invalid path", 174 205 }, nil 175 206 176 207 } 208 + 209 + func handlePLCError(err error, did string) *abcitypes.ResponseQuery { 210 + if err == nil { 211 + return nil 212 + } 213 + switch { 214 + case errors.Is(err, plc.ErrDIDNotFound): 215 + return &abcitypes.ResponseQuery{ 216 + Key: []byte(did), 217 + Code: http.StatusNotFound, 218 + Log: "DID not registered: " + did, 219 + } 220 + case errors.Is(err, plc.ErrDIDGone): 221 + return &abcitypes.ResponseQuery{ 222 + Key: []byte(did), 223 + Code: http.StatusGone, 224 + Log: "DID not available: " + did, 225 + } 226 + default: 227 + return nil 228 + } 229 + }
+1 -1
abciapp/mempool.go
··· 18 18 // this type of transaction is meant to be included only by validator nodes 19 19 return &abcitypes.ResponseCheckTx{ 20 20 Code: 4002, 21 - Info: "AuthoritativeImport transactions can only be introduced by validator nodes", 21 + Log: "AuthoritativeImport transactions can only be introduced by validator nodes", 22 22 }, nil 23 23 } 24 24
+5 -5
abciapp/tx.go
··· 197 197 if !IsTransactionSanitized(txBytes) { 198 198 return &processResult{ 199 199 Code: 4000, 200 - Info: "Transaction bytes do not follow canonical serialization format", 200 + Log: "Transaction bytes do not follow canonical serialization format", 201 201 }, "", nil, nil 202 202 } 203 203 var v map[string]interface{} ··· 205 205 if err != nil { 206 206 return &processResult{ 207 207 Code: 4001, 208 - Info: "Invalid transaction", 208 + Log: "Invalid transaction", 209 209 }, "", nil, nil 210 210 } 211 211 actionInterface, ok := v["action"] 212 212 if !ok { 213 213 return &processResult{ 214 214 Code: 4001, 215 - Info: "Unknown transaction action", 215 + Log: "Unknown transaction action", 216 216 }, "", nil, nil 217 217 } 218 218 actionString, ok := actionInterface.(string) 219 219 if !ok { 220 220 return &processResult{ 221 221 Code: 4001, 222 - Info: "Unknown transaction action", 222 + Log: "Unknown transaction action", 223 223 }, "", nil, nil 224 224 } 225 225 ··· 229 229 if !ok { 230 230 return &processResult{ 231 231 Code: 4001, 232 - Info: "Unknown transaction action", 232 + Log: "Unknown transaction action", 233 233 }, "", nil, nil 234 234 } 235 235
+23 -23
abciapp/tx_challenge.go
··· 61 61 if err != nil { 62 62 return &processResult{ 63 63 Code: 4000, 64 - Info: err.Error(), 64 + Log: err.Error(), 65 65 }, nil 66 66 } 67 67 ··· 73 73 if !verified { 74 74 return &processResult{ 75 75 Code: 4200, 76 - Info: "invalid signature", 76 + Log: "invalid signature", 77 77 }, nil 78 78 } 79 79 ··· 82 82 if tx.Arguments.ToHeight < tx.Arguments.FromHeight || tx.Arguments.ToHeight >= deps.workingHeight { 83 83 return &processResult{ 84 84 Code: 4201, 85 - Info: "invalid challenge range", 85 + Log: "invalid challenge range", 86 86 }, nil 87 87 } 88 88 ··· 90 90 if rangeSize < CommitToChallengeMinRange { 91 91 return &processResult{ 92 92 Code: 4202, 93 - Info: "insufficient challenge range", 93 + Log: "insufficient challenge range", 94 94 }, nil 95 95 } 96 96 if rangeSize > CommitToChallengeMaxRange { 97 97 return &processResult{ 98 98 Code: 4203, 99 - Info: "excessive challenge range", 99 + Log: "excessive challenge range", 100 100 }, nil 101 101 } 102 102 103 103 if tx.Arguments.ToHeight+CommitToChallengeMaxAgeInBlocks < deps.workingHeight { 104 104 return &processResult{ 105 105 Code: 4204, 106 - Info: "outdated challenge range", 106 + Log: "outdated challenge range", 107 107 }, nil 108 108 } 109 109 ··· 111 111 if toHeightBlockMeta == nil { 112 112 return &processResult{ 113 113 Code: 4205, 114 - Info: "unknown block in challenge range", 114 + Log: "unknown block in challenge range", 115 115 }, nil 116 116 } 117 117 118 118 if deps.readTx.Timestamp().Sub(toHeightBlockMeta.Header.Time) > CommitToChallengeMaxAge { 119 119 return &processResult{ 120 120 Code: 4206, 121 - Info: "outdated challenge range", 121 + Log: "outdated challenge range", 122 122 }, nil 123 123 } 124 124 ··· 127 127 if tx.Arguments.FromHeight <= int64(currentCompletion) { 128 128 return &processResult{ 129 129 Code: 4207, 130 - Info: "challenge range overlaps already completed challenge", 130 + Log: "challenge range overlaps already completed challenge", 131 131 }, nil 132 132 } 133 133 } else if !errors.Is(err, store.ErrNoRecentChallengeCompletion) { ··· 137 137 if len(tx.Arguments.Root) != 32 { 138 138 return &processResult{ 139 139 Code: 4208, 140 - Info: "invalid root", 140 + Log: "invalid root", 141 141 }, nil 142 142 } 143 143 ··· 146 146 if err != nil || proof.GetExist() == nil { 147 147 return &processResult{ 148 148 Code: 4209, 149 - Info: "invalid proof", 149 + Log: "invalid proof", 150 150 }, nil 151 151 } 152 152 existenceProof := proof.GetExist() ··· 163 163 if proofHeight != expectedProofHeight { 164 164 return &processResult{ 165 165 Code: 4210, 166 - Info: "invalid proof", 166 + Log: "invalid proof", 167 167 }, nil 168 168 } 169 169 ··· 174 174 if !blockProofValid { 175 175 return &processResult{ 176 176 Code: 4211, 177 - Info: "invalid proof", 177 + Log: "invalid proof", 178 178 }, nil 179 179 } 180 180 181 181 if !ics23.VerifyMembership(ics23.IavlSpec, tx.Arguments.Root, proof, existenceProof.Key, existenceProof.Value) { 182 182 return &processResult{ 183 183 Code: 4212, 184 - Info: "invalid proof", 184 + Log: "invalid proof", 185 185 }, nil 186 186 } 187 187 ··· 232 232 if err != nil { 233 233 return &processResult{ 234 234 Code: 4000, 235 - Info: err.Error(), 235 + Log: err.Error(), 236 236 }, nil 237 237 } 238 238 ··· 241 241 if errors.Is(err, store.ErrNoActiveChallengeCommitment) { 242 242 return &processResult{ 243 243 Code: 4300, 244 - Info: "validator is not committed to a challenge", 244 + Log: "validator is not committed to a challenge", 245 245 }, nil 246 246 } 247 247 return nil, stacktrace.Propagate(err) ··· 251 251 // validator must commit to a new challenge 252 252 return &processResult{ 253 253 Code: 4301, 254 - Info: "outdated challenge commitment", 254 + Log: "outdated challenge commitment", 255 255 }, nil 256 256 } 257 257 ··· 260 260 // this shouldn't happen unless the prover is submitting the completion on the same block as the commitment 261 261 return &processResult{ 262 262 Code: 4302, 263 - Info: "premature challenge completion", 263 + Log: "premature challenge completion", 264 264 }, nil 265 265 } 266 266 ··· 268 268 // validator must commit to a new challenge 269 269 return &processResult{ 270 270 Code: 4303, 271 - Info: "outdated challenge commitment", 271 + Log: "outdated challenge commitment", 272 272 }, nil 273 273 } 274 274 ··· 277 277 if err != nil || proof.GetExist() == nil { 278 278 return &processResult{ 279 279 Code: 4304, 280 - Info: "invalid proof", 280 + Log: "invalid proof", 281 281 }, nil 282 282 } 283 283 existenceProof := proof.GetExist() ··· 294 294 if proofHeight != expectedProofHeight { 295 295 return &processResult{ 296 296 Code: 4305, 297 - Info: "incorrect key proven", 297 + Log: "incorrect key proven", 298 298 }, nil 299 299 } 300 300 ··· 305 305 if !blockProofValid { 306 306 return &processResult{ 307 307 Code: 4306, 308 - Info: "invalid proof", 308 + Log: "invalid proof", 309 309 }, nil 310 310 } 311 311 312 312 if !ics23.VerifyMembership(ics23.IavlSpec, committedTreeRoot, proof, existenceProof.Key, existenceProof.Value) { 313 313 return &processResult{ 314 314 Code: 4307, 315 - Info: "invalid proof", 315 + Log: "invalid proof", 316 316 }, nil 317 317 } 318 318
+2 -2
abciapp/tx_create_plc_op.go
··· 31 31 if err != nil { 32 32 return &processResult{ 33 33 Code: 4000, 34 - Info: err.Error(), 34 + Log: err.Error(), 35 35 }, nil 36 36 } 37 37 ··· 61 61 if code, ok := plc.InvalidOperationErrorCode(err); ok { 62 62 return &processResult{ 63 63 Code: code, 64 - Info: err.Error(), 64 + Log: err.Error(), 65 65 }, nil 66 66 } 67 67 return nil, stacktrace.Propagate(err, "internal error")
+9 -9
abciapp/tx_import.go
··· 31 31 if err != nil { 32 32 return &processResult{ 33 33 Code: 4000, 34 - Info: err.Error(), 34 + Log: err.Error(), 35 35 }, nil 36 36 } 37 37 ··· 48 48 if err != nil || parsed.Scheme != "https" { 49 49 return &processResult{ 50 50 Code: 4100, 51 - Info: "Malformed Authoritative PLC URL", 51 + Log: "Malformed Authoritative PLC URL", 52 52 }, nil 53 53 } 54 54 } ··· 98 98 if err != nil { 99 99 return &processResult{ 100 100 Code: 4000, 101 - Info: err.Error(), 101 + Log: err.Error(), 102 102 }, nil 103 103 } 104 104 ··· 110 110 if expectedPlcUrl != tx.Arguments.PLCURL || expectedPlcUrl == "" { 111 111 return &processResult{ 112 112 Code: 4110, 113 - Info: "Unexpected Authoritative PLC URL", 113 + Log: "Unexpected Authoritative PLC URL", 114 114 }, nil 115 115 } 116 116 ··· 124 124 if expectedCursor != tx.Arguments.Cursor { 125 125 return &processResult{ 126 126 Code: 4111, 127 - Info: "Unexpected import cursor", 127 + Log: "Unexpected import cursor", 128 128 }, nil 129 129 } 130 130 131 131 if tx.Arguments.Count > MaxOpsPerImportTx || tx.Arguments.Count == 0 { 132 132 return &processResult{ 133 133 Code: 4112, 134 - Info: "Unexpected import count", 134 + Log: "Unexpected import count", 135 135 }, nil 136 136 } 137 137 ··· 139 139 if err != nil { 140 140 return &processResult{ 141 141 Code: 4113, 142 - Info: "Failure to obtain authoritative operations", 142 + Log: "Failure to obtain authoritative operations", 143 143 }, nil 144 144 } 145 145 146 146 if uint64(len(operations)) < tx.Arguments.Count { 147 147 return &processResult{ 148 148 Code: 4114, 149 - Info: "Unexpected import count", 149 + Log: "Unexpected import count", 150 150 }, nil 151 151 } 152 152 ··· 158 158 if hex.EncodeToString(expectedHashBytes) != tx.Arguments.Hash { 159 159 return &processResult{ 160 160 Code: 4115, 161 - Info: "Unexpected import hash", 161 + Log: "Unexpected import hash", 162 162 }, nil 163 163 } 164 164