Skip to content

Commit d7ee663

Browse files
authored
Split commit nonces from funding nonce in tx_complete (#3145)
The commit nonces and funding nonce provided in `tx_complete` are actually completely orthogonal: - the commit nonces must be provided whenever the *next* commitment format is using taproot - the funding nonce must be provided whenever the *previous* commitment format is using taproot It thus makes more sense to split them into separate TLVs.
1 parent 0e0da42 commit d7ee663

File tree

7 files changed

+52
-48
lines changed

7 files changed

+52
-48
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
722722
replyTo ! RemoteFailure(cause)
723723
unlockAndStop(session)
724724
case Right(completeTx) =>
725-
signCommitTx(completeTx, session.txCompleteReceived.flatMap(_.nonces_opt))
725+
signCommitTx(completeTx, session.txCompleteReceived.flatMap(_.fundingNonce_opt), session.txCompleteReceived.flatMap(_.commitNonces_opt))
726726
}
727727
case _: WalletFailure =>
728728
replyTo ! RemoteFailure(UnconfirmedInteractiveTxInputs(fundingParams.channelId))
@@ -799,7 +799,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
799799
case _: SegwitV0CommitmentFormat => ()
800800
case _: SimpleTaprootChannelCommitmentFormat =>
801801
// If we're spending a taproot channel, our peer must provide a nonce for the shared input.
802-
val remoteFundingNonce_opt: Option[IndividualNonce] = session.txCompleteReceived.flatMap(_.nonces_opt).flatMap(_.fundingNonce_opt)
802+
val remoteFundingNonce_opt = session.txCompleteReceived.flatMap(_.fundingNonce_opt)
803803
if (remoteFundingNonce_opt.isEmpty) return Left(MissingFundingNonce(fundingParams.channelId, sharedInput.info.outPoint.txid))
804804
}
805805
sharedInputs.headOption match {
@@ -821,7 +821,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
821821
fundingParams.commitmentFormat match {
822822
case _: SegwitV0CommitmentFormat => ()
823823
case _: SimpleTaprootChannelCommitmentFormat =>
824-
val remoteCommitNonces_opt = session.txCompleteReceived.flatMap(_.nonces_opt)
824+
val remoteCommitNonces_opt = session.txCompleteReceived.flatMap(_.commitNonces_opt)
825825
if (remoteCommitNonces_opt.isEmpty) return Left(MissingCommitNonce(fundingParams.channelId, tx.txid, purpose.remoteCommitIndex))
826826
}
827827

@@ -890,7 +890,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
890890
Right(sharedTx)
891891
}
892892

