Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/release-notes/eclair-vnext.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,39 @@

## Major changes

### Channel Splicing

With this release, we add support for the final version of [splicing](https://github.com/lightning/bolts/pull/1160) that was recently added to the BOLTs.
Splicing allows node operators to change the size of their existing channels, which makes it easier and more efficient to allocate liquidity where it is most needed.
Most node operators can now have a single channel with each of their peer, which costs less on-chain fees and resources, and makes path-finding easier.

The size of an existing channel can be increased with the `splicein` API:

```sh
eclair-cli splicein --channelId=<channel_id> --amountIn=<amount_satoshis>
```

Once that transaction confirms, the additional liquidity can be used to send outgoing payments.
If the transaction doesn't confirm, the node operator can speed up confirmation with the `rbfsplice` API:

```sh
eclair-cli rbfsplice --channelId=<channel_id> --targetFeerateSatByte=<feerate_satoshis_per_byte> --fundingFeeBudgetSatoshis=<maximum_on_chain_fee_satoshis>
```

If the node operator wants to reduce the size of a channel, or send some of the channel funds to an on-chain address, they can use the `spliceout` API:

```sh
eclair-cli spliceout --channelId=<channel_id> --amountOut=<amount_satoshis> --scriptPubKey=<on_chain_address>
```

That operation can also be RBF-ed with the `rbfsplice` API to speed up confirmation if necessary.

Note that when 0-conf is used for the channel, it is not possible to RBF splice transactions.
Node operators should instead create a new splice transaction (with `splicein` or `spliceout`) to CPFP the previous transaction.

Note that eclair had already introduced support for a splicing prototype in v0.9.0, which helped improve the BOLT proposal.
We're removing support for the previous splicing prototype feature: users that depended on this protocol must upgrade to create official splice transactions.

### New MPP splitting strategy

Eclair can send large payments using multiple low-capacity routes by sending as much as it can through each route (if `randomize-route-selection = false`) or some random fraction (if `randomize-route-selection = true`).
Expand Down
3 changes: 2 additions & 1 deletion eclair-core/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ eclair {
option_zeroconf = disabled
keysend = disabled
option_simple_close=optional
option_splice = optional
trampoline_payment_prototype = disabled
async_payment_prototype = disabled
on_the_fly_funding = disabled
Expand All @@ -109,7 +110,7 @@ eclair {
funding {
// 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.
remote-rbf-limits {
max-attempts = 5 // maximum number of RBF attempts our peer is allowed to make
max-attempts = 10 // maximum number of RBF attempts our peer is allowed to make
attempt-delta-blocks = 6 // minimum number of blocks between RBF attempts
}
// Duration after which we abort a channel creation. If our peer seems unresponsive and doesn't complete the
Expand Down
17 changes: 7 additions & 10 deletions eclair-core/src/main/scala/fr/acinq/eclair/Features.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ object Features {
val mandatory = 28
}

// TODO: this should also extend NodeFeature once the spec is finalized
case object Quiescence extends Feature with InitFeature {
case object Quiescence extends Feature with InitFeature with NodeFeature {
val rfcName = "option_quiesce"
val mandatory = 34
}
Expand Down Expand Up @@ -312,6 +311,11 @@ object Features {
val mandatory = 60
}

case object Splicing extends Feature with InitFeature with NodeFeature {
val rfcName = "option_splice"
val mandatory = 62
}

case object PhoenixZeroReserve extends Feature with InitFeature with ChannelTypeFeature with PermanentChannelFeature {
val rfcName = "phoenix_zero_reserve"
val mandatory = 128
Expand Down Expand Up @@ -340,12 +344,6 @@ object Features {
val mandatory = 152
}

// TODO: @pm47 custom splices implementation for phoenix, to be replaced once splices is spec-ed (currently reserved here: https://github.com/lightning/bolts/issues/605)
case object SplicePrototype extends Feature with InitFeature {
val rfcName = "splice_prototype"
val mandatory = 154
}

case object SimpleTaprootChannelsPhoenix extends Feature with InitFeature with NodeFeature with ChannelTypeFeature {
val rfcName = "option_simple_taproot_phoenix"
val mandatory = 564
Expand Down Expand Up @@ -399,12 +397,12 @@ object Features {
ZeroConf,
KeySend,
SimpleClose,
Splicing,
SimpleTaprootChannelsPhoenix,
SimpleTaprootChannelsStaging,
WakeUpNotificationClient,
TrampolinePaymentPrototype,
AsyncPaymentPrototype,
SplicePrototype,
OnTheFlyFunding,
FundingFeeCredit,
PhoenixZeroReserve
Expand All @@ -423,7 +421,6 @@ object Features {
SimpleClose -> (ShutdownAnySegwit :: Nil),
SimpleTaprootChannelsPhoenix -> (ChannelType :: SimpleClose :: Nil),
AsyncPaymentPrototype -> (TrampolinePaymentPrototype :: Nil),
OnTheFlyFunding -> (SplicePrototype :: Nil),
FundingFeeCredit -> (OnTheFlyFunding :: Nil)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,14 @@ case class RemoteCommit(index: Long, spec: CommitmentSpec, txId: TxId, remotePer
commitmentFormat match {
case _: SegwitV0CommitmentFormat =>
val sig = remoteCommitTx.sign(fundingKey, remoteFundingPubKey)
Right(CommitSig(channelParams.channelId, sig, htlcSigs.toList, batchSize))
Right(CommitSig(channelParams.channelId, commitInput.outPoint.txid, sig, htlcSigs.toList, batchSize))
case _: SimpleTaprootChannelCommitmentFormat =>
remoteNonce_opt match {
case Some(remoteNonce) =>
val localNonce = NonceGenerator.signingNonce(fundingKey.publicKey, remoteFundingPubKey, commitInput.outPoint.txid)
remoteCommitTx.partialSign(fundingKey, remoteFundingPubKey, localNonce, Seq(localNonce.publicNonce, remoteNonce)) match {
case Left(_) => Left(InvalidCommitNonce(channelParams.channelId, commitInput.outPoint.txid, index))
case Right(psig) => Right(CommitSig(channelParams.channelId, psig, htlcSigs.toList, batchSize))
case Right(psig) => Right(CommitSig(channelParams.channelId, commitInput.outPoint.txid, psig, htlcSigs.toList, batchSize))
}
case None => Left(MissingCommitNonce(channelParams.channelId, commitInput.outPoint.txid, index))
}
Expand Down Expand Up @@ -676,7 +676,7 @@ case class Commitment(fundingTxIndex: Long,
case None => return Left(MissingCommitNonce(params.channelId, fundingTxId, remoteCommit.index + 1))
}
}
val commitSig = CommitSig(params.channelId, sig, htlcSigs.toList, batchSize)
val commitSig = CommitSig(params.channelId, fundingTxId, sig, htlcSigs.toList, batchSize)
val nextRemoteCommit = RemoteCommit(remoteCommit.index + 1, spec, remoteCommitTx.tx.txid, remoteNextPerCommitmentPoint)
Right((copy(nextRemoteCommit_opt = Some(nextRemoteCommit)), commitSig))
}
Expand Down Expand Up @@ -1117,9 +1117,11 @@ case class Commitments(channelParams: ChannelParams,
case _: CommitSig if active.size > 1 => return Left(CommitSigCountMismatch(channelId, active.size, 1))
case commitSig: CommitSig => Seq(commitSig)
}
// Signatures are sent in order (most recent first), calling `zip` will drop trailing sigs that are for deactivated/pruned commitments.
val active1 = active.zip(sigs).map { case (commitment, commit) =>
val active1 = active.zipWithIndex.map { case (commitment, idx) =>
// If the funding_txid isn't provided, we assume that signatures are sent in order (most recent first).
// This matches the behavior of peers who only support the experimental version of splicing.
val commitKeys = LocalCommitmentKeys(channelParams, channelKeys, localCommitIndex + 1, commitment.commitmentFormat)
val commit = sigs.find(_.fundingTxId_opt.contains(commitment.fundingTxId)).getOrElse(sigs(idx))
commitment.receiveCommit(channelParams, channelKeys, commitKeys, changes, commit) match {
case Left(f) => return Left(f)
case Right(commitment1) => commitment1
Expand Down
Loading