Skip to content

Commit

Permalink
sweep: allow updating all sweep parameters
Browse files Browse the repository at this point in the history
This is a preparation for adding additional parameters besides the fee
preference.
  • Loading branch information
joostjager committed Jan 15, 2020
1 parent 280611a commit 16832ce
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 39 deletions.
6 changes: 5 additions & 1 deletion lnrpc/walletrpc/walletkit_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,11 @@ func (w *WalletKit) BumpFee(ctx context.Context,
// bump its fee, which will result in a replacement transaction (RBF)
// being broadcast. If it is not aware of the input however,
// lnwallet.ErrNotMine is returned.
_, err = w.cfg.Sweeper.BumpFee(*op, feePreference)
params := sweep.Params{
Fee: feePreference,
}

_, err = w.cfg.Sweeper.UpdateParams(*op, params)
switch err {
case nil:
return &BumpFeeResponse{}, nil
Expand Down
75 changes: 40 additions & 35 deletions sweep/sweeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ type Params struct {
Fee FeePreference
}

// String returns a human readable interpretation of the sweep parameters.
func (p Params) String() string {
return fmt.Sprintf("fee=%v", p.Fee)
}

// pendingInput is created when an input reaches the main loop for the first
// time. It wraps the input and tracks all relevant state that is needed for
// sweeping.
Expand Down Expand Up @@ -151,17 +156,17 @@ type PendingInput struct {
Params Params
}

// bumpFeeReq is an internal message we'll use to represent an external caller's
// intent to bump the fee rate of a given input.
type bumpFeeReq struct {
input wire.OutPoint
feePreference FeePreference
responseChan chan *bumpFeeResp
// updateReq is an internal message we'll use to represent an external caller's
// intent to update the sweep parameters of a given input.
type updateReq struct {
input wire.OutPoint
params Params
responseChan chan *updateResp
}

// bumpFeeResp 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.
type bumpFeeResp struct {
// updateResp is an internal message we'll use to hand off the response of a
// updateReq from the UtxoSweeper's main event loop back to the caller.
type updateResp struct {
resultChan chan Result
err error
}
Expand All @@ -181,9 +186,9 @@ type UtxoSweeper struct {
// UtxoSweeper is attempting to sweep.
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.
bumpFeeReqs chan *bumpFeeReq
updateReqs chan *updateReq

// pendingInputs is the total set of inputs the UtxoSweeper has been
// requested to sweep.
Expand Down Expand Up @@ -290,7 +295,7 @@ func New(cfg *UtxoSweeperConfig) *UtxoSweeper {
cfg: cfg,
newInputs: make(chan *sweepInputMessage),
spendChan: make(chan *chainntnfs.SpendDetail),
bumpFeeReqs: make(chan *bumpFeeReq),
updateReqs: make(chan *updateReq),
pendingSweepsReqs: make(chan *pendingSweepsReq),
quit: make(chan struct{}),
pendingInputs: make(pendingInputs),
Expand Down Expand Up @@ -575,9 +580,9 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) {

// A new external request has been received to bump the fee rate
// of a given input.
case req := <-s.bumpFeeReqs:
resultChan, err := s.handleBumpFeeReq(req, bestHeight)
req.responseChan <- &bumpFeeResp{
case req := <-s.updateReqs:
resultChan, err := s.handleUpdateReq(req, bestHeight)
req.responseChan <- &updateResp{
resultChan: resultChan,
err: err,
}
Expand Down Expand Up @@ -1044,28 +1049,28 @@ func (s *UtxoSweeper) handlePendingSweepsReq(
return pendingInputs
}

// BumpFee allows bumping the fee of an input being swept by the UtxoSweeper
// according to the provided fee preference. The new fee preference will be used
// for a new sweep transaction of the input that will act as a replacement
// transaction (RBF) of the original sweeping transaction, if any.
// UpdateParams allows updating the sweep parameters of a pending input in the
// UtxoSweeper. This function can be used to provide an updated fee preference
// that will be used for a new sweep transaction of the input that will act as a
// 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
// is actually successful. The responsibility of doing so should be handled by
// the caller.
func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
feePreference FeePreference) (chan Result, error) {
func (s *UtxoSweeper) UpdateParams(input wire.OutPoint,
params Params) (chan Result, error) {

// 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
}

responseChan := make(chan *bumpFeeResp, 1)
responseChan := make(chan *updateResp, 1)
select {
case s.bumpFeeReqs <- &bumpFeeReq{
input: input,
feePreference: feePreference,
responseChan: responseChan,
case s.updateReqs <- &updateReq{
input: input,
params: params,
responseChan: responseChan,
}:
case <-s.quit:
return nil, ErrSweeperShuttingDown
Expand All @@ -1079,9 +1084,9 @@ func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
}
}

// handleBumpFeeReq handles a bump fee request by simply updating the inputs fee
// preference. Currently, no validation is done on the new fee preference to
// ensure it will properly create a replacement transaction.
// handleUpdateReq handles an update request by simply updating the sweep
// parameters of the pending input. Currently, no validation is done on the new
// fee preference to ensure it will properly create a replacement transaction.
//
// TODO(wilmer):
// * Validate fee preference to ensure we'll create a valid replacement
Expand All @@ -1090,8 +1095,8 @@ func (s *UtxoSweeper) BumpFee(input wire.OutPoint,
// * 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
// replacement transaction.
func (s *UtxoSweeper) handleBumpFeeReq(req *bumpFeeReq,
bestHeight int32) (chan Result, error) {
func (s *UtxoSweeper) handleUpdateReq(req *updateReq, bestHeight int32) (
chan Result, error) {

// 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
Expand All @@ -1103,10 +1108,10 @@ func (s *UtxoSweeper) handleBumpFeeReq(req *bumpFeeReq,
return nil, lnwallet.ErrNotMine
}

log.Debugf("Updating fee preference for %v from %v to %v", req.input,
pendingInput.params.Fee, req.feePreference)
log.Debugf("Updating sweep parameters for %v from %v to %v", req.input,
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
// transaction can be created that replaces the transaction currently
Expand Down
10 changes: 7 additions & 3 deletions sweep/sweeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,9 @@ func TestBumpFeeRBF(t *testing.T) {

// 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.
bumpResult, err := ctx.sweeper.BumpFee(wire.OutPoint{}, lowFeePref)
_, err := ctx.sweeper.UpdateParams(
wire.OutPoint{}, Params{Fee: lowFeePref},
)
if err != lnwallet.ErrNotMine {
t.Fatalf("expected error lnwallet.ErrNotMine, got \"%v\"", err)
}
Expand Down Expand Up @@ -1206,12 +1208,14 @@ func TestBumpFeeRBF(t *testing.T) {
ctx.estimator.blocksToFee[highFeePref.ConfTarget] = highFeeRate

// 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 {
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 {
t.Fatalf("unable to bump input's fee: %v", err)
}
Expand Down

0 comments on commit 16832ce

Please sign in to comment.