diff --git a/lnwire/message.go b/lnwire/message.go index f92705d9..f12fa2f7 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -108,12 +108,21 @@ func (u *UnknownMessage) Error() string { u.messageType) } +// Serializable is an interface which defines a lightning wire serializable +// object. +type Serializable interface { + // Decode reads the bytes stream and converts it to the object. + Decode(io.Reader, uint32) error + + // Encode converts object to the bytes stream and write it in th writer. + Encode(io.Writer, uint32) error +} + // Message is an interface that defines a lightning wire protocol message. The // interface is general in order to allow implementing types full control over // the representation of its data. type Message interface { - Decode(io.Reader, uint32) error - Encode(io.Writer, uint32) error + Serializable MsgType() MessageType MaxPayloadLength(uint32) uint32 } diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go new file mode 100644 index 00000000..f2852123 --- /dev/null +++ b/lnwire/onion_error.go @@ -0,0 +1,985 @@ +package lnwire + +import ( + "crypto/sha256" + "io" + + "bytes" + + "github.com/go-errors/errors" + "github.com/roasbeef/btcutil" +) + +// FailureMessage represent the onion failure object which is able to identify itself +// by returning the unique failure code. +type FailureMessage interface { + Code() FailCode +} + +// failureMessageLength is the size of the failure message plus the size +// of padding. FailureMessage message always should be equals to this size. +const failureMessageLength = 128 + +const ( + // FlagBadOnion error flag denotes unparsable onion, encrypted by + // previous node. + FlagBadOnion FailCode = 0x8000 + + // FlagPerm error flag denotes permanent failure. + FlagPerm FailCode = 0x4000 + + // FlagNode error flag denotes node failure. + FlagNode FailCode = 0x2000 + + // FlagUpdate error flag denotes new channel update enclosed. + FlagUpdate FailCode = 0x1000 +) + +// FailCode specifies the precise reason that an upstream HTLC was cancelled. +// Each UpdateFailHTLC message carries a FailCode which is to be passed back +// unaltered to the source of the HTLC within the route. +type FailCode uint16 + +// The currently defined onion failure types within this current version of the +// Lightning protocol. +const ( + CodeNone FailCode = 0 + CodeInvalidRealm = FlagBadOnion | 1 + CodeTemporaryNodeFailure = FlagNode | 2 + CodePermanentNodeFailure = FlagPerm | FlagNode | 2 + CodeRequiredNodeFeatureMissing = FlagPerm | FlagNode | 3 + CodeInvalidOnionVersion = FlagBadOnion | FlagPerm | 4 + CodeInvalidOnionHmac = FlagBadOnion | FlagPerm | 5 + CodeInvalidOnionKey = FlagBadOnion | FlagPerm | 6 + CodeTemporaryChannelFailure = FlagUpdate | 7 + CodePermanentChannelFailure = FlagPerm | 8 + CodeRequiredChannelFeatureMissing = FlagPerm | 9 + CodeUnknownNextPeer = FlagPerm | 10 + CodeAmountBelowMinimum = FlagUpdate | 11 + CodeFeeInsufficient = FlagUpdate | 12 + CodeIncorrectCltvExpiry = FlagUpdate | 13 + CodeExpiryTooSoon = FlagUpdate | 14 + CodeChannelDisabled = FlagUpdate | 20 + CodeUnknownPaymentHash = FlagPerm | 15 + CodeIncorrectPaymentAmount = FlagPerm | 16 + CodeFinalExpiryTooSoon FailCode = 17 + CodeFinalIncorrectCltvExpiry FailCode = 18 + CodeFinalIncorrectHtlcAmount FailCode = 19 +) + +// String returns string representation of the failure code. +func (c FailCode) String() string { + switch c { + case CodeInvalidRealm: + return "InvalidRealm" + + case CodeTemporaryNodeFailure: + return "TemporaryNodeFailure" + + case CodePermanentNodeFailure: + return "PermanentNodeFailure" + + case CodeRequiredNodeFeatureMissing: + return "RequiredNodeFeatureMissing" + + case CodeInvalidOnionVersion: + return "InvalidOnionVersion" + + case CodeInvalidOnionHmac: + return "InvalidOnionHmac" + + case CodeInvalidOnionKey: + return "InvalidOnionKey" + + case CodeTemporaryChannelFailure: + return "TemporaryChannelFailure" + + case CodePermanentChannelFailure: + return "PermanentChannelFailure" + + case CodeRequiredChannelFeatureMissing: + return "RequiredChannelFeatureMissing" + + case CodeUnknownNextPeer: + return "UnknownNextPeer" + + case CodeAmountBelowMinimum: + return "AmountBelowMinimum" + + case CodeFeeInsufficient: + return "FeeInsufficient" + + case CodeIncorrectCltvExpiry: + return "IncorrectCltvExpiry" + + case CodeExpiryTooSoon: + return "ExpiryTooSoon" + + case CodeChannelDisabled: + return "ChannelDisabled" + + case CodeUnknownPaymentHash: + return "UnknownPaymentHash" + + case CodeIncorrectPaymentAmount: + return "IncorrectPaymentAmount" + + case CodeFinalExpiryTooSoon: + return "FinalExpiryTooSoon" + + case CodeFinalIncorrectCltvExpiry: + return "FinalIncorrectCltvExpiry" + + case CodeFinalIncorrectHtlcAmount: + return "FinalIncorrectHtlcAmount" + + default: + return "" + } +} + +// FailInvalidRealm is returned if the realm byte is unknown. +// +// NOTE: might be returned by any node. +type FailInvalidRealm struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailInvalidRealm) Code() FailCode { + return CodeInvalidRealm +} + +// FailTemporaryNodeFailure is returned if an otherwise unspecified transient +// error occurs for the entire node. +// +// NOTE: might be returned by any node. +type FailTemporaryNodeFailure struct{} + +// Code returns the failure unique code. +// NOTE: Part of the FailureMessage interface. +func (f FailTemporaryNodeFailure) Code() FailCode { + return CodeTemporaryNodeFailure +} + +// FailPermanentNodeFailure is returned if an otherwise unspecified permanent +// error occurs for the entire node. +// +// NOTE: might be returned by any node. +type FailPermanentNodeFailure struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailPermanentNodeFailure) Code() FailCode { + return CodePermanentNodeFailure +} + +// FailRequiredNodeFeatureMissing is returned if a node has requirement +// advertised in its node_announcement features which were not present in the +// onion. +// +// NOTE: might be returned by any node. +type FailRequiredNodeFeatureMissing struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailRequiredNodeFeatureMissing) Code() FailCode { + return CodeRequiredNodeFeatureMissing +} + +// FailPermanentChannelFailure is return if an otherwise unspecified +// permanent error occurs for the outgoing channel (eg. channel (recently). +// +// NOTE: might be return by forwarding node only. +type FailPermanentChannelFailure struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailPermanentChannelFailure) Code() FailCode { + return CodePermanentChannelFailure +} + +// FailRequiredChannelFeatureMissing is return if the outgoing channel has +// requirement advertised in its channel announcement features which were not +// present in the onion. +// +// NOTE: might be return by forwarding node only. +type FailRequiredChannelFeatureMissing struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailRequiredChannelFeatureMissing) Code() FailCode { + return CodeRequiredChannelFeatureMissing +} + +// FailUnknownNextPeer is returned if the next peer specified by the onion is +// not known. +// +// NOTE: might be return by forwarding node only. +type FailUnknownNextPeer struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailUnknownNextPeer) Code() FailCode { + return CodeUnknownNextPeer +} + +// FailUnknownPaymentHash is returned If the payment hash has already been paid, +// the final node MAY treat the payment hash as unknown, or may succeed in +// accepting the HTLC. If the payment hash is unknown, the final node MUST fail +// the HTLC. +// +// NOTE: might be returned by final node only. +type FailUnknownPaymentHash struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailUnknownPaymentHash) Code() FailCode { + return CodeUnknownPaymentHash +} + +// FailIncorrectPaymentAmount is returned if the amount paid is less than the +// amount expected, the final node MUST fail the HTLC. If the amount paid is +// more than twice the amount expected, the final node SHOULD fail the HTLC. +// This allows the sender to reduce information leakage by altering the amount, +// without allowing accidental gross overpayment. +// +// NOTE: might be returned by final node only. +type FailIncorrectPaymentAmount struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailIncorrectPaymentAmount) Code() FailCode { + return CodeIncorrectPaymentAmount +} + +// FailFinalExpiryTooSoon is returned if the cltv_expiry is too low, the final +// node MUST fail the HTLC. +// +// NOTE: might be returned by final node only. +type FailFinalExpiryTooSoon struct{} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f FailFinalExpiryTooSoon) Code() FailCode { + return CodeFinalExpiryTooSoon +} + +// FailInvalidOnionVersion is returned if the onion version byte is unknown. +// +// NOTE: should be return by forwarding node only. +type FailInvalidOnionVersion struct { + // OnionSHA256 hash of the onion blob which haven't been proceeded. + OnionSHA256 [sha256.Size]byte +} + +// NewInvalidOnionVersion creates new instance of the FailInvalidOnionVersion. +func NewInvalidOnionVersion(onion []byte) *FailInvalidOnionVersion { + return &FailInvalidOnionVersion{OnionSHA256: sha256.Sum256(onion)} +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailInvalidOnionVersion) Code() FailCode { + return CodeInvalidOnionVersion +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionVersion) Decode(r io.Reader, pver uint32) error { + return readElement(r, f.OnionSHA256[:]) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionVersion) Encode(w io.Writer, pver uint32) error { + return writeElement(w, f.OnionSHA256[:]) +} + +// FailInvalidOnionHmac is return if the onion HMAC is incorrect. +// +// NOTE: might be return by forwarding node only. +type FailInvalidOnionHmac struct { + // OnionSHA256 hash of the onion blob which haven't been proceeded. + OnionSHA256 [sha256.Size]byte +} + +// NewInvalidOnionHmac creates new instance of the FailInvalidOnionHmac. +func NewInvalidOnionHmac(onion []byte) *FailInvalidOnionHmac { + return &FailInvalidOnionHmac{OnionSHA256: sha256.Sum256(onion)} +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailInvalidOnionHmac) Code() FailCode { + return CodeInvalidOnionHmac +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionHmac) Decode(r io.Reader, pver uint32) error { + return readElement(r, f.OnionSHA256[:]) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionHmac) Encode(w io.Writer, pver uint32) error { + return writeElement(w, f.OnionSHA256[:]) +} + +// FailInvalidOnionKey is return if the ephemeral key in the onion is +// unparsable. +// +// NOTE: might be return by forwarding node only. +type FailInvalidOnionKey struct { + // OnionSHA256 hash of the onion blob which haven't been proceeded. + OnionSHA256 [sha256.Size]byte +} + +// NewInvalidOnionKey creates new instance of the FailInvalidOnionKey. +func NewInvalidOnionKey(onion []byte) *FailInvalidOnionKey { + return &FailInvalidOnionKey{OnionSHA256: sha256.Sum256(onion)} +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailInvalidOnionKey) Code() FailCode { + return CodeInvalidOnionKey +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionKey) Decode(r io.Reader, pver uint32) error { + return readElement(r, f.OnionSHA256[:]) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailInvalidOnionKey) Encode(w io.Writer, pver uint32) error { + return writeElement(w, f.OnionSHA256[:]) +} + +// FailTemporaryChannelFailure is if an otherwise unspecified transient +// error occurs for the outgoing channel (eg. channel capacity reached, +// too many in-flight htlc) +// +// NOTE: might be return by forwarding node only. +type FailTemporaryChannelFailure struct { + // Update is used to update information about state of the channel which + // caused the failure. + // + // NOTE: Field is optional. + Update *ChannelUpdate +} + +// NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure. +func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure { + return &FailTemporaryChannelFailure{Update: update} +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailTemporaryChannelFailure) Code() FailCode { + return CodeTemporaryChannelFailure +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error { + var length uint16 + err := readElement(r, &length) + if err != nil { + return err + } + + if length != 0 { + f.Update = &ChannelUpdate{} + return f.Update.Decode(r, pver) + } + return nil +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailTemporaryChannelFailure) Encode(w io.Writer, pver uint32) error { + var payload []byte + if f.Update != nil { + var bw bytes.Buffer + if err := f.Update.Encode(&bw, pver); err != nil { + return err + } + payload = bw.Bytes() + } + + if err := writeElement(w, uint16(len(payload))); err != nil { + return err + } + + _, err := w.Write(payload) + return err +} + +// FailAmountBelowMinimum is returned if the HTLC does not reach the +// current minimum amount, we tell them the amount of the incoming HTLC +// and the current channel setting for the outgoing channel. +// +// NOTE: might be return by forwarding node only. +type FailAmountBelowMinimum struct { + // HtlcMsat is the wrong amount of the incoming HTLC. + HtlcMsat btcutil.Amount + + // Update is used to update information about state of the channel which + // caused the failure. + Update ChannelUpdate +} + +// NewAmountBelowMinimum creates new instance of the FailAmountBelowMinimum. +func NewAmountBelowMinimum(htlcMsat btcutil.Amount, + update ChannelUpdate) *FailAmountBelowMinimum { + return &FailAmountBelowMinimum{ + HtlcMsat: htlcMsat, + Update: update, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailAmountBelowMinimum) Code() FailCode { + return CodeAmountBelowMinimum +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error { + if err := readElement(r, &f.HtlcMsat); err != nil { + return err + } + + // At current moment length is not used but in future we may use it to + // differ versions of the update message. + var length uint16 + if err := readElement(r, &length); err != nil { + return err + } + + f.Update = ChannelUpdate{} + return f.Update.Decode(r, pver) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailAmountBelowMinimum) Encode(w io.Writer, pver uint32) error { + if err := writeElement(w, f.HtlcMsat); err != nil { + return err + } + + // We write the length here as the size of the channel updates may differ in + // the future and at times additional information is coupled (appended to or + // prepended to the channel update itself) along with the channel update. + err := writeElement(w, uint16(f.Update.MaxPayloadLength(pver))) + if err != nil { + return err + } + + return f.Update.Encode(w, pver) +} + +// FailFeeInsufficient is returned if the HTLC does not pay sufficient +// fee, we tell them the amount of the incoming HTLC and the current +// channel setting for the outgoing channel. +// +// NOTE: might be return by forwarding node only. +type FailFeeInsufficient struct { + // HtlcMsat is the wrong amount of the incoming HTLC. + HtlcMsat btcutil.Amount + + // Update is used to update information about state of the channel which + // caused the failure. + Update ChannelUpdate +} + +// NewFeeInsufficient creates new instance of the FailFeeInsufficient. +func NewFeeInsufficient(htlcMsat btcutil.Amount, + update ChannelUpdate) *FailFeeInsufficient { + return &FailFeeInsufficient{ + HtlcMsat: htlcMsat, + Update: update, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailFeeInsufficient) Code() FailCode { + return CodeFeeInsufficient +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error { + if err := readElement(r, &f.HtlcMsat); err != nil { + return err + } + + // At current moment length is not used but in future we may use it to + // differ versions of the update message. + var length uint16 + if err := readElement(r, &length); err != nil { + return err + } + + f.Update = ChannelUpdate{} + return f.Update.Decode(r, pver) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFeeInsufficient) Encode(w io.Writer, pver uint32) error { + if err := writeElement(w, f.HtlcMsat); err != nil { + return err + } + + // We write the length here as the size of the channel updates may differ in + // the future and at times additional information is coupled (appended to or + // prepended to the channel update itself) along with the channel update. + err := writeElement(w, uint16(f.Update.MaxPayloadLength(pver))) + if err != nil { + return err + } + + return f.Update.Encode(w, pver) +} + +// FailIncorrectCltvExpiry is returned if outgoing cltv value does not +// match the update add htlc's cltv expiry minus cltv expiry delta +// for the outgoing channel, we tell them the cltv expiry and the +// current channel setting for the outgoing channel. +// +// NOTE: might be return by forwarding node only. +type FailIncorrectCltvExpiry struct { + // CltvExpiry is the wrong absolute timeout in blocks, after which outgoing + // HTLC expires. + CltvExpiry uint32 + + // Update is used to update information about state of the channel which + // caused the failure. + Update ChannelUpdate +} + +// NewIncorrectCltvExpiry creates new instance of the FailIncorrectCltvExpiry. +func NewIncorrectCltvExpiry(cltvExpiry uint32, update ChannelUpdate) *FailIncorrectCltvExpiry { + return &FailIncorrectCltvExpiry{ + CltvExpiry: cltvExpiry, + Update: update, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailIncorrectCltvExpiry) Code() FailCode { + return CodeIncorrectCltvExpiry +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error { + if err := readElement(r, &f.CltvExpiry); err != nil { + return err + } + + // At current moment length is not used but in future we may use it to + // differ versions of the update message. + var length uint16 + if err := readElement(r, &length); err != nil { + return err + } + + f.Update = ChannelUpdate{} + return f.Update.Decode(r, pver) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error { + if err := writeElement(w, f.CltvExpiry); err != nil { + return err + } + + // We write the length here as the size of the channel updates may differ in + // the future and at times additional information is coupled (appended to or + // prepended to the channel update itself) along with the channel update. + err := writeElement(w, uint16(f.Update.MaxPayloadLength(pver))) + if err != nil { + return err + } + + return f.Update.Encode(w, pver) +} + +// FailExpiryTooSoon is returned if the ctlv-expiry is too near, we tell +// them the the current channel setting for the outgoing channel. +// +// NOTE: might be return by forwarding node only. +type FailExpiryTooSoon struct { + // Update is used to update information about state of the channel which + // caused the failure. + Update ChannelUpdate +} + +// NewExpiryTooSoon creates new instance of the FailExpiryTooSoon +func NewExpiryTooSoon(update ChannelUpdate) *FailExpiryTooSoon { + return &FailExpiryTooSoon{ + Update: update, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailExpiryTooSoon) Code() FailCode { + return CodeExpiryTooSoon +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error { + // At current moment length is not used but in future we may use it to + // differ versions of the update message. + var length uint16 + if err := readElement(r, &length); err != nil { + return err + } + + f.Update = ChannelUpdate{} + return f.Update.Decode(r, pver) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailExpiryTooSoon) Encode(w io.Writer, pver uint32) error { + // We write the length here as the size of the channel updates may differ in + // the future and at times additional information is coupled (appended to or + // prepended to the channel update itself) along with the channel update. + err := writeElement(w, uint16(f.Update.MaxPayloadLength(pver))) + if err != nil { + return err + } + + return f.Update.Encode(w, pver) +} + +// FailChannelDisabled is returned if the channel is disabled, we tell +// them the the current channel setting for the outgoing channel. +// +// NOTE: might be return by forwarding node only. +type FailChannelDisabled struct { + // Flags least-significant bit must be set to 0 if the creating node + // corresponds to the first node in the previously sent channel + // announcement and 1 otherwise. + Flags uint16 + + // Update is used to update information about state of the channel which + // caused the failure. + Update ChannelUpdate +} + +// NewChannelDisabled creates new instance of the FailChannelDisabled. +func NewChannelDisabled(flags uint16, update ChannelUpdate) *FailChannelDisabled { + return &FailChannelDisabled{ + Flags: flags, + Update: update, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailChannelDisabled) Code() FailCode { + return CodeChannelDisabled +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error { + if err := readElement(r, &f.Flags); err != nil { + return err + } + + // At current moment length is not used but in future we may use it to + // differ versions of the update message. + var length uint16 + if err := readElement(r, &length); err != nil { + return err + } + + f.Update = ChannelUpdate{} + return f.Update.Decode(r, pver) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailChannelDisabled) Encode(w io.Writer, pver uint32) error { + if err := writeElement(w, f.Flags); err != nil { + return err + } + + // We write the length here as the size of the channel updates may differ in + // the future and at times additional information is coupled (appended to or + // prepended to the channel update itself) along with the channel update. + err := writeElement(w, uint16(f.Update.MaxPayloadLength(pver))) + if err != nil { + return err + } + + return f.Update.Encode(w, pver) +} + +// FailFinalIncorrectCltvExpiry is returned if the outgoing_cltv_value does not +// match the ctlv_expiry of the HTLC at the final hop. +// +// NOTE: might be returned by final node only. +type FailFinalIncorrectCltvExpiry struct { + // CltvExpiry is the wrong absolute timeout in blocks, after which outgoing + // HTLC expires. + CltvExpiry uint32 +} + +// NewFinalIncorrectCltvExpiry creates new instance of the +// FailFinalIncorrectCltvExpiry. +func NewFinalIncorrectCltvExpiry(cltvExpiry uint32) *FailFinalIncorrectCltvExpiry { + return &FailFinalIncorrectCltvExpiry{ + CltvExpiry: cltvExpiry, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailFinalIncorrectCltvExpiry) Code() FailCode { + return CodeFinalIncorrectCltvExpiry +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFinalIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error { + return readElement(r, &f.CltvExpiry) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFinalIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error { + return writeElement(w, f.CltvExpiry) +} + +// FailFinalIncorrectHtlcAmount is returned if the amt_to_forward is +// higher than incoming_htlc_amt of the HTLC at the final hop. +// +// NOTE: might be returned by final node only. +type FailFinalIncorrectHtlcAmount struct { + // IncomingHTLCAmount is the wrong forwarded htlc amount. + IncomingHTLCAmount btcutil.Amount +} + +// NewFinalIncorrectHtlcAmount creates new instance of the +// FailFinalIncorrectHtlcAmount. +func NewFinalIncorrectHtlcAmount(amount btcutil.Amount) *FailFinalIncorrectHtlcAmount { + return &FailFinalIncorrectHtlcAmount{ + IncomingHTLCAmount: amount, + } +} + +// Code returns the failure unique code. +// +// NOTE: Part of the FailureMessage interface. +func (f *FailFinalIncorrectHtlcAmount) Code() FailCode { + return CodeFinalIncorrectHtlcAmount +} + +// Decode decodes the failure from bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFinalIncorrectHtlcAmount) Decode(r io.Reader, pver uint32) error { + return readElement(r, &f.IncomingHTLCAmount) +} + +// Encode writes the failure in bytes stream. +// +// NOTE: Part of the Serializable interface. +func (f *FailFinalIncorrectHtlcAmount) Encode(w io.Writer, pver uint32) error { + return writeElement(w, f.IncomingHTLCAmount) +} + +// DecodeFailure decodes, validates, and parses the lnwire onion failure, for the +// provided protocol version. +func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) { + // Start processing the failure message by reading the code. + var code uint16 + if err := readElement(r, &code); err != nil { + return nil, err + } + + // Create the empty failure by given code and populate the failure + // with additional data if needed. + failure, err := makeEmptyOnionError(FailCode(code)) + if err != nil { + return nil, err + } + + // Read failure length, check its size and read the failure message + // in order to check padding afterwards. + var failureLength uint16 + if err := readElement(r, &failureLength); err != nil { + return nil, err + } + if failureLength > failureMessageLength { + return nil, errors.New("failure message is too long") + } + + failureData := make([]byte, failureLength) + if _, err := io.ReadFull(r, failureData); err != nil { + return nil, err + } + failureReader := bytes.NewReader(failureData) + + switch f := failure.(type) { + case Serializable: + if err := f.Decode(failureReader, pver); err != nil { + return nil, err + } + } + + return failure, nil +} + +// EncodeFailure encodes, including the necessary onion failure header information. +func EncodeFailure(w io.Writer, failure FailureMessage, pver uint32) error { + var failureMessageBuffer bytes.Buffer + + switch failure := failure.(type) { + case Serializable: + if err := failure.Encode(&failureMessageBuffer, pver); err != nil { + return err + } + } + + failureMessage := failureMessageBuffer.Bytes() + if len(failureMessage) > failureMessageLength { + return errors.New("failure message exceed max available size") + } + + code := uint16(failure.Code()) + pad := make([]byte, failureMessageLength-len(failureMessage)) + + return writeElements(w, + code, + uint16(len(failureMessage)), + failureMessage, + uint16(len(pad)), + pad, + ) +} + +// makeEmptyOnionError creates a new empty onion error of the proper concrete +// type based on the passed failure code. +func makeEmptyOnionError(code FailCode) (FailureMessage, error) { + switch code { + case CodeInvalidRealm: + return &FailInvalidRealm{}, nil + + case CodeTemporaryNodeFailure: + return &FailTemporaryNodeFailure{}, nil + + case CodePermanentNodeFailure: + return &FailPermanentNodeFailure{}, nil + + case CodeRequiredNodeFeatureMissing: + return &FailRequiredNodeFeatureMissing{}, nil + + case CodePermanentChannelFailure: + return &FailPermanentChannelFailure{}, nil + + case CodeRequiredChannelFeatureMissing: + return &FailRequiredChannelFeatureMissing{}, nil + + case CodeUnknownNextPeer: + return &FailUnknownNextPeer{}, nil + + case CodeUnknownPaymentHash: + return &FailUnknownPaymentHash{}, nil + + case CodeIncorrectPaymentAmount: + return &FailIncorrectPaymentAmount{}, nil + + case CodeFinalExpiryTooSoon: + return &FailFinalExpiryTooSoon{}, nil + + case CodeInvalidOnionVersion: + return &FailInvalidOnionVersion{}, nil + + case CodeInvalidOnionHmac: + return &FailInvalidOnionHmac{}, nil + + case CodeInvalidOnionKey: + return &FailInvalidOnionKey{}, nil + + case CodeTemporaryChannelFailure: + return &FailTemporaryChannelFailure{}, nil + + case CodeAmountBelowMinimum: + return &FailAmountBelowMinimum{}, nil + + case CodeFeeInsufficient: + return &FailFeeInsufficient{}, nil + + case CodeIncorrectCltvExpiry: + return &FailIncorrectCltvExpiry{}, nil + + case CodeExpiryTooSoon: + return &FailExpiryTooSoon{}, nil + + case CodeChannelDisabled: + return &FailChannelDisabled{}, nil + + case CodeFinalIncorrectCltvExpiry: + return &FailFinalIncorrectCltvExpiry{}, nil + + case CodeFinalIncorrectHtlcAmount: + return &FailFinalIncorrectHtlcAmount{}, nil + default: + return nil, errors.Errorf("unknown error code: %v", code) + } +} diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go new file mode 100644 index 00000000..3650b59d --- /dev/null +++ b/lnwire/onion_error_test.go @@ -0,0 +1,72 @@ +package lnwire + +import ( + "bytes" + "reflect" + "testing" + + "github.com/roasbeef/btcutil" +) + +var ( + testOnionHash = []byte{} + testAmount = btcutil.Amount(1) + testCtlvExpiry = uint32(2) + testFlags = uint16(2) + testChannelUpdate = ChannelUpdate{ + Signature: testSig, + ShortChannelID: NewShortChanIDFromInt(1), + Timestamp: 1, + Flags: 1, + } +) + +var onionFailures = []FailureMessage{ + &FailInvalidRealm{}, + &FailTemporaryNodeFailure{}, + &FailPermanentNodeFailure{}, + &FailRequiredNodeFeatureMissing{}, + &FailPermanentChannelFailure{}, + &FailRequiredChannelFeatureMissing{}, + &FailUnknownNextPeer{}, + &FailUnknownPaymentHash{}, + &FailIncorrectPaymentAmount{}, + &FailFinalExpiryTooSoon{}, + + NewInvalidOnionVersion(testOnionHash), + NewInvalidOnionHmac(testOnionHash), + NewInvalidOnionKey(testOnionHash), + NewTemporaryChannelFailure(&testChannelUpdate), + NewTemporaryChannelFailure(nil), + NewAmountBelowMinimum(testAmount, testChannelUpdate), + NewFeeInsufficient(testAmount, testChannelUpdate), + NewIncorrectCltvExpiry(testCtlvExpiry, testChannelUpdate), + NewExpiryTooSoon(testChannelUpdate), + NewChannelDisabled(testFlags, testChannelUpdate), + NewFinalIncorrectCltvExpiry(testCtlvExpiry), + NewFinalIncorrectHtlcAmount(testAmount), +} + +// TestEncodeDecodeCode tests the ability of onion errors to be properly encoded +// and decoded. +func TestEncodeDecodeCode(t *testing.T) { + for _, failure1 := range onionFailures { + var b bytes.Buffer + + if err := EncodeFailure(&b, failure1, 0); err != nil { + t.Fatalf("unable to encode failure code(%v): %v", + failure1.Code(), err) + } + + failure2, err := DecodeFailure(&b, 0) + if err != nil { + t.Fatalf("unable to decode failure code(%v): %v", + failure1.Code(), err) + } + + if !reflect.DeepEqual(failure1, failure2) { + t.Fatalf("failure message are different, failure "+ + "code(%v)", failure1.Code()) + } + } +} diff --git a/lnwire/update_fail_htlc.go b/lnwire/update_fail_htlc.go index 4b7a92b4..9b70c881 100644 --- a/lnwire/update_fail_htlc.go +++ b/lnwire/update_fail_htlc.go @@ -1,102 +1,19 @@ package lnwire -import ( - "errors" - "io" -) - -// FailCode specifies the precise reason that an upstream HTLC was cancelled. -// Each UpdateFailHTLC message carries a FailCode which is to be passed back -// unaltered to the source of the HTLC within the route. -// -// TODO(roasbeef): implement proper encrypted error messages as defined in spec -// * these errors as it stands reveal the error cause to all links in the -// route and are horrible for privacy -type FailCode uint16 - -const ( - // InsufficientCapacity indicates that a payment failed due to a link - // in the ultimate route not having enough satoshi flow to successfully - // carry the payment. - InsufficientCapacity FailCode = 0 - - // UpstreamTimeout indicates that an upstream link had to enforce the - // absolute HTLC timeout, removing the HTLC. - UpstreamTimeout FailCode = 1 - - // UnknownPaymentHash indicates that the destination did not recognize - // the payment hash. - UnknownPaymentHash FailCode = 2 - - // UnknownDestination indicates that the specified next hop within the - // Sphinx packet at a point in the route contained an unknown or - // invalid "next hop". - UnknownDestination FailCode = 3 - - // SphinxParseError indicates that an intermediate node was unable - // properly parse the HTLC. - SphinxParseError FailCode = 4 - - // IncorrectValue indicates that the HTLC ultimately extended to the - // destination did not match the value that was expected. - IncorrectValue FailCode = 5 - - // UnknownError indicates the error which should be returned, but - // not exist in specification yet. - UnknownError FailCode = 6 -) - -// String returns a human-readable version of the FailCode type. -func (c FailCode) String() string { - switch c { - case InsufficientCapacity: - return "InsufficientCapacity: next hop had insufficient " + - "capacity for payment" - - case UpstreamTimeout: - return "UpstreamTimeout: HTLC has timed out upstream" - - case UnknownPaymentHash: - return "UnknownPaymentHash: the destination did not know the " + - "preimage" - - case UnknownDestination: - return "UnknownDestination: next hop unknown" - - case SphinxParseError: - return "SphinxParseError: unable to parse sphinx packet" - - case IncorrectValue: - return "IncorrectValue: htlc value was wrong" - - default: - return "unknown reason" - } -} +import "io" // OpaqueReason is an opaque encrypted byte slice that encodes the exact // failure reason and additional some supplemental data. The contents of this // slice can only be decrypted by the sender of the original HTLC. type OpaqueReason []byte -// ToFailCode converts the reason in fail code. -// TODO(andrew.shvv) Future version of this method should implement -// decryption opaque reason logic. -func (r OpaqueReason) ToFailCode() (FailCode, error) { - if len(r) != 1 { - return 0, errors.New("wrong opaque code length") - } - - return FailCode(r[0]), nil -} - // UpdateFailHTLC is sent by Alice to Bob in order to remove a previously added // HTLC. Upon receipt of an UpdateFailHTLC the HTLC should be removed from the // next commitment transaction, with the UpdateFailHTLC propagated backwards in // the route to fully undo the HTLC. type UpdateFailHTLC struct { - // ChannelPoint is the particular active channel that this - // UpdateFailHTLC is bound to. + // ChanIDPoint is the particular active channel that this UpdateFailHTLC + // is bound to. ChanID ChannelID // ID references which HTLC on the remote node's commitment transaction @@ -106,7 +23,6 @@ type UpdateFailHTLC struct { // Reason is an onion-encrypted blob that details why the HTLC was // failed. This blob is only fully decryptable by the initiator of the // HTLC message. - // TODO(roasbeef): properly format the encrypted failure reason Reason OpaqueReason } @@ -151,6 +67,19 @@ func (c *UpdateFailHTLC) MsgType() MessageType { // // This is part of the lnwire.Message interface. func (c *UpdateFailHTLC) MaxPayloadLength(uint32) uint32 { - // 32 + 8 + 154 - return 194 + var length uint32 + + // Length of the ChanID + length += 32 + + // Length of the ID + length += 8 + + // Length of the length opaque reason + length += 2 + + // Length of the Reason + length += 166 + + return length }