Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sweep: remove possible RBF when sweeping new inputs #8091

Merged
merged 11 commits into from
Oct 25, 2023

Conversation

yyforyongyu
Copy link
Member

Depends on some of the test framework from,

Fixes #7972 and possible #7181

This PR removes a possible case that when sweeping new and old inputs, the new inputs may end up in a second transaction which accidentally attempts an RBF(and fails). It also refactors the sweeper and simplified the ticker logic, preparing for incoming refactors.

@yyforyongyu yyforyongyu added this to the v0.18.0 milestone Oct 12, 2023
@yyforyongyu yyforyongyu self-assigned this Oct 12, 2023
Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid set of refactoring and additional tests! I need to sit a bit more with the RBF change. IIUC, this shouldn't affect our sweeping unless the new sweep actually violates a rule of RBF. One example would be using the same transaction, but adding an a new unconfirmed input.

sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper_test.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
input/mocks.go Outdated Show resolved Hide resolved
sweep/sweeper_test.go Outdated Show resolved Hide resolved
sweep/sweeper.go Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Show resolved Hide resolved
sweep/sweeper_test.go Show resolved Hide resolved
@yyforyongyu yyforyongyu force-pushed the remove-rbf-sweeper branch 5 times, most recently from 6e27721 to 48273b2 Compare October 13, 2023 12:08
Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only realized after reviewing the first part, that it's another PR 😅, so left nits I did not seeing when I reviewed the base PR for it.

In terms of the new PR, I really like the tests and the refactoring. What I am not so sure about is the fix of the RBF case, because I think it deteriorates the sweeper situation mainly because most node runners have those min-relay-anchors sitting around and marking almost all sweeps invalid as soon as a third party bundled a big tx with a lot of anchors (increasing the RBF fee condition). So in the old case, we would at least publish the newSet as well, having a change for the new inputs to get broadcasted. LMKWYT ?

sweep/sweeper_test.go Outdated Show resolved Hide resolved
sweep/sweeper_test.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper_test.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Outdated Show resolved Hide resolved
sweep/sweeper.go Show resolved Hide resolved
sweep/sweeper.go Show resolved Hide resolved
@yyforyongyu
Copy link
Member Author

yyforyongyu commented Oct 17, 2023

What I am not so sure about is the fix of the RBF case, because I think it deteriorates the sweeper situation mainly because most node runners have those min-relay-anchors sitting around and marking almost all sweeps invalid as soon as a third party bundled a big tx with a lot of anchors (increasing the RBF fee condition). So in the old case, we would at least publish the newSet as well, having a change for the new inputs to get broadcasted. LMKWYT ?

There are two RBF happening at the moment,

  1. when creating sweeping txes, if there are mixed inputs, at least two txes are created - txAll for all inputs, txNew for new inputs, as shown in [bug]: sweeper (bumpfee) creates invalid transaction (not paying higher fees when necessary) #7972, and the possible result,
    • txAll gets in mempool and be replaced by txNew, or the other way around where txNew gets replace. Tho unwanted, this is at least a good case as there's no broadcasting errors.
    • txAll gets in mempool and txNew gets a broadcast error, or the other way around, and we don't know which case will happen because we can't guarantee the tx propagation order.
  2. when sweeping all inputs, retried inputs are already in mempool, thus creating an RBF case which may or may not be satisfied.

This PR aims to fix case 1 so we don't have this RBF condition - we can try broadcast txAll first, if it fails, we then broadcast txNew.


// Create a new pendingInput and initialize the listeners slice with
// the passed in result channel. If this input is offered for sweep
// again, the result channel will be appended to this slice.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason you append to this slice is to notify all of the requesters?

"due to tx %v: ", spendHash)
}

