package mempool import ( "github.com/p9c/p9/pkg/blockchain" "github.com/p9c/p9/pkg/wire" ) // RuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a // rule violation and use the Err field to access the underlying error, which will be either a TxRuleError or a // blockchain. RuleError. type RuleError struct { Err error } // Error satisfies the error interface and prints human-readable errors. func (e RuleError) Error() string { if e.Err == nil { return "" } return e.Err.Error() } // TxRuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a // rule violation and access the ErrorCode field to ascertain the specific reason for the rule violation. type TxRuleError struct { RejectCode wire.RejectCode // The code to send with reject messages Description string // Human readable description of the issue } // Error satisfies the error interface and prints human-readable errors. func (e TxRuleError) Error() string { return e.Description } // txRuleError creates an underlying TxRuleError with the given a set of arguments and returns a RuleError that // encapsulates it. func txRuleError(c wire.RejectCode, desc string) RuleError { return RuleError{ Err: TxRuleError{RejectCode: c, Description: desc}, } } // chainRuleError returns a RuleError that encapsulates the given blockchain. RuleError. func chainRuleError(chainErr blockchain.RuleError) RuleError { return RuleError{ Err: chainErr, } } // extractRejectCode attempts to return a relevant reject code for a given error by examining the error for known types. // It will return true if a code was successfully extracted. func extractRejectCode(e error) (wire.RejectCode, bool) { // Pull the underlying error out of a RuleError. if rerr, ok := e.(RuleError); ok { e = rerr.Err } switch er := e.(type) { case blockchain.RuleError: // Convert the chain error to a reject code. var code wire.RejectCode switch er.ErrorCode { // Rejected due to duplicate. case blockchain.ErrDuplicateBlock: code = wire.RejectDuplicate // Rejected due to obsolete version. case blockchain.ErrBlockVersionTooOld: code = wire.RejectObsolete // Rejected due to checkpoint. case blockchain.ErrCheckpointTimeTooOld: fallthrough case blockchain.ErrDifficultyTooLow: fallthrough case blockchain.ErrBadCheckpoint: fallthrough case blockchain.ErrForkTooOld: code = wire.RejectCheckpoint // Everything else is due to the block or transaction being invalid. default: code = wire.RejectInvalid } return code, true case TxRuleError: return er.RejectCode, true case nil: return wire.RejectInvalid, false } return wire.RejectInvalid, false } // ErrToRejectErr examines the underlying type of the error and returns a reject code and string appropriate to be sent // in a wire.MsgReject message. func ErrToRejectErr(e error) (wire.RejectCode, string) { // Return the reject code along with the error text if it can be // extracted from the error. rejectCode, found := extractRejectCode(e) if found { return rejectCode, e.Error() } // Return a generic rejected string if there is no error. This really should not happen unless the code elsewhere is // not setting an error as it should be but it's best to be safe and simply return a generic string rather than // allowing the following code that dereferences the err to panic. if e == nil { return wire.RejectInvalid, "rejected" } // When the underlying error is not one of the above cases, just return wire.RejectInvalid with a generic rejected // string plus the error text. return wire.RejectInvalid, "rejected: " + e.Error() }