diff --git a/cmd/lncli/walletrpc_active.go b/cmd/lncli/walletrpc_active.go index 15c62755f5..36caf82279 100644 --- a/cmd/lncli/walletrpc_active.go +++ b/cmd/lncli/walletrpc_active.go @@ -1160,7 +1160,8 @@ var fundPsbtCommand = cli.Command{ Name: "fund", Usage: "Fund a Partially Signed Bitcoin Transaction (PSBT).", ArgsUsage: "[--template_psbt=T | [--outputs=O [--inputs=I]]] " + - "[--conf_target=C | --sat_per_vbyte=S] [--change_type=A]", + "[--conf_target=C | --sat_per_vbyte=S | --sat_per_kw=K] " + + "[--change_type=A]", Description: ` The fund command creates a fully populated PSBT that contains enough inputs to fund the outputs specified in either the PSBT or the @@ -1222,6 +1223,11 @@ var fundPsbtCommand = cli.Command{ Usage: "a manual fee expressed in sat/vbyte that " + "should be used when creating the transaction", }, + cli.Uint64Flag{ + Name: "sat_per_kw", + Usage: "a manual fee expressed in sat/kw that " + + "should be used when creating the transaction", + }, cli.StringFlag{ Name: "account", Usage: "(optional) the name of the account to use to " + @@ -1302,10 +1308,11 @@ func fundPsbt(ctx *cli.Context) error { ) if len(ctx.String("outputs")) > 0 { - // Parse the address to amount map as JSON now. At least one - // entry must be present. + // Parse the address to amount map as JSON now. At least + // one entry must be present. jsonMap := []byte(ctx.String("outputs")) - if err := json.Unmarshal(jsonMap, &amountToAddr); err != nil { + err := json.Unmarshal(jsonMap, &amountToAddr) + if err != nil { return fmt.Errorf("error parsing outputs "+ "JSON: %w", err) } @@ -1317,7 +1324,8 @@ func fundPsbt(ctx *cli.Context) error { var inputs []string jsonList := []byte(ctx.String("inputs")) - if err := json.Unmarshal(jsonList, &inputs); err != nil { + err := json.Unmarshal(jsonList, &inputs) + if err != nil { return fmt.Errorf("error parsing inputs JSON: "+ "%v", err) } @@ -1344,15 +1352,23 @@ func fundPsbt(ctx *cli.Context) error { // Parse fee flags. switch { - case ctx.IsSet("conf_target") && ctx.IsSet("sat_per_vbyte"): - return fmt.Errorf("cannot set conf_target and sat_per_vbyte " + - "at the same time") + case ctx.IsSet("conf_target") && ctx.IsSet("sat_per_vbyte") || + ctx.IsSet("conf_target") && ctx.IsSet("sat_per_kw") || + ctx.IsSet("sat_per_vbyte") && ctx.IsSet("sat_per_kw"): + + return fmt.Errorf("only one of conf_target, sat_per_vbyte, " + + "or sat_per_kw can be set at the same time") case ctx.Uint64("sat_per_vbyte") > 0: req.Fees = &walletrpc.FundPsbtRequest_SatPerVbyte{ SatPerVbyte: ctx.Uint64("sat_per_vbyte"), } + case ctx.Uint64("sat_per_kw") > 0: + req.Fees = &walletrpc.FundPsbtRequest_SatPerKw{ + SatPerKw: ctx.Uint64("sat_per_kw"), + } + // Check conf_target last because it has a default value. case ctx.Uint64("conf_target") > 0: req.Fees = &walletrpc.FundPsbtRequest_TargetConf{ diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md index 869e235115..fdcd13c6c6 100644 --- a/docs/release-notes/release-notes-0.19.0.md +++ b/docs/release-notes/release-notes-0.19.0.md @@ -31,8 +31,16 @@ `BumpForceCloseFee` which moves the functionality soley available in the `lncli` to LND hence making it more universal. +* [The `walletrpc.FundPsbt` RPC method now has an option to specify the fee as + `sat_per_kw` which allows for more precise + fees](https://github.com/lightningnetwork/lnd/pull/9013). + ## lncli Additions +* [The `lncli wallet fundpsbt` sub command now has a `--sat_per_kw` flag to + specify more precise fee + rates](https://github.com/lightningnetwork/lnd/pull/9013). + # Improvements ## Functional Updates diff --git a/lnrpc/walletrpc/walletkit.pb.go b/lnrpc/walletrpc/walletkit.pb.go index 4976f2186e..c2ac8a8a5b 100644 --- a/lnrpc/walletrpc/walletkit.pb.go +++ b/lnrpc/walletrpc/walletkit.pb.go @@ -3645,6 +3645,7 @@ type FundPsbtRequest struct { // // *FundPsbtRequest_TargetConf // *FundPsbtRequest_SatPerVbyte + // *FundPsbtRequest_SatPerKw Fees isFundPsbtRequest_Fees `protobuf_oneof:"fees"` // The name of the account to fund the PSBT with. If empty, the default wallet // account is used. @@ -3744,6 +3745,13 @@ func (x *FundPsbtRequest) GetSatPerVbyte() uint64 { return 0 } +func (x *FundPsbtRequest) GetSatPerKw() uint64 { + if x, ok := x.GetFees().(*FundPsbtRequest_SatPerKw); ok { + return x.SatPerKw + } + return 0 +} + func (x *FundPsbtRequest) GetAccount() string { if x != nil { return x.Account @@ -3841,10 +3849,18 @@ type FundPsbtRequest_SatPerVbyte struct { SatPerVbyte uint64 `protobuf:"varint,4,opt,name=sat_per_vbyte,json=satPerVbyte,proto3,oneof"` } +type FundPsbtRequest_SatPerKw struct { + // The fee rate, expressed in sat/kWU, that should be used to spend the + // input with. + SatPerKw uint64 `protobuf:"varint,11,opt,name=sat_per_kw,json=satPerKw,proto3,oneof"` +} + func (*FundPsbtRequest_TargetConf) isFundPsbtRequest_Fees() {} func (*FundPsbtRequest_SatPerVbyte) isFundPsbtRequest_Fees() {} +func (*FundPsbtRequest_SatPerKw) isFundPsbtRequest_Fees() {} + type FundPsbtResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4905,7 +4921,7 @@ var file_walletrpc_walletkit_proto_rawDesc = []byte{ 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xe6, 0x03, 0x0a, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x86, 0x04, 0x0a, 0x0f, 0x46, 0x75, 0x6e, 0x64, 0x50, 0x73, 0x62, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x73, 0x62, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x04, 0x70, 0x73, 0x62, 0x74, 0x12, 0x29, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x02, 0x20, @@ -4919,7 +4935,9 @@ var file_walletrpc_walletkit_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x61, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x76, 0x62, 0x79, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x61, 0x74, - 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, + 0x50, 0x65, 0x72, 0x56, 0x62, 0x79, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x61, 0x74, 0x5f, + 0x70, 0x65, 0x72, 0x5f, 0x6b, 0x77, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x08, + 0x73, 0x61, 0x74, 0x50, 0x65, 0x72, 0x4b, 0x77, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x73, 0x12, @@ -6200,6 +6218,7 @@ func file_walletrpc_walletkit_proto_init() { (*FundPsbtRequest_CoinSelect)(nil), (*FundPsbtRequest_TargetConf)(nil), (*FundPsbtRequest_SatPerVbyte)(nil), + (*FundPsbtRequest_SatPerKw)(nil), } file_walletrpc_walletkit_proto_msgTypes[53].OneofWrappers = []interface{}{ (*PsbtCoinSelect_ExistingOutputIndex)(nil), diff --git a/lnrpc/walletrpc/walletkit.proto b/lnrpc/walletrpc/walletkit.proto index eac7a733f6..c79dc7c06b 100644 --- a/lnrpc/walletrpc/walletkit.proto +++ b/lnrpc/walletrpc/walletkit.proto @@ -1388,6 +1388,12 @@ message FundPsbtRequest { input with. */ uint64 sat_per_vbyte = 4; + + /* + The fee rate, expressed in sat/kWU, that should be used to spend the + input with. + */ + uint64 sat_per_kw = 11; } /* diff --git a/lnrpc/walletrpc/walletkit.swagger.json b/lnrpc/walletrpc/walletkit.swagger.json index dd117a4d9b..2d6194d154 100644 --- a/lnrpc/walletrpc/walletkit.swagger.json +++ b/lnrpc/walletrpc/walletkit.swagger.json @@ -1564,6 +1564,11 @@ "format": "uint64", "description": "The fee rate, expressed in sat/vbyte, that should be used to spend the\ninput with." }, + "sat_per_kw": { + "type": "string", + "format": "uint64", + "description": "The fee rate, expressed in sat/kWU, that should be used to spend the\ninput with." + }, "account": { "type": "string", "description": "The name of the account to fund the PSBT with. If empty, the default wallet\naccount is used." diff --git a/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go index fc24ae13ed..9ce87cca21 100644 --- a/lnrpc/walletrpc/walletkit_server.go +++ b/lnrpc/walletrpc/walletkit_server.go @@ -1502,9 +1502,13 @@ func (w *WalletKit) FundPsbt(_ context.Context, req.GetSatPerVbyte() * 1000, ).FeePerKWeight() + case req.GetSatPerKw() != 0: + feeSatPerKW = chainfee.SatPerKWeight(req.GetSatPerKw()) + default: return nil, fmt.Errorf("fee definition missing, need to " + - "specify either target_conf or sat_per_vbyte") + "specify either target_conf, sat_per_vbyte or " + + "sat_per_kw") } // Then, we'll extract the minimum number of confirmations that each