ElementsProject / lightning

Core Lightning — Lightning Network implementation focusing on spec compliance and performance
Other
2.83k stars 896 forks source link

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

Open ZmnSCPxj opened 6 years ago

ZmnSCPxj commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

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 commented 4 years ago

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 commented 4 years ago

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.

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

saubyk commented 3 years ago

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 commented 3 years ago

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

niftynei commented 3 years ago

You can do CPFP today if you use an external wallet to make the funding tx and it has a change output.

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

On Fri, Mar 26, 2021 at 18:28 Evan Kaloudis @.***> wrote:

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

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ElementsProject/lightning/issues/475#issuecomment-808586617, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIMAKPPRS37PWSSS4SSRQTTFUKDJANCNFSM4EJUVVFA .

jaonoctus commented 3 years ago

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 commented 3 years ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

Surenic commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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