log.Debugf("Detected third party spend related to in flight "+
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be raised to a more visible log level, e.g Warnf ?

sweep/sweeper.go Show resolved Hide resolved
// lists of inputs.
inputLists, err := s.getInputLists(cluster, currentHeight)
// Examine pending inputs and try to construct lists of inputs.
allSets, newSets, err := s.getInputLists(cluster, currentHeight)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like allSets only returns sets of previously-published sweeps. Why not reflect that on the name, e.g existingSets ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allSet uses all the inputs to make the sweeps tho.

@yyforyongyu yyforyongyu force-pushed the remove-rbf-sweeper branch 5 times, most recently from 2bcdf68 to 7cd9fce Compare October 19, 2023 02:02
@saubyk saubyk requested review from Roasbeef and ziggie1984 October 19, 2023 15:25
sweep/sweeper.go Show resolved Hide resolved
The method `waitForSpend` makes it sounding like it's blocking while
it's not, thus it's renamed to avoid confusion.
This commit attempts to make the polling logic in sweeper more linear.
Previously, the sweep's timer is reset/restarted in multiple places,
such as when a new input comes in, or a new block comes in, or a
previous input being spent, making it difficult to follow. We now remove
the old timer and replaces it with a simple polling logic - we will
schedule sweeps every 5s(default), and if there's no input to be swept,
we'd skip, just like the previous `scheduleSweep` does.
It's also worthy noting that, although `scheduleSweep` triggers the
timer to tick, by the time we do the actual sweep in `sweepCluster`,
conditions may have changed. This is now also fixed because we only have
one place to create the clusters and sweeps.
This commit changes how we create the input sets which are used to
construct the sweeping transactions. Assume the sweeper has two inputs,
one is new and one is retried, we'd end up having two transactions,
- tx1: which spends both the new and old inputs.
- tx2: which spends the new inputs only.
When publishing these txes, depending on which one gets into the mempool
first, the other one will be viewed as an RBF for the first one since
they both spending the same input(the new input).

This is now fixed by only attempt to publish the second tx when there
isn't a first tx - when there is a tx1, it means the new inputs are
already used in this tx along with retried inputs, hence there's no need
to publish tx2 which spends the new inputs only.
Copy link
Collaborator

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one thing I want to highlight, we should definitely add a follow-up PR where we trigger the sweeper when the sweep has the force flag set. Because we have the variable batchwindow-duration people can mess-up there config and end up not sweeping time sensitive inputs.

Moreover it is worth noting that this PR is just one part of the fix in regards of the RBF problem (see yy's comment).

}

// If we have successfully swept all inputs, there's no need to
// sweep the new inputs as it'd create an RBF case.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: ... there's no need to sweep the new inputs separately ...

return nil
}

// We'd end up there if there's no retried inputs. In this
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Comment needs an update as well now

@Roasbeef
Copy link
Member

Ultimately, the RBF rules are pretty finicky, and the only way we can ensure we're about to publish a replaceable transaction, is by using testmempoolaccept each time. So I consider this a stop gap to start to refractor, simplify the code, and fix any low hanging fruits before we start to integrate something like that.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🛩️

err = s.cfg.Wallet.PublishTransaction(
tx, labels.MakeLabel(labels.LabelTypeSweepTransaction, nil),
)

// In case of an unexpected error, don't try to recover.
if err != nil && err != lnwallet.ErrDoubleSpend {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't this try to explicitly catch the double spend error any longer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the rationale that since we reschedule everything above, we don't need to specially handle this control flow?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly we want to make sure, that even unexpected errors increase their attempt counter allowing them to be removed after the default of 10 attempts (default value). Moreover we use the error to signal, that we try to publish all the new inputs in a separate transaction when the combined one (combination of retried and new inputs) failed.

@Roasbeef Roasbeef merged commit a1fa195 into lightningnetwork:master Oct 25, 2023
24 of 25 checks passed
@yyforyongyu yyforyongyu deleted the remove-rbf-sweeper branch October 26, 2023 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[bug]: sweeper (bumpfee) creates invalid transaction (not paying higher fees when necessary)
4 participants