diff --git a/src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt b/src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt index 164775c86..864c8cf01 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/NodeEvents.kt @@ -1,9 +1,12 @@ package fr.acinq.lightning import fr.acinq.bitcoin.ByteVector32 +import fr.acinq.bitcoin.OutPoint import fr.acinq.bitcoin.Satoshi +import fr.acinq.lightning.blockchain.electrum.WalletState import fr.acinq.lightning.channel.InteractiveTxParams import fr.acinq.lightning.channel.SharedFundingInput +import fr.acinq.lightning.channel.TransactionFees import fr.acinq.lightning.channel.states.ChannelStateWithCommitments import fr.acinq.lightning.channel.states.Normal import fr.acinq.lightning.channel.states.WaitForFundingCreated @@ -12,6 +15,15 @@ import fr.acinq.lightning.utils.sum sealed interface NodeEvents +sealed interface SwapInEvents : NodeEvents { + data class Requested(val walletInputs: List) : SwapInEvents { + val totalAmount: Satoshi = walletInputs.map { it.amount }.sum() + } + data class Accepted(val inputs: Set, val amount: Satoshi, val fees: TransactionFees) : SwapInEvents { + val receivedAmount: Satoshi = amount - fees.serviceFee - fees.miningFee + } +} + sealed interface ChannelEvents : NodeEvents { data class Creating(val state: WaitForFundingCreated) : ChannelEvents data class Created(val state: ChannelStateWithCommitments) : ChannelEvents @@ -19,6 +31,7 @@ sealed interface ChannelEvents : NodeEvents { } sealed interface LiquidityEvents : NodeEvents { + /** Amount of the liquidity event, before fees are paid. */ val amount: MilliSatoshi val fee: MilliSatoshi val source: Source @@ -36,6 +49,7 @@ sealed interface LiquidityEvents : NodeEvents { data class ChannelFundingCancelled(val paymentHash: ByteVector32) : Reason() } } + data class Accepted(override val amount: MilliSatoshi, override val fee: MilliSatoshi, override val source: Source) : LiquidityEvents } /** This is useful on iOS to ask the OS for time to finish some sensitive tasks. */ @@ -48,7 +62,6 @@ sealed interface SensitiveTaskEvents : NodeEvents { } data class TaskStarted(val id: TaskIdentifier) : SensitiveTaskEvents data class TaskEnded(val id: TaskIdentifier) : SensitiveTaskEvents - } /** This will be emitted in a corner case where the user restores a wallet on an older version of the app, which is unable to read the channel data. */ diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt index 53bc1619b..ae7cb8a95 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/ChannelAction.kt @@ -1,9 +1,9 @@ package fr.acinq.lightning.channel import fr.acinq.bitcoin.* -import fr.acinq.lightning.ChannelEvents import fr.acinq.lightning.CltvExpiry import fr.acinq.lightning.MilliSatoshi +import fr.acinq.lightning.NodeEvents import fr.acinq.lightning.blockchain.Watch import fr.acinq.lightning.channel.states.PersistedChannelState import fr.acinq.lightning.db.ChannelClosingType @@ -134,8 +134,8 @@ sealed class ChannelAction { } } - data class EmitEvent(val event: ChannelEvents) : ChannelAction() + data class EmitEvent(val event: NodeEvents) : ChannelAction() - object Disconnect : ChannelAction() + data object Disconnect : ChannelAction() // @formatter:on } \ No newline at end of file diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Normal.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Normal.kt index cfb7891c8..742c194a9 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Normal.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Normal.kt @@ -4,9 +4,7 @@ import fr.acinq.bitcoin.Bitcoin import fr.acinq.bitcoin.SigHash import fr.acinq.bitcoin.TxId import fr.acinq.bitcoin.utils.Either -import fr.acinq.lightning.Feature -import fr.acinq.lightning.Features -import fr.acinq.lightning.ShortChannelId +import fr.acinq.lightning.* import fr.acinq.lightning.blockchain.BITCOIN_FUNDING_DEPTHOK import fr.acinq.lightning.blockchain.WatchConfirmed import fr.acinq.lightning.blockchain.WatchEventConfirmed @@ -902,6 +900,12 @@ data class Normal( val miningFees = action.fundingTx.sharedTx.tx.localFees.truncateToSatoshi() + liquidityLease.fees.miningFee add(ChannelAction.Storage.StoreOutgoingPayment.ViaInboundLiquidityRequest(txId = action.fundingTx.txId, miningFees = miningFees, lease = liquidityLease)) } + addAll(origins.map { origin -> + when (origin) { + is Origin.OffChainPayment -> ChannelAction.EmitEvent(LiquidityEvents.Accepted(origin.amount, origin.fees.total.toMilliSatoshi(), LiquidityEvents.Source.OffChainPayment)) + is Origin.OnChainWallet -> ChannelAction.EmitEvent(SwapInEvents.Accepted(origin.inputs, origin.amount.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) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSigned.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSigned.kt index c4eebe328..64cff8bda 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSigned.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSigned.kt @@ -4,9 +4,7 @@ import fr.acinq.bitcoin.ByteVector32 import fr.acinq.bitcoin.PublicKey import fr.acinq.bitcoin.crypto.Pack import fr.acinq.bitcoin.utils.Either -import fr.acinq.lightning.ChannelEvents -import fr.acinq.lightning.MilliSatoshi -import fr.acinq.lightning.ShortChannelId +import fr.acinq.lightning.* import fr.acinq.lightning.blockchain.BITCOIN_FUNDING_DEPTHOK import fr.acinq.lightning.blockchain.WatchConfirmed import fr.acinq.lightning.channel.* @@ -131,6 +129,12 @@ data class WaitForFundingSigned( origin = channelOrigin ) ) + channelOrigin?.let { + when (it) { + is Origin.OffChainPayment -> add(ChannelAction.EmitEvent(LiquidityEvents.Accepted(it.amount, it.fees.total.toMilliSatoshi(), LiquidityEvents.Source.OffChainPayment))) + is Origin.OnChainWallet -> add(ChannelAction.EmitEvent(SwapInEvents.Accepted(it.inputs, it.amount.truncateToSatoshi(), it.fees))) + } + } } return if (staticParams.useZeroConf) { logger.info { "channel is using 0-conf, we won't wait for the funding tx to confirm" } diff --git a/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt b/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt index 8873daf91..5dc47b094 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt @@ -1138,6 +1138,7 @@ class Peer( } } input.send(WrappedChannelCommand(channel.channelId, spliceCommand)) + nodeParams._nodeEvents.emit(SwapInEvents.Requested(cmd.walletInputs)) } } } @@ -1197,6 +1198,7 @@ class Peer( val msg = actions.filterIsInstance().map { it.message }.filterIsInstance().first() _channels = _channels + (msg.temporaryChannelId to state) processActions(msg.temporaryChannelId, peerConnection, actions) + nodeParams._nodeEvents.emit(SwapInEvents.Requested(cmd.walletInputs)) } } } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt index 4284d6ec3..1bcb4754d 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/WaitForFundingSignedTestsCommon.kt @@ -139,7 +139,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(alice1.state) - assertEquals(actionsAlice1.size, 5) + assertEquals(actionsAlice1.size, 6) actionsAlice1.hasOutgoingMessage() actionsAlice1.has() actionsAlice1.find().also { @@ -148,7 +148,9 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() { assertEquals(alice1.commitments.latest.fundingTxId, it.txId) } actionsAlice1.hasWatch() - actionsAlice1.has() + val events = actionsAlice1.filterIsInstance().map { it.event } + assertTrue(events.any { it is ChannelEvents.Created }) + assertTrue(events.any { it is LiquidityEvents.Accepted }) } @Test @@ -157,7 +159,7 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() { val (alice, _, _, commitSigBob) = init(aliceFundingAmount = 200_000.sat, alicePushAmount = 0.msat, bobFundingAmount = 500_000.sat, channelOrigin = channelOrigin) val (alice1, actionsAlice1) = alice.process(ChannelCommand.MessageReceived(commitSigBob)) assertIs(alice1.state) - assertEquals(actionsAlice1.size, 5) + assertEquals(actionsAlice1.size, 6) actionsAlice1.hasOutgoingMessage() actionsAlice1.has() actionsAlice1.find().also { @@ -166,7 +168,9 @@ class WaitForFundingSignedTestsCommon : LightningTestSuite() { assertTrue(it.localInputs.isNotEmpty()) } actionsAlice1.hasWatch() - actionsAlice1.has() + val events = actionsAlice1.filterIsInstance().map { it.event } + assertTrue(events.any { it is ChannelEvents.Created }) + assertTrue(events.any { it is SwapInEvents.Accepted }) } @Test