893-
private def signCommitTx(completeTx: SharedTransaction, remoteNonces_opt: Option[TxCompleteTlv.Nonces]): Behavior[Command] = {
893+
private def signCommitTx(completeTx: SharedTransaction, remoteFundingNonce_opt: Option[IndividualNonce], remoteCommitNonces_opt: Option[TxCompleteTlv.CommitNonces]): Behavior[Command] = {
894894
val fundingTx = completeTx.buildUnsignedTx()
895895
val fundingOutputIndex = fundingTx.txOut.indexWhere(_.publicKeyScript == fundingPubkeyScript)
896896
val liquidityFee = fundingParams.liquidityFees(liquidityPurchase_opt)
@@ -916,7 +916,7 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
916916
val localSigOfRemoteTx = fundingParams.commitmentFormat match {
917917
case _: SegwitV0CommitmentFormat => Right(remoteCommitTx.sign(localFundingKey, fundingParams.remoteFundingPubKey))
918918
case _: SimpleTaprootChannelCommitmentFormat =>
919-
remoteNonces_opt match {
919+
remoteCommitNonces_opt match {
920920
case Some(remoteNonces) =>
921921
val localNonce = NonceGenerator.signingNonce(localFundingKey.publicKey, fundingParams.remoteFundingPubKey, fundingTx.txid)
922922
remoteCommitTx.partialSign(localFundingKey, fundingParams.remoteFundingPubKey, localNonce, Seq(localNonce.publicNonce, remoteNonces.commitNonce)) match {
@@ -935,13 +935,13 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon
935935
val localCommitSig = CommitSig(fundingParams.channelId, localSigOfRemoteTx, htlcSignatures, batchSize = 1)
936936
val localCommit = UnsignedLocalCommit(purpose.localCommitIndex, localSpec, localCommitTx.tx.txid)
937937
val remoteCommit = RemoteCommit(purpose.remoteCommitIndex, remoteSpec, remoteCommitTx.tx.txid, purpose.remotePerCommitmentPoint)
938-
signFundingTx(completeTx, remoteNonces_opt, localCommitSig, localCommit, remoteCommit)
938+
signFundingTx(completeTx, remoteFundingNonce_opt, remoteCommitNonces_opt.map(_.nextCommitNonce), localCommitSig, localCommit, remoteCommit)
939939
}
940940
}
941941
}
942942

943-
private def signFundingTx(completeTx: SharedTransaction, remoteNonces_opt: Option[TxCompleteTlv.Nonces], commitSig: CommitSig, localCommit: UnsignedLocalCommit, remoteCommit: RemoteCommit): Behavior[Command] = {
944-
signTx(completeTx, remoteNonces_opt.flatMap(_.fundingNonce_opt))
943+
private def signFundingTx(completeTx: SharedTransaction, remoteFundingNonce_opt: Option[IndividualNonce], nextRemoteCommitNonce_opt: Option[IndividualNonce], commitSig: CommitSig, localCommit: UnsignedLocalCommit, remoteCommit: RemoteCommit): Behavior[Command] = {
944+
signTx(completeTx, remoteFundingNonce_opt)
945945
Behaviors.receiveMessagePartial {
946946
case SignTransactionResult(signedTx) =>
947947
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
978978
remoteCommit,
979979
liquidityPurchase_opt.map(_.basicInfo(isBuyer = fundingParams.isInitiator))
980980
)
981-
val nextRemoteCommitNonce_opt = remoteNonces_opt.map(n => signedTx.txId -> n.nextCommitNonce)
982-
replyTo ! Succeeded(signingSession, commitSig, liquidityPurchase_opt, nextRemoteCommitNonce_opt)
981+
replyTo ! Succeeded(signingSession, commitSig, liquidityPurchase_opt, nextRemoteCommitNonce_opt.map(n => signedTx.txId -> n))
983982
Behaviors.stopped
984983
case WalletFailure(t) =>
985984
log.error("could not sign funding transaction: ", t)

eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/InteractiveTxTlv.scala

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,20 @@ sealed trait TxCompleteTlv extends Tlv
7676

7777
object TxCompleteTlv {
7878
/**
79-
* Musig2 nonces exchanged during an interactive tx session, when using a taproot channel or upgrading a channel to
80-
* use taproot.
79+
* Musig2 nonces for the commitment transaction(s), exchanged during an interactive tx session, when using a taproot
80+
* channel or upgrading a channel to use taproot.
8181
*
82-
* @param commitNonce the sender's verification nonce for the current commit tx spending the interactive tx.
83-
* @param nextCommitNonce the sender's verification nonce for the next commit tx spending the interactive tx.
84-
* @param fundingNonce_opt when splicing a taproot channel, the sender's random signing nonce for the previous funding output.
82+
* @param commitNonce the sender's verification nonce for the current commit tx spending the interactive tx.
83+
* @param nextCommitNonce the sender's verification nonce for the next commit tx spending the interactive tx.
8584
*/
86-
case class Nonces(commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]) extends TxCompleteTlv
85+
case class CommitNonces(commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce) extends TxCompleteTlv
8786

88-
object Nonces {
89-
val codec: Codec[Nonces] = tlvField((publicNonce :: publicNonce :: optional(bitsRemaining, publicNonce)).as[Nonces])
90-
}
87+
/** When splicing a taproot channel, the sender's random signing nonce for the previous funding output. */
88+
case class FundingInputNonce(nonce: IndividualNonce) extends TxCompleteTlv
9189

9290
val txCompleteTlvCodec: Codec[TlvStream[TxCompleteTlv]] = tlvStream(discriminated[TxCompleteTlv].by(varint)
93-
.typecase(UInt64(4), Nonces.codec)
91+
.typecase(UInt64(4), tlvField[CommitNonces, CommitNonces]((publicNonce :: publicNonce).as[CommitNonces]))
92+
.typecase(UInt64(6), tlvField[FundingInputNonce, FundingInputNonce](publicNonce.as[FundingInputNonce]))
9493
)
9594
}
9695

eclair-core/src/main/scala/fr/acinq/eclair/wire/protocol/LightningMessageTypes.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,18 @@ case class TxRemoveOutput(channelId: ByteVector32,
122122

123123
case class TxComplete(channelId: ByteVector32,
124124
tlvStream: TlvStream[TxCompleteTlv] = TlvStream.empty) extends InteractiveTxConstructionMessage with HasChannelId {
125-
val nonces_opt: Option[TxCompleteTlv.Nonces] = tlvStream.get[TxCompleteTlv.Nonces]
125+
val commitNonces_opt: Option[TxCompleteTlv.CommitNonces] = tlvStream.get[TxCompleteTlv.CommitNonces]
126+
val fundingNonce_opt: Option[IndividualNonce] = tlvStream.get[TxCompleteTlv.FundingInputNonce].map(_.nonce)
126127
}
127128

128129
object TxComplete {
129-
def apply(channelId: ByteVector32, commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]): TxComplete =
130-
TxComplete(channelId, TlvStream(TxCompleteTlv.Nonces(commitNonce, nextCommitNonce, fundingNonce_opt)))
130+
def apply(channelId: ByteVector32, commitNonce: IndividualNonce, nextCommitNonce: IndividualNonce, fundingNonce_opt: Option[IndividualNonce]): TxComplete = {
131+
val tlvs = Set(
132+
Some(TxCompleteTlv.CommitNonces(commitNonce, nextCommitNonce)),
133+
fundingNonce_opt.map(TxCompleteTlv.FundingInputNonce(_)),
134+
).flatten[TxCompleteTlv]
135+
TxComplete(channelId, TlvStream(tlvs))
136+
}
131137
}
132138

133139
case class TxSignatures(channelId: ByteVector32,

eclair-core/src/test/scala/fr/acinq/eclair/channel/InteractiveTxBuilderSpec.scala

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -503,14 +503,14 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
503503
val txCompleteB3 = fwd.forwardBob2Alice[TxComplete]
504504
// Alice --- tx_complete --> Bob
505505
val txCompleteA = fwd.forwardAlice2Bob[TxComplete]
506-
assert(txCompleteA.nonces_opt.nonEmpty)
507-
assert(txCompleteA.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty)
506+
assert(txCompleteA.commitNonces_opt.nonEmpty)
507+
assert(txCompleteA.fundingNonce_opt.isEmpty)
508508
Seq(txCompleteB1, txCompleteB2, txCompleteB3).foreach(txCompleteB => {
509-
assert(txCompleteB.nonces_opt.nonEmpty)
510-
assert(txCompleteB.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty)
509+
assert(txCompleteB.commitNonces_opt.nonEmpty)
510+
assert(txCompleteB.fundingNonce_opt.isEmpty)
511511
})
512512
// Nonces change every time the shared transaction changes.
513-
assert(Seq(txCompleteB1, txCompleteB2, txCompleteB3).flatMap(_.nonces_opt).flatMap(n => Seq(n.commitNonce, n.nextCommitNonce)).toSet.size == 6)
513+
assert(Seq(txCompleteB1, txCompleteB2, txCompleteB3).flatMap(_.commitNonces_opt).flatMap(n => Seq(n.commitNonce, n.nextCommitNonce)).toSet.size == 6)
514514

515515
// Alice is responsible for adding the shared output.
516516
assert(aliceParams.fundingAmount == fundingA)
@@ -523,8 +523,8 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
523523
val successB = bob2alice.expectMsgType[Succeeded]
524524
assert(successB.commitSig.sigOrPartialSig.isInstanceOf[PartialSignatureWithNonce])
525525
val (txA, _, txB, _) = fixtureParams.exchangeSigsBobFirst(bobParams, successA, successB)
526-
assert(successA.nextRemoteCommitNonce_opt.contains((txA.txId, txCompleteB3.nonces_opt.get.nextCommitNonce)))
527-
assert(successB.nextRemoteCommitNonce_opt.contains((txB.txId, txCompleteA.nonces_opt.get.nextCommitNonce)))
526+
assert(successA.nextRemoteCommitNonce_opt.contains((txA.txId, txCompleteB3.commitNonces_opt.get.nextCommitNonce)))
527+
assert(successB.nextRemoteCommitNonce_opt.contains((txB.txId, txCompleteA.commitNonces_opt.get.nextCommitNonce)))
528528
// The resulting transaction is valid and has the right feerate.
529529
assert(txA.txId == txB.txId)
530530
assert(txA.signedTx.lockTime == aliceParams.lockTime)
@@ -1175,9 +1175,9 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
11751175
// Alice --- tx_complete --> Bob
11761176
val txCompleteA = fwdSplice.forwardAlice2Bob[TxComplete]
11771177
Seq(txCompleteA, txCompleteB).foreach(txComplete => {
1178-
assert(txComplete.nonces_opt.nonEmpty)
1179-
assert(txComplete.nonces_opt.flatMap(_.fundingNonce_opt).isEmpty) // the previous commitment didn't use taproot
1180-
assert(txComplete.nonces_opt.map(n => Seq(n.commitNonce, n.nextCommitNonce)).get.size == 2)
1178+
assert(txComplete.commitNonces_opt.nonEmpty)
1179+
assert(txComplete.fundingNonce_opt.isEmpty) // the previous commitment didn't use taproot
1180+
assert(txComplete.commitNonces_opt.map(n => Seq(n.commitNonce, n.nextCommitNonce)).get.size == 2)
11811181
})
11821182

11831183
val successA2 = alice2bob.expectMsgType[Succeeded]
@@ -1189,8 +1189,8 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
11891189
assert(successB2.signingSession.fundingTx.localSigs.previousFundingTxPartialSig_opt.isEmpty)
11901190
assert(successB2.commitSig.sigOrPartialSig.isInstanceOf[PartialSignatureWithNonce])
11911191
val (spliceTxA, commitmentA2, spliceTxB, commitmentB2) = fixtureParams.exchangeSigsBobFirst(spliceFixtureParams.fundingParamsB, successA2, successB2)
1192-
assert(successA2.nextRemoteCommitNonce_opt.contains((spliceTxA.txId, txCompleteB.nonces_opt.get.nextCommitNonce)))
1193-
assert(successB2.nextRemoteCommitNonce_opt.contains((spliceTxB.txId, txCompleteA.nonces_opt.get.nextCommitNonce)))
1192+
assert(successA2.nextRemoteCommitNonce_opt.contains((spliceTxA.txId, txCompleteB.commitNonces_opt.get.nextCommitNonce)))
1193+
assert(successB2.nextRemoteCommitNonce_opt.contains((spliceTxB.txId, txCompleteA.commitNonces_opt.get.nextCommitNonce)))
11941194
assert(spliceTxA.tx.localAmountIn > spliceTxA.tx.remoteAmountIn)
11951195
assert(spliceTxA.signedTx.txIn.exists(_.outPoint == commitmentA1.fundingInput))
11961196
assert(0.msat < spliceTxA.tx.localFees)
@@ -2948,13 +2948,13 @@ class InteractiveTxBuilderSpec extends TestKitBaseClass with AnyFunSuiteLike wit
29482948
// Alice --- tx_add_output --> Bob
29492949
bob ! ReceiveMessage(alice2bob.expectMsgType[SendMessage].msg.asInstanceOf[TxAddOutput])
29502950
val txCompleteBob = bob2alice.expectMsgType[SendMessage].msg.asInstanceOf[TxComplete]
2951-
assert(txCompleteBob.nonces_opt.nonEmpty)
2951+
assert(txCompleteBob.commitNonces_opt.nonEmpty)
29522952
alice ! ReceiveMessage(txCompleteBob)
29532953
// Alice --- tx_complete --> Bob
29542954
bob ! ReceiveMessage(alice2bob.expectMsgType[SendMessage].msg.asInstanceOf[TxComplete])
29552955
// Alice <-- commit_sig --- Bob
29562956
val successA1 = alice2bob.expectMsgType[Succeeded]
2957-
val invalidCommitSig = CommitSig(params.channelId, PartialSignatureWithNonce(randomBytes32(), txCompleteBob.nonces_opt.get.commitNonce), Nil, batchSize = 1)
2957+
val invalidCommitSig = CommitSig(params.channelId, PartialSignatureWithNonce(randomBytes32(), txCompleteBob.commitNonces_opt.get.commitNonce), Nil, batchSize = 1)
29582958
val Left(error) = successA1.signingSession.receiveCommitSig(params.channelParamsA, params.channelKeysA, invalidCommitSig, params.nodeParamsA.currentBlockHeight)(akka.event.NoLogging)
29592959
assert(error.isInstanceOf[InvalidCommitmentSignature])
29602960
}

eclair-core/src/test/scala/fr/acinq/eclair/channel/states/b/WaitForDualFundingCreatedStateSpec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ class WaitForDualFundingCreatedStateSpec extends TestKitBaseClass with FixtureAn
124124
alice2bob.expectMsgType[TxAddOutput]
125125
alice2bob.forward(bob)
126126
val txComplete = bob2alice.expectMsgType[TxComplete]
127-
assert(txComplete.nonces_opt.isDefined)
128-
bob2alice.forward(alice, txComplete.copy(tlvStream = txComplete.tlvStream.copy(records = txComplete.tlvStream.records.filterNot(_.isInstanceOf[TxCompleteTlv.Nonces]))))
127+
assert(txComplete.commitNonces_opt.isDefined)
128+
bob2alice.forward(alice, txComplete.copy(tlvStream = txComplete.tlvStream.copy(records = txComplete.tlvStream.records.filterNot(_.isInstanceOf[TxCompleteTlv.CommitNonces]))))
129129
aliceListener.expectMsgType[ChannelAborted]
130130
awaitCond(alice.stateName == CLOSED)
131131
}

eclair-core/src/test/scala/fr/acinq/eclair/channel/states/c/WaitForDualFundingConfirmedStateSpec.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -993,8 +993,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
993993
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
994994
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
995995
})
996-
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
997-
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
996+
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
997+
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
998998
}
999999

