Split commit nonces from funding nonce in `tx_complete` (#3145)
What changed, and why it matters
This commit refactors how cryptographic nonces are packaged inside a Lightning protocol message called tx_complete. Previously, two unrelated nonce types were bundled together; now they are split into separate optional data fields. The change is a protocol cleanup and does not by itself fix a known exploit, but it removes a design ambiguity that could lead to incorrect nonce handling during taproot channel funding or splicing.
Treat as a protocol-correctness change rather than an urgent security patch. Reviewers should verify that backward compatibility with peers sending the old combined TLV is handled (or intentionally not supported), and that the new TLV type numbers do not collide with any in-flight protocol specification. If this change is part of an unmerged BOLT update, ensure both implementations agree on TLV type 6.
Security signals we found
Refactor of cryptographic nonce wire encoding
Separation of commitment nonces from funding input nonce
No new input validation or bounds checks added
Existing MissingFundingNonce/MissingCommitNonce checks retained
Protocol-level change in TLV type assignment (new type 6)
Evidence from the diff
The commit splits the TxCompleteTlv.Nonces TLV into two independent TLVs: CommitNonces (type 4) for the current/next commitment transaction MuSig2 verification nonces, and FundingInputNonce (type 6) for the previous taproot funding input signing nonce. InteractiveTxBuilder.scala is updated to read commitNonces_opt and fundingNonce_opt separately, preserving the existing validation logic (MissingFundingNonce, MissingCommitNonce) and signature paths. Tests and codec fixtures are adjusted to the new TLV layout. The wire encoding changes: a tx_complete with both nonces now carries two TLV records instead of one variable-length record.
Changed components
eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scalaeclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scalaeclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scalaInspect captured patch +52 / −48
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 791e6d7..731b9ae 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
@@ -722,7 +722,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
replyTo ! RemoteFailure(cause)
unlockAndStop(session)
case Right(completeTx) =>
- signCommitTx(completeTx, session.txCompleteReceived.flatMap(_.nonces_opt))
+ signCommitTx(completeTx, session.txCompleteReceived.flatMap(_.fundingNonce_opt), session.txCompleteReceived.flatMap(_.commitNonces_opt))
}
case _: WalletFailure =>
replyTo ! RemoteFailure(UnconfirmedInteractiveTxInputs(fundingParams.channelId))
@@ -799,7 +799,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
case _: SegwitV0CommitmentFormat => ()
case _: SimpleTaprootChannelCommitmentFormat =>
// If we're spending a taproot channel, our peer must provide a nonce for the shared input.
- val remoteFundingNonce_opt: Option[IndividualNonce] = session.txCompleteReceived.flatMap(_.nonces_opt).flatMap(_.fundingNonce_opt)
+ val remoteFundingNonce_opt = session.txCompleteReceived.flatMap(_.fundingNonce_opt)
if (remoteFundingNonce_opt.isEmpty) return Left(MissingFundingNonce(fundingParams.channelId, sharedInput.info.outPoint.txid))
}
sharedInputs.headOption match {
@@ -821,7 +821,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
fundingParams.commitmentFormat match {
case _: SegwitV0CommitmentFormat => ()
case _: SimpleTaprootChannelCommitmentFormat =>
- val remoteCommitNonces_opt = session.txCompleteReceived.flatMap(_.nonces_opt)
+ val remoteCommitNonces_opt = session.txCompleteReceived.flatMap(_.commitNonces_opt)
if (remoteCommitNonces_opt.isEmpty) return Left(MissingCommitNonce(fundingParams.channelId, tx.txid, purpose.remoteCommitIndex))
}
@@ -890,7 +890,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
Right(sharedTx)
}
- private def signCommitTx(completeTx: SharedTransaction, remoteNonces_opt: Option[TxCompleteTlv.Nonces]): Behavior[Command] = {
+ private def signCommitTx(completeTx: SharedTransaction, remoteFundingNonce_opt: Option[IndividualNonce], remoteCommitNonces_opt: Option[TxCompleteTlv.CommitNonces]): Behavior[Command] = {
val fundingTx = completeTx.buildUnsignedTx()
val fundingOutputIndex = fundingTx.txOut.indexWhere(_.publicKeyScript == fundingPubkeyScript)
val liquidityFee = fundingParams.liquidityFees(liquidityPurchase_opt)
@@ -916,7 +916,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
val localSigOfRemoteTx = fundingParams.commitmentFormat match {
case _: SegwitV0CommitmentFormat => Right(remoteCommitTx.sign(localFundingKey, fundingParams.remoteFundingPubKey))
case _: SimpleTaprootChannelCommitmentFormat =>
- remoteNonces_opt match {
+ remoteCommitNonces_opt match {
case Some(remoteNonces) =>
val localNonce = NonceGenerator.signingNonce(localFundingKey.publicKey, fundingParams.remoteFundingPubKey, fundingTx.txid)
remoteCommitTx.partialSign(localFundingKey, fundingParams.remoteFundingPubKey, localNonce, Seq(localNonce.publicNonce, remoteNonces.commitNonce)) match {
@@ -935,13 +935,13 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
val localCommitSig = CommitSig(fundingParams.channelId, localSigOfRemoteTx, htlcSignatures, batchSize = 1)
val localCommit = UnsignedLocalCommit(purpose.localCommitIndex, localSpec, localCommitTx.tx.txid)
val remoteCommit = RemoteCommit(purpose.remoteCommitIndex, remoteSpec, remoteCommitTx.tx.txid, purpose.remotePerCommitmentPoint)
- signFundingTx(completeTx, remoteNonces_opt, localCommitSig, localCommit, remoteCommit)
+ signFundingTx(completeTx, remoteFundingNonce_opt, remoteCommitNonces_opt.map(_.nextCommitNonce), localCommitSig, localCommit, remoteCommit)
}
}
}
- private def signFundingTx(completeTx: SharedTransaction, remoteNonces_opt: Option[TxCompleteTlv.Nonces], commitSig: CommitSig, localCommit: UnsignedLocalCommit, remoteCommit: RemoteCommit): Behavior[Command] = {
- signTx(completeTx, remoteNonces_opt.flatMap(_.fundingNonce_opt))
+ private def signFundingTx(completeTx: SharedTransaction, remoteFundingNonce_opt: Option[IndividualNonce], nextRemoteCommitNonce_opt: Option[IndividualNonce], commitSig: CommitSig, localCommit: UnsignedLocalCommit, remoteCommit: RemoteCommit): Behavior[Command] = {
+ signTx(completeTx, remoteFundingNonce_opt)
Behaviors.receiveMessagePartial {
case SignTransactionResult(signedTx) =>
log.info(s"interactive-tx txid=${signedTx.txId} partially signed with {} local inputs, {} remote inputs, {} local outputs and {} remote outputs", signedTx.tx.localInputs.length, signedTx.tx.remoteInputs.length, signedTx.tx.localOutputs.length, signedTx.tx.remoteOutputs.length)
@@ -978,8 +978,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
remoteCommit,
liquidityPurchase_opt.map(_.basicInfo(isBuyer = fundingParams.isInitiator))
)
- val nextRemoteCommitNonce_opt = remoteNonces_opt.map(n => signedTx.txId -> n.nextCommitNonce)
- replyTo ! Succeeded(signingSession, commitSig, liquidityPurchase_opt, nextRemoteCommitNonce_opt)
+ replyTo ! Succeeded(signingSession, commitSig, liquidityPurchase_opt, nextRemoteCommitNonce_opt.map(n => signedTx.txId -> n))
Behaviors.stopped
case WalletFailure(t) =>
log.error("could not sign funding transaction: ", t)
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 6edf10f..9f0f9e3 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
@@ -76,21 +76,20 @@ sealed trait TxCompleteTlv extends Tlv
object TxCompleteTlv {
/**
- * Musig2 nonces exchanged during an interactive tx session, when using a taproot channel or upgrading a channel to
- * use taproot.
+ * Musig2 nonces for the commitment transaction(s), exchanged during an interactive tx session, when using a taproot
+ * channel or upgrading a channel to use taproot.
*
- * @param commitNonce the sender's verification nonce for the current commit tx spending the interactive tx.
- * @param nextCommitNonce the sender's verification nonce for the next commit tx spending the interactive tx.
- * @param fundingNonce_opt when splicing a taproot channel, the sender's random signing nonce for the previous funding output.
+ * @param commitNonce the sender's verification nonce for the current commit tx spending the interactive tx.
+ * @param nextCommitNonce the sender's verification nonce for the next commit tx spending the interactive tx.
*/
- case class Nonces(commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]) extends TxCompleteTlv
+ case class CommitNonces(commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce) extends TxCompleteTlv
- object Nonces {
- val codec: Codec[Nonces] = tlvField((publicNonce :: publicNonce :: optional(bitsRemaining, publicNonce)).as[Nonces])
- }
+ /** When splicing a taproot channel, the sender's random signing nonce for the previous funding output. */
+ case class FundingInputNonce(nonce: IndividualNonce) extends TxCompleteTlv
val txCompleteTlvCodec: Codec[TlvStream[TxCompleteTlv]] = tlvStream(discriminated[TxCompleteTlv].by(varint)
- .typecase(UInt64(4), Nonces.codec)
+ .typecase(UInt64(4), tlvField[CommitNonces, CommitNonces]((publicNonce :: publicNonce).as[CommitNonces]))
+ .typecase(UInt64(6), tlvField[FundingInputNonce, FundingInputNonce](publicNonce.as[FundingInputNonce]))
)
}
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 6573602..01fd334 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
@@ -122,12 +122,18 @@ case class TxRemoveOutput(channelId: ByteVector32,
case class TxComplete(channelId: ByteVector32,
tlvStream: TlvStream[TxCompleteTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId {
- val nonces_opt: Option[TxCompleteTlv.Nonces] = tlvStream.get[TxCompleteTlv.Nonces]
+ val commitNonces_opt: Option[TxCompleteTlv.CommitNonces] = tlvStream.get[TxCompleteTlv.CommitNonces]
+ val fundingNonce_opt: Option[IndividualNonce] = tlvStream.get[TxCompleteTlv.FundingInputNonce].map(_.nonce)
}
object TxComplete {
- def apply(channelId: ByteVector32, commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]): TxComplete =
- TxComplete(channelId, TlvStream(TxCompleteTlv.Nonces(commitNonce, nextCommitNonce, fundingNonce_opt)))
+ def apply(channelId: ByteVector32, commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]): TxComplete = {
+ val tlvs = Set(
+ Some(TxCompleteTlv.CommitNonces(commitNonce, nextCommitNonce)),
+ fundingNonce_opt.map(TxCompleteTlv.FundingInputNonce(_)),
+ ).flatten[TxCompleteTlv]
+ TxComplete(channelId, TlvStream(tlvs))
+ }
}
case class TxSignatures(channelId: ByteVector32,
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/InteractiveTxBuilderSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/InteractiveTxBuilderSpec.scala
index 32e81a0..8318941 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/InteractiveTxBuilderSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/InteractiveTxBuilderSpec.scala
@@ -503,14 +503,14 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val txCompleteB3 = fwd.forwardBob2Alice[TxComplete]
// Alice --- tx_complete --> Bob
val txCompleteA = fwd.forwardAlice2Bob[TxComplete]
- assert(txCompleteA.nonces_opt.nonEmpty)
- assert(txCompleteA.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty)
+ assert(txCompleteA.commitNonces_opt.nonEmpty)
+ assert(txCompleteA.fundingNonce_opt.isEmpty)
Seq(txCompleteB1, txCompleteB2, txCompleteB3).foreach(txCompleteB => {
- assert(txCompleteB.nonces_opt.nonEmpty)
- assert(txCompleteB.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty)
+ assert(txCompleteB.commitNonces_opt.nonEmpty)
+ assert(txCompleteB.fundingNonce_opt.isEmpty)
})
// Nonces change every time the shared transaction changes.
- assert(Seq(txCompleteB1, txCompleteB2, txCompleteB3).flatMap(_.nonces_opt).flatMap(n => Seq(n.commitNonce, n.nextCommitNonce)).toSet.size == 6)
+ assert(Seq(txCompleteB1, txCompleteB2, txCompleteB3).flatMap(_.commitNonces_opt).flatMap(n => Seq(n.commitNonce, n.nextCommitNonce)).toSet.size == 6)
// Alice is responsible for adding the shared output.
assert(aliceParams.fundingAmount == fundingA)
@@ -523,8 +523,8 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
val successB = bob2alice.expectMsgType[Succeeded]
assert(successB.commitSig.sigOrPartialSig.isInstanceOf[PartialSignatureWithNonce])
val (txA, _, txB, _) = fixtureParams.exchangeSigsBobFirst(bobParams, successA, successB)
- assert(successA.nextRemoteCommitNonce_opt.contains((txA.txId, txCompleteB3.nonces_opt.get.nextCommitNonce)))
- assert(successB.nextRemoteCommitNonce_opt.contains((txB.txId, txCompleteA.nonces_opt.get.nextCommitNonce)))
+ assert(successA.nextRemoteCommitNonce_opt.contains((txA.txId, txCompleteB3.commitNonces_opt.get.nextCommitNonce)))
+ assert(successB.nextRemoteCommitNonce_opt.contains((txB.txId, txCompleteA.commitNonces_opt.get.nextCommitNonce)))
// The resulting transaction is valid and has the right feerate.
assert(txA.txId == txB.txId)
assert(txA.signedTx.lockTime == aliceParams.lockTime)
@@ -1175,9 +1175,9 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
// Alice --- tx_complete --> Bob
val txCompleteA = fwdSplice.forwardAlice2Bob[TxComplete]
Seq(txCompleteA, txCompleteB).foreach(txComplete => {
- assert(txComplete.nonces_opt.nonEmpty)
- assert(txComplete.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty) // the previous commitment didn't use taproot
- assert(txComplete.nonces_opt.map(n => Seq(n.commitNonce, n.nextCommitNonce)).get.size == 2)
+ assert(txComplete.commitNonces_opt.nonEmpty)
+ assert(txComplete.fundingNonce_opt.isEmpty) // the previous commitment didn't use taproot
+ assert(txComplete.commitNonces_opt.map(n => Seq(n.commitNonce, n.nextCommitNonce)).get.size == 2)
})
val successA2 = alice2bob.expectMsgType[Succeeded]
@@ -1189,8 +1189,8 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
assert(successB2.signingSession.fundingTx.localSigs.previousFundingTxPartialSig_opt.isEmpty)
assert(successB2.commitSig.sigOrPartialSig.isInstanceOf[PartialSignatureWithNonce])
val (spliceTxA, commitmentA2, spliceTxB, commitmentB2) = fixtureParams.exchangeSigsBobFirst(spliceFixtureParams.fundingParamsB, successA2, successB2)
- assert(successA2.nextRemoteCommitNonce_opt.contains((spliceTxA.txId, txCompleteB.nonces_opt.get.nextCommitNonce)))
- assert(successB2.nextRemoteCommitNonce_opt.contains((spliceTxB.txId, txCompleteA.nonces_opt.get.nextCommitNonce)))
+ assert(successA2.nextRemoteCommitNonce_opt.contains((spliceTxA.txId, txCompleteB.commitNonces_opt.get.nextCommitNonce)))
+ assert(successB2.nextRemoteCommitNonce_opt.contains((spliceTxB.txId, txCompleteA.commitNonces_opt.get.nextCommitNonce)))
assert(spliceTxA.tx.localAmountIn > spliceTxA.tx.remoteAmountIn)
assert(spliceTxA.signedTx.txIn.exists(_.outPoint == commitmentA1.fundingInput))
assert(0.msat < spliceTxA.tx.localFees)
@@ -2948,13 +2948,13 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
// Alice --- tx_add_output --> Bob
bob ! ReceiveMessage(alice2bob.expectMsgType[SendMessage].msg.asInstanceOf[TxAddOutput])
val txCompleteBob = bob2alice.expectMsgType[SendMessage].msg.asInstanceOf[TxComplete]
- assert(txCompleteBob.nonces_opt.nonEmpty)
+ assert(txCompleteBob.commitNonces_opt.nonEmpty)
alice ! ReceiveMessage(txCompleteBob)
// Alice --- tx_complete --> Bob
bob ! ReceiveMessage(alice2bob.expectMsgType[SendMessage].msg.asInstanceOf[TxComplete])
// Alice <-- commit_sig --- Bob
val successA1 = alice2bob.expectMsgType[Succeeded]
- val invalidCommitSig = CommitSig(params.channelId, PartialSignatureWithNonce(randomBytes32(), txCompleteBob.nonces_opt.get.commitNonce), Nil, batchSize = 1)
+ val invalidCommitSig = CommitSig(params.channelId, PartialSignatureWithNonce(randomBytes32(), txCompleteBob.commitNonces_opt.get.commitNonce), Nil, batchSize = 1)
val Left(error) = successA1.signingSession.receiveCommitSig(params.channelParamsA, params.channelKeysA, invalidCommitSig, params.nodeParamsA.currentBlockHeight)(akka.event.NoLogging)
assert(error.isInstanceOf[InvalidCommitmentSignature])
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForDualFundingCreatedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForDualFundingCreatedStateSpec.scala
index b11293e..beadb3f 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForDualFundingCreatedStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForDualFundingCreatedStateSpec.scala
@@ -124,8 +124,8 @@ class WaitForDualFundingCreatedStateSpec extends TestKitBaseClass with FixtureAn
alice2bob.expectMsgType[TxAddOutput]
alice2bob.forward(bob)
val txComplete = bob2alice.expectMsgType[TxComplete]
- assert(txComplete.nonces_opt.isDefined)
- bob2alice.forward(alice, txComplete.copy(tlvStream = txComplete.tlvStream.copy(records = txComplete.tlvStream.records.filterNot(_.isInstanceOf[TxCompleteTlv.Nonces]))))
+ assert(txComplete.commitNonces_opt.isDefined)
+ bob2alice.forward(alice, txComplete.copy(tlvStream = txComplete.tlvStream.copy(records = txComplete.tlvStream.records.filterNot(_.isInstanceOf[TxCompleteTlv.CommitNonces]))))
aliceListener.expectMsgType[ChannelAborted]
awaitCond(alice.stateName == CLOSED)
}
diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala
index 29495eb..6e110fe 100644
--- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala
+++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala
@@ -993,8 +993,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
})
- assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
- assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
+ assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
+ assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
}
// Alice retransmits commit_sig, and they exchange tx_signatures afterwards.
@@ -1054,8 +1054,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
})
- assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
- assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
+ assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
+ assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
}
// Bob retransmits commit_sig and tx_signatures, then Alice sends her tx_signatures.
@@ -1149,8 +1149,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
assert(channelReestablish.nextCommitNonces.contains(rbfTx.txId))
})
- assert(channelReestablishAlice.nextCommitNonces.get(rbfTx.txId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
- assert(channelReestablishBob.nextCommitNonces.get(rbfTx.txId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
+ assert(channelReestablishAlice.nextCommitNonces.get(rbfTx.txId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
+ assert(channelReestablishBob.nextCommitNonces.get(rbfTx.txId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
}
// Alice and Bob exchange tx_signatures and complete the RBF attempt.
@@ -1252,8 +1252,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
assert(channelReestablish.nextCommitNonces.contains(currentFundingTxId))
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
})
- assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
- assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
+ assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
+ assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
}
// Alice and Bob exchange signatures and complete the RBF attempt.
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 64e9258..c9424a3 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
@@ -230,7 +230,7 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
TxRemoveOutput(channelId1, UInt64(1)) -> hex"0045 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0000000000000001",
TxComplete(channelId1) -> hex"0046 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
TxComplete(channelId1, nonce, nextNonce, None) -> hex"0046 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 04 84 2062534ccb3be5a8997843f3b6bc530a94cbc60eceb538674ceedd62d8be07f2dfa5df6acf3ded7444268d56925bb2c33afe71a55f4fa88f3985451a681415930f6b b218b34786408f0a1aee2b35a0e860aa234b8013d1c385d1fcb4583fc4472bedfdd69a53c71006ec9f8b33724b719a50aa137814f4d0c00caff4e1da0d9856a957e7",
- TxComplete(channelId1, nonce, nextNonce, Some(fundingNonce)) -> hex"0046 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 04 c6 2062534ccb3be5a8997843f3b6bc530a94cbc60eceb538674ceedd62d8be07f2dfa5df6acf3ded7444268d56925bb2c33afe71a55f4fa88f3985451a681415930f6b b218b34786408f0a1aee2b35a0e860aa234b8013d1c385d1fcb4583fc4472bedfdd69a53c71006ec9f8b33724b719a50aa137814f4d0c00caff4e1da0d9856a957e7 a49ff67b08c720b993c946556cde1be1c3b664bc847c4792135dfd6ef0986e00e9871808c6620b0420567dad525b27431453d4434fd326f8ac56496639b72326eb5d",
+ TxComplete(channelId1, nonce, nextNonce, Some(fundingNonce)) -> hex"0046 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 04842062534ccb3be5a8997843f3b6bc530a94cbc60eceb538674ceedd62d8be07f2dfa5df6acf3ded7444268d56925bb2c33afe71a55f4fa88f3985451a681415930f6bb218b34786408f0a1aee2b35a0e860aa234b8013d1c385d1fcb4583fc4472bedfdd69a53c71006ec9f8b33724b719a50aa137814f4d0c00caff4e1da0d9856a957e7 0642a49ff67b08c720b993c946556cde1be1c3b664bc847c4792135dfd6ef0986e00e9871808c6620b0420567dad525b27431453d4434fd326f8ac56496639b72326eb5d",
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",
Why this scored 25/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.