diff --git a/rpcserver.go b/rpcserver.go index a9052af1..be78dc87 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -3516,50 +3516,71 @@ func (r *rpcServer) marshallRoute(route *routing.Route) *lnrpc.Route { return resp } +// unmarshallHopByChannelLookup unmarshalls an rpc hop for which the pub key is +// not known. This function will query the channel graph with channel id to +// retrieve both endpoints and determine the hop pubkey using the previous hop +// pubkey. If the channel is unknown, an error is returned. +func unmarshallHopByChannelLookup(graph *channeldb.ChannelGraph, hop *lnrpc.Hop, + prevPubKeyBytes []byte) (*routing.Hop, error) { + + // Discard edge policies, because they may be nil. + edgeInfo, _, _, err := graph.FetchChannelEdgesByID(hop.ChanId) + if err != nil { + return nil, fmt.Errorf("unable to fetch channel edges by "+ + "channel ID %d: %v", hop.ChanId, err) + } + + var pubKeyBytes [33]byte + switch { + case bytes.Equal(prevPubKeyBytes, edgeInfo.NodeKey1Bytes[:]): + pubKeyBytes = edgeInfo.NodeKey2Bytes + case bytes.Equal(prevPubKeyBytes, edgeInfo.NodeKey2Bytes[:]): + pubKeyBytes = edgeInfo.NodeKey1Bytes + default: + return nil, fmt.Errorf("channel edge does not match expected node") + } + + return &routing.Hop{ + OutgoingTimeLock: hop.Expiry, + AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), + PubKeyBytes: pubKeyBytes, + ChannelID: edgeInfo.ChannelID, + }, nil +} + +// unmarshallRoute unmarshalls an rpc route. For hops that don't specify a +// pubkey, the channel graph is queried. func (r *rpcServer) unmarshallRoute(rpcroute *lnrpc.Route, graph *channeldb.ChannelGraph) (*routing.Route, error) { - node, err := graph.SourceNode() + sourceNode, err := graph.SourceNode() if err != nil { return nil, fmt.Errorf("unable to fetch source node from graph "+ "while unmarshaling route. %v", err) } - nodePubKeyBytes := node.PubKeyBytes[:] + prevNodePubKey := sourceNode.PubKeyBytes[:] hops := make([]*routing.Hop, len(rpcroute.Hops)) for i, hop := range rpcroute.Hops { - // Discard edge policies, because they may be nil. - edgeInfo, _, _, err := graph.FetchChannelEdgesByID(hop.ChanId) + // No hop pub key is given, so the local channel graph needs to + // be queried to complete the information necessary for routing. + routeHop, err := unmarshallHopByChannelLookup(graph, + hop, prevNodePubKey) + if err != nil { - return nil, fmt.Errorf("unable to fetch channel edges by "+ - "channel ID for hop (%d): %v", i, err) + return nil, err } - var pubKeyBytes [33]byte - switch { - case bytes.Equal(nodePubKeyBytes[:], edgeInfo.NodeKey1Bytes[:]): - pubKeyBytes = edgeInfo.NodeKey2Bytes - case bytes.Equal(nodePubKeyBytes[:], edgeInfo.NodeKey2Bytes[:]): - pubKeyBytes = edgeInfo.NodeKey1Bytes - default: - return nil, fmt.Errorf("channel edge does not match expected node") - } + hops[i] = routeHop - hops[i] = &routing.Hop{ - OutgoingTimeLock: hop.Expiry, - AmtToForward: lnwire.MilliSatoshi(hop.AmtToForwardMsat), - PubKeyBytes: pubKeyBytes, - ChannelID: edgeInfo.ChannelID, - } - - nodePubKeyBytes = pubKeyBytes[:] + prevNodePubKey = routeHop.PubKeyBytes[:] } route := routing.NewRouteFromHops( lnwire.MilliSatoshi(rpcroute.TotalAmtMsat), rpcroute.TotalTimeLock, - node.PubKeyBytes, + sourceNode.PubKeyBytes, hops, )