Merge pull request #3809 from joostjager/anchor-sweeper
sweep: allow force sweeping of negatively yielding inputs
This commit is contained in:
commit
ae9c6faebf
@ -4,7 +4,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||||
@ -113,7 +112,13 @@ var bumpFeeCommand = cli.Command{
|
|||||||
Note that this command currently doesn't perform any validation checks
|
Note that this command currently doesn't perform any validation checks
|
||||||
on the fee preference being provided. For now, the responsibility of
|
on the fee preference being provided. For now, the responsibility of
|
||||||
ensuring that the new fee preference is sufficient is delegated to the
|
ensuring that the new fee preference is sufficient is delegated to the
|
||||||
user.`,
|
user.
|
||||||
|
|
||||||
|
The force flag enables sweeping of inputs that are negatively yielding.
|
||||||
|
Normally it does not make sense to lose money on sweeping, unless a
|
||||||
|
parent transaction needs to get confirmed and there is only a small
|
||||||
|
output available to attach the child transaction to.
|
||||||
|
`,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.Uint64Flag{
|
cli.Uint64Flag{
|
||||||
Name: "conf_target",
|
Name: "conf_target",
|
||||||
@ -125,6 +130,10 @@ var bumpFeeCommand = cli.Command{
|
|||||||
Usage: "a manual fee expressed in sat/byte that " +
|
Usage: "a manual fee expressed in sat/byte that " +
|
||||||
"should be used when sweeping the output",
|
"should be used when sweeping the output",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "force",
|
||||||
|
Usage: "sweep even if the yield is negative",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: actionDecorator(bumpFee),
|
Action: actionDecorator(bumpFee),
|
||||||
}
|
}
|
||||||
@ -132,7 +141,7 @@ var bumpFeeCommand = cli.Command{
|
|||||||
func bumpFee(ctx *cli.Context) error {
|
func bumpFee(ctx *cli.Context) error {
|
||||||
// Display the command's help message if we do not have the expected
|
// Display the command's help message if we do not have the expected
|
||||||
// number of arguments/flags.
|
// number of arguments/flags.
|
||||||
if ctx.NArg() != 1 || ctx.NumFlags() != 1 {
|
if ctx.NArg() != 1 {
|
||||||
return cli.ShowCommandHelp(ctx, "bumpfee")
|
return cli.ShowCommandHelp(ctx, "bumpfee")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,24 +151,14 @@ func bumpFee(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var confTarget, satPerByte uint32
|
|
||||||
switch {
|
|
||||||
case ctx.IsSet("conf_target") && ctx.IsSet("sat_per_byte"):
|
|
||||||
return fmt.Errorf("either conf_target or sat_per_byte should " +
|
|
||||||
"be set, but not both")
|
|
||||||
case ctx.IsSet("conf_target"):
|
|
||||||
confTarget = uint32(ctx.Uint64("conf_target"))
|
|
||||||
case ctx.IsSet("sat_per_byte"):
|
|
||||||
satPerByte = uint32(ctx.Uint64("sat_per_byte"))
|
|
||||||
}
|
|
||||||
|
|
||||||
client, cleanUp := getWalletClient(ctx)
|
client, cleanUp := getWalletClient(ctx)
|
||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
resp, err := client.BumpFee(context.Background(), &walletrpc.BumpFeeRequest{
|
resp, err := client.BumpFee(context.Background(), &walletrpc.BumpFeeRequest{
|
||||||
Outpoint: protoOutPoint,
|
Outpoint: protoOutPoint,
|
||||||
TargetConf: confTarget,
|
TargetConf: uint32(ctx.Uint64("conf_target")),
|
||||||
SatPerByte: satPerByte,
|
SatPerByte: uint32(ctx.Uint64("sat_per_byte")),
|
||||||
|
Force: ctx.Bool("force"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -11,6 +11,9 @@ type PendingSweep struct {
|
|||||||
SatPerByte uint32 `json:"sat_per_byte"`
|
SatPerByte uint32 `json:"sat_per_byte"`
|
||||||
BroadcastAttempts uint32 `json:"broadcast_attempts"`
|
BroadcastAttempts uint32 `json:"broadcast_attempts"`
|
||||||
NextBroadcastHeight uint32 `json:"next_broadcast_height"`
|
NextBroadcastHeight uint32 `json:"next_broadcast_height"`
|
||||||
|
RequestedSatPerByte uint32 `json:"requested_sat_per_byte"`
|
||||||
|
RequestedConfTarget uint32 `json:"requested_conf_target"`
|
||||||
|
Force bool `json:"force"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPendingSweepFromProto converts the walletrpc.PendingSweep proto type into
|
// NewPendingSweepFromProto converts the walletrpc.PendingSweep proto type into
|
||||||
@ -23,5 +26,8 @@ func NewPendingSweepFromProto(pendingSweep *walletrpc.PendingSweep) *PendingSwee
|
|||||||
SatPerByte: pendingSweep.SatPerByte,
|
SatPerByte: pendingSweep.SatPerByte,
|
||||||
BroadcastAttempts: pendingSweep.BroadcastAttempts,
|
BroadcastAttempts: pendingSweep.BroadcastAttempts,
|
||||||
NextBroadcastHeight: pendingSweep.NextBroadcastHeight,
|
NextBroadcastHeight: pendingSweep.NextBroadcastHeight,
|
||||||
|
RequestedSatPerByte: pendingSweep.RequestedSatPerByte,
|
||||||
|
RequestedConfTarget: pendingSweep.RequestedConfTarget,
|
||||||
|
Force: pendingSweep.Force,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -532,6 +532,14 @@ type PendingSweep struct {
|
|||||||
//The next height of the chain at which we'll attempt to broadcast the
|
//The next height of the chain at which we'll attempt to broadcast the
|
||||||
//sweep transaction of the output.
|
//sweep transaction of the output.
|
||||||
NextBroadcastHeight uint32 `protobuf:"varint,6,opt,name=next_broadcast_height,proto3" json:"next_broadcast_height,omitempty"`
|
NextBroadcastHeight uint32 `protobuf:"varint,6,opt,name=next_broadcast_height,proto3" json:"next_broadcast_height,omitempty"`
|
||||||
|
// The requested confirmation target for this output.
|
||||||
|
RequestedConfTarget uint32 `protobuf:"varint,8,opt,name=requested_conf_target,proto3" json:"requested_conf_target,omitempty"`
|
||||||
|
// The requested fee rate, expressed in sat/byte, for this output.
|
||||||
|
RequestedSatPerByte uint32 `protobuf:"varint,9,opt,name=requested_sat_per_byte,proto3" json:"requested_sat_per_byte,omitempty"`
|
||||||
|
//*
|
||||||
|
//Whether this input must be force-swept. This means that it is swept even
|
||||||
|
//if it has a negative yield.
|
||||||
|
Force bool `protobuf:"varint,7,opt,name=force,proto3" json:"force,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -604,6 +612,27 @@ func (m *PendingSweep) GetNextBroadcastHeight() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *PendingSweep) GetRequestedConfTarget() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.RequestedConfTarget
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PendingSweep) GetRequestedSatPerByte() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.RequestedSatPerByte
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PendingSweep) GetForce() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Force
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type PendingSweepsRequest struct {
|
type PendingSweepsRequest struct {
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
@ -685,6 +714,10 @@ type BumpFeeRequest struct {
|
|||||||
//The fee rate, expressed in sat/byte, that should be used to spend the input
|
//The fee rate, expressed in sat/byte, that should be used to spend the input
|
||||||
//with.
|
//with.
|
||||||
SatPerByte uint32 `protobuf:"varint,3,opt,name=sat_per_byte,proto3" json:"sat_per_byte,omitempty"`
|
SatPerByte uint32 `protobuf:"varint,3,opt,name=sat_per_byte,proto3" json:"sat_per_byte,omitempty"`
|
||||||
|
//*
|
||||||
|
//Whether this input must be force-swept. This means that it is swept even
|
||||||
|
//if it has a negative yield.
|
||||||
|
Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -736,6 +769,13 @@ func (m *BumpFeeRequest) GetSatPerByte() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *BumpFeeRequest) GetForce() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.Force
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type BumpFeeResponse struct {
|
type BumpFeeResponse struct {
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
@ -788,68 +828,72 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("walletrpc/walletkit.proto", fileDescriptor_6cc6942ac78249e5) }
|
func init() { proto.RegisterFile("walletrpc/walletkit.proto", fileDescriptor_6cc6942ac78249e5) }
|
||||||
|
|
||||||
var fileDescriptor_6cc6942ac78249e5 = []byte{
|
var fileDescriptor_6cc6942ac78249e5 = []byte{
|
||||||
// 976 bytes of a gzipped FileDescriptorProto
|
// 1026 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xed, 0x6e, 0xe2, 0x46,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0x6d, 0x6f, 0xe2, 0xc6,
|
||||||
0x14, 0x2d, 0x21, 0x61, 0xc3, 0x05, 0x12, 0x67, 0x08, 0x89, 0x97, 0xcd, 0x6e, 0xa8, 0xfb, 0x21,
|
0x13, 0xff, 0x13, 0x12, 0x02, 0x03, 0x24, 0xce, 0xe6, 0xc9, 0xc7, 0xe5, 0x2e, 0xfc, 0xdd, 0x07,
|
||||||
0xd4, 0x56, 0xa0, 0x66, 0xdb, 0xaa, 0x6a, 0x7f, 0xb4, 0x59, 0x70, 0x44, 0xc4, 0x87, 0xa9, 0xed,
|
0xa1, 0xb6, 0x02, 0x35, 0xd7, 0x9e, 0xaa, 0xf6, 0x45, 0x9b, 0x23, 0x8e, 0x12, 0x41, 0x30, 0xb5,
|
||||||
0x6c, 0xba, 0x55, 0xa5, 0x91, 0x81, 0x59, 0xb0, 0x00, 0xdb, 0x3b, 0x1e, 0x0a, 0xfc, 0x6d, 0x9f,
|
0x9d, 0x4b, 0xaf, 0xaa, 0xb4, 0x72, 0x60, 0x03, 0x16, 0x60, 0xfb, 0xd6, 0x4b, 0x81, 0xb7, 0xfd,
|
||||||
0xa4, 0xaf, 0xd1, 0xa7, 0xab, 0x3c, 0xb6, 0xc9, 0x18, 0x92, 0x4a, 0xfd, 0x15, 0xe7, 0x9c, 0x73,
|
0x16, 0xfd, 0x00, 0xfd, 0x12, 0xfd, 0x74, 0x95, 0xd7, 0x0f, 0x59, 0xf3, 0x50, 0xa9, 0xaf, 0x82,
|
||||||
0xcf, 0xdc, 0xb9, 0x33, 0x73, 0x04, 0x3c, 0x5f, 0x5a, 0xb3, 0x19, 0x61, 0xd4, 0x1b, 0xd6, 0xc3,
|
0x7f, 0xbf, 0xdf, 0xcc, 0xce, 0xce, 0xcc, 0xce, 0x04, 0x5e, 0xcc, 0xac, 0xf1, 0x98, 0x30, 0xea,
|
||||||
0xaf, 0xa9, 0xcd, 0x6a, 0x1e, 0x75, 0x99, 0x8b, 0xb2, 0x1b, 0xaa, 0x9c, 0xa5, 0xde, 0x30, 0x44,
|
0xf5, 0x1a, 0xe1, 0xaf, 0x91, 0xcd, 0xea, 0x1e, 0x75, 0x99, 0x8b, 0x0a, 0x09, 0x55, 0x29, 0x50,
|
||||||
0xcb, 0xa7, 0xbe, 0x3d, 0x76, 0x02, 0x79, 0xf0, 0x97, 0xd0, 0x10, 0x55, 0x7e, 0x81, 0x4c, 0x9b,
|
0xaf, 0x17, 0xa2, 0x95, 0x23, 0xdf, 0x1e, 0x38, 0x81, 0x3c, 0xf8, 0x4b, 0x68, 0x88, 0x2a, 0x3f,
|
||||||
0xac, 0x75, 0xf2, 0x01, 0x55, 0x41, 0x9a, 0x92, 0x35, 0x7e, 0x6f, 0x3b, 0x63, 0x42, 0xb1, 0x47,
|
0x43, 0xae, 0x45, 0x16, 0x3a, 0xf9, 0x88, 0x6a, 0x20, 0x8d, 0xc8, 0x02, 0x3f, 0xd9, 0xce, 0x80,
|
||||||
0x6d, 0x87, 0xc9, 0xa9, 0x4a, 0xaa, 0x7a, 0xa0, 0x1f, 0x4d, 0xc9, 0xfa, 0x86, 0xc3, 0xfd, 0x00,
|
0x50, 0xec, 0x51, 0xdb, 0x61, 0x72, 0xa6, 0x9a, 0xa9, 0xed, 0xe8, 0x7b, 0x23, 0xb2, 0xb8, 0xe6,
|
||||||
0x45, 0x2f, 0x01, 0xb8, 0xd2, 0x9a, 0xdb, 0xb3, 0xb5, 0xbc, 0xc7, 0x35, 0xd9, 0x40, 0xc3, 0x01,
|
0x70, 0x37, 0x40, 0xd1, 0x2b, 0x00, 0xae, 0xb4, 0x26, 0xf6, 0x78, 0x21, 0x6f, 0x71, 0x4d, 0x21,
|
||||||
0xa5, 0x00, 0xb9, 0xeb, 0xd1, 0x88, 0xea, 0xe4, 0xc3, 0x82, 0xf8, 0x4c, 0x51, 0x20, 0x1f, 0xfe,
|
0xd0, 0x70, 0x40, 0x29, 0x43, 0xf1, 0xb2, 0xdf, 0xa7, 0x3a, 0xf9, 0x38, 0x25, 0x3e, 0x53, 0x14,
|
||||||
0xeb, 0x7b, 0xae, 0xe3, 0x13, 0x84, 0x60, 0xdf, 0x1a, 0x8d, 0x28, 0xf7, 0xce, 0xea, 0xfc, 0x5b,
|
0x28, 0x85, 0x9f, 0xbe, 0xe7, 0x3a, 0x3e, 0x41, 0x08, 0xb6, 0xad, 0x7e, 0x9f, 0x72, 0xdf, 0x05,
|
||||||
0xf9, 0x14, 0x72, 0x26, 0xb5, 0x1c, 0xdf, 0x1a, 0x32, 0xdb, 0x75, 0x50, 0x09, 0x32, 0x6c, 0x85,
|
0x9d, 0xff, 0x56, 0x3e, 0x85, 0xa2, 0x49, 0x2d, 0xc7, 0xb7, 0x7a, 0xcc, 0x76, 0x1d, 0x74, 0x0c,
|
||||||
0x27, 0x64, 0xc5, 0x45, 0x79, 0xfd, 0x80, 0xad, 0x5a, 0x64, 0xa5, 0x7c, 0x07, 0xc7, 0xfd, 0xc5,
|
0x39, 0x36, 0xc7, 0x43, 0x32, 0xe7, 0xa2, 0x92, 0xbe, 0xc3, 0xe6, 0x37, 0x64, 0xae, 0xbc, 0x85,
|
||||||
0x60, 0x66, 0xfb, 0x93, 0x8d, 0xd9, 0x27, 0x50, 0xf0, 0x42, 0x08, 0x13, 0x4a, 0xdd, 0xd8, 0x35,
|
0xfd, 0xee, 0xf4, 0x71, 0x6c, 0xfb, 0xc3, 0xc4, 0xd9, 0x27, 0x50, 0xf6, 0x42, 0x08, 0x13, 0x4a,
|
||||||
0x1f, 0x81, 0x6a, 0x80, 0x29, 0xbf, 0x03, 0x32, 0x88, 0x33, 0xd2, 0x16, 0xcc, 0x5b, 0x30, 0x3f,
|
0xdd, 0xd8, 0x6b, 0x29, 0x02, 0xd5, 0x00, 0x53, 0x7e, 0x03, 0x64, 0x10, 0xa7, 0xaf, 0x4d, 0x99,
|
||||||
0xea, 0x0b, 0x5d, 0x00, 0xf8, 0x16, 0xc3, 0x1e, 0xa1, 0x78, 0xba, 0xe4, 0x75, 0x69, 0xfd, 0xd0,
|
0x37, 0x65, 0x7e, 0x14, 0x17, 0x3a, 0x03, 0xf0, 0x2d, 0x86, 0x3d, 0x42, 0xf1, 0x68, 0xc6, 0xed,
|
||||||
0xb7, 0x58, 0x9f, 0xd0, 0xf6, 0x12, 0x55, 0xe1, 0x99, 0x1b, 0xea, 0xe5, 0xbd, 0x4a, 0xba, 0x9a,
|
0xb2, 0x7a, 0xde, 0xb7, 0x58, 0x97, 0xd0, 0xd6, 0x0c, 0xd5, 0x60, 0xd7, 0x0d, 0xf5, 0xf2, 0x56,
|
||||||
0xbb, 0x3a, 0xaa, 0x45, 0xf3, 0xab, 0x99, 0x2b, 0x6d, 0xc1, 0xf4, 0x98, 0x56, 0xbe, 0x82, 0x62,
|
0x35, 0x5b, 0x2b, 0x5e, 0xec, 0xd5, 0xa3, 0xfc, 0xd5, 0xcd, 0xb9, 0x36, 0x65, 0x7a, 0x4c, 0x2b,
|
||||||
0xc2, 0x3d, 0xea, 0xac, 0x04, 0x19, 0x6a, 0x2d, 0x31, 0xdb, 0xec, 0x81, 0x5a, 0x4b, 0x73, 0xa5,
|
0x5f, 0xc1, 0x61, 0xca, 0x7b, 0x14, 0xd9, 0x31, 0xe4, 0xa8, 0x35, 0xc3, 0x2c, 0xb9, 0x03, 0xb5,
|
||||||
0x7c, 0x0b, 0x48, 0xf5, 0x99, 0x3d, 0xb7, 0x18, 0xb9, 0x21, 0x24, 0xee, 0xe5, 0x12, 0x72, 0x43,
|
0x66, 0xe6, 0x5c, 0xf9, 0x16, 0x90, 0xea, 0x33, 0x7b, 0x62, 0x31, 0x72, 0x4d, 0x48, 0x1c, 0xcb,
|
||||||
0xd7, 0x79, 0x8f, 0x99, 0x45, 0xc7, 0x24, 0x1e, 0x3b, 0x04, 0x90, 0xc9, 0x11, 0xe5, 0x35, 0x14,
|
0x39, 0x14, 0x7b, 0xae, 0xf3, 0x84, 0x99, 0x45, 0x07, 0x24, 0x4e, 0x3b, 0x04, 0x90, 0xc9, 0x11,
|
||||||
0x13, 0x65, 0xd1, 0x22, 0xff, 0xb9, 0x07, 0xe5, 0xef, 0x3d, 0xc8, 0xf7, 0x89, 0x33, 0xb2, 0x9d,
|
0xe5, 0x0d, 0x1c, 0xa6, 0xcc, 0xa2, 0x43, 0xfe, 0xf5, 0x0e, 0xca, 0x5f, 0x59, 0x28, 0x75, 0x89,
|
||||||
0xb1, 0xb1, 0x24, 0xc4, 0x43, 0x5f, 0xc2, 0x61, 0xd0, 0xb5, 0x1b, 0x1f, 0x6d, 0xee, 0xea, 0xb8,
|
0xd3, 0xb7, 0x9d, 0x81, 0x31, 0x23, 0xc4, 0x43, 0x5f, 0x42, 0x3e, 0x88, 0xda, 0x8d, 0x4b, 0x5b,
|
||||||
0x36, 0xe3, 0x7b, 0xd2, 0x16, 0xac, 0x1f, 0xc0, 0xfa, 0x46, 0x80, 0x7e, 0x80, 0xfc, 0xd2, 0x66,
|
0xbc, 0xd8, 0xaf, 0x8f, 0xf9, 0x9d, 0xb4, 0x29, 0xeb, 0x06, 0xb0, 0x9e, 0x08, 0xd0, 0xf7, 0x50,
|
||||||
0x0e, 0xf1, 0x7d, 0xcc, 0xd6, 0x1e, 0xe1, 0xe7, 0x7c, 0x74, 0x75, 0x56, 0xdb, 0x5c, 0xae, 0xda,
|
0x9a, 0xd9, 0xcc, 0x21, 0xbe, 0x8f, 0xd9, 0xc2, 0x23, 0xbc, 0xce, 0x7b, 0x17, 0x27, 0xf5, 0xa4,
|
||||||
0x7d, 0x48, 0x9b, 0x6b, 0x8f, 0xe8, 0x09, 0x2d, 0x7a, 0x05, 0x60, 0xcd, 0xdd, 0x85, 0xc3, 0xb0,
|
0xb9, 0xea, 0x0f, 0x21, 0x6d, 0x2e, 0x3c, 0xa2, 0xa7, 0xb4, 0xe8, 0x35, 0x80, 0x35, 0x71, 0xa7,
|
||||||
0x6f, 0x31, 0x39, 0x5d, 0x49, 0x55, 0x0b, 0xba, 0x80, 0x20, 0x05, 0xf2, 0x71, 0xdf, 0x83, 0x35,
|
0x0e, 0xc3, 0xbe, 0xc5, 0xe4, 0x6c, 0x35, 0x53, 0x2b, 0xeb, 0x02, 0x82, 0x14, 0x28, 0xc5, 0x71,
|
||||||
0x23, 0xf2, 0x3e, 0x57, 0x24, 0x30, 0x54, 0x03, 0x34, 0xa0, 0xae, 0x35, 0x1a, 0x5a, 0x3e, 0xc3,
|
0x3f, 0x2e, 0x18, 0x91, 0xb7, 0xb9, 0x22, 0x85, 0xa1, 0x3a, 0xa0, 0x47, 0xea, 0x5a, 0xfd, 0x9e,
|
||||||
0x16, 0x63, 0x64, 0xee, 0x31, 0x5f, 0x3e, 0xe0, 0xca, 0x47, 0x18, 0xf4, 0x0d, 0x94, 0x1c, 0xb2,
|
0xe5, 0x33, 0x6c, 0x31, 0x46, 0x26, 0x1e, 0xf3, 0xe5, 0x1d, 0xae, 0x5c, 0xc3, 0xa0, 0x6f, 0xe0,
|
||||||
0x62, 0xf8, 0x81, 0x9a, 0x10, 0x7b, 0x3c, 0x61, 0x72, 0x86, 0x97, 0x3c, 0x4e, 0x2a, 0x67, 0x70,
|
0xd8, 0x21, 0x73, 0x86, 0x9f, 0xa9, 0x21, 0xb1, 0x07, 0x43, 0x26, 0xe7, 0xb8, 0xc9, 0x7a, 0x32,
|
||||||
0x2a, 0x8e, 0x28, 0xbe, 0x1d, 0xca, 0xaf, 0x50, 0xda, 0xc2, 0xa3, 0x91, 0xff, 0x04, 0x47, 0x5e,
|
0xb0, 0xa2, 0x61, 0x11, 0x48, 0x1f, 0x8b, 0x35, 0xc8, 0x87, 0x56, 0x6b, 0x49, 0xf4, 0x16, 0x4e,
|
||||||
0x48, 0x60, 0x9f, 0x33, 0x72, 0x8a, 0xdf, 0x8f, 0x73, 0x61, 0x30, 0x62, 0xa5, 0xbe, 0x25, 0x57,
|
0x9e, 0x89, 0xd4, 0x4d, 0x0a, 0xdc, 0x6c, 0x03, 0x8b, 0x8e, 0x60, 0xe7, 0xc9, 0xa5, 0x3d, 0x22,
|
||||||
0xfe, 0x4a, 0xc1, 0xd1, 0x9b, 0xc5, 0xdc, 0x13, 0x8e, 0xff, 0x7f, 0x9d, 0x4b, 0x05, 0x72, 0xe1,
|
0xef, 0x56, 0x33, 0xb5, 0xbc, 0x1e, 0x7e, 0x28, 0x27, 0x70, 0x24, 0x96, 0x29, 0xee, 0x50, 0xe5,
|
||||||
0x35, 0xc1, 0xc1, 0xfd, 0xe0, 0xc7, 0x52, 0xd0, 0x45, 0x68, 0x67, 0xba, 0xe9, 0xdd, 0xe9, 0x2a,
|
0x17, 0x38, 0x5e, 0xc2, 0xa3, 0xb2, 0xff, 0x08, 0x7b, 0x5e, 0x48, 0x60, 0x9f, 0x33, 0x72, 0x86,
|
||||||
0x27, 0x70, 0xbc, 0x69, 0x22, 0xdc, 0xd9, 0x17, 0x7f, 0xa6, 0x21, 0x27, 0x1c, 0x29, 0x2a, 0xc2,
|
0xf7, 0xe8, 0xa9, 0x50, 0x1c, 0xd1, 0x52, 0x5f, 0x92, 0x2b, 0x7f, 0x66, 0x60, 0xef, 0xdd, 0x74,
|
||||||
0xf1, 0x5d, 0xaf, 0xdd, 0xd3, 0xee, 0x7b, 0xf8, 0xfe, 0xd6, 0xec, 0xa9, 0x86, 0x21, 0x7d, 0x84,
|
0xe2, 0x09, 0x2d, 0xf8, 0x9f, 0x7a, 0xa3, 0x0a, 0xc5, 0x30, 0x13, 0x3c, 0x2b, 0xbc, 0x35, 0xca,
|
||||||
0x64, 0x38, 0x6d, 0x68, 0xdd, 0xee, 0xad, 0xd9, 0x55, 0x7b, 0x26, 0x36, 0x6f, 0xbb, 0x2a, 0xee,
|
0xba, 0x08, 0xad, 0x54, 0x38, 0xbb, 0xa6, 0xc2, 0x49, 0x36, 0xb6, 0xc5, 0x6c, 0x1c, 0xc0, 0x7e,
|
||||||
0x68, 0x8d, 0xb6, 0x94, 0x42, 0xe7, 0x50, 0x14, 0x98, 0x9e, 0x86, 0x9b, 0x6a, 0xe7, 0xfa, 0x9d,
|
0x12, 0x5a, 0x78, 0xdf, 0x2f, 0xfe, 0xc8, 0x42, 0x51, 0x68, 0x36, 0x74, 0x08, 0xfb, 0xf7, 0x9d,
|
||||||
0xb4, 0x87, 0x4a, 0x70, 0x22, 0x10, 0xba, 0xfa, 0x56, 0x6b, 0xab, 0x52, 0x3a, 0xd0, 0xb7, 0xcc,
|
0x56, 0x47, 0x7b, 0xe8, 0xe0, 0x87, 0x5b, 0xb3, 0xa3, 0x1a, 0x86, 0xf4, 0x3f, 0x24, 0xc3, 0x51,
|
||||||
0x4e, 0x03, 0x6b, 0x37, 0x37, 0xaa, 0xae, 0x36, 0x63, 0x62, 0x3f, 0x58, 0x82, 0x13, 0xd7, 0x8d,
|
0x53, 0xbb, 0xbb, 0xbb, 0x35, 0xef, 0xd4, 0x8e, 0x89, 0xcd, 0xdb, 0x3b, 0x15, 0xb7, 0xb5, 0x66,
|
||||||
0x86, 0xda, 0x37, 0x1f, 0x98, 0x03, 0xf4, 0x19, 0x7c, 0x9c, 0x28, 0x09, 0x96, 0xd7, 0xee, 0x4c,
|
0x4b, 0xca, 0xa0, 0x53, 0x38, 0x14, 0x98, 0x8e, 0x86, 0xaf, 0xd4, 0xf6, 0xe5, 0x07, 0x69, 0x0b,
|
||||||
0x6c, 0xa8, 0x0d, 0xad, 0xd7, 0xc4, 0x1d, 0xf5, 0xad, 0xda, 0x91, 0x32, 0xe8, 0x73, 0x50, 0x92,
|
0x1d, 0xc3, 0x81, 0x40, 0xe8, 0xea, 0x7b, 0xad, 0xa5, 0x4a, 0xd9, 0x40, 0x7f, 0x63, 0xb6, 0x9b,
|
||||||
0x06, 0xc6, 0x5d, 0xa3, 0xa1, 0x1a, 0x46, 0x52, 0xf7, 0x0c, 0x5d, 0xc2, 0x8b, 0xad, 0x0e, 0xba,
|
0x58, 0xbb, 0xbe, 0x56, 0x75, 0xf5, 0x2a, 0x26, 0xb6, 0x83, 0x23, 0x38, 0x71, 0xd9, 0x6c, 0xaa,
|
||||||
0x9a, 0xa9, 0xc6, 0xae, 0xd2, 0x21, 0xaa, 0xc0, 0xc5, 0x76, 0x27, 0x5c, 0x11, 0xf9, 0x49, 0x59,
|
0x5d, 0xf3, 0x99, 0xd9, 0x41, 0x9f, 0xc1, 0xff, 0x53, 0x26, 0xc1, 0xf1, 0xda, 0xbd, 0x89, 0x0d,
|
||||||
0x74, 0x01, 0x32, 0x57, 0x88, 0xce, 0x71, 0xbf, 0x80, 0x4e, 0x41, 0x8a, 0x26, 0x87, 0xdb, 0xea,
|
0xb5, 0xa9, 0x75, 0xae, 0x70, 0x5b, 0x7d, 0xaf, 0xb6, 0xa5, 0x1c, 0xfa, 0x1c, 0x94, 0xb4, 0x03,
|
||||||
0x3b, 0xdc, 0xba, 0x36, 0x5a, 0x52, 0x0e, 0xbd, 0x80, 0xf3, 0x9e, 0x6a, 0x04, 0x76, 0x3b, 0x64,
|
0xe3, 0xbe, 0xd9, 0x54, 0x0d, 0x23, 0xad, 0xdb, 0x45, 0xe7, 0xf0, 0x72, 0x29, 0x82, 0x3b, 0xcd,
|
||||||
0xfe, 0xea, 0x9f, 0x7d, 0xc8, 0xde, 0xf3, 0x8b, 0xd4, 0xb6, 0x83, 0x37, 0x58, 0x68, 0x12, 0x6a,
|
0x54, 0x63, 0xaf, 0x52, 0x1e, 0x55, 0xe1, 0x6c, 0x39, 0x12, 0xae, 0x88, 0xfc, 0x49, 0x05, 0x74,
|
||||||
0xff, 0x41, 0x7a, 0x64, 0xc5, 0xda, 0x64, 0x8d, 0x4e, 0x84, 0x5b, 0x16, 0xe6, 0x76, 0xf9, 0x6c,
|
0x06, 0x32, 0x57, 0x88, 0x9e, 0xe3, 0x78, 0x01, 0x1d, 0x81, 0x14, 0x65, 0x0e, 0xb7, 0xd4, 0x0f,
|
||||||
0x13, 0x4c, 0x6d, 0xb2, 0x6e, 0x12, 0x7f, 0x48, 0x6d, 0x8f, 0xb9, 0x14, 0x7d, 0x0f, 0xd9, 0xb0,
|
0xf8, 0xe6, 0xd2, 0xb8, 0x91, 0x8a, 0xe8, 0x25, 0x9c, 0x76, 0x54, 0x23, 0x70, 0xb7, 0x42, 0x96,
|
||||||
0x36, 0xa8, 0x2b, 0x8a, 0xa2, 0x8e, 0x3b, 0xb4, 0x98, 0x4b, 0x9f, 0xac, 0xfc, 0x11, 0x0e, 0x83,
|
0x2e, 0xfe, 0xde, 0x86, 0xc2, 0x03, 0x6f, 0xaf, 0x96, 0x1d, 0x4c, 0x87, 0xf2, 0x15, 0xa1, 0xf6,
|
||||||
0xf5, 0x82, 0xd4, 0x46, 0xe2, 0x7b, 0x17, 0x52, 0xbd, 0x7c, 0xbe, 0x83, 0x47, 0xef, 0xa3, 0x05,
|
0xef, 0xa4, 0x43, 0xe6, 0xac, 0x45, 0x16, 0xe8, 0x40, 0xe8, 0xbd, 0x70, 0xa3, 0x54, 0x4e, 0x92,
|
||||||
0x28, 0x0a, 0x69, 0x31, 0xd1, 0x45, 0x1b, 0x01, 0x2f, 0x97, 0xc5, 0x57, 0xb3, 0x95, 0xed, 0x1d,
|
0x91, 0xd9, 0x22, 0x8b, 0x2b, 0xe2, 0xf7, 0xa8, 0xed, 0x31, 0x97, 0xa2, 0xef, 0xa0, 0x10, 0xda,
|
||||||
0xc8, 0x09, 0xc1, 0x8a, 0x5e, 0x0a, 0xd2, 0xdd, 0x38, 0x2f, 0xbf, 0x7a, 0x8a, 0x7e, 0x70, 0x13,
|
0x06, 0x76, 0x87, 0xa2, 0xa8, 0xed, 0xf6, 0x2c, 0xe6, 0xd2, 0x8d, 0x96, 0x3f, 0x40, 0x3e, 0x38,
|
||||||
0x12, 0x34, 0xe1, 0xb6, 0x1b, 0xc8, 0x09, 0xb7, 0xc7, 0x82, 0x57, 0x87, 0x42, 0x22, 0x1e, 0xd0,
|
0x2f, 0xd8, 0x27, 0x48, 0x9c, 0x44, 0xc2, 0xbe, 0xa9, 0x9c, 0xae, 0xe0, 0xd1, 0xab, 0xb9, 0x01,
|
||||||
0xe5, 0x13, 0xcf, 0x7f, 0xd3, 0x5f, 0xe5, 0x69, 0x41, 0xe4, 0xf9, 0x33, 0x3c, 0x8b, 0x9e, 0x24,
|
0x14, 0xad, 0x0f, 0x71, 0xd7, 0x88, 0x6e, 0x04, 0xbc, 0x52, 0x11, 0xdf, 0xd2, 0xd2, 0xd6, 0x69,
|
||||||
0x7a, 0x2e, 0x88, 0x93, 0x59, 0x91, 0x98, 0xd8, 0xd6, 0x0b, 0x7e, 0xf3, 0xf5, 0x6f, 0xf5, 0xb1,
|
0x43, 0x51, 0x18, 0xf9, 0xe8, 0x95, 0x20, 0x5d, 0x5d, 0x34, 0x95, 0xd7, 0x9b, 0xe8, 0x67, 0x6f,
|
||||||
0xcd, 0x26, 0x8b, 0x41, 0x6d, 0xe8, 0xce, 0xeb, 0xb3, 0x20, 0xe0, 0x1c, 0xdb, 0x19, 0x3b, 0x84,
|
0xc2, 0x6c, 0x4f, 0x79, 0x5b, 0x5d, 0x15, 0x29, 0x6f, 0xeb, 0x56, 0x82, 0x0e, 0xe5, 0xd4, 0xd0,
|
||||||
0x2d, 0x5d, 0x3a, 0xad, 0xcf, 0x9c, 0x51, 0x9d, 0xc7, 0x4a, 0x7d, 0x63, 0x31, 0xc8, 0xf0, 0x9f,
|
0x40, 0xe7, 0x1b, 0x86, 0x42, 0x12, 0x5f, 0x75, 0xb3, 0x20, 0xf2, 0xf9, 0x13, 0xec, 0x46, 0x4f,
|
||||||
0x01, 0xaf, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xd3, 0xc7, 0x77, 0x11, 0x4f, 0x08, 0x00, 0x00,
|
0x12, 0xbd, 0x10, 0xc4, 0xe9, 0x09, 0x92, 0xca, 0xd8, 0xd2, 0x0b, 0x7e, 0xf7, 0xf5, 0xaf, 0x8d,
|
||||||
|
0x81, 0xcd, 0x86, 0xd3, 0xc7, 0x7a, 0xcf, 0x9d, 0x34, 0xc6, 0xc1, 0xe8, 0x75, 0x6c, 0x67, 0xe0,
|
||||||
|
0x10, 0x36, 0x73, 0xe9, 0xa8, 0x31, 0x76, 0xfa, 0x0d, 0x3e, 0x6c, 0x1a, 0x89, 0x8b, 0xc7, 0x1c,
|
||||||
|
0xff, 0x07, 0xe5, 0xcd, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5a, 0x4e, 0xaf, 0xaf, 0xe9, 0x08,
|
||||||
|
0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -191,6 +191,18 @@ message PendingSweep {
|
|||||||
sweep transaction of the output.
|
sweep transaction of the output.
|
||||||
*/
|
*/
|
||||||
uint32 next_broadcast_height = 6 [json_name = "next_broadcast_height"];
|
uint32 next_broadcast_height = 6 [json_name = "next_broadcast_height"];
|
||||||
|
|
||||||
|
// The requested confirmation target for this output.
|
||||||
|
uint32 requested_conf_target = 8 [json_name = "requested_conf_target"];
|
||||||
|
|
||||||
|
// The requested fee rate, expressed in sat/byte, for this output.
|
||||||
|
uint32 requested_sat_per_byte = 9 [json_name = "requested_sat_per_byte"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
Whether this input must be force-swept. This means that it is swept even
|
||||||
|
if it has a negative yield.
|
||||||
|
*/
|
||||||
|
bool force = 7 [json_name = "force"];
|
||||||
}
|
}
|
||||||
|
|
||||||
message PendingSweepsRequest {
|
message PendingSweepsRequest {
|
||||||
@ -215,6 +227,12 @@ message BumpFeeRequest {
|
|||||||
with.
|
with.
|
||||||
*/
|
*/
|
||||||
uint32 sat_per_byte = 3 [json_name = "sat_per_byte"];
|
uint32 sat_per_byte = 3 [json_name = "sat_per_byte"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
Whether this input must be force-swept. This means that it is swept even
|
||||||
|
if it has a negative yield.
|
||||||
|
*/
|
||||||
|
bool force = 4 [json_name = "force"];
|
||||||
}
|
}
|
||||||
|
|
||||||
message BumpFeeResponse {
|
message BumpFeeResponse {
|
||||||
|
@ -404,6 +404,9 @@ func (w *WalletKit) PendingSweeps(ctx context.Context,
|
|||||||
broadcastAttempts := uint32(pendingInput.BroadcastAttempts)
|
broadcastAttempts := uint32(pendingInput.BroadcastAttempts)
|
||||||
nextBroadcastHeight := uint32(pendingInput.NextBroadcastHeight)
|
nextBroadcastHeight := uint32(pendingInput.NextBroadcastHeight)
|
||||||
|
|
||||||
|
requestedFee := pendingInput.Params.Fee
|
||||||
|
requestedFeeRate := uint32(requestedFee.FeeRate.FeePerKVByte() / 1000)
|
||||||
|
|
||||||
rpcPendingSweeps = append(rpcPendingSweeps, &PendingSweep{
|
rpcPendingSweeps = append(rpcPendingSweeps, &PendingSweep{
|
||||||
Outpoint: op,
|
Outpoint: op,
|
||||||
WitnessType: witnessType,
|
WitnessType: witnessType,
|
||||||
@ -411,6 +414,9 @@ func (w *WalletKit) PendingSweeps(ctx context.Context,
|
|||||||
SatPerByte: satPerByte,
|
SatPerByte: satPerByte,
|
||||||
BroadcastAttempts: broadcastAttempts,
|
BroadcastAttempts: broadcastAttempts,
|
||||||
NextBroadcastHeight: nextBroadcastHeight,
|
NextBroadcastHeight: nextBroadcastHeight,
|
||||||
|
RequestedSatPerByte: requestedFeeRate,
|
||||||
|
RequestedConfTarget: requestedFee.ConfTarget,
|
||||||
|
Force: pendingInput.Params.Force,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,7 +486,12 @@ func (w *WalletKit) BumpFee(ctx context.Context,
|
|||||||
// bump its fee, which will result in a replacement transaction (RBF)
|
// bump its fee, which will result in a replacement transaction (RBF)
|
||||||
// being broadcast. If it is not aware of the input however,
|
// being broadcast. If it is not aware of the input however,
|
||||||
// lnwallet.ErrNotMine is returned.
|
// lnwallet.ErrNotMine is returned.
|
||||||
_, err = w.cfg.Sweeper.BumpFee(*op, feePreference)
|
params := sweep.Params{
|
||||||
|
Fee: feePreference,
|
||||||
|
Force: in.Force,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.cfg.Sweeper.UpdateParams(*op, params)
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
return &BumpFeeResponse{}, nil
|
return &BumpFeeResponse{}, nil
|
||||||
|
@ -67,6 +67,15 @@ type Params struct {
|
|||||||
// swept. If a confirmation target is specified, then we'll map it into
|
// swept. If a confirmation target is specified, then we'll map it into
|
||||||
// a fee rate whenever we attempt to cluster inputs for a sweep.
|
// a fee rate whenever we attempt to cluster inputs for a sweep.
|
||||||
Fee FeePreference
|
Fee FeePreference
|
||||||
|
|
||||||
|
// Force indicates whether the input should be swept regardless of
|
||||||
|
// whether it is economical to do so.
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable interpretation of the sweep parameters.
|
||||||
|
func (p Params) String() string {
|
||||||
|
return fmt.Sprintf("fee=%v, force=%v", p.Fee, p.Force)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pendingInput is created when an input reaches the main loop for the first
|
// pendingInput is created when an input reaches the main loop for the first
|
||||||
@ -146,19 +155,22 @@ type PendingInput struct {
|
|||||||
// NextBroadcastHeight is the next height of the chain at which we'll
|
// NextBroadcastHeight is the next height of the chain at which we'll
|
||||||
// attempt to broadcast a transaction sweeping the input.
|
// attempt to broadcast a transaction sweeping the input.
|
||||||
NextBroadcastHeight uint32
|
NextBroadcastHeight uint32
|
||||||
|
|
||||||
|
// Params contains the sweep parameters for this pending request.
|
||||||
|
Params Params
|
||||||
}
|
}
|
||||||
|
|
||||||
// bumpFeeReq is an internal message we'll use to represent an external caller's
|
// updateReq is an internal message we'll use to represent an external caller's
|
||||||
// intent to bump the fee rate of a given input.
|
// intent to update the sweep parameters of a given input.
|
||||||
type bumpFeeReq struct {
|
type updateReq struct {
|
||||||
input wire.OutPoint
|
input wire.OutPoint
|
||||||
feePreference FeePreference
|
params Params
|
||||||
responseChan chan *bumpFeeResp
|
responseChan chan *updateResp
|
||||||
}
|
}
|
||||||
|
|
||||||
// bumpFeeResp is an internal message we'll use to hand off the response of a
|
// updateResp is an internal message we'll use to hand off the response of a
|
||||||
// bumpFeeReq from the UtxoSweeper's main event loop back to the caller.
|
// updateReq from the UtxoSweeper's main event loop back to the caller.
|
||||||
type bumpFeeResp struct {
|
type updateResp struct {
|
||||||
resultChan chan Result
|
resultChan chan Result
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
@ -178,9 +190,9 @@ type UtxoSweeper struct {
|
|||||||
// UtxoSweeper is attempting to sweep.
|
// UtxoSweeper is attempting to sweep.
|
||||||
pendingSweepsReqs chan *pendingSweepsReq
|
pendingSweepsReqs chan *pendingSweepsReq
|
||||||
|
|
||||||
// bumpFeeReqs is a channel that will be sent requests by external
|
// updateReqs is a channel that will be sent requests by external
|
||||||
// callers who wish to bump the fee rate of a given input.
|
// callers who wish to bump the fee rate of a given input.
|
||||||
bumpFeeReqs chan *bumpFeeReq
|
updateReqs chan *updateReq
|
||||||
|
|
||||||
// pendingInputs is the total set of inputs the UtxoSweeper has been
|
// pendingInputs is the total set of inputs the UtxoSweeper has been
|
||||||
// requested to sweep.
|
// requested to sweep.
|
||||||
@ -287,7 +299,7 @@ func New(cfg *UtxoSweeperConfig) *UtxoSweeper {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
newInputs: make(chan *sweepInputMessage),
|
newInputs: make(chan *sweepInputMessage),
|
||||||
spendChan: make(chan *chainntnfs.SpendDetail),
|
spendChan: make(chan *chainntnfs.SpendDetail),
|
||||||
bumpFeeReqs: make(chan *bumpFeeReq),
|
updateReqs: make(chan *updateReq),
|
||||||
pendingSweepsReqs: make(chan *pendingSweepsReq),
|
pendingSweepsReqs: make(chan *pendingSweepsReq),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
pendingInputs: make(pendingInputs),
|
pendingInputs: make(pendingInputs),
|
||||||
@ -390,10 +402,10 @@ func (s *UtxoSweeper) SweepInput(input input.Input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Sweep request received: out_point=%v, witness_type=%v, "+
|
log.Infof("Sweep request received: out_point=%v, witness_type=%v, "+
|
||||||
"time_lock=%v, amount=%v, fee_preference=%v", input.OutPoint(),
|
"time_lock=%v, amount=%v, fee_preference=%v, force=%v",
|
||||||
input.WitnessType(), input.BlocksToMaturity(),
|
input.OutPoint(), input.WitnessType(), input.BlocksToMaturity(),
|
||||||
btcutil.Amount(input.SignDesc().Output.Value),
|
btcutil.Amount(input.SignDesc().Output.Value),
|
||||||
params.Fee)
|
params.Fee, params.Force)
|
||||||
|
|
||||||
sweeperInput := &sweepInputMessage{
|
sweeperInput := &sweepInputMessage{
|
||||||
input: input,
|
input: input,
|
||||||
@ -572,9 +584,9 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) {
|
|||||||
|
|
||||||
// A new external request has been received to bump the fee rate
|
// A new external request has been received to bump the fee rate
|
||||||
// of a given input.
|
// of a given input.
|
||||||
case req := <-s.bumpFeeReqs:
|
case req := <-s.updateReqs:
|
||||||
resultChan, err := s.handleBumpFeeReq(req, bestHeight)
|
resultChan, err := s.handleUpdateReq(req, bestHeight)
|
||||||
req.responseChan <- &bumpFeeResp{
|
req.responseChan <- &updateResp{
|
||||||
resultChan: resultChan,
|
resultChan: resultChan,
|
||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
@ -1034,33 +1046,34 @@ func (s *UtxoSweeper) handlePendingSweepsReq(
|
|||||||
LastFeeRate: pendingInput.lastFeeRate,
|
LastFeeRate: pendingInput.lastFeeRate,
|
||||||
BroadcastAttempts: pendingInput.publishAttempts,
|
BroadcastAttempts: pendingInput.publishAttempts,
|
||||||
NextBroadcastHeight: uint32(pendingInput.minPublishHeight),
|
NextBroadcastHeight: uint32(pendingInput.minPublishHeight),
|
||||||
|
Params: pendingInput.params,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pendingInputs
|
return pendingInputs
|
||||||
}
|
}
|
||||||
|
|
||||||
// BumpFee allows bumping the fee of an input being swept by the UtxoSweeper
|
// UpdateParams allows updating the sweep parameters of a pending input in the
|
||||||
// according to the provided fee preference. The new fee preference will be used
|
// UtxoSweeper. This function can be used to provide an updated fee preference
|
||||||
// for a new sweep transaction of the input that will act as a replacement
|
// that will be used for a new sweep transaction of the input that will act as a
|
||||||
// transaction (RBF) of the original sweeping transaction, if any.
|
// replacement transaction (RBF) of the original sweeping transaction, if any.
|
||||||
//
|
//
|
||||||
// NOTE: This currently doesn't do any fee rate validation to ensure that a bump
|
// NOTE: This currently doesn't do any fee rate validation to ensure that a bump
|
||||||
// is actually successful. The responsibility of doing so should be handled by
|
// is actually successful. The responsibility of doing so should be handled by
|
||||||
// the caller.
|
// the caller.
|
||||||
func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
|
func (s *UtxoSweeper) UpdateParams(input wire.OutPoint,
|
||||||
feePreference FeePreference) (chan Result, error) {
|
params Params) (chan Result, error) {
|
||||||
|
|
||||||
// Ensure the client provided a sane fee preference.
|
// Ensure the client provided a sane fee preference.
|
||||||
if _, err := s.feeRateForPreference(feePreference); err != nil {
|
if _, err := s.feeRateForPreference(params.Fee); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
responseChan := make(chan *bumpFeeResp, 1)
|
responseChan := make(chan *updateResp, 1)
|
||||||
select {
|
select {
|
||||||
case s.bumpFeeReqs <- &bumpFeeReq{
|
case s.updateReqs <- &updateReq{
|
||||||
input: input,
|
input: input,
|
||||||
feePreference: feePreference,
|
params: params,
|
||||||
responseChan: responseChan,
|
responseChan: responseChan,
|
||||||
}:
|
}:
|
||||||
case <-s.quit:
|
case <-s.quit:
|
||||||
@ -1075,9 +1088,9 @@ func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleBumpFeeReq handles a bump fee request by simply updating the inputs fee
|
// handleUpdateReq handles an update request by simply updating the sweep
|
||||||
// preference. Currently, no validation is done on the new fee preference to
|
// parameters of the pending input. Currently, no validation is done on the new
|
||||||
// ensure it will properly create a replacement transaction.
|
// fee preference to ensure it will properly create a replacement transaction.
|
||||||
//
|
//
|
||||||
// TODO(wilmer):
|
// TODO(wilmer):
|
||||||
// * Validate fee preference to ensure we'll create a valid replacement
|
// * Validate fee preference to ensure we'll create a valid replacement
|
||||||
@ -1086,8 +1099,8 @@ func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
|
|||||||
// * Ensure we don't combine this input with any other unconfirmed inputs that
|
// * Ensure we don't combine this input with any other unconfirmed inputs that
|
||||||
// did not exist in the original sweep transaction, resulting in an invalid
|
// did not exist in the original sweep transaction, resulting in an invalid
|
||||||
// replacement transaction.
|
// replacement transaction.
|
||||||
func (s *UtxoSweeper) handleBumpFeeReq(req *bumpFeeReq,
|
func (s *UtxoSweeper) handleUpdateReq(req *updateReq, bestHeight int32) (
|
||||||
bestHeight int32) (chan Result, error) {
|
chan Result, error) {
|
||||||
|
|
||||||
// If the UtxoSweeper is already trying to sweep this input, then we can
|
// If the UtxoSweeper is already trying to sweep this input, then we can
|
||||||
// simply just increase its fee rate. This will allow the input to be
|
// simply just increase its fee rate. This will allow the input to be
|
||||||
@ -1099,10 +1112,10 @@ func (s *UtxoSweeper) handleBumpFeeReq(req *bumpFeeReq,
|
|||||||
return nil, lnwallet.ErrNotMine
|
return nil, lnwallet.ErrNotMine
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Updating fee preference for %v from %v to %v", req.input,
|
log.Debugf("Updating sweep parameters for %v from %v to %v", req.input,
|
||||||
pendingInput.params.Fee, req.feePreference)
|
pendingInput.params, req.params)
|
||||||
|
|
||||||
pendingInput.params.Fee = req.feePreference
|
pendingInput.params = req.params
|
||||||
|
|
||||||
// We'll reset the input's publish height to the current so that a new
|
// We'll reset the input's publish height to the current so that a new
|
||||||
// transaction can be created that replaces the transaction currently
|
// transaction can be created that replaces the transaction currently
|
||||||
|
@ -1178,7 +1178,9 @@ func TestBumpFeeRBF(t *testing.T) {
|
|||||||
|
|
||||||
// We'll first try to bump the fee of an output currently unknown to the
|
// We'll first try to bump the fee of an output currently unknown to the
|
||||||
// UtxoSweeper. Doing so should result in a lnwallet.ErrNotMine error.
|
// UtxoSweeper. Doing so should result in a lnwallet.ErrNotMine error.
|
||||||
bumpResult, err := ctx.sweeper.BumpFee(wire.OutPoint{}, lowFeePref)
|
_, err := ctx.sweeper.UpdateParams(
|
||||||
|
wire.OutPoint{}, Params{Fee: lowFeePref},
|
||||||
|
)
|
||||||
if err != lnwallet.ErrNotMine {
|
if err != lnwallet.ErrNotMine {
|
||||||
t.Fatalf("expected error lnwallet.ErrNotMine, got \"%v\"", err)
|
t.Fatalf("expected error lnwallet.ErrNotMine, got \"%v\"", err)
|
||||||
}
|
}
|
||||||
@ -1206,12 +1208,14 @@ func TestBumpFeeRBF(t *testing.T) {
|
|||||||
ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate
|
ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate
|
||||||
|
|
||||||
// We should expect to see an error if a fee preference isn't provided.
|
// We should expect to see an error if a fee preference isn't provided.
|
||||||
_, err = ctx.sweeper.BumpFee(*input.OutPoint(), FeePreference{})
|
_, err = ctx.sweeper.UpdateParams(*input.OutPoint(), Params{})
|
||||||
if err != ErrNoFeePreference {
|
if err != ErrNoFeePreference {
|
||||||
t.Fatalf("expected ErrNoFeePreference, got %v", err)
|
t.Fatalf("expected ErrNoFeePreference, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bumpResult, err = ctx.sweeper.BumpFee(*input.OutPoint(), highFeePref)
|
bumpResult, err := ctx.sweeper.UpdateParams(
|
||||||
|
*input.OutPoint(), Params{Fee: highFeePref},
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to bump input's fee: %v", err)
|
t.Fatalf("unable to bump input's fee: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,23 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// addConstraints defines the constraints to apply when adding an input.
|
||||||
|
type addConstraints uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// constraintsRegular is for regular input sweeps that should have a positive
|
||||||
|
// yield.
|
||||||
|
constraintsRegular addConstraints = iota
|
||||||
|
|
||||||
|
// constraintsWallet is for wallet inputs that are only added to bring up the tx
|
||||||
|
// output value.
|
||||||
|
constraintsWallet
|
||||||
|
|
||||||
|
// constraintsForce is for inputs that should be swept even with a negative
|
||||||
|
// yield at the set fee rate.
|
||||||
|
constraintsForce
|
||||||
|
)
|
||||||
|
|
||||||
// txInputSet is an object that accumulates tx inputs and keeps running counters
|
// txInputSet is an object that accumulates tx inputs and keeps running counters
|
||||||
// on various properties of the tx.
|
// on various properties of the tx.
|
||||||
type txInputSet struct {
|
type txInputSet struct {
|
||||||
@ -45,6 +62,10 @@ type txInputSet struct {
|
|||||||
// wallet contains wallet functionality required by the input set to
|
// wallet contains wallet functionality required by the input set to
|
||||||
// retrieve utxos.
|
// retrieve utxos.
|
||||||
wallet Wallet
|
wallet Wallet
|
||||||
|
|
||||||
|
// force indicates that this set must be swept even if the total yield
|
||||||
|
// is negative.
|
||||||
|
force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTxInputSet constructs a new, empty input set.
|
// newTxInputSet constructs a new, empty input set.
|
||||||
@ -78,10 +99,12 @@ func (t *txInputSet) dustLimitReached() bool {
|
|||||||
// add adds a new input to the set. It returns a bool indicating whether the
|
// add adds a new input to the set. It returns a bool indicating whether the
|
||||||
// input was added to the set. An input is rejected if it decreases the tx
|
// input was added to the set. An input is rejected if it decreases the tx
|
||||||
// output value after paying fees.
|
// output value after paying fees.
|
||||||
func (t *txInputSet) add(input input.Input, fromWallet bool) bool {
|
func (t *txInputSet) add(input input.Input, constraints addConstraints) bool {
|
||||||
// Stop if max inputs is reached. Do not count additional wallet inputs,
|
// Stop if max inputs is reached. Do not count additional wallet inputs,
|
||||||
// because we don't know in advance how many we may need.
|
// because we don't know in advance how many we may need.
|
||||||
if !fromWallet && len(t.inputs) >= t.maxInputs {
|
if constraints != constraintsWallet &&
|
||||||
|
len(t.inputs) >= t.maxInputs {
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,23 +131,42 @@ func (t *txInputSet) add(input input.Input, fromWallet bool) bool {
|
|||||||
// added to the set.
|
// added to the set.
|
||||||
newOutputValue := newInputTotal - fee
|
newOutputValue := newInputTotal - fee
|
||||||
|
|
||||||
// If adding this input makes the total output value of the set
|
// Initialize new wallet total with the current wallet total. This is
|
||||||
// decrease, this is a negative yield input. We don't add the input to
|
// updated below if this input is a wallet input.
|
||||||
// the set and return the outcome.
|
newWalletTotal := t.walletInputTotal
|
||||||
if newOutputValue <= t.outputValue {
|
|
||||||
|
// Calculate the yield of this input from the change in tx output value.
|
||||||
|
inputYield := newOutputValue - t.outputValue
|
||||||
|
|
||||||
|
switch constraints {
|
||||||
|
|
||||||
|
// Don't sweep inputs that cost us more to sweep than they give us.
|
||||||
|
case constraintsRegular:
|
||||||
|
if inputYield <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// For force adds, no further constraints apply.
|
||||||
|
case constraintsForce:
|
||||||
|
t.force = true
|
||||||
|
|
||||||
|
// We are attaching a wallet input to raise the tx output value above
|
||||||
|
// the dust limit.
|
||||||
|
case constraintsWallet:
|
||||||
|
// Skip this wallet input if adding it would lower the output
|
||||||
|
// value.
|
||||||
|
if inputYield <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this input comes from the wallet, verify that we still gain
|
|
||||||
// something with this transaction.
|
|
||||||
if fromWallet {
|
|
||||||
// Calculate the total value that we spend in this tx from the
|
// Calculate the total value that we spend in this tx from the
|
||||||
// wallet if we'd add this wallet input.
|
// wallet if we'd add this wallet input.
|
||||||
newWalletTotal := t.walletInputTotal + value
|
newWalletTotal += value
|
||||||
|
|
||||||
// In any case, we don't want to lose money by sweeping. If we
|
// In any case, we don't want to lose money by sweeping. If we
|
||||||
// don't get more out of the tx then we put in ourselves, do not
|
// don't get more out of the tx then we put in ourselves, do not
|
||||||
// add this wallet input.
|
// add this wallet input. If there is at least one force sweep
|
||||||
|
// in the set, this does no longer apply.
|
||||||
//
|
//
|
||||||
// We should only add wallet inputs to get the tx output value
|
// We should only add wallet inputs to get the tx output value
|
||||||
// above the dust limit, otherwise we'd only burn into fees.
|
// above the dust limit, otherwise we'd only burn into fees.
|
||||||
@ -134,7 +176,7 @@ func (t *txInputSet) add(input input.Input, fromWallet bool) bool {
|
|||||||
// value of the wallet input and what we get out of this
|
// value of the wallet input and what we get out of this
|
||||||
// transaction. To prevent attaching and locking a big utxo for
|
// transaction. To prevent attaching and locking a big utxo for
|
||||||
// very little benefit.
|
// very little benefit.
|
||||||
if newWalletTotal >= newOutputValue {
|
if !t.force && newWalletTotal >= newOutputValue {
|
||||||
log.Debugf("Rejecting wallet input of %v, because it "+
|
log.Debugf("Rejecting wallet input of %v, because it "+
|
||||||
"would make a negative yielding transaction "+
|
"would make a negative yielding transaction "+
|
||||||
"(%v)",
|
"(%v)",
|
||||||
@ -142,17 +184,16 @@ func (t *txInputSet) add(input input.Input, fromWallet bool) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've decided to add the wallet input. Increment the total
|
|
||||||
// wallet funds that go into this tx.
|
|
||||||
t.walletInputTotal = newWalletTotal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update running values.
|
// Update running values.
|
||||||
|
//
|
||||||
|
// TODO: Return new instance?
|
||||||
t.inputTotal = newInputTotal
|
t.inputTotal = newInputTotal
|
||||||
t.outputValue = newOutputValue
|
t.outputValue = newOutputValue
|
||||||
t.inputs = append(t.inputs, input)
|
t.inputs = append(t.inputs, input)
|
||||||
t.weightEstimate = newWeightEstimate
|
t.weightEstimate = newWeightEstimate
|
||||||
|
t.walletInputTotal = newWalletTotal
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -167,11 +208,17 @@ func (t *txInputSet) add(input input.Input, fromWallet bool) bool {
|
|||||||
// whole.
|
// whole.
|
||||||
func (t *txInputSet) addPositiveYieldInputs(sweepableInputs []txInput) {
|
func (t *txInputSet) addPositiveYieldInputs(sweepableInputs []txInput) {
|
||||||
for _, input := range sweepableInputs {
|
for _, input := range sweepableInputs {
|
||||||
|
// Apply relaxed constraints for force sweeps.
|
||||||
|
constraints := constraintsRegular
|
||||||
|
if input.parameters().Force {
|
||||||
|
constraints = constraintsForce
|
||||||
|
}
|
||||||
|
|
||||||
// Try to add the input to the transaction. If that doesn't
|
// Try to add the input to the transaction. If that doesn't
|
||||||
// succeed because it wouldn't increase the output value,
|
// succeed because it wouldn't increase the output value,
|
||||||
// return. Assuming inputs are sorted by yield, any further
|
// return. Assuming inputs are sorted by yield, any further
|
||||||
// inputs wouldn't increase the output value either.
|
// inputs wouldn't increase the output value either.
|
||||||
if !t.add(input, false) {
|
if !t.add(input, constraints) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,7 +249,7 @@ func (t *txInputSet) tryAddWalletInputsIfNeeded() error {
|
|||||||
|
|
||||||
// If the wallet input isn't positively-yielding at this fee
|
// If the wallet input isn't positively-yielding at this fee
|
||||||
// rate, skip it.
|
// rate, skip it.
|
||||||
if !t.add(input, true) {
|
if !t.add(input, constraintsWallet) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,13 +24,13 @@ func TestTxInputSet(t *testing.T) {
|
|||||||
// Create a 300 sat input. The fee to sweep this input to a P2WKH output
|
// Create a 300 sat input. The fee to sweep this input to a P2WKH output
|
||||||
// is 439 sats. That means that this input yields -139 sats and we
|
// is 439 sats. That means that this input yields -139 sats and we
|
||||||
// expect it not to be added.
|
// expect it not to be added.
|
||||||
if set.add(createP2WKHInput(300), false) {
|
if set.add(createP2WKHInput(300), constraintsRegular) {
|
||||||
t.Fatal("expected add of negatively yielding input to fail")
|
t.Fatal("expected add of negatively yielding input to fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
// A 700 sat input should be accepted into the set, because it yields
|
// A 700 sat input should be accepted into the set, because it yields
|
||||||
// positively.
|
// positively.
|
||||||
if !set.add(createP2WKHInput(700), false) {
|
if !set.add(createP2WKHInput(700), constraintsRegular) {
|
||||||
t.Fatal("expected add of positively yielding input to succeed")
|
t.Fatal("expected add of positively yielding input to succeed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ func TestTxInputSet(t *testing.T) {
|
|||||||
|
|
||||||
// Add a 1000 sat input. This increases the tx fee to 712 sats. The tx
|
// Add a 1000 sat input. This increases the tx fee to 712 sats. The tx
|
||||||
// output should now be 1000+700 - 712 = 988 sats.
|
// output should now be 1000+700 - 712 = 988 sats.
|
||||||
if !set.add(createP2WKHInput(1000), false) {
|
if !set.add(createP2WKHInput(1000), constraintsRegular) {
|
||||||
t.Fatal("expected add of positively yielding input to succeed")
|
t.Fatal("expected add of positively yielding input to succeed")
|
||||||
}
|
}
|
||||||
if set.outputValue != 988 {
|
if set.outputValue != 988 {
|
||||||
@ -70,13 +70,23 @@ func TestTxInputSetFromWallet(t *testing.T) {
|
|||||||
|
|
||||||
// Add a 700 sat input to the set. It yields positively, but doesn't
|
// Add a 700 sat input to the set. It yields positively, but doesn't
|
||||||
// reach the output dust limit.
|
// reach the output dust limit.
|
||||||
if !set.add(createP2WKHInput(700), false) {
|
if !set.add(createP2WKHInput(700), constraintsRegular) {
|
||||||
t.Fatal("expected add of positively yielding input to succeed")
|
t.Fatal("expected add of positively yielding input to succeed")
|
||||||
}
|
}
|
||||||
if set.dustLimitReached() {
|
if set.dustLimitReached() {
|
||||||
t.Fatal("expected dust limit not yet to be reached")
|
t.Fatal("expected dust limit not yet to be reached")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expect that adding a negative yield input fails.
|
||||||
|
if set.add(createP2WKHInput(50), constraintsRegular) {
|
||||||
|
t.Fatal("expected negative yield input add to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force add the negative yield input. It should succeed.
|
||||||
|
if !set.add(createP2WKHInput(50), constraintsForce) {
|
||||||
|
t.Fatal("expected forced add to succeed")
|
||||||
|
}
|
||||||
|
|
||||||
err := set.tryAddWalletInputsIfNeeded()
|
err := set.tryAddWalletInputsIfNeeded()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -67,6 +67,14 @@ func generateInputPartitionings(sweepableInputs []txInput,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(sweepableInputs, func(i, j int) bool {
|
sort.Slice(sweepableInputs, func(i, j int) bool {
|
||||||
|
// Because of the specific ordering and termination condition
|
||||||
|
// that is described above, we place force sweeps at the start
|
||||||
|
// of the list. Otherwise we can't be sure that they will be
|
||||||
|
// included in an input set.
|
||||||
|
if sweepableInputs[i].parameters().Force {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return yields[*sweepableInputs[i].OutPoint()] >
|
return yields[*sweepableInputs[i].OutPoint()] >
|
||||||
yields[*sweepableInputs[j].OutPoint()]
|
yields[*sweepableInputs[j].OutPoint()]
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user