Files
lnd/graph/db/models/channel_edge_info_test.go
Elle Mouton 6842252bf8 graph/db: add v2 channel announcement fields to ChannelEdgeInfo
This commit extends ChannelEdgeInfo to support v2 (taproot) channel
announcements by adding:
- MerkleRootHash for the optional Merkle tree commitment
- ExtraSignedFields for additional signed TLV fields
- ChannelV2Fields struct to encapsulate v2-specific optional fields
- NewV2Channel constructor for creating v2 channel edges
- FundingPKScript handling for MuSig2 key aggregation with optional
  taproot tweaks
2026-02-13 07:21:22 +02:00

148 lines
4.0 KiB
Go

package models
import (
"testing"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/require"
)
// TestFundingPKScriptV2 tests the FundingPKScript method for v2 channels
// which uses MuSig2 key aggregation.
func TestFundingPKScriptV2(t *testing.T) {
t.Parallel()
// Generate two test keys for bitcoin keys.
privKey1, err := btcec.NewPrivateKey()
require.NoError(t, err)
pubKey1 := privKey1.PubKey()
privKey2, err := btcec.NewPrivateKey()
require.NoError(t, err)
pubKey2 := privKey2.PubKey()
// Convert to route.Vertex format.
var btcKey1, btcKey2 route.Vertex
copy(btcKey1[:], pubKey1.SerializeCompressed())
copy(btcKey2[:], pubKey2.SerializeCompressed())
// Create a test merkle root.
var merkleRoot chainhash.Hash
copy(merkleRoot[:], []byte("test-merkle-root-hash-32-bytes!"))
t.Run("v2 with btc keys, no merkle root (BIP86)", func(t *testing.T) {
t.Parallel()
edge := &ChannelEdgeInfo{
Version: lnwire.GossipVersion2,
BitcoinKey1Bytes: fn.Some(btcKey1),
BitcoinKey2Bytes: fn.Some(btcKey2),
}
pkScript, err := edge.FundingPKScript()
require.NoError(t, err)
require.NotEmpty(t, pkScript)
// Verify it's a valid taproot script (OP_1 <32-byte-key>).
require.Len(t, pkScript, 34)
require.Equal(t, byte(0x51), pkScript[0]) // OP_1
// Manually compute expected script using BIP86 tweak.
combinedKey, _, _, err := musig2.AggregateKeys(
[]*btcec.PublicKey{pubKey1, pubKey2}, true,
musig2.WithBIP86KeyTweak(),
)
require.NoError(t, err)
expectedScript, err := input.PayToTaprootScript(
combinedKey.FinalKey,
)
require.NoError(t, err)
require.Equal(t, expectedScript, pkScript)
})
t.Run("v2 with bitcoin keys and merkle root", func(t *testing.T) {
t.Parallel()
edge := &ChannelEdgeInfo{
Version: lnwire.GossipVersion2,
BitcoinKey1Bytes: fn.Some(btcKey1),
BitcoinKey2Bytes: fn.Some(btcKey2),
MerkleRootHash: fn.Some(merkleRoot),
}
pkScript, err := edge.FundingPKScript()
require.NoError(t, err)
require.NotEmpty(t, pkScript)
// Verify it's a valid taproot script.
require.Len(t, pkScript, 34)
require.Equal(t, byte(0x51), pkScript[0]) // OP_1
// Manually compute expected script with taproot tweak.
combinedKey, _, _, err := musig2.AggregateKeys(
[]*btcec.PublicKey{pubKey1, pubKey2}, true,
musig2.WithTaprootKeyTweak(merkleRoot[:]),
)
require.NoError(t, err)
expectedScript, err := input.PayToTaprootScript(
combinedKey.FinalKey,
)
require.NoError(t, err)
require.Equal(t, expectedScript, pkScript)
})
t.Run("v2 no btc keys returns stored script", func(t *testing.T) {
t.Parallel()
storedScript := []byte{0x51, 0x20} // OP_1 + push 32
storedScript = append(storedScript, make([]byte, 32)...)
edge := &ChannelEdgeInfo{
Version: lnwire.GossipVersion2,
FundingScript: fn.Some(storedScript),
}
pkScript, err := edge.FundingPKScript()
require.NoError(t, err)
require.Equal(t, storedScript, pkScript)
})
t.Run("v2 no btc keys and no stored script errors", func(t *testing.T) {
t.Parallel()
edge := &ChannelEdgeInfo{
Version: lnwire.GossipVersion2,
}
_, err := edge.FundingPKScript()
require.Error(t, err)
require.Contains(t, err.Error(), "expected a funding pk script")
})
t.Run("v2 one btc key returns stored script", func(t *testing.T) {
t.Parallel()
storedScript := []byte{0x51, 0x20}
storedScript = append(storedScript, make([]byte, 32)...)
// Only key1 set, key2 missing.
edge := &ChannelEdgeInfo{
Version: lnwire.GossipVersion2,
BitcoinKey1Bytes: fn.Some(btcKey1),
FundingScript: fn.Some(storedScript),
}
pkScript, err := edge.FundingPKScript()
require.NoError(t, err)
require.Equal(t, storedScript, pkScript)
})
}