10001000
// Alice retransmits commit_sig, and they exchange tx_signatures afterwards.
@@ -1054,8 +1054,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
10541054
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
10551055
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
10561056
})
1057-
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
1058-
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
1057+
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
1058+
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
10591059
}
10601060

10611061
// Bob retransmits commit_sig and tx_signatures, then Alice sends her tx_signatures.
@@ -1149,8 +1149,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
11491149
assert(channelReestablish.nextCommitNonces.contains(fundingTxId))
11501150
assert(channelReestablish.nextCommitNonces.contains(rbfTx.txId))
11511151
})
1152-
assert(channelReestablishAlice.nextCommitNonces.get(rbfTx.txId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
1153-
assert(channelReestablishBob.nextCommitNonces.get(rbfTx.txId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
1152+
assert(channelReestablishAlice.nextCommitNonces.get(rbfTx.txId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
1153+
assert(channelReestablishBob.nextCommitNonces.get(rbfTx.txId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
11541154
}
11551155

11561156
// Alice and Bob exchange tx_signatures and complete the RBF attempt.
@@ -1252,8 +1252,8 @@ class WaitForDualFundingConfirmedStateSpec extends TestKitBaseClass with Fixture
12521252
assert(channelReestablish.nextCommitNonces.contains(currentFundingTxId))
12531253
assert(channelReestablish.nextCommitNonces.contains(rbfTxId))
12541254
})
1255-
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.nonces_opt.get.nextCommitNonce))
1256-
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.nonces_opt.get.nextCommitNonce))
1255+
assert(channelReestablishAlice.nextCommitNonces.get(rbfTxId).contains(txCompleteAlice.commitNonces_opt.get.nextCommitNonce))
1256+
assert(channelReestablishBob.nextCommitNonces.get(rbfTxId).contains(txCompleteBob.commitNonces_opt.get.nextCommitNonce))
12571257
}
12581258

12591259
// Alice and Bob exchange signatures and complete the RBF attempt.

0 commit comments

Comments
 (0)