You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix min_final_expiry_delta failures for on-the-fly funding (#714)
When using on-the-fly funding, we have to create and publish an on-chain
transaction after accepting `will_add_htlc`, before we can relay the matching
HTLCs. It may take a few blocks before the transaction is published (or even
confirmed), which means the HTLCs will be held and relayed after a delay.
This can create an issue when there isn't enough margin in the HTLC's expiry:
when we receive `will_add_htlc`, its expiry is below `currentBlockHeight +
minFinalExpiryDelta`, so we accept the on-the-fly funding proposal, but when
the HTLC is actually relayed a few blocks later, the previous condition isn't true
anymore so we reject the HTLC and thus aren't paying the LSP if
`from_future_htlc` was used.
The BOLTs recommend that payers add an additional `cltv_expiry_delta` of
their own to the current block height when sending payments: Phoenix correctly
does that and adds at least 72 blocks, but there are other implementations that
only add a couple of blocks. We thus relax this condition when receiving on-the-fly
HTLCs: as long as the remaining expiry delta is greater than twice our
`fulfill_safety_before_timeout` parameter, we have enough time to publish a force
close and claim funds on-chain. We use this condition when evaluating whether
we accept HTLCs to fix this issue.
We also increase all of our default expiry parameters (which is a sane thing to do
even outside of the context of this issue).
@@ -105,9 +107,12 @@ data class RecipientCltvExpiryParams(val min: CltvExpiryDelta, val max: CltvExpi
105
107
* @param maxHtlcValueInFlightMsat cap on the total value of pending HTLCs in a channel: this lets us limit our exposure to HTLCs risk.
106
108
* @param maxAcceptedHtlcs cap on the number of pending HTLCs in a channel: this lets us limit our exposure to HTLCs risk.
107
109
* @param expiryDeltaBlocks cltv-expiry-delta used in our channel_update: since our channels are private and we don't relay payments, this will be basically ignored.
110
+
* @param minFinalCltvExpiryDelta cltv-expiry-delta that we require when receiving a payment.
108
111
* @param fulfillSafetyBeforeTimeoutBlocks number of blocks necessary to react to a malicious peer that doesn't acknowledge and sign our HTLC preimages.
109
112
* @param checkHtlcTimeoutAfterStartupDelay delay before we check for timed out HTLCs in our channels after a wallet restart.
110
-
* @param htlcMinimum minimum accepted htlc value.
113
+
* @param bolt11InvoiceExpiry duration for which bolt11 invoices that we create are valid.
114
+
* @param bolt12InvoiceExpiry duration for which bolt12 invoices that we create are valid.
115
+
* @param htlcMinimum minimum accepted HTLC value.
111
116
* @param toRemoteDelayBlocks number of blocks our peer will have to wait before they get their main output back in case they force-close a channel.
112
117
* @param maxToLocalDelayBlocks maximum number of blocks we will have to wait before we get our main output back in case we force-close a channel.
113
118
* @param minDepthBlocks minimum depth of a transaction before we consider it safely confirmed.
@@ -116,14 +121,11 @@ data class RecipientCltvExpiryParams(val min: CltvExpiryDelta, val max: CltvExpi
116
121
* @param pingInterval delay between ping messages.
117
122
* @param initialRandomReconnectDelay delay before which we reconnect to our peers (will be randomized based on this value).
118
123
* @param maxReconnectInterval maximum delay between reconnection attempts.
119
-
* @param mppAggregationWindow amount of time we will wait to receive all parts of a multi-part payment.
124
+
* @param mppAggregationWindow amount of time we will wait to receive all parts of a multipart payment.
120
125
* @param maxPaymentAttempts maximum number of retries when attempting an outgoing payment.
121
126
* @param paymentRecipientExpiryParams configure the expiry delta used for the final node when sending payments.
122
127
* @param zeroConfPeers list of peers with whom we use zero-conf (note that this is a strong trust assumption).
123
128
* @param liquidityPolicy fee policy for liquidity events, can be modified at any time.
124
-
* @param minFinalCltvExpiryDelta cltv-expiry-delta that we require when receiving a payment.
125
-
* @param maxFinalCltvExpiryDelta maximum cltv-expiry-delta that we accept when receiving a payment.
126
-
* @param bolt12invoiceExpiry duration for which bolt12 invoices that we create are valid.
val nodePrivateKey get() = keyManager.nodeKeys.nodeKey.privateKey
161
163
val nodeId get() = keyManager.nodeKeys.nodeKey.publicKey
@@ -165,6 +167,7 @@ data class NodeParams(
165
167
val nodeEvents:SharedFlow<NodeEvents> get() =_nodeEvents.asSharedFlow()
166
168
167
169
init {
170
+
// Verify required features are set and obsolete features aren't set.
168
171
require(features.hasFeature(Feature.VariableLengthOnion, FeatureSupport.Mandatory)) { "${Feature.VariableLengthOnion.rfcName} should be mandatory" }
169
172
require(features.hasFeature(Feature.PaymentSecret, FeatureSupport.Mandatory)) { "${Feature.PaymentSecret.rfcName} should be mandatory" }
170
173
require(features.hasFeature(Feature.ChannelType, FeatureSupport.Mandatory)) { "${Feature.ChannelType.rfcName} should be mandatory" }
@@ -176,6 +179,8 @@ data class NodeParams(
176
179
require(!features.hasFeature(Feature.PayToOpenClient)) { "${Feature.PayToOpenClient.rfcName} has been deprecated" }
177
180
require(!features.hasFeature(Feature.PayToOpenProvider)) { "${Feature.PayToOpenProvider.rfcName} has been deprecated" }
178
181
Features.validateFeatureGraph(features)
182
+
// Verify expiry parameters are consistent with each other.
183
+
require((fulfillSafetyBeforeTimeoutBlocks *2) < minFinalCltvExpiryDelta) { "min_final_expiry_delta must be at least twice as long as fulfill_safety_before_timeout_blocks" }
logger.warning { "payment with expiry too small: ${paymentPart.htlc.cltvExpiry}, min is ${minFinalCltvExpiry(incomingPayment.origin.paymentRequest, currentBlockHeight)}" }
logger.warning { "payment with expiry too small: ${paymentPart.htlc.cltvExpiry}, min is ${minFinalCltvExpiry(nodeParams, paymentPart, incomingPayment.origin, currentBlockHeight)}" }
logger.warning { "payment with expiry too small: ${paymentPart.htlc.cltvExpiry}, min is ${nodeParams.minFinalCltvExpiryDelta.toCltvExpiry(currentBlockHeight.toLong())}" }
logger.warning { "payment with expiry too small: ${paymentPart.htlc.cltvExpiry}, min is ${minFinalCltvExpiry(nodeParams, paymentPart, incomingPayment.origin, currentBlockHeight)}" }
0 commit comments