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

Feature Request: Speed up channel opening via CPFP+RBF #475

Open
ZmnSCPxj opened this issue Dec 27, 2017 · 19 comments
Open

Feature Request: Speed up channel opening via CPFP+RBF #475

ZmnSCPxj opened this issue Dec 27, 2017 · 19 comments

Comments

@ZmnSCPxj
Copy link
Collaborator

ZmnSCPxj commented Dec 27, 2017

An idea, is to ensure as much as possible that channel funding transactions have a "change" output that is controlled by the built-in onchain wallet.

Then, if the funding transaction takes a long time to confirm on mainnet (due to the well-known congestion problems there), it would be possible to accelerate the channel funding transaction by creating a child transaction from the "change" output. This child transaction would have a larger-than-usual feerate (CPFP) to encourage mining of the channel funding transaction, and pay out to yet another address of the built-in onchain wallet. In addition, the child transaction would be marked RBF so that further increases of the fee can be done.

Using CPFP to speed up the channel funding transaction has the advantage that it requires no change to the Lightning BOLT protocol. Using RBF on the channel funding transaction directly requires that the both ends of the channel sign new versions of the commitment transaction.

While not a priority, I suspect this feature would be necessary before we can practically deploy to mainnet.


At the JSON-RPC level, I imagine:

# open the channel but do not wait to confirm
tx=$(lightning-cli fundchannelasync $node $satoshis)
sleep 1h
# Not yet confirmed even after one hour...?
if [$(lightning-cli fundchannelconfirmed $tx) = False]; then
  # If we accelerate, how much more will we spend?
  moresatoshis=$(lightning-cli fundchannelaccelerateestimate $tx)
  # so expensive! can we afford it?
  if [$moresatoshis < $satoshithreshhold]; then
    # fine, pay more to speed it up.
    lightning-cli fundchannelaccelerate $tx
  fi
fi

Further calls to fundchannelaccelerate will be possible and will RBF the child transaction instead.

The fundchannelaccelerate command will silently fail without an error if the specified channel is already confirmed at least once.


Implementation-wise, we would need a new table for pending channel funding transactions. Entries in this table will be removed when the peer enters CHANNELD_NORMAL.

CREATE TABLE funding_tx
  ( id INTEGER PRIMARY KEY REFERENCES channel(id) ON DELETE CASCADE
  , funding_tx_change_outnum INTEGER
  , cpfp_feerate INTEGER
  );

When initially created only the funding_tx_change_outnum column has a non-null value. The first fundchannelaccelerate will fill in cpfp_feerate, with future calls bumping the feerate and recreating the CPFP acceleration transaction. fundchannelaccelerateestimate will compute a fee based on whether cpfp_feerate exists or not and assuming a simple 1-input 1-output RBF transaction. Care point is when the change output runs out: this will cause acceleration to fail as there is no longer any change to pay with.

Since lightningd will now scan UTXOs that appear onchain we should not use addfunds especially since the child transaction is RBF.

@ZmnSCPxj
Copy link
Collaborator Author

Thinking more, it seems that using RBF for onchain withdrawals requires withdrawals to wait for channel funding to confirm at least once, and channel funding to wait for withdrawals to confirm at least once.

The issue with using RBF is the below race condition:

  1. We currently have an RBF transaction T in the miner mempool.
  2. The client requests an acceleration, causing us to generate a higher-feerate RBF transaction U.
  3. We create this RBF transaction U and broadcast it.
  4. Before U reaches the miner mempool, the miner finds a block and includes T rather than U (because U has not reached it).

Thus we cannot spend from the change output of an RBF transaction until the RBF transaction confirms deeply enough. At the minimum, we should suspend fundchannel commands if there are pending RBF withdrawals and there are not enough unspent coins, but the RBF withdrawal change output does have enough coins.

There is also the question of the below race condition:

  1. We currently have an RBF transaction T in the miner M and N mempool.
  2. The client requests an acceleration, causing us to generate a higher-feerate RBF transaction U.
  3. We create this RBF transaction U and broadcast it.
  4. U reaches the miner M mempool (but not yet miner N).
  5. Before U reaches the miner N mempool, the miner N finds a block and includes T rather than U (because U has not reached miner N yet). We receive this block.
  6. Miner M finds two blocks (orphaning the block from miner N), with the first block containing U.

In the above case we should use the transaction U and not T. Of course, deeper reorgs are also theoretically possible. Note that this is also important when we are receiving funds.

RBF gets complicated, so I suppose there is a reason why it is not well-implemented.

Ideally we should be able to accelerate both withdrawals and channel funding.

@Sjors
Copy link
Contributor

Sjors commented Jan 19, 2018

CPFP could be useful as a workaround, but I generally don't like it because it's quite expensive. If transaction A paid $1 in fees and is stuck, then in order to increase its effective fee to $1.50, you need to generate a child transaction B with $2 in fees. So you'd be spending $2, whereas RBF would only cost $0.50 (assuming equal size).

It's also cheaper to avoid change outputs when opening a channel, see #665.

I opened #668 to keep track of both approaches.

