Skip to content

Commit e45ad10

Browse files
committed
sweep: make sure non-fee related errors are notified
So these inputs can be retried by the sweeper.
1 parent ccc8f9b commit e45ad10

File tree

2 files changed

+68
-41
lines changed

2 files changed

+68
-41
lines changed

sweep/fee_bumper.go

+47-30
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/btcsuite/btcwallet/chain"
1414
"github.com/davecgh/go-spew/spew"
1515
"github.com/lightningnetwork/lnd/chainntnfs"
16+
"github.com/lightningnetwork/lnd/fn"
1617
"github.com/lightningnetwork/lnd/input"
1718
"github.com/lightningnetwork/lnd/labels"
1819
"github.com/lightningnetwork/lnd/lnutils"
@@ -815,22 +816,20 @@ func (t *TxPublisher) handleFeeBumpTx(requestID uint64, r *monitorRecord,
815816

816817
// The fee function now has a new fee rate, we will use it to bump the
817818
// fee of the tx.
818-
result, err := t.createAndPublishTx(requestID, r)
819-
if err != nil {
820-
log.Errorf("Failed to bump tx %v: %v", oldTxid, err)
821-
822-
return
823-
}
819+
resultOpt := t.createAndPublishTx(requestID, r)
824820

825-
// Notify the new result.
826-
t.handleResult(result)
821+
// If there's a result, we will notify the caller about the result.
822+
resultOpt.WhenSome(func(result BumpResult) {
823+
// Notify the new result.
824+
t.handleResult(&result)
825+
})
827826
}
828827

