-
Notifications
You must be signed in to change notification settings - Fork 208
/
Copy patherrors.go
132 lines (114 loc) · 4.42 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package api
import (
"fmt"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// The canonical errors from the EigenDA gRPC API endpoints.
//
// Notes:
// - We start with a small (but sufficient) subset of grpc's error codes,
// and expand when there is an important failure case to separate out. See:
// https://grpc.io/docs/guides/status-codes/
// - Make sure that internally propagated errors are eventually wrapped in one of the
// user-facing errors defined here, since grpc otherwise returns an UNKNOWN error code,
// which is harder to debug and understand for users.
// - See https://github.com/googleapis/googleapis/blob/ba8ea80f25d19bde8501cd51f314391f8d39bde8/google/rpc/code.proto
// for the mapping of grpc error codes to HTTP status codes.
func newErrorGRPC(code codes.Code, msg string) error {
return status.Error(code, msg)
}
// HTTP Mapping: 400 Bad Request
func NewErrorInvalidArg(msg string) error {
return newErrorGRPC(codes.InvalidArgument, msg)
}
// HTTP Mapping: 404 Not Found
func NewErrorNotFound(msg string) error {
return newErrorGRPC(codes.NotFound, msg)
}
// HTTP Mapping: 429 Too Many Requests
func NewErrorResourceExhausted(msg string) error {
return newErrorGRPC(codes.ResourceExhausted, msg)
}
// HTTP Mapping: 500 Internal Server Error
func NewErrorInternal(msg string) error {
return newErrorGRPC(codes.Internal, msg)
}
// HTTP Mapping: 500 Internal Server Error
func NewErrorUnknown(msg string) error {
return newErrorGRPC(codes.Unknown, msg)
}
// HTTP Mapping: 501 Not Implemented
func NewErrorUnimplemented() error {
return newErrorGRPC(codes.Unimplemented, "not implemented")
}
// HTTP Mapping: 504 Gateway Timeout
func NewErrorDeadlineExceeded(msg string) error {
return newErrorGRPC(codes.DeadlineExceeded, msg)
}
func NewErrorCanceled(msg string) error {
return newErrorGRPC(codes.Canceled, msg)
}
func NewErrorAlreadyExists(msg string) error {
return newErrorGRPC(codes.AlreadyExists, msg)
}
// ErrorFailover is returned by the disperser-client and eigenda-client to signify
// that eigenda is temporarily unavailable, and suggest to the caller
// (most likely some rollup batcher via the eigenda-proxy) to failover
// to ethda for some amount of time.
// See https://github.com/ethereum-optimism/specs/issues/434 for more details.
//
// Given that both clients already return grpc errors, we could potentially use
// a grpc UNAVAILABLE error instead, but we don't because:
// 1. UNAVAILABLE is typically used to tell the client to retry the request, not failover
// 2. the grpc framework itself also returns UNAVAILABLE errors in some cases, see:
// https://github.com/grpc/grpc-go/blob/192ee33f6fc0f07070eeaaa1d34e41746740e64c/codes/codes.go#L184.
// We could differentiate from those generated by the grpc framework by using error details, like
// https://github.com/grpc/grpc-go/tree/master/examples/features/error_details, but that would complicate things
// and it feels much simpler to just use a custom error type for this specific purpose.
//
// 3 reasons for returning api.ErrorFailover:
// 1. Failed to put the blob in the disperser's queue (disperser is down)
// 2. Timed out before getting confirmed onchain (batcher is down)
// 3. Insufficient signatures (eigenda network is down)
//
// One can check if an error is an ErrorFailover by using errors.Is:
//
// failoverErr := NewErrorFailover(someOtherErr)
// if !errors.Is(wrappedFailoverErr, &ErrorFailover{}) {
// // do something...
// }
type ErrorFailover struct {
Err error
}
// NewErrorFailover creates a new ErrorFailover with the given underlying error.
// See ErrorFailover for more details.
func NewErrorFailover(err error) *ErrorFailover {
return &ErrorFailover{
Err: err,
}
}
func (e *ErrorFailover) Error() string {
if e.Err == nil {
return "Failover"
}
return fmt.Sprintf("Failover: %s", e.Err.Error())
}
func (e *ErrorFailover) Unwrap() error {
return e.Err
}
// Is only checks the type of the error, not the underlying error.
// This is because we want to be able to check that an error is an ErrorFailover,
// even when wrapped. This can now be done with errors.Is.
//
// baseErr := fmt.Errorf("some error")
// failoverErr := NewErrorFailover(baseErr)
// wrappedFailoverErr := fmt.Errorf("some extra context: %w", failoverErr)
//
// if !errors.Is(wrappedFailoverErr, &ErrorFailover{}) {
// // do something...
// }
func (e *ErrorFailover) Is(target error) bool {
_, ok := target.(*ErrorFailover)
return ok
}