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
148 lines
4.0 KiB
Go
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)
|
|
})
|
|
}
|