Merge pull request #9477 from ellemouton/graph2

discovery+graph: various preparations for moving funding tx validation to the gossiper
This commit is contained in:
Oliver Gugger authored and GitHub committed 2025-02-10 05:58:05 -06:00
commit 6eb8f1f6e3
7 files changed
+153 -102

No files matched your search

+4 -5
View File
@@ -2678,10 +2678,9 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
return anns, true return anns, true
case graph.IsError( case errors.Is(err, graph.ErrNoFundingTransaction),
err, graph.ErrNoFundingTransaction, errors.Is(err, graph.ErrInvalidFundingOutput):
graph.ErrInvalidFundingOutput,
):
key := newRejectCacheKey( key := newRejectCacheKey(
scid.ToUint64(), scid.ToUint64(),
sourceToPub(nMsg.source), sourceToPub(nMsg.source),
@@ -2695,7 +2694,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
d.banman.incrementBanScore(nMsg.peer.PubKey()) d.banman.incrementBanScore(nMsg.peer.PubKey())
} }
case graph.IsError(err, graph.ErrChannelSpent): case errors.Is(err, graph.ErrChannelSpent):
key := newRejectCacheKey( key := newRejectCacheKey(
scid.ToUint64(), scid.ToUint64(),
sourceToPub(nMsg.source), sourceToPub(nMsg.source),
+107 -72
View File
@@ -24,11 +24,11 @@ import (
"github.com/lightningnetwork/lnd/batch" "github.com/lightningnetwork/lnd/batch"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/graph"
graphdb "github.com/lightningnetwork/lnd/graph/db" graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models" "github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnmock"
"github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntest/mock"
"github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntest/wait"
@@ -82,7 +82,7 @@ type mockGraphSource struct {
edges map[uint64][]models.ChannelEdgePolicy edges map[uint64][]models.ChannelEdgePolicy
zombies map[uint64][][33]byte zombies map[uint64][][33]byte
chansToReject map[uint64]struct{} chansToReject map[uint64]struct{}
addEdgeErrCode fn.Option[graph.ErrorCode] addEdgeErr error
} }
func newMockRouter(height uint32) *mockGraphSource { func newMockRouter(height uint32) *mockGraphSource {
@@ -113,10 +113,8 @@ func (r *mockGraphSource) AddEdge(info *models.ChannelEdgeInfo,
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
if r.addEdgeErrCode.IsSome() { if r.addEdgeErr != nil {
return graph.NewErrf( return r.addEdgeErr
r.addEdgeErrCode.UnsafeFromSome(), "received error",
)
} }
if _, ok := r.infos[info.ChannelID]; ok { if _, ok := r.infos[info.ChannelID]; ok {
@@ -131,12 +129,12 @@ func (r *mockGraphSource) AddEdge(info *models.ChannelEdgeInfo,
return nil return nil
} }
func (r *mockGraphSource) resetAddEdgeErrCode() { func (r *mockGraphSource) resetAddEdgeErr() {
r.addEdgeErrCode = fn.None[graph.ErrorCode]() r.addEdgeErr = nil
} }
func (r *mockGraphSource) setAddEdgeErrCode(code graph.ErrorCode) { func (r *mockGraphSource) setAddEdgeErr(err error) {
r.addEdgeErrCode = fn.Some[graph.ErrorCode](code) r.addEdgeErr = err
} }
func (r *mockGraphSource) queueValidationFail(chanID uint64) { func (r *mockGraphSource) queueValidationFail(chanID uint64) {
@@ -471,15 +469,23 @@ type annBatch struct {
remoteProofAnn *lnwire.AnnounceSignatures1 remoteProofAnn *lnwire.AnnounceSignatures1
} }
func createLocalAnnouncements(blockHeight uint32) (*annBatch, error) { func (ctx *testCtx) createLocalAnnouncements(blockHeight uint32) (*annBatch,
return createAnnouncements(blockHeight, selfKeyPriv, remoteKeyPriv1) error) {
return ctx.createAnnouncements(blockHeight, selfKeyPriv, remoteKeyPriv1)
} }
func createRemoteAnnouncements(blockHeight uint32) (*annBatch, error) { func (ctx *testCtx) createRemoteAnnouncements(blockHeight uint32) (*annBatch,
return createAnnouncements(blockHeight, remoteKeyPriv1, remoteKeyPriv2) error) {
return ctx.createAnnouncements(
blockHeight, remoteKeyPriv1, remoteKeyPriv2,
)
} }
func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*annBatch, error) { func (ctx *testCtx) createAnnouncements(blockHeight uint32, key1,
key2 *btcec.PrivateKey) (*annBatch, error) {
var err error var err error
var batch annBatch var batch annBatch
timestamp := testTimestamp timestamp := testTimestamp
@@ -494,7 +500,9 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann
return nil, err return nil, err
} }
batch.chanAnn, err = createChannelAnnouncement(blockHeight, key1, key2) batch.chanAnn, err = ctx.createChannelAnnouncement(
blockHeight, key1, key2,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -619,9 +627,26 @@ func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate1) error {
return nil return nil
} }
func createAnnouncementWithoutProof(blockHeight uint32, type fundingTxOpts struct {
extraBytes []byte
}
type fundingTxOption func(*fundingTxOpts)
func withExtraBytes(extraBytes []byte) fundingTxOption {
return func(opts *fundingTxOpts) {
opts.extraBytes = extraBytes
}
}
func (ctx *testCtx) createAnnouncementWithoutProof(blockHeight uint32,
key1, key2 *btcec.PublicKey, key1, key2 *btcec.PublicKey,
extraBytes ...[]byte) *lnwire.ChannelAnnouncement1 { options ...fundingTxOption) *lnwire.ChannelAnnouncement1 {
var opts fundingTxOpts
for _, opt := range options {
opt(&opts)
}
a := &lnwire.ChannelAnnouncement1{ a := &lnwire.ChannelAnnouncement1{
ShortChannelID: lnwire.ShortChannelID{ ShortChannelID: lnwire.ShortChannelID{
@@ -635,23 +660,26 @@ func createAnnouncementWithoutProof(blockHeight uint32,
copy(a.NodeID2[:], key2.SerializeCompressed()) copy(a.NodeID2[:], key2.SerializeCompressed())
copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed()) copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed())
copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed()) copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed())
if len(extraBytes) == 1 { a.ExtraOpaqueData = opts.extraBytes
a.ExtraOpaqueData = extraBytes[0]
}
return a return a
} }
func createRemoteChannelAnnouncement(blockHeight uint32, func (ctx *testCtx) createRemoteChannelAnnouncement(blockHeight uint32,
extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) { opts ...fundingTxOption) (*lnwire.ChannelAnnouncement1, error) {
return createChannelAnnouncement(blockHeight, remoteKeyPriv1, remoteKeyPriv2, extraBytes...) return ctx.createChannelAnnouncement(
blockHeight, remoteKeyPriv1, remoteKeyPriv2, opts...,
)
} }
func createChannelAnnouncement(blockHeight uint32, key1, key2 *btcec.PrivateKey, func (ctx *testCtx) createChannelAnnouncement(blockHeight uint32, key1,
extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) { key2 *btcec.PrivateKey,
opts ...fundingTxOption) (*lnwire.ChannelAnnouncement1, error) {
a := createAnnouncementWithoutProof(blockHeight, key1.PubKey(), key2.PubKey(), extraBytes...) a := ctx.createAnnouncementWithoutProof(
blockHeight, key1.PubKey(), key2.PubKey(), opts...,
)
signer := mock.SingleSigner{Privkey: key1} signer := mock.SingleSigner{Privkey: key1}
sig, err := netann.SignAnnouncement(&signer, testKeyLoc, a) sig, err := netann.SignAnnouncement(&signer, testKeyLoc, a)
@@ -703,10 +731,12 @@ func mockFindChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) (
} }
type testCtx struct { type testCtx struct {
t *testing.T
gossiper *AuthenticatedGossiper gossiper *AuthenticatedGossiper
router *mockGraphSource router *mockGraphSource
notifier *mockNotifier notifier *mockNotifier
broadcastedMessage chan msgWithSenders broadcastedMessage chan msgWithSenders
chain *lnmock.MockChain
} }
func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) ( func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
@@ -718,6 +748,10 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
// broadcast functions won't be populated. // broadcast functions won't be populated.
notifier := newMockNotifier() notifier := newMockNotifier()
router := newMockRouter(startHeight) router := newMockRouter(startHeight)
chain := &lnmock.MockChain{}
t.Cleanup(func() {
chain.AssertExpectations(t)
})
db := channeldb.OpenForTesting(t, t.TempDir()) db := channeldb.OpenForTesting(t, t.TempDir())
@@ -749,6 +783,7 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
} }
gossiper := New(Config{ gossiper := New(Config{
ChainIO: chain,
Notifier: notifier, Notifier: notifier,
Broadcast: func(senders map[route.Vertex]struct{}, Broadcast: func(senders map[route.Vertex]struct{},
msgs ...lnwire.Message) error { msgs ...lnwire.Message) error {
@@ -820,10 +855,12 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
}) })
return &testCtx{ return &testCtx{
t: t,
router: router, router: router,
notifier: notifier, notifier: notifier,
gossiper: gossiper, gossiper: gossiper,
broadcastedMessage: broadcastedMessage, broadcastedMessage: broadcastedMessage,
chain: chain,
}, nil }, nil
} }
@@ -849,7 +886,7 @@ func TestProcessAnnouncement(t *testing.T) {
// First, we'll craft a valid remote channel announcement and send it to // First, we'll craft a valid remote channel announcement and send it to
// the gossiper so that it can be processed. // the gossiper so that it can be processed.
ca, err := createRemoteChannelAnnouncement(0) ca, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
select { select {
@@ -961,7 +998,7 @@ func TestPrematureAnnouncement(t *testing.T) {
// remote side, but block height of this announcement is greater than // remote side, but block height of this announcement is greater than
// highest know to us, for that reason it should be ignored and not // highest know to us, for that reason it should be ignored and not
// added to the router. // added to the router.
ca, err := createRemoteChannelAnnouncement(1) ca, err := ctx.createRemoteChannelAnnouncement(1)
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
select { select {
@@ -999,7 +1036,7 @@ func TestSignatureAnnouncementLocalFirst(t *testing.T) {
} }
} }
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1175,7 +1212,7 @@ func TestOrphanSignatureAnnouncement(t *testing.T) {
} }
} }
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1346,7 +1383,7 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1581,7 +1618,7 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1769,6 +1806,8 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
timestamp := testTimestamp timestamp := testTimestamp
announcements := deDupedAnnouncements{} announcements := deDupedAnnouncements{}
announcements.Reset() announcements.Reset()
ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err)
// Ensure that after new deDupedAnnouncements struct is created and // Ensure that after new deDupedAnnouncements struct is created and
// reset that storage of each announcement type is empty. // reset that storage of each announcement type is empty.
@@ -1784,7 +1823,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
// Ensure that remote channel announcements are properly stored // Ensure that remote channel announcements are properly stored
// and de-duplicated. // and de-duplicated.
ca, err := createRemoteChannelAnnouncement(0) ca, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "can't create remote channel announcement") require.NoError(t, err, "can't create remote channel announcement")
nodePeer := &mockPeer{bitcoinKeyPub2, nil, nil, atomic.Bool{}} nodePeer := &mockPeer{bitcoinKeyPub2, nil, nil, atomic.Bool{}}
@@ -1800,7 +1839,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
// We'll create a second instance of the same announcement with the // We'll create a second instance of the same announcement with the
// same channel ID. Adding this shouldn't cause an increase in the // same channel ID. Adding this shouldn't cause an increase in the
// number of items as they should be de-duplicated. // number of items as they should be de-duplicated.
ca2, err := createRemoteChannelAnnouncement(0) ca2, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "can't create remote channel announcement") require.NoError(t, err, "can't create remote channel announcement")
announcements.AddMsgs(networkMsg{ announcements.AddMsgs(networkMsg{
msg: ca2, msg: ca2,
@@ -2025,7 +2064,7 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
// We'll start off by processing a channel announcement without a proof // We'll start off by processing a channel announcement without a proof
// (i.e., an unadvertised channel), followed by a node announcement for // (i.e., an unadvertised channel), followed by a node announcement for
// this same channel announcement. // this same channel announcement.
chanAnn := createAnnouncementWithoutProof( chanAnn := ctx.createAnnouncementWithoutProof(
startingHeight-2, selfKeyDesc.PubKey, remoteKeyPub1, startingHeight-2, selfKeyDesc.PubKey, remoteKeyPub1,
) )
pubKey := remoteKeyPriv1.PubKey() pubKey := remoteKeyPriv1.PubKey()
@@ -2071,7 +2110,9 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
// by opening a public channel on the network. We'll create a // by opening a public channel on the network. We'll create a
// ChannelAnnouncement and hand it off to the gossiper in order to // ChannelAnnouncement and hand it off to the gossiper in order to
// process it. // process it.
remoteChanAnn, err := createRemoteChannelAnnouncement(startingHeight - 1) remoteChanAnn, err := ctx.createRemoteChannelAnnouncement(
startingHeight - 1,
)
require.NoError(t, err, "unable to create remote channel announcement") require.NoError(t, err, "unable to create remote channel announcement")
peer := &mockPeer{pubKey, nil, nil, atomic.Bool{}} peer := &mockPeer{pubKey, nil, nil, atomic.Bool{}}
@@ -2121,7 +2162,7 @@ func TestRejectZombieEdge(t *testing.T) {
ctx, err := createTestCtx(t, 0, false) ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err, "unable to create test context") require.NoError(t, err, "unable to create test context")
batch, err := createRemoteAnnouncements(0) batch, err := ctx.createRemoteAnnouncements(0)
require.NoError(t, err, "unable to create announcements") require.NoError(t, err, "unable to create announcements")
remotePeer := &mockPeer{pk: remoteKeyPriv2.PubKey()} remotePeer := &mockPeer{pk: remoteKeyPriv2.PubKey()}
@@ -2222,7 +2263,7 @@ func TestProcessZombieEdgeNowLive(t *testing.T) {
ctx, err := createTestCtx(t, 0, false) ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err, "unable to create test context") require.NoError(t, err, "unable to create test context")
batch, err := createRemoteAnnouncements(0) batch, err := ctx.createRemoteAnnouncements(0)
require.NoError(t, err, "unable to create announcements") require.NoError(t, err, "unable to create announcements")
remotePeer := &mockPeer{pk: remoteKeyPriv1.PubKey()} remotePeer := &mockPeer{pk: remoteKeyPriv1.PubKey()}
@@ -2377,7 +2418,7 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -2584,7 +2625,9 @@ func TestExtraDataChannelAnnouncementValidation(t *testing.T) {
// that we don't know of ourselves, but should still include in the // that we don't know of ourselves, but should still include in the
// final signature check. // final signature check.
extraBytes := []byte("gotta validate this still!") extraBytes := []byte("gotta validate this still!")
ca, err := createRemoteChannelAnnouncement(0, extraBytes) ca, err := ctx.createRemoteChannelAnnouncement(
0, withExtraBytes(extraBytes),
)
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
// We'll now send the announcement to the main gossiper. We should be // We'll now send the announcement to the main gossiper. We should be
@@ -2616,7 +2659,7 @@ func TestExtraDataChannelUpdateValidation(t *testing.T) {
// In this scenario, we'll create two announcements, one regular // In this scenario, we'll create two announcements, one regular
// channel announcement, and another channel update announcement, that // channel announcement, and another channel update announcement, that
// has additional data that we won't be interpreting. // has additional data that we won't be interpreting.
chanAnn, err := createRemoteChannelAnnouncement(0) chanAnn, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "unable to create chan ann") require.NoError(t, err, "unable to create chan ann")
chanUpdAnn1, err := createUpdateAnnouncement( chanUpdAnn1, err := createUpdateAnnouncement(
0, 0, remoteKeyPriv1, timestamp, 0, 0, remoteKeyPriv1, timestamp,
@@ -2732,7 +2775,7 @@ func TestRetransmit(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -2838,7 +2881,7 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
ctx, err := createTestCtx(t, 0, false) ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createRemoteAnnouncements(0) batch, err := ctx.createRemoteAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -2931,7 +2974,7 @@ func TestOptionalFieldsChannelUpdateValidation(t *testing.T) {
// In this scenario, we'll test whether the message flags field in a // In this scenario, we'll test whether the message flags field in a
// channel update is properly handled. // channel update is properly handled.
chanAnn, err := createRemoteChannelAnnouncement(chanUpdateHeight) chanAnn, err := ctx.createRemoteChannelAnnouncement(chanUpdateHeight)
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
select { select {
@@ -3022,7 +3065,7 @@ func TestSendChannelUpdateReliably(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "unable to create test context") require.NoError(t, err, "unable to create test context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
// We'll also create two keys, one for ourselves and another for the // We'll also create two keys, one for ourselves and another for the
@@ -3379,7 +3422,7 @@ func TestPropagateChanPolicyUpdate(t *testing.T) {
const numChannels = 3 const numChannels = 3
channelsToAnnounce := make([]*annBatch, 0, numChannels) channelsToAnnounce := make([]*annBatch, 0, numChannels)
for i := 0; i < numChannels; i++ { for i := 0; i < numChannels; i++ {
newChan, err := createLocalAnnouncements(uint32(i + 1)) newChan, err := ctx.createLocalAnnouncements(uint32(i + 1))
if err != nil { if err != nil {
t.Fatalf("unable to make new channel ann: %v", err) t.Fatalf("unable to make new channel ann: %v", err)
} }
@@ -3556,10 +3599,10 @@ func TestProcessChannelAnnouncementOptionalMsgFields(t *testing.T) {
ctx, err := createTestCtx(t, 0, false) ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err, "unable to create test context") require.NoError(t, err, "unable to create test context")
chanAnn1 := createAnnouncementWithoutProof( chanAnn1 := ctx.createAnnouncementWithoutProof(
100, selfKeyDesc.PubKey, remoteKeyPub1, 100, selfKeyDesc.PubKey, remoteKeyPub1,
) )
chanAnn2 := createAnnouncementWithoutProof( chanAnn2 := ctx.createAnnouncementWithoutProof(
101, selfKeyDesc.PubKey, remoteKeyPub1, 101, selfKeyDesc.PubKey, remoteKeyPub1,
) )
@@ -3775,7 +3818,7 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
// A remote channel announcement should not be broadcast since the graph // A remote channel announcement should not be broadcast since the graph
// has not yet been synced. // has not yet been synced.
chanAnn1, err := createRemoteChannelAnnouncement(0) chanAnn1, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "unable to create channel announcement") require.NoError(t, err, "unable to create channel announcement")
assertBroadcast(chanAnn1, true, false) assertBroadcast(chanAnn1, true, false)
@@ -3789,7 +3832,7 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
// should to be broadcast. // should to be broadcast.
ctx.gossiper.syncMgr.markGraphSynced() ctx.gossiper.syncMgr.markGraphSynced()
chanAnn2, err := createRemoteChannelAnnouncement(1) chanAnn2, err := ctx.createRemoteChannelAnnouncement(1)
require.NoError(t, err, "unable to create channel announcement") require.NoError(t, err, "unable to create channel announcement")
assertBroadcast(chanAnn2, true, true) assertBroadcast(chanAnn2, true, true)
} }
@@ -3814,7 +3857,7 @@ func TestRateLimitChannelUpdates(t *testing.T) {
// We'll create a batch of signed announcements, including updates for // We'll create a batch of signed announcements, including updates for
// both sides, for a channel and process them. They should all be // both sides, for a channel and process them. They should all be
// forwarded as this is our first time learning about the channel. // forwarded as this is our first time learning about the channel.
batch, err := createRemoteAnnouncements(blockHeight) batch, err := ctx.createRemoteAnnouncements(blockHeight)
require.NoError(t, err) require.NoError(t, err)
nodePeer1 := &mockPeer{ nodePeer1 := &mockPeer{
@@ -3954,7 +3997,7 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false) ctx, err := createTestCtx(t, proofMatureDelta, false)
require.NoError(t, err, "can't create context") require.NoError(t, err, "can't create context")
batch, err := createLocalAnnouncements(0) batch, err := ctx.createLocalAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -4100,7 +4143,7 @@ func TestRejectCacheChannelAnn(t *testing.T) {
// First, we create a channel announcement to send over to our test // First, we create a channel announcement to send over to our test
// peer. // peer.
batch, err := createRemoteAnnouncements(0) batch, err := ctx.createRemoteAnnouncements(0)
require.NoError(t, err, "can't generate announcements") require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:]) remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -4185,25 +4228,21 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
remoteKeyPriv2.PubKey(), nil, nil, atomic.Bool{}, remoteKeyPriv2.PubKey(), nil, nil, atomic.Bool{},
} }
ctx.router.setAddEdgeErrCode(graph.ErrInvalidFundingOutput) ctx.router.setAddEdgeErr(graph.ErrInvalidFundingOutput)
// Loop 100 times to get nodePeer banned. // Loop 100 times to get nodePeer banned.
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
// Craft a valid channel announcement for a channel we don't // Craft a valid channel announcement for a channel we don't
// have. We will ensure that it fails validation by modifying // have. We will ensure that it fails validation by modifying
// the router. // the router.
ca, err := createRemoteChannelAnnouncement(uint32(i)) ca, err := ctx.createRemoteChannelAnnouncement(uint32(i))
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
select { select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement( case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
ca, nodePeer1, ca, nodePeer1,
): ):
require.True( require.ErrorIs(t, err, graph.ErrInvalidFundingOutput)
t, graph.IsError(
err, graph.ErrInvalidFundingOutput,
),
)
case <-time.After(2 * time.Second): case <-time.After(2 * time.Second):
t.Fatalf("remote announcement not processed") t.Fatalf("remote announcement not processed")
@@ -4216,16 +4255,16 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// Assert that nodePeer has been disconnected. // Assert that nodePeer has been disconnected.
require.True(t, nodePeer1.disconnected.Load()) require.True(t, nodePeer1.disconnected.Load())
ca, err := createRemoteChannelAnnouncement(101) ca, err := ctx.createRemoteChannelAnnouncement(101)
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
// Set the error to ErrChannelSpent so that we can test that the // Set the error to ErrChannelSpent so that we can test that the
// gossiper ignores closed channels. // gossiper ignores closed channels.
ctx.router.setAddEdgeErrCode(graph.ErrChannelSpent) ctx.router.setAddEdgeErr(graph.ErrChannelSpent)
select { select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer2): case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer2):
require.True(t, graph.IsError(err, graph.ErrChannelSpent)) require.ErrorIs(t, err, graph.ErrChannelSpent)
case <-time.After(2 * time.Second): case <-time.After(2 * time.Second):
t.Fatalf("remote announcement not processed") t.Fatalf("remote announcement not processed")
@@ -4248,7 +4287,7 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// Reset the AddEdge error and pass the same announcement again. An // Reset the AddEdge error and pass the same announcement again. An
// error should be returned even though AddEdge won't fail. // error should be returned even though AddEdge won't fail.
ctx.router.resetAddEdgeErrCode() ctx.router.resetAddEdgeErr()
select { select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer2): case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer2):
@@ -4269,25 +4308,21 @@ func TestChanAnnBanningChanPeer(t *testing.T) {
nodePeer := &mockPeer{remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{}} nodePeer := &mockPeer{remoteKeyPriv1.PubKey(), nil, nil, atomic.Bool{}}
ctx.router.setAddEdgeErrCode(graph.ErrInvalidFundingOutput) ctx.router.setAddEdgeErr(graph.ErrInvalidFundingOutput)
// Loop 100 times to get nodePeer banned. // Loop 100 times to get nodePeer banned.
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
// Craft a valid channel announcement for a channel we don't // Craft a valid channel announcement for a channel we don't
// have. We will ensure that it fails validation by modifying // have. We will ensure that it fails validation by modifying
// the router. // the router.
ca, err := createRemoteChannelAnnouncement(uint32(i)) ca, err := ctx.createRemoteChannelAnnouncement(uint32(i))
require.NoError(t, err, "can't create channel announcement") require.NoError(t, err, "can't create channel announcement")
select { select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement( case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
ca, nodePeer, ca, nodePeer,
): ):
require.True( require.ErrorIs(t, err, graph.ErrInvalidFundingOutput)
t, graph.IsError(
err, graph.ErrInvalidFundingOutput,
),
)
case <-time.After(2 * time.Second): case <-time.After(2 * time.Second):
t.Fatalf("remote announcement not processed") t.Fatalf("remote announcement not processed")
+5 -2
View File
@@ -243,8 +243,11 @@ The underlying functionality between those two options remain the same.
* [Golang was updated to * [Golang was updated to
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462). `v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).
* Various refactors to simplify the * Various refactors and preparations to simplify the
`graph.Builder` [1](https://github.com/lightningnetwork/lnd/pull/9476). `graph.Builder` and to move the funding tx validation to the gossiper.
[1](https://github.com/lightningnetwork/lnd/pull/9476)
[2](https://github.com/lightningnetwork/lnd/pull/9477)
## Breaking Changes ## Breaking Changes
## Performance Improvements ## Performance Improvements
+4 -6
View File
@@ -1314,8 +1314,7 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
default: default:
} }
return NewErrf(ErrNoFundingTransaction, "unable to "+ return fmt.Errorf("%w: %w", ErrNoFundingTransaction, err)
"locate funding tx: %v", err)
} }
// Recreate witness output to be sure that declared in channel edge // Recreate witness output to be sure that declared in channel edge
@@ -1348,8 +1347,7 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
return err return err
} }
return NewErrf(ErrInvalidFundingOutput, "output failed "+ return fmt.Errorf("%w: %w", ErrInvalidFundingOutput, err)
"validation: %w", err)
} }
// Now that we have the funding outpoint of the channel, ensure // Now that we have the funding outpoint of the channel, ensure
@@ -1366,8 +1364,8 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
} }
} }
return NewErrf(ErrChannelSpent, "unable to fetch utxo for "+ return fmt.Errorf("%w: unable to fetch utxo for chan_id=%v, "+
"chan_id=%v, chan_point=%v: %v", edge.ChannelID, "chan_point=%v: %w", ErrChannelSpent, scid.ToUint64(),
fundingPoint, err) fundingPoint, err)
} }
+2 -4
View File
@@ -1275,14 +1275,12 @@ func newChannelEdgeInfo(t *testing.T, ctx *testCtx, fundingHeight uint32,
} }
func assertChanChainRejection(t *testing.T, ctx *testCtx, func assertChanChainRejection(t *testing.T, ctx *testCtx,
edge *models.ChannelEdgeInfo, failCode ErrorCode) { edge *models.ChannelEdgeInfo, expectedErr error) {
t.Helper() t.Helper()
err := ctx.builder.AddEdge(edge) err := ctx.builder.AddEdge(edge)
if !IsError(err, failCode) { require.ErrorIs(t, err, expectedErr)
t.Fatalf("validation should have failed: %v", err)
}
// This channel should now be present in the zombie channel index. // This channel should now be present in the zombie channel index.
_, _, _, isZombie, err := ctx.graph.HasChannelEdge( _, _, _, isZombie, err := ctx.graph.HasChannelEdge(
+19 -13
View File
@@ -2,6 +2,25 @@ package graph
import "github.com/go-errors/errors" import "github.com/go-errors/errors"
var (
// ErrNoFundingTransaction is returned when we are unable to find the
// funding transaction described by the short channel ID on chain.
ErrNoFundingTransaction = errors.New(
"unable to find the funding transaction",
)
// ErrInvalidFundingOutput is returned if the channel funding output
// fails validation.
ErrInvalidFundingOutput = errors.New(
"channel funding output validation failed",
)
// ErrChannelSpent is returned when we go to validate a channel, but
// the purported funding output has actually already been spent on
// chain.
ErrChannelSpent = errors.New("channel output has been spent")
)
// ErrorCode is used to represent the various errors that can occur within this // ErrorCode is used to represent the various errors that can occur within this
// package. // package.
type ErrorCode uint8 type ErrorCode uint8
@@ -15,19 +34,6 @@ const (
// this update can't bring us something new, or because a node // this update can't bring us something new, or because a node
// announcement was given for node not found in any channel. // announcement was given for node not found in any channel.
ErrIgnored ErrIgnored
// ErrChannelSpent is returned when we go to validate a channel, but
// the purported funding output has actually already been spent on
// chain.
ErrChannelSpent
// ErrNoFundingTransaction is returned when we are unable to find the
// funding transaction described by the short channel ID on chain.
ErrNoFundingTransaction
// ErrInvalidFundingOutput is returned if the channel funding output
// fails validation.
ErrInvalidFundingOutput
) )
// Error is a structure that represent the error inside the graph package, // Error is a structure that represent the error inside the graph package,
+12
View File
@@ -74,6 +74,18 @@ func (m *MockChain) GetBlockHeader(hash *chainhash.Hash) (
return args.Get(0).(*wire.BlockHeader), args.Error(1) return args.Get(0).(*wire.BlockHeader), args.Error(1)
} }
func (m *MockChain) GetUtxo(op *wire.OutPoint, pkScript []byte,
heightHint uint32, cancel <-chan struct{}) (*wire.TxOut, error) {
args := m.Called(op, pkScript, heightHint, cancel)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*wire.TxOut), args.Error(1)
}
func (m *MockChain) IsCurrent() bool { func (m *MockChain) IsCurrent() bool {
args := m.Called() args := m.Called()