@@ -3073,6 +3073,25 @@ func TestAddHTLCNegativeBalance(t *testing.T) {
3073
3073
// assertNoChanSyncNeeded is a helper function that asserts that upon restart,
3074
3074
// two channels conclude that they're fully synchronized and don't need to
3075
3075
// retransmit any new messages.
3076
+ // extractCommitmentNonce extracts the commitment nonce from a ChannelReestablish
3077
+ // message, prioritizing LocalNonces over the legacy LocalNonce field.
3078
+ func extractCommitmentNonce (t * testing.T , msg * lnwire.ChannelReestablish ) lnwire.Musig2Nonce {
3079
+ // Prefer LocalNonces if present
3080
+ if msg .LocalNonces .IsSome () {
3081
+ noncesData := msg .LocalNonces .UnwrapOrFail (t )
3082
+
3083
+ // Return the first nonce (for main commitment)
3084
+ for _ , nonce := range noncesData .NoncesMap {
3085
+ return nonce
3086
+ }
3087
+
3088
+ // If map is empty, fall back to LocalNonce
3089
+ }
3090
+
3091
+ // Fall back to legacy LocalNonce field
3092
+ return msg .LocalNonce .UnwrapOrFailV (t )
3093
+ }
3094
+
3076
3095
func assertNoChanSyncNeeded (t * testing.T , aliceChannel * LightningChannel ,
3077
3096
bobChannel * LightningChannel ) {
3078
3097
@@ -3090,13 +3109,14 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel,
3090
3109
}
3091
3110
3092
3111
// For taproot channels, simulate the link/peer binding the generated
3093
- // nonces.
3112
+ // nonces. Use helper to extract nonces from either LocalNonces or
3113
+ // LocalNonce.
3094
3114
if aliceChannel .channelState .ChanType .IsTaproot () {
3095
3115
aliceChannel .pendingVerificationNonce = & musig2.Nonces {
3096
- PubNonce : aliceChanSyncMsg . LocalNonce . UnwrapOrFailV ( t ),
3116
+ PubNonce : extractCommitmentNonce ( t , aliceChanSyncMsg ),
3097
3117
}
3098
3118
bobChannel .pendingVerificationNonce = & musig2.Nonces {
3099
- PubNonce : bobChanSyncMsg . LocalNonce . UnwrapOrFailV ( t ),
3119
+ PubNonce : extractCommitmentNonce ( t , bobChanSyncMsg ),
3100
3120
}
3101
3121
}
3102
3122
@@ -3538,6 +3558,71 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) {
3538
3558
}
3539
3559
}
3540
3560
3561
+ // TestChanSyncTaprootLocalNonces tests that for taproot channels, both the
3562
+ // legacy LocalNonce field and the new LocalNonces field are populated in
3563
+ // ChannelReestablish messages, and that the receiving side can handle either.
3564
+ func TestChanSyncTaprootLocalNonces (t * testing.T ) {
3565
+ t .Parallel ()
3566
+
3567
+ // Create a taproot test channel.
3568
+ chanType := channeldb .SimpleTaprootFeatureBit
3569
+ aliceChannel , bobChannel , err := CreateTestChannels (t , chanType )
3570
+ require .NoError (t , err , "unable to create test channels" )
3571
+
3572
+ // Both sides should be fully synced from the start.
3573
+ assertNoChanSyncNeeded (t , aliceChannel , bobChannel )
3574
+
3575
+ // Generate ChannelReestablish messages.
3576
+ aliceChanSyncMsg , err := aliceChannel .channelState .ChanSyncMsg ()
3577
+ require .NoError (t , err , "unable to produce chan sync msg" )
3578
+ bobChanSyncMsg , err := bobChannel .channelState .ChanSyncMsg ()
3579
+ require .NoError (t , err , "unable to produce chan sync msg" )
3580
+
3581
+ // For taproot channels, both LocalNonce and LocalNonces should be populated.
3582
+ require .True (t , aliceChanSyncMsg .LocalNonce .IsSome (), "LocalNonce should be set" )
3583
+ require .True (t , aliceChanSyncMsg .LocalNonces .IsSome (), "LocalNonces should be set" )
3584
+ require .True (t , bobChanSyncMsg .LocalNonce .IsSome (), "LocalNonce should be set" )
3585
+ require .True (t , bobChanSyncMsg .LocalNonces .IsSome (), "LocalNonces should be set" )
3586
+
3587
+ // The nonces from both fields should be identical.
3588
+ aliceLegacyNonce := aliceChanSyncMsg .LocalNonce .UnwrapOrFailV (t )
3589
+ aliceNoncesData := aliceChanSyncMsg .LocalNonces .UnwrapOrFail (t )
3590
+ require .Len (t , aliceNoncesData .NoncesMap , 1 , "should have exactly one nonce" )
3591
+ var aliceMapNonce lnwire.Musig2Nonce
3592
+ for _ , nonce := range aliceNoncesData .NoncesMap {
3593
+ aliceMapNonce = nonce
3594
+ break
3595
+ }
3596
+ require .Equal (t , aliceLegacyNonce , aliceMapNonce , "nonces should match" )
3597
+
3598
+ // Test that our helper function works correctly.
3599
+ extractedNonce := extractCommitmentNonce (t , aliceChanSyncMsg )
3600
+ require .Equal (t , aliceLegacyNonce , extractedNonce , "helper should extract correct nonce" )
3601
+
3602
+ // Test sync behavior when only LocalNonces is present by clearing LocalNonce.
3603
+ aliceModifiedMsg := * aliceChanSyncMsg
3604
+ aliceModifiedMsg .LocalNonce = lnwire.OptMusig2NonceTLV {}
3605
+
3606
+ // Bob should still be able to process the message with only LocalNonces.
3607
+ bobChannel .pendingVerificationNonce = & musig2.Nonces {
3608
+ PubNonce : extractCommitmentNonce (t , bobChanSyncMsg ),
3609
+ }
3610
+ bobMsgsToSend , _ , _ , err := bobChannel .ProcessChanSyncMsg (
3611
+ ctxb , & aliceModifiedMsg ,
3612
+ )
3613
+ require .NoError (t , err , "unable to process modified ChannelReestablish msg" )
3614
+ require .Empty (t , bobMsgsToSend , "bob shouldn't need to send messages" )
3615
+
3616
+ // Test that missing both fields results in an error.
3617
+ aliceEmptyMsg := * aliceChanSyncMsg
3618
+ aliceEmptyMsg .LocalNonce = lnwire.OptMusig2NonceTLV {}
3619
+ aliceEmptyMsg .LocalNonces = lnwire.OptLocalNonces {}
3620
+
3621
+ _ , _ , _ , err = bobChannel .ProcessChanSyncMsg (ctxb , & aliceEmptyMsg )
3622
+ require .Error (t , err , "should error when no nonce is provided" )
3623
+ require .Contains (t , err .Error (), "remote verification nonce not sent" )
3624
+ }
3625
+
3541
3626
// TestChanSyncOweCommitment tests that if Bob restarts (and then Alice) before
3542
3627
// he receives Alice's CommitSig message, then Alice concludes that she needs
3543
3628
// to re-send the CommitDiff. After the diff has been sent, both nodes should
0 commit comments