-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
wallet: RBF batch payments manager #9298
base: master
Are you sure you want to change the base?
Conversation
b71b11f
to
45c7543
Compare
a48e7ca
to
5c7a247
Compare
3bdbf87
to
3ad4406
Compare
4826f7e
to
5fd2ec7
Compare
I get this exception and electrum crashes when batching is enabled and trying to fully spend an unconfirmed utxo from the "Coins" tab with right click -> fully spend -> send to address in clipboard.
|
9daa851
to
3f8bc37
Compare
This does not correspond to the code in the |
Can confirm it doesn't happen on this branch, only on master. |
1b9218c
to
be6f9bb
Compare
9f589b0
to
ccb0cc3
Compare
Note: This PR does not work well with fee settings. When we change the fee settings in |
|
||
# list of tx that were broadcast. Each tx is a RBF replacement of the previous one. Ony one can get mined. | ||
self.batch_txids = self.db.get_stored_item("batch_txids", []) | ||
self._base_tx = None # current batch tx. last element of batch_txids |
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.
TxBatcher needs to be taught the 100 KB max tx size standardness rule. It must not try creating txs that are too large.
electrum/lnwatcher.py
Outdated
if old_tx and old_tx.txid() != new_tx.txid(): | ||
self.lnworker.wallet.set_label(old_tx.txid(), None) | ||
util.trigger_callback('wallet_updated', self.lnworker.wallet) | ||
self.lnworker.wallet.txbatcher.add_sweep_info(sweep_info) |
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.
In general it is not safe to batch all lightning sweeps together, due to (1) pinning attacks and (2) split views of mempools due to distributed nature of network.
Perhaps the scariest problem is to do with revocations:
- counterparty force-closes using old state
- ctx has some htlcs
- counterparty creates a 2nd stage htlc tx ("tx1"), that spends one or more of the ctx's htlc outputs, and they add a lot of inputs/outputs to this tx (which they can do when using anchor channels due to sighash_single). They ensure tx1 is large (in vbytes), pays a low feerate so it does not confirm, but it pays a high absolute fee (as it is large).
- the local client then tries to punish the counterparty: we want to sweep the relevant htlc_tx outputs but we also want to sweep
to_local
- if we batch the sweeping of those two utxos, to create "tx2", as "tx1" might ~never confirm, "tx2" won't get mined either.
- and in the meantime, the relative timelock (to_self_delay) on
to_local
is ticking. If that expires, the remote can just sweep it for themselves.
(see lightning/bolts#803)
The easiest workaround I believe would be to avoid batching all revocations. That is, any sweep where we use the revocation path, should be a completely separate 1-in-1-out tx.
However, it's not just about revocations. In general, I believe it is not safe to batch the sweeping of txos where some txos are spendable by both us and the remote, and some only by us. Perhaps we should have a separate "safe" batch for sweeping txos that are only spendable by us, and another for the risky txos.
One complication is that some utxos transition from "only spendable by us" to "spendable by either party" over time (~already when a timelock gets close to expiry, but especially after it expires).
I am not yet sure exactly how to handle this.
Also, btw, to simplify reasoning, I think we should consider strictly separating the batching of user-initiated txs (e.g. simple sends), and lnwatcher/swap_manager stuff.
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.
(2) split views of mempools due to distributed nature of network
Example:
- at block 0, remote counterparty force-closes small-value chan1, the to_self_delay is 1000
- we are offline
- at block 1000, remote force-closes high-value chan2, the to_self_delay is 1000
- at block 1100, we come online - and the remote somehow learns of this instantly
- they try to partition the mempool
- "at the same time":
- remote broadcasts tx1, that sweeps the to_local output of their chan1
- they try to pin this tx to the mempool: it is large in size, low feerate, but highish absolute fee
- we broadcast tx2, that sweeps both to_locals (from chan1 and chan2) in a single batched tx
- remote broadcasts tx1, that sweeps the to_local output of their chan1
- if the remote is successful, the mempool is now partitioned so that most nodes and miners have tx1, but our electrum server has tx2
- the attacker's goal here is that neither tx1 nor tx2 gets mined
- now the attacker waits.
- at block 2000, the CSV for "high-value" chan2 has expired, and the remote broadcasts a 1-in-1-out tx that spends just that to_local
The class TxBatcher handles the creation, broadcast and replacement of replaceable transactions. Callers (LNWatcher, SwapManager) use methods add_payment_output and add_sweep_info. Transactions created by TxBatcher may combine sweeps and outgoing payments. TxBatcher is always used when sweeping. When sending outgoing payments with the GUI, its use is conditional to the config variable WALLET_BATCH_RBF. This means that this config variable essentially becomes a GUI setting. Transactions created by TxBatcher will have their fee bumped automatically (this was only the case for sweeps before). wallet: - instead of reading config variables, make_unsigned_transaction takes new parameters: base_tx, send_change_to_lighting tests: - add tests in test_txbatcher.py - remove test_sswaps.py - force regtests to use MPP, so that we sweep transactions with several HTLCs. This forces the payment manager to aggregate HTLC tx inputs. GUI: - if coin control is active, disable batching - forward submarine swaps can be batched Note: If batching is enabled, the GUI will not merge duplicate outputs. To enable that, we would need extra logic that matches txbatcher._base_tx outputs to txbatcher.batch_payments (Note that this is also what we would need in order to make payments robust to reorgs, so it might be worth considering at one point.)
ccb0cc3
to
3325ca1
Compare
note: this is not "reorg safe", meaning that there is no guarantee that all payments will end up in in the blockchain.
However, I believe this is "double send safe", meaning that we will never send a payment twice.