1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-23 22:46:44 +01:00

Add localChannelReserve and remoteChannelReserve (#2237)

This is easier to use than having to decide which params we should look
into (local or remote). It will also be easier to integrate with dual funding.

Rename initialFeeratePerKw: this name was very confusing.
This feerate only applies to the commit tx, so we make that explicit.
This commit is contained in:
Bastien Teinturier 2022-05-19 09:43:08 +02:00 committed by GitHub
parent bb7703aa5d
commit 03097b0d42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 157 additions and 100 deletions

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.channel
import akka.actor.{ActorRef, PossiblyHarmful}
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
import fr.acinq.bitcoin.scalacompat.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, Transaction}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, DeterministicWallet, OutPoint, Satoshi, SatoshiLong, Transaction}
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
import fr.acinq.eclair.payment.OutgoingPaymentPacket.Upstream
import fr.acinq.eclair.transactions.CommitmentSpec
@ -78,8 +78,8 @@ case object ERR_INFORMATION_LEAK extends ChannelState
case class INPUT_INIT_FUNDER(temporaryChannelId: ByteVector32,
fundingAmount: Satoshi,
pushAmount: MilliSatoshi,
initialFeeratePerKw: FeeratePerKw,
fundingTxFeeratePerKw: FeeratePerKw,
commitTxFeerate: FeeratePerKw,
fundingTxFeerate: FeeratePerKw,
localParams: LocalParams,
remote: ActorRef,
remoteInit: Init,
@ -196,7 +196,7 @@ final case class CMD_GET_CHANNEL_INFO(replyTo: ActorRef)extends HasReplyToComman
/** response to [[Command]] requests */
sealed trait CommandResponse[+C <: Command]
sealed trait CommandSuccess[+C <: Command] extends CommandResponse[C]
sealed trait CommandFailure[+C <: Command, +T <: Throwable] extends CommandResponse[C] { def t: Throwable }
sealed trait CommandFailure[+C <: Command, +T <: Throwable] extends CommandResponse[C] { def t: T }
/** generic responses */
final case class RES_SUCCESS[+C <: Command](cmd: C, channelId: ByteVector32) extends CommandSuccess[C]
@ -387,7 +387,7 @@ final case class DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId: ByteVector32
remoteParams: RemoteParams,
fundingAmount: Satoshi,
pushAmount: MilliSatoshi,
initialFeeratePerKw: FeeratePerKw,
commitTxFeerate: FeeratePerKw,
remoteFirstPerCommitmentPoint: PublicKey,
channelConfig: ChannelConfig,
channelFeatures: ChannelFeatures,
@ -399,7 +399,7 @@ final case class DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId: ByteVector32,
remoteParams: RemoteParams,
fundingAmount: Satoshi,
pushAmount: MilliSatoshi,
initialFeeratePerKw: FeeratePerKw,
commitTxFeerate: FeeratePerKw,
remoteFirstPerCommitmentPoint: PublicKey,
channelFlags: ChannelFlags,
channelConfig: ChannelConfig,
@ -466,7 +466,7 @@ case class LocalParams(nodeId: PublicKey,
fundingKeyPath: DeterministicWallet.KeyPath,
dustLimit: Satoshi,
maxHtlcValueInFlightMsat: UInt64, // this is not MilliSatoshi because it can exceed the total amount of MilliSatoshi
channelReserve: Satoshi,
requestedChannelReserve_opt: Option[Satoshi],
htlcMinimum: MilliSatoshi,
toSelfDelay: CltvExpiryDelta,
maxAcceptedHtlcs: Int,
@ -481,7 +481,7 @@ case class LocalParams(nodeId: PublicKey,
case class RemoteParams(nodeId: PublicKey,
dustLimit: Satoshi,
maxHtlcValueInFlightMsat: UInt64, // this is not MilliSatoshi because it can exceed the total amount of MilliSatoshi
channelReserve: Satoshi,
requestedChannelReserve_opt: Option[Satoshi],
htlcMinimum: MilliSatoshi,
toSelfDelay: CltvExpiryDelta,
maxAcceptedHtlcs: Int,

View file

@ -30,7 +30,7 @@ import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelUpdate}
trait ChannelEvent
case class ChannelCreated(channel: ActorRef, peer: ActorRef, remoteNodeId: PublicKey, isInitiator: Boolean, temporaryChannelId: ByteVector32, initialFeeratePerKw: FeeratePerKw, fundingTxFeeratePerKw: Option[FeeratePerKw]) extends ChannelEvent
case class ChannelCreated(channel: ActorRef, peer: ActorRef, remoteNodeId: PublicKey, isInitiator: Boolean, temporaryChannelId: ByteVector32, commitTxFeerate: FeeratePerKw, fundingTxFeerate: Option[FeeratePerKw]) extends ChannelEvent
// This trait can be used by non-standard channels to inject themselves into Register actor and thus make them usable for routing
trait AbstractChannelRestored extends ChannelEvent {

View file

@ -211,6 +211,12 @@ case class Commitments(channelId: ByteVector32,
val capacity: Satoshi = commitInput.txOut.amount
/** Channel reserve that applies to our funds. */
val localChannelReserve: Satoshi = remoteParams.requestedChannelReserve_opt.getOrElse(0 sat)
/** Channel reserve that applies to our peer's funds. */
val remoteChannelReserve: Satoshi = localParams.requestedChannelReserve_opt.getOrElse(0 sat)
// NB: when computing availableBalanceForSend and availableBalanceForReceive, the initiator keeps an extra buffer on
// top of its usual channel reserve to avoid getting channels stuck in case the on-chain feerate increases (see
// https://github.com/lightningnetwork/lightning-rfc/issues/728 for details).
@ -241,7 +247,7 @@ case class Commitments(channelId: ByteVector32,
// we need to base the next current commitment on the last sig we sent, even if we didn't yet receive their revocation
val remoteCommit1 = remoteNextCommitInfo.left.toOption.map(_.nextRemoteCommit).getOrElse(remoteCommit)
val reduced = CommitmentSpec.reduce(remoteCommit1.spec, remoteChanges.acked, localChanges.proposed)
val balanceNoFees = (reduced.toRemote - remoteParams.channelReserve).max(0 msat)
val balanceNoFees = (reduced.toRemote - localChannelReserve).max(0 msat)
if (localParams.isInitiator) {
// The initiator always pays the on-chain fees, so we must subtract that from the amount we can send.
val commitFees = commitTxTotalCostMsat(remoteParams.dustLimit, reduced, commitmentFormat)
@ -267,7 +273,7 @@ case class Commitments(channelId: ByteVector32,
lazy val availableBalanceForReceive: MilliSatoshi = {
val reduced = CommitmentSpec.reduce(localCommit.spec, localChanges.acked, remoteChanges.proposed)
val balanceNoFees = (reduced.toRemote - localParams.channelReserve).max(0 msat)
val balanceNoFees = (reduced.toRemote - remoteChannelReserve).max(0 msat)
if (localParams.isInitiator) {
// The non-initiator doesn't pay on-chain fees so we don't take those into account when receiving.
balanceNoFees
@ -372,15 +378,15 @@ object Commitments {
val funderFeeBuffer = commitTxTotalCostMsat(commitments1.remoteParams.dustLimit, reduced.copy(commitTxFeerate = reduced.commitTxFeerate * 2), commitments.commitmentFormat) + htlcOutputFee(reduced.commitTxFeerate * 2, commitments.commitmentFormat)
// NB: increasing the feerate can actually remove htlcs from the commit tx (if they fall below the trim threshold)
// which may result in a lower commit tx fee; this is why we take the max of the two.
val missingForSender = reduced.toRemote - commitments1.remoteParams.channelReserve - (if (commitments1.localParams.isInitiator) fees.max(funderFeeBuffer.truncateToSatoshi) else 0.sat)
val missingForReceiver = reduced.toLocal - commitments1.localParams.channelReserve - (if (commitments1.localParams.isInitiator) 0.sat else fees)
val missingForSender = reduced.toRemote - commitments1.localChannelReserve - (if (commitments1.localParams.isInitiator) fees.max(funderFeeBuffer.truncateToSatoshi) else 0.sat)
val missingForReceiver = reduced.toLocal - commitments1.remoteChannelReserve - (if (commitments1.localParams.isInitiator) 0.sat else fees)
if (missingForSender < 0.msat) {
return Left(InsufficientFunds(commitments.channelId, amount = cmd.amount, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = if (commitments1.localParams.isInitiator) fees else 0.sat))
return Left(InsufficientFunds(commitments.channelId, amount = cmd.amount, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.localChannelReserve, fees = if (commitments1.localParams.isInitiator) fees else 0.sat))
} else if (missingForReceiver < 0.msat) {
if (commitments.localParams.isInitiator) {
// receiver is not the channel initiator; it is ok if it can't maintain its channel_reserve for now, as long as its balance is increasing, which is the case if it is receiving a payment
} else {
return Left(RemoteCannotAffordFeesForNewHtlc(commitments.channelId, amount = cmd.amount, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = fees))
return Left(RemoteCannotAffordFeesForNewHtlc(commitments.channelId, amount = cmd.amount, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteChannelReserve, fees = fees))
}
}
@ -441,13 +447,13 @@ object Commitments {
val fees = commitTxTotalCost(commitments1.remoteParams.dustLimit, reduced, commitments.commitmentFormat)
// NB: we don't enforce the funderFeeReserve (see sendAdd) because it would confuse a remote initiator that doesn't have this mitigation in place
// We could enforce it once we're confident a large portion of the network implements it.
val missingForSender = reduced.toRemote - commitments1.localParams.channelReserve - (if (commitments1.localParams.isInitiator) 0.sat else fees)
val missingForReceiver = reduced.toLocal - commitments1.remoteParams.channelReserve - (if (commitments1.localParams.isInitiator) fees else 0.sat)
val missingForSender = reduced.toRemote - commitments1.remoteChannelReserve - (if (commitments1.localParams.isInitiator) 0.sat else fees)
val missingForReceiver = reduced.toLocal - commitments1.localChannelReserve - (if (commitments1.localParams.isInitiator) fees else 0.sat)
if (missingForSender < 0.sat) {
return Left(InsufficientFunds(commitments.channelId, amount = add.amountMsat, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.localParams.channelReserve, fees = if (commitments1.localParams.isInitiator) 0.sat else fees))
return Left(InsufficientFunds(commitments.channelId, amount = add.amountMsat, missing = -missingForSender.truncateToSatoshi, reserve = commitments1.remoteChannelReserve, fees = if (commitments1.localParams.isInitiator) 0.sat else fees))
} else if (missingForReceiver < 0.sat) {
if (commitments.localParams.isInitiator) {
return Left(CannotAffordFees(commitments.channelId, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.remoteParams.channelReserve, fees = fees))
return Left(CannotAffordFees(commitments.channelId, missing = -missingForReceiver.truncateToSatoshi, reserve = commitments1.localChannelReserve, fees = fees))
} else {
// receiver is not the channel initiator; it is ok if it can't maintain its channel_reserve for now, as long as its balance is increasing, which is the case if it is receiving a payment
}
@ -558,9 +564,9 @@ object Commitments {
// a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee
// we look from remote's point of view, so if local is initiator remote doesn't pay the fees
val fees = commitTxTotalCost(commitments1.remoteParams.dustLimit, reduced, commitments.commitmentFormat)
val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteParams.channelReserve - fees
val missing = reduced.toRemote.truncateToSatoshi - commitments1.localChannelReserve - fees
if (missing < 0.sat) {
return Left(CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.remoteParams.channelReserve, fees = fees))
return Left(CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localChannelReserve, fees = fees))
}
// if we would overflow our dust exposure with the new feerate, we avoid sending this fee update
@ -608,9 +614,9 @@ object Commitments {
// a node cannot spend pending incoming htlcs, and need to keep funds above the reserve required by the counterparty, after paying the fee
val fees = commitTxTotalCost(commitments1.localParams.dustLimit, reduced, commitments.commitmentFormat)
val missing = reduced.toRemote.truncateToSatoshi - commitments1.localParams.channelReserve - fees
val missing = reduced.toRemote.truncateToSatoshi - commitments1.remoteChannelReserve - fees
if (missing < 0.sat) {
return Left(CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.localParams.channelReserve, fees = fees))
return Left(CannotAffordFees(commitments.channelId, missing = -missing, reserve = commitments1.remoteChannelReserve, fees = fees))
}
// if we would overflow our dust exposure with the new feerate, we reject this fee update

View file

@ -17,10 +17,10 @@
package fr.acinq.eclair.channel
import akka.event.{DiagnosticLoggingAdapter, LoggingAdapter}
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.bitcoin.scalacompat.Crypto.{PrivateKey, PublicKey, sha256}
import fr.acinq.bitcoin.scalacompat.Script._
import fr.acinq.bitcoin.scalacompat._
import fr.acinq.bitcoin.ScriptFlags
import fr.acinq.eclair._
import fr.acinq.eclair.blockchain.OnChainAddressGenerator
import fr.acinq.eclair.blockchain.fee.{FeeEstimator, FeeTargets, FeeratePerKw}
@ -254,8 +254,8 @@ object Helpers {
}
val toRemoteSatoshis = remoteCommit.spec.toRemote.truncateToSatoshi
// NB: this is an approximation (we don't take network fees into account)
val result = toRemoteSatoshis > commitments.remoteParams.channelReserve
log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.remoteParams.channelReserve} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}")
val result = toRemoteSatoshis > commitments.localChannelReserve
log.debug(s"toRemoteSatoshis=$toRemoteSatoshis reserve=${commitments.localChannelReserve} aboveReserve=$result for remoteCommitNumber=${remoteCommit.index}")
result
}
@ -289,20 +289,21 @@ object Helpers {
*
* @return (localSpec, localTx, remoteSpec, remoteTx, fundingTxOutput)
*/
def makeFirstCommitTxs(keyManager: ChannelKeyManager, channelConfig: ChannelConfig, channelFeatures: ChannelFeatures, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushMsat: MilliSatoshi, initialFeeratePerKw: FeeratePerKw, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx)] = {
def makeFirstCommitTxs(keyManager: ChannelKeyManager, channelConfig: ChannelConfig, channelFeatures: ChannelFeatures, temporaryChannelId: ByteVector32, localParams: LocalParams, remoteParams: RemoteParams, fundingAmount: Satoshi, pushMsat: MilliSatoshi, commitTxFeerate: FeeratePerKw, fundingTxHash: ByteVector32, fundingTxOutputIndex: Int, remoteFirstPerCommitmentPoint: PublicKey): Either[ChannelException, (CommitmentSpec, CommitTx, CommitmentSpec, CommitTx)] = {
val toLocalMsat = if (localParams.isInitiator) fundingAmount.toMilliSatoshi - pushMsat else pushMsat
val toRemoteMsat = if (localParams.isInitiator) pushMsat else fundingAmount.toMilliSatoshi - pushMsat
val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], initialFeeratePerKw, toLocal = toLocalMsat, toRemote = toRemoteMsat)
val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], initialFeeratePerKw, toLocal = toRemoteMsat, toRemote = toLocalMsat)
val localSpec = CommitmentSpec(Set.empty[DirectedHtlc], commitTxFeerate, toLocal = toLocalMsat, toRemote = toRemoteMsat)
val remoteSpec = CommitmentSpec(Set.empty[DirectedHtlc], commitTxFeerate, toLocal = toRemoteMsat, toRemote = toLocalMsat)
if (!localParams.isInitiator) {
// they initiated the channel open, therefore they pay the fee: we need to make sure they can afford it!
val toRemoteMsat = remoteSpec.toLocal
val fees = commitTxTotalCost(remoteParams.dustLimit, remoteSpec, channelFeatures.commitmentFormat)
val missing = toRemoteMsat.truncateToSatoshi - localParams.channelReserve - fees
val reserve = localParams.requestedChannelReserve_opt.getOrElse(0 sat)
val missing = toRemoteMsat.truncateToSatoshi - reserve - fees
if (missing < Satoshi(0)) {
return Left(CannotAffordFees(temporaryChannelId, missing = -missing, reserve = localParams.channelReserve, fees = fees))
return Left(CannotAffordFees(temporaryChannelId, missing = -missing, reserve = reserve, fees = fees))
}
}

View file

@ -208,8 +208,8 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder, val
startWith(WAIT_FOR_INIT_INTERNAL, Nothing)
when(WAIT_FOR_INIT_INTERNAL)(handleExceptions {
case Event(initFunder@INPUT_INIT_FUNDER(temporaryChannelId, fundingSatoshis, pushMsat, initialFeeratePerKw, fundingTxFeeratePerKw, localParams, remote, remoteInit, channelFlags, channelConfig, channelType), Nothing) =>
context.system.eventStream.publish(ChannelCreated(self, peer, remoteNodeId, isInitiator = true, temporaryChannelId, initialFeeratePerKw, Some(fundingTxFeeratePerKw)))
case Event(initFunder@INPUT_INIT_FUNDER(temporaryChannelId, fundingSatoshis, pushMsat, commitTxFeerate, fundingTxFeerate, localParams, remote, remoteInit, channelFlags, channelConfig, channelType), Nothing) =>
context.system.eventStream.publish(ChannelCreated(self, peer, remoteNodeId, isInitiator = true, temporaryChannelId, commitTxFeerate, Some(fundingTxFeerate)))
activeConnection = remote
txPublisher ! SetChannelId(remoteNodeId, temporaryChannelId)
val fundingPubKey = keyManager.fundingPublicKey(localParams.fundingKeyPath).publicKey
@ -223,9 +223,9 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder, val
pushMsat = pushMsat,
dustLimitSatoshis = localParams.dustLimit,
maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat,
channelReserveSatoshis = localParams.channelReserve,
channelReserveSatoshis = localParams.requestedChannelReserve_opt.getOrElse(0 sat),
htlcMinimumMsat = localParams.htlcMinimum,
feeratePerKw = initialFeeratePerKw,
feeratePerKw = commitTxFeerate,
toSelfDelay = localParams.toSelfDelay,
maxAcceptedHtlcs = localParams.maxAcceptedHtlcs,
fundingPubkey = fundingPubKey,

View file

@ -87,7 +87,7 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
val accept = AcceptChannel(temporaryChannelId = open.temporaryChannelId,
dustLimitSatoshis = localParams.dustLimit,
maxHtlcValueInFlightMsat = localParams.maxHtlcValueInFlightMsat,
channelReserveSatoshis = localParams.channelReserve,
channelReserveSatoshis = localParams.requestedChannelReserve_opt.getOrElse(0 sat),
minimumDepth = minimumDepth,
htlcMinimumMsat = localParams.htlcMinimum,
toSelfDelay = localParams.toSelfDelay,
@ -106,7 +106,7 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
nodeId = remoteNodeId,
dustLimit = open.dustLimitSatoshis,
maxHtlcValueInFlightMsat = open.maxHtlcValueInFlightMsat,
channelReserve = open.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment
requestedChannelReserve_opt = Some(open.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
htlcMinimum = open.htlcMinimumMsat,
toSelfDelay = open.toSelfDelay,
maxAcceptedHtlcs = open.maxAcceptedHtlcs,
@ -129,7 +129,7 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
})
when(WAIT_FOR_ACCEPT_CHANNEL)(handleExceptions {
case Event(accept: AcceptChannel, d@DATA_WAIT_FOR_ACCEPT_CHANNEL(INPUT_INIT_FUNDER(temporaryChannelId, fundingSatoshis, pushMsat, initialFeeratePerKw, fundingTxFeeratePerKw, localParams, _, remoteInit, _, channelConfig, channelType), open)) =>
case Event(accept: AcceptChannel, d@DATA_WAIT_FOR_ACCEPT_CHANNEL(INPUT_INIT_FUNDER(temporaryChannelId, fundingSatoshis, pushMsat, commitTxFeerate, fundingTxFeerate, localParams, _, remoteInit, _, channelConfig, channelType), open)) =>
Helpers.validateParamsFunder(nodeParams, channelType, localParams.initFeatures, remoteInit.features, open, accept) match {
case Left(t) =>
channelOpenReplyToUser(Left(LocalError(t)))
@ -139,7 +139,7 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
nodeId = remoteNodeId,
dustLimit = accept.dustLimitSatoshis,
maxHtlcValueInFlightMsat = accept.maxHtlcValueInFlightMsat,
channelReserve = accept.channelReserveSatoshis, // remote requires local to keep this much satoshis as direct payment
requestedChannelReserve_opt = Some(accept.channelReserveSatoshis), // our peer requires us to always have at least that much satoshis in our balance
htlcMinimum = accept.htlcMinimumMsat,
toSelfDelay = accept.toSelfDelay,
maxAcceptedHtlcs = accept.maxAcceptedHtlcs,
@ -153,8 +153,8 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
log.debug("remote params: {}", remoteParams)
val localFundingPubkey = keyManager.fundingPublicKey(localParams.fundingKeyPath)
val fundingPubkeyScript = Script.write(Script.pay2wsh(Scripts.multiSig2of2(localFundingPubkey.publicKey, remoteParams.fundingPubKey)))
wallet.makeFundingTx(fundingPubkeyScript, fundingSatoshis, fundingTxFeeratePerKw).pipeTo(self)
goto(WAIT_FOR_FUNDING_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, initialFeeratePerKw, accept.firstPerCommitmentPoint, channelConfig, channelFeatures, open)
wallet.makeFundingTx(fundingPubkeyScript, fundingSatoshis, fundingTxFeerate).pipeTo(self)
goto(WAIT_FOR_FUNDING_INTERNAL) using DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingSatoshis, pushMsat, commitTxFeerate, accept.firstPerCommitmentPoint, channelConfig, channelFeatures, open)
}
case Event(c: CloseCommand, d: DATA_WAIT_FOR_ACCEPT_CHANNEL) =>
@ -175,9 +175,9 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
})
when(WAIT_FOR_FUNDING_INTERNAL)(handleExceptions {
case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), d@DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelConfig, channelFeatures, open)) =>
case Event(MakeFundingTxResponse(fundingTx, fundingTxOutputIndex, fundingTxFee), d@DATA_WAIT_FOR_FUNDING_INTERNAL(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, commitTxFeerate, remoteFirstPerCommitmentPoint, channelConfig, channelFeatures, open)) =>
// let's create the first commitment tx that spends the yet uncommitted funding tx
Funding.makeFirstCommitTxs(keyManager, channelConfig, channelFeatures, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint) match {
Funding.makeFirstCommitTxs(keyManager, channelConfig, channelFeatures, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, commitTxFeerate, fundingTx.hash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint) match {
case Left(ex) => handleLocalError(ex, d, None)
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
require(fundingTx.txOut(fundingTxOutputIndex).publicKeyScript == localCommitTx.input.txOut.publicKeyScript, s"pubkey script mismatch!")
@ -220,9 +220,9 @@ trait ChannelOpenSingleFunder extends FundingHandlers with ErrorHandlers {
})
when(WAIT_FOR_FUNDING_CREATED)(handleExceptions {
case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig, _), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, remoteFirstPerCommitmentPoint, channelFlags, channelConfig, channelFeatures, _)) =>
case Event(FundingCreated(_, fundingTxHash, fundingTxOutputIndex, remoteSig, _), d@DATA_WAIT_FOR_FUNDING_CREATED(temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, commitTxFeerate, remoteFirstPerCommitmentPoint, channelFlags, channelConfig, channelFeatures, _)) =>
// they fund the channel with their funding tx, so the money is theirs (but we are paid pushMsat)
Funding.makeFirstCommitTxs(keyManager, channelConfig, channelFeatures, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, initialFeeratePerKw, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint) match {
Funding.makeFirstCommitTxs(keyManager, channelConfig, channelFeatures, temporaryChannelId, localParams, remoteParams, fundingAmount, pushMsat, commitTxFeerate, fundingTxHash, fundingTxOutputIndex, remoteFirstPerCommitmentPoint) match {
case Left(ex) => handleLocalError(ex, d, None)
case Right((localSpec, localCommitTx, remoteSpec, remoteCommitTx)) =>
// check remote signature validity

View file

@ -506,7 +506,7 @@ object Peer {
nodeParams.channelKeyManager.newFundingKeyPath(isInitiator), // we make sure that initiator and non-initiator key paths end differently
dustLimit = nodeParams.channelConf.dustLimit,
maxHtlcValueInFlightMsat = nodeParams.channelConf.maxHtlcValueInFlightMsat,
channelReserve = (fundingAmount * nodeParams.channelConf.reserveToFundingRatio).max(nodeParams.channelConf.dustLimit), // BOLT #2: make sure that our reserve is above our dust limit
requestedChannelReserve_opt = Some((fundingAmount * nodeParams.channelConf.reserveToFundingRatio).max(nodeParams.channelConf.dustLimit)), // BOLT #2: make sure that our reserve is above our dust limit
htlcMinimum = nodeParams.channelConf.htlcMinimum,
toSelfDelay = nodeParams.channelConf.toRemoteDelay, // we choose their delay
maxAcceptedHtlcs = nodeParams.channelConf.maxAcceptedHtlcs,

View file

@ -401,8 +401,8 @@ object ChannelEventSerializer extends MinimalSerializer({
JField("remoteNodeId", JString(e.remoteNodeId.toString())),
JField("isInitiator", JBool(e.isInitiator)),
JField("temporaryChannelId", JString(e.temporaryChannelId.toHex)),
JField("initialFeeratePerKw", JLong(e.initialFeeratePerKw.toLong)),
JField("fundingTxFeeratePerKw", e.fundingTxFeeratePerKw.map(f => JLong(f.toLong)).getOrElse(JNothing))
JField("commitTxFeeratePerKw", JLong(e.commitTxFeerate.toLong)),
JField("fundingTxFeeratePerKw", e.fundingTxFeerate.map(f => JLong(f.toLong)).getOrElse(JNothing))
)
case e: ChannelStateChanged => JObject(
JField("type", JString("channel-state-changed")),

View file

@ -18,7 +18,7 @@ package fr.acinq.eclair.wire.internal.channel.version0
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, ByteVector64, Crypto, OutPoint, Transaction, TxOut}
import fr.acinq.eclair.TimestampSecond
import fr.acinq.eclair.{BlockHeight, TimestampSecond}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.transactions.Transactions._
@ -27,7 +27,6 @@ import fr.acinq.eclair.wire.internal.channel.version0.ChannelTypes0.{HtlcTxAndSi
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs.{channelAnnouncementCodec, channelUpdateCodec, combinedFeaturesCodec}
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{BlockHeight, Features, InitFeature, TimestampSecond}
import scodec.Codec
import scodec.bits.{BitVector, ByteVector}
import scodec.codecs._
@ -69,7 +68,7 @@ private[channel] object ChannelCodecs0 {
("channelPath" | keyPathCodec) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::
@ -82,7 +81,7 @@ private[channel] object ChannelCodecs0 {
("nodeId" | publicKey) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::

View file

@ -18,6 +18,7 @@ package fr.acinq.eclair.wire.internal.channel.version1
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Transaction, TxOut}
import fr.acinq.eclair.BlockHeight
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.transactions.Transactions._
@ -27,7 +28,6 @@ import fr.acinq.eclair.wire.internal.channel.version0.ChannelTypes0.{HtlcTxAndSi
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs._
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{BlockHeight, Features, InitFeature}
import scodec.bits.ByteVector
import scodec.codecs._
import scodec.{Attempt, Codec}
@ -55,7 +55,7 @@ private[channel] object ChannelCodecs1 {
("channelPath" | keyPathCodec) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::
@ -68,7 +68,7 @@ private[channel] object ChannelCodecs1 {
("nodeId" | publicKey) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::

View file

@ -18,6 +18,7 @@ package fr.acinq.eclair.wire.internal.channel.version2
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{OutPoint, Transaction, TxOut}
import fr.acinq.eclair.BlockHeight
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.transactions.Transactions._
@ -27,7 +28,6 @@ import fr.acinq.eclair.wire.internal.channel.version0.ChannelTypes0.{HtlcTxAndSi
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs._
import fr.acinq.eclair.wire.protocol._
import fr.acinq.eclair.{BlockHeight, Features, InitFeature}
import scodec.bits.ByteVector
import scodec.codecs._
import scodec.{Attempt, Codec}
@ -55,7 +55,7 @@ private[channel] object ChannelCodecs2 {
("channelPath" | keyPathCodec) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::
@ -68,7 +68,7 @@ private[channel] object ChannelCodecs2 {
("nodeId" | publicKey) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(included = true, satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::

View file

@ -17,7 +17,7 @@
package fr.acinq.eclair.wire.internal.channel.version3
import fr.acinq.bitcoin.scalacompat.DeterministicWallet.{ExtendedPrivateKey, KeyPath}
import fr.acinq.bitcoin.scalacompat.{ByteVector32, OutPoint, Transaction, TxOut}
import fr.acinq.bitcoin.scalacompat.{OutPoint, Satoshi, Transaction, TxOut}
import fr.acinq.eclair.channel._
import fr.acinq.eclair.crypto.ShaChain
import fr.acinq.eclair.transactions.Transactions._
@ -25,7 +25,7 @@ import fr.acinq.eclair.transactions.{CommitmentSpec, DirectedHtlc, IncomingHtlc,
import fr.acinq.eclair.wire.protocol.CommonCodecs._
import fr.acinq.eclair.wire.protocol.LightningMessageCodecs._
import fr.acinq.eclair.wire.protocol.UpdateMessage
import fr.acinq.eclair.{BlockHeight, FeatureSupport, Features, InitFeature}
import fr.acinq.eclair.{BlockHeight, FeatureSupport, Features}
import scodec.bits.{BitVector, ByteVector}
import scodec.codecs._
import scodec.{Attempt, Codec}
@ -75,7 +75,7 @@ private[channel] object ChannelCodecs3 {
("channelPath" | keyPathCodec) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(!channelFeatures.hasFeature(Features.DualFunding), satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::
@ -84,11 +84,11 @@ private[channel] object ChannelCodecs3 {
("walletStaticPaymentBasepoint" | optional(provide(channelFeatures.paysDirectlyToWallet), publicKey)) ::
("features" | combinedFeaturesCodec)).as[LocalParams]
val remoteParamsCodec: Codec[RemoteParams] = (
def remoteParamsCodec(channelFeatures: ChannelFeatures): Codec[RemoteParams] = (
("nodeId" | publicKey) ::
("dustLimit" | satoshi) ::
("maxHtlcValueInFlightMsat" | uint64) ::
("channelReserve" | satoshi) ::
("channelReserve" | conditional(!channelFeatures.hasFeature(Features.DualFunding), satoshi)) ::
("htlcMinimum" | millisatoshi) ::
("toSelfDelay" | cltvExpiryDelta) ::
("maxAcceptedHtlcs" | uint16) ::
@ -269,7 +269,7 @@ private[channel] object ChannelCodecs3 {
("channelConfig" | channelConfigCodec) ::
(("channelFeatures" | channelFeaturesCodec) >>:~ { channelFeatures =>
("localParams" | localParamsCodec(channelFeatures)) ::
("remoteParams" | remoteParamsCodec) ::
("remoteParams" | remoteParamsCodec(channelFeatures)) ::
("channelFlags" | channelflags) ::
("localCommit" | localCommitCodec) ::
("remoteCommit" | remoteCommitCodec) ::

View file

@ -208,7 +208,7 @@ object TestConstants {
isInitiator = true,
fundingSatoshis
).copy(
channelReserve = 10000 sat // Bob will need to keep that much satoshis as direct payment
requestedChannelReserve_opt = Some(10_000 sat) // Bob will need to keep that much satoshis in his balance
)
}
@ -346,7 +346,7 @@ object TestConstants {
isInitiator = false,
fundingSatoshis
).copy(
channelReserve = 20000 sat // Alice will need to keep that much satoshis as direct payment
requestedChannelReserve_opt = Some(20_000 sat) // Alice will need to keep that much satoshis in her balance
)
}

View file

@ -480,8 +480,8 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
object CommitmentsSpec {
def makeCommitments(toLocal: MilliSatoshi, toRemote: MilliSatoshi, feeRatePerKw: FeeratePerKw = FeeratePerKw(0 sat), dustLimit: Satoshi = 0 sat, isInitiator: Boolean = true, announceChannel: Boolean = true): Commitments = {
val localParams = LocalParams(randomKey().publicKey, DeterministicWallet.KeyPath(Seq(42L)), dustLimit, UInt64.MaxValue, 0 sat, 1 msat, CltvExpiryDelta(144), 50, isInitiator, ByteVector.empty, None, Features.empty)
val remoteParams = RemoteParams(randomKey().publicKey, dustLimit, UInt64.MaxValue, 0 sat, 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val localParams = LocalParams(randomKey().publicKey, DeterministicWallet.KeyPath(Seq(42L)), dustLimit, UInt64.MaxValue, None, 1 msat, CltvExpiryDelta(144), 50, isInitiator, ByteVector.empty, None, Features.empty)
val remoteParams = RemoteParams(randomKey().publicKey, dustLimit, UInt64.MaxValue, None, 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val commitmentInput = Funding.makeFundingInputInfo(randomBytes32(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteParams.fundingPubKey)
Commitments(
channelId = randomBytes32(),
@ -503,8 +503,8 @@ object CommitmentsSpec {
}
def makeCommitments(toLocal: MilliSatoshi, toRemote: MilliSatoshi, localNodeId: PublicKey, remoteNodeId: PublicKey, announceChannel: Boolean): Commitments = {
val localParams = LocalParams(localNodeId, DeterministicWallet.KeyPath(Seq(42L)), 0 sat, UInt64.MaxValue, 0 sat, 1 msat, CltvExpiryDelta(144), 50, isInitiator = true, ByteVector.empty, None, Features.empty)
val remoteParams = RemoteParams(remoteNodeId, 0 sat, UInt64.MaxValue, 0 sat, 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val localParams = LocalParams(localNodeId, DeterministicWallet.KeyPath(Seq(42L)), 0 sat, UInt64.MaxValue, None, 1 msat, CltvExpiryDelta(144), 50, isInitiator = true, ByteVector.empty, None, Features.empty)
val remoteParams = RemoteParams(remoteNodeId, 0 sat, UInt64.MaxValue, None, 1 msat, CltvExpiryDelta(144), 50, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, randomKey().publicKey, Features.empty, None)
val commitmentInput = Funding.makeFundingInputInfo(randomBytes32(), 0, (toLocal + toRemote).truncateToSatoshi, randomKey().publicKey, remoteParams.fundingPubKey)
Commitments(
channelId = randomBytes32(),

View file

@ -200,7 +200,7 @@ trait ChannelStateTestsHelperMethods extends TestKitBase {
val channelConfig = ChannelConfig.standard
val (aliceParams, bobParams, channelType) = computeFeatures(setup, tags)
val channelFlags = ChannelFlags(announceChannel = tags.contains(ChannelStateTestsTags.ChannelsPublic))
val initialFeeratePerKw = if (tags.contains(ChannelStateTestsTags.AnchorOutputs) || tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val commitTxFeerate = if (tags.contains(ChannelStateTestsTags.AnchorOutputs) || tags.contains(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val (fundingSatoshis, pushMsat) = if (tags.contains(ChannelStateTestsTags.NoPushMsat)) {
(TestConstants.fundingSatoshis, 0.msat)
} else {
@ -209,7 +209,7 @@ trait ChannelStateTestsHelperMethods extends TestKitBase {
val aliceInit = Init(aliceParams.initFeatures)
val bobInit = Init(bobParams.initFeatures)
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, fundingSatoshis, pushMsat, initialFeeratePerKw, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, channelFlags, channelConfig, channelType)
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, fundingSatoshis, pushMsat, commitTxFeerate, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, channelFlags, channelConfig, channelType)
assert(alice2blockchain.expectMsgType[TxPublisher.SetChannelId].channelId === ByteVector32.Zeroes)
bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, bobParams, bob2alice.ref, aliceInit, channelConfig, channelType)
assert(bob2blockchain.expectMsgType[TxPublisher.SetChannelId].channelId === ByteVector32.Zeroes)
@ -241,7 +241,7 @@ trait ChannelStateTestsHelperMethods extends TestKitBase {
bob2blockchain.expectMsgType[WatchFundingDeeplyBuried]
awaitCond(alice.stateName == NORMAL)
awaitCond(bob.stateName == NORMAL)
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == (pushMsat - aliceParams.channelReserve).max(0 msat))
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == (pushMsat - aliceParams.requestedChannelReserve_opt.getOrElse(0 sat)).max(0 msat))
// x2 because alice and bob share the same relayer
channelUpdateListener.expectMsgType[LocalChannelUpdate]
channelUpdateListener.expectMsgType[LocalChannelUpdate]

View file

@ -58,12 +58,12 @@ class WaitForAcceptChannelStateSpec extends TestKitBaseClass with FixtureAnyFunS
val channelConfig = ChannelConfig.standard
val (aliceParams, bobParams, defaultChannelType) = computeFeatures(setup, test.tags)
val channelType = if (test.tags.contains("standard-channel-type")) ChannelTypes.Standard else defaultChannelType
val initialFeeratePerKw = if (channelType == ChannelTypes.AnchorOutputs || channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val commitTxFeerate = if (channelType == ChannelTypes.AnchorOutputs || channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val aliceInit = Init(aliceParams.initFeatures)
val bobInit = Init(bobParams.initFeatures)
within(30 seconds) {
val fundingAmount = if (test.tags.contains(ChannelStateTestsTags.Wumbo)) Btc(5).toSatoshi else TestConstants.fundingSatoshis
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, fundingAmount, TestConstants.pushMsat, initialFeeratePerKw, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, ChannelFlags.Private, channelConfig, channelType)
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, fundingAmount, TestConstants.pushMsat, commitTxFeerate, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, ChannelFlags.Private, channelConfig, channelType)
bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, bobParams, bob2alice.ref, aliceInit, channelConfig, channelType)
alice2bob.expectMsgType[OpenChannel]
alice2bob.forward(bob)

View file

@ -52,11 +52,11 @@ class WaitForOpenChannelStateSpec extends TestKitBaseClass with FixtureAnyFunSui
val channelConfig = ChannelConfig.standard
val (aliceParams, bobParams, defaultChannelType) = computeFeatures(setup, test.tags)
val channelType = if (test.tags.contains("standard-channel-type")) ChannelTypes.Standard else defaultChannelType
val initialFeeratePerKw = if (channelType == ChannelTypes.AnchorOutputs || channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val commitTxFeerate = if (channelType == ChannelTypes.AnchorOutputs || channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx) TestConstants.anchorOutputsFeeratePerKw else TestConstants.feeratePerKw
val aliceInit = Init(aliceParams.initFeatures)
val bobInit = Init(bobParams.initFeatures)
within(30 seconds) {
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, TestConstants.fundingSatoshis, TestConstants.pushMsat, initialFeeratePerKw, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, ChannelFlags.Private, channelConfig, channelType)
alice ! INPUT_INIT_FUNDER(ByteVector32.Zeroes, TestConstants.fundingSatoshis, TestConstants.pushMsat, commitTxFeerate, TestConstants.feeratePerKw, aliceParams, alice2bob.ref, bobInit, ChannelFlags.Private, channelConfig, channelType)
bob ! INPUT_INIT_FUNDEE(ByteVector32.Zeroes, bobParams, bob2alice.ref, aliceInit, channelConfig, channelType)
awaitCond(bob.stateName == WAIT_FOR_OPEN_CHANNEL)
withFixture(test.toNoArgTest(FixtureParam(alice, bob, alice2bob, bob2alice, bob2blockchain)))

View file

@ -106,7 +106,7 @@ class WaitForFundingCreatedStateSpec extends TestKitBaseClass with FixtureAnyFun
import f._
val fees = Transactions.weight2fee(TestConstants.feeratePerKw, Transactions.DefaultCommitmentFormat.commitWeight)
val bobParams = bob.stateData.asInstanceOf[DATA_WAIT_FOR_FUNDING_CREATED].localParams
val reserve = bobParams.channelReserve
val reserve = bobParams.requestedChannelReserve_opt.get
val missing = 100.sat - fees - reserve
val fundingCreated = alice2bob.expectMsgType[FundingCreated]
alice2bob.forward(bob)

View file

@ -257,7 +257,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
// but this one will dip alice below her reserve: we must wait for the previous HTLCs to settle before sending any more
val failedAdd = CMD_ADD_HTLC(sender.ref, 11000000 msat, randomBytes32(), CltvExpiry(400144), TestConstants.emptyOnionPacket, localOrigin(sender.ref))
bob ! failedAdd
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), failedAdd.amount, missing = 1360 sat, 10000 sat, 22720 sat)
val error = RemoteCannotAffordFeesForNewHtlc(channelId(bob), failedAdd.amount, missing = 1360 sat, 20000 sat, 22720 sat)
sender.expectMsg(RES_ADD_FAILED(failedAdd, error, Some(bob.stateData.asInstanceOf[DATA_NORMAL].channelUpdate)))
}

View file

@ -416,8 +416,8 @@ class PeerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Paralle
val init = channel.expectMsgType[INPUT_INIT_FUNDER]
assert(init.channelType === ChannelTypes.AnchorOutputs)
assert(init.fundingAmount === 15000.sat)
assert(init.initialFeeratePerKw === TestConstants.anchorOutputsFeeratePerKw)
assert(init.fundingTxFeeratePerKw === feeEstimator.getFeeratePerKw(nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget))
assert(init.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
assert(init.fundingTxFeerate === feeEstimator.getFeeratePerKw(nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget))
}
test("use correct on-chain fee rates when spawning a channel (anchor outputs zero fee htlc)", Tag("anchor_outputs_zero_fee_htlc_tx")) { f =>
@ -434,8 +434,8 @@ class PeerSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with Paralle
val init = channel.expectMsgType[INPUT_INIT_FUNDER]
assert(init.channelType === ChannelTypes.AnchorOutputsZeroFeeHtlcTx)
assert(init.fundingAmount === 15000.sat)
assert(init.initialFeeratePerKw === TestConstants.anchorOutputsFeeratePerKw)
assert(init.fundingTxFeeratePerKw === feeEstimator.getFeeratePerKw(nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget))
assert(init.commitTxFeerate === TestConstants.anchorOutputsFeeratePerKw)
assert(init.fundingTxFeerate === feeEstimator.getFeeratePerKw(nodeParams.onChainFeeConf.feeTargets.fundingBlockTarget))
}
test("use correct final script if option_static_remotekey is negotiated", Tag("static_remotekey")) { f =>

View file

@ -369,8 +369,8 @@ object PaymentPacketSpec {
}
def makeCommitments(channelId: ByteVector32, testAvailableBalanceForSend: MilliSatoshi = 50000000 msat, testAvailableBalanceForReceive: MilliSatoshi = 50000000 msat, testCapacity: Satoshi = 100000 sat): Commitments = {
val params = LocalParams(null, null, null, null, null, null, null, 0, isInitiator = true, null, None, null)
val remoteParams = RemoteParams(randomKey().publicKey, null, null, null, null, null, maxAcceptedHtlcs = 0, null, null, null, null, null, null, None)
val params = LocalParams(null, null, null, null, None, null, null, 0, isInitiator = true, null, None, null)
val remoteParams = RemoteParams(randomKey().publicKey, null, null, None, null, null, maxAcceptedHtlcs = 0, null, null, null, null, null, null, None)
val commitInput = InputInfo(OutPoint(randomBytes32(), 1), TxOut(testCapacity, Nil), Nil)
val channelFlags = ChannelFlags.Private
new Commitments(channelId, ChannelConfig.standard, ChannelFeatures(), params, remoteParams, channelFlags, null, null, null, null, 0, 0, Map.empty, null, commitInput, null) {

File diff suppressed because one or more lines are too long

View file

@ -51,7 +51,7 @@ class ChannelCodecs1Spec extends AnyFunSuite {
assert(channelVersionCodec.encode(ChannelVersion.ANCHOR_OUTPUTS) === Attempt.successful(hex"00000007".bits))
}
test("encode/decode localparams") {
test("encode/decode local params") {
def roundtrip(localParams: LocalParams, codec: Codec[LocalParams]) = {
val encoded = codec.encode(localParams).require
val decoded = codec.decode(encoded).require
@ -63,7 +63,7 @@ class ChannelCodecs1Spec extends AnyFunSuite {
fundingKeyPath = DeterministicWallet.KeyPath(Seq(42L)),
dustLimit = Satoshi(Random.nextInt(Int.MaxValue)),
maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
channelReserve = Satoshi(Random.nextInt(Int.MaxValue)),
requestedChannelReserve_opt = Some(Satoshi(Random.nextInt(Int.MaxValue))),
htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)),
toSelfDelay = CltvExpiryDelta(Random.nextInt(Short.MaxValue)),
maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
@ -78,12 +78,12 @@ class ChannelCodecs1Spec extends AnyFunSuite {
roundtrip(o, localParamsCodec(ChannelVersion.ANCHOR_OUTPUTS))
}
test("encode/decode remoteparams") {
test("encode/decode remote params") {
val o = RemoteParams(
nodeId = randomKey().publicKey,
dustLimit = Satoshi(Random.nextInt(Int.MaxValue)),
maxHtlcValueInFlightMsat = UInt64(Random.nextInt(Int.MaxValue)),
channelReserve = Satoshi(Random.nextInt(Int.MaxValue)),
requestedChannelReserve_opt = Some(Satoshi(Random.nextInt(Int.MaxValue))),
htlcMinimum = MilliSatoshi(Random.nextInt(Int.MaxValue)),
toSelfDelay = CltvExpiryDelta(Random.nextInt(Short.MaxValue)),
maxAcceptedHtlcs = Random.nextInt(Short.MaxValue),
@ -98,7 +98,7 @@ class ChannelCodecs1Spec extends AnyFunSuite {
val decoded = remoteParamsCodec.decodeValue(encoded).require
assert(o === decoded)
// Backwards-compatibility: decode remoteparams with global features.
// Backwards-compatibility: decode remote params with global features.
val withGlobalFeatures = hex"03c70c3b813815a8b79f41622b6f2c343fa24d94fb35fa7110bbb3d4d59cd9612e0000000059844cbc000000001b1524ea000000001503cbac000000006b75d3272e38777e029fa4e94066163024177311de7ba1befec2e48b473c387bbcee1484bf276a54460215e3dfb8e6f262222c5f343f5e38c5c9a43d2594c7f06dd7ac1a4326c665dd050347aba4d56d7007a7dcf03594423dccba9ed700d11e665d261594e1154203df31020d457ee336ba6eeb328d00f1b8bd8bfefb8a4dcd5af6db4c438b7ec5106c7edc0380df17e1beb0f238e51a39122ac4c6fb57f3c4f5b7bc9432f991b1ef4a8af3570002020000018a"
val withGlobalFeaturesDecoded = remoteParamsCodec.decode(withGlobalFeatures.bits).require.value
assert(withGlobalFeaturesDecoded.initFeatures.toByteVector === hex"028a")

View file

@ -1156,7 +1156,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
wsClient.expectMessage(expectedSerializedPset)
val chcr = ChannelCreated(system.deadLetters, system.deadLetters, bobNodeId, isInitiator = true, ByteVector32.One, FeeratePerKw(25 sat), Some(FeeratePerKw(20 sat)))
val expectedSerializedChcr = """{"type":"channel-opened","remoteNodeId":"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585","isInitiator":true,"temporaryChannelId":"0100000000000000000000000000000000000000000000000000000000000000","initialFeeratePerKw":25,"fundingTxFeeratePerKw":20}"""
val expectedSerializedChcr = """{"type":"channel-opened","remoteNodeId":"039dc0e0b1d25905e44fdf6f8e89755a5e219685840d0bc1d28d3308f9628a3585","isInitiator":true,"temporaryChannelId":"0100000000000000000000000000000000000000000000000000000000000000","commitTxFeeratePerKw":25,"fundingTxFeeratePerKw":20}"""
assert(serialization.write(chcr) === expectedSerializedChcr)
system.eventStream.publish(chcr)
wsClient.expectMessage(expectedSerializedChcr)