github.com/koko1123/flow-go-1@v0.29.6/fvm/errors/errors.go (about) 1 package errors 2 3 import ( 4 stdErrors "errors" 5 "fmt" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/onflow/cadence/runtime" 9 "github.com/onflow/cadence/runtime/errors" 10 ) 11 12 type Unwrappable interface { 13 Unwrap() error 14 } 15 16 type CodedError interface { 17 Code() ErrorCode 18 19 Unwrappable 20 error 21 } 22 23 // Is is a utility function to call std error lib `Is` function for instance equality checks. 24 func Is(err error, target error) bool { 25 return stdErrors.Is(err, target) 26 } 27 28 // As is a utility function to call std error lib `As` function. 29 // As finds the first error in err's chain that matches target, 30 // and if so, sets target to that error value and returns true. Otherwise, it returns false. 31 // The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap. 32 func As(err error, target interface{}) bool { 33 return stdErrors.As(err, target) 34 } 35 36 // findImportantCodedError recursively unwraps the error to search for important 37 // coded error: 38 // 1. If err is nil, this returns (nil, false), 39 // 2. If err has no error code, this returns (nil, true), 40 // 3. If err has a failure error code, this returns 41 // (<the shallowest failure coded error>, false), 42 // 4. If err has a non-failure error code, this returns 43 // (<the deepest, aka root cause, non-failure coded error>, false) 44 func findImportantCodedError(err error) (CodedError, bool) { 45 if err == nil { 46 return nil, false 47 } 48 49 var coded CodedError 50 if !As(err, &coded) { 51 return nil, true 52 } 53 54 for { 55 if coded.Code().IsFailure() { 56 return coded, false 57 } 58 59 var nextCoded CodedError 60 if !As(coded.Unwrap(), &nextCoded) { 61 return coded, false 62 } 63 64 coded = nextCoded 65 } 66 } 67 68 // IsFailure returns true if the error is un-coded, or if the error contains 69 // a failure code. 70 func IsFailure(err error) bool { 71 if err == nil { 72 return false 73 } 74 75 coded, isUnknown := findImportantCodedError(err) 76 return isUnknown || coded.Code().IsFailure() 77 } 78 79 // SplitErrorTypes splits the error into fatal (failures) and non-fatal errors 80 func SplitErrorTypes(inp error) (err CodedError, failure CodedError) { 81 if inp == nil { 82 return nil, nil 83 } 84 85 coded, isUnknown := findImportantCodedError(inp) 86 if isUnknown { 87 return nil, NewUnknownFailure(inp) 88 } 89 90 if coded.Code().IsFailure() { 91 return nil, WrapCodedError( 92 coded.Code(), 93 inp, 94 "failure caused by") 95 } 96 97 return WrapCodedError( 98 coded.Code(), 99 inp, 100 "error caused by"), nil 101 } 102 103 // HandleRuntimeError handles runtime errors and separates 104 // errors generated by runtime from fvm errors (e.g. environment errors) 105 func HandleRuntimeError(err error) error { 106 if err == nil { 107 return nil 108 } 109 110 // if is not a runtime error return as vm error 111 // this should never happen unless a bug in the code 112 runErr, ok := err.(runtime.Error) 113 if !ok { 114 return NewUnknownFailure(err) 115 } 116 117 // External errors are reported by the runtime but originate from the VM. 118 // External errors may be fatal or non-fatal, so additional handling by SplitErrorTypes 119 if externalErr, ok := errors.GetExternalError(err); ok { 120 if recoveredErr, ok := externalErr.Recovered.(error); ok { 121 // If the recovered value is an error, pass it to the original 122 // error handler to distinguish between fatal and non-fatal errors. 123 return recoveredErr 124 } 125 // if not recovered return 126 return NewUnknownFailure(externalErr) 127 } 128 129 // All other errors are non-fatal Cadence errors. 130 return NewCadenceRuntimeError(runErr) 131 } 132 133 // This returns true if the error or one of its nested errors matches the 134 // specified error code. 135 func HasErrorCode(err error, code ErrorCode) bool { 136 return Find(err, code) != nil 137 } 138 139 // This recursively unwraps the error and returns first CodedError that matches 140 // the specified error code. 141 func Find(originalErr error, code ErrorCode) CodedError { 142 if originalErr == nil { 143 return nil 144 } 145 146 var unwrappable Unwrappable 147 if !As(originalErr, &unwrappable) { 148 return nil 149 } 150 151 coded, ok := unwrappable.(CodedError) 152 if ok && coded.Code() == code { 153 return coded 154 } 155 156 // NOTE: we need to special case multierror.Error since As() will only 157 // inspect the first error within multierror.Error. 158 errors, ok := unwrappable.(*multierror.Error) 159 if !ok { 160 return Find(unwrappable.Unwrap(), code) 161 } 162 163 for _, innerErr := range errors.Errors { 164 coded = Find(innerErr, code) 165 if coded != nil { 166 return coded 167 } 168 } 169 170 return nil 171 } 172 173 type codedError struct { 174 code ErrorCode 175 176 err error 177 } 178 179 func newError( 180 code ErrorCode, 181 rootCause error, 182 ) codedError { 183 return codedError{ 184 code: code, 185 err: rootCause, 186 } 187 } 188 189 func WrapCodedError( 190 code ErrorCode, 191 err error, 192 prefixMsgFormat string, 193 formatArguments ...interface{}, 194 ) codedError { 195 if prefixMsgFormat != "" { 196 msg := fmt.Sprintf(prefixMsgFormat, formatArguments...) 197 err = fmt.Errorf("%s: %w", msg, err) 198 } 199 return newError(code, err) 200 } 201 202 func NewCodedError( 203 code ErrorCode, 204 format string, 205 formatArguments ...interface{}, 206 ) codedError { 207 return newError(code, fmt.Errorf(format, formatArguments...)) 208 } 209 210 func (err codedError) Unwrap() error { 211 return err.err 212 } 213 214 func (err codedError) Error() string { 215 return fmt.Sprintf("%v %v", err.code, err.err) 216 } 217 218 func (err codedError) Code() ErrorCode { 219 return err.code 220 } 221 222 // NewEventEncodingError construct a new CodedError which indicates 223 // that encoding event has failed 224 func NewEventEncodingError(err error) CodedError { 225 return NewCodedError( 226 ErrCodeEventEncodingError, 227 "error while encoding emitted event: %w ", err) 228 }