Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ sealed class ChannelAction {
abstract val txId: TxId
data class ViaSpliceOut(val amount: Satoshi, override val miningFees: Satoshi, val address: String, override val txId: TxId) : StoreOutgoingPayment()
data class ViaSpliceCpfp(override val miningFees: Satoshi, override val txId: TxId) : StoreOutgoingPayment()
data class ViaInboundLiquidityRequest(override val txId: TxId, val lease: LiquidityAds.Lease) : StoreOutgoingPayment() {
override val miningFees: Satoshi = lease.fees.miningFee
}
data class ViaClose(val amount: Satoshi, override val miningFees: Satoshi, val address: String, override val txId: TxId, val isSentToDefaultAddress: Boolean, val closingType: ChannelClosingType) : StoreOutgoingPayment()
}
data class SetLocked(val txId: TxId) : Storage()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import fr.acinq.lightning.utils.UUID
import fr.acinq.lightning.utils.msat
import fr.acinq.lightning.wire.FailureMessage
import fr.acinq.lightning.wire.LightningMessage
import fr.acinq.lightning.wire.LiquidityAds
import fr.acinq.lightning.wire.OnionRoutingPacket
import kotlinx.coroutines.CompletableDeferred
import fr.acinq.lightning.wire.Init as InitMessage
Expand Down Expand Up @@ -83,7 +84,7 @@ sealed class ChannelCommand {
data class UpdateFee(val feerate: FeeratePerKw, val commit: Boolean = false) : Commitment(), ForbiddenDuringSplice
object CheckHtlcTimeout : Commitment()
sealed class Splice : Commitment() {
data class Request(val replyTo: CompletableDeferred<Response>, val spliceIn: SpliceIn?, val spliceOut: SpliceOut?, val feerate: FeeratePerKw, val origins: List<Origin.PayToOpenOrigin> = emptyList()) : Splice() {
data class Request(val replyTo: CompletableDeferred<Response>, val spliceIn: SpliceIn?, val spliceOut: SpliceOut?, val requestRemoteFunding: LiquidityAds.RequestRemoteFunding?, val feerate: FeeratePerKw, val origins: List<Origin.PayToOpenOrigin> = emptyList()) : Splice() {
val pushAmount: MilliSatoshi = spliceIn?.pushAmount ?: 0.msat
val spliceOutputs: List<TxOut> = spliceOut?.let { listOf(TxOut(it.amount, it.scriptPubKey)) } ?: emptyList()

Expand All @@ -101,14 +102,16 @@ sealed class ChannelCommand {
val fundingTxIndex: Long,
val fundingTxId: TxId,
val capacity: Satoshi,
val balance: MilliSatoshi
val balance: MilliSatoshi,
val liquidityPurchased: LiquidityAds.Lease?,
) : Response()

sealed class Failure : Response() {
object InsufficientFunds : Failure()
object InvalidSpliceOutPubKeyScript : Failure()
object SpliceAlreadyInProgress : Failure()
object ChannelNotIdle : Failure()
data class InvalidLiquidityAds(val reason: ChannelException) : Failure()
data class FundingFailure(val reason: FundingContributionFailure) : Failure()
object CannotStartSession : Failure()
data class InteractiveTxSessionFailed(val reason: InteractiveTxSessionAction.RemoteFailure) : Failure()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ data class MissingChannelType (override val channelId: Byte
data class DustLimitTooSmall (override val channelId: ByteVector32, val dustLimit: Satoshi, val min: Satoshi) : ChannelException(channelId, "dustLimit=$dustLimit is too small (min=$min)")
data class DustLimitTooLarge (override val channelId: ByteVector32, val dustLimit: Satoshi, val max: Satoshi) : ChannelException(channelId, "dustLimit=$dustLimit is too large (max=$max)")
data class ToSelfDelayTooHigh (override val channelId: ByteVector32, val toSelfDelay: CltvExpiryDelta, val max: CltvExpiryDelta) : ChannelException(channelId, "unreasonable to_self_delay=$toSelfDelay (max=$max)")
data class MissingLiquidityAds (override val channelId: ByteVector32) : ChannelException(channelId, "liquidity ads field is missing")
data class InvalidLiquidityAdsSig (override val channelId: ByteVector32) : ChannelException(channelId, "liquidity ads signature is invalid")
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)")
data class InvalidLiquidityRates (override val channelId: ByteVector32) : ChannelException(channelId, "rejecting liquidity ads proposed rates")
data class ChannelFundingError (override val channelId: ByteVector32) : ChannelException(channelId, "channel funding error")
data class RbfAttemptAborted (override val channelId: ByteVector32) : ChannelException(channelId, "rbf attempt aborted")
data class SpliceAborted (override val channelId: ByteVector32) : ChannelException(channelId, "splice aborted")
Expand Down
4 changes: 4 additions & 0 deletions src/commonMain/kotlin/fr/acinq/lightning/channel/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ object Helpers {
}
}

fun makeFundingPubKeyScript(localFundingPubkey: PublicKey, remoteFundingPubkey: PublicKey): ByteVector {
return write(pay2wsh(multiSig2of2(localFundingPubkey, remoteFundingPubkey))).toByteVector()
}

fun makeFundingInputInfo(
fundingTxId: TxId,
fundingTxOutputIndex: Int,
Expand Down
22 changes: 16 additions & 6 deletions src/commonMain/kotlin/fr/acinq/lightning/channel/InteractiveTx.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ data class InteractiveTxParams(

fun fundingPubkeyScript(channelKeys: KeyManager.ChannelKeys): ByteVector {
val fundingTxIndex = (sharedInput as? SharedFundingInput.Multisig2of2)?.let { it.fundingTxIndex + 1 } ?: 0
return Script.write(Script.pay2wsh(Scripts.multiSig2of2(channelKeys.fundingPubKey(fundingTxIndex), remoteFundingPubkey))).toByteVector()
return Helpers.Funding.makeFundingPubKeyScript(channelKeys.fundingPubKey(fundingTxIndex), remoteFundingPubkey)
}
}

Expand Down Expand Up @@ -751,8 +751,9 @@ data class InteractiveTxSigningSession(
val fundingParams: InteractiveTxParams,
val fundingTxIndex: Long,
val fundingTx: PartiallySignedSharedTransaction,
val liquidityPurchased: LiquidityAds.Lease?,
val localCommit: Either<UnsignedLocalCommit, LocalCommit>,
val remoteCommit: RemoteCommit
val remoteCommit: RemoteCommit,
) {

// Example flow:
Expand Down Expand Up @@ -826,6 +827,7 @@ data class InteractiveTxSigningSession(
sharedTx: SharedTransaction,
localPushAmount: MilliSatoshi,
remotePushAmount: MilliSatoshi,
liquidityPurchased: LiquidityAds.Lease?,
localCommitmentIndex: Long,
remoteCommitmentIndex: Long,
commitTxFeerate: FeeratePerKw,
Expand All @@ -834,13 +836,14 @@ data class InteractiveTxSigningSession(
val channelKeys = channelParams.localParams.channelKeys(keyManager)
val unsignedTx = sharedTx.buildUnsignedTx()
val sharedOutputIndex = unsignedTx.txOut.indexOfFirst { it.publicKeyScript == fundingParams.fundingPubkeyScript(channelKeys) }
val liquidityFees = liquidityPurchased?.fees?.total?.toMilliSatoshi() ?: 0.msat
return Helpers.Funding.makeCommitTxsWithoutHtlcs(
channelKeys,
channelParams.channelId,
channelParams.localParams, channelParams.remoteParams,
fundingAmount = sharedTx.sharedOutput.amount,
toLocal = sharedTx.sharedOutput.localAmount - localPushAmount + remotePushAmount,
toRemote = sharedTx.sharedOutput.remoteAmount - remotePushAmount + localPushAmount,
toLocal = sharedTx.sharedOutput.localAmount - localPushAmount + remotePushAmount - liquidityFees,
toRemote = sharedTx.sharedOutput.remoteAmount - remotePushAmount + localPushAmount + liquidityFees,
localCommitmentIndex = localCommitmentIndex,
remoteCommitmentIndex = remoteCommitmentIndex,
commitTxFeerate,
Expand Down Expand Up @@ -869,7 +872,7 @@ data class InteractiveTxSigningSession(
val unsignedLocalCommit = UnsignedLocalCommit(localCommitmentIndex, firstCommitTx.localSpec, firstCommitTx.localCommitTx, listOf())
val remoteCommit = RemoteCommit(remoteCommitmentIndex, firstCommitTx.remoteSpec, firstCommitTx.remoteCommitTx.tx.txid, remotePerCommitmentPoint)
val signedFundingTx = sharedTx.sign(keyManager, fundingParams, channelParams.localParams, channelParams.remoteParams.nodeId)
Pair(InteractiveTxSigningSession(fundingParams, fundingTxIndex, signedFundingTx, Either.Left(unsignedLocalCommit), remoteCommit), commitSig)
Pair(InteractiveTxSigningSession(fundingParams, fundingTxIndex, signedFundingTx, liquidityPurchased, Either.Left(unsignedLocalCommit), remoteCommit), commitSig)
}
}

Expand Down Expand Up @@ -900,7 +903,14 @@ sealed class RbfStatus {
sealed class SpliceStatus {
object None : SpliceStatus()
data class Requested(val command: ChannelCommand.Commitment.Splice.Request, val spliceInit: SpliceInit) : SpliceStatus()
data class InProgress(val replyTo: CompletableDeferred<ChannelCommand.Commitment.Splice.Response>?, val spliceSession: InteractiveTxSession, val localPushAmount: MilliSatoshi, val remotePushAmount: MilliSatoshi, val origins: List<Origin.PayToOpenOrigin>) : SpliceStatus()
data class InProgress(
val replyTo: CompletableDeferred<ChannelCommand.Commitment.Splice.Response>?,
val spliceSession: InteractiveTxSession,
val localPushAmount: MilliSatoshi,
val remotePushAmount: MilliSatoshi,
val liquidityPurchased: LiquidityAds.Lease?,
val origins: List<Origin.PayToOpenOrigin>
) : SpliceStatus()
data class WaitingForSigs(val session: InteractiveTxSigningSession, val origins: List<Origin.PayToOpenOrigin>) : SpliceStatus()
object Aborted : SpliceStatus()
}
Loading