lnd/graph/db/reject_cache.go
Elle Mouton 506ec037e2
graph/db: add versioned HasChannelEdge method
Add a new HasChannelEdge method that takes a gossip version parameter
and returns only existence and zombie status, without timestamp data.
This supports both v1 and v2 gossip protocols.

The original HasChannelEdge method is renamed to HasV1ChannelEdge to
preserve v1-specific functionality for callers that need timestamp
information. All call sites are updated accordingly.

The SQL store implementation now handles both gossip versions, using
timestamps for v1 and block heights for v2 policies, with proper
reject cache support for both versions.
2026-02-13 07:21:24 +02:00

170 lines
4.4 KiB
Go

package graphdb
import (
"time"
"github.com/lightningnetwork/lnd/lnwire"
)
// rejectFlags is a compact representation of various metadata stored by the
// reject cache about a particular channel.
type rejectFlags uint8
const (
// rejectFlagExists is a flag indicating whether the channel exists,
// i.e. the channel is open and has a recent channel update. If this
// flag is not set, the channel is either a zombie or unknown.
rejectFlagExists rejectFlags = 1 << iota
// rejectFlagZombie is a flag indicating whether the channel is a
// zombie, i.e. the channel is open but has no recent channel updates.
rejectFlagZombie
)
// packRejectFlags computes the rejectFlags corresponding to the passed boolean
// values indicating whether the edge exists or is a zombie.
func packRejectFlags(exists, isZombie bool) rejectFlags {
var flags rejectFlags
if exists {
flags |= rejectFlagExists
}
if isZombie {
flags |= rejectFlagZombie
}
return flags
}
// unpack returns the booleans packed into the rejectFlags. The first indicates
// if the edge exists in our graph, the second indicates if the edge is a
// zombie.
func (f rejectFlags) unpack() (bool, bool) {
return f&rejectFlagExists == rejectFlagExists,
f&rejectFlagZombie == rejectFlagZombie
}
// rejectCacheEntry caches frequently accessed information about a channel,
// including the timestamps of its latest edge policies and whether or not the
// channel exists in the graph.
type rejectCacheEntry struct {
// upd{1,2}Time are Unix timestamps for v1 policies.
upd1Time int64
upd2Time int64
// upd{1,2}BlockHeight are the last known block heights for v2
// policies.
upd1BlockHeight uint32
upd2BlockHeight uint32
flags rejectFlags
}
func newRejectCacheEntryV1(upd1, upd2 time.Time, exists,
isZombie bool) rejectCacheEntry {
return rejectCacheEntry{
upd1Time: upd1.Unix(),
upd2Time: upd2.Unix(),
flags: packRejectFlags(exists, isZombie),
}
}
func newRejectCacheEntryV2(upd1, upd2 uint32, exists,
isZombie bool) rejectCacheEntry {
return rejectCacheEntry{
upd1BlockHeight: upd1,
upd2BlockHeight: upd2,
flags: packRejectFlags(exists, isZombie),
}
}
func updateRejectCacheEntryV1(entry *rejectCacheEntry, isUpdate1 bool,
lastUpdate time.Time) {
if isUpdate1 {
entry.upd1Time = lastUpdate.Unix()
} else {
entry.upd2Time = lastUpdate.Unix()
}
}
func updateRejectCacheEntryV2(entry *rejectCacheEntry, isUpdate1 bool,
blockHeight uint32) {
if isUpdate1 {
entry.upd1BlockHeight = blockHeight
} else {
entry.upd2BlockHeight = blockHeight
}
}
// rejectCacheKey uniquely identifies a channel entry in the reject cache by
// gossip version and channel ID.
type rejectCacheKey struct {
version lnwire.GossipVersion
chanID uint64
}
// rejectCache is an in-memory cache used to improve the performance of
// HasChannelEdge. It caches information about the whether or channel exists, as
// well as the most recent timestamps for each policy (if they exists).
type rejectCache struct {
n int
edges map[rejectCacheKey]rejectCacheEntry
}
// newRejectCache creates a new rejectCache with maximum capacity of n entries.
func newRejectCache(n int) *rejectCache {
return &rejectCache{
n: n,
edges: make(map[rejectCacheKey]rejectCacheEntry, n),
}
}
// get returns the entry from the cache for chanid, if it exists.
func (c *rejectCache) get(version lnwire.GossipVersion, chanid uint64) (
rejectCacheEntry, bool) {
entry, ok := c.edges[rejectCacheKey{
version: version,
chanID: chanid,
}]
return entry, ok
}
// insert adds the entry to the reject cache. If an entry for chanid already
// exists, it will be replaced with the new entry. If the entry doesn't exists,
// it will be inserted to the cache, performing a random eviction if the cache
// is at capacity.
func (c *rejectCache) insert(version lnwire.GossipVersion, chanid uint64,
entry rejectCacheEntry) {
key := rejectCacheKey{
version: version,
chanID: chanid,
}
// If entry exists, replace it.
if _, ok := c.edges[key]; ok {
c.edges[key] = entry
return
}
// Otherwise, evict an entry at random and insert.
if len(c.edges) == c.n {
for id := range c.edges {
delete(c.edges, id)
break
}
}
c.edges[key] = entry
}
// remove deletes an entry for chanid from the cache, if it exists.
func (c *rejectCache) remove(version lnwire.GossipVersion, chanid uint64) {
delete(c.edges, rejectCacheKey{
version: version,
chanID: chanid,
})
}