Skip to content

Return "Temporary Channel Failure" for offline peer #6803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/release-notes/release-notes-0.15.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
* [Re-initialise registered middleware index lookup map after removal of a
registered middleware](https://github.com/lightningnetwork/lnd/pull/6739)

* [Only return permanent "Unknown next peer" error if peer/channel is
unknown](https://github.com/lightningnetwork/lnd/pull/6803)

## Code Health

### Code cleanup, refactor, typo fixes
Expand Down
6 changes: 3 additions & 3 deletions htlcswitch/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ func (s *Switch) getLocalLink(pkt *htlcPacket, htlc *lnwire.UpdateAddHTLC) (
// If the link was not found for the outgoingChanID, an outside
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic

lock indexMtx
try to get LinkByShortID
if fails check baseIndex
retry to get LinkBysShortID with the new scid

is similar to the one in GetLinkByShortID I think we could simply call that function here.

// subsystem may be using the confirmed SCID of a zero-conf
// channel. In this case, we'll consult the Switch maps to see
// if an alias exists and use the alias to lookup the link.
// if an alias exists and use the alias to look up the link.
// This extra step is a consequence of not updating the Switch
// forwardingIndex when a zero-conf channel is confirmed. We
// don't need to change the outgoingChanID since the link will
Expand All @@ -867,7 +867,7 @@ func (s *Switch) getLocalLink(pkt *htlcPacket, htlc *lnwire.UpdateAddHTLC) (
link, err = s.getLinkByShortID(baseScid)
if err != nil {
log.Errorf("Link %v not found", baseScid)
return nil, NewLinkError(&lnwire.FailUnknownNextPeer{})
return nil, NewLinkError(&lnwire.FailTemporaryChannelFailure{})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this also apply to non zero conf channels? Are we currently sending FailTemporaryChannelFailure instead of FailUnknownNextPeer for those?

}
}

Expand Down Expand Up @@ -1125,7 +1125,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
"destination %v", packet.outgoingChanID)

// If packet was forwarded from another channel link
// than we should notify this link that some error
// then we should notify this link that some error
// occurred.
linkError := NewLinkError(
&lnwire.FailUnknownNextPeer{},
Expand Down
74 changes: 62 additions & 12 deletions htlcswitch/switch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ import (
var zeroCircuit = channeldb.CircuitKey{}
var emptyScid = lnwire.ShortChannelID{}

type SuccessOrFailureOption string

const (
Success SuccessOrFailureOption = "success"
PeerOffline = "peerOffline"
UnknownChannel = "unknownChannel"
)

var SuccessOrFailureOptions = []SuccessOrFailureOption{
Success,
PeerOffline,
UnknownChannel,
}

func genPreimage() ([32]byte, error) {
var preimage [32]byte
if _, err := io.ReadFull(rand.Reader, preimage[:]); err != nil {
Expand Down Expand Up @@ -656,22 +670,40 @@ func TestSwitchSendHTLCMapping(t *testing.T) {
TxPosition: 45,
},
},
{
name: "zero-conf unknown scid",
zeroConf: true,
useAlias: false,
alias: lnwire.ShortChannelID{
BlockHeight: 10035,
TxIndex: 35,
TxPosition: 35,
},
real: lnwire.ShortChannelID{
BlockHeight: 470000,
TxIndex: 35,
TxPosition: 45,
},
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
testSwitchSendHtlcMapping(
t, test.zeroConf, test.useAlias, test.alias,
test.real, test.optionFeature,
)
})
for _, successOrFailure := range SuccessOrFailureOptions {
for _, test := range tests {
test := test
testName := fmt.Sprintf("%s[%s]", test.name, successOrFailure)
t.Run(testName, func(t *testing.T) {
testSwitchSendHtlcMapping(
t, test.zeroConf, test.useAlias, test.alias,
test.real, test.optionFeature, successOrFailure,
)
})
}
}
}

func testSwitchSendHtlcMapping(t *testing.T, zeroConf, useAlias bool, alias,
realScid lnwire.ShortChannelID, optionFeature bool) {
realScid lnwire.ShortChannelID, optionFeature bool,
successOrFailureOption SuccessOrFailureOption) {

peer, err := newMockServer(
t, "alice", testStartingHeight, nil, testDefaultDelta,
Expand Down Expand Up @@ -708,6 +740,10 @@ func testSwitchSendHtlcMapping(t *testing.T, zeroConf, useAlias bool, alias,
err = s.AddLink(link)
require.NoError(t, err)

if successOrFailureOption == PeerOffline {
s.RemoveLink(link.chanID)
}

// Generate preimage.
preimage, err := genPreimage()
require.NoError(t, err)
Expand All @@ -718,15 +754,29 @@ func testSwitchSendHtlcMapping(t *testing.T, zeroConf, useAlias bool, alias,
if useAlias {
outgoingSCID = alias
}
if successOrFailureOption == UnknownChannel {
outgoingSCID = lnwire.ShortChannelID{
BlockHeight: 123,
TxIndex: 123,
TxPosition: 123,
}
}

// Send the HTLC and assert that we don't get an error.
htlc := &lnwire.UpdateAddHTLC{
PaymentHash: rhash,
Amount: 1,
}

err = s.SendHTLC(outgoingSCID, 0, htlc)
require.NoError(t, err)

switch successOrFailureOption {
case Success:
require.NoError(t, err)
case PeerOffline:
require.EqualError(t, err, "TemporaryChannelFailure")
case UnknownChannel:
require.EqualError(t, err, "UnknownNextPeer")
}
}

// TestSwitchUpdateScid verifies that zero-conf and non-zero-conf
Expand Down
6 changes: 3 additions & 3 deletions routing/result_interpretation.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,10 +307,10 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(

// If a node reports onion payload corruption or an invalid version,
// that node may be responsible, but it could also be that it is just
// relaying a malformed htlc failure from it successor. By reporting the
// relaying a malformed htlc failure from its successor. By reporting the
// outgoing channel set, we will surely hit the responsible node. At
// this point, it is not possible that the node's predecessor corrupted
// the onion blob. If the predecessor would have corrupted the payload,
// the onion blob. If the predecessor had corrupted the payload,
// the error source wouldn't have been able to encrypt this failure
// message for us.
case *lnwire.FailInvalidOnionVersion,
Expand Down Expand Up @@ -360,7 +360,7 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(
// When an HTLC parameter is incorrect, the node sending the error may
// be doing something wrong. But it could also be that its predecessor
// is intentionally modifying the htlc parameters that we instructed it
// via the hop payload. Therefore we penalize the incoming node pair. A
// via the hop payload. Therefore, we penalize the incoming node pair. A
// third cause of this error may be that we have an out of date channel
// update. This is handled by the second chance logic up in mission
// control.
Expand Down