Skip to content

Commit

Permalink
Fix blinded path min_final_expiry_delta check (#2678)
Browse files Browse the repository at this point in the history
The spec says that we must return an error if:

- cltv_expiry < outgoing_cltv_value
- cltv_expiry < current_block_height + min_final_cltv_expiry_delta

For the second check, we actually tested if:

- cltv_expiry < max(outgoing_cltv_value, current_block_height) + min_final_cltv_expiry_delta

But that check should only verify that our `min_final_expiry_delta`
requirement is fulfilled, which is unrelated to the `outgoing_cltv_value`.

It was redundant with the first check, which already guarantees that
intermediate nodes inside the blinded path cannot cheat by using a higher
`cltv_expiry_delta` than what the recipient intended.
  • Loading branch information
t-bast authored Jun 8, 2023
1 parent b084d73 commit 05ef2f9
Show file tree
Hide file tree
Showing 3 changed files with 4 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -472,12 +472,7 @@ object MultiPartHandler {
}

private def validatePaymentCltv(nodeParams: NodeParams, add: UpdateAddHtlc, payload: FinalPayload)(implicit log: LoggingAdapter): Boolean = {
val minExpiry = payload match {
case _: FinalPayload.Standard => nodeParams.channelConf.minFinalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)
// For blinded payments, the min-final-expiry-delta is included in the blinded path instead of being added
// explicitly by the sender to their onion payload's expiry.
case _: FinalPayload.Blinded => nodeParams.channelConf.minFinalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight.max(payload.expiry.blockHeight))
}
val minExpiry = nodeParams.channelConf.minFinalExpiryDelta.toCltvExpiry(nodeParams.currentBlockHeight)
if (add.cltvExpiry < minExpiry) {
log.warning("received payment with expiry too small for amount={} totalAmount={}", add.amountMsat, payload.totalAmount)
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ object PaymentInitiator {
def finalExpiry(nodeParams: NodeParams): CltvExpiry = {
val minFinalCltvExpiryDelta = invoice match {
case invoice: Bolt11Invoice => invoice.minFinalCltvExpiryDelta
// For blinded payments, the min-final-expiry-delta is included in the blinded path instead of being added explicitly by the sender.
case _: Bolt12Invoice => CltvExpiryDelta(0)
}
nodeParams.paymentFinalExpiry.computeFinalExpiry(nodeParams.currentBlockHeight, minFinalCltvExpiryDelta)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,8 @@ class MultiPartHandlerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
val invoice = sender.expectMsgType[CreateInvoiceActor.InvoiceCreated].invoice
assert(invoice.features.hasFeature(RouteBlinding, Some(Mandatory)))

val packet = createBlindedPacket(5000 msat, invoice.paymentHash, defaultExpiry, CltvExpiry(nodeParams.currentBlockHeight) + CltvExpiryDelta(1), pathId)
// We test the case where the HTLC's cltv_expiry is lower than expected and doesn't meet the min_final_expiry_delta.
val packet = createBlindedPacket(5000 msat, invoice.paymentHash, defaultExpiry - CltvExpiryDelta(1), defaultExpiry, pathId)
sender.send(handlerWithRouteBlinding, packet)
val receivePayment = offerManager.expectMsgType[OfferManager.ReceivePayment]
assert(receivePayment.paymentHash == invoice.paymentHash)
Expand Down

0 comments on commit 05ef2f9

Please sign in to comment.