mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-09 21:03:33 +02:00
Merge pull request #10129 from ellemouton/graphPerf9
[8] graph/db: use batch loading for various graph SQL methods
This commit is contained in:
@@ -736,6 +736,15 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
|||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ChanUpdatesInHorizon",
|
||||||
|
fn: func(b testing.TB, store V1Store) {
|
||||||
|
_, err := store.ChanUpdatesInHorizon(
|
||||||
|
time.Unix(0, 0), time.Now(),
|
||||||
|
)
|
||||||
|
require.NoError(b, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@@ -2789,7 +2789,8 @@ func (c *KVStore) delChannelEdgeUnsafe(edges, edgeIndex, chanIndex,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodeKey1, nodeKey2 = makeZombiePubkeys(
|
nodeKey1, nodeKey2 = makeZombiePubkeys(
|
||||||
&edgeInfo, e1UpdateTime, e2UpdateTime,
|
edgeInfo.NodeKey1Bytes, edgeInfo.NodeKey2Bytes,
|
||||||
|
e1UpdateTime, e2UpdateTime,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2814,27 +2815,27 @@ func (c *KVStore) delChannelEdgeUnsafe(edges, edgeIndex, chanIndex,
|
|||||||
// the channel. If the channel were to be marked zombie again, it would be
|
// the channel. If the channel were to be marked zombie again, it would be
|
||||||
// marked with the correct lagging channel since we received an update from only
|
// marked with the correct lagging channel since we received an update from only
|
||||||
// one side.
|
// one side.
|
||||||
func makeZombiePubkeys(info *models.ChannelEdgeInfo,
|
func makeZombiePubkeys(node1, node2 [33]byte, e1, e2 *time.Time) ([33]byte,
|
||||||
e1, e2 *time.Time) ([33]byte, [33]byte) {
|
[33]byte) {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// If we don't have either edge policy, we'll return both pubkeys so
|
// If we don't have either edge policy, we'll return both pubkeys so
|
||||||
// that the channel can be resurrected by either party.
|
// that the channel can be resurrected by either party.
|
||||||
case e1 == nil && e2 == nil:
|
case e1 == nil && e2 == nil:
|
||||||
return info.NodeKey1Bytes, info.NodeKey2Bytes
|
return node1, node2
|
||||||
|
|
||||||
// If we're missing edge1, or if both edges are present but edge1 is
|
// If we're missing edge1, or if both edges are present but edge1 is
|
||||||
// older, we'll return edge1's pubkey and a blank pubkey for edge2. This
|
// older, we'll return edge1's pubkey and a blank pubkey for edge2. This
|
||||||
// means that only an update from edge1 will be able to resurrect the
|
// means that only an update from edge1 will be able to resurrect the
|
||||||
// channel.
|
// channel.
|
||||||
case e1 == nil || (e2 != nil && e1.Before(*e2)):
|
case e1 == nil || (e2 != nil && e1.Before(*e2)):
|
||||||
return info.NodeKey1Bytes, [33]byte{}
|
return node1, [33]byte{}
|
||||||
|
|
||||||
// Otherwise, we're missing edge2 or edge2 is the older side, so we
|
// Otherwise, we're missing edge2 or edge2 is the older side, so we
|
||||||
// return a blank pubkey for edge1. In this case, only an update from
|
// return a blank pubkey for edge1. In this case, only an update from
|
||||||
// edge2 can resurect the channel.
|
// edge2 can resurect the channel.
|
||||||
default:
|
default:
|
||||||
return [33]byte{}, info.NodeKey2Bytes
|
return [33]byte{}, node1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -192,7 +192,7 @@ func migrateNodes(ctx context.Context, kvBackend kvdb.Backend,
|
|||||||
pub, id, dbNode.ID)
|
pub, id, dbNode.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
migratedNode, err := buildNode(ctx, sqlDB, &dbNode)
|
migratedNode, err := buildNode(ctx, sqlDB, dbNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not build migrated node "+
|
return fmt.Errorf("could not build migrated node "+
|
||||||
"from dbNode(db id: %d, node pub: %x): %w",
|
"from dbNode(db id: %d, node pub: %x): %w",
|
||||||
@@ -410,6 +410,8 @@ func migrateChannelsAndPolicies(ctx context.Context, kvBackend kvdb.Backend,
|
|||||||
}
|
}
|
||||||
|
|
||||||
channelCount++
|
channelCount++
|
||||||
|
chunk++
|
||||||
|
|
||||||
err = migrateSingleChannel(
|
err = migrateSingleChannel(
|
||||||
ctx, sqlDB, channel, policy1, policy2, migChanPolicy,
|
ctx, sqlDB, channel, policy1, policy2, migChanPolicy,
|
||||||
)
|
)
|
||||||
|
@@ -911,6 +911,7 @@ func (s *SQLStore) ChanUpdatesInHorizon(startTime,
|
|||||||
edges []ChannelEdge
|
edges []ChannelEdge
|
||||||
hits int
|
hits int
|
||||||
)
|
)
|
||||||
|
|
||||||
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
|
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
|
||||||
rows, err := db.GetChannelsByPolicyLastUpdateRange(
|
rows, err := db.GetChannelsByPolicyLastUpdateRange(
|
||||||
ctx, sqlc.GetChannelsByPolicyLastUpdateRangeParams{
|
ctx, sqlc.GetChannelsByPolicyLastUpdateRangeParams{
|
||||||
@@ -923,72 +924,61 @@ func (s *SQLStore) ChanUpdatesInHorizon(startTime,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(rows) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll pre-allocate the slices and maps here with a best
|
||||||
|
// effort size in order to avoid unnecessary allocations later
|
||||||
|
// on.
|
||||||
|
uncachedRows := make(
|
||||||
|
[]sqlc.GetChannelsByPolicyLastUpdateRangeRow, 0,
|
||||||
|
len(rows),
|
||||||
|
)
|
||||||
|
edgesToCache = make(map[uint64]ChannelEdge, len(rows))
|
||||||
|
edgesSeen = make(map[uint64]struct{}, len(rows))
|
||||||
|
edges = make([]ChannelEdge, 0, len(rows))
|
||||||
|
|
||||||
|
// Separate cached from non-cached channels since we will only
|
||||||
|
// batch load the data for the ones we haven't cached yet.
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
// If we've already retrieved the info and policies for
|
|
||||||
// this edge, then we can skip it as we don't need to do
|
|
||||||
// so again.
|
|
||||||
chanIDInt := byteOrder.Uint64(row.GraphChannel.Scid)
|
chanIDInt := byteOrder.Uint64(row.GraphChannel.Scid)
|
||||||
|
|
||||||
|
// Skip duplicates.
|
||||||
if _, ok := edgesSeen[chanIDInt]; ok {
|
if _, ok := edgesSeen[chanIDInt]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
edgesSeen[chanIDInt] = struct{}{}
|
||||||
|
|
||||||
|
// Check cache first.
|
||||||
if channel, ok := s.chanCache.get(chanIDInt); ok {
|
if channel, ok := s.chanCache.get(chanIDInt); ok {
|
||||||
hits++
|
hits++
|
||||||
edgesSeen[chanIDInt] = struct{}{}
|
|
||||||
edges = append(edges, channel)
|
edges = append(edges, channel)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
node1, node2, err := buildNodes(
|
// Mark this row as one we need to batch load data for.
|
||||||
ctx, db, row.GraphNode, row.GraphNode_2,
|
uncachedRows = append(uncachedRows, row)
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := getAndBuildEdgeInfo(
|
|
||||||
ctx, db, s.cfg.ChainHash, row.GraphChannel,
|
|
||||||
node1.PubKeyBytes, node2.PubKeyBytes,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to build channel "+
|
|
||||||
"info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dbPol1, dbPol2, err := extractChannelPolicies(row)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to extract channel "+
|
|
||||||
"policies: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p1, p2, err := getAndBuildChanPolicies(
|
|
||||||
ctx, db, dbPol1, dbPol2, channel.ChannelID,
|
|
||||||
node1.PubKeyBytes, node2.PubKeyBytes,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to build channel "+
|
|
||||||
"policies: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
edgesSeen[chanIDInt] = struct{}{}
|
|
||||||
chanEdge := ChannelEdge{
|
|
||||||
Info: channel,
|
|
||||||
Policy1: p1,
|
|
||||||
Policy2: p2,
|
|
||||||
Node1: node1,
|
|
||||||
Node2: node2,
|
|
||||||
}
|
|
||||||
edges = append(edges, chanEdge)
|
|
||||||
edgesToCache[chanIDInt] = chanEdge
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are no uncached rows, then we can return early.
|
||||||
|
if len(uncachedRows) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch load data for all uncached channels.
|
||||||
|
newEdges, err := batchBuildChannelEdges(
|
||||||
|
ctx, s.cfg, db, uncachedRows,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to batch build channel "+
|
||||||
|
"edges: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
edges = append(edges, newEdges...)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, func() {
|
}, sqldb.NoOpReset)
|
||||||
edgesSeen = make(map[uint64]struct{})
|
|
||||||
edgesToCache = make(map[uint64]ChannelEdge)
|
|
||||||
edges = nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to fetch channels: %w", err)
|
return nil, fmt.Errorf("unable to fetch channels: %w", err)
|
||||||
}
|
}
|
||||||
@@ -1625,11 +1615,12 @@ func (s *SQLStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = context.TODO()
|
ctx = context.TODO()
|
||||||
deleted []*models.ChannelEdgeInfo
|
edges []*models.ChannelEdgeInfo
|
||||||
)
|
)
|
||||||
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
|
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
|
||||||
chanIDsToDelete := make([]int64, 0, len(chanIDs))
|
// First, collect all channel rows.
|
||||||
|
var channelRows []sqlc.GetChannelsBySCIDWithPoliciesRow
|
||||||
chanCallBack := func(ctx context.Context,
|
chanCallBack := func(ctx context.Context,
|
||||||
row sqlc.GetChannelsBySCIDWithPoliciesRow) error {
|
row sqlc.GetChannelsBySCIDWithPoliciesRow) error {
|
||||||
|
|
||||||
@@ -1638,64 +1629,7 @@ func (s *SQLStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
|||||||
scid := byteOrder.Uint64(row.GraphChannel.Scid)
|
scid := byteOrder.Uint64(row.GraphChannel.Scid)
|
||||||
delete(chanLookup, scid)
|
delete(chanLookup, scid)
|
||||||
|
|
||||||
node1, node2, err := buildNodeVertices(
|
channelRows = append(channelRows, row)
|
||||||
row.GraphNode.PubKey, row.GraphNode_2.PubKey,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := getAndBuildEdgeInfo(
|
|
||||||
ctx, db, s.cfg.ChainHash, row.GraphChannel,
|
|
||||||
node1, node2,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
deleted = append(deleted, info)
|
|
||||||
chanIDsToDelete = append(
|
|
||||||
chanIDsToDelete, row.GraphChannel.ID,
|
|
||||||
)
|
|
||||||
|
|
||||||
if !markZombie {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeKey1, nodeKey2 := info.NodeKey1Bytes,
|
|
||||||
info.NodeKey2Bytes
|
|
||||||
if strictZombiePruning {
|
|
||||||
var e1UpdateTime, e2UpdateTime *time.Time
|
|
||||||
if row.Policy1LastUpdate.Valid {
|
|
||||||
e1Time := time.Unix(
|
|
||||||
row.Policy1LastUpdate.Int64, 0,
|
|
||||||
)
|
|
||||||
e1UpdateTime = &e1Time
|
|
||||||
}
|
|
||||||
if row.Policy2LastUpdate.Valid {
|
|
||||||
e2Time := time.Unix(
|
|
||||||
row.Policy2LastUpdate.Int64, 0,
|
|
||||||
)
|
|
||||||
e2UpdateTime = &e2Time
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeKey1, nodeKey2 = makeZombiePubkeys(
|
|
||||||
info, e1UpdateTime, e2UpdateTime,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.UpsertZombieChannel(
|
|
||||||
ctx, sqlc.UpsertZombieChannelParams{
|
|
||||||
Version: int16(ProtocolV1),
|
|
||||||
Scid: channelIDToBytes(scid),
|
|
||||||
NodeKey1: nodeKey1[:],
|
|
||||||
NodeKey2: nodeKey2[:],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to mark channel as "+
|
|
||||||
"zombie: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1711,9 +1645,37 @@ func (s *SQLStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
|||||||
return ErrEdgeNotFound
|
return ErrEdgeNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(channelRows) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch build all channel edges.
|
||||||
|
var chanIDsToDelete []int64
|
||||||
|
edges, chanIDsToDelete, err = batchBuildChannelInfo(
|
||||||
|
ctx, s.cfg, db, channelRows,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if markZombie {
|
||||||
|
for i, row := range channelRows {
|
||||||
|
scid := byteOrder.Uint64(row.GraphChannel.Scid)
|
||||||
|
|
||||||
|
err := handleZombieMarking(
|
||||||
|
ctx, db, row, edges[i],
|
||||||
|
strictZombiePruning, scid,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to mark "+
|
||||||
|
"channel as zombie: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return s.deleteChannels(ctx, db, chanIDsToDelete)
|
return s.deleteChannels(ctx, db, chanIDsToDelete)
|
||||||
}, func() {
|
}, func() {
|
||||||
deleted = nil
|
edges = nil
|
||||||
|
|
||||||
// Re-fill the lookup map.
|
// Re-fill the lookup map.
|
||||||
for _, chanID := range chanIDs {
|
for _, chanID := range chanIDs {
|
||||||
@@ -1730,7 +1692,7 @@ func (s *SQLStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
|||||||
s.chanCache.remove(chanID)
|
s.chanCache.remove(chanID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return deleted, nil
|
return edges, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchChannelEdgesByID attempts to lookup the two directed edges for the
|
// FetchChannelEdgesByID attempts to lookup the two directed edges for the
|
||||||
@@ -2093,55 +2055,40 @@ func (s *SQLStore) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
|
|||||||
edges = make(map[uint64]ChannelEdge)
|
edges = make(map[uint64]ChannelEdge)
|
||||||
)
|
)
|
||||||
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
|
err := s.db.ExecTx(ctx, sqldb.ReadTxOpt(), func(db SQLQueries) error {
|
||||||
|
// First, collect all channel rows.
|
||||||
|
var channelRows []sqlc.GetChannelsBySCIDWithPoliciesRow
|
||||||
chanCallBack := func(ctx context.Context,
|
chanCallBack := func(ctx context.Context,
|
||||||
row sqlc.GetChannelsBySCIDWithPoliciesRow) error {
|
row sqlc.GetChannelsBySCIDWithPoliciesRow) error {
|
||||||
|
|
||||||
node1, node2, err := buildNodes(
|
channelRows = append(channelRows, row)
|
||||||
ctx, db, row.GraphNode, row.GraphNode_2,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to fetch nodes: %w",
|
|
||||||
err)
|
|
||||||
}
|
|
||||||
|
|
||||||
edge, err := getAndBuildEdgeInfo(
|
|
||||||
ctx, db, s.cfg.ChainHash, row.GraphChannel,
|
|
||||||
node1.PubKeyBytes, node2.PubKeyBytes,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to build "+
|
|
||||||
"channel info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
dbPol1, dbPol2, err := extractChannelPolicies(row)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to extract channel "+
|
|
||||||
"policies: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
p1, p2, err := getAndBuildChanPolicies(
|
|
||||||
ctx, db, dbPol1, dbPol2, edge.ChannelID,
|
|
||||||
node1.PubKeyBytes, node2.PubKeyBytes,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to build channel "+
|
|
||||||
"policies: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
edges[edge.ChannelID] = ChannelEdge{
|
|
||||||
Info: edge,
|
|
||||||
Policy1: p1,
|
|
||||||
Policy2: p2,
|
|
||||||
Node1: node1,
|
|
||||||
Node2: node2,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.forEachChanWithPoliciesInSCIDList(
|
err := s.forEachChanWithPoliciesInSCIDList(
|
||||||
ctx, db, chanCallBack, chanIDs,
|
ctx, db, chanCallBack, chanIDs,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(channelRows) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch build all channel edges.
|
||||||
|
chans, err := batchBuildChannelEdges(
|
||||||
|
ctx, s.cfg, db, channelRows,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to build channel edges: %w",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range chans {
|
||||||
|
edges[c.Info.ChannelID] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}, func() {
|
}, func() {
|
||||||
clear(edges)
|
clear(edges)
|
||||||
})
|
})
|
||||||
@@ -2363,31 +2310,12 @@ func (s *SQLStore) PruneGraph(spentOutputs []*wire.OutPoint,
|
|||||||
prunedNodes []route.Vertex
|
prunedNodes []route.Vertex
|
||||||
)
|
)
|
||||||
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
|
err := s.db.ExecTx(ctx, sqldb.WriteTxOpt(), func(db SQLQueries) error {
|
||||||
var chansToDelete []int64
|
// First, collect all channel rows that need to be pruned.
|
||||||
|
var channelRows []sqlc.GetChannelsByOutpointsRow
|
||||||
// Define the callback function for processing each channel.
|
|
||||||
channelCallback := func(ctx context.Context,
|
channelCallback := func(ctx context.Context,
|
||||||
row sqlc.GetChannelsByOutpointsRow) error {
|
row sqlc.GetChannelsByOutpointsRow) error {
|
||||||
|
|
||||||
node1, node2, err := buildNodeVertices(
|
channelRows = append(channelRows, row)
|
||||||
row.Node1Pubkey, row.Node2Pubkey,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := getAndBuildEdgeInfo(
|
|
||||||
ctx, db, s.cfg.ChainHash, row.GraphChannel,
|
|
||||||
node1, node2,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
closedChans = append(closedChans, info)
|
|
||||||
chansToDelete = append(
|
|
||||||
chansToDelete, row.GraphChannel.ID,
|
|
||||||
)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -2400,6 +2328,32 @@ func (s *SQLStore) PruneGraph(spentOutputs []*wire.OutPoint,
|
|||||||
"outpoints: %w", err)
|
"outpoints: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(channelRows) == 0 {
|
||||||
|
// There are no channels to prune. So we can exit early
|
||||||
|
// after updating the prune log.
|
||||||
|
err = db.UpsertPruneLogEntry(
|
||||||
|
ctx, sqlc.UpsertPruneLogEntryParams{
|
||||||
|
BlockHash: blockHash[:],
|
||||||
|
BlockHeight: int64(blockHeight),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to insert prune log "+
|
||||||
|
"entry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch build all channel edges for pruning.
|
||||||
|
var chansToDelete []int64
|
||||||
|
closedChans, chansToDelete, err = batchBuildChannelInfo(
|
||||||
|
ctx, s.cfg, db, channelRows,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = s.deleteChannels(ctx, db, chansToDelete)
|
err = s.deleteChannels(ctx, db, chansToDelete)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to delete channels: %w", err)
|
return fmt.Errorf("unable to delete channels: %w", err)
|
||||||
@@ -2658,27 +2612,29 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) (
|
|||||||
return fmt.Errorf("unable to fetch channels: %w", err)
|
return fmt.Errorf("unable to fetch channels: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chanIDsToDelete := make([]int64, len(rows))
|
if len(rows) == 0 {
|
||||||
for i, row := range rows {
|
// No channels to disconnect, but still clean up prune
|
||||||
node1, node2, err := buildNodeVertices(
|
// log.
|
||||||
row.Node1PubKey, row.Node2PubKey,
|
return db.DeletePruneLogEntriesInRange(
|
||||||
|
ctx, sqlc.DeletePruneLogEntriesInRangeParams{
|
||||||
|
StartHeight: int64(height),
|
||||||
|
EndHeight: int64(
|
||||||
|
endShortChanID.BlockHeight,
|
||||||
|
),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, err := getAndBuildEdgeInfo(
|
|
||||||
ctx, db, s.cfg.ChainHash, row.GraphChannel,
|
|
||||||
node1, node2,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
chanIDsToDelete[i] = row.GraphChannel.ID
|
|
||||||
removedChans = append(removedChans, channel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Batch build all channel edges for disconnection.
|
||||||
|
channelEdges, chanIDsToDelete, err := batchBuildChannelInfo(
|
||||||
|
ctx, s.cfg, db, rows,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
removedChans = channelEdges
|
||||||
|
|
||||||
err = s.deleteChannels(ctx, db, chanIDsToDelete)
|
err = s.deleteChannels(ctx, db, chanIDsToDelete)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to delete channels: %w", err)
|
return fmt.Errorf("unable to delete channels: %w", err)
|
||||||
@@ -3230,7 +3186,7 @@ func getNodeByPubKey(ctx context.Context, db SQLQueries,
|
|||||||
return 0, nil, fmt.Errorf("unable to fetch node: %w", err)
|
return 0, nil, fmt.Errorf("unable to fetch node: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := buildNode(ctx, db, &dbNode)
|
node, err := buildNode(ctx, db, dbNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, fmt.Errorf("unable to build node: %w", err)
|
return 0, nil, fmt.Errorf("unable to build node: %w", err)
|
||||||
}
|
}
|
||||||
@@ -3255,7 +3211,7 @@ func buildCacheableChannelInfo(scid []byte, capacity int64, node1Pub,
|
|||||||
// record. The node's features, addresses and extra signed fields are also
|
// record. The node's features, addresses and extra signed fields are also
|
||||||
// fetched from the database and set on the node.
|
// fetched from the database and set on the node.
|
||||||
func buildNode(ctx context.Context, db SQLQueries,
|
func buildNode(ctx context.Context, db SQLQueries,
|
||||||
dbNode *sqlc.GraphNode) (*models.LightningNode, error) {
|
dbNode sqlc.GraphNode) (*models.LightningNode, error) {
|
||||||
|
|
||||||
// NOTE: buildNode is only used to load the data for a single node, and
|
// NOTE: buildNode is only used to load the data for a single node, and
|
||||||
// so no paged queries will be performed. This means that it's ok to
|
// so no paged queries will be performed. This means that it's ok to
|
||||||
@@ -3275,7 +3231,7 @@ func buildNode(ctx context.Context, db SQLQueries,
|
|||||||
// from the provided sqlc.GraphNode and batchNodeData. If the node does have
|
// from the provided sqlc.GraphNode and batchNodeData. If the node does have
|
||||||
// features/addresses/extra fields, then the corresponding fields are expected
|
// features/addresses/extra fields, then the corresponding fields are expected
|
||||||
// to be present in the batchNodeData.
|
// to be present in the batchNodeData.
|
||||||
func buildNodeWithBatchData(dbNode *sqlc.GraphNode,
|
func buildNodeWithBatchData(dbNode sqlc.GraphNode,
|
||||||
batchData *batchNodeData) (*models.LightningNode, error) {
|
batchData *batchNodeData) (*models.LightningNode, error) {
|
||||||
|
|
||||||
if dbNode.Version != int16(ProtocolV1) {
|
if dbNode.Version != int16(ProtocolV1) {
|
||||||
@@ -3363,7 +3319,7 @@ func forEachNodeInBatch(ctx context.Context, cfg *sqldb.QueryConfig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, dbNode := range nodes {
|
for _, dbNode := range nodes {
|
||||||
node, err := buildNodeWithBatchData(&dbNode, batchData)
|
node, err := buildNodeWithBatchData(dbNode, batchData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to build node(id=%d): %w",
|
return fmt.Errorf("unable to build node(id=%d): %w",
|
||||||
dbNode.ID, err)
|
dbNode.ID, err)
|
||||||
@@ -4228,25 +4184,6 @@ func buildChanPolicy(dbPolicy sqlc.GraphChannelPolicy, channelID uint64,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildNodes builds the models.LightningNode instances for the
|
|
||||||
// given row which is expected to be a sqlc type that contains node information.
|
|
||||||
func buildNodes(ctx context.Context, db SQLQueries, dbNode1,
|
|
||||||
dbNode2 sqlc.GraphNode) (*models.LightningNode, *models.LightningNode,
|
|
||||||
error) {
|
|
||||||
|
|
||||||
node1, err := buildNode(ctx, db, &dbNode1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
node2, err := buildNode(ctx, db, &dbNode2)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return node1, node2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractChannelPolicies extracts the sqlc.GraphChannelPolicy records from the give
|
// extractChannelPolicies extracts the sqlc.GraphChannelPolicy records from the give
|
||||||
// row which is expected to be a sqlc type that contains channel policy
|
// row which is expected to be a sqlc type that contains channel policy
|
||||||
// information. It returns two policies, which may be nil if the policy
|
// information. It returns two policies, which may be nil if the policy
|
||||||
@@ -5089,7 +5026,7 @@ func forEachNodePaginated(ctx context.Context, cfg *sqldb.QueryConfig,
|
|||||||
processItem := func(ctx context.Context, dbNode sqlc.GraphNode,
|
processItem := func(ctx context.Context, dbNode sqlc.GraphNode,
|
||||||
batchData *batchNodeData) error {
|
batchData *batchNodeData) error {
|
||||||
|
|
||||||
node, err := buildNodeWithBatchData(&dbNode, batchData)
|
node, err := buildNodeWithBatchData(dbNode, batchData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to build "+
|
return fmt.Errorf("unable to build "+
|
||||||
"node(id=%d): %w", dbNode.ID, err)
|
"node(id=%d): %w", dbNode.ID, err)
|
||||||
@@ -5297,3 +5234,208 @@ func buildDirectedChannel(chain chainhash.Hash, nodeID int64,
|
|||||||
|
|
||||||
return directedChannel, nil
|
return directedChannel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// batchBuildChannelEdges builds a slice of ChannelEdge instances from the
|
||||||
|
// provided rows. It uses batch loading for channels, policies, and nodes.
|
||||||
|
func batchBuildChannelEdges[T sqlc.ChannelAndNodes](ctx context.Context,
|
||||||
|
cfg *SQLStoreConfig, db SQLQueries, rows []T) ([]ChannelEdge, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
channelIDs = make([]int64, len(rows))
|
||||||
|
policyIDs = make([]int64, 0, len(rows)*2)
|
||||||
|
nodeIDs = make([]int64, 0, len(rows)*2)
|
||||||
|
|
||||||
|
// nodeIDSet is used to ensure we only collect unique node IDs.
|
||||||
|
nodeIDSet = make(map[int64]bool)
|
||||||
|
|
||||||
|
// edges will hold the final channel edges built from the rows.
|
||||||
|
edges = make([]ChannelEdge, 0, len(rows))
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collect all IDs needed for batch loading.
|
||||||
|
for i, row := range rows {
|
||||||
|
channelIDs[i] = row.Channel().ID
|
||||||
|
|
||||||
|
// Collect policy IDs
|
||||||
|
dbPol1, dbPol2, err := extractChannelPolicies(row)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to extract channel "+
|
||||||
|
"policies: %w", err)
|
||||||
|
}
|
||||||
|
if dbPol1 != nil {
|
||||||
|
policyIDs = append(policyIDs, dbPol1.ID)
|
||||||
|
}
|
||||||
|
if dbPol2 != nil {
|
||||||
|
policyIDs = append(policyIDs, dbPol2.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
node1ID = row.Node1().ID
|
||||||
|
node2ID = row.Node2().ID
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collect unique node IDs.
|
||||||
|
if !nodeIDSet[node1ID] {
|
||||||
|
nodeIDs = append(nodeIDs, node1ID)
|
||||||
|
nodeIDSet[node1ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nodeIDSet[node2ID] {
|
||||||
|
nodeIDs = append(nodeIDs, node2ID)
|
||||||
|
nodeIDSet[node2ID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch the data for all the channels and policies.
|
||||||
|
channelBatchData, err := batchLoadChannelData(
|
||||||
|
ctx, cfg.QueryCfg, db, channelIDs, policyIDs,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to batch load channel and "+
|
||||||
|
"policy data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch the data for all the nodes.
|
||||||
|
nodeBatchData, err := batchLoadNodeData(ctx, cfg.QueryCfg, db, nodeIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to batch load node data: %w",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build all channel edges using batch data.
|
||||||
|
for _, row := range rows {
|
||||||
|
// Build nodes using batch data.
|
||||||
|
node1, err := buildNodeWithBatchData(row.Node1(), nodeBatchData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to build node1: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
node2, err := buildNodeWithBatchData(row.Node2(), nodeBatchData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to build node2: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build channel info using batch data.
|
||||||
|
channel, err := buildEdgeInfoWithBatchData(
|
||||||
|
cfg.ChainHash, row.Channel(), node1.PubKeyBytes,
|
||||||
|
node2.PubKeyBytes, channelBatchData,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to build channel "+
|
||||||
|
"info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and build policies using batch data.
|
||||||
|
dbPol1, dbPol2, err := extractChannelPolicies(row)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to extract channel "+
|
||||||
|
"policies: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p1, p2, err := buildChanPoliciesWithBatchData(
|
||||||
|
dbPol1, dbPol2, channel.ChannelID,
|
||||||
|
node1.PubKeyBytes, node2.PubKeyBytes, channelBatchData,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to build channel "+
|
||||||
|
"policies: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
edges = append(edges, ChannelEdge{
|
||||||
|
Info: channel,
|
||||||
|
Policy1: p1,
|
||||||
|
Policy2: p2,
|
||||||
|
Node1: node1,
|
||||||
|
Node2: node2,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// batchBuildChannelInfo builds a slice of models.ChannelEdgeInfo
|
||||||
|
// instances from the provided rows using batch loading for channel data.
|
||||||
|
func batchBuildChannelInfo[T sqlc.ChannelAndNodeIDs](ctx context.Context,
|
||||||
|
cfg *SQLStoreConfig, db SQLQueries, rows []T) (
|
||||||
|
[]*models.ChannelEdgeInfo, []int64, error) {
|
||||||
|
|
||||||
|
if len(rows) == 0 {
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all the channel IDs needed for batch loading.
|
||||||
|
channelIDs := make([]int64, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
channelIDs[i] = row.Channel().ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch load the channel data.
|
||||||
|
channelBatchData, err := batchLoadChannelData(
|
||||||
|
ctx, cfg.QueryCfg, db, channelIDs, nil,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to batch load channel "+
|
||||||
|
"data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build all channel edges using batch data.
|
||||||
|
edges := make([]*models.ChannelEdgeInfo, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
node1, node2, err := buildNodeVertices(
|
||||||
|
row.Node1Pub(), row.Node2Pub(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build channel info using batch data
|
||||||
|
info, err := buildEdgeInfoWithBatchData(
|
||||||
|
cfg.ChainHash, row.Channel(), node1, node2,
|
||||||
|
channelBatchData,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
edges = append(edges, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges, channelIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleZombieMarking is a helper function that handles the logic of
|
||||||
|
// marking a channel as a zombie in the database. It takes into account whether
|
||||||
|
// we are in strict zombie pruning mode, and adjusts the node public keys
|
||||||
|
// accordingly based on the last update timestamps of the channel policies.
|
||||||
|
func handleZombieMarking(ctx context.Context, db SQLQueries,
|
||||||
|
row sqlc.GetChannelsBySCIDWithPoliciesRow, info *models.ChannelEdgeInfo,
|
||||||
|
strictZombiePruning bool, scid uint64) error {
|
||||||
|
|
||||||
|
nodeKey1, nodeKey2 := info.NodeKey1Bytes, info.NodeKey2Bytes
|
||||||
|
|
||||||
|
if strictZombiePruning {
|
||||||
|
var e1UpdateTime, e2UpdateTime *time.Time
|
||||||
|
if row.Policy1LastUpdate.Valid {
|
||||||
|
e1Time := time.Unix(row.Policy1LastUpdate.Int64, 0)
|
||||||
|
e1UpdateTime = &e1Time
|
||||||
|
}
|
||||||
|
if row.Policy2LastUpdate.Valid {
|
||||||
|
e2Time := time.Unix(row.Policy2LastUpdate.Int64, 0)
|
||||||
|
e2UpdateTime = &e2Time
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeKey1, nodeKey2 = makeZombiePubkeys(
|
||||||
|
info.NodeKey1Bytes, info.NodeKey2Bytes, e1UpdateTime,
|
||||||
|
e2UpdateTime,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.UpsertZombieChannel(
|
||||||
|
ctx, sqlc.UpsertZombieChannelParams{
|
||||||
|
Version: int16(ProtocolV1),
|
||||||
|
Scid: channelIDToBytes(scid),
|
||||||
|
NodeKey1: nodeKey1[:],
|
||||||
|
NodeKey2: nodeKey2[:],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -37,3 +37,127 @@ func makeQueryParams(numTotalArgs, numListArgs int) string {
|
|||||||
|
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelAndNodes is an interface that provides access to a channel and its
|
||||||
|
// two nodes.
|
||||||
|
type ChannelAndNodes interface {
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
Channel() GraphChannel
|
||||||
|
|
||||||
|
// Node1 returns the first GraphNode associated with this channel.
|
||||||
|
Node1() GraphNode
|
||||||
|
|
||||||
|
// Node2 returns the second GraphNode associated with this channel.
|
||||||
|
Node2() GraphNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodes interface.
|
||||||
|
func (r GetChannelsByPolicyLastUpdateRangeRow) Channel() GraphChannel {
|
||||||
|
return r.GraphChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node1 returns the first GraphNode associated with this channel.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodes interface.
|
||||||
|
func (r GetChannelsByPolicyLastUpdateRangeRow) Node1() GraphNode {
|
||||||
|
return r.GraphNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node2 returns the second GraphNode associated with this channel.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodes interface.
|
||||||
|
func (r GetChannelsByPolicyLastUpdateRangeRow) Node2() GraphNode {
|
||||||
|
return r.GraphNode_2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelAndNodeIDs is an interface that provides access to a channel and its
|
||||||
|
// two node public keys.
|
||||||
|
type ChannelAndNodeIDs interface {
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
Channel() GraphChannel
|
||||||
|
|
||||||
|
// Node1Pub returns the public key of the first node as a byte slice.
|
||||||
|
Node1Pub() []byte
|
||||||
|
|
||||||
|
// Node2Pub returns the public key of the second node as a byte slice.
|
||||||
|
Node2Pub() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDWithPoliciesRow) Channel() GraphChannel {
|
||||||
|
return r.GraphChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node1Pub returns the public key of the first node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDWithPoliciesRow) Node1Pub() []byte {
|
||||||
|
return r.GraphNode.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node2Pub returns the public key of the second node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDWithPoliciesRow) Node2Pub() []byte {
|
||||||
|
return r.GraphNode_2.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node1 returns the first GraphNode associated with this channel.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodes interface.
|
||||||
|
func (r GetChannelsBySCIDWithPoliciesRow) Node1() GraphNode {
|
||||||
|
return r.GraphNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node2 returns the second GraphNode associated with this channel.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodes interface.
|
||||||
|
func (r GetChannelsBySCIDWithPoliciesRow) Node2() GraphNode {
|
||||||
|
return r.GraphNode_2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsByOutpointsRow) Channel() GraphChannel {
|
||||||
|
return r.GraphChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node1Pub returns the public key of the first node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsByOutpointsRow) Node1Pub() []byte {
|
||||||
|
return r.Node1Pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node2Pub returns the public key of the second node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsByOutpointsRow) Node2Pub() []byte {
|
||||||
|
return r.Node2Pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel returns the GraphChannel associated with this interface.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDRangeRow) Channel() GraphChannel {
|
||||||
|
return r.GraphChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node1Pub returns the public key of the first node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDRangeRow) Node1Pub() []byte {
|
||||||
|
return r.Node1PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node2Pub returns the public key of the second node as a byte slice.
|
||||||
|
//
|
||||||
|
// NOTE: This method is part of the ChannelAndNodeIDs interface.
|
||||||
|
func (r GetChannelsBySCIDRangeRow) Node2Pub() []byte {
|
||||||
|
return r.Node2PubKey
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user