Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ kotlinx-coroutines = "1.10.2"
kotlinx-datetime = "0.6.2"
kotlinx-serialization = "1.8.1"
ktor = "3.1.2"
bitcoinkmp = "0.25.1" # when upgrading bitcoin-kmp, keep secpjnijvm in sync!
secpjnijvm = "0.18.0"
bitcoinkmp = "0.26.0" # when upgrading bitcoin-kmp, keep secpjnijvm in sync!
secpjnijvm = "0.19.0"
kermit = "2.0.5"
slf4j = "2.0.16"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ data class FundingContributions(val inputs: List<InteractiveTxInput.Outgoing>, v
swapInProtocol.userPublicKey, swapInProtocol.serverPublicKey, swapInProtocol.userRefundKey, swapInProtocol.refundDelay
)
}

else -> InteractiveTxInput.LocalLegacySwapIn(
0,
i.previousTx.stripInputWitnesses(),
Expand Down Expand Up @@ -714,6 +715,7 @@ data class InteractiveTxSession(
Pair(next, InteractiveTxSessionAction.SendMessage(txComplete))
}
}

is Either.Left -> {
val inputOutgoing = msg.value
val txAddInput = when (inputOutgoing) {
Expand All @@ -722,27 +724,32 @@ data class InteractiveTxSession(
val swapInParams = TxAddInputTlv.SwapInParamsLegacy(swapInKeys.userPublicKey, swapInKeys.remoteServerPublicKey, swapInKeys.refundDelay)
TxAddInput(fundingParams.channelId, inputOutgoing.serialId, inputOutgoing.previousTx, inputOutgoing.previousTxOutput, inputOutgoing.sequence, TlvStream(swapInParams))
}

is InteractiveTxInput.LocalSwapIn -> {
val swapInProtocol = swapInKeys.getSwapInProtocol(inputOutgoing.addressIndex)
val swapInParams = TxAddInputTlv.SwapInParams(swapInProtocol.userPublicKey, swapInProtocol.serverPublicKey, swapInProtocol.userRefundKey, swapInProtocol.refundDelay)
TxAddInput(fundingParams.channelId, inputOutgoing.serialId, inputOutgoing.previousTx, inputOutgoing.previousTxOutput, inputOutgoing.sequence, TlvStream(swapInParams))
}

is InteractiveTxInput.Shared -> TxAddInput(fundingParams.channelId, inputOutgoing.serialId, inputOutgoing.outPoint, inputOutgoing.sequence)
}
val nextSecretNonces = when (inputOutgoing) {
// Generate a secret nonce for this input if we don't already have one.
is InteractiveTxInput.LocalSwapIn -> when (secretNonces[inputOutgoing.serialId]) {
null -> {
val secretNonce = Musig2.generateNonce(randomBytes32(), swapInKeys.userPrivateKey, swapInKeys.userPublicKey, listOf(swapInKeys.userPublicKey, swapInKeys.remoteServerPublicKey), null, null)
val secretNonce = Musig2.generateNonce(randomBytes32(), Either.Right(swapInKeys.userPublicKey), listOf(swapInKeys.userPublicKey, swapInKeys.remoteServerPublicKey), null, null)
secretNonces + (inputOutgoing.serialId to secretNonce)
}

else -> secretNonces
}

else -> secretNonces
}
val next = copy(toSend = toSend.tail(), localInputs = localInputs + msg.value, txCompleteSent = null, secretNonces = nextSecretNonces)
Pair(next, InteractiveTxSessionAction.SendMessage(txAddInput))
}

