// Copyright (c) 2013-2015 The btcsuite developers // Copyright (c) 2015-2023 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package wire import ( "fmt" ) // ErrorCode describes a kind of message error. type ErrorCode int // These constants are used to identify a specific Error. const ( // ErrNonCanonicalVarInt is returned when a variable length integer is // not canonically encoded. ErrNonCanonicalVarInt ErrorCode = iota // ErrVarStringTooLong is returned when a variable string exceeds the // maximum message size allowed. ErrVarStringTooLong // ErrVarBytesTooLong is returned when a variable-length byte slice // exceeds the maximum message size allowed. ErrVarBytesTooLong // ErrCmdTooLong is returned when a command exceeds the maximum command // size allowed. ErrCmdTooLong // ErrPayloadTooLarge is returned when a payload exceeds the maximum // payload size allowed. ErrPayloadTooLarge // ErrWrongNetwork is returned when a message intended for a different // network is received. ErrWrongNetwork // ErrMalformedCmd is returned when a malformed command is received. ErrMalformedCmd // ErrUnknownCmd is returned when an unknown command is received. ErrUnknownCmd // ErrPayloadChecksum is returned when a message with an invalid checksum // is received. ErrPayloadChecksum // ErrTooManyAddrs is returned when an address list exceeds the maximum // allowed. ErrTooManyAddrs // ErrTooManyTxs is returned when a the number of transactions exceed the // maximum allowed. ErrTooManyTxs // ErrMsgInvalidForPVer is returned when a message is invalid for // the expected protocol version. ErrMsgInvalidForPVer // ErrFilterTooLarge is returned when a committed filter exceeds // the maximum size allowed. ErrFilterTooLarge // ErrTooManyProofs is returned when the number of proof hashes // exceeds the maximum allowed. ErrTooManyProofs // ErrTooManyFilterTypes is returned when the number of filter types // exceeds the maximum allowed. ErrTooManyFilterTypes // ErrTooManyLocators is returned when the number of block locators exceed // the maximum allowed. ErrTooManyLocators // ErrTooManyVectors is returned when the number of inventory vectors // exceed the maximum allowed. ErrTooManyVectors // ErrTooManyHeaders is returned when the number of block headers exceed // the maximum allowed. ErrTooManyHeaders // ErrHeaderContainsTxs is returned when a header's transactions // count is greater than zero. ErrHeaderContainsTxs // ErrTooManyVotes is returned when the number of vote hashes exceed the // maximum allowed. ErrTooManyVotes // ErrTooManyBlocks is returned when the number of block hashes exceed the // maximum allowed. ErrTooManyBlocks // ErrMismatchedWitnessCount returned when a transaction has unequal witness // and prefix txin quantities. ErrMismatchedWitnessCount // ErrUnknownTxType is returned when a transaction type is unknown. ErrUnknownTxType // ErrReadInPrefixFromWitnessOnlyTx is returned when attempting to read a // transaction input prefix from a witness only transaction. ErrReadInPrefixFromWitnessOnlyTx // ErrInvalidMsg is returned for an invalid message structure. ErrInvalidMsg // ErrUserAgentTooLong is returned when the provided user agent exceeds // the maximum allowed. ErrUserAgentTooLong // ErrTooManyFilterHeaders is returned when the number of committed filter // headers exceed the maximum allowed. ErrTooManyFilterHeaders // ErrMalformedStrictString is returned when a string that has strict // formatting requirements does not conform to the requirements. ErrMalformedStrictString // ErrTooManyInitialStateTypes is returned when the number of initial // state types is larger than the maximum allowed by the protocol. ErrTooManyInitStateTypes // ErrInitialStateTypeTooLong is returned when an individual initial // state type is longer than allowed by the protocol. ErrInitStateTypeTooLong // ErrTooManyTSpends is returned when the number of tspend hashes // exceeds the maximum allowed. ErrTooManyTSpends ) // Map of ErrorCode values back to their constant names for pretty printing. var errorCodeStrings = map[ErrorCode]string{ ErrNonCanonicalVarInt: "ErrNonCanonicalVarInt", ErrVarStringTooLong: "ErrVarStringTooLong", ErrVarBytesTooLong: "ErrVarBytesTooLong", ErrCmdTooLong: "ErrCmdTooLong", ErrPayloadTooLarge: "ErrPayloadTooLarge", ErrWrongNetwork: "ErrWrongNetwork", ErrMalformedCmd: "ErrMalformedCmd", ErrUnknownCmd: "ErrUnknownCmd", ErrPayloadChecksum: "ErrPayloadChecksum", ErrTooManyAddrs: "ErrTooManyAddrs", ErrTooManyTxs: "ErrTooManyTxs", ErrMsgInvalidForPVer: "ErrMsgInvalidForPVer", ErrFilterTooLarge: "ErrFilterTooLarge", ErrTooManyProofs: "ErrTooManyProofs", ErrTooManyFilterTypes: "ErrTooManyFilterTypes", ErrTooManyLocators: "ErrTooManyLocators", ErrTooManyVectors: "ErrTooManyVectors", ErrTooManyHeaders: "ErrTooManyHeaders", ErrHeaderContainsTxs: "ErrHeaderContainsTxs", ErrTooManyVotes: "ErrTooManyVotes", ErrTooManyBlocks: "ErrTooManyBlocks", ErrMismatchedWitnessCount: "ErrMismatchedWitnessCount", ErrUnknownTxType: "ErrUnknownTxType", ErrReadInPrefixFromWitnessOnlyTx: "ErrReadInPrefixFromWitnessOnlyTx", ErrInvalidMsg: "ErrInvalidMsg", ErrUserAgentTooLong: "ErrUserAgentTooLong", ErrTooManyFilterHeaders: "ErrTooManyFilterHeaders", ErrMalformedStrictString: "ErrMalformedStrictString", ErrTooManyInitStateTypes: "ErrTooManyInitStateTypes", ErrInitStateTypeTooLong: "ErrInitStateTypeTooLong", ErrTooManyTSpends: "ErrTooManyTSpends", } // String returns the ErrorCode as a human-readable name. func (e ErrorCode) String() string { if s := errorCodeStrings[e]; s != "" { return s } return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) } // Error implements the error interface. func (e ErrorCode) Error() string { return e.String() } // Is implements the interface to work with the standard library's errors.Is. // // It returns true in the following cases: // - The target is a *MessageError and the error codes match // - The target is an ErrorCode and it the error codes match func (e ErrorCode) Is(target error) bool { // nolint: errorlint switch target := target.(type) { case *MessageError: return e == target.ErrorCode case ErrorCode: return e == target } return false } // MessageError describes an issue with a message. // An example of some potential issues are messages from the wrong decred // network, invalid commands, mismatched checksums, and exceeding max payloads. // // This provides a mechanism for the caller to type assert the error to // differentiate between general io errors such as io.EOF and issues that // resulted from malformed messages. type MessageError struct { Func string // Function name ErrorCode ErrorCode // Describes the kind of error Description string // Human readable description of the issue } // Error satisfies the error interface and prints human-readable errors. func (m MessageError) Error() string { if m.Func != "" { return fmt.Sprintf("%v: %v", m.Func, m.Description) } return m.Description } // messageError creates an Error given a set of arguments. func messageError(funcName string, c ErrorCode, desc string) *MessageError { return &MessageError{Func: funcName, ErrorCode: c, Description: desc} } // Is implements the interface to work with the standard library's errors.Is. // // It returns true in the following cases: // - The target is a *MessageError and the error codes match // - The target is an ErrorCode and it the error codes match func (m *MessageError) Is(target error) bool { // nolint: errorlint switch target := target.(type) { case *MessageError: return m.ErrorCode == target.ErrorCode case ErrorCode: return target == m.ErrorCode } return false } // Unwrap returns the underlying wrapped error if it is not ErrOther. // Unwrap returns the ErrorCode. Else, it returns nil. func (m *MessageError) Unwrap() error { return m.ErrorCode }