Merge pull request #6592 from priyanshiiit/walletreserve
lnrpc+lncli: adds required wallet reserve rpc & cli
This commit is contained in:
21 files changed
+2642
-2112
No files matched your search
@@ -66,6 +66,7 @@ func walletCommands() []cli.Command {
|
||||
listLeasesCommand,
|
||||
psbtCommand,
|
||||
accountsCommand,
|
||||
requiredReserveCommand,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1083,6 +1084,53 @@ func listAccounts(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var requiredReserveCommand = cli.Command{
|
||||
Name: "requiredreserve",
|
||||
Usage: "Returns the wallet reserve.",
|
||||
Description: `
|
||||
Returns the minimum amount of satoshis that should be kept in the
|
||||
wallet in order to fee bump anchor channels if necessary. The value
|
||||
scales with the number of public anchor channels but is capped at
|
||||
a maximum.
|
||||
|
||||
Use the flag --additional_channels to get the reserve value based
|
||||
on the additional channels you would like to open.
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
cli.Uint64Flag{
|
||||
Name: "additional_channels",
|
||||
Usage: "(optional) specify the additional public channels " +
|
||||
"that you would like to open",
|
||||
},
|
||||
},
|
||||
Action: actionDecorator(requiredReserve),
|
||||
}
|
||||
|
||||
func requiredReserve(ctx *cli.Context) error {
|
||||
ctxc := getContext()
|
||||
|
||||
// Display the command's help message if we do not have the expected
|
||||
// number of arguments/flags.
|
||||
if ctx.NArg() > 0 || ctx.NumFlags() > 1 {
|
||||
return cli.ShowCommandHelp(ctx, "requiredreserve")
|
||||
}
|
||||
|
||||
walletClient, cleanUp := getWalletClient(ctx)
|
||||
defer cleanUp()
|
||||
|
||||
req := &walletrpc.RequiredReserveRequest{
|
||||
AdditionalPublicChannels: uint32(ctx.Uint64("additional_channels")),
|
||||
}
|
||||
resp, err := walletClient.RequiredReserve(ctxc, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printRespJSON(resp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var importAccountCommand = cli.Command{
|
||||
Name: "import",
|
||||
Usage: "Import an on-chain account into the wallet through its " +
|
||||
|
||||
@@ -14,15 +14,18 @@
|
||||
|
||||
* [Add previous_outpoints to listchaintxns](https://github.com/lightningnetwork/lnd/pull/6321)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed data race found in
|
||||
[`TestSerializeHTLCEntries`](https://github.com/lightningnetwork/lnd/pull/6673).
|
||||
|
||||
## RPC Server
|
||||
|
||||
* [Add wallet reserve rpc & field in wallet balance](https://github.com/lightningnetwork/lnd/pull/6592)
|
||||
|
||||
# Contributors (Alphabetical Order)
|
||||
|
||||
* Elle Mouton
|
||||
* ErikEk
|
||||
* Priyansh Rastogi
|
||||
* Yong Yu
|
||||
* Yong Yu
|
||||
+1549
-1536
File diff suppressed because it is too large.
Load diff
@@ -2567,6 +2567,9 @@ message WalletBalanceResponse {
|
||||
// other usage.
|
||||
int64 locked_balance = 5;
|
||||
|
||||
// The amount of reserve required.
|
||||
int64 reserved_balance_anchor_chan = 6;
|
||||
|
||||
// A mapping of each wallet account's name to its balance.
|
||||
map<string, WalletAccountBalance> account_balance = 4;
|
||||
}
|
||||
|
||||
@@ -6646,6 +6646,11 @@
|
||||
"format": "int64",
|
||||
"description": "The total amount of wallet UTXOs held in outputs that are locked for\nother usage."
|
||||
},
|
||||
"reserved_balance_anchor_chan": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "The amount of reserve required."
|
||||
},
|
||||
"account_balance": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
||||
@@ -67,4 +67,8 @@ type Config struct {
|
||||
|
||||
// ChainParams are the parameters of the wallet's backing chain.
|
||||
ChainParams *chaincfg.Params
|
||||
|
||||
// CurrentNumAnchorChans returns the current number of non-private
|
||||
// anchor channels the wallet should be ready to fee bump if needed.
|
||||
CurrentNumAnchorChans func() (int, error)
|
||||
}
|
||||
+697
-558
File diff suppressed because it is too large.
Load diff
@@ -290,6 +290,42 @@ func local_request_WalletKit_ListAccounts_0(ctx context.Context, marshaler runti
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_WalletKit_RequiredReserve_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||
)
|
||||
|
||||
func request_WalletKit_RequiredReserve_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq RequiredReserveRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WalletKit_RequiredReserve_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.RequiredReserve(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func local_request_WalletKit_RequiredReserve_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq RequiredReserveRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WalletKit_RequiredReserve_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := server.RequiredReserve(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_WalletKit_ImportAccount_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ImportAccountRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -892,6 +928,29 @@ func RegisterWalletKitHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_WalletKit_RequiredReserve_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/walletrpc.WalletKit/RequiredReserve", runtime.WithHTTPPathPattern("/v2/wallet/reserve"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_WalletKit_RequiredReserve_0(rctx, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_WalletKit_RequiredReserve_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1369,6 +1428,26 @@ func RegisterWalletKitHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_WalletKit_RequiredReserve_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req, "/walletrpc.WalletKit/RequiredReserve", runtime.WithHTTPPathPattern("/v2/wallet/reserve"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_WalletKit_RequiredReserve_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_WalletKit_RequiredReserve_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_WalletKit_ImportAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1629,6 +1708,8 @@ var (
|
||||
|
||||
pattern_WalletKit_ListAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "accounts"}, ""))
|
||||
|
||||
pattern_WalletKit_RequiredReserve_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "reserve"}, ""))
|
||||
|
||||
pattern_WalletKit_ImportAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "accounts", "import"}, ""))
|
||||
|
||||
pattern_WalletKit_ImportPublicKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "key", "import"}, ""))
|
||||
@@ -1671,6 +1752,8 @@ var (
|
||||
|
||||
forward_WalletKit_ListAccounts_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_WalletKit_RequiredReserve_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_WalletKit_ImportAccount_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_WalletKit_ImportPublicKey_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -224,6 +224,31 @@ func RegisterWalletKitJSONCallbacks(registry map[string]func(ctx context.Context
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["walletrpc.WalletKit.RequiredReserve"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
req := &RequiredReserveRequest{}
|
||||
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewWalletKitClient(conn)
|
||||
resp, err := client.RequiredReserve(ctx, req)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
|
||||
respBytes, err := marshaler.Marshal(resp)
|
||||
if err != nil {
|
||||
callback("", err)
|
||||
return
|
||||
}
|
||||
callback(string(respBytes), nil)
|
||||
}
|
||||
|
||||
registry["walletrpc.WalletKit.ImportAccount"] = func(ctx context.Context,
|
||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||
|
||||
|
||||
@@ -64,6 +64,14 @@ service WalletKit {
|
||||
*/
|
||||
rpc ListAccounts (ListAccountsRequest) returns (ListAccountsResponse);
|
||||
|
||||
/*
|
||||
RequiredReserve returns the minimum amount of satoshis that should be kept
|
||||
in the wallet in order to fee bump anchor channels if necessary. The value
|
||||
scales with the number of public anchor channels but is capped at a maximum.
|
||||
*/
|
||||
rpc RequiredReserve (RequiredReserveRequest)
|
||||
returns (RequiredReserveResponse);
|
||||
|
||||
/*
|
||||
ImportAccount imports an account backed by an account extended public key.
|
||||
The master key fingerprint denotes the fingerprint of the root key
|
||||
@@ -400,6 +408,16 @@ message ListAccountsResponse {
|
||||
repeated Account accounts = 1;
|
||||
}
|
||||
|
||||
message RequiredReserveRequest {
|
||||
// The number of additional channels the user would like to open.
|
||||
uint32 additional_public_channels = 1;
|
||||
}
|
||||
|
||||
message RequiredReserveResponse {
|
||||
// The amount of reserve required.
|
||||
int64 required_reserve = 1;
|
||||
}
|
||||
|
||||
message ImportAccountRequest {
|
||||
// A name to identify the account with.
|
||||
string name = 1;
|
||||
|
||||
@@ -399,6 +399,39 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/wallet/reserve": {
|
||||
"get": {
|
||||
"summary": "RequiredReserve returns the minimum amount of satoshis that should be kept\nin the wallet in order to fee bump anchor channels if necessary. The value\nscales with the number of public anchor channels but is capped at a maximum.",
|
||||
"operationId": "WalletKit_RequiredReserve",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/walletrpcRequiredReserveResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rpcStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "additional_public_channels",
|
||||
"description": "The number of additional channels the user would like to open.",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"WalletKit"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/wallet/send": {
|
||||
"post": {
|
||||
"summary": "SendOutputs is similar to the existing sendmany call in Bitcoind, and\nallows the caller to create a transaction that sends to several outputs at\nonce. This is ideal when wanting to batch create a set of transactions.",
|
||||
@@ -1467,6 +1500,16 @@
|
||||
"walletrpcReleaseOutputResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
"walletrpcRequiredReserveResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"required_reserve": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
"description": "The amount of reserve required."
|
||||
}
|
||||
}
|
||||
},
|
||||
"walletrpcSendOutputsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -55,6 +55,8 @@ http:
|
||||
body: "*"
|
||||
- selector: walletrpc.WalletKit.ListAccounts
|
||||
get: "/v2/wallet/accounts"
|
||||
- selector: walletrpc.WalletKit.RequiredReserve
|
||||
get: "/v2/wallet/reserve"
|
||||
- selector: walletrpc.WalletKit.ImportAccount
|
||||
post: "/v2/wallet/accounts/import"
|
||||
body: "*"
|
||||
@@ -58,6 +58,11 @@ type WalletKitClient interface {
|
||||
//wallet accounts and return only those matching.
|
||||
ListAccounts(ctx context.Context, in *ListAccountsRequest, opts ...grpc.CallOption) (*ListAccountsResponse, error)
|
||||
//
|
||||
//RequiredReserve returns the minimum amount of satoshis that should be kept
|
||||
//in the wallet in order to fee bump anchor channels if necessary. The value
|
||||
//scales with the number of public anchor channels but is capped at a maximum.
|
||||
RequiredReserve(ctx context.Context, in *RequiredReserveRequest, opts ...grpc.CallOption) (*RequiredReserveResponse, error)
|
||||
//
|
||||
//ImportAccount imports an account backed by an account extended public key.
|
||||
//The master key fingerprint denotes the fingerprint of the root key
|
||||
//corresponding to the account public key (also known as the key with
|
||||
@@ -281,6 +286,15 @@ func (c *walletKitClient) ListAccounts(ctx context.Context, in *ListAccountsRequ
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletKitClient) RequiredReserve(ctx context.Context, in *RequiredReserveRequest, opts ...grpc.CallOption) (*RequiredReserveResponse, error) {
|
||||
out := new(RequiredReserveResponse)
|
||||
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/RequiredReserve", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *walletKitClient) ImportAccount(ctx context.Context, in *ImportAccountRequest, opts ...grpc.CallOption) (*ImportAccountResponse, error) {
|
||||
out := new(ImportAccountResponse)
|
||||
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/ImportAccount", in, out, opts...)
|
||||
@@ -432,6 +446,11 @@ type WalletKitServer interface {
|
||||
//wallet accounts and return only those matching.
|
||||
ListAccounts(context.Context, *ListAccountsRequest) (*ListAccountsResponse, error)
|
||||
//
|
||||
//RequiredReserve returns the minimum amount of satoshis that should be kept
|
||||
//in the wallet in order to fee bump anchor channels if necessary. The value
|
||||
//scales with the number of public anchor channels but is capped at a maximum.
|
||||
RequiredReserve(context.Context, *RequiredReserveRequest) (*RequiredReserveResponse, error)
|
||||
//
|
||||
//ImportAccount imports an account backed by an account extended public key.
|
||||
//The master key fingerprint denotes the fingerprint of the root key
|
||||
//corresponding to the account public key (also known as the key with
|
||||
@@ -604,6 +623,9 @@ func (UnimplementedWalletKitServer) NextAddr(context.Context, *AddrRequest) (*Ad
|
||||
func (UnimplementedWalletKitServer) ListAccounts(context.Context, *ListAccountsRequest) (*ListAccountsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListAccounts not implemented")
|
||||
}
|
||||
func (UnimplementedWalletKitServer) RequiredReserve(context.Context, *RequiredReserveRequest) (*RequiredReserveResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RequiredReserve not implemented")
|
||||
}
|
||||
func (UnimplementedWalletKitServer) ImportAccount(context.Context, *ImportAccountRequest) (*ImportAccountResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ImportAccount not implemented")
|
||||
}
|
||||
@@ -797,6 +819,24 @@ func _WalletKit_ListAccounts_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletKit_RequiredReserve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RequiredReserveRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(WalletKitServer).RequiredReserve(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/walletrpc.WalletKit/RequiredReserve",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(WalletKitServer).RequiredReserve(ctx, req.(*RequiredReserveRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _WalletKit_ImportAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ImportAccountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -1052,6 +1092,10 @@ var WalletKit_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListAccounts",
|
||||
Handler: _WalletKit_ListAccounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RequiredReserve",
|
||||
Handler: _WalletKit_RequiredReserve_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ImportAccount",
|
||||
Handler: _WalletKit_ImportAccount_Handler,
|
||||
|
||||
@@ -136,6 +136,10 @@ var (
|
||||
Entity: "onchain",
|
||||
Action: "read",
|
||||
}},
|
||||
"/walletrpc.WalletKit/RequiredReserve": {{
|
||||
Entity: "onchain",
|
||||
Action: "read",
|
||||
}},
|
||||
"/walletrpc.WalletKit/ImportAccount": {{
|
||||
Entity: "onchain",
|
||||
Action: "write",
|
||||
@@ -1468,6 +1472,27 @@ func (w *WalletKit) ListAccounts(ctx context.Context,
|
||||
return &ListAccountsResponse{Accounts: rpcAccounts}, nil
|
||||
}
|
||||
|
||||
// RequiredReserve returns the minimum amount of satoshis that should be
|
||||
// kept in the wallet in order to fee bump anchor channels if necessary.
|
||||
// The value scales with the number of public anchor channels but is
|
||||
// capped at a maximum.
|
||||
func (w *WalletKit) RequiredReserve(ctx context.Context,
|
||||
req *RequiredReserveRequest) (*RequiredReserveResponse, error) {
|
||||
|
||||
numAnchorChans, err := w.cfg.CurrentNumAnchorChans()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
additionalChans := req.AdditionalPublicChannels
|
||||
totalChans := uint32(numAnchorChans) + additionalChans
|
||||
reserved := w.cfg.Wallet.RequiredReserve(totalChans)
|
||||
|
||||
return &RequiredReserveResponse{
|
||||
RequiredReserve: int64(reserved),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseAddrType parses an address type from its RPC representation to a
|
||||
// *waddrmgr.AddressType.
|
||||
func parseAddrType(addrType AddressType,
|
||||
|
||||
@@ -341,6 +341,39 @@ func testAnchorReservedValue(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// The reserved value is now back in Alice's wallet.
|
||||
aliceBalance := waitForConfirmedBalance()
|
||||
|
||||
// The reserved value should be equal to the required reserve for anchor
|
||||
// channels.
|
||||
walletBalanceResp, err := alice.WalletBalance(
|
||||
ctxb, &lnrpc.WalletBalanceRequest{},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
require.Equal(
|
||||
t.t, aliceBalance, walletBalanceResp.ReservedBalanceAnchorChan,
|
||||
)
|
||||
|
||||
additionalChannels := int64(1)
|
||||
|
||||
// Required reserve when additional channels are provided.
|
||||
requiredReserveResp, err := alice.WalletKitClient.RequiredReserve(
|
||||
ctxb, &walletrpc.RequiredReserveRequest{
|
||||
AdditionalPublicChannels: uint32(additionalChannels),
|
||||
},
|
||||
)
|
||||
require.NoError(t.t, err)
|
||||
|
||||
additionalReservedValue := btcutil.Amount(additionalChannels *
|
||||
int64(lnwallet.AnchorChanReservedValue))
|
||||
totalReserved := btcutil.Amount(aliceBalance) + additionalReservedValue
|
||||
|
||||
// The total reserved value should not exceed the maximum value reserved
|
||||
// for anchor channels.
|
||||
if totalReserved > lnwallet.MaxAnchorChanReservedValue {
|
||||
totalReserved = lnwallet.MaxAnchorChanReservedValue
|
||||
}
|
||||
require.Equal(
|
||||
t.t, int64(totalReserved), requiredReserveResp.RequiredReserve,
|
||||
)
|
||||
|
||||
// Alice closes channel, should now be allowed to send everything to an
|
||||
// external address.
|
||||
for _, chanPoint := range chanPoints {
|
||||
|
||||
@@ -105,6 +105,11 @@ func (w *WalletController) ListAccounts(string,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RequiredReserve currently returns a dummy value.
|
||||
func (w *WalletController) RequiredReserve(uint32) btcutil.Amount {
|
||||
return 0
|
||||
}
|
||||
|
||||
// ImportAccount currently returns a dummy value.
|
||||
func (w *WalletController) ImportAccount(string, *hdkeychain.ExtendedKey,
|
||||
uint32, *waddrmgr.AddressType, bool) (*waddrmgr.AccountProperties,
|
||||
|
||||
@@ -711,6 +711,22 @@ func (b *BtcWallet) ListAccounts(name string,
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// RequiredReserve returns the minimum amount of satoshis that should be
|
||||
// kept in the wallet in order to fee bump anchor channels if necessary.
|
||||
// The value scales with the number of public anchor channels but is
|
||||
// capped at a maximum.
|
||||
func (b *BtcWallet) RequiredReserve(
|
||||
numAnchorChans uint32) btcutil.Amount {
|
||||
|
||||
anchorChanReservedValue := lnwallet.AnchorChanReservedValue
|
||||
reserved := btcutil.Amount(numAnchorChans) * anchorChanReservedValue
|
||||
if reserved > lnwallet.MaxAnchorChanReservedValue {
|
||||
reserved = lnwallet.MaxAnchorChanReservedValue
|
||||
}
|
||||
|
||||
return reserved
|
||||
}
|
||||
|
||||
// ImportAccount imports an account backed by an account extended public key.
|
||||
// The master key fingerprint denotes the fingerprint of the root key
|
||||
// corresponding to the account public key (also known as the key with
|
||||
|
||||
@@ -244,6 +244,12 @@ type WalletController interface {
|
||||
ListAccounts(string, *waddrmgr.KeyScope) ([]*waddrmgr.AccountProperties,
|
||||
error)
|
||||
|
||||
// RequiredReserve returns the minimum amount of satoshis that should be
|
||||
// kept in the wallet in order to fee bump anchor channels if necessary.
|
||||
// The value scales with the number of public anchor channels but is
|
||||
// capped at a maximum.
|
||||
RequiredReserve(uint32) btcutil.Amount
|
||||
|
||||
// ImportAccount imports an account backed by an account extended public
|
||||
// key. The master key fingerprint denotes the fingerprint of the root
|
||||
// key corresponding to the account public key (also known as the key
|
||||
|
||||
+11
-11
@@ -35,18 +35,18 @@ const (
|
||||
// outside word.
|
||||
msgBufferSize = 100
|
||||
|
||||
// anchorChanReservedValue is the amount we'll keep around in the
|
||||
// AnchorChanReservedValue is the amount we'll keep around in the
|
||||
// wallet in case we have to fee bump anchor channels on force close.
|
||||
// TODO(halseth): update constant to target a specific commit size at
|
||||
// set fee rate.
|
||||
anchorChanReservedValue = btcutil.Amount(10_000)
|
||||
AnchorChanReservedValue = btcutil.Amount(10_000)
|
||||
|
||||
// maxAnchorChanReservedValue is the maximum value we'll reserve for
|
||||
// MaxAnchorChanReservedValue is the maximum value we'll reserve for
|
||||
// anchor channel fee bumping. We cap it at 10 times the per-channel
|
||||
// amount such that nodes with a high number of channels don't have to
|
||||
// keep around a very large amount for the unlikely scenario that they
|
||||
// all close at the same time.
|
||||
maxAnchorChanReservedValue = 10 * anchorChanReservedValue
|
||||
MaxAnchorChanReservedValue = 10 * AnchorChanReservedValue
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -900,7 +900,7 @@ func (l *LightningWallet) enforceNewReservedValue(fundingIntent chanfunding.Inte
|
||||
return nil
|
||||
}
|
||||
|
||||
numAnchors, err := l.currentNumAnchorChans()
|
||||
numAnchors, err := l.CurrentNumAnchorChans()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -919,9 +919,9 @@ func (l *LightningWallet) enforceNewReservedValue(fundingIntent chanfunding.Inte
|
||||
})
|
||||
}
|
||||
|
||||
// currentNumAnchorChans returns the current number of non-private anchor
|
||||
// CurrentNumAnchorChans returns the current number of non-private anchor
|
||||
// channels the wallet should be ready to fee bump if needed.
|
||||
func (l *LightningWallet) currentNumAnchorChans() (int, error) {
|
||||
func (l *LightningWallet) CurrentNumAnchorChans() (int, error) {
|
||||
// Count all anchor channels that are open or pending
|
||||
// open, or waiting close.
|
||||
chans, err := l.Cfg.Database.FetchAllChannels()
|
||||
@@ -1046,9 +1046,9 @@ func (l *LightningWallet) CheckReservedValue(in []wire.OutPoint,
|
||||
}
|
||||
|
||||
// We reserve a given amount for each anchor channel.
|
||||
reserved := btcutil.Amount(numAnchorChans) * anchorChanReservedValue
|
||||
if reserved > maxAnchorChanReservedValue {
|
||||
reserved = maxAnchorChanReservedValue
|
||||
reserved := btcutil.Amount(numAnchorChans) * AnchorChanReservedValue
|
||||
if reserved > MaxAnchorChanReservedValue {
|
||||
reserved = MaxAnchorChanReservedValue
|
||||
}
|
||||
|
||||
if walletBalance < reserved {
|
||||
@@ -1069,7 +1069,7 @@ func (l *LightningWallet) CheckReservedValue(in []wire.OutPoint,
|
||||
func (l *LightningWallet) CheckReservedValueTx(req CheckReservedValueTxReq) (
|
||||
btcutil.Amount, error) {
|
||||
|
||||
numAnchors, err := l.currentNumAnchorChans()
|
||||
numAnchors, err := l.CurrentNumAnchorChans()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
+17
-5
@@ -3136,16 +3136,28 @@ func (r *rpcServer) WalletBalance(ctx context.Context,
|
||||
lockedBalance += utxoInfo.Value
|
||||
}
|
||||
|
||||
// Get the current number of non-private anchor channels.
|
||||
currentNumAnchorChans, err := r.server.cc.Wallet.CurrentNumAnchorChans()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the required reserve for the wallet.
|
||||
requiredReserve := r.server.cc.Wallet.RequiredReserve(
|
||||
uint32(currentNumAnchorChans),
|
||||
)
|
||||
|
||||
rpcsLog.Debugf("[walletbalance] Total balance=%v (confirmed=%v, "+
|
||||
"unconfirmed=%v)", totalBalance, confirmedBalance,
|
||||
unconfirmedBalance)
|
||||
|
||||
return &lnrpc.WalletBalanceResponse{
|
||||
TotalBalance: int64(totalBalance),
|
||||
ConfirmedBalance: int64(confirmedBalance),
|
||||
UnconfirmedBalance: int64(unconfirmedBalance),
|
||||
LockedBalance: int64(lockedBalance),
|
||||
AccountBalance: rpcAccountBalances,
|
||||
TotalBalance: int64(totalBalance),
|
||||
ConfirmedBalance: int64(confirmedBalance),
|
||||
UnconfirmedBalance: int64(unconfirmedBalance),
|
||||
LockedBalance: int64(lockedBalance),
|
||||
ReservedBalanceAnchorChan: int64(requiredReserve),
|
||||
AccountBalance: rpcAccountBalances,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
||||
subCfgValue.FieldByName("ChainParams").Set(
|
||||
reflect.ValueOf(activeNetParams),
|
||||
)
|
||||
subCfgValue.FieldByName("CurrentNumAnchorChans").Set(
|
||||
reflect.ValueOf(cc.Wallet.CurrentNumAnchorChans),
|
||||
)
|
||||
|
||||
case *autopilotrpc.Config:
|
||||
subCfgValue := extractReflectValue(subCfg)
|
||||
|
||||
Reference in new issue
Block a user