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  }