channeldb: extend DeleteChannelEdge to mark edge as zombie

We mark the edges as zombies when pruning them to ensure we don't
attempt to reprocess them later on. This also applies to channels that
have been removed from the graph due to being stale.
This commit is contained in:
Wilmer Paulino 2019-03-27 13:06:34 -07:00
parent b780dfacdb
commit e98f4d6d9d
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
2 changed files with 48 additions and 12 deletions

@ -728,6 +728,10 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
if nodes == nil { if nodes == nil {
return ErrSourceNodeNotSet return ErrSourceNodeNotSet
} }
zombieIndex, err := edges.CreateBucketIfNotExists(zombieBucket)
if err != nil {
return err
}
// For each of the outpoints that have been spent within the // For each of the outpoints that have been spent within the
// block, we attempt to delete them from the graph as if that // block, we attempt to delete them from the graph as if that
@ -761,7 +765,8 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
// a channel. If no error is returned, then a channel // a channel. If no error is returned, then a channel
// was successfully pruned. // was successfully pruned.
err = delChannelByEdge( err = delChannelByEdge(
edges, edgeIndex, chanIndex, nodes, chanPoint, edges, edgeIndex, chanIndex, zombieIndex, nodes,
chanPoint, false,
) )
if err != nil && err != ErrEdgeNotFound { if err != nil && err != ErrEdgeNotFound {
return err return err
@ -971,6 +976,10 @@ func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) ([]*ChannelEdgeInf
if err != nil { if err != nil {
return err return err
} }
zombieIndex, err := edges.CreateBucketIfNotExists(zombieBucket)
if err != nil {
return err
}
nodes, err := tx.CreateBucketIfNotExists(nodeBucket) nodes, err := tx.CreateBucketIfNotExists(nodeBucket)
if err != nil { if err != nil {
return err return err
@ -988,7 +997,8 @@ func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) ([]*ChannelEdgeInf
return err return err
} }
err = delChannelByEdge( err = delChannelByEdge(
edges, edgeIndex, chanIndex, nodes, &edgeInfo.ChannelPoint, edges, edgeIndex, chanIndex, zombieIndex, nodes,
&edgeInfo.ChannelPoint, false,
) )
if err != nil && err != ErrEdgeNotFound { if err != nil && err != ErrEdgeNotFound {
return err return err
@ -1075,8 +1085,9 @@ func (c *ChannelGraph) PruneTip() (*chainhash.Hash, uint32, error) {
} }
// DeleteChannelEdge removes an edge from the database as identified by its // DeleteChannelEdge removes an edge from the database as identified by its
// funding outpoint. If the edge does not exist within the database, then // funding outpoint and also marks it as a zombie. This ensures that we're
// ErrEdgeNotFound will be returned. // unable to re-add this to our database once again. If the edge does not exist
// within the database, then ErrEdgeNotFound will be returned.
func (c *ChannelGraph) DeleteChannelEdge(chanPoint *wire.OutPoint) error { func (c *ChannelGraph) DeleteChannelEdge(chanPoint *wire.OutPoint) error {
// TODO(roasbeef): possibly delete from node bucket if node has no more // TODO(roasbeef): possibly delete from node bucket if node has no more
// channels // channels
@ -1096,19 +1107,22 @@ func (c *ChannelGraph) DeleteChannelEdge(chanPoint *wire.OutPoint) error {
if edgeIndex == nil { if edgeIndex == nil {
return ErrEdgeNotFound return ErrEdgeNotFound
} }
chanIndex := edges.Bucket(channelPointBucket) chanIndex := edges.Bucket(channelPointBucket)
if chanIndex == nil { if chanIndex == nil {
return ErrEdgeNotFound return ErrEdgeNotFound
} }
nodes := tx.Bucket(nodeBucket) nodes := tx.Bucket(nodeBucket)
if nodes == nil { if nodes == nil {
return ErrGraphNodeNotFound return ErrGraphNodeNotFound
} }
zombieIndex, err := edges.CreateBucketIfNotExists(zombieBucket)
if err != nil {
return err
}
return delChannelByEdge( return delChannelByEdge(
edges, edgeIndex, chanIndex, nodes, chanPoint, edges, edgeIndex, chanIndex, zombieIndex, nodes,
chanPoint, true,
) )
}) })
} }
@ -1579,8 +1593,9 @@ func delEdgeUpdateIndexEntry(edgesBucket *bbolt.Bucket, chanID uint64,
return nil return nil
} }
func delChannelByEdge(edges *bbolt.Bucket, edgeIndex *bbolt.Bucket, func delChannelByEdge(edges, edgeIndex, chanIndex, zombieIndex,
chanIndex *bbolt.Bucket, nodes *bbolt.Bucket, chanPoint *wire.OutPoint) error { nodes *bbolt.Bucket, chanPoint *wire.OutPoint, isZombie bool) error {
var b bytes.Buffer var b bytes.Buffer
if err := writeOutpoint(&b, chanPoint); err != nil { if err := writeOutpoint(&b, chanPoint); err != nil {
return err return err
@ -1638,12 +1653,29 @@ func delChannelByEdge(edges *bbolt.Bucket, edgeIndex *bbolt.Bucket,
} }
} }
// Finally, with the edge data deleted, we can purge the information // With the edge data deleted, we can purge the information from the two
// from the two edge indexes. // edge indexes.
if err := edgeIndex.Delete(chanID); err != nil { if err := edgeIndex.Delete(chanID); err != nil {
return err return err
} }
return chanIndex.Delete(b.Bytes()) if err := chanIndex.Delete(b.Bytes()); err != nil {
return err
}
// Finally, we'll mark the edge as a zombie within our index if it's
// being removed due to the channel becoming a zombie. We do this to
// ensure we don't store unnecessary data for spent channels.
if !isZombie {
return nil
}
var pubKey1, pubKey2 [33]byte
copy(pubKey1[:], nodeKeys[:33])
copy(pubKey2[:], nodeKeys[33:])
return markEdgeZombie(
zombieIndex, byteOrder.Uint64(chanID), pubKey1, pubKey2,
)
} }
// UpdateEdgePolicy updates the edge routing policy for a single directed edge // UpdateEdgePolicy updates the edge routing policy for a single directed edge

@ -374,6 +374,10 @@ func TestEdgeInsertionDeletion(t *testing.T) {
if _, _, _, err := graph.FetchChannelEdgesByID(chanID); err == nil { if _, _, _, err := graph.FetchChannelEdgesByID(chanID); err == nil {
t.Fatalf("channel edge not deleted") t.Fatalf("channel edge not deleted")
} }
isZombie, _, _ := graph.IsZombieEdge(chanID)
if !isZombie {
t.Fatal("channel edge not marked as zombie")
}
// Finally, attempt to delete a (now) non-existent edge within the // Finally, attempt to delete a (now) non-existent edge within the
// database, this should result in an error. // database, this should result in an error.