It seems like lnd sends this error whenever something wrong happens on
their side, regardless of whether the channel actually needs to be closed.
We ignore it to avoid paying the cost of a channel force-close, it's up
to them to broadcast their commitment if they wish.
See https://github.com/lightningnetwork/lnd/issues/7657 for example.
We were creating an index on the `remote_node_id` based on the channel's
JSON serialization, which isn't very robust. The data model changes for
splicing have changed the JSON format and thus broken that index.
We now use and explicit DB column for `remote_node_id`.
We add a `cpfpbumpfees` API that lets node operators bump the fees
of a package of unconfirmed transactions.
Node operators can for example ensure their funding txs confirm before
they hit the `2016` funding timeout. It's also very useful when you have
a long chain of unconfirmed funding transactions and/or mutual close
transactions and want to bump them all at once.
NB: the node operator needs to figure out which outpoints belong to him
(which should be fairly easy using existing APIs).
Included doc folder in the assembly zip. ** wildcard indicates all the files in the current folder and all the files in the subfolders of the current folder.
Fixes#1645
There was a confusion between `fundingKeyPath` and `channelKeyPath`.
Also simplified the funding key derivation. It's not backward compatible but current version of the code doesn't run on mainnet so it's fine.
---------
Co-authored-by: Bastien Teinturier <31281497+t-bast@users.noreply.github.com>
* do not check features at all for splices
It makes more sense than having to announce the feature to all peers.
* check actual min_conf value in swichToZeroConf
When we receive an early `channel_ready`, we may decide to not wait for
confirmations even if the channel isn't formally zero-conf. However, when
checking whether the channel is zero-conf, we currently only look at the
`ZeroConf` feature, which is incorrect in the general case.
This is normally invisible, because the race between:
- local `WatchFundingTxPublished` event from the watcher
- remote `ChannelReady`
is normally won by `WatchFundingTxPublished`. But in tests
`ChannelReady` usually wins.
It causes us to go through the `swichToZeroConf` path even if the
channel is already zero-conf, which then leads us to send a
`splice_locked` for the initial funding tx.
* check features before ignoring sig at reconnection
Legacy single-funding channels may have
`localFundingStatus=SingleFundedUnconfirmedFundingTx`, so we cannot
just rely on the absence of `signedTx`.
* fix post-migration startup of channels
Legacy single-funded channels will all be restored with a
`localFundingStatus=SingleFundedUnconfirmedFundingTx`, whatever the
actual status of the funding confirmation. The idea is that we
immediately put a `watchFundingConfirmed()` and set the correct state
shortly after the first startup.
However, we currently also send a `GetTxWithMeta`, which we should only
do for channels that are in state `WAIT_FOR_FUNDING_CONFIRMED`,
otherwise we will have loads of `unhandled event GetTxWithMetaResponse`.
* ignore watcher events in CLOSED
With splices, notifications from the watcher are tricky, there may be
races due to dealing with multiple funding txs. When we eventually go to
the `CLOSED` state, we should ignore those events, otherwise they may
wrongly cause us to go back to `CLOSING`.
* use correct expiry when accepting splice
The non-initiator must use the `locktime` provided in the `splice_init`
and not choose its own, otherwise transactions and signatures won't
match if we splice around the time a block has been found.
We can recompute `minDepth` based on our default config, the channel
capacity and our local features.
The only parameter that could change is our local features, which could
create issues if we enable/disable zero-conf in the middle of a funding
attempt: we may accept an RBF attempt for a transaction that we previously
treated as zero-conf, which will break the channel. But since activating
zero-conf means we have trust in our peer, and this is an unlikely
scenario, this is acceptable.
Co-authored-by: Pierre-Marie Padiou <pm47@users.noreply.github.com>
A peer that receives Disconnect *may* also be sent the Init message. The Init message is ignored by the two disconnect tests so these tests should occur after all of the tests that do not result in a disconnect. Otherwise the extra Init triggers an error when we expect no additional messages to be sent to the peer.
We now reject onion message payloads that contain unexpected fields and classify final payloads as being either an invoice request, an invoice response, an error or an invalid payload.
Each of these cases are mutually exclusive, it is not allowed to send both an invoice request and an invoice at the same time for instance.
Invalid payloads are not dropped immediately so that if they are the response we were waiting for, we can stop waiting and return an error without retrying.
The lengths of the encrypted recipient data leak the base fees as they are encoded with a variable length, to compensate for that we always add padding.
Funding pubkeys are now dynamic and change for each splice.
Main changes:
- `remoteFundingPubkey` has been moved from `RemoteParams` to `Commitment`
- `localFundingPubkey` is computed by appending the `fundingTxIndex` to a new dedicated `fundingKeyPath`. As a nice side-effect, the resulting funding pubkey is constant across rbf attempts. Also, there is no change in the data model, since the base `fundingKeyPath` is constant and still belongs to `LocalParams`.
---------
Co-authored-by: Bastien Teinturier <31281497+t-bast@users.noreply.github.com>
Add support for both splice-in and splice-out in Eclair. Mixing concurrent local/remote splice-in/splice-out is wired, although not supported in the API.
The implementation differs from the current wip BOLT proposal on at least the following points:
- we use a poor man's _quiescence_ protocol which just rejects the splice if the channel is not idle
- splice txs always _spend_ the previous funding/splice tx, even if it isn't confirmed yet and could theoretically be RBFed. This is done to be compatible with zero-conf splices
- the persistence/reconnection follows the logic described in https://gist.github.com/t-bast/1ac31f4e27734a10c5b9847d06db8d86.
We add a new `fundingTxIndex` to `Commitment`, which has two nice advantages:
- making debug much easier compared to dealing with txid:
`splice=1 is now active, removed=0 remaining=2,1`
- allowing to discriminate between initial funding, splices, rbf, and
combinations thereof.
We closely mimick RBFing the initial funding tx (e.g. `RbfStatus` vs `SpliceStatus`).
---------
Co-authored-by: Bastien Teinturier <31281497+t-bast@users.noreply.github.com>
* Allow negative contributions in InteractiveTxBuilder
When splicing over an existing channel, it is simpler to specify the amount
we will add or remove from the channel, rather than the resulting amount
that takes the previous balance into account. It removes the need for
truncating msat balances and trivially carries over those balances to
the new commitment.
* Update RBF contributions to allow negative amounts
When RBF-ing a splice, we will need to be consistent with the
`splice_init` and `splice_ack` messages and specify the funds we're
adding or removing from the channel instead of the absolute value.
We thus change the `tx_init_rbf` and `tx_ack_rbf` messages to use signed
amounts.
* Remove `max-funding-satoshis` config
This configuration parameter doesn't provide any meaningful guarantees
since there are no limits on the number of channels remote peers can open
to us. It's better to remove this check from eclair and let plugins decide
whether to accept channels or not, based on whatever metric makes sense
for their usecase.
* Remove wumbo from permanent channel features
We've never been really convinced that this feature made sense to keep in
the channel features. It becomes especially weird with splicing, since a
channel may initially be larger than the wumbo size, but could then shrink
and become smaller than that threshold. Similarly, a channel that is
initially below the wumbo size may exceed the wumbo size after a splice-in.
We must store the channel state after sending our `tx_signatures`,
because our peer may broadcast the transaction at that point.
But it's useful to store it earlier than that, to allow resuming the
signatures exchange if we get disconnected, otherwise we could
end up in a state where one peer has forgotten the channel while
the other has sent `tx_signatures` and must thus wait for the
channel to be spent or double-spent.
With that change, we can cleanly handle any disconnection:
- if we get disconnected before any peer sent `commitment_signed`,
the channel is forgotten by both peers
- if we get disconnected after one peer send `commitment_signed`
but the other peer didn't, the channel will be forgotten when
reconnecting (this is safe because the peer that sent
`commitment_signed` did *not* send `tx_signatures` since the
other peer didn't send `commitment_signed`)
- if we get disconnected after both peers sent `commitment_signed`,
we simply resume the signatures exchange on reconnection
We introduce a new TLV field to `channel_reestablish` that contains
the `txid` of the funding transaction that is partially signed: this lets
peers figure out which messages to send back on reconnection.
Main behavior changes (see commit messages for details):
- channel opening errors are returned with a 200/OK status from the api
- we return a success in the case of dual-funding or rbf, if the interactive tx has completed, even if the publish fails
- for rbf, we send the success response later in the flow: only when the rbf flow is successful, as opposed to when we initiate it
This is a prerequisite to splices, but also a first step towards reworking the channel request/response mechanism.
Co-authored-by: Bastien Teinturier <31281497+t-bast@users.noreply.github.com>
Because offers are a very generic mechanism, handling them can require interacting with an inventory system (do we actually have the quantity that the payer is requesting) or other such systems which do not have their place inside eclair. For this reason offer handlers must be implemented as plugins that communicate with the offer manager.
On startup, the offer handlers must register their offers with the offer manager, the offer manager will then forward the invoice requests and blinded payments to the relevant offer handler for approval.
If we didn't plan on using zero-conf, but our peer sends us an early
channel_ready, we can opportunistically switch to zero-conf.
But we can only do that if we're sure that our peer cannot double-spend
the funding transaction. We previously checked their contribution to the
funding output, but that's not enough: they may add inputs to the funding
transaction even if they don't contribute to the funding output.
We were also setting duplicate `WatchPublished` in case we were already
using zero-conf, which is now fixed.
When our peer sends us channel_ready while we're still waiting for
confirmations, we may opportunistically switch to zero-conf, in which
case we have both a WatchPublished and a WatchConfirmed pending.
But it may not actually be a real switch to zero-conf: maybe the
transaction is confirmed, and they simply received the block slightly
before us. In that case, the WatchConfirmed may trigger first, and it
would be inefficient to let the WatchPublished override our funding
status: it will make us set a new WatchConfirmed that will instantly
trigger and rewrite the funding status again.
After calling this method, we perform actions at several places that
only make sense if the correct behavior happened. Instead of assuming
things went ok, we use proper typing and make the result explicit.
Each RBF attempt adds more data that we need to store and process,
so we want to limit our peers to a reasonable use of RBF.
We send a warning to let them know that they are close to reaching the
limits.
There are two possible strategies for MetaCommitments:
1. Move the duplication between every Commitment in a shared field
2. Access duplicated fields through the first Commitment
I initially wanted to go with solution 1., but it makes commitments really
hard to work with. The main reason is that the `CommitmentSpec` abstraction
is a very good abstraction to work with when updating commitments,
and it requires keeping the htlcs inside every commitment.
As long as we update commitments in a loop, there is no risk of common
values being desynchronized. Since they contain mostly pointers to shared
data, the memory overhead is negligible, as long as we make sure we don't
duplicate the data when serializing to disk. We're thus choosing solution 2.
We serialize htlcs separately and rebuild `CommitmentSpec` objects when
deserializing. We also only keep the main htlc fields during json serialization
to avoid performance issues when writing json fields to the DB. There was
no good reason to serialize everything as we previously did.
This requirement was always met for single-funded channels, but for dual
funded it may not be met if the non-initiator contributes a very large
amount (more than 100x what the initiator contributes). That scenario
could happen, and the funding attempt shouldn't fail as long as the
initiator's balance contains enough funds to pay the commit tx fees.
Adding splicing to the InteractiveTxBuilder requires:
- adding a shared input (the current funding output)
- adding outputs (when splicing out)
The `localAmount` and `remoteAmount` provided are the amounts each peer
contributes to the new funding output. Those amounts should be computed
by the caller depending on what they intend to do (splice funds in/out).
This model allows batching operations: if the caller wants to do several
splices in and several splices out, this is easy to do by iterating over
these operations and updating the targeted `localAmount` accordingly,
the InteractiveTxFunder will take care of the rest.
Note that when splicing, we currently truncate previous balances to sats.
This results in 1 sat being given away to miners as fees, and a passive
participant losing up to 999 msat of their balance. This can be changed
in the future depending on the final spec choice, and shouldn't need a
codec update since the previous balances come from the commitment field
provided in the purpose.
When splicing, the tx_add_input message we send for the shared input
doesn't contain the previous transaction. There are two reasons for
that:
- it potentially doesn't fit into a lightning message (if > 65kB)
- we don't need it, we know it correctly uses segwit
The ReplaceableTxPublisher only looked at the remote commit and ignored
the case where our peer publishes their next commit. This created two
issues:
- eclair would keep trying to publish htlc transactions that had no chance
of confirming, which is a waste of resources
- eclair would fail to RBF claim-htlc transactions: this had no impact today
because we currently target the next 2 blocks so RBF isn't necessary,
but it will become useful if we allow setting a more economical block
target in the future
We used to consider zero-conf funding txs as _confirmed_ as soon as they were _published_, but it was hacky. Before splices, it prevented RBF-ing zero-conf txs, which in theory make sense (we start using the channel without confirmations, but may still want to bump the fees later). After splices, it would prevent using a channel while a splice tx is pending confirmation (even if the channel isn't zero-conf).
What we are really doing is separating the state of the channel, from the state of the blockchain. As a consequence the management of the funding tx is more independent from the channel FSM, which is why we end up with catch-all handlers for `WatchPublishedTriggered`/`WatchFundingConfirmedTriggered`.
As a side effect, we also simplify how we put watchers: instead of putting them back at every connection, we do this once and for all (either when creating the channel, or at restart).
---------
Co-authored-by: t-bast <bastuc@hotmail.fr>
That additional event lets subcribers know when a channel was closed
without ever being used.
The possible flows for a channel lifecycle are now:
- ChannelCreated -> ChannelOpened -> ChannelClosed
- ChannelCreated -> ChannelOpened -> ChannelAborted
- ChannelCreated -> ChannelAborted
- ChannelCreated -> ChannelClosed
- ChannelAborted
Co-authored-by: Richard Myers <remyers@yakshaver.org>
We previously inflated the funding amount to always exceed the dust limit,
otherwise bitcoind would reject the funding attempt. It complicated the
code for a scenario that doesn't make sense in practice, so it's better
to just let the funding attempt fail in that case.
We also randomize the order of inputs and outputs, which is a common
privacy best practice.
It makes sense to store the params with each `rbf` attempt (as
opposed to the last one currently), and allows us to remove
`DATA_WAIT_FOR_DUAL_FUNDING_CONFIRMED.fundingParams`.
In case we are the introduction node of a message blinded route, we would not be able to send a message to that route. We now unwrap the first hop in case the route starts with us.
The order of the elements in a TLV stream is an implementation detail that will disappear with serialization. Equality between TlvStream shouldn't depend on this order.
For that we use `Set`s instead of `Iterable`s.