Skip to content

Commit 655f5b0

Browse files
committed
multi: make deletion of edge atomic.
When the option SCID is used we need to make sure we update the channel data in an atomic way so that we don't miss any updates by our peer.
1 parent fdb43c0 commit 655f5b0

File tree

4 files changed

+120
-37
lines changed

4 files changed

+120
-37
lines changed

funding/manager.go

+7-25
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,7 @@ type Config struct {
532532

533533
// DeleteAliasEdge allows the Manager to delete an alias channel edge
534534
// from the graph. It also returns our local to-be-deleted policy.
535-
DeleteAliasEdge func(scid lnwire.ShortChannelID) (
536-
*models.ChannelEdgePolicy, error)
535+
DeleteAliasEdge func(aliasScID, newScID lnwire.ShortChannelID) error
537536

538537
// AliasManager is an implementation of the aliasHandler interface that
539538
// abstracts away the handling of many alias functions.
@@ -3723,20 +3722,14 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
37233722
// addToGraph. This is because the peer may have
37243723
// sent us a ChannelUpdate with an alias and we don't
37253724
// want to relay this.
3726-
ourPolicy, err := f.cfg.DeleteAliasEdge(baseScid)
3725+
err = f.cfg.DeleteAliasEdge(
3726+
baseScid, baseScid,
3727+
)
37273728
if err != nil {
37283729
return fmt.Errorf("failed deleting real edge "+
37293730
"for alias channel from graph: %v",
37303731
err)
37313732
}
3732-
3733-
err = f.addToGraph(
3734-
completeChan, &baseScid, nil, ourPolicy,
3735-
)
3736-
if err != nil {
3737-
return fmt.Errorf("failed to re-add to "+
3738-
"graph: %v", err)
3739-
}
37403733
}
37413734

37423735
// Create and broadcast the proofs required to make this channel
@@ -3808,24 +3801,13 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
38083801
"six confirmations: %v", err)
38093802
}
38103803

3811-
// TODO: Make this atomic!
3812-
ourPolicy, err := f.cfg.DeleteAliasEdge(c.ShortChanID())
3804+
err := f.cfg.DeleteAliasEdge(
3805+
c.ShortChanID(), confChan.shortChanID,
3806+
)
38133807
if err != nil {
38143808
return fmt.Errorf("unable to delete alias edge from "+
38153809
"graph: %v", err)
38163810
}
3817-
3818-
// We'll need to update the graph with the new ShortChannelID
3819-
// via an addToGraph call. We don't pass in the peer's
3820-
// alias since we'll be using the confirmed SCID from now on
3821-
// regardless if it's public or not.
3822-
err = f.addToGraph(
3823-
c, &confChan.shortChanID, nil, ourPolicy,
3824-
)
3825-
if err != nil {
3826-
return fmt.Errorf("failed adding confirmed zero-conf "+
3827-
"SCID to graph: %v", err)
3828-
}
38293811
}
38303812

38313813
// Since we have now marked down the confirmed SCID, we'll also need to

