Skip to content

Commit

Permalink
Merge pull request #8136 from hieblmi/fee-estimation-probe
Browse files Browse the repository at this point in the history
Probing for more reliable route fee estimation
  • Loading branch information
guggero authored Mar 5, 2024
2 parents f617612 + fba6fda commit 6cef367
Show file tree
Hide file tree
Showing 21 changed files with 1,879 additions and 632 deletions.
92 changes: 91 additions & 1 deletion cmd/lncli/cmd_payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -1799,10 +1799,100 @@ func deletePayments(ctx *cli.Context) error {
return nil
}

var estimateRouteFeeCommand = cli.Command{
Name: "estimateroutefee",
Category: "Payments",
Usage: "Estimate routing fees based on a destination or an invoice.",
Action: actionDecorator(estimateRouteFee),
Flags: []cli.Flag{
cli.StringFlag{
Name: "dest",
Usage: "the 33-byte hex-encoded public key for the " +
"probe destination. If it is specified then " +
"the amt flag is required. If it isn't " +
"specified then the pay_req field has to.",
},
cli.Int64Flag{
Name: "amt",
Usage: "the payment amount expressed in satoshis " +
"that should be probed for. This field is " +
"mandatory if dest is specified.",
},
cli.StringFlag{
Name: "pay_req",
Usage: "a zpay32 encoded payment request which is " +
"used to probe. If the destination is " +
"not public then route hints are scanned for " +
"a public node.",
},
cli.DurationFlag{
Name: "timeout",
Usage: "a deadline for the probe attempt. Only " +
"applicable if pay_req is specified.",
Value: paymentTimeout,
},
},
}

func estimateRouteFee(ctx *cli.Context) error {
ctxc := getContext()
conn := getClientConn(ctx, false)
defer conn.Close()

client := routerrpc.NewRouterClient(conn)

req := &routerrpc.RouteFeeRequest{}

switch {
case ctx.IsSet("dest") && ctx.IsSet("pay_req"):
return fmt.Errorf("either dest or pay_req can be set")

case ctx.IsSet("dest") && !ctx.IsSet("amt"):
return fmt.Errorf("amt is required when dest is set")

case ctx.IsSet("dest"):
dest, err := hex.DecodeString(ctx.String("dest"))
if err != nil {
return err
}

if len(dest) != 33 {
return fmt.Errorf("dest node pubkey must be exactly "+
"33 bytes, is instead: %v", len(dest))
}

amtSat := ctx.Int64("amt")
if amtSat == 0 {
return fmt.Errorf("non-zero amount required")
}

req.Dest = dest
req.AmtSat = amtSat

case ctx.IsSet("pay_req"):
req.PaymentRequest = stripPrefix(ctx.String("pay_req"))
if ctx.IsSet("timeout") {
req.Timeout = uint32(ctx.Duration("timeout").Seconds())
}

default:
return fmt.Errorf("fee estimation arguments missing")
}

resp, err := client.EstimateRouteFee(ctxc, req)
if err != nil {
return err
}

printRespJSON(resp)

return nil
}

// ESC is the ASCII code for escape character.
const ESC = 27

// clearCode defines a terminal escape code to clear the currently line and move
// clearCode defines a terminal escape code to clear the current line and move
// the cursor up.
var clearCode = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC)

Expand Down
1 change: 1 addition & 0 deletions cmd/lncli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ func main() {
subscribeCustomCommand,
fishCompletionCommand,
listAliasesCommand,
estimateRouteFeeCommand,
}

// Add any extra commands determined by build flags.
Expand Down
11 changes: 10 additions & 1 deletion docs/release-notes/release-notes-0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,15 @@
error is defined as a routing error found in one of a MPP's HTLC attempts.
If, however, there's only one HTLC attempt, when it's failed, this payment is
considered failed, thus there's no such thing as temp error for a non-MPP.

* Support for
[MinConf](https://github.com/lightningnetwork/lnd/pull/8097)(minimum number
of confirmations) has been added to the `WalletBalance` RPC call.

* [EstimateRouteFee](https://github.com/lightningnetwork/lnd/pull/8136) extends
the graph based estimation by a payment probe approach which can lead to more
accurate estimates. The new estimation method manually incorporates fees of
destinations that lie hidden behind lightning service providers.

* `PendingChannels` now optionally returns the
[raw hex of the closing tx](https://github.com/lightningnetwork/lnd/pull/8426)
Expand All @@ -250,6 +256,9 @@
* `pendingchannels` now optionally returns the
[raw hex of the closing tx](https://github.com/lightningnetwork/lnd/pull/8426)
in `waiting_close_channels`.

* The [estimateroutefee](https://github.com/lightningnetwork/lnd/pull/8136)
subcommand now gives access to graph based and payment probe fee estimation.

## Code Health

Expand Down Expand Up @@ -346,10 +355,10 @@
* Keagan McClelland
* Marcos Fernandez Perez
* Matt Morehouse
* Ononiwu Maureen Chiamaka
* Slyghtning
* Tee8z
* Turtle
* Ononiwu Maureen Chiamaka
* w3irdrobot
* Yong Yu
* Ziggie
4 changes: 4 additions & 0 deletions itest/list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ var allTestCases = []*lntest.TestCase{
Name: "multi-hop payments",
TestFunc: testMultiHopPayments,
},
{
Name: "estimate route fee",
TestFunc: testEstimateRouteFee,
},
{
Name: "anchors reserved value",
TestFunc: testAnchorReservedValue,
Expand Down
22 changes: 18 additions & 4 deletions itest/lnd_amp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,14 @@ func testSendPaymentAMPInvoiceCase(ht *lntest.HarnessTest,
// Increase Dave's fee to make the test deterministic. Otherwise it
// would be unpredictable whether pathfinding would go through Charlie
// or Dave for the first shard.
expectedPolicy := mts.updateDaveGlobalPolicy()
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: 500_000,
FeeRateMilliMsat: int64(0.001 * 1_000_000),
TimeLockDelta: 40,
MinHtlc: 1000, // default value
MaxHtlcMsat: 133_650_000,
}
mts.dave.UpdateGlobalPolicy(expectedPolicy)

// Make sure Alice has heard it for both Dave's channels.
ht.AssertChannelPolicyUpdate(
Expand Down Expand Up @@ -382,10 +389,17 @@ func testSendPaymentAMP(ht *lntest.HarnessTest) {
mts.openChannels(mppReq)
chanPointAliceDave := mts.channelPoints[1]

// Increase Dave's fee to make the test deterministic. Otherwise it
// Increase Dave's fee to make the test deterministic. Otherwise, it
// would be unpredictable whether pathfinding would go through Charlie
// or Dave for the first shard.
expectedPolicy := mts.updateDaveGlobalPolicy()
expectedPolicy := &lnrpc.RoutingPolicy{
FeeBaseMsat: 500_000,
FeeRateMilliMsat: int64(0.001 * 1_000_000),
TimeLockDelta: 40,
MinHtlc: 1000, // default value
MaxHtlcMsat: 133_650_000,
}
mts.dave.UpdateGlobalPolicy(expectedPolicy)

// Make sure Alice has heard it.
ht.AssertChannelPolicyUpdate(
Expand Down Expand Up @@ -493,7 +507,7 @@ func testSendToRouteAMP(ht *lntest.HarnessTest) {
// Alice -- Carol ---- Bob
// \ /
// \__ Dave ____/
///
//
mppReq := &mppOpenChannelRequest{
// Since the channel Alice-> Carol will have to carry two
// shards, we make it larger.
Expand Down
Loading

0 comments on commit 6cef367

Please sign in to comment.