From 9cc48128201ff492e0696a6e398ab1645a60c8d2 Mon Sep 17 00:00:00 2001 From: t-bast Date: Wed, 4 Mar 2026 11:59:35 +0100 Subject: [PATCH] Remove support for legacy splicing protocol We remove support for the legacy splicing protocol used by older Phoenix wallets. Phoenix users can simply update their app to get the latest version that supports the official splicing protocol. --- .../main/scala/fr/acinq/eclair/Features.scala | 7 - .../fr/acinq/eclair/channel/Commitments.scala | 2 - .../fr/acinq/eclair/channel/fsm/Channel.scala | 173 +++--------------- .../channel/fund/InteractiveTxBuilder.scala | 7 - .../fr/acinq/eclair/io/PeerConnection.scala | 54 ------ .../eclair/wire/protocol/ChannelTlv.scala | 61 +----- .../wire/protocol/InteractiveTxTlv.scala | 8 - .../protocol/LightningMessageCodecs.scala | 22 --- .../wire/protocol/LightningMessageTypes.scala | 57 +----- .../fundee/data.json | 2 - .../funder/data.json | 2 - .../funder/data.json | 2 - .../fundee/data.json | 2 - .../funder/data.json | 2 - .../fundee/data.json | 2 - .../funder/data.json | 2 - .../funder/data.json | 2 - .../announced-splice/data.json | 2 - .../050006-DATA_NORMAL/announced/data.json | 2 - .../050006-DATA_NORMAL/fundee/data.json | 2 - .../050006-DATA_NORMAL/funder/data.json | 2 - .../splice-commitment-upgrade/data.json | 2 - .../anchor-outputs/data.json | 2 - .../050007-DATA_SHUTDOWN/taproot/data.json | 2 - .../050008-DATA_NEGOTIATING/fundee/data.json | 2 - .../anchor-outputs/data.json | 2 - .../taproot/data.json | 2 - .../05000a-DATA_CLOSING/local/data.json | 2 - .../05000a-DATA_CLOSING/next-remote/data.json | 2 - .../05000a-DATA_CLOSING/remote/data.json | 2 - .../05000a-DATA_CLOSING/revoked/data.json | 2 - .../states/e/NormalSplicesStateSpec.scala | 118 +----------- .../acinq/eclair/io/PeerConnectionSpec.scala | 130 +------------ .../protocol/LightningMessageCodecsSpec.scala | 94 +--------- 34 files changed, 44 insertions(+), 733 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala index d093969876..988b2ef49d 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Features.scala @@ -432,12 +432,6 @@ object Features { val mandatory = 152 } - // TODO: @pm47 custom splices implementation for phoenix, to be replaced once splices is spec-ed (currently reserved here: https://github.com/lightning/bolts/issues/605) - case object SplicePrototype extends Feature with InitFeature { - val rfcName = "splice_prototype" - val mandatory = 154 - } - case object SimpleTaprootChannelsPhoenix extends Feature with InitFeature with NodeFeature with ChannelTypeFeature { val rfcName = "option_simple_taproot_phoenix" val mandatory = 564 @@ -492,7 +486,6 @@ object Features { WakeUpNotificationClient, TrampolinePaymentPrototype, AsyncPaymentPrototype, - SplicePrototype, OnTheFlyFunding, FundingFeeCredit, PhoenixZeroReserve diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala index 2af018db84..81ffef169e 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -33,8 +33,6 @@ case class ChannelParams(channelId: ByteVector32, val remoteNodeId: PublicKey = remoteParams.nodeId // If we've set the 0-conf feature bit for this peer, we will always use 0-conf with them. val zeroConf: Boolean = localParams.initFeatures.hasFeature(Features.ZeroConf) - // TODO: we keep supporting the legacy splicing protocol for non-upgraded Phoenix users. - lazy val useLegacySpliceProtocol = remoteParams.initFeatures.hasFeature(Features.SplicePrototype) /** We update local/global features at reconnection. */ def updateFeatures(localInit: Init, remoteInit: Init): ChannelParams = copy( diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala index af889c5140..0263a3d562 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala @@ -247,8 +247,6 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall // we record the announcement_signatures messages we already sent to avoid unnecessary retransmission var announcementSigsSent = Set.empty[RealShortChannelId] // we keep track of the splice_locked we sent after channel_reestablish and it's funding tx index to avoid sending it again - // TODO: we can remove that once we stop supporting the legacy splicing protocol - private var spliceLockedSent = Map.empty[TxId, Long] private def trimAnnouncementSigsStashIfNeeded(): Unit = { if (announcementSigsStash.size >= 10) { @@ -260,17 +258,6 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall } } - private def trimSpliceLockedSentIfNeeded(): Unit = { - if (spliceLockedSent.size >= 10) { - // We shouldn't store an unbounded number of splice_locked: on long-lived connections where we do a lot of splice - // transactions, we only need to keep track of the most recent ones. - val oldestFundingTxId = spliceLockedSent.toSeq - .sortBy { case (_, fundingTxIndex) => fundingTxIndex } - .map { case (fundingTxId, _) => fundingTxId }.head - spliceLockedSent -= oldestFundingTxId - } - } - val txPublisher = txPublisherFactory.spawnTxPublisher(context, remoteNodeId) // this will be used to detect htlc timeouts @@ -956,7 +943,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall } case Event(cmd: CMD_SPLICE, d: DATA_NORMAL) => - if (!d.commitments.remoteChannelParams.initFeatures.hasFeature(Features.Splicing) && !d.commitments.remoteChannelParams.initFeatures.hasFeature(Features.SplicePrototype)) { + if (!d.commitments.remoteChannelParams.initFeatures.hasFeature(Features.Splicing)) { log.warning("cannot initiate splice, peer doesn't support splicing") cmd.replyTo ! RES_FAILURE(cmd, CommandUnavailableInThisState(d.channelId, "splice", NORMAL)) stay() @@ -1477,12 +1464,10 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case Event(w: WatchPublishedTriggered, d: DATA_NORMAL) => val fundingStatus = LocalFundingStatus.ZeroconfPublishedFundingTx(w.tx, d.commitments.localFundingSigs(w.tx.txid), d.commitments.liquidityPurchase(w.tx.txid)) d.commitments.updateLocalFundingStatus(w.tx.txid, fundingStatus, d.lastAnnouncedFundingTxId_opt) match { - case Right((commitments1, commitment)) => + case Right((commitments1, _)) => watchFundingConfirmed(w.tx.txid, Some(nodeParams.channelConf.minDepth), delay_opt = None) maybeEmitEventsPostSplice(d.aliases, d.commitments, commitments1, d.lastAnnouncement_opt) maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1) - spliceLockedSent += (commitment.fundingTxId -> commitment.fundingTxIndex) - trimSpliceLockedSentIfNeeded() stay() using d.copy(commitments = commitments1) storing() sending SpliceLocked(d.channelId, w.tx.txid) case Left(_) => stay() } @@ -1493,11 +1478,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall // We check if this commitment was already locked before receiving the event (which happens when using 0-conf // or for the initial funding transaction). If it was previously not locked, we must send splice_locked now. val previouslyNotLocked = d.commitments.all.exists(c => c.fundingTxId == commitment.fundingTxId && c.localFundingStatus.isInstanceOf[LocalFundingStatus.NotLocked]) - val spliceLocked_opt = if (previouslyNotLocked) { - spliceLockedSent += (commitment.fundingTxId -> commitment.fundingTxIndex) - trimSpliceLockedSentIfNeeded() - Some(SpliceLocked(d.channelId, w.tx.txid)) - } else None + val spliceLocked_opt = if (previouslyNotLocked) Some(SpliceLocked(d.channelId, w.tx.txid)) else None // If the channel is public and we've received the remote splice_locked, we send our announcement_signatures // in order to generate the channel_announcement. val remoteLocked = commitment.fundingTxIndex == 0 || d.commitments.all.exists(c => c.fundingTxId == commitment.fundingTxId && c.remoteFundingStatus == RemoteFundingStatus.Locked) @@ -1520,25 +1501,6 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case Event(msg: SpliceLocked, d: DATA_NORMAL) => d.commitments.updateRemoteFundingStatus(msg.fundingTxId, d.lastAnnouncedFundingTxId_opt) match { case Right((commitments1, commitment)) => - val spliceLocked_opt = if (d.channelParams.useLegacySpliceProtocol) { - // If we have both already sent splice_locked for this commitment, then we are receiving splice_locked - // again after a reconnection and must retransmit our splice_locked and new announcement_signatures. Nodes - // retransmit splice_locked after a reconnection when they have received splice_locked but NOT matching signatures - // before the last disconnect. If a matching splice_locked has already been sent since reconnecting, then do not - // retransmit splice_locked to avoid a loop. - // NB: It is important both nodes retransmit splice_locked after reconnecting to ensure new Taproot nonces - // are exchanged for channel announcements. - val isLatestLocked = d.commitments.lastLocalLocked_opt.exists(_.fundingTxId == msg.fundingTxId) && d.commitments.lastRemoteLocked_opt.exists(_.fundingTxId == msg.fundingTxId) - if (d.commitments.announceChannel && isLatestLocked && !spliceLockedSent.contains(commitment.fundingTxId)) { - spliceLockedSent += (commitment.fundingTxId -> commitment.fundingTxIndex) - trimSpliceLockedSentIfNeeded() - Some(SpliceLocked(d.channelId, commitment.fundingTxId)) - } else { - None - } - } else { - None - } // If the commitment is confirmed, we were waiting to receive the remote splice_locked before sending our announcement_signatures. val localAnnSigs_opt = commitment.signAnnouncement(nodeParams, commitments1.channelParams, channelKeys.fundingKey(commitment.fundingTxIndex)) match { case Some(localAnnSigs) if !announcementSigsSent.contains(localAnnSigs.shortChannelId) => @@ -1551,7 +1513,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall } maybeEmitEventsPostSplice(d.aliases, d.commitments, commitments1, d.lastAnnouncement_opt) maybeUpdateMaxHtlcAmount(d.channelUpdate.htlcMaximumMsat, commitments1) - stay() using d.copy(commitments = commitments1) storing() sending spliceLocked_opt.toSeq ++ localAnnSigs_opt.toSeq + stay() using d.copy(commitments = commitments1) storing() sending localAnnSigs_opt.toSeq case Left(_) => stay() } @@ -2418,11 +2380,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall activeConnection = r val d1 = Helpers.updateFeatures(d, localInit, remoteInit) val myFirstPerCommitmentPoint = channelKeys.commitmentPoint(0) - val nextFundingTlv = if (d1.channelParams.useLegacySpliceProtocol) { - Set[ChannelReestablishTlv](ChannelReestablishTlv.ExperimentalNextFundingTlv(d1.signingSession.fundingTxId)) - } else { - Set[ChannelReestablishTlv](ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(d1.signingSession.fundingTxId, d1.signingSession.retransmitRemoteCommitSig)) - } + val nextFundingTlv = Set[ChannelReestablishTlv](ChannelReestablishTlv.NextFundingTlv(d1.signingSession.fundingTxId, d1.signingSession.retransmitRemoteCommitSig)) val nonceTlvs = d1.signingSession.fundingParams.commitmentFormat match { case _: SegwitV0CommitmentFormat => Set.empty case _: SimpleTaprootChannelCommitmentFormat => @@ -2440,7 +2398,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall } val channelReestablish = ChannelReestablish( channelId = d1.channelId, - nextLocalCommitmentNumber = d1.signingSession.nextLocalCommitmentNumber(d1.channelParams.useLegacySpliceProtocol), + nextLocalCommitmentNumber = 1, nextRemoteRevocationNumber = 0, yourLastPerCommitmentSecret = PrivateKey(ByteVector32.Zeroes), myCurrentPerCommitmentPoint = myFirstPerCommitmentPoint, @@ -2455,61 +2413,25 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall val remotePerCommitmentSecrets = d1.commitments.remotePerCommitmentSecrets val yourLastPerCommitmentSecret = remotePerCommitmentSecrets.lastIndex.flatMap(remotePerCommitmentSecrets.getHash).getOrElse(ByteVector32.Zeroes) val myCurrentPerCommitmentPoint = channelKeys.commitmentPoint(d1.commitments.localCommitIndex) - // TODO: replace by d1.commitments.localCommitIndex + 1 when removing support for the legacy splice protocol. - val nextLocalCommitmentNumber = d1 match { - case d1: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d1.status match { - case DualFundingStatus.RbfWaitingForSigs(status) => status.nextLocalCommitmentNumber(d1.channelParams.useLegacySpliceProtocol) - case _ => d1.commitments.localCommitIndex + 1 - } - case d1: DATA_NORMAL => d1.spliceStatus match { - case SpliceStatus.SpliceWaitingForSigs(status) => status.nextLocalCommitmentNumber(d1.channelParams.useLegacySpliceProtocol) - case _ => d1.commitments.localCommitIndex + 1 - } - case _ => d1.commitments.localCommitIndex + 1 - } // If we disconnected while signing a funding transaction, we may need our peer to (re)transmit their tx_signatures and commit_sig. - val rbfTlv: Set[ChannelReestablishTlv] = if (d1.channelParams.useLegacySpliceProtocol) { - d1 match { - case d1: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d1.status match { - case DualFundingStatus.RbfWaitingForSigs(status) => Set(ChannelReestablishTlv.ExperimentalNextFundingTlv(status.fundingTx.txId)) - case _ => d1.latestFundingTx.sharedTx match { - case _: InteractiveTxBuilder.PartiallySignedSharedTransaction => Set(ChannelReestablishTlv.ExperimentalNextFundingTlv(d1.latestFundingTx.sharedTx.txId)) - case _: InteractiveTxBuilder.FullySignedSharedTransaction => Set.empty - } - } - case d1: DATA_NORMAL => d1.spliceStatus match { - case SpliceStatus.SpliceWaitingForSigs(status) => Set(ChannelReestablishTlv.ExperimentalNextFundingTlv(status.fundingTx.txId)) - case _ => d1.commitments.latest.localFundingStatus match { - case LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx: PartiallySignedSharedTransaction, _, _, _) => Set(ChannelReestablishTlv.ExperimentalNextFundingTlv(fundingTx.txId)) - case _ => Set.empty - } + val rbfTlv: Set[ChannelReestablishTlv] = d1 match { + case d1: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d1.status match { + case DualFundingStatus.RbfWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId, status.retransmitRemoteCommitSig)) + case _ => d1.latestFundingTx.sharedTx match { + case _: InteractiveTxBuilder.PartiallySignedSharedTransaction => Set(ChannelReestablishTlv.NextFundingTlv(d1.latestFundingTx.sharedTx.txId, retransmitCommitSig = false)) + case _: InteractiveTxBuilder.FullySignedSharedTransaction => Set.empty } - case _ => Set.empty } - } else { - d1 match { - case d1: DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED => d1.status match { - case DualFundingStatus.RbfWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(status.fundingTx.txId, status.retransmitRemoteCommitSig)) - case _ => d1.latestFundingTx.sharedTx match { - case _: InteractiveTxBuilder.PartiallySignedSharedTransaction => Set(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(d1.latestFundingTx.sharedTx.txId, retransmitCommitSig = false)) - case _: InteractiveTxBuilder.FullySignedSharedTransaction => Set.empty - } - } - case d1: DATA_NORMAL => d1.spliceStatus match { - case SpliceStatus.SpliceWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(status.fundingTx.txId, status.retransmitRemoteCommitSig)) - case _ => d1.commitments.latest.localFundingStatus match { - case LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx: PartiallySignedSharedTransaction, _, _, _) => Set(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(fundingTx.txId, retransmitCommitSig = false)) - case _ => Set.empty - } + case d1: DATA_NORMAL => d1.spliceStatus match { + case SpliceStatus.SpliceWaitingForSigs(status) => Set(ChannelReestablishTlv.NextFundingTlv(status.fundingTx.txId, status.retransmitRemoteCommitSig)) + case _ => d1.commitments.latest.localFundingStatus match { + case LocalFundingStatus.DualFundedUnconfirmedFundingTx(fundingTx: PartiallySignedSharedTransaction, _, _, _) => Set(ChannelReestablishTlv.NextFundingTlv(fundingTx.txId, retransmitCommitSig = false)) + case _ => Set.empty } - case _ => Set.empty } + case _ => Set.empty } - val lastFundingLockedTlvs: Set[ChannelReestablishTlv] = if (d1.channelParams.useLegacySpliceProtocol) { - val myCurrentFundingLocked_opt = d1.commitments.lastLocalLocked_opt.map(c => ChannelReestablishTlv.ExperimentalMyCurrentFundingLockedTlv(c.fundingTxId)) - val yourLastFundingLocked_opt = d1.commitments.lastRemoteLocked_opt.map(c => ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asExperimentalYourLastFundingLocked(c.fundingTxId)) - myCurrentFundingLocked_opt.toSet ++ yourLastFundingLocked_opt.toSet - } else if (d1.channelParams.remoteParams.initFeatures.hasFeature(Features.Splicing)) { + val lastFundingLockedTlvs: Set[ChannelReestablishTlv] = if (d1.channelParams.remoteParams.initFeatures.hasFeature(Features.Splicing)) { d1.commitments.lastLocalLocked_opt.map(c => { // We ask our peer to retransmit their announcement_signatures if we haven't already announced that splice. val retransmitAnnSigs = d1 match { @@ -2554,7 +2476,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall val channelReestablish = ChannelReestablish( channelId = d1.channelId, - nextLocalCommitmentNumber = nextLocalCommitmentNumber, + nextLocalCommitmentNumber = d1.commitments.localCommitIndex + 1, nextRemoteRevocationNumber = d1.commitments.remoteCommitIndex, yourLastPerCommitmentSecret = PrivateKey(yourLastPerCommitmentSecret), myCurrentPerCommitmentPoint = myCurrentPerCommitmentPoint, @@ -2603,13 +2525,8 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall handleLocalError(f, d, Some(channelReestablish)) case _ => remoteNextCommitNonces = channelReestablish.nextCommitNonces - val retransmitCommitSig = if (d.channelParams.useLegacySpliceProtocol) { - channelReestablish.nextLocalCommitmentNumber == 0 - } else { - channelReestablish.retransmitInteractiveTxCommitSig - } channelReestablish.nextFundingTxId_opt match { - case Some(fundingTxId) if fundingTxId == d.signingSession.fundingTx.txId && retransmitCommitSig => + case Some(fundingTxId) if fundingTxId == d.signingSession.fundingTx.txId && channelReestablish.retransmitInteractiveTxCommitSig => // They haven't received our commit_sig: we retransmit it, and will send our tx_signatures once we've received // their commit_sig or their tx_signatures (depending on who must send tx_signatures first). val fundingParams = d.signingSession.fundingParams @@ -2633,16 +2550,11 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case Some(f) => handleLocalError(f, d, Some(channelReestablish)) case None => remoteNextCommitNonces = channelReestablish.nextCommitNonces - val retransmitCommitSig = if (d.channelParams.useLegacySpliceProtocol) { - channelReestablish.nextLocalCommitmentNumber == 0 - } else { - channelReestablish.retransmitInteractiveTxCommitSig - } channelReestablish.nextFundingTxId_opt match { case Some(fundingTxId) => d.status match { case DualFundingStatus.RbfWaitingForSigs(signingSession) if signingSession.fundingTx.txId == fundingTxId => - if (retransmitCommitSig) { + if (channelReestablish.retransmitInteractiveTxCommitSig) { // They haven't received our commit_sig: we retransmit it. // We're also waiting for signatures from them, and will send our tx_signatures once we receive them. val fundingParams = signingSession.fundingParams @@ -2659,7 +2571,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case _ if d.latestFundingTx.sharedTx.txId == fundingTxId => // We've already received their commit_sig and sent our tx_signatures. We retransmit our tx_signatures // and our commit_sig if they haven't received it already. - if (retransmitCommitSig) { + if (channelReestablish.retransmitInteractiveTxCommitSig) { val remoteNonce_opt = channelReestablish.currentCommitNonce_opt d.commitments.latest.remoteCommit.sign(d.commitments.channelParams, d.commitments.latest.remoteCommitParams, channelKeys, d.commitments.latest.fundingTxIndex, d.commitments.latest.remoteFundingPubKey, d.commitments.latest.commitInput(channelKeys), d.commitments.latest.commitmentFormat, remoteNonce_opt) match { case Left(e) => handleLocalError(e, d, Some(channelReestablish)) @@ -2694,15 +2606,10 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall val channelReady = createChannelReady(d.aliases, d.commitments) // We've already received their commit_sig and sent our tx_signatures. We retransmit our tx_signatures // and our commit_sig if they haven't received it already. - val retransmitCommitSig = if (d.channelParams.useLegacySpliceProtocol) { - channelReestablish.nextLocalCommitmentNumber == 0 - } else { - channelReestablish.retransmitInteractiveTxCommitSig - } channelReestablish.nextFundingTxId_opt match { case Some(fundingTxId) if fundingTxId == d.commitments.latest.fundingTxId => d.commitments.latest.localFundingStatus.localSigs_opt match { - case Some(txSigs) if retransmitCommitSig => + case Some(txSigs) if channelReestablish.retransmitInteractiveTxCommitSig => log.info("re-sending commit_sig and tx_signatures for fundingTxIndex={} fundingTxId={}", d.commitments.latest.fundingTxIndex, d.commitments.latest.fundingTxId) val remoteNonce_opt = channelReestablish.currentCommitNonce_opt d.commitments.latest.remoteCommit.sign(d.commitments.channelParams, d.commitments.latest.remoteCommitParams, channelKeys, d.commitments.latest.fundingTxIndex, d.commitments.latest.remoteFundingPubKey, d.commitments.latest.commitInput(channelKeys), d.commitments.latest.commitmentFormat, remoteNonce_opt) match { @@ -2754,11 +2661,9 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case Some(f) => handleLocalError(f, d, Some(channelReestablish)) case None => remoteNextCommitNonces = channelReestablish.nextCommitNonces - // We re-send our latest splice_locked if needed. - val spliceLocked_opt = resendSpliceLockedIfNeeded(commitments1) // We retransmit our latest announcement_signatures if our peer requests it. val spliceAnnSigs_opt = resendSpliceAnnSigsIfNeeded(channelReestablish, commitments1) - sendQueue = sendQueue ++ spliceLocked_opt.toSeq ++ spliceAnnSigs_opt.toSeq + sendQueue = sendQueue ++ spliceAnnSigs_opt.toSeq // We may need to retransmit updates and/or commit_sig and/or revocation to resume the channel. sendQueue = sendQueue ++ syncSuccess.retransmit @@ -3261,7 +3166,6 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case _ -> OFFLINE => announcementSigsStash = Map.empty announcementSigsSent = Set.empty - spliceLockedSent = Map.empty[TxId, Long] remoteNextCommitNonces = Map.empty localCloseeNonce_opt = None remoteCloseeNonce_opt = None @@ -3462,7 +3366,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall // will also send announcement_signatures. val notAnnouncedYet = d.commitments.announceChannel && c.shortChannelId_opt.nonEmpty && d.lastAnnouncement_opt.isEmpty // If our peer is a phoenix wallet using the legacy splicing protocol, we always retransmit channel_ready. - val channelReady_opt = if (notAnnouncedYet || notReceivedByRemote || d.channelParams.useLegacySpliceProtocol) { + val channelReady_opt = if (notAnnouncedYet || notReceivedByRemote) { log.debug("re-sending channel_ready") Some(createChannelReady(d.aliases, d.commitments)) } else { @@ -3484,16 +3388,11 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall private def resumeSpliceSigningSessionIfNeeded(channelReestablish: ChannelReestablish, d: DATA_NORMAL): (SpliceStatus, Queue[LightningMessage]) = { var sendQueue = Queue.empty[LightningMessage] - val retransmitCommitSig = if (d.channelParams.useLegacySpliceProtocol) { - channelReestablish.nextLocalCommitmentNumber == d.commitments.remoteCommitIndex - } else { - channelReestablish.retransmitInteractiveTxCommitSig - } val spliceStatus1 = channelReestablish.nextFundingTxId_opt match { case Some(fundingTxId) => d.spliceStatus match { case SpliceStatus.SpliceWaitingForSigs(signingSession) if signingSession.fundingTx.txId == fundingTxId => - if (retransmitCommitSig) { + if (channelReestablish.retransmitInteractiveTxCommitSig) { // They haven't received our commit_sig: we retransmit it. // We're also waiting for signatures from them, and will send our tx_signatures once we receive them. log.info("re-sending commit_sig for splice attempt with fundingTxIndex={} fundingTxId={}", signingSession.fundingTxIndex, signingSession.fundingTx.txId) @@ -3510,7 +3409,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall case dfu: LocalFundingStatus.DualFundedUnconfirmedFundingTx => // We've already received their commit_sig and sent our tx_signatures. We retransmit our // tx_signatures and our commit_sig if they haven't received it already. - if (retransmitCommitSig) { + if (channelReestablish.retransmitInteractiveTxCommitSig) { log.info("re-sending commit_sig and tx_signatures for fundingTxIndex={} fundingTxId={}", d.commitments.latest.fundingTxIndex, d.commitments.latest.fundingTxId) val remoteNonce_opt = channelReestablish.currentCommitNonce_opt d.commitments.latest.remoteCommit.sign(d.commitments.channelParams, d.commitments.latest.remoteCommitParams, channelKeys, d.commitments.latest.fundingTxIndex, d.commitments.latest.remoteFundingPubKey, d.commitments.latest.commitInput(channelKeys), d.commitments.latest.commitmentFormat, remoteNonce_opt) match { @@ -3539,22 +3438,6 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall (spliceStatus1, sendQueue) } - private def resendSpliceLockedIfNeeded(commitments: Commitments): Option[SpliceLocked] = { - commitments.lastLocalLocked_opt match { - case None => None - // We only send splice_locked for splice transactions. - case Some(c) if c.fundingTxIndex == 0 => None - case Some(c) => - // We only send splice_locked for legacy phoenix wallets using the old splicing protocol. - if (commitments.channelParams.useLegacySpliceProtocol) { - log.debug("re-sending splice_locked for fundingTxId={}", c.fundingTxId) - Some(SpliceLocked(commitments.channelId, c.fundingTxId)) - } else { - None - } - } - } - private def resendSpliceAnnSigsIfNeeded(channelReestablish: ChannelReestablish, commitments: Commitments): Option[AnnouncementSignatures] = { commitments.lastLocalLocked_opt match { case None => None diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala index bf468c9b26..704a761aed 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala @@ -1210,13 +1210,6 @@ object InteractiveTxSigningSession { // If we haven't received the remote commit_sig, we will request a retransmission on reconnection. val retransmitRemoteCommitSig: Boolean = localCommit.isLeft - // For the legacy splice protocol, we use the next_commitment_number to let our peer know whether they needed to - // retransmit commit_sig or not. We're now using an explicit bit instead, but need to maintain backwards-compatibility. - def nextLocalCommitmentNumber(useLegacySpliceProtocol: Boolean): Long = localCommit match { - case Left(unsignedCommit) if useLegacySpliceProtocol => unsignedCommit.index - case _ => localCommitIndex + 1 - } - def localFundingKey(channelKeys: ChannelKeys): PrivateKey = channelKeys.fundingKey(fundingTxIndex) def commitInput(fundingKey: PrivateKey): InputInfo = { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala index 4a81d38cc4..b3a3e9cb47 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/PeerConnection.scala @@ -207,15 +207,7 @@ class PeerConnection(keyPair: KeyPair, conf: PeerConnection.Conf, switchboard: A stay() case Event(msg: LightningMessage, d: ConnectedData) if sender() != d.transport => // if the message doesn't originate from the transport, it is an outgoing message - val useExperimentalSplice = d.remoteInit.features.hasFeature(Features.SplicePrototype) msg match { - // If our peer is using the experimental splice version, we convert splice messages. - case msg: SpliceInit if useExperimentalSplice => d.transport forward ExperimentalSpliceInit.from(msg) - case msg: SpliceAck if useExperimentalSplice => d.transport forward ExperimentalSpliceAck.from(msg) - case msg: SpliceLocked if useExperimentalSplice => d.transport forward ExperimentalSpliceLocked.from(msg) - case msg: TxAddInput if useExperimentalSplice => d.transport forward msg.copy(tlvStream = TlvStream(msg.tlvStream.records.filterNot(_.isInstanceOf[TxAddInputTlv.SharedInputTxId]))) - case msg: TxSignatures if useExperimentalSplice => d.transport forward msg.copy(tlvStream = TlvStream(msg.tlvStream.records.filterNot(_.isInstanceOf[TxSignaturesTlv.PreviousFundingTxSig]))) - case batch: CommitSigBatch if useExperimentalSplice => batch.messages.foreach(msg => d.transport forward msg.copy(tlvStream = TlvStream(msg.tlvStream.records.filterNot(_.isInstanceOf[CommitSigTlv.FundingTx])))) case batch: CommitSigBatch => // We insert a start_batch message to let our peer know how many commit_sig they will receive. d.transport forward StartBatch.commitSigBatch(batch.channelId, batch.batchSize) @@ -399,51 +391,6 @@ class PeerConnection(keyPair: KeyPair, conf: PeerConnection.Conf, switchboard: A d.peer ! msg stay() using d.copy(commitSigBatch_opt = None) } - case msg: CommitSig => - // We keep supporting the experimental version of splicing that older Phoenix wallets use. - // Once we're confident that enough Phoenix users have upgraded, we should remove this branch. - msg.tlvStream.get[CommitSigTlv.ExperimentalBatchTlv].map(_.size) match { - case Some(batchSize) if batchSize > 25 => - log.warning("received legacy batch of commit_sig exceeding our threshold ({} > 25), processing messages individually", batchSize) - // We don't want peers to be able to exhaust our memory by sending batches of dummy messages that we keep in RAM. - d.peer ! msg - stay() - case Some(batchSize) if batchSize > 1 => - d.legacyCommitSigBatch_opt match { - case Some(pending) if pending.channelId != msg.channelId || pending.batchSize != batchSize => - log.warning("received invalid commit_sig batch while a different batch isn't complete") - // This should never happen, otherwise it will likely lead to a force-close. - d.peer ! CommitSigBatch(pending.received) - stay() using d.copy(legacyCommitSigBatch_opt = Some(PendingCommitSigBatch(msg.channelId, batchSize, Seq(msg)))) - case Some(pending) => - val received1 = pending.received :+ msg - if (received1.size == batchSize) { - log.debug("received last commit_sig in legacy batch for channel_id={}", msg.channelId) - d.peer ! CommitSigBatch(received1) - stay() using d.copy(legacyCommitSigBatch_opt = None) - } else { - log.debug("received commit_sig {}/{} in legacy batch for channel_id={}", received1.size, batchSize, msg.channelId) - stay() using d.copy(legacyCommitSigBatch_opt = Some(pending.copy(received = received1))) - } - case None => - log.debug("received first commit_sig in legacy batch of size {} for channel_id={}", batchSize, msg.channelId) - stay() using d.copy(legacyCommitSigBatch_opt = Some(PendingCommitSigBatch(msg.channelId, batchSize, Seq(msg)))) - } - case _ => - log.debug("received individual commit_sig for channel_id={}", msg.channelId) - d.peer ! msg - stay() - } - // If our peer is using the experimental splice version, we convert splice messages. - case msg: ExperimentalSpliceInit => - d.peer ! msg.toSpliceInit - stay() - case msg: ExperimentalSpliceAck => - d.peer ! msg.toSpliceAck - stay() - case msg: ExperimentalSpliceLocked => - d.peer ! msg.toSpliceLocked - stay() case _ => d.peer ! msg stay() @@ -677,7 +624,6 @@ object PeerConnection { behavior: Behavior = Behavior(), expectedPong_opt: Option[ExpectedPong] = None, commitSigBatch_opt: Option[PendingCommitSigBatch] = None, - legacyCommitSigBatch_opt: Option[PendingCommitSigBatch] = None, isPersistent: Boolean) extends Data with HasTransport case class PendingCommitSigBatch(channelId: ByteVector32, batchSize: Int, received: Seq[CommitSig]) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/ChannelTlv.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/ChannelTlv.scala index 6c6ec2ed13..29ddbd22ea 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/ChannelTlv.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/ChannelTlv.scala @@ -17,7 +17,7 @@ package fr.acinq.eclair.wire.protocol import fr.acinq.bitcoin.scalacompat.Musig2.IndividualNonce -import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi, TxHash, TxId} +import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Satoshi, TxId} import fr.acinq.eclair.channel.ChannelSpendSignature.PartialSignatureWithNonce import fr.acinq.eclair.channel.{ChannelType, ChannelTypes} import fr.acinq.eclair.wire.protocol.CommonCodecs._ @@ -255,31 +255,6 @@ sealed trait ChannelReestablishTlv extends Tlv object ChannelReestablishTlv { - /** TODO: replaced by [[NextFundingTlv]], remove once Phoenix users have upgraded. */ - case class ExperimentalNextFundingTlv(txId: TxId) extends ChannelReestablishTlv - - /** - * We unfortunately have a conflict between the splicing protocol we use for Phoenix and the official one. - * The official protocol uses TLV = 1 for the next_funding TLV, which should be implemented by [[NextFundingTlv]] - * (which is commented out below). - * The splicing protocol used for Phoenix also uses TLV = 1, but for a different TLV that was removed from the - * official splicing protocol (your_last_funding_locked), which contained the txid of the last [[ChannelReady]] or - * [[SpliceLocked]] message received before disconnecting, if any. - * - * To guarantee backwards-compatibility, we create a TLV field that may contain both options. When using the official - * splicing protocol, it will contain 33 bytes (a txid and a bitfield), while when using the legacy protocol it only - * contains a txid, which lets us easily distinguish the two. - * - * TODO: once we can remove support for Phoenix users with the legacy splicing protocol, this should just be replaced - * by [[NextFundingTlv]] which is commented out below and should be uncommented. - */ - case class NextFundingOrExperimentalYourLastFundingLockedTlv(data: ByteVector) extends ChannelReestablishTlv { - val isOfficial: Boolean = data.size == 33 - val txId: TxId = TxId(TxHash(ByteVector32(data.take(32)))) - // NB: this is only used for the official splicing protocol. - val retransmitCommitSig: Boolean = if (isOfficial) (data.last.toInt % 2) == 1 else false - } - /** * When disconnected in the middle of an interactive-tx session, this field is used to request a retransmission of * [[TxSignatures]] for the given [[txId]]. @@ -287,10 +262,7 @@ object ChannelReestablishTlv { * @param txId the txid of the partially signed funding transaction. * @param retransmitCommitSig true if [[CommitSig]] must be retransmitted before [[TxSignatures]]. */ - // case class NextFundingTlv(txId: TxId, retransmitCommitSig: Boolean) extends ChannelReestablishTlv - - /** TODO: replaced by [[MyCurrentFundingLockedTlv]], remove once Phoenix users have upgraded. */ - case class ExperimentalMyCurrentFundingLockedTlv(txId: TxId) extends ChannelReestablishTlv + case class NextFundingTlv(txId: TxId, retransmitCommitSig: Boolean) extends ChannelReestablishTlv /** * @param txId the txid of our latest outgoing [[ChannelReady]] or [[SpliceLocked]] for this channel. @@ -311,27 +283,8 @@ object ChannelReestablishTlv { */ case class NextLocalNoncesTlv(nonces: Seq[(TxId, IndividualNonce)]) extends ChannelReestablishTlv - object ExperimentalNextFundingTlv { - val codec: Codec[ExperimentalNextFundingTlv] = tlvField(txIdAsHash) - } - - // object NextFundingTlv { - // val codec: Codec[NextFundingTlv] = tlvField(("next_funding_txid" | txIdAsHash) :: ("retransmit_flags" | (ignore(7) :: bool))) - // } - - object NextFundingOrExperimentalYourLastFundingLockedTlv { - def asNextFunding(txId: TxId, retransmitCommitSig: Boolean): NextFundingOrExperimentalYourLastFundingLockedTlv = { - val retransmitFlags = if (retransmitCommitSig) ByteVector.fromValidHex("01") else ByteVector.fromValidHex("00") - NextFundingOrExperimentalYourLastFundingLockedTlv(TxHash(txId).value ++ retransmitFlags) - } - - def asExperimentalYourLastFundingLocked(txId: TxId): NextFundingOrExperimentalYourLastFundingLockedTlv = NextFundingOrExperimentalYourLastFundingLockedTlv(TxHash(txId).value) - - val codec: Codec[NextFundingOrExperimentalYourLastFundingLockedTlv] = tlvField(bytes) - } - - object ExperimentalMyCurrentFundingLockedTlv { - val codec: Codec[ExperimentalMyCurrentFundingLockedTlv] = tlvField("my_current_funding_locked_txid" | txIdAsHash) + object NextFundingTlv { + val codec: Codec[NextFundingTlv] = tlvField(("next_funding_txid" | txIdAsHash) :: ("retransmit_flags" | (ignore(7) :: bool))) } object MyCurrentFundingLockedTlv { @@ -347,11 +300,7 @@ object ChannelReestablishTlv { } val channelReestablishTlvCodec: Codec[TlvStream[ChannelReestablishTlv]] = tlvStream(discriminated[ChannelReestablishTlv].by(varint) - .typecase(UInt64(0), ExperimentalNextFundingTlv.codec) - // TODO: replace with the commented line below when removing support for the legacy splicing protocol. - .typecase(UInt64(1), NextFundingOrExperimentalYourLastFundingLockedTlv.codec) - // .typecase(UInt64(1), NextFundingTlv.codec) - .typecase(UInt64(3), ExperimentalMyCurrentFundingLockedTlv.codec) + .typecase(UInt64(1), NextFundingTlv.codec) .typecase(UInt64(5), MyCurrentFundingLockedTlv.codec) .typecase(UInt64(22), NextLocalNoncesTlv.codec) .typecase(UInt64(24), CurrentCommitNonceTlv.codec) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scala index 3eb0c34b98..f5355aa0b4 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scala @@ -36,9 +36,6 @@ object TxAddInputTlv { /** When doing a splice, the initiator must provide the previous funding txId instead of the whole transaction. */ case class SharedInputTxId(txId: TxId) extends TxAddInputTlv - /** Same as [[SharedInputTxId]] for peers who only support the experimental version of splicing. */ - case class ExperimentalSharedInputTxId(txId: TxId) extends TxAddInputTlv - /** * When creating an interactive-tx where both participants sign a taproot input, we don't need to provide the entire * previous transaction in [[TxAddInput]]: signatures will commit to the txOut of *all* of the transaction's inputs, @@ -53,7 +50,6 @@ object TxAddInputTlv { val txAddInputTlvCodec: Codec[TlvStream[TxAddInputTlv]] = tlvStream(discriminated[TxAddInputTlv].by(varint) // Note that we actually encode as a tx_hash to be consistent with other lightning messages. .typecase(UInt64(0), tlvField(txIdAsHash.as[SharedInputTxId])) - .typecase(UInt64(1105), tlvField(txIdAsHash.as[ExperimentalSharedInputTxId])) .typecase(UInt64(1111), PrevTxOut.codec) ) } @@ -106,13 +102,9 @@ object TxSignaturesTlv { /** When doing a splice for a taproot channel, each peer must provide their partial signature for the previous musig2 funding output. */ case class PreviousFundingTxPartialSig(partialSigWithNonce: PartialSignatureWithNonce) extends TxSignaturesTlv - /** Same as [[PreviousFundingTxSig]] for peers who only support the experimental version of splicing. */ - case class ExperimentalPreviousFundingTxSig(sig: ByteVector64) extends TxSignaturesTlv - val txSignaturesTlvCodec: Codec[TlvStream[TxSignaturesTlv]] = tlvStream(discriminated[TxSignaturesTlv].by(varint) .typecase(UInt64(0), tlvField(bytes64.as[PreviousFundingTxSig])) .typecase(UInt64(2), tlvField(partialSignatureWithNonce.as[PreviousFundingTxPartialSig])) - .typecase(UInt64(601), tlvField(bytes64.as[ExperimentalPreviousFundingTxSig])) ) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala index 95797a7080..21b03e3e66 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecs.scala @@ -437,36 +437,17 @@ object LightningMessageCodecs { ("fundingPubkey" | publicKey) :: ("tlvStream" | SpliceInitTlv.spliceInitTlvCodec)).as[SpliceInit] - val experimentalSpliceInitCodec: Codec[ExperimentalSpliceInit] = ( - ("channelId" | bytes32) :: - ("fundingContribution" | satoshiSigned) :: - ("feerate" | feeratePerKw) :: - ("lockTime" | uint32) :: - ("fundingPubkey" | publicKey) :: - ("tlvStream" | SpliceInitTlv.spliceInitTlvCodec)).as[ExperimentalSpliceInit] - val spliceAckCodec: Codec[SpliceAck] = ( ("channelId" | bytes32) :: ("fundingContribution" | satoshiSigned) :: ("fundingPubkey" | publicKey) :: ("tlvStream" | SpliceAckTlv.spliceAckTlvCodec)).as[SpliceAck] - val experimentalSpliceAckCodec: Codec[ExperimentalSpliceAck] = ( - ("channelId" | bytes32) :: - ("fundingContribution" | satoshiSigned) :: - ("fundingPubkey" | publicKey) :: - ("tlvStream" | SpliceAckTlv.spliceAckTlvCodec)).as[ExperimentalSpliceAck] - val spliceLockedCodec: Codec[SpliceLocked] = ( ("channelId" | bytes32) :: ("fundingTxHash" | txIdAsHash) :: ("tlvStream" | SpliceLockedTlv.spliceLockedTlvCodec)).as[SpliceLocked] - val experimentalSpliceLockedCodec: Codec[ExperimentalSpliceLocked] = ( - ("channelId" | bytes32) :: - ("fundingTxHash" | txIdAsHash) :: - ("tlvStream" | SpliceLockedTlv.spliceLockedTlvCodec)).as[ExperimentalSpliceLocked] - val stfuCodec: Codec[Stfu] = ( ("channelId" | bytes32) :: ("initiator" | byte.xmap[Boolean](b => b != 0, b => if (b) 1 else 0))).as[Stfu] @@ -584,9 +565,6 @@ object LightningMessageCodecs { .typecase(41045, addFeeCreditCodec) .typecase(41046, currentFeeCreditCodec) // - .typecase(37000, experimentalSpliceInitCodec) - .typecase(37002, experimentalSpliceAckCodec) - .typecase(37004, experimentalSpliceLockedCodec) // // diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala index 8f405b55dd..eed2989b2b 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala @@ -97,14 +97,13 @@ case class TxAddInput(channelId: ByteVector32, tlvStream: TlvStream[TxAddInputTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId with HasSerialId { /** This field may replace [[previousTx_opt]] when using taproot. */ val previousTxOut_opt: Option[InputInfo] = tlvStream.get[TxAddInputTlv.PrevTxOut].map(tlv => InputInfo(OutPoint(tlv.txId, previousTxOutput), TxOut(tlv.amount, tlv.publicKeyScript))) - val sharedInput_opt: Option[OutPoint] = tlvStream.get[TxAddInputTlv.SharedInputTxId].map(i => OutPoint(i.txId, previousTxOutput)).orElse(tlvStream.get[TxAddInputTlv.ExperimentalSharedInputTxId].map(i => OutPoint(i.txId, previousTxOutput))) + val sharedInput_opt: Option[OutPoint] = tlvStream.get[TxAddInputTlv.SharedInputTxId].map(i => OutPoint(i.txId, previousTxOutput)) } object TxAddInput { def apply(channelId: ByteVector32, serialId: UInt64, sharedInput: OutPoint, sequence: Long): TxAddInput = { val tlvs = Set[TxAddInputTlv]( TxAddInputTlv.SharedInputTxId(sharedInput.txid), - TxAddInputTlv.ExperimentalSharedInputTxId(sharedInput.txid), ) TxAddInput(channelId, serialId, None, sharedInput.index, sequence, TlvStream(tlvs)) } @@ -144,7 +143,7 @@ case class TxSignatures(channelId: ByteVector32, txId: TxId, witnesses: Seq[ScriptWitness], tlvStream: TlvStream[TxSignaturesTlv] = TlvStream.empty) extends InteractiveTxMessage with HasChannelId { - val previousFundingTxSig_opt: Option[ByteVector64] = tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].map(_.sig).orElse(tlvStream.get[TxSignaturesTlv.ExperimentalPreviousFundingTxSig].map(_.sig)) + val previousFundingTxSig_opt: Option[ByteVector64] = tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].map(_.sig) val previousFundingTxPartialSig_opt: Option[PartialSignatureWithNonce] = tlvStream.get[TxSignaturesTlv.PreviousFundingTxPartialSig].map(_.partialSigWithNonce) } @@ -159,11 +158,6 @@ object TxSignatures { case Some(partialSig: PartialSignatureWithNonce) => Some(TxSignaturesTlv.PreviousFundingTxPartialSig(partialSig)) case None => None }, - // We keep supporting the experimental splicing protocol. - previousFundingSig_opt match { - case Some(IndividualSignature(sig)) => Some(TxSignaturesTlv.ExperimentalPreviousFundingTxSig(sig)) - case _ => None - } ).flatten TxSignatures(channelId, tx.txid, witnesses, TlvStream(tlvs)) } @@ -222,16 +216,9 @@ case class ChannelReestablish(channelId: ByteVector32, yourLastPerCommitmentSecret: PrivateKey, myCurrentPerCommitmentPoint: PublicKey, tlvStream: TlvStream[ChannelReestablishTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { - val nextFundingTxId_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv] match { - case Some(tlv) if tlv.isOfficial => Some(tlv.txId) - case _ => tlvStream.get[ChannelReestablishTlv.ExperimentalNextFundingTlv].map(_.txId) - } - val retransmitInteractiveTxCommitSig: Boolean = tlvStream.get[ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv].exists(_.retransmitCommitSig) - val myCurrentFundingLocked_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.MyCurrentFundingLockedTlv].map(_.txId).orElse(tlvStream.get[ChannelReestablishTlv.ExperimentalMyCurrentFundingLockedTlv].map(_.txId)) - val yourLastFundingLocked_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv] match { - case Some(tlv) if !tlv.isOfficial => Some(tlv.txId) - case _ => None - } + val nextFundingTxId_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.NextFundingTlv].map(_.txId) + val retransmitInteractiveTxCommitSig: Boolean = tlvStream.get[ChannelReestablishTlv.NextFundingTlv].exists(_.retransmitCommitSig) + val myCurrentFundingLocked_opt: Option[TxId] = tlvStream.get[ChannelReestablishTlv.MyCurrentFundingLockedTlv].map(_.txId) val retransmitAnnSigs: Boolean = tlvStream.get[ChannelReestablishTlv.MyCurrentFundingLockedTlv].exists(_.retransmitAnnSigs) val nextCommitNonces: Map[TxId, IndividualNonce] = tlvStream.get[ChannelReestablishTlv.NextLocalNoncesTlv].map(_.nonces.toMap).getOrElse(Map.empty) val currentCommitNonce_opt: Option[IndividualNonce] = tlvStream.get[ChannelReestablishTlv.CurrentCommitNonceTlv].map(_.nonce) @@ -430,19 +417,6 @@ object SpliceInit { apply(channelId, fundingContribution, lockTime, feerate, fundingPubKey, pushAmount, requireConfirmedInputs, requestFunding_opt, None) } -case class ExperimentalSpliceInit(channelId: ByteVector32, - fundingContribution: Satoshi, - feerate: FeeratePerKw, - lockTime: Long, - fundingPubKey: PublicKey, - tlvStream: TlvStream[SpliceInitTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { - def toSpliceInit: SpliceInit = SpliceInit(channelId, fundingContribution, feerate, lockTime, fundingPubKey, tlvStream) -} - -object ExperimentalSpliceInit { - def from(msg: SpliceInit): ExperimentalSpliceInit = ExperimentalSpliceInit(msg.channelId, msg.fundingContribution, msg.feerate, msg.lockTime, msg.fundingPubKey, msg.tlvStream) -} - case class SpliceAck(channelId: ByteVector32, fundingContribution: Satoshi, fundingPubKey: PublicKey, @@ -469,32 +443,11 @@ object SpliceAck { apply(channelId, fundingContribution, fundingPubKey, pushAmount, requireConfirmedInputs, willFund_opt, feeCreditUsed_opt, None) } -case class ExperimentalSpliceAck(channelId: ByteVector32, - fundingContribution: Satoshi, - fundingPubKey: PublicKey, - tlvStream: TlvStream[SpliceAckTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { - def toSpliceAck: SpliceAck = SpliceAck(channelId, fundingContribution, fundingPubKey, tlvStream) -} - -object ExperimentalSpliceAck { - def from(msg: SpliceAck): ExperimentalSpliceAck = ExperimentalSpliceAck(msg.channelId, msg.fundingContribution, msg.fundingPubKey, msg.tlvStream) -} - case class SpliceLocked(channelId: ByteVector32, fundingTxId: TxId, tlvStream: TlvStream[SpliceLockedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { } -case class ExperimentalSpliceLocked(channelId: ByteVector32, - fundingTxId: TxId, - tlvStream: TlvStream[SpliceLockedTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId { - def toSpliceLocked: SpliceLocked = SpliceLocked(channelId, fundingTxId, tlvStream) -} - -object ExperimentalSpliceLocked { - def from(msg: SpliceLocked): ExperimentalSpliceLocked = ExperimentalSpliceLocked(msg.channelId, msg.fundingTxId, msg.tlvStream) -} - case class Shutdown(channelId: ByteVector32, scriptPubKey: ByteVector, tlvStream: TlvStream[ShutdownTlv] = TlvStream.empty) extends ChannelMessage with HasChannelId with ForbiddenMessageWhenQuiescent { diff --git a/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/fundee/data.json b/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/fundee/data.json index 00359d1dd1..f1c29fba23 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/fundee/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/fundee/data.json @@ -14,7 +14,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/funder/data.json index 00607d6828..614b2fef9f 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050001-DATA_WAIT_FOR_FUNDING_CONFIRMED/funder/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050002-DATA_WAIT_FOR_CHANNEL_READY/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050002-DATA_WAIT_FOR_CHANNEL_READY/funder/data.json index 8f24844a1b..e341aeb7e1 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050002-DATA_WAIT_FOR_CHANNEL_READY/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050002-DATA_WAIT_FOR_CHANNEL_READY/funder/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/fundee/data.json b/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/fundee/data.json index effb9f28de..cef9c23db7 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/fundee/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/fundee/data.json @@ -13,7 +13,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -40,7 +39,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/funder/data.json index aec54a2192..64a530da86 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050003-DATA_WAIT_FOR_DUAL_FUNDING_SIGNED/funder/data.json @@ -14,7 +14,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -40,7 +39,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/fundee/data.json b/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/fundee/data.json index a58fd441bf..0d095c1da3 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/fundee/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/fundee/data.json @@ -14,7 +14,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/funder/data.json index 396d0320b2..13ba552625 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050004-DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED/funder/data.json @@ -15,7 +15,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050005-DATA_WAIT_FOR_DUAL_FUNDING_READY/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050005-DATA_WAIT_FOR_DUAL_FUNDING_READY/funder/data.json index ed5e877948..c455575e64 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050005-DATA_WAIT_FOR_DUAL_FUNDING_READY/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050005-DATA_WAIT_FOR_DUAL_FUNDING_READY/funder/data.json @@ -15,7 +15,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced-splice/data.json b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced-splice/data.json index a45a824832..25860f3c7c 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced-splice/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced-splice/data.json @@ -15,7 +15,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced/data.json b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced/data.json index ad8e02ed61..9ffb34c974 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/announced/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/fundee/data.json b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/fundee/data.json index 5ae49350f7..dce59881ca 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/fundee/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/fundee/data.json @@ -14,7 +14,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/funder/data.json b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/funder/data.json index 22c05b772c..4d5a6baec6 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/funder/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/funder/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/splice-commitment-upgrade/data.json b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/splice-commitment-upgrade/data.json index 4a4b9aa91c..bca9606338 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/splice-commitment-upgrade/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050006-DATA_NORMAL/splice-commitment-upgrade/data.json @@ -15,7 +15,6 @@ "option_route_blinding" : "optional", "option_provide_storage" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_dual_fund" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/anchor-outputs/data.json b/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/anchor-outputs/data.json index 3c4eb67c38..ccaf24e86b 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/anchor-outputs/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/anchor-outputs/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/taproot/data.json b/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/taproot/data.json index c17f677034..1d3ff13bc7 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/taproot/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050007-DATA_SHUTDOWN/taproot/data.json @@ -16,7 +16,6 @@ "option_simple_close" : "optional", "option_simple_taproot_phoenix" : "optional", "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_quiesce" : "optional", @@ -45,7 +44,6 @@ "option_simple_taproot_phoenix" : "optional", "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_quiesce" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050008-DATA_NEGOTIATING/fundee/data.json b/eclair-core/src/test/resources/nonreg/codecs/050008-DATA_NEGOTIATING/fundee/data.json index 4e7a724f6d..42819821c8 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050008-DATA_NEGOTIATING/fundee/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050008-DATA_NEGOTIATING/fundee/data.json @@ -14,7 +14,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/anchor-outputs/data.json b/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/anchor-outputs/data.json index 6bc8aee948..abbfdee509 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/anchor-outputs/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/anchor-outputs/data.json @@ -15,7 +15,6 @@ "activated" : { "option_simple_close" : "optional", "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -43,7 +42,6 @@ "option_simple_close" : "optional", "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/taproot/data.json b/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/taproot/data.json index 62e5d660de..9bc2cbce59 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/taproot/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/050009-DATA_NEGOTIATING_SIMPLE/taproot/data.json @@ -18,7 +18,6 @@ "option_simple_taproot_phoenix" : "optional", "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_quiesce" : "optional", @@ -43,7 +42,6 @@ "option_simple_close" : "optional", "option_simple_taproot_phoenix" : "optional", "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_quiesce" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/local/data.json b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/local/data.json index 5f55537978..bf06a2b4a3 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/local/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/local/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/next-remote/data.json b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/next-remote/data.json index cec6b56ccc..8955c14c56 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/next-remote/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/next-remote/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/remote/data.json b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/remote/data.json index 8dbe936e3d..8b21172d6f 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/remote/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/remote/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/revoked/data.json b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/revoked/data.json index 301a5c95df..ef8d2026c0 100644 --- a/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/revoked/data.json +++ b/eclair-core/src/test/resources/nonreg/codecs/05000a-DATA_CLOSING/revoked/data.json @@ -15,7 +15,6 @@ "activated" : { "option_route_blinding" : "optional", "option_provide_storage" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", @@ -41,7 +40,6 @@ "initFeatures" : { "activated" : { "option_route_blinding" : "optional", - "splice_prototype" : "optional", "payment_secret" : "mandatory", "gossip_queries_ex" : "optional", "option_anchor_outputs" : "optional", diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala index 053d3929cc..1252dea8da 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala @@ -1902,31 +1902,6 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik (channelReestablishAlice, channelReestablishBob) } - private def reconnectWithLegacyPeer(f: FixtureParam, sendReestablish: Boolean = true): (ChannelReestablish, ChannelReestablish) = { - import f._ - - // Modify both nodes' state data so they see each other as using the legacy splice protocol. - // This must be done before INPUT_RECONNECTED because the channel_reestablish is constructed using the current state data. - Seq(alice, bob).foreach { node => - val data = node.stateData.asInstanceOf[DATA_NORMAL] - val newData = data.modify(_.commitments.channelParams.remoteParams.initFeatures).using { features => - features.remove(Features.Splicing).add(Features.SplicePrototype, FeatureSupport.Optional) - } - node.setState(node.stateName, newData) - } - - // Use legacy features for reconnection so that updateFeatures preserves the legacy setting. - val baseFeatures = alice.commitments.localChannelParams.initFeatures - val legacyInit = Init(baseFeatures.remove(Features.Splicing).add(Features.SplicePrototype, FeatureSupport.Optional)) - alice ! INPUT_RECONNECTED(alice2bob.ref, legacyInit, legacyInit) - bob ! INPUT_RECONNECTED(bob2alice.ref, legacyInit, legacyInit) - val channelReestablishAlice = alice2bob.expectMsgType[ChannelReestablish] - if (sendReestablish) alice2bob.forward(bob) - val channelReestablishBob = bob2alice.expectMsgType[ChannelReestablish] - if (sendReestablish) bob2alice.forward(alice) - (channelReestablishAlice, channelReestablishBob) - } - test("disconnect (tx_complete not received)") { f => import f._ // Disconnection with one side sending commit_sig @@ -2009,7 +1984,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(channelReestablishBob1.retransmitInteractiveTxCommitSig) assert(channelReestablishBob1.nextLocalCommitmentNumber == bobCommitIndex + 1) alice2bob.forward(bob, channelReestablishAlice1) - bob2alice.forward(alice, channelReestablishBob1.copy(tlvStream = TlvStream(channelReestablishBob1.tlvStream.records.filterNot(_.isInstanceOf[ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv])))) + bob2alice.forward(alice, channelReestablishBob1.copy(tlvStream = TlvStream(channelReestablishBob1.tlvStream.records.filterNot(_.isInstanceOf[ChannelReestablishTlv.NextFundingTlv])))) // In that case Alice won't retransmit commit_sig and the splice won't complete since they haven't exchanged tx_signatures. assert(bob2alice.expectMsgType[CommitSig].fundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId)) bob2alice.forward(alice) @@ -2058,97 +2033,6 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs) } - test("disconnect (commit_sig not received) with legacy peer") { f => - import f._ - - val htlcs = setupHtlcs(f) - val aliceCommitIndex = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex - val bobCommitIndex = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.localCommitIndex - - val sender = initiateSpliceWithoutSigs(f, spliceIn_opt = Some(SpliceIn(500_000 sat)), spliceOut_opt = Some(SpliceOut(100_000 sat, defaultSpliceOutScriptPubKey))) - alice2bob.expectMsgType[CommitSig] // Bob doesn't receive Alice's commit_sig - bob2alice.expectMsgType[CommitSig] // Alice doesn't receive Bob's commit_sig - awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.isInstanceOf[SpliceStatus.SpliceWaitingForSigs]) - val spliceStatus = alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus.asInstanceOf[SpliceStatus.SpliceWaitingForSigs] - - disconnect(f) - - val (channelReestablishAlice, channelReestablishBob) = reconnectWithLegacyPeer(f) - - // Experimental protocol uses an experimental TLV. - assert(channelReestablishAlice.nextFundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId)) - assert(channelReestablishAlice.tlvStream.get[ChannelReestablishTlv.ExperimentalNextFundingTlv].map(_.txId).contains(spliceStatus.signingSession.fundingTx.txId)) - // Experimental protocol doesn't use the explicit retransmit flag. - assert(!channelReestablishAlice.retransmitInteractiveTxCommitSig) - // Experimental protocol rolls back nextLocalCommitmentNumber to signal commit_sig wasn't received. - assert(channelReestablishAlice.nextLocalCommitmentNumber == aliceCommitIndex) - assert(channelReestablishBob.nextLocalCommitmentNumber == bobCommitIndex) - - // Legacy peers always retransmit channel_ready for the initial funding. - alice2bob.expectMsgType[ChannelReady] - alice2bob.forward(bob) - bob2alice.expectMsgType[ChannelReady] - bob2alice.forward(alice) - - // Both sides retransmit commit_sig. - assert(alice2bob.expectMsgType[CommitSig].fundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId)) - alice2bob.forward(bob) - assert(bob2alice.expectMsgType[CommitSig].fundingTxId_opt.contains(spliceStatus.signingSession.fundingTx.txId)) - bob2alice.forward(alice) - bob2alice.expectMsgType[TxSignatures] - bob2alice.forward(alice) - alice2bob.expectMsgType[TxSignatures] - alice2bob.forward(bob) - sender.expectMsgType[RES_SPLICE] - - val spliceTx = alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localFundingStatus.signedTx_opt.get - assert(spliceTx.txid == spliceStatus.signingSession.fundingTx.txId) - alice2blockchain.expectWatchFundingConfirmed(spliceTx.txid) - bob2blockchain.expectWatchFundingConfirmed(spliceTx.txid) - alice ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx) - alice2bob.expectMsgType[SpliceLocked] - alice2bob.forward(bob) - bob ! WatchFundingConfirmedTriggered(BlockHeight(42), 0, spliceTx) - bob2alice.expectMsgType[SpliceLocked] - bob2alice.forward(alice) - awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1) - awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1) - - resolveHtlcs(f, htlcs) - } - - test("re-send splice_locked for legacy peers") { f => - import f._ - - val fundingTx = initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat))) - checkWatchConfirmed(f, fundingTx) - - // Both sides confirm the splice and exchange splice_locked. - alice ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx) - alice2blockchain.expectMsgTypeHaving[WatchFundingSpent](_.txId == fundingTx.txid) - assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx.txid) - alice2bob.forward(bob) - bob ! WatchFundingConfirmedTriggered(BlockHeight(400000), 42, fundingTx) - bob2blockchain.expectMsgTypeHaving[WatchFundingSpent](_.txId == fundingTx.txid) - assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx.txid) - bob2alice.forward(alice) - awaitCond(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1) - awaitCond(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.size == 1) - - disconnect(f) - val (channelReestablishAlice, channelReestablishBob) = reconnectWithLegacyPeer(f) - - // With the experimental protocol, peers retransmit splice_locked on reconnection. - assert(channelReestablishAlice.myCurrentFundingLocked_opt.contains(fundingTx.txid)) - assert(channelReestablishBob.myCurrentFundingLocked_opt.contains(fundingTx.txid)) - assert(!channelReestablishAlice.retransmitAnnSigs) - assert(!channelReestablishBob.retransmitAnnSigs) - assert(alice2bob.expectMsgType[SpliceLocked].fundingTxId == fundingTx.txid) - alice2bob.forward(bob) - assert(bob2alice.expectMsgType[SpliceLocked].fundingTxId == fundingTx.txid) - bob2alice.forward(alice) - } - test("don't re-send splice_locked on reconnection") { f => import f._ diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala index cc7904d6e8..52fb3d32da 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerConnectionSpec.scala @@ -19,12 +19,11 @@ package fr.acinq.eclair.io import akka.actor.PoisonPill import akka.testkit.{TestFSMRef, TestProbe} import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey} -import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32, OutPoint, SatoshiLong, Transaction, TxId} +import fr.acinq.bitcoin.scalacompat.{Block, ByteVector32} import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional} import fr.acinq.eclair.Features._ import fr.acinq.eclair.TestConstants._ import fr.acinq.eclair.TestUtils.randomTxId -import fr.acinq.eclair.blockchain.fee.FeeratePerKw import fr.acinq.eclair.channel.ChannelSpendSignature.IndividualSignature import fr.acinq.eclair.crypto.TransportHandler import fr.acinq.eclair.io.Peer.ConnectionDown @@ -353,85 +352,6 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi transport.expectNoMessage(100 millis) } - test("receive legacy batch of commit_sig messages") { f => - import f._ - connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer) - - // We receive a batch of commit_sig messages from a first channel. - val channelId1 = randomBytes32() - val commitSigs1 = Seq( - CommitSig(channelId1, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - CommitSig(channelId1, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - ) - transport.send(peerConnection, commitSigs1.head) - transport.expectMsg(TransportHandler.ReadAck(commitSigs1.head)) - peer.expectNoMessage(100 millis) - transport.send(peerConnection, commitSigs1.last) - transport.expectMsg(TransportHandler.ReadAck(commitSigs1.last)) - peer.expectMsg(CommitSigBatch(commitSigs1)) - - // We receive a batch of commit_sig messages from a second channel. - val channelId2 = randomBytes32() - val commitSigs2 = Seq( - CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(3))), - CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(3))), - CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(3))), - ) - commitSigs2.dropRight(1).foreach(commitSig => { - transport.send(peerConnection, commitSig) - transport.expectMsg(TransportHandler.ReadAck(commitSig)) - }) - peer.expectNoMessage(100 millis) - transport.send(peerConnection, commitSigs2.last) - transport.expectMsg(TransportHandler.ReadAck(commitSigs2.last)) - peer.expectMsg(CommitSigBatch(commitSigs2)) - - // We receive another batch of commit_sig messages from the first channel, with unrelated messages in the batch. - val commitSigs3 = Seq( - CommitSig(channelId1, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - CommitSig(channelId1, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - ) - transport.send(peerConnection, commitSigs3.head) - transport.expectMsg(TransportHandler.ReadAck(commitSigs3.head)) - val spliceLocked1 = SpliceLocked(channelId1, randomTxId()) - transport.send(peerConnection, spliceLocked1) - transport.expectMsg(TransportHandler.ReadAck(spliceLocked1)) - peer.expectMsg(spliceLocked1) - val spliceLocked2 = SpliceLocked(channelId2, randomTxId()) - transport.send(peerConnection, spliceLocked2) - transport.expectMsg(TransportHandler.ReadAck(spliceLocked2)) - peer.expectMsg(spliceLocked2) - peer.expectNoMessage(100 millis) - transport.send(peerConnection, commitSigs3.last) - transport.expectMsg(TransportHandler.ReadAck(commitSigs3.last)) - peer.expectMsg(CommitSigBatch(commitSigs3)) - - // We start receiving a batch of commit_sig messages from the first channel, interleaved with a batch from the second - // channel, which is not supported. - val commitSigs4 = Seq( - CommitSig(channelId1, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(2))), - ) - transport.send(peerConnection, commitSigs4.head) - transport.expectMsg(TransportHandler.ReadAck(commitSigs4.head)) - peer.expectNoMessage(100 millis) - transport.send(peerConnection, commitSigs4(1)) - transport.expectMsg(TransportHandler.ReadAck(commitSigs4(1))) - peer.expectMsg(CommitSigBatch(commitSigs4.take(1))) - transport.send(peerConnection, commitSigs4.last) - transport.expectMsg(TransportHandler.ReadAck(commitSigs4.last)) - peer.expectMsg(CommitSigBatch(commitSigs4.tail)) - - // We receive a batch that exceeds our threshold: we process them individually. - val invalidCommitSigs = (0 until 30).map(_ => CommitSig(channelId2, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.ExperimentalBatchTlv(30)))) - invalidCommitSigs.foreach(commitSig => { - transport.send(peerConnection, commitSig) - transport.expectMsg(TransportHandler.ReadAck(commitSig)) - peer.expectMsg(commitSig) - }) - } - test("receive batch of commit_sig messages") { f => import f._ connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer) @@ -724,53 +644,5 @@ class PeerConnectionSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wi } } - test("convert experimental splice messages") { f => - import f._ - val remoteInit = protocol.Init(Bob.nodeParams.features.initFeatures().add(Features.SplicePrototype, FeatureSupport.Optional)) - connect(nodeParams, remoteNodeId, switchboard, router, connection, transport, peerConnection, peer, remoteInit) - - val spliceInit = SpliceInit(randomBytes32(), 100_000 sat, FeeratePerKw(5000 sat), 0, randomKey().publicKey) - val spliceAck = SpliceAck(randomBytes32(), 50_000 sat, randomKey().publicKey) - val spliceLocked = SpliceLocked(randomBytes32(), TxId(randomBytes32())) - - // Outgoing messages use the experimental version of splicing. - peer.send(peerConnection, spliceInit) - transport.expectMsg(ExperimentalSpliceInit.from(spliceInit)) - peer.send(peerConnection, spliceAck) - transport.expectMsg(ExperimentalSpliceAck.from(spliceAck)) - peer.send(peerConnection, spliceLocked) - transport.expectMsg(ExperimentalSpliceLocked.from(spliceLocked)) - - // Incoming messages are converted from their experimental version. - transport.send(peerConnection, ExperimentalSpliceInit.from(spliceInit)) - peer.expectMsg(spliceInit) - transport.expectMsgType[TransportHandler.ReadAck] - transport.send(peerConnection, ExperimentalSpliceAck.from(spliceAck)) - peer.expectMsg(spliceAck) - transport.expectMsgType[TransportHandler.ReadAck] - transport.send(peerConnection, ExperimentalSpliceLocked.from(spliceLocked)) - peer.expectMsg(spliceLocked) - transport.expectMsgType[TransportHandler.ReadAck] - - // Incompatible TLVs are dropped when sending messages to peers using the experimental version. - val txAddInput = TxAddInput(randomBytes32(), UInt64(0), OutPoint(TxId(randomBytes32()), 3), 0) - assert(txAddInput.tlvStream.get[TxAddInputTlv.SharedInputTxId].nonEmpty) - peer.send(peerConnection, txAddInput) - assert(transport.expectMsgType[TxAddInput].tlvStream.get[TxAddInputTlv.SharedInputTxId].isEmpty) - val txSignatures = TxSignatures(randomBytes32(), Transaction(2, Nil, Nil, 0), Nil, Some(IndividualSignature(randomBytes64()))) - assert(txSignatures.tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].nonEmpty) - peer.send(peerConnection, txSignatures) - assert(transport.expectMsgType[TxSignatures].tlvStream.get[TxSignaturesTlv.PreviousFundingTxSig].isEmpty) - val channelId = randomBytes32() - val commitSigBatch = CommitSigBatch(Seq( - CommitSig(channelId, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.FundingTx(TxId(randomBytes32())), CommitSigTlv.ExperimentalBatchTlv(2))), - CommitSig(channelId, IndividualSignature(randomBytes64()), Nil, TlvStream(CommitSigTlv.FundingTx(TxId(randomBytes32())), CommitSigTlv.ExperimentalBatchTlv(2))), - )) - peer.send(peerConnection, commitSigBatch) - assert(transport.expectMsgType[CommitSig].tlvStream.get[CommitSigTlv.FundingTx].isEmpty) - assert(transport.expectMsgType[CommitSig].tlvStream.get[CommitSigTlv.FundingTx].isEmpty) - transport.expectNoMessage(100 millis) - } - } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala index 1296c6a7c0..94696cd53b 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/wire/protocol/LightningMessageCodecsSpec.scala @@ -159,14 +159,8 @@ class LightningMessageCodecsSpec extends AnyFunSuite { hex"0023" ++ channelId ++ signature ++ hex"fe47010000 07 cccccccccccccc" -> FundingSigned(channelId, signature, TlvStream[FundingSignedTlv](Set.empty[FundingSignedTlv], Set(GenericTlv(tlvTag, hex"cccccccccccccc")))), hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point), - hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"00 20" ++ txId.value.reverse -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.ExperimentalNextFundingTlv(txId))), - // TODO: replace those test vectors with the commented ones below when we remove support for the legacy splicing protocol. - hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 20" ++ txId.value.reverse -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv(txId.value.reverse))), - hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"00" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv(txId.value.reverse ++ hex"00"))), - hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"01" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv(txId.value.reverse ++ hex"01"))), - // hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"00" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingTlv(txId, retransmitCommitSig = false))), - // hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"01" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingTlv(txId, retransmitCommitSig = true))), - hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"03 20" ++ txId.value.reverse -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.ExperimentalMyCurrentFundingLockedTlv(txId))), + hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"00" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingTlv(txId, retransmitCommitSig = false))), + hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"01 21" ++ txId.value.reverse ++ hex"01" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.NextFundingTlv(txId, retransmitCommitSig = true))), hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"05 21" ++ txId.value.reverse ++ hex"00" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.MyCurrentFundingLockedTlv(txId, retransmitAnnSigs = false))), hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"05 21" ++ txId.value.reverse ++ hex"01" -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.MyCurrentFundingLockedTlv(txId, retransmitAnnSigs = true))), hex"0088" ++ channelId ++ hex"0001020304050607 0809aabbccddeeff" ++ key.value ++ point.value ++ hex"18 42" ++ nonce.data -> ChannelReestablish(channelId, 0x01020304050607L, 0x0809aabbccddeeffL, key, point, TlvStream(ChannelReestablishTlv.CurrentCommitNonceTlv(nonce))), @@ -229,7 +223,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite { TxAddInput(channelId1, UInt64(561), Some(tx1), 1, 5) -> hex"0042 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000231 00f7 020000000001014ade359c5deb7c1cde2e94f401854658f97d7fa31c17ce9a831db253120a0a410100000017160014eb9a5bd79194a23d19d6ec473c768fb74f9ed32cffffffff021ca408000000000017a914946118f24bb7b37d5e9e39579e4a411e70f5b6a08763e703000000000017a9143638b2602d11f934c04abc6adb1494f69d1f14af8702473044022059ddd943b399211e4266a349f26b3289979e29f9b067792c6cfa8cc5ae25f44602204d627a5a5b603d0562e7969011fb3d64908af90a3ec7c876eaa9baf61e1958af012102f5188df1da92ed818581c29778047800ed6635788aa09d9469f7d17628f7323300000000 00000001 00000005", TxAddInput(channelId2, UInt64(0), Some(tx2), 2, 0) -> hex"0042 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0000000000000000 0100 0200000000010142180a8812fc79a3da7fb2471eff3e22d7faee990604c2ba7f2fc8dfb15b550a0200000000feffffff030f241800000000001976a9146774040642a78ca3b8b395e70f8391b21ec026fc88ac4a155801000000001600148d2e0b57adcb8869e603fd35b5179caf053361253b1d010000000000160014e032f4f4b9f8611df0d30a20648c190c263bbc33024730440220506005aa347f5b698542cafcb4f1a10250aeb52a609d6fd67ef68f9c1a5d954302206b9bb844343f4012bccd9d08a0f5430afb9549555a3252e499be7df97aae477a012103976d6b3eea3de4b056cd88cdfd50a22daf121e0fb5c6e45ba0f40e1effbd275a00000000 00000002 00000000", TxAddInput(channelId1, UInt64(561), Some(tx1), 0, 0) -> hex"0042 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000231 00f7 020000000001014ade359c5deb7c1cde2e94f401854658f97d7fa31c17ce9a831db253120a0a410100000017160014eb9a5bd79194a23d19d6ec473c768fb74f9ed32cffffffff021ca408000000000017a914946118f24bb7b37d5e9e39579e4a411e70f5b6a08763e703000000000017a9143638b2602d11f934c04abc6adb1494f69d1f14af8702473044022059ddd943b399211e4266a349f26b3289979e29f9b067792c6cfa8cc5ae25f44602204d627a5a5b603d0562e7969011fb3d64908af90a3ec7c876eaa9baf61e1958af012102f5188df1da92ed818581c29778047800ed6635788aa09d9469f7d17628f7323300000000 00000000 00000000", - TxAddInput(channelId1, UInt64(561), OutPoint(tx1, 1), 5) -> hex"0042 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000231 0000 00000001 00000005 00201f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106 fd0451201f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106", + TxAddInput(channelId1, UInt64(561), OutPoint(tx1, 1), 5) -> hex"0042 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000231 0000 00000001 00000005 00201f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106", TxAddInput(channelId1, UInt64(561), None, 1, 0xfffffffdL, TlvStream(TxAddInputTlv.PrevTxOut(tx2.txid, 22_549_834 sat, hex"00148d2e0b57adcb8869e603fd35b5179caf05336125"))) -> hex"0042 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000231 0000 00000001 fffffffd fd04573efc7aa8845f192959202c1b7ff704e7cbddded463c05e844676a94ccb4bed69f1000000000158154a00148d2e0b57adcb8869e603fd35b5179caf05336125", TxAddOutput(channelId1, UInt64(1105), 2047 sat, hex"00149357014afd0ccd265658c9ae81efa995e771f472") -> hex"0043 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000451 00000000000007ff 0016 00149357014afd0ccd265658c9ae81efa995e771f472", TxAddOutput(channelId1, UInt64(1105), 2047 sat, hex"00149357014afd0ccd265658c9ae81efa995e771f472", TlvStream(Set.empty[TxAddOutputTlv], Set(GenericTlv(UInt64(301), hex"2a")))) -> hex"0043 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000451 00000000000007ff 0016 00149357014afd0ccd265658c9ae81efa995e771f472 fd012d012a", @@ -241,7 +235,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite { TxComplete(channelId1, TlvStream(Set.empty[TxCompleteTlv], Set(GenericTlv(UInt64(231), hex"deadbeef"), GenericTlv(UInt64(507), hex"")))) -> hex"0046 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa e704deadbeef fd01fb00", TxSignatures(channelId1, tx2, Seq(ScriptWitness(Seq(hex"68656c6c6f2074686572652c2074686973206973206120626974636f6e212121", hex"82012088a820add57dfe5277079d069ca4ad4893c96de91f88ffb981fdc6a2a34d5336c66aff87")), ScriptWitness(Seq(hex"304402207de9ba56bb9f641372e805782575ee840a899e61021c8b1572b3ec1d5b5950e9022069e9ba998915dae193d3c25cb89b5e64370e6a3a7755e7f31cf6d7cbc2a49f6d01", hex"034695f5b7864c580bf11f9f8cb1a94eb336f2ce9ef872d2ae1a90ee276c772484"))), None) -> hex"0047 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa fc7aa8845f192959202c1b7ff704e7cbddded463c05e844676a94ccb4bed69f1 0002 004a 022068656c6c6f2074686572652c2074686973206973206120626974636f6e2121212782012088a820add57dfe5277079d069ca4ad4893c96de91f88ffb981fdc6a2a34d5336c66aff87 006b 0247304402207de9ba56bb9f641372e805782575ee840a899e61021c8b1572b3ec1d5b5950e9022069e9ba998915dae193d3c25cb89b5e64370e6a3a7755e7f31cf6d7cbc2a49f6d0121034695f5b7864c580bf11f9f8cb1a94eb336f2ce9ef872d2ae1a90ee276c772484", TxSignatures(channelId2, tx1, Nil, None) -> hex"0047 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 1f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106 0000", - TxSignatures(channelId2, tx1, Nil, Some(IndividualSignature(signature))) -> hex"0047 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 1f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106 0000 0040aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb fd025940aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + TxSignatures(channelId2, tx1, Nil, Some(IndividualSignature(signature))) -> hex"0047 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 1f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106 0000 0040aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", TxSignatures(channelId2, tx1, Nil, Some(PartialSignatureWithNonce(partialSig, fundingNonce))) -> hex"0047 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 1f2ec025a33e39ef8e177afcdc1adc855bf128dc906182255aeb64efa825f106 0000 02 62 bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb a49ff67b08c720b993c946556cde1be1c3b664bc847c4792135dfd6ef0986e00e9871808c6620b0420567dad525b27431453d4434fd326f8ac56496639b72326eb5d", TxInitRbf(channelId1, 8388607, FeeratePerKw(4000 sat)) -> hex"0048 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 007fffff 00000fa0", TxInitRbf(channelId1, 0, FeeratePerKw(4000 sat), 1_500_000 sat, requireConfirmedInputs = true, None) -> hex"0048 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 00000000 00000fa0 0008000000000016e360 0200", @@ -862,84 +856,4 @@ class LightningMessageCodecsSpec extends AnyFunSuite { } } - test("channel_reestablish backwards-compatibility with legacy splice TLVs") { - val channelId = randomBytes32() - val key = randomKey() - val point = randomKey().publicKey - val txId1 = randomTxId() - val txId2 = randomTxId() - val txId3 = randomTxId() - - def reestablish(tlvs: ChannelReestablishTlv*): ChannelReestablish = { - ChannelReestablish(channelId, 1, 0, key, point, TlvStream(tlvs: _*)) - } - - // Legacy TLVs: tag 0 (experimental next_funding) + tag 1 (experimental your_last_funding_locked) + tag 3 (experimental my_current_funding_locked). - { - val msg = reestablish( - ChannelReestablishTlv.ExperimentalNextFundingTlv(txId1), - ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asExperimentalYourLastFundingLocked(txId2), - ChannelReestablishTlv.ExperimentalMyCurrentFundingLockedTlv(txId3), - ) - assert(msg.nextFundingTxId_opt.contains(txId1)) - assert(!msg.retransmitInteractiveTxCommitSig) - assert(msg.yourLastFundingLocked_opt.contains(txId2)) - assert(msg.myCurrentFundingLocked_opt.contains(txId3)) - assert(!msg.retransmitAnnSigs) - val encoded = lightningMessageCodec.encode(msg).require - assert(lightningMessageCodec.decode(encoded).require.value == msg) - } - - // Official TLVs with retransmit flags set: tag 1 (official next_funding) + tag 5 (my_current_funding_locked). - { - val msg = reestablish( - ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(txId1, retransmitCommitSig = true), - ChannelReestablishTlv.MyCurrentFundingLockedTlv(txId2, retransmitAnnSigs = true), - ) - assert(msg.nextFundingTxId_opt.contains(txId1)) - assert(msg.retransmitInteractiveTxCommitSig) - assert(msg.yourLastFundingLocked_opt.isEmpty) - assert(msg.myCurrentFundingLocked_opt.contains(txId2)) - assert(msg.retransmitAnnSigs) - val encoded = lightningMessageCodec.encode(msg).require - assert(lightningMessageCodec.decode(encoded).require.value == msg) - } - - // Official TLVs with retransmit flags unset. - { - val msg = reestablish( - ChannelReestablishTlv.NextFundingOrExperimentalYourLastFundingLockedTlv.asNextFunding(txId1, retransmitCommitSig = false), - ChannelReestablishTlv.MyCurrentFundingLockedTlv(txId2, retransmitAnnSigs = false), - ) - assert(msg.nextFundingTxId_opt.contains(txId1)) - assert(!msg.retransmitInteractiveTxCommitSig) - assert(msg.yourLastFundingLocked_opt.isEmpty) - assert(msg.myCurrentFundingLocked_opt.contains(txId2)) - assert(!msg.retransmitAnnSigs) - val encoded = lightningMessageCodec.encode(msg).require - assert(lightningMessageCodec.decode(encoded).require.value == msg) - } - - // my_current_funding_locked priority: official tag 5 takes priority over legacy tag 3. - { - val msg = reestablish( - ChannelReestablishTlv.ExperimentalMyCurrentFundingLockedTlv(txId1), - ChannelReestablishTlv.MyCurrentFundingLockedTlv(txId2, retransmitAnnSigs = false), - ) - assert(msg.myCurrentFundingLocked_opt.contains(txId2)) - val encoded = lightningMessageCodec.encode(msg).require - assert(lightningMessageCodec.decode(encoded).require.value == msg) - } - - // Empty TLV stream: all splice-related accessors return None/false. - { - val msg = ChannelReestablish(channelId, 1, 0, key, point) - assert(msg.nextFundingTxId_opt.isEmpty) - assert(!msg.retransmitInteractiveTxCommitSig) - assert(msg.yourLastFundingLocked_opt.isEmpty) - assert(msg.myCurrentFundingLocked_opt.isEmpty) - assert(!msg.retransmitAnnSigs) - } - } - }