Skip to content

Commit b409c7d

Browse files
committed
lntest: make sure HTLCs are locked in when sending a payment
Before this change, CompletePaymentRequestsNoWait would return as soon as the channel's NumUpdates increased by at least one. When sending multiple payments, this meant the function could return while some HTLCs were still in-flight and not yet committed to the channel state. The problem occurred when tests captured the channel state immediately after calling this function. Even though we read the current NumUpdates from the channel, HTLCs could still be in the process of being committed. This led to a race where the channel would progress to a new state after we thought we had correctly captured it, causing tests to see unexpected commitment heights. Fix this by waiting for all outgoing HTLCs to appear in PendingHtlcs before returning. We count outgoing HTLCs before sending, then wait until exactly len(paymentRequests) new HTLCs are present. This guarantees all payments have fully completed their commitment exchange and are locked in on both sides. Fixes the flaky revokedCloseRetributionRemoteHodlCase test where backups would capture state at height N+1 instead of the expected height N.
1 parent 91423ee commit b409c7d

1 file changed

Lines changed: 33 additions & 15 deletions

File tree

lntest/harness.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1619,8 +1619,9 @@ func (h *HarnessTest) CompletePaymentRequests(hn *node.HarnessNode,
16191619
}
16201620

16211621
// CompletePaymentRequestsNoWait sends payments from a node to complete all
1622-
// payment requests without waiting for the results. Instead, it checks the
1623-
// number of updates in the specified channel has increased.
1622+
// payment requests without waiting for the results. Instead, it waits for
1623+
// all HTLCs to be locked in on the sender's channel by checking the number
1624+
// of pending HTLCs.
16241625
func (h *HarnessTest) CompletePaymentRequestsNoWait(hn *node.HarnessNode,
16251626
paymentRequests []string, chanPoint *lnrpc.ChannelPoint) {
16261627

@@ -1629,31 +1630,48 @@ func (h *HarnessTest) CompletePaymentRequestsNoWait(hn *node.HarnessNode,
16291630
// we return.
16301631
oldResp := h.GetChannelByChanPoint(hn, chanPoint)
16311632

1633+
// Count existing outgoing HTLCs before sending.
1634+
oldOutgoingCount := 0
1635+
for _, htlc := range oldResp.PendingHtlcs {
1636+
if !htlc.Incoming {
1637+
oldOutgoingCount++
1638+
}
1639+
}
1640+
1641+
numPayments := len(paymentRequests)
1642+
16321643
// Send payments and assert they are in-flight.
16331644
h.completePaymentRequestsAssertStatus(
16341645
hn, paymentRequests, lnrpc.Payment_IN_FLIGHT,
16351646
)
16361647

1637-
// We are not waiting for feedback in the form of a response, but we
1638-
// should still wait long enough for the server to receive and handle
1639-
// the send before cancelling the request. We wait for the number of
1640-
// updates to one of our channels has increased before we return.
1648+
// Wait for all HTLCs to be locked in. We check that the number of
1649+
// outgoing pending HTLCs has increased by exactly the number of
1650+
// payments sent. This ensures all HTLCs are committed on the sender's
1651+
// side.
16411652
err := wait.NoError(func() error {
16421653
newResp := h.GetChannelByChanPoint(hn, chanPoint)
16431654

1644-
// If this channel has an increased number of updates, we
1645-
// assume the payments are committed, and we can return.
1646-
if newResp.NumUpdates > oldResp.NumUpdates {
1655+
// Count current outgoing HTLCs.
1656+
newOutgoingCount := 0
1657+
for _, htlc := range newResp.PendingHtlcs {
1658+
if !htlc.Incoming {
1659+
newOutgoingCount++
1660+
}
1661+
}
1662+
1663+
htlcsAdded := newOutgoingCount - oldOutgoingCount
1664+
1665+
// Verify all HTLCs are locked in.
1666+
if htlcsAdded == numPayments {
16471667
return nil
16481668
}
16491669

1650-
// Otherwise return an error as the NumUpdates are not
1651-
// increased.
1652-
return fmt.Errorf("%s: channel:%v not updated after sending "+
1653-
"payments, old updates: %v, new updates: %v", hn.Name(),
1654-
chanPoint, oldResp.NumUpdates, newResp.NumUpdates)
1670+
return fmt.Errorf("%s: channel:%v waiting for HTLCs, "+
1671+
"added: %d/%d", hn.Name(), chanPoint,
1672+
htlcsAdded, numPayments)
16551673
}, DefaultTimeout)
1656-
require.NoError(h, err, "timeout while checking for channel updates")
1674+
require.NoError(h, err, "timeout while waiting for HTLCs to lock in")
16571675
}
16581676

16591677
// OpenChannelPsbt attempts to open a channel between srcNode and destNode with

0 commit comments

Comments
 (0)