discovery+graph: convert errors from codes to variables

In preparation for moving funding transaction validiation from the
Builder to the Gossiper in later commit, we first convert these graph
Error Codes to normal error variables. This will help make the later
commit a pure code move.
This commit is contained in:
Elle Mouton 2025-01-30 12:59:43 +02:00
parent 3c0350e481
commit b117daaa3c
No known key found for this signature in database
GPG Key ID: D7D916376026F177
5 changed files with 49 additions and 59 deletions

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),

View File

@ -24,7 +24,6 @@ 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"
@ -76,13 +75,13 @@ var (
type mockGraphSource struct { type mockGraphSource struct {
bestHeight uint32 bestHeight uint32
mu sync.Mutex mu sync.Mutex
nodes []models.LightningNode nodes []models.LightningNode
infos map[uint64]models.ChannelEdgeInfo infos map[uint64]models.ChannelEdgeInfo
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 +112,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 +128,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) {
@ -4185,7 +4182,7 @@ 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++ {
@ -4199,11 +4196,7 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
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")
@ -4221,11 +4214,11 @@ func TestChanAnnBanningNonChanPeer(t *testing.T) {
// 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 +4241,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,7 +4262,7 @@ 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++ {
@ -4283,11 +4276,7 @@ func TestChanAnnBanningChanPeer(t *testing.T) {
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")

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)
} }

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(

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,