Skip to content

Commit da7ca33

Browse files
committed
Add liquidity ads to the channel opening flow
We previously only used liquidity ads with splicing: we now support it during the initial channel opening flow as well. This lets us add more unit tests, including tests for the case where the node receiving the `open_channel` message is responsible for paying the commitment fees. We also update liquidity ads to use the latest version of the spec from lightning/bolts#1153. This introduces more ways of paying the liquidity fees, to support on-the-fly funding without existing channel balance (not implemented in this commit). Note that we need some backwards-compatibility with the previous liquidity ads types in our state serialization code: when we're in the middle of signing a splice transaction, we may have a legacy liquidity lease in our splice status. We ignore it when finalizing the splice: the only consequence is that we won't store an entry in our DB for that lease, but the channel will otherwise work correctly.
1 parent 174c43d commit da7ca33

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1110
-506
lines changed

src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,15 @@ object DefaultSwapInParams {
7070
* @param trampolineFees ordered list of trampoline fees to try when making an outgoing payment.
7171
* @param invoiceDefaultRoutingFees default routing fees set in invoices when we don't have any channel.
7272
* @param swapInParams parameters for swap-in transactions.
73-
* @param leaseRate rate at which our peer sells their liquidity.
73+
* @param remoteFundingRates rates at which our peer sells their liquidity.
7474
*/
7575
data class WalletParams(
7676
val trampolineNode: NodeUri,
7777
val trampolineFees: List<TrampolineFees>,
7878
val invoiceDefaultRoutingFees: InvoiceDefaultRoutingFees,
7979
val swapInParams: SwapInParams,
80-
val leaseRate: LiquidityAds.LeaseRate,
80+
// TODO: once standardized, we should get this data from our peer's init message.
81+
val remoteFundingRates: LiquidityAds.WillFundRates,
8182
)
8283

8384
/**

src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ sealed class ChannelAction {
8787
abstract val txId: TxId
8888
data class ViaSpliceOut(val amount: Satoshi, override val miningFees: Satoshi, val address: String, override val txId: TxId) : StoreOutgoingPayment()
8989
data class ViaSpliceCpfp(override val miningFees: Satoshi, override val txId: TxId) : StoreOutgoingPayment()
90-
data class ViaInboundLiquidityRequest(override val txId: TxId, override val miningFees: Satoshi, val lease: LiquidityAds.Lease) : StoreOutgoingPayment()
90+
data class ViaInboundLiquidityRequest(override val txId: TxId, override val miningFees: Satoshi, val purchase: LiquidityAds.Purchase) : StoreOutgoingPayment()
9191
data class ViaClose(val amount: Satoshi, override val miningFees: Satoshi, val address: String, override val txId: TxId, val isSentToDefaultAddress: Boolean, val closingType: ChannelClosingType) : StoreOutgoingPayment()
9292
}
9393
data class SetLocked(val txId: TxId) : Storage()

src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelCommand.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ sealed class ChannelCommand {
3535
val channelFlags: ChannelFlags,
3636
val channelConfig: ChannelConfig,
3737
val channelType: ChannelType.SupportedChannelType,
38-
val requestRemoteFunding: LiquidityAds.RequestRemoteFunding?,
38+
val requestRemoteFunding: LiquidityAds.RequestFunding?,
3939
val channelOrigin: Origin?,
4040
) : Init() {
4141
fun temporaryChannelId(keyManager: KeyManager): ByteVector32 = keyManager.channelKeys(localParams.fundingKeyPath).temporaryChannelId
@@ -48,7 +48,8 @@ sealed class ChannelCommand {
4848
val walletInputs: List<WalletState.Utxo>,
4949
val localParams: LocalParams,
5050
val channelConfig: ChannelConfig,
51-
val remoteInit: InitMessage
51+
val remoteInit: InitMessage,
52+
val fundingRates: LiquidityAds.WillFundRates?
5253
) : Init()
5354

5455
data class Restore(val state: PersistedChannelState) : Init()
@@ -86,7 +87,7 @@ sealed class ChannelCommand {
8687
data class UpdateFee(val feerate: FeeratePerKw, val commit: Boolean = false) : Commitment(), ForbiddenDuringSplice, ForbiddenDuringQuiescence
8788
data object CheckHtlcTimeout : Commitment()
8889
sealed class Splice : Commitment() {
89-
data class Request(val replyTo: CompletableDeferred<Response>, val spliceIn: SpliceIn?, val spliceOut: SpliceOut?, val requestRemoteFunding: LiquidityAds.RequestRemoteFunding?, val feerate: FeeratePerKw, val origins: List<Origin>) : Splice() {
90+
data class Request(val replyTo: CompletableDeferred<Response>, val spliceIn: SpliceIn?, val spliceOut: SpliceOut?, val requestRemoteFunding: LiquidityAds.RequestFunding?, val feerate: FeeratePerKw, val origins: List<Origin>) : Splice() {
9091
val pushAmount: MilliSatoshi = spliceIn?.pushAmount ?: 0.msat
9192
val spliceOutputs: List<TxOut> = spliceOut?.let { listOf(TxOut(it.amount, it.scriptPubKey)) } ?: emptyList()
9293

@@ -105,7 +106,7 @@ sealed class ChannelCommand {
105106
val fundingTxId: TxId,
106107
val capacity: Satoshi,
107108
val balance: MilliSatoshi,
108-
val liquidityLease: LiquidityAds.Lease?,
109+
val liquidityPurchase: LiquidityAds.Purchase?,
109110
) : Response()
110111

111112
sealed class Failure : Response() {

src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelException.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ data class ToSelfDelayTooHigh (override val channelId: Byte
2828
data class MissingLiquidityAds (override val channelId: ByteVector32) : ChannelException(channelId, "liquidity ads field is missing")
2929
data class InvalidLiquidityAdsSig (override val channelId: ByteVector32) : ChannelException(channelId, "liquidity ads signature is invalid")
3030
data class InvalidLiquidityAdsAmount (override val channelId: ByteVector32, val proposed: Satoshi, val min: Satoshi) : ChannelException(channelId, "liquidity ads funding amount is too low (expected at least $min, got $proposed)")
31-
data class InvalidLiquidityRates (override val channelId: ByteVector32) : ChannelException(channelId, "rejecting liquidity ads proposed rates")
3231
data class ChannelFundingError (override val channelId: ByteVector32) : ChannelException(channelId, "channel funding error")
3332
data class RbfAttemptAborted (override val channelId: ByteVector32) : ChannelException(channelId, "rbf attempt aborted")
3433
data class SpliceAborted (override val channelId: ByteVector32) : ChannelException(channelId, "splice aborted")

src/commonMain/kotlin/fr/acinq/lightning/channel/InteractiveTx.kt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,7 @@ data class InteractiveTxSession(
673673
val isComplete: Boolean = txCompleteSent != null && txCompleteReceived != null
674674

675675
fun send(): Pair<InteractiveTxSession, InteractiveTxSessionAction> {
676-
val msg = toSend.firstOrNull()
677-
return when (msg) {
676+
return when (val msg = toSend.firstOrNull()) {
678677
null -> {
679678
val localSwapIns = localInputs.filterIsInstance<InteractiveTxInput.LocalSwapIn>()
680679
val remoteSwapIns = remoteInputs.filterIsInstance<InteractiveTxInput.RemoteSwapIn>()
@@ -987,7 +986,6 @@ data class InteractiveTxSigningSession(
987986
val fundingParams: InteractiveTxParams,
988987
val fundingTxIndex: Long,
989988
val fundingTx: PartiallySignedSharedTransaction,
990-
val liquidityLease: LiquidityAds.Lease?,
991989
val localCommit: Either<UnsignedLocalCommit, LocalCommit>,
992990
val remoteCommit: RemoteCommit,
993991
) {
@@ -1065,7 +1063,7 @@ data class InteractiveTxSigningSession(
10651063
sharedTx: SharedTransaction,
10661064
localPushAmount: MilliSatoshi,
10671065
remotePushAmount: MilliSatoshi,
1068-
liquidityLease: LiquidityAds.Lease?,
1066+
liquidityPurchase: LiquidityAds.Purchase?,
10691067
localCommitmentIndex: Long,
10701068
remoteCommitmentIndex: Long,
10711069
commitTxFeerate: FeeratePerKw,
@@ -1075,7 +1073,16 @@ data class InteractiveTxSigningSession(
10751073
val channelKeys = channelParams.localParams.channelKeys(keyManager)
10761074
val unsignedTx = sharedTx.buildUnsignedTx()
10771075
val sharedOutputIndex = unsignedTx.txOut.indexOfFirst { it.publicKeyScript == fundingParams.fundingPubkeyScript(channelKeys) }
1078-
val liquidityFees = liquidityLease?.fees?.total?.toMilliSatoshi() ?: 0.msat
1076+
val liquidityFees = liquidityPurchase?.let { l ->
1077+
val fees = l.fees.total.toMilliSatoshi()
1078+
when (l.paymentDetails) {
1079+
is LiquidityAds.PaymentDetails.FromChannelBalance -> if (fundingParams.isInitiator) fees else -fees
1080+
is LiquidityAds.PaymentDetails.FromChannelBalanceForFutureHtlc -> if (fundingParams.isInitiator) fees else -fees
1081+
// Fees will be paid later, from relayed HTLCs.
1082+
is LiquidityAds.PaymentDetails.FromFutureHtlc -> 0.msat
1083+
is LiquidityAds.PaymentDetails.FromFutureHtlcWithPreimage -> 0.msat
1084+
}
1085+
} ?: 0.msat
10791086
return Helpers.Funding.makeCommitTxs(
10801087
channelKeys,
10811088
channelParams.channelId,
@@ -1120,7 +1127,7 @@ data class InteractiveTxSigningSession(
11201127
val unsignedLocalCommit = UnsignedLocalCommit(localCommitmentIndex, firstCommitTx.localSpec, firstCommitTx.localCommitTx, listOf())
11211128
val remoteCommit = RemoteCommit(remoteCommitmentIndex, firstCommitTx.remoteSpec, firstCommitTx.remoteCommitTx.tx.txid, remotePerCommitmentPoint)
11221129
val signedFundingTx = sharedTx.sign(session, keyManager, fundingParams, channelParams.localParams, channelParams.remoteParams.nodeId)
1123-
Pair(InteractiveTxSigningSession(fundingParams, fundingTxIndex, signedFundingTx, liquidityLease, Either.Left(unsignedLocalCommit), remoteCommit), commitSig)
1130+
Pair(InteractiveTxSigningSession(fundingParams, fundingTxIndex, signedFundingTx, Either.Left(unsignedLocalCommit), remoteCommit), commitSig)
11241131
}
11251132
}
11261133

@@ -1168,7 +1175,7 @@ sealed class SpliceStatus {
11681175
/** Our peer has asked us to stop sending new updates and wait for our updates to be added to the local and remote commitments. */
11691176
data class ReceivedStfu(val stfu: Stfu) : QuiescenceNegotiation.NonInitiator()
11701177
/** Our updates have been added to the local and remote commitments, we wait for our peer to use the now quiescent channel. */
1171-
object NonInitiatorQuiescent : QuiescentSpliceStatus()
1178+
data object NonInitiatorQuiescent : QuiescentSpliceStatus()
11721179
/** We told our peer we want to splice funds in the channel. */
11731180
data class Requested(val command: ChannelCommand.Commitment.Splice.Request, val spliceInit: SpliceInit) : QuiescentSpliceStatus()
11741181
/** We both agreed to splice and are building the splice transaction. */
@@ -1177,11 +1184,11 @@ sealed class SpliceStatus {
11771184
val spliceSession: InteractiveTxSession,
11781185
val localPushAmount: MilliSatoshi,
11791186
val remotePushAmount: MilliSatoshi,
1180-
val liquidityLease: LiquidityAds.Lease?,
1187+
val liquidityPurchase: LiquidityAds.Purchase?,
11811188
val origins: List<Origin>
11821189
) : QuiescentSpliceStatus()
11831190
/** The splice transaction has been negotiated, we're exchanging signatures. */
1184-
data class WaitingForSigs(val session: InteractiveTxSigningSession, val origins: List<Origin>) : QuiescentSpliceStatus()
1191+
data class WaitingForSigs(val session: InteractiveTxSigningSession, val liquidityPurchase: LiquidityAds.Purchase?, val origins: List<Origin>) : QuiescentSpliceStatus()
11851192
/** The splice attempt was aborted by us, we're waiting for our peer to ack. */
11861193
data object Aborted : QuiescentSpliceStatus()
11871194
}

src/commonMain/kotlin/fr/acinq/lightning/channel/states/LegacyWaitForFundingLocked.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ data class LegacyWaitForFundingLocked(
4848
null,
4949
null,
5050
SpliceStatus.None,
51-
listOf(),
5251
)
5352
val actions = listOf(
5453
ChannelAction.Storage.StoreState(nextState),

0 commit comments

Comments
 (0)