commonMain.fr.acinq.lightning.channel.states.WaitForOpenChannel.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lightning-kmp-jvm Show documentation
Show all versions of lightning-kmp-jvm Show documentation
A Kotlin Multiplatform implementation of the Lightning Network
package fr.acinq.lightning.channel.states
import fr.acinq.bitcoin.ByteVector32
import fr.acinq.bitcoin.Satoshi
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.ChannelEvents
import fr.acinq.lightning.MilliSatoshi
import fr.acinq.lightning.blockchain.electrum.WalletState
import fr.acinq.lightning.channel.*
import fr.acinq.lightning.channel.Helpers.Funding.computeChannelId
import fr.acinq.lightning.utils.msat
import fr.acinq.lightning.wire.*
import kotlinx.coroutines.CompletableDeferred
/*
* We are waiting for our peer to initiate a channel open.
*
* Local Remote
* | open_channel2 |
* |<---------------------------|
* | accept_channel2 |
* |--------------------------->|
*/
data class WaitForOpenChannel(
val replyTo: CompletableDeferred,
val temporaryChannelId: ByteVector32,
val fundingAmount: Satoshi,
val pushAmount: MilliSatoshi,
val walletInputs: List,
val localParams: LocalParams,
val channelConfig: ChannelConfig,
val remoteInit: Init,
val fundingRates: LiquidityAds.WillFundRates?
) : ChannelState() {
override suspend fun ChannelContext.processInternal(cmd: ChannelCommand): Pair> {
return when (cmd) {
is ChannelCommand.MessageReceived -> when (cmd.message) {
is OpenDualFundedChannel -> {
val open = cmd.message
when (val res = Helpers.validateParamsNonInitiator(staticParams.nodeParams, open)) {
is Either.Right -> {
val channelType = res.value
val channelFeatures = ChannelFeatures(channelType, localFeatures = localParams.features, remoteFeatures = remoteInit.features)
val minimumDepth = if (staticParams.useZeroConf) 0 else Helpers.minDepthForFunding(staticParams.nodeParams, open.fundingAmount)
val channelKeys = keyManager.channelKeys(localParams.fundingKeyPath)
val localFundingPubkey = channelKeys.fundingPubKey(0)
val fundingScript = Helpers.Funding.makeFundingPubKeyScript(localFundingPubkey, open.fundingPubkey)
val requestFunding = open.requestFunding
val willFund = when {
fundingRates == null -> null
requestFunding == null -> null
requestFunding.requestedAmount > fundingAmount -> null
else -> fundingRates.validateRequest(staticParams.nodeParams.nodePrivateKey, fundingScript, open.fundingFeerate, requestFunding, isChannelCreation = true, 0.msat)
}
val accept = AcceptDualFundedChannel(
temporaryChannelId = open.temporaryChannelId,
fundingAmount = fundingAmount,
dustLimit = localParams.dustLimit,
maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat,
htlcMinimum = localParams.htlcMinimum,
minimumDepth = minimumDepth.toLong(),
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = localFundingPubkey,
revocationBasepoint = channelKeys.revocationBasepoint,
paymentBasepoint = channelKeys.paymentBasepoint,
delayedPaymentBasepoint = channelKeys.delayedPaymentBasepoint,
htlcBasepoint = channelKeys.htlcBasepoint,
firstPerCommitmentPoint = channelKeys.commitmentPoint(0),
secondPerCommitmentPoint = channelKeys.commitmentPoint(1),
tlvStream = TlvStream(
buildSet {
add(ChannelTlv.ChannelTypeTlv(channelType))
willFund?.let { add(ChannelTlv.ProvideFundingTlv(it.willFund)) }
if (pushAmount > 0.msat) add(ChannelTlv.PushAmountTlv(pushAmount))
}
),
)
val remoteParams = RemoteParams(
nodeId = staticParams.remoteNodeId,
dustLimit = open.dustLimit,
maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat,
htlcMinimum = open.htlcMinimum,
toSelfDelay = open.toSelfDelay,
maxAcceptedHtlcs = open.maxAcceptedHtlcs,
revocationBasepoint = open.revocationBasepoint,
paymentBasepoint = open.paymentBasepoint,
delayedPaymentBasepoint = open.delayedPaymentBasepoint,
htlcBasepoint = open.htlcBasepoint,
features = remoteInit.features
)
val channelId = computeChannelId(open, accept)
val remoteFundingPubkey = open.fundingPubkey
val dustLimit = open.dustLimit.max(localParams.dustLimit)
val fundingParams = InteractiveTxParams(channelId, false, fundingAmount, open.fundingAmount, remoteFundingPubkey, open.lockTime, dustLimit, open.fundingFeerate)
when (val fundingContributions = FundingContributions.create(channelKeys, keyManager.swapInOnChainWallet, fundingParams, walletInputs, accept.pushAmount, open.pushAmount, null)) {
is Either.Left -> {
logger.error { "could not fund channel: ${fundingContributions.value}" }
replyTo.complete(ChannelFundingResponse.Failure.FundingFailure(fundingContributions.value))
Pair(Aborted, listOf(ChannelAction.Message.Send(Error(temporaryChannelId, ChannelFundingError(temporaryChannelId).message))))
}
is Either.Right -> {
val interactiveTxSession = InteractiveTxSession(staticParams.remoteNodeId, channelKeys, keyManager.swapInOnChainWallet, fundingParams, 0.msat, 0.msat, emptySet(), fundingContributions.value)
val nextState = WaitForFundingCreated(
replyTo,
// If our peer asks us to pay the commit tx fees, we accept (only used in tests, as we're otherwise always the channel opener).
localParams.copy(paysCommitTxFees = open.channelFlags.nonInitiatorPaysCommitFees),
remoteParams,
interactiveTxSession,
pushAmount,
open.pushAmount,
open.commitmentFeerate,
open.firstPerCommitmentPoint,
open.secondPerCommitmentPoint,
open.channelFlags,
channelConfig,
channelFeatures,
willFund?.purchase,
channelOrigin = null,
)
val actions = listOf(
ChannelAction.ChannelId.IdAssigned(staticParams.remoteNodeId, temporaryChannelId, channelId),
ChannelAction.Message.Send(accept),
ChannelAction.EmitEvent(ChannelEvents.Creating(nextState))
)
Pair(nextState, actions)
}
}
}
is Either.Left -> {
logger.error(res.value) { "invalid ${cmd.message::class} in state ${this::class}" }
replyTo.complete(ChannelFundingResponse.Failure.InvalidChannelParameters(res.value))
Pair(Aborted, listOf(ChannelAction.Message.Send(Error(temporaryChannelId, res.value.message))))
}
}
}
is Error -> {
logger.error { "peer sent error: ascii=${cmd.message.toAscii()} bin=${cmd.message.data.toHex()}" }
replyTo.complete(ChannelFundingResponse.Failure.AbortedByPeer(cmd.message.toAscii()))
return Pair(Aborted, listOf())
}
else -> unhandled(cmd)
}
is ChannelCommand.Close.MutualClose -> Pair(this@WaitForOpenChannel, listOf(ChannelAction.ProcessCmdRes.NotExecuted(cmd, CommandUnavailableInThisState(temporaryChannelId, stateName))))
is ChannelCommand.Close.ForceClose -> handleLocalError(cmd, ForcedLocalCommit(temporaryChannelId))
is ChannelCommand.Connected -> unhandled(cmd)
is ChannelCommand.Disconnected -> {
replyTo.complete(ChannelFundingResponse.Failure.Disconnected)
Pair(Aborted, listOf())
}
is ChannelCommand.Init -> unhandled(cmd)
is ChannelCommand.Commitment -> unhandled(cmd)
is ChannelCommand.Htlc -> unhandled(cmd)
is ChannelCommand.WatchReceived -> unhandled(cmd)
is ChannelCommand.Funding -> unhandled(cmd)
is ChannelCommand.Closing -> unhandled(cmd)
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy