Skip to content

Commit 6cef367

Browse files
authored
Merge pull request #8136 from hieblmi/fee-estimation-probe
Probing for more reliable route fee estimation
2 parents f617612 + fba6fda commit 6cef367

21 files changed

+1879
-632
lines changed

cmd/lncli/cmd_payments.go

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1799,10 +1799,100 @@ func deletePayments(ctx *cli.Context) error {
17991799
return nil
18001800
}
18011801

1802+
var estimateRouteFeeCommand = cli.Command{
1803+
Name: "estimateroutefee",
1804+
Category: "Payments",
1805+
Usage: "Estimate routing fees based on a destination or an invoice.",
1806+
Action: actionDecorator(estimateRouteFee),
1807+
Flags: []cli.Flag{
1808+
cli.StringFlag{
1809+
Name: "dest",
1810+
Usage: "the 33-byte hex-encoded public key for the " +
1811+
"probe destination. If it is specified then " +
1812+
"the amt flag is required. If it isn't " +
1813+
"specified then the pay_req field has to.",
1814+
},
1815+
cli.Int64Flag{
1816+
Name: "amt",
1817+
Usage: "the payment amount expressed in satoshis " +
1818+
"that should be probed for. This field is " +
1819+
"mandatory if dest is specified.",
1820+
},
1821+
cli.StringFlag{
1822+
Name: "pay_req",
1823+
Usage: "a zpay32 encoded payment request which is " +
1824+
"used to probe. If the destination is " +
1825+
"not public then route hints are scanned for " +
1826+
"a public node.",
1827+
},
1828+
cli.DurationFlag{
1829+
Name: "timeout",
1830+
Usage: "a deadline for the probe attempt. Only " +
1831+
"applicable if pay_req is specified.",
1832+
Value: paymentTimeout,
1833+
},
1834+
},
1835+
}
1836+
1837+
func estimateRouteFee(ctx *cli.Context) error {
1838+
ctxc := getContext()
1839+
conn := getClientConn(ctx, false)
1840+
defer conn.Close()
1841+
1842+
client := routerrpc.NewRouterClient(conn)
1843+
1844+
req := &routerrpc.RouteFeeRequest{}
1845+
1846+
switch {
1847+
case ctx.IsSet("dest") && ctx.IsSet("pay_req"):
1848+
return fmt.Errorf("either dest or pay_req can be set")
1849+
1850+
case ctx.IsSet("dest") && !ctx.IsSet("amt"):
1851+
return fmt.Errorf("amt is required when dest is set")
1852+
1853+
case ctx.IsSet("dest"):
1854+
dest, err := hex.DecodeString(ctx.String("dest"))
1855+
if err != nil {
1856+
return err
1857+
}
1858+
1859+
if len(dest) != 33 {
1860+
return fmt.Errorf("dest node pubkey must be exactly "+
1861+
"33 bytes, is instead: %v", len(dest))
1862+
}
1863+
1864+
amtSat := ctx.Int64("amt")
1865+
if amtSat == 0 {
1866+
return fmt.Errorf("non-zero amount required")
1867+
}
1868+
1869+
req.Dest = dest
1870+
req.AmtSat = amtSat
1871+
1872+
case ctx.IsSet("pay_req"):
1873+
req.PaymentRequest = stripPrefix(ctx.String("pay_req"))
1874+
if ctx.IsSet("timeout") {
1875+
req.Timeout = uint32(ctx.Duration("timeout").Seconds())
1876+
}
1877+
1878+
default:
1879+
return fmt.Errorf("fee estimation arguments missing")
1880+
}
1881+
1882+
resp, err := client.EstimateRouteFee(ctxc, req)
1883+
if err != nil {
1884+
return err
1885+
}
1886+
1887+
printRespJSON(resp)
1888+
1889+
return nil
1890+
}
1891+
18021892
// ESC is the ASCII code for escape character.
18031893
const ESC = 27
18041894

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

cmd/lncli/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ func main() {
507507
subscribeCustomCommand,
508508
fishCompletionCommand,
509509
listAliasesCommand,
510+
estimateRouteFeeCommand,
510511
}
511512

512513
// Add any extra commands determined by build flags.

