routing+docs: make sure non-MPP cannot use skipTempErr

This commit is contained in:
yyforyongyu 2023-10-24 14:53:39 +08:00
parent 168cfd7cd5
commit 678f416008
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
3 changed files with 74 additions and 1 deletions

View File

@ -91,6 +91,12 @@
hash](https://github.com/lightningnetwork/lnd/pull/8106) to the
signer.SignMessage/signer.VerifyMessage RPCs.
* `sendtoroute` will return an error when it's called using the flag
`--skip_temp_err` on a payment that's not a MPP. This is needed as a temp
error is defined as a routing error found in one of a MPP's HTLC attempts.
If, however, there's only one HTLC attempt, when it's failed, this payment is
considered failed, thus there's no such thing as temp error for a non-MPP.
## lncli Updates
## Code Health

View File

@ -119,6 +119,10 @@ var (
// provided by either a blinded route or a cleartext pubkey.
ErrNoTarget = errors.New("destination not set in target or blinded " +
"path")
// ErrSkipTempErr is returned when a non-MPP is made yet the
// skipTempErr flag is set.
ErrSkipTempErr = errors.New("cannot skip temp error for non-MPP")
)
// ChannelGraphSource represents the source of information about the topology
@ -2450,6 +2454,13 @@ func (r *ChannelRouter) sendToRoute(htlcHash lntypes.Hash, rt *route.Route,
amt = mpp.TotalMsat()
}
// For non-MPP, there's no such thing as temp error as there's only one
// HTLC attempt being made. When this HTLC is failed, the payment is
// failed hence cannot be retried.
if skipTempErr && mpp == nil {
return nil, ErrSkipTempErr
}
// For non-AMP payments the overall payment identifier will be the same
// hash as used for this HTLC.
paymentIdentifier := htlcHash

View File

@ -3384,7 +3384,8 @@ func TestBlockDifferenceFix(t *testing.T) {
initialBlockHeight := uint32(0)
// Starting height here is set to 0, which is behind where we want to be.
// Starting height here is set to 0, which is behind where we want to
// be.
ctx := createTestCtxSingleNode(t, initialBlockHeight)
// Add initial block to our mini blockchain.
@ -3454,6 +3455,8 @@ func TestBlockDifferenceFix(t *testing.T) {
// TestSendToRouteSkipTempErrSuccess validates a successful payment send.
func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
t.Parallel()
var (
payHash lntypes.Hash
payAmt = lnwire.MilliSatoshi(10000)
@ -3536,9 +3539,62 @@ func TestSendToRouteSkipTempErrSuccess(t *testing.T) {
payment.AssertExpectations(t)
}
// TestSendToRouteSkipTempErrNonMPP checks that an error is return when
// skipping temp error for non-MPP.
func TestSendToRouteSkipTempErrNonMPP(t *testing.T) {
t.Parallel()
var (
payHash lntypes.Hash
payAmt = lnwire.MilliSatoshi(10000)
)
node, err := createTestNode()
require.NoError(t, err)
// Create a simple 1-hop route without the MPP field.
hops := []*route.Hop{
{
ChannelID: 1,
PubKeyBytes: node.PubKeyBytes,
AmtToForward: payAmt,
},
}
rt, err := route.NewRouteFromHops(payAmt, 100, node.PubKeyBytes, hops)
require.NoError(t, err)
// Create mockers.
controlTower := &mockControlTower{}
payer := &mockPaymentAttemptDispatcher{}
missionControl := &mockMissionControl{}
// Create the router.
router := &ChannelRouter{cfg: &Config{
Control: controlTower,
Payer: payer,
MissionControl: missionControl,
Clock: clock.NewTestClock(time.Unix(1, 0)),
NextPaymentID: func() (uint64, error) {
return 0, nil
},
}}
// Expect an error to be returned.
attempt, err := router.SendToRouteSkipTempErr(payHash, rt)
require.ErrorIs(t, ErrSkipTempErr, err)
require.Nil(t, attempt)
// Assert the above methods are not called.
controlTower.AssertExpectations(t)
payer.AssertExpectations(t)
missionControl.AssertExpectations(t)
}
// TestSendToRouteSkipTempErrTempFailure validates a temporary failure won't
// cause the payment to be failed.
func TestSendToRouteSkipTempErrTempFailure(t *testing.T) {
t.Parallel()
var (
payHash lntypes.Hash
payAmt = lnwire.MilliSatoshi(10000)