829828
// createAndPublishTx creates a new tx with a higher fee rate and publishes it
830829
// to the network. It will update the record with the new tx and fee rate if
831830
// successfully created, and return the result when published successfully.
832831
func (t *TxPublisher) createAndPublishTx(requestID uint64,
833-
r *monitorRecord) (*BumpResult, error) {
832+
r *monitorRecord) fn.Option[BumpResult] {
834833

835834
// Fetch the old tx.
836835
oldTx := r.tx
@@ -842,21 +841,8 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
842841
// directly here.
843842
tx, fee, err := t.createAndCheckTx(r.req, r.feeFunction)
844843

845-
// If the tx doesn't not have enought budget, we will return a result
846-
// so the sweeper can handle it by re-clustering the utxos.
847-
if errors.Is(err, ErrNotEnoughBudget) {
848-
log.Warnf("Fail to fee bump tx %v: %v", oldTx.TxHash(), err)
849-
850-
return &BumpResult{
851-
Event: TxFailed,
852-
Tx: oldTx,
853-
Err: err,
854-
requestID: requestID,
855-
}, nil
856-
}
857-
858-
// If the error is not budget related, we will return an error and let
859-
// the fee bumper retry it at next block.
844+
// If the error is fee related, we will return an error and let the fee
845+
// bumper retry it at next block.
860846
//
861847
// NOTE: we can check the RBF error here and ask the fee function to
862848
// recalculate the fee rate. However, this would defeat the purpose of
@@ -865,12 +851,40 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
865851
// - if the deadline is close, we expect the fee function to give us a
866852
// higher fee rate. If the fee rate cannot satisfy the RBF rules, it
867853
// means the budget is not enough.
854+
if errors.Is(err, rpcclient.ErrInsufficientFee) ||
855+
errors.Is(err, lnwallet.ErrMempoolFee) {
856+
857+
log.Debugf("Failed to bump tx %v: %v", oldTx.TxHash(), err)
858+
return fn.None[BumpResult]()
859+
}
860+
861+
// If the error is not fee related, we will return a `TxFailed` event
862+
// so this input can be retried.
868863
if err != nil {
869-
log.Infof("Failed to bump tx %v: %v", oldTx.TxHash(), err)
870-
return nil, err
864+
// If the tx doesn't not have enought budget, we will return a
865+
// result so the sweeper can handle it by re-clustering the
866+
// utxos.
867+
if errors.Is(err, ErrNotEnoughBudget) {
868+
log.Warnf("Fail to fee bump tx %v: %v", oldTx.TxHash(),
869+
err)
870+
} else {
871+
// Otherwise, an unexpected error occurred, we will
872+
// fail the tx and let the sweeper retry the whole
873+
// process.
874+
log.Errorf("Failed to bump tx %v: %v", oldTx.TxHash(),
875+
err)
876+
}
877+
878+
return fn.Some(BumpResult{
879+
Event: TxFailed,
880+
Tx: oldTx,
881+
Err: err,
882+
requestID: requestID,
883+
})
871884
}
872885

873-
// Register a new record by overwriting the same requestID.
886+
// The tx has been created without any errors, we now register a new
887+
// record by overwriting the same requestID.
874888
t.records.Store(requestID, &monitorRecord{
875889
tx: tx,
876890
req: r.req,
@@ -881,7 +895,10 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
881895
// Attempt to broadcast this new tx.
882896
result, err := t.broadcast(requestID)
883897
if err != nil {
884-
return nil, err
898+
log.Infof("Failed to broadcast replacement tx %v: %v",
899+
tx.TxHash(), err)
900+
901+
return fn.None[BumpResult]()
885902
}
886903

887904
// A successful replacement tx is created, attach the old tx.
@@ -890,15 +907,15 @@ func (t *TxPublisher) createAndPublishTx(requestID uint64,
890907
// If the new tx failed to be published, we will return the result so
891908
// the caller can handle it.
892909
if result.Event == TxFailed {
893-
return result, nil
910+
return fn.Some(*result)
894911
}
895912

896913
log.Infof("Replaced tx=%v with new tx=%v", oldTx.TxHash(), tx.TxHash())
897914

898915
// Otherwise, it's a successful RBF, set the event and return.
899916
result.Event = TxReplaced
900917

901-
return result, nil
918+
return fn.Some(*result)
902919
}
903920

904921
// isConfirmed checks the btcwallet to see whether the tx is confirmed.

sweep/fee_bumper_test.go

+21-11
Original file line numberDiff line numberDiff line change
@@ -1027,8 +1027,8 @@ func TestCreateAnPublishFail(t *testing.T) {
10271027
mock.Anything).Return(script, nil)
10281028

10291029
// Call the createAndPublish method.
1030-
result, err := tp.createAndPublishTx(requestID, record)
1031-
require.NoError(t, err)
1030+
resultOpt := tp.createAndPublishTx(requestID, record)
1031+
result := resultOpt.UnwrapOrFail(t)
10321032

10331033
// We expect the result to be TxFailed and the error is set in the
10341034
// result.
@@ -1040,14 +1040,23 @@ func TestCreateAnPublishFail(t *testing.T) {
10401040
// error to be returned from CheckMempoolAcceptance.
10411041
req.Budget = 1000
10421042

1043-
// Mock the testmempoolaccept to return an error.
1043+
// Mock the testmempoolaccept to return a fee related error that should
1044+
// be ignored.
10441045
m.wallet.On("CheckMempoolAcceptance",
10451046
mock.Anything).Return(lnwallet.ErrMempoolFee).Once()
10461047

1047-
// Call the createAndPublish method and expect an error.
1048-
result, err = tp.createAndPublishTx(requestID, record)
1049-
require.ErrorIs(t, err, lnwallet.ErrMempoolFee)
1050-
require.Nil(t, result)
1048+
// Call the createAndPublish method and expect a none option.
1049+
resultOpt = tp.createAndPublishTx(requestID, record)
1050+
require.True(t, resultOpt.IsNone())
1051+
1052+
// Mock the testmempoolaccept to return a fee related error that should
1053+
// be ignored.
1054+
m.wallet.On("CheckMempoolAcceptance",
1055+
mock.Anything).Return(rpcclient.ErrInsufficientFee).Once()
1056+
1057+
// Call the createAndPublish method and expect a none option.
1058+
resultOpt = tp.createAndPublishTx(requestID, record)
1059+
require.True(t, resultOpt.IsNone())
10511060
}
10521061

10531062
// TestCreateAnPublishSuccess checks the expected result is returned from the
@@ -1090,8 +1099,8 @@ func TestCreateAnPublishSuccess(t *testing.T) {
10901099
mock.Anything, mock.Anything).Return(errDummy).Once()
10911100

10921101
// Call the createAndPublish method and expect a failure result.
1093-
result, err := tp.createAndPublishTx(requestID, record)
1094-
require.NoError(t, err)
1102+
resultOpt := tp.createAndPublishTx(requestID, record)
1103+
result := resultOpt.UnwrapOrFail(t)
10951104

10961105
// We expect the result to be TxFailed and the error is set.
10971106
require.Equal(t, TxFailed, result.Event)
@@ -1111,8 +1120,9 @@ func TestCreateAnPublishSuccess(t *testing.T) {
11111120
mock.Anything, mock.Anything).Return(nil).Once()
11121121

11131122
// Call the createAndPublish method and expect a success result.
1114-
result, err = tp.createAndPublishTx(requestID, record)
1115-
require.NoError(t, err)
1123+
resultOpt = tp.createAndPublishTx(requestID, record)
1124+
result = resultOpt.UnwrapOrFail(t)
1125+
require.True(t, resultOpt.IsSome())
11161126

11171127
// We expect the result to be TxReplaced and the error is nil.
11181128
require.Equal(t, TxReplaced, result.Event)

0 commit comments

Comments
 (0)