Skip to content

Commit

Permalink
Follow-ups to #649 and #660 (#704)
Browse files Browse the repository at this point in the history
* enable feature FundingFeeCredit

* emit a liquidity event for each liquidity purchase

Independently of whether the purchase was triggered by an on-chain or
off-chain payment.

Also renamed `LiquidityEvents.Accepted` to `LiquidityEvents.Purchased`.

* expose serviceFee in InboundLiquidityOutgoingPayment

* add events PaymentEvents.PaymentSent
  • Loading branch information
pm47 authored Sep 27, 2024
1 parent 7383ee2 commit c8acf4c
Show file tree
Hide file tree
Showing 7 changed files with 18 additions and 22 deletions.
12 changes: 5 additions & 7 deletions src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import fr.acinq.lightning.channel.states.ChannelStateWithCommitments
import fr.acinq.lightning.channel.states.Normal
import fr.acinq.lightning.channel.states.WaitForFundingCreated
import fr.acinq.lightning.db.IncomingPayment
import fr.acinq.lightning.db.OutgoingPayment
import fr.acinq.lightning.utils.sum
import fr.acinq.lightning.wire.Init
import fr.acinq.lightning.wire.LiquidityAds

sealed interface NodeEvents

Expand All @@ -35,13 +37,8 @@ sealed interface ChannelEvents : NodeEvents {
}