@ZmnSCPxj
Copy link
Collaborator Author

CPFP+RBF is simpler to implement under our current one-peer-one-channel architecture. Of course we probably want to have multiple channels per peer eventually, there are a few FIXME comments referring to those. Even so CPFP+RBF is still simpler to implement than my full RBF algo.

@ZmnSCPxj
Copy link
Collaborator Author

ZmnSCPxj commented Jul 6, 2020

A more general approach would be to have txaccelerateestimate and txaccelerate. Then, not only fundchannel txid would be accepted, but also withdraw txid (and more generally, txsend/signpsbt txid) as well.

How to implement that is something of an open question however.

We can CPFP by creating a new transaction spending from the change output of any transaction we created ourselves. However, this new CPFP transaction should be RBF-able, so that further txaccelerate would just replace that transaction instead of chaining yet another CPFP on top of it.

But supporting RBF probably means additional complexity in our wallet logic. In particular, if we implement txaccelerate as plugin on top of fundinputs/reserveinputs/unreserveinputs/signpsbt/sendpsbt we have to be able to "unspend" an input in order to sign a higher-fee RBF. I think.

Thoughts @niftynei @rustyrussell ? I think you are the main people hacking around the new PSBT-based wallet bits currently.

@ZmnSCPxj
Copy link
Collaborator Author

ZmnSCPxj commented Jul 6, 2020

Anyway here is a new proposed interface:

txacceleratestart txid => txacc_id

Starts a transaction acceleration session. No additional funds will be spent, but creates an object, a txacc session, with a unique txacc_id, for use with other txaccelerate.

The txacc session is automatically destroyed when lightningd shuts down, or when the transaction originally being accelerated has been confirmed.

txaccelerateestimate txacc_id => total_fee delta_fee max_fee

For the given txacc session, gives an estimate on how much more a succeeding txaccelerate will cost. This might change when called multiple times, depending on current mempool/blockchain fee estimation.

max_fee is the amount that can be put into fees. If total_fee reaches max_fee and delta_fee is zero, then it is not possible to accelerate the transaction further, either because of lack of funds in the wallet, or if we are using CPFP to accelerate the transaction and there is insufficient change to accelerate the funds.

This may error with error code TXACCELERATE_ID_NOT_FOUND if the transaction was already confirmed.

txaccelerate txacc_id [total_fee] => total_fee delta_fee max_fee

Actually attempt to accelerate the transaction originally specified in txacceleratestart. If txaccelerateestimate was called, uses the most recent estimate, otherwise it will perform the estimation and then apply it immediately.

You may override the total_fee from the most recent estimation, providing a total_fee that is equal or larger than the total_fee from the most recent estimation, but less than the max_fee.

This may fail with error code TXACCELERATE_ID_NOT_FOUND if the transaction was already confirmed.

commentary

The above interface gives users some protection from lower-level details.

  • With fundchannel v1 protocol (i.e. the current fundchannel protocol in BOLT1.1), we have to use CPFP, we cannot implement the RBF fundchannel because we do not support even multiple started channels per peer.
  • For simplicity, withdraw/txsend/sendpsbt transactions could also be CPFP-ed for now, for simplicity.
  • A withdraw/txsend/sendpsbt transaction where all inputs are owned only by our wallet, and which we know to be broadcast already, could in the future be RBFed instead of CPFPed.
  • As well, a fundchannel v2 transaction can be RBFed if all inputs are owned by us or by the peer.
  • Once we switch to RBF of any kind, the transaction being accelerated will not have a stable txid, so using a txacc_id gives the user a wrapper around this.
  • For now, if we do not want to support CPFP+RBF but support a single-shot CPFP, we can expose this as well using the above interface by making a single estimation with max_fee equal to total_fee, and once the single-shot CPFP has been, uh, shotted, then we set max_fee to total_fee with a delta_fee of 0 to indicate to the user that it cannot be accelerated further. This allows us to provide this interface minimally (just one shot, and the tx has to have a change output we can CPFP on) without having to handle any kind of RBF yet, at least for now.

What do you think? @Sjors @niftynei @rustyrussell @cdecker @darosior ?

@saubyk
Copy link

saubyk commented Mar 26, 2021

Hi @rustyrussell @cdecker
Is there any hope to get CPFP or RBF on pending channel open transactions?

The current state of mempool is making this a burning requirement, as a-lot of people inadvertently low ball the channel opening fee and then are stuck in mempool with pending transactions.

@kaloudis
Copy link

This would be an awesome addition to the project and would definitely be helpful in this fee environment

@niftynei
Copy link
Collaborator

niftynei commented Mar 26, 2021 via email

@jaonoctus
Copy link

The withdraw command worked for me (as CPFP bump).

lightning-cli withdraw NEW_CHANGE_ADDRESS all 100000 0 [FUNDING_UNCONFIRMED_TXID:CURRENT_CHANGE_INDEX]

* 100000 = 100sat/vbyte

@urza
Copy link
Contributor

urza commented May 24, 2021

RBF has been implemented for experimental dual-funding/v2 opens.

