@@ -2,6 +2,7 @@ package channeldb
2
2
3
3
import (
4
4
"bytes"
5
+ "crypto/hmac"
5
6
"crypto/sha256"
6
7
"encoding/binary"
7
8
"errors"
@@ -13,6 +14,7 @@ import (
13
14
"sync"
14
15
15
16
"github.com/btcsuite/btcd/btcec/v2"
17
+ "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
16
18
"github.com/btcsuite/btcd/btcutil"
17
19
"github.com/btcsuite/btcd/chaincfg/chainhash"
18
20
"github.com/btcsuite/btcd/wire"
@@ -303,6 +305,10 @@ const (
303
305
// ScidAliasFeatureBit indicates that the scid-alias feature bit was
304
306
// negotiated during the lifetime of this channel.
305
307
ScidAliasFeatureBit ChannelType = 1 << 9
308
+
309
+ // SimpleTaprootFeatureBit indicates that the simple-taproot-channels
310
+ // feature bit was negotiated during the lifetime of the channel.
311
+ SimpleTaprootFeatureBit ChannelType = 1 << 10
306
312
)
307
313
308
314
// IsSingleFunder returns true if the channel type if one of the known single
@@ -368,6 +374,11 @@ func (c ChannelType) HasScidAliasFeature() bool {
368
374
return c & ScidAliasFeatureBit == ScidAliasFeatureBit
369
375
}
370
376
377
+ // IsTaproot returns true if the channel is using taproot features.
378
+ func (c ChannelType ) IsTaproot () bool {
379
+ return c & SimpleTaprootFeatureBit == SimpleTaprootFeatureBit
380
+ }
381
+
371
382
// ChannelConstraints represents a set of constraints meant to allow a node to
372
383
// limit their exposure, enact flow control and ensure that all HTLCs are
373
384
// economically relevant. This struct will be mirrored for both sides of the
@@ -1372,6 +1383,65 @@ func (c *OpenChannel) SecondCommitmentPoint() (*btcec.PublicKey, error) {
1372
1383
return input .ComputeCommitmentPoint (revocation [:]), nil
1373
1384
}
1374
1385
1386
+ var (
1387
+ // taprootRevRootKey is the key used to derive the revocation root for
1388
+ // the taproot nonces. This is done via HMAC of the existing revocation
1389
+ // root.
1390
+ taprootRevRootKey = []byte ("taproot-rev-root" )
1391
+ )
1392
+
1393
+ // DeriveMusig2Shachain derives a shachain producer for the taproot channel
1394
+ // from normal shachain revocation root.
1395
+ func DeriveMusig2Shachain (revRoot shachain.Producer ) (shachain.Producer , error ) { //nolint:lll
1396
+ // In order to obtain the revocation root hash to create the taproot
1397
+ // revocation, we'll encode the producer into a buffer, then use that
1398
+ // to derive the shachain root needed.
1399
+ var rootHashBuf bytes.Buffer
1400
+ if err := revRoot .Encode (& rootHashBuf ); err != nil {
1401
+ return nil , fmt .Errorf ("unable to encode producer: %w" , err )
1402
+ }
1403
+
1404
+ revRootHash := chainhash .HashH (rootHashBuf .Bytes ())
1405
+
1406
+ // For taproot channel types, we'll also generate a distinct shachain
1407
+ // root using the same seed information. We'll use this to generate
1408
+ // verification nonces for the channel. We'll bind with this a simple
1409
+ // hmac.
1410
+ taprootRevHmac := hmac .New (sha256 .New , taprootRevRootKey )
1411
+ if _ , err := taprootRevHmac .Write (revRootHash [:]); err != nil {
1412
+ return nil , err
1413
+ }
1414
+
1415
+ taprootRevRoot := taprootRevHmac .Sum (nil )
1416
+
1417
+ // Once we have the root, we can then generate our shachain producer
1418
+ // and from that generate the per-commitment point.
1419
+ return shachain .NewRevocationProducerFromBytes (
1420
+ taprootRevRoot ,
1421
+ )
1422
+ }
1423
+
1424
+ // NewMusigVerificationNonce generates the local or verification nonce for
1425
+ // another musig2 session. In order to permit our implementation to not have to
1426
+ // write any secret nonce state to disk, we'll use the _next_ shachain
1427
+ // pre-image as our primary randomness source. When used to generate the nonce
1428
+ // again to broadcast our commitment hte current height will be used.
1429
+ func NewMusigVerificationNonce (pubKey * btcec.PublicKey , targetHeight uint64 ,
1430
+ shaGen shachain.Producer ) (* musig2.Nonces , error ) {
1431
+
1432
+ // Now that we know what height we need, we'll grab the shachain
1433
+ // pre-image at the target destination.
1434
+ nextPreimage , err := shaGen .AtIndex (targetHeight )
1435
+ if err != nil {
1436
+ return nil , err
1437
+ }
1438
+
1439
+ shaChainRand := musig2 .WithCustomRand (bytes .NewBuffer (nextPreimage [:]))
1440
+ pubKeyOpt := musig2 .WithPublicKey (pubKey )
1441
+
1442
+ return musig2 .GenNonces (pubKeyOpt , shaChainRand )
1443
+ }
1444
+
1375
1445
// ChanSyncMsg returns the ChannelReestablish message that should be sent upon
1376
1446
// reconnection with the remote peer that we're maintaining this channel with.
1377
1447
// The information contained within this message is necessary to re-sync our
@@ -1443,6 +1513,30 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
1443
1513
}
1444
1514
}
1445
1515
1516
+ // If this is a taproot channel, then we'll need to generate our next
1517
+ // verification nonce to send to the remote party. They'll use this to
1518
+ // sign the next update to our commitment transaction.
1519
+ var nextTaprootNonce * lnwire.Musig2Nonce
1520
+ if c .ChanType .IsTaproot () {
1521
+ taprootRevProducer , err := DeriveMusig2Shachain (
1522
+ c .RevocationProducer ,
1523
+ )
1524
+ if err != nil {
1525
+ return nil , err
1526
+ }
1527
+
1528
+ nextNonce , err := NewMusigVerificationNonce (
1529
+ c .LocalChanCfg .MultiSigKey .PubKey ,
1530
+ nextLocalCommitHeight , taprootRevProducer ,
1531
+ )
1532
+ if err != nil {
1533
+ return nil , fmt .Errorf ("unable to gen next " +
1534
+ "nonce: %w" , err )
1535
+ }
1536
+
1537
+ nextTaprootNonce = (* lnwire .Musig2Nonce )(& nextNonce .PubNonce )
1538
+ }
1539
+
1446
1540
return & lnwire.ChannelReestablish {
1447
1541
ChanID : lnwire .NewChanIDFromOutPoint (
1448
1542
& c .FundingOutpoint ,
@@ -1453,6 +1547,7 @@ func (c *OpenChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
1453
1547
LocalUnrevokedCommitPoint : input .ComputeCommitmentPoint (
1454
1548
currentCommitSecret [:],
1455
1549
),
1550
+ LocalNonce : nextTaprootNonce ,
1456
1551
}, nil
1457
1552
}
1458
1553
@@ -3854,8 +3949,6 @@ func putChanRevocationState(chanBucket kvdb.RwBucket, channel *OpenChannel) erro
3854
3949
return err
3855
3950
}
3856
3951
3857
- // TODO(roasbeef): don't keep producer on disk
3858
-
3859
3952
// If the next revocation is present, which is only the case after the
3860
3953
// ChannelReady message has been sent, then we'll write it to disk.
3861
3954
if channel .RemoteNextRevocation != nil {
0 commit comments