sealed interface LiquidityEvents : NodeEvents {
/** Amount of liquidity purchased, before fees are paid. */
val amount: MilliSatoshi
val fee: MilliSatoshi
val source: Source

enum class Source { OnChainWallet, OffChainPayment }
data class Rejected(override val amount: MilliSatoshi, override val fee: MilliSatoshi, override val source: Source, val reason: Reason) : LiquidityEvents {
data class Rejected(val amount: MilliSatoshi, val fee: MilliSatoshi, val source: Source, val reason: Reason) : LiquidityEvents {
sealed class Reason {
data object PolicySetToDisabled : Reason()
sealed class TooExpensive : Reason() {
Expand All @@ -54,7 +51,7 @@ sealed interface LiquidityEvents : NodeEvents {
data class TooManyParts(val parts: Int) : Reason()
}
}
data class Accepted(override val amount: MilliSatoshi, override val fee: MilliSatoshi, override val source: Source) : LiquidityEvents
data class Purchased(val purchase: LiquidityAds.Purchase) : LiquidityEvents
}

/** This is useful on iOS to ask the OS for time to finish some sensitive tasks. */
Expand All @@ -77,4 +74,5 @@ sealed interface PaymentEvents : NodeEvents {
val amount: MilliSatoshi = receivedWith.map { it.amountReceived }.sum()
val fees: MilliSatoshi = receivedWith.map { it.fees }.sum()
}
data class PaymentSent(val payment: OutgoingPayment) : PaymentEvents
}
1 change: 1 addition & 0 deletions src/commonMain/kotlin/fr/acinq/lightning/NodeParams.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ data class NodeParams(
Feature.ChannelBackupClient to FeatureSupport.Optional,
Feature.ExperimentalSplice to FeatureSupport.Optional,
Feature.OnTheFlyFunding to FeatureSupport.Optional,
Feature.FundingFeeCredit to FeatureSupport.Optional,
),
dustLimit = 546.sat,
maxRemoteDustLimit = 600.sat,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,13 +913,11 @@ data class Normal(
// and what we refunded the remote peer for some of their inputs and outputs via the lease.
val miningFees = action.fundingTx.sharedTx.tx.localFees.truncateToSatoshi() + purchase.fees.miningFee
add(ChannelAction.Storage.StoreOutgoingPayment.ViaInboundLiquidityRequest(txId = action.fundingTx.txId, miningFees = miningFees, purchase = purchase))
add(ChannelAction.EmitEvent(LiquidityEvents.Purchased(purchase)))
}
origins.filterIsInstance<Origin.OnChainWallet>().forEach { origin ->
add(ChannelAction.EmitEvent(SwapInEvents.Accepted(origin.inputs, origin.amountBeforeFees.truncateToSatoshi(), origin.fees)))
}
addAll(origins.map { origin ->
when (origin) {
is Origin.OffChainPayment -> ChannelAction.EmitEvent(LiquidityEvents.Accepted(liquidityPurchase?.amount?.toMilliSatoshi() ?: 0.msat, origin.fees.total.toMilliSatoshi(), LiquidityEvents.Source.OffChainPayment))
is Origin.OnChainWallet -> ChannelAction.EmitEvent(SwapInEvents.Accepted(origin.inputs, origin.amountBeforeFees.truncateToSatoshi(), origin.fees))
}
})
if (staticParams.useZeroConf) {
logger.info { "channel is using 0-conf, sending splice_locked right away" }
val spliceLocked = SpliceLocked(channelId, action.fundingTx.txId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,11 @@ data class WaitForFundingSigned(
// and what we refunded the remote peer for some of their inputs and outputs via the lease.
val miningFees = action.fundingTx.sharedTx.tx.localFees.truncateToSatoshi() + purchase.fees.miningFee
add(ChannelAction.Storage.StoreOutgoingPayment.ViaInboundLiquidityRequest(txId = action.fundingTx.txId, miningFees = miningFees, purchase = purchase))
add(ChannelAction.EmitEvent(LiquidityEvents.Purchased(purchase)))
}
}
channelOrigin?.let {
when (it) {
is Origin.OffChainPayment -> add(ChannelAction.EmitEvent(LiquidityEvents.Accepted(liquidityPurchase?.amount?.toMilliSatoshi() ?: 0.msat, it.fees.total.toMilliSatoshi(), LiquidityEvents.Source.OffChainPayment)))
is Origin.OnChainWallet -> add(ChannelAction.EmitEvent(SwapInEvents.Accepted(it.inputs, it.amountBeforeFees.truncateToSatoshi(), it.fees)))
}
listOfNotNull(channelOrigin).filterIsInstance<Origin.OnChainWallet>().forEach { origin ->
add(ChannelAction.EmitEvent(SwapInEvents.Accepted(origin.inputs, origin.amountBeforeFees.truncateToSatoshi(), origin.fees)))
}
}
return if (staticParams.useZeroConf) {
Expand Down
3 changes: 2 additions & 1 deletion src/commonMain/kotlin/fr/acinq/lightning/db/PaymentsDb.kt
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,8 @@ data class InboundLiquidityOutgoingPayment(
override val confirmedAt: Long?,
override val lockedAt: Long?,
) : OnChainOutgoingPayment() {
override val fees: MilliSatoshi = (miningFees + purchase.fees.serviceFee).toMilliSatoshi()
val serviceFees: Satoshi = purchase.fees.serviceFee
override val fees: MilliSatoshi = (miningFees + serviceFees).toMilliSatoshi()
override val amount: MilliSatoshi = fees
override val completedAt: Long? = lockedAt
val fundingFee: LiquidityAds.FundingFee = LiquidityAds.FundingFee(purchase.fees.total.toMilliSatoshi(), txId)
Expand Down
1 change: 1 addition & 0 deletions src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,7 @@ class Peer(
)
}
}
nodeParams._nodeEvents.emit(PaymentEvents.PaymentSent(payment))
db.payments.addOutgoingPayment(payment)
}
is ChannelAction.Storage.SetLocked -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() {
val (alice, _, _, commitSigBob) = init(aliceFundingAmount = 0.sat, alicePushAmount = 0.msat, bobPushAmount = 50_000_000.msat, channelOrigin = channelOrigin)
val (alice1, actionsAlice1) = alice.process(ChannelCommand.MessageReceived(commitSigBob))
assertIs<WaitForFundingConfirmed>(alice1.state)
assertEquals(actionsAlice1.size, 6)
assertEquals(5, actionsAlice1.size)
actionsAlice1.hasOutgoingMessage<TxSignatures>()
actionsAlice1.has<ChannelAction.Storage.StoreState>()
actionsAlice1.find<ChannelAction.Storage.StoreIncomingPayment.ViaNewChannel>().also {
Expand All @@ -110,7 +110,6 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() {
actionsAlice1.hasWatch<WatchConfirmed>()
val events = actionsAlice1.filterIsInstance<ChannelAction.EmitEvent>().map { it.event }
assertTrue(events.any { it is ChannelEvents.Created })
assertTrue(events.any { it is LiquidityEvents.Accepted })
}

@Test
Expand Down Expand Up @@ -194,7 +193,7 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() {
assertTrue(actionsAlice1.isEmpty())
val (alice2, actionsAlice2) = alice1.process(ChannelCommand.MessageReceived(txSigsBob))
assertIs<WaitForFundingConfirmed>(alice2.state)
assertEquals(7, actionsAlice2.size)
assertEquals(8, actionsAlice2.size)
assertTrue(actionsAlice2.hasOutgoingMessage<TxSignatures>().channelData.isEmpty())
actionsAlice2.has<ChannelAction.Storage.StoreState>()
val watchConfirmedAlice = actionsAlice2.findWatch<WatchConfirmed>()
Expand Down

0 comments on commit c8acf4c

Please sign in to comment.