github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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 error 14 Unwrap() error 15 } 16 17 type UnwrappableErrors interface { 18 error 19 Unwrap() []error 20 } 21 22 type CodedError interface { 23 Code() ErrorCode 24 25 Unwrappable 26 error 27 } 28 29 type CodedFailure interface { 30 Code() FailureCode 31 32 Unwrappable 33 error 34 } 35 36 // Is is a utility function to call std error lib `Is` function for instance equality checks. 37 func Is(err error, target error) bool { 38 return stdErrors.Is(err, target) 39 } 40 41 // As is a utility function to call std error lib `As` function. 42 // As finds the first error in err's chain that matches target, 43 // and if so, sets target to that error value and returns true. Otherwise, it returns false. 44 // The chain consists of err itself followed by the sequence of errors obtained by repeatedly calling Unwrap. 45 func As(err error, target interface{}) bool { 46 return stdErrors.As(err, target) 47 } 48 49 // findRootCodedError recursively unwraps the error to search for the root (deepest) coded error: 50 // 1. If err is nil, this returns (nil, false), 51 // 2. If err has no error code, this returns (nil, true), 52 // 3. If err has an error code, this returns 53 // (<the deepest, aka root cause, coded error>, false) 54 // 55 // Note: This assumes the caller has already checked if the error contains a CodedFailure. 56 func findRootCodedError(err error) (CodedError, bool) { 57 if err == nil { 58 return nil, false 59 } 60 61 var coded CodedError 62 if !As(err, &coded) { 63 return nil, true 64 } 65 66 for { 67 var nextCoded CodedError 68 if !As(coded.Unwrap(), &nextCoded) { 69 return coded, false 70 } 71 72 coded = nextCoded 73 } 74 } 75 76 // IsFailure returns true if the error is un-coded, or if the error contains 77 // a failure code. 78 func IsFailure(err error) bool { 79 return AsFailure(err) != nil 80 } 81 82 func AsFailure(err error) CodedFailure { 83 if err == nil { 84 return nil 85 } 86 87 var failure CodedFailure 88 if As(err, &failure) { 89 return failure 90 } 91 92 var coded CodedError 93 if !As(err, &coded) { 94 return NewUnknownFailure(err) 95 } 96 97 return nil 98 } 99 100 // SplitErrorTypes splits the error into fatal (failures) and non-fatal errors 101 func SplitErrorTypes(inp error) (err CodedError, failure CodedFailure) { 102 if inp == nil { 103 return nil, nil 104 } 105 106 if failure = AsFailure(inp); failure != nil { 107 return nil, WrapCodedFailure( 108 failure.Code(), 109 inp, 110 "failure caused by") 111 } 112 113 coded, isUnknown := findRootCodedError(inp) 114 if isUnknown { 115 return nil, NewUnknownFailure(inp) 116 } 117 118 return WrapCodedError( 119 coded.Code(), 120 inp, 121 "error caused by"), nil 122 } 123 124 // HandleRuntimeError handles runtime errors and separates 125 // errors generated by runtime from fvm errors (e.g. environment errors) 126 func HandleRuntimeError(err error) error { 127 if err == nil { 128 return nil 129 } 130 131 // if is not a runtime error return as vm error 132 // this should never happen unless a bug in the code 133 runErr, ok := err.(runtime.Error) 134 if !ok { 135 return NewUnknownFailure(err) 136 } 137 138 // All other errors are non-fatal Cadence errors. 139 return NewCadenceRuntimeError(runErr) 140 } 141 142 // HasErrorCode returns true if the error or one of its nested errors matches the 143 // specified error code. 144 func HasErrorCode(err error, code ErrorCode) bool { 145 return Find(err, code) != nil 146 } 147 148 // HasFailureCode returns true if the error or one of its nested errors matches the 149 // specified failure code. 150 func HasFailureCode(err error, code FailureCode) bool { 151 return FindFailure(err, code) != nil 152 } 153 154 // Find recursively unwraps the error and returns the first CodedError that matches 155 // the specified error code. 156 func Find(originalErr error, code ErrorCode) CodedError { 157 if originalErr == nil { 158 return nil 159 } 160 161 // Handle non-chained errors 162 var unwrappedErrs []error 163 switch err := originalErr.(type) { 164 case *multierror.Error: 165 unwrappedErrs = err.WrappedErrors() 166 case UnwrappableErrors: 167 unwrappedErrs = err.Unwrap() 168 169 // IMPORTANT: this check needs to run after *multierror.Error because multierror does implement 170 // the Unwrappable interface, however its implementation only visits the base errors in the list, 171 // and ignores their descendants. 172 case Unwrappable: 173 coded, ok := err.(CodedError) 174 if ok && coded.Code() == code { 175 return coded 176 } 177 return Find(err.Unwrap(), code) 178 default: 179 return nil 180 } 181 182 for _, innerErr := range unwrappedErrs { 183 coded := Find(innerErr, code) 184 if coded != nil { 185 return coded 186 } 187 } 188 189 return nil 190 } 191 192 // FindFailure recursively unwraps the error and returns the first CodedFailure that matches 193 // the specified error code. 194 func FindFailure(originalErr error, code FailureCode) CodedFailure { 195 if originalErr == nil { 196 return nil 197 } 198 199 // Handle non-chained errors 200 var unwrappedErrs []error 201 switch err := originalErr.(type) { 202 case *multierror.Error: 203 unwrappedErrs = err.WrappedErrors() 204 case UnwrappableErrors: 205 unwrappedErrs = err.Unwrap() 206 207 // IMPORTANT: this check needs to run after *multierror.Error because multierror does implement 208 // the Unwrappable interface, however its implementation only visits the base errors in the list, 209 // and ignores their descendants. 210 case Unwrappable: 211 coded, ok := err.(CodedFailure) 212 if ok && coded.Code() == code { 213 return coded 214 } 215 return FindFailure(err.Unwrap(), code) 216 default: 217 return nil 218 } 219 220 for _, innerErr := range unwrappedErrs { 221 coded := FindFailure(innerErr, code) 222 if coded != nil { 223 return coded 224 } 225 } 226 227 return nil 228 } 229 230 var _ CodedError = (*codedError)(nil) 231 232 type codedError struct { 233 code ErrorCode 234 235 err error 236 } 237 238 func newError( 239 code ErrorCode, 240 rootCause error, 241 ) codedError { 242 return codedError{ 243 code: code, 244 err: rootCause, 245 } 246 } 247 248 func WrapCodedError( 249 code ErrorCode, 250 err error, 251 prefixMsgFormat string, 252 formatArguments ...interface{}, 253 ) codedError { 254 if prefixMsgFormat != "" { 255 msg := fmt.Sprintf(prefixMsgFormat, formatArguments...) 256 err = fmt.Errorf("%s: %w", msg, err) 257 } 258 return newError(code, err) 259 } 260 261 func NewCodedError( 262 code ErrorCode, 263 format string, 264 formatArguments ...interface{}, 265 ) codedError { 266 return newError(code, fmt.Errorf(format, formatArguments...)) 267 } 268 269 func (err codedError) Unwrap() error { 270 return err.err 271 } 272 273 func (err codedError) Error() string { 274 return fmt.Sprintf("%v %v", err.code, err.err) 275 } 276 277 func (err codedError) Code() ErrorCode { 278 return err.code 279 } 280 281 var _ CodedFailure = (*codedFailure)(nil) 282 283 type codedFailure struct { 284 code FailureCode 285 err error 286 } 287 288 func newFailure( 289 code FailureCode, 290 rootCause error, 291 ) codedFailure { 292 return codedFailure{ 293 code: code, 294 err: rootCause, 295 } 296 } 297 298 func WrapCodedFailure( 299 code FailureCode, 300 err error, 301 prefixMsgFormat string, 302 formatArguments ...interface{}, 303 ) codedFailure { 304 if prefixMsgFormat != "" { 305 msg := fmt.Sprintf(prefixMsgFormat, formatArguments...) 306 err = fmt.Errorf("%s: %w", msg, err) 307 } 308 return newFailure(code, err) 309 } 310 311 func NewCodedFailure( 312 code FailureCode, 313 format string, 314 formatArguments ...interface{}, 315 ) codedFailure { 316 return newFailure(code, fmt.Errorf(format, formatArguments...)) 317 } 318 319 func (err codedFailure) Unwrap() error { 320 return err.err 321 } 322 323 func (err codedFailure) Error() string { 324 return fmt.Sprintf("%v %v", err.code, err.err) 325 } 326 327 func (err codedFailure) Code() FailureCode { 328 return err.code 329 } 330 331 // NewEventEncodingError construct a new CodedError which indicates 332 // that encoding event has failed 333 func NewEventEncodingError(err error) CodedError { 334 return NewCodedError( 335 ErrCodeEventEncodingError, 336 "error while encoding emitted event: %w ", err) 337 } 338 339 // EVMError needs to satisfy the user error interface 340 // in order for Cadence to correctly handle the error 341 var _ errors.UserError = &(EVMError{}) 342 343 // EVMError captures any non-fatal EVM error 344 type EVMError struct { 345 CodedError 346 } 347 348 func (e EVMError) IsUserError() {} 349 350 // NewEVMError constructs a new CodedError which captures a 351 // collection of errors provided by (non-fatal) evm runtime. 352 func NewEVMError(err error) EVMError { 353 return EVMError{ 354 WrapCodedError( 355 ErrEVMExecutionError, 356 err, 357 "evm runtime error"), 358 } 359 } 360 361 // IsEVMError returns true if error is an EVM error 362 func IsEVMError(err error) bool { 363 return HasErrorCode(err, ErrEVMExecutionError) 364 }