@@ -108,6 +108,14 @@ type SQLQueries interface {
108108 InsertChanPolicyExtraType (ctx context.Context , arg sqlc.InsertChanPolicyExtraTypeParams ) error
109109 GetChannelPolicyExtraTypes (ctx context.Context , arg sqlc.GetChannelPolicyExtraTypesParams ) ([]sqlc.GetChannelPolicyExtraTypesRow , error )
110110 DeleteChannelPolicyExtraTypes (ctx context.Context , channelPolicyID int64 ) error
111+
112+ /*
113+ Zombie index queries.
114+ */
115+ UpsertZombieChannel (ctx context.Context , arg sqlc.UpsertZombieChannelParams ) error
116+ GetZombieChannel (ctx context.Context , arg sqlc.GetZombieChannelParams ) (sqlc.ZombieChannel , error )
117+ CountZombieChannels (ctx context.Context , version int16 ) (int64 , error )
118+ DeleteZombieChannel (ctx context.Context , arg sqlc.DeleteZombieChannelParams ) (sql.Result , error )
111119}
112120
113121// BatchedSQLQueries is a version of SQLQueries that's capable of batched
@@ -1390,6 +1398,160 @@ func (s *SQLStore) FilterChannelRange(startHeight, endHeight uint32,
13901398 }), nil
13911399}
13921400
1401+ // MarkEdgeZombie attempts to mark a channel identified by its channel ID as a
1402+ // zombie. This method is used on an ad-hoc basis, when channels need to be
1403+ // marked as zombies outside the normal pruning cycle.
1404+ //
1405+ // NOTE: part of the V1Store interface.
1406+ func (s * SQLStore ) MarkEdgeZombie (chanID uint64 ,
1407+ pubKey1 , pubKey2 [33 ]byte ) error {
1408+
1409+ ctx := context .TODO ()
1410+
1411+ s .cacheMu .Lock ()
1412+ defer s .cacheMu .Unlock ()
1413+
1414+ chanIDB := channelIDToBytes (chanID )
1415+
1416+ err := s .db .ExecTx (ctx , sqldb .WriteTxOpt (), func (db SQLQueries ) error {
1417+ return db .UpsertZombieChannel (
1418+ ctx , sqlc.UpsertZombieChannelParams {
1419+ Version : int16 (ProtocolV1 ),
1420+ Scid : chanIDB [:],
1421+ NodeKey1 : pubKey1 [:],
1422+ NodeKey2 : pubKey2 [:],
1423+ },
1424+ )
1425+ }, sqldb .NoOpReset )
1426+ if err != nil {
1427+ return fmt .Errorf ("unable to upsert zombie channel " +
1428+ "(channel_id=%d): %w" , chanID , err )
1429+ }
1430+
1431+ s .rejectCache .remove (chanID )
1432+ s .chanCache .remove (chanID )
1433+
1434+ return nil
1435+ }
1436+
1437+ // MarkEdgeLive clears an edge from our zombie index, deeming it as live.
1438+ //
1439+ // NOTE: part of the V1Store interface.
1440+ func (s * SQLStore ) MarkEdgeLive (chanID uint64 ) error {
1441+ s .cacheMu .Lock ()
1442+ defer s .cacheMu .Unlock ()
1443+
1444+ var (
1445+ ctx = context .TODO ()
1446+ chanIDB = channelIDToBytes (chanID )
1447+ )
1448+
1449+ err := s .db .ExecTx (ctx , sqldb .WriteTxOpt (), func (db SQLQueries ) error {
1450+ res , err := db .DeleteZombieChannel (
1451+ ctx , sqlc.DeleteZombieChannelParams {
1452+ Scid : chanIDB [:],
1453+ Version : int16 (ProtocolV1 ),
1454+ },
1455+ )
1456+ if err != nil {
1457+ return fmt .Errorf ("unable to delete zombie channel: %w" ,
1458+ err )
1459+ }
1460+
1461+ rows , err := res .RowsAffected ()
1462+ if err != nil {
1463+ return err
1464+ }
1465+
1466+ if rows == 0 {
1467+ return ErrZombieEdgeNotFound
1468+ } else if rows > 1 {
1469+ return fmt .Errorf ("deleted %d zombie rows, " +
1470+ "expected 1" , rows )
1471+ }
1472+
1473+ return nil
1474+ }, sqldb .NoOpReset )
1475+ if err != nil {
1476+ return fmt .Errorf ("unable to mark edge live " +
1477+ "(channel_id=%d): %w" , chanID , err )
1478+ }
1479+
1480+ s .rejectCache .remove (chanID )
1481+ s .chanCache .remove (chanID )
1482+
1483+ return err
1484+ }
1485+
1486+ // IsZombieEdge returns whether the edge is considered zombie. If it is a
1487+ // zombie, then the two node public keys corresponding to this edge are also
1488+ // returned.
1489+ //
1490+ // NOTE: part of the V1Store interface.
1491+ func (s * SQLStore ) IsZombieEdge (chanID uint64 ) (bool , [33 ]byte , [33 ]byte ) {
1492+ var (
1493+ ctx = context .TODO ()
1494+ isZombie bool
1495+ pubKey1 , pubKey2 route.Vertex
1496+ chanIDB = channelIDToBytes (chanID )
1497+ )
1498+
1499+ err := s .db .ExecTx (ctx , sqldb .ReadTxOpt (), func (db SQLQueries ) error {
1500+ zombie , err := db .GetZombieChannel (
1501+ ctx , sqlc.GetZombieChannelParams {
1502+ Scid : chanIDB [:],
1503+ Version : int16 (ProtocolV1 ),
1504+ },
1505+ )
1506+ if errors .Is (err , sql .ErrNoRows ) {
1507+ return nil
1508+ }
1509+ if err != nil {
1510+ return fmt .Errorf ("unable to fetch zombie channel: %w" ,
1511+ err )
1512+ }
1513+
1514+ copy (pubKey1 [:], zombie .NodeKey1 )
1515+ copy (pubKey2 [:], zombie .NodeKey2 )
1516+ isZombie = true
1517+
1518+ return nil
1519+ }, sqldb .NoOpReset )
1520+ if err != nil {
1521+ // TODO(elle): update the IsZombieEdge method to return an
1522+ // error.
1523+ return false , route.Vertex {}, route.Vertex {}
1524+ }
1525+
1526+ return isZombie , pubKey1 , pubKey2
1527+ }
1528+
1529+ // NumZombies returns the current number of zombie channels in the graph.
1530+ //
1531+ // NOTE: part of the V1Store interface.
1532+ func (s * SQLStore ) NumZombies () (uint64 , error ) {
1533+ var (
1534+ ctx = context .TODO ()
1535+ numZombies uint64
1536+ )
1537+ err := s .db .ExecTx (ctx , sqldb .ReadTxOpt (), func (db SQLQueries ) error {
1538+ count , err := db .CountZombieChannels (ctx , int16 (ProtocolV1 ))
1539+ if err != nil {
1540+ return fmt .Errorf ("unable to count zombie channels: %w" ,
1541+ err )
1542+ }
1543+
1544+ numZombies = uint64 (count )
1545+
1546+ return nil
1547+ }, sqldb .NoOpReset )
1548+ if err != nil {
1549+ return 0 , fmt .Errorf ("unable to count zombies: %w" , err )
1550+ }
1551+
1552+ return numZombies , nil
1553+ }
1554+
13931555// forEachNodeDirectedChannel iterates through all channels of a given
13941556// node, executing the passed callback on the directed edge representing the
13951557// channel and its incoming policy. If the node is not found, no error is
0 commit comments