github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/errors.go (about)

     1  package engine
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/rs/zerolog"
     8  )
     9  
    10  var (
    11  	// IncompatibleInputTypeError indicates that the input has an incompatible type
    12  	IncompatibleInputTypeError = errors.New("incompatible input type")
    13  )
    14  
    15  // InvalidInputError are errors for caused by invalid inputs.
    16  // It's useful to distinguish these known errors from exceptions.
    17  // By distinguishing errors from exceptions, we can log them
    18  // differently.
    19  // For instance, log InvalidInputError error as a warn log, and log
    20  // other error as an error log.
    21  type InvalidInputError struct {
    22  	err error
    23  }
    24  
    25  func NewInvalidInputError(msg string) error {
    26  	return NewInvalidInputErrorf(msg)
    27  }
    28  
    29  func NewInvalidInputErrorf(msg string, args ...interface{}) error {
    30  	return InvalidInputError{
    31  		err: fmt.Errorf(msg, args...),
    32  	}
    33  }
    34  
    35  func (e InvalidInputError) Unwrap() error {
    36  	return e.err
    37  }
    38  
    39  func (e InvalidInputError) Error() string {
    40  	return e.err.Error()
    41  }
    42  
    43  // IsInvalidInputError returns whether the given error is an InvalidInputError error
    44  func IsInvalidInputError(err error) bool {
    45  	var errInvalidInputError InvalidInputError
    46  	return errors.As(err, &errInvalidInputError)
    47  }
    48  
    49  // IsNetworkTransmissionError returns whether the given error is a NetworkTransmissionError error
    50  func IsNetworkTransmissionError(err error) bool {
    51  	var errNetworkTransmissionError NetworkTransmissionError
    52  	return errors.As(err, &errNetworkTransmissionError)
    53  }
    54  
    55  // NetworkTransmissionError captures the general sentinel errors upon network transmission. It is used to distinguish
    56  // network transmission errors from other errors.
    57  type NetworkTransmissionError struct {
    58  	err error
    59  }
    60  
    61  func NewNetworkTransmissionErrorf(msg string, args ...interface{}) error {
    62  	return NetworkTransmissionError{
    63  		err: fmt.Errorf(msg, args...),
    64  	}
    65  }
    66  
    67  func NewNetworkTransmissionError(msg string) error {
    68  	return NetworkTransmissionError{
    69  		err: fmt.Errorf(msg),
    70  	}
    71  }
    72  
    73  func (e NetworkTransmissionError) Unwrap() error {
    74  	return e.err
    75  }
    76  
    77  func (e NetworkTransmissionError) Error() string {
    78  	return e.err.Error()
    79  }
    80  
    81  // OutdatedInputError are for inputs that are outdated. An outdated input doesn't mean
    82  // whether the input was invalid or not, knowing that would take more computation that
    83  // isn't necessary.
    84  // An outdated input could also for a duplicated input: the duplication is outdated.
    85  type OutdatedInputError struct {
    86  	err error
    87  }
    88  
    89  func NewOutdatedInputErrorf(msg string, args ...interface{}) error {
    90  	return OutdatedInputError{
    91  		err: fmt.Errorf(msg, args...),
    92  	}
    93  }
    94  
    95  func (e OutdatedInputError) Unwrap() error {
    96  	return e.err
    97  }
    98  
    99  func (e OutdatedInputError) Error() string {
   100  	return e.err.Error()
   101  }
   102  
   103  func IsOutdatedInputError(err error) bool {
   104  	var errOutdatedInputError OutdatedInputError
   105  	return errors.As(err, &errOutdatedInputError)
   106  }
   107  
   108  // UnverifiableInputError are for inputs that cannot be verified at this moment.
   109  // Usually it means that we don't have enough data to verify it. A good example is missing
   110  // data in DB to process input.
   111  type UnverifiableInputError struct {
   112  	err error
   113  }
   114  
   115  func NewUnverifiableInputError(msg string, args ...interface{}) error {
   116  	return UnverifiableInputError{
   117  		err: fmt.Errorf(msg, args...),
   118  	}
   119  }
   120  
   121  func (e UnverifiableInputError) Unwrap() error {
   122  	return e.err
   123  }
   124  
   125  func (e UnverifiableInputError) Error() string {
   126  	return e.err.Error()
   127  }
   128  
   129  func IsUnverifiableInputError(err error) bool {
   130  	var errUnverifiableInputError UnverifiableInputError
   131  	return errors.As(err, &errUnverifiableInputError)
   132  }
   133  
   134  type DuplicatedEntryError struct {
   135  	err error
   136  }
   137  
   138  func NewDuplicatedEntryErrorf(msg string, args ...interface{}) error {
   139  	return DuplicatedEntryError{
   140  		err: fmt.Errorf(msg, args...),
   141  	}
   142  }
   143  
   144  func (e DuplicatedEntryError) Unwrap() error {
   145  	return e.err
   146  }
   147  
   148  func (e DuplicatedEntryError) Error() string {
   149  	return e.err.Error()
   150  }
   151  
   152  func IsDuplicatedEntryError(err error) bool {
   153  	var errDuplicatedEntryError DuplicatedEntryError
   154  	return errors.As(err, &errDuplicatedEntryError)
   155  }
   156  
   157  // LogError logs the engine processing error
   158  func LogError(log zerolog.Logger, err error) {
   159  	LogErrorWithMsg(log, "could not process message", err)
   160  }
   161  
   162  func LogErrorWithMsg(log zerolog.Logger, msg string, err error) {
   163  	if err == nil {
   164  		return
   165  	}
   166  
   167  	// Invalid input errors could be logged as warning, because they can be
   168  	// part of normal operations when the network is open and anyone can send
   169  	// weird messages around. However, during the non-BFT phase where we
   170  	// control the majority of the network, we should not be seeing any of
   171  	// them. Logging them as error will help us to identify and address
   172  	// issues with our application logic before going full BFT.
   173  	if IsInvalidInputError(err) {
   174  		log.Error().Str("error_type", "invalid_input").Err(err).Msg(msg)
   175  		return
   176  	}
   177  
   178  	// Outdated input errors, on the other hand, can happen regularly, even
   179  	// before opening the network up, as some messages might just be late
   180  	// due to network delays or other infrastructure issues. They should
   181  	// thus be logged as warnings.
   182  	if IsOutdatedInputError(err) {
   183  		log.Warn().Str("error_type", "outdated_input").Err(err).Msg(msg)
   184  		return
   185  	}
   186  
   187  	// Unverifiable input errors may be due to out-of-date node state, or could
   188  	// indicate a malicious/unexpected message from another node. Since we don't
   189  	// know, log as warning.
   190  	if IsUnverifiableInputError(err) {
   191  		log.Warn().Str("error_type", "unverifiable_input").Err(err).Msg(msg)
   192  		return
   193  	}
   194  
   195  	// all other errors should just be logged as usual
   196  	log.Error().Str("error_type", "internal_error").Err(err).Msg(msg)
   197  }
   198  
   199  func IsIncompatibleInputTypeError(err error) bool {
   200  	return errors.Is(err, IncompatibleInputTypeError)
   201  }