error.go raw

   1  package mempool
   2  
   3  import (
   4  	"github.com/p9c/p9/pkg/blockchain"
   5  	"github.com/p9c/p9/pkg/wire"
   6  )
   7  
   8  // RuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of
   9  // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a
  10  // rule violation and use the Err field to access the underlying error, which will be either a TxRuleError or a
  11  // blockchain. RuleError.
  12  type RuleError struct {
  13  	Err error
  14  }
  15  
  16  // Error satisfies the error interface and prints human-readable errors.
  17  func (e RuleError) Error() string {
  18  	if e.Err == nil {
  19  		return "<nil>"
  20  	}
  21  	return e.Err.Error()
  22  }
  23  
  24  // TxRuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of
  25  // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a
  26  // rule violation and access the ErrorCode field to ascertain the specific reason for the rule violation.
  27  type TxRuleError struct {
  28  	RejectCode  wire.RejectCode // The code to send with reject messages
  29  	Description string          // Human readable description of the issue
  30  }
  31  
  32  // Error satisfies the error interface and prints human-readable errors.
  33  func (e TxRuleError) Error() string {
  34  	return e.Description
  35  }
  36  
  37  // txRuleError creates an underlying TxRuleError with the given a set of arguments and returns a RuleError that
  38  // encapsulates it.
  39  func txRuleError(c wire.RejectCode, desc string) RuleError {
  40  	return RuleError{
  41  		Err: TxRuleError{RejectCode: c, Description: desc},
  42  	}
  43  }
  44  
  45  // chainRuleError returns a RuleError that encapsulates the given blockchain. RuleError.
  46  func chainRuleError(chainErr blockchain.RuleError) RuleError {
  47  	return RuleError{
  48  		Err: chainErr,
  49  	}
  50  }
  51  
  52  // extractRejectCode attempts to return a relevant reject code for a given error by examining the error for known types.
  53  // It will return true if a code was successfully extracted.
  54  func extractRejectCode(e error) (wire.RejectCode, bool) {
  55  	// Pull the underlying error out of a RuleError.
  56  	if rerr, ok := e.(RuleError); ok {
  57  		e = rerr.Err
  58  	}
  59  	switch er := e.(type) {
  60  	case blockchain.RuleError:
  61  		// Convert the chain error to a reject code.
  62  		var code wire.RejectCode
  63  		switch er.ErrorCode {
  64  		// Rejected due to duplicate.
  65  		case blockchain.ErrDuplicateBlock:
  66  			code = wire.RejectDuplicate
  67  		// Rejected due to obsolete version.
  68  		case blockchain.ErrBlockVersionTooOld:
  69  			code = wire.RejectObsolete
  70  		// Rejected due to checkpoint.
  71  		case blockchain.ErrCheckpointTimeTooOld:
  72  			fallthrough
  73  		case blockchain.ErrDifficultyTooLow:
  74  			fallthrough
  75  		case blockchain.ErrBadCheckpoint:
  76  			fallthrough
  77  		case blockchain.ErrForkTooOld:
  78  			code = wire.RejectCheckpoint
  79  		// Everything else is due to the block or transaction being invalid.
  80  		default:
  81  			code = wire.RejectInvalid
  82  		}
  83  		return code, true
  84  	case TxRuleError:
  85  		return er.RejectCode, true
  86  	case nil:
  87  		return wire.RejectInvalid, false
  88  	}
  89  	return wire.RejectInvalid, false
  90  }
  91  
  92  // ErrToRejectErr examines the underlying type of the error and returns a reject code and string appropriate to be sent
  93  // in a wire.MsgReject message.
  94  func ErrToRejectErr(e error) (wire.RejectCode, string) {
  95  	// Return the reject code along with the error text if it can be
  96  	// extracted from the error.
  97  	rejectCode, found := extractRejectCode(e)
  98  	if found {
  99  		return rejectCode, e.Error()
 100  	}
 101  	// Return a generic rejected string if there is no error. This really should not happen unless the code elsewhere is
 102  	// not setting an error as it should be but it's best to be safe and simply return a generic string rather than
 103  	// allowing the following code that dereferences the err to panic.
 104  	if e == nil {
 105  		return wire.RejectInvalid, "rejected"
 106  	}
 107  	// When the underlying error is not one of the above cases, just return wire.RejectInvalid with a generic rejected
 108  	// string plus the error text.
 109  	return wire.RejectInvalid, "rejected: " + e.Error()
 110  }
 111