diff --git a/protocol/shannon/errors.go b/protocol/shannon/errors.go index 91055aa98..e38bc1003 100644 --- a/protocol/shannon/errors.go +++ b/protocol/shannon/errors.go @@ -2,8 +2,10 @@ package shannon import ( "context" + "crypto/x509" "errors" - "strings" + "crypto/tls" + "net" pathhttp "github.com/pokt-network/path/network/http" protocolobservations "github.com/pokt-network/path/observation/protocol" @@ -13,80 +15,39 @@ var ( // Unsupported gateway mode errProtocolContextSetupUnsupportedGatewayMode = errors.New("unsupported gateway mode") - // ** Network errors ** - // endpoint configuration error: - // - TLS certificate verification error. - // - DNS error on lookup of endpoint URL. - errRelayEndpointConfig = errors.New("endpoint configuration error") - - // endpoint timeout - errRelayEndpointTimeout = errors.New("timeout waiting for endpoint response") - // PATH manually canceled the context for the request. - // E.g. Parallel requests were made and one succeeded so the other was canceled. - errContextCanceled = errors.New("context canceled manually") - - // HTTP relay request failed - wraps net/http package errors - errSendHTTPRelay = errors.New("HTTP relay request failed") - - // ** Centralized gateway mode errors ** - - // Centralized gateway mode: Error getting onchain data for app - errProtocolContextSetupCentralizedAppFetchErr = errors.New("error getting onchain data for app owned by the gateway") - // Centralized gateway mode app does not delegate to the gateway. - errProtocolContextSetupCentralizedAppDelegation = errors.New("centralized gateway mode app does not delegate to the gateway") - // Centralized gateway mode: no active sessions could be retrieved for the service. - errProtocolContextSetupCentralizedNoSessions = errors.New("no active sessions could be retrieved for the service") - // Centralized gateway mode: no owned apps found for the service. + // === Network & relay errors === + errRelayEndpointConfig = errors.New("endpoint configuration error") // TLS/DNS/config issues + errRelayEndpointTimeout = errors.New("timeout waiting for endpoint response") + errContextCanceled = errors.New("context canceled manually") // PATH-initiated cancel + errSendHTTPRelay = errors.New("HTTP relay request failed") + errMalformedEndpointPayload = errors.New("endpoint returned malformed payload") + + // === Gateway mode errors === + errProtocolContextSetupCentralizedAppFetchErr = errors.New("error getting onchain data for app owned by the gateway") + errProtocolContextSetupCentralizedAppDelegation = errors.New("centralized gateway mode app does not delegate to the gateway") + errProtocolContextSetupCentralizedNoSessions = errors.New("no active sessions could be retrieved for the service") errProtocolContextSetupCentralizedNoAppsForService = errors.New("ZERO owned apps found for service") - - // Delegated gateway mode: could not extract app from HTTP request. - errProtocolContextSetupGetAppFromHTTPReq = errors.New("error getting the selected app from the HTTP request") - // Delegated gateway mode: could not fetch session for app from the full node - errProtocolContextSetupFetchSession = errors.New("error getting a session from the full node for app") - // Delegated gateway mode: gateway does not have delegation for the app. - errProtocolContextSetupAppDoesNotDelegate = errors.New("gateway does not have delegation for app") - // Delegated gateway mode: app is not staked for the service. - errProtocolContextSetupAppNotStaked = errors.New("app is not staked for the service") - - // ** Request context setup errors ** - - // No endpoints available for the service. - // Can be due to one or more of the following: - // - Any of the gateway mode errors above. - // - Error fetching a session for an app. - errProtocolContextSetupNoEndpoints = errors.New("no endpoints found for service: relay request will fail") - // Selected endpoint is no longer available. - // Can happen due to: - // - Bug in endpoint selection logic. - // - Endpoint sanctioned due to an observation while selection logic was running. - errRequestContextSetupInvalidEndpointSelected = errors.New("selected endpoint is not available: relay request will fail") - // Error initializing a signer for the current gateway mode. - errRequestContextSetupErrSignerSetup = errors.New("error getting the permitted signer: relay request will fail") - - // The endpoint returned a malformed payload. - // Helps track more fine-grained metrics on endpoint errors. - errMalformedEndpointPayload = errors.New("endpoint returned malformed payload") - - // The endpoint returned a non-2XX response. - errEndpointNon2XXHTTPStatusCode = errors.New("endpoint returned non-2xx HTTP status code") - - // ** Websocket errors ** - - // Error creating a Websocket connection. - errCreatingWebSocketConnection = errors.New("error creating Websocket connection") - - // Error signing the relay request in a websocket message. - errRelayRequestWebsocketMessageSigningFailed = errors.New("error signing relay request in websocket message") - - // Error validating the relay response in a websocket message. + errProtocolContextSetupGetAppFromHTTPReq = errors.New("error getting the selected app from the HTTP request") + errProtocolContextSetupFetchSession = errors.New("error getting a session from the full node for app") + errProtocolContextSetupAppDoesNotDelegate = errors.New("gateway does not have delegation for app") + errProtocolContextSetupAppNotStaked = errors.New("app is not staked for the service") + + // === Request context setup errors === + errProtocolContextSetupNoEndpoints = errors.New("no endpoints found for service: relay request will fail") + errRequestContextSetupInvalidEndpointSelected = errors.New("selected endpoint is not available: relay request will fail") + errRequestContextSetupErrSignerSetup = errors.New("error getting the permitted signer: relay request will fail") + + // === Websocket errors === + errCreatingWebSocketConnection = errors.New("error creating Websocket connection") + errRelayRequestWebsocketMessageSigningFailed = errors.New("error signing relay request in websocket message") errRelayResponseInWebsocketMessageValidationFailed = errors.New("error validating relay response in websocket message") ) // extractErrFromRelayError: // • Analyzes errors returned during relay operations // • Matches errors to predefined types through: -// - Primary: Error comparison (with unwrapping) -// - Fallback: String analysis for unrecognized types +// - Primary: errors.Is / errors.As (robust, works with wrapped errors) +// - Secondary: Type-specific matching for network/config errors // // • Centralizes error recognition logic to avoid duplicate string matching // • Provides fine-grained HTTP error classification @@ -101,17 +62,16 @@ func extractErrFromRelayError(err error) error { return errRelayEndpointConfig } - // http endpoint timeout - if strings.Contains(err.Error(), context.DeadlineExceeded.Error()) { // "context deadline exceeded" + // Context-based errors (robust, works recursively through wrapped errors) + if errors.Is(err, context.DeadlineExceeded) { return errRelayEndpointTimeout } - // Endpoint's backend service returned a non 2xx HTTP status code. - if strings.Contains(err.Error(), "non 2xx HTTP status code") { + if errors.Is(err, pathhttp.ErrRelayEndpointHTTPError) { return pathhttp.ErrRelayEndpointHTTPError } - // context canceled manually - if strings.Contains(err.Error(), "context canceled") { + + if errors.Is(err, context.Canceled) { return errContextCanceled } @@ -126,15 +86,38 @@ func extractErrFromRelayError(err error) error { // - Error verifying endpoint's TLS certificate // - Error on DNS lookup of endpoint's URL. func isEndpointNetworkConfigError(err error) bool { - errStr := err.Error() - switch { - case strings.Contains(errStr, "dial tcp: lookup"): + // Use errors.As for robust type matching (works with wrapped errors) + // Covers common DNS, connection, and TLS issues + + // DNS resolution errors + if errors.As(err, &net.DNSError{}) { return true - case strings.Contains(errStr, "tls: failed to verify certificate"): + } + + // General network operation errors (dial, read, write, connect, etc.) + var opErr *net.OpError + if errors.As(err, &opErr) { + return true + } + + // Common x509 certificate validation errors + if errors.As(err, &x509.HostnameError{}) { + return true + } + if errors.As(err, &x509.UnknownAuthorityError{}) { + return true + } + if errors.As(err, &x509.CertificateInvalidError{}) { return true - default: - return false } + + // TLS handshake / record-level errors not covered by x509 + var tlsRecordErr *tls.RecordHeaderError + if errors.As(err, &tlsRecordErr) { + return true + } + + return false } // isMalformedEndpointPayloadError returns true if the error indicates a malformed endpoint payload.