-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
lnrpc+sweep: properly remove any unconfirmed descendant chains a to-be-swept input is spent #6274
lnrpc+sweep: properly remove any unconfirmed descendant chains a to-be-swept input is spent #6274
Conversation
I'm a bit worried about reorgs. What if the foreign tx that steals my anchor is confirmed, your new code removes a bunch of outputs, but then there's a new block that un-confirms the foreign transaction? |
Great question! I have a TODO there about only doing this after a certain "epoch", which would be an interval N blocks after the conflicting transaction confirms. Even w/o this, you'd have those issues when spending unconfirmed change for a funding input as anything can happen until confirmation. So there's a bit of a tradeoff here: do we do this as soon as we see a conflicting confirmation, which can allow us to immediately free up those outputs, or do we wait for 12 blocks (or w/e) and then do this, which ends up leaving your transaction state (assuming unconfirmed outputs) in a weird state for a longer period of time? I think the change to start showing the leased/locked UTOXs in |
@carlaKC: review reminder |
06fffd5
to
6b9d69d
Compare
Ok I added ain integration test that does the following:
With that, we've confirmed the scenario as well as the fix. The final thing I think we need to address is @C-Otto's point to either wait a few blocks until we remove the transactions, or do it immediately so the wallet UTXO state is correct sooner. |
It's worth noting that this fix won't remove the now invalid pending channel, but will ensure that those outputs are now free from the wallet's PoV. The pending channel fix is here: #5567. |
6b9d69d
to
20c273b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice itest(also cured the headache I had while writing the fee bumper itest, so thank you)! A major question is how efficient it is to find a double spend by querying all sweep transactions, and a few nits/typos.
// | ||
// TODO(roasbeef): can be last sweep here if we remove anything confirmed | ||
// from the store? | ||
pastSweepHashes, err := s.cfg.Store.ListSweeps() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how large the sweeps can be? If a lot this may be expensive.
if we remove anything confirmed from the store
Do you think we can modify the sweeper-last-tx
to store all unconfirmed txes? Atm it seems to only store the latest publish tx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's the thing, we never delete the set of past sweeps as is. For very old nodes, this could end up being hundreds of past weeps. I chose to do this rather than just use the last sweep tx as it seems possible that we back off the failed sweep and then try another combination, causing us to not actually remove this set of conflicting transactions...
Though the disk footprint isn't very large (32 bytes per sweep), I think we should start to prune these sweeps once either we remove them here, we find a conflict, or they confirm with a block of depth N.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I guess for old sweeps we don't ever use them for anything...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I guess for old sweeps we don't ever use them for anything...
Yeah nothing other than this new logic, but once the set of swept items are "well confirmed" then we no longer need them, I think it makes sense to roll out this garbage collection into a new issue, so we can expedite getting this into the next minor release.
// in common with the inputs the spendingTx spent, then we'll remove | ||
// those. | ||
// | ||
// TODO(roasbeef): need to start to remove all transaction hashes after |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To save disk space?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah exactly, also with bolt buckets that have a very large key space can bog things down over time.
The original design of the sweeper just intended to keep things simple and continue to try diff combinations of input sweeps untli something confirmed. We got the logic mostly right in that we signaled to the anchor resolver that it was "lost" (also added an assertion for that in the test), but didn't think of users spending unconfirmed change that was a descendent of a sweep output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we're going to want to keep around the sweeps that confirmed (either in a new bucket or filter our lookup to skip confirmed) because ListSweeps
is pretty useful for accounting.
PTAL! All review comments addressed. I'll pull out that sweeper garbage collection question to a new issue, as it isn't 100% critical we do so, but depending on how frequent operations like this happen, will make the conflict removal process more efficient (as there're be less transactions we need to scan). |
20c273b
to
f7ddacf
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost LGTM! A few nits and the only thing to address is that we are now using require
instead of t.Fatalf
to save some lines.
// in common with the inputs the spendingTx spent, then we'll remove | ||
// those. | ||
// | ||
// TODO(roasbeef): need to start to remove all transaction hashes after |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we're going to want to keep around the sweeps that confirmed (either in a new bucket or filter our lookup to skip confirmed) because ListSweeps
is pretty useful for accounting.
lntest/itest/lnd_onchain_test.go
Outdated
// We'll fund her with *just* enough coins to open the channel. | ||
const ( | ||
firstChanSize = 1_000_000 | ||
secondChanSize = 500_000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
secondUtxo size? We don't actually open a channel witth this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah left over from a prior design where I was trying to open a channel which was juuust the right size, but then realized I could just do a sweep all instead. Will remove in the next revision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or actually the other portion was to ensure she had enough on chain to be able to add inputs for a sweep as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to use a different variable name here.
f7ddacf
to
b714bba
Compare
In this commit, we add a new field to the WalletBalance call that permits users to account for the set of outputs that may be locked due to a pending transaction. Without this field any time users locked outputs for things like PSBT signing, then they disappear from the WalletBalance call, which may cause a panic.
…nflicts Before this commit, we we were trying to sweep an anchor output, and that output was spent by someone else (not the sweeper), then we would report this back to the original resolver (allowing it to be cleaned up), and also remove the set of inputs spent by that transaction from the set we need to sweep. However, it's possible that if a user is spending unconfirmed outputs, then the wallet is holding onto an invalid transaction, as the outputs that were used as inputs have been double spent elsewhere. In this commit, we fix this issue by recursively removing all descendant transactions of our past sweeps that have an intersecting input set as the spending transaction. In cases where a user spent an unconfirmed output to funding a channel, and that output was a descendant of the now swept anchor output, the funds will now properly be marked as available. Fixes lightningnetwork#6241
In this commit, we add a new integration tests to exercise the fix introduced in the prior commit. In this test, we reconstruct a scenario for a 3rd party to sweep an anchor spend after force closing, causing a prior spend we had to be invalidated. Without the prior commit, this test fails as the original anchor sweep is still found in the wallet.
b714bba
to
d164307
Compare
Github Actions is having sone issues, but I've run the itests+lint locally and confirmed they all pass. Given nothing major changd on this PR since the last run (just adding some newlines mainly), I think this can land. |
In this PR, we address issue #6241 by ensuring that if someone sweeps an anchor output of ours after 16 blocks, then we recursively remove any transactions descendant from the set of outputs created by our sweep attempt. Along the way, we add a new
LockedBalance
output to theWalletBalance
command that allows users to keep track of teh total sum of locked outputs more easily. Anytime we do a funding attempt, the outputs get locked, so if a user has a funding transaction that was invalidated by an input double spend, those funds would show as missing until the lease expired.One quirk with the current solution is that end up cross checking every transaction the sweeper has ever published. This is due to the fact that right now the sweeper's store doesn't remove any prior sweeps even once they're confirmed. If we just looked at the last published sweep, then it's possible that we'd miss a set of relevant transactions.
The methods use in this PR can likely be used elsewhere to remove any weird transactions from the
ListTransactions
output.TODO Items