diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go index 5439f02aa75..c95aaece24a 100644 --- a/autopilot/prefattach_test.go +++ b/autopilot/prefattach_test.go @@ -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" @@ -492,15 +493,24 @@ 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 diff --git a/discovery/chan_series.go b/discovery/chan_series.go index 050b82b1082..9ba9607e93e 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -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 diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 96a8c047ae3..a77f4cb5c81 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1996,10 +1996,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 @@ -2392,44 +2396,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. @@ -2692,28 +2665,40 @@ 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 @@ -3558,7 +3543,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", @@ -3620,21 +3605,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) @@ -3660,7 +3653,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) diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index 01faf5561c4..a6a6aa43eec 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -272,10 +272,18 @@ 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()] diff --git a/docs/release-notes/release-notes-0.21.0.md b/docs/release-notes/release-notes-0.21.0.md index f26ca933d97..9d24b36dc1e 100644 --- a/docs/release-notes/release-notes-0.21.0.md +++ b/docs/release-notes/release-notes-0.21.0.md @@ -68,8 +68,9 @@ * 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](https://github.com/lightningnetwork/lnd/pull/10339). +* 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). ## Code Health diff --git a/graph/builder_test.go b/graph/builder_test.go index ca57a44c9b2..111a69fea41 100644 --- a/graph/builder_test.go +++ b/graph/builder_test.go @@ -17,6 +17,7 @@ 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" @@ -65,16 +66,21 @@ 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, + }, + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) require.NoError(t, ctx.builder.AddEdge(ctxb, edge)) @@ -151,16 +157,23 @@ 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, + }, + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) edgePolicy := &models.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), ChannelID: edge.ChannelID, @@ -272,41 +285,56 @@ 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(), + 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, }, - Features: lnwire.EmptyFeatureVector(), - FundingScript: fn.Some(fundingScript1), - } - copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + )), + ) + require.NoError(t, err) + edge1.FundingScript = fn.Some(fundingScript1) 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(), + edge2, err := models.NewV1Channel( + chanID2, + *chaincfg.SimNetParams.GenesisHash, + node1.PubKeyBytes, + node2.PubKeyBytes, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: btcKey1, + BitcoinKey2Bytes: btcKey2, }, - Features: lnwire.EmptyFeatureVector(), - FundingScript: fn.Some(fundingScript2), - } - copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + )), + ) + require.NoError(t, err) + edge2.FundingScript = fn.Some(fundingScript2) if err := ctx.builder.AddEdge(ctxb, edge2); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -482,45 +510,47 @@ 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(), + 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, }, - Features: lnwire.EmptyFeatureVector(), - FundingScript: fn.Some([]byte{}), - } - copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + ) + require.NoError(t, err) + edge1.FundingScript = fn.Some([]byte{}) 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(), + edge2, err := models.NewV1Channel( + chanID2, + *chaincfg.SimNetParams.GenesisHash, + node1.PubKeyBytes, + node2.PubKeyBytes, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: btcKey1, + BitcoinKey2Bytes: btcKey2, }, - Features: lnwire.EmptyFeatureVector(), - FundingScript: fn.Some([]byte{}), - } - copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + ) + require.NoError(t, err) + edge2.FundingScript = fn.Some([]byte{}) if err := ctx.builder.AddEdge(ctxb, edge2); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -638,23 +668,31 @@ 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(), + 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, }, - ChannelPoint: *chanUTXO, - Capacity: chanValue, - Features: lnwire.EmptyFeatureVector(), - FundingScript: fn.Some(script), - } - copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + models.WithCapacity(chanValue), + models.WithChannelPoint(*chanUTXO), + ) + require.NoError(t, err) + edge1.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge1); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -1061,16 +1099,18 @@ 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, + }, + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -1141,16 +1181,18 @@ 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, + }, + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -1201,16 +1243,18 @@ 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, + }, + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -1513,19 +1557,31 @@ 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) + + 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 } - copy(edgeInfo.NodeKey1Bytes[:], node1Bytes) - copy(edgeInfo.NodeKey2Bytes[:], node2Bytes) - copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes) - copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes) - shortID := lnwire.NewShortChanIDFromInt(edge.ChannelID) links[shortID] = &mockLink{ bandwidth: lnwire.MilliSatoshi( @@ -1533,7 +1589,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 } @@ -1573,12 +1629,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 } @@ -1884,20 +1940,24 @@ 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) { diff --git a/graph/db/channel_cache_test.go b/graph/db/channel_cache_test.go index 767958d9a15..1b7b0d3025e 100644 --- a/graph/db/channel_cache_test.go +++ b/graph/db/channel_cache_test.go @@ -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,17 @@ 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, } } diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index bfe9c7bddd9..72b200d554c 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -379,7 +379,7 @@ func TestPartialNode(t *testing.T) { // Create an edge attached to these nodes and add it to the graph. edgeInfo, _ := createEdge(140, 0, 0, 0, &node1, &node2) - require.NoError(t, graph.AddChannelEdge(ctx, &edgeInfo)) + require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo)) // Both of the nodes should now be in both the graph (as partial/shell) // nodes _and_ the cache should also have an awareness of both nodes. @@ -505,30 +505,53 @@ func TestEdgeInsertionDeletion(t *testing.T) { require.NoError(t, err, "unable to generate node key") node2Pub, err := node2.PubKey() require.NoError(t, err, "unable to generate node key") - edgeInfo := models.ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: *chaincfg.MainNetParams.GenesisHash, - AuthProof: &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), + + node1Vertex, err := route.NewVertexFromBytes( + node1Pub.SerializeCompressed(), + ) + require.NoError(t, err) + node2Vertex, err := route.NewVertexFromBytes( + node2Pub.SerializeCompressed(), + ) + require.NoError(t, err) + + btcKey1, err := route.NewVertexFromBytes( + node1Pub.SerializeCompressed(), + ) + require.NoError(t, err) + btcKey2, err := route.NewVertexFromBytes( + node2Pub.SerializeCompressed(), + ) + require.NoError(t, err) + + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + ) + + edgeInfo, err := models.NewV1Channel( + chanID, + *chaincfg.MainNetParams.GenesisHash, + node1Vertex, + node2Vertex, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: btcKey1, + BitcoinKey2Bytes: btcKey2, }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: outpoint, - Capacity: 9000, - } - copy(edgeInfo.NodeKey1Bytes[:], node1Pub.SerializeCompressed()) - copy(edgeInfo.NodeKey2Bytes[:], node2Pub.SerializeCompressed()) - copy(edgeInfo.BitcoinKey1Bytes[:], node1Pub.SerializeCompressed()) - copy(edgeInfo.BitcoinKey2Bytes[:], node2Pub.SerializeCompressed()) + models.WithChanProof(proof), + models.WithChannelPoint(outpoint), + models.WithCapacity(9000), + ) + require.NoError(t, err) - require.NoError(t, graph.AddChannelEdge(ctx, &edgeInfo)) - assertEdgeWithNoPoliciesInCache(t, graph, &edgeInfo) + require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo)) + assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo) // Show that trying to insert the same channel again will return the // expected error. - err = graph.AddChannelEdge(ctx, &edgeInfo) + err = graph.AddChannelEdge(ctx, edgeInfo) require.ErrorIs(t, err, ErrEdgeAlreadyExist) // Ensure that both policies are returned as unknown (nil). @@ -565,7 +588,7 @@ func TestEdgeInsertionDeletion(t *testing.T) { } func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32, - node1, node2 *models.Node) (models.ChannelEdgeInfo, + node1, node2 *models.Node) (*models.ChannelEdgeInfo, lnwire.ShortChannelID) { shortChanID := lnwire.ShortChannelID{ @@ -580,25 +603,41 @@ func createEdge(height, txIndex uint32, txPosition uint16, outPointIndex uint32, node1Pub, _ := node1.PubKey() node2Pub, _ := node2.PubKey() - edgeInfo := models.ChannelEdgeInfo{ - ChannelID: shortChanID.ToUint64(), - ChainHash: *chaincfg.MainNetParams.GenesisHash, - AuthProof: &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), - }, - ChannelPoint: outpoint, - Capacity: 9000, - ExtraOpaqueData: make([]byte, 0), - Features: lnwire.EmptyFeatureVector(), - } - copy(edgeInfo.NodeKey1Bytes[:], node1Pub.SerializeCompressed()) - copy(edgeInfo.NodeKey2Bytes[:], node2Pub.SerializeCompressed()) - copy(edgeInfo.BitcoinKey1Bytes[:], node1Pub.SerializeCompressed()) - copy(edgeInfo.BitcoinKey2Bytes[:], node2Pub.SerializeCompressed()) + node1Vertex, _ := route.NewVertexFromBytes( + node1Pub.SerializeCompressed(), + ) + node2Vertex, _ := route.NewVertexFromBytes( + node2Pub.SerializeCompressed(), + ) + btcKey1, _ := route.NewVertexFromBytes( + node1Pub.SerializeCompressed(), + ) + btcKey2, _ := route.NewVertexFromBytes( + node2Pub.SerializeCompressed(), + ) + + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + ) + + edgeInfo, _ := models.NewV1Channel( + shortChanID.ToUint64(), + *chaincfg.MainNetParams.GenesisHash, + node1Vertex, + node2Vertex, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: btcKey1, + BitcoinKey2Bytes: btcKey2, + ExtraOpaqueData: make([]byte, 0), + }, + models.WithChanProof(proof), + models.WithChannelPoint(outpoint), + models.WithCapacity(9000), + ) return edgeInfo, shortChanID } @@ -657,20 +696,20 @@ func TestDisconnectBlockAtHeight(t *testing.T) { edgeInfo3, _ := createEdge(height-1, 0, 0, 2, node1, node2) // Now add all these new edges to the database. - if err := graph.AddChannelEdge(ctx, &edgeInfo); err != nil { + if err := graph.AddChannelEdge(ctx, edgeInfo); err != nil { t.Fatalf("unable to create channel edge: %v", err) } - if err := graph.AddChannelEdge(ctx, &edgeInfo2); err != nil { + if err := graph.AddChannelEdge(ctx, edgeInfo2); err != nil { t.Fatalf("unable to create channel edge: %v", err) } - if err := graph.AddChannelEdge(ctx, &edgeInfo3); err != nil { + if err := graph.AddChannelEdge(ctx, edgeInfo3); err != nil { t.Fatalf("unable to create channel edge: %v", err) } - assertEdgeWithNoPoliciesInCache(t, graph, &edgeInfo) - assertEdgeWithNoPoliciesInCache(t, graph, &edgeInfo2) - assertEdgeWithNoPoliciesInCache(t, graph, &edgeInfo3) + assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo) + assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo2) + assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo3) // Call DisconnectBlockAtHeight, which should prune every channel // that has a funding height of 'height' or greater. @@ -680,7 +719,7 @@ func TestDisconnectBlockAtHeight(t *testing.T) { } assertNoEdge(t, graph, edgeInfo.ChannelID) assertNoEdge(t, graph, edgeInfo2.ChannelID) - assertEdgeWithNoPoliciesInCache(t, graph, &edgeInfo3) + assertEdgeWithNoPoliciesInCache(t, graph, edgeInfo3) // The two edges should have been removed. if len(removed) != 2 { @@ -749,10 +788,14 @@ func assertEdgeInfoEqual(t *testing.T, e1 *models.ChannelEdgeInfo, if !bytes.Equal(e1.NodeKey2Bytes[:], e2.NodeKey2Bytes[:]) { t.Fatalf("nodekey2 doesn't match") } - if !bytes.Equal(e1.BitcoinKey1Bytes[:], e2.BitcoinKey1Bytes[:]) { + btcKey1E1 := e1.BitcoinKey1Bytes.UnwrapOr(route.Vertex{}) + btcKey1E2 := e2.BitcoinKey1Bytes.UnwrapOr(route.Vertex{}) + if !bytes.Equal(btcKey1E1[:], btcKey1E2[:]) { t.Fatalf("bitcoinkey1 doesn't match") } - if !bytes.Equal(e1.BitcoinKey2Bytes[:], e2.BitcoinKey2Bytes[:]) { + btcKey2E1 := e1.BitcoinKey2Bytes.UnwrapOr(route.Vertex{}) + btcKey2E2 := e2.BitcoinKey2Bytes.UnwrapOr(route.Vertex{}) + if !bytes.Equal(btcKey2E1[:], btcKey2E2[:]) { t.Fatalf("bitcoinkey2 doesn't match") } @@ -762,17 +805,20 @@ func assertEdgeInfoEqual(t *testing.T, e1 *models.ChannelEdgeInfo, } require.True(t, bytes.Equal( - e1.AuthProof.NodeSig1Bytes, e2.AuthProof.NodeSig1Bytes, + e1.AuthProof.NodeSig1(), + e2.AuthProof.NodeSig1(), )) require.True(t, bytes.Equal( - e1.AuthProof.NodeSig2Bytes, e2.AuthProof.NodeSig2Bytes, + e1.AuthProof.NodeSig2(), + e2.AuthProof.NodeSig2(), )) require.True(t, bytes.Equal( - e1.AuthProof.BitcoinSig1Bytes, - e2.AuthProof.BitcoinSig1Bytes, + e1.AuthProof.BitcoinSig1(), + e2.AuthProof.BitcoinSig1(), )) require.True(t, bytes.Equal( - e1.AuthProof.BitcoinSig2Bytes, e2.AuthProof.BitcoinSig2Bytes, + e1.AuthProof.BitcoinSig2(), + e2.AuthProof.BitcoinSig2(), )) if e1.ChannelPoint != e2.ChannelPoint { @@ -836,33 +882,56 @@ func createChannelEdge(node1, node2 *models.Node, // Add the new edge to the database, this should proceed without any // errors. - edgeInfo := &models.ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: *chaincfg.MainNetParams.GenesisHash, - ChannelPoint: outpoint, - Capacity: 1000, - ExtraOpaqueData: []byte{ - 1, 1, 1, - 2, 2, 2, 2, - 3, 3, 3, 3, 3, - }, - Features: lnwire.EmptyFeatureVector(), + var node1Key, node2Key route.Vertex + copy(node1Key[:], firstNode[:]) + copy(node2Key[:], secondNode[:]) + + extraData := []byte{ + 1, 1, 1, + 2, 2, 2, 2, + 3, 3, 3, 3, 3, } - copy(edgeInfo.NodeKey1Bytes[:], firstNode[:]) - copy(edgeInfo.NodeKey2Bytes[:], secondNode[:]) - copy(edgeInfo.BitcoinKey1Bytes[:], firstNode[:]) - copy(edgeInfo.BitcoinKey2Bytes[:], secondNode[:]) + var edgeInfo *models.ChannelEdgeInfo if opts.skipProofs { + edgeInfo, _ = models.NewV1Channel( + chanID, + *chaincfg.MainNetParams.GenesisHash, + node1Key, + node2Key, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: node1Key, + BitcoinKey2Bytes: node2Key, + ExtraOpaqueData: extraData, + }, + models.WithChannelPoint(outpoint), + models.WithCapacity(1000), + ) + return edgeInfo, nil, nil } - edgeInfo.AuthProof = &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), - } + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + ) + + edgeInfo, _ = models.NewV1Channel( + chanID, + *chaincfg.MainNetParams.GenesisHash, + node1Key, + node2Key, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: node1Key, + BitcoinKey2Bytes: node2Key, + ExtraOpaqueData: extraData, + }, + models.WithChanProof(proof), + models.WithChannelPoint(outpoint), + models.WithCapacity(1000), + ) edge1 := &models.ChannelEdgePolicy{ SigBytes: testSig.Serialize(), @@ -1313,12 +1382,12 @@ func TestAddEdgeProof(t *testing.T) { require.Equal(t, edge1, dbEdge) // Now, add the edge proof. - proof := &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), - } + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + ) // First, add the proof to the rest of the channel edge info and try // to call AddChannelEdge again - this should fail due to the channel @@ -1715,24 +1784,32 @@ func fillTestGraph(t testing.TB, graph *ChannelGraph, numNodes, Index: 0, } - edgeInfo := models.ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: *chaincfg.MainNetParams.GenesisHash, - AuthProof: &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), + var node1Key, node2Key route.Vertex + copy(node1Key[:], node1.PubKeyBytes[:]) + copy(node2Key[:], node2.PubKeyBytes[:]) + + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + ) + + edgeInfo, err := models.NewV1Channel( + chanID, + *chaincfg.MainNetParams.GenesisHash, + node1Key, + node2Key, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: node1Key, + BitcoinKey2Bytes: node2Key, }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: op, - Capacity: 1000, - } - copy(edgeInfo.NodeKey1Bytes[:], node1.PubKeyBytes[:]) - copy(edgeInfo.NodeKey2Bytes[:], node2.PubKeyBytes[:]) - copy(edgeInfo.BitcoinKey1Bytes[:], node1.PubKeyBytes[:]) - copy(edgeInfo.BitcoinKey2Bytes[:], node2.PubKeyBytes[:]) - err := graph.AddChannelEdge(ctx, &edgeInfo) + models.WithChanProof(proof), + models.WithChannelPoint(op), + models.WithCapacity(1000), + ) + require.NoError(t, err) + err = graph.AddChannelEdge(ctx, edgeInfo) require.NoError(t, err) // Create and add an edge with random data that points @@ -1897,37 +1974,40 @@ func TestGraphPruning(t *testing.T) { channelPoints = append(channelPoints, &op) - edgeInfo := models.ChannelEdgeInfo{ - ChannelID: chanID, - ChainHash: *chaincfg.MainNetParams.GenesisHash, - AuthProof: &models.ChannelAuthProof{ - NodeSig1Bytes: testSig.Serialize(), - NodeSig2Bytes: testSig.Serialize(), - BitcoinSig1Bytes: testSig.Serialize(), - BitcoinSig2Bytes: testSig.Serialize(), - }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: op, - Capacity: 1000, - } - copy(edgeInfo.NodeKey1Bytes[:], graphNodes[i].PubKeyBytes[:]) - copy(edgeInfo.NodeKey2Bytes[:], graphNodes[i+1].PubKeyBytes[:]) - copy(edgeInfo.BitcoinKey1Bytes[:], graphNodes[i].PubKeyBytes[:]) - copy( - edgeInfo.BitcoinKey2Bytes[:], - graphNodes[i+1].PubKeyBytes[:], + var node1Key, node2Key route.Vertex + copy(node1Key[:], graphNodes[i].PubKeyBytes[:]) + copy(node2Key[:], graphNodes[i+1].PubKeyBytes[:]) + + proof := models.NewV1ChannelAuthProof( + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), + testSig.Serialize(), ) - if err := graph.AddChannelEdge(ctx, &edgeInfo); err != nil { - t.Fatalf("unable to add node: %v", err) - } - pkScript, err := genMultiSigP2WSH( - edgeInfo.BitcoinKey1Bytes[:], - edgeInfo.BitcoinKey2Bytes[:], + edgeInfo, err := models.NewV1Channel( + chanID, + *chaincfg.MainNetParams.GenesisHash, + node1Key, + node2Key, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: node1Key, + BitcoinKey2Bytes: node2Key, + }, + models.WithChanProof(proof), + models.WithChannelPoint(op), + models.WithCapacity(1000), ) if err != nil { - t.Fatalf("unable to gen multi-sig p2wsh: %v", err) + t.Fatalf("unable to create edge: %v", err) } + if err := graph.AddChannelEdge(ctx, edgeInfo); err != nil { + t.Fatalf("unable to add node: %v", err) + } + + pkScript, err := edgeInfo.FundingPKScript() + require.NoError(t, err) + edgePoints = append(edgePoints, EdgePoint{ FundingPkScript: pkScript, OutPoint: op, @@ -2073,10 +2153,10 @@ func TestHighestChanID(t *testing.T) { edge1, _ := createEdge(10, 0, 0, 0, node1, node2) edge2, chanID2 := createEdge(100, 0, 0, 0, node1, node2) - if err := graph.AddChannelEdge(ctx, &edge1); err != nil { + if err := graph.AddChannelEdge(ctx, edge1); err != nil { t.Fatalf("unable to create channel edge: %v", err) } - if err := graph.AddChannelEdge(ctx, &edge2); err != nil { + if err := graph.AddChannelEdge(ctx, edge2); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -2093,7 +2173,7 @@ func TestHighestChanID(t *testing.T) { // If we add another edge, then the current best chan ID should be // updated as well. edge3, chanID3 := createEdge(1000, 0, 0, 0, node1, node2) - if err := graph.AddChannelEdge(ctx, &edge3); err != nil { + if err := graph.AddChannelEdge(ctx, edge3); err != nil { t.Fatalf("unable to create channel edge: %v", err) } bestID, err = graph.HighestChanID(ctx) @@ -2149,7 +2229,7 @@ func TestChanUpdatesInHorizon(t *testing.T) { uint32(i*10), 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &channel); err != nil { + if err := graph.AddChannelEdge(ctx, channel); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -2178,7 +2258,7 @@ func TestChanUpdatesInHorizon(t *testing.T) { } edges = append(edges, ChannelEdge{ - Info: &channel, + Info: channel, Policy1: edge1, Policy2: edge2, }) @@ -2616,7 +2696,7 @@ func TestChanUpdatesInHorizonBoundaryConditions(t *testing.T) { uint32(i*10), 0, 0, 0, node1, node2, ) require.NoError( - t, graph.AddChannelEdge(ctx, &channel), + t, graph.AddChannelEdge(ctx, channel), ) edge1 := newEdgePolicy( @@ -2784,7 +2864,7 @@ func TestFilterKnownChanIDs(t *testing.T) { uint32(i*10), 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &channel); err != nil { + if err := graph.AddChannelEdge(ctx, channel); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -2799,7 +2879,7 @@ func TestFilterKnownChanIDs(t *testing.T) { channel, chanID := createEdge( uint32(i*10+1), 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &channel); err != nil { + if err := graph.AddChannelEdge(ctx, channel); err != nil { t.Fatalf("unable to create channel edge: %v", err) } err := graph.DeleteChannelEdges(false, true, channel.ChannelID) @@ -2960,7 +3040,7 @@ func TestStressTestChannelGraphAPI(t *testing.T) { ) newChan := &chanInfo{ - info: channel, + info: *channel, id: chanID, } chans = append(chans, newChan) @@ -3272,12 +3352,12 @@ func TestFilterChannelRange(t *testing.T) { channel1, chanID1 := createEdge( chanHeight, uint32(i+1), 0, 0, node1, node2, ) - require.NoError(t, graph.AddChannelEdge(ctx, &channel1)) + require.NoError(t, graph.AddChannelEdge(ctx, channel1)) channel2, chanID2 := createEdge( chanHeight, uint32(i+2), 0, 0, node1, node2, ) - require.NoError(t, graph.AddChannelEdge(ctx, &channel2)) + require.NoError(t, graph.AddChannelEdge(ctx, channel2)) chanInfo1 := NewChannelUpdateInfo( chanID1, time.Time{}, time.Time{}, @@ -3453,7 +3533,7 @@ func TestFetchChanInfos(t *testing.T) { uint32(i*10), 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &channel); err != nil { + if err := graph.AddChannelEdge(ctx, channel); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -3477,7 +3557,7 @@ func TestFetchChanInfos(t *testing.T) { } edges = append(edges, ChannelEdge{ - Info: &channel, + Info: channel, Policy1: edge1, Policy2: edge2, }) @@ -3494,7 +3574,7 @@ func TestFetchChanInfos(t *testing.T) { zombieChan, zombieChanID := createEdge( 666, 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &zombieChan); err != nil { + if err := graph.AddChannelEdge(ctx, zombieChan); err != nil { t.Fatalf("unable to create channel edge: %v", err) } err := graph.DeleteChannelEdges(false, true, zombieChan.ChannelID) @@ -3547,7 +3627,7 @@ func TestIncompleteChannelPolicies(t *testing.T) { uint32(0), 0, 0, 0, node1, node2, ) - if err := graph.AddChannelEdge(ctx, &channel); err != nil { + if err := graph.AddChannelEdge(ctx, channel); err != nil { t.Fatalf("unable to create channel edge: %v", err) } @@ -3651,7 +3731,7 @@ func TestChannelEdgePruningUpdateIndexDeletion(t *testing.T) { // With the two nodes created, we'll now create a random channel, as // well as two edges in the database with distinct update times. edgeInfo, chanID := createEdge(100, 0, 0, 0, node1, node2) - if err := graph.AddChannelEdge(ctx, &edgeInfo); err != nil { + if err := graph.AddChannelEdge(ctx, edgeInfo); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -3800,7 +3880,7 @@ func TestPruneGraphNodes(t *testing.T) { // We'll now add a new edge to the graph, but only actually advertise // the edge of *one* of the nodes. edgeInfo, chanID := createEdge(100, 0, 0, 0, node1, node2) - if err := graph.AddChannelEdge(ctx, &edgeInfo); err != nil { + if err := graph.AddChannelEdge(ctx, edgeInfo); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -3849,7 +3929,7 @@ func TestAddChannelEdgeShellNodes(t *testing.T) { // We'll now create an edge between the two nodes, as a result, node2 // should be inserted into the database as a shell node. edgeInfo, _ := createEdge(100, 0, 0, 0, node1, node2) - require.NoError(t, graph.AddChannelEdge(ctx, &edgeInfo)) + require.NoError(t, graph.AddChannelEdge(ctx, edgeInfo)) // Ensure that node1 was inserted as a full node, while node2 only has // a shell node present. @@ -3863,7 +3943,7 @@ func TestAddChannelEdgeShellNodes(t *testing.T) { // Show that attempting to add the channel again will result in an // error. - err = graph.AddChannelEdge(ctx, &edgeInfo) + err = graph.AddChannelEdge(ctx, edgeInfo) require.ErrorIs(t, err, ErrEdgeAlreadyExist) // Show that updating the shell node to a full node record works. @@ -3982,7 +4062,7 @@ func TestNodeIsPublic(t *testing.T) { // After creating all of our nodes and edges, we'll add them to each // participant's graph. nodes := []*models.Node{aliceNode, bobNode, carolNode} - edges := []*models.ChannelEdgeInfo{&aliceBobEdge, &bobCarolEdge} + edges := []*models.ChannelEdgeInfo{aliceBobEdge, bobCarolEdge} graphs := []*ChannelGraph{aliceGraph, bobGraph, carolGraph} for _, graph := range graphs { for _, node := range nodes { @@ -4064,7 +4144,7 @@ func TestNodeIsPublic(t *testing.T) { } bobCarolEdge.AuthProof = nil - if err := graph.AddChannelEdge(ctx, &bobCarolEdge); err != nil { + if err := graph.AddChannelEdge(ctx, bobCarolEdge); err != nil { t.Fatalf("unable to add edge: %v", err) } } @@ -4545,7 +4625,7 @@ func TestBatchedAddChannelEdge(t *testing.T) { // Create a third edge, this with a block height of 155. edgeInfo3, _ := createEdge(height-1, 0, 0, 2, node1, node2) - edges := []models.ChannelEdgeInfo{edgeInfo, edgeInfo2, edgeInfo3} + edges := []models.ChannelEdgeInfo{*edgeInfo, *edgeInfo2, *edgeInfo3} errChan := make(chan error, len(edges)) errTimeout := errors.New("timeout adding batched channel") diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go index ccc67978589..b801d6780b1 100644 --- a/graph/db/kv_store.go +++ b/graph/db/kv_store.go @@ -1262,9 +1262,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[:]) @@ -1398,6 +1398,12 @@ func (c *KVStore) HasChannelEdge( 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()) @@ -3942,10 +3948,17 @@ 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 } @@ -4100,10 +4113,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 } @@ -4702,6 +4712,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 { @@ -4710,10 +4726,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 } @@ -4729,10 +4759,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 { @@ -4751,7 +4781,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 } @@ -4792,18 +4822,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 { @@ -4818,24 +4856,42 @@ func deserializeChanEdgeInfo(r io.Reader) (*models.ChannelEdgeInfo, error) { } edgeInfo.Features = lnwire.NewFeatureVector(features, lnwire.Features) - 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 diff --git a/graph/db/models/channel_auth_proof.go b/graph/db/models/channel_auth_proof.go index daf120b10d2..98ebd7c8fef 100644 --- a/graph/db/models/channel_auth_proof.go +++ b/graph/db/models/channel_auth_proof.go @@ -1,35 +1,119 @@ 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 v2 channel announcements, we only have a single signature. + if c.Signature.IsSome() { + return len(c.Signature.UnwrapOr([]byte{})) == 0 + } + + // For v1 channel announcements, we either have all four signatures or + // none. + return len(c.NodeSig1Bytes.UnwrapOr([]byte{})) == 0 +} + +// 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 an empty slice if not +// present. +func (c *ChannelAuthProof) NodeSig1() []byte { + return c.NodeSig1Bytes.UnwrapOr([]byte{}) +} + +// NodeSig2 returns the second node signature bytes, or an empty slice if not +// present. +func (c *ChannelAuthProof) NodeSig2() []byte { + return c.NodeSig2Bytes.UnwrapOr([]byte{}) +} + +// BitcoinSig1 returns the first bitcoin signature bytes, or an empty slice if +// not present. +func (c *ChannelAuthProof) BitcoinSig1() []byte { + return c.BitcoinSig1Bytes.UnwrapOr([]byte{}) +} + +// BitcoinSig2 returns the second bitcoin signature bytes, or an empty slice if +// not present. +func (c *ChannelAuthProof) BitcoinSig2() []byte { + return c.BitcoinSig2Bytes.UnwrapOr([]byte{}) +} + +// Sig returns the v2 signature bytes, or an empty slice if not present. +func (c *ChannelAuthProof) Sig() []byte { + return c.Signature.UnwrapOr([]byte{}) } diff --git a/graph/db/models/channel_edge_info.go b/graph/db/models/channel_edge_info.go index b86c140bc1d..05573fb1d1a 100644 --- a/graph/db/models/channel_edge_info.go +++ b/graph/db/models/channel_edge_info.go @@ -9,7 +9,9 @@ import ( "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 +21,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,18 +34,18 @@ type ChannelEdgeInfo struct { ChainHash chainhash.Hash // NodeKey1Bytes is the raw public key of the first node. - NodeKey1Bytes [33]byte + NodeKey1Bytes route.Vertex nodeKey1 *btcec.PublicKey // NodeKey2Bytes is the raw public key of the first node. - NodeKey2Bytes [33]byte + NodeKey2Bytes route.Vertex nodeKey2 *btcec.PublicKey // BitcoinKey1Bytes is the raw public key of the first node. - BitcoinKey1Bytes [33]byte + BitcoinKey1Bytes fn.Option[route.Vertex] // BitcoinKey2Bytes is the raw public key of the first node. - BitcoinKey2Bytes [33]byte + BitcoinKey2Bytes fn.Option[route.Vertex] // Features is the list of protocol features supported by this channel // edge. @@ -74,6 +79,92 @@ type ChannelEdgeInfo struct { ExtraOpaqueData []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 + } +} + +// 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 first 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 + // 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. + 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 +} + // 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 @@ -118,7 +209,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): @@ -126,7 +217,115 @@ 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) + + 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 +} diff --git a/graph/db/models/channel_edge_policy.go b/graph/db/models/channel_edge_policy.go index 48d748ee0ab..b47707f20f4 100644 --- a/graph/db/models/channel_edge_policy.go +++ b/graph/db/models/channel_edge_policy.go @@ -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" ) @@ -21,9 +20,6 @@ type ChannelEdgePolicy struct { // use SetSigBytes instead to make sure that the cache is invalidated. 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. @@ -84,31 +80,10 @@ type ChannelEdgePolicy struct { ExtraOpaqueData lnwire.ExtraOpaqueData } -// 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 - } - - sig, err := ecdsa.ParseSignature(c.SigBytes) - if err != nil { - return nil, err - } - - c.sig = sig - - return sig, nil -} - // SetSigBytes updates the signature and invalidates the cached parsed // signature. func (c *ChannelEdgePolicy) SetSigBytes(sig []byte) { c.SigBytes = sig - c.sig = nil } // IsDisabled determines whether the edge has the disabled bit set. diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go index b30f0a944c7..9f0b3f7e1e4 100644 --- a/graph/db/sql_store.go +++ b/graph/db/sql_store.go @@ -2022,9 +2022,25 @@ func (s *SQLStore) FetchChannelEdgesByID(chanID uint64) ( // populate the edge info with the public keys of each // party as this is the only information we have about // it. - edge = &models.ChannelEdgeInfo{} - copy(edge.NodeKey1Bytes[:], zombie.NodeKey1) - copy(edge.NodeKey2Bytes[:], zombie.NodeKey2) + node1, err := route.NewVertexFromBytes(zombie.NodeKey1) + if err != nil { + return err + } + node2, err := route.NewVertexFromBytes(zombie.NodeKey2) + if err != nil { + return err + } + zombieEdge, err := models.NewV1Channel( + 0, + chainhash.Hash{}, + node1, + node2, + &models.ChannelV1Fields{}, + ) + if err != nil { + return err + } + edge = zombieEdge return ErrZombieEdge } else if err != nil { @@ -2739,6 +2755,7 @@ func (s *SQLStore) ChannelView() ([]EdgePoint, error) { handleChannel := func(_ context.Context, channel sqlc.ListChannelsPaginatedRow) error { + // TODO(elle): update to handle V2 channels. pkScript, err := genMultiSigP2WSH( channel.BitcoinKey1, channel.BitcoinKey2, ) @@ -2946,6 +2963,12 @@ func (s *SQLStore) DisconnectBlockAtHeight(height uint32) ( func (s *SQLStore) AddEdgeProof(scid lnwire.ShortChannelID, proof *models.ChannelAuthProof) error { + // For now, we only support v1 channel proofs. + if proof.Version != lnwire.GossipVersion1 { + return fmt.Errorf("only v1 channel proofs supported, got v%d", + proof.Version) + } + var ( ctx = context.TODO() scidBytes = channelIDToBytes(scid.ToUint64()) @@ -2955,10 +2978,10 @@ func (s *SQLStore) AddEdgeProof(scid lnwire.ShortChannelID, res, err := db.AddV1ChannelProof( ctx, sqlc.AddV1ChannelProofParams{ Scid: scidBytes, - Node1Signature: proof.NodeSig1Bytes, - Node2Signature: proof.NodeSig2Bytes, - Bitcoin1Signature: proof.BitcoinSig1Bytes, - Bitcoin2Signature: proof.BitcoinSig2Bytes, + Node1Signature: proof.NodeSig1(), + Node2Signature: proof.NodeSig2(), + Bitcoin1Signature: proof.BitcoinSig1(), + Bitcoin2Signature: proof.BitcoinSig2(), }, ) if err != nil { @@ -4112,14 +4135,26 @@ func marshalExtraOpaqueData(data []byte) (map[uint64][]byte, error) { func insertChannel(ctx context.Context, db SQLQueries, edge *models.ChannelEdgeInfo) error { + v := lnwire.GossipVersion1 + + // For now, we only support V1 channel edges in the SQL store. + if edge.Version != v { + return fmt.Errorf("only V1 channel edges supported, got V%d", + edge.Version) + } + // Make sure that at least a "shell" entry for each node is present in // the nodes table. - node1DBID, err := maybeCreateShellNode(ctx, db, edge.NodeKey1Bytes) + node1DBID, err := maybeCreateShellNode( + ctx, db, v, edge.NodeKey1Bytes, + ) if err != nil { return fmt.Errorf("unable to create shell node: %w", err) } - node2DBID, err := maybeCreateShellNode(ctx, db, edge.NodeKey2Bytes) + node2DBID, err := maybeCreateShellNode( + ctx, db, v, edge.NodeKey2Bytes, + ) if err != nil { return fmt.Errorf("unable to create shell node: %w", err) } @@ -4130,23 +4165,27 @@ func insertChannel(ctx context.Context, db SQLQueries, } createParams := sqlc.CreateChannelParams{ - Version: int16(lnwire.GossipVersion1), - Scid: channelIDToBytes(edge.ChannelID), - NodeID1: node1DBID, - NodeID2: node2DBID, - Outpoint: edge.ChannelPoint.String(), - Capacity: capacity, - BitcoinKey1: edge.BitcoinKey1Bytes[:], - BitcoinKey2: edge.BitcoinKey2Bytes[:], - } + Version: int16(v), + Scid: channelIDToBytes(edge.ChannelID), + NodeID1: node1DBID, + NodeID2: node2DBID, + Outpoint: edge.ChannelPoint.String(), + Capacity: capacity, + } + edge.BitcoinKey1Bytes.WhenSome(func(vertex route.Vertex) { + createParams.BitcoinKey1 = vertex[:] + }) + edge.BitcoinKey2Bytes.WhenSome(func(vertex route.Vertex) { + createParams.BitcoinKey2 = vertex[:] + }) if edge.AuthProof != nil { proof := edge.AuthProof - createParams.Node1Signature = proof.NodeSig1Bytes - createParams.Node2Signature = proof.NodeSig2Bytes - createParams.Bitcoin1Signature = proof.BitcoinSig1Bytes - createParams.Bitcoin2Signature = proof.BitcoinSig2Bytes + createParams.Node1Signature = proof.NodeSig1() + createParams.Node2Signature = proof.NodeSig2() + createParams.Bitcoin1Signature = proof.BitcoinSig1() + createParams.Bitcoin2Signature = proof.BitcoinSig2() } // Insert the new channel record. @@ -4199,12 +4238,12 @@ func insertChannel(ctx context.Context, db SQLQueries, // created. The ID of the node is returned. A shell node only has a protocol // version and public key persisted. func maybeCreateShellNode(ctx context.Context, db SQLQueries, - pubKey route.Vertex) (int64, error) { + v lnwire.GossipVersion, pubKey route.Vertex) (int64, error) { dbNode, err := db.GetNodeByPubKey( ctx, sqlc.GetNodeByPubKeyParams{ PubKey: pubKey[:], - Version: int16(lnwire.GossipVersion1), + Version: int16(v), }, ) // The node exists. Return the ID. @@ -4217,7 +4256,7 @@ func maybeCreateShellNode(ctx context.Context, db SQLQueries, // Otherwise, the node does not exist, so we create a shell entry for // it. id, err := db.UpsertNode(ctx, sqlc.UpsertNodeParams{ - Version: int16(lnwire.GossipVersion1), + Version: int16(v), PubKey: pubKey[:], }) if err != nil { @@ -4320,33 +4359,47 @@ func buildEdgeInfoWithBatchData(chain chainhash.Hash, recs = make([]byte, 0) } - var btcKey1, btcKey2 route.Vertex - copy(btcKey1[:], dbChan.BitcoinKey1) - copy(btcKey2[:], dbChan.BitcoinKey2) + btcKey1, err := route.NewVertexFromBytes(dbChan.BitcoinKey1) + if err != nil { + return nil, err + } + btcKey2, err := route.NewVertexFromBytes(dbChan.BitcoinKey2) + if err != nil { + return nil, err + } - channel := &models.ChannelEdgeInfo{ - ChainHash: chain, - ChannelID: byteOrder.Uint64(dbChan.Scid), - NodeKey1Bytes: node1, - NodeKey2Bytes: node2, - BitcoinKey1Bytes: btcKey1, - BitcoinKey2Bytes: btcKey2, - ChannelPoint: *op, - Capacity: btcutil.Amount(dbChan.Capacity.Int64), - Features: fv, - ExtraOpaqueData: recs, + channel, err := models.NewV1Channel( + byteOrder.Uint64(dbChan.Scid), + chain, + node1, + node2, + &models.ChannelV1Fields{ + BitcoinKey1Bytes: btcKey1, + BitcoinKey2Bytes: btcKey2, + ExtraOpaqueData: recs, + }, + models.WithChannelPoint(*op), + models.WithCapacity(btcutil.Amount(dbChan.Capacity.Int64)), + models.WithFeatures(fv.RawFeatureVector), + ) + if err != nil { + return nil, err } // We always set all the signatures at the same time, so we can // safely check if one signature is present to determine if we have the // rest of the signatures for the auth proof. if len(dbChan.Bitcoin1Signature) > 0 { - channel.AuthProof = &models.ChannelAuthProof{ - NodeSig1Bytes: dbChan.Node1Signature, - NodeSig2Bytes: dbChan.Node2Signature, - BitcoinSig1Bytes: dbChan.Bitcoin1Signature, - BitcoinSig2Bytes: dbChan.Bitcoin2Signature, + // For v1 channels, we have four signatures. + if dbChan.Version == int16(lnwire.GossipVersion1) { + channel.AuthProof = models.NewV1ChannelAuthProof( + dbChan.Node1Signature, + dbChan.Node2Signature, + dbChan.Bitcoin1Signature, + dbChan.Bitcoin2Signature, + ) } + // TODO(elle): Add v2 support when needed. } return channel, nil diff --git a/graph/notifications_test.go b/graph/notifications_test.go index e3f48712e2d..4edb49a87cd 100644 --- a/graph/notifications_test.go +++ b/graph/notifications_test.go @@ -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 { @@ -447,23 +448,31 @@ 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(), + 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, }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: *chanPoint, - Capacity: chanValue, - FundingScript: fn.Some(script), - } - copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + models.WithChannelPoint(*chanPoint), + models.WithCapacity(chanValue), + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) @@ -641,21 +650,29 @@ 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(), + 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, }, - FundingScript: fn.Some(script), - } - copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) // Adding the edge will add the nodes to the graph, but with no info // except the pubkey known. @@ -827,23 +844,31 @@ 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(), + 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, }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: *chanPoint, - Capacity: chanValue, - FundingScript: fn.Some(script), - } - copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + models.WithChannelPoint(*chanPoint), + models.WithCapacity(chanValue), + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) } @@ -903,23 +928,31 @@ 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(), + 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, }, - Features: lnwire.EmptyFeatureVector(), - ChannelPoint: *chanUtxo, - Capacity: chanValue, - FundingScript: fn.Some(script), - } - copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed()) - copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed()) + models.WithChanProof(proof), + models.WithChannelPoint(*chanUtxo), + models.WithCapacity(chanValue), + ) + require.NoError(t, err) + edge.FundingScript = fn.Some(script) if err := ctx.builder.AddEdge(ctxb, edge); err != nil { t.Fatalf("unable to add edge: %v", err) } diff --git a/lnrpc/devrpc/dev_server.go b/lnrpc/devrpc/dev_server.go index 08f01e14704..27b7f5b640a 100644 --- a/lnrpc/devrpc/dev_server.go +++ b/lnrpc/devrpc/dev_server.go @@ -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 { @@ -273,27 +272,33 @@ 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), + node1, err := parsePubKey(rpcEdge.Node1Pub) + if err != nil { + return nil, err } - edge.NodeKey1Bytes, err = parsePubKey(rpcEdge.Node1Pub) + node2, err := parsePubKey(rpcEdge.Node2Pub) if err != nil { return nil, err } - edge.NodeKey2Bytes, err = parsePubKey(rpcEdge.Node2Pub) + channelPoint, err := parseOutPoint(rpcEdge.ChanPoint) if err != nil { return nil, err } - channelPoint, err := parseOutPoint(rpcEdge.ChanPoint) + 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 } - edge.ChannelPoint = *channelPoint if err := graphDB.AddChannelEdge(ctx, edge); err != nil { return nil, fmt.Errorf("unable to add edge %v: %w", diff --git a/lnrpc/invoicesrpc/addinvoice_test.go b/lnrpc/invoicesrpc/addinvoice_test.go index 9394ce26d4a..04db2463fac 100644 --- a/lnrpc/invoicesrpc/addinvoice_test.go +++ b/lnrpc/invoicesrpc/addinvoice_test.go @@ -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,9 +307,18 @@ 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 + }(), &models.ChannelEdgePolicy{ FeeBaseMSat: 1000, FeeProportionalMillionths: 20, diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go index 024b779322b..06b1628e3ef 100644 --- a/netann/chan_status_manager_test.go +++ b/netann/chan_status_manager_test.go @@ -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,11 +102,25 @@ 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{ + BitcoinKey1Bytes: pubkey1Vertex, + BitcoinKey2Bytes: pubkey2Vertex, }, + models.WithChannelPoint(channel.FundingOutpoint), + ) + require.NoError(t, err) + + return edgeInfo, &models.ChannelEdgePolicy{ ChannelID: channel.ShortChanID().ToUint64(), ChannelFlags: dir1, diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 9bb21c40155..14f65c3f549 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -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 } diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go index 38949e046e4..f5f8aa0923d 100644 --- a/netann/channel_announcement_test.go +++ b/netann/channel_announcement_test.go @@ -40,29 +40,29 @@ 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) diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go index b1d281187d1..8f694b73406 100644 --- a/routing/localchans/manager.go +++ b/routing/localchans/manager.go @@ -17,6 +17,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 @@ -321,18 +322,38 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel, shortChanID = channel.ZeroConfRealScid() } - info := &models.ChannelEdgeInfo{ - ChannelID: shortChanID.ToUint64(), - ChainHash: channel.ChainHash, - Features: lnwire.EmptyFeatureVector(), - Capacity: channel.Capacity, - ChannelPoint: channel.FundingOutpoint, + 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), + ) + 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. diff --git a/routing/localchans/manager_test.go b/routing/localchans/manager_test.go index a2e7164b20b..571df8f65b9 100644 --- a/routing/localchans/manager_test.go +++ b/routing/localchans/manager_test.go @@ -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" "github.com/stretchr/testify/require" ) @@ -209,10 +210,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}, @@ -227,10 +239,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{}, @@ -245,10 +268,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}, @@ -267,10 +302,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}, @@ -385,21 +432,21 @@ func TestCreateEdgeLower(t *testing.T) { Index: 0, }, } - 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, - } + 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), + ) + require.NoError(t, err) expectedEdge := &models.ChannelEdgePolicy{ ChannelID: 8, LastUpdate: timestamp, @@ -473,21 +520,21 @@ func TestCreateEdgeHigher(t *testing.T) { Index: 0, }, } - 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, - } + 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), + ) + require.NoError(t, err) expectedEdge := &models.ChannelEdgePolicy{ ChannelID: 8, LastUpdate: timestamp, diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index 132d61fb873..9db14e99e0b 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -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,19 +346,31 @@ 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) + + 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 } - copy(edgeInfo.NodeKey1Bytes[:], node1Bytes) - copy(edgeInfo.NodeKey2Bytes[:], node2Bytes) - copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes) - copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes) - shortID := lnwire.NewShortChanIDFromInt(edge.ChannelID) links[shortID] = &mockLink{ bandwidth: lnwire.MilliSatoshi( @@ -365,7 +378,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 } @@ -395,12 +408,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 } @@ -677,20 +690,24 @@ 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 } diff --git a/routing/router_test.go b/routing/router_test.go index cb4393c06ed..09df64c26d1 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -2738,15 +2738,17 @@ 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 @@ -2818,15 +2820,20 @@ 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)) diff --git a/rpcserver.go b/rpcserver.go index 3997a06eb2e..ca9343bb6e8 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -6937,10 +6937,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(), } } diff --git a/sqldb/sqlc/graph.sql.go b/sqldb/sqlc/graph.sql.go index 26d03c96aa4..4342eeba980 100644 --- a/sqldb/sqlc/graph.sql.go +++ b/sqldb/sqlc/graph.sql.go @@ -78,9 +78,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 ` @@ -98,6 +98,9 @@ type CreateChannelParams struct { Node2Signature []byte Bitcoin1Signature []byte Bitcoin2Signature []byte + Signature []byte + FundingPkScript []byte + MerkleRootHash []byte } func (q *Queries) CreateChannel(ctx context.Context, arg CreateChannelParams) (int64, error) { @@ -114,6 +117,9 @@ func (q *Queries) CreateChannel(ctx context.Context, arg CreateChannelParams) (i arg.Node2Signature, arg.Bitcoin1Signature, arg.Bitcoin2Signature, + arg.Signature, + arg.FundingPkScript, + arg.MerkleRootHash, ) var id int64 err := row.Scan(&id) diff --git a/sqldb/sqlc/queries/graph.sql b/sqldb/sqlc/queries/graph.sql index b1e3523d86e..4f02f1775c5 100644 --- a/sqldb/sqlc/queries/graph.sql +++ b/sqldb/sqlc/queries/graph.sql @@ -266,9 +266,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;