Skip to content

Commit bd301d2

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 bd301d2

File tree

5 files changed

+149
-49
lines changed

5 files changed

+149
-49
lines changed

funding/manager.go

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,11 @@ type Config struct {
530530
// the initiator for channels of the anchor type.
531531
MaxAnchorsCommitFeeRate chainfee.SatPerKWeight
532532

533-
// DeleteAliasEdge allows the Manager to delete an alias channel edge
534-
// from the graph. It also returns our local to-be-deleted policy.
535-
DeleteAliasEdge func(scid lnwire.ShortChannelID) (
536-
*models.ChannelEdgePolicy, error)
533+
// UpdateAliasEdge allows the Manager to update an alias channel edge
534+
// from the graph. This is necessary because SCID channels change their
535+
// scid during their lifetime so we need to make sure to update the
536+
// underlying graph.
537+
UpdateAliasEdge func(aliasScID, newScID lnwire.ShortChannelID) error
537538

538539
// AliasManager is an implementation of the aliasHandler interface that
539540
// abstracts away the handling of many alias functions.
@@ -3719,24 +3720,18 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
37193720
"maps: %v", err)
37203721
}
37213722

3722-
// We'll delete the edge and add it again via
3723-
// addToGraph. This is because the peer may have
3724-
// sent us a ChannelUpdate with an alias and we don't
3725-
// want to relay this.
3726-
ourPolicy, err := f.cfg.DeleteAliasEdge(baseScid)
3723+
// We update the SCID so that potential ChannelUpdates
3724+
// we the alias as the short channel id are also removed
3725+
// and not relayed to the broader network (because the
3726+
// alias is not a verifiable channel id).
3727+
err = f.cfg.UpdateAliasEdge(
3728+
baseScid, baseScid,
3729+
)
37273730
if err != nil {
37283731
return fmt.Errorf("failed deleting real edge "+
37293732
"for alias channel from graph: %v",
37303733
err)
37313734
}
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-
}
37403735
}
37413736

37423737
// Create and broadcast the proofs required to make this channel
@@ -3808,24 +3803,17 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error {
38083803
"six confirmations: %v", err)
38093804
}
38103805

3811-
// TODO: Make this atomic!
3812-
ourPolicy, err := f.cfg.DeleteAliasEdge(c.ShortChanID())
3806+
// The underlying graph entry for this channel id needs to be
3807+
// updated with the new confirmed scid. Moreover channel updates
3808+
// with the alias id are removed and we do not relay them to the
3809+
// broader network.
3810+
err := f.cfg.UpdateAliasEdge(
3811+
c.ShortChanID(), confChan.shortChanID,
3812+
)
38133813
if err != nil {
38143814
return fmt.Errorf("unable to delete alias edge from "+
38153815
"graph: %v", err)
38163816
}
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-
}
38293817
}
38303818

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

funding/manager_test.go

Lines changed: 4 additions & 4 deletions
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+
UpdateAliasEdge: 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
@@ -674,7 +674,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
674674
ZombieSweeperInterval: oldCfg.ZombieSweeperInterval,
675675
ReservationTimeout: oldCfg.ReservationTimeout,
676676
OpenChannelPredicate: chainedAcceptor,
677-
DeleteAliasEdge: oldCfg.DeleteAliasEdge,
677+
UpdateAliasEdge: oldCfg.UpdateAliasEdge,
678678
AliasManager: oldCfg.AliasManager,
679679
AuxLeafStore: oldCfg.AuxLeafStore,
680680
AuxSigner: oldCfg.AuxSigner,

