channeldb+routing: refactor DeleteChannelEdge to use ChannelID

In this commit, we refactor DeleteChannelEdge to use ChannelIDs rather
than ChannelPoints. We do this as the only use of DeleteChannelEdge is
when we are pruning zombie channels from our graph. When running under a
light client, we are unable to obtain the ChannelPoint of each edge due
to the expensive operations required to do so. As a stop-gap, we'll
resort towards using an edge's ChannelID instead, which is already
gossiped between nodes.
This commit is contained in:
Wilmer Paulino 2019-04-17 13:26:34 -07:00
parent 760f38736e
commit 0b0a9f4172
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
3 changed files with 62 additions and 82 deletions

View File

@ -810,7 +810,7 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
var opBytes bytes.Buffer
if err := writeOutpoint(&opBytes, chanPoint); err != nil {
return nil
return err
}
// First attempt to see if the channel exists within
@ -832,9 +832,9 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
// will be returned if that outpoint isn't known to be
// a channel. If no error is returned, then a channel
// was successfully pruned.
err = delChannelByEdge(
err = delChannelEdge(
edges, edgeIndex, chanIndex, zombieIndex, nodes,
chanPoint, false,
chanID, false,
)
if err != nil && err != ErrEdgeNotFound {
return err
@ -1070,9 +1070,9 @@ func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) ([]*ChannelEdgeInf
if err != nil {
return err
}
err = delChannelByEdge(
err = delChannelEdge(
edges, edgeIndex, chanIndex, zombieIndex, nodes,
&edgeInfo.ChannelPoint, false,
k, false,
)
if err != nil && err != ErrEdgeNotFound {
return err
@ -1163,11 +1163,11 @@ func (c *ChannelGraph) PruneTip() (*chainhash.Hash, uint32, error) {
return &tipHash, tipHeight, nil
}
// DeleteChannelEdge removes an edge from the database as identified by its
// funding outpoint and also marks it as a zombie. This ensures that we're
// 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 {
// DeleteChannelEdges removes edges with the given channel IDs from the database
// and marks them as zombies. This ensures that we're unable to re-add it to our
// database once again. If an edge does not exist within the database, then
// ErrEdgeNotFound will be returned.
func (c *ChannelGraph) DeleteChannelEdges(chanIDs ...uint64) error {
// TODO(roasbeef): possibly delete from node bucket if node has no more
// channels
// TODO(roasbeef): don't delete both edges?
@ -1175,23 +1175,11 @@ func (c *ChannelGraph) DeleteChannelEdge(chanPoint *wire.OutPoint) error {
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
var chanID uint64
err := c.db.Update(func(tx *bbolt.Tx) error {
var err error
chanID, err = getChanID(tx, chanPoint)
if err != nil {
return err
}
// First grab the edges bucket which houses the information
// we'd like to delete
edges := tx.Bucket(edgeBucket)
if edges == nil {
return ErrEdgeNotFound
}
// Next grab the two edge indexes which will also need to be
// updated.
edgeIndex := edges.Bucket(edgeIndexBucket)
if edgeIndex == nil {
return ErrEdgeNotFound
@ -1209,17 +1197,28 @@ func (c *ChannelGraph) DeleteChannelEdge(chanPoint *wire.OutPoint) error {
return err
}
return delChannelByEdge(
edges, edgeIndex, chanIndex, zombieIndex, nodes,
chanPoint, true,
)
var rawChanID [8]byte
for _, chanID := range chanIDs {
byteOrder.PutUint64(rawChanID[:], chanID)
err := delChannelEdge(
edges, edgeIndex, chanIndex, zombieIndex, nodes,
rawChanID[:], true,
)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
c.rejectCache.remove(chanID)
c.chanCache.remove(chanID)
for _, chanID := range chanIDs {
c.rejectCache.remove(chanID)
c.chanCache.remove(chanID)
}
return nil
}
@ -1244,7 +1243,7 @@ func (c *ChannelGraph) ChannelID(chanPoint *wire.OutPoint) (uint64, error) {
func getChanID(tx *bbolt.Tx, chanPoint *wire.OutPoint) (uint64, error) {
var b bytes.Buffer
if err := writeOutpoint(&b, chanPoint); err != nil {
return 0, nil
return 0, err
}
edges := tx.Bucket(edgeBucket)
@ -1742,30 +1741,14 @@ func delEdgeUpdateIndexEntry(edgesBucket *bbolt.Bucket, chanID uint64,
return nil
}
func delChannelByEdge(edges, edgeIndex, chanIndex, zombieIndex,
nodes *bbolt.Bucket, chanPoint *wire.OutPoint, isZombie bool) error {
func delChannelEdge(edges, edgeIndex, chanIndex, zombieIndex,
nodes *bbolt.Bucket, chanID []byte, isZombie bool) error {
var b bytes.Buffer
if err := writeOutpoint(&b, chanPoint); err != nil {
edgeInfo, err := fetchChanEdgeInfo(edgeIndex, chanID)
if err != nil {
return err
}
// If the channel's outpoint doesn't exist within the outpoint index,
// then the edge does not exist.
chanID := chanIndex.Get(b.Bytes())
if chanID == nil {
return ErrEdgeNotFound
}
// Otherwise we obtain the two public keys from the mapping: chanID ->
// pubKey1 || pubKey2. With this, we can construct the keys which house
// both of the directed edges for this channel.
nodeKeys := edgeIndex.Get(chanID)
if nodeKeys == nil {
return fmt.Errorf("could not find nodekeys for chanID %v",
chanID)
}
// We'll also remove the entry in the edge update index bucket before
// we delete the edges themselves so we can access their last update
// times.
@ -1789,13 +1772,13 @@ func delChannelByEdge(edges, edgeIndex, chanIndex, zombieIndex,
// With the latter half constructed, copy over the first public key to
// delete the edge in this direction, then the second to delete the
// edge in the opposite direction.
copy(edgeKey[:33], nodeKeys[:33])
copy(edgeKey[:33], edgeInfo.NodeKey1Bytes[:])
if edges.Get(edgeKey[:]) != nil {
if err := edges.Delete(edgeKey[:]); err != nil {
return err
}
}
copy(edgeKey[:33], nodeKeys[33:])
copy(edgeKey[:33], edgeInfo.NodeKey2Bytes[:])
if edges.Get(edgeKey[:]) != nil {
if err := edges.Delete(edgeKey[:]); err != nil {
return err
@ -1807,6 +1790,10 @@ func delChannelByEdge(edges, edgeIndex, chanIndex, zombieIndex,
if err := edgeIndex.Delete(chanID); err != nil {
return err
}
var b bytes.Buffer
if err := writeOutpoint(&b, &edgeInfo.ChannelPoint); err != nil {
return err
}
if err := chanIndex.Delete(b.Bytes()); err != nil {
return err
}
@ -1818,12 +1805,9 @@ func delChannelByEdge(edges, edgeIndex, chanIndex, zombieIndex,
return nil
}
var pubKey1, pubKey2 [33]byte
copy(pubKey1[:], nodeKeys[:33])
copy(pubKey2[:], nodeKeys[33:])
return markEdgeZombie(
zombieIndex, byteOrder.Uint64(chanID), pubKey1, pubKey2,
zombieIndex, byteOrder.Uint64(chanID), edgeInfo.NodeKey1Bytes,
edgeInfo.NodeKey2Bytes,
)
}

View File

@ -362,7 +362,7 @@ func TestEdgeInsertionDeletion(t *testing.T) {
// Next, attempt to delete the edge from the database, again this
// should proceed without any issues.
if err := graph.DeleteChannelEdge(&outpoint); err != nil {
if err := graph.DeleteChannelEdges(chanID); err != nil {
t.Fatalf("unable to delete edge: %v", err)
}
@ -381,7 +381,7 @@ func TestEdgeInsertionDeletion(t *testing.T) {
// Finally, attempt to delete a (now) non-existent edge within the
// database, this should result in an error.
err = graph.DeleteChannelEdge(&outpoint)
err = graph.DeleteChannelEdges(chanID)
if err != ErrEdgeNotFound {
t.Fatalf("deleting a non-existent edge should fail!")
}
@ -1750,7 +1750,7 @@ func TestFilterKnownChanIDs(t *testing.T) {
if err := graph.AddChannelEdge(&channel); err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
err := graph.DeleteChannelEdge(&channel.ChannelPoint)
err := graph.DeleteChannelEdges(channel.ChannelID)
if err != nil {
t.Fatalf("unable to mark edge zombie: %v", err)
}
@ -2017,7 +2017,7 @@ func TestFetchChanInfos(t *testing.T) {
if err := graph.AddChannelEdge(&zombieChan); err != nil {
t.Fatalf("unable to create channel edge: %v", err)
}
err = graph.DeleteChannelEdge(&zombieChan.ChannelPoint)
err = graph.DeleteChannelEdges(zombieChan.ChannelID)
if err != nil {
t.Fatalf("unable to delete and mark edge zombie: %v", err)
}
@ -2634,7 +2634,7 @@ func TestNodeIsPublic(t *testing.T) {
// graph. This will make Alice be seen as a private node as it no longer
// has any advertised edges.
for _, graph := range graphs {
err := graph.DeleteChannelEdge(&aliceBobEdge.ChannelPoint)
err := graph.DeleteChannelEdges(aliceBobEdge.ChannelID)
if err != nil {
t.Fatalf("unable to remove edge: %v", err)
}
@ -2651,7 +2651,7 @@ func TestNodeIsPublic(t *testing.T) {
// completely remove the edge as it is not possible for her to know of
// it without it being advertised.
for i, graph := range graphs {
err := graph.DeleteChannelEdge(&bobCarolEdge.ChannelPoint)
err := graph.DeleteChannelEdges(bobCarolEdge.ChannelID)
if err != nil {
t.Fatalf("unable to remove edge: %v", err)
}
@ -2884,7 +2884,7 @@ func TestGraphZombieIndex(t *testing.T) {
// If we delete the edge and mark it as a zombie, then we should expect
// to see it within the index.
err = graph.DeleteChannelEdge(&edge.ChannelPoint)
err = graph.DeleteChannelEdges(edge.ChannelID)
if err != nil {
t.Fatalf("unable to mark edge as zombie: %v", err)
}

View File

@ -634,7 +634,7 @@ func (r *ChannelRouter) syncGraphWithChain() error {
// usually signals that a channel has been closed on-chain. We do this
// periodically to keep a healthy, lively routing table.
func (r *ChannelRouter) pruneZombieChans() error {
var chansToPrune []wire.OutPoint
var chansToPrune []uint64
chanExpiry := r.cfg.ChannelPruneExpiry
log.Infof("Examining channel graph for zombie channels")
@ -660,17 +660,17 @@ func (r *ChannelRouter) pruneZombieChans() error {
if e1 != nil {
e1Zombie = time.Since(e1.LastUpdate) >= chanExpiry
if e1Zombie {
log.Tracef("Edge #1 of ChannelPoint(%v) "+
"last update: %v",
info.ChannelPoint, e1.LastUpdate)
log.Tracef("Edge #1 of ChannelID(%v) last "+
"update: %v", info.ChannelID,
e1.LastUpdate)
}
}
if e2 != nil {
e2Zombie = time.Since(e2.LastUpdate) >= chanExpiry
if e2Zombie {
log.Tracef("Edge #2 of ChannelPoint(%v) "+
"last update: %v",
info.ChannelPoint, e2.LastUpdate)
log.Tracef("Edge #2 of ChannelID(%v) last "+
"update: %v", info.ChannelID,
e2.LastUpdate)
}
}
@ -705,11 +705,11 @@ func (r *ChannelRouter) pruneZombieChans() error {
return nil
}
log.Debugf("ChannelPoint(%v) is a zombie, collecting to prune",
info.ChannelPoint)
log.Debugf("ChannelID(%v) is a zombie, collecting to prune",
info.ChannelID)
// TODO(roasbeef): add ability to delete single directional edge
chansToPrune = append(chansToPrune, info.ChannelPoint)
chansToPrune = append(chansToPrune, info.ChannelID)
// As we're detecting this as a zombie channel, we'll add this
// to the set of recently rejected items so we don't re-accept
@ -732,15 +732,11 @@ func (r *ChannelRouter) pruneZombieChans() error {
// With the set of zombie-like channels obtained, we'll do another pass
// to delete them from the channel graph.
for _, chanToPrune := range chansToPrune {
log.Tracef("Pruning zombie channel with ChannelPoint(%v)",
chanToPrune)
err := r.cfg.Graph.DeleteChannelEdge(&chanToPrune)
if err != nil {
return fmt.Errorf("unable to prune zombie with "+
"ChannelPoint(%v): %v", chanToPrune, err)
}
for _, chanID := range chansToPrune {
log.Tracef("Pruning zombie channel with ChannelID(%v)", chanID)
}
if err := r.cfg.Graph.DeleteChannelEdges(chansToPrune...); err != nil {
return fmt.Errorf("unable to delete zombie channels: %v", err)
}
// With the channels pruned, we'll also attempt to prune any nodes that