@@ -91,12 +91,14 @@ type SQLQueries interface {
91
91
*/
92
92
CreateChannel (ctx context.Context , arg sqlc.CreateChannelParams ) (int64 , error )
93
93
GetChannelBySCID (ctx context.Context , arg sqlc.GetChannelBySCIDParams ) (sqlc.Channel , error )
94
+ GetChannelByOutpoint (ctx context.Context , outpoint string ) (sqlc.GetChannelByOutpointRow , error )
94
95
GetChannelBySCIDWithPolicies (ctx context.Context , arg sqlc.GetChannelBySCIDWithPoliciesParams ) (sqlc.GetChannelBySCIDWithPoliciesRow , error )
95
96
GetChannelAndNodesBySCID (ctx context.Context , arg sqlc.GetChannelAndNodesBySCIDParams ) (sqlc.GetChannelAndNodesBySCIDRow , error )
96
97
GetChannelFeaturesAndExtras (ctx context.Context , channelID int64 ) ([]sqlc.GetChannelFeaturesAndExtrasRow , error )
97
98
HighestSCID (ctx context.Context , version int16 ) ([]byte , error )
98
99
ListChannelsByNodeID (ctx context.Context , arg sqlc.ListChannelsByNodeIDParams ) ([]sqlc.ListChannelsByNodeIDRow , error )
99
100
ListChannelsWithPoliciesPaginated (ctx context.Context , arg sqlc.ListChannelsWithPoliciesPaginatedParams ) ([]sqlc.ListChannelsWithPoliciesPaginatedRow , error )
101
+ ListChannelsPaginated (ctx context.Context , arg sqlc.ListChannelsPaginatedParams ) ([]sqlc.ListChannelsPaginatedRow , error )
100
102
GetChannelsByPolicyLastUpdateRange (ctx context.Context , arg sqlc.GetChannelsByPolicyLastUpdateRangeParams ) ([]sqlc.GetChannelsByPolicyLastUpdateRangeRow , error )
101
103
GetChannelByOutpointWithPolicies (ctx context.Context , arg sqlc.GetChannelByOutpointWithPoliciesParams ) (sqlc.GetChannelByOutpointWithPoliciesRow , error )
102
104
GetPublicV1ChannelsBySCID (ctx context.Context , arg sqlc.GetPublicV1ChannelsBySCIDParams ) ([]sqlc.Channel , error )
@@ -125,6 +127,12 @@ type SQLQueries interface {
125
127
CountZombieChannels (ctx context.Context , version int16 ) (int64 , error )
126
128
DeleteZombieChannel (ctx context.Context , arg sqlc.DeleteZombieChannelParams ) (sql.Result , error )
127
129
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
128
136
}
129
137
130
138
// BatchedSQLQueries is a version of SQLQueries that's capable of batched
@@ -2230,6 +2238,213 @@ func (s *SQLStore) PruneGraphNodes() ([]route.Vertex, error) {
2230
2238
return prunedNodes , nil
2231
2239
}
2232
2240
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
+
2233
2448
// pruneGraphNodes deletes any node in the DB that doesn't have a channel.
2234
2449
//
2235
2450
// NOTE: this prunes nodes across protocol versions. It will never prune the
@@ -3389,6 +3604,11 @@ func getAndBuildEdgeInfo(ctx context.Context, db SQLQueries,
3389
3604
chain chainhash.Hash , dbChanID int64 , dbChan sqlc.Channel , node1 ,
3390
3605
node2 route.Vertex ) (* models.ChannelEdgeInfo , error ) {
3391
3606
3607
+ if dbChan .Version != int16 (ProtocolV1 ) {
3608
+ return nil , fmt .Errorf ("unsupported channel version: %d" ,
3609
+ dbChan .Version )
3610
+ }
3611
+
3392
3612
fv , extras , err := getChanFeaturesAndExtras (
3393
3613
ctx , db , dbChanID ,
3394
3614
)
0 commit comments