lnrpc+routing: convert amt pointer to fn.Option

This commit is contained in:
bitromortac 2024-07-31 14:32:48 +02:00
parent 8b32e3e785
commit 452db01ad7
No known key found for this signature in database
GPG Key ID: 1965063FC13BEBE2
3 changed files with 55 additions and 54 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lntypes"
@ -1411,10 +1412,10 @@ func (s *Server) BuildRoute(_ context.Context,
} }
// Prepare BuildRoute call parameters from rpc request. // Prepare BuildRoute call parameters from rpc request.
var amt *lnwire.MilliSatoshi var amt fn.Option[lnwire.MilliSatoshi]
if req.AmtMsat != 0 { if req.AmtMsat != 0 {
rpcAmt := lnwire.MilliSatoshi(req.AmtMsat) rpcAmt := lnwire.MilliSatoshi(req.AmtMsat)
amt = &rpcAmt amt = fn.Some(rpcAmt)
} }
var outgoingChan *uint64 var outgoingChan *uint64

View File

@ -1406,7 +1406,7 @@ func (e ErrNoChannel) Error() string {
// BuildRoute returns a fully specified route based on a list of pubkeys. If // BuildRoute returns a fully specified route based on a list of pubkeys. If
// amount is nil, the minimum routable amount is used. To force a specific // amount is nil, the minimum routable amount is used. To force a specific
// outgoing channel, use the outgoingChan parameter. // outgoing channel, use the outgoingChan parameter.
func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi, func (r *ChannelRouter) BuildRoute(amt fn.Option[lnwire.MilliSatoshi],
hops []route.Vertex, outgoingChan *uint64, hops []route.Vertex, outgoingChan *uint64,
finalCltvDelta int32, payAddr *[32]byte) (*route.Route, error) { finalCltvDelta int32, payAddr *[32]byte) (*route.Route, error) {
@ -1439,43 +1439,36 @@ func (r *ChannelRouter) BuildRoute(amt *lnwire.MilliSatoshi,
return nil, err return nil, err
} }
// If no amount is specified, we need to build a route for the minimum
// amount that this route can carry.
useMinAmt := amt == nil
var ( var (
receiverAmt lnwire.MilliSatoshi receiverAmt lnwire.MilliSatoshi
senderAmt lnwire.MilliSatoshi
pathEdges []*unifiedEdge pathEdges []*unifiedEdge
) )
if useMinAmt { // We determine the edges compatible with the requested amount, as well
// For minimum amount routes, aim to deliver at least 1 msat to // as the amount to send, which can be used to determine the final
// the destination. There are nodes in the wild that have a // receiver amount, if a minimal amount was requested.
// min_htlc channel policy of zero, which could lead to a zero pathEdges, senderAmt, err = senderAmtBackwardPass(
// amount payment being made. unifiers, amt, bandwidthHints,
var senderAmt lnwire.MilliSatoshi )
pathEdges, senderAmt, err = senderAmtBackwardPass( if err != nil {
unifiers, useMinAmt, 1, bandwidthHints, return nil, err
) }
if err != nil {
return nil, err
}
receiverAmt, err = receiverAmtForwardPass(senderAmt, pathEdges) // For the minimal amount search, we need to do a forward pass to find a
if err != nil { // larger receiver amount due to possible min HTLC bumps, otherwise we
return nil, err // just use the requested amount.
} receiverAmt, err = fn.ElimOption(
} else { amt,
// If an amount is specified, we need to build a route that func() fn.Result[lnwire.MilliSatoshi] {
// delivers exactly this amount to the final destination. return fn.NewResult(
pathEdges, _, err = senderAmtBackwardPass( receiverAmtForwardPass(senderAmt, pathEdges),
unifiers, useMinAmt, *amt, bandwidthHints, )
) },
if err != nil { fn.Ok[lnwire.MilliSatoshi],
return nil, err ).Unpack()
} if err != nil {
return nil, err
receiverAmt = *amt
} }
// Fetch the current block height outside the routing transaction, to // Fetch the current block height outside the routing transaction, to
@ -1545,9 +1538,9 @@ func getEdgeUnifiers(source route.Vertex, hops []route.Vertex,
// senderAmtBackwardPass returns a list of unified edges for the given route and // senderAmtBackwardPass returns a list of unified edges for the given route and
// determines the amount that should be sent to fulfill min HTLC requirements. // determines the amount that should be sent to fulfill min HTLC requirements.
// The minimal sender amount can be searched for by activating useMinAmt. // The minimal sender amount can be searched for by using amt=None.
func senderAmtBackwardPass(unifiers []*edgeUnifier, useMinAmt bool, func senderAmtBackwardPass(unifiers []*edgeUnifier,
receiverAmt lnwire.MilliSatoshi, amt fn.Option[lnwire.MilliSatoshi],
bandwidthHints bandwidthHints) ([]*unifiedEdge, lnwire.MilliSatoshi, bandwidthHints bandwidthHints) ([]*unifiedEdge, lnwire.MilliSatoshi,
error) { error) {
@ -1563,11 +1556,15 @@ func senderAmtBackwardPass(unifiers []*edgeUnifier, useMinAmt bool,
// incomingAmt tracks the amount that is forwarded on the edges of a // incomingAmt tracks the amount that is forwarded on the edges of a
// route. The last hop only forwards the amount that the receiver should // route. The last hop only forwards the amount that the receiver should
// receive, as there are no fees paid to the last node. // receive, as there are no fees paid to the last node.
incomingAmt := receiverAmt // For minimum amount routes, aim to deliver at least 1 msat to
// the destination. There are nodes in the wild that have a
// min_htlc channel policy of zero, which could lead to a zero
// amount payment being made.
incomingAmt := amt.UnwrapOr(1)
// If using min amt, increase the amount if needed to fulfill min HTLC // If using min amt, increase the amount if needed to fulfill min HTLC
// requirements. // requirements.
if useMinAmt { if amt.IsNone() {
min := edgeUnifier.minAmt() min := edgeUnifier.minAmt()
if min > incomingAmt { if min > incomingAmt {
incomingAmt = min incomingAmt = min
@ -1591,7 +1588,7 @@ func senderAmtBackwardPass(unifiers []*edgeUnifier, useMinAmt bool,
// If using min amt, increase the amount if needed to fulfill // If using min amt, increase the amount if needed to fulfill
// min HTLC requirements. // min HTLC requirements.
if useMinAmt { if amt.IsNone() {
min := edgeUnifier.minAmt() min := edgeUnifier.minAmt()
if min > incomingAmt { if min > incomingAmt {
incomingAmt = min incomingAmt = min

View File

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/graph"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
@ -1641,14 +1642,16 @@ func TestBuildRoute(t *testing.T) {
_, err = rand.Read(payAddr[:]) _, err = rand.Read(payAddr[:])
require.NoError(t, err) require.NoError(t, err)
noAmt := fn.None[lnwire.MilliSatoshi]()
// Test that we can't build a route when no hops are given. // Test that we can't build a route when no hops are given.
hops = []route.Vertex{} hops = []route.Vertex{}
_, err = ctx.router.BuildRoute(nil, hops, nil, 40, nil) _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil)
require.Error(t, err) require.Error(t, err)
// Create hop list for an unknown destination. // Create hop list for an unknown destination.
hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]} hops := []route.Vertex{ctx.aliases["b"], ctx.aliases["y"]}
_, err = ctx.router.BuildRoute(nil, hops, nil, 40, &payAddr) _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
noChanErr := ErrNoChannel{} noChanErr := ErrNoChannel{}
require.ErrorAs(t, err, &noChanErr) require.ErrorAs(t, err, &noChanErr)
require.Equal(t, 1, noChanErr.position) require.Equal(t, 1, noChanErr.position)
@ -1658,7 +1661,7 @@ func TestBuildRoute(t *testing.T) {
amt := lnwire.NewMSatFromSatoshis(100) amt := lnwire.NewMSatFromSatoshis(100)
// Build the route for the given amount. // Build the route for the given amount.
rt, err := ctx.router.BuildRoute(&amt, hops, nil, 40, &payAddr) rt, err := ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr)
require.NoError(t, err) require.NoError(t, err)
// Check that we get the expected route back. The total amount should be // Check that we get the expected route back. The total amount should be
@ -1668,7 +1671,7 @@ func TestBuildRoute(t *testing.T) {
require.Equal(t, lnwire.MilliSatoshi(106000), rt.TotalAmount) require.Equal(t, lnwire.MilliSatoshi(106000), rt.TotalAmount)
// Build the route for the minimum amount. // Build the route for the minimum amount.
rt, err = ctx.router.BuildRoute(nil, hops, nil, 40, &payAddr) rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
require.NoError(t, err) require.NoError(t, err)
// Check that we get the expected route back. The minimum that we can // Check that we get the expected route back. The minimum that we can
@ -1684,7 +1687,7 @@ func TestBuildRoute(t *testing.T) {
// Test a route that contains incompatible channel htlc constraints. // Test a route that contains incompatible channel htlc constraints.
// There is no amount that can pass through both channel 5 and 4. // There is no amount that can pass through both channel 5 and 4.
hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]} hops = []route.Vertex{ctx.aliases["e"], ctx.aliases["c"]}
_, err = ctx.router.BuildRoute(nil, hops, nil, 40, nil) _, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, nil)
require.Error(t, err) require.Error(t, err)
noChanErr = ErrNoChannel{} noChanErr = ErrNoChannel{}
require.ErrorAs(t, err, &noChanErr) require.ErrorAs(t, err, &noChanErr)
@ -1702,7 +1705,7 @@ func TestBuildRoute(t *testing.T) {
// amount that could be delivered to the receiver of 21819 msat, using // amount that could be delivered to the receiver of 21819 msat, using
// policy of channel 3. // policy of channel 3.
hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]} hops = []route.Vertex{ctx.aliases["b"], ctx.aliases["z"]}
rt, err = ctx.router.BuildRoute(nil, hops, nil, 40, &payAddr) rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
require.NoError(t, err) require.NoError(t, err)
checkHops(rt, []uint64{1, 8}, payAddr) checkHops(rt, []uint64{1, 8}, payAddr)
require.Equal(t, lnwire.MilliSatoshi(21200), rt.TotalAmount) require.Equal(t, lnwire.MilliSatoshi(21200), rt.TotalAmount)
@ -1714,7 +1717,7 @@ func TestBuildRoute(t *testing.T) {
// We get 106000 - 1000 (base in) - 0.001 * 106000 (rate in) = 104894. // We get 106000 - 1000 (base in) - 0.001 * 106000 (rate in) = 104894.
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
amt = lnwire.NewMSatFromSatoshis(100) amt = lnwire.NewMSatFromSatoshis(100)
rt, err = ctx.router.BuildRoute(&amt, hops, nil, 40, &payAddr) rt, err = ctx.router.BuildRoute(fn.Some(amt), hops, nil, 40, &payAddr)
require.NoError(t, err) require.NoError(t, err)
checkHops(rt, []uint64{9, 10}, payAddr) checkHops(rt, []uint64{9, 10}, payAddr)
require.EqualValues(t, 104894, rt.TotalAmount) require.EqualValues(t, 104894, rt.TotalAmount)
@ -1728,7 +1731,7 @@ func TestBuildRoute(t *testing.T) {
// of 20179 msat, which results in underpayment of 1 msat in fee. There // of 20179 msat, which results in underpayment of 1 msat in fee. There
// is a third pass through newRoute in which this gets corrected to end // is a third pass through newRoute in which this gets corrected to end
hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]} hops = []route.Vertex{ctx.aliases["d"], ctx.aliases["f"]}
rt, err = ctx.router.BuildRoute(nil, hops, nil, 40, &payAddr) rt, err = ctx.router.BuildRoute(noAmt, hops, nil, 40, &payAddr)
require.NoError(t, err) require.NoError(t, err)
checkHops(rt, []uint64{9, 10}, payAddr) checkHops(rt, []uint64{9, 10}, payAddr)
require.EqualValues(t, 20180, rt.TotalAmount, "%v", rt.TotalAmount) require.EqualValues(t, 20180, rt.TotalAmount, "%v", rt.TotalAmount)
@ -1919,20 +1922,20 @@ func TestSenderAmtBackwardPass(t *testing.T) {
// A search for an amount that is below the minimum HTLC amount should // A search for an amount that is below the minimum HTLC amount should
// fail. // fail.
_, _, err := senderAmtBackwardPass( _, _, err := senderAmtBackwardPass(
edgeUnifiers, false, minHTLC-1, &bandwidthHints, edgeUnifiers, fn.Some(minHTLC-1), &bandwidthHints,
) )
require.Error(t, err) require.Error(t, err)
// Do a min amount search. // Do a min amount search.
unifiedEdges, senderAmount, err := senderAmtBackwardPass( _, senderAmount, err := senderAmtBackwardPass(
edgeUnifiers, true, 1, &bandwidthHints, edgeUnifiers, fn.None[lnwire.MilliSatoshi](), &bandwidthHints,
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, minHTLC+333+222+222+111, senderAmount) require.Equal(t, minHTLC+333+222+222+111, senderAmount)
// Do a search for a specific amount. // Do a search for a specific amount.
unifiedEdges, senderAmount, err = senderAmtBackwardPass( unifiedEdges, senderAmount, err := senderAmtBackwardPass(
edgeUnifiers, false, testReceiverAmt, &bandwidthHints, edgeUnifiers, fn.Some(testReceiverAmt), &bandwidthHints,
) )
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, testReceiverAmt+333+222+222+111, senderAmount) require.Equal(t, testReceiverAmt+333+222+222+111, senderAmount)
@ -1961,7 +1964,7 @@ func TestSenderAmtBackwardPass(t *testing.T) {
} }
unifiedEdges, senderAmount, err = senderAmtBackwardPass( unifiedEdges, senderAmount, err = senderAmtBackwardPass(
edgeUnifiers, false, testReceiverAmt, &bandwidthHints, edgeUnifiers, fn.Some(testReceiverAmt), &bandwidthHints,
) )
require.NoError(t, err) require.NoError(t, err)