docs/release-notes/release-notes-0.18.0.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,15 @@
221221
error is defined as a routing error found in one of a MPP's HTLC attempts.
222222
If, however, there's only one HTLC attempt, when it's failed, this payment is
223223
considered failed, thus there's no such thing as temp error for a non-MPP.
224+
224225
* Support for
225226
[MinConf](https://github.com/lightningnetwork/lnd/pull/8097)(minimum number
226227
of confirmations) has been added to the `WalletBalance` RPC call.
228+
229+
* [EstimateRouteFee](https://github.com/lightningnetwork/lnd/pull/8136) extends
230+
the graph based estimation by a payment probe approach which can lead to more
231+
accurate estimates. The new estimation method manually incorporates fees of
232+
destinations that lie hidden behind lightning service providers.
227233

228234
* `PendingChannels` now optionally returns the
229235
[raw hex of the closing tx](https://github.com/lightningnetwork/lnd/pull/8426)
@@ -250,6 +256,9 @@
250256
* `pendingchannels` now optionally returns the
251257
[raw hex of the closing tx](https://github.com/lightningnetwork/lnd/pull/8426)
252258
in `waiting_close_channels`.
259+
260+
* The [estimateroutefee](https://github.com/lightningnetwork/lnd/pull/8136)
261+
subcommand now gives access to graph based and payment probe fee estimation.
253262

254263
## Code Health
255264

@@ -346,10 +355,10 @@
346355
* Keagan McClelland
347356
* Marcos Fernandez Perez
348357
* Matt Morehouse
358+
* Ononiwu Maureen Chiamaka
349359
* Slyghtning
350360
* Tee8z
351361
* Turtle
352-
* Ononiwu Maureen Chiamaka
353362
* w3irdrobot
354363
* Yong Yu
355364
* Ziggie

itest/list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ var allTestCases = []*lntest.TestCase{
257257
Name: "multi-hop payments",
258258
TestFunc: testMultiHopPayments,
259259
},
260+
{
261+
Name: "estimate route fee",
262+
TestFunc: testEstimateRouteFee,
263+
},
260264
{
261265
Name: "anchors reserved value",
262266
TestFunc: testAnchorReservedValue,

itest/lnd_amp_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,14 @@ func testSendPaymentAMPInvoiceCase(ht *lntest.HarnessTest,
8989
// Increase Dave's fee to make the test deterministic. Otherwise it
9090
// would be unpredictable whether pathfinding would go through Charlie
9191
// or Dave for the first shard.
92-
expectedPolicy := mts.updateDaveGlobalPolicy()
92+
expectedPolicy := &lnrpc.RoutingPolicy{
93+
FeeBaseMsat: 500_000,
94+
FeeRateMilliMsat: int64(0.001 * 1_000_000),
95+
TimeLockDelta: 40,
96+
MinHtlc: 1000, // default value
97+
MaxHtlcMsat: 133_650_000,
98+
}
99+
mts.dave.UpdateGlobalPolicy(expectedPolicy)
93100

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

385-
// Increase Dave's fee to make the test deterministic. Otherwise it
392+
// Increase Dave's fee to make the test deterministic. Otherwise, it
386393
// would be unpredictable whether pathfinding would go through Charlie
387394
// or Dave for the first shard.
388-
expectedPolicy := mts.updateDaveGlobalPolicy()
395+
expectedPolicy := &lnrpc.RoutingPolicy{
396+
FeeBaseMsat: 500_000,
397+
FeeRateMilliMsat: int64(0.001 * 1_000_000),
398+
TimeLockDelta: 40,
399+
MinHtlc: 1000, // default value
400+
MaxHtlcMsat: 133_650_000,
401+
}
402+
mts.dave.UpdateGlobalPolicy(expectedPolicy)
389403

390404
// Make sure Alice has heard it.
391405
ht.AssertChannelPolicyUpdate(
@@ -493,7 +507,7 @@ func testSendToRouteAMP(ht *lntest.HarnessTest) {
493507
// Alice -- Carol ---- Bob
494508
// \ /
495509
// \__ Dave ____/
496-
///
510+
//
497511
mppReq := &mppOpenChannelRequest{
498512
// Since the channel Alice-> Carol will have to carry two
499513
// shards, we make it larger.

0 commit comments

Comments
 (0)