From a04fa76a4c4fe06e07f5bc5020c32fb7591f85e7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 14 Jul 2017 20:08:29 -0700 Subject: [PATCH] lnwire+htlcswitch: minor grammatical, formatting fixes after error PR --- htlcswitch/circuit.go | 4 +- htlcswitch/failure.go | 60 +++++++------ htlcswitch/iterator.go | 9 +- htlcswitch/link.go | 121 +++++++++++++++++--------- htlcswitch/packet.go | 23 +++-- htlcswitch/switch.go | 49 ++++++++--- lnwire/onion_error.go | 191 +++++++++++++++++++---------------------- server.go | 11 +-- 8 files changed, 265 insertions(+), 203 deletions(-) diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index bcebc7a16..2feb06019 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -20,7 +20,7 @@ func (k *circuitKey) String() string { } // paymentCircuit is used by the htlc switch subsystem to determine the -// fowrards/backwards path for the settle/fail HTLC messages. A payment circuit +// forwards/backwards path for the settle/fail HTLC messages. A payment circuit // will be created once a channel link forwards the htlc add request and // removed when we receive settle/fail htlc message. type paymentCircuit struct { @@ -38,7 +38,7 @@ type paymentCircuit struct { // request back. Dest lnwire.ShortChannelID - // Obfuscator is used to obfuscate the onion failure before sending it + // Obfuscator is used to re-encrypt the onion failure before sending it // back to the originator of the payment. Obfuscator Obfuscator diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index 82e3be45c..7784e51b4 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -7,22 +7,31 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -// Deobfuscator entity which is used to de-obfuscate the onion opaque reason and -// extract failure. +// Deobfuscator is an interface that is used to decrypt the onion encrypted +// failure reason an extra out a well formed error. type Deobfuscator interface { - // Deobfuscate function decodes the onion error failure. + // Deobfuscate peels off each layer of onion encryption from the first + // hop, to the source of the error. A fully populated + // lnwire.FailureMessage is returned. Deobfuscate(lnwire.OpaqueReason) (lnwire.FailureMessage, error) } -// Obfuscator entity which is used to do the initial and backward onion -// failure obfuscation. +// Obfuscator is an interface that is used to encrypt HTLC related errors at +// the source of the error, and also at each intermediate hop all the way back +// to the source of the payment. type Obfuscator interface { // InitialObfuscate is used to convert the failure into opaque // reason. + + // InitialObfuscate transforms a concrete failure message into an + // encrypted opaque failure reason. This method will be used at the + // source that the error occurs. It differs from BackwardObfuscate + // slightly, in that it computes a proper MAC over the error. InitialObfuscate(lnwire.FailureMessage) (lnwire.OpaqueReason, error) - // BackwardObfuscate is used to make the processing over onion error - // when it moves backward to the htlc sender. + // BackwardObfuscate wraps an already encrypted opaque reason error in + // an additional layer of onion encryption. This process repeats until + // the error arrives at the source of the payment. BackwardObfuscate(lnwire.OpaqueReason) lnwire.OpaqueReason } @@ -31,35 +40,36 @@ type FailureObfuscator struct { *sphinx.OnionObfuscator } -// InitialObfuscate is used by the failure sender to decode the failure and -// make the initial failure obfuscation with addition of the failure data hmac. +// InitialObfuscate transforms a concrete failure message into an encrypted +// opaque failure reason. This method will be used at the source that the error +// occurs. It differs from BackwardObfuscate slightly, in that it computes a +// proper MAC over the error. // // NOTE: Part of the Obfuscator interface. -func (o *FailureObfuscator) InitialObfuscate(failure lnwire.FailureMessage) ( - lnwire.OpaqueReason, error) { +func (o *FailureObfuscator) InitialObfuscate(failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { var b bytes.Buffer if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { return nil, err } - // Make the initial obfuscation with appending hmac. + // We pass a true as the first parameter to indicate that a MAC should + // be added. return o.OnionObfuscator.Obfuscate(true, b.Bytes()), nil } -// BackwardObfuscate is used by the forwarding nodes in order to obfuscate the -// already obfuscated onion failure blob with the stream which have been -// generated with our shared secret. The reason we re-encrypt the message on the -// backwards path is to ensure that the error is computationally -// indistinguishable from any other error seen. +// BackwardObfuscate wraps an already encrypted opaque reason error in an +// additional layer of onion encryption. This process repeats until the error +// arrives at the source of the payment. We re-encrypt the message on the +// backwards path to ensure that the error is indistinguishable from any other +// error seen. // // NOTE: Part of the Obfuscator interface. -func (o *FailureObfuscator) BackwardObfuscate( - reason lnwire.OpaqueReason) lnwire.OpaqueReason { +func (o *FailureObfuscator) BackwardObfuscate(reason lnwire.OpaqueReason) lnwire.OpaqueReason { return o.OnionObfuscator.Obfuscate(false, reason) } -// A compile time check to ensure FailureObfuscator implements the -// Obfuscator interface. +// A compile time check to ensure FailureObfuscator implements the Obfuscator +// interface. var _ Obfuscator = (*FailureObfuscator)(nil) // FailureDeobfuscator wraps the sphinx data obfuscator and adds awareness of @@ -68,11 +78,13 @@ type FailureDeobfuscator struct { *sphinx.OnionDeobfuscator } -// Deobfuscate decodes the obfuscated onion failure. +// Deobfuscate peels off each layer of onion encryption from the first hop, to +// the source of the error. A fully populated lnwire.FailureMessage is +// returned. // // NOTE: Part of the Obfuscator interface. -func (o *FailureDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage, - error) { +func (o *FailureDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage, error) { + _, failureData, err := o.OnionDeobfuscator.Deobfuscate(reason) if err != nil { return nil, err diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index 628f0e34f..0db989ea3 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -191,10 +191,11 @@ func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte) (HopIterat }, lnwire.CodeNone } -// DecodeOnionObfuscator takes the onion blob as input extract the shared secret -// and return the entity which is able to obfuscate failure data. -func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator, - lnwire.FailCode) { +// DecodeOnionObfuscator takes an io.Reader which should contain the onion +// packet as original received by a forwarding node and creates an Obfuscator +// instance using the derived shared secret. In the case that en error occurs, +// a lnwire failure code detailing the parsing failure will be returned. +func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator, lnwire.FailCode) { onionPkt := &sphinx.OnionPacket{} if err := onionPkt.Decode(r); err != nil { return nil, lnwire.CodeTemporaryChannelFailure diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 554dfa104..61d1ce76e 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -83,17 +83,18 @@ type ChannelLinkConfig struct { // contained in the forwarding blob within each HTLC. Switch *Switch - // DecodeHopIterator function is responsible for decoding htlc Sphinx onion - // blob, and creating hop iterator which will give us next destination - // of htlc. + // DecodeHopIterator function is responsible for decoding htlc Sphinx + // onion blob, and creating hop iterator which will give us next + // destination of htlc. DecodeHopIterator func(r io.Reader, rHash []byte) (HopIterator, lnwire.FailCode) - // DecodeOnionObfuscator function is responsible for decoding htlc Sphinx - // onion blob, and creating onion failure obfuscator. + // DecodeOnionObfuscator function is responsible for decoding htlc + // Sphinx onion blob, and creating onion failure obfuscator. DecodeOnionObfuscator func(r io.Reader) (Obfuscator, lnwire.FailCode) - // GetLastChannelUpdate retrieve the topology info about the channel in - // order to create the channel update. + // GetLastChannelUpdate reterives the latest routing policy for this + // particualr channel. This will be used to provide payment senders our + // laest policy when sending encrypted error messages. GetLastChannelUpdate func() (*lnwire.ChannelUpdate, error) // Peer is a lightning network node with which we have the channel link @@ -419,6 +420,9 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) { index, err := l.channel.AddHTLC(htlc) if err != nil { switch err { + + // The channels spare bandwidth is fully allocated, so + // we'll put this HTLC into the overflow queue. case lnwallet.ErrMaxHTLCNumber: log.Infof("Downstream htlc add update with "+ "payment hash(%x) have been added to "+ @@ -428,19 +432,26 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) { l.overflowQueue.consume(pkt) return + // The HTLC was unable to be added to the state + // machine, as a result, we'll signal the switch to + // cancel the pending payment. default: - // The HTLC was unable to be added to the state - // machine, as a result, we'll signal the switch to - // cancel the pending payment. var ( isObfuscated bool reason lnwire.OpaqueReason ) + // We'll parse the sphinx packet enclosed so we + // can obtain the shared secret required to + // encrypt the error back to the source. failure := lnwire.NewTemporaryChannelFailure(nil) onionReader := bytes.NewReader(htlc.OnionBlob[:]) obfuscator, failCode := l.cfg.DecodeOnionObfuscator(onionReader) - if failCode != lnwire.CodeNone { + + switch { + // If we were unable to parse the onion blob, + // then we'll send an error back to the source. + case failCode != lnwire.CodeNone: var b bytes.Buffer err := lnwire.EncodeFailure(&b, failure, 0) if err != nil { @@ -449,7 +460,10 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) { } reason = lnwire.OpaqueReason(b.Bytes()) isObfuscated = false - } else { + + // Otherwise, we'll send back a proper failure + // message. + default: reason, err = obfuscator.InitialObfuscate(failure) if err != nil { log.Errorf("unable to obfuscate error: %v", err) @@ -458,13 +472,16 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket) { isObfuscated = true } - go l.cfg.Switch.forward(newFailPacket( - l.ShortChanID(), - &lnwire.UpdateFailHTLC{ - Reason: reason, - }, htlc.PaymentHash, htlc.Amount, isObfuscated, - )) + upddateFail := &lnwire.UpdateFailHTLC{ + Reason: reason, + } + failPkt := newFailPacket( + l.ShortChanID(), upddateFail, + htlc.PaymentHash, htlc.Amount, + isObfuscated, + ) + go l.cfg.Switch.forward(failPkt) log.Infof("Unable to handle downstream add HTLC: %v", err) return } @@ -568,16 +585,18 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { // TODO(roasbeef): add preimage to DB in order to swipe // repeated r-values - // If remote side have been unable to parse the onion blob we have sent - // to it, than we should transform the malformed notification to the the - // usual htlc fail message. case *lnwire.UpdateFailMalformedHTLC: + // If remote side have been unable to parse the onion blob we + // have sent to it, than we should transform the malformed HTLC + // message to the usual HTLC fail message. idx := msg.ID if err := l.channel.ReceiveFailHTLC(idx); err != nil { l.fail("unable to handle upstream fail HTLC: %v", err) return } + // Convert the failure type encoded within the HTLC fail + // message to the proper generic lnwire error code. var failure lnwire.FailureMessage switch msg.FailureCode { case lnwire.CodeInvalidOnionVersion: @@ -594,11 +613,14 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) { OnionSHA256: msg.ShaOnionBlob, } default: + // TODO(roasbeef): fail channel here? log.Errorf("unable to understand code of received " + "malformed error") return } + // With the error parsed, we'll convert the into it's opaque + // form. var b bytes.Buffer if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { log.Errorf("unable to encode malformed error: %v", err) @@ -943,7 +965,7 @@ func (l *channelLink) processLockedInHtlcs( // An incoming HTLC add has been full-locked in. As a result we // can no examine the forwarding details of the HTLC, and the - // HTLC itself ti decide if: we should forward it, cancel it, + // HTLC itself to decide if: we should forward it, cancel it, // or are able to settle it (and it adheres to our fee related // constraints). case lnwallet.Add: @@ -952,23 +974,26 @@ func (l *channelLink) processLockedInHtlcs( onionBlob := l.clearedOnionBlobs[pd.Index] delete(l.clearedOnionBlobs, pd.Index) - // Retrieve onion obfuscator from onion blob in order to produce - // initial obfuscation of the onion failureCode. + // Retrieve onion obfuscator from onion blob in order + // to produce initial obfuscation of the onion + // failureCode. onionReader := bytes.NewReader(onionBlob[:]) obfuscator, failureCode := l.cfg.DecodeOnionObfuscator(onionReader) if failureCode != lnwire.CodeNone { - log.Error("unable to decode onion obfuscator") - // If we unable to process the onion blob than we should send - // the malformed htlc error to payment sender. + // If we unable to process the onion blob than + // we should send the malformed htlc error to + // payment sender. l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:]) needUpdate = true + + log.Error("unable to decode onion obfuscator") continue } // Before adding the new htlc to the state machine, // parse the onion object in order to obtain the - // routing information with DecodeHopIterator function which - // process the Sphinx packet. + // routing information with DecodeHopIterator function + // which process the Sphinx packet. // // We include the payment hash of the htlc as it's // authenticated within the Sphinx packet itself as @@ -979,11 +1004,13 @@ func (l *channelLink) processLockedInHtlcs( onionReader = bytes.NewReader(onionBlob[:]) chanIterator, failureCode := l.cfg.DecodeHopIterator(onionReader, pd.RHash[:]) if failureCode != lnwire.CodeNone { - log.Error("unable to decode onion hop iterator") - // If we unable to process the onion blob than we should send - // the malformed htlc error to payment sender. + // If we unable to process the onion blob than + // we should send the malformed htlc error to + // payment sender. l.sendMalformedHTLCError(pd.RHash, failureCode, onionBlob[:]) needUpdate = true + + log.Error("unable to decode onion hop iterator") continue } @@ -1009,9 +1036,7 @@ func (l *channelLink) processLockedInHtlcs( // the hop-payload included in the HTLC to // ensure that it was crafted correctly by the // sender and matches the HTLC we were - // extended. Additionally, we'll ensure that - // our time-lock value has been computed - // correctly. + // extended. if !l.cfg.DebugHTLC && fwdInfo.AmountToForward != invoice.Terms.Value { @@ -1026,6 +1051,9 @@ func (l *channelLink) processLockedInHtlcs( needUpdate = true continue } + + // We'll also ensure that our time-lock value + // has been computed correctly. if !l.cfg.DebugHTLC && fwdInfo.OutgoingCTLV != l.cfg.FwrdingPolicy.TimeLockDelta { @@ -1096,6 +1124,10 @@ func (l *channelLink) processLockedInHtlcs( pd.RHash[:], l.cfg.FwrdingPolicy.MinHTLC, pd.Amount) + // As part of the returned error, we'll + // send our latest routing policy so + // the sending node obtains the most up + // to date data. var failure lnwire.FailureMessage update, err := l.cfg.GetLastChannelUpdate() if err != nil { @@ -1134,6 +1166,10 @@ func (l *channelLink) processLockedInHtlcs( btcutil.Amount(pd.Amount-fwdInfo.AmountToForward), btcutil.Amount(expectedFee)) + // As part of the returned error, we'll + // send our latest routing policy so + // the sending node obtains the most up + // to date data. var failure lnwire.FailureMessage update, err := l.cfg.GetLastChannelUpdate() if err != nil { @@ -1163,6 +1199,9 @@ func (l *channelLink) processLockedInHtlcs( pd.RHash[:], pd.Timeout-timeDelta, fwdInfo.OutgoingCTLV) + // Grab the latest routing policy so + // the sending node is up to date with + // our current policy. update, err := l.cfg.GetLastChannelUpdate() if err != nil { l.fail("unable to create channel update "+ @@ -1211,7 +1250,7 @@ func (l *channelLink) processLockedInHtlcs( if needUpdate { // With all the settle/cancel updates added to the local and - // remote htlc logs, initiate a state transition by updating + // remote HTLC logs, initiate a state transition by updating // the remote commitment chain. if err := l.updateCommitTx(); err != nil { l.fail("unable to update commitment: %v", err) @@ -1222,8 +1261,8 @@ func (l *channelLink) processLockedInHtlcs( return packetsToForward } -// sendHTLCError functions cancels htlc and send cancel message back to the -// peer from which htlc was received. +// sendHTLCError functions cancels HTLC and send cancel message back to the +// peer from which HTLC was received. func (l *channelLink) sendHTLCError(rHash [32]byte, failure lnwire.FailureMessage, obfuscator Obfuscator) { reason, err := obfuscator.InitialObfuscate(failure) @@ -1245,7 +1284,7 @@ func (l *channelLink) sendHTLCError(rHash [32]byte, failure lnwire.FailureMessag }) } -// sendMalformedHTLCError helper function which sends the malformed htlc update +// sendMalformedHTLCError helper function which sends the malformed HTLC update // to the payment sender. func (l *channelLink) sendMalformedHTLCError(rHash [32]byte, code lnwire.FailCode, onionBlob []byte) { @@ -1263,8 +1302,8 @@ func (l *channelLink) sendMalformedHTLCError(rHash [32]byte, code lnwire.FailCod }) } -// fail helper function which is used to encapsulate the action neccessary -// for proper disconnect. +// fail helper function which is used to encapsulate the action necessary for +// proper disconnect. func (l *channelLink) fail(format string, a ...interface{}) { reason := errors.Errorf(format, a...) log.Error(reason) diff --git a/htlcswitch/packet.go b/htlcswitch/packet.go index 68302bcbe..e93f477cb 100644 --- a/htlcswitch/packet.go +++ b/htlcswitch/packet.go @@ -35,20 +35,19 @@ type htlcPacket struct { // htlc lnwire message type of which depends on switch request type. htlc lnwire.Message - // obfuscator is entity which is needed to make the obfuscation of the - // onion failure, it is carried inside the packet from channel - // link to the switch because we have to create onion error inside the - // switch to, but we unable to restore obfuscator from the onion, because - // on stage of forwarding onion inside payment belongs to the remote node. - // TODO(andrew.shvv) revisit after refactoring the way of returning errors - // inside the htlcswitch packet. + // obfuscator contains the necessary state to allow the switch to wrap + // any forwarded errors in an additional layer of encryption. + // + // TODO(andrew.shvv) revisit after refactoring the way of returning + // errors inside the htlcswitch packet. obfuscator Obfuscator - // isObfuscated is used in case if switch sent the packet to the link, - // but error have occurred locally, in this case we shouldn't obfuscate - // it again. - // TODO(andrew.shvv) revisit after refactoring the way of returning errors - // inside the htlcswitch packet. + // isObfuscated is set to true if an error occurs as soon as the switch + // forwards a packet to the link. If so, and this is an error packet, + // then this allows the switch to avoid doubly encrypting the error. + // + // TODO(andrew.shvv) revisit after refactoring the way of returning + // errors inside the htlcswitch packet. isObfuscated bool } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index dacf73539..ad15b2284 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -304,23 +304,27 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket payment.preimage <- htlc.PaymentPreimage s.removePendingPayment(payment.amount, payment.paymentHash) - // We've just received a fail update which means we can finalize - // the user payment and return fail response. + // We've just received a fail update which means we can finalize the + // user payment and return fail response. case *lnwire.UpdateFailHTLC: - // Retrieving the fail code from byte representation of error. var userErr error + // We'll attempt to fully decrypt the onion encrypted error. If + // we're unable to then we'll bail early. failure, err := payment.deobfuscator.Deobfuscate(htlc.Reason) if err != nil { userErr = errors.Errorf("unable to de-obfuscate "+ - "onion failure, htlc with hash(%v): %v", payment.paymentHash[:], - err) + "onion failure, htlc with hash(%v): %v", + payment.paymentHash[:], err) log.Error(userErr) } else { - // Process payment failure by updating the lightning network - // topology by using router subsystem handler. + // Process payment failure by updating the lightning + // network topology by using router subsystem handler. var update *lnwire.ChannelUpdate + // Only a few error message actually contain a channel + // update message, so we'll filter out for those that + // do. switch failure := failure.(type) { case *lnwire.FailTemporaryChannelFailure: update = failure.Update @@ -336,10 +340,21 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket update = &failure.Update } + // If we've been sent an error that includes an update, + // then we'll apply it to the local graph. + // + // TODO(roasbeef): instead, make all onion errors the + // error interface, and handle this within the router. + // Will allow us more flexibility w.r.t how we handle + // the error. if update != nil { - log.Info("Received payment failure(%v), applying lightning "+ - "network topology update", failure.Code()) - s.cfg.UpdateTopology(update) + log.Info("Received payment failure(%v), "+ + "applying lightning network topology update", + failure.Code()) + + if err := s.cfg.UpdateTopology(update); err != nil { + log.Errorf("unable to update topology: %v", err) + } } userErr = errors.New(failure.Code()) @@ -611,15 +626,23 @@ func (s *Switch) htlcForwarder() { for { select { + // A local close request has arrived, we'll forward this to the + // relevant link (if it exists) so the channel can be + // cooperatively closed (if possible). case req := <-s.chanCloseRequests: s.handleChanelClose(req) + // A new packet has arrived for forwarding, we'll interpret the + // packet concretely, then either forward it along, or + // interpret a return packet to a locally initialized one. case cmd := <-s.htlcPlex: var ( paymentHash lnwallet.PaymentHash amount btcutil.Amount ) + // Only three types of message should be forwarded: + // add, fails, and settles. Anything else is an error. switch m := cmd.pkt.htlc.(type) { case *lnwire.UpdateAddHTLC: paymentHash = m.PaymentHash @@ -632,6 +655,12 @@ func (s *Switch) htlcForwarder() { return } + // If we can locate this packet in our local records, + // then this means a local sub-system initiated it. + // Otherwise, this is just a packet to be forwarded, so + // we'll treat it as so. + // + // TODO(roasbeef): can fast path this payment, err := s.findPayment(amount, paymentHash) if err != nil { cmd.err <- s.handlePacketForward(cmd.pkt) diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index f28521233..4f87ad4a2 100644 --- a/lnwire/onion_error.go +++ b/lnwire/onion_error.go @@ -10,34 +10,36 @@ import ( "github.com/roasbeef/btcutil" ) -// FailureMessage represent the onion failure object which is able to identify itself -// by returning the unique failure code. +// FailureMessage represents the onion failure object identified by its 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. +// failureMessageLength is the size of the failure message plus the size of +// padding. The FailureMessage message should always be EXACLTY this size. const failureMessageLength = 128 const ( - // FlagBadOnion error flag denotes unparsable onion, encrypted by + // FlagBadOnion error flag describes an unparseable, encrypted by // previous node. FlagBadOnion FailCode = 0x8000 - // FlagPerm error flag denotes permanent failure. + // FlagPerm error flag indicates a permanent failure. FlagPerm FailCode = 0x4000 - // FlagNode error flag denotes node failure. + // FlagNode error flag indicates anode failure. FlagNode FailCode = 0x2000 - // FlagUpdate error flag denotes new channel update enclosed. + // FlagUpdate error flag indicates a new channel update is enclosed + // within the error. 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. +// Each UpdateFailHTLC message carries a FailCode which is to be passed +// backwards, encrypted at each step back to the source of the HTLC within the +// route. type FailCode uint16 // The currently defined onion failure types within this current version of the @@ -67,7 +69,7 @@ const ( CodeFinalIncorrectHtlcAmount FailCode = 19 ) -// String returns string representation of the failure code. +// String returns the string representation of the failure code. func (c FailCode) String() string { switch c { case CodeInvalidRealm: @@ -140,7 +142,7 @@ func (c FailCode) String() string { // FailInvalidRealm is returned if the realm byte is unknown. // -// NOTE: might be returned by any node. +// NOTE: May be returned by any node in the payment route. type FailInvalidRealm struct{} // Code returns the failure unique code. @@ -153,7 +155,7 @@ func (f FailInvalidRealm) Code() FailCode { // FailTemporaryNodeFailure is returned if an otherwise unspecified transient // error occurs for the entire node. // -// NOTE: might be returned by any node. +// NOTE: May be returned by any node in the payment route. type FailTemporaryNodeFailure struct{} // Code returns the failure unique code. @@ -165,7 +167,7 @@ func (f FailTemporaryNodeFailure) Code() FailCode { // FailPermanentNodeFailure is returned if an otherwise unspecified permanent // error occurs for the entire node. // -// NOTE: might be returned by any node. +// NOTE: May be returned by any node in the payment route. type FailPermanentNodeFailure struct{} // Code returns the failure unique code. @@ -179,7 +181,7 @@ func (f FailPermanentNodeFailure) Code() FailCode { // advertised in its node_announcement features which were not present in the // onion. // -// NOTE: might be returned by any node. +// NOTE: May be returned by any node in the payment route. type FailRequiredNodeFeatureMissing struct{} // Code returns the failure unique code. @@ -189,10 +191,10 @@ func (f FailRequiredNodeFeatureMissing) Code() FailCode { return CodeRequiredNodeFeatureMissing } -// FailPermanentChannelFailure is return if an otherwise unspecified -// permanent error occurs for the outgoing channel (eg. channel (recently). +// 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. +// NOTE: May be returned by any node in the payment route. type FailPermanentChannelFailure struct{} // Code returns the failure unique code. @@ -202,11 +204,11 @@ func (f FailPermanentChannelFailure) Code() FailCode { return CodePermanentChannelFailure } -// FailRequiredChannelFeatureMissing is return if the outgoing channel has +// FailRequiredChannelFeatureMissing is returned if the outgoing channel has a // requirement advertised in its channel announcement features which were not // present in the onion. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailRequiredChannelFeatureMissing struct{} // Code returns the failure unique code. @@ -219,7 +221,7 @@ func (f FailRequiredChannelFeatureMissing) Code() FailCode { // FailUnknownNextPeer is returned if the next peer specified by the onion is // not known. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailUnknownNextPeer struct{} // Code returns the failure unique code. @@ -229,12 +231,12 @@ 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. +// 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. +// NOTE: May only be returned by the final node in the path. type FailUnknownPaymentHash struct{} // Code returns the failure unique code. @@ -250,7 +252,7 @@ func (f FailUnknownPaymentHash) Code() FailCode { // 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. +// NOTE: May only be returned by the final node in the path. type FailIncorrectPaymentAmount struct{} // Code returns the failure unique code. @@ -263,7 +265,7 @@ func (f FailIncorrectPaymentAmount) Code() FailCode { // 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. +// NOTE: May only be returned by the final node in the path. type FailFinalExpiryTooSoon struct{} // Code returns the failure unique code. @@ -275,7 +277,7 @@ func (f FailFinalExpiryTooSoon) Code() FailCode { // FailInvalidOnionVersion is returned if the onion version byte is unknown. // -// NOTE: should be return by forwarding node only. +// NOTE: May be returned only by intermediate nodes. type FailInvalidOnionVersion struct { // OnionSHA256 hash of the onion blob which haven't been proceeded. OnionSHA256 [sha256.Size]byte @@ -309,7 +311,7 @@ func (f *FailInvalidOnionVersion) Encode(w io.Writer, pver uint32) error { // FailInvalidOnionHmac is return if the onion HMAC is incorrect. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailInvalidOnionHmac struct { // OnionSHA256 hash of the onion blob which haven't been proceeded. OnionSHA256 [sha256.Size]byte @@ -344,7 +346,7 @@ func (f *FailInvalidOnionHmac) Encode(w io.Writer, pver uint32) error { // FailInvalidOnionKey is return if the ephemeral key in the onion is // unparsable. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailInvalidOnionKey struct { // OnionSHA256 hash of the onion blob which haven't been proceeded. OnionSHA256 [sha256.Size]byte @@ -376,16 +378,16 @@ 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) +// FailTemporaryChannelFailure is if an otherwise unspecified transient error +// occurs for the outgoing channel (eg. channel capacity reached, too many +// in-flight htlcs) // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailTemporaryChannelFailure struct { - // Update is used to update information about state of the channel which - // caused the failure. + // Update is used to update information about state of the channel + // which caused the failure. // - // NOTE: Field is optional. + // NOTE: This field is optional. Update *ChannelUpdate } @@ -439,11 +441,11 @@ func (f *FailTemporaryChannelFailure) Encode(w io.Writer, pver uint32) error { 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. +// 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. +// NOTE: May only be returned by the intermediate nodes in the path. type FailAmountBelowMinimum struct { // HtlcMsat is the wrong amount of the incoming HTLC. HtlcMsat btcutil.Amount @@ -456,6 +458,7 @@ type FailAmountBelowMinimum struct { // NewAmountBelowMinimum creates new instance of the FailAmountBelowMinimum. func NewAmountBelowMinimum(htlcMsat btcutil.Amount, update ChannelUpdate) *FailAmountBelowMinimum { + return &FailAmountBelowMinimum{ HtlcMsat: htlcMsat, Update: update, @@ -477,8 +480,6 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error { 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 @@ -496,9 +497,6 @@ func (f *FailAmountBelowMinimum) Encode(w io.Writer, pver uint32) error { 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 @@ -507,17 +505,17 @@ func (f *FailAmountBelowMinimum) Encode(w io.Writer, pver uint32) error { 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. +// 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. +// NOTE: May only be returned by intermediate nodes. 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 is used to update information about state of the channel + // which caused the failure. Update ChannelUpdate } @@ -545,8 +543,6 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error { 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 @@ -564,9 +560,6 @@ func (f *FailFeeInsufficient) Encode(w io.Writer, pver uint32) error { 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 @@ -575,24 +568,26 @@ func (f *FailFeeInsufficient) Encode(w io.Writer, pver uint32) error { 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. +// 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. +// NOTE: May only be returned by intermediate nodes. type FailIncorrectCltvExpiry struct { - // CltvExpiry is the wrong absolute timeout in blocks, after which outgoing - // HTLC expires. + // 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 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 { +func NewIncorrectCltvExpiry(cltvExpiry uint32, + update ChannelUpdate) *FailIncorrectCltvExpiry { + return &FailIncorrectCltvExpiry{ CltvExpiry: cltvExpiry, Update: update, @@ -614,8 +609,6 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error { 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 @@ -633,9 +626,6 @@ func (f *FailIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error { 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 @@ -644,17 +634,17 @@ func (f *FailIncorrectCltvExpiry) Encode(w io.Writer, pver uint32) error { 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. +// FailExpiryTooSoon is returned if the ctlv-expiry is too near, we tell them +// the current channel setting for the outgoing channel. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. type FailExpiryTooSoon struct { - // Update is used to update information about state of the channel which - // caused the failure. + // Update is used to update information about state of the channel + // which caused the failure. Update ChannelUpdate } -// NewExpiryTooSoon creates new instance of the FailExpiryTooSoon +// NewExpiryTooSoon creates new instance of the FailExpiryTooSoon. func NewExpiryTooSoon(update ChannelUpdate) *FailExpiryTooSoon { return &FailExpiryTooSoon{ Update: update, @@ -672,8 +662,6 @@ func (f *FailExpiryTooSoon) Code() FailCode { // // 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 @@ -687,9 +675,6 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error { // // 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 @@ -698,18 +683,18 @@ func (f *FailExpiryTooSoon) Encode(w io.Writer, pver uint32) error { 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. +// FailChannelDisabled is returned if the channel is disabled, we tell them the +// current channel setting for the outgoing channel. // -// NOTE: might be return by forwarding node only. +// NOTE: May only be returned by intermediate nodes. 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 is used to update information about state of the channel + // which caused the failure. Update ChannelUpdate } @@ -736,8 +721,6 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error { 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 @@ -755,9 +738,6 @@ func (f *FailChannelDisabled) Encode(w io.Writer, pver uint32) error { 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 @@ -771,8 +751,8 @@ func (f *FailChannelDisabled) Encode(w io.Writer, pver uint32) error { // // 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 is the wrong absolute timeout in blocks, after which + // outgoing HTLC expires. CltvExpiry uint32 } @@ -805,10 +785,10 @@ 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. +// 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. +// NOTE: May only be returned by the final node. type FailFinalIncorrectHtlcAmount struct { // IncomingHTLCAmount is the wrong forwarded htlc amount. IncomingHTLCAmount btcutil.Amount @@ -843,8 +823,8 @@ 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. +// 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 @@ -852,14 +832,14 @@ func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) { return nil, err } - // Create the empty failure by given code and populate the failure - // with additional data if needed. + // 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 + // Read the 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 { @@ -885,7 +865,8 @@ func DecodeFailure(r io.Reader, pver uint32) (FailureMessage, error) { return failure, nil } -// EncodeFailure encodes, including the necessary onion failure header information. +// EncodeFailure encodes, including the necessary onion failure header +// information. func EncodeFailure(w io.Writer, failure FailureMessage, pver uint32) error { var failureMessageBuffer bytes.Buffer diff --git a/server.go b/server.go index e8ab4ac82..65e0b8a04 100644 --- a/server.go +++ b/server.go @@ -244,16 +244,17 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl, htlcAdd *lnwire.UpdateAddHTLC, circuit *sphinx.Circuit) ([32]byte, error) { - // Initialize the data obfuscator in order to be able to decode the - // onion failure and wrap it so that we could process lnwire onion - // failures. - failureDeobfuscator := &htlcswitch.FailureDeobfuscator{ + // Using the created circuit, initialize the error + // decryptor so we can parse+decode any failures + // incurred by this payment within the switch. + errorDecryptor := &htlcswitch.FailureDeobfuscator{ OnionDeobfuscator: sphinx.NewOnionDeobfuscator(circuit), } var firstHopPub [33]byte copy(firstHopPub[:], firstHop.SerializeCompressed()) - return s.htlcSwitch.SendHTLC(firstHopPub, htlcAdd, failureDeobfuscator) + + return s.htlcSwitch.SendHTLC(firstHopPub, htlcAdd, errorDecryptor) }, }) if err != nil {