All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.fr.acinq.lightning.channel.states.WaitForFundingCreated.kt Maven / Gradle / Ivy

package fr.acinq.lightning.channel.states

import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.PublicKey
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.blockchain.fee.FeeratePerKw
import fr.acinq.lightning.channel.*
import fr.acinq.lightning.wire.*
import kotlinx.coroutines.CompletableDeferred

/*
 * We build the funding transaction for a new channel.
 *
 *       Local                        Remote
 *         |       tx_add_input         |
 *         |<---------------------------|
 *         |       tx_add_input         |
 *         |--------------------------->|
 *         |            ...             |
 *         |       tx_add_output        |
 *         |<---------------------------|
 *         |       tx_add_output        |
 *         |--------------------------->|
 *         |            ...             |
 *         |      tx_add_complete       |
 *         |<---------------------------|
 *         |      tx_add_complete       |
 *         |--------------------------->|
 *         |        commit_sig          |
 *         |--------------------------->|
 */
data class WaitForFundingCreated(
    val replyTo: CompletableDeferred,
    val localParams: LocalParams,
    val remoteParams: RemoteParams,
    val interactiveTxSession: InteractiveTxSession,
    val localPushAmount: MilliSatoshi,
    val remotePushAmount: MilliSatoshi,
    val commitTxFeerate: FeeratePerKw,
    val remoteFirstPerCommitmentPoint: PublicKey,
    val remoteSecondPerCommitmentPoint: PublicKey,
    val channelFlags: ChannelFlags,
    val channelConfig: ChannelConfig,
    val channelFeatures: ChannelFeatures,
    val liquidityPurchase: LiquidityAds.Purchase?,
    val channelOrigin: Origin?
) : ChannelState() {
    val channelId: ByteVector32 = interactiveTxSession.fundingParams.channelId

    override suspend fun ChannelContext.processInternal(cmd: ChannelCommand): Pair> {
        return when (cmd) {
            is ChannelCommand.MessageReceived -> when (cmd.message) {
                is InteractiveTxConstructionMessage -> {
                    val (interactiveTxSession1, interactiveTxAction) = interactiveTxSession.receive(cmd.message)
                    when (interactiveTxAction) {
                        is InteractiveTxSessionAction.SendMessage -> Pair([email protected](interactiveTxSession = interactiveTxSession1), listOf(ChannelAction.Message.Send(interactiveTxAction.msg)))
                        is InteractiveTxSessionAction.SignSharedTx -> {
                            val channelParams = ChannelParams(channelId, channelConfig, channelFeatures, localParams, remoteParams, channelFlags)
                            val signingSession = InteractiveTxSigningSession.create(
                                interactiveTxSession1,
                                keyManager,
                                channelParams,
                                interactiveTxSession.fundingParams,
                                fundingTxIndex = 0,
                                interactiveTxAction.sharedTx,
                                localPushAmount,
                                remotePushAmount,
                                liquidityPurchase,
                                localCommitmentIndex = 0,
                                remoteCommitmentIndex = 0,
                                commitTxFeerate,
                                remoteFirstPerCommitmentPoint,
                                emptySet()
                            )
                            when (signingSession) {
                                is Either.Left -> {
                                    logger.error(signingSession.value) { "cannot initiate interactive-tx signing session" }
                                    replyTo.complete(ChannelFundingResponse.Failure.CannotStartSession)
                                    handleLocalError(cmd, signingSession.value)
                                }
                                is Either.Right -> {
                                    val (session, commitSig) = signingSession.value
                                    // We cannot guarantee that the channel creation is successful: the only way to guarantee that is to wait for on-chain confirmations.
                                    // It is likely that we will restart before the transaction is confirmed, in which case we will lose the replyTo and the ability to notify the caller.
                                    // We should be able to resume the signing steps and complete the funding process if we disconnect, so we optimistically notify the caller now.
                                    replyTo.complete(
                                        ChannelFundingResponse.Success(
                                            channelId = channelId,
                                            fundingTxIndex = 0,
                                            fundingTxId = session.fundingTx.txId,
                                            capacity = session.fundingParams.fundingAmount,
                                            balance = session.localCommit.fold({ it.spec }, { it.spec }).toLocal,
                                            liquidityPurchase = liquidityPurchase,
                                        )
                                    )
                                    val nextState = WaitForFundingSigned(
                                        channelParams,
                                        session,
                                        localPushAmount,
                                        remotePushAmount,
                                        remoteSecondPerCommitmentPoint,
                                        liquidityPurchase,
                                        channelOrigin
                                    )
                                    val actions = buildList {
                                        interactiveTxAction.txComplete?.let { add(ChannelAction.Message.Send(it)) }
                                        add(ChannelAction.Storage.StoreState(nextState))
                                        add(ChannelAction.Message.Send(commitSig))
                                    }
                                    Pair(nextState, actions)
                                }
                            }
                        }
                        is InteractiveTxSessionAction.RemoteFailure -> {
                            logger.warning { "interactive-tx failed: $interactiveTxAction" }
                            replyTo.complete(ChannelFundingResponse.Failure.InteractiveTxSessionFailed(interactiveTxAction))
                            handleLocalError(cmd, DualFundingAborted(channelId, interactiveTxAction.toString()))
                        }
                    }
                }
                is CommitSig -> {
                    logger.warning { "received commit_sig too early, aborting" }
                    replyTo.complete(ChannelFundingResponse.Failure.UnexpectedMessage(cmd.message))
                    handleLocalError(cmd, UnexpectedCommitSig(channelId))
                }
                is TxSignatures -> {
                    logger.warning { "received tx_signatures too early, aborting" }
                    replyTo.complete(ChannelFundingResponse.Failure.UnexpectedMessage(cmd.message))
                    handleLocalError(cmd, UnexpectedFundingSignatures(channelId))
                }
                is TxInitRbf -> {
                    logger.info { "ignoring unexpected tx_init_rbf message" }
                    replyTo.complete(ChannelFundingResponse.Failure.UnexpectedMessage(cmd.message))
                    Pair(this@WaitForFundingCreated, listOf(ChannelAction.Message.Send(Warning(channelId, InvalidRbfAttempt(channelId).message))))
                }
                is TxAckRbf -> {
                    logger.info { "ignoring unexpected tx_ack_rbf message" }
                    replyTo.complete(ChannelFundingResponse.Failure.UnexpectedMessage(cmd.message))
                    Pair(this@WaitForFundingCreated, listOf(ChannelAction.Message.Send(Warning(channelId, InvalidRbfAttempt(channelId).message))))
                }
                is TxAbort -> {
                    logger.warning { "our peer aborted the dual funding flow: ascii='${cmd.message.toAscii()}' bin=${cmd.message.data.toHex()}" }
                    replyTo.complete(ChannelFundingResponse.Failure.AbortedByPeer(cmd.message.toAscii()))
                    Pair(Aborted, listOf(ChannelAction.Message.Send(TxAbort(channelId, DualFundingAborted(channelId, "requested by peer").message))))
                }
                is Error -> {
                    replyTo.complete(ChannelFundingResponse.Failure.AbortedByPeer(cmd.message.toAscii()))
                    handleRemoteError(cmd.message)
                }
                else -> unhandled(cmd)
            }
            is ChannelCommand.Close.MutualClose -> Pair(this@WaitForFundingCreated, listOf(ChannelAction.ProcessCmdRes.NotExecuted(cmd, CommandUnavailableInThisState(channelId, stateName))))
            is ChannelCommand.Close.ForceClose -> handleLocalError(cmd, ForcedLocalCommit(channelId))
            is ChannelCommand.Init -> unhandled(cmd)
            is ChannelCommand.Htlc -> unhandled(cmd)
            is ChannelCommand.Commitment -> unhandled(cmd)
            is ChannelCommand.WatchReceived -> unhandled(cmd)
            is ChannelCommand.Funding -> unhandled(cmd)
            is ChannelCommand.Closing -> unhandled(cmd)
            is ChannelCommand.Connected -> unhandled(cmd)
            is ChannelCommand.Disconnected -> {
                replyTo.complete(ChannelFundingResponse.Failure.Disconnected)
                Pair(Aborted, listOf())
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy