mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-30 07:35:07 +02:00
Merge pull request #7575 from lightningnetwork/0-16-1-staging
lnd: merge the 0.16.1 staging branch into master
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/lightninglabs/protobuf-hex-display/jsonpb"
|
||||
"github.com/lightninglabs/protobuf-hex-display/proto"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/lightningnetwork/lnd/signal"
|
||||
"github.com/urfave/cli"
|
||||
@@ -2029,7 +2030,7 @@ var updateChannelPolicyCommand = cli.Command{
|
||||
"with a granularity of 0.000001 (millionths). Can not " +
|
||||
"be set at the same time as fee_rate.",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "time_lock_delta",
|
||||
Usage: "the CLTV delta that will be applied to all " +
|
||||
"forwarded HTLCs",
|
||||
@@ -2080,6 +2081,25 @@ func parseChanPoint(s string) (*lnrpc.ChannelPoint, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseTimeLockDelta is expected to get a uint16 type of timeLockDelta,
|
||||
// which maximum value is MaxTimeLockDelta.
|
||||
func parseTimeLockDelta(timeLockDeltaStr string) (uint16, error) {
|
||||
timeLockDeltaUnCheck, err := strconv.ParseUint(
|
||||
timeLockDeltaStr, 10, 64,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse time_lock_delta: %s "+
|
||||
"to uint64, err: %v", timeLockDeltaStr, err)
|
||||
}
|
||||
|
||||
if timeLockDeltaUnCheck > routing.MaxCLTVDelta {
|
||||
return 0, fmt.Errorf("time_lock_delta is too big, "+
|
||||
"max value is %d", routing.MaxCLTVDelta)
|
||||
}
|
||||
|
||||
return uint16(timeLockDeltaUnCheck), nil
|
||||
}
|
||||
|
||||
func updateChannelPolicy(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
client, cleanUp := getClient(ctx)
|
||||
@@ -2089,7 +2109,7 @@ func updateChannelPolicy(ctx *cli.Context) error {
|
||||
baseFee int64
|
||||
feeRate float64
|
||||
feeRatePpm uint64
|
||||
timeLockDelta int64
|
||||
timeLockDelta uint16
|
||||
err error
|
||||
)
|
||||
args := ctx.Args()
|
||||
@@ -2127,12 +2147,15 @@ func updateChannelPolicy(ctx *cli.Context) error {
|
||||
|
||||
switch {
|
||||
case ctx.IsSet("time_lock_delta"):
|
||||
timeLockDelta = ctx.Int64("time_lock_delta")
|
||||
case args.Present():
|
||||
timeLockDelta, err = strconv.ParseInt(args.First(), 10, 64)
|
||||
timeLockDeltaStr := ctx.String("time_lock_delta")
|
||||
timeLockDelta, err = parseTimeLockDelta(timeLockDeltaStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to decode time_lock_delta: %v",
|
||||
err)
|
||||
return err
|
||||
}
|
||||
case args.Present():
|
||||
timeLockDelta, err = parseTimeLockDelta(args.First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args = args.Tail()
|
||||
@@ -2291,8 +2314,9 @@ func exportChanBackup(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
chanPointStr string
|
||||
err error
|
||||
chanPointStr string
|
||||
outputFileName string
|
||||
)
|
||||
args := ctx.Args()
|
||||
|
||||
@@ -2307,6 +2331,10 @@ func exportChanBackup(ctx *cli.Context) error {
|
||||
return fmt.Errorf("must specify chan_point if --all isn't set")
|
||||
}
|
||||
|
||||
if ctx.IsSet("output_file") {
|
||||
outputFileName = ctx.String("output_file")
|
||||
}
|
||||
|
||||
if chanPointStr != "" {
|
||||
chanPointRPC, err := parseChanPoint(chanPointStr)
|
||||
if err != nil {
|
||||
@@ -2334,6 +2362,14 @@ func exportChanBackup(ctx *cli.Context) error {
|
||||
Index: chanPointRPC.OutputIndex,
|
||||
}
|
||||
|
||||
if outputFileName != "" {
|
||||
return os.WriteFile(
|
||||
outputFileName,
|
||||
chanBackup.ChanBackup,
|
||||
0666,
|
||||
)
|
||||
}
|
||||
|
||||
printJSON(struct {
|
||||
ChanPoint string `json:"chan_point"`
|
||||
ChanBackup []byte `json:"chan_backup"`
|
||||
@@ -2355,9 +2391,9 @@ func exportChanBackup(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if ctx.IsSet("output_file") {
|
||||
return ioutil.WriteFile(
|
||||
ctx.String("output_file"),
|
||||
if outputFileName != "" {
|
||||
return os.WriteFile(
|
||||
outputFileName,
|
||||
chanBackup.MultiChanBackup.MultiChanBackup,
|
||||
0666,
|
||||
)
|
||||
@@ -2393,13 +2429,16 @@ var verifyChanBackupCommand = cli.Command{
|
||||
backup for integrity. This is useful when a user has a backup, but is
|
||||
unsure as to if it's valid or for the target node.
|
||||
|
||||
The command will accept backups in one of three forms:
|
||||
The command will accept backups in one of four forms:
|
||||
|
||||
* A single channel packed SCB, which can be obtained from
|
||||
exportchanbackup. This should be passed in hex encoded format.
|
||||
|
||||
* A packed multi-channel SCB, which couples several individual
|
||||
static channel backups in single blob.
|
||||
|
||||
* A file path which points to a packed single-channel backup within a
|
||||
file, using the same format that lnd does in its channel.backup file.
|
||||
|
||||
* A file path which points to a packed multi-channel backup within a
|
||||
file, using the same format that lnd does in its channel.backup
|
||||
@@ -2416,6 +2455,13 @@ var verifyChanBackupCommand = cli.Command{
|
||||
Usage: "a hex encoded multi-channel backup obtained " +
|
||||
"from exportchanbackup",
|
||||
},
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "single_file",
|
||||
Usage: "the path to a single-channel backup file",
|
||||
TakesFile: true,
|
||||
},
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "multi_file",
|
||||
Usage: "the path to a multi-channel back up file",
|
||||
@@ -2476,7 +2522,7 @@ var restoreChanBackupCommand = cli.Command{
|
||||
channel. If successful, this command will allows the user to recover
|
||||
the settled funds stored in the recovered channels.
|
||||
|
||||
The command will accept backups in one of three forms:
|
||||
The command will accept backups in one of four forms:
|
||||
|
||||
* A single channel packed SCB, which can be obtained from
|
||||
exportchanbackup. This should be passed in hex encoded format.
|
||||
@@ -2484,6 +2530,10 @@ var restoreChanBackupCommand = cli.Command{
|
||||
* A packed multi-channel SCB, which couples several individual
|
||||
static channel backups in single blob.
|
||||
|
||||
* A file path which points to a packed single-channel backup within
|
||||
a file, using the same format that lnd does in its channel.backup
|
||||
file.
|
||||
|
||||
* A file path which points to a packed multi-channel backup within a
|
||||
file, using the same format that lnd does in its channel.backup
|
||||
file.
|
||||
@@ -2499,6 +2549,13 @@ var restoreChanBackupCommand = cli.Command{
|
||||
Usage: "a hex encoded multi-channel backup obtained " +
|
||||
"from exportchanbackup",
|
||||
},
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "single_file",
|
||||
Usage: "the path to a single-channel backup file",
|
||||
TakesFile: true,
|
||||
},
|
||||
|
||||
cli.StringFlag{
|
||||
Name: "multi_file",
|
||||
Usage: "the path to a multi-channel back up file",
|
||||
@@ -2550,6 +2607,23 @@ func parseChanBackups(ctx *cli.Context) (*lnrpc.RestoreChanBackupRequest, error)
|
||||
},
|
||||
}, nil
|
||||
|
||||
case ctx.IsSet("single_file"):
|
||||
packedSingle, err := os.ReadFile(ctx.String("single_file"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode single "+
|
||||
"packed backup: %v", err)
|
||||
}
|
||||
|
||||
return &lnrpc.RestoreChanBackupRequest{
|
||||
Backup: &lnrpc.RestoreChanBackupRequest_ChanBackups{
|
||||
ChanBackups: &lnrpc.ChannelBackups{
|
||||
ChanBackups: []*lnrpc.ChannelBackup{{
|
||||
ChanBackup: packedSingle,
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
|
||||
case ctx.IsSet("multi_file"):
|
||||
packedMulti, err := ioutil.ReadFile(ctx.String("multi_file"))
|
||||
if err != nil {
|
||||
|
@@ -2,6 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -68,3 +71,53 @@ func TestParseChanPoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseTimeLockDelta tests parseTimeLockDelta with various
|
||||
// valid and invalid input values and verifies the output.
|
||||
func TestParseTimeLockDelta(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := []struct {
|
||||
timeLockDeltaStr string
|
||||
expectedTimeLockDelta uint16
|
||||
expectErr bool
|
||||
expectedErrContent string
|
||||
}{
|
||||
{
|
||||
timeLockDeltaStr: "-1",
|
||||
expectErr: true,
|
||||
expectedErrContent: fmt.Sprintf(
|
||||
"failed to parse time_lock_delta: %d "+
|
||||
"to uint64", -1,
|
||||
),
|
||||
},
|
||||
{
|
||||
timeLockDeltaStr: "0",
|
||||
},
|
||||
{
|
||||
timeLockDeltaStr: "3",
|
||||
expectedTimeLockDelta: 3,
|
||||
},
|
||||
{
|
||||
timeLockDeltaStr: strconv.FormatUint(
|
||||
uint64(math.MaxUint16), 10,
|
||||
),
|
||||
expectedTimeLockDelta: math.MaxUint16,
|
||||
},
|
||||
{
|
||||
timeLockDeltaStr: "18446744073709551616",
|
||||
expectErr: true,
|
||||
expectedErrContent: fmt.Sprint(
|
||||
"failed to parse time_lock_delta:" +
|
||||
" 18446744073709551616 to uint64"),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
timeLockDelta, err := parseTimeLockDelta(tc.timeLockDeltaStr)
|
||||
require.Equal(t, tc.expectedTimeLockDelta, timeLockDelta)
|
||||
if tc.expectErr {
|
||||
require.ErrorContains(t, err, tc.expectedErrContent)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -660,6 +660,13 @@ var fundPsbtCommand = cli.Command{
|
||||
"always use the coin selection key scope to " +
|
||||
"generate the change address",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "min_confs",
|
||||
Usage: "(optional) the minimum number of " +
|
||||
"confirmations each input used for the PSBT " +
|
||||
"transaction must satisfy",
|
||||
Value: defaultUtxoMinConf,
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(fundPsbt),
|
||||
}
|
||||
@@ -673,8 +680,11 @@ func fundPsbt(ctx *cli.Context) error {
|
||||
return cli.ShowCommandHelp(ctx, "fund")
|
||||
}
|
||||
|
||||
minConfs := int32(ctx.Uint64("min_confs"))
|
||||
req := &walletrpc.FundPsbtRequest{
|
||||
Account: ctx.String("account"),
|
||||
Account: ctx.String("account"),
|
||||
MinConfs: minConfs,
|
||||
SpendUnconfirmed: minConfs == 0,
|
||||
}
|
||||
|
||||
// Parse template flags.
|
||||
|
@@ -151,6 +151,12 @@ var listTowersCommand = cli.Command{
|
||||
Usage: "include sessions with the watchtower in the " +
|
||||
"response",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "exclude_exhausted_sessions",
|
||||
Usage: "Whether to exclude exhausted sessions in " +
|
||||
"the response info. This option is only " +
|
||||
"meaningful if include_sessions is true",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(listTowers),
|
||||
}
|
||||
@@ -160,7 +166,7 @@ func listTowers(ctx *cli.Context) error {
|
||||
|
||||
// Display the command's help message if the number of arguments/flags
|
||||
// is not what we expect.
|
||||
if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
|
||||
if ctx.NArg() > 0 || ctx.NumFlags() > 2 {
|
||||
return cli.ShowCommandHelp(ctx, "towers")
|
||||
}
|
||||
|
||||
@@ -169,6 +175,9 @@ func listTowers(ctx *cli.Context) error {
|
||||
|
||||
req := &wtclientrpc.ListTowersRequest{
|
||||
IncludeSessions: ctx.Bool("include_sessions"),
|
||||
ExcludeExhaustedSessions: ctx.Bool(
|
||||
"exclude_exhausted_sessions",
|
||||
),
|
||||
}
|
||||
resp, err := client.ListTowers(ctxc, req)
|
||||
if err != nil {
|
||||
@@ -190,6 +199,12 @@ var getTowerCommand = cli.Command{
|
||||
Usage: "include sessions with the watchtower in the " +
|
||||
"response",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "exclude_exhausted_sessions",
|
||||
Usage: "Whether to exclude exhausted sessions in " +
|
||||
"the response info. This option is only " +
|
||||
"meaningful if include_sessions is true",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(getTower),
|
||||
}
|
||||
@@ -199,7 +214,7 @@ func getTower(ctx *cli.Context) error {
|
||||
|
||||
// Display the command's help message if the number of arguments/flags
|
||||
// is not what we expect.
|
||||
if ctx.NArg() != 1 || ctx.NumFlags() > 1 {
|
||||
if ctx.NArg() != 1 || ctx.NumFlags() > 2 {
|
||||
return cli.ShowCommandHelp(ctx, "tower")
|
||||
}
|
||||
|
||||
@@ -217,6 +232,9 @@ func getTower(ctx *cli.Context) error {
|
||||
req := &wtclientrpc.GetTowerInfoRequest{
|
||||
Pubkey: pubKey,
|
||||
IncludeSessions: ctx.Bool("include_sessions"),
|
||||
ExcludeExhaustedSessions: ctx.Bool(
|
||||
"exclude_exhausted_sessions",
|
||||
),
|
||||
}
|
||||
resp, err := client.GetTowerInfo(ctxc, req)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user