Reviving this old thread, but I got into situation where I opened channel with feerate=low and mempool started filling up. Because I opened from whole utxo (no change), I can't use CPFP.

In this situation RBF bump fee option would be useful even in regular, not dual funding channel.

LND has possibility to do lncli wallet bumpfee --sat_per_byte XXX TXID:NUMBER but if I understand correctly it is not possible with c-lightning? Would be super useful.

/me now going to quietly panic about potentially stuck channel opening

@Surenic
Copy link

Surenic commented Mar 17, 2023

want to bring this up again. I have 3 closing txs stuck in mempool and no idea what to do

Is a CPFP possible? If yes, how? What about the withdraw command here?

The withdraw command worked for me (as CPFP bump).

lightning-cli withdraw NEW_CHANGE_ADDRESS all 100000 0 [FUNDING_UNCONFIRMED_TXID:CURRENT_CHANGE_INDEX]

* 100000 = 100sat/vbyte

Please, I really need help to get those tx confirmed

@saubyk
Copy link

saubyk commented Mar 20, 2023

Hi @Surenic what type of channel close are we talking about here, coop or force?
If it's a coop close, and the outputs are sizable, you should be able to CPFP with the withdraw command as described above. Lookup the unconfirmed transactions in the mempool to find the correct index

If it's a force close and you initiated the close, you would need to wait till your lock time (or CSV delay) before you are in a position to CPFP the transaction

@Surenic
Copy link

Surenic commented Mar 20, 2023

It is indeed a coop close. How do I know if the outputs are sizable?

And what would be the CHANGE_INDEX then?

Can you help me building the right command? Don't want to make anything wrong

lightning-cli withdraw NEW_CHANGE_ADDRESS all 30000 0 [cadb70f6b467f79a7fe969fe5b465f82d7369d3b4f6e3db2215c5a2ff2778085:CURRENT_CHANGE_INDEX]

Filled in the tx already and set fees to 30 sats/vB

NEW_CHANGE_ADDRESS - can I use any address or does it have to be a CLN Wallet address?

Right now my CLN wallet is completely empty. Would the fees be taken from the original UTXOs or do I need to put some sats on it?

🙏 thank you

@saubyk
Copy link

saubyk commented Mar 20, 2023

Hi @Surenic, here's what you can do:

  1. Look at the transaction in the mempool and find out the index of the output which is paying to you (this should be the output paying the local balance amount back to you). That index value should be used for CURRENT_CHANGE_INDEX
  2. Generate an address via the CLN wallet (it has to be this, because you're re-spending the unconfirmed output from your node)
  3. Try to go a bit higher than the high priority rate

The fee will be taken from the unconfirmed output, so you don't have to worry about not having any sats on your wallet.
Hope this helps

@Surenic
Copy link

Surenic commented Mar 20, 2023

Thank you @saubyk,

unfortunately I get an Unknown UTXO error. I am sure that I have the correct txid and change_index (0), but it doesn't work. I was thinking that the tx is out of my mempool so I even resent it via bitcoin-cli sendwartransaction txhex, but this doesn't work too. I am out ideas now

@saubyk
Copy link

saubyk commented Mar 20, 2023

Not sure if this will help, but you can try lightning-cli sendrawtransaction once

@Surenic
Copy link

Surenic commented Mar 21, 2023

I did that but the command was calling for allowhighfees. As I tried to add this it said that allowhighfees has to be true or false :/ If you have a solution to this as I can't find any, it would be nice but otherwise I am giving up on this. Gladly the stuck amount is not much on my side. The peer is Kraken, so they won't do anything too I guess 🤣

But I definitely want to say thank you again for not giving up on helping me :)

edit: allowhighfees is a boolean so I had to set it to true. the tx was then sent with a success message. But anyway, the withdraw command fails with an unknown UTXO. Just to be clear, don't have anything to hide here anymore:

https://mempool.space/tx/cadb70f6b467f79a7fe969fe5b465f82d7369d3b4f6e3db2215c5a2ff2778085

the unconfirmed tx, its tx hash equals the txid? the output, which belongs to me is 0

so this command should be correct?

lightning-cli withdraw bc1qs7665seq49f0dmc0nsv2cnrdmprwrjalsmxdqq all 40000 0 '["cadb70f6b467f79a7fe969fe5b465f82d7369d3b4f6e3db2215c5a2ff2778085:0"]'

@saubyk
Copy link

saubyk commented Mar 22, 2023

hi @Surenic the command you posted should've worked. It would help if you post the exact error message that you're getting when you execute it. My guess is that transaction is not in the mempool and I am unsure if sendrawtransaction is rebroadcasting it. You can try restarting the node once, and then try again, in case the restart rebroadcasts the pending transactions back into the mempool.

cc @rustyrussell in case you have any other insights

@Surenic
Copy link

Surenic commented Mar 22, 2023

Yes, sorry for that!

This is the output on sending the raw transaction by lightning-cli with allowhighfees=true

grafik

Even though I restart the node, the following error occurs on withdraw command

grafik

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants