Merge pull request #10414 from lightningnetwork/elle-g175Prep-base
[g175] graph/db: merge g175 types-prep side branch
This commit is contained in:
52 files changed
+4676
-2016
No files matched your search
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
@@ -31,12 +32,14 @@ type testGraph interface {
|
||||
}
|
||||
|
||||
type testDBGraph struct {
|
||||
db *graphdb.ChannelGraph
|
||||
db *graphdb.VersionedGraph
|
||||
databaseChannelGraph
|
||||
}
|
||||
|
||||
func newDiskChanGraph(t *testing.T) (testGraph, error) {
|
||||
graphDB := graphdb.MakeTestGraph(t)
|
||||
graphDB := graphdb.NewVersionedGraph(
|
||||
graphdb.MakeTestGraph(t), lnwire.GossipVersion1,
|
||||
)
|
||||
require.NoError(t, graphDB.Start())
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, graphDB.Stop())
|
||||
@@ -490,20 +493,26 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
|
||||
}
|
||||
|
||||
chanID := randChanID()
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
Capacity: capacity,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
nodeKey1 := route.NewVertex(lnNode1)
|
||||
nodeKey2 := route.NewVertex(lnNode2)
|
||||
btcKey1 := route.NewVertex(lnNode1)
|
||||
btcKey2 := route.NewVertex(lnNode2)
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), chainhash.Hash{}, nodeKey1, nodeKey2,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithCapacity(capacity),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
copy(edge.NodeKey1Bytes[:], lnNode1.SerializeCompressed())
|
||||
copy(edge.NodeKey2Bytes[:], lnNode2.SerializeCompressed())
|
||||
copy(edge.BitcoinKey1Bytes[:], lnNode1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], lnNode2.SerializeCompressed())
|
||||
|
||||
if err := d.db.AddChannelEdge(ctx, edge); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: chanID.ToUint64(),
|
||||
LastUpdate: time.Now(),
|
||||
@@ -520,6 +529,7 @@ func (d *testDBGraph) addRandChannel(node1, node2 *btcec.PublicKey,
|
||||
return nil, nil, err
|
||||
}
|
||||
edgePolicy = &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: chanID.ToUint64(),
|
||||
LastUpdate: time.Now(),
|
||||
|
||||
+1
-1
@@ -1096,7 +1096,7 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
|
||||
|
||||
// The graph store implementation we will use depends on whether
|
||||
// native SQL is enabled or not.
|
||||
var graphStore graphdb.V1Store
|
||||
var graphStore graphdb.Store
|
||||
|
||||
// Instantiate a native SQL store if the flag is set.
|
||||
if d.cfg.DB.UseNativeSQL {
|
||||
|
||||
@@ -133,8 +133,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash,
|
||||
|
||||
//nolint:ll
|
||||
chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
|
||||
channel.Info.AuthProof, channel.Info,
|
||||
channel.Policy1, channel.Policy2,
|
||||
channel.Info, channel.Policy1, channel.Policy2,
|
||||
)
|
||||
if err != nil {
|
||||
if !yield(nil, err) {
|
||||
@@ -281,8 +280,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash,
|
||||
}
|
||||
|
||||
chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement(
|
||||
channel.Info.AuthProof, channel.Info, channel.Policy1,
|
||||
channel.Policy2,
|
||||
channel.Info, channel.Policy1, channel.Policy2,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+59
-77
@@ -2096,10 +2096,14 @@ func (d *AuthenticatedGossiper) processRejectedEdge(_ context.Context,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Attach the proof to the channel info before creating the
|
||||
// announcement.
|
||||
chanInfo.AuthProof = proof
|
||||
|
||||
// We'll then create then validate the new fully assembled
|
||||
// announcement.
|
||||
chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement(
|
||||
proof, chanInfo, e1, e2,
|
||||
chanInfo, e1, e2,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -2492,44 +2496,13 @@ func (d *AuthenticatedGossiper) updateChannel(ctx context.Context,
|
||||
// have a full channel announcement for this channel.
|
||||
var chanAnn *lnwire.ChannelAnnouncement1
|
||||
if info.AuthProof != nil {
|
||||
chanID := lnwire.NewShortChanIDFromInt(info.ChannelID)
|
||||
chanAnn = &lnwire.ChannelAnnouncement1{
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: info.NodeKey1Bytes,
|
||||
NodeID2: info.NodeKey2Bytes,
|
||||
ChainHash: info.ChainHash,
|
||||
BitcoinKey1: info.BitcoinKey1Bytes,
|
||||
Features: lnwire.NewRawFeatureVector(),
|
||||
BitcoinKey2: info.BitcoinKey2Bytes,
|
||||
ExtraOpaqueData: info.ExtraOpaqueData,
|
||||
}
|
||||
chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
info.AuthProof.NodeSig1Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
info.AuthProof.NodeSig2Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
info.AuthProof.BitcoinSig1Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
info.AuthProof.BitcoinSig2Bytes,
|
||||
)
|
||||
chanAnn, err = info.ToChannelAnnouncement()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return chanAnn, chanUpdate, err
|
||||
return chanAnn, chanUpdate, nil
|
||||
}
|
||||
|
||||
// SyncManager returns the gossiper's SyncManager instance.
|
||||
@@ -2808,28 +2781,36 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(ctx context.Context,
|
||||
// If the proof checks out, then we'll save the proof itself to
|
||||
// the database so we can fetch it later when gossiping with
|
||||
// other nodes.
|
||||
proof = &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: ann.NodeSig1.ToSignatureBytes(),
|
||||
NodeSig2Bytes: ann.NodeSig2.ToSignatureBytes(),
|
||||
BitcoinSig1Bytes: ann.BitcoinSig1.ToSignatureBytes(),
|
||||
BitcoinSig2Bytes: ann.BitcoinSig2.ToSignatureBytes(),
|
||||
}
|
||||
proof = models.NewV1ChannelAuthProof(
|
||||
ann.NodeSig1.ToSignatureBytes(),
|
||||
ann.NodeSig2.ToSignatureBytes(),
|
||||
ann.BitcoinSig1.ToSignatureBytes(),
|
||||
ann.BitcoinSig2.ToSignatureBytes(),
|
||||
)
|
||||
}
|
||||
|
||||
// With the proof validated (if necessary), we can now store it within
|
||||
// the database for our path finding and syncing needs.
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: scid.ToUint64(),
|
||||
ChainHash: ann.ChainHash,
|
||||
NodeKey1Bytes: ann.NodeID1,
|
||||
NodeKey2Bytes: ann.NodeID2,
|
||||
BitcoinKey1Bytes: ann.BitcoinKey1,
|
||||
BitcoinKey2Bytes: ann.BitcoinKey2,
|
||||
AuthProof: proof,
|
||||
Features: lnwire.NewFeatureVector(
|
||||
ann.Features, lnwire.Features,
|
||||
),
|
||||
ExtraOpaqueData: ann.ExtraOpaqueData,
|
||||
edge, err := models.NewV1Channel(
|
||||
scid.ToUint64(), ann.ChainHash, ann.NodeID1, ann.NodeID2,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: ann.BitcoinKey1,
|
||||
BitcoinKey2Bytes: ann.BitcoinKey2,
|
||||
ExtraOpaqueData: ann.ExtraOpaqueData,
|
||||
},
|
||||
models.WithChanProof(proof), models.WithFeatures(ann.Features),
|
||||
)
|
||||
if err != nil {
|
||||
key := newRejectCacheKey(
|
||||
ann.GossipVersion(),
|
||||
scid.ToUint64(),
|
||||
sourceToPub(nMsg.source),
|
||||
)
|
||||
_, _ = d.recentRejects.Put(key, &cachedReject{})
|
||||
|
||||
log.Errorf("unable to create channel edge: %v", err)
|
||||
nMsg.err <- err
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// If there were any optional message fields provided, we'll include
|
||||
@@ -3441,19 +3422,12 @@ func (d *AuthenticatedGossiper) handleChanUpdate(ctx context.Context,
|
||||
// different alias. This might mean that SigBytes is incorrect as it
|
||||
// signs a different SCID than the database SCID, but since there will
|
||||
// only be a difference if AuthProof == nil, this is fine.
|
||||
update := &models.ChannelEdgePolicy{
|
||||
SigBytes: upd.Signature.ToSignatureBytes(),
|
||||
ChannelID: chanInfo.ChannelID,
|
||||
LastUpdate: timestamp,
|
||||
MessageFlags: upd.MessageFlags,
|
||||
ChannelFlags: upd.ChannelFlags,
|
||||
TimeLockDelta: upd.TimeLockDelta,
|
||||
MinHTLC: upd.HtlcMinimumMsat,
|
||||
MaxHTLC: upd.HtlcMaximumMsat,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(upd.BaseFee),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(upd.FeeRate),
|
||||
InboundFee: upd.InboundFee.ValOpt(),
|
||||
ExtraOpaqueData: upd.ExtraOpaqueData,
|
||||
update, err := models.ChanEdgePolicyFromWire(
|
||||
chanInfo.ChannelID, upd,
|
||||
)
|
||||
if err != nil {
|
||||
nMsg.err <- err
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if err := d.cfg.Graph.UpdateEdge(ctx, update, ops...); err != nil {
|
||||
@@ -3695,7 +3669,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
|
||||
ann.ChannelID, peerID)
|
||||
|
||||
ca, _, _, err := netann.CreateChanAnnouncement(
|
||||
chanInfo.AuthProof, chanInfo, e1, e2,
|
||||
chanInfo, e1, e2,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("unable to gen ann: %v",
|
||||
@@ -3757,21 +3731,29 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
|
||||
// We now have both halves of the channel announcement proof, then
|
||||
// we'll reconstruct the initial announcement so we can validate it
|
||||
// shortly below.
|
||||
var dbProof models.ChannelAuthProof
|
||||
var dbProof *models.ChannelAuthProof
|
||||
if isFirstNode {
|
||||
dbProof.NodeSig1Bytes = ann.NodeSignature.ToSignatureBytes()
|
||||
dbProof.NodeSig2Bytes = oppProof.NodeSignature.ToSignatureBytes()
|
||||
dbProof.BitcoinSig1Bytes = ann.BitcoinSignature.ToSignatureBytes()
|
||||
dbProof.BitcoinSig2Bytes = oppProof.BitcoinSignature.ToSignatureBytes()
|
||||
dbProof = models.NewV1ChannelAuthProof(
|
||||
ann.NodeSignature.ToSignatureBytes(),
|
||||
oppProof.NodeSignature.ToSignatureBytes(),
|
||||
ann.BitcoinSignature.ToSignatureBytes(),
|
||||
oppProof.BitcoinSignature.ToSignatureBytes(),
|
||||
)
|
||||
} else {
|
||||
dbProof.NodeSig1Bytes = oppProof.NodeSignature.ToSignatureBytes()
|
||||
dbProof.NodeSig2Bytes = ann.NodeSignature.ToSignatureBytes()
|
||||
dbProof.BitcoinSig1Bytes = oppProof.BitcoinSignature.ToSignatureBytes()
|
||||
dbProof.BitcoinSig2Bytes = ann.BitcoinSignature.ToSignatureBytes()
|
||||
dbProof = models.NewV1ChannelAuthProof(
|
||||
oppProof.NodeSignature.ToSignatureBytes(),
|
||||
ann.NodeSignature.ToSignatureBytes(),
|
||||
oppProof.BitcoinSignature.ToSignatureBytes(),
|
||||
ann.BitcoinSignature.ToSignatureBytes(),
|
||||
)
|
||||
}
|
||||
|
||||
// Attach the proof to the channel info before creating the
|
||||
// announcement.
|
||||
chanInfo.AuthProof = dbProof
|
||||
|
||||
chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement(
|
||||
&dbProof, chanInfo, e1, e2,
|
||||
chanInfo, e1, e2,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@@ -3797,7 +3779,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(ctx context.Context,
|
||||
// attest to the bitcoin keys by validating the signatures of
|
||||
// announcement. If proof is valid then we'll populate the channel edge
|
||||
// with it, so we can announce it on peer connect.
|
||||
err = d.cfg.Graph.AddProof(ann.ShortChannelID, &dbProof)
|
||||
err = d.cfg.Graph.AddProof(ann.ShortChannelID, dbProof)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable add proof to the channel chanID=%v:"+
|
||||
" %v", ann.ChannelID, err)
|
||||
|
||||
@@ -272,10 +272,15 @@ func (r *mockGraphSource) GetChannelByID(chanID lnwire.ShortChannelID) (
|
||||
return nil, nil, nil, graphdb.ErrEdgeNotFound
|
||||
}
|
||||
|
||||
return &models.ChannelEdgeInfo{
|
||||
NodeKey1Bytes: pubKeys[0],
|
||||
NodeKey2Bytes: pubKeys[1],
|
||||
}, nil, nil, graphdb.ErrZombieEdge
|
||||
zombieEdge, err := models.NewV1Channel(
|
||||
0, chainhash.Hash{}, pubKeys[0], pubKeys[1],
|
||||
&models.ChannelV1Fields{},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return zombieEdge, nil, nil, graphdb.ErrZombieEdge
|
||||
}
|
||||
|
||||
edges := r.edges[chanID.ToUint64()]
|
||||
@@ -4443,7 +4448,9 @@ func TestRateLimitChannelUpdates(t *testing.T) {
|
||||
// our rebroadcast interval.
|
||||
rateLimitKeepAliveUpdate := *batch.chanUpdAnn1
|
||||
rateLimitKeepAliveUpdate.Timestamp++
|
||||
require.NoError(t, signUpdate(remoteKeyPriv1, &rateLimitKeepAliveUpdate))
|
||||
require.NoError(
|
||||
t, signUpdate(remoteKeyPriv1, &rateLimitKeepAliveUpdate),
|
||||
)
|
||||
assertRateLimit(&rateLimitKeepAliveUpdate, nodePeer1, true)
|
||||
|
||||
keepAliveUpdate := *batch.chanUpdAnn1
|
||||
|
||||
@@ -176,6 +176,11 @@
|
||||
* Freeze the [graph SQL migration
|
||||
code](https://github.com/lightningnetwork/lnd/pull/10338) to prevent the
|
||||
need for maintenance as the sqlc code evolves.
|
||||
* Prepare the graph DB for handling gossip V2
|
||||
nodes and channels [1](https://github.com/lightningnetwork/lnd/pull/10339)
|
||||
[2](https://github.com/lightningnetwork/lnd/pull/10379)
|
||||
[3](https://github.com/lightningnetwork/lnd/pull/10380)
|
||||
[4](https://github.com/lightningnetwork/lnd/pull/10542).
|
||||
|
||||
## Code Health
|
||||
|
||||
|
||||
+27
-27
@@ -111,7 +111,8 @@ type Builder struct {
|
||||
|
||||
bestHeight atomic.Uint32
|
||||
|
||||
cfg *Config
|
||||
cfg *Config
|
||||
v1Graph *graphdb.VersionedGraph
|
||||
|
||||
// newBlocks is a channel in which new blocks connected to the end of
|
||||
// the main chain are sent over, and blocks updated after a call to
|
||||
@@ -146,7 +147,11 @@ var _ ChannelGraphSource = (*Builder)(nil)
|
||||
// NewBuilder constructs a new Builder.
|
||||
func NewBuilder(cfg *Config) (*Builder, error) {
|
||||
return &Builder{
|
||||
cfg: cfg,
|
||||
cfg: cfg,
|
||||
// For now, we'll just use V1 graph reader.
|
||||
v1Graph: graphdb.NewVersionedGraph(
|
||||
cfg.Graph, lnwire.GossipVersion1,
|
||||
),
|
||||
channelEdgeMtx: multimutex.NewMutex[uint64](),
|
||||
statTicker: ticker.New(defaultStatInterval),
|
||||
stats: new(builderStats),
|
||||
@@ -620,7 +625,7 @@ func (b *Builder) pruneZombieChans() error {
|
||||
toPrune = append(toPrune, chanID)
|
||||
log.Tracef("Pruning zombie channel with ChannelID(%v)", chanID)
|
||||
}
|
||||
err := b.cfg.Graph.DeleteChannelEdges(
|
||||
err := b.v1Graph.DeleteChannelEdges(
|
||||
b.cfg.StrictZombiePruning, true, toPrune...,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -874,7 +879,7 @@ func (b *Builder) assertNodeAnnFreshness(ctx context.Context, node route.Vertex,
|
||||
// node announcements, we will ignore such nodes. If we do know about
|
||||
// this node, check that this update brings info newer than what we
|
||||
// already have.
|
||||
lastUpdate, exists, err := b.cfg.Graph.HasNode(ctx, node)
|
||||
lastUpdate, exists, err := b.cfg.Graph.HasV1Node(ctx, node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to query for the "+
|
||||
"existence of node: %w", err)
|
||||
@@ -947,19 +952,12 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
update := &models.ChannelEdgePolicy{
|
||||
SigBytes: msg.Signature.ToSignatureBytes(),
|
||||
ChannelID: msg.ShortChannelID.ToUint64(),
|
||||
LastUpdate: time.Unix(int64(msg.Timestamp), 0),
|
||||
MessageFlags: msg.MessageFlags,
|
||||
ChannelFlags: msg.ChannelFlags,
|
||||
TimeLockDelta: msg.TimeLockDelta,
|
||||
MinHTLC: msg.HtlcMinimumMsat,
|
||||
MaxHTLC: msg.HtlcMaximumMsat,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
|
||||
InboundFee: msg.InboundFee.ValOpt(),
|
||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
update, err := models.ChanEdgePolicyFromWire(
|
||||
msg.ShortChannelID.ToUint64(), msg,
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to parse channel update: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
err = b.UpdateEdge(ctx, update)
|
||||
@@ -1045,8 +1043,8 @@ func (b *Builder) addEdge(ctx context.Context, edge *models.ChannelEdgeInfo,
|
||||
|
||||
// Prior to processing the announcement we first check if we
|
||||
// already know of this channel, if so, then we can exit early.
|
||||
_, _, exists, isZombie, err := b.cfg.Graph.HasChannelEdge(
|
||||
edge.ChannelID,
|
||||
exists, isZombie, err := b.cfg.Graph.HasChannelEdge(
|
||||
edge.Version, edge.ChannelID,
|
||||
)
|
||||
if err != nil && !errors.Is(err, graphdb.ErrGraphNoEdgesFound) {
|
||||
return fmt.Errorf("unable to check for edge existence: %w",
|
||||
@@ -1147,7 +1145,7 @@ func (b *Builder) updateEdge(ctx context.Context,
|
||||
defer b.channelEdgeMtx.Unlock(policy.ChannelID)
|
||||
|
||||
edge1Timestamp, edge2Timestamp, exists, isZombie, err :=
|
||||
b.cfg.Graph.HasChannelEdge(policy.ChannelID)
|
||||
b.cfg.Graph.HasV1ChannelEdge(policy.ChannelID)
|
||||
if err != nil && !errors.Is(err, graphdb.ErrGraphNoEdgesFound) {
|
||||
return fmt.Errorf("unable to check for edge existence: %w", err)
|
||||
}
|
||||
@@ -1266,7 +1264,7 @@ func (b *Builder) GetChannelByID(chanID lnwire.ShortChannelID) (
|
||||
func (b *Builder) FetchNode(ctx context.Context,
|
||||
node route.Vertex) (*models.Node, error) {
|
||||
|
||||
return b.cfg.Graph.FetchNode(ctx, node)
|
||||
return b.v1Graph.FetchNode(ctx, node)
|
||||
}
|
||||
|
||||
// ForAllOutgoingChannels is used to iterate over all outgoing channels owned by
|
||||
@@ -1278,7 +1276,7 @@ func (b *Builder) ForAllOutgoingChannels(ctx context.Context,
|
||||
reset func()) error {
|
||||
|
||||
return b.cfg.Graph.ForEachNodeChannel(
|
||||
ctx, b.cfg.SelfNode,
|
||||
ctx, lnwire.GossipVersion1, b.cfg.SelfNode,
|
||||
func(c *models.ChannelEdgeInfo, e *models.ChannelEdgePolicy,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
|
||||
@@ -1325,7 +1323,7 @@ func (b *Builder) IsStaleNode(ctx context.Context, node route.Vertex,
|
||||
//
|
||||
// NOTE: This method is part of the ChannelGraphSource interface.
|
||||
func (b *Builder) IsPublicNode(node route.Vertex) (bool, error) {
|
||||
return b.cfg.Graph.IsPublicNode(node)
|
||||
return b.v1Graph.IsPublicNode(node)
|
||||
}
|
||||
|
||||
// IsKnownEdge returns true if the graph source already knows of the passed
|
||||
@@ -1333,8 +1331,8 @@ func (b *Builder) IsPublicNode(node route.Vertex) (bool, error) {
|
||||
//
|
||||
// NOTE: This method is part of the ChannelGraphSource interface.
|
||||
func (b *Builder) IsKnownEdge(chanID lnwire.ShortChannelID) bool {
|
||||
_, _, exists, isZombie, _ := b.cfg.Graph.HasChannelEdge(
|
||||
chanID.ToUint64(),
|
||||
exists, isZombie, _ := b.cfg.Graph.HasChannelEdge(
|
||||
lnwire.GossipVersion1, chanID.ToUint64(),
|
||||
)
|
||||
|
||||
return exists || isZombie
|
||||
@@ -1345,7 +1343,9 @@ func (b *Builder) IsKnownEdge(chanID lnwire.ShortChannelID) bool {
|
||||
//
|
||||
// NOTE: This method is part of the ChannelGraphSource interface.
|
||||
func (b *Builder) IsZombieEdge(chanID lnwire.ShortChannelID) (bool, error) {
|
||||
_, _, _, isZombie, err := b.cfg.Graph.HasChannelEdge(chanID.ToUint64())
|
||||
_, isZombie, err := b.cfg.Graph.HasChannelEdge(
|
||||
lnwire.GossipVersion1, chanID.ToUint64(),
|
||||
)
|
||||
|
||||
return isZombie, err
|
||||
}
|
||||
@@ -1358,7 +1358,7 @@ func (b *Builder) IsStaleEdgePolicy(chanID lnwire.ShortChannelID,
|
||||
timestamp time.Time, flags lnwire.ChanUpdateChanFlags) bool {
|
||||
|
||||
edge1Timestamp, edge2Timestamp, exists, isZombie, err :=
|
||||
b.cfg.Graph.HasChannelEdge(chanID.ToUint64())
|
||||
b.cfg.Graph.HasV1ChannelEdge(chanID.ToUint64())
|
||||
if err != nil {
|
||||
log.Debugf("Check stale edge policy got error: %v", err)
|
||||
return false
|
||||
|
||||
+197
-173
@@ -17,10 +17,10 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||
"github.com/lightningnetwork/lnd/htlcswitch"
|
||||
@@ -65,17 +65,17 @@ func TestAddProof(t *testing.T) {
|
||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
|
||||
|
||||
// After utxo was recreated adding the edge without the proof.
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: nil,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, ctx.builder.AddEdge(ctxb, edge))
|
||||
|
||||
// Now we'll attempt to update the proof and check that it has been
|
||||
@@ -151,17 +151,22 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
|
||||
}
|
||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: pub1,
|
||||
NodeKey2Bytes: pub2,
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
AuthProof: nil,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
pub1Vertex, err := route.NewVertexFromBytes(pub1[:])
|
||||
require.NoError(t, err)
|
||||
pub2Vertex, err := route.NewVertexFromBytes(pub2[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
pub1Vertex, pub2Vertex, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: pub1Vertex,
|
||||
BitcoinKey2Bytes: pub2Vertex,
|
||||
}, models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
@@ -272,48 +277,49 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
edge1 := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID1,
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(fundingScript1),
|
||||
}
|
||||
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1, err := route.NewVertexFromBytes(
|
||||
bitcoinKey1.SerializeCompressed(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
btcKey2, err := route.NewVertexFromBytes(
|
||||
bitcoinKey2.SerializeCompressed(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
edge1, err := models.NewV1Channel(
|
||||
chanID1, *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(), testSig.Serialize(),
|
||||
testSig.Serialize(), testSig.Serialize(),
|
||||
)), models.WithFundingScript(fundingScript1),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge1); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
edge2 := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID2,
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(fundingScript2),
|
||||
}
|
||||
copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
edge2, err := models.NewV1Channel(
|
||||
chanID2, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes,
|
||||
node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(), testSig.Serialize(),
|
||||
testSig.Serialize(), testSig.Serialize(),
|
||||
)), models.WithFundingScript(fundingScript2),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge2); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// Check that the fundingTxs are in the graph db.
|
||||
_, _, has, isZombie, err := ctx.graph.HasChannelEdge(chanID1)
|
||||
has, isZombie, err := ctx.graph.HasChannelEdge(chanID1)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID1)
|
||||
}
|
||||
@@ -324,7 +330,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
||||
t.Fatal("edge was marked as zombie")
|
||||
}
|
||||
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID2)
|
||||
}
|
||||
@@ -359,7 +365,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
||||
// Create new router with same graph database.
|
||||
router, err := NewBuilder(&Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
Graph: ctx.graph,
|
||||
Graph: ctx.graph.ChannelGraph,
|
||||
Chain: ctx.chain,
|
||||
ChainView: ctx.chainView,
|
||||
ChannelPruneExpiry: time.Hour * 24,
|
||||
@@ -381,7 +387,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
||||
// The channel with chanID2 should not be in the database anymore,
|
||||
// since it is not confirmed on the longest chain. chanID1 should
|
||||
// still be.
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID1)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID1)
|
||||
require.NoError(t, err)
|
||||
|
||||
if !has {
|
||||
@@ -391,7 +397,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
|
||||
t.Fatal("edge was marked as zombie")
|
||||
}
|
||||
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID2)
|
||||
}
|
||||
@@ -482,52 +488,46 @@ func TestDisconnectedBlocks(t *testing.T) {
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
edge1 := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID1,
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
BitcoinKey1Bytes: node1.PubKeyBytes,
|
||||
BitcoinKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some([]byte{}),
|
||||
}
|
||||
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge1, err := models.NewV1Channel(
|
||||
chanID1, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes,
|
||||
node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithFundingScript([]byte{}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge1); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
edge2 := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID2,
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
BitcoinKey1Bytes: node1.PubKeyBytes,
|
||||
BitcoinKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some([]byte{}),
|
||||
}
|
||||
copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
edge2, err := models.NewV1Channel(
|
||||
chanID2, *chaincfg.SimNetParams.GenesisHash, node1.PubKeyBytes,
|
||||
node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithFundingScript([]byte{}),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge2); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// Check that the fundingTxs are in the graph db.
|
||||
_, _, has, isZombie, err := ctx.graph.HasChannelEdge(chanID1)
|
||||
has, isZombie, err := ctx.graph.HasChannelEdge(chanID1)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID1)
|
||||
}
|
||||
@@ -538,7 +538,7 @@ func TestDisconnectedBlocks(t *testing.T) {
|
||||
t.Fatal("edge was marked as zombie")
|
||||
}
|
||||
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID2)
|
||||
}
|
||||
@@ -580,7 +580,7 @@ func TestDisconnectedBlocks(t *testing.T) {
|
||||
|
||||
// chanID2 should not be in the database anymore, since it is not
|
||||
// confirmed on the longest chain. chanID1 should still be.
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID1)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID1)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID1)
|
||||
}
|
||||
@@ -591,7 +591,7 @@ func TestDisconnectedBlocks(t *testing.T) {
|
||||
t.Fatal("edge was marked as zombie")
|
||||
}
|
||||
|
||||
_, _, has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
has, isZombie, err = ctx.graph.HasChannelEdge(chanID2)
|
||||
if err != nil {
|
||||
t.Fatalf("error looking for edge: %v", chanID2)
|
||||
}
|
||||
@@ -638,29 +638,34 @@ func TestChansClosedOfflinePruneGraph(t *testing.T) {
|
||||
node1 := createTestNode(t)
|
||||
node2 := createTestNode(t)
|
||||
|
||||
edge1 := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID1.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
ChannelPoint: *chanUTXO,
|
||||
Capacity: chanValue,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge1, err := models.NewV1Channel(
|
||||
chanID1.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithCapacity(chanValue),
|
||||
models.WithChannelPoint(*chanUTXO),
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge1); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// The router should now be aware of the channel we created above.
|
||||
_, _, hasChan, isZombie, err := ctx.graph.HasChannelEdge(
|
||||
hasChan, isZombie, err := ctx.graph.HasChannelEdge(
|
||||
chanID1.ToUint64(),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -742,7 +747,7 @@ func TestChansClosedOfflinePruneGraph(t *testing.T) {
|
||||
|
||||
// At this point, the channel that was pruned should no longer be known
|
||||
// by the router.
|
||||
_, _, hasChan, isZombie, err = ctx.graph.HasChannelEdge(
|
||||
hasChan, isZombie, err = ctx.graph.HasChannelEdge(
|
||||
chanID1.ToUint64(),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1061,16 +1066,15 @@ func TestIsStaleNode(t *testing.T) {
|
||||
}
|
||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: pub1,
|
||||
NodeKey2Bytes: pub2,
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
AuthProof: nil,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
pub1, pub2, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
}, models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
@@ -1141,16 +1145,16 @@ func TestIsKnownEdge(t *testing.T) {
|
||||
}
|
||||
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: pub1,
|
||||
NodeKey2Bytes: pub2,
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
AuthProof: nil,
|
||||
FundingScript: fn.Some(script),
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
}
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, pub1,
|
||||
pub2, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
},
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
@@ -1201,22 +1205,22 @@ func TestIsStaleEdgePolicy(t *testing.T) {
|
||||
t.Fatalf("router failed to detect fresh edge policy")
|
||||
}
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: pub1,
|
||||
NodeKey2Bytes: pub2,
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
AuthProof: nil,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash, pub1,
|
||||
pub2, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
}, models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
|
||||
// We'll also add two edge policies, one for each direction.
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: updateTimeStamp,
|
||||
@@ -1231,6 +1235,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
|
||||
}
|
||||
|
||||
edgePolicy = &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: updateTimeStamp,
|
||||
@@ -1513,18 +1518,27 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
|
||||
// We first insert the existence of the edge between the two
|
||||
// nodes.
|
||||
edgeInfo := models.ChannelEdgeInfo{
|
||||
ChannelID: edge.ChannelID,
|
||||
AuthProof: &testAuthProof,
|
||||
ChannelPoint: fundingPoint,
|
||||
Capacity: btcutil.Amount(edge.Capacity),
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
}
|
||||
var node1Vertex, node2Vertex route.Vertex
|
||||
copy(node1Vertex[:], node1Bytes)
|
||||
copy(node2Vertex[:], node2Bytes)
|
||||
|
||||
copy(edgeInfo.NodeKey1Bytes[:], node1Bytes)
|
||||
copy(edgeInfo.NodeKey2Bytes[:], node2Bytes)
|
||||
copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes)
|
||||
copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes)
|
||||
var btcKey1, btcKey2 route.Vertex
|
||||
copy(btcKey1[:], node1Bytes)
|
||||
copy(btcKey2[:], node2Bytes)
|
||||
|
||||
edgeInfo, err := models.NewV1Channel(
|
||||
edge.ChannelID, *chaincfg.SimNetParams.GenesisHash,
|
||||
node1Vertex, node2Vertex, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
},
|
||||
models.WithChanProof(&testAuthProof),
|
||||
models.WithChannelPoint(fundingPoint),
|
||||
models.WithCapacity(btcutil.Amount(edge.Capacity)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortID := lnwire.NewShortChanIDFromInt(edge.ChannelID)
|
||||
links[shortID] = &mockLink{
|
||||
@@ -1533,7 +1547,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
),
|
||||
}
|
||||
|
||||
err = graph.AddChannelEdge(ctx, &edgeInfo)
|
||||
err = graph.AddChannelEdge(ctx, edgeInfo)
|
||||
if err != nil && !errors.Is(err, graphdb.ErrEdgeAlreadyExist) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1546,6 +1560,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: lnwire.ChanUpdateMsgFlags(
|
||||
edge.MessageFlags,
|
||||
@@ -1573,12 +1588,12 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
// We also store the channel IDs info for each of the node.
|
||||
node1Vertex, err := route.NewVertexFromBytes(node1Bytes)
|
||||
node1Vertex, err = route.NewVertexFromBytes(node1Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node2Vertex, err := route.NewVertexFromBytes(node2Bytes)
|
||||
node2Vertex, err = route.NewVertexFromBytes(node2Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1595,7 +1610,9 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
return &testGraphInstance{
|
||||
graph: graph,
|
||||
graph: graphdb.NewVersionedGraph(
|
||||
graph, lnwire.GossipVersion1,
|
||||
),
|
||||
aliasMap: aliasMap,
|
||||
privKeyMap: privKeyMap,
|
||||
channelIDs: channelIDs,
|
||||
@@ -1690,7 +1707,7 @@ func asymmetricTestChannel(alias1, alias2 string, capacity btcutil.Amount,
|
||||
|
||||
// assertChannelsPruned ensures that only the given channels are pruned from the
|
||||
// graph out of the set of all channels.
|
||||
func assertChannelsPruned(t *testing.T, graph *graphdb.ChannelGraph,
|
||||
func assertChannelsPruned(t *testing.T, graph *graphdb.VersionedGraph,
|
||||
channels []*testChannel, prunedChanIDs ...uint64) {
|
||||
|
||||
t.Helper()
|
||||
@@ -1702,7 +1719,7 @@ func assertChannelsPruned(t *testing.T, graph *graphdb.ChannelGraph,
|
||||
|
||||
for _, channel := range channels {
|
||||
_, shouldPrune := pruned[channel.ChannelID]
|
||||
_, _, exists, isZombie, err := graph.HasChannelEdge(
|
||||
exists, isZombie, err := graph.HasChannelEdge(
|
||||
channel.ChannelID,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1882,20 +1899,21 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
|
||||
// We first insert the existence of the edge between the two
|
||||
// nodes.
|
||||
edgeInfo := models.ChannelEdgeInfo{
|
||||
ChannelID: channelID,
|
||||
AuthProof: &testAuthProof,
|
||||
ChannelPoint: *fundingPoint,
|
||||
Capacity: testChannel.Capacity,
|
||||
|
||||
NodeKey1Bytes: node1Vertex,
|
||||
BitcoinKey1Bytes: node1Vertex,
|
||||
NodeKey2Bytes: node2Vertex,
|
||||
BitcoinKey2Bytes: node2Vertex,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
edgeInfo, err := models.NewV1Channel(
|
||||
channelID, *chaincfg.SimNetParams.GenesisHash,
|
||||
node1Vertex, node2Vertex, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: node1Vertex,
|
||||
BitcoinKey2Bytes: node2Vertex,
|
||||
},
|
||||
models.WithChanProof(&testAuthProof),
|
||||
models.WithChannelPoint(*fundingPoint),
|
||||
models.WithCapacity(testChannel.Capacity),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = graph.AddChannelEdge(ctx, &edgeInfo)
|
||||
err = graph.AddChannelEdge(ctx, edgeInfo)
|
||||
if err != nil &&
|
||||
!errors.Is(err, graphdb.ErrEdgeAlreadyExist) {
|
||||
|
||||
@@ -1925,7 +1943,9 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
channelFlags |= lnwire.ChanUpdateDisabled
|
||||
}
|
||||
|
||||
//nolint:ll
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
ChannelFlags: channelFlags,
|
||||
@@ -1956,7 +1976,9 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
}
|
||||
channelFlags |= lnwire.ChanUpdateDirection
|
||||
|
||||
//nolint:ll
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
ChannelFlags: channelFlags,
|
||||
@@ -1980,7 +2002,9 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
}
|
||||
|
||||
return &testGraphInstance{
|
||||
graph: graph,
|
||||
graph: graphdb.NewVersionedGraph(
|
||||
graph, lnwire.GossipVersion1,
|
||||
),
|
||||
aliasMap: aliasMap,
|
||||
privKeyMap: privKeyMap,
|
||||
links: links,
|
||||
|
||||
+68
-66
@@ -72,19 +72,19 @@ var (
|
||||
// and a function to open the connection.
|
||||
type dbConnection struct {
|
||||
name string
|
||||
open func(testing.TB) V1Store
|
||||
open func(testing.TB) Store
|
||||
}
|
||||
|
||||
// This var block defines the various database connections that we will use
|
||||
// for testing. Each connection is defined as a dbConnection struct that
|
||||
// contains a name and an open function. The open function is used to create
|
||||
// a new V1Store instance for the given database type.
|
||||
// a new Store instance for the given database type.
|
||||
var (
|
||||
// kvdbBBoltConn is a connection to a kvdb-bbolt database called
|
||||
// channel.db.
|
||||
kvdbBBoltConn = dbConnection{
|
||||
name: "kvdb-bbolt",
|
||||
open: func(b testing.TB) V1Store {
|
||||
open: func(b testing.TB) Store {
|
||||
return connectBBoltDB(b, bboltDBPath, kvdbBBoltFile)
|
||||
},
|
||||
}
|
||||
@@ -93,7 +93,7 @@ var (
|
||||
// channel.sqlite.
|
||||
kvdbSqliteConn = dbConnection{
|
||||
name: "kvdb-sqlite",
|
||||
open: func(b testing.TB) V1Store {
|
||||
open: func(b testing.TB) Store {
|
||||
return connectKVDBSqlite(
|
||||
b, kvdbSqlitePath, kvdbSqliteFile,
|
||||
)
|
||||
@@ -104,7 +104,7 @@ var (
|
||||
// called lnd.sqlite.
|
||||
nativeSQLSqliteConn = dbConnection{
|
||||
name: "native-sqlite",
|
||||
open: func(b testing.TB) V1Store {
|
||||
open: func(b testing.TB) Store {
|
||||
return connectNativeSQLite(
|
||||
b, sqldb.DefaultSQLiteConfig(),
|
||||
nativeSQLSqlitePath, nativeSQLSqliteFile,
|
||||
@@ -116,7 +116,7 @@ var (
|
||||
// using a postgres connection string.
|
||||
kvdbPostgresConn = dbConnection{
|
||||
name: "kvdb-postgres",
|
||||
open: func(b testing.TB) V1Store {
|
||||
open: func(b testing.TB) Store {
|
||||
return connectKVDBPostgres(b, kvdbPostgresDNS)
|
||||
},
|
||||
}
|
||||
@@ -125,7 +125,7 @@ var (
|
||||
// database using a postgres connection string.
|
||||
nativeSQLPostgresConn = dbConnection{
|
||||
name: "native-postgres",
|
||||
open: func(b testing.TB) V1Store {
|
||||
open: func(b testing.TB) Store {
|
||||
return connectNativePostgres(
|
||||
b, sqldb.DefaultPostgresConfig(),
|
||||
nativeSQLPostgresDNS,
|
||||
@@ -134,10 +134,10 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// connectNativePostgres creates a V1Store instance backed by a native Postgres
|
||||
// connectNativePostgres creates a Store instance backed by a native Postgres
|
||||
// database for testing purposes.
|
||||
func connectNativePostgres(t testing.TB, cfg *sqldb.QueryConfig,
|
||||
dsn string) V1Store {
|
||||
dsn string) Store {
|
||||
|
||||
return newSQLStore(t, cfg, sqlPostgres(t, dsn))
|
||||
}
|
||||
@@ -157,10 +157,10 @@ func sqlPostgres(t testing.TB, dsn string) BatchedSQLQueries {
|
||||
return newSQLExecutor(t, store)
|
||||
}
|
||||
|
||||
// connectNativeSQLite creates a V1Store instance backed by a native SQLite
|
||||
// connectNativeSQLite creates a Store instance backed by a native SQLite
|
||||
// database for testing purposes.
|
||||
func connectNativeSQLite(t testing.TB, cfg *sqldb.QueryConfig, dbPath,
|
||||
file string) V1Store {
|
||||
file string) Store {
|
||||
|
||||
return newSQLStore(t, cfg, sqlSQLite(t, dbPath, file))
|
||||
}
|
||||
@@ -205,9 +205,9 @@ func kvdbPostgres(t testing.TB, dsn string) kvdb.Backend {
|
||||
return kvStore
|
||||
}
|
||||
|
||||
// connectKVDBPostgres creates a V1Store instance backed by a kvdb-postgres
|
||||
// connectKVDBPostgres creates a Store instance backed by a kvdb-postgres
|
||||
// database for testing purposes.
|
||||
func connectKVDBPostgres(t testing.TB, dsn string) V1Store {
|
||||
func connectKVDBPostgres(t testing.TB, dsn string) Store {
|
||||
return newKVStore(t, kvdbPostgres(t, dsn))
|
||||
}
|
||||
|
||||
@@ -231,14 +231,14 @@ func kvdbSqlite(t testing.TB, dbPath, fileName string) kvdb.Backend {
|
||||
return kvStore
|
||||
}
|
||||
|
||||
// connectKVDBSqlite creates a V1Store instance backed by a kvdb-sqlite
|
||||
// connectKVDBSqlite creates a Store instance backed by a kvdb-sqlite
|
||||
// database for testing purposes.
|
||||
func connectKVDBSqlite(t testing.TB, dbPath, fileName string) V1Store {
|
||||
func connectKVDBSqlite(t testing.TB, dbPath, fileName string) Store {
|
||||
return newKVStore(t, kvdbSqlite(t, dbPath, fileName))
|
||||
}
|
||||
|
||||
// connectBBoltDB creates a new BBolt database connection for testing.
|
||||
func connectBBoltDB(t testing.TB, dbPath, fileName string) V1Store {
|
||||
func connectBBoltDB(t testing.TB, dbPath, fileName string) Store {
|
||||
return newKVStore(t, kvdbBBolt(t, dbPath, fileName))
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ func kvdbBBolt(t testing.TB, dbPath, fileName string) kvdb.Backend {
|
||||
|
||||
// newKVStore creates a new KVStore instance for testing using a provided
|
||||
// kvdb.Backend instance.
|
||||
func newKVStore(t testing.TB, backend kvdb.Backend) V1Store {
|
||||
func newKVStore(t testing.TB, backend kvdb.Backend) Store {
|
||||
store, err := NewKVStore(backend, testStoreOptions...)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -286,7 +286,7 @@ func newSQLExecutor(t testing.TB, db sqldb.DB) BatchedSQLQueries {
|
||||
// newSQLStore creates a new SQLStore instance for testing using a provided
|
||||
// sqldb.DB instance.
|
||||
func newSQLStore(t testing.TB, cfg *sqldb.QueryConfig,
|
||||
db BatchedSQLQueries) V1Store {
|
||||
db BatchedSQLQueries) Store {
|
||||
|
||||
store, err := NewSQLStore(
|
||||
&SQLStoreConfig{
|
||||
@@ -370,8 +370,8 @@ func TestPopulateDBs(t *testing.T) {
|
||||
numPolicies = 0
|
||||
)
|
||||
err := graph.ForEachChannel(
|
||||
ctx, func(info *models.ChannelEdgeInfo,
|
||||
policy,
|
||||
ctx, lnwire.GossipVersion1,
|
||||
func(info *models.ChannelEdgeInfo, policy,
|
||||
policy2 *models.ChannelEdgePolicy) error {
|
||||
|
||||
numChans++
|
||||
@@ -497,48 +497,49 @@ func syncGraph(t *testing.T, src, dest *ChannelGraph) {
|
||||
}
|
||||
|
||||
var wgChans sync.WaitGroup
|
||||
err = src.ForEachChannel(ctx, func(info *models.ChannelEdgeInfo,
|
||||
policy1, policy2 *models.ChannelEdgePolicy) error {
|
||||
err = src.ForEachChannel(ctx, lnwire.GossipVersion1,
|
||||
func(info *models.ChannelEdgeInfo,
|
||||
policy1, policy2 *models.ChannelEdgePolicy) error {
|
||||
|
||||
// Add each channel & policy. We do this in a goroutine to
|
||||
// take advantage of batch processing.
|
||||
wgChans.Add(1)
|
||||
go func() {
|
||||
defer wgChans.Done()
|
||||
// Add each channel & policy. We do this in a goroutine
|
||||
// to take advantage of batch processing.
|
||||
wgChans.Add(1)
|
||||
go func() {
|
||||
defer wgChans.Done()
|
||||
|
||||
err := dest.AddChannelEdge(
|
||||
ctx, info, batch.LazyAdd(),
|
||||
)
|
||||
if !errors.Is(err, ErrEdgeAlreadyExist) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if policy1 != nil {
|
||||
err = dest.UpdateEdgePolicy(
|
||||
ctx, policy1, batch.LazyAdd(),
|
||||
err := dest.AddChannelEdge(
|
||||
ctx, info, batch.LazyAdd(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if !errors.Is(err, ErrEdgeAlreadyExist) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if policy2 != nil {
|
||||
err = dest.UpdateEdgePolicy(
|
||||
ctx, policy2, batch.LazyAdd(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if policy1 != nil {
|
||||
err = dest.UpdateEdgePolicy(
|
||||
ctx, policy1, batch.LazyAdd(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
total++
|
||||
chunk++
|
||||
s.Do(func() {
|
||||
reportChanStats()
|
||||
chunk = 0
|
||||
})
|
||||
mu.Unlock()
|
||||
}()
|
||||
if policy2 != nil {
|
||||
err = dest.UpdateEdgePolicy(
|
||||
ctx, policy2, batch.LazyAdd(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
mu.Lock()
|
||||
total++
|
||||
chunk++
|
||||
s.Do(func() {
|
||||
reportChanStats()
|
||||
chunk = 0
|
||||
})
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}, func() {})
|
||||
require.NoError(t, err)
|
||||
|
||||
wgChans.Wait()
|
||||
@@ -587,7 +588,7 @@ func BenchmarkCacheLoading(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGraphReadMethods benchmarks various read calls of various V1Store
|
||||
// BenchmarkGraphReadMethods benchmarks various read calls of various Store
|
||||
// implementations.
|
||||
//
|
||||
// NOTE: this is to be run against a local graph database. It can be run
|
||||
@@ -614,11 +615,11 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func(b testing.TB, store V1Store)
|
||||
fn func(b testing.TB, store Store)
|
||||
}{
|
||||
{
|
||||
name: "ForEachNode",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
err := store.ForEachNode(
|
||||
ctx,
|
||||
func(_ *models.Node) error {
|
||||
@@ -635,10 +636,11 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
},
|
||||
{
|
||||
name: "ForEachChannel",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
//nolint:ll
|
||||
err := store.ForEachChannel(
|
||||
ctx, func(_ *models.ChannelEdgeInfo,
|
||||
ctx, lnwire.GossipVersion1,
|
||||
func(_ *models.ChannelEdgeInfo,
|
||||
_ *models.ChannelEdgePolicy,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
|
||||
@@ -655,7 +657,7 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
},
|
||||
{
|
||||
name: "NodeUpdatesInHorizon",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
iter := store.NodeUpdatesInHorizon(
|
||||
time.Unix(0, 0), time.Now(),
|
||||
)
|
||||
@@ -665,7 +667,7 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
},
|
||||
{
|
||||
name: "ForEachNodeCacheable",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
err := store.ForEachNodeCacheable(
|
||||
ctx, func(_ route.Vertex,
|
||||
_ *lnwire.FeatureVector) error {
|
||||
@@ -683,7 +685,7 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
},
|
||||
{
|
||||
name: "ForEachNodeCached",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
//nolint:ll
|
||||
err := store.ForEachNodeCached(
|
||||
ctx, false, func(context.Context,
|
||||
@@ -704,7 +706,7 @@ func BenchmarkGraphReadMethods(b *testing.B) {
|
||||
},
|
||||
{
|
||||
name: "ChanUpdatesInHorizon",
|
||||
fn: func(b testing.TB, store V1Store) {
|
||||
fn: func(b testing.TB, store Store) {
|
||||
iter := store.ChanUpdatesInHorizon(
|
||||
time.Unix(0, 0), time.Now(),
|
||||
)
|
||||
@@ -821,7 +823,7 @@ func BenchmarkFindOptimalSQLQueryConfig(b *testing.B) {
|
||||
|
||||
//nolint:ll
|
||||
err = store.ForEachChannel(
|
||||
ctx,
|
||||
ctx, lnwire.GossipVersion1,
|
||||
func(_ *models.ChannelEdgeInfo,
|
||||
_,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// TestChannelCache checks the behavior of the channelCache with respect to
|
||||
@@ -99,9 +101,14 @@ func assertHasChanEntries(t *testing.T, c *channelCache, start, end uint64) {
|
||||
|
||||
// channelForInt generates a unique ChannelEdge given an integer.
|
||||
func channelForInt(i uint64) ChannelEdge {
|
||||
info, err := models.NewV1Channel(
|
||||
i, chainhash.Hash{}, route.Vertex{}, route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ChannelEdge{
|
||||
Info: &models.ChannelEdgeInfo{
|
||||
ChannelID: i,
|
||||
},
|
||||
Info: info,
|
||||
}
|
||||
}
|
||||
+368
-41
@@ -4,12 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"iter"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/batch"
|
||||
@@ -30,7 +32,7 @@ type ChannelGraph struct {
|
||||
|
||||
graphCache *GraphCache
|
||||
|
||||
V1Store
|
||||
db Store
|
||||
*topologyManager
|
||||
|
||||
quit chan struct{}
|
||||
@@ -38,7 +40,7 @@ type ChannelGraph struct {
|
||||
}
|
||||
|
||||
// NewChannelGraph creates a new ChannelGraph instance with the given backend.
|
||||
func NewChannelGraph(v1Store V1Store,
|
||||
func NewChannelGraph(v1Store Store,
|
||||
options ...ChanGraphOption) (*ChannelGraph, error) {
|
||||
|
||||
opts := defaultChanGraphOptions()
|
||||
@@ -47,7 +49,7 @@ func NewChannelGraph(v1Store V1Store,
|
||||
}
|
||||
|
||||
g := &ChannelGraph{
|
||||
V1Store: v1Store,
|
||||
db: v1Store,
|
||||
topologyManager: newTopologyManager(),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
@@ -161,7 +163,7 @@ func (c *ChannelGraph) populateCache(ctx context.Context) error {
|
||||
log.Info("Populating in-memory channel graph, this might take a " +
|
||||
"while...")
|
||||
|
||||
err := c.V1Store.ForEachNodeCacheable(ctx, func(node route.Vertex,
|
||||
err := c.db.ForEachNodeCacheable(ctx, func(node route.Vertex,
|
||||
features *lnwire.FeatureVector) error {
|
||||
|
||||
c.graphCache.AddNodeFeatures(node, features)
|
||||
@@ -172,7 +174,7 @@ func (c *ChannelGraph) populateCache(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.V1Store.ForEachChannelCacheable(
|
||||
err = c.db.ForEachChannelCacheable(
|
||||
func(info *models.CachedEdgeInfo,
|
||||
policy1, policy2 *models.CachedEdgePolicy) error {
|
||||
|
||||
@@ -208,7 +210,7 @@ func (c *ChannelGraph) ForEachNodeDirectedChannel(node route.Vertex,
|
||||
return c.graphCache.ForEachChannel(node, cb)
|
||||
}
|
||||
|
||||
return c.V1Store.ForEachNodeDirectedChannel(node, cb, reset)
|
||||
return c.db.ForEachNodeDirectedChannel(node, cb, reset)
|
||||
}
|
||||
|
||||
// FetchNodeFeatures returns the features of the given node. If no features are
|
||||
@@ -224,7 +226,7 @@ func (c *ChannelGraph) FetchNodeFeatures(node route.Vertex) (
|
||||
return c.graphCache.GetFeatures(node), nil
|
||||
}
|
||||
|
||||
return c.V1Store.FetchNodeFeatures(node)
|
||||
return c.db.FetchNodeFeatures(lnwire.GossipVersion1, node)
|
||||
}
|
||||
|
||||
// GraphSession will provide the call-back with access to a NodeTraverser
|
||||
@@ -238,7 +240,7 @@ func (c *ChannelGraph) GraphSession(cb func(graph NodeTraverser) error,
|
||||
return cb(c)
|
||||
}
|
||||
|
||||
return c.V1Store.GraphSession(cb, reset)
|
||||
return c.db.GraphSession(cb, reset)
|
||||
}
|
||||
|
||||
// ForEachNodeCached iterates through all the stored vertices/nodes in the
|
||||
@@ -259,7 +261,7 @@ func (c *ChannelGraph) ForEachNodeCached(ctx context.Context, withAddrs bool,
|
||||
)
|
||||
}
|
||||
|
||||
return c.V1Store.ForEachNodeCached(ctx, withAddrs, cb, reset)
|
||||
return c.db.ForEachNodeCached(ctx, withAddrs, cb, reset)
|
||||
}
|
||||
|
||||
// AddNode adds a vertex/node to the graph database. If the node is not
|
||||
@@ -271,7 +273,7 @@ func (c *ChannelGraph) ForEachNodeCached(ctx context.Context, withAddrs bool,
|
||||
func (c *ChannelGraph) AddNode(ctx context.Context,
|
||||
node *models.Node, op ...batch.SchedulerOption) error {
|
||||
|
||||
err := c.V1Store.AddNode(ctx, node, op...)
|
||||
err := c.db.AddNode(ctx, node, op...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -291,23 +293,6 @@ func (c *ChannelGraph) AddNode(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteNode starts a new database transaction to remove a vertex/node
|
||||
// from the database according to the node's public key.
|
||||
func (c *ChannelGraph) DeleteNode(ctx context.Context,
|
||||
nodePub route.Vertex) error {
|
||||
|
||||
err := c.V1Store.DeleteNode(ctx, nodePub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.graphCache != nil {
|
||||
c.graphCache.RemoveNode(nodePub)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddChannelEdge adds a new (undirected, blank) edge to the graph database. An
|
||||
// undirected edge from the two target nodes are created. The information stored
|
||||
// denotes the static attributes of the channel, such as the channelID, the keys
|
||||
@@ -317,7 +302,7 @@ func (c *ChannelGraph) DeleteNode(ctx context.Context,
|
||||
func (c *ChannelGraph) AddChannelEdge(ctx context.Context,
|
||||
edge *models.ChannelEdgeInfo, op ...batch.SchedulerOption) error {
|
||||
|
||||
err := c.V1Store.AddChannelEdge(ctx, edge, op...)
|
||||
err := c.db.AddChannelEdge(ctx, edge, op...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -339,7 +324,7 @@ func (c *ChannelGraph) AddChannelEdge(ctx context.Context,
|
||||
// If the cache is enabled, the edge will be added back to the graph cache if
|
||||
// we still have a record of this channel in the DB.
|
||||
func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
|
||||
err := c.V1Store.MarkEdgeLive(chanID)
|
||||
err := c.db.MarkEdgeLive(chanID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -347,7 +332,7 @@ func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
|
||||
if c.graphCache != nil {
|
||||
// We need to add the channel back into our graph cache,
|
||||
// otherwise we won't use it for path finding.
|
||||
infos, err := c.V1Store.FetchChanInfos([]uint64{chanID})
|
||||
infos, err := c.db.FetchChanInfos([]uint64{chanID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -385,8 +370,9 @@ func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
|
||||
func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
chanIDs ...uint64) error {
|
||||
|
||||
infos, err := c.V1Store.DeleteChannelEdges(
|
||||
strictZombiePruning, markZombie, chanIDs...,
|
||||
infos, err := c.db.DeleteChannelEdges(
|
||||
lnwire.GossipVersion1, strictZombiePruning, markZombie,
|
||||
chanIDs...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -414,7 +400,7 @@ func (c *ChannelGraph) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
func (c *ChannelGraph) DisconnectBlockAtHeight(height uint32) (
|
||||
[]*models.ChannelEdgeInfo, error) {
|
||||
|
||||
edges, err := c.V1Store.DisconnectBlockAtHeight(height)
|
||||
edges, err := c.db.DisconnectBlockAtHeight(height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -442,7 +428,7 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
|
||||
blockHash *chainhash.Hash, blockHeight uint32) (
|
||||
[]*models.ChannelEdgeInfo, error) {
|
||||
|
||||
edges, nodes, err := c.V1Store.PruneGraph(
|
||||
edges, nodes, err := c.db.PruneGraph(
|
||||
spentOutputs, blockHash, blockHeight,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -487,7 +473,7 @@ func (c *ChannelGraph) PruneGraph(spentOutputs []*wire.OutPoint,
|
||||
// that we only maintain a graph of reachable nodes. In the event that a pruned
|
||||
// node gains more channels, it will be re-added back to the graph.
|
||||
func (c *ChannelGraph) PruneGraphNodes() error {
|
||||
nodes, err := c.V1Store.PruneGraphNodes()
|
||||
nodes, err := c.db.PruneGraphNodes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -509,7 +495,7 @@ func (c *ChannelGraph) PruneGraphNodes() error {
|
||||
func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
|
||||
isZombieChan func(time.Time, time.Time) bool) ([]uint64, error) {
|
||||
|
||||
unknown, knownZombies, err := c.V1Store.FilterKnownChanIDs(chansInfo)
|
||||
unknown, knownZombies, err := c.db.FilterKnownChanIDs(chansInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -538,7 +524,7 @@ func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
|
||||
// timestamps could bring it back from the dead, then we mark it
|
||||
// alive, and we let it be added to the set of IDs to query our
|
||||
// peer for.
|
||||
err := c.V1Store.MarkEdgeLive(
|
||||
err := c.db.MarkEdgeLive(
|
||||
info.ShortChannelID.ToUint64(),
|
||||
)
|
||||
// Since there is a chance that the edge could have been marked
|
||||
@@ -559,7 +545,7 @@ func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
|
||||
func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
|
||||
pubKey1, pubKey2 [33]byte) error {
|
||||
|
||||
err := c.V1Store.MarkEdgeZombie(chanID, pubKey1, pubKey2)
|
||||
err := c.db.MarkEdgeZombie(chanID, pubKey1, pubKey2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -581,7 +567,7 @@ func (c *ChannelGraph) MarkEdgeZombie(chanID uint64,
|
||||
func (c *ChannelGraph) UpdateEdgePolicy(ctx context.Context,
|
||||
edge *models.ChannelEdgePolicy, op ...batch.SchedulerOption) error {
|
||||
|
||||
from, to, err := c.V1Store.UpdateEdgePolicy(ctx, edge, op...)
|
||||
from, to, err := c.db.UpdateEdgePolicy(ctx, edge, op...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -601,12 +587,353 @@ func (c *ChannelGraph) UpdateEdgePolicy(ctx context.Context,
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForEachSourceNodeChannel iterates through all channels of the source node.
|
||||
func (c *ChannelGraph) ForEachSourceNodeChannel(ctx context.Context,
|
||||
cb func(chanPoint wire.OutPoint, havePolicy bool,
|
||||
otherNode *models.Node) error, reset func()) error {
|
||||
|
||||
return c.db.ForEachSourceNodeChannel(ctx, cb, reset)
|
||||
}
|
||||
|
||||
// ForEachNodeChannel iterates through all channels of the given node.
|
||||
func (c *ChannelGraph) ForEachNodeChannel(ctx context.Context,
|
||||
v lnwire.GossipVersion, nodePub route.Vertex,
|
||||
cb func(*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error {
|
||||
|
||||
return c.db.ForEachNodeChannel(ctx, v, nodePub, cb, reset)
|
||||
}
|
||||
|
||||
// ForEachNode iterates through all stored vertices/nodes in the graph.
|
||||
func (c *ChannelGraph) ForEachNode(ctx context.Context,
|
||||
cb func(*models.Node) error, reset func()) error {
|
||||
|
||||
return c.db.ForEachNode(ctx, cb, reset)
|
||||
}
|
||||
|
||||
// ForEachNodeCacheable iterates through all stored vertices/nodes in the graph.
|
||||
func (c *ChannelGraph) ForEachNodeCacheable(ctx context.Context,
|
||||
cb func(route.Vertex, *lnwire.FeatureVector) error,
|
||||
reset func()) error {
|
||||
|
||||
return c.db.ForEachNodeCacheable(ctx, cb, reset)
|
||||
}
|
||||
|
||||
// NodeUpdatesInHorizon returns all known lightning nodes with updates in the
|
||||
// range.
|
||||
func (c *ChannelGraph) NodeUpdatesInHorizon(startTime, endTime time.Time,
|
||||
opts ...IteratorOption) iter.Seq2[*models.Node, error] {
|
||||
|
||||
return c.db.NodeUpdatesInHorizon(startTime, endTime, opts...)
|
||||
}
|
||||
|
||||
// HasV1Node determines if the graph has a vertex identified by the target node
|
||||
// in the V1 graph.
|
||||
func (c *ChannelGraph) HasV1Node(ctx context.Context,
|
||||
nodePub [33]byte) (time.Time, bool, error) {
|
||||
|
||||
return c.db.HasV1Node(ctx, nodePub)
|
||||
}
|
||||
|
||||
// IsPublicNode determines whether the node is seen as public in the graph.
|
||||
func (c *ChannelGraph) IsPublicNode(pubKey [33]byte) (bool, error) {
|
||||
return c.db.IsPublicNode(lnwire.GossipVersion1, pubKey)
|
||||
}
|
||||
|
||||
// ForEachChannel iterates through all channel edges stored within the graph.
|
||||
func (c *ChannelGraph) ForEachChannel(ctx context.Context,
|
||||
v lnwire.GossipVersion, cb func(*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) error,
|
||||
reset func()) error {
|
||||
|
||||
return c.db.ForEachChannel(ctx, v, cb, reset)
|
||||
}
|
||||
|
||||
// ForEachChannelCacheable iterates through all channel edges for the cache.
|
||||
func (c *ChannelGraph) ForEachChannelCacheable(cb func(*models.CachedEdgeInfo,
|
||||
*models.CachedEdgePolicy, *models.CachedEdgePolicy) error,
|
||||
reset func()) error {
|
||||
|
||||
return c.db.ForEachChannelCacheable(cb, reset)
|
||||
}
|
||||
|
||||
// DisabledChannelIDs returns the channel ids of disabled channels.
|
||||
func (c *ChannelGraph) DisabledChannelIDs() ([]uint64, error) {
|
||||
return c.db.DisabledChannelIDs()
|
||||
}
|
||||
|
||||
// HasV1ChannelEdge returns true if the database knows of a channel edge.
|
||||
func (c *ChannelGraph) HasV1ChannelEdge(chanID uint64) (time.Time,
|
||||
time.Time, bool, bool, error) {
|
||||
|
||||
return c.db.HasV1ChannelEdge(chanID)
|
||||
}
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge.
|
||||
func (c *ChannelGraph) HasChannelEdge(v lnwire.GossipVersion,
|
||||
chanID uint64) (bool, bool, error) {
|
||||
|
||||
return c.db.HasChannelEdge(v, chanID)
|
||||
}
|
||||
|
||||
// AddEdgeProof sets the proof of an existing edge in the graph database.
|
||||
func (c *ChannelGraph) AddEdgeProof(chanID lnwire.ShortChannelID,
|
||||
proof *models.ChannelAuthProof) error {
|
||||
|
||||
return c.db.AddEdgeProof(chanID, proof)
|
||||
}
|
||||
|
||||
// ChannelID attempts to lookup the 8-byte compact channel ID.
|
||||
func (c *ChannelGraph) ChannelID(chanPoint *wire.OutPoint) (uint64, error) {
|
||||
return c.db.ChannelID(chanPoint)
|
||||
}
|
||||
|
||||
// HighestChanID returns the "highest" known channel ID in the channel graph.
|
||||
func (c *ChannelGraph) HighestChanID(ctx context.Context) (uint64, error) {
|
||||
return c.db.HighestChanID(ctx)
|
||||
}
|
||||
|
||||
// ChanUpdatesInHorizon returns all known channel edges with updates in the
|
||||
// horizon.
|
||||
func (c *ChannelGraph) ChanUpdatesInHorizon(startTime, endTime time.Time,
|
||||
opts ...IteratorOption) iter.Seq2[ChannelEdge, error] {
|
||||
|
||||
return c.db.ChanUpdatesInHorizon(startTime, endTime, opts...)
|
||||
}
|
||||
|
||||
// FilterChannelRange returns channel IDs within the passed block height range.
|
||||
func (c *ChannelGraph) FilterChannelRange(startHeight, endHeight uint32,
|
||||
withTimestamps bool) ([]BlockChannelRange, error) {
|
||||
|
||||
return c.db.FilterChannelRange(startHeight, endHeight, withTimestamps)
|
||||
}
|
||||
|
||||
// FetchChanInfos returns the set of channel edges for the passed channel IDs.
|
||||
func (c *ChannelGraph) FetchChanInfos(chanIDs []uint64) ([]ChannelEdge, error) {
|
||||
return c.db.FetchChanInfos(chanIDs)
|
||||
}
|
||||
|
||||
// FetchChannelEdgesByOutpoint attempts to lookup directed edges by funding
|
||||
// outpoint.
|
||||
func (c *ChannelGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
return c.db.FetchChannelEdgesByOutpoint(
|
||||
lnwire.GossipVersion1, op,
|
||||
)
|
||||
}
|
||||
|
||||
// FetchChannelEdgesByID attempts to lookup directed edges by channel ID.
|
||||
func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
return c.db.FetchChannelEdgesByID(
|
||||
lnwire.GossipVersion1, chanID,
|
||||
)
|
||||
}
|
||||
|
||||
// ChannelView returns the verifiable edge information for each active channel.
|
||||
func (c *ChannelGraph) ChannelView() ([]EdgePoint, error) {
|
||||
return c.db.ChannelView()
|
||||
}
|
||||
|
||||
// IsZombieEdge returns whether the edge is considered zombie.
|
||||
func (c *ChannelGraph) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
|
||||
error) {
|
||||
|
||||
return c.db.IsZombieEdge(lnwire.GossipVersion1, chanID)
|
||||
}
|
||||
|
||||
// NumZombies returns the current number of zombie channels in the graph.
|
||||
func (c *ChannelGraph) NumZombies() (uint64, error) {
|
||||
return c.db.NumZombies()
|
||||
}
|
||||
|
||||
// PutClosedScid stores a SCID for a closed channel in the database.
|
||||
func (c *ChannelGraph) PutClosedScid(scid lnwire.ShortChannelID) error {
|
||||
return c.db.PutClosedScid(scid)
|
||||
}
|
||||
|
||||
// IsClosedScid checks whether a channel identified by the scid is closed.
|
||||
func (c *ChannelGraph) IsClosedScid(scid lnwire.ShortChannelID) (bool, error) {
|
||||
return c.db.IsClosedScid(scid)
|
||||
}
|
||||
|
||||
// SetSourceNode sets the source node within the graph database.
|
||||
func (c *ChannelGraph) SetSourceNode(ctx context.Context,
|
||||
node *models.Node) error {
|
||||
|
||||
return c.db.SetSourceNode(ctx, node)
|
||||
}
|
||||
|
||||
// PruneTip returns the block height and hash of the latest pruning block.
|
||||
func (c *ChannelGraph) PruneTip() (*chainhash.Hash, uint32, error) {
|
||||
return c.db.PruneTip()
|
||||
}
|
||||
|
||||
// VersionedGraph is a wrapper around ChannelGraph that will call underlying
|
||||
// Store methods with a specific gossip version.
|
||||
type VersionedGraph struct {
|
||||
*ChannelGraph
|
||||
v lnwire.GossipVersion
|
||||
}
|
||||
|
||||
// NewVersionedGraph creates a new VersionedGraph.
|
||||
func NewVersionedGraph(c *ChannelGraph,
|
||||
v lnwire.GossipVersion) *VersionedGraph {
|
||||
|
||||
return &VersionedGraph{
|
||||
ChannelGraph: c,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
|
||||
// FetchNode attempts to look up a target node by its identity public key.
|
||||
func (c *VersionedGraph) FetchNode(ctx context.Context,
|
||||
nodePub route.Vertex) (*models.Node, error) {
|
||||
|
||||
return c.db.FetchNode(ctx, c.v, nodePub)
|
||||
}
|
||||
|
||||
// FetchChannelEdgesByID attempts to lookup directed edges by channel ID.
|
||||
func (c *VersionedGraph) FetchChannelEdgesByID(chanID uint64) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
return c.db.FetchChannelEdgesByID(c.v, chanID)
|
||||
}
|
||||
|
||||
// FetchChannelEdgesByOutpoint attempts to lookup directed edges by funding
|
||||
// outpoint.
|
||||
func (c *VersionedGraph) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
return c.db.FetchChannelEdgesByOutpoint(c.v, op)
|
||||
}
|
||||
|
||||
// IsZombieEdge returns whether the edge is considered zombie for this version.
|
||||
func (c *VersionedGraph) IsZombieEdge(chanID uint64) (bool, [33]byte,
|
||||
[33]byte, error) {
|
||||
|
||||
return c.db.IsZombieEdge(c.v, chanID)
|
||||
}
|
||||
|
||||
// AddrsForNode returns all known addresses for the target node public key.
|
||||
func (c *VersionedGraph) AddrsForNode(ctx context.Context,
|
||||
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
|
||||
|
||||
return c.db.AddrsForNode(ctx, c.v, nodePub)
|
||||
}
|
||||
|
||||
// DeleteNode starts a new database transaction to remove a vertex/node
|
||||
// from the database according to the node's public key.
|
||||
func (c *VersionedGraph) DeleteNode(ctx context.Context,
|
||||
nodePub route.Vertex) error {
|
||||
|
||||
err := c.db.DeleteNode(ctx, c.v, nodePub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.graphCache != nil {
|
||||
c.graphCache.RemoveNode(nodePub)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasNode determines if the graph has a vertex identified by the target node
|
||||
// in the V1 graph.
|
||||
func (c *VersionedGraph) HasNode(ctx context.Context, nodePub [33]byte) (bool,
|
||||
error) {
|
||||
|
||||
return c.db.HasNode(ctx, c.v, nodePub)
|
||||
}
|
||||
|
||||
// LookupAlias attempts to return the alias as advertised by the target node.
|
||||
func (c *VersionedGraph) LookupAlias(ctx context.Context,
|
||||
pub *btcec.PublicKey) (string, error) {
|
||||
|
||||
return c.db.LookupAlias(ctx, c.v, pub)
|
||||
}
|
||||
|
||||
// SourceNode returns the source node of the graph.
|
||||
func (c *VersionedGraph) SourceNode(ctx context.Context) (*models.Node,
|
||||
error) {
|
||||
|
||||
return c.db.SourceNode(ctx, c.v)
|
||||
}
|
||||
|
||||
// DeleteChannelEdges removes edges with the given channel IDs from the
|
||||
// database and marks them as zombies. This ensures that we're unable to re-add
|
||||
// it to our database once again. If an edge does not exist within the
|
||||
// database, then ErrEdgeNotFound will be returned. If strictZombiePruning is
|
||||
// true, then when we mark these edges as zombies, we'll set up the keys such
|
||||
// that we require the node that failed to send the fresh update to be the one
|
||||
// that resurrects the channel from its zombie state. The markZombie bool
|
||||
// denotes whether to mark the channel as a zombie.
|
||||
func (c *VersionedGraph) DeleteChannelEdges(strictZombiePruning,
|
||||
markZombie bool, chanIDs ...uint64) error {
|
||||
|
||||
infos, err := c.db.DeleteChannelEdges(
|
||||
c.v, strictZombiePruning, markZombie, chanIDs...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.graphCache != nil {
|
||||
for _, info := range infos {
|
||||
c.graphCache.RemoveChannel(
|
||||
info.NodeKey1Bytes, info.NodeKey2Bytes,
|
||||
info.ChannelID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge with the
|
||||
// passed channel ID and this graph's gossip version, and false otherwise. If it
|
||||
// is not found, then the zombie index is checked and its result is returned as
|
||||
// the second boolean.
|
||||
func (c *VersionedGraph) HasChannelEdge(chanID uint64) (bool, bool, error) {
|
||||
return c.db.HasChannelEdge(c.v, chanID)
|
||||
}
|
||||
|
||||
// ForEachNodeChannel iterates through all channels of the given node.
|
||||
func (c *VersionedGraph) ForEachNodeChannel(ctx context.Context,
|
||||
nodePub route.Vertex, cb func(*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error {
|
||||
|
||||
return c.db.ForEachNodeChannel(ctx, c.v, nodePub, cb, reset)
|
||||
}
|
||||
|
||||
// ForEachChannel iterates through all channel edges stored within the graph.
|
||||
func (c *VersionedGraph) ForEachChannel(ctx context.Context,
|
||||
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error {
|
||||
|
||||
return c.db.ForEachChannel(ctx, c.v, cb, reset)
|
||||
}
|
||||
|
||||
// IsPublicNode determines whether the node is seen as public in the graph.
|
||||
func (c *VersionedGraph) IsPublicNode(pubKey [33]byte) (bool, error) {
|
||||
return c.db.IsPublicNode(c.v, pubKey)
|
||||
}
|
||||
|
||||
// MakeTestGraph creates a new instance of the ChannelGraph for testing
|
||||
// purposes. The backing V1Store implementation depends on the version of
|
||||
// purposes. The backing Store implementation depends on the version of
|
||||
// NewTestDB included in the current build.
|
||||
//
|
||||
// NOTE: this is currently unused, but is left here for future use to show how
|
||||
// NewTestDB can be used. As the SQL implementation of the V1Store is
|
||||
// NewTestDB can be used. As the SQL implementation of the Store is
|
||||
// implemented, unit tests will be switched to use this function instead of
|
||||
// the existing MakeTestGraph helper. Once only this function is used, the
|
||||
// existing MakeTestGraph function will be removed and this one will be renamed.
|
||||
|
||||
@@ -142,8 +142,8 @@ func (c *GraphCache) AddChannel(info *models.CachedEdgeInfo,
|
||||
// Skip adding policies if both are disabled, as the channel is
|
||||
// currently unusable for routing. However, we still add the channel
|
||||
// structure above so that policy updates can later enable it.
|
||||
if policy1 != nil && policy1.IsDisabled() &&
|
||||
policy2 != nil && policy2.IsDisabled() {
|
||||
if policy1 != nil && policy1.IsDisabled &&
|
||||
policy2 != nil && policy2.IsDisabled {
|
||||
|
||||
log.Debugf("Skipping policies for channel %v: both "+
|
||||
"policies are disabled (channel structure still "+
|
||||
@@ -156,14 +156,14 @@ func (c *GraphCache) AddChannel(info *models.CachedEdgeInfo,
|
||||
// of node 2 then we have the policy 1 as seen from node 1.
|
||||
if policy1 != nil {
|
||||
fromNode, toNode := info.NodeKey1Bytes, info.NodeKey2Bytes
|
||||
if !policy1.IsNode1() {
|
||||
if !policy1.IsNode1 {
|
||||
fromNode, toNode = toNode, fromNode
|
||||
}
|
||||
c.UpdatePolicy(policy1, fromNode, toNode)
|
||||
}
|
||||
if policy2 != nil {
|
||||
fromNode, toNode := info.NodeKey2Bytes, info.NodeKey1Bytes
|
||||
if policy2.IsNode1() {
|
||||
if policy2.IsNode1 {
|
||||
fromNode, toNode = toNode, fromNode
|
||||
}
|
||||
c.UpdatePolicy(policy2, fromNode, toNode)
|
||||
@@ -210,7 +210,7 @@ func (c *GraphCache) UpdatePolicy(policy *models.CachedEdgePolicy, fromNode,
|
||||
switch {
|
||||
// This is node 1, and it is edge 1, so this is the outgoing
|
||||
// policy for node 1.
|
||||
case channel.IsNode1 && policy.IsNode1():
|
||||
case channel.IsNode1 && policy.IsNode1:
|
||||
channel.OutPolicySet = true
|
||||
policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||
channel.InboundFee = fee
|
||||
@@ -218,7 +218,7 @@ func (c *GraphCache) UpdatePolicy(policy *models.CachedEdgePolicy, fromNode,
|
||||
|
||||
// This is node 2, and it is edge 2, so this is the outgoing
|
||||
// policy for node 2.
|
||||
case !channel.IsNode1 && !policy.IsNode1():
|
||||
case !channel.IsNode1 && !policy.IsNode1:
|
||||
channel.OutPolicySet = true
|
||||
policy.InboundFee.WhenSome(func(fee lnwire.Fee) {
|
||||
channel.InboundFee = fee
|
||||
|
||||
@@ -33,9 +33,9 @@ func TestGraphCacheAddNode(t *testing.T) {
|
||||
runTest := func(nodeA, nodeB route.Vertex) {
|
||||
t.Helper()
|
||||
|
||||
channelFlagA, channelFlagB := 0, 1
|
||||
isNode1A, isNode1B := true, false
|
||||
if nodeA == pubKey2 {
|
||||
channelFlagA, channelFlagB = 1, 0
|
||||
isNode1A, isNode1B = false, true
|
||||
}
|
||||
|
||||
inboundFee := lnwire.Fee{
|
||||
@@ -44,8 +44,9 @@ func TestGraphCacheAddNode(t *testing.T) {
|
||||
}
|
||||
|
||||
outPolicy1 := &models.CachedEdgePolicy{
|
||||
ChannelID: 1000,
|
||||
ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagA),
|
||||
ChannelID: 1000,
|
||||
IsNode1: isNode1A,
|
||||
IsDisabled: false,
|
||||
ToNodePubKey: func() route.Vertex {
|
||||
return nodeB
|
||||
},
|
||||
@@ -53,8 +54,9 @@ func TestGraphCacheAddNode(t *testing.T) {
|
||||
InboundFee: fn.Some(inboundFee),
|
||||
}
|
||||
inPolicy1 := &models.CachedEdgePolicy{
|
||||
ChannelID: 1000,
|
||||
ChannelFlags: lnwire.ChanUpdateChanFlags(channelFlagB),
|
||||
ChannelID: 1000,
|
||||
IsNode1: isNode1B,
|
||||
IsDisabled: false,
|
||||
ToNodePubKey: func() route.Vertex {
|
||||
return nodeA
|
||||
},
|
||||
@@ -125,8 +127,9 @@ func assertCachedPolicyEqual(t *testing.T, original,
|
||||
cached *models.CachedEdgePolicy) {
|
||||
|
||||
require.Equal(t, original.ChannelID, cached.ChannelID)
|
||||
require.Equal(t, original.MessageFlags, cached.MessageFlags)
|
||||
require.Equal(t, original.ChannelFlags, cached.ChannelFlags)
|
||||
require.Equal(t, original.HasMaxHTLC, cached.HasMaxHTLC)
|
||||
require.Equal(t, original.IsNode1, cached.IsNode1)
|
||||
require.Equal(t, original.IsDisabled, cached.IsDisabled)
|
||||
require.Equal(t, original.TimeLockDelta, cached.TimeLockDelta)
|
||||
require.Equal(t, original.MinHTLC, cached.MinHTLC)
|
||||
require.Equal(t, original.MaxHTLC, cached.MaxHTLC)
|
||||
@@ -171,13 +174,14 @@ func TestGraphCacheDisabledPoliciesRegression(t *testing.T) {
|
||||
|
||||
// Create two disabled policies.
|
||||
disabledPolicy1 := &models.CachedEdgePolicy{
|
||||
ChannelID: chanID,
|
||||
ChannelFlags: lnwire.ChanUpdateDisabled,
|
||||
ChannelID: chanID,
|
||||
IsNode1: true,
|
||||
IsDisabled: true,
|
||||
}
|
||||
disabledPolicy2 := &models.CachedEdgePolicy{
|
||||
ChannelID: chanID,
|
||||
ChannelFlags: lnwire.ChanUpdateDisabled |
|
||||
lnwire.ChanUpdateDirection,
|
||||
ChannelID: chanID,
|
||||
IsNode1: false,
|
||||
IsDisabled: true,
|
||||
}
|
||||
|
||||
// Add the channel with both policies disabled (simulating
|
||||
@@ -207,7 +211,8 @@ func TestGraphCacheDisabledPoliciesRegression(t *testing.T) {
|
||||
// Now simulate receiving a fresh update enabling one direction.
|
||||
enabledPolicy1 := &models.CachedEdgePolicy{
|
||||
ChannelID: chanID,
|
||||
ChannelFlags: 0, // NOT disabled anymore
|
||||
IsNode1: true,
|
||||
IsDisabled: false,
|
||||
TimeLockDelta: 40,
|
||||
MinHTLC: lnwire.MilliSatoshi(1000),
|
||||
}
|
||||
|
||||
+871
-523
File diff suppressed because it is too large.
Load diff
+57
-26
@@ -28,10 +28,17 @@ type NodeTraverser interface {
|
||||
FetchNodeFeatures(nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
||||
}
|
||||
|
||||
// V1Store represents the main interface for the channel graph database for all
|
||||
// Store represents the main interface for the channel graph database for all
|
||||
// channels and nodes gossiped via the V1 gossip protocol as defined in BOLT 7.
|
||||
type V1Store interface { //nolint:interfacebloat
|
||||
NodeTraverser
|
||||
type Store interface { //nolint:interfacebloat
|
||||
// ForEachNodeDirectedChannel calls the callback for every channel of
|
||||
// the given node.
|
||||
ForEachNodeDirectedChannel(nodePub route.Vertex,
|
||||
cb func(channel *DirectedChannel) error, reset func()) error
|
||||
|
||||
// FetchNodeFeatures returns the features of the given node.
|
||||
FetchNodeFeatures(v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) (*lnwire.FeatureVector, error)
|
||||
|
||||
// AddNode adds a vertex/node to the graph database. If the
|
||||
// node is not in the database from before, this will add a new,
|
||||
@@ -45,7 +52,7 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// AddrsForNode returns all known addresses for the target node public
|
||||
// key that the graph DB is aware of. The returned boolean indicates if
|
||||
// the given node is unknown to the graph DB or not.
|
||||
AddrsForNode(ctx context.Context,
|
||||
AddrsForNode(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub *btcec.PublicKey) (bool, []net.Addr, error)
|
||||
|
||||
// ForEachSourceNodeChannel iterates through all channels of the source
|
||||
@@ -66,8 +73,9 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
ForEachNodeChannel(ctx context.Context, nodePub route.Vertex,
|
||||
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
ForEachNodeChannel(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub route.Vertex, cb func(*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error
|
||||
|
||||
// ForEachNodeCached is similar to forEachNode, but it returns
|
||||
@@ -100,11 +108,13 @@ type V1Store interface { //nolint:interfacebloat
|
||||
|
||||
// LookupAlias attempts to return the alias as advertised by the target
|
||||
// node.
|
||||
LookupAlias(ctx context.Context, pub *btcec.PublicKey) (string, error)
|
||||
LookupAlias(ctx context.Context, v lnwire.GossipVersion,
|
||||
pub *btcec.PublicKey) (string, error)
|
||||
|
||||
// DeleteNode starts a new database transaction to remove a
|
||||
// vertex/node from the database according to the node's public key.
|
||||
DeleteNode(ctx context.Context, nodePub route.Vertex) error
|
||||
DeleteNode(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) error
|
||||
|
||||
// NodeUpdatesInHorizon returns all the known lightning node which have
|
||||
// an update timestamp within the passed range. This method can be used
|
||||
@@ -116,20 +126,28 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// FetchNode attempts to look up a target node by its identity
|
||||
// public key. If the node isn't found in the database, then
|
||||
// ErrGraphNodeNotFound is returned.
|
||||
FetchNode(ctx context.Context, nodePub route.Vertex) (*models.Node,
|
||||
FetchNode(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) (*models.Node, error)
|
||||
|
||||
// HasV1Node determines if the graph has a vertex identified by
|
||||
// the target node identity public key in the V1 graph. If the node
|
||||
// exists in the database, a timestamp of when the data for the node
|
||||
// was lasted updated is returned along with a true boolean. Otherwise,
|
||||
// an empty time.Time is returned with a false boolean.
|
||||
// This is specific to the V1 graph since only V1 node announcements
|
||||
// use timestamps for their latest update timestamp.
|
||||
HasV1Node(ctx context.Context, nodePub [33]byte) (time.Time, bool,
|
||||
error)
|
||||
|
||||
// HasNode determines if the graph has a vertex identified by
|
||||
// the target node identity public key. If the node exists in the
|
||||
// database, a timestamp of when the data for the node was lasted
|
||||
// updated is returned along with a true boolean. Otherwise, an empty
|
||||
// time.Time is returned with a false boolean.
|
||||
HasNode(ctx context.Context, nodePub [33]byte) (time.Time, bool, error)
|
||||
// the target node identity public key.
|
||||
HasNode(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub [33]byte) (bool, error)
|
||||
|
||||
// IsPublicNode is a helper method that determines whether the node with
|
||||
// the given public key is seen as a public node in the graph from the
|
||||
// graph's source node's point of view.
|
||||
IsPublicNode(pubKey [33]byte) (bool, error)
|
||||
IsPublicNode(v lnwire.GossipVersion, pubKey [33]byte) (bool, error)
|
||||
|
||||
// GraphSession will provide the call-back with access to a
|
||||
// NodeTraverser instance which can be used to perform queries against
|
||||
@@ -145,9 +163,12 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// NOTE: If an edge can't be found, or wasn't advertised, then a nil
|
||||
// pointer for that particular channel edge routing policy will be
|
||||
// passed into the callback.
|
||||
ForEachChannel(ctx context.Context, cb func(*models.ChannelEdgeInfo,
|
||||
*models.ChannelEdgePolicy, *models.ChannelEdgePolicy) error,
|
||||
reset func()) error
|
||||
//
|
||||
// TODO(elle): add a cross-version iteration API and make this iterate
|
||||
// over all versions.
|
||||
ForEachChannel(ctx context.Context, v lnwire.GossipVersion,
|
||||
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error
|
||||
|
||||
// ForEachChannelCacheable iterates through all the channel edges stored
|
||||
// within the graph and invokes the passed callback for each edge. The
|
||||
@@ -180,13 +201,20 @@ type V1Store interface { //nolint:interfacebloat
|
||||
AddChannelEdge(ctx context.Context, edge *models.ChannelEdgeInfo,
|
||||
op ...batch.SchedulerOption) error
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge
|
||||
// HasV1ChannelEdge returns true if the database knows of a channel edge
|
||||
// with the passed channel ID, and false otherwise. If an edge with that
|
||||
// ID is found within the graph, then two time stamps representing the
|
||||
// last time the edge was updated for both directed edges are returned
|
||||
// along with the boolean. If it is not found, then the zombie index is
|
||||
// checked and its result is returned as the second boolean.
|
||||
HasChannelEdge(chanID uint64) (time.Time, time.Time, bool, bool,
|
||||
HasV1ChannelEdge(chanID uint64) (time.Time, time.Time, bool, bool,
|
||||
error)
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge
|
||||
// with the passed channel ID and gossip version, and false otherwise.
|
||||
// If it is not found, then the zombie index is checked and its result
|
||||
// is returned as the second boolean.
|
||||
HasChannelEdge(v lnwire.GossipVersion, chanID uint64) (bool, bool,
|
||||
error)
|
||||
|
||||
// DeleteChannelEdges removes edges with the given channel IDs from the
|
||||
@@ -198,8 +226,9 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// failed to send the fresh update to be the one that resurrects the
|
||||
// channel from its zombie state. The markZombie bool denotes whether
|
||||
// to mark the channel as a zombie.
|
||||
DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
chanIDs ...uint64) ([]*models.ChannelEdgeInfo, error)
|
||||
DeleteChannelEdges(v lnwire.GossipVersion, strictZombiePruning,
|
||||
markZombie bool, chanIDs ...uint64) (
|
||||
[]*models.ChannelEdgeInfo, error)
|
||||
|
||||
// AddEdgeProof sets the proof of an existing edge in the graph
|
||||
// database.
|
||||
@@ -258,7 +287,7 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// houses the general information for the channel itself is returned as
|
||||
// well as two structs that contain the routing policies for the channel
|
||||
// in either direction.
|
||||
FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
FetchChannelEdgesByOutpoint(v lnwire.GossipVersion, op *wire.OutPoint) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error)
|
||||
|
||||
@@ -273,7 +302,7 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// zombie within the database. In this case, the ChannelEdgePolicy's
|
||||
// will be nil, and the ChannelEdgeInfo will only include the public
|
||||
// keys of each node.
|
||||
FetchChannelEdgesByID(chanID uint64) (
|
||||
FetchChannelEdgesByID(v lnwire.GossipVersion, chanID uint64) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error)
|
||||
|
||||
@@ -296,7 +325,8 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// IsZombieEdge returns whether the edge is considered zombie. If it is
|
||||
// a zombie, then the two node public keys corresponding to this edge
|
||||
// are also returned.
|
||||
IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte, error)
|
||||
IsZombieEdge(v lnwire.GossipVersion, chanID uint64) (bool, [33]byte,
|
||||
[33]byte, error)
|
||||
|
||||
// NumZombies returns the current number of zombie channels in the
|
||||
// graph.
|
||||
@@ -327,7 +357,8 @@ type V1Store interface { //nolint:interfacebloat
|
||||
// treated as the center node within a star-graph. This method may be
|
||||
// used to kick off a path finding algorithm in order to explore the
|
||||
// reachability of another node based off the source node.
|
||||
SourceNode(ctx context.Context) (*models.Node, error)
|
||||
SourceNode(ctx context.Context, v lnwire.GossipVersion) (*models.Node,
|
||||
error)
|
||||
|
||||
// SetSourceNode sets the source node within the graph database. The
|
||||
// source node is to be used as the center of a star-graph within path
|
||||
|
||||
+264
-73
@@ -164,6 +164,11 @@ var (
|
||||
//
|
||||
// maps: scid -> []byte{}
|
||||
closedScidBucket = []byte("closed-scid")
|
||||
|
||||
// ErrVersionNotSupportedForKVDB is returned with KVStore queries are
|
||||
// made using a gossip version other than V1.
|
||||
ErrVersionNotSupportedForKVDB = errors.New("only gossip v1 is " +
|
||||
"supported for kvdb graph store")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -199,8 +204,8 @@ type KVStore struct {
|
||||
}
|
||||
|
||||
// A compile-time assertion to ensure that the KVStore struct implements the
|
||||
// V1Store interface.
|
||||
var _ V1Store = (*KVStore)(nil)
|
||||
// Store interface.
|
||||
var _ Store = (*KVStore)(nil)
|
||||
|
||||
// NewKVStore allocates a new KVStore backed by a DB instance. The
|
||||
// returned instance has its own unique reject cache and channel cache.
|
||||
@@ -376,7 +381,7 @@ func initKVStore(db kvdb.Backend) error {
|
||||
// unknown to the graph DB or not.
|
||||
//
|
||||
// NOTE: this is part of the channeldb.AddrSource interface.
|
||||
func (c *KVStore) AddrsForNode(ctx context.Context,
|
||||
func (c *KVStore) AddrsForNode(ctx context.Context, v lnwire.GossipVersion,
|
||||
nodePub *btcec.PublicKey) (bool, []net.Addr, error) {
|
||||
|
||||
pubKey, err := route.NewVertexFromBytes(nodePub.SerializeCompressed())
|
||||
@@ -384,7 +389,7 @@ func (c *KVStore) AddrsForNode(ctx context.Context,
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
node, err := c.FetchNode(ctx, pubKey)
|
||||
node, err := c.FetchNode(ctx, v, pubKey)
|
||||
// We don't consider it an error if the graph is unaware of the node.
|
||||
switch {
|
||||
case err != nil && !errors.Is(err, ErrGraphNodeNotFound):
|
||||
@@ -406,10 +411,14 @@ func (c *KVStore) AddrsForNode(ctx context.Context,
|
||||
// NOTE: If an edge can't be found, or wasn't advertised, then a nil pointer
|
||||
// for that particular channel edge routing policy will be passed into the
|
||||
// callback.
|
||||
func (c *KVStore) ForEachChannel(_ context.Context,
|
||||
func (c *KVStore) ForEachChannel(_ context.Context, v lnwire.GossipVersion,
|
||||
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
return forEachChannel(c.db, cb, reset)
|
||||
}
|
||||
|
||||
@@ -666,8 +675,12 @@ func (c *KVStore) ForEachNodeDirectedChannel(nodePub route.Vertex,
|
||||
// known for the node, an empty feature vector is returned.
|
||||
//
|
||||
// NOTE: this is part of the graphdb.NodeTraverser interface.
|
||||
func (c *KVStore) FetchNodeFeatures(nodePub route.Vertex) (
|
||||
*lnwire.FeatureVector, error) {
|
||||
func (c *KVStore) FetchNodeFeatures(v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) (*lnwire.FeatureVector, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
return c.fetchNodeFeatures(nil, nodePub)
|
||||
}
|
||||
@@ -804,7 +817,7 @@ func (c *KVStore) DisabledChannelIDs() ([]uint64, error) {
|
||||
// returns an error, then the transaction is aborted and the iteration stops
|
||||
// early.
|
||||
//
|
||||
// NOTE: this is part of the V1Store interface.
|
||||
// NOTE: this is part of the Store interface.
|
||||
func (c *KVStore) ForEachNode(_ context.Context,
|
||||
cb func(*models.Node) error, reset func()) error {
|
||||
|
||||
@@ -901,7 +914,13 @@ func (c *KVStore) ForEachNodeCacheable(_ context.Context,
|
||||
// as the center node within a star-graph. This method may be used to kick off
|
||||
// a path finding algorithm in order to explore the reachability of another
|
||||
// node based off the source node.
|
||||
func (c *KVStore) SourceNode(_ context.Context) (*models.Node, error) {
|
||||
func (c *KVStore) SourceNode(_ context.Context,
|
||||
v lnwire.GossipVersion) (*models.Node, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
return sourceNode(c.db)
|
||||
}
|
||||
|
||||
@@ -955,6 +974,10 @@ func sourceNodeWithTx(nodes kvdb.RBucket) (*models.Node, error) {
|
||||
func (c *KVStore) SetSourceNode(_ context.Context,
|
||||
node *models.Node) error {
|
||||
|
||||
if node.Version != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
nodePubBytes := node.PubKeyBytes[:]
|
||||
|
||||
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
||||
@@ -1021,9 +1044,13 @@ func addLightningNode(tx kvdb.RwTx, node *models.Node) error {
|
||||
|
||||
// LookupAlias attempts to return the alias as advertised by the target node.
|
||||
// TODO(roasbeef): currently assumes that aliases are unique...
|
||||
func (c *KVStore) LookupAlias(_ context.Context,
|
||||
func (c *KVStore) LookupAlias(_ context.Context, v lnwire.GossipVersion,
|
||||
pub *btcec.PublicKey) (string, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return "", ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
var alias string
|
||||
|
||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||
@@ -1060,9 +1087,13 @@ func (c *KVStore) LookupAlias(_ context.Context,
|
||||
|
||||
// DeleteNode starts a new database transaction to remove a vertex/node
|
||||
// from the database according to the node's public key.
|
||||
func (c *KVStore) DeleteNode(_ context.Context,
|
||||
func (c *KVStore) DeleteNode(_ context.Context, v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) error {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
// TODO(roasbeef): ensure dangling edges are removed...
|
||||
return kvdb.Update(c.db, func(tx kvdb.RwTx) error {
|
||||
nodes := tx.ReadWriteBucket(nodeBucket)
|
||||
@@ -1152,8 +1183,11 @@ func (c *KVStore) AddChannelEdge(ctx context.Context,
|
||||
case alreadyExists:
|
||||
return ErrEdgeAlreadyExist
|
||||
default:
|
||||
c.rejectCache.remove(edge.ChannelID)
|
||||
c.rejectCache.remove(
|
||||
lnwire.GossipVersion1, edge.ChannelID,
|
||||
)
|
||||
c.chanCache.remove(edge.ChannelID)
|
||||
|
||||
return nil
|
||||
}
|
||||
},
|
||||
@@ -1235,9 +1269,9 @@ func (c *KVStore) addChannelEdge(tx kvdb.RwTx,
|
||||
|
||||
// Mark edge policies for both sides as unknown. This is to enable
|
||||
// efficient incoming channel lookup for a node.
|
||||
keys := []*[33]byte{
|
||||
&edge.NodeKey1Bytes,
|
||||
&edge.NodeKey2Bytes,
|
||||
keys := []route.Vertex{
|
||||
edge.NodeKey1Bytes,
|
||||
edge.NodeKey2Bytes,
|
||||
}
|
||||
for _, key := range keys {
|
||||
err := putChanEdgePolicyUnknown(edges, edge.ChannelID, key[:])
|
||||
@@ -1256,13 +1290,13 @@ func (c *KVStore) addChannelEdge(tx kvdb.RwTx,
|
||||
return chanIndex.Put(b.Bytes(), chanKey[:])
|
||||
}
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge with the
|
||||
// passed channel ID, and false otherwise. If an edge with that ID is found
|
||||
// within the graph, then two time stamps representing the last time the edge
|
||||
// was updated for both directed edges are returned along with the boolean. If
|
||||
// it is not found, then the zombie index is checked and its result is returned
|
||||
// as the second boolean.
|
||||
func (c *KVStore) HasChannelEdge(
|
||||
// HasV1ChannelEdge returns true if the database knows of a channel edge
|
||||
// with the passed channel ID, and false otherwise. If an edge with that ID
|
||||
// is found within the graph, then two time stamps representing the last time
|
||||
// the edge was updated for both directed edges are returned along with the
|
||||
// boolean. If it is not found, then the zombie index is checked and its
|
||||
// result is returned as the second boolean.
|
||||
func (c *KVStore) HasV1ChannelEdge(
|
||||
chanID uint64) (time.Time, time.Time, bool, bool, error) {
|
||||
|
||||
var (
|
||||
@@ -1275,7 +1309,7 @@ func (c *KVStore) HasChannelEdge(
|
||||
// We'll query the cache with the shared lock held to allow multiple
|
||||
// readers to access values in the cache concurrently if they exist.
|
||||
c.cacheMu.RLock()
|
||||
if entry, ok := c.rejectCache.get(chanID); ok {
|
||||
if entry, ok := c.rejectCache.get(lnwire.GossipVersion1, chanID); ok {
|
||||
c.cacheMu.RUnlock()
|
||||
upd1Time = time.Unix(entry.upd1Time, 0)
|
||||
upd2Time = time.Unix(entry.upd2Time, 0)
|
||||
@@ -1291,7 +1325,7 @@ func (c *KVStore) HasChannelEdge(
|
||||
// The item was not found with the shared lock, so we'll acquire the
|
||||
// exclusive lock and check the cache again in case another method added
|
||||
// the entry to the cache while no lock was held.
|
||||
if entry, ok := c.rejectCache.get(chanID); ok {
|
||||
if entry, ok := c.rejectCache.get(lnwire.GossipVersion1, chanID); ok {
|
||||
upd1Time = time.Unix(entry.upd1Time, 0)
|
||||
upd2Time = time.Unix(entry.upd2Time, 0)
|
||||
exists, isZombie = entry.flags.unpack()
|
||||
@@ -1358,7 +1392,7 @@ func (c *KVStore) HasChannelEdge(
|
||||
return time.Time{}, time.Time{}, exists, isZombie, err
|
||||
}
|
||||
|
||||
c.rejectCache.insert(chanID, rejectCacheEntry{
|
||||
c.rejectCache.insert(lnwire.GossipVersion1, chanID, rejectCacheEntry{
|
||||
upd1Time: upd1Time.Unix(),
|
||||
upd2Time: upd2Time.Unix(),
|
||||
flags: packRejectFlags(exists, isZombie),
|
||||
@@ -1367,10 +1401,32 @@ func (c *KVStore) HasChannelEdge(
|
||||
return upd1Time, upd2Time, exists, isZombie, nil
|
||||
}
|
||||
|
||||
// HasChannelEdge returns true if the database knows of a channel edge with the
|
||||
// passed channel ID and gossip version, and false otherwise. If it is not
|
||||
// found, then the zombie index is checked and its result is returned as the
|
||||
// second boolean.
|
||||
func (c *KVStore) HasChannelEdge(v lnwire.GossipVersion,
|
||||
chanID uint64) (bool, bool, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return false, false, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
_, _, exists, isZombie, err := c.HasV1ChannelEdge(chanID)
|
||||
|
||||
return exists, isZombie, err
|
||||
}
|
||||
|
||||
// AddEdgeProof sets the proof of an existing edge in the graph database.
|
||||
func (c *KVStore) AddEdgeProof(chanID lnwire.ShortChannelID,
|
||||
proof *models.ChannelAuthProof) error {
|
||||
|
||||
// We only support v1 channel proofs in the KVStore.
|
||||
if proof.Version != lnwire.GossipVersion1 {
|
||||
return fmt.Errorf("only v1 channel proofs supported, got v%d",
|
||||
proof.Version)
|
||||
}
|
||||
|
||||
// Construct the channel's primary key which is the 8-byte channel ID.
|
||||
var chanKey [8]byte
|
||||
binary.BigEndian.PutUint64(chanKey[:], chanID.ToUint64())
|
||||
@@ -1531,7 +1587,7 @@ func (c *KVStore) PruneGraph(spentOutputs []*wire.OutPoint,
|
||||
}
|
||||
|
||||
for _, channel := range chansClosed {
|
||||
c.rejectCache.remove(channel.ChannelID)
|
||||
c.rejectCache.remove(lnwire.GossipVersion1, channel.ChannelID)
|
||||
c.chanCache.remove(channel.ChannelID)
|
||||
}
|
||||
|
||||
@@ -1798,7 +1854,7 @@ func (c *KVStore) DisconnectBlockAtHeight(height uint32) (
|
||||
}
|
||||
|
||||
for _, channel := range removedChans {
|
||||
c.rejectCache.remove(channel.ChannelID)
|
||||
c.rejectCache.remove(lnwire.GossipVersion1, channel.ChannelID)
|
||||
c.chanCache.remove(channel.ChannelID)
|
||||
}
|
||||
|
||||
@@ -1856,8 +1912,13 @@ func (c *KVStore) PruneTip() (*chainhash.Hash, uint32, error) {
|
||||
// that we require the node that failed to send the fresh update to be the one
|
||||
// that resurrects the channel from its zombie state. The markZombie bool
|
||||
// denotes whether or not to mark the channel as a zombie.
|
||||
func (c *KVStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
chanIDs ...uint64) ([]*models.ChannelEdgeInfo, error) {
|
||||
func (c *KVStore) DeleteChannelEdges(v lnwire.GossipVersion,
|
||||
strictZombiePruning, markZombie bool, chanIDs ...uint64) (
|
||||
[]*models.ChannelEdgeInfo, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
// TODO(roasbeef): possibly delete from node bucket if node has no more
|
||||
// channels
|
||||
@@ -1912,7 +1973,7 @@ func (c *KVStore) DeleteChannelEdges(strictZombiePruning, markZombie bool,
|
||||
}
|
||||
|
||||
for _, chanID := range chanIDs {
|
||||
c.rejectCache.remove(chanID)
|
||||
c.rejectCache.remove(lnwire.GossipVersion1, chanID)
|
||||
c.chanCache.remove(chanID)
|
||||
}
|
||||
|
||||
@@ -3227,13 +3288,14 @@ func (c *KVStore) updateEdgeCache(e *models.ChannelEdgePolicy,
|
||||
// the entry with the updated timestamp for the direction that was just
|
||||
// written. If the edge doesn't exist, we'll load the cache entry lazily
|
||||
// during the next query for this edge.
|
||||
if entry, ok := c.rejectCache.get(e.ChannelID); ok {
|
||||
entry, ok := c.rejectCache.get(lnwire.GossipVersion1, e.ChannelID)
|
||||
if ok {
|
||||
if isUpdate1 {
|
||||
entry.upd1Time = e.LastUpdate.Unix()
|
||||
} else {
|
||||
entry.upd2Time = e.LastUpdate.Unix()
|
||||
}
|
||||
c.rejectCache.insert(e.ChannelID, entry)
|
||||
c.rejectCache.insert(lnwire.GossipVersion1, e.ChannelID, entry)
|
||||
}
|
||||
|
||||
// If an entry for this channel is found in channel cache, we'll modify
|
||||
@@ -3258,6 +3320,9 @@ func updateEdgePolicy(tx kvdb.RwTx, edge *models.ChannelEdgePolicy) (
|
||||
route.Vertex, route.Vertex, bool, error) {
|
||||
|
||||
var noVertex route.Vertex
|
||||
if edge.Version != lnwire.GossipVersion1 {
|
||||
return noVertex, noVertex, false, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
edges := tx.ReadWriteBucket(edgeBucket)
|
||||
if edges == nil {
|
||||
@@ -3339,8 +3404,11 @@ func (c *KVStore) isPublic(tx kvdb.RTx, nodePub route.Vertex,
|
||||
}
|
||||
|
||||
// Since the edge _does_ extend to the source node, we'll also
|
||||
// need to ensure that this is a public edge.
|
||||
if info.AuthProof != nil {
|
||||
// need to ensure that this is a public edge with valid
|
||||
// signatures (not empty).
|
||||
if info.AuthProof != nil && !info.AuthProof.IsEmpty() &&
|
||||
len(info.AuthProof.BitcoinSig1()) > 0 {
|
||||
|
||||
nodeIsPublic = true
|
||||
return errDone
|
||||
}
|
||||
@@ -3370,9 +3438,13 @@ func (c *KVStore) fetchNodeTx(tx kvdb.RTx, nodePub route.Vertex) (*models.Node,
|
||||
// FetchNode attempts to look up a target node by its identity public
|
||||
// key. If the node isn't found in the database, then ErrGraphNodeNotFound is
|
||||
// returned.
|
||||
func (c *KVStore) FetchNode(_ context.Context,
|
||||
func (c *KVStore) FetchNode(_ context.Context, v lnwire.GossipVersion,
|
||||
nodePub route.Vertex) (*models.Node, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
return c.fetchLightningNode(nil, nodePub)
|
||||
}
|
||||
|
||||
@@ -3433,11 +3505,12 @@ func (c *KVStore) fetchLightningNode(tx kvdb.RTx,
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// HasNode determines if the graph has a vertex identified by the target node
|
||||
// identity public key. If the node exists in the database, a timestamp of when
|
||||
// the data for the node was lasted updated is returned along with a true
|
||||
// boolean. Otherwise, an empty time.Time is returned with a false boolean.
|
||||
func (c *KVStore) HasNode(_ context.Context,
|
||||
// HasV1Node determines if the graph has a vertex identified by the
|
||||
// target node identity public key. If the node exists in the database, a
|
||||
// timestamp of when the data for the node was lasted updated is returned along
|
||||
// with a true boolean. Otherwise, an empty time.Time is returned with a false
|
||||
// boolean.
|
||||
func (c *KVStore) HasV1Node(_ context.Context,
|
||||
nodePub [33]byte) (time.Time, bool, error) {
|
||||
|
||||
var (
|
||||
@@ -3457,7 +3530,6 @@ func (c *KVStore) HasNode(_ context.Context,
|
||||
// exit early.
|
||||
nodeBytes := nodes.Get(nodePub[:])
|
||||
if nodeBytes == nil {
|
||||
exists = false
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3485,6 +3557,44 @@ func (c *KVStore) HasNode(_ context.Context,
|
||||
return updateTime, exists, nil
|
||||
}
|
||||
|
||||
// HasNode determines if the graph has a vertex identified by the target node
|
||||
// identity public key.
|
||||
func (c *KVStore) HasNode(_ context.Context, v lnwire.GossipVersion,
|
||||
nodePub [33]byte) (bool, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return false, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
var exists bool
|
||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||
// First grab the nodes bucket which stores the mapping from
|
||||
// pubKey to node information.
|
||||
nodes := tx.ReadBucket(nodeBucket)
|
||||
if nodes == nil {
|
||||
return ErrGraphNotFound
|
||||
}
|
||||
|
||||
// If a key for this serialized public key isn't found, we can
|
||||
// exit early.
|
||||
nodeBytes := nodes.Get(nodePub[:])
|
||||
if nodeBytes == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
exists = true
|
||||
|
||||
return nil
|
||||
}, func() {
|
||||
exists = false
|
||||
})
|
||||
if err != nil {
|
||||
return exists, err
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// nodeTraversal is used to traverse all channels of a node given by its
|
||||
// public key and passes channel information into the specified callback.
|
||||
//
|
||||
@@ -3580,10 +3690,15 @@ func nodeTraversal(tx kvdb.RTx, nodePub []byte, db kvdb.Backend,
|
||||
// halted with the error propagated back up to the caller.
|
||||
//
|
||||
// Unknown policies are passed into the callback as nil values.
|
||||
func (c *KVStore) ForEachNodeChannel(_ context.Context, nodePub route.Vertex,
|
||||
func (c *KVStore) ForEachNodeChannel(_ context.Context,
|
||||
v lnwire.GossipVersion, nodePub route.Vertex,
|
||||
cb func(*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy) error, reset func()) error {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
return nodeTraversal(
|
||||
nil, nodePub[:], c.db, func(_ kvdb.RTx,
|
||||
info *models.ChannelEdgeInfo, policy,
|
||||
@@ -3731,8 +3846,8 @@ func computeEdgePolicyKeys(info *models.ChannelEdgeInfo) ([]byte, []byte) {
|
||||
// found, then ErrEdgeNotFound is returned. A struct which houses the general
|
||||
// information for the channel itself is returned as well as two structs that
|
||||
// contain the routing policies for the channel in either direction.
|
||||
func (c *KVStore) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
func (c *KVStore) FetchChannelEdgesByOutpoint(v lnwire.GossipVersion,
|
||||
op *wire.OutPoint) (*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
var (
|
||||
@@ -3741,6 +3856,10 @@ func (c *KVStore) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
policy2 *models.ChannelEdgePolicy
|
||||
)
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, nil, nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||
// First, grab the node bucket. This will be used to populate
|
||||
// the Node pointers in each edge read from disk.
|
||||
@@ -3817,10 +3936,14 @@ func (c *KVStore) FetchChannelEdgesByOutpoint(op *wire.OutPoint) (
|
||||
// ErrZombieEdge an be returned if the edge is currently marked as a zombie
|
||||
// within the database. In this case, the ChannelEdgePolicy's will be nil, and
|
||||
// the ChannelEdgeInfo will only include the public keys of each node.
|
||||
func (c *KVStore) FetchChannelEdgesByID(chanID uint64) (
|
||||
*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
func (c *KVStore) FetchChannelEdgesByID(v lnwire.GossipVersion,
|
||||
chanID uint64) (*models.ChannelEdgeInfo, *models.ChannelEdgePolicy,
|
||||
*models.ChannelEdgePolicy, error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return nil, nil, nil, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
var (
|
||||
edgeInfo *models.ChannelEdgeInfo
|
||||
policy1 *models.ChannelEdgePolicy
|
||||
@@ -3875,10 +3998,14 @@ func (c *KVStore) FetchChannelEdgesByID(chanID uint64) (
|
||||
// populate the edge info with the public keys of each
|
||||
// party as this is the only information we have about
|
||||
// it and return an error signaling so.
|
||||
edgeInfo = &models.ChannelEdgeInfo{
|
||||
NodeKey1Bytes: pubKey1,
|
||||
NodeKey2Bytes: pubKey2,
|
||||
zombieEdge, err := models.NewV1Channel(
|
||||
0, chainhash.Hash{}, pubKey1, pubKey2,
|
||||
&models.ChannelV1Fields{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
edgeInfo = zombieEdge
|
||||
|
||||
return ErrZombieEdge
|
||||
}
|
||||
@@ -3921,7 +4048,13 @@ func (c *KVStore) FetchChannelEdgesByID(chanID uint64) (
|
||||
// IsPublicNode is a helper method that determines whether the node with the
|
||||
// given public key is seen as a public node in the graph from the graph's
|
||||
// source node's point of view.
|
||||
func (c *KVStore) IsPublicNode(pubKey [33]byte) (bool, error) {
|
||||
func (c *KVStore) IsPublicNode(v lnwire.GossipVersion, pubKey [33]byte) (bool,
|
||||
error) {
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return false, ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
var nodeIsPublic bool
|
||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||
nodes := tx.ReadBucket(nodeBucket)
|
||||
@@ -4033,10 +4166,7 @@ func (c *KVStore) ChannelView() ([]EdgePoint, error) {
|
||||
return err
|
||||
}
|
||||
|
||||
pkScript, err := genMultiSigP2WSH(
|
||||
edgeInfo.BitcoinKey1Bytes[:],
|
||||
edgeInfo.BitcoinKey2Bytes[:],
|
||||
)
|
||||
pkScript, err := edgeInfo.FundingPKScript()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -4084,7 +4214,7 @@ func (c *KVStore) MarkEdgeZombie(chanID uint64,
|
||||
return err
|
||||
}
|
||||
|
||||
c.rejectCache.remove(chanID)
|
||||
c.rejectCache.remove(lnwire.GossipVersion1, chanID)
|
||||
c.chanCache.remove(chanID)
|
||||
|
||||
return nil
|
||||
@@ -4153,7 +4283,7 @@ func (c *KVStore) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.rejectCache.remove(chanID)
|
||||
c.rejectCache.remove(lnwire.GossipVersion1, chanID)
|
||||
c.chanCache.remove(chanID)
|
||||
|
||||
return nil
|
||||
@@ -4162,14 +4292,19 @@ func (c *KVStore) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
|
||||
// IsZombieEdge returns whether the edge is considered zombie. If it is a
|
||||
// zombie, then the two node public keys corresponding to this edge are also
|
||||
// returned.
|
||||
func (c *KVStore) IsZombieEdge(chanID uint64) (bool, [33]byte, [33]byte,
|
||||
error) {
|
||||
func (c *KVStore) IsZombieEdge(v lnwire.GossipVersion,
|
||||
chanID uint64) (bool, [33]byte, [33]byte, error) {
|
||||
|
||||
var (
|
||||
isZombie bool
|
||||
pubKey1, pubKey2 [33]byte
|
||||
)
|
||||
|
||||
if v != lnwire.GossipVersion1 {
|
||||
return false, [33]byte{}, [33]byte{},
|
||||
ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
err := kvdb.View(c.db, func(tx kvdb.RTx) error {
|
||||
edges := tx.ReadBucket(edgeBucket)
|
||||
if edges == nil {
|
||||
@@ -4334,6 +4469,10 @@ func (c *nodeTraverserSession) FetchNodeFeatures(nodePub route.Vertex) (
|
||||
func putLightningNode(nodeBucket, aliasBucket, updateIndex kvdb.RwBucket,
|
||||
node *models.Node) error {
|
||||
|
||||
if node.Version != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
var (
|
||||
scratch [16]byte
|
||||
b bytes.Buffer
|
||||
@@ -4631,6 +4770,12 @@ func deserializeLightningNode(r io.Reader) (*models.Node, error) {
|
||||
func putChanEdgeInfo(edgeIndex kvdb.RwBucket,
|
||||
edgeInfo *models.ChannelEdgeInfo, chanID [8]byte) error {
|
||||
|
||||
// We only support V1 channel edges in the KV store.
|
||||
if edgeInfo.Version != lnwire.GossipVersion1 {
|
||||
return fmt.Errorf("only V1 channel edges supported, got V%d",
|
||||
edgeInfo.Version)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
if _, err := b.Write(edgeInfo.NodeKey1Bytes[:]); err != nil {
|
||||
@@ -4639,10 +4784,24 @@ func putChanEdgeInfo(edgeIndex kvdb.RwBucket,
|
||||
if _, err := b.Write(edgeInfo.NodeKey2Bytes[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := b.Write(edgeInfo.BitcoinKey1Bytes[:]); err != nil {
|
||||
|
||||
btc1Key, err := edgeInfo.BitcoinKey1Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("edge missing bitcoin key 1"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := b.Write(edgeInfo.BitcoinKey2Bytes[:]); err != nil {
|
||||
btc2Key, err := edgeInfo.BitcoinKey2Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("edge missing bitcoin key 2"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := b.Write(btc1Key[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := b.Write(btc2Key[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4658,10 +4817,10 @@ func putChanEdgeInfo(edgeIndex kvdb.RwBucket,
|
||||
authProof := edgeInfo.AuthProof
|
||||
var nodeSig1, nodeSig2, bitcoinSig1, bitcoinSig2 []byte
|
||||
if authProof != nil {
|
||||
nodeSig1 = authProof.NodeSig1Bytes
|
||||
nodeSig2 = authProof.NodeSig2Bytes
|
||||
bitcoinSig1 = authProof.BitcoinSig1Bytes
|
||||
bitcoinSig2 = authProof.BitcoinSig2Bytes
|
||||
nodeSig1 = authProof.NodeSig1()
|
||||
nodeSig2 = authProof.NodeSig2()
|
||||
bitcoinSig1 = authProof.BitcoinSig1()
|
||||
bitcoinSig2 = authProof.BitcoinSig2()
|
||||
}
|
||||
|
||||
if err := wire.WriteVarBytes(&b, 0, nodeSig1); err != nil {
|
||||
@@ -4680,7 +4839,7 @@ func putChanEdgeInfo(edgeIndex kvdb.RwBucket,
|
||||
if err := WriteOutpoint(&b, &edgeInfo.ChannelPoint); err != nil {
|
||||
return err
|
||||
}
|
||||
err := binary.Write(&b, byteOrder, uint64(edgeInfo.Capacity))
|
||||
err = binary.Write(&b, byteOrder, uint64(edgeInfo.Capacity))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -4793,18 +4952,26 @@ func deserializeChanEdgeInfo(r io.Reader) (*models.ChannelEdgeInfo, error) {
|
||||
edgeInfo models.ChannelEdgeInfo
|
||||
)
|
||||
|
||||
// All channel edges in the KV store are V1.
|
||||
edgeInfo.Version = lnwire.GossipVersion1
|
||||
|
||||
if _, err := io.ReadFull(r, edgeInfo.NodeKey1Bytes[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, edgeInfo.NodeKey2Bytes[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, edgeInfo.BitcoinKey1Bytes[:]); err != nil {
|
||||
|
||||
var btcKey1, btcKey2 route.Vertex
|
||||
if _, err := io.ReadFull(r, btcKey1[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, edgeInfo.BitcoinKey2Bytes[:]); err != nil {
|
||||
edgeInfo.BitcoinKey1Bytes = fn.Some(btcKey1)
|
||||
|
||||
if _, err := io.ReadFull(r, btcKey2[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
edgeInfo.BitcoinKey2Bytes = fn.Some(btcKey2)
|
||||
|
||||
featureBytes, err := wire.ReadVarBytes(r, 0, 900, "features")
|
||||
if err != nil {
|
||||
@@ -4816,24 +4983,42 @@ func deserializeChanEdgeInfo(r io.Reader) (*models.ChannelEdgeInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proof := &models.ChannelAuthProof{}
|
||||
proof := &models.ChannelAuthProof{
|
||||
// KV store always uses v1.
|
||||
Version: lnwire.GossipVersion1,
|
||||
}
|
||||
|
||||
proof.NodeSig1Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
nodeSig1, err := wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proof.NodeSig2Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if len(nodeSig1) > 0 {
|
||||
proof.NodeSig1Bytes = fn.Some(nodeSig1)
|
||||
}
|
||||
|
||||
nodeSig2, err := wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proof.BitcoinSig1Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if len(nodeSig2) > 0 {
|
||||
proof.NodeSig2Bytes = fn.Some(nodeSig2)
|
||||
}
|
||||
|
||||
bitcoinSig1, err := wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
proof.BitcoinSig2Bytes, err = wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if len(bitcoinSig1) > 0 {
|
||||
proof.BitcoinSig1Bytes = fn.Some(bitcoinSig1)
|
||||
}
|
||||
|
||||
bitcoinSig2, err := wire.ReadVarBytes(r, 0, 80, "sigs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(bitcoinSig2) > 0 {
|
||||
proof.BitcoinSig2Bytes = fn.Some(bitcoinSig2)
|
||||
}
|
||||
|
||||
if !proof.IsEmpty() {
|
||||
edgeInfo.AuthProof = proof
|
||||
@@ -5073,6 +5258,10 @@ func fetchChanEdgePolicies(edgeIndex kvdb.RBucket, edges kvdb.RBucket,
|
||||
func serializeChanEdgePolicy(w io.Writer, edge *models.ChannelEdgePolicy,
|
||||
to []byte) error {
|
||||
|
||||
if edge.Version != lnwire.GossipVersion1 {
|
||||
return ErrVersionNotSupportedForKVDB
|
||||
}
|
||||
|
||||
err := wire.WriteVarBytes(w, 0, edge.SigBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -5161,7 +5350,9 @@ func deserializeChanEdgePolicy(r io.Reader) (*models.ChannelEdgePolicy, error) {
|
||||
func deserializeChanEdgePolicyRaw(r io.Reader) (*models.ChannelEdgePolicy,
|
||||
error) {
|
||||
|
||||
edge := &models.ChannelEdgePolicy{}
|
||||
edge := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
}
|
||||
|
||||
var err error
|
||||
edge.SigBytes, err = wire.ReadVarBytes(r, 0, 80, "sig")
|
||||
|
||||
@@ -20,13 +20,15 @@ type CachedEdgePolicy struct {
|
||||
// and the last 2 bytes are the output index for the channel.
|
||||
ChannelID uint64
|
||||
|
||||
// MessageFlags is a bitfield which indicates the presence of optional
|
||||
// fields (like max_htlc) in the policy.
|
||||
MessageFlags lnwire.ChanUpdateMsgFlags
|
||||
// HasMaxHTLC indicates whether the policy has a max HTLC value.
|
||||
HasMaxHTLC bool
|
||||
|
||||
// ChannelFlags is a bitfield which signals the capabilities of the
|
||||
// channel as well as the directed edge this update applies to.
|
||||
ChannelFlags lnwire.ChanUpdateChanFlags
|
||||
// IsNode1 indicates whether this policy was announced by the channel's
|
||||
// node_1.
|
||||
IsNode1 bool
|
||||
|
||||
// IsDisabled indicates whether the policy disables forwarding.
|
||||
IsDisabled bool
|
||||
|
||||
// TimeLockDelta is the number of blocks this node will subtract from
|
||||
// the expiry of an incoming HTLC. This value expresses the time buffer
|
||||
@@ -75,24 +77,31 @@ func (c *CachedEdgePolicy) ComputeFee(
|
||||
return c.FeeBaseMSat + (amt*c.FeeProportionalMillionths)/feeRateParts
|
||||
}
|
||||
|
||||
// IsDisabled returns true if the channel is disabled in the direction from the
|
||||
// advertising node.
|
||||
func (c *CachedEdgePolicy) IsDisabled() bool {
|
||||
return c.ChannelFlags&lnwire.ChanUpdateDisabled != 0
|
||||
}
|
||||
|
||||
// IsNode1 returns true if this policy was announced by the channel's node_1
|
||||
// node.
|
||||
func (c *CachedEdgePolicy) IsNode1() bool {
|
||||
return c.ChannelFlags&lnwire.ChanUpdateDirection == 0
|
||||
}
|
||||
|
||||
// NewCachedPolicy turns a full policy into a minimal one that can be cached.
|
||||
func NewCachedPolicy(policy *ChannelEdgePolicy) *CachedEdgePolicy {
|
||||
if policy.Version != lnwire.GossipVersion2 {
|
||||
return &CachedEdgePolicy{
|
||||
ChannelID: policy.ChannelID,
|
||||
HasMaxHTLC: policy.MessageFlags.HasMaxHtlc(),
|
||||
IsDisabled: policy.ChannelFlags&
|
||||
lnwire.ChanUpdateDisabled != 0,
|
||||
IsNode1: policy.ChannelFlags&
|
||||
lnwire.ChanUpdateDirection == 0,
|
||||
TimeLockDelta: policy.TimeLockDelta,
|
||||
MinHTLC: policy.MinHTLC,
|
||||
MaxHTLC: policy.MaxHTLC,
|
||||
FeeBaseMSat: policy.FeeBaseMSat,
|
||||
FeeProportionalMillionths: policy.
|
||||
FeeProportionalMillionths,
|
||||
InboundFee: policy.InboundFee,
|
||||
}
|
||||
}
|
||||
|
||||
return &CachedEdgePolicy{
|
||||
ChannelID: policy.ChannelID,
|
||||
MessageFlags: policy.MessageFlags,
|
||||
ChannelFlags: policy.ChannelFlags,
|
||||
HasMaxHTLC: true,
|
||||
IsNode1: !policy.SecondPeer,
|
||||
IsDisabled: !policy.DisableFlags.IsEnabled(),
|
||||
TimeLockDelta: policy.TimeLockDelta,
|
||||
MinHTLC: policy.MinHTLC,
|
||||
MaxHTLC: policy.MaxHTLC,
|
||||
|
||||
@@ -1,35 +1,117 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
// ChannelAuthProof is the authentication proof (the signature portion) for a
|
||||
// channel. Using the four signatures contained in the struct, and some
|
||||
// channel.
|
||||
//
|
||||
// For v1 channels:
|
||||
// Using the four node and bitcoin signatures contained in the struct, and some
|
||||
// auxiliary knowledge (the funding script, node identities, and outpoint) nodes
|
||||
// on the network are able to validate the authenticity and existence of a
|
||||
// channel. Each of these signatures signs the following digest: chanID ||
|
||||
// nodeID1 || nodeID2 || bitcoinKey1|| bitcoinKey2 || 2-byte-feature-len ||
|
||||
// features.
|
||||
//
|
||||
// For v2 channels:
|
||||
// The single schnorr signature signs the tlv fields of the v2 channel
|
||||
// announcement message which are in the signed range.
|
||||
type ChannelAuthProof struct {
|
||||
// Version is the version of the channel announcement.
|
||||
Version lnwire.GossipVersion
|
||||
|
||||
// NodeSig1Bytes are the raw bytes of the first node signature encoded
|
||||
// in DER format.
|
||||
NodeSig1Bytes []byte
|
||||
//
|
||||
// NOTE: v1 channel announcements only.
|
||||
NodeSig1Bytes fn.Option[[]byte]
|
||||
|
||||
// NodeSig2Bytes are the raw bytes of the second node signature
|
||||
// encoded in DER format.
|
||||
NodeSig2Bytes []byte
|
||||
//
|
||||
// NOTE: v1 channel announcements only.
|
||||
NodeSig2Bytes fn.Option[[]byte]
|
||||
|
||||
// BitcoinSig1Bytes are the raw bytes of the first bitcoin signature
|
||||
// encoded in DER format.
|
||||
BitcoinSig1Bytes []byte
|
||||
//
|
||||
// NOTE: v1 channel announcements only.
|
||||
BitcoinSig1Bytes fn.Option[[]byte]
|
||||
|
||||
// BitcoinSig2Bytes are the raw bytes of the second bitcoin signature
|
||||
// encoded in DER format.
|
||||
BitcoinSig2Bytes []byte
|
||||
//
|
||||
// NOTE: v1 channel announcements only.
|
||||
BitcoinSig2Bytes fn.Option[[]byte]
|
||||
|
||||
// Signature is the raw bytes of the single schnorr signature for v2
|
||||
// channel announcements.
|
||||
//
|
||||
// NOTE: v2 channel announcements only.
|
||||
Signature fn.Option[[]byte]
|
||||
}
|
||||
|
||||
// IsEmpty check is the authentication proof is empty Proof is empty if at
|
||||
// least one of the signatures are equal to nil.
|
||||
// IsEmpty check is the authentication proof is empty Proof is empty.
|
||||
func (c *ChannelAuthProof) IsEmpty() bool {
|
||||
return len(c.NodeSig1Bytes) == 0 ||
|
||||
len(c.NodeSig2Bytes) == 0 ||
|
||||
len(c.BitcoinSig1Bytes) == 0 ||
|
||||
len(c.BitcoinSig2Bytes) == 0
|
||||
// For v1 channel announcements, we either have all four signatures or
|
||||
// none.
|
||||
if c.Version == lnwire.GossipVersion1 {
|
||||
return c.NodeSig1Bytes.IsNone()
|
||||
}
|
||||
|
||||
// For v2 channel announcements, we only have a single signature.
|
||||
return c.Signature.IsNone()
|
||||
}
|
||||
|
||||
// NewV1ChannelAuthProof creates a new ChannelAuthProof for a v1 channel
|
||||
// announcement.
|
||||
func NewV1ChannelAuthProof(nodeSig1, nodeSig2, bitcoinSig1,
|
||||
bitcoinSig2 []byte) *ChannelAuthProof {
|
||||
|
||||
return &ChannelAuthProof{
|
||||
Version: lnwire.GossipVersion1,
|
||||
NodeSig1Bytes: fn.Some(nodeSig1),
|
||||
NodeSig2Bytes: fn.Some(nodeSig2),
|
||||
BitcoinSig1Bytes: fn.Some(bitcoinSig1),
|
||||
BitcoinSig2Bytes: fn.Some(bitcoinSig2),
|
||||
}
|
||||
}
|
||||
|
||||
// NewV2ChannelAuthProof creates a new ChannelAuthProof for a v2 channel
|
||||
// announcement.
|
||||
func NewV2ChannelAuthProof(signature []byte) *ChannelAuthProof {
|
||||
return &ChannelAuthProof{
|
||||
Version: lnwire.GossipVersion2,
|
||||
Signature: fn.Some(signature),
|
||||
}
|
||||
}
|
||||
|
||||
// NodeSig1 returns the first node signature bytes, or nil if not present.
|
||||
func (c *ChannelAuthProof) NodeSig1() []byte {
|
||||
return c.NodeSig1Bytes.UnwrapOr(nil)
|
||||
}
|
||||
|
||||
// NodeSig2 returns the second node signature bytes, or nil if not present.
|
||||
func (c *ChannelAuthProof) NodeSig2() []byte {
|
||||
return c.NodeSig2Bytes.UnwrapOr(nil)
|
||||
}
|
||||
|
||||
// BitcoinSig1 returns the first bitcoin signature bytes, or nil if not
|
||||
// present.
|
||||
func (c *ChannelAuthProof) BitcoinSig1() []byte {
|
||||
return c.BitcoinSig1Bytes.UnwrapOr(nil)
|
||||
}
|
||||
|
||||
// BitcoinSig2 returns the second bitcoin signature bytes, or nil if not
|
||||
// present.
|
||||
func (c *ChannelAuthProof) BitcoinSig2() []byte {
|
||||
return c.BitcoinSig2Bytes.UnwrapOr(nil)
|
||||
}
|
||||
|
||||
// Sig returns the v2 signature bytes, or nil if not present.
|
||||
func (c *ChannelAuthProof) Sig() []byte {
|
||||
return c.Signature.UnwrapOr(nil)
|
||||
}
|
||||
@@ -5,11 +5,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// ChannelEdgeInfo represents a fully authenticated channel along with all its
|
||||
@@ -19,6 +22,9 @@ import (
|
||||
// policy of a channel are stored within a ChannelEdgePolicy for each direction
|
||||
// of the channel.
|
||||
type ChannelEdgeInfo struct {
|
||||
// Version is the gossip version that this channel was advertised on.
|
||||
Version lnwire.GossipVersion
|
||||
|
||||
// ChannelID is the unique channel ID for the channel. The first 3
|
||||
// bytes are the block height, the next 3 the index within the block,
|
||||
// and the last 2 bytes are the output index for the channel.
|
||||
@@ -29,16 +35,22 @@ type ChannelEdgeInfo struct {
|
||||
ChainHash chainhash.Hash
|
||||
|
||||
// NodeKey1Bytes is the raw public key of the first node.
|
||||
NodeKey1Bytes [33]byte
|
||||
NodeKey1Bytes route.Vertex
|
||||
|
||||
// NodeKey2Bytes is the raw public key of the first node.
|
||||
NodeKey2Bytes [33]byte
|
||||
// NodeKey2Bytes is the raw public key of the second node.
|
||||
NodeKey2Bytes route.Vertex
|
||||
|
||||
// BitcoinKey1Bytes is the raw public key of the first node.
|
||||
BitcoinKey1Bytes [33]byte
|
||||
//
|
||||
// NOTE: this must be set for v1 channels but is optional for v2 and
|
||||
// beyond.
|
||||
BitcoinKey1Bytes fn.Option[route.Vertex]
|
||||
|
||||
// BitcoinKey2Bytes is the raw public key of the first node.
|
||||
BitcoinKey2Bytes [33]byte
|
||||
// BitcoinKey2Bytes is the raw public key of the second node.
|
||||
//
|
||||
// NOTE: this must be set for v1 channels but is optional for v2 and
|
||||
// beyond.
|
||||
BitcoinKey2Bytes fn.Option[route.Vertex]
|
||||
|
||||
// Features is the list of protocol features supported by this channel
|
||||
// edge.
|
||||
@@ -63,6 +75,84 @@ type ChannelEdgeInfo struct {
|
||||
// the edge object is loaded from the database.
|
||||
FundingScript fn.Option[[]byte]
|
||||
|
||||
// MerkleRootHash is an optional root hash of a Merkle tree that the
|
||||
// funding output is committed to. This is then used to compute the
|
||||
// final funding output script.
|
||||
//
|
||||
// NOTE: only used for version 2 channels and beyond.
|
||||
MerkleRootHash fn.Option[chainhash.Hash]
|
||||
|
||||
// ExtraOpaqueData is the set of data that was appended to this
|
||||
// message, some of which we may not actually know how to iterate or
|
||||
// parse. By holding onto this data, we ensure that we're able to
|
||||
// properly validate the set of signatures that cover these new fields,
|
||||
// and ensure we're able to make upgrades to the network in a forwards
|
||||
// compatible manner.
|
||||
//
|
||||
// NOTE: only used for version 1 channels.
|
||||
ExtraOpaqueData []byte
|
||||
|
||||
// ExtraSignedFields is a map of extra fields that are covered by the
|
||||
// node announcement's signature that we have not explicitly parsed.
|
||||
//
|
||||
// NOTE: This is only used for version 2 node announcements and beyond.
|
||||
ExtraSignedFields map[uint64][]byte
|
||||
}
|
||||
|
||||
// EdgeModifier is a functional option that modifies a ChannelEdgeInfo.
|
||||
type EdgeModifier func(*ChannelEdgeInfo)
|
||||
|
||||
// WithChannelPoint sets the channel point (funding outpoint) on the edge.
|
||||
func WithChannelPoint(cp wire.OutPoint) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.ChannelPoint = cp
|
||||
}
|
||||
}
|
||||
|
||||
// WithFeatures sets the feature vector on the edge.
|
||||
func WithFeatures(f *lnwire.RawFeatureVector) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.Features = lnwire.NewFeatureVector(f, lnwire.Features)
|
||||
}
|
||||
}
|
||||
|
||||
// WithCapacity sets the capacity on the edge.
|
||||
func WithCapacity(c btcutil.Amount) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.Capacity = c
|
||||
}
|
||||
}
|
||||
|
||||
// WithChanProof sets the authentication proof on the edge.
|
||||
func WithChanProof(proof *ChannelAuthProof) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.AuthProof = proof
|
||||
}
|
||||
}
|
||||
|
||||
// WithFundingScript sets the funding script on the edge.
|
||||
func WithFundingScript(script []byte) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.FundingScript = fn.Some(script)
|
||||
}
|
||||
}
|
||||
|
||||
// WithMerkleRootHash sets the merkle root hash on the edge.
|
||||
func WithMerkleRootHash(hash chainhash.Hash) EdgeModifier {
|
||||
return func(e *ChannelEdgeInfo) {
|
||||
e.MerkleRootHash = fn.Some(hash)
|
||||
}
|
||||
}
|
||||
|
||||
// ChannelV1Fields contains the fields that are specific to v1 channel
|
||||
// announcements.
|
||||
type ChannelV1Fields struct {
|
||||
// BitcoinKey1Bytes is the raw public key of the first node.
|
||||
BitcoinKey1Bytes route.Vertex
|
||||
|
||||
// BitcoinKey2Bytes is the raw public key of the second node.
|
||||
BitcoinKey2Bytes route.Vertex
|
||||
|
||||
// ExtraOpaqueData is the set of data that was appended to this
|
||||
// message, some of which we may not actually know how to iterate or
|
||||
// parse. By holding onto this data, we ensure that we're able to
|
||||
@@ -72,6 +162,100 @@ type ChannelEdgeInfo struct {
|
||||
ExtraOpaqueData []byte
|
||||
}
|
||||
|
||||
// NewV1Channel creates a new ChannelEdgeInfo for a v1 channel announcement.
|
||||
// It takes the required fields for all channels (chanID, chainHash, node keys)
|
||||
// and v1-specific fields, along with optional modifiers for setting additional
|
||||
// fields like capacity, channel point, features, and auth proof.
|
||||
//
|
||||
// The constructor validates that if an AuthProof is provided via modifiers, its
|
||||
// version matches the channel version (v1).
|
||||
func NewV1Channel(chanID uint64, chainHash chainhash.Hash, node1,
|
||||
node2 route.Vertex, v1Fields *ChannelV1Fields,
|
||||
opts ...EdgeModifier) (*ChannelEdgeInfo, error) {
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion1,
|
||||
NodeKey1Bytes: node1,
|
||||
NodeKey2Bytes: node2,
|
||||
BitcoinKey1Bytes: fn.Some(v1Fields.BitcoinKey1Bytes),
|
||||
BitcoinKey2Bytes: fn.Some(v1Fields.BitcoinKey2Bytes),
|
||||
ChannelID: chanID,
|
||||
ChainHash: chainHash,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
ExtraOpaqueData: v1Fields.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(edge)
|
||||
}
|
||||
|
||||
// Validate some fields after the options have been applied.
|
||||
if edge.AuthProof != nil && edge.AuthProof.Version != edge.Version {
|
||||
return nil, fmt.Errorf("channel auth proof version %d does "+
|
||||
"not match channel version %d", edge.AuthProof.Version,
|
||||
edge.Version)
|
||||
}
|
||||
|
||||
return edge, nil
|
||||
}
|
||||
|
||||
// ChannelV2Fields contains the fields that are specific to v2 channel
|
||||
// announcements.
|
||||
type ChannelV2Fields struct {
|
||||
// BitcoinKey1Bytes is the raw public key of the first node.
|
||||
BitcoinKey1Bytes fn.Option[route.Vertex]
|
||||
|
||||
// BitcoinKey2Bytes is the raw public key of the second node.
|
||||
BitcoinKey2Bytes fn.Option[route.Vertex]
|
||||
|
||||
// FundingScript is the funding output's pkScript. This is required for
|
||||
// v2 channels when the bitcoin keys are not provided.
|
||||
FundingScript fn.Option[[]byte]
|
||||
|
||||
// MerkleRootHash is an optional root hash of a Merkle tree that the
|
||||
// funding output is committed to.
|
||||
MerkleRootHash fn.Option[chainhash.Hash]
|
||||
|
||||
// ExtraSignedFields is a map of extra fields that are covered by the
|
||||
// node announcement's signature that we have not explicitly parsed.
|
||||
//
|
||||
// NOTE: This is only used for version 2 node announcements and beyond.
|
||||
ExtraSignedFields map[uint64][]byte
|
||||
}
|
||||
|
||||
// NewV2Channel creates a new ChannelEdgeInfo for a v2 channel announcement.
|
||||
func NewV2Channel(chanID uint64, chainHash chainhash.Hash, node1,
|
||||
node2 route.Vertex, v2Fields *ChannelV2Fields,
|
||||
opts ...EdgeModifier) (*ChannelEdgeInfo, error) {
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
NodeKey1Bytes: node1,
|
||||
NodeKey2Bytes: node2,
|
||||
BitcoinKey1Bytes: v2Fields.BitcoinKey1Bytes,
|
||||
BitcoinKey2Bytes: v2Fields.BitcoinKey2Bytes,
|
||||
FundingScript: v2Fields.FundingScript,
|
||||
MerkleRootHash: v2Fields.MerkleRootHash,
|
||||
ChannelID: chanID,
|
||||
ChainHash: chainHash,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
ExtraSignedFields: v2Fields.ExtraSignedFields,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(edge)
|
||||
}
|
||||
|
||||
// Validate some fields after the options have been applied.
|
||||
if edge.AuthProof != nil && edge.AuthProof.Version != edge.Version {
|
||||
return nil, fmt.Errorf("channel auth proof version %d does "+
|
||||
"not match channel version %d", edge.AuthProof.Version,
|
||||
edge.Version)
|
||||
}
|
||||
|
||||
return edge, nil
|
||||
}
|
||||
|
||||
// NodeKey1 is the identity public key of the "first" node that was involved in
|
||||
// the creation of this channel. A node is considered "first" if the
|
||||
// lexicographical ordering the its serialized public key is "smaller" than
|
||||
@@ -90,7 +274,7 @@ func (c *ChannelEdgeInfo) NodeKey2() (*btcec.PublicKey, error) {
|
||||
|
||||
// OtherNodeKeyBytes returns the node key bytes of the other end of the channel.
|
||||
func (c *ChannelEdgeInfo) OtherNodeKeyBytes(thisNodeKey []byte) (
|
||||
[33]byte, error) {
|
||||
route.Vertex, error) {
|
||||
|
||||
switch {
|
||||
case bytes.Equal(c.NodeKey1Bytes[:], thisNodeKey):
|
||||
@@ -98,7 +282,173 @@ func (c *ChannelEdgeInfo) OtherNodeKeyBytes(thisNodeKey []byte) (
|
||||
case bytes.Equal(c.NodeKey2Bytes[:], thisNodeKey):
|
||||
return c.NodeKey1Bytes, nil
|
||||
default:
|
||||
return [33]byte{}, fmt.Errorf("node not participating in " +
|
||||
return route.Vertex{}, fmt.Errorf("node not participating in " +
|
||||
"this channel")
|
||||
}
|
||||
}
|
||||
|
||||
// FundingPKScript returns the funding output's pkScript for the channel.
|
||||
func (c *ChannelEdgeInfo) FundingPKScript() ([]byte, error) {
|
||||
switch c.Version {
|
||||
case lnwire.GossipVersion1:
|
||||
btc1Key, err := c.BitcoinKey1Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("missing bitcoin key 1"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
btc2Key, err := c.BitcoinKey2Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("missing bitcoin key 2"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
witnessScript, err := input.GenMultiSigScript(
|
||||
btc1Key[:], btc2Key[:],
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return input.WitnessScriptHash(witnessScript)
|
||||
|
||||
case lnwire.GossipVersion2:
|
||||
var (
|
||||
pubKey1 *btcec.PublicKey
|
||||
pubKey2 *btcec.PublicKey
|
||||
err error
|
||||
)
|
||||
c.BitcoinKey1Bytes.WhenSome(func(key route.Vertex) {
|
||||
pubKey1, err = btcec.ParsePubKey(key[:])
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.BitcoinKey2Bytes.WhenSome(func(key route.Vertex) {
|
||||
pubKey2, err = btcec.ParsePubKey(key[:])
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If both bitcoin keys are not present in the announcement,
|
||||
// then we should previously have stored the funding script
|
||||
// found on-chain.
|
||||
if pubKey1 == nil || pubKey2 == nil {
|
||||
return c.FundingScript.UnwrapOrErr(fmt.Errorf(
|
||||
"expected a funding pk script since no " +
|
||||
"bitcoin keys were provided",
|
||||
))
|
||||
}
|
||||
|
||||
// By default, the tweak is empty which results in a BIP86
|
||||
// output. If we have a merkle root, we'll use that as the
|
||||
// tweak.
|
||||
muSig2Opt := musig2.WithBIP86KeyTweak()
|
||||
c.MerkleRootHash.WhenSome(func(hash chainhash.Hash) {
|
||||
muSig2Opt = musig2.WithTaprootKeyTweak(hash[:])
|
||||
})
|
||||
|
||||
// Compute the output key.
|
||||
combinedKey, _, _, err := musig2.AggregateKeys(
|
||||
[]*btcec.PublicKey{pubKey1, pubKey2}, true, muSig2Opt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now that we have the combined key, we can create a taproot
|
||||
// pkScript from this, and then make the txout given the amount.
|
||||
fundingScript, err := input.PayToTaprootScript(
|
||||
combinedKey.FinalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to make taproot "+
|
||||
"pkscript: %w", err)
|
||||
}
|
||||
|
||||
return fundingScript, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported channel version: %d",
|
||||
c.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// ToChannelAnnouncement converts the ChannelEdgeInfo to a
|
||||
// lnwire.ChannelAnnouncement1 message. Returns an error if AuthProof is nil
|
||||
// or if the version is not v1.
|
||||
func (c *ChannelEdgeInfo) ToChannelAnnouncement() (
|
||||
*lnwire.ChannelAnnouncement1, error) {
|
||||
|
||||
// We currently only support v1 channel announcements.
|
||||
if c.Version != lnwire.GossipVersion1 {
|
||||
return nil, fmt.Errorf("unsupported channel version: %d",
|
||||
c.Version)
|
||||
}
|
||||
|
||||
// If there's no auth proof, we can't create a full channel
|
||||
// announcement.
|
||||
if c.AuthProof == nil {
|
||||
return nil, fmt.Errorf("cannot create channel announcement " +
|
||||
"without auth proof")
|
||||
}
|
||||
|
||||
btc1, err := c.BitcoinKey1Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("bitcoin key 1 missing for v1 channel announcement"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
btc2, err := c.BitcoinKey2Bytes.UnwrapOrErr(
|
||||
fmt.Errorf("bitcoin key 2 missing for v1 channel announcement"),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chanID := lnwire.NewShortChanIDFromInt(c.ChannelID)
|
||||
chanAnn := &lnwire.ChannelAnnouncement1{
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: c.NodeKey1Bytes,
|
||||
NodeID2: c.NodeKey2Bytes,
|
||||
ChainHash: c.ChainHash,
|
||||
BitcoinKey1: btc1,
|
||||
BitcoinKey2: btc2,
|
||||
Features: c.Features.RawFeatureVector,
|
||||
ExtraOpaqueData: c.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
c.AuthProof.NodeSig1(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
c.AuthProof.NodeSig2(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
c.AuthProof.BitcoinSig1(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
c.AuthProof.BitcoinSig2(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return chanAnn, nil
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestFundingPKScriptV2 tests the FundingPKScript method for v2 channels
|
||||
// which uses MuSig2 key aggregation.
|
||||
func TestFundingPKScriptV2(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Generate two test keys for bitcoin keys.
|
||||
privKey1, err := btcec.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pubKey1 := privKey1.PubKey()
|
||||
|
||||
privKey2, err := btcec.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pubKey2 := privKey2.PubKey()
|
||||
|
||||
// Convert to route.Vertex format.
|
||||
var btcKey1, btcKey2 route.Vertex
|
||||
copy(btcKey1[:], pubKey1.SerializeCompressed())
|
||||
copy(btcKey2[:], pubKey2.SerializeCompressed())
|
||||
|
||||
// Create a test merkle root.
|
||||
var merkleRoot chainhash.Hash
|
||||
copy(merkleRoot[:], []byte("test-merkle-root-hash-32-bytes!"))
|
||||
|
||||
t.Run("v2 with btc keys, no merkle root (BIP86)", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
BitcoinKey1Bytes: fn.Some(btcKey1),
|
||||
BitcoinKey2Bytes: fn.Some(btcKey2),
|
||||
}
|
||||
|
||||
pkScript, err := edge.FundingPKScript()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pkScript)
|
||||
|
||||
// Verify it's a valid taproot script (OP_1 <32-byte-key>).
|
||||
require.Len(t, pkScript, 34)
|
||||
require.Equal(t, byte(0x51), pkScript[0]) // OP_1
|
||||
|
||||
// Manually compute expected script using BIP86 tweak.
|
||||
combinedKey, _, _, err := musig2.AggregateKeys(
|
||||
[]*btcec.PublicKey{pubKey1, pubKey2}, true,
|
||||
musig2.WithBIP86KeyTweak(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedScript, err := input.PayToTaprootScript(
|
||||
combinedKey.FinalKey,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedScript, pkScript)
|
||||
})
|
||||
|
||||
t.Run("v2 with bitcoin keys and merkle root", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
BitcoinKey1Bytes: fn.Some(btcKey1),
|
||||
BitcoinKey2Bytes: fn.Some(btcKey2),
|
||||
MerkleRootHash: fn.Some(merkleRoot),
|
||||
}
|
||||
|
||||
pkScript, err := edge.FundingPKScript()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, pkScript)
|
||||
|
||||
// Verify it's a valid taproot script.
|
||||
require.Len(t, pkScript, 34)
|
||||
require.Equal(t, byte(0x51), pkScript[0]) // OP_1
|
||||
|
||||
// Manually compute expected script with taproot tweak.
|
||||
combinedKey, _, _, err := musig2.AggregateKeys(
|
||||
[]*btcec.PublicKey{pubKey1, pubKey2}, true,
|
||||
musig2.WithTaprootKeyTweak(merkleRoot[:]),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedScript, err := input.PayToTaprootScript(
|
||||
combinedKey.FinalKey,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedScript, pkScript)
|
||||
})
|
||||
|
||||
t.Run("v2 no btc keys returns stored script", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
storedScript := []byte{0x51, 0x20} // OP_1 + push 32
|
||||
storedScript = append(storedScript, make([]byte, 32)...)
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
FundingScript: fn.Some(storedScript),
|
||||
}
|
||||
|
||||
pkScript, err := edge.FundingPKScript()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, storedScript, pkScript)
|
||||
})
|
||||
|
||||
t.Run("v2 no btc keys and no stored script errors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
}
|
||||
|
||||
_, err := edge.FundingPKScript()
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "expected a funding pk script")
|
||||
})
|
||||
|
||||
t.Run("v2 one btc key returns stored script", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
storedScript := []byte{0x51, 0x20}
|
||||
storedScript = append(storedScript, make([]byte, 32)...)
|
||||
|
||||
// Only key1 set, key2 missing.
|
||||
edge := &ChannelEdgeInfo{
|
||||
Version: lnwire.GossipVersion2,
|
||||
BitcoinKey1Bytes: fn.Some(btcKey1),
|
||||
FundingScript: fn.Some(storedScript),
|
||||
}
|
||||
|
||||
pkScript, err := edge.FundingPKScript()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, storedScript, pkScript)
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
@@ -15,15 +14,15 @@ import (
|
||||
// information concerning fees, and minimum time-lock information which is
|
||||
// utilized during path finding.
|
||||
type ChannelEdgePolicy struct {
|
||||
// Version is the gossip version of the channel update that produced
|
||||
// this policy.
|
||||
Version lnwire.GossipVersion
|
||||
|
||||
// SigBytes is the raw bytes of the signature of the channel edge
|
||||
// policy. We'll only parse these if the caller needs to access the
|
||||
// signature for validation purposes. Do not set SigBytes directly, but
|
||||
// use SetSigBytes instead to make sure that the cache is invalidated.
|
||||
// signature for validation purposes.
|
||||
SigBytes []byte
|
||||
|
||||
// sig is a cached fully parsed signature.
|
||||
sig *ecdsa.Signature
|
||||
|
||||
// ChannelID is the unique channel ID for the channel. The first 3
|
||||
// bytes are the block height, the next 3 the index within the block,
|
||||
// and the last 2 bytes are the output index for the channel.
|
||||
@@ -33,6 +32,14 @@ type ChannelEdgePolicy struct {
|
||||
// was received.
|
||||
LastUpdate time.Time
|
||||
|
||||
// LastBlockHeight is the block height that timestamps the last update
|
||||
// for v2 channel updates.
|
||||
LastBlockHeight uint32
|
||||
|
||||
// SecondPeer indicates whether this policy was announced by the second
|
||||
// peer in the channel for v2 channel updates.
|
||||
SecondPeer bool
|
||||
|
||||
// MessageFlags is a bitfield which indicates the presence of optional
|
||||
// fields (like max_htlc) in the policy.
|
||||
MessageFlags lnwire.ChanUpdateMsgFlags
|
||||
@@ -41,6 +48,10 @@ type ChannelEdgePolicy struct {
|
||||
// channel as well as the directed edge this update applies to.
|
||||
ChannelFlags lnwire.ChanUpdateChanFlags
|
||||
|
||||
// DisableFlags is a v2-specific bitfield which signals whether the
|
||||
// channel is disabled for incoming or outgoing traffic.
|
||||
DisableFlags lnwire.ChanUpdateDisableFlags
|
||||
|
||||
// TimeLockDelta is the number of blocks this node will subtract from
|
||||
// the expiry of an incoming HTLC. This value expresses the time buffer
|
||||
// the node would like to HTLC exchanges.
|
||||
@@ -82,38 +93,81 @@ type ChannelEdgePolicy struct {
|
||||
// and ensure we're able to make upgrades to the network in a forwards
|
||||
// compatible manner.
|
||||
ExtraOpaqueData lnwire.ExtraOpaqueData
|
||||
|
||||
// ExtraSignedFields are the extra signed fields found in v2 channel
|
||||
// updates.
|
||||
ExtraSignedFields map[uint64][]byte
|
||||
}
|
||||
|
||||
// Signature is a channel announcement signature, which is needed for proper
|
||||
// edge policy announcement.
|
||||
//
|
||||
// NOTE: By having this method to access an attribute, we ensure we only need
|
||||
// to fully deserialize the signature if absolutely necessary.
|
||||
func (c *ChannelEdgePolicy) Signature() (*ecdsa.Signature, error) {
|
||||
if c.sig != nil {
|
||||
return c.sig, nil
|
||||
// ChanEdgePolicyFromWire constructs a ChannelEdgePolicy from a channel update
|
||||
// message.
|
||||
func ChanEdgePolicyFromWire(scid uint64,
|
||||
update lnwire.ChannelUpdate) (*ChannelEdgePolicy, error) {
|
||||
|
||||
switch upd := update.(type) {
|
||||
case *lnwire.ChannelUpdate1:
|
||||
//nolint:ll
|
||||
return &ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: upd.Signature.ToSignatureBytes(),
|
||||
ChannelID: scid,
|
||||
LastUpdate: time.Unix(int64(upd.Timestamp), 0),
|
||||
MessageFlags: upd.MessageFlags,
|
||||
ChannelFlags: upd.ChannelFlags,
|
||||
TimeLockDelta: upd.TimeLockDelta,
|
||||
MinHTLC: upd.HtlcMinimumMsat,
|
||||
MaxHTLC: upd.HtlcMaximumMsat,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(upd.BaseFee),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(upd.FeeRate),
|
||||
InboundFee: upd.InboundFee.ValOpt(),
|
||||
ExtraOpaqueData: upd.ExtraOpaqueData,
|
||||
}, nil
|
||||
|
||||
case *lnwire.ChannelUpdate2:
|
||||
return &ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion2,
|
||||
SigBytes: upd.Signature.Val.ToSignatureBytes(),
|
||||
ChannelID: scid,
|
||||
LastBlockHeight: upd.BlockHeight.Val,
|
||||
SecondPeer: upd.SecondPeer.IsSome(),
|
||||
DisableFlags: upd.DisabledFlags.Val,
|
||||
TimeLockDelta: upd.CLTVExpiryDelta.Val,
|
||||
MinHTLC: upd.HTLCMinimumMsat.Val,
|
||||
MaxHTLC: upd.HTLCMaximumMsat.Val,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(
|
||||
upd.FeeBaseMsat.Val,
|
||||
),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(
|
||||
upd.FeeProportionalMillionths.Val,
|
||||
),
|
||||
InboundFee: upd.InboundFee.ValOpt(),
|
||||
ExtraSignedFields: upd.ExtraSignedFields,
|
||||
}, nil
|
||||
}
|
||||
|
||||
sig, err := ecdsa.ParseSignature(c.SigBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.sig = sig
|
||||
|
||||
return sig, nil
|
||||
return nil, fmt.Errorf("unknown channel update version: %v",
|
||||
update.MsgType())
|
||||
}
|
||||
|
||||
// SetSigBytes updates the signature and invalidates the cached parsed
|
||||
// signature.
|
||||
func (c *ChannelEdgePolicy) SetSigBytes(sig []byte) {
|
||||
c.SigBytes = sig
|
||||
c.sig = nil
|
||||
// IsNode1 returns true if this policy was announced by the channel's node_1.
|
||||
func (c *ChannelEdgePolicy) IsNode1() bool {
|
||||
if c.Version == lnwire.GossipVersion1 {
|
||||
return c.ChannelFlags&lnwire.ChanUpdateDirection == 0
|
||||
}
|
||||
|
||||
return !c.SecondPeer
|
||||
}
|
||||
|
||||
// IsDisabled determines whether the edge has the disabled bit set.
|
||||
//
|
||||
// NOTE: for v2 channel updates, we return true here only if both the incoming
|
||||
// and outgoing disabled bits are set.
|
||||
func (c *ChannelEdgePolicy) IsDisabled() bool {
|
||||
return c.ChannelFlags.IsDisabled()
|
||||
if c.Version == lnwire.GossipVersion1 {
|
||||
return c.ChannelFlags.IsDisabled()
|
||||
}
|
||||
|
||||
return !c.DisableFlags.IsEnabled()
|
||||
}
|
||||
|
||||
// ComputeFee computes the fee to forward an HTLC of `amt` milli-satoshis over
|
||||
@@ -127,7 +181,13 @@ func (c *ChannelEdgePolicy) ComputeFee(
|
||||
|
||||
// String returns a human-readable version of the channel edge policy.
|
||||
func (c *ChannelEdgePolicy) String() string {
|
||||
return fmt.Sprintf("ChannelID=%v, MessageFlags=%v, ChannelFlags=%v, "+
|
||||
"LastUpdate=%v", c.ChannelID, c.MessageFlags, c.ChannelFlags,
|
||||
c.LastUpdate)
|
||||
if c.Version == lnwire.GossipVersion1 {
|
||||
return fmt.Sprintf("ChannelID=%v, MessageFlags=%v, "+
|
||||
"ChannelFlags=%v, LastUpdate=%v", c.ChannelID,
|
||||
c.MessageFlags, c.ChannelFlags, c.LastUpdate)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("ChannelID=%v, Node1=%v, DisableFlags=%v, "+
|
||||
"BlockHeight=%v", c.ChannelID, !c.SecondPeer,
|
||||
c.DisableFlags, c.LastBlockHeight)
|
||||
}
|
||||
+58
-1
@@ -27,6 +27,11 @@ type Node struct {
|
||||
// been updated.
|
||||
LastUpdate time.Time
|
||||
|
||||
// LastBlockHeight is the block height that timestamps the last update
|
||||
// we received for this node. This is only used if this is a V2 node
|
||||
// announcement.
|
||||
LastBlockHeight uint32
|
||||
|
||||
// Address is the TCP address this node is reachable over.
|
||||
Addresses []net.Addr
|
||||
|
||||
@@ -49,8 +54,13 @@ type Node struct {
|
||||
// parse. By holding onto this data, we ensure that we're able to
|
||||
// properly validate the set of signatures that cover these new fields,
|
||||
// and ensure we're able to make upgrades to the network in a forwards
|
||||
// compatible manner.
|
||||
// compatible manner. This is only used for V1 node announcements.
|
||||
ExtraOpaqueData []byte
|
||||
|
||||
// ExtraSignedFields is a map of extra fields that are covered by the
|
||||
// node announcement's signature that we have not explicitly parsed.
|
||||
// This is only used for version 2 node announcements and beyond.
|
||||
ExtraSignedFields map[uint64][]byte
|
||||
}
|
||||
|
||||
// NodeV1Fields houses the fields that are specific to a version 1 node
|
||||
@@ -103,6 +113,53 @@ func NewV1Node(pub route.Vertex, n *NodeV1Fields) *Node {
|
||||
}
|
||||
}
|
||||
|
||||
// NodeV2Fields houses the fields that are specific to a version 2 node
|
||||
// announcement.
|
||||
type NodeV2Fields struct {
|
||||
// LastBlockHeight is the block height that timestamps the last update
|
||||
// we received for this node.
|
||||
LastBlockHeight uint32
|
||||
|
||||
// Address is the TCP address this node is reachable over.
|
||||
Addresses []net.Addr
|
||||
|
||||
// Color is the selected color for the node.
|
||||
Color fn.Option[color.RGBA]
|
||||
|
||||
// Alias is a nick-name for the node. The alias can be used to confirm
|
||||
// a node's identity or to serve as a short ID for an address book.
|
||||
Alias fn.Option[string]
|
||||
|
||||
// Signature is the schnorr signature under the advertised public key
|
||||
// which serves to authenticate the attributes announced by this node.
|
||||
Signature []byte
|
||||
|
||||
// Features is the list of protocol features supported by this node.
|
||||
Features *lnwire.RawFeatureVector
|
||||
|
||||
// ExtraSignedFields is a map of extra fields that are covered by the
|
||||
// node announcement's signature that we have not explicitly parsed.
|
||||
ExtraSignedFields map[uint64][]byte
|
||||
}
|
||||
|
||||
// NewV2Node creates a new version 2 node from the passed fields.
|
||||
func NewV2Node(pub route.Vertex, n *NodeV2Fields) *Node {
|
||||
return &Node{
|
||||
Version: lnwire.GossipVersion2,
|
||||
PubKeyBytes: pub,
|
||||
Addresses: n.Addresses,
|
||||
AuthSigBytes: n.Signature,
|
||||
Features: lnwire.NewFeatureVector(
|
||||
n.Features, lnwire.Features,
|
||||
),
|
||||
LastBlockHeight: n.LastBlockHeight,
|
||||
Color: n.Color,
|
||||
Alias: n.Alias,
|
||||
LastUpdate: time.Unix(0, 0),
|
||||
ExtraSignedFields: n.ExtraSignedFields,
|
||||
}
|
||||
}
|
||||
|
||||
// NewV1ShellNode creates a new shell version 1 node.
|
||||
func NewV1ShellNode(pubKey route.Vertex) *Node {
|
||||
return NewShellNode(lnwire.GossipVersion1, pubKey)
|
||||
|
||||
+86
-11
@@ -1,5 +1,11 @@
|
||||
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
|
||||
@@ -41,9 +47,63 @@ func (f rejectFlags) unpack() (bool, bool) {
|
||||
// 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
|
||||
flags rejectFlags
|
||||
|
||||
// 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
|
||||
@@ -51,20 +111,25 @@ type rejectCacheEntry struct {
|
||||
// well as the most recent timestamps for each policy (if they exists).
|
||||
type rejectCache struct {
|
||||
n int
|
||||
edges map[uint64]rejectCacheEntry
|
||||
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[uint64]rejectCacheEntry, n),
|
||||
edges: make(map[rejectCacheKey]rejectCacheEntry, n),
|
||||
}
|
||||
}
|
||||
|
||||
// get returns the entry from the cache for chanid, if it exists.
|
||||
func (c *rejectCache) get(chanid uint64) (rejectCacheEntry, bool) {
|
||||
entry, ok := c.edges[chanid]
|
||||
func (c *rejectCache) get(version lnwire.GossipVersion, chanid uint64) (
|
||||
rejectCacheEntry, bool) {
|
||||
|
||||
entry, ok := c.edges[rejectCacheKey{
|
||||
version: version,
|
||||
chanID: chanid,
|
||||
}]
|
||||
return entry, ok
|
||||
}
|
||||
|
||||
@@ -72,10 +137,17 @@ func (c *rejectCache) get(chanid uint64) (rejectCacheEntry, bool) {
|
||||
// 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(chanid uint64, entry rejectCacheEntry) {
|
||||
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[chanid]; ok {
|
||||
c.edges[chanid] = entry
|
||||
if _, ok := c.edges[key]; ok {
|
||||
c.edges[key] = entry
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,10 +158,13 @@ func (c *rejectCache) insert(chanid uint64, entry rejectCacheEntry) {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.edges[chanid] = entry
|
||||
c.edges[key] = entry
|
||||
}
|
||||
|
||||
// remove deletes an entry for chanid from the cache, if it exists.
|
||||
func (c *rejectCache) remove(chanid uint64) {
|
||||
delete(c.edges, chanid)
|
||||
func (c *rejectCache) remove(version lnwire.GossipVersion, chanid uint64) {
|
||||
delete(c.edges, rejectCacheKey{
|
||||
version: version,
|
||||
chanID: chanid,
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package graphdb
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
// TestRejectCache checks the behavior of the rejectCache with respect to insertion,
|
||||
@@ -15,14 +17,14 @@ func TestRejectCache(t *testing.T) {
|
||||
|
||||
// As a sanity check, assert that querying the empty cache does not
|
||||
// return an entry.
|
||||
_, ok := c.get(0)
|
||||
_, ok := c.get(lnwire.GossipVersion1, 0)
|
||||
if ok {
|
||||
t.Fatalf("reject cache should be empty")
|
||||
}
|
||||
|
||||
// Now, fill up the cache entirely.
|
||||
for i := uint64(0); i < cacheSize; i++ {
|
||||
c.insert(i, entryForInt(i))
|
||||
c.insert(lnwire.GossipVersion1, i, entryForInt(i))
|
||||
}
|
||||
|
||||
// Assert that the cache has all of the entries just inserted, since no
|
||||
@@ -30,7 +32,10 @@ func TestRejectCache(t *testing.T) {
|
||||
assertHasEntries(t, c, 0, cacheSize)
|
||||
|
||||
// Now, insert a new element that causes the cache to evict an element.
|
||||
c.insert(cacheSize, entryForInt(cacheSize))
|
||||
c.insert(
|
||||
lnwire.GossipVersion1, cacheSize,
|
||||
entryForInt(cacheSize),
|
||||
)
|
||||
|
||||
// Assert that the cache has this last entry, as the cache should evict
|
||||
// some prior element and not the newly inserted one.
|
||||
@@ -40,7 +45,7 @@ func TestRejectCache(t *testing.T) {
|
||||
// elements.
|
||||
evicted := make(map[uint64]struct{})
|
||||
for i := uint64(0); i < cacheSize+1; i++ {
|
||||
_, ok := c.get(i)
|
||||
_, ok := c.get(lnwire.GossipVersion1, i)
|
||||
if !ok {
|
||||
evicted[i] = struct{}{}
|
||||
}
|
||||
@@ -54,9 +59,9 @@ func TestRejectCache(t *testing.T) {
|
||||
|
||||
// Remove the highest item which initially caused the eviction and
|
||||
// reinsert the element that was evicted prior.
|
||||
c.remove(cacheSize)
|
||||
c.remove(lnwire.GossipVersion1, cacheSize)
|
||||
for i := range evicted {
|
||||
c.insert(i, entryForInt(i))
|
||||
c.insert(lnwire.GossipVersion1, i, entryForInt(i))
|
||||
}
|
||||
|
||||
// Since the removal created an extra slot, the last insertion should
|
||||
@@ -69,7 +74,7 @@ func TestRejectCache(t *testing.T) {
|
||||
// happening on inserts for existing cache items, we expect this to fail
|
||||
// with high probability.
|
||||
for i := uint64(0); i < cacheSize; i++ {
|
||||
c.insert(i, entryForInt(i))
|
||||
c.insert(lnwire.GossipVersion1, i, entryForInt(i))
|
||||
}
|
||||
assertHasEntries(t, c, 0, cacheSize)
|
||||
|
||||
@@ -82,7 +87,7 @@ func assertHasEntries(t *testing.T, c *rejectCache, start, end uint64) {
|
||||
t.Helper()
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
entry, ok := c.get(i)
|
||||
entry, ok := c.get(lnwire.GossipVersion1, i)
|
||||
if !ok {
|
||||
t.Fatalf("reject cache should contain chan %d", i)
|
||||
}
|
||||
|
||||
+807
-294
File diff suppressed because it is too large.
Load diff
@@ -9,8 +9,11 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// isSQLDB indicates that this build does not use a SQL database.
|
||||
var isSQLDB = false
|
||||
|
||||
// NewTestDB is a helper function that creates an BBolt database for testing.
|
||||
func NewTestDB(t testing.TB) V1Store {
|
||||
func NewTestDB(t testing.TB) Store {
|
||||
backend, backendCleanup, err := kvdb.GetTestBackend(t.TempDir(), "cgr")
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// isSQLDB indicates that this build uses a SQL database.
|
||||
var isSQLDB = true
|
||||
|
||||
// NewTestDB is a helper function that creates a SQLStore backed by a SQL
|
||||
// database for testing.
|
||||
func NewTestDB(t testing.TB) V1Store {
|
||||
func NewTestDB(t testing.TB) Store {
|
||||
return NewTestDBWithFixture(t, nil)
|
||||
}
|
||||
|
||||
@@ -31,7 +34,7 @@ func NewTestDBFixture(t *testing.T) *sqldb.TestPgFixture {
|
||||
// NewTestDBWithFixture is a helper function that creates a SQLStore backed by a
|
||||
// SQL database for testing.
|
||||
func NewTestDBWithFixture(t testing.TB,
|
||||
pgFixture *sqldb.TestPgFixture) V1Store {
|
||||
pgFixture *sqldb.TestPgFixture) Store {
|
||||
|
||||
var querier BatchedSQLQueries
|
||||
if pgFixture == nil {
|
||||
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// isSQLDB indicates that this build uses a SQL database.
|
||||
var isSQLDB = true
|
||||
|
||||
// NewTestDB is a helper function that creates a SQLStore backed by a SQL
|
||||
// database for testing.
|
||||
func NewTestDB(t testing.TB) V1Store {
|
||||
func NewTestDB(t testing.TB) Store {
|
||||
return NewTestDBWithFixture(t, nil)
|
||||
}
|
||||
|
||||
@@ -24,7 +27,7 @@ func NewTestDBFixture(_ *testing.T) *sqldb.TestPgFixture {
|
||||
|
||||
// NewTestDBWithFixture is a helper function that creates a SQLStore backed by a
|
||||
// SQL database for testing.
|
||||
func NewTestDBWithFixture(t testing.TB, _ *sqldb.TestPgFixture) V1Store {
|
||||
func NewTestDBWithFixture(t testing.TB, _ *sqldb.TestPgFixture) Store {
|
||||
store, err := NewSQLStore(
|
||||
&SQLStoreConfig{
|
||||
ChainHash: *chaincfg.MainNetParams.GenesisHash,
|
||||
|
||||
+99
-77
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
@@ -68,12 +69,12 @@ var (
|
||||
_ = testSScalar.SetByteSlice(testSBytes)
|
||||
testSig = ecdsa.NewSignature(testRScalar, testSScalar)
|
||||
|
||||
testAuthProof = models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
}
|
||||
testAuthProof = *models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
)
|
||||
|
||||
func createTestNode(t *testing.T) *models.Node {
|
||||
@@ -112,6 +113,7 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID,
|
||||
}
|
||||
|
||||
return &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: chanID.ToUint64(),
|
||||
LastUpdate: time.Unix(int64(prand.Int31()), 0),
|
||||
@@ -447,23 +449,27 @@ func TestEdgeUpdateNotification(t *testing.T) {
|
||||
|
||||
// Finally, to conclude our test set up, we'll create a channel
|
||||
// update to announce the created channel between the two nodes.
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
ChannelPoint: *chanPoint,
|
||||
Capacity: chanValue,
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithChannelPoint(*chanPoint),
|
||||
models.WithCapacity(chanValue),
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
@@ -641,21 +647,25 @@ func TestNodeUpdateNotification(t *testing.T) {
|
||||
testFeaturesBuf := new(bytes.Buffer)
|
||||
require.NoError(t, testFeatures.Encode(testFeaturesBuf))
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Adding the edge will add the nodes to the graph, but with no info
|
||||
// except the pubkey known.
|
||||
@@ -827,23 +837,28 @@ func TestNotificationCancellation(t *testing.T) {
|
||||
// to the client.
|
||||
ntfnClient.Cancel()
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
ChannelPoint: *chanPoint,
|
||||
Capacity: chanValue,
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithChannelPoint(*chanPoint),
|
||||
models.WithCapacity(chanValue),
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
@@ -903,23 +918,28 @@ func TestChannelCloseNotification(t *testing.T) {
|
||||
|
||||
// Finally, to conclude our test set up, we'll create a channel
|
||||
// announcement to announce the created channel between the two nodes.
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: node1.PubKeyBytes,
|
||||
NodeKey2Bytes: node2.PubKeyBytes,
|
||||
AuthProof: &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
},
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
ChannelPoint: *chanUtxo,
|
||||
Capacity: chanValue,
|
||||
FundingScript: fn.Some(script),
|
||||
}
|
||||
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
|
||||
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
|
||||
btcKey1 := route.NewVertex(bitcoinKey1)
|
||||
btcKey2 := route.NewVertex(bitcoinKey2)
|
||||
|
||||
proof := models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), *chaincfg.SimNetParams.GenesisHash,
|
||||
node1.PubKeyBytes, node2.PubKeyBytes, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
}, models.WithChanProof(proof),
|
||||
models.WithChannelPoint(*chanUtxo),
|
||||
models.WithCapacity(chanValue),
|
||||
models.WithFundingScript(script),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := ctx.builder.AddEdge(ctxb, edge); err != nil {
|
||||
t.Fatalf("unable to add edge: %v", err)
|
||||
}
|
||||
@@ -1042,7 +1062,7 @@ func TestEncodeHexColor(t *testing.T) {
|
||||
type testCtx struct {
|
||||
builder *Builder
|
||||
|
||||
graph *graphdb.ChannelGraph
|
||||
graph *graphdb.VersionedGraph
|
||||
|
||||
aliases map[string]route.Vertex
|
||||
|
||||
@@ -1059,7 +1079,9 @@ type testCtx struct {
|
||||
func createTestCtxSingleNode(t *testing.T,
|
||||
startingHeight uint32) *testCtx {
|
||||
|
||||
graph := graphdb.MakeTestGraph(t)
|
||||
graph := graphdb.NewVersionedGraph(
|
||||
graphdb.MakeTestGraph(t), lnwire.GossipVersion1,
|
||||
)
|
||||
sourceNode := createTestNode(t)
|
||||
|
||||
require.NoError(t,
|
||||
@@ -1086,7 +1108,7 @@ func (c *testCtx) RestartBuilder(t *testing.T) {
|
||||
// start it.
|
||||
builder, err := NewBuilder(&Config{
|
||||
SelfNode: selfNode.PubKeyBytes,
|
||||
Graph: c.graph,
|
||||
Graph: c.graph.ChannelGraph,
|
||||
Chain: c.chain,
|
||||
ChainView: c.chainView,
|
||||
Notifier: c.builder.cfg.Notifier,
|
||||
@@ -1108,7 +1130,7 @@ func (c *testCtx) RestartBuilder(t *testing.T) {
|
||||
}
|
||||
|
||||
type testGraphInstance struct {
|
||||
graph *graphdb.ChannelGraph
|
||||
graph *graphdb.VersionedGraph
|
||||
|
||||
// aliasMap is a map from a node's alias to its public key. This type is
|
||||
// provided in order to allow easily look up from the human memorable
|
||||
@@ -1157,7 +1179,7 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
||||
|
||||
graphBuilder, err := NewBuilder(&Config{
|
||||
SelfNode: selfnode.PubKeyBytes,
|
||||
Graph: graphInstance.graph,
|
||||
Graph: graphInstance.graph.ChannelGraph,
|
||||
Chain: chain,
|
||||
ChainView: chainView,
|
||||
Notifier: notifier,
|
||||
|
||||
@@ -60,7 +60,7 @@ func testGraphMigration(ht *lntest.HarnessTest) {
|
||||
|
||||
// assertDBState is a helper function that asserts the state of the
|
||||
// graph DB.
|
||||
assertDBState := func(db graphdb.V1Store) {
|
||||
assertDBState := func(db graphdb.Store) {
|
||||
var (
|
||||
numNodes int
|
||||
edges = make(map[uint64]bool)
|
||||
@@ -127,7 +127,7 @@ func testGraphMigration(ht *lntest.HarnessTest) {
|
||||
}
|
||||
|
||||
func openNativeSQLGraphDB(ht *lntest.HarnessTest,
|
||||
hn *node.HarnessNode) graphdb.V1Store {
|
||||
hn *node.HarnessNode) graphdb.Store {
|
||||
|
||||
db := openNativeSQLDB(ht, hn)
|
||||
|
||||
|
||||
+14
-11
@@ -224,7 +224,6 @@ func (s *Server) ImportGraph(ctx context.Context,
|
||||
// Obtain the pointer to the global singleton channel graph.
|
||||
graphDB := s.cfg.GraphDB
|
||||
|
||||
var err error
|
||||
for _, rpcNode := range graph.Nodes {
|
||||
pubKeyBytes, err := parsePubKey(rpcNode.PubKey)
|
||||
if err != nil {
|
||||
@@ -258,7 +257,7 @@ func (s *Server) ImportGraph(ctx context.Context,
|
||||
Color: nodeColor,
|
||||
// NOTE: this is a workaround to ensure that
|
||||
// HaveAnnouncement() returns true so that the other
|
||||
// fields are properly persisted. However,
|
||||
// fields are properly persisted.
|
||||
AuthSigBytes: []byte{0},
|
||||
})
|
||||
|
||||
@@ -273,18 +272,12 @@ func (s *Server) ImportGraph(ctx context.Context,
|
||||
for _, rpcEdge := range graph.Edges {
|
||||
rpcEdge := rpcEdge
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: rpcEdge.ChannelId,
|
||||
ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
|
||||
Capacity: btcutil.Amount(rpcEdge.Capacity),
|
||||
}
|
||||
|
||||
edge.NodeKey1Bytes, err = parsePubKey(rpcEdge.Node1Pub)
|
||||
node1, err := parsePubKey(rpcEdge.Node1Pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
edge.NodeKey2Bytes, err = parsePubKey(rpcEdge.Node2Pub)
|
||||
node2, err := parsePubKey(rpcEdge.Node2Pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -293,7 +286,16 @@ func (s *Server) ImportGraph(ctx context.Context,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
edge.ChannelPoint = *channelPoint
|
||||
|
||||
edge, err := models.NewV1Channel(
|
||||
rpcEdge.ChannelId, *s.cfg.ActiveNetParams.GenesisHash,
|
||||
node1, node2, &models.ChannelV1Fields{},
|
||||
models.WithCapacity(btcutil.Amount(rpcEdge.Capacity)),
|
||||
models.WithChannelPoint(*channelPoint),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := graphDB.AddChannelEdge(ctx, edge); err != nil {
|
||||
return nil, fmt.Errorf("unable to add edge %v: %w",
|
||||
@@ -302,6 +304,7 @@ func (s *Server) ImportGraph(ctx context.Context,
|
||||
|
||||
makePolicy := func(rpcPolicy *lnrpc.RoutingPolicy) *models.ChannelEdgePolicy { //nolint:ll
|
||||
policy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: rpcEdge.ChannelId,
|
||||
LastUpdate: time.Unix(
|
||||
int64(rpcPolicy.LastUpdate), 0,
|
||||
|
||||
@@ -6,10 +6,12 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/zpay32"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -305,10 +307,19 @@ var shouldIncludeChannelTestCases = []struct {
|
||||
h.Mock.On(
|
||||
"FetchChannelEdgesByID", mock.Anything,
|
||||
).Once().Return(
|
||||
&models.ChannelEdgeInfo{
|
||||
NodeKey1Bytes: selectedPolicy,
|
||||
},
|
||||
func() *models.ChannelEdgeInfo {
|
||||
edge, err := models.NewV1Channel(
|
||||
0, chainhash.Hash{}, selectedPolicy,
|
||||
route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
)
|
||||
require.NoError(h.t, err)
|
||||
|
||||
return edge
|
||||
}(),
|
||||
//nolint:ll
|
||||
&models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
FeeBaseMSat: 1000,
|
||||
FeeProportionalMillionths: 20,
|
||||
TimeLockDelta: 13,
|
||||
@@ -355,7 +366,9 @@ var shouldIncludeChannelTestCases = []struct {
|
||||
).Once().Return(
|
||||
&models.ChannelEdgeInfo{},
|
||||
&models.ChannelEdgePolicy{},
|
||||
//nolint:ll
|
||||
&models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
FeeBaseMSat: 1000,
|
||||
FeeProportionalMillionths: 20,
|
||||
TimeLockDelta: 13,
|
||||
@@ -400,7 +413,9 @@ var shouldIncludeChannelTestCases = []struct {
|
||||
).Once().Return(
|
||||
&models.ChannelEdgeInfo{},
|
||||
&models.ChannelEdgePolicy{},
|
||||
//nolint:ll
|
||||
&models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
FeeBaseMSat: 1000,
|
||||
FeeProportionalMillionths: 20,
|
||||
TimeLockDelta: 13,
|
||||
|
||||
@@ -380,9 +380,23 @@ func (c ChanUpdateDisableFlags) IsEnabled() bool {
|
||||
return c == 0
|
||||
}
|
||||
|
||||
// String returns the bitfield flags as a string.
|
||||
// String returns a human-readable representation of the disable flags.
|
||||
func (c ChanUpdateDisableFlags) String() string {
|
||||
return fmt.Sprintf("%08b", c)
|
||||
if c.IsEnabled() {
|
||||
return "Enabled"
|
||||
}
|
||||
|
||||
incoming := c.IncomingDisabled()
|
||||
outgoing := c.OutgoingDisabled()
|
||||
|
||||
switch {
|
||||
case incoming && outgoing:
|
||||
return "Disabled(incoming&outgoing)"
|
||||
case incoming:
|
||||
return "Disabled(incoming)"
|
||||
default:
|
||||
return "Disabled(outgoing)"
|
||||
}
|
||||
}
|
||||
|
||||
// Record returns the tlv record for the disable flags.
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
graphdb "github.com/lightningnetwork/lnd/graph/db"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/netann"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -100,18 +102,28 @@ func createEdgePolicies(t *testing.T, channel *channeldb.OpenChannel,
|
||||
// bit.
|
||||
dir2 |= lnwire.ChanUpdateDirection
|
||||
|
||||
return &models.ChannelEdgeInfo{
|
||||
ChannelPoint: channel.FundingOutpoint,
|
||||
NodeKey1Bytes: pubkey1,
|
||||
NodeKey2Bytes: pubkey2,
|
||||
},
|
||||
pubkey1Vertex, err := route.NewVertexFromBytes(pubkey1[:])
|
||||
require.NoError(t, err)
|
||||
pubkey2Vertex, err := route.NewVertexFromBytes(pubkey2[:])
|
||||
require.NoError(t, err)
|
||||
|
||||
edgeInfo, err := models.NewV1Channel(
|
||||
channel.ShortChanID().ToUint64(), chainhash.Hash{},
|
||||
pubkey1Vertex, pubkey2Vertex, &models.ChannelV1Fields{},
|
||||
models.WithChannelPoint(channel.FundingOutpoint),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return edgeInfo,
|
||||
&models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: channel.ShortChanID().ToUint64(),
|
||||
ChannelFlags: dir1,
|
||||
LastUpdate: time.Now(),
|
||||
SigBytes: testSigBytes,
|
||||
},
|
||||
&models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: channel.ShortChanID().ToUint64(),
|
||||
ChannelFlags: dir2,
|
||||
LastUpdate: time.Now(),
|
||||
@@ -212,6 +224,7 @@ func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate1,
|
||||
timestamp := time.Unix(int64(update.Timestamp), 0)
|
||||
|
||||
policy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: update.ShortChannelID.ToUint64(),
|
||||
ChannelFlags: update.ChannelFlags,
|
||||
LastUpdate: timestamp,
|
||||
|
||||
@@ -34,48 +34,14 @@ const (
|
||||
// function is used to transform out database structs into the corresponding wire
|
||||
// structs for announcing new channels to other peers, or simply syncing up a
|
||||
// peer's initial routing table upon connect.
|
||||
func CreateChanAnnouncement(chanProof *models.ChannelAuthProof,
|
||||
chanInfo *models.ChannelEdgeInfo,
|
||||
func CreateChanAnnouncement(chanInfo *models.ChannelEdgeInfo,
|
||||
e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1,
|
||||
*lnwire.ChannelUpdate1, *lnwire.ChannelUpdate1, error) {
|
||||
|
||||
// First, using the parameters of the channel, along with the channel
|
||||
// authentication chanProof, we'll create re-create the original
|
||||
// authentication proof, we'll create re-create the original
|
||||
// authenticated channel announcement.
|
||||
chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID)
|
||||
chanAnn := &lnwire.ChannelAnnouncement1{
|
||||
ShortChannelID: chanID,
|
||||
NodeID1: chanInfo.NodeKey1Bytes,
|
||||
NodeID2: chanInfo.NodeKey2Bytes,
|
||||
ChainHash: chanInfo.ChainHash,
|
||||
BitcoinKey1: chanInfo.BitcoinKey1Bytes,
|
||||
BitcoinKey2: chanInfo.BitcoinKey2Bytes,
|
||||
Features: chanInfo.Features.RawFeatureVector,
|
||||
ExtraOpaqueData: chanInfo.ExtraOpaqueData,
|
||||
}
|
||||
|
||||
var err error
|
||||
chanAnn.BitcoinSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
chanProof.BitcoinSig1Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
chanAnn.BitcoinSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
chanProof.BitcoinSig2Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature(
|
||||
chanProof.NodeSig1Bytes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
chanAnn.NodeSig2, err = lnwire.NewSigFromECDSARawSignature(
|
||||
chanProof.NodeSig2Bytes,
|
||||
)
|
||||
chanAnn, err := chanInfo.ToChannelAnnouncement()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
@@ -40,29 +40,26 @@ func TestCreateChanAnnouncement(t *testing.T) {
|
||||
ExtraOpaqueData: []byte{0x1},
|
||||
}
|
||||
|
||||
chanProof := &models.ChannelAuthProof{
|
||||
NodeSig1Bytes: expChanAnn.NodeSig1.ToSignatureBytes(),
|
||||
NodeSig2Bytes: expChanAnn.NodeSig2.ToSignatureBytes(),
|
||||
BitcoinSig1Bytes: expChanAnn.BitcoinSig1.ToSignatureBytes(),
|
||||
BitcoinSig2Bytes: expChanAnn.BitcoinSig2.ToSignatureBytes(),
|
||||
}
|
||||
chanInfo := &models.ChannelEdgeInfo{
|
||||
ChainHash: expChanAnn.ChainHash,
|
||||
ChannelID: expChanAnn.ShortChannelID.ToUint64(),
|
||||
ChannelPoint: wire.OutPoint{Index: 1},
|
||||
Capacity: btcutil.SatoshiPerBitcoin,
|
||||
NodeKey1Bytes: key,
|
||||
NodeKey2Bytes: key,
|
||||
BitcoinKey1Bytes: key,
|
||||
BitcoinKey2Bytes: key,
|
||||
Features: lnwire.NewFeatureVector(
|
||||
features, lnwire.Features,
|
||||
),
|
||||
ExtraOpaqueData: expChanAnn.ExtraOpaqueData,
|
||||
}
|
||||
chanAnn, _, _, err := CreateChanAnnouncement(
|
||||
chanProof, chanInfo, nil, nil,
|
||||
chanProof := models.NewV1ChannelAuthProof(
|
||||
expChanAnn.NodeSig1.ToSignatureBytes(),
|
||||
expChanAnn.NodeSig2.ToSignatureBytes(),
|
||||
expChanAnn.BitcoinSig1.ToSignatureBytes(),
|
||||
expChanAnn.BitcoinSig2.ToSignatureBytes(),
|
||||
)
|
||||
chanInfo, err := models.NewV1Channel(
|
||||
expChanAnn.ShortChannelID.ToUint64(), expChanAnn.ChainHash,
|
||||
key, key, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: key,
|
||||
BitcoinKey2Bytes: key,
|
||||
ExtraOpaqueData: expChanAnn.ExtraOpaqueData,
|
||||
},
|
||||
models.WithChanProof(chanProof),
|
||||
models.WithChannelPoint(wire.OutPoint{Index: 1}),
|
||||
models.WithCapacity(btcutil.SatoshiPerBitcoin),
|
||||
models.WithFeatures(features),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
chanAnn, _, _, err := CreateChanAnnouncement(chanInfo, nil, nil)
|
||||
require.NoError(t, err, "unable to create channel announcement")
|
||||
|
||||
assert.Equal(t, chanAnn, expChanAnn)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
// Manager manages the node's local channels. The only operation that is
|
||||
@@ -328,24 +329,42 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel,
|
||||
"script: %v", err)
|
||||
}
|
||||
|
||||
info := &models.ChannelEdgeInfo{
|
||||
ChannelID: shortChanID.ToUint64(),
|
||||
ChainHash: channel.ChainHash,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
Capacity: channel.Capacity,
|
||||
ChannelPoint: channel.FundingOutpoint,
|
||||
FundingScript: fn.Some(fundingScript),
|
||||
nodeKey1, err := route.NewVertexFromBytes(nodeKey1Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
nodeKey2, err := route.NewVertexFromBytes(nodeKey2Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bitcoinKey1, err := route.NewVertexFromBytes(bitcoinKey1Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bitcoinKey2, err := route.NewVertexFromBytes(bitcoinKey2Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
copy(info.NodeKey1Bytes[:], nodeKey1Bytes)
|
||||
copy(info.NodeKey2Bytes[:], nodeKey2Bytes)
|
||||
copy(info.BitcoinKey1Bytes[:], bitcoinKey1Bytes)
|
||||
copy(info.BitcoinKey2Bytes[:], bitcoinKey2Bytes)
|
||||
info, err := models.NewV1Channel(
|
||||
shortChanID.ToUint64(), channel.ChainHash, nodeKey1, nodeKey2,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: bitcoinKey1,
|
||||
BitcoinKey2Bytes: bitcoinKey2,
|
||||
},
|
||||
models.WithCapacity(channel.Capacity),
|
||||
models.WithChannelPoint(channel.FundingOutpoint),
|
||||
models.WithFundingScript(fundingScript),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Construct a dummy channel edge policy with default values that will
|
||||
// be updated with the new values in the call to processChan below.
|
||||
timeLockDelta := uint16(r.DefaultRoutingPolicy.TimeLockDelta)
|
||||
edge := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: shortChanID.ToUint64(),
|
||||
LastUpdate: timestamp,
|
||||
TimeLockDelta: timeLockDelta,
|
||||
@@ -449,7 +468,7 @@ func (r *Manager) updateEdge(chanPoint wire.OutPoint,
|
||||
}
|
||||
|
||||
// Clear signature to help prevent usage of the previous signature.
|
||||
edge.SetSigBytes(nil)
|
||||
edge.SigBytes = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@ import (
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/discovery"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/graph/db/models"
|
||||
"github.com/lightningnetwork/lnd/keychain"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -64,6 +64,7 @@ func TestManager(t *testing.T) {
|
||||
}
|
||||
|
||||
currentPolicy := models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
MinHTLC: minHTLC,
|
||||
MessageFlags: lnwire.ChanUpdateRequiredMaxHtlc,
|
||||
}
|
||||
@@ -211,10 +212,21 @@ func TestManager(t *testing.T) {
|
||||
newPolicy: newPolicy,
|
||||
channelSet: []channel{
|
||||
{
|
||||
edgeInfo: &models.ChannelEdgeInfo{
|
||||
Capacity: chanCap,
|
||||
ChannelPoint: chanPointValid,
|
||||
},
|
||||
//nolint:ll
|
||||
edgeInfo: func() *models.ChannelEdgeInfo {
|
||||
info, err := models.NewV1Channel(
|
||||
0,
|
||||
chainhash.Hash{},
|
||||
route.Vertex{},
|
||||
route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
models.WithCapacity(chanCap),
|
||||
models.WithChannelPoint(chanPointValid),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return info
|
||||
}(),
|
||||
},
|
||||
},
|
||||
specifiedChanPoints: []wire.OutPoint{chanPointValid},
|
||||
@@ -229,10 +241,21 @@ func TestManager(t *testing.T) {
|
||||
newPolicy: newPolicy,
|
||||
channelSet: []channel{
|
||||
{
|
||||
edgeInfo: &models.ChannelEdgeInfo{
|
||||
Capacity: chanCap,
|
||||
ChannelPoint: chanPointValid,
|
||||
},
|
||||
//nolint:ll
|
||||
edgeInfo: func() *models.ChannelEdgeInfo {
|
||||
info, err := models.NewV1Channel(
|
||||
0,
|
||||
chainhash.Hash{},
|
||||
route.Vertex{},
|
||||
route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
models.WithCapacity(chanCap),
|
||||
models.WithChannelPoint(chanPointValid),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return info
|
||||
}(),
|
||||
},
|
||||
},
|
||||
specifiedChanPoints: []wire.OutPoint{},
|
||||
@@ -247,10 +270,22 @@ func TestManager(t *testing.T) {
|
||||
newPolicy: newPolicy,
|
||||
channelSet: []channel{
|
||||
{
|
||||
edgeInfo: &models.ChannelEdgeInfo{
|
||||
Capacity: chanCap,
|
||||
ChannelPoint: chanPointValid,
|
||||
},
|
||||
//nolint:ll
|
||||
edgeInfo: func() *models.ChannelEdgeInfo {
|
||||
info, err := models.NewV1Channel(
|
||||
0,
|
||||
chainhash.Hash{},
|
||||
route.Vertex{},
|
||||
route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
models.WithCapacity(chanCap),
|
||||
models.WithChannelPoint(chanPointValid),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
return info
|
||||
}(),
|
||||
},
|
||||
},
|
||||
specifiedChanPoints: []wire.OutPoint{chanPointMissing},
|
||||
@@ -269,10 +304,22 @@ func TestManager(t *testing.T) {
|
||||
newPolicy: noMaxHtlcPolicy,
|
||||
channelSet: []channel{
|
||||
{
|
||||
edgeInfo: &models.ChannelEdgeInfo{
|
||||
Capacity: chanCap,
|
||||
ChannelPoint: chanPointValid,
|
||||
},
|
||||
//nolint:ll
|
||||
edgeInfo: func() *models.ChannelEdgeInfo {
|
||||
info, err := models.NewV1Channel(
|
||||
0,
|
||||
chainhash.Hash{},
|
||||
route.Vertex{},
|
||||
route.Vertex{},
|
||||
&models.ChannelV1Fields{},
|
||||
models.WithCapacity(chanCap),
|
||||
models.WithChannelPoint(chanPointValid),
|
||||
)
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
return info
|
||||
}(),
|
||||
},
|
||||
},
|
||||
specifiedChanPoints: []wire.OutPoint{chanPointValid},
|
||||
@@ -391,23 +438,21 @@ func TestCreateEdgeLower(t *testing.T) {
|
||||
fundingScript, err := funding.MakeFundingScript(channel)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedInfo := &models.ChannelEdgeInfo{
|
||||
ChannelID: 8,
|
||||
ChainHash: channel.ChainHash,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
Capacity: 9,
|
||||
ChannelPoint: channel.FundingOutpoint,
|
||||
NodeKey1Bytes: sp,
|
||||
NodeKey2Bytes: rp,
|
||||
BitcoinKey1Bytes: [33]byte(
|
||||
localMultisigKey.SerializeCompressed()),
|
||||
BitcoinKey2Bytes: [33]byte(
|
||||
remoteMultisigKey.SerializeCompressed()),
|
||||
AuthProof: nil,
|
||||
ExtraOpaqueData: nil,
|
||||
FundingScript: fn.Some(fundingScript),
|
||||
}
|
||||
btcKey1 := route.NewVertex(localMultisigKey)
|
||||
btcKey2 := route.NewVertex(remoteMultisigKey)
|
||||
expectedInfo, err := models.NewV1Channel(
|
||||
8, channel.ChainHash, sp, rp, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
},
|
||||
models.WithCapacity(9),
|
||||
models.WithChannelPoint(channel.FundingOutpoint),
|
||||
models.WithFundingScript(fundingScript),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedEdge := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: 8,
|
||||
LastUpdate: timestamp,
|
||||
TimeLockDelta: 7,
|
||||
@@ -484,23 +529,22 @@ func TestCreateEdgeHigher(t *testing.T) {
|
||||
fundingScript, err := funding.MakeFundingScript(channel)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedInfo := &models.ChannelEdgeInfo{
|
||||
ChannelID: 8,
|
||||
ChainHash: channel.ChainHash,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
Capacity: 9,
|
||||
ChannelPoint: channel.FundingOutpoint,
|
||||
NodeKey1Bytes: rp,
|
||||
NodeKey2Bytes: sp,
|
||||
BitcoinKey1Bytes: [33]byte(
|
||||
remoteMultisigKey.SerializeCompressed()),
|
||||
BitcoinKey2Bytes: [33]byte(
|
||||
localMultisigKey.SerializeCompressed()),
|
||||
AuthProof: nil,
|
||||
ExtraOpaqueData: nil,
|
||||
FundingScript: fn.Some(fundingScript),
|
||||
}
|
||||
btcKey1 := route.NewVertex(remoteMultisigKey)
|
||||
btcKey2 := route.NewVertex(localMultisigKey)
|
||||
expectedInfo, err := models.NewV1Channel(
|
||||
8, channel.ChainHash, rp, sp,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
},
|
||||
models.WithCapacity(9),
|
||||
models.WithChannelPoint(channel.FundingOutpoint),
|
||||
models.WithFundingScript(fundingScript),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedEdge := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
ChannelID: 8,
|
||||
LastUpdate: timestamp,
|
||||
TimeLockDelta: 7,
|
||||
|
||||
+90
-63
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
@@ -99,12 +100,12 @@ var (
|
||||
_ = testSScalar.SetByteSlice(testSBytes)
|
||||
testSig = ecdsa.NewSignature(testRScalar, testSScalar)
|
||||
|
||||
testAuthProof = models.ChannelAuthProof{
|
||||
NodeSig1Bytes: testSig.Serialize(),
|
||||
NodeSig2Bytes: testSig.Serialize(),
|
||||
BitcoinSig1Bytes: testSig.Serialize(),
|
||||
BitcoinSig2Bytes: testSig.Serialize(),
|
||||
}
|
||||
testAuthProof = *models.NewV1ChannelAuthProof(
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
testSig.Serialize(),
|
||||
)
|
||||
)
|
||||
|
||||
// noProbabilitySource is used in testing to return the same probability 1 for
|
||||
@@ -345,18 +346,28 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
|
||||
// We first insert the existence of the edge between the two
|
||||
// nodes.
|
||||
edgeInfo := models.ChannelEdgeInfo{
|
||||
ChannelID: edge.ChannelID,
|
||||
AuthProof: &testAuthProof,
|
||||
ChannelPoint: fundingPoint,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
Capacity: btcutil.Amount(edge.Capacity),
|
||||
}
|
||||
var node1Vertex, node2Vertex route.Vertex
|
||||
copy(node1Vertex[:], node1Bytes)
|
||||
copy(node2Vertex[:], node2Bytes)
|
||||
|
||||
copy(edgeInfo.NodeKey1Bytes[:], node1Bytes)
|
||||
copy(edgeInfo.NodeKey2Bytes[:], node2Bytes)
|
||||
copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes)
|
||||
copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes)
|
||||
var btcKey1, btcKey2 route.Vertex
|
||||
copy(btcKey1[:], node1Bytes)
|
||||
copy(btcKey2[:], node2Bytes)
|
||||
|
||||
edgeInfo, err := models.NewV1Channel(
|
||||
edge.ChannelID, *chaincfg.SimNetParams.GenesisHash,
|
||||
node1Vertex, node2Vertex,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: btcKey1,
|
||||
BitcoinKey2Bytes: btcKey2,
|
||||
},
|
||||
models.WithChanProof(&testAuthProof),
|
||||
models.WithChannelPoint(fundingPoint),
|
||||
models.WithCapacity(btcutil.Amount(edge.Capacity)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shortID := lnwire.NewShortChanIDFromInt(edge.ChannelID)
|
||||
links[shortID] = &mockLink{
|
||||
@@ -365,7 +376,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
),
|
||||
}
|
||||
|
||||
err = graph.AddChannelEdge(ctx, &edgeInfo)
|
||||
err = graph.AddChannelEdge(ctx, edgeInfo)
|
||||
if err != nil && !errors.Is(err, graphdb.ErrEdgeAlreadyExist) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -378,6 +389,7 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: lnwire.ChanUpdateMsgFlags(edge.MessageFlags),
|
||||
ChannelFlags: channelFlags,
|
||||
@@ -395,12 +407,12 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
// We also store the channel IDs info for each of the node.
|
||||
node1Vertex, err := route.NewVertexFromBytes(node1Bytes)
|
||||
node1Vertex, err = route.NewVertexFromBytes(node1Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node2Vertex, err := route.NewVertexFromBytes(node2Bytes)
|
||||
node2Vertex, err = route.NewVertexFromBytes(node2Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -417,7 +429,10 @@ func parseTestGraph(t *testing.T, useCache bool, path string) (
|
||||
}
|
||||
|
||||
return &testGraphInstance{
|
||||
graph: graph,
|
||||
graph: graph,
|
||||
v1Graph: graphdb.NewVersionedGraph(
|
||||
graph, lnwire.GossipVersion1,
|
||||
),
|
||||
mcBackend: mcBackend,
|
||||
aliasMap: aliasMap,
|
||||
privKeyMap: privKeyMap,
|
||||
@@ -487,6 +502,7 @@ type testChannel struct {
|
||||
|
||||
type testGraphInstance struct {
|
||||
graph *graphdb.ChannelGraph
|
||||
v1Graph *graphdb.VersionedGraph
|
||||
mcBackend kvdb.Backend
|
||||
|
||||
// aliasMap is a map from a node's alias to its public key. This type is
|
||||
@@ -673,20 +689,21 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
|
||||
// We first insert the existence of the edge between the two
|
||||
// nodes.
|
||||
edgeInfo := models.ChannelEdgeInfo{
|
||||
ChannelID: channelID,
|
||||
AuthProof: &testAuthProof,
|
||||
ChannelPoint: *fundingPoint,
|
||||
Capacity: testChannel.Capacity,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
|
||||
NodeKey1Bytes: node1Vertex,
|
||||
BitcoinKey1Bytes: node1Vertex,
|
||||
NodeKey2Bytes: node2Vertex,
|
||||
BitcoinKey2Bytes: node2Vertex,
|
||||
edgeInfo, err := models.NewV1Channel(
|
||||
channelID, *chaincfg.SimNetParams.GenesisHash,
|
||||
node1Vertex, node2Vertex, &models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: node1Vertex,
|
||||
BitcoinKey2Bytes: node2Vertex,
|
||||
},
|
||||
models.WithChanProof(&testAuthProof),
|
||||
models.WithChannelPoint(*fundingPoint),
|
||||
models.WithCapacity(testChannel.Capacity),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = graph.AddChannelEdge(ctx, &edgeInfo)
|
||||
err = graph.AddChannelEdge(ctx, edgeInfo)
|
||||
if err != nil && !errors.Is(err, graphdb.ErrEdgeAlreadyExist) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -724,7 +741,9 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
channelFlags |= lnwire.ChanUpdateDisabled
|
||||
}
|
||||
|
||||
//nolint:ll
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
ChannelFlags: channelFlags,
|
||||
@@ -756,7 +775,9 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
}
|
||||
channelFlags |= lnwire.ChanUpdateDirection
|
||||
|
||||
//nolint:ll
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
MessageFlags: msgFlags,
|
||||
ChannelFlags: channelFlags,
|
||||
@@ -781,7 +802,10 @@ func createTestGraphFromChannels(t *testing.T, useCache bool,
|
||||
}
|
||||
|
||||
return &testGraphInstance{
|
||||
graph: graph,
|
||||
graph: graph,
|
||||
v1Graph: graphdb.NewVersionedGraph(
|
||||
graph, lnwire.GossipVersion1,
|
||||
),
|
||||
mcBackend: graphBackend,
|
||||
aliasMap: aliasMap,
|
||||
privKeyMap: privKeyMap,
|
||||
@@ -1095,7 +1119,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
||||
expectedHops := test.expectedHops
|
||||
expectedHopCount := len(expectedHops)
|
||||
|
||||
sourceNode, err := graphInstance.graph.SourceNode(ctx)
|
||||
sourceNode, err := graphInstance.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
sourceVertex := route.Vertex(sourceNode.PubKeyBytes)
|
||||
|
||||
@@ -1107,7 +1131,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
|
||||
paymentAmt := lnwire.NewMSatFromSatoshis(test.paymentAmt)
|
||||
target := graphInstance.aliasMap[test.target]
|
||||
path, err := dbFindPath(
|
||||
graphInstance.graph, nil, &mockBandwidthHints{},
|
||||
graphInstance.v1Graph, nil, &mockBandwidthHints{},
|
||||
&RestrictParams{
|
||||
FeeLimit: test.feeLimit,
|
||||
ProbabilitySource: noProbabilitySource,
|
||||
@@ -1237,7 +1261,7 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||
@@ -1282,7 +1306,8 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
|
||||
[]*unifiedEdge, error) {
|
||||
|
||||
return dbFindPath(
|
||||
graph.graph, additionalEdges, &mockBandwidthHints{},
|
||||
graph.v1Graph, additionalEdges,
|
||||
&mockBandwidthHints{},
|
||||
r, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, doge.PubKeyBytes, paymentAmt,
|
||||
0, 0,
|
||||
@@ -1322,7 +1347,7 @@ func runPathFindingWithBlindedPathDuplicateHop(t *testing.T, useCache bool) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
paymentAmt := lnwire.NewMSatFromSatoshis(100)
|
||||
@@ -1396,7 +1421,7 @@ func runPathFindingWithBlindedPathDuplicateHop(t *testing.T, useCache bool) {
|
||||
[]*unifiedEdge, error) {
|
||||
|
||||
return dbFindPath(
|
||||
graph.graph, blindedPath, &mockBandwidthHints{},
|
||||
graph.v1Graph, blindedPath, &mockBandwidthHints{},
|
||||
r, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, dummyTarget, paymentAmt,
|
||||
0, 0,
|
||||
@@ -1456,7 +1481,7 @@ func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
|
||||
}
|
||||
|
||||
path, err := dbFindPath(
|
||||
ctx.graph, additionalEdges, ctx.bandwidthHints,
|
||||
ctx.v1Graph, additionalEdges, ctx.bandwidthHints,
|
||||
&ctx.restrictParams, &ctx.pathFindingConfig, ctx.source, target,
|
||||
paymentAmt, ctx.timePref, 0,
|
||||
)
|
||||
@@ -1809,7 +1834,7 @@ func runPathNotAvailable(t *testing.T, useCache bool) {
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
// With the test graph loaded, we'll test that queries for target that
|
||||
@@ -1822,7 +1847,7 @@ func runPathNotAvailable(t *testing.T, useCache bool) {
|
||||
copy(unknownNode[:], unknownNodeBytes)
|
||||
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, unknownNode, 100, 0, 0,
|
||||
)
|
||||
@@ -1865,14 +1890,14 @@ func runDestTLVGraphFallback(t *testing.T, useCache bool) {
|
||||
|
||||
ctx := newPathFindingTestContext(t, useCache, testChannels, "roasbeef")
|
||||
|
||||
sourceNode, err := ctx.graph.SourceNode(t.Context())
|
||||
sourceNode, err := ctx.v1Graph.SourceNode(t.Context())
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
find := func(r *RestrictParams,
|
||||
target route.Vertex) ([]*unifiedEdge, error) {
|
||||
|
||||
return dbFindPath(
|
||||
ctx.graph, nil, &mockBandwidthHints{},
|
||||
ctx.v1Graph, nil, &mockBandwidthHints{},
|
||||
r, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, 100, 0, 0,
|
||||
)
|
||||
@@ -2084,7 +2109,7 @@ func runPathInsufficientCapacity(t *testing.T, useCache bool) {
|
||||
require.NoError(t, err, "unable to create graph")
|
||||
|
||||
ctx := t.Context()
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
// Next, test that attempting to find a path in which the current
|
||||
@@ -2099,7 +2124,7 @@ func runPathInsufficientCapacity(t *testing.T, useCache bool) {
|
||||
|
||||
payAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2115,7 +2140,7 @@ func runRouteFailMinHTLC(t *testing.T, useCache bool) {
|
||||
require.NoError(t, err, "unable to create graph")
|
||||
|
||||
ctx := t.Context()
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
// We'll not attempt to route an HTLC of 10 SAT from roasbeef to Son
|
||||
@@ -2124,7 +2149,7 @@ func runRouteFailMinHTLC(t *testing.T, useCache bool) {
|
||||
target := graph.aliasMap["songoku"]
|
||||
payAmt := lnwire.MilliSatoshi(10)
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2200,7 +2225,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
require.NoError(t, err, "unable to create graph")
|
||||
|
||||
ctx := t.Context()
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
// First, we'll try to route from roasbeef -> sophon. This should
|
||||
@@ -2208,7 +2233,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
target := graph.aliasMap["sophon"]
|
||||
payAmt := lnwire.NewMSatFromSatoshis(105000)
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2232,7 +2257,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
}
|
||||
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2252,7 +2277,7 @@ func runRouteFailDisabledEdge(t *testing.T, useCache bool) {
|
||||
// If we attempt to route through that edge, we should get a failure as
|
||||
// it is no longer eligible.
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2269,7 +2294,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
require.NoError(t, err, "unable to create graph")
|
||||
|
||||
ctx := t.Context()
|
||||
sourceNode, err := graph.graph.SourceNode(ctx)
|
||||
sourceNode, err := graph.v1Graph.SourceNode(ctx)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
// First, we'll try to route from roasbeef -> sophon. This should
|
||||
@@ -2278,7 +2303,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
target := graph.aliasMap["sophon"]
|
||||
payAmt := lnwire.NewMSatFromSatoshis(50000)
|
||||
path, err := dbFindPath(
|
||||
graph.graph, nil, &mockBandwidthHints{},
|
||||
graph.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2299,7 +2324,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
// Since both these edges has a bandwidth of zero, no path should be
|
||||
// found.
|
||||
_, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
graph.v1Graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2314,7 +2339,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
// Now, if we attempt to route again, we should find the path via
|
||||
// phamnuven, as the other source edge won't be considered.
|
||||
path, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
graph.v1Graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -2340,7 +2365,7 @@ func runPathSourceEdgesBandwidth(t *testing.T, useCache bool) {
|
||||
// Since we ignore disable flags for local channels, a path should
|
||||
// still be found.
|
||||
path, err = dbFindPath(
|
||||
graph.graph, nil, bandwidths,
|
||||
graph.v1Graph, nil, bandwidths,
|
||||
noRestrictions, testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, payAmt, 0, 0,
|
||||
)
|
||||
@@ -3184,6 +3209,7 @@ func runInboundFees(t *testing.T, useCache bool) {
|
||||
type pathFindingTestContext struct {
|
||||
t *testing.T
|
||||
graph *graphdb.ChannelGraph
|
||||
v1Graph *graphdb.VersionedGraph
|
||||
restrictParams RestrictParams
|
||||
bandwidthHints bandwidthHints
|
||||
pathFindingConfig PathFindingConfig
|
||||
@@ -3201,7 +3227,7 @@ func newPathFindingTestContext(t *testing.T, useCache bool,
|
||||
)
|
||||
require.NoError(t, err, "unable to create graph")
|
||||
|
||||
sourceNode, err := testGraphInstance.graph.SourceNode(
|
||||
sourceNode, err := testGraphInstance.v1Graph.SourceNode(
|
||||
t.Context(),
|
||||
)
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
@@ -3212,6 +3238,7 @@ func newPathFindingTestContext(t *testing.T, useCache bool,
|
||||
source: route.Vertex(sourceNode.PubKeyBytes),
|
||||
pathFindingConfig: *testPathFindingConfig,
|
||||
graph: testGraphInstance.graph,
|
||||
v1Graph: testGraphInstance.v1Graph,
|
||||
restrictParams: *noRestrictions,
|
||||
bandwidthHints: &mockBandwidthHints{},
|
||||
}
|
||||
@@ -3247,7 +3274,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex,
|
||||
error) {
|
||||
|
||||
return dbFindPath(
|
||||
c.graph, nil, c.bandwidthHints, &c.restrictParams,
|
||||
c.v1Graph, nil, c.bandwidthHints, &c.restrictParams,
|
||||
&c.pathFindingConfig, c.source, target, amt, c.timePref, 0,
|
||||
)
|
||||
}
|
||||
@@ -3255,7 +3282,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex,
|
||||
func (c *pathFindingTestContext) findBlindedPaths(
|
||||
restrictions *blindedPathRestrictions) ([][]blindedHop, error) {
|
||||
|
||||
return dbFindBlindedPaths(c.graph, restrictions)
|
||||
return dbFindBlindedPaths(c.v1Graph, restrictions)
|
||||
}
|
||||
|
||||
func (c *pathFindingTestContext) assertPath(path []*unifiedEdge,
|
||||
@@ -3277,7 +3304,7 @@ func (c *pathFindingTestContext) assertPath(path []*unifiedEdge,
|
||||
|
||||
// dbFindPath calls findPath after getting a db transaction from the database
|
||||
// graph.
|
||||
func dbFindPath(graph *graphdb.ChannelGraph,
|
||||
func dbFindPath(graph *graphdb.VersionedGraph,
|
||||
additionalEdges map[route.Vertex][]AdditionalEdge,
|
||||
bandwidthHints bandwidthHints,
|
||||
r *RestrictParams, cfg *PathFindingConfig,
|
||||
@@ -3315,7 +3342,7 @@ func dbFindPath(graph *graphdb.ChannelGraph,
|
||||
|
||||
// dbFindBlindedPaths calls findBlindedPaths after getting a db transaction from
|
||||
// the database graph.
|
||||
func dbFindBlindedPaths(graph *graphdb.ChannelGraph,
|
||||
func dbFindBlindedPaths(graph *graphdb.VersionedGraph,
|
||||
restrictions *blindedPathRestrictions) ([][]blindedHop, error) {
|
||||
|
||||
sourceNode, err := graph.SourceNode(context.Background())
|
||||
|
||||
+47
-44
@@ -61,7 +61,8 @@ type testCtx struct {
|
||||
|
||||
graphBuilder *mockGraphBuilder
|
||||
|
||||
graph *graphdb.ChannelGraph
|
||||
graph *graphdb.ChannelGraph
|
||||
v1Graph *graphdb.VersionedGraph
|
||||
|
||||
aliases map[string]route.Vertex
|
||||
|
||||
@@ -131,7 +132,7 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
sourceNode, err := graphInstance.graph.SourceNode(t.Context())
|
||||
sourceNode, err := graphInstance.v1Graph.SourceNode(t.Context())
|
||||
require.NoError(t, err)
|
||||
sessionSource := &SessionSource{
|
||||
GraphSessionFactory: graphInstance.graph,
|
||||
@@ -170,9 +171,12 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
|
||||
router: router,
|
||||
graphBuilder: graphBuilder,
|
||||
graph: graphInstance.graph,
|
||||
aliases: graphInstance.aliasMap,
|
||||
privKeys: graphInstance.privKeyMap,
|
||||
channelIDs: graphInstance.channelIDs,
|
||||
v1Graph: graphdb.NewVersionedGraph(
|
||||
graphInstance.graph, lnwire.GossipVersion1,
|
||||
),
|
||||
aliases: graphInstance.aliasMap,
|
||||
privKeys: graphInstance.privKeyMap,
|
||||
channelIDs: graphInstance.channelIDs,
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
@@ -1201,7 +1205,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
||||
var preImage [32]byte
|
||||
copy(preImage[:], bytes.Repeat([]byte{9}, 32))
|
||||
|
||||
sourceNode, err := ctx.graph.SourceNode(t.Context())
|
||||
sourceNode, err := ctx.v1Graph.SourceNode(t.Context())
|
||||
require.NoError(t, err, "unable to fetch source node")
|
||||
|
||||
amt := lnwire.MilliSatoshi(100)
|
||||
@@ -1212,7 +1216,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
||||
// the edge weighting, we should select the direct path over the 2 hop
|
||||
// path even though the direct path has a higher potential time lock.
|
||||
path, err := dbFindPath(
|
||||
ctx.graph, nil, &mockBandwidthHints{},
|
||||
ctx.v1Graph, nil, &mockBandwidthHints{},
|
||||
noRestrictions,
|
||||
testPathFindingConfig,
|
||||
sourceNode.PubKeyBytes, target, amt, 0, 0,
|
||||
@@ -2717,11 +2721,11 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
copy(pub2[:], priv2.PubKey().SerializeCompressed())
|
||||
|
||||
// The two nodes we are about to add should not exist yet.
|
||||
_, exists1, err := ctx.graph.HasNode(ctxb, pub1)
|
||||
exists1, err := ctx.v1Graph.HasNode(ctxb, pub1)
|
||||
require.NoError(t, err, "unable to query graph")
|
||||
require.False(t, exists1)
|
||||
|
||||
_, exists2, err := ctx.graph.HasNode(ctxb, pub2)
|
||||
exists2, err := ctx.v1Graph.HasNode(ctxb, pub2)
|
||||
require.NoError(t, err, "unable to query graph")
|
||||
require.False(t, exists2)
|
||||
|
||||
@@ -2734,20 +2738,20 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err, "unable to create channel edge")
|
||||
|
||||
edge := &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
NodeKey1Bytes: pub1,
|
||||
NodeKey2Bytes: pub2,
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
AuthProof: nil,
|
||||
}
|
||||
edge, err := models.NewV1Channel(
|
||||
chanID.ToUint64(), chainhash.Hash{}, pub1, pub2,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: pub1,
|
||||
BitcoinKey2Bytes: pub2,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, ctx.graph.AddChannelEdge(ctxb, edge))
|
||||
|
||||
// We must add the edge policy to be able to use the edge for route
|
||||
// finding.
|
||||
edgePolicy := &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
@@ -2763,6 +2767,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
|
||||
// Create edge in the other direction as well.
|
||||
edgePolicy = &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
@@ -2778,11 +2783,11 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
|
||||
// After adding the edge between the two previously unknown nodes, they
|
||||
// should have been added to the graph.
|
||||
_, exists1, err = ctx.graph.HasNode(ctxb, pub1)
|
||||
exists1, err = ctx.v1Graph.HasNode(ctxb, pub1)
|
||||
require.NoError(t, err, "unable to query graph")
|
||||
require.True(t, exists1)
|
||||
|
||||
_, exists2, err = ctx.graph.HasNode(ctxb, pub2)
|
||||
exists2, err = ctx.v1Graph.HasNode(ctxb, pub2)
|
||||
require.NoError(t, err, "unable to query graph")
|
||||
require.True(t, exists2)
|
||||
|
||||
@@ -2814,19 +2819,22 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
10000, 510)
|
||||
require.NoError(t, err, "unable to create channel edge")
|
||||
|
||||
edge = &models.ChannelEdgeInfo{
|
||||
ChannelID: chanID.ToUint64(),
|
||||
Features: lnwire.EmptyFeatureVector(),
|
||||
AuthProof: nil,
|
||||
}
|
||||
copy(edge.NodeKey1Bytes[:], node1Bytes)
|
||||
edge.NodeKey2Bytes = node2Bytes
|
||||
copy(edge.BitcoinKey1Bytes[:], node1Bytes)
|
||||
edge.BitcoinKey2Bytes = node2Bytes
|
||||
node1Vertex, err := route.NewVertexFromBytes(node1Bytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
edge, err = models.NewV1Channel(
|
||||
chanID.ToUint64(), chainhash.Hash{}, node1Vertex, node2Bytes,
|
||||
&models.ChannelV1Fields{
|
||||
BitcoinKey1Bytes: node1Vertex,
|
||||
BitcoinKey2Bytes: node2Bytes,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, ctx.graph.AddChannelEdge(ctxb, edge))
|
||||
|
||||
edgePolicy = &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
@@ -2841,6 +2849,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
require.NoError(t, ctx.graph.UpdateEdgePolicy(ctxb, edgePolicy))
|
||||
|
||||
edgePolicy = &models.ChannelEdgePolicy{
|
||||
Version: lnwire.GossipVersion1,
|
||||
SigBytes: testSig.Serialize(),
|
||||
ChannelID: edge.ChannelID,
|
||||
LastUpdate: testTime,
|
||||
@@ -2907,12 +2916,12 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
|
||||
_, _, err = ctx.router.FindRoute(req)
|
||||
require.NoError(t, err, "unable to find any routes")
|
||||
|
||||
copy1, err := ctx.graph.FetchNode(ctxb, pub1)
|
||||
copy1, err := ctx.v1Graph.FetchNode(ctxb, pub1)
|
||||
require.NoError(t, err, "unable to fetch node")
|
||||
|
||||
require.Equal(t, n1.Alias, copy1.Alias)
|
||||
|
||||
copy2, err := ctx.graph.FetchNode(ctxb, pub2)
|
||||
copy2, err := ctx.v1Graph.FetchNode(ctxb, pub2)
|
||||
require.NoError(t, err, "unable to fetch node")
|
||||
|
||||
require.Equal(t, n2.Alias, copy2.Alias)
|
||||
@@ -2959,19 +2968,13 @@ func (m *mockGraphBuilder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
err := m.updateEdge(&models.ChannelEdgePolicy{
|
||||
SigBytes: msg.Signature.ToSignatureBytes(),
|
||||
ChannelID: msg.ShortChannelID.ToUint64(),
|
||||
LastUpdate: time.Unix(int64(msg.Timestamp), 0),
|
||||
MessageFlags: msg.MessageFlags,
|
||||
ChannelFlags: msg.ChannelFlags,
|
||||
TimeLockDelta: msg.TimeLockDelta,
|
||||
MinHTLC: msg.HtlcMinimumMsat,
|
||||
MaxHTLC: msg.HtlcMaximumMsat,
|
||||
FeeBaseMSat: lnwire.MilliSatoshi(msg.BaseFee),
|
||||
FeeProportionalMillionths: lnwire.MilliSatoshi(msg.FeeRate),
|
||||
ExtraOpaqueData: msg.ExtraOpaqueData,
|
||||
})
|
||||
update, err := models.ChanEdgePolicyFromWire(
|
||||
msg.ShortChannelID.ToUint64(), msg,
|
||||
)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
err = m.updateEdge(update)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ func (u *unifiedEdge) amtInRange(amt lnwire.MilliSatoshi) bool {
|
||||
}
|
||||
|
||||
// Skip channels for which this htlc is too large.
|
||||
if u.policy.MessageFlags.HasMaxHtlc() &&
|
||||
if u.policy.HasMaxHTLC &&
|
||||
amt > u.policy.MaxHTLC {
|
||||
|
||||
log.Tracef("Exceeds policy's MaxHTLC: amt=%v, MaxHTLC=%v",
|
||||
@@ -376,7 +376,7 @@ func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
||||
}
|
||||
|
||||
// For network channels, skip the disabled ones.
|
||||
if edge.policy.IsDisabled() {
|
||||
if edge.policy.IsDisabled {
|
||||
log.Debugf("Skipped edge %v due to it being disabled",
|
||||
edge.policy.ChannelID)
|
||||
continue
|
||||
@@ -385,7 +385,7 @@ func (u *edgeUnifier) getEdgeNetwork(netAmtReceived lnwire.MilliSatoshi,
|
||||
// Track the maximal capacity for usable channels. If we don't
|
||||
// know the capacity, we fall back to MaxHTLC.
|
||||
capMsat := lnwire.NewMSatFromSatoshis(edge.capacity)
|
||||
if capMsat == 0 && edge.policy.MessageFlags.HasMaxHtlc() {
|
||||
if capMsat == 0 && edge.policy.HasMaxHTLC {
|
||||
log.Tracef("No capacity available for channel %v, "+
|
||||
"using MaxHtlcMsat (%v) as a fallback.",
|
||||
edge.policy.ChannelID, edge.policy.MaxHTLC)
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestNodeEdgeUnifier(t *testing.T) {
|
||||
FeeProportionalMillionths: 100000,
|
||||
FeeBaseMSat: 30,
|
||||
TimeLockDelta: 60,
|
||||
MessageFlags: lnwire.ChanUpdateRequiredMaxHtlc,
|
||||
HasMaxHTLC: true,
|
||||
MaxHTLC: 5000,
|
||||
MinHTLC: 100,
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func TestNodeEdgeUnifier(t *testing.T) {
|
||||
FeeProportionalMillionths: 190000,
|
||||
FeeBaseMSat: 10,
|
||||
TimeLockDelta: 40,
|
||||
MessageFlags: lnwire.ChanUpdateRequiredMaxHtlc,
|
||||
HasMaxHTLC: true,
|
||||
MaxHTLC: 4000,
|
||||
MinHTLC: 100,
|
||||
}
|
||||
|
||||
+20
-18
@@ -704,7 +704,7 @@ func (r *rpcServer) addDeps(ctx context.Context, s *server,
|
||||
invoiceHtlcModifier *invoices.HtlcModificationInterceptor) error {
|
||||
|
||||
// Set up router rpc backend.
|
||||
selfNode, err := s.graphDB.SourceNode(ctx)
|
||||
selfNode, err := s.v1Graph.SourceNode(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -746,7 +746,7 @@ func (r *rpcServer) addDeps(ctx context.Context, s *server,
|
||||
return info.NodeKey1Bytes, info.NodeKey2Bytes, nil
|
||||
},
|
||||
HasNode: func(nodePub route.Vertex) (bool, error) {
|
||||
_, exists, err := graph.HasNode(ctx, nodePub)
|
||||
exists, err := s.v1Graph.HasNode(ctx, nodePub)
|
||||
|
||||
return exists, err
|
||||
},
|
||||
@@ -1821,8 +1821,8 @@ func (r *rpcServer) VerifyMessage(ctx context.Context,
|
||||
// channels signed the message.
|
||||
//
|
||||
// TODO(phlip9): Require valid nodes to have capital in active channels.
|
||||
graph := r.server.graphDB
|
||||
_, active, err := graph.HasNode(ctx, pub)
|
||||
graph := r.server.v1Graph
|
||||
active, err := graph.HasNode(ctx, pub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query graph: %w", err)
|
||||
}
|
||||
@@ -3196,7 +3196,7 @@ func createRPCCloseUpdate(
|
||||
// abandonChanFromGraph attempts to remove a channel from the channel graph. If
|
||||
// we can't find the chanID in the graph, then we assume it has already been
|
||||
// removed, and will return a nop.
|
||||
func abandonChanFromGraph(chanGraph *graphdb.ChannelGraph,
|
||||
func abandonChanFromGraph(chanGraph *graphdb.VersionedGraph,
|
||||
chanPoint *wire.OutPoint) error {
|
||||
|
||||
// First, we'll obtain the channel ID. If we can't locate this, then
|
||||
@@ -3237,7 +3237,8 @@ func (r *rpcServer) abandonChan(chanPoint *wire.OutPoint,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = abandonChanFromGraph(r.server.graphDB, chanPoint)
|
||||
// TODO: update to support deletions for v2 channels.
|
||||
err = abandonChanFromGraph(r.server.v1Graph, chanPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -4995,7 +4996,7 @@ func createRPCOpenChannel(ctx context.Context, r *rpcServer,
|
||||
|
||||
// Look up our channel peer's node alias if the caller requests it.
|
||||
if peerAliasLookup {
|
||||
peerAlias, err := r.server.graphDB.LookupAlias(ctx, nodePub)
|
||||
peerAlias, err := r.server.v1Graph.LookupAlias(ctx, nodePub)
|
||||
if err != nil {
|
||||
peerAlias = fmt.Sprintf("unable to lookup "+
|
||||
"peer alias: %v", err)
|
||||
@@ -6852,10 +6853,11 @@ func (r *rpcServer) DescribeGraph(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the pointer to the global singleton channel graph, this will
|
||||
// provide a consistent view of the graph due to bolt db's
|
||||
// transactional model.
|
||||
graph := r.server.graphDB
|
||||
// Obtain the pointer to the V1 channel graph. This will provide a
|
||||
// consistent view of the graph due to bolt db's transactional model.
|
||||
//
|
||||
// TODO(elle): switch to a cross-version graph view when available.
|
||||
graph := r.server.v1Graph
|
||||
|
||||
// First iterate through all the known nodes (connected or unconnected
|
||||
// within the graph), collating their current state into the RPC
|
||||
@@ -6980,10 +6982,10 @@ func marshalDBEdge(edgeInfo *models.ChannelEdgeInfo,
|
||||
// channel announcement.
|
||||
if includeAuthProof && edgeInfo.AuthProof != nil {
|
||||
edge.AuthProof = &lnrpc.ChannelAuthProof{
|
||||
NodeSig1: edgeInfo.AuthProof.NodeSig1Bytes,
|
||||
BitcoinSig1: edgeInfo.AuthProof.BitcoinSig1Bytes,
|
||||
NodeSig2: edgeInfo.AuthProof.NodeSig2Bytes,
|
||||
BitcoinSig2: edgeInfo.AuthProof.BitcoinSig2Bytes,
|
||||
NodeSig1: edgeInfo.AuthProof.NodeSig1(),
|
||||
BitcoinSig1: edgeInfo.AuthProof.BitcoinSig1(),
|
||||
NodeSig2: edgeInfo.AuthProof.NodeSig2(),
|
||||
BitcoinSig2: edgeInfo.AuthProof.BitcoinSig2(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7143,7 +7145,7 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
|
||||
"include_channels")
|
||||
}
|
||||
|
||||
graph := r.server.graphDB
|
||||
graph := r.server.v1Graph
|
||||
|
||||
// First, parse the hex-encoded public key into a full in-memory public
|
||||
// key object we can work with for querying.
|
||||
@@ -7909,7 +7911,7 @@ const feeBase float64 = 1000000
|
||||
func (r *rpcServer) FeeReport(ctx context.Context,
|
||||
_ *lnrpc.FeeReportRequest) (*lnrpc.FeeReportResponse, error) {
|
||||
|
||||
channelGraph := r.server.graphDB
|
||||
channelGraph := r.server.v1Graph
|
||||
selfNode, err := channelGraph.SourceNode(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -8298,7 +8300,7 @@ func (r *rpcServer) ForwardingHistory(ctx context.Context,
|
||||
return "", err
|
||||
}
|
||||
|
||||
peer, err := r.server.graphDB.FetchNode(ctx, vertex)
|
||||
peer, err := r.server.v1Graph.FetchNode(ctx, vertex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -321,6 +321,7 @@ type server struct {
|
||||
fundingMgr *funding.Manager
|
||||
|
||||
graphDB *graphdb.ChannelGraph
|
||||
v1Graph *graphdb.VersionedGraph
|
||||
|
||||
chanStateDB *channeldb.ChannelStateDB
|
||||
|
||||
@@ -669,12 +670,17 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
|
||||
HtlcInterceptor: invoiceHtlcModifier,
|
||||
}
|
||||
|
||||
addrSource := channeldb.NewMultiAddrSource(dbs.ChanStateDB, dbs.GraphDB)
|
||||
v1Graph := graphdb.NewVersionedGraph(
|
||||
dbs.GraphDB, lnwire.GossipVersion1,
|
||||
)
|
||||
|
||||
addrSource := channeldb.NewMultiAddrSource(dbs.ChanStateDB, v1Graph)
|
||||
|
||||
s := &server{
|
||||
cfg: cfg,
|
||||
implCfg: implCfg,
|
||||
graphDB: dbs.GraphDB,
|
||||
v1Graph: v1Graph,
|
||||
chanStateDB: dbs.ChanStateDB.ChannelStateDB(),
|
||||
addrSource: addrSource,
|
||||
miscDB: dbs.ChanStateDB,
|
||||
@@ -982,7 +988,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
|
||||
MinProbability: routingConfig.MinRouteProbability,
|
||||
}
|
||||
|
||||
sourceNode, err := dbs.GraphDB.SourceNode(ctx)
|
||||
sourceNode, err := s.v1Graph.SourceNode(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting source node: %w", err)
|
||||
}
|
||||
@@ -1125,7 +1131,8 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
|
||||
*models.ChannelEdgePolicy) error,
|
||||
reset func()) error {
|
||||
|
||||
return s.graphDB.ForEachNodeChannel(ctx, selfVertex,
|
||||
return s.v1Graph.ForEachNodeChannel(
|
||||
ctx, selfVertex,
|
||||
func(c *models.ChannelEdgeInfo,
|
||||
e *models.ChannelEdgePolicy,
|
||||
_ *models.ChannelEdgePolicy) error {
|
||||
@@ -1416,7 +1423,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
|
||||
return nil, fmt.Errorf("we don't have an edge")
|
||||
}
|
||||
|
||||
err = s.graphDB.DeleteChannelEdges(
|
||||
err = s.v1Graph.DeleteChannelEdges(
|
||||
false, false, scid.ToUint64(),
|
||||
)
|
||||
return ourPolicy, err
|
||||
@@ -3434,7 +3441,7 @@ func (s *server) updateAndBroadcastSelfNode(ctx context.Context,
|
||||
// Update the on-disk version of our announcement.
|
||||
// Load and modify self node istead of creating anew instance so we
|
||||
// don't risk overwriting any existing values.
|
||||
selfNode, err := s.graphDB.SourceNode(ctx)
|
||||
selfNode, err := s.v1Graph.SourceNode(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get current source node: %w", err)
|
||||
}
|
||||
@@ -5207,7 +5214,7 @@ func (s *server) fetchNodeAdvertisedAddrs(ctx context.Context,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node, err := s.graphDB.FetchNode(ctx, vertex)
|
||||
node, err := s.v1Graph.FetchNode(ctx, vertex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5618,7 +5625,7 @@ func (s *server) setSelfNode(ctx context.Context, nodePub route.Vertex,
|
||||
nodeLastUpdate = time.Now()
|
||||
)
|
||||
|
||||
srcNode, err := s.graphDB.SourceNode(ctx)
|
||||
srcNode, err := s.v1Graph.SourceNode(ctx)
|
||||
switch {
|
||||
case err == nil:
|
||||
// If we have a source node persisted in the DB already, then we
|
||||
|
||||
@@ -92,6 +92,11 @@ var (
|
||||
// schema. This is optional and can be disabled by the
|
||||
// user if necessary.
|
||||
},
|
||||
{
|
||||
Name: "000009_graph_v2_columns",
|
||||
Version: 11,
|
||||
SchemaVersion: 9,
|
||||
},
|
||||
}, migrationAdditions...)
|
||||
|
||||
// ErrMigrationMismatch is returned when a migrated record does not
|
||||
|
||||
@@ -452,132 +452,6 @@ func TestCustomMigration(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSchemaMigrationIdempotency tests that the our schema migrations are
|
||||
// idempotent. This means that we can apply the migrations multiple times and
|
||||
// the schema version will always be the same.
|
||||
func TestSchemaMigrationIdempotency(t *testing.T) {
|
||||
dropMigrationTrackerEntries := func(t *testing.T, db *BaseDB) {
|
||||
_, err := db.Exec("DELETE FROM migration_tracker;")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
lastMigration := migrationConfig[len(migrationConfig)-1]
|
||||
|
||||
t.Run("SQLite", func(t *testing.T) {
|
||||
// First instantiate the database and run the migrations
|
||||
// including the custom migrations.
|
||||
t.Logf("Creating new SQLite DB for testing migrations")
|
||||
|
||||
dbFileName := filepath.Join(t.TempDir(), "tmp.db")
|
||||
var (
|
||||
db *SqliteStore
|
||||
err error
|
||||
)
|
||||
|
||||
// Run the migration 3 times to test that the migrations
|
||||
// are idempotent.
|
||||
for i := 0; i < 3; i++ {
|
||||
db, err = NewSqliteStore(&SqliteConfig{
|
||||
SkipMigrations: false,
|
||||
}, dbFileName)
|
||||
require.NoError(t, err)
|
||||
|
||||
dbToCleanup := db.DB
|
||||
t.Cleanup(func() {
|
||||
require.NoError(
|
||||
t, dbToCleanup.Close(),
|
||||
)
|
||||
})
|
||||
|
||||
ctxb := t.Context()
|
||||
require.NoError(
|
||||
t, db.ApplyAllMigrations(ctxb, GetMigrations()),
|
||||
)
|
||||
|
||||
version, dirty, err := db.GetSchemaVersion()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now reset the schema version to 0 and make sure that
|
||||
// we can apply the migrations again.
|
||||
require.Equal(t, lastMigration.SchemaVersion, version)
|
||||
require.False(t, dirty)
|
||||
|
||||
require.NoError(
|
||||
t, db.SetSchemaVersion(
|
||||
database.NilVersion, false,
|
||||
),
|
||||
)
|
||||
dropMigrationTrackerEntries(t, db.BaseDB)
|
||||
|
||||
// Make sure that we reset the schema version.
|
||||
version, dirty, err = db.GetSchemaVersion()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, version)
|
||||
require.False(t, dirty)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Postgres", func(t *testing.T) {
|
||||
// First create a temporary Postgres database to run
|
||||
// the migrations on.
|
||||
fixture := NewTestPgFixture(
|
||||
t, DefaultPostgresFixtureLifetime,
|
||||
)
|
||||
t.Cleanup(func() {
|
||||
fixture.TearDown(t)
|
||||
})
|
||||
|
||||
dbName := randomDBName(t)
|
||||
|
||||
// Next instantiate the database and run the migrations
|
||||
// including the custom migrations.
|
||||
t.Logf("Creating new Postgres DB '%s' for testing "+
|
||||
"migrations", dbName)
|
||||
|
||||
_, err := fixture.db.ExecContext(
|
||||
t.Context(), "CREATE DATABASE "+dbName,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := fixture.GetConfig(dbName)
|
||||
var db *PostgresStore
|
||||
|
||||
// Run the migration 3 times to test that the migrations
|
||||
// are idempotent.
|
||||
for i := 0; i < 3; i++ {
|
||||
cfg.SkipMigrations = false
|
||||
db, err = NewPostgresStore(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctxb := t.Context()
|
||||
require.NoError(
|
||||
t, db.ApplyAllMigrations(ctxb, GetMigrations()),
|
||||
)
|
||||
|
||||
version, dirty, err := db.GetSchemaVersion()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now reset the schema version to 0 and make sure that
|
||||
// we can apply the migrations again.
|
||||
require.Equal(t, lastMigration.SchemaVersion, version)
|
||||
require.False(t, dirty)
|
||||
|
||||
require.NoError(
|
||||
t, db.SetSchemaVersion(
|
||||
database.NilVersion, false,
|
||||
),
|
||||
)
|
||||
dropMigrationTrackerEntries(t, db.BaseDB)
|
||||
|
||||
// Make sure that we reset the schema version.
|
||||
version, dirty, err = db.GetSchemaVersion()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, version)
|
||||
require.False(t, dirty)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestMigrationBug19RC1 tests a bug that was present in the migration code
|
||||
// at the v0.19.0-rc1 release.
|
||||
// The bug was fixed in: https://github.com/lightningnetwork/lnd/pull/9647
|
||||
|
||||
+327
-62
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
||||
-- Remove the block_height column from graph_nodes
|
||||
ALTER TABLE graph_nodes DROP COLUMN block_height;
|
||||
|
||||
-- Remove the signature column from graph_channels
|
||||
ALTER TABLE graph_channels DROP COLUMN signature;
|
||||
|
||||
-- Remove the funding_pk_script column from graph_channels
|
||||
ALTER TABLE graph_channels DROP COLUMN funding_pk_script;
|
||||
|
||||
-- Remove the merkle_root_hash column from graph_channels
|
||||
ALTER TABLE graph_channels DROP COLUMN merkle_root_hash;
|
||||
|
||||
-- Remove the block_height column from graph_channel_policies
|
||||
ALTER TABLE graph_channel_policies DROP COLUMN block_height;
|
||||
|
||||
-- Remove the disable_flags column from graph_channel_policies
|
||||
ALTER TABLE graph_channel_policies DROP COLUMN disable_flags;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- The block height timestamp of this node's latest received node announcement.
|
||||
-- It may be zero if we have not received a node announcement yet.
|
||||
ALTER TABLE graph_nodes ADD COLUMN block_height BIGINT;
|
||||
|
||||
-- The signature of the channel announcement. If this is null, then the channel
|
||||
-- belongs to the source node and the channel has not been announced yet.
|
||||
ALTER TABLE graph_channels ADD COLUMN signature BLOB;
|
||||
|
||||
-- For v2 channels onwards, we cant necessarily derive the funding pk script
|
||||
-- from the other fields in the announcement, so we store it here so that
|
||||
-- we have easy access to it when we want to subscribe to channel closures.
|
||||
ALTER TABLE graph_channels ADD COLUMN funding_pk_script BLOB;
|
||||
|
||||
-- The optional merkel root hash advertised in the V2 channel announcement.
|
||||
ALTER TABLE graph_channels ADD COLUMN merkle_root_hash BLOB;
|
||||
|
||||
-- The block height timestamp of this channel's latest received channel-update
|
||||
-- message (for v2 channel update messages).
|
||||
ALTER TABLE graph_channel_policies ADD COLUMN block_height BIGINT;
|
||||
|
||||
-- A bitfield describing the disabled flags for a v2 channel update.
|
||||
ALTER TABLE graph_channel_policies ADD COLUMN disable_flags SMALLINT
|
||||
CHECK (disable_flags >= 0 AND disable_flags <= 255);
|
||||
+13
-7
@@ -42,6 +42,9 @@ type GraphChannel struct {
|
||||
Node2Signature []byte
|
||||
Bitcoin1Signature []byte
|
||||
Bitcoin2Signature []byte
|
||||
Signature []byte
|
||||
FundingPkScript []byte
|
||||
MerkleRootHash []byte
|
||||
}
|
||||
|
||||
type GraphChannelExtraType struct {
|
||||
@@ -72,6 +75,8 @@ type GraphChannelPolicy struct {
|
||||
MessageFlags sql.NullInt16
|
||||
ChannelFlags sql.NullInt16
|
||||
Signature []byte
|
||||
BlockHeight sql.NullInt64
|
||||
DisableFlags sql.NullInt16
|
||||
}
|
||||
|
||||
type GraphChannelPolicyExtraType struct {
|
||||
@@ -85,13 +90,14 @@ type GraphClosedScid struct {
|
||||
}
|
||||
|
||||
type GraphNode struct {
|
||||
ID int64
|
||||
Version int16
|
||||
PubKey []byte
|
||||
Alias sql.NullString
|
||||
LastUpdate sql.NullInt64
|
||||
Color sql.NullString
|
||||
Signature []byte
|
||||
ID int64
|
||||
Version int16
|
||||
PubKey []byte
|
||||
Alias sql.NullString
|
||||
LastUpdate sql.NullInt64
|
||||
Color sql.NullString
|
||||
Signature []byte
|
||||
BlockHeight sql.NullInt64
|
||||
}
|
||||
|
||||
type GraphNodeAddress struct {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
type Querier interface {
|
||||
AddSourceNode(ctx context.Context, nodeID int64) error
|
||||
AddV1ChannelProof(ctx context.Context, arg AddV1ChannelProofParams) (sql.Result, error)
|
||||
AddV2ChannelProof(ctx context.Context, arg AddV2ChannelProofParams) (sql.Result, error)
|
||||
ClearKVInvoiceHashIndex(ctx context.Context) error
|
||||
CountZombieChannels(ctx context.Context, version int16) (int64, error)
|
||||
CreateChannel(ctx context.Context, arg CreateChannelParams) (int64, error)
|
||||
@@ -116,6 +117,7 @@ type Querier interface {
|
||||
InsertNodeMig(ctx context.Context, arg InsertNodeMigParams) (int64, error)
|
||||
IsClosedChannel(ctx context.Context, scid []byte) (bool, error)
|
||||
IsPublicV1Node(ctx context.Context, pubKey []byte) (bool, error)
|
||||
IsPublicV2Node(ctx context.Context, pubKey []byte) (bool, error)
|
||||
IsZombieChannel(ctx context.Context, arg IsZombieChannelParams) (bool, error)
|
||||
ListChannelsByNodeID(ctx context.Context, arg ListChannelsByNodeIDParams) ([]ListChannelsByNodeIDRow, error)
|
||||
ListChannelsForNodeIDs(ctx context.Context, arg ListChannelsForNodeIDsParams) ([]ListChannelsForNodeIDsRow, error)
|
||||
@@ -125,6 +127,7 @@ type Querier interface {
|
||||
ListNodeIDsAndPubKeys(ctx context.Context, arg ListNodeIDsAndPubKeysParams) ([]ListNodeIDsAndPubKeysRow, error)
|
||||
ListNodesPaginated(ctx context.Context, arg ListNodesPaginatedParams) ([]GraphNode, error)
|
||||
NextInvoiceSettleIndex(ctx context.Context) (int64, error)
|
||||
NodeExists(ctx context.Context, arg NodeExistsParams) (bool, error)
|
||||
OnAMPSubInvoiceCanceled(ctx context.Context, arg OnAMPSubInvoiceCanceledParams) error
|
||||
OnAMPSubInvoiceCreated(ctx context.Context, arg OnAMPSubInvoiceCreatedParams) error
|
||||
OnAMPSubInvoiceSettled(ctx context.Context, arg OnAMPSubInvoiceSettledParams) error
|
||||
|
||||
+117
-26
@@ -5,9 +5,9 @@
|
||||
|
||||
-- name: UpsertNode :one
|
||||
INSERT INTO graph_nodes (
|
||||
version, pub_key, alias, last_update, color, signature
|
||||
version, pub_key, alias, last_update, block_height, color, signature
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
)
|
||||
ON CONFLICT (pub_key, version)
|
||||
-- Update the following fields if a conflict occurs on pub_key
|
||||
@@ -15,10 +15,13 @@ ON CONFLICT (pub_key, version)
|
||||
DO UPDATE SET
|
||||
alias = EXCLUDED.alias,
|
||||
last_update = EXCLUDED.last_update,
|
||||
block_height = EXCLUDED.block_height,
|
||||
color = EXCLUDED.color,
|
||||
signature = EXCLUDED.signature
|
||||
WHERE graph_nodes.last_update IS NULL
|
||||
OR EXCLUDED.last_update > graph_nodes.last_update
|
||||
WHERE (graph_nodes.last_update IS NULL
|
||||
OR EXCLUDED.last_update > graph_nodes.last_update)
|
||||
AND (graph_nodes.block_height IS NULL
|
||||
OR EXCLUDED.block_height >= graph_nodes.block_height)
|
||||
RETURNING id;
|
||||
|
||||
-- We use a separate upsert for our own node since we want to be less strict
|
||||
@@ -26,9 +29,9 @@ RETURNING id;
|
||||
-- update the record even if the last_update is the same as what we have.
|
||||
-- name: UpsertSourceNode :one
|
||||
INSERT INTO graph_nodes (
|
||||
version, pub_key, alias, last_update, color, signature
|
||||
version, pub_key, alias, last_update, block_height, color, signature
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
)
|
||||
ON CONFLICT (pub_key, version)
|
||||
-- Update the following fields if a conflict occurs on pub_key
|
||||
@@ -36,10 +39,13 @@ ON CONFLICT (pub_key, version)
|
||||
DO UPDATE SET
|
||||
alias = EXCLUDED.alias,
|
||||
last_update = EXCLUDED.last_update,
|
||||
block_height = EXCLUDED.block_height,
|
||||
color = EXCLUDED.color,
|
||||
signature = EXCLUDED.signature
|
||||
WHERE graph_nodes.last_update IS NULL
|
||||
OR EXCLUDED.last_update >= graph_nodes.last_update
|
||||
AND (graph_nodes.block_height IS NULL
|
||||
OR EXCLUDED.block_height >= graph_nodes.block_height)
|
||||
RETURNING id;
|
||||
|
||||
-- name: GetNodesByIDs :many
|
||||
@@ -53,6 +59,14 @@ FROM graph_nodes
|
||||
WHERE pub_key = $1
|
||||
AND version = $2;
|
||||
|
||||
-- name: NodeExists :one
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM graph_nodes
|
||||
WHERE pub_key = $1
|
||||
AND version = $2
|
||||
) AS node_exists;
|
||||
|
||||
-- name: GetNodeIDByPubKey :one
|
||||
SELECT id
|
||||
FROM graph_nodes
|
||||
@@ -87,14 +101,36 @@ SELECT EXISTS (
|
||||
-- one of the signatures since we only ever set them
|
||||
-- together.
|
||||
WHERE c.version = 1
|
||||
AND c.bitcoin_1_signature IS NOT NULL
|
||||
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||
AND n.pub_key = $1
|
||||
UNION ALL
|
||||
SELECT 1
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n ON n.id = c.node_id_2
|
||||
WHERE c.version = 1
|
||||
AND c.bitcoin_1_signature IS NOT NULL
|
||||
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||
AND n.pub_key = $1
|
||||
);
|
||||
|
||||
-- name: IsPublicV2Node :one
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n ON n.id = c.node_id_1
|
||||
-- NOTE: we hard-code the version here since the clauses
|
||||
-- here that determine if a node is public is specific
|
||||
-- to the V2 gossip protocol.
|
||||
WHERE c.version = 2
|
||||
AND COALESCE(length(c.signature), 0) > 0
|
||||
AND n.pub_key = $1
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT 1
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n ON n.id = c.node_id_2
|
||||
WHERE c.version = 2
|
||||
AND COALESCE(length(c.signature), 0) > 0
|
||||
AND n.pub_key = $1
|
||||
);
|
||||
|
||||
@@ -215,7 +251,7 @@ WHERE last_update >= @start_time
|
||||
SELECT 1
|
||||
FROM graph_channels c
|
||||
WHERE c.version = 1
|
||||
AND c.bitcoin_1_signature IS NOT NULL
|
||||
AND COALESCE(length(c.bitcoin_1_signature), 0) > 0
|
||||
AND (c.node_id_1 = graph_nodes.id OR c.node_id_2 = graph_nodes.id)
|
||||
)
|
||||
)
|
||||
@@ -283,9 +319,9 @@ INSERT INTO graph_channels (
|
||||
version, scid, node_id_1, node_id_2,
|
||||
outpoint, capacity, bitcoin_key_1, bitcoin_key_2,
|
||||
node_1_signature, node_2_signature, bitcoin_1_signature,
|
||||
bitcoin_2_signature
|
||||
bitcoin_2_signature, signature, funding_pk_script, merkle_root_hash
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
|
||||
)
|
||||
RETURNING id;
|
||||
|
||||
@@ -298,6 +334,12 @@ SET node_1_signature = $2,
|
||||
WHERE scid = $1
|
||||
AND version = 1;
|
||||
|
||||
-- name: AddV2ChannelProof :execresult
|
||||
UPDATE graph_channels
|
||||
SET signature = $2
|
||||
WHERE scid = $1
|
||||
AND version = 2;
|
||||
|
||||
-- name: GetChannelsBySCIDRange :many
|
||||
SELECT sqlc.embed(c),
|
||||
n1.pub_key AS node1_pub_key,
|
||||
@@ -365,6 +407,8 @@ SELECT
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2
|
||||
cp2.id AS policy2_id,
|
||||
@@ -381,7 +425,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy_2_message_flags,
|
||||
cp2.channel_flags AS policy_2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -420,6 +466,8 @@ SELECT
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2
|
||||
cp2.id AS policy2_id,
|
||||
@@ -436,7 +484,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -469,6 +519,8 @@ SELECT
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2 (node_id_2)
|
||||
cp2.id AS policy2_id,
|
||||
@@ -485,7 +537,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -548,6 +602,8 @@ SELECT
|
||||
cp1.message_flags AS policy_1_message_flags,
|
||||
cp1.channel_flags AS policy_1_channel_flags,
|
||||
cp1.signature AS policy_1_signature,
|
||||
cp1.block_height AS policy_1_block_height,
|
||||
cp1.disable_flags AS policy_1_disable_flags,
|
||||
|
||||
-- Node 2 policy
|
||||
cp2.id AS policy_2_id,
|
||||
@@ -564,7 +620,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy_2_message_flags,
|
||||
cp2.channel_flags AS policy_2_channel_flags,
|
||||
cp2.signature AS policy_2_signature
|
||||
cp2.signature AS policy_2_signature,
|
||||
cp2.block_height AS policy_2_block_height,
|
||||
cp2.disable_flags AS policy_2_disable_flags
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
JOIN graph_nodes n2 ON c.node_id_2 = n2.id
|
||||
@@ -605,6 +663,8 @@ SELECT sqlc.embed(c),
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2
|
||||
cp2.id AS policy2_id,
|
||||
@@ -621,7 +681,9 @@ SELECT sqlc.embed(c),
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -658,6 +720,8 @@ SELECT sqlc.embed(c),
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2
|
||||
cp2.id AS policy2_id,
|
||||
@@ -674,7 +738,9 @@ SELECT sqlc.embed(c),
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -689,7 +755,7 @@ WHERE c.version = $1
|
||||
-- name: GetPublicV1ChannelsBySCID :many
|
||||
SELECT *
|
||||
FROM graph_channels
|
||||
WHERE node_1_signature IS NOT NULL
|
||||
WHERE COALESCE(length(node_1_signature), 0) > 0
|
||||
AND scid >= @start_scid
|
||||
AND scid < @end_scid;
|
||||
|
||||
@@ -723,6 +789,8 @@ SELECT
|
||||
cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
cp1.signature AS policy_1_signature,
|
||||
|
||||
-- Node 2 policy
|
||||
@@ -740,7 +808,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.signature AS policy_2_signature
|
||||
cp2.signature AS policy_2_signature,
|
||||
cp2.block_height AS policy_2_block_height,
|
||||
cp2.disable_flags AS policy_2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -774,6 +844,8 @@ SELECT
|
||||
cp1.inbound_fee_rate_milli_msat AS policy1_inbound_fee_rate_milli_msat,
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Node 2 policy
|
||||
cp2.timelock AS policy_2_timelock,
|
||||
@@ -785,7 +857,9 @@ SELECT
|
||||
cp2.inbound_base_fee_msat AS policy2_inbound_base_fee_msat,
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy2_message_flags,
|
||||
cp2.channel_flags AS policy2_channel_flags
|
||||
cp2.channel_flags AS policy2_channel_flags,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -858,9 +932,9 @@ INSERT INTO graph_channel_policies (
|
||||
base_fee_msat, min_htlc_msat, last_update, disabled,
|
||||
max_htlc_msat, inbound_base_fee_msat,
|
||||
inbound_fee_rate_milli_msat, message_flags, channel_flags,
|
||||
signature
|
||||
signature, block_height, disable_flags
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
|
||||
)
|
||||
ON CONFLICT (channel_id, node_id, version)
|
||||
-- Update the following fields if a conflict occurs on channel_id,
|
||||
@@ -877,8 +951,21 @@ ON CONFLICT (channel_id, node_id, version)
|
||||
inbound_fee_rate_milli_msat = EXCLUDED.inbound_fee_rate_milli_msat,
|
||||
message_flags = EXCLUDED.message_flags,
|
||||
channel_flags = EXCLUDED.channel_flags,
|
||||
signature = EXCLUDED.signature
|
||||
WHERE EXCLUDED.last_update > graph_channel_policies.last_update
|
||||
signature = EXCLUDED.signature,
|
||||
block_height = EXCLUDED.block_height,
|
||||
disable_flags = EXCLUDED.disable_flags
|
||||
WHERE (
|
||||
EXCLUDED.version = 1 AND (
|
||||
graph_channel_policies.last_update IS NULL
|
||||
OR EXCLUDED.last_update > graph_channel_policies.last_update
|
||||
)
|
||||
)
|
||||
OR (
|
||||
EXCLUDED.version = 2 AND (
|
||||
graph_channel_policies.block_height IS NULL
|
||||
OR EXCLUDED.block_height >= graph_channel_policies.block_height
|
||||
)
|
||||
)
|
||||
RETURNING id;
|
||||
|
||||
-- name: GetChannelPolicyByChannelAndNode :one
|
||||
@@ -910,6 +997,8 @@ SELECT
|
||||
cp1.message_flags AS policy1_message_flags,
|
||||
cp1.channel_flags AS policy1_channel_flags,
|
||||
cp1.signature AS policy1_signature,
|
||||
cp1.block_height AS policy1_block_height,
|
||||
cp1.disable_flags AS policy1_disable_flags,
|
||||
|
||||
-- Policy 2
|
||||
cp2.id AS policy2_id,
|
||||
@@ -926,7 +1015,9 @@ SELECT
|
||||
cp2.inbound_fee_rate_milli_msat AS policy2_inbound_fee_rate_milli_msat,
|
||||
cp2.message_flags AS policy_2_message_flags,
|
||||
cp2.channel_flags AS policy_2_channel_flags,
|
||||
cp2.signature AS policy2_signature
|
||||
cp2.signature AS policy2_signature,
|
||||
cp2.block_height AS policy2_block_height,
|
||||
cp2.disable_flags AS policy2_disable_flags
|
||||
|
||||
FROM graph_channels c
|
||||
JOIN graph_nodes n1 ON c.node_id_1 = n1.id
|
||||
@@ -1175,4 +1266,4 @@ ON CONFLICT (channel_id, node_id, version)
|
||||
message_flags = EXCLUDED.message_flags,
|
||||
channel_flags = EXCLUDED.channel_flags,
|
||||
signature = EXCLUDED.signature
|
||||
RETURNING id;
|
||||
RETURNING id;
|
||||
Reference in new issue
Block a user