-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement custom error types for DA (#115)
* feat: implement custom error types for DA Introduced multiple custom error types for the DA package. Because of JSON-RPC library used, each error has to be it's own type (can't use sentinel values). gRPC on the other hand doesn't support wrapping typed errors, and codes are "standardized" (currently only "Not Found" used). * feat: add detailed gRPC error handling for DA errors Implemented detailed gRPC error handling by defining custom error types and appropriate error codes for better differentiation in the DA package. Each error type now provides a gRPC status with granular error details using the newly introduced error codes. * refactor: add proper support for future height error Introduced a new error code and corresponding error type for when a requested height is from the future. Updated functions to handle this new type and included its gRPC status representation. * feat: add error mapping to all methods in gRPC proxy client Previously, only `Get` method supported proper error mapping. * refactor: extract error mapping into a reusable function Centralize the error code to error type mapping by creating a reusable `getKnownErrorsMapping` function. This change reduces code redundancy and improves maintainability. * refactor: reformat ErrorCode enum entries Unified the indentation style for all enum entries in ErrorCode. This improves code readability and consistency within the proto file. * chore: fix protobuf enum names (linting) * chore: regenerate protobuf with latest version of protoc-gen-gocosmos * docs: fix typos in comments * refactor: group methods by type in errors.go
- Loading branch information
Showing
10 changed files
with
497 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package da | ||
|
||
import ( | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
|
||
pbda "github.com/rollkit/go-da/types/pb/da" | ||
) | ||
|
||
// Code defines error codes for JSON-RPC. | ||
// | ||
// They are reused for gRPC | ||
type Code int | ||
|
||
// gRPC checks for GRPCStatus method on errors to enable advanced error handling. | ||
|
||
// Codes are used by JSON-RPC client and server | ||
const ( | ||
CodeBlobNotFound Code = 32001 | ||
CodeBlobSizeOverLimit Code = 32002 | ||
CodeTxTimedOut Code = 32003 | ||
CodeTxAlreadyInMempool Code = 32004 | ||
CodeTxIncorrectAccountSequence Code = 32005 | ||
CodeTxTooLarge Code = 32006 | ||
CodeContextDeadline Code = 32007 | ||
CodeFutureHeight Code = 32008 | ||
) | ||
|
||
// ErrBlobNotFound is used to indicate that the blob was not found. | ||
type ErrBlobNotFound struct{} | ||
|
||
func (e *ErrBlobNotFound) Error() string { | ||
return "blob: not found" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrBlobNotFound error. | ||
func (e *ErrBlobNotFound) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.NotFound, pbda.ErrorCode_ERROR_CODE_BLOB_NOT_FOUND) | ||
} | ||
|
||
// ErrBlobSizeOverLimit is used to indicate that the blob size is over limit. | ||
type ErrBlobSizeOverLimit struct{} | ||
|
||
func (e *ErrBlobSizeOverLimit) Error() string { | ||
return "blob: over size limit" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrBlobSizeOverLimit error. | ||
func (e *ErrBlobSizeOverLimit) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.ResourceExhausted, pbda.ErrorCode_ERROR_CODE_BLOB_SIZE_OVER_LIMIT) | ||
} | ||
|
||
// ErrTxTimedOut is the error message returned by the DA when mempool is congested. | ||
type ErrTxTimedOut struct{} | ||
|
||
func (e *ErrTxTimedOut) Error() string { | ||
return "timed out waiting for tx to be included in a block" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrTxTimedOut error. | ||
func (e *ErrTxTimedOut) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.DeadlineExceeded, pbda.ErrorCode_ERROR_CODE_TX_TIMED_OUT) | ||
} | ||
|
||
// ErrTxAlreadyInMempool is the error message returned by the DA when tx is already in mempool. | ||
type ErrTxAlreadyInMempool struct{} | ||
|
||
func (e *ErrTxAlreadyInMempool) Error() string { | ||
return "tx already in mempool" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrTxAlreadyInMempool error. | ||
func (e *ErrTxAlreadyInMempool) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.AlreadyExists, pbda.ErrorCode_ERROR_CODE_TX_ALREADY_IN_MEMPOOL) | ||
} | ||
|
||
// ErrTxIncorrectAccountSequence is the error message returned by the DA when tx has incorrect sequence. | ||
type ErrTxIncorrectAccountSequence struct{} | ||
|
||
func (e *ErrTxIncorrectAccountSequence) Error() string { | ||
return "incorrect account sequence" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrTxIncorrectAccountSequence error. | ||
func (e *ErrTxIncorrectAccountSequence) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.InvalidArgument, pbda.ErrorCode_ERROR_CODE_TX_INCORRECT_ACCOUNT_SEQUENCE) | ||
} | ||
|
||
// ErrTxTooLarge is the err message returned by the DA when tx size is too large. | ||
type ErrTxTooLarge struct{} | ||
|
||
func (e *ErrTxTooLarge) Error() string { | ||
return "tx too large" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrTxTooLarge error. | ||
func (e *ErrTxTooLarge) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.ResourceExhausted, pbda.ErrorCode_ERROR_CODE_TX_TOO_LARGE) | ||
} | ||
|
||
// ErrContextDeadline is the error message returned by the DA when context deadline exceeds. | ||
type ErrContextDeadline struct{} | ||
|
||
func (e *ErrContextDeadline) Error() string { | ||
return "context deadline" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrContextDeadline error. | ||
func (e *ErrContextDeadline) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.DeadlineExceeded, pbda.ErrorCode_ERROR_CODE_CONTEXT_DEADLINE) | ||
} | ||
|
||
// ErrFutureHeight is returned when requested height is from the future | ||
type ErrFutureHeight struct{} | ||
|
||
func (e *ErrFutureHeight) Error() string { | ||
return "given height is from the future" | ||
} | ||
|
||
// GRPCStatus returns the gRPC status with details for an ErrFutureHeight error. | ||
func (e *ErrFutureHeight) GRPCStatus() *status.Status { | ||
return getGRPCStatus(e, codes.OutOfRange, pbda.ErrorCode_ERROR_CODE_FUTURE_HEIGHT) | ||
} | ||
|
||
// getGRPCStatus constructs a gRPC status with error details based on the provided error, gRPC code, and DA error code. | ||
func getGRPCStatus(err error, grpcCode codes.Code, daCode pbda.ErrorCode) *status.Status { | ||
base := status.New(grpcCode, err.Error()) | ||
detailed, err := base.WithDetails(&pbda.ErrorDetails{Code: daCode}) | ||
if err != nil { | ||
return base | ||
} | ||
return detailed | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package grpc | ||
|
||
import ( | ||
"errors" | ||
|
||
"google.golang.org/grpc/status" | ||
|
||
"github.com/rollkit/go-da" | ||
pbda "github.com/rollkit/go-da/types/pb/da" | ||
) | ||
|
||
func tryToMapError(err error) error { | ||
if err == nil { | ||
return nil | ||
} | ||
|
||
s, ok := status.FromError(err) | ||
if ok { | ||
details := s.Proto().Details | ||
if len(details) == 1 { | ||
var errorDetail pbda.ErrorDetails | ||
unmarshalError := errorDetail.Unmarshal(details[0].Value) | ||
if unmarshalError != nil { | ||
return err | ||
} | ||
return errorForCode(errorDetail.Code) | ||
} | ||
} | ||
return err | ||
} | ||
|
||
func errorForCode(code pbda.ErrorCode) error { | ||
switch code { | ||
case pbda.ErrorCode_ERROR_CODE_BLOB_NOT_FOUND: | ||
return &da.ErrBlobNotFound{} | ||
case pbda.ErrorCode_ERROR_CODE_BLOB_SIZE_OVER_LIMIT: | ||
return &da.ErrBlobSizeOverLimit{} | ||
case pbda.ErrorCode_ERROR_CODE_TX_TIMED_OUT: | ||
return &da.ErrTxTimedOut{} | ||
case pbda.ErrorCode_ERROR_CODE_TX_ALREADY_IN_MEMPOOL: | ||
return &da.ErrTxAlreadyInMempool{} | ||
case pbda.ErrorCode_ERROR_CODE_TX_INCORRECT_ACCOUNT_SEQUENCE: | ||
return &da.ErrTxIncorrectAccountSequence{} | ||
case pbda.ErrorCode_ERROR_CODE_TX_TOO_LARGE: | ||
return &da.ErrTxTooLarge{} | ||
case pbda.ErrorCode_ERROR_CODE_CONTEXT_DEADLINE: | ||
return &da.ErrContextDeadline{} | ||
case pbda.ErrorCode_ERROR_CODE_FUTURE_HEIGHT: | ||
return &da.ErrFutureHeight{} | ||
default: | ||
return errors.New("unknown error code") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package jsonrpc | ||
|
||
import ( | ||
"github.com/filecoin-project/go-jsonrpc" | ||
|
||
"github.com/rollkit/go-da" | ||
) | ||
|
||
// getKnownErrorsMapping returns a mapping of known error codes to their corresponding error types. | ||
func getKnownErrorsMapping() jsonrpc.Errors { | ||
errs := jsonrpc.NewErrors() | ||
errs.Register(jsonrpc.ErrorCode(da.CodeBlobNotFound), new(*da.ErrBlobNotFound)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeBlobSizeOverLimit), new(*da.ErrBlobSizeOverLimit)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeTxTimedOut), new(*da.ErrTxTimedOut)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeTxAlreadyInMempool), new(*da.ErrTxAlreadyInMempool)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeTxIncorrectAccountSequence), new(*da.ErrTxIncorrectAccountSequence)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeTxTooLarge), new(*da.ErrTxTooLarge)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeContextDeadline), new(*da.ErrContextDeadline)) | ||
errs.Register(jsonrpc.ErrorCode(da.CodeFutureHeight), new(*da.ErrFutureHeight)) | ||
return errs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.