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
12 changes: 10 additions & 2 deletions modules/core/src/commonMain/kotlin/fr/acinq/lightning/Features.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ sealed class Feature {
override val scopes: Set<FeatureScope> get() = setOf(FeatureScope.Init, FeatureScope.Node)
}

@Serializable
object ProvideStorage : Feature() {
override val rfcName get() = "option_provide_storage"
override val mandatory get() = 42
override val scopes: Set<FeatureScope> get() = setOf(FeatureScope.Init, FeatureScope.Node)
}

@Serializable
object ChannelType : Feature() {
override val rfcName get() = "option_channel_type"
Expand Down Expand Up @@ -231,15 +238,15 @@ sealed class Feature {
override val scopes: Set<FeatureScope> get() = setOf(FeatureScope.Init, FeatureScope.Node)
}

/** This feature bit should be activated when a node wants to send channel backups to their peers. */
/** This feature is deprecated but must be kept to allow deserialization of old backups. */
@Serializable
object ChannelBackupClient : Feature() {
override val rfcName get() = "channel_backup_client"
override val mandatory get() = 144
override val scopes: Set<FeatureScope> get() = setOf(FeatureScope.Init)
}

/** This feature bit should be activated when a node stores channel backups for their peers. */
/** This feature is deprecated but must be kept to allow deserialization of old backups. */
@Serializable
object ChannelBackupProvider : Feature() {
override val rfcName get() = "channel_backup_provider"
Expand Down Expand Up @@ -344,6 +351,7 @@ data class Features(val activated: Map<Feature, FeatureSupport>, val unknown: Se
Feature.ShutdownAnySegwit,
Feature.DualFunding,
Feature.Quiescence,
Feature.ProvideStorage,
Feature.ChannelType,
Feature.PaymentMetadata,
Feature.TrampolinePayment,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ data class NodeParams(
val paymentRecipientExpiryParams: RecipientCltvExpiryParams,
val zeroConfPeers: Set<PublicKey>,
val liquidityPolicy: MutableStateFlow<LiquidityPolicy>,
val usePeerStorage: Boolean,
Comment thread
t-bast marked this conversation as resolved.
) {
val nodePrivateKey get() = keyManager.nodeKeys.nodeKey.privateKey
val nodeId get() = keyManager.nodeKeys.nodeKey.publicKey
Expand Down Expand Up @@ -211,7 +212,6 @@ data class NodeParams(
Feature.ExperimentalTrampolinePayment to FeatureSupport.Optional,
Feature.ZeroReserveChannels to FeatureSupport.Optional,
Feature.WakeUpNotificationClient to FeatureSupport.Optional,
Feature.ChannelBackupClient to FeatureSupport.Optional,
Feature.ExperimentalSplice to FeatureSupport.Optional,
Feature.OnTheFlyFunding to FeatureSupport.Optional,
Feature.FundingFeeCredit to FeatureSupport.Optional,
Expand Down Expand Up @@ -258,6 +258,7 @@ data class NodeParams(
maxAllowedFeeCredit = 0.msat
)
),
usePeerStorage = true,
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,6 @@ data class Commitments(
val payments: Map<Long, UUID>, // for outgoing htlcs, maps to paymentId
val remoteNextCommitInfo: Either<WaitingForRevocation, PublicKey>, // this one is tricky, it must be kept in sync with Commitment.nextRemoteCommit
val remotePerCommitmentSecrets: ShaChain,
val remoteChannelData: EncryptedChannelData = EncryptedChannelData.empty
) {
init {
require(active.isNotEmpty()) { "there must be at least one active commitment" }
Expand Down Expand Up @@ -799,7 +798,6 @@ data class Commitments(
localChanges = changes.localChanges.copy(acked = emptyList()),
remoteChanges = changes.remoteChanges.copy(proposed = emptyList(), acked = changes.remoteChanges.acked + changes.remoteChanges.proposed)
),
remoteChannelData = commits.last().channelData // the last message is the most recent
)
return Either.Right(Pair(commitments1, revocation))
}
Expand Down Expand Up @@ -854,7 +852,6 @@ data class Commitments(
remoteNextCommitInfo = Either.Right(revocation.nextPerCommitmentPoint),
remotePerCommitmentSecrets = remotePerCommitmentSecrets.addHash(revocation.perCommitmentSecret.value, 0xFFFFFFFFFFFFL - remoteCommitIndex),
payments = payments1,
remoteChannelData = revocation.channelData
)
return Either.Right(Pair(commitments1, actions.toList()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,12 @@ sealed class ChannelState {
suspend fun ChannelContext.process(cmd: ChannelCommand): Pair<ChannelState, List<ChannelAction>> {
return try {
processInternal(cmd)
.let { (newState, actions) -> Pair(newState, newState.run { maybeAddBackupToMessages(actions) }) }
.let { (newState, actions) -> Pair(newState, actions + onTransition(newState)) }
} catch (t: Throwable) {
handleLocalError(cmd, t)
}
}

/** Update outgoing messages to include an encrypted backup when necessary. */
private fun ChannelContext.maybeAddBackupToMessages(actions: List<ChannelAction>): List<ChannelAction> = when {
this@ChannelState is PersistedChannelState && staticParams.nodeParams.features.hasFeature(Feature.ChannelBackupClient) -> actions.map {
when {
it is ChannelAction.Message.Send && it.message is TxSignatures -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
it is ChannelAction.Message.Send && it.message is CommitSig -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
it is ChannelAction.Message.Send && it.message is RevokeAndAck -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
it is ChannelAction.Message.Send && it.message is Shutdown -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
it is ChannelAction.Message.Send && it.message is ClosingComplete -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
it is ChannelAction.Message.Send && it.message is ClosingSig -> it.copy(message = it.message.withChannelData(EncryptedChannelData.from(privateKey, this@ChannelState), logger))
else -> it
}
}
else -> actions
}

/** Add actions for some transitions */
private fun ChannelContext.onTransition(newState: ChannelState): List<ChannelAction> {
val oldState = when (this@ChannelState) {
Expand Down Expand Up @@ -300,7 +283,7 @@ sealed class ChannelState {
sealed class PersistedChannelState : ChannelState() {
abstract val channelId: ByteVector32

internal fun ChannelContext.createChannelReestablish(): HasEncryptedChannelData = when (val state = this@PersistedChannelState) {
internal fun ChannelContext.createChannelReestablish(): ChannelReestablish = when (val state = this@PersistedChannelState) {
is WaitForFundingSigned -> {
val myFirstPerCommitmentPoint = keyManager.channelKeys(state.channelParams.localParams.fundingKeyPath).commitmentPoint(0)
ChannelReestablish(
Expand All @@ -310,7 +293,7 @@ sealed class PersistedChannelState : ChannelState() {
yourLastCommitmentSecret = PrivateKey(ByteVector32.Zeroes),
myCurrentPerCommitmentPoint = myFirstPerCommitmentPoint,
TlvStream(ChannelReestablishTlv.NextFunding(state.signingSession.fundingTx.txId))
).withChannelData(state.remoteChannelData, logger)
)
}
is ChannelStateWithCommitments -> {
val yourLastPerCommitmentSecret = state.commitments.remotePerCommitmentSecrets.lastIndex?.let { state.commitments.remotePerCommitmentSecrets.getHash(it) } ?: ByteVector32.Zeroes
Expand Down Expand Up @@ -341,7 +324,7 @@ sealed class PersistedChannelState : ChannelState() {
yourLastCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret),
myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint,
tlvStream = tlvs
).withChannelData(state.commitments.remoteChannelData, logger)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,6 @@ data class Closing(
null -> Pair(closing1, listOf())
else -> {
logger.info { "channel is now closed" }
if (closingType !is MutualClose) {
logger.debug { "last known remoteChannelData=${commitments.remoteChannelData}" }
}
Pair(Closed(closing1), listOf(setClosingStatus(closingType)))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ data class Normal(
logger.info { "waiting for tx_sigs" }
Pair(this@Normal.copy(spliceStatus = spliceStatus.copy(session = signingSession1)), listOf())
}
is InteractiveTxSigningSessionAction.SendTxSigs -> sendSpliceTxSigs(spliceStatus.origins, action, spliceStatus.liquidityPurchase, cmd.message.channelData)
is InteractiveTxSigningSessionAction.SendTxSigs -> sendSpliceTxSigs(spliceStatus.origins, action, spliceStatus.liquidityPurchase)
}
}
ignoreRetransmittedCommitSig(cmd.message) -> {
Expand Down Expand Up @@ -299,7 +299,7 @@ data class Normal(
}
is Either.Right -> {
// no, let's sign right away
val newState = this@Normal.copy(remoteShutdown = cmd.message, commitments = commitments.copy(remoteChannelData = cmd.message.channelData))
val newState = this@Normal.copy(remoteShutdown = cmd.message)
Pair(newState, listOf(ChannelAction.Message.SendToSelf(ChannelCommand.Commitment.Sign)))
}
}
Expand All @@ -309,12 +309,11 @@ data class Normal(
val actions = mutableListOf<ChannelAction>()
val localShutdown = this@Normal.localShutdown ?: Shutdown(channelId, commitments.params.localParams.defaultFinalScriptPubKey)
if (this@Normal.localShutdown == null) actions.add(ChannelAction.Message.Send(localShutdown))
val commitments1 = commitments.copy(remoteChannelData = cmd.message.channelData)
when {
commitments1.hasNoPendingHtlcsOrFeeUpdate() -> startClosingNegotiation(closeCommand, commitments1, localShutdown, cmd.message, actions)
commitments.hasNoPendingHtlcsOrFeeUpdate() -> startClosingNegotiation(closeCommand, commitments, localShutdown, cmd.message, actions)
else -> {
// there are some pending changes, we need to wait for them to be settled (fail/fulfill htlcs and sign fee updates)
val nextState = ShuttingDown(commitments1, localShutdown, cmd.message, closeCommand)
val nextState = ShuttingDown(commitments, localShutdown, cmd.message, closeCommand)
actions.add(ChannelAction.Storage.StoreState(nextState))
Pair(nextState, actions)
}
Expand Down Expand Up @@ -656,7 +655,7 @@ data class Normal(
}
is Either.Right -> {
val action: InteractiveTxSigningSessionAction.SendTxSigs = res.value
sendSpliceTxSigs(spliceStatus.origins, action, spliceStatus.liquidityPurchase, cmd.message.channelData)
sendSpliceTxSigs(spliceStatus.origins, action, spliceStatus.liquidityPurchase)
}
}
}
Expand Down Expand Up @@ -837,12 +836,11 @@ data class Normal(
origins: List<Origin>,
action: InteractiveTxSigningSessionAction.SendTxSigs,
liquidityPurchase: LiquidityAds.Purchase?,
remoteChannelData: EncryptedChannelData
): Pair<Normal, List<ChannelAction>> {
logger.info { "sending tx_sigs" }
// We watch for confirmation in all cases, to allow pruning outdated commitments when transactions confirm.
val watchConfirmed = WatchConfirmed(channelId, action.commitment.fundingTxId, action.commitment.commitInput.txOut.publicKeyScript, staticParams.nodeParams.minDepthBlocks, WatchConfirmed.ChannelFundingDepthOk)
val commitments = commitments.add(action.commitment).copy(remoteChannelData = remoteChannelData)
val commitments = commitments.add(action.commitment)
val nextState = this@Normal.copy(commitments = commitments, spliceStatus = SpliceStatus.None)
val actions = buildList {
add(ChannelAction.Storage.StoreState(nextState))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package fr.acinq.lightning.channel.states

import fr.acinq.bitcoin.utils.Either
import fr.acinq.lightning.Feature
import fr.acinq.lightning.ShortChannelId
import fr.acinq.lightning.blockchain.WatchConfirmed
import fr.acinq.lightning.blockchain.WatchConfirmedTriggered
Expand Down Expand Up @@ -39,11 +38,11 @@ data class Offline(val state: PersistedChannelState) : ChannelState() {
}
is ChannelStateWithCommitments -> {
logger.info { "syncing ${state::class}" }
val sendChannelReestablish = !staticParams.nodeParams.features.hasFeature(Feature.ChannelBackupClient)
val sendChannelReestablish = !staticParams.nodeParams.usePeerStorage
val actions = buildList {
if (!sendChannelReestablish) {
// We wait for them to go first, which lets us restore from the latest backup if we've lost data.
logger.info { "waiting for their channel_reestablish message" }
logger.info { "waiting for their peer_storage_retrieval and channel_reestablish message" }
} else {
val channelReestablish = state.run { createChannelReestablish() }
add(ChannelAction.Message.Send(channelReestablish))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ data class WaitForFundingConfirmed(
}
is Either.Right -> {
val action: InteractiveTxSigningSessionAction.SendTxSigs = res.value
sendRbfTxSigs(action, cmd.message.channelData)
sendRbfTxSigs(action)
}
}
}
Expand Down Expand Up @@ -227,7 +227,7 @@ data class WaitForFundingConfirmed(
}
// No need to store their commit_sig, they will re-send it if we disconnect.
InteractiveTxSigningSessionAction.WaitForTxSigs -> Pair(this@WaitForFundingConfirmed.copy(rbfStatus = rbfStatus.copy(session = signingSession1)), listOf())
is InteractiveTxSigningSessionAction.SendTxSigs -> sendRbfTxSigs(action, cmd.message.channelData)
is InteractiveTxSigningSessionAction.SendTxSigs -> sendRbfTxSigs(action)
}
}
else -> {
Expand Down Expand Up @@ -322,12 +322,12 @@ data class WaitForFundingConfirmed(
}
}

private fun ChannelContext.sendRbfTxSigs(action: InteractiveTxSigningSessionAction.SendTxSigs, remoteChannelData: EncryptedChannelData): Pair<WaitForFundingConfirmed, List<ChannelAction>> {
private fun ChannelContext.sendRbfTxSigs(action: InteractiveTxSigningSessionAction.SendTxSigs): Pair<WaitForFundingConfirmed, List<ChannelAction>> {
logger.info { "rbf funding tx created with txId=${action.fundingTx.txId}, ${action.fundingTx.sharedTx.tx.localInputs.size} local inputs, ${action.fundingTx.sharedTx.tx.remoteInputs.size} remote inputs, ${action.fundingTx.sharedTx.tx.localOutputs.size} local outputs and ${action.fundingTx.sharedTx.tx.remoteOutputs.size} remote outputs" }
logger.info { "will wait for ${staticParams.nodeParams.minDepthBlocks} confirmations" }
val watchConfirmed = WatchConfirmed(channelId, action.commitment.fundingTxId, action.commitment.commitInput.txOut.publicKeyScript, staticParams.nodeParams.minDepthBlocks, WatchConfirmed.ChannelFundingDepthOk)
val nextState = WaitForFundingConfirmed(
commitments.add(action.commitment).copy(remoteChannelData = remoteChannelData),
commitments.add(action.commitment),
waitingSinceBlock,
deferred,
RbfStatus.None
Expand Down
Loading