From 44f41714152cdbdd493df635d9ed5b85c21b73ed Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 19 Nov 2025 13:11:02 +0200 Subject: [PATCH] graph/db: make some ChannelEdgeInfo fields optional Since not all will be required for V2 channels. Wrap BitcoinKey1Bytes and BitcoinKey2Bytes in fn.Option since these fields are only required for v1 channel announcements. V2 channels may or may not have bitcoin keys present in their announcement. NewV1Channel constructor wraps the bitcoin keys with fn.Some(). All access sites updated to unwrap the options, using UnwrapOr for non-critical paths and UnwrapOrErr where the keys must be present (e.g., KV serialization, ToChannelAnnouncement). --- graph/db/graph_test.go | 13 +++++++--- graph/db/kv_store.go | 38 +++++++++++++++++++++++----- graph/db/models/channel_edge_info.go | 27 +++++++++++++++----- graph/db/sql_store.go | 20 +++++++++------ 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/graph/db/graph_test.go b/graph/db/graph_test.go index c9e703f4d..5c9c0308d 100644 --- a/graph/db/graph_test.go +++ b/graph/db/graph_test.go @@ -838,10 +838,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") } @@ -2040,9 +2044,10 @@ func TestGraphPruning(t *testing.T) { t.Fatalf("unable to add node: %v", err) } + btcKey1 := edgeInfo.BitcoinKey1Bytes.UnwrapOr(route.Vertex{}) + btcKey2 := edgeInfo.BitcoinKey2Bytes.UnwrapOr(route.Vertex{}) pkScript, err := genMultiSigP2WSH( - edgeInfo.BitcoinKey1Bytes[:], - edgeInfo.BitcoinKey2Bytes[:], + btcKey1[:], btcKey2[:], ) if err != nil { t.Fatalf("unable to gen multi-sig p2wsh: %v", err) diff --git a/graph/db/kv_store.go b/graph/db/kv_store.go index 14185f11a..f5a1ec219 100644 --- a/graph/db/kv_store.go +++ b/graph/db/kv_store.go @@ -4112,9 +4112,14 @@ func (c *KVStore) ChannelView() ([]EdgePoint, error) { return err } + btcKey1 := edgeInfo.BitcoinKey1Bytes.UnwrapOr( + route.Vertex{}, + ) + btcKey2 := edgeInfo.BitcoinKey2Bytes.UnwrapOr( + route.Vertex{}, + ) pkScript, err := genMultiSigP2WSH( - edgeInfo.BitcoinKey1Bytes[:], - edgeInfo.BitcoinKey2Bytes[:], + btcKey1[:], btcKey2[:], ) if err != nil { return err @@ -4728,10 +4733,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 } @@ -4769,7 +4788,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 } @@ -4891,12 +4910,17 @@ func deserializeChanEdgeInfo(r io.Reader) (*models.ChannelEdgeInfo, error) { 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 { diff --git a/graph/db/models/channel_edge_info.go b/graph/db/models/channel_edge_info.go index 9f9477064..aea69e919 100644 --- a/graph/db/models/channel_edge_info.go +++ b/graph/db/models/channel_edge_info.go @@ -39,10 +39,10 @@ type ChannelEdgeInfo struct { NodeKey2Bytes route.Vertex // BitcoinKey1Bytes is the raw public key of the first node. - BitcoinKey1Bytes route.Vertex + BitcoinKey1Bytes fn.Option[route.Vertex] // BitcoinKey2Bytes is the raw public key of the second node. - BitcoinKey2Bytes route.Vertex + BitcoinKey2Bytes fn.Option[route.Vertex] // Features is the list of protocol features supported by this channel // edge. @@ -147,8 +147,8 @@ func NewV1Channel(chanID uint64, chainHash chainhash.Hash, node1, Version: lnwire.GossipVersion1, NodeKey1Bytes: node1, NodeKey2Bytes: node2, - BitcoinKey1Bytes: v1Fields.BitcoinKey1Bytes, - BitcoinKey2Bytes: v1Fields.BitcoinKey2Bytes, + BitcoinKey1Bytes: fn.Some(v1Fields.BitcoinKey1Bytes), + BitcoinKey2Bytes: fn.Some(v1Fields.BitcoinKey2Bytes), ChannelID: chanID, ChainHash: chainHash, Features: lnwire.EmptyFeatureVector(), @@ -219,19 +219,32 @@ func (c *ChannelEdgeInfo) ToChannelAnnouncement() ( "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: c.BitcoinKey1Bytes, - BitcoinKey2: c.BitcoinKey2Bytes, + BitcoinKey1: btc1, + BitcoinKey2: btc2, Features: c.Features.RawFeatureVector, ExtraOpaqueData: c.ExtraOpaqueData, } - var err error chanAnn.NodeSig1, err = lnwire.NewSigFromECDSARawSignature( c.AuthProof.NodeSig1(), ) diff --git a/graph/db/sql_store.go b/graph/db/sql_store.go index 0ef5855bf..281975e0c 100644 --- a/graph/db/sql_store.go +++ b/graph/db/sql_store.go @@ -4288,15 +4288,19 @@ func insertChannel(ctx context.Context, db SQLQueries, } createParams := sqlc.CreateChannelParams{ - Version: int16(v), - 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