@@ -91,12 +91,14 @@ type SQLQueries interface {
9191 */
9292 CreateChannel (ctx context.Context , arg sqlc.CreateChannelParams ) (int64 , error )
9393 GetChannelBySCID (ctx context.Context , arg sqlc.GetChannelBySCIDParams ) (sqlc.Channel , error )
94+ GetChannelByOutpoint (ctx context.Context , outpoint string ) (sqlc.GetChannelByOutpointRow , error )
9495 GetChannelBySCIDWithPolicies (ctx context.Context , arg sqlc.GetChannelBySCIDWithPoliciesParams ) (sqlc.GetChannelBySCIDWithPoliciesRow , error )
9596 GetChannelAndNodesBySCID (ctx context.Context , arg sqlc.GetChannelAndNodesBySCIDParams ) (sqlc.GetChannelAndNodesBySCIDRow , error )
9697 GetChannelFeaturesAndExtras (ctx context.Context , channelID int64 ) ([]sqlc.GetChannelFeaturesAndExtrasRow , error )
9798 HighestSCID (ctx context.Context , version int16 ) ([]byte , error )
9899 ListChannelsByNodeID (ctx context.Context , arg sqlc.ListChannelsByNodeIDParams ) ([]sqlc.ListChannelsByNodeIDRow , error )
99100 ListChannelsWithPoliciesPaginated (ctx context.Context , arg sqlc.ListChannelsWithPoliciesPaginatedParams ) ([]sqlc.ListChannelsWithPoliciesPaginatedRow , error )
101+ ListChannelsPaginated (ctx context.Context , arg sqlc.ListChannelsPaginatedParams ) ([]sqlc.ListChannelsPaginatedRow , error )
100102 GetChannelsByPolicyLastUpdateRange (ctx context.Context , arg sqlc.GetChannelsByPolicyLastUpdateRangeParams ) ([]sqlc.GetChannelsByPolicyLastUpdateRangeRow , error )
101103 GetChannelByOutpointWithPolicies (ctx context.Context , arg sqlc.GetChannelByOutpointWithPoliciesParams ) (sqlc.GetChannelByOutpointWithPoliciesRow , error )
102104 GetPublicV1ChannelsBySCID (ctx context.Context , arg sqlc.GetPublicV1ChannelsBySCIDParams ) ([]sqlc.Channel , error )
@@ -125,6 +127,12 @@ type SQLQueries interface {
125127 CountZombieChannels (ctx context.Context , version int16 ) (int64 , error )
126128 DeleteZombieChannel (ctx context.Context , arg sqlc.DeleteZombieChannelParams ) (sql.Result , error )
127129 IsZombieChannel (ctx context.Context , arg sqlc.IsZombieChannelParams ) (bool , error )
130+
131+ /*
132+ Prune log table queries.
133+ */
134+ GetPruneTip (ctx context.Context ) (sqlc.PruneLog , error )
135+ UpsertPruneLogEntry (ctx context.Context , arg sqlc.UpsertPruneLogEntryParams ) error
128136}
129137
130138// BatchedSQLQueries is a version of SQLQueries that's capable of batched
@@ -2230,6 +2238,213 @@ func (s *SQLStore) PruneGraphNodes() ([]route.Vertex, error) {
22302238 return prunedNodes , nil
22312239}
22322240
2241+ // PruneGraph prunes newly closed channels from the channel graph in response
2242+ // to a new block being solved on the network. Any transactions which spend the
2243+ // funding output of any known channels within he graph will be deleted.
2244+ // Additionally, the "prune tip", or the last block which has been used to
2245+ // prune the graph is stored so callers can ensure the graph is fully in sync
2246+ // with the current UTXO state. A slice of channels that have been closed by
2247+ // the target block along with any pruned nodes are returned if the function
2248+ // succeeds without error.
2249+ //
2250+ // NOTE: part of the V1Store interface.
2251+ func (s * SQLStore ) PruneGraph (spentOutputs []* wire.OutPoint ,
2252+ blockHash * chainhash.Hash , blockHeight uint32 ) (
2253+ []* models.ChannelEdgeInfo , []route.Vertex , error ) {
2254+
2255+ ctx := context .TODO ()
2256+
2257+ s .cacheMu .Lock ()
2258+ defer s .cacheMu .Unlock ()
2259+
2260+ var (
2261+ closedChans []* models.ChannelEdgeInfo
2262+ prunedNodes []route.Vertex
2263+ )
2264+ err := s .db .ExecTx (ctx , sqldb .WriteTxOpt (), func (db SQLQueries ) error {
2265+ for _ , outpoint := range spentOutputs {
2266+ // TODO(elle): potentially optimize this by using
2267+ // sqlc.slice() once that works for both SQLite and
2268+ // Postgres.
2269+ //
2270+ // NOTE: this fetches channels for all protocol
2271+ // versions.
2272+ row , err := db .GetChannelByOutpoint (
2273+ ctx , outpoint .String (),
2274+ )
2275+ if errors .Is (err , sql .ErrNoRows ) {
2276+ continue
2277+ } else if err != nil {
2278+ return fmt .Errorf ("unable to fetch channel: %w" ,
2279+ err )
2280+ }
2281+
2282+ node1 , node2 , err := buildNodeVertices (
2283+ row .Node1Pubkey , row .Node2Pubkey ,
2284+ )
2285+ if err != nil {
2286+ return err
2287+ }
2288+
2289+ info , err := getAndBuildEdgeInfo (
2290+ ctx , db , s .cfg .ChainHash , row .Channel .ID ,
2291+ row .Channel , node1 , node2 ,
2292+ )
2293+ if err != nil {
2294+ return err
2295+ }
2296+
2297+ err = db .DeleteChannel (ctx , row .Channel .ID )
2298+ if err != nil {
2299+ return fmt .Errorf ("unable to delete " +
2300+ "channel: %w" , err )
2301+ }
2302+
2303+ closedChans = append (closedChans , info )
2304+ }
2305+
2306+ err := db .UpsertPruneLogEntry (
2307+ ctx , sqlc.UpsertPruneLogEntryParams {
2308+ BlockHash : blockHash [:],
2309+ BlockHeight : int64 (blockHeight ),
2310+ },
2311+ )
2312+ if err != nil {
2313+ return fmt .Errorf ("unable to insert prune log " +
2314+ "entry: %w" , err )
2315+ }
2316+
2317+ // Now that we've pruned some channels, we'll also prune any
2318+ // nodes that no longer have any channels.
2319+ prunedNodes , err = s .pruneGraphNodes (ctx , db )
2320+ if err != nil {
2321+ return fmt .Errorf ("unable to prune graph nodes: %w" ,
2322+ err )
2323+ }
2324+
2325+ return nil
2326+ }, func () {
2327+ prunedNodes = nil
2328+ closedChans = nil
2329+ })
2330+ if err != nil {
2331+ return nil , nil , fmt .Errorf ("unable to prune graph: %w" , err )
2332+ }
2333+
2334+ for _ , channel := range closedChans {
2335+ s .rejectCache .remove (channel .ChannelID )
2336+ s .chanCache .remove (channel .ChannelID )
2337+ }
2338+
2339+ return closedChans , prunedNodes , nil
2340+ }
2341+
2342+ // ChannelView returns the verifiable edge information for each active channel
2343+ // within the known channel graph. The set of UTXOs (along with their scripts)
2344+ // returned are the ones that need to be watched on chain to detect channel
2345+ // closes on the resident blockchain.
2346+ //
2347+ // NOTE: part of the V1Store interface.
2348+ func (s * SQLStore ) ChannelView () ([]EdgePoint , error ) {
2349+ var (
2350+ ctx = context .TODO ()
2351+ edgePoints []EdgePoint
2352+ )
2353+
2354+ handleChannel := func (db SQLQueries ,
2355+ channel sqlc.ListChannelsPaginatedRow ) error {
2356+
2357+ pkScript , err := genMultiSigP2WSH (
2358+ channel .BitcoinKey1 , channel .BitcoinKey2 ,
2359+ )
2360+ if err != nil {
2361+ return err
2362+ }
2363+
2364+ op , err := wire .NewOutPointFromString (channel .Outpoint )
2365+ if err != nil {
2366+ return err
2367+ }
2368+
2369+ edgePoints = append (edgePoints , EdgePoint {
2370+ FundingPkScript : pkScript ,
2371+ OutPoint : * op ,
2372+ })
2373+
2374+ return nil
2375+ }
2376+
2377+ err := s .db .ExecTx (ctx , sqldb .ReadTxOpt (), func (db SQLQueries ) error {
2378+ lastID := int64 (- 1 )
2379+ for {
2380+ rows , err := db .ListChannelsPaginated (
2381+ ctx , sqlc.ListChannelsPaginatedParams {
2382+ Version : int16 (ProtocolV1 ),
2383+ ID : lastID ,
2384+ Limit : pageSize ,
2385+ },
2386+ )
2387+ if err != nil {
2388+ return err
2389+ }
2390+
2391+ if len (rows ) == 0 {
2392+ break
2393+ }
2394+
2395+ for _ , row := range rows {
2396+ err := handleChannel (db , row )
2397+ if err != nil {
2398+ return err
2399+ }
2400+
2401+ lastID = row .ID
2402+ }
2403+ }
2404+
2405+ return nil
2406+ }, func () {
2407+ edgePoints = nil
2408+ })
2409+ if err != nil {
2410+ return nil , fmt .Errorf ("unable to fetch channel view: %w" , err )
2411+ }
2412+
2413+ return edgePoints , nil
2414+ }
2415+
2416+ // PruneTip returns the block height and hash of the latest block that has been
2417+ // used to prune channels in the graph. Knowing the "prune tip" allows callers
2418+ // to tell if the graph is currently in sync with the current best known UTXO
2419+ // state.
2420+ //
2421+ // NOTE: part of the V1Store interface.
2422+ func (s * SQLStore ) PruneTip () (* chainhash.Hash , uint32 , error ) {
2423+ var (
2424+ ctx = context .TODO ()
2425+ tipHash chainhash.Hash
2426+ tipHeight uint32
2427+ )
2428+ err := s .db .ExecTx (ctx , sqldb .WriteTxOpt (), func (db SQLQueries ) error {
2429+ pruneTip , err := db .GetPruneTip (ctx )
2430+ if errors .Is (err , sql .ErrNoRows ) {
2431+ return ErrGraphNeverPruned
2432+ } else if err != nil {
2433+ return fmt .Errorf ("unable to fetch prune tip: %w" , err )
2434+ }
2435+
2436+ tipHash = chainhash .Hash (pruneTip .BlockHash )
2437+ tipHeight = uint32 (pruneTip .BlockHeight )
2438+
2439+ return nil
2440+ }, sqldb .NoOpReset )
2441+ if err != nil {
2442+ return nil , 0 , err
2443+ }
2444+
2445+ return & tipHash , tipHeight , nil
2446+ }
2447+
22332448// pruneGraphNodes deletes any node in the DB that doesn't have a channel.
22342449//
22352450// NOTE: this prunes nodes across protocol versions. It will never prune the
@@ -3389,6 +3604,11 @@ func getAndBuildEdgeInfo(ctx context.Context, db SQLQueries,
33893604 chain chainhash.Hash , dbChanID int64 , dbChan sqlc.Channel , node1 ,
33903605 node2 route.Vertex ) (* models.ChannelEdgeInfo , error ) {
33913606
3607+ if dbChan .Version != int16 (ProtocolV1 ) {
3608+ return nil , fmt .Errorf ("unsupported channel version: %d" ,
3609+ dbChan .Version )
3610+ }
3611+
33923612 fv , extras , err := getChanFeaturesAndExtras (
33933613 ctx , db , dbChanID ,
33943614 )
0 commit comments