channeldb+routing: extend edge lookup methods with zombie index check
In this commit, we extend the graph's FetchChannelEdgesByID and HasChannelEdge methods to also check the zombie index whenever the edge to be looked up doesn't exist within the edge index. We do this to signal to callers that the edge is known, but only as a zombie, and the only information that we have about the edge are the node public keys of the two parties involved in the edge. In the event that an edge does exist within the zombie index, we make an additional check on edge policies to ensure they are not within the router's pruning window, indicating that it is a fresh update.
This commit is contained in:
5 files changed
+166
-43
No files matched your search
+8
-1
@@ -1,6 +1,9 @@
|
||||
package channeldb
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoChanDBExists is returned when a channel bucket hasn't been
|
||||
@@ -79,6 +82,10 @@ var (
|
||||
// can't be found.
|
||||
ErrEdgeNotFound = fmt.Errorf("edge not found")
|
||||
|
||||
// ErrZombieEdge is an error returned when we attempt to look up an edge
|
||||
// but it is marked as a zombie within the zombie index.
|
||||
ErrZombieEdge = errors.New("edge marked as zombie")
|
||||
|
||||
// ErrEdgeAlreadyExist is returned when edge with specific
|
||||
// channel id can't be added because it already exist.
|
||||
ErrEdgeAlreadyExist = fmt.Errorf("edge already exist")
|
||||
|
||||
+64
-10
@@ -595,17 +595,20 @@ func (c *ChannelGraph) addChannelEdge(tx *bbolt.Tx, edge *ChannelEdgeInfo) error
|
||||
// HasChannelEdge returns true if the database knows of a channel edge with the
|
||||
// passed channel ID, and false otherwise. If an edge with that ID is found
|
||||
// within the graph, then two time stamps representing the last time the edge
|
||||
// was updated for both directed edges are returned along with the boolean.
|
||||
func (c *ChannelGraph) HasChannelEdge(chanID uint64) (time.Time, time.Time, bool, error) {
|
||||
// TODO(roasbeef): check internal bloom filter first
|
||||
// was updated for both directed edges are returned along with the boolean. If
|
||||
// it is not found, then the zombie index is checked and its result is returned
|
||||
// as the second boolean.
|
||||
func (c *ChannelGraph) HasChannelEdge(chanID uint64,
|
||||
) (time.Time, time.Time, bool, bool, error) {
|
||||
|
||||
var (
|
||||
node1UpdateTime time.Time
|
||||
node2UpdateTime time.Time
|
||||
exists bool
|
||||
isZombie bool
|
||||
)
|
||||
|
||||
if err := c.db.View(func(tx *bbolt.Tx) error {
|
||||
err := c.db.View(func(tx *bbolt.Tx) error {
|
||||
edges := tx.Bucket(edgeBucket)
|
||||
if edges == nil {
|
||||
return ErrGraphNoEdgesFound
|
||||
@@ -617,12 +620,21 @@ func (c *ChannelGraph) HasChannelEdge(chanID uint64) (time.Time, time.Time, bool
|
||||
|
||||
var channelID [8]byte
|
||||
byteOrder.PutUint64(channelID[:], chanID)
|
||||
|
||||
// If the edge doesn't exist, then we'll also check our zombie
|
||||
// index.
|
||||
if edgeIndex.Get(channelID[:]) == nil {
|
||||
exists = false
|
||||
zombieIndex := edges.Bucket(zombieBucket)
|
||||
if zombieIndex != nil {
|
||||
isZombie, _, _ = isZombieEdge(zombieIndex, chanID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
exists = true
|
||||
isZombie = false
|
||||
|
||||
// If the channel has been found in the graph, then retrieve
|
||||
// the edges itself so we can return the last updated
|
||||
@@ -648,11 +660,9 @@ func (c *ChannelGraph) HasChannelEdge(chanID uint64) (time.Time, time.Time, bool
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return time.Time{}, time.Time{}, exists, err
|
||||
}
|
||||
})
|
||||
|
||||
return node1UpdateTime, node2UpdateTime, exists, nil
|
||||
return node1UpdateTime, node2UpdateTime, exists, isZombie, err
|
||||
}
|
||||
|
||||
// UpdateChannelEdge retrieves and update edge of the graph database. Method
|
||||
@@ -2537,7 +2547,8 @@ func (c *ChannelEdgePolicy) Signature() (*btcec.Signature, error) {
|
||||
// found, then ErrEdgeNotFound is returned. A struct which houses the general
|
||||
// information for the channel itself is returned as well as two structs that
|
||||
// contain the routing policies for the channel in either direction.
|
||||
func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
|
||||
func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint,
|
||||
) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
|
||||
|
||||
var (
|
||||
edgeInfo *ChannelEdgeInfo
|
||||
@@ -2615,7 +2626,12 @@ func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (*ChannelE
|
||||
// ErrEdgeNotFound is returned. A struct which houses the general information
|
||||
// for the channel itself is returned as well as two structs that contain the
|
||||
// routing policies for the channel in either direction.
|
||||
func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
|
||||
//
|
||||
// ErrZombieEdge an be returned if the edge is currently marked as a zombie
|
||||
// within the database. In this case, the ChannelEdgePolicy's will be nil, and
|
||||
// the ChannelEdgeInfo will only include the public keys of each node.
|
||||
func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64,
|
||||
) (*ChannelEdgeInfo, *ChannelEdgePolicy, *ChannelEdgePolicy, error) {
|
||||
|
||||
var (
|
||||
edgeInfo *ChannelEdgeInfo
|
||||
@@ -2646,13 +2662,48 @@ func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (*ChannelEdgeInfo, *
|
||||
|
||||
byteOrder.PutUint64(channelID[:], chanID)
|
||||
|
||||
// Now, attempt to fetch edge.
|
||||
edge, err := fetchChanEdgeInfo(edgeIndex, channelID[:])
|
||||
|
||||
// If it doesn't exist, we'll quickly check our zombie index to
|
||||
// see if we've previously marked it as so.
|
||||
if err == ErrEdgeNotFound {
|
||||
// If the zombie index doesn't exist, or the edge is not
|
||||
// marked as a zombie within it, then we'll return the
|
||||
// original ErrEdgeNotFound error.
|
||||
zombieIndex := edges.Bucket(zombieBucket)
|
||||
if zombieIndex == nil {
|
||||
return ErrEdgeNotFound
|
||||
}
|
||||
|
||||
isZombie, pubKey1, pubKey2 := isZombieEdge(
|
||||
zombieIndex, chanID,
|
||||
)
|
||||
if !isZombie {
|
||||
return ErrEdgeNotFound
|
||||
}
|
||||
|
||||
// Otherwise, the edge is marked as a zombie, so we'll
|
||||
// populate the edge info with the public keys of each
|
||||
// party as this is the only information we have about
|
||||
// it and return an error signaling so.
|
||||
edgeInfo = &ChannelEdgeInfo{
|
||||
NodeKey1Bytes: pubKey1,
|
||||
NodeKey2Bytes: pubKey2,
|
||||
}
|
||||
return ErrZombieEdge
|
||||
}
|
||||
|
||||
// Otherwise, we'll just return the error if any.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
edgeInfo = &edge
|
||||
edgeInfo.db = c.db
|
||||
|
||||
// Then we'll attempt to fetch the accompanying policies of this
|
||||
// edge.
|
||||
e1, e2, err := fetchChanEdgePolicies(
|
||||
edgeIndex, edges, nodes, channelID[:], c.db,
|
||||
)
|
||||
@@ -2664,6 +2715,9 @@ func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (*ChannelEdgeInfo, *
|
||||
policy2 = e2
|
||||
return nil
|
||||
})
|
||||
if err == ErrZombieEdge {
|
||||
return edgeInfo, nil, nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
+18
-5
@@ -526,29 +526,38 @@ func TestDisconnectBlockAtHeight(t *testing.T) {
|
||||
}
|
||||
|
||||
// The two first edges should be removed from the db.
|
||||
_, _, has, err := graph.HasChannelEdge(edgeInfo.ChannelID)
|
||||
_, _, has, isZombie, err := graph.HasChannelEdge(edgeInfo.ChannelID)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for edge: %v", err)
|
||||
}
|
||||
if has {
|
||||
t.Fatalf("edge1 was not pruned from the graph")
|
||||
}
|
||||
_, _, has, err = graph.HasChannelEdge(edgeInfo2.ChannelID)
|
||||
if isZombie {
|
||||
t.Fatal("reorged edge1 should not be marked as zombie")
|
||||
}
|
||||
_, _, has, isZombie, err = graph.HasChannelEdge(edgeInfo2.ChannelID)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for edge: %v", err)
|
||||
}
|
||||
if has {
|
||||
t.Fatalf("edge2 was not pruned from the graph")
|
||||
}
|
||||
if isZombie {
|
||||
t.Fatal("reorged edge2 should not be marked as zombie")
|
||||
}
|
||||
|
||||
// Edge 3 should not be removed.
|
||||
_, _, has, err = graph.HasChannelEdge(edgeInfo3.ChannelID)
|
||||
_, _, has, isZombie, err = graph.HasChannelEdge(edgeInfo3.ChannelID)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for edge: %v", err)
|
||||
}
|
||||
if !has {
|
||||
t.Fatalf("edge3 was pruned from the graph")
|
||||
}
|
||||
if isZombie {
|
||||
t.Fatal("edge3 was marked as zombie")
|
||||
}
|
||||
|
||||
// PruneTip should be set to the blockHash we specified for the block
|
||||
// at height 155.
|
||||
@@ -759,12 +768,16 @@ func TestEdgeInfoUpdates(t *testing.T) {
|
||||
|
||||
// Check for existence of the edge within the database, it should be
|
||||
// found.
|
||||
_, _, found, err := graph.HasChannelEdge(chanID)
|
||||
_, _, found, isZombie, err := graph.HasChannelEdge(chanID)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to query for edge: %v", err)
|
||||
} else if !found {
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("graph should have of inserted edge")
|
||||
}
|
||||
if isZombie {
|
||||
t.Fatal("live edge should not be marked as zombie")
|
||||
}
|
||||
|
||||
// We should also be able to retrieve the channelID only knowing the
|
||||
// channel point of the channel.
|
||||
|
||||
Reference in new issue
Block a user