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
+159 -108

No files matched your search

+4 -5
View File
@@ -2678,10 +2678,9 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
return anns, true
case graph.IsError(
err, graph.ErrNoFundingTransaction,
graph.ErrInvalidFundingOutput,
):
case errors.Is(err, graph.ErrNoFundingTransaction),
errors.Is(err, graph.ErrInvalidFundingOutput):
key := newRejectCacheKey(
scid.ToUint64(),
sourceToPub(nMsg.source),
@@ -2695,7 +2694,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
d.banman.incrementBanScore(nMsg.peer.PubKey())
}
case graph.IsError(err, graph.ErrChannelSpent):
case errors.Is(err, graph.ErrChannelSpent):
key := newRejectCacheKey(
scid.ToUint64(),
sourceToPub(nMsg.source),
+113 -78
View File
@@ -24,11 +24,11 @@ import (
"github.com/lightningnetwork/lnd/batch"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/graph"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnmock"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntest/mock"
"github.com/lightningnetwork/lnd/lntest/wait"
@@ -76,13 +76,13 @@ var (
type mockGraphSource struct {
bestHeight uint32
mu sync.Mutex
nodes []models.LightningNode
infos map[uint64]models.ChannelEdgeInfo
edges map[uint64][]models.ChannelEdgePolicy
zombies map[uint64][][33]byte
chansToReject map[uint64]struct{}
addEdgeErrCode fn.Option[graph.ErrorCode]
mu sync.Mutex
nodes []models.LightningNode
infos map[uint64]models.ChannelEdgeInfo
edges map[uint64][]models.ChannelEdgePolicy
zombies map[uint64][][33]byte
chansToReject map[uint64]struct{}
addEdgeErr error
}
func newMockRouter(height uint32) *mockGraphSource {
@@ -113,10 +113,8 @@ func (r *mockGraphSource) AddEdge(info *models.ChannelEdgeInfo,
r.mu.Lock()
defer r.mu.Unlock()
if r.addEdgeErrCode.IsSome() {
return graph.NewErrf(
r.addEdgeErrCode.UnsafeFromSome(), "received error",
)
if r.addEdgeErr != nil {
return r.addEdgeErr
}
if _, ok := r.infos[info.ChannelID]; ok {
@@ -131,12 +129,12 @@ func (r *mockGraphSource) AddEdge(info *models.ChannelEdgeInfo,
return nil
}
func (r *mockGraphSource) resetAddEdgeErrCode() {
r.addEdgeErrCode = fn.None[graph.ErrorCode]()
func (r *mockGraphSource) resetAddEdgeErr() {
r.addEdgeErr = nil
}
func (r *mockGraphSource) setAddEdgeErrCode(code graph.ErrorCode) {
r.addEdgeErrCode = fn.Some[graph.ErrorCode](code)
func (r *mockGraphSource) setAddEdgeErr(err error) {
r.addEdgeErr = err
}
func (r *mockGraphSource) queueValidationFail(chanID uint64) {
@@ -471,15 +469,23 @@ type annBatch struct {
remoteProofAnn *lnwire.AnnounceSignatures1
}
func createLocalAnnouncements(blockHeight uint32) (*annBatch, error) {
return createAnnouncements(blockHeight, selfKeyPriv, remoteKeyPriv1)
func (ctx *testCtx) createLocalAnnouncements(blockHeight uint32) (*annBatch,
error) {
return ctx.createAnnouncements(blockHeight, selfKeyPriv, remoteKeyPriv1)
}
func createRemoteAnnouncements(blockHeight uint32) (*annBatch, error) {
return createAnnouncements(blockHeight, remoteKeyPriv1, remoteKeyPriv2)
func (ctx *testCtx) createRemoteAnnouncements(blockHeight uint32) (*annBatch,
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 batch annBatch
timestamp := testTimestamp
@@ -494,7 +500,9 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann
return nil, err
}
batch.chanAnn, err = createChannelAnnouncement(blockHeight, key1, key2)
batch.chanAnn, err = ctx.createChannelAnnouncement(
blockHeight, key1, key2,
)
if err != nil {
return nil, err
}
@@ -619,9 +627,26 @@ func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate1) error {
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,
extraBytes ...[]byte) *lnwire.ChannelAnnouncement1 {
options ...fundingTxOption) *lnwire.ChannelAnnouncement1 {
var opts fundingTxOpts
for _, opt := range options {
opt(&opts)
}
a := &lnwire.ChannelAnnouncement1{
ShortChannelID: lnwire.ShortChannelID{
@@ -635,23 +660,26 @@ func createAnnouncementWithoutProof(blockHeight uint32,
copy(a.NodeID2[:], key2.SerializeCompressed())
copy(a.BitcoinKey1[:], bitcoinKeyPub1.SerializeCompressed())
copy(a.BitcoinKey2[:], bitcoinKeyPub2.SerializeCompressed())
if len(extraBytes) == 1 {
a.ExtraOpaqueData = extraBytes[0]
}
a.ExtraOpaqueData = opts.extraBytes
return a
}
func createRemoteChannelAnnouncement(blockHeight uint32,
extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) {
func (ctx *testCtx) createRemoteChannelAnnouncement(blockHeight uint32,
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,
extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) {
func (ctx *testCtx) createChannelAnnouncement(blockHeight uint32, key1,
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}
sig, err := netann.SignAnnouncement(&signer, testKeyLoc, a)
@@ -703,10 +731,12 @@ func mockFindChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) (
}
type testCtx struct {
t *testing.T
gossiper *AuthenticatedGossiper
router *mockGraphSource
notifier *mockNotifier
broadcastedMessage chan msgWithSenders
chain *lnmock.MockChain
}
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.
notifier := newMockNotifier()
router := newMockRouter(startHeight)
chain := &lnmock.MockChain{}
t.Cleanup(func() {
chain.AssertExpectations(t)
})
db := channeldb.OpenForTesting(t, t.TempDir())
@@ -749,6 +783,7 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
}
gossiper := New(Config{
ChainIO: chain,
Notifier: notifier,
Broadcast: func(senders map[route.Vertex]struct{},
msgs ...lnwire.Message) error {
@@ -820,10 +855,12 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
})
return &testCtx{
t: t,
router: router,
notifier: notifier,
gossiper: gossiper,
broadcastedMessage: broadcastedMessage,
chain: chain,
}, nil
}
@@ -849,7 +886,7 @@ func TestProcessAnnouncement(t *testing.T) {
// First, we'll craft a valid remote channel announcement and send it to
// 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")
select {
@@ -961,7 +998,7 @@ func TestPrematureAnnouncement(t *testing.T) {
// remote side, but block height of this announcement is greater than
// highest know to us, for that reason it should be ignored and not
// added to the router.
ca, err := createRemoteChannelAnnouncement(1)
ca, err := ctx.createRemoteChannelAnnouncement(1)
require.NoError(t, err, "can't create channel announcement")
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")
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")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1346,7 +1383,7 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1581,7 +1618,7 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -1769,6 +1806,8 @@ func TestDeDuplicatedAnnouncements(t *testing.T) {
timestamp := testTimestamp
announcements := deDupedAnnouncements{}
announcements.Reset()
ctx, err := createTestCtx(t, 0, false)
require.NoError(t, err)
// Ensure that after new deDupedAnnouncements struct is created and
// 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
// and de-duplicated.
ca, err := createRemoteChannelAnnouncement(0)
ca, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "can't create remote channel announcement")
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
// same channel ID. Adding this shouldn't cause an increase in the
// 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")
announcements.AddMsgs(networkMsg{
msg: ca2,
@@ -2025,7 +2064,7 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
// We'll start off by processing a channel announcement without a proof
// (i.e., an unadvertised channel), followed by a node announcement for
// this same channel announcement.
chanAnn := createAnnouncementWithoutProof(
chanAnn := ctx.createAnnouncementWithoutProof(
startingHeight-2, selfKeyDesc.PubKey, remoteKeyPub1,
)
pubKey := remoteKeyPriv1.PubKey()
@@ -2071,7 +2110,9 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
// by opening a public channel on the network. We'll create a
// ChannelAnnouncement and hand it off to the gossiper in order to
// process it.
remoteChanAnn, err := createRemoteChannelAnnouncement(startingHeight - 1)
remoteChanAnn, err := ctx.createRemoteChannelAnnouncement(
startingHeight - 1,
)
require.NoError(t, err, "unable to create remote channel announcement")
peer := &mockPeer{pubKey, nil, nil, atomic.Bool{}}
@@ -2121,7 +2162,7 @@ func TestRejectZombieEdge(t *testing.T) {
ctx, err := createTestCtx(t, 0, false)
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")
remotePeer := &mockPeer{pk: remoteKeyPriv2.PubKey()}
@@ -2222,7 +2263,7 @@ func TestProcessZombieEdgeNowLive(t *testing.T) {
ctx, err := createTestCtx(t, 0, false)
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")
remotePeer := &mockPeer{pk: remoteKeyPriv1.PubKey()}
@@ -2377,7 +2418,7 @@ func TestReceiveRemoteChannelUpdateFirst(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
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
// final signature check.
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")
// 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
// channel announcement, and another channel update announcement, that
// 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")
chanUpdAnn1, err := createUpdateAnnouncement(
0, 0, remoteKeyPriv1, timestamp,
@@ -2732,7 +2775,7 @@ func TestRetransmit(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -2838,7 +2881,7 @@ func TestNodeAnnouncementNoChannels(t *testing.T) {
ctx, err := createTestCtx(t, 0, false)
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")
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
// channel update is properly handled.
chanAnn, err := createRemoteChannelAnnouncement(chanUpdateHeight)
chanAnn, err := ctx.createRemoteChannelAnnouncement(chanUpdateHeight)
require.NoError(t, err, "can't create channel announcement")
select {
@@ -3022,7 +3065,7 @@ func TestSendChannelUpdateReliably(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
// 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
channelsToAnnounce := make([]*annBatch, 0, numChannels)
for i := 0; i < numChannels; i++ {
newChan, err := createLocalAnnouncements(uint32(i + 1))
newChan, err := ctx.createLocalAnnouncements(uint32(i + 1))
if err != nil {
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)
require.NoError(t, err, "unable to create test context")
chanAnn1 := createAnnouncementWithoutProof(
chanAnn1 := ctx.createAnnouncementWithoutProof(
100, selfKeyDesc.PubKey, remoteKeyPub1,
)
chanAnn2 := createAnnouncementWithoutProof(
chanAnn2 := ctx.createAnnouncementWithoutProof(
101, selfKeyDesc.PubKey, remoteKeyPub1,
)
@@ -3775,7 +3818,7 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
// A remote channel announcement should not be broadcast since the graph
// has not yet been synced.
chanAnn1, err := createRemoteChannelAnnouncement(0)
chanAnn1, err := ctx.createRemoteChannelAnnouncement(0)
require.NoError(t, err, "unable to create channel announcement")
assertBroadcast(chanAnn1, true, false)
@@ -3789,7 +3832,7 @@ func TestBroadcastAnnsAfterGraphSynced(t *testing.T) {
// should to be broadcast.
ctx.gossiper.syncMgr.markGraphSynced()
chanAnn2, err := createRemoteChannelAnnouncement(1)
chanAnn2, err := ctx.createRemoteChannelAnnouncement(1)
require.NoError(t, err, "unable to create channel announcement")
assertBroadcast(chanAnn2, true, true)
}
@@ -3814,7 +3857,7 @@ func TestRateLimitChannelUpdates(t *testing.T) {
// We'll create a batch of signed announcements, including updates for
// both sides, for a channel and process them. They should all be
// forwarded as this is our first time learning about the channel.
batch, err := createRemoteAnnouncements(blockHeight)
batch, err := ctx.createRemoteAnnouncements(blockHeight)
require.NoError(t, err)
nodePeer1 := &mockPeer{
@@ -3954,7 +3997,7 @@ func TestIgnoreOwnAnnouncement(t *testing.T) {
ctx, err := createTestCtx(t, proofMatureDelta, false)
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")
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
// peer.
batch, err := createRemoteAnnouncements(0)
batch, err := ctx.createRemoteAnnouncements(0)
require.NoError(t, err, "can't generate announcements")
remoteKey, err := btcec.ParsePubKey(batch.nodeAnn2.NodeID[:])
@@ -4185,25 +4228,21 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
remoteKeyPriv2.PubKey(), nil, nil, atomic.Bool{},
}
ctx.router.setAddEdgeErrCode(graph.ErrInvalidFundingOutput)
ctx.router.setAddEdgeErr(graph.ErrInvalidFundingOutput)
// Loop 100 times to get nodePeer banned.
for i := 0; i < 100; i++ {
// Craft a valid channel announcement for a channel we don't
// have. We will ensure that it fails validation by modifying
// the router.
ca, err := createRemoteChannelAnnouncement(uint32(i))
ca, err := ctx.createRemoteChannelAnnouncement(uint32(i))
require.NoError(t, err, "can't create channel announcement")
select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
ca, nodePeer1,
):
require.True(
t, graph.IsError(
err, graph.ErrInvalidFundingOutput,
),
)
require.ErrorIs(t, err, graph.ErrInvalidFundingOutput)
case <-time.After(2 * time.Second):
t.Fatalf("remote announcement not processed")
@@ -4216,16 +4255,16 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// Assert that nodePeer has been disconnected.
require.True(t, nodePeer1.disconnected.Load())
ca, err := createRemoteChannelAnnouncement(101)
ca, err := ctx.createRemoteChannelAnnouncement(101)
require.NoError(t, err, "can't create channel announcement")
// Set the error to ErrChannelSpent so that we can test that the
// gossiper ignores closed channels.
ctx.router.setAddEdgeErrCode(graph.ErrChannelSpent)
ctx.router.setAddEdgeErr(graph.ErrChannelSpent)
select {
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):
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
// error should be returned even though AddEdge won't fail.
ctx.router.resetAddEdgeErrCode()
ctx.router.resetAddEdgeErr()
select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(ca, nodePeer2):
@@ -4269,25 +4308,21 @@ func TestChanAnnBanningChanPeer(t *testing.T) {
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.
for i := 0; i < 100; i++ {
// Craft a valid channel announcement for a channel we don't
// have. We will ensure that it fails validation by modifying
// the router.
ca, err := createRemoteChannelAnnouncement(uint32(i))
ca, err := ctx.createRemoteChannelAnnouncement(uint32(i))
require.NoError(t, err, "can't create channel announcement")
select {
case err = <-ctx.gossiper.ProcessRemoteAnnouncement(
ca, nodePeer,
):
require.True(
t, graph.IsError(
err, graph.ErrInvalidFundingOutput,
),
)
require.ErrorIs(t, err, graph.ErrInvalidFundingOutput)
case <-time.After(2 * time.Second):
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
`v1.22.11`](https://github.com/lightningnetwork/lnd/pull/9462).
* Various refactors to simplify the
`graph.Builder` [1](https://github.com/lightningnetwork/lnd/pull/9476).
* Various refactors and preparations to simplify the
`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
## Performance Improvements
+4 -6
View File
@@ -1314,8 +1314,7 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
default:
}
return NewErrf(ErrNoFundingTransaction, "unable to "+
"locate funding tx: %v", err)
return fmt.Errorf("%w: %w", ErrNoFundingTransaction, err)
}
// 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 NewErrf(ErrInvalidFundingOutput, "output failed "+
"validation: %w", err)
return fmt.Errorf("%w: %w", ErrInvalidFundingOutput, err)
}
// 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 "+
"chan_id=%v, chan_point=%v: %v", edge.ChannelID,
return fmt.Errorf("%w: unable to fetch utxo for chan_id=%v, "+
"chan_point=%v: %w", ErrChannelSpent, scid.ToUint64(),
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,
edge *models.ChannelEdgeInfo, failCode ErrorCode) {
edge *models.ChannelEdgeInfo, expectedErr error) {
t.Helper()
err := ctx.builder.AddEdge(edge)
if !IsError(err, failCode) {
t.Fatalf("validation should have failed: %v", err)
}
require.ErrorIs(t, err, expectedErr)
// This channel should now be present in the zombie channel index.
_, _, _, isZombie, err := ctx.graph.HasChannelEdge(
+19 -13
View File
@@ -2,6 +2,25 @@ package graph
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
// package.
type ErrorCode uint8
@@ -15,19 +34,6 @@ const (
// this update can't bring us something new, or because a node
// announcement was given for node not found in any channel.
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,
+12
View File
@@ -74,6 +74,18 @@ func (m *MockChain) GetBlockHeader(hash *chainhash.Hash) (
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 {
args := m.Called()