diff --git a/channeldb/graph.go b/channeldb/graph.go index b53933e15..a1b7c7a84 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -507,6 +507,34 @@ func (c *ChannelGraph) HasChannelEdge(chanID uint64) (time.Time, time.Time, bool return node1UpdateTime, node2UpdateTime, exists, nil } +// UpdateChannelEdge retrieves and update edge of the graph database. Method +// only reserved for updating an edge info after it's already been created. +// In order to maintain this constraints, we return an error in the scenario +// that an edge info hasn't yet been created yet, but someone attempts to update +// it. +func (c *ChannelGraph) UpdateChannelEdge(edge *ChannelEdgeInfo) error { + // Construct the channel's primary key which is the 8-byte channel ID. + var chanKey [8]byte + binary.BigEndian.PutUint64(chanKey[:], edge.ChannelID) + + return c.db.Update(func(tx *bolt.Tx) error { + edges, err := tx.CreateBucketIfNotExists(edgeBucket) + if err != nil { + return err + } + edgeIndex, err := edges.CreateBucketIfNotExists(edgeIndexBucket) + if err != nil { + return err + } + + if edgeInfo := edgeIndex.Get(chanKey[:]); edgeInfo == nil { + return ErrEdgeNotFound + } + + return putChanEdgeInfo(edgeIndex, edge, chanKey) + }) +} + const ( // pruneTipBytes is the total size of the value which stores the // current prune tip of the graph. The prune tip indicates if the diff --git a/discovery/service_test.go b/discovery/service_test.go index 85ce3e795..a0b0885dc 100644 --- a/discovery/service_test.go +++ b/discovery/service_test.go @@ -73,6 +73,11 @@ func (r *mockGraphSource) CurrentBlockHeight() (uint32, error) { return r.bestHeight, nil } +func (r *mockGraphSource) AddProof(chanID lnwire.ShortChannelID, + proof *channeldb.ChannelAuthProof) error { + return nil +} + func (r *mockGraphSource) ForEachNode(func(node *channeldb.LightningNode) error) error { return nil } diff --git a/routing/router.go b/routing/router.go index 3dbdd74b7..2012cad4a 100644 --- a/routing/router.go +++ b/routing/router.go @@ -34,6 +34,10 @@ type ChannelGraphSource interface { // edge/channel might be used in construction of payment path. AddEdge(edge *channeldb.ChannelEdgeInfo) error + // AddProof updates the channel edge info with proof which is needed + // to properly announce the edge to the rest of the network. + AddProof(chanID lnwire.ShortChannelID, proof *channeldb.ChannelAuthProof) error + // UpdateEdge is used to update edge information, without this // message edge considered as not fully constructed. UpdateEdge(policy *channeldb.ChannelEdgePolicy) error @@ -1063,3 +1067,18 @@ func (r *ChannelRouter) ForEachChannel(cb func(chanInfo *channeldb.ChannelEdgeIn e1, e2 *channeldb.ChannelEdgePolicy) error) error { return r.cfg.Graph.ForEachChannel(cb) } + +// AddProof updates the channel edge info with proof which is needed +// to properly announce the edge to the rest of the network. +// NOTE: Part of the Router interface. +func (r *ChannelRouter) AddProof(chanID lnwire.ShortChannelID, + proof *channeldb.ChannelAuthProof) error { + + info, _, _, err := r.cfg.Graph.FetchChannelEdgesByID(chanID.ToUint64()) + if err != nil { + return err + } + + info.AuthProof = proof + return r.cfg.Graph.UpdateChannelEdge(info) +} diff --git a/routing/router_test.go b/routing/router_test.go index a8bd7d566..b965916ed 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -206,3 +206,51 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) { route.Hops[0].Channel.Node.Alias) } } + +// TestAddProof checks that we can update the channel proof after channel +// info was added to the database. +func TestAddProof(t *testing.T) { + ctx, cleanup, err := createTestCtx(0) + if err != nil { + t.Fatal(err) + } + defer cleanup() + + // In order to be able to add the edge we should have the valud + // funding UTXO within the blockchain. + fundingTx, _, chanID, err := createChannelEdge(ctx, + bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(), + 100, 0) + if err != nil { + t.Fatalf("unable create channel edge: %v", err) + } + + fundingBlock := &wire.MsgBlock{ + Transactions: []*wire.MsgTx{fundingTx}, + } + ctx.chain.addBlock(fundingBlock, chanID.BlockHeight) + + // After utxo was recreated adding the edge without the proof. + edge := &channeldb.ChannelEdgeInfo{ + ChannelID: chanID.ToUint64(), + NodeKey1: priv1.PubKey(), + NodeKey2: priv1.PubKey(), + BitcoinKey1: bitcoinKey1, + BitcoinKey2: bitcoinKey2, + AuthProof: nil, + } + + if err := ctx.router.AddEdge(edge); err != nil { + t.Fatalf("unable to add edge: %v", err) + } + + // No trying to update the proof and checking that it have been updated. + if err := ctx.router.AddProof(*chanID, &testAuthProof); err != nil { + t.Fatalf("unable to add proof: %v", err) + } + + info, _, _, err := ctx.router.GetChannelByID(*chanID) + if info.AuthProof == nil { + t.Fatal("proof have been updated") + } +}