multi: add bumpforceclosefee rpc endpoint.
Add a new bumpforceclosefee rpc endpoint to the wallet server. Move the logic from the lncli level to the wallet server rpc level. This is more in line with a proper client-server design. wallet lncli: use new bumpforceclosefee endpoint. Besides using the new bumpforceclosefee rpc endpoint we also enable the bumping of taproot anchor channels.
This commit is contained in:
12 files changed
+1229
-617
No files matched your search
@@ -5,7 +5,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -21,7 +20,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||||
@@ -431,51 +429,50 @@ var bumpForceCloseFeeCommand = cli.Command{
|
|||||||
allows the fee of a channel force closing transaction to be increased by
|
allows the fee of a channel force closing transaction to be increased by
|
||||||
using the child-pays-for-parent mechanism. It will instruct the sweeper
|
using the child-pays-for-parent mechanism. It will instruct the sweeper
|
||||||
to sweep the anchor outputs of the closing transaction at the requested
|
to sweep the anchor outputs of the closing transaction at the requested
|
||||||
fee rate or confirmation target. The specified fee rate will be the
|
confirmation target and limit the fees to the specified budget.
|
||||||
effective fee rate taking the parent fee into account.
|
|
||||||
`,
|
`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.Uint64Flag{
|
cli.Uint64Flag{
|
||||||
Name: "conf_target",
|
Name: "conf_target",
|
||||||
Usage: `
|
Usage: `
|
||||||
The deadline in number of blocks that the input should be spent within.
|
The deadline in number of blocks that the anchor output should be spent
|
||||||
When not set, for new inputs, the default value (1008) is used; for
|
within to bump the closing transaction.`,
|
||||||
exiting inputs, their current values will be retained.`,
|
|
||||||
},
|
},
|
||||||
cli.Uint64Flag{
|
cli.Uint64Flag{
|
||||||
Name: "sat_per_byte",
|
Name: "sat_per_byte",
|
||||||
Usage: "Deprecated, use sat_per_vbyte instead.",
|
Usage: "Deprecated, use sat_per_vbyte instead.",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
|
cli.Uint64Flag{
|
||||||
|
Name: "sat_per_vbyte",
|
||||||
|
Usage: `
|
||||||
|
The starting fee rate, expressed in sat/vbyte. This value will be used
|
||||||
|
by the sweeper's fee function as its starting fee rate. When not set,
|
||||||
|
the sweeper will use the estimated fee rate using the target_conf as the
|
||||||
|
starting fee rate.`,
|
||||||
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "force",
|
Name: "force",
|
||||||
Usage: "Deprecated, use immediate instead.",
|
Usage: "Deprecated, use immediate instead.",
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
cli.Uint64Flag{
|
|
||||||
Name: "sat_per_vbyte",
|
|
||||||
Usage: `
|
|
||||||
The starting fee rate, expressed in sat/vbyte, that will be used to
|
|
||||||
spend the input with initially. This value will be used by the
|
|
||||||
sweeper's fee function as its starting fee rate. When not set, the
|
|
||||||
sweeper will use the estimated fee rate using the target_conf as the
|
|
||||||
starting fee rate.`,
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "immediate",
|
Name: "immediate",
|
||||||
Usage: `
|
Usage: `
|
||||||
Whether this input will be swept immediately. When set to true, the
|
Whether this cpfp transaction will be triggered immediately. When set to
|
||||||
sweeper will sweep this input without waiting for the next batch.`,
|
true, the sweeper will consider all currently pending registered sweeps
|
||||||
|
and trigger new batch transactions including the sweeping of the anchor
|
||||||
|
output related to the selected force close transaction.`,
|
||||||
},
|
},
|
||||||
cli.Uint64Flag{
|
cli.Uint64Flag{
|
||||||
Name: "budget",
|
Name: "budget",
|
||||||
Usage: `
|
Usage: `
|
||||||
The max amount in sats that can be used as the fees. Setting this value
|
The max amount in sats that can be used as the fees. For already
|
||||||
greater than the input's value may result in CPFP - one or more wallet
|
registered anchor outputs if not set explicitly the old value will be
|
||||||
utxos will be used to pay the fees specified by the budget. If not set,
|
used. For channel force closes which have no HTLCs in their commitment
|
||||||
for new inputs, by default 50% of the input's value will be treated as
|
transaction this value has to be set to an appropriate amount to pay for
|
||||||
the budget for fee bumping; for existing inputs, their current budgets
|
the cpfp transaction of the force closed channel otherwise the fee
|
||||||
will be retained.`,
|
bumping will fail.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: actionDecorator(bumpForceCloseFee),
|
Action: actionDecorator(bumpForceCloseFee),
|
||||||
@@ -492,99 +489,44 @@ func bumpForceCloseFee(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Validate the channel point.
|
// Validate the channel point.
|
||||||
channelPoint := ctx.Args().Get(0)
|
channelPoint := ctx.Args().Get(0)
|
||||||
_, err := NewProtoOutPoint(channelPoint)
|
rpcChannelPoint, err := parseChanPoint(channelPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch all waiting close channels.
|
// `sat_per_byte` was deprecated we only use sats/vbyte now.
|
||||||
client, cleanUp := getClient(ctx)
|
if ctx.IsSet("sat_per_byte") {
|
||||||
defer cleanUp()
|
return fmt.Errorf("deprecated, use sat_per_vbyte instead.")
|
||||||
|
|
||||||
// Fetch waiting close channel commitments.
|
|
||||||
commitments, err := getWaitingCloseCommitments(
|
|
||||||
ctxc, client, channelPoint,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve pending sweeps.
|
// Retrieve pending sweeps.
|
||||||
walletClient, cleanUp := getWalletClient(ctx)
|
walletClient, cleanUp := getWalletClient(ctx)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
sweeps, err := walletClient.PendingSweeps(
|
// Parse immediate flag (force flag was deprecated).
|
||||||
ctxc, &walletrpc.PendingSweepsRequest{},
|
if ctx.IsSet("immediate") && ctx.IsSet("force") {
|
||||||
)
|
return fmt.Errorf("cannot set immediate and force flag at " +
|
||||||
if err != nil {
|
"the same time")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
immediate := ctx.Bool("immediate") || ctx.Bool("force")
|
||||||
|
|
||||||
// Match pending sweeps with commitments of the channel for which a bump
|
resp, err := walletClient.BumpForceCloseFee(
|
||||||
// is requested and bump their fees.
|
ctxc, &walletrpc.BumpForceCloseFeeRequest{
|
||||||
commitSet := map[string]struct{}{
|
ChanPoint: rpcChannelPoint,
|
||||||
commitments.LocalTxid: {},
|
DeadlineDelta: uint32(ctx.Uint64("conf_target")),
|
||||||
commitments.RemoteTxid: {},
|
|
||||||
}
|
|
||||||
if commitments.RemotePendingTxid != "" {
|
|
||||||
commitSet[commitments.RemotePendingTxid] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sweep := range sweeps.PendingSweeps {
|
|
||||||
// Only bump anchor sweeps.
|
|
||||||
if sweep.WitnessType != walletrpc.WitnessType_COMMITMENT_ANCHOR {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip unrelated sweeps.
|
|
||||||
sweepTxID, err := chainhash.NewHash(sweep.Outpoint.TxidBytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, match := commitSet[sweepTxID.String()]; !match {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := walletClient.BumpFee(
|
|
||||||
ctxc, &walletrpc.BumpFeeRequest{
|
|
||||||
Outpoint: sweep.Outpoint,
|
|
||||||
TargetConf: uint32(ctx.Uint64("conf_target")),
|
|
||||||
Budget: ctx.Uint64("budget"),
|
Budget: ctx.Uint64("budget"),
|
||||||
Immediate: ctx.Bool("immediate"),
|
Immediate: immediate,
|
||||||
SatPerVbyte: ctx.Uint64("sat_per_vbyte"),
|
StartingFeerate: ctx.Uint64("sat_per_vbyte"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bump fee of the anchor sweep.
|
fmt.Printf("BumpForceCloseFee result: %s\n", resp.Status)
|
||||||
fmt.Printf("Bumping fee of %v:%v: %v\n",
|
|
||||||
sweepTxID, sweep.Outpoint.OutputIndex, resp.GetStatus())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWaitingCloseCommitments(ctxc context.Context,
|
|
||||||
client lnrpc.LightningClient, channelPoint string) (
|
|
||||||
*lnrpc.PendingChannelsResponse_Commitments, error) {
|
|
||||||
|
|
||||||
req := &lnrpc.PendingChannelsRequest{}
|
|
||||||
resp, err := client.PendingChannels(ctxc, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup the channel commit tx hashes.
|
|
||||||
for _, channel := range resp.WaitingCloseChannels {
|
|
||||||
if channel.Channel.ChannelPoint == channelPoint {
|
|
||||||
return channel.Commitments, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.New("channel not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
var listSweepsCommand = cli.Command{
|
var listSweepsCommand = cli.Command{
|
||||||
Name: "listsweeps",
|
Name: "listsweeps",
|
||||||
Usage: "Lists all sweeps that have been published by our node.",
|
Usage: "Lists all sweeps that have been published by our node.",
|
||||||
|
|||||||
@@ -197,6 +197,32 @@ func GetChanPointFundingTxid(chanPoint *ChannelPoint) (*chainhash.Hash, error) {
|
|||||||
return chainhash.NewHash(txid)
|
return chainhash.NewHash(txid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelOutPoint returns the outpoint of the related channel point.
|
||||||
|
func GetChannelOutPoint(chanPoint *ChannelPoint) (*OutPoint, error) {
|
||||||
|
var txid []byte
|
||||||
|
|
||||||
|
// A channel point's funding txid can be get/set as a byte slice or a
|
||||||
|
// string. In the case it is a string, decode it.
|
||||||
|
switch chanPoint.GetFundingTxid().(type) {
|
||||||
|
case *ChannelPoint_FundingTxidBytes:
|
||||||
|
txid = chanPoint.GetFundingTxidBytes()
|
||||||
|
|
||||||
|
case *ChannelPoint_FundingTxidStr:
|
||||||
|
s := chanPoint.GetFundingTxidStr()
|
||||||
|
h, err := chainhash.NewHashFromStr(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txid = h[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &OutPoint{
|
||||||
|
TxidBytes: txid,
|
||||||
|
OutputIndex: chanPoint.OutputIndex,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CalculateFeeRate uses either satPerByte or satPerVByte, but not both, from a
|
// CalculateFeeRate uses either satPerByte or satPerVByte, but not both, from a
|
||||||
// request to calculate the fee rate. It provides compatibility for the
|
// request to calculate the fee rate. It provides compatibility for the
|
||||||
// deprecated field, satPerByte. Once the field is safe to be removed, the
|
// deprecated field, satPerByte. Once the field is safe to be removed, the
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package walletrpc
|
|||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcwallet/wallet"
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/keychain"
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
@@ -76,4 +77,7 @@ type Config struct {
|
|||||||
// CoinSelectionStrategy is the strategy that is used for selecting
|
// CoinSelectionStrategy is the strategy that is used for selecting
|
||||||
// coins when funding a transaction.
|
// coins when funding a transaction.
|
||||||
CoinSelectionStrategy wallet.CoinSelectionStrategy
|
CoinSelectionStrategy wallet.CoinSelectionStrategy
|
||||||
|
|
||||||
|
// ChanStateDB is the reference to the channel db.
|
||||||
|
ChanStateDB *channeldb.ChannelStateDB
|
||||||
}
|
}
|
||||||
+718
-522
File diff suppressed because it is too large.
Load diff
@@ -774,6 +774,40 @@ func local_request_WalletKit_BumpFee_0(ctx context.Context, marshaler runtime.Ma
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func request_WalletKit_BumpForceCloseFee_0(ctx context.Context, marshaler runtime.Marshaler, client WalletKitClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq BumpForceCloseFeeRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||||
|
if berr != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||||
|
}
|
||||||
|
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := client.BumpForceCloseFee(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func local_request_WalletKit_BumpForceCloseFee_0(ctx context.Context, marshaler runtime.Marshaler, server WalletKitServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||||
|
var protoReq BumpForceCloseFeeRequest
|
||||||
|
var metadata runtime.ServerMetadata
|
||||||
|
|
||||||
|
newReader, berr := utilities.IOReaderFactory(req.Body)
|
||||||
|
if berr != nil {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
|
||||||
|
}
|
||||||
|
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
|
||||||
|
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := server.BumpForceCloseFee(ctx, &protoReq)
|
||||||
|
return msg, metadata, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
filter_WalletKit_ListSweeps_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
filter_WalletKit_ListSweeps_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||||
)
|
)
|
||||||
@@ -1458,6 +1492,29 @@ func RegisterWalletKitHandlerServer(ctx context.Context, mux *runtime.ServeMux,
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_BumpForceCloseFee_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/BumpForceCloseFee", runtime.WithHTTPPathPattern("/v2/wallet/BumpForceCloseFee"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := local_request_WalletKit_BumpForceCloseFee_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_BumpForceCloseFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_WalletKit_ListSweeps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_WalletKit_ListSweeps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -2054,6 +2111,26 @@ func RegisterWalletKitHandlerClient(ctx context.Context, mux *runtime.ServeMux,
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mux.Handle("POST", pattern_WalletKit_BumpForceCloseFee_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/BumpForceCloseFee", runtime.WithHTTPPathPattern("/v2/wallet/BumpForceCloseFee"))
|
||||||
|
if err != nil {
|
||||||
|
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, md, err := request_WalletKit_BumpForceCloseFee_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_BumpForceCloseFee_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("GET", pattern_WalletKit_ListSweeps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
mux.Handle("GET", pattern_WalletKit_ListSweeps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||||
ctx, cancel := context.WithCancel(req.Context())
|
ctx, cancel := context.WithCancel(req.Context())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -2202,6 +2279,8 @@ var (
|
|||||||
|
|
||||||
pattern_WalletKit_BumpFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "bumpfee"}, ""))
|
pattern_WalletKit_BumpFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "bumpfee"}, ""))
|
||||||
|
|
||||||
|
pattern_WalletKit_BumpForceCloseFee_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "BumpForceCloseFee"}, ""))
|
||||||
|
|
||||||
pattern_WalletKit_ListSweeps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "sweeps"}, ""))
|
pattern_WalletKit_ListSweeps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "wallet", "sweeps"}, ""))
|
||||||
|
|
||||||
pattern_WalletKit_LabelTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "tx", "label"}, ""))
|
pattern_WalletKit_LabelTransaction_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v2", "wallet", "tx", "label"}, ""))
|
||||||
@@ -2258,6 +2337,8 @@ var (
|
|||||||
|
|
||||||
forward_WalletKit_BumpFee_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_BumpFee_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
|
forward_WalletKit_BumpForceCloseFee_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WalletKit_ListSweeps_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_ListSweeps_0 = runtime.ForwardResponseMessage
|
||||||
|
|
||||||
forward_WalletKit_LabelTransaction_0 = runtime.ForwardResponseMessage
|
forward_WalletKit_LabelTransaction_0 = runtime.ForwardResponseMessage
|
||||||
|
|||||||
@@ -572,6 +572,31 @@ func RegisterWalletKitJSONCallbacks(registry map[string]func(ctx context.Context
|
|||||||
callback(string(respBytes), nil)
|
callback(string(respBytes), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registry["walletrpc.WalletKit.BumpForceCloseFee"] = func(ctx context.Context,
|
||||||
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
req := &BumpForceCloseFeeRequest{}
|
||||||
|
err := marshaler.Unmarshal([]byte(reqJSON), req)
|
||||||
|
if err != nil {
|
||||||
|
callback("", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := NewWalletKitClient(conn)
|
||||||
|
resp, err := client.BumpForceCloseFee(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.ListSweeps"] = func(ctx context.Context,
|
registry["walletrpc.WalletKit.ListSweeps"] = func(ctx context.Context,
|
||||||
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
conn *grpc.ClientConn, reqJSON string, callback func(string, error)) {
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,13 @@ service WalletKit {
|
|||||||
*/
|
*/
|
||||||
rpc BumpFee (BumpFeeRequest) returns (BumpFeeResponse);
|
rpc BumpFee (BumpFeeRequest) returns (BumpFeeResponse);
|
||||||
|
|
||||||
|
/* lncli: `wallet bumpforceclosefee`
|
||||||
|
BumpForceCloseFee is an endpoint that allows users to bump the fee of a
|
||||||
|
channel force close. This only works for channels with option_anchors.
|
||||||
|
*/
|
||||||
|
rpc BumpForceCloseFee (BumpForceCloseFeeRequest)
|
||||||
|
returns (BumpForceCloseFeeResponse);
|
||||||
|
|
||||||
/* lncli: `wallet listsweeps`
|
/* lncli: `wallet listsweeps`
|
||||||
ListSweeps returns a list of the sweep transactions our node has produced.
|
ListSweeps returns a list of the sweep transactions our node has produced.
|
||||||
Note that these sweeps may not be confirmed yet, as we record sweeps on
|
Note that these sweeps may not be confirmed yet, as we record sweeps on
|
||||||
@@ -1226,6 +1233,46 @@ message BumpFeeResponse {
|
|||||||
string status = 1;
|
string status = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message BumpForceCloseFeeRequest {
|
||||||
|
// The channel point which force close transaction we are attempting to
|
||||||
|
// bump the fee rate for.
|
||||||
|
lnrpc.ChannelPoint chan_point = 1;
|
||||||
|
|
||||||
|
// Optional. The deadline delta in number of blocks that the anchor output
|
||||||
|
// should be spent within to bump the closing transaction.
|
||||||
|
uint32 deadline_delta = 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optional. The starting fee rate, expressed in sat/vbyte. This value will be
|
||||||
|
used by the sweeper's fee function as its starting fee rate. When not set,
|
||||||
|
the sweeper will use the estimated fee rate using the target_conf as the
|
||||||
|
starting fee rate.
|
||||||
|
*/
|
||||||
|
uint64 starting_feerate = 3;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optional. Whether this cpfp transaction will be triggered immediately. When
|
||||||
|
set to true, the sweeper will consider all currently registered sweeps and
|
||||||
|
trigger new batch transactions including the sweeping of the anchor output
|
||||||
|
related to the selected force close transaction.
|
||||||
|
*/
|
||||||
|
bool immediate = 4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Optional. The max amount in sats that can be used as the fees. For already
|
||||||
|
registered anchor outputs if not set explicitly the old value will be used.
|
||||||
|
For channel force closes which have no HTLCs in their commitment transaction
|
||||||
|
this value has to be set to an appropriate amount to pay for the cpfp
|
||||||
|
transaction of the force closed channel otherwise the fee bumping will fail.
|
||||||
|
*/
|
||||||
|
uint64 budget = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BumpForceCloseFeeResponse {
|
||||||
|
// The status of the force close fee bump operation.
|
||||||
|
string status = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message ListSweepsRequest {
|
message ListSweepsRequest {
|
||||||
/*
|
/*
|
||||||
Retrieve the full sweep transaction details. If false, only the sweep txids
|
Retrieve the full sweep transaction details. If false, only the sweep txids
|
||||||
|
|||||||
@@ -16,6 +16,39 @@
|
|||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/v2/wallet/BumpForceCloseFee": {
|
||||||
|
"post": {
|
||||||
|
"summary": "lncli: `wallet bumpforceclosefee`\nBumpForceCloseFee is an endpoint that allows users to bump the fee of a\nchannel force close. This only works for channels with option_anchors.",
|
||||||
|
"operationId": "WalletKit_BumpForceCloseFee",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "A successful response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcBumpForceCloseFeeResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"description": "An unexpected error response.",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rpcStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/walletrpcBumpForceCloseFeeRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"WalletKit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v2/wallet/accounts": {
|
"/v2/wallet/accounts": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "lncli: `wallet accounts list`\nListAccounts retrieves all accounts belonging to the wallet by default. A\nname and key scope filter can be provided to filter through all of the\nwallet accounts and return only those matching.",
|
"summary": "lncli: `wallet accounts list`\nListAccounts retrieves all accounts belonging to the wallet by default. A\nname and key scope filter can be provided to filter through all of the\nwallet accounts and return only those matching.",
|
||||||
@@ -949,6 +982,25 @@
|
|||||||
"description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4)",
|
"description": "- `p2wkh`: Pay to witness key hash (`WITNESS_PUBKEY_HASH` = 0)\n- `np2wkh`: Pay to nested witness key hash (`NESTED_PUBKEY_HASH` = 1)\n- `p2tr`: Pay to taproot pubkey (`TAPROOT_PUBKEY` = 4)",
|
||||||
"title": "`AddressType` has to be one of:"
|
"title": "`AddressType` has to be one of:"
|
||||||
},
|
},
|
||||||
|
"lnrpcChannelPoint": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"funding_txid_bytes": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "byte",
|
||||||
|
"description": "Txid of the funding transaction. When using REST, this field must be\nencoded as base64."
|
||||||
|
},
|
||||||
|
"funding_txid_str": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Hex-encoded string representing the byte-reversed hash of the funding\ntransaction."
|
||||||
|
},
|
||||||
|
"output_index": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"title": "The index of the output of the funding transaction"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"lnrpcCoinSelectionStrategy": {
|
"lnrpcCoinSelectionStrategy": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -1396,6 +1448,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"walletrpcBumpForceCloseFeeRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"chan_point": {
|
||||||
|
"$ref": "#/definitions/lnrpcChannelPoint",
|
||||||
|
"description": "The channel point which force close transaction we are attempting to\nbump the fee rate for."
|
||||||
|
},
|
||||||
|
"deadline_delta": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"description": "Optional. The deadline delta in number of blocks that the anchor output\nshould be spent within to bump the closing transaction."
|
||||||
|
},
|
||||||
|
"starting_feerate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64",
|
||||||
|
"description": "Optional. The starting fee rate, expressed in sat/vbyte. This value will be\nused by the sweeper's fee function as its starting fee rate. When not set,\nthe sweeper will use the estimated fee rate using the target_conf as the\nstarting fee rate."
|
||||||
|
},
|
||||||
|
"immediate": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Optional. Whether this cpfp transaction will be triggered immediately. When\nset to true, the sweeper will consider all currently registered sweeps and\ntrigger new batch transactions including the sweeping of the anchor output\nrelated to the selected force close transaction."
|
||||||
|
},
|
||||||
|
"budget": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "uint64",
|
||||||
|
"description": "Optional. The max amount in sats that can be used as the fees. For already\nregistered anchor outputs if not set explicitly the old value will be used.\nFor channel force closes which have no HTLCs in their commitment transaction\nthis value has to be set to an appropriate amount to pay for the cpfp\ntransaction of the force closed channel otherwise the fee bumping will fail."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"walletrpcBumpForceCloseFeeResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The status of the force close fee bump operation."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"walletrpcChangeAddressType": {
|
"walletrpcChangeAddressType": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|||||||
@@ -76,3 +76,6 @@ http:
|
|||||||
- selector: walletrpc.WalletKit.RemoveTransaction
|
- selector: walletrpc.WalletKit.RemoveTransaction
|
||||||
post: "/v2/wallet/removetx"
|
post: "/v2/wallet/removetx"
|
||||||
body: "*"
|
body: "*"
|
||||||
|
- selector: walletrpc.WalletKit.BumpForceCloseFee
|
||||||
|
post: "/v2/wallet/BumpForceCloseFee"
|
||||||
|
body: "*"
|
||||||
@@ -208,6 +208,10 @@ type WalletKitClient interface {
|
|||||||
// done by specifying an outpoint within the low fee transaction that is under
|
// done by specifying an outpoint within the low fee transaction that is under
|
||||||
// the control of the wallet.
|
// the control of the wallet.
|
||||||
BumpFee(ctx context.Context, in *BumpFeeRequest, opts ...grpc.CallOption) (*BumpFeeResponse, error)
|
BumpFee(ctx context.Context, in *BumpFeeRequest, opts ...grpc.CallOption) (*BumpFeeResponse, error)
|
||||||
|
// lncli: `wallet bumpforceclosefee`
|
||||||
|
// BumpForceCloseFee is an endpoint that allows users to bump the fee of a
|
||||||
|
// channel force close. This only works for channels with option_anchors.
|
||||||
|
BumpForceCloseFee(ctx context.Context, in *BumpForceCloseFeeRequest, opts ...grpc.CallOption) (*BumpForceCloseFeeResponse, error)
|
||||||
// lncli: `wallet listsweeps`
|
// lncli: `wallet listsweeps`
|
||||||
// ListSweeps returns a list of the sweep transactions our node has produced.
|
// ListSweeps returns a list of the sweep transactions our node has produced.
|
||||||
// Note that these sweeps may not be confirmed yet, as we record sweeps on
|
// Note that these sweeps may not be confirmed yet, as we record sweeps on
|
||||||
@@ -482,6 +486,15 @@ func (c *walletKitClient) BumpFee(ctx context.Context, in *BumpFeeRequest, opts
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *walletKitClient) BumpForceCloseFee(ctx context.Context, in *BumpForceCloseFeeRequest, opts ...grpc.CallOption) (*BumpForceCloseFeeResponse, error) {
|
||||||
|
out := new(BumpForceCloseFeeResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/BumpForceCloseFee", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *walletKitClient) ListSweeps(ctx context.Context, in *ListSweepsRequest, opts ...grpc.CallOption) (*ListSweepsResponse, error) {
|
func (c *walletKitClient) ListSweeps(ctx context.Context, in *ListSweepsRequest, opts ...grpc.CallOption) (*ListSweepsResponse, error) {
|
||||||
out := new(ListSweepsResponse)
|
out := new(ListSweepsResponse)
|
||||||
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/ListSweeps", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/walletrpc.WalletKit/ListSweeps", in, out, opts...)
|
||||||
@@ -719,6 +732,10 @@ type WalletKitServer interface {
|
|||||||
// done by specifying an outpoint within the low fee transaction that is under
|
// done by specifying an outpoint within the low fee transaction that is under
|
||||||
// the control of the wallet.
|
// the control of the wallet.
|
||||||
BumpFee(context.Context, *BumpFeeRequest) (*BumpFeeResponse, error)
|
BumpFee(context.Context, *BumpFeeRequest) (*BumpFeeResponse, error)
|
||||||
|
// lncli: `wallet bumpforceclosefee`
|
||||||
|
// BumpForceCloseFee is an endpoint that allows users to bump the fee of a
|
||||||
|
// channel force close. This only works for channels with option_anchors.
|
||||||
|
BumpForceCloseFee(context.Context, *BumpForceCloseFeeRequest) (*BumpForceCloseFeeResponse, error)
|
||||||
// lncli: `wallet listsweeps`
|
// lncli: `wallet listsweeps`
|
||||||
// ListSweeps returns a list of the sweep transactions our node has produced.
|
// ListSweeps returns a list of the sweep transactions our node has produced.
|
||||||
// Note that these sweeps may not be confirmed yet, as we record sweeps on
|
// Note that these sweeps may not be confirmed yet, as we record sweeps on
|
||||||
@@ -858,6 +875,9 @@ func (UnimplementedWalletKitServer) PendingSweeps(context.Context, *PendingSweep
|
|||||||
func (UnimplementedWalletKitServer) BumpFee(context.Context, *BumpFeeRequest) (*BumpFeeResponse, error) {
|
func (UnimplementedWalletKitServer) BumpFee(context.Context, *BumpFeeRequest) (*BumpFeeResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method BumpFee not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method BumpFee not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedWalletKitServer) BumpForceCloseFee(context.Context, *BumpForceCloseFeeRequest) (*BumpForceCloseFeeResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method BumpForceCloseFee not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedWalletKitServer) ListSweeps(context.Context, *ListSweepsRequest) (*ListSweepsResponse, error) {
|
func (UnimplementedWalletKitServer) ListSweeps(context.Context, *ListSweepsRequest) (*ListSweepsResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListSweeps not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListSweeps not implemented")
|
||||||
}
|
}
|
||||||
@@ -1282,6 +1302,24 @@ func _WalletKit_BumpFee_Handler(srv interface{}, ctx context.Context, dec func(i
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _WalletKit_BumpForceCloseFee_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(BumpForceCloseFeeRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(WalletKitServer).BumpForceCloseFee(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/walletrpc.WalletKit/BumpForceCloseFee",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(WalletKitServer).BumpForceCloseFee(ctx, req.(*BumpForceCloseFeeRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _WalletKit_ListSweeps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _WalletKit_ListSweeps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(ListSweepsRequest)
|
in := new(ListSweepsRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@@ -1467,6 +1505,10 @@ var WalletKit_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "BumpFee",
|
MethodName: "BumpFee",
|
||||||
Handler: _WalletKit_BumpFee_Handler,
|
Handler: _WalletKit_BumpFee_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "BumpForceCloseFee",
|
||||||
|
Handler: _WalletKit_BumpForceCloseFee_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "ListSweeps",
|
MethodName: "ListSweeps",
|
||||||
Handler: _WalletKit_ListSweeps_Handler,
|
Handler: _WalletKit_ListSweeps_Handler,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
base "github.com/btcsuite/btcwallet/wallet"
|
base "github.com/btcsuite/btcwallet/wallet"
|
||||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||||
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/contractcourt"
|
"github.com/lightningnetwork/lnd/contractcourt"
|
||||||
"github.com/lightningnetwork/lnd/fn"
|
"github.com/lightningnetwork/lnd/fn"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
@@ -42,6 +43,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
"github.com/lightningnetwork/lnd/lnwallet/chanfunding"
|
||||||
"github.com/lightningnetwork/lnd/macaroons"
|
"github.com/lightningnetwork/lnd/macaroons"
|
||||||
"github.com/lightningnetwork/lnd/sweep"
|
"github.com/lightningnetwork/lnd/sweep"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
)
|
)
|
||||||
@@ -106,6 +108,10 @@ var (
|
|||||||
Entity: "onchain",
|
Entity: "onchain",
|
||||||
Action: "write",
|
Action: "write",
|
||||||
}},
|
}},
|
||||||
|
"/walletrpc.WalletKit/BumpForceCloseFee": {{
|
||||||
|
Entity: "onchain",
|
||||||
|
Action: "write",
|
||||||
|
}},
|
||||||
"/walletrpc.WalletKit/ListSweeps": {{
|
"/walletrpc.WalletKit/ListSweeps": {{
|
||||||
Entity: "onchain",
|
Entity: "onchain",
|
||||||
Action: "read",
|
Action: "read",
|
||||||
@@ -1125,6 +1131,154 @@ func (w *WalletKit) BumpFee(ctx context.Context,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getWaitingCloseChannel returns the waiting close channel in case it does
|
||||||
|
// exist in the underlying channel state database.
|
||||||
|
func (w *WalletKit) getWaitingCloseChannel(
|
||||||
|
chanPoint wire.OutPoint) (*channeldb.OpenChannel, error) {
|
||||||
|
|
||||||
|
// Fetch all channels, which still have their commitment transaction not
|
||||||
|
// confirmed (waiting close channels).
|
||||||
|
chans, err := w.cfg.ChanStateDB.FetchWaitingCloseChannels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
channel := fn.Find(func(c *channeldb.OpenChannel) bool {
|
||||||
|
return c.FundingOutpoint == chanPoint
|
||||||
|
}, chans)
|
||||||
|
|
||||||
|
return channel.UnwrapOrErr(errors.New("channel not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BumpForceCloseFee bumps the fee rate of an unconfirmed anchor channel. It
|
||||||
|
// updates the new fee rate parameters with the sweeper subsystem. Additionally
|
||||||
|
// it will try to create anchor cpfp transactions for all possible commitment
|
||||||
|
// transactions (local, remote, remote-dangling) so depending on which
|
||||||
|
// commitment is in the local mempool only one of them will succeed in being
|
||||||
|
// broadcasted.
|
||||||
|
func (w *WalletKit) BumpForceCloseFee(_ context.Context,
|
||||||
|
in *BumpForceCloseFeeRequest) (*BumpForceCloseFeeResponse, error) {
|
||||||
|
|
||||||
|
if in.ChanPoint == nil {
|
||||||
|
return nil, fmt.Errorf("no chan_point provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
lnrpcOutpoint, err := lnrpc.GetChannelOutPoint(in.ChanPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
outPoint, err := UnmarshallOutPoint(lnrpcOutpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the relevant channel if it is in the waiting close state.
|
||||||
|
channel, err := w.getWaitingCloseChannel(*outPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match pending sweeps with commitments of the channel for which a bump
|
||||||
|
// is requested. Depending on the commitment state when force closing
|
||||||
|
// the channel we might have up to 3 commitments to consider when
|
||||||
|
// bumping the fee.
|
||||||
|
commitSet := fn.NewSet[chainhash.Hash]()
|
||||||
|
|
||||||
|
if channel.LocalCommitment.CommitTx != nil {
|
||||||
|
localTxID := channel.LocalCommitment.CommitTx.TxHash()
|
||||||
|
commitSet.Add(localTxID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if channel.RemoteCommitment.CommitTx != nil {
|
||||||
|
remoteTxID := channel.RemoteCommitment.CommitTx.TxHash()
|
||||||
|
commitSet.Add(remoteTxID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether there was a dangling commitment at the time the channel
|
||||||
|
// was force closed.
|
||||||
|
remoteCommitDiff, err := channel.RemoteCommitChainTip()
|
||||||
|
if err != nil && !errors.Is(err, channeldb.ErrNoPendingCommit) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if remoteCommitDiff != nil {
|
||||||
|
hash := remoteCommitDiff.Commitment.CommitTx.TxHash()
|
||||||
|
commitSet.Add(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve all of the outputs the UtxoSweeper is currently trying to
|
||||||
|
// sweep.
|
||||||
|
inputsMap, err := w.cfg.Sweeper.PendingInputs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current height so we can calculate the deadline height.
|
||||||
|
_, currentHeight, err := w.cfg.Chain.GetBestBlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to retrieve current height: %w",
|
||||||
|
err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingSweeps := maps.Values(inputsMap)
|
||||||
|
|
||||||
|
// Discard everything except for the anchor sweeps.
|
||||||
|
anchors := fn.Filter(func(sweep *sweep.PendingInputResponse) bool {
|
||||||
|
// Only filter for anchor inputs because these are the only
|
||||||
|
// inputs which can be used to bump a closed unconfirmed
|
||||||
|
// commitment transaction.
|
||||||
|
if sweep.WitnessType != input.CommitmentAnchor &&
|
||||||
|
sweep.WitnessType != input.TaprootAnchorSweepSpend {
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return commitSet.Contains(sweep.OutPoint.Hash)
|
||||||
|
}, pendingSweeps)
|
||||||
|
|
||||||
|
// Filter all relevant anchor sweeps and update the sweep request.
|
||||||
|
for _, anchor := range anchors {
|
||||||
|
// Anchor cpfp bump request are predictable because they are
|
||||||
|
// swept separately hence not batched with other sweeps (they
|
||||||
|
// are marked with the exclusive group flag). Bumping the fee
|
||||||
|
// rate does not create any conflicting fee bump conditions.
|
||||||
|
// Either the rbf requirements are met or the bump is rejected
|
||||||
|
// by the mempool rules.
|
||||||
|
params, existing, err := w.prepareSweepParams(
|
||||||
|
&BumpFeeRequest{
|
||||||
|
Outpoint: lnrpcOutpoint,
|
||||||
|
TargetConf: in.DeadlineDelta,
|
||||||
|
SatPerVbyte: in.StartingFeerate,
|
||||||
|
Immediate: in.Immediate,
|
||||||
|
Budget: in.Budget,
|
||||||
|
}, anchor.OutPoint, currentHeight,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// There might be the case when an anchor sweep is confirmed
|
||||||
|
// between fetching the pending sweeps and preparing the sweep
|
||||||
|
// params. We log this case and proceed.
|
||||||
|
if !existing {
|
||||||
|
log.Errorf("Sweep anchor input(%v) not known to the " +
|
||||||
|
"sweeper subsystem")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.cfg.Sweeper.UpdateParams(anchor.OutPoint, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BumpForceCloseFeeResponse{
|
||||||
|
Status: "Successfully registered anchor-cpfp transaction to" +
|
||||||
|
"bump channel force close transaction",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// sweepNewInput handles the case where an input is seen the first time by the
|
// sweepNewInput handles the case where an input is seen the first time by the
|
||||||
// sweeper. It will fetch the output from the wallet and construct an input and
|
// sweeper. It will fetch the output from the wallet and construct an input and
|
||||||
// offer it to the sweeper.
|
// offer it to the sweeper.
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
|
|||||||
cc.Wallet.Cfg.CoinSelectionStrategy,
|
cc.Wallet.Cfg.CoinSelectionStrategy,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
subCfgValue.FieldByName("ChanStateDB").Set(
|
||||||
|
reflect.ValueOf(chanStateDB),
|
||||||
|
)
|
||||||
|
|
||||||
case *autopilotrpc.Config:
|
case *autopilotrpc.Config:
|
||||||
subCfgValue := extractReflectValue(subCfg)
|
subCfgValue := extractReflectValue(subCfg)
|
||||||
|
|||||||
Reference in new issue
Block a user