package htlcswitch import ( "bytes" "fmt" sphinx "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/lnwire" ) // ClearTextError is an interface which is implemented by errors that occur // when we know the underlying wire failure message. These errors are the // opposite to opaque errors which are onion-encrypted blobs only understandable // to the initiating node. ClearTextErrors are used when we fail a htlc at our // node, or one of our initiated payments failed and we can decrypt the onion // encrypted error fully. type ClearTextError interface { error // WireMessage extracts a valid wire failure message from an internal // error which may contain additional metadata (which should not be // exposed to the network). This value may be nil in the case where // an unknown wire error is returned by one of our peers. WireMessage() lnwire.FailureMessage } // LinkError is an implementation of the ClearTextError interface which // represents failures that occur on our incoming or outgoing link. type LinkError struct { // msg returns the wire failure associated with the error. // This value should *not* be nil, because we should always // know the failure type for failures which occur at our own // node. msg lnwire.FailureMessage // FailureDetail enriches the wire error with additional information. FailureDetail } // NewLinkError returns a LinkError with the failure message provided. // The failure message provided should *not* be nil, because we should // always know the failure type for failures which occur at our own node. func NewLinkError(msg lnwire.FailureMessage) *LinkError { return &LinkError{msg: msg} } // NewDetailedLinkError returns a link error that enriches a wire message with // a failure detail. func NewDetailedLinkError(msg lnwire.FailureMessage, detail FailureDetail) *LinkError { return &LinkError{ msg: msg, FailureDetail: detail, } } // WireMessage extracts a valid wire failure message from an internal // error which may contain additional metadata (which should not be // exposed to the network). This value should never be nil for LinkErrors, // because we are the ones failing the htlc. // // Note this is part of the ClearTextError interface. func (l *LinkError) WireMessage() lnwire.FailureMessage { return l.msg } // Error returns the string representation of a link error. // // Note this is part of the ClearTextError interface. func (l *LinkError) Error() string { // If the link error has no failure detail, return the wire message's // error. if l.FailureDetail == nil { return l.msg.Error() } return l.FailureDetail.FailureString() } // ForwardingError wraps an lnwire.FailureMessage in a struct that also // includes the source of the error. type ForwardingError struct { // FailureSourceIdx is the index of the node that sent the failure. With // this information, the dispatcher of a payment can modify their set of // candidate routes in response to the type of failure extracted. Index // zero is the self node. FailureSourceIdx int // msg is the wire message associated with the error. This value may // be nil in the case where we fail to decode failure message sent by // a peer. msg lnwire.FailureMessage } // WireMessage extracts a valid wire failure message from an internal // error which may contain additional metadata (which should not be // exposed to the network). This value may be nil in the case where // an unknown wire error is returned by one of our peers. // // Note this is part of the ClearTextError interface. func (f *ForwardingError) WireMessage() lnwire.FailureMessage { return f.msg } // Error implements the built-in error interface. We use this method to allow // the switch or any callers to insert additional context to the error message // returned. func (f *ForwardingError) Error() string { return fmt.Sprintf( "%v@%v", f.msg, f.FailureSourceIdx, ) } // NewForwardingError creates a new payment error which wraps a wire error // with additional metadata. func NewForwardingError(failure lnwire.FailureMessage, index int) *ForwardingError { return &ForwardingError{ FailureSourceIdx: index, msg: failure, } } // NewUnknownForwardingError returns a forwarding error which has a nil failure // message. This constructor should only be used in the case where we cannot // decode the failure we have received from a peer. func NewUnknownForwardingError(index int) *ForwardingError { return &ForwardingError{ FailureSourceIdx: index, } } // ErrorDecrypter is an interface that is used to decrypt the onion encrypted // failure reason an extra out a well formed error. type ErrorDecrypter interface { // DecryptError peels off each layer of onion encryption from the first // hop, to the source of the error. A fully populated // lnwire.FailureMessage is returned along with the source of the // error. DecryptError(lnwire.OpaqueReason) (*ForwardingError, error) } // UnknownEncrypterType is an error message used to signal that an unexpected // EncrypterType was encountered during decoding. type UnknownEncrypterType hop.EncrypterType // Error returns a formatted error indicating the invalid EncrypterType. func (e UnknownEncrypterType) Error() string { return fmt.Sprintf("unknown error encrypter type: %d", e) } // OnionErrorDecrypter is the interface that provides onion level error // decryption. type OnionErrorDecrypter interface { // DecryptError attempts to decrypt the passed encrypted error response. // The onion failure is encrypted in backward manner, starting from the // node where error have occurred. As a result, in order to decrypt the // error we need get all shared secret and apply decryption in the // reverse order. DecryptError(encryptedData []byte) (*sphinx.DecryptedError, error) } // SphinxErrorDecrypter wraps the sphinx data SphinxErrorDecrypter and maps the // returned errors to concrete lnwire.FailureMessage instances. type SphinxErrorDecrypter struct { OnionErrorDecrypter } // DecryptError peels off each layer of onion encryption from the first hop, to // the source of the error. A fully populated lnwire.FailureMessage is returned // along with the source of the error. // // NOTE: Part of the ErrorDecrypter interface. func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) ( *ForwardingError, error) { failure, err := s.OnionErrorDecrypter.DecryptError(reason) if err != nil { return nil, err } // Decode the failure. If an error occurs, we leave the failure message // field nil. r := bytes.NewReader(failure.Message) failureMsg, err := lnwire.DecodeFailure(r, 0) if err != nil { return NewUnknownForwardingError(failure.SenderIdx), nil } return NewForwardingError(failureMsg, failure.SenderIdx), nil } // A compile time check to ensure ErrorDecrypter implements the Deobfuscator // interface. var _ ErrorDecrypter = (*SphinxErrorDecrypter)(nil)