is Either.Right -> {
val outputOutgoing = msg.value
val next = copy(toSend = toSend.tail(), localOutputs = localOutputs + outputOutgoing, txCompleteSent = null)
Expand Down Expand Up @@ -796,6 +803,7 @@ data class InteractiveTxSession(
message.swapInParams.userRefundKey,
message.swapInParams.refundDelay
)

message.swapInParamsLegacy != null -> InteractiveTxInput.RemoteLegacySwapIn(
message.serialId,
outpoint,
Expand All @@ -805,6 +813,7 @@ data class InteractiveTxSession(
message.swapInParamsLegacy.serverKey,
message.swapInParamsLegacy.refundDelay
)

else -> InteractiveTxInput.RemoteOnly(message.serialId, outpoint, txOut, message.sequence)
}
}
Expand All @@ -819,11 +828,13 @@ data class InteractiveTxSession(
// Generate a secret nonce for this input if we don't already have one.
is InteractiveTxInput.RemoteSwapIn -> when (secretNonces[input.serialId]) {
null -> {
val secretNonce = Musig2.generateNonce(randomBytes32(), swapInKeys.localServerPrivateKey(remoteNodeId), input.serverKey, listOf(input.userKey, input.serverKey), null, null)
val secretNonce = Musig2.generateNonce(randomBytes32(), Either.Right(input.serverKey), listOf(input.userKey, input.serverKey), null, null)
secretNonces + (input.serialId to secretNonce)
}

else -> secretNonces
}

else -> secretNonces
}
val session1 = this.copy(remoteInputs = remoteInputs + input, inputsReceivedCount = inputsReceivedCount + 1, txCompleteReceived = null, secretNonces = secretNonces1)
Expand Down Expand Up @@ -859,12 +870,14 @@ data class InteractiveTxSession(
{ next -> next.send() }
)
}

is TxAddOutput -> {
receiveOutput(message).fold(
{ f -> Pair(this, f) },
{ output -> copy(remoteOutputs = remoteOutputs + output, outputsReceivedCount = outputsReceivedCount + 1, txCompleteReceived = null).send() }
)
}

