sweep: remove possible RBF when sweeping new inputs#8091
Conversation
3106609 to
1e14ae2
Compare
Roasbeef
left a comment
There was a problem hiding this comment.
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.
6e27721 to
48273b2
Compare
48273b2 to
a34c370
Compare
There was a problem hiding this comment.
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 ?
There are two RBF happening at the moment,
This PR aims to fix case 1 so we don't have this RBF condition - we can try broadcast |
a34c370 to
f4bf4f0
Compare
There was a problem hiding this comment.
the reason you append to this slice is to notify all of the requesters?
There was a problem hiding this comment.
should this be raised to a more visible log level, e.g Warnf ?
There was a problem hiding this comment.
seems like allSets only returns sets of previously-published sweeps. Why not reflect that on the name, e.g existingSets ?
There was a problem hiding this comment.
allSet uses all the inputs to make the sweeps tho.
2bcdf68 to
7cd9fce
Compare
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.
7cd9fce to
5d3c72b
Compare
5d3c72b to
dba4c8e
Compare
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Nit: Comment needs an update as well now
|
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 |
| ) | ||
|
|
||
| // In case of an unexpected error, don't try to recover. | ||
| if err != nil && err != lnwallet.ErrDoubleSpend { |
There was a problem hiding this comment.
Why doesn't this try to explicitly catch the double spend error any longer?
There was a problem hiding this comment.
Is the rationale that since we reschedule everything above, we don't need to specially handle this control flow?
There was a problem hiding this comment.
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.
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.