mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
Merge pull request #5699 from guggero/lncli-deletepayment
lncli: add deletepayments command
This commit is contained in:
commit
f60a1ba0ce
@ -1,91 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/lightningnetwork/lnd/chainreg"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var buildRouteCommand = cli.Command{
|
||||
Name: "buildroute",
|
||||
Category: "Payments",
|
||||
Usage: "Build a route from a list of hop pubkeys.",
|
||||
Action: actionDecorator(buildRoute),
|
||||
Flags: []cli.Flag{
|
||||
cli.Int64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount to send expressed in satoshis. If" +
|
||||
"not set, the minimum routable amount is used",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
Value: chainreg.DefaultBitcoinTimeLockDelta,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hops",
|
||||
Usage: "comma separated hex pubkeys",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "outgoing_chan_id",
|
||||
Usage: "short channel id of the outgoing channel to " +
|
||||
"use for the first hop of the payment",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildRoute(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
if !ctx.IsSet("hops") {
|
||||
return errors.New("hops required")
|
||||
}
|
||||
|
||||
// Build list of hop addresses for the rpc.
|
||||
hops := strings.Split(ctx.String("hops"), ",")
|
||||
rpcHops := make([][]byte, 0, len(hops))
|
||||
for _, k := range hops {
|
||||
pubkey, err := route.NewVertexFromStr(k)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing %v: %v", k, err)
|
||||
}
|
||||
rpcHops = append(rpcHops, pubkey[:])
|
||||
}
|
||||
|
||||
var amtMsat int64
|
||||
hasAmt := ctx.IsSet("amt")
|
||||
if hasAmt {
|
||||
amtMsat = ctx.Int64("amt") * 1000
|
||||
if amtMsat == 0 {
|
||||
return fmt.Errorf("non-zero amount required")
|
||||
}
|
||||
}
|
||||
|
||||
// Call BuildRoute rpc.
|
||||
req := &routerrpc.BuildRouteRequest{
|
||||
AmtMsat: amtMsat,
|
||||
FinalCltvDelta: int32(ctx.Int64("final_cltv_delta")),
|
||||
HopPubkeys: rpcHops,
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
|
||||
}
|
||||
|
||||
route, err := client.BuildRoute(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(route)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var getCfgCommand = cli.Command{
|
||||
Name: "getmccfg",
|
||||
Usage: "Display mission control's config.",
|
||||
Description: `
|
||||
Returns the config currently being used by mission control.
|
||||
`,
|
||||
Action: actionDecorator(getCfg),
|
||||
}
|
||||
|
||||
func getCfg(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
resp, err := client.GetMissionControlConfig(
|
||||
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(resp)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var setCfgCommand = cli.Command{
|
||||
Name: "setmccfg",
|
||||
Usage: "Set mission control's config.",
|
||||
Description: `
|
||||
Update the config values being used by mission control to calculate
|
||||
the probability that payment routes will succeed.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "halflife",
|
||||
Usage: "the amount of time taken to restore a node " +
|
||||
"or channel to 50% probability of success.",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "hopprob",
|
||||
Usage: "the probability of success assigned " +
|
||||
"to hops that we have no information about",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "weight",
|
||||
Usage: "the degree to which mission control should " +
|
||||
"rely on historical results, expressed as " +
|
||||
"value in [0;1]",
|
||||
}, cli.UintFlag{
|
||||
Name: "pmtnr",
|
||||
Usage: "the number of payments mission control " +
|
||||
"should store",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "failrelax",
|
||||
Usage: "the amount of time to wait after a failure " +
|
||||
"before raising failure amount",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(setCfg),
|
||||
}
|
||||
|
||||
func setCfg(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
resp, err := client.GetMissionControlConfig(
|
||||
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var haveValue bool
|
||||
|
||||
if ctx.IsSet("halflife") {
|
||||
haveValue = true
|
||||
resp.Config.HalfLifeSeconds = uint64(ctx.Duration(
|
||||
"halflife",
|
||||
).Seconds())
|
||||
}
|
||||
|
||||
if ctx.IsSet("hopprob") {
|
||||
haveValue = true
|
||||
resp.Config.HopProbability = float32(ctx.Float64("hopprob"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("weight") {
|
||||
haveValue = true
|
||||
resp.Config.Weight = float32(ctx.Float64("weight"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("pmtnr") {
|
||||
haveValue = true
|
||||
resp.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("failrelax") {
|
||||
haveValue = true
|
||||
resp.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
|
||||
"failrelax",
|
||||
).Seconds())
|
||||
}
|
||||
|
||||
if !haveValue {
|
||||
return cli.ShowCommandHelp(ctx, "setmccfg")
|
||||
}
|
||||
|
||||
_, err = client.SetMissionControlConfig(
|
||||
ctxc, &routerrpc.SetMissionControlConfigRequest{
|
||||
Config: resp.Config,
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
236
cmd/lncli/cmd_mission_control.go
Normal file
236
cmd/lncli/cmd_mission_control.go
Normal file
@ -0,0 +1,236 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var getCfgCommand = cli.Command{
|
||||
Name: "getmccfg",
|
||||
Category: "Mission Control",
|
||||
Usage: "Display mission control's config.",
|
||||
Description: `
|
||||
Returns the config currently being used by mission control.
|
||||
`,
|
||||
Action: actionDecorator(getCfg),
|
||||
}
|
||||
|
||||
func getCfg(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
resp, err := client.GetMissionControlConfig(
|
||||
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(resp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var setCfgCommand = cli.Command{
|
||||
Name: "setmccfg",
|
||||
Category: "Mission Control",
|
||||
Usage: "Set mission control's config.",
|
||||
Description: `
|
||||
Update the config values being used by mission control to calculate
|
||||
the probability that payment routes will succeed.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "halflife",
|
||||
Usage: "the amount of time taken to restore a node " +
|
||||
"or channel to 50% probability of success.",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "hopprob",
|
||||
Usage: "the probability of success assigned " +
|
||||
"to hops that we have no information about",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "weight",
|
||||
Usage: "the degree to which mission control should " +
|
||||
"rely on historical results, expressed as " +
|
||||
"value in [0;1]",
|
||||
}, cli.UintFlag{
|
||||
Name: "pmtnr",
|
||||
Usage: "the number of payments mission control " +
|
||||
"should store",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "failrelax",
|
||||
Usage: "the amount of time to wait after a failure " +
|
||||
"before raising failure amount",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(setCfg),
|
||||
}
|
||||
|
||||
func setCfg(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
resp, err := client.GetMissionControlConfig(
|
||||
ctxc, &routerrpc.GetMissionControlConfigRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var haveValue bool
|
||||
|
||||
if ctx.IsSet("halflife") {
|
||||
haveValue = true
|
||||
resp.Config.HalfLifeSeconds = uint64(ctx.Duration(
|
||||
"halflife",
|
||||
).Seconds())
|
||||
}
|
||||
|
||||
if ctx.IsSet("hopprob") {
|
||||
haveValue = true
|
||||
resp.Config.HopProbability = float32(ctx.Float64("hopprob"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("weight") {
|
||||
haveValue = true
|
||||
resp.Config.Weight = float32(ctx.Float64("weight"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("pmtnr") {
|
||||
haveValue = true
|
||||
resp.Config.MaximumPaymentResults = uint32(ctx.Int("pmtnr"))
|
||||
}
|
||||
|
||||
if ctx.IsSet("failrelax") {
|
||||
haveValue = true
|
||||
resp.Config.MinimumFailureRelaxInterval = uint64(ctx.Duration(
|
||||
"failrelax",
|
||||
).Seconds())
|
||||
}
|
||||
|
||||
if !haveValue {
|
||||
return cli.ShowCommandHelp(ctx, "setmccfg")
|
||||
}
|
||||
|
||||
_, err = client.SetMissionControlConfig(
|
||||
ctxc, &routerrpc.SetMissionControlConfigRequest{
|
||||
Config: resp.Config,
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
var queryMissionControlCommand = cli.Command{
|
||||
Name: "querymc",
|
||||
Category: "Mission Control",
|
||||
Usage: "Query the internal mission control state.",
|
||||
Action: actionDecorator(queryMissionControl),
|
||||
}
|
||||
|
||||
func queryMissionControl(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.QueryMissionControlRequest{}
|
||||
snapshot, err := client.QueryMissionControl(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(snapshot)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var queryProbCommand = cli.Command{
|
||||
Name: "queryprob",
|
||||
Category: "Mission Control",
|
||||
Usage: "Estimate a success probability.",
|
||||
ArgsUsage: "from-node to-node amt",
|
||||
Action: actionDecorator(queryProb),
|
||||
}
|
||||
|
||||
func queryProb(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
args := ctx.Args()
|
||||
|
||||
if len(args) != 3 {
|
||||
return cli.ShowCommandHelp(ctx, "queryprob")
|
||||
}
|
||||
|
||||
fromNode, err := route.NewVertexFromStr(args.Get(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid from node key: %v", err)
|
||||
}
|
||||
|
||||
toNode, err := route.NewVertexFromStr(args.Get(1))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid to node key: %v", err)
|
||||
}
|
||||
|
||||
amtSat, err := strconv.ParseUint(args.Get(2), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid amt: %v", err)
|
||||
}
|
||||
|
||||
amtMsat := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(amtSat),
|
||||
)
|
||||
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.QueryProbabilityRequest{
|
||||
FromNode: fromNode[:],
|
||||
ToNode: toNode[:],
|
||||
AmtMsat: int64(amtMsat),
|
||||
}
|
||||
|
||||
response, err := client.QueryProbability(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(response)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var resetMissionControlCommand = cli.Command{
|
||||
Name: "resetmc",
|
||||
Category: "Mission Control",
|
||||
Usage: "Reset internal mission control state.",
|
||||
Action: actionDecorator(resetMissionControl),
|
||||
}
|
||||
|
||||
func resetMissionControl(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.ResetMissionControlRequest{}
|
||||
_, err := client.ResetMissionControl(ctxc, req)
|
||||
return err
|
||||
}
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/jedib0t/go-pretty/table"
|
||||
"github.com/jedib0t/go-pretty/text"
|
||||
"github.com/lightninglabs/protobuf-hex-display/jsonpb"
|
||||
"github.com/lightningnetwork/lnd/chainreg"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
@ -987,6 +988,550 @@ func sendToRouteRequest(ctx *cli.Context, req *routerrpc.SendToRouteRequest) err
|
||||
return nil
|
||||
}
|
||||
|
||||
var queryRoutesCommand = cli.Command{
|
||||
Name: "queryroutes",
|
||||
Category: "Payments",
|
||||
Usage: "Query a route to a destination.",
|
||||
Description: "Queries the channel router for a potential path to the destination that has sufficient flow for the amount including fees",
|
||||
ArgsUsage: "dest amt",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "dest",
|
||||
Usage: "the 33-byte hex-encoded public key for the payment " +
|
||||
"destination",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount to send expressed in satoshis",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "fee_limit",
|
||||
Usage: "maximum fee allowed in satoshis when sending " +
|
||||
"the payment",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "fee_limit_percent",
|
||||
Usage: "percentage of the payment's amount used as the " +
|
||||
"maximum fee allowed when sending the payment",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "(optional) number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "use_mc",
|
||||
Usage: "use mission control probabilities",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "outgoing_chanid",
|
||||
Usage: "(optional) the channel id of the channel " +
|
||||
"that must be taken to the first hop",
|
||||
},
|
||||
cltvLimitFlag,
|
||||
},
|
||||
Action: actionDecorator(queryRoutes),
|
||||
}
|
||||
|
||||
func queryRoutes(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
var (
|
||||
dest string
|
||||
amt int64
|
||||
err error
|
||||
)
|
||||
|
||||
args := ctx.Args()
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("dest"):
|
||||
dest = ctx.String("dest")
|
||||
case args.Present():
|
||||
dest = args.First()
|
||||
args = args.Tail()
|
||||
default:
|
||||
return fmt.Errorf("dest argument missing")
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("amt"):
|
||||
amt = ctx.Int64("amt")
|
||||
case args.Present():
|
||||
amt, err = strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode amt argument: %v", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("amt argument missing")
|
||||
}
|
||||
|
||||
feeLimit, err := retrieveFeeLimitLegacy(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &lnrpc.QueryRoutesRequest{
|
||||
PubKey: dest,
|
||||
Amt: amt,
|
||||
FeeLimit: feeLimit,
|
||||
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
|
||||
UseMissionControl: ctx.Bool("use_mc"),
|
||||
CltvLimit: uint32(ctx.Uint64(cltvLimitFlag.Name)),
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chanid"),
|
||||
}
|
||||
|
||||
route, err := client.QueryRoutes(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(route)
|
||||
return nil
|
||||
}
|
||||
|
||||
// retrieveFeeLimitLegacy retrieves the fee limit based on the different fee
|
||||
// limit flags passed. This function will eventually disappear in favor of
|
||||
// retrieveFeeLimit and the new payment rpc.
|
||||
func retrieveFeeLimitLegacy(ctx *cli.Context) (*lnrpc.FeeLimit, error) {
|
||||
switch {
|
||||
case ctx.IsSet("fee_limit") && ctx.IsSet("fee_limit_percent"):
|
||||
return nil, fmt.Errorf("either fee_limit or fee_limit_percent " +
|
||||
"can be set, but not both")
|
||||
case ctx.IsSet("fee_limit"):
|
||||
return &lnrpc.FeeLimit{
|
||||
Limit: &lnrpc.FeeLimit_Fixed{
|
||||
Fixed: ctx.Int64("fee_limit"),
|
||||
},
|
||||
}, nil
|
||||
case ctx.IsSet("fee_limit_percent"):
|
||||
feeLimitPercent := ctx.Int64("fee_limit_percent")
|
||||
if feeLimitPercent < 0 {
|
||||
return nil, errors.New("negative fee limit percentage " +
|
||||
"provided")
|
||||
}
|
||||
return &lnrpc.FeeLimit{
|
||||
Limit: &lnrpc.FeeLimit_Percent{
|
||||
Percent: feeLimitPercent,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Since the fee limit flags aren't required, we don't return an error
|
||||
// if they're not set.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var listPaymentsCommand = cli.Command{
|
||||
Name: "listpayments",
|
||||
Category: "Payments",
|
||||
Usage: "List all outgoing payments.",
|
||||
Description: "This command enables the retrieval of payments stored " +
|
||||
"in the database. Pagination is supported by the usage of " +
|
||||
"index_offset in combination with the paginate_forwards flag. " +
|
||||
"Reversed pagination is enabled by default to receive " +
|
||||
"current payments first. Pagination can be resumed by using " +
|
||||
"the returned last_index_offset (for forwards order), or " +
|
||||
"first_index_offset (for reversed order) as the offset_index. ",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "include_incomplete",
|
||||
Usage: "if set to true, payments still in flight (or " +
|
||||
"failed) will be returned as well, keeping" +
|
||||
"indices for payments the same as without " +
|
||||
"the flag",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "index_offset",
|
||||
Usage: "The index of a payment that will be used as " +
|
||||
"either the start (in forwards mode) or end " +
|
||||
"(in reverse mode) of a query to determine " +
|
||||
"which payments should be returned in the " +
|
||||
"response, where the index_offset is " +
|
||||
"excluded. If index_offset is set to zero in " +
|
||||
"reversed mode, the query will end with the " +
|
||||
"last payment made.",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "max_payments",
|
||||
Usage: "the max number of payments to return, by " +
|
||||
"default, all completed payments are returned",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "paginate_forwards",
|
||||
Usage: "if set, payments succeeding the " +
|
||||
"index_offset will be returned, allowing " +
|
||||
"forwards pagination",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(listPayments),
|
||||
}
|
||||
|
||||
func listPayments(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
req := &lnrpc.ListPaymentsRequest{
|
||||
IncludeIncomplete: ctx.Bool("include_incomplete"),
|
||||
IndexOffset: uint64(ctx.Uint("index_offset")),
|
||||
MaxPayments: uint64(ctx.Uint("max_payments")),
|
||||
Reversed: !ctx.Bool("paginate_forwards"),
|
||||
}
|
||||
|
||||
payments, err := client.ListPayments(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(payments)
|
||||
return nil
|
||||
}
|
||||
|
||||
var forwardingHistoryCommand = cli.Command{
|
||||
Name: "fwdinghistory",
|
||||
Category: "Payments",
|
||||
Usage: "Query the history of all forwarded HTLCs.",
|
||||
ArgsUsage: "start_time [end_time] [index_offset] [max_events]",
|
||||
Description: `
|
||||
Query the HTLC switch's internal forwarding log for all completed
|
||||
payment circuits (HTLCs) over a particular time range (--start_time and
|
||||
--end_time). The start and end times are meant to be expressed in
|
||||
seconds since the Unix epoch.
|
||||
Alternatively negative time ranges can be used, e.g. "-3d". Supports
|
||||
s(seconds), m(minutes), h(ours), d(ays), w(eeks), M(onths), y(ears).
|
||||
Month equals 30.44 days, year equals 365.25 days.
|
||||
If --start_time isn't provided, then 24 hours ago is used. If
|
||||
--end_time isn't provided, then the current time is used.
|
||||
|
||||
The max number of events returned is 50k. The default number is 100,
|
||||
callers can use the --max_events param to modify this value.
|
||||
|
||||
Finally, callers can skip a series of events using the --index_offset
|
||||
parameter. Each response will contain the offset index of the last
|
||||
entry. Using this callers can manually paginate within a time slice.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "start_time",
|
||||
Usage: "the starting time for the query " +
|
||||
`as unix timestamp or relative e.g. "-1w"`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "end_time",
|
||||
Usage: "the end time for the query " +
|
||||
`as unix timestamp or relative e.g. "-1w"`,
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "index_offset",
|
||||
Usage: "the number of events to skip",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "max_events",
|
||||
Usage: "the max number of events to return",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(forwardingHistory),
|
||||
}
|
||||
|
||||
func forwardingHistory(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
var (
|
||||
startTime, endTime uint64
|
||||
indexOffset, maxEvents uint32
|
||||
err error
|
||||
)
|
||||
args := ctx.Args()
|
||||
now := time.Now()
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("start_time"):
|
||||
startTime, err = parseTime(ctx.String("start_time"), now)
|
||||
case args.Present():
|
||||
startTime, err = parseTime(args.First(), now)
|
||||
args = args.Tail()
|
||||
default:
|
||||
now := time.Now()
|
||||
startTime = uint64(now.Add(-time.Hour * 24).Unix())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode start_time: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("end_time"):
|
||||
endTime, err = parseTime(ctx.String("end_time"), now)
|
||||
case args.Present():
|
||||
endTime, err = parseTime(args.First(), now)
|
||||
args = args.Tail()
|
||||
default:
|
||||
endTime = uint64(now.Unix())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode end_time: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("index_offset"):
|
||||
indexOffset = uint32(ctx.Int64("index_offset"))
|
||||
case args.Present():
|
||||
i, err := strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode index_offset: %v", err)
|
||||
}
|
||||
indexOffset = uint32(i)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("max_events"):
|
||||
maxEvents = uint32(ctx.Int64("max_events"))
|
||||
case args.Present():
|
||||
m, err := strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode max_events: %v", err)
|
||||
}
|
||||
maxEvents = uint32(m)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
req := &lnrpc.ForwardingHistoryRequest{
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
IndexOffset: indexOffset,
|
||||
NumMaxEvents: maxEvents,
|
||||
}
|
||||
resp, err := client.ForwardingHistory(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
var buildRouteCommand = cli.Command{
|
||||
Name: "buildroute",
|
||||
Category: "Payments",
|
||||
Usage: "Build a route from a list of hop pubkeys.",
|
||||
Action: actionDecorator(buildRoute),
|
||||
Flags: []cli.Flag{
|
||||
cli.Int64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount to send expressed in satoshis. If" +
|
||||
"not set, the minimum routable amount is used",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
Value: chainreg.DefaultBitcoinTimeLockDelta,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hops",
|
||||
Usage: "comma separated hex pubkeys",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "outgoing_chan_id",
|
||||
Usage: "short channel id of the outgoing channel to " +
|
||||
"use for the first hop of the payment",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func buildRoute(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
if !ctx.IsSet("hops") {
|
||||
return errors.New("hops required")
|
||||
}
|
||||
|
||||
// Build list of hop addresses for the rpc.
|
||||
hops := strings.Split(ctx.String("hops"), ",")
|
||||
rpcHops := make([][]byte, 0, len(hops))
|
||||
for _, k := range hops {
|
||||
pubkey, err := route.NewVertexFromStr(k)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing %v: %v", k, err)
|
||||
}
|
||||
rpcHops = append(rpcHops, pubkey[:])
|
||||
}
|
||||
|
||||
var amtMsat int64
|
||||
hasAmt := ctx.IsSet("amt")
|
||||
if hasAmt {
|
||||
amtMsat = ctx.Int64("amt") * 1000
|
||||
if amtMsat == 0 {
|
||||
return fmt.Errorf("non-zero amount required")
|
||||
}
|
||||
}
|
||||
|
||||
// Call BuildRoute rpc.
|
||||
req := &routerrpc.BuildRouteRequest{
|
||||
AmtMsat: amtMsat,
|
||||
FinalCltvDelta: int32(ctx.Int64("final_cltv_delta")),
|
||||
HopPubkeys: rpcHops,
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chan_id"),
|
||||
}
|
||||
|
||||
route, err := client.BuildRoute(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(route)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var deletePaymentsCommand = cli.Command{
|
||||
Name: "deletepayments",
|
||||
Category: "Payments",
|
||||
Usage: "Delete a single or multiple payments from the database.",
|
||||
ArgsUsage: "--all [--failed_htlcs_only --include_non_failed] | " +
|
||||
"--payment_hash hash [--failed_htlcs_only]",
|
||||
Description: `
|
||||
This command either deletes all failed payments or a single payment from
|
||||
the database to reclaim disk space.
|
||||
|
||||
If the --all flag is used, then all failed payments are removed. If so
|
||||
desired, _ALL_ payments (even the successful ones) can be deleted
|
||||
by additionally specifying --include_non_failed.
|
||||
|
||||
If a --payment_hash is specified, that single payment is deleted,
|
||||
independent of its state.
|
||||
|
||||
If --failed_htlcs_only is specified then the payments themselves (or the
|
||||
single payment itself if used with --payment_hash) is not deleted, only
|
||||
the information about any failed HTLC attempts during the payment.
|
||||
|
||||
NOTE: Removing payments from the database does free up disk space within
|
||||
the internal bbolt database. But that disk space is only reclaimed after
|
||||
compacting the database. Users might want to turn on auto compaction
|
||||
(db.bolt.auto-compact=true in the config file or --db.bolt.auto-compact
|
||||
as a command line flag) and restart lnd after deleting a large number of
|
||||
payments to see a reduction in the file size of the channel.db file.
|
||||
`,
|
||||
Action: actionDecorator(deletePayments),
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "all",
|
||||
Usage: "delete all failed payments",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "payment_hash",
|
||||
Usage: "delete a specific payment identified by its " +
|
||||
"payment hash",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "failed_htlcs_only",
|
||||
Usage: "only delete failed HTLCs from payments, not " +
|
||||
"the payment itself",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "include_non_failed",
|
||||
Usage: "delete ALL payments, not just the failed ones",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func deletePayments(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
// Show command help if arguments or no flags are provided.
|
||||
if ctx.NArg() > 0 || ctx.NumFlags() == 0 {
|
||||
_ = cli.ShowCommandHelp(ctx, "deletepayments")
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
paymentHash []byte
|
||||
all = ctx.Bool("all")
|
||||
singlePayment = ctx.IsSet("payment_hash")
|
||||
failedHTLCsOnly = ctx.Bool("failed_htlcs_only")
|
||||
includeNonFailed = ctx.Bool("include_non_failed")
|
||||
err error
|
||||
okMsg = struct {
|
||||
OK bool `json:"ok"`
|
||||
}{
|
||||
OK: true,
|
||||
}
|
||||
)
|
||||
|
||||
// We pack two RPCs into the same CLI so there are a few non-valid
|
||||
// combinations of the flags we need to filter out.
|
||||
switch {
|
||||
case all && singlePayment:
|
||||
return fmt.Errorf("cannot use --all and --payment_hash at " +
|
||||
"the same time")
|
||||
|
||||
case singlePayment && includeNonFailed:
|
||||
return fmt.Errorf("cannot use --payment_hash and " +
|
||||
"--include_non_failed at the same time, when using " +
|
||||
"a payment hash the payment is deleted independent " +
|
||||
"of its state")
|
||||
}
|
||||
|
||||
// Deleting a single payment is implemented in a different RPC than
|
||||
// removing all/multiple payments.
|
||||
switch {
|
||||
case singlePayment:
|
||||
paymentHash, err = hex.DecodeString(ctx.String("payment_hash"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error decoding payment_hash: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
_, err = client.DeletePayment(ctxc, &lnrpc.DeletePaymentRequest{
|
||||
PaymentHash: paymentHash,
|
||||
FailedHtlcsOnly: failedHTLCsOnly,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting single payment: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
case all:
|
||||
what := "failed"
|
||||
if includeNonFailed {
|
||||
what = "all"
|
||||
}
|
||||
if failedHTLCsOnly {
|
||||
what = fmt.Sprintf("failed HTLCs from %s", what)
|
||||
}
|
||||
|
||||
fmt.Printf("Removing %s payments, this might take a while...\n",
|
||||
what)
|
||||
_, err = client.DeleteAllPayments(
|
||||
ctxc, &lnrpc.DeleteAllPaymentsRequest{
|
||||
FailedPaymentsOnly: !includeNonFailed,
|
||||
FailedHtlcsOnly: failedHTLCsOnly,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting payments: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Users are confused by empty JSON outputs so let's return a simple OK
|
||||
// instead of just printing the empty response RPC message.
|
||||
printJSON(okMsg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ESC is the ASCII code for escape character
|
||||
const ESC = 27
|
||||
|
@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var queryMissionControlCommand = cli.Command{
|
||||
Name: "querymc",
|
||||
Category: "Payments",
|
||||
Usage: "Query the internal mission control state.",
|
||||
Action: actionDecorator(queryMissionControl),
|
||||
}
|
||||
|
||||
func queryMissionControl(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.QueryMissionControlRequest{}
|
||||
snapshot, err := client.QueryMissionControl(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(snapshot)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var queryProbCommand = cli.Command{
|
||||
Name: "queryprob",
|
||||
Category: "Payments",
|
||||
Usage: "Estimate a success probability.",
|
||||
ArgsUsage: "from-node to-node amt",
|
||||
Action: actionDecorator(queryProb),
|
||||
}
|
||||
|
||||
func queryProb(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
args := ctx.Args()
|
||||
|
||||
if len(args) != 3 {
|
||||
return cli.ShowCommandHelp(ctx, "queryprob")
|
||||
}
|
||||
|
||||
fromNode, err := route.NewVertexFromStr(args.Get(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid from node key: %v", err)
|
||||
}
|
||||
|
||||
toNode, err := route.NewVertexFromStr(args.Get(1))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid to node key: %v", err)
|
||||
}
|
||||
|
||||
amtSat, err := strconv.ParseUint(args.Get(2), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid amt: %v", err)
|
||||
}
|
||||
|
||||
amtMsat := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(amtSat),
|
||||
)
|
||||
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.QueryProbabilityRequest{
|
||||
FromNode: fromNode[:],
|
||||
ToNode: toNode[:],
|
||||
AmtMsat: int64(amtMsat),
|
||||
}
|
||||
|
||||
response, err := client.QueryProbability(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(response)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var resetMissionControlCommand = cli.Command{
|
||||
Name: "resetmc",
|
||||
Category: "Payments",
|
||||
Usage: "Reset internal mission control state.",
|
||||
Action: actionDecorator(resetMissionControl),
|
||||
}
|
||||
|
||||
func resetMissionControl(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
conn := getClientConn(ctx, false)
|
||||
defer conn.Close()
|
||||
|
||||
client := routerrpc.NewRouterClient(conn)
|
||||
|
||||
req := &routerrpc.ResetMissionControlRequest{}
|
||||
_, err := client.ResetMissionControl(ctxc, req)
|
||||
return err
|
||||
}
|
@ -14,7 +14,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
@ -1550,72 +1549,6 @@ func getNodeMetrics(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var listPaymentsCommand = cli.Command{
|
||||
Name: "listpayments",
|
||||
Category: "Payments",
|
||||
Usage: "List all outgoing payments.",
|
||||
Description: "This command enables the retrieval of payments stored " +
|
||||
"in the database. Pagination is supported by the usage of " +
|
||||
"index_offset in combination with the paginate_forwards flag. " +
|
||||
"Reversed pagination is enabled by default to receive " +
|
||||
"current payments first. Pagination can be resumed by using " +
|
||||
"the returned last_index_offset (for forwards order), or " +
|
||||
"first_index_offset (for reversed order) as the offset_index. ",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "include_incomplete",
|
||||
Usage: "if set to true, payments still in flight (or " +
|
||||
"failed) will be returned as well, keeping" +
|
||||
"indices for payments the same as without " +
|
||||
"the flag",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "index_offset",
|
||||
Usage: "The index of a payment that will be used as " +
|
||||
"either the start (in forwards mode) or end " +
|
||||
"(in reverse mode) of a query to determine " +
|
||||
"which payments should be returned in the " +
|
||||
"response, where the index_offset is " +
|
||||
"excluded. If index_offset is set to zero in " +
|
||||
"reversed mode, the query will end with the " +
|
||||
"last payment made.",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "max_payments",
|
||||
Usage: "the max number of payments to return, by " +
|
||||
"default, all completed payments are returned",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "paginate_forwards",
|
||||
Usage: "if set, payments succeeding the " +
|
||||
"index_offset will be returned, allowing " +
|
||||
"forwards pagination",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(listPayments),
|
||||
}
|
||||
|
||||
func listPayments(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
req := &lnrpc.ListPaymentsRequest{
|
||||
IncludeIncomplete: ctx.Bool("include_incomplete"),
|
||||
IndexOffset: uint64(ctx.Uint("index_offset")),
|
||||
MaxPayments: uint64(ctx.Uint("max_payments")),
|
||||
Reversed: !ctx.Bool("paginate_forwards"),
|
||||
}
|
||||
|
||||
payments, err := client.ListPayments(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(payments)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getChanInfoCommand = cli.Command{
|
||||
Name: "getchaninfo",
|
||||
Category: "Graph",
|
||||
@ -1719,142 +1652,6 @@ func getNodeInfo(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var queryRoutesCommand = cli.Command{
|
||||
Name: "queryroutes",
|
||||
Category: "Payments",
|
||||
Usage: "Query a route to a destination.",
|
||||
Description: "Queries the channel router for a potential path to the destination that has sufficient flow for the amount including fees",
|
||||
ArgsUsage: "dest amt",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "dest",
|
||||
Usage: "the 33-byte hex-encoded public key for the payment " +
|
||||
"destination",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "amt",
|
||||
Usage: "the amount to send expressed in satoshis",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "fee_limit",
|
||||
Usage: "maximum fee allowed in satoshis when sending " +
|
||||
"the payment",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "fee_limit_percent",
|
||||
Usage: "percentage of the payment's amount used as the " +
|
||||
"maximum fee allowed when sending the payment",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "final_cltv_delta",
|
||||
Usage: "(optional) number of blocks the last hop has to reveal " +
|
||||
"the preimage",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "use_mc",
|
||||
Usage: "use mission control probabilities",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "outgoing_chanid",
|
||||
Usage: "(optional) the channel id of the channel " +
|
||||
"that must be taken to the first hop",
|
||||
},
|
||||
cltvLimitFlag,
|
||||
},
|
||||
Action: actionDecorator(queryRoutes),
|
||||
}
|
||||
|
||||
func queryRoutes(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
var (
|
||||
dest string
|
||||
amt int64
|
||||
err error
|
||||
)
|
||||
|
||||
args := ctx.Args()
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("dest"):
|
||||
dest = ctx.String("dest")
|
||||
case args.Present():
|
||||
dest = args.First()
|
||||
args = args.Tail()
|
||||
default:
|
||||
return fmt.Errorf("dest argument missing")
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("amt"):
|
||||
amt = ctx.Int64("amt")
|
||||
case args.Present():
|
||||
amt, err = strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode amt argument: %v", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("amt argument missing")
|
||||
}
|
||||
|
||||
feeLimit, err := retrieveFeeLimitLegacy(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := &lnrpc.QueryRoutesRequest{
|
||||
PubKey: dest,
|
||||
Amt: amt,
|
||||
FeeLimit: feeLimit,
|
||||
FinalCltvDelta: int32(ctx.Int("final_cltv_delta")),
|
||||
UseMissionControl: ctx.Bool("use_mc"),
|
||||
CltvLimit: uint32(ctx.Uint64(cltvLimitFlag.Name)),
|
||||
OutgoingChanId: ctx.Uint64("outgoing_chanid"),
|
||||
}
|
||||
|
||||
route, err := client.QueryRoutes(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(route)
|
||||
return nil
|
||||
}
|
||||
|
||||
// retrieveFeeLimitLegacy retrieves the fee limit based on the different fee
|
||||
// limit flags passed. This function will eventually disappear in favor of
|
||||
// retrieveFeeLimit and the new payment rpc.
|
||||
func retrieveFeeLimitLegacy(ctx *cli.Context) (*lnrpc.FeeLimit, error) {
|
||||
switch {
|
||||
case ctx.IsSet("fee_limit") && ctx.IsSet("fee_limit_percent"):
|
||||
return nil, fmt.Errorf("either fee_limit or fee_limit_percent " +
|
||||
"can be set, but not both")
|
||||
case ctx.IsSet("fee_limit"):
|
||||
return &lnrpc.FeeLimit{
|
||||
Limit: &lnrpc.FeeLimit_Fixed{
|
||||
Fixed: ctx.Int64("fee_limit"),
|
||||
},
|
||||
}, nil
|
||||
case ctx.IsSet("fee_limit_percent"):
|
||||
feeLimitPercent := ctx.Int64("fee_limit_percent")
|
||||
if feeLimitPercent < 0 {
|
||||
return nil, errors.New("negative fee limit percentage " +
|
||||
"provided")
|
||||
}
|
||||
return &lnrpc.FeeLimit{
|
||||
Limit: &lnrpc.FeeLimit_Percent{
|
||||
Percent: feeLimitPercent,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Since the fee limit flags aren't required, we don't return an error
|
||||
// if they're not set.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var getNetworkInfoCommand = cli.Command{
|
||||
Name: "getnetworkinfo",
|
||||
Category: "Channels",
|
||||
@ -2330,131 +2127,6 @@ func updateChannelPolicy(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var forwardingHistoryCommand = cli.Command{
|
||||
Name: "fwdinghistory",
|
||||
Category: "Payments",
|
||||
Usage: "Query the history of all forwarded HTLCs.",
|
||||
ArgsUsage: "start_time [end_time] [index_offset] [max_events]",
|
||||
Description: `
|
||||
Query the HTLC switch's internal forwarding log for all completed
|
||||
payment circuits (HTLCs) over a particular time range (--start_time and
|
||||
--end_time). The start and end times are meant to be expressed in
|
||||
seconds since the Unix epoch.
|
||||
Alternatively negative time ranges can be used, e.g. "-3d". Supports
|
||||
s(seconds), m(minutes), h(ours), d(ays), w(eeks), M(onths), y(ears).
|
||||
Month equals 30.44 days, year equals 365.25 days.
|
||||
If --start_time isn't provided, then 24 hours ago is used. If
|
||||
--end_time isn't provided, then the current time is used.
|
||||
|
||||
The max number of events returned is 50k. The default number is 100,
|
||||
callers can use the --max_events param to modify this value.
|
||||
|
||||
Finally, callers can skip a series of events using the --index_offset
|
||||
parameter. Each response will contain the offset index of the last
|
||||
entry. Using this callers can manually paginate within a time slice.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "start_time",
|
||||
Usage: "the starting time for the query " +
|
||||
`as unix timestamp or relative e.g. "-1w"`,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "end_time",
|
||||
Usage: "the end time for the query " +
|
||||
`as unix timestamp or relative e.g. "-1w"`,
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "index_offset",
|
||||
Usage: "the number of events to skip",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "max_events",
|
||||
Usage: "the max number of events to return",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(forwardingHistory),
|
||||
}
|
||||
|
||||
func forwardingHistory(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
var (
|
||||
startTime, endTime uint64
|
||||
indexOffset, maxEvents uint32
|
||||
err error
|
||||
)
|
||||
args := ctx.Args()
|
||||
now := time.Now()
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("start_time"):
|
||||
startTime, err = parseTime(ctx.String("start_time"), now)
|
||||
case args.Present():
|
||||
startTime, err = parseTime(args.First(), now)
|
||||
args = args.Tail()
|
||||
default:
|
||||
now := time.Now()
|
||||
startTime = uint64(now.Add(-time.Hour * 24).Unix())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode start_time: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("end_time"):
|
||||
endTime, err = parseTime(ctx.String("end_time"), now)
|
||||
case args.Present():
|
||||
endTime, err = parseTime(args.First(), now)
|
||||
args = args.Tail()
|
||||
default:
|
||||
endTime = uint64(now.Unix())
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode end_time: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("index_offset"):
|
||||
indexOffset = uint32(ctx.Int64("index_offset"))
|
||||
case args.Present():
|
||||
i, err := strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode index_offset: %v", err)
|
||||
}
|
||||
indexOffset = uint32(i)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("max_events"):
|
||||
maxEvents = uint32(ctx.Int64("max_events"))
|
||||
case args.Present():
|
||||
m, err := strconv.ParseInt(args.First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode max_events: %v", err)
|
||||
}
|
||||
maxEvents = uint32(m)
|
||||
args = args.Tail()
|
||||
}
|
||||
|
||||
req := &lnrpc.ForwardingHistoryRequest{
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
IndexOffset: indexOffset,
|
||||
NumMaxEvents: maxEvents,
|
||||
}
|
||||
resp, err := client.ForwardingHistory(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
var exportChanBackupCommand = cli.Command{
|
||||
Name: "exportchanbackup",
|
||||
Category: "Channels",
|
||||
|
@ -383,6 +383,7 @@ func main() {
|
||||
versionCommand,
|
||||
profileSubCommand,
|
||||
getStateCommand,
|
||||
deletePaymentsCommand,
|
||||
}
|
||||
|
||||
// Add any extra commands determined by build flags.
|
||||
|
@ -211,6 +211,11 @@ you.
|
||||
with the `addInvoice` rpc interface. However, now the function has been
|
||||
[exposed in the go package `invoicesrpc`](https://github.com/lightningnetwork/lnd/pull/5697).
|
||||
|
||||
* The `DeleteAllPayments` and `DeletePayment` RPC methods can now be called from
|
||||
the command line with the [new
|
||||
`lncli deletepayments`](https://github.com/lightningnetwork/lnd/pull/5699)
|
||||
command.
|
||||
|
||||
## Code Health
|
||||
|
||||
### Code cleanup, refactor, typo fixes
|
||||
|
Loading…
Reference in New Issue
Block a user