is TxRemoveInput -> {
val remoteInputs1 = remoteInputs.filterNot { i -> (i as InteractiveTxInput).serialId == message.serialId }
if (remoteInputs.size != remoteInputs1.size) {
Expand All @@ -874,6 +887,7 @@ data class InteractiveTxSession(
Pair(this, InteractiveTxSessionAction.UnknownSerialId(message.channelId, message.serialId))
}
}

is TxRemoveOutput -> {
val remoteOutputs1 = remoteOutputs.filterNot { o -> (o as InteractiveTxOutput).serialId == message.serialId }
if (remoteOutputs.size != remoteOutputs1.size) {
Expand All @@ -883,6 +897,7 @@ data class InteractiveTxSession(
Pair(this, InteractiveTxSessionAction.UnknownSerialId(message.channelId, message.serialId))
}
}

is TxComplete -> {
val next = copy(txCompleteReceived = message)
if (next.isComplete) {
Expand Down Expand Up @@ -1028,6 +1043,7 @@ data class InteractiveTxSigningSession(
is Either.Left -> localCommit.value.commitTx.input
is Either.Right -> localCommit.value.publishableTxs.commitTx.input
}

// This value tells our peer whether we need them to retransmit their commit_sig on reconnection or not.
val reconnectNextLocalCommitmentNumber = when (localCommit) {
is Either.Left -> localCommit.value.index
Expand All @@ -1039,7 +1055,8 @@ data class InteractiveTxSigningSession(
is Either.Left -> {
val localCommitIndex = localCommit.value.index
val localPerCommitmentPoint = channelKeys.commitmentPoint(localCommitIndex)
when (val signedLocalCommit = LocalCommit.fromCommitSig(channelKeys, channelParams, fundingTxIndex, fundingParams.remoteFundingPubkey, commitInput, remoteCommitSig, localCommitIndex, localCommit.value.spec, localPerCommitmentPoint, logger)) {
when (val signedLocalCommit =
LocalCommit.fromCommitSig(channelKeys, channelParams, fundingTxIndex, fundingParams.remoteFundingPubkey, commitInput, remoteCommitSig, localCommitIndex, localCommit.value.spec, localPerCommitmentPoint, logger)) {
is Either.Left -> {
val fundingKey = channelKeys.fundingKey(fundingTxIndex)
val localSigOfLocalTx = Transactions.sign(localCommit.value.commitTx, fundingKey)
Expand All @@ -1051,6 +1068,7 @@ data class InteractiveTxSigningSession(
logger.info { "signedLocalCommitTx=$signedLocalCommitTx" }
Pair(this, InteractiveTxSigningSessionAction.AbortFundingAttempt(signedLocalCommit.value))
}

is Either.Right -> {
if (shouldSignFirst(fundingParams.isInitiator, channelParams, fundingTx.tx)) {
val fundingStatus = LocalFundingStatus.UnconfirmedFundingTx(fundingTx, fundingParams, currentBlockHeight)
Expand All @@ -1063,6 +1081,7 @@ data class InteractiveTxSigningSession(
}
}
}

is Either.Right -> Pair(this, InteractiveTxSigningSessionAction.WaitForTxSigs)
}
}
Expand Down Expand Up @@ -1180,6 +1199,7 @@ sealed class QuiescenceNegotiation : SpliceStatus() {
abstract class Initiator : QuiescenceNegotiation() {
abstract val command: ChannelCommand.Commitment.Splice.Request
}

abstract class NonInitiator : QuiescenceNegotiation()
}

Expand All @@ -1188,25 +1208,33 @@ sealed class QuiescentSpliceStatus : SpliceStatus()

sealed class SpliceStatus {
data object None : SpliceStatus()

/** We stop sending new updates and wait for our updates to be added to the local and remote commitments. */
data class QuiescenceRequested(override val command: ChannelCommand.Commitment.Splice.Request) : QuiescenceNegotiation.Initiator()

/** Our updates have been added to the local and remote commitments, we wait for our peer to do the same. */
data class InitiatorQuiescent(override val command: ChannelCommand.Commitment.Splice.Request) : QuiescenceNegotiation.Initiator()

/** Our peer has asked us to stop sending new updates and wait for our updates to be added to the local and remote commitments. */
data class ReceivedStfu(val stfu: Stfu) : QuiescenceNegotiation.NonInitiator()

/** Our updates have been added to the local and remote commitments, we wait for our peer to use the now quiescent channel. */
data object NonInitiatorQuiescent : QuiescentSpliceStatus()

/** We told our peer we want to splice funds in the channel. */
data class Requested(val command: ChannelCommand.Commitment.Splice.Request, val spliceInit: SpliceInit) : QuiescentSpliceStatus()

/** We both agreed to splice and are building the splice transaction. */
data class InProgress(
val replyTo: CompletableDeferred<ChannelFundingResponse>?,
val spliceSession: InteractiveTxSession,
val liquidityPurchase: LiquidityAds.Purchase?,
val origins: List<Origin>
) : QuiescentSpliceStatus()

/** The splice transaction has been negotiated, we're exchanging signatures. */
data class WaitingForSigs(val session: InteractiveTxSigningSession, val liquidityPurchase: LiquidityAds.Purchase?, val origins: List<Origin>) : QuiescentSpliceStatus()

/** The splice attempt was aborted by us, we're waiting for our peer to ack. */
data object Aborted : QuiescentSpliceStatus()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import fr.acinq.bitcoin.Script.pay2wsh
import fr.acinq.bitcoin.Script.write
import fr.acinq.bitcoin.crypto.Pack
import fr.acinq.bitcoin.crypto.musig2.Musig2
import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.CltvExpiry
import fr.acinq.lightning.CltvExpiryDelta
import fr.acinq.lightning.Lightning.randomBytes32
Expand Down Expand Up @@ -514,8 +515,8 @@ class TransactionsTestsCommon : LightningTestSuite() {
)
// The first step of a musig2 signing session is to exchange nonces.
// If participants are disconnected before the end of the signing session, they must start again with fresh nonces.
val userNonce = Musig2.generateNonce(randomBytes32(), userPrivateKey, userPrivateKey.publicKey(), listOf(userPrivateKey.publicKey(), serverPrivateKey.publicKey()), null, null)
val serverNonce = Musig2.generateNonce(randomBytes32(), serverPrivateKey, serverPrivateKey.publicKey(), listOf(serverPrivateKey.publicKey(), userPrivateKey.publicKey()), null, null)
val userNonce = Musig2.generateNonce(randomBytes32(), Either.Right(userPrivateKey.publicKey()), listOf(userPrivateKey.publicKey(), serverPrivateKey.publicKey()), null, null)
val serverNonce = Musig2.generateNonce(randomBytes32(), Either.Right(serverPrivateKey.publicKey()), listOf(serverPrivateKey.publicKey(), userPrivateKey.publicKey()), null, null)

// Once they have each other's public nonce, they can produce partial signatures.
val userSig = swapInProtocol.signSwapInputUser(tx, 0, swapInTx.txOut, userPrivateKey, userNonce.first, userNonce.second, serverNonce.second).right!!
Expand Down