funding/manager_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -550,10 +550,10 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
550550
NotifyOpenChannelEvent: evt.NotifyOpenChannelEvent,
551551
OpenChannelPredicate: chainedAcceptor,
552552
NotifyPendingOpenChannelEvent: evt.NotifyPendingOpenChannelEvent,
553-
DeleteAliasEdge: func(scid lnwire.ShortChannelID) (
554-
*models.ChannelEdgePolicy, error) {
553+
DeleteAliasEdge: func(
554+
aliasScID, newScID lnwire.ShortChannelID) error {
555555

556-
return nil, nil
556+
return nil
557557
},
558558
AliasManager: aliasMgr,
559559
// For unit tests we default to false meaning that no funds

graph/db/graph.go

+61
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,67 @@ func (c *ChannelGraph) addChannelEdge(tx kvdb.RwTx,
11781178
return chanIndex.Put(b.Bytes(), chanKey[:])
11791179
}
11801180

1181+
// ReAddChannelEdge removes the edge with the given channel ID from the
1182+
// database and adds the new edge to garantee atomicity.
1183+
// This is important for option-scid-alias channel which might over the course
1184+
// of their lifetime change their SCID (e.g. public zero-conf channels).
1185+
func (c *ChannelGraph) ReAddChannelEdge(
1186+
chanID uint64, newEdge *models.ChannelEdgeInfo,
1187+
ourPolicy *models.ChannelEdgePolicy) error {
1188+
1189+
c.cacheMu.Lock()
1190+
defer c.cacheMu.Unlock()
1191+
1192+
err := kvdb.Update(c.db, func(tx kvdb.RwTx) error {
1193+
edges := tx.ReadWriteBucket(edgeBucket)
1194+
if edges == nil {
1195+
return ErrEdgeNotFound
1196+
}
1197+
edgeIndex := edges.NestedReadWriteBucket(edgeIndexBucket)
1198+
if edgeIndex == nil {
1199+
return ErrEdgeNotFound
1200+
}
1201+
chanIndex := edges.NestedReadWriteBucket(channelPointBucket)
1202+
if chanIndex == nil {
1203+
return ErrEdgeNotFound
1204+
}
1205+
nodes := tx.ReadWriteBucket(nodeBucket)
1206+
if nodes == nil {
1207+
return ErrGraphNodeNotFound
1208+
}
1209+
1210+
var rawChanID [8]byte
1211+
byteOrder.PutUint64(rawChanID[:], chanID)
1212+
1213+
// We don't mark this channel as zombie, because we are readding
1214+
// it immediately after deleting it below.
1215+
err := c.delChannelEdgeUnsafe(
1216+
edges, edgeIndex, chanIndex, nil,
1217+
rawChanID[:], false, false,
1218+
)
1219+
if err != nil {
1220+
return err
1221+
}
1222+
1223+
// Now we add the channel with the new edge info
1224+
err = c.addChannelEdge(tx, newEdge)
1225+
1226+
// Also add the new channel update from our side.
1227+
_, err = updateEdgePolicy(tx, ourPolicy, c.graphCache)
1228+
1229+
return err
1230+
}, func() {})
1231+
if err != nil {
1232+
return err
1233+
}
1234+
1235+
// Remove the Cache entries.
1236+
c.rejectCache.remove(chanID)
1237+
c.chanCache.remove(chanID)
1238+
1239+
return nil
1240+
}
1241+
11811242
// HasChannelEdge returns true if the database knows of a channel edge with the
11821243
// passed channel ID, and false otherwise. If an edge with that ID is found
11831244
// within the graph, then two time stamps representing the last time the edge

server.go

+49-9
Original file line numberDiff line numberDiff line change
@@ -1382,22 +1382,42 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
13821382

13831383
// Wrap the DeleteChannelEdges method so that the funding manager can
13841384
// use it without depending on several layers of indirection.
1385-
deleteAliasEdge := func(scid lnwire.ShortChannelID) (
1386-
*models.ChannelEdgePolicy, error) {
1385+
deleteAliasEdge := func(
1386+
aliasScID, newScID lnwire.ShortChannelID) error {
13871387

13881388
info, e1, e2, err := s.graphDB.FetchChannelEdgesByID(
1389-
scid.ToUint64(),
1389+
aliasScID.ToUint64(),
13901390
)
13911391
if errors.Is(err, graphdb.ErrEdgeNotFound) {
13921392
// This is unlikely but there is a slim chance of this
13931393
// being hit if lnd was killed via SIGKILL and the
13941394
// funding manager was stepping through the delete
13951395
// alias edge logic.
1396-
return nil, nil
1396+
return nil
13971397
} else if err != nil {
1398-
return nil, err
1398+
return err
13991399
}
14001400

1401+
// The AuthProof is only available when the channel is public
1402+
// and therefore announced to the broader network after six
1403+
// confirmation. This happens after readding the edge with the
1404+
// confirmed scid.
1405+
newEdgeInfo := models.ChannelEdgeInfo{
1406+
ChannelID: newScID.ToUint64(),
1407+
ChannelPoint: info.ChannelPoint,
1408+
Capacity: info.Capacity,
1409+
ChainHash: info.ChainHash,
1410+
NodeKey1Bytes: info.NodeKey1Bytes,
1411+
NodeKey2Bytes: info.NodeKey2Bytes,
1412+
BitcoinKey1Bytes: info.BitcoinKey1Bytes,
1413+
BitcoinKey2Bytes: info.BitcoinKey2Bytes,
1414+
Features: info.Features,
1415+
ExtraOpaqueData: info.ExtraOpaqueData,
1416+
}
1417+
1418+
// We also readd the channel policy from our side with the new
1419+
// short channel id.
1420+
//
14011421
// Grab our key to find our policy.
14021422
var ourKey [33]byte
14031423
copy(ourKey[:], nodeKeyDesc.PubKey.SerializeCompressed())
@@ -1411,13 +1431,33 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
14111431

14121432
if ourPolicy == nil {
14131433
// Something is wrong, so return an error.
1414-
return nil, fmt.Errorf("we don't have an edge")
1434+
return fmt.Errorf("edge policy not found")
1435+
}
1436+
1437+
// Update the policy data, this invalidates the signature
1438+
// therefore we need to resign the data.
1439+
ourPolicy.ChannelID = newEdgeInfo.ChannelID
1440+
chanUpdate := netann.UnsignedChannelUpdateFromEdge(
1441+
&newEdgeInfo, ourPolicy)
1442+
1443+
data, err := chanUpdate.DataToSign()
1444+
1445+
nodeSig, err := cc.MsgSigner.SignMessage(
1446+
nodeKeyDesc.KeyLocator, data, true,
1447+
)
1448+
sig, err := lnwire.NewSigFromSignature(nodeSig)
1449+
if err != nil {
1450+
return err
14151451
}
1452+
ourPolicy.SetSigBytes(sig.ToSignatureBytes())
14161453

1417-
err = s.graphDB.DeleteChannelEdges(
1418-
false, false, scid.ToUint64(),
1454+
// Delete the old edge information under the alias SCID and add
1455+
// the updated data with the new SCID.
1456+
err = s.graphDB.ReAddChannelEdge(
1457+
aliasScID.ToUint64(), &newEdgeInfo, ourPolicy,
14191458
)
1420-
return ourPolicy, err
1459+
1460+
return err
14211461
}
14221462

14231463
// For the reservationTimeout and the zombieSweeperInterval different

0 commit comments

Comments
 (0)