graph/db/graph.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,70 @@ 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 guarantee 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+
if err != nil {
1226+
return err
1227+
}
1228+
1229+
// Also add the new channel update from our side.
1230+
_, err = updateEdgePolicy(tx, ourPolicy, c.graphCache)
1231+
1232+
return err
1233+
}, func() {})
1234+
if err != nil {
1235+
return err
1236+
}
1237+
1238+
// Remove the Cache entries.
1239+
c.rejectCache.remove(chanID)
1240+
c.chanCache.remove(chanID)
1241+
1242+
return nil
1243+
}
1244+
11811245
// HasChannelEdge returns true if the database knows of a channel edge with the
11821246
// passed channel ID, and false otherwise. If an edge with that ID is found
11831247
// within the graph, then two time stamps representing the last time the edge

peer/brontide.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2268,8 +2268,7 @@ func messageSummary(msg lnwire.Message) string {
22682268
return fmt.Sprintf("chan_id=%v", msg.ChanID)
22692269

22702270
case *lnwire.ChannelReady:
2271-
return fmt.Sprintf("chan_id=%v, next_point=%x",
2272-
msg.ChanID, msg.NextPerCommitmentPoint.SerializeCompressed())
2271+
return fmt.Sprintf("%v", msg)
22732272

22742273
case *lnwire.Shutdown:
22752274
return fmt.Sprintf("chan_id=%v, script=%x", msg.ChannelID,

server.go

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,24 +1380,44 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
13801380
return nil, err
13811381
}
13821382

1383-
// Wrap the DeleteChannelEdges method so that the funding manager can
1383+
// Wrap the `ReAddChannelEdge` 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+
updateAliasEdge := 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())
@@ -1410,14 +1430,43 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
14101430
}
14111431

14121432
if ourPolicy == nil {
1413-
// Something is wrong, so return an error.
1414-
return nil, fmt.Errorf("we don't have an edge")
1433+
// We should always have our policy available. If that
1434+
// is not the case there might be an error in the
1435+
// ChannelUpdate msg logic so we return early.
1436+
return fmt.Errorf("edge policy not found")
1437+
}
1438+
1439+
// Update the policy data, this invalidates the signature
1440+
// therefore we need to resign the data.
1441+
ourPolicy.ChannelID = newEdgeInfo.ChannelID
1442+
chanUpdate := netann.UnsignedChannelUpdateFromEdge(
1443+
&newEdgeInfo, ourPolicy)
1444+
1445+
data, err := chanUpdate.DataToSign()
1446+
if err != nil {
1447+
return err
14151448
}
14161449

1417-
err = s.graphDB.DeleteChannelEdges(
1418-
false, false, scid.ToUint64(),
1450+
nodeSig, err := cc.MsgSigner.SignMessage(
1451+
nodeKeyDesc.KeyLocator, data, true,
14191452
)
1420-
return ourPolicy, err
1453+
if err != nil {
1454+
return err
1455+
}
1456+
1457+
sig, err := lnwire.NewSigFromSignature(nodeSig)
1458+
if err != nil {
1459+
return err
1460+
}
1461+
ourPolicy.SetSigBytes(sig.ToSignatureBytes())
1462+
1463+
// Delete the old edge information under the alias SCID and add
1464+
// the updated data with the new SCID.
1465+
err = s.graphDB.ReAddChannelEdge(
1466+
aliasScID.ToUint64(), &newEdgeInfo, ourPolicy,
1467+
)
1468+
1469+
return err
14211470
}
14221471

14231472
// For the reservationTimeout and the zombieSweeperInterval different
@@ -1612,7 +1661,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
16121661
EnableUpfrontShutdown: cfg.EnableUpfrontShutdown,
16131662
MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte(
16141663
s.cfg.MaxCommitFeeRateAnchors * 1000).FeePerKWeight(),
1615-
DeleteAliasEdge: deleteAliasEdge,
1664+
UpdateAliasEdge: updateAliasEdge,
16161665
AliasManager: s.aliasMgr,
16171666
IsSweeperOutpoint: s.sweeper.IsSweeperOutpoint,
16181667
AuxFundingController: implCfg.AuxFundingController,

0 commit comments

Comments
 (0)