mirror of
https://github.com/ACINQ/eclair.git
synced 2024-11-19 01:43:22 +01:00
Add recommended_feerates
optional message (#2860)
We send to our peers an optional message that tells them the feerates we'd like to use for funding channels. This lets them know which values are acceptable to us, in case we reject their funding requests. This is using an odd type and will be automatically ignored by existing nodes who don't support that feature. Co-authored-by: Pierre-Marie Padiou <pm47@users.noreply.github.com>
This commit is contained in:
parent
cfdb0885f8
commit
db8290f80e
@ -381,9 +381,9 @@ class EclairImpl(appKit: Kit) extends Eclair with Logging {
|
|||||||
override def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[TxId] = {
|
override def sendOnChain(address: String, amount: Satoshi, confirmationTargetOrFeerate: Either[Long, FeeratePerByte]): Future[TxId] = {
|
||||||
val feeRate = confirmationTargetOrFeerate match {
|
val feeRate = confirmationTargetOrFeerate match {
|
||||||
case Left(blocks) =>
|
case Left(blocks) =>
|
||||||
if (blocks < 3) appKit.nodeParams.currentFeerates.fast
|
if (blocks < 3) appKit.nodeParams.currentBitcoinCoreFeerates.fast
|
||||||
else if (blocks > 6) appKit.nodeParams.currentFeerates.slow
|
else if (blocks > 6) appKit.nodeParams.currentBitcoinCoreFeerates.slow
|
||||||
else appKit.nodeParams.currentFeerates.medium
|
else appKit.nodeParams.currentBitcoinCoreFeerates.medium
|
||||||
case Right(feeratePerByte) => FeeratePerKw(feeratePerByte)
|
case Right(feeratePerByte) => FeeratePerKw(feeratePerByte)
|
||||||
}
|
}
|
||||||
appKit.wallet match {
|
appKit.wallet match {
|
||||||
|
@ -21,9 +21,9 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
|
|||||||
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, Crypto, Satoshi, SatoshiLong}
|
import fr.acinq.bitcoin.scalacompat.{Block, BlockHash, Crypto, Satoshi, SatoshiLong}
|
||||||
import fr.acinq.eclair.Setup.Seeds
|
import fr.acinq.eclair.Setup.Seeds
|
||||||
import fr.acinq.eclair.blockchain.fee._
|
import fr.acinq.eclair.blockchain.fee._
|
||||||
import fr.acinq.eclair.channel.ChannelFlags
|
|
||||||
import fr.acinq.eclair.channel.fsm.Channel
|
import fr.acinq.eclair.channel.fsm.Channel
|
||||||
import fr.acinq.eclair.channel.fsm.Channel.{BalanceThreshold, ChannelConf, UnhandledExceptionStrategy}
|
import fr.acinq.eclair.channel.fsm.Channel.{BalanceThreshold, ChannelConf, UnhandledExceptionStrategy}
|
||||||
|
import fr.acinq.eclair.channel.{ChannelFlags, ChannelTypes}
|
||||||
import fr.acinq.eclair.crypto.Noise.KeyPair
|
import fr.acinq.eclair.crypto.Noise.KeyPair
|
||||||
import fr.acinq.eclair.crypto.keymanager.{ChannelKeyManager, NodeKeyManager, OnChainKeyManager}
|
import fr.acinq.eclair.crypto.keymanager.{ChannelKeyManager, NodeKeyManager, OnChainKeyManager}
|
||||||
import fr.acinq.eclair.db._
|
import fr.acinq.eclair.db._
|
||||||
@ -36,6 +36,7 @@ import fr.acinq.eclair.router.Graph.{HeuristicsConstants, WeightRatios}
|
|||||||
import fr.acinq.eclair.router.Router._
|
import fr.acinq.eclair.router.Router._
|
||||||
import fr.acinq.eclair.router.{Graph, PathFindingExperimentConf}
|
import fr.acinq.eclair.router.{Graph, PathFindingExperimentConf}
|
||||||
import fr.acinq.eclair.tor.Socks5ProxyParams
|
import fr.acinq.eclair.tor.Socks5ProxyParams
|
||||||
|
import fr.acinq.eclair.transactions.Transactions
|
||||||
import fr.acinq.eclair.wire.protocol._
|
import fr.acinq.eclair.wire.protocol._
|
||||||
import grizzled.slf4j.Logging
|
import grizzled.slf4j.Logging
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
@ -57,7 +58,7 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
|
|||||||
onChainKeyManager_opt: Option[OnChainKeyManager],
|
onChainKeyManager_opt: Option[OnChainKeyManager],
|
||||||
instanceId: UUID, // a unique instance ID regenerated after each restart
|
instanceId: UUID, // a unique instance ID regenerated after each restart
|
||||||
private val blockHeight: AtomicLong,
|
private val blockHeight: AtomicLong,
|
||||||
private val feerates: AtomicReference[FeeratesPerKw],
|
private val bitcoinCoreFeerates: AtomicReference[FeeratesPerKw],
|
||||||
alias: String,
|
alias: String,
|
||||||
color: Color,
|
color: Color,
|
||||||
publicAddresses: List[NodeAddress],
|
publicAddresses: List[NodeAddress],
|
||||||
@ -102,13 +103,34 @@ case class NodeParams(nodeKeyManager: NodeKeyManager,
|
|||||||
|
|
||||||
def currentBlockHeight: BlockHeight = BlockHeight(blockHeight.get)
|
def currentBlockHeight: BlockHeight = BlockHeight(blockHeight.get)
|
||||||
|
|
||||||
def currentFeerates: FeeratesPerKw = feerates.get()
|
def currentBitcoinCoreFeerates: FeeratesPerKw = bitcoinCoreFeerates.get()
|
||||||
|
|
||||||
/** Only to be used in tests. */
|
/** Only to be used in tests. */
|
||||||
def setFeerates(value: FeeratesPerKw): Unit = feerates.set(value)
|
def setBitcoinCoreFeerates(value: FeeratesPerKw): Unit = bitcoinCoreFeerates.set(value)
|
||||||
|
|
||||||
/** Returns the features that should be used in our init message with the given peer. */
|
/** Returns the features that should be used in our init message with the given peer. */
|
||||||
def initFeaturesFor(nodeId: PublicKey): Features[InitFeature] = overrideInitFeatures.getOrElse(nodeId, features).initFeatures()
|
def initFeaturesFor(nodeId: PublicKey): Features[InitFeature] = overrideInitFeatures.getOrElse(nodeId, features).initFeatures()
|
||||||
|
|
||||||
|
/** Returns the feerates we'd like our peer to use when funding channels. */
|
||||||
|
def recommendedFeerates(remoteNodeId: PublicKey, localFeatures: Features[InitFeature], remoteFeatures: Features[InitFeature]): RecommendedFeerates = {
|
||||||
|
val feerateTolerance = onChainFeeConf.feerateToleranceFor(remoteNodeId)
|
||||||
|
val fundingFeerate = onChainFeeConf.getFundingFeerate(currentBitcoinCoreFeerates)
|
||||||
|
val fundingRange = RecommendedFeeratesTlv.FundingFeerateRange(
|
||||||
|
min = fundingFeerate * feerateTolerance.ratioLow,
|
||||||
|
max = fundingFeerate * feerateTolerance.ratioHigh,
|
||||||
|
)
|
||||||
|
// We use the most likely commitment format, even though there is no guarantee that this is the one that will be used.
|
||||||
|
val commitmentFormat = ChannelTypes.defaultFromFeatures(localFeatures, remoteFeatures, announceChannel = false).commitmentFormat
|
||||||
|
val commitmentFeerate = onChainFeeConf.getCommitmentFeerate(currentBitcoinCoreFeerates, remoteNodeId, commitmentFormat, channelConf.minFundingPrivateSatoshis)
|
||||||
|
val commitmentRange = RecommendedFeeratesTlv.CommitmentFeerateRange(
|
||||||
|
min = commitmentFeerate * feerateTolerance.ratioLow,
|
||||||
|
max = commitmentFormat match {
|
||||||
|
case Transactions.DefaultCommitmentFormat => commitmentFeerate * feerateTolerance.ratioHigh
|
||||||
|
case _: Transactions.AnchorOutputsCommitmentFormat => (commitmentFeerate * feerateTolerance.ratioHigh).max(feerateTolerance.anchorOutputMaxCommitFeerate)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
RecommendedFeerates(chainHash, fundingFeerate, commitmentFeerate, TlvStream(fundingRange, commitmentRange))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class PaymentFinalExpiryConf(min: CltvExpiryDelta, max: CltvExpiryDelta) {
|
case class PaymentFinalExpiryConf(min: CltvExpiryDelta, max: CltvExpiryDelta) {
|
||||||
@ -219,7 +241,7 @@ object NodeParams extends Logging {
|
|||||||
|
|
||||||
def makeNodeParams(config: Config, instanceId: UUID,
|
def makeNodeParams(config: Config, instanceId: UUID,
|
||||||
nodeKeyManager: NodeKeyManager, channelKeyManager: ChannelKeyManager, onChainKeyManager_opt: Option[OnChainKeyManager],
|
nodeKeyManager: NodeKeyManager, channelKeyManager: ChannelKeyManager, onChainKeyManager_opt: Option[OnChainKeyManager],
|
||||||
torAddress_opt: Option[NodeAddress], database: Databases, blockHeight: AtomicLong, feerates: AtomicReference[FeeratesPerKw],
|
torAddress_opt: Option[NodeAddress], database: Databases, blockHeight: AtomicLong, bitcoinCoreFeerates: AtomicReference[FeeratesPerKw],
|
||||||
pluginParams: Seq[PluginParams] = Nil): NodeParams = {
|
pluginParams: Seq[PluginParams] = Nil): NodeParams = {
|
||||||
// check configuration for keys that have been renamed
|
// check configuration for keys that have been renamed
|
||||||
val deprecatedKeyPaths = Map(
|
val deprecatedKeyPaths = Map(
|
||||||
@ -513,7 +535,7 @@ object NodeParams extends Logging {
|
|||||||
onChainKeyManager_opt = onChainKeyManager_opt,
|
onChainKeyManager_opt = onChainKeyManager_opt,
|
||||||
instanceId = instanceId,
|
instanceId = instanceId,
|
||||||
blockHeight = blockHeight,
|
blockHeight = blockHeight,
|
||||||
feerates = feerates,
|
bitcoinCoreFeerates = bitcoinCoreFeerates,
|
||||||
alias = nodeAlias,
|
alias = nodeAlias,
|
||||||
color = Color(color(0), color(1), color(2)),
|
color = Color(color(0), color(1), color(2)),
|
||||||
publicAddresses = addresses,
|
publicAddresses = addresses,
|
||||||
|
@ -255,7 +255,7 @@ class Setup(val datadir: File,
|
|||||||
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Medium).update(feeratesPerKw.get.medium.toLong.toDouble)
|
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Medium).update(feeratesPerKw.get.medium.toLong.toDouble)
|
||||||
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Fast).update(feeratesPerKw.get.fast.toLong.toDouble)
|
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Fast).update(feeratesPerKw.get.fast.toLong.toDouble)
|
||||||
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Fastest).update(feeratesPerKw.get.fastest.toLong.toDouble)
|
blockchain.Monitoring.Metrics.FeeratesPerByte.withTag(blockchain.Monitoring.Tags.Priority, blockchain.Monitoring.Tags.Priorities.Fastest).update(feeratesPerKw.get.fastest.toLong.toDouble)
|
||||||
system.eventStream.publish(CurrentFeerates(feeratesPerKw.get))
|
system.eventStream.publish(CurrentFeerates.BitcoinCore(feeratesPerKw.get))
|
||||||
logger.info(s"current feeratesPerKB=$feeratesPerKB feeratesPerKw=${feeratesPerKw.get}")
|
logger.info(s"current feeratesPerKB=$feeratesPerKB feeratesPerKw=${feeratesPerKw.get}")
|
||||||
feeratesRetrieved.trySuccess(Done)
|
feeratesRetrieved.trySuccess(Done)
|
||||||
case Failure(exception) =>
|
case Failure(exception) =>
|
||||||
|
@ -32,4 +32,14 @@ case class NewTransaction(tx: Transaction) extends BlockchainEvent
|
|||||||
|
|
||||||
case class CurrentBlockHeight(blockHeight: BlockHeight) extends BlockchainEvent
|
case class CurrentBlockHeight(blockHeight: BlockHeight) extends BlockchainEvent
|
||||||
|
|
||||||
case class CurrentFeerates(feeratesPerKw: FeeratesPerKw) extends BlockchainEvent
|
sealed trait CurrentFeerates extends BlockchainEvent {
|
||||||
|
val feeratesPerKw: FeeratesPerKw
|
||||||
|
}
|
||||||
|
|
||||||
|
object CurrentFeerates {
|
||||||
|
//@formatter:off
|
||||||
|
case class BitcoinCore(feeratesPerKw: FeeratesPerKw) extends CurrentFeerates
|
||||||
|
//@formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ object Helpers {
|
|||||||
val channelFeatures = ChannelFeatures(channelType, localFeatures, remoteFeatures, open.channelFlags.announceChannel)
|
val channelFeatures = ChannelFeatures(channelType, localFeatures, remoteFeatures, open.channelFlags.announceChannel)
|
||||||
|
|
||||||
// BOLT #2: The receiving node MUST fail the channel if: it considers feerate_per_kw too small for timely processing or unreasonably large.
|
// BOLT #2: The receiving node MUST fail the channel if: it considers feerate_per_kw too small for timely processing or unreasonably large.
|
||||||
val localFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, channelFeatures.commitmentFormat, open.fundingSatoshis)
|
val localFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, channelFeatures.commitmentFormat, open.fundingSatoshis)
|
||||||
if (nodeParams.onChainFeeConf.feerateToleranceFor(remoteNodeId).isFeeDiffTooHigh(channelFeatures.commitmentFormat, localFeeratePerKw, open.feeratePerKw)) return Left(FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw))
|
if (nodeParams.onChainFeeConf.feerateToleranceFor(remoteNodeId).isFeeDiffTooHigh(channelFeatures.commitmentFormat, localFeeratePerKw, open.feeratePerKw)) return Left(FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.feeratePerKw))
|
||||||
|
|
||||||
// we don't check that the funder's amount for the initial commitment transaction is sufficient for full fee payment
|
// we don't check that the funder's amount for the initial commitment transaction is sufficient for full fee payment
|
||||||
@ -166,7 +166,7 @@ object Helpers {
|
|||||||
val channelFeatures = ChannelFeatures(channelType, localFeatures, remoteFeatures, open.channelFlags.announceChannel)
|
val channelFeatures = ChannelFeatures(channelType, localFeatures, remoteFeatures, open.channelFlags.announceChannel)
|
||||||
|
|
||||||
// BOLT #2: The receiving node MUST fail the channel if: it considers feerate_per_kw too small for timely processing or unreasonably large.
|
// BOLT #2: The receiving node MUST fail the channel if: it considers feerate_per_kw too small for timely processing or unreasonably large.
|
||||||
val localFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, channelFeatures.commitmentFormat, open.fundingAmount)
|
val localFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, channelFeatures.commitmentFormat, open.fundingAmount)
|
||||||
if (nodeParams.onChainFeeConf.feerateToleranceFor(remoteNodeId).isFeeDiffTooHigh(channelFeatures.commitmentFormat, localFeeratePerKw, open.commitmentFeerate)) return Left(FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.commitmentFeerate))
|
if (nodeParams.onChainFeeConf.feerateToleranceFor(remoteNodeId).isFeeDiffTooHigh(channelFeatures.commitmentFormat, localFeeratePerKw, open.commitmentFeerate)) return Left(FeerateTooDifferent(open.temporaryChannelId, localFeeratePerKw, open.commitmentFeerate))
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -223,7 +223,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
// the constant delay by which we delay processing of blocks (it will be smoothened among all channels)
|
// the constant delay by which we delay processing of blocks (it will be smoothened among all channels)
|
||||||
private val blockProcessingDelay = Random.nextLong(nodeParams.channelConf.maxBlockProcessingDelay.toMillis + 1).millis
|
private val blockProcessingDelay = Random.nextLong(nodeParams.channelConf.maxBlockProcessingDelay.toMillis + 1).millis
|
||||||
// this will be used to make sure the current commitment fee is up-to-date
|
// this will be used to make sure the current commitment fee is up-to-date
|
||||||
context.system.eventStream.subscribe(self, classOf[CurrentFeerates])
|
context.system.eventStream.subscribe(self, classOf[CurrentFeerates.BitcoinCore])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
8888888 888b 888 8888888 88888888888
|
8888888 888b 888 8888888 88888888888
|
||||||
@ -435,7 +435,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
handleAddHtlcCommandError(c, error, Some(d.channelUpdate))
|
handleAddHtlcCommandError(c, error, Some(d.channelUpdate))
|
||||||
|
|
||||||
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) =>
|
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) =>
|
||||||
d.commitments.sendAdd(c, nodeParams.currentBlockHeight, nodeParams.channelConf, nodeParams.currentFeerates, nodeParams.onChainFeeConf) match {
|
d.commitments.sendAdd(c, nodeParams.currentBlockHeight, nodeParams.channelConf, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf) match {
|
||||||
case Right((commitments1, add)) =>
|
case Right((commitments1, add)) =>
|
||||||
if (c.commit) self ! CMD_SIGN()
|
if (c.commit) self ! CMD_SIGN()
|
||||||
context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortIds, commitments1))
|
context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortIds, commitments1))
|
||||||
@ -444,7 +444,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Event(add: UpdateAddHtlc, d: DATA_NORMAL) =>
|
case Event(add: UpdateAddHtlc, d: DATA_NORMAL) =>
|
||||||
d.commitments.receiveAdd(add, nodeParams.currentFeerates, nodeParams.onChainFeeConf) match {
|
d.commitments.receiveAdd(add, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf) match {
|
||||||
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
||||||
case Left(cause) => handleLocalError(cause, d, Some(add))
|
case Left(cause) => handleLocalError(cause, d, Some(add))
|
||||||
}
|
}
|
||||||
@ -519,7 +519,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Event(fee: UpdateFee, d: DATA_NORMAL) =>
|
case Event(fee: UpdateFee, d: DATA_NORMAL) =>
|
||||||
d.commitments.receiveFee(fee, nodeParams.currentFeerates, nodeParams.onChainFeeConf) match {
|
d.commitments.receiveFee(fee, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf) match {
|
||||||
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
||||||
case Left(cause) => handleLocalError(cause, d, Some(fee))
|
case Left(cause) => handleLocalError(cause, d, Some(fee))
|
||||||
}
|
}
|
||||||
@ -735,7 +735,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
// there are no pending signed changes, let's go directly to NEGOTIATING
|
// there are no pending signed changes, let's go directly to NEGOTIATING
|
||||||
if (d.commitments.params.localParams.paysClosingFees) {
|
if (d.commitments.params.localParams.paysClosingFees) {
|
||||||
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
||||||
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, localShutdown.scriptPubKey, remoteShutdownScript, nodeParams.currentFeerates, nodeParams.onChainFeeConf, d.closingFeerates)
|
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, localShutdown.scriptPubKey, remoteShutdownScript, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, d.closingFeerates)
|
||||||
goto(NEGOTIATING) using DATA_NEGOTIATING(d.commitments, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending sendList :+ closingSigned
|
goto(NEGOTIATING) using DATA_NEGOTIATING(d.commitments, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending sendList :+ closingSigned
|
||||||
} else {
|
} else {
|
||||||
// we are not the channel initiator, will wait for their closing_signed
|
// we are not the channel initiator, will wait for their closing_signed
|
||||||
@ -750,7 +750,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
|
|
||||||
case Event(ProcessCurrentBlockHeight(c), d: DATA_NORMAL) => handleNewBlock(c, d)
|
case Event(ProcessCurrentBlockHeight(c), d: DATA_NORMAL) => handleNewBlock(c, d)
|
||||||
|
|
||||||
case Event(c: CurrentFeerates, d: DATA_NORMAL) => handleCurrentFeerate(c, d)
|
case Event(c: CurrentFeerates.BitcoinCore, d: DATA_NORMAL) => handleCurrentFeerate(c, d)
|
||||||
|
|
||||||
case Event(WatchFundingDeeplyBuriedTriggered(blockHeight, txIndex, fundingTx), d: DATA_NORMAL) if d.channelAnnouncement.isEmpty =>
|
case Event(WatchFundingDeeplyBuriedTriggered(blockHeight, txIndex, fundingTx), d: DATA_NORMAL) if d.channelAnnouncement.isEmpty =>
|
||||||
val finalRealShortId = RealScidStatus.Final(RealShortChannelId(blockHeight, txIndex, d.commitments.latest.commitInput.outPoint.index.toInt))
|
val finalRealShortId = RealScidStatus.Final(RealShortChannelId(blockHeight, txIndex, d.commitments.latest.commitInput.outPoint.index.toInt))
|
||||||
@ -944,7 +944,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
if (!d.commitments.isQuiescent) {
|
if (!d.commitments.isQuiescent) {
|
||||||
log.info("rejecting splice request: channel not quiescent")
|
log.info("rejecting splice request: channel not quiescent")
|
||||||
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage)
|
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage)
|
||||||
} else if (msg.feerate < nodeParams.currentFeerates.minimum) {
|
} else if (msg.feerate < nodeParams.currentBitcoinCoreFeerates.minimum) {
|
||||||
log.info("rejecting splice request: feerate too low")
|
log.info("rejecting splice request: feerate too low")
|
||||||
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceRequest(d.channelId).getMessage)
|
stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceRequest(d.channelId).getMessage)
|
||||||
} else {
|
} else {
|
||||||
@ -1295,7 +1295,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Event(fee: UpdateFee, d: DATA_SHUTDOWN) =>
|
case Event(fee: UpdateFee, d: DATA_SHUTDOWN) =>
|
||||||
d.commitments.receiveFee(fee, nodeParams.currentFeerates, nodeParams.onChainFeeConf) match {
|
d.commitments.receiveFee(fee, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf) match {
|
||||||
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
case Right(commitments1) => stay() using d.copy(commitments = commitments1)
|
||||||
case Left(cause) => handleLocalError(cause, d, Some(fee))
|
case Left(cause) => handleLocalError(cause, d, Some(fee))
|
||||||
}
|
}
|
||||||
@ -1341,7 +1341,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
if (commitments1.hasNoPendingHtlcsOrFeeUpdate) {
|
if (commitments1.hasNoPendingHtlcsOrFeeUpdate) {
|
||||||
if (d.commitments.params.localParams.paysClosingFees) {
|
if (d.commitments.params.localParams.paysClosingFees) {
|
||||||
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
||||||
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, commitments1.latest, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf, closingFeerates)
|
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, commitments1.latest, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, closingFeerates)
|
||||||
goto(NEGOTIATING) using DATA_NEGOTIATING(commitments1, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending revocation :: closingSigned :: Nil
|
goto(NEGOTIATING) using DATA_NEGOTIATING(commitments1, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending revocation :: closingSigned :: Nil
|
||||||
} else {
|
} else {
|
||||||
// we are not the channel initiator, will wait for their closing_signed
|
// we are not the channel initiator, will wait for their closing_signed
|
||||||
@ -1383,7 +1383,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
log.debug("switching to NEGOTIATING spec:\n{}", commitments1.latest.specs2String)
|
log.debug("switching to NEGOTIATING spec:\n{}", commitments1.latest.specs2String)
|
||||||
if (d.commitments.params.localParams.paysClosingFees) {
|
if (d.commitments.params.localParams.paysClosingFees) {
|
||||||
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
// we pay the closing fees, so we initiate the negotiation by sending the first closing_signed
|
||||||
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, commitments1.latest, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf, closingFeerates)
|
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, commitments1.latest, localShutdown.scriptPubKey, remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, closingFeerates)
|
||||||
goto(NEGOTIATING) using DATA_NEGOTIATING(commitments1, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending closingSigned
|
goto(NEGOTIATING) using DATA_NEGOTIATING(commitments1, localShutdown, remoteShutdown, List(List(ClosingTxProposed(closingTx, closingSigned))), bestUnpublishedClosingTx_opt = None) storing() sending closingSigned
|
||||||
} else {
|
} else {
|
||||||
// we are not the channel initiator, will wait for their closing_signed
|
// we are not the channel initiator, will wait for their closing_signed
|
||||||
@ -1402,7 +1402,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
|
|
||||||
case Event(ProcessCurrentBlockHeight(c), d: DATA_SHUTDOWN) => handleNewBlock(c, d)
|
case Event(ProcessCurrentBlockHeight(c), d: DATA_SHUTDOWN) => handleNewBlock(c, d)
|
||||||
|
|
||||||
case Event(c: CurrentFeerates, d: DATA_SHUTDOWN) => handleCurrentFeerate(c, d)
|
case Event(c: CurrentFeerates.BitcoinCore, d: DATA_SHUTDOWN) => handleCurrentFeerate(c, d)
|
||||||
|
|
||||||
case Event(c: CMD_CLOSE, d: DATA_SHUTDOWN) =>
|
case Event(c: CMD_CLOSE, d: DATA_SHUTDOWN) =>
|
||||||
c.feerates match {
|
c.feerates match {
|
||||||
@ -1456,7 +1456,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
case Some(ClosingSignedTlv.FeeRange(minFee, maxFee)) if !d.commitments.params.localParams.paysClosingFees =>
|
case Some(ClosingSignedTlv.FeeRange(minFee, maxFee)) if !d.commitments.params.localParams.paysClosingFees =>
|
||||||
// if we are not paying the closing fees and they proposed a fee range, we pick a value in that range and they should accept it without further negotiation
|
// if we are not paying the closing fees and they proposed a fee range, we pick a value in that range and they should accept it without further negotiation
|
||||||
// we don't care much about the closing fee since they're paying it (not us) and we can use CPFP if we want to speed up confirmation
|
// we don't care much about the closing fee since they're paying it (not us) and we can use CPFP if we want to speed up confirmation
|
||||||
val localClosingFees = Closing.MutualClose.firstClosingFee(d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf)
|
val localClosingFees = Closing.MutualClose.firstClosingFee(d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf)
|
||||||
if (maxFee < localClosingFees.min) {
|
if (maxFee < localClosingFees.min) {
|
||||||
log.warning("their highest closing fee is below our minimum fee: {} < {}", maxFee, localClosingFees.min)
|
log.warning("their highest closing fee is below our minimum fee: {} < {}", maxFee, localClosingFees.min)
|
||||||
stay() sending Warning(d.channelId, s"closing fee range must not be below ${localClosingFees.min}")
|
stay() sending Warning(d.channelId, s"closing fee range must not be below ${localClosingFees.min}")
|
||||||
@ -1483,7 +1483,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
val lastLocalClosingFee_opt = lastLocalClosingSigned_opt.map(_.localClosingSigned.feeSatoshis)
|
val lastLocalClosingFee_opt = lastLocalClosingSigned_opt.map(_.localClosingSigned.feeSatoshis)
|
||||||
val (closingTx, closingSigned) = {
|
val (closingTx, closingSigned) = {
|
||||||
// if we are not the channel initiator and we were waiting for them to send their first closing_signed, we don't have a lastLocalClosingFee, so we compute a firstClosingFee
|
// if we are not the channel initiator and we were waiting for them to send their first closing_signed, we don't have a lastLocalClosingFee, so we compute a firstClosingFee
|
||||||
val localClosingFees = Closing.MutualClose.firstClosingFee(d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf)
|
val localClosingFees = Closing.MutualClose.firstClosingFee(d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf)
|
||||||
val nextPreferredFee = Closing.MutualClose.nextClosingFee(lastLocalClosingFee_opt.getOrElse(localClosingFees.preferred), remoteClosingFee)
|
val nextPreferredFee = Closing.MutualClose.nextClosingFee(lastLocalClosingFee_opt.getOrElse(localClosingFees.preferred), remoteClosingFee)
|
||||||
Closing.MutualClose.makeClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, localClosingFees.copy(preferred = nextPreferredFee))
|
Closing.MutualClose.makeClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, localClosingFees.copy(preferred = nextPreferredFee))
|
||||||
}
|
}
|
||||||
@ -1514,7 +1514,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
handleCommandError(ClosingAlreadyInProgress(d.channelId), c)
|
handleCommandError(ClosingAlreadyInProgress(d.channelId), c)
|
||||||
} else {
|
} else {
|
||||||
log.info("updating our closing feerates: {}", feerates)
|
log.info("updating our closing feerates: {}", feerates)
|
||||||
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf, Some(feerates))
|
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, Some(feerates))
|
||||||
val closingTxProposed1 = d.closingTxProposed match {
|
val closingTxProposed1 = d.closingTxProposed match {
|
||||||
case previousNegotiations :+ currentNegotiation => previousNegotiations :+ (currentNegotiation :+ ClosingTxProposed(closingTx, closingSigned))
|
case previousNegotiations :+ currentNegotiation => previousNegotiations :+ (currentNegotiation :+ ClosingTxProposed(closingTx, closingSigned))
|
||||||
case previousNegotiations => previousNegotiations :+ List(ClosingTxProposed(closingTx, closingSigned))
|
case previousNegotiations => previousNegotiations :+ List(ClosingTxProposed(closingTx, closingSigned))
|
||||||
@ -1540,8 +1540,8 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
log.info("got valid settlement for htlc={}, recalculating htlc transactions", c.id)
|
log.info("got valid settlement for htlc={}, recalculating htlc transactions", c.id)
|
||||||
val commitment = commitments1.latest
|
val commitment = commitments1.latest
|
||||||
val localCommitPublished1 = d.localCommitPublished.map(localCommitPublished => localCommitPublished.copy(htlcTxs = Closing.LocalClose.claimHtlcOutputs(keyManager, commitment)))
|
val localCommitPublished1 = d.localCommitPublished.map(localCommitPublished => localCommitPublished.copy(htlcTxs = Closing.LocalClose.claimHtlcOutputs(keyManager, commitment)))
|
||||||
val remoteCommitPublished1 = d.remoteCommitPublished.map(remoteCommitPublished => remoteCommitPublished.copy(claimHtlcTxs = Closing.RemoteClose.claimHtlcOutputs(keyManager, commitment, commitment.remoteCommit, nodeParams.currentFeerates, d.finalScriptPubKey)))
|
val remoteCommitPublished1 = d.remoteCommitPublished.map(remoteCommitPublished => remoteCommitPublished.copy(claimHtlcTxs = Closing.RemoteClose.claimHtlcOutputs(keyManager, commitment, commitment.remoteCommit, nodeParams.currentBitcoinCoreFeerates, d.finalScriptPubKey)))
|
||||||
val nextRemoteCommitPublished1 = d.nextRemoteCommitPublished.map(remoteCommitPublished => remoteCommitPublished.copy(claimHtlcTxs = Closing.RemoteClose.claimHtlcOutputs(keyManager, commitment, commitment.nextRemoteCommit_opt.get.commit, nodeParams.currentFeerates, d.finalScriptPubKey)))
|
val nextRemoteCommitPublished1 = d.nextRemoteCommitPublished.map(remoteCommitPublished => remoteCommitPublished.copy(claimHtlcTxs = Closing.RemoteClose.claimHtlcOutputs(keyManager, commitment, commitment.nextRemoteCommit_opt.get.commit, nodeParams.currentBitcoinCoreFeerates, d.finalScriptPubKey)))
|
||||||
|
|
||||||
def republish(): Unit = {
|
def republish(): Unit = {
|
||||||
localCommitPublished1.foreach(lcp => doPublish(lcp, commitment))
|
localCommitPublished1.foreach(lcp => doPublish(lcp, commitment))
|
||||||
@ -1716,7 +1716,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val revokedCommitPublished1 = d.revokedCommitPublished.map { rev =>
|
val revokedCommitPublished1 = d.revokedCommitPublished.map { rev =>
|
||||||
val (rev1, penaltyTxs) = Closing.RevokedClose.claimHtlcTxOutputs(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, rev, tx, nodeParams.currentFeerates, d.finalScriptPubKey)
|
val (rev1, penaltyTxs) = Closing.RevokedClose.claimHtlcTxOutputs(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, rev, tx, nodeParams.currentBitcoinCoreFeerates, d.finalScriptPubKey)
|
||||||
penaltyTxs.foreach(claimTx => txPublisher ! PublishFinalTx(claimTx, claimTx.fee, None))
|
penaltyTxs.foreach(claimTx => txPublisher ! PublishFinalTx(claimTx, claimTx.fee, None))
|
||||||
penaltyTxs.foreach(claimTx => blockchain ! WatchOutputSpent(self, tx.txid, claimTx.input.outPoint.index.toInt, hints = Set(claimTx.tx.txid)))
|
penaltyTxs.foreach(claimTx => blockchain ! WatchOutputSpent(self, tx.txid, claimTx.input.outPoint.index.toInt, hints = Set(claimTx.tx.txid)))
|
||||||
rev1
|
rev1
|
||||||
@ -1730,7 +1730,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
val d1 = d.copy(
|
val d1 = d.copy(
|
||||||
localCommitPublished = d.localCommitPublished.map(localCommitPublished => {
|
localCommitPublished = d.localCommitPublished.map(localCommitPublished => {
|
||||||
// If the tx is one of our HTLC txs, we now publish a 3rd-stage claim-htlc-tx that claims its output.
|
// If the tx is one of our HTLC txs, we now publish a 3rd-stage claim-htlc-tx that claims its output.
|
||||||
val (localCommitPublished1, claimHtlcTx_opt) = Closing.LocalClose.claimHtlcDelayedOutput(localCommitPublished, keyManager, d.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, d.finalScriptPubKey)
|
val (localCommitPublished1, claimHtlcTx_opt) = Closing.LocalClose.claimHtlcDelayedOutput(localCommitPublished, keyManager, d.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, d.finalScriptPubKey)
|
||||||
claimHtlcTx_opt.foreach(claimHtlcTx => {
|
claimHtlcTx_opt.foreach(claimHtlcTx => {
|
||||||
txPublisher ! PublishFinalTx(claimHtlcTx, claimHtlcTx.fee, None)
|
txPublisher ! PublishFinalTx(claimHtlcTx, claimHtlcTx.fee, None)
|
||||||
blockchain ! WatchTxConfirmed(self, claimHtlcTx.tx.txid, nodeParams.channelConf.minDepthBlocks, Some(RelativeDelay(tx.txid, d.commitments.params.remoteParams.toSelfDelay.toInt.toLong)))
|
blockchain ! WatchTxConfirmed(self, claimHtlcTx.tx.txid, nodeParams.channelConf.minDepthBlocks, Some(RelativeDelay(tx.txid, d.commitments.params.remoteParams.toSelfDelay.toInt.toLong)))
|
||||||
@ -1939,7 +1939,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
|
|
||||||
case Event(ProcessCurrentBlockHeight(c), d: ChannelDataWithCommitments) => handleNewBlock(c, d)
|
case Event(ProcessCurrentBlockHeight(c), d: ChannelDataWithCommitments) => handleNewBlock(c, d)
|
||||||
|
|
||||||
case Event(c: CurrentFeerates, d: ChannelDataWithCommitments) => handleCurrentFeerateDisconnected(c, d)
|
case Event(c: CurrentFeerates.BitcoinCore, d: ChannelDataWithCommitments) => handleCurrentFeerateDisconnected(c, d)
|
||||||
|
|
||||||
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) => handleAddDisconnected(c, d)
|
case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) => handleAddDisconnected(c, d)
|
||||||
|
|
||||||
@ -2133,7 +2133,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
if (d.commitments.params.localParams.paysCommitTxFees && !shutdownInProgress) {
|
if (d.commitments.params.localParams.paysCommitTxFees && !shutdownInProgress) {
|
||||||
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
||||||
val currentFeeratePerKw = d.commitments.latest.localCommit.spec.commitTxFeerate
|
val currentFeeratePerKw = d.commitments.latest.localCommit.spec.commitTxFeerate
|
||||||
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, d.commitments.params.commitmentFormat, d.commitments.latest.capacity)
|
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, d.commitments.params.commitmentFormat, d.commitments.latest.capacity)
|
||||||
if (nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)) {
|
if (nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)) {
|
||||||
self ! CMD_UPDATE_FEE(networkFeeratePerKw, commit = true)
|
self ! CMD_UPDATE_FEE(networkFeeratePerKw, commit = true)
|
||||||
}
|
}
|
||||||
@ -2163,7 +2163,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
// note: in any case we still need to keep all previously sent closing_signed, because they may publish one of them
|
// note: in any case we still need to keep all previously sent closing_signed, because they may publish one of them
|
||||||
if (d.commitments.params.localParams.paysClosingFees) {
|
if (d.commitments.params.localParams.paysClosingFees) {
|
||||||
// we could use the last closing_signed we sent, but network fees may have changed while we were offline so it is better to restart from scratch
|
// we could use the last closing_signed we sent, but network fees may have changed while we were offline so it is better to restart from scratch
|
||||||
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentFeerates, nodeParams.onChainFeeConf, None)
|
val (closingTx, closingSigned) = Closing.MutualClose.makeFirstClosingTx(keyManager, d.commitments.latest, d.localShutdown.scriptPubKey, d.remoteShutdown.scriptPubKey, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, None)
|
||||||
val closingTxProposed1 = d.closingTxProposed :+ List(ClosingTxProposed(closingTx, closingSigned))
|
val closingTxProposed1 = d.closingTxProposed :+ List(ClosingTxProposed(closingTx, closingSigned))
|
||||||
goto(NEGOTIATING) using d.copy(closingTxProposed = closingTxProposed1) storing() sending d.localShutdown :: closingSigned :: Nil
|
goto(NEGOTIATING) using d.copy(closingTxProposed = closingTxProposed1) storing() sending d.localShutdown :: closingSigned :: Nil
|
||||||
} else {
|
} else {
|
||||||
@ -2192,7 +2192,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
|
|
||||||
case Event(ProcessCurrentBlockHeight(c), d: ChannelDataWithCommitments) => handleNewBlock(c, d)
|
case Event(ProcessCurrentBlockHeight(c), d: ChannelDataWithCommitments) => handleNewBlock(c, d)
|
||||||
|
|
||||||
case Event(c: CurrentFeerates, d: ChannelDataWithCommitments) => handleCurrentFeerateDisconnected(c, d)
|
case Event(c: CurrentFeerates.BitcoinCore, d: ChannelDataWithCommitments) => handleCurrentFeerateDisconnected(c, d)
|
||||||
|
|
||||||
case Event(getTxResponse: GetTxWithMetaResponse, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) if getTxResponse.txid == d.commitments.latest.fundingTxId => handleGetFundingTx(getTxResponse, d.waitingSince, d.fundingTx_opt)
|
case Event(getTxResponse: GetTxWithMetaResponse, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) if getTxResponse.txid == d.commitments.latest.fundingTxId => handleGetFundingTx(getTxResponse, d.waitingSince, d.fundingTx_opt)
|
||||||
|
|
||||||
@ -2282,7 +2282,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
case Event(ProcessCurrentBlockHeight(_), _) => stay()
|
case Event(ProcessCurrentBlockHeight(_), _) => stay()
|
||||||
|
|
||||||
// we only care about this event in NORMAL and SHUTDOWN state, and we never unregister to the event stream
|
// we only care about this event in NORMAL and SHUTDOWN state, and we never unregister to the event stream
|
||||||
case Event(CurrentFeerates(_), _) => stay()
|
case Event(_: CurrentFeerates.BitcoinCore, _) => stay()
|
||||||
|
|
||||||
// we only care about this event in NORMAL state
|
// we only care about this event in NORMAL state
|
||||||
case Event(_: BroadcastChannelUpdate, _) => stay()
|
case Event(_: BroadcastChannelUpdate, _) => stay()
|
||||||
@ -2558,7 +2558,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
private def handleCurrentFeerate(c: CurrentFeerates, d: ChannelDataWithCommitments) = {
|
private def handleCurrentFeerate(c: CurrentFeerates, d: ChannelDataWithCommitments) = {
|
||||||
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
||||||
val commitments = d.commitments.latest
|
val commitments = d.commitments.latest
|
||||||
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, d.commitments.params.commitmentFormat, commitments.capacity)
|
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, d.commitments.params.commitmentFormat, commitments.capacity)
|
||||||
val currentFeeratePerKw = commitments.localCommit.spec.commitTxFeerate
|
val currentFeeratePerKw = commitments.localCommit.spec.commitTxFeerate
|
||||||
val shouldUpdateFee = d.commitments.params.localParams.paysCommitTxFees && nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)
|
val shouldUpdateFee = d.commitments.params.localParams.paysCommitTxFees && nodeParams.onChainFeeConf.shouldUpdateFee(currentFeeratePerKw, networkFeeratePerKw)
|
||||||
val shouldClose = !d.commitments.params.localParams.paysCommitTxFees &&
|
val shouldClose = !d.commitments.params.localParams.paysCommitTxFees &&
|
||||||
@ -2584,7 +2584,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
private def handleCurrentFeerateDisconnected(c: CurrentFeerates, d: ChannelDataWithCommitments) = {
|
private def handleCurrentFeerateDisconnected(c: CurrentFeerates, d: ChannelDataWithCommitments) = {
|
||||||
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
// TODO: all active commitments use the same feerate, but may have a different channel capacity: how should we compute networkFeeratePerKw?
|
||||||
val commitments = d.commitments.latest
|
val commitments = d.commitments.latest
|
||||||
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, d.commitments.params.commitmentFormat, commitments.capacity)
|
val networkFeeratePerKw = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, d.commitments.params.commitmentFormat, commitments.capacity)
|
||||||
val currentFeeratePerKw = commitments.localCommit.spec.commitTxFeerate
|
val currentFeeratePerKw = commitments.localCommit.spec.commitTxFeerate
|
||||||
// if the network fees are too high we risk to not be able to confirm our current commitment
|
// if the network fees are too high we risk to not be able to confirm our current commitment
|
||||||
val shouldClose = networkFeeratePerKw > currentFeeratePerKw &&
|
val shouldClose = networkFeeratePerKw > currentFeeratePerKw &&
|
||||||
@ -2769,7 +2769,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
|
|||||||
private def initiateSplice(cmd: CMD_SPLICE, d: DATA_NORMAL): Either[ChannelException, SpliceInit] = {
|
private def initiateSplice(cmd: CMD_SPLICE, d: DATA_NORMAL): Either[ChannelException, SpliceInit] = {
|
||||||
if (d.commitments.isQuiescent) {
|
if (d.commitments.isQuiescent) {
|
||||||
val parentCommitment = d.commitments.latest.commitment
|
val parentCommitment = d.commitments.latest.commitment
|
||||||
val targetFeerate = nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentFeerates)
|
val targetFeerate = nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentBitcoinCoreFeerates)
|
||||||
val fundingContribution = InteractiveTxFunder.computeSpliceContribution(
|
val fundingContribution = InteractiveTxFunder.computeSpliceContribution(
|
||||||
isInitiator = true,
|
isInitiator = true,
|
||||||
sharedInput = Multisig2of2Input(parentCommitment),
|
sharedInput = Multisig2of2Input(parentCommitment),
|
||||||
|
@ -206,7 +206,7 @@ trait ErrorHandlers extends CommonHandlers {
|
|||||||
val commitment = d.commitments.latest
|
val commitment = d.commitments.latest
|
||||||
log.error(s"force-closing with fundingIndex=${commitment.fundingTxIndex}")
|
log.error(s"force-closing with fundingIndex=${commitment.fundingTxIndex}")
|
||||||
val commitTx = commitment.fullySignedLocalCommitTx(keyManager).tx
|
val commitTx = commitment.fullySignedLocalCommitTx(keyManager).tx
|
||||||
val localCommitPublished = Closing.LocalClose.claimCommitTxOutputs(keyManager, commitment, commitTx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
val localCommitPublished = Closing.LocalClose.claimCommitTxOutputs(keyManager, commitment, commitTx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
||||||
val nextData = d match {
|
val nextData = d match {
|
||||||
case closing: DATA_CLOSING => closing.copy(localCommitPublished = Some(localCommitPublished))
|
case closing: DATA_CLOSING => closing.copy(localCommitPublished = Some(localCommitPublished))
|
||||||
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, negotiating.closingTxProposed.flatten.map(_.unsignedTx), localCommitPublished = Some(localCommitPublished))
|
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, negotiating.closingTxProposed.flatten.map(_.unsignedTx), localCommitPublished = Some(localCommitPublished))
|
||||||
@ -252,7 +252,7 @@ trait ErrorHandlers extends CommonHandlers {
|
|||||||
require(commitTx.txid == commitments.remoteCommit.txid, "txid mismatch")
|
require(commitTx.txid == commitments.remoteCommit.txid, "txid mismatch")
|
||||||
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
||||||
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, commitTx, Closing.commitTxFee(commitments.commitInput, commitTx, d.commitments.params.localParams.paysCommitTxFees), "remote-commit"))
|
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, commitTx, Closing.commitTxFee(commitments.commitInput, commitTx, d.commitments.params.localParams.paysCommitTxFees), "remote-commit"))
|
||||||
val remoteCommitPublished = Closing.RemoteClose.claimCommitTxOutputs(keyManager, commitments, commitments.remoteCommit, commitTx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
val remoteCommitPublished = Closing.RemoteClose.claimCommitTxOutputs(keyManager, commitments, commitments.remoteCommit, commitTx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
||||||
val nextData = d match {
|
val nextData = d match {
|
||||||
case closing: DATA_CLOSING => closing.copy(remoteCommitPublished = Some(remoteCommitPublished))
|
case closing: DATA_CLOSING => closing.copy(remoteCommitPublished = Some(remoteCommitPublished))
|
||||||
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, mutualCloseProposed = negotiating.closingTxProposed.flatten.map(_.unsignedTx), remoteCommitPublished = Some(remoteCommitPublished))
|
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, mutualCloseProposed = negotiating.closingTxProposed.flatten.map(_.unsignedTx), remoteCommitPublished = Some(remoteCommitPublished))
|
||||||
@ -270,7 +270,7 @@ trait ErrorHandlers extends CommonHandlers {
|
|||||||
|
|
||||||
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
||||||
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, commitTx, Closing.commitTxFee(commitment.commitInput, commitTx, d.commitments.params.localParams.paysCommitTxFees), "next-remote-commit"))
|
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, commitTx, Closing.commitTxFee(commitment.commitInput, commitTx, d.commitments.params.localParams.paysCommitTxFees), "next-remote-commit"))
|
||||||
val remoteCommitPublished = Closing.RemoteClose.claimCommitTxOutputs(keyManager, commitment, remoteCommit, commitTx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
val remoteCommitPublished = Closing.RemoteClose.claimCommitTxOutputs(keyManager, commitment, remoteCommit, commitTx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
||||||
val nextData = d match {
|
val nextData = d match {
|
||||||
case closing: DATA_CLOSING => closing.copy(nextRemoteCommitPublished = Some(remoteCommitPublished))
|
case closing: DATA_CLOSING => closing.copy(nextRemoteCommitPublished = Some(remoteCommitPublished))
|
||||||
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, mutualCloseProposed = negotiating.closingTxProposed.flatten.map(_.unsignedTx), nextRemoteCommitPublished = Some(remoteCommitPublished))
|
case negotiating: DATA_NEGOTIATING => DATA_CLOSING(d.commitments, waitingSince = nodeParams.currentBlockHeight, finalScriptPubKey = finalScriptPubKey, mutualCloseProposed = negotiating.closingTxProposed.flatten.map(_.unsignedTx), nextRemoteCommitPublished = Some(remoteCommitPublished))
|
||||||
@ -305,7 +305,7 @@ trait ErrorHandlers extends CommonHandlers {
|
|||||||
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
val finalScriptPubKey = getOrGenerateFinalScriptPubKey(d)
|
||||||
Closing.RevokedClose.getRemotePerCommitmentSecret(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, tx) match {
|
Closing.RevokedClose.getRemotePerCommitmentSecret(keyManager, d.commitments.params, d.commitments.remotePerCommitmentSecrets, tx) match {
|
||||||
case Some((commitmentNumber, remotePerCommitmentSecret)) =>
|
case Some((commitmentNumber, remotePerCommitmentSecret)) =>
|
||||||
val revokedCommitPublished = Closing.RevokedClose.claimCommitTxOutputs(keyManager, d.commitments.params, tx, commitmentNumber, remotePerCommitmentSecret, nodeParams.db.channels, nodeParams.currentFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
val revokedCommitPublished = Closing.RevokedClose.claimCommitTxOutputs(keyManager, d.commitments.params, tx, commitmentNumber, remotePerCommitmentSecret, nodeParams.db.channels, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, finalScriptPubKey)
|
||||||
log.warning(s"txid=${tx.txid} was a revoked commitment, publishing the penalty tx")
|
log.warning(s"txid=${tx.txid} was a revoked commitment, publishing the penalty tx")
|
||||||
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, tx, Closing.commitTxFee(commitment.commitInput, tx, d.commitments.params.localParams.paysCommitTxFees), "revoked-commit"))
|
context.system.eventStream.publish(TransactionPublished(d.channelId, remoteNodeId, tx, Closing.commitTxFee(commitment.commitInput, tx, d.commitments.params.localParams.paysCommitTxFees), "revoked-commit"))
|
||||||
val exc = FundingTxSpent(d.channelId, tx.txid)
|
val exc = FundingTxSpent(d.channelId, tx.txid)
|
||||||
@ -324,7 +324,7 @@ trait ErrorHandlers extends CommonHandlers {
|
|||||||
val remotePerCommitmentPoint = d.remoteChannelReestablish.myCurrentPerCommitmentPoint
|
val remotePerCommitmentPoint = d.remoteChannelReestablish.myCurrentPerCommitmentPoint
|
||||||
val remoteCommitPublished = RemoteCommitPublished(
|
val remoteCommitPublished = RemoteCommitPublished(
|
||||||
commitTx = tx,
|
commitTx = tx,
|
||||||
claimMainOutputTx = Closing.RemoteClose.claimMainOutput(keyManager, d.commitments.params, remotePerCommitmentPoint, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, finalScriptPubKey),
|
claimMainOutputTx = Closing.RemoteClose.claimMainOutput(keyManager, d.commitments.params, remotePerCommitmentPoint, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, finalScriptPubKey),
|
||||||
claimHtlcTxs = Map.empty,
|
claimHtlcTxs = Map.empty,
|
||||||
claimAnchorTxs = List.empty,
|
claimAnchorTxs = List.empty,
|
||||||
irrevocablySpent = Map.empty)
|
irrevocablySpent = Map.empty)
|
||||||
|
@ -75,7 +75,7 @@ object ReplaceableTxFunder {
|
|||||||
Behaviors.withMdc(txPublishContext.mdc()) {
|
Behaviors.withMdc(txPublishContext.mdc()) {
|
||||||
Behaviors.receiveMessagePartial {
|
Behaviors.receiveMessagePartial {
|
||||||
case FundTransaction(replyTo, cmd, tx, requestedFeerate) =>
|
case FundTransaction(replyTo, cmd, tx, requestedFeerate) =>
|
||||||
val targetFeerate = requestedFeerate.min(maxFeerate(cmd.txInfo, cmd.commitment, nodeParams.currentFeerates, nodeParams.onChainFeeConf))
|
val targetFeerate = requestedFeerate.min(maxFeerate(cmd.txInfo, cmd.commitment, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf))
|
||||||
val txFunder = new ReplaceableTxFunder(nodeParams, replyTo, cmd, bitcoinClient, context)
|
val txFunder = new ReplaceableTxFunder(nodeParams, replyTo, cmd, bitcoinClient, context)
|
||||||
tx match {
|
tx match {
|
||||||
case Right(txWithWitnessData) => txFunder.fund(txWithWitnessData, targetFeerate)
|
case Right(txWithWitnessData) => txFunder.fund(txWithWitnessData, targetFeerate)
|
||||||
|
@ -158,7 +158,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams,
|
|||||||
}
|
}
|
||||||
Behaviors.receiveMessagePartial {
|
Behaviors.receiveMessagePartial {
|
||||||
case CheckUtxosResult(isSafe, currentBlockHeight) =>
|
case CheckUtxosResult(isSafe, currentBlockHeight) =>
|
||||||
val targetFeerate = getFeerate(nodeParams.currentFeerates, confirmationTarget, currentBlockHeight, isSafe)
|
val targetFeerate = getFeerate(nodeParams.currentBitcoinCoreFeerates, confirmationTarget, currentBlockHeight, isSafe)
|
||||||
fund(txWithWitnessData, targetFeerate)
|
fund(txWithWitnessData, targetFeerate)
|
||||||
case UpdateConfirmationTarget(target) =>
|
case UpdateConfirmationTarget(target) =>
|
||||||
confirmationTarget = target
|
confirmationTarget = target
|
||||||
@ -220,7 +220,7 @@ private class ReplaceableTxPublisher(nodeParams: NodeParams,
|
|||||||
case CheckUtxosResult(isSafe, currentBlockHeight) =>
|
case CheckUtxosResult(isSafe, currentBlockHeight) =>
|
||||||
// We make sure we increase the fees by at least 20% as we get closer to the confirmation target.
|
// We make sure we increase the fees by at least 20% as we get closer to the confirmation target.
|
||||||
val bumpRatio = 1.2
|
val bumpRatio = 1.2
|
||||||
val currentFeerate = getFeerate(nodeParams.currentFeerates, confirmationTarget, currentBlockHeight, isSafe)
|
val currentFeerate = getFeerate(nodeParams.currentBitcoinCoreFeerates, confirmationTarget, currentBlockHeight, isSafe)
|
||||||
val targetFeerate_opt = confirmationTarget match {
|
val targetFeerate_opt = confirmationTarget match {
|
||||||
case ConfirmationTarget.Absolute(confirmBefore) =>
|
case ConfirmationTarget.Absolute(confirmBefore) =>
|
||||||
if (confirmBefore <= currentBlockHeight + 6) {
|
if (confirmBefore <= currentBlockHeight + 6) {
|
||||||
|
@ -29,7 +29,7 @@ import fr.acinq.eclair.NotificationsLogger.NotifyNodeOperator
|
|||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
import fr.acinq.eclair.blockchain.bitcoind.ZmqWatcher
|
||||||
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.blockchain.{OnChainChannelFunder, OnchainPubkeyCache}
|
import fr.acinq.eclair.blockchain.{CurrentFeerates, OnChainChannelFunder, OnchainPubkeyCache}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.channel.fsm.Channel
|
import fr.acinq.eclair.channel.fsm.Channel
|
||||||
import fr.acinq.eclair.io.MessageRelay.Status
|
import fr.acinq.eclair.io.MessageRelay.Status
|
||||||
@ -63,6 +63,8 @@ class Peer(val nodeParams: NodeParams,
|
|||||||
|
|
||||||
import Peer._
|
import Peer._
|
||||||
|
|
||||||
|
context.system.eventStream.subscribe(self, classOf[CurrentFeerates])
|
||||||
|
|
||||||
startWith(INSTANTIATING, Nothing)
|
startWith(INSTANTIATING, Nothing)
|
||||||
|
|
||||||
when(INSTANTIATING) {
|
when(INSTANTIATING) {
|
||||||
@ -170,8 +172,8 @@ class Peer(val nodeParams: NodeParams,
|
|||||||
} else {
|
} else {
|
||||||
randomBytes32()
|
randomBytes32()
|
||||||
}
|
}
|
||||||
val fundingTxFeerate = c.fundingTxFeerate_opt.getOrElse(nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentFeerates))
|
val fundingTxFeerate = c.fundingTxFeerate_opt.getOrElse(nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentBitcoinCoreFeerates))
|
||||||
val commitTxFeerate = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentFeerates, remoteNodeId, channelType.commitmentFormat, c.fundingAmount)
|
val commitTxFeerate = nodeParams.onChainFeeConf.getCommitmentFeerate(nodeParams.currentBitcoinCoreFeerates, remoteNodeId, channelType.commitmentFormat, c.fundingAmount)
|
||||||
log.info(s"requesting a new channel with type=$channelType fundingAmount=${c.fundingAmount} dualFunded=$dualFunded pushAmount=${c.pushAmount_opt} fundingFeerate=$fundingTxFeerate temporaryChannelId=$temporaryChannelId localParams=$localParams")
|
log.info(s"requesting a new channel with type=$channelType fundingAmount=${c.fundingAmount} dualFunded=$dualFunded pushAmount=${c.pushAmount_opt} fundingFeerate=$fundingTxFeerate temporaryChannelId=$temporaryChannelId localParams=$localParams")
|
||||||
channel ! INPUT_INIT_CHANNEL_INITIATOR(temporaryChannelId, c.fundingAmount, dualFunded, commitTxFeerate, fundingTxFeerate, c.fundingTxFeeBudget_opt, c.pushAmount_opt, requireConfirmedInputs, c.requestFunding_opt, localParams, d.peerConnection, d.remoteInit, c.channelFlags_opt.getOrElse(nodeParams.channelConf.channelFlags), channelConfig, channelType, c.channelOrigin, replyTo)
|
channel ! INPUT_INIT_CHANNEL_INITIATOR(temporaryChannelId, c.fundingAmount, dualFunded, commitTxFeerate, fundingTxFeerate, c.fundingTxFeeBudget_opt, c.pushAmount_opt, requireConfirmedInputs, c.requestFunding_opt, localParams, d.peerConnection, d.remoteInit, c.channelFlags_opt.getOrElse(nodeParams.channelConf.channelFlags), channelConfig, channelType, c.channelOrigin, replyTo)
|
||||||
stay() using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel))
|
stay() using d.copy(channels = d.channels + (TemporaryChannelId(temporaryChannelId) -> channel))
|
||||||
@ -345,6 +347,13 @@ class Peer(val nodeParams: NodeParams,
|
|||||||
}
|
}
|
||||||
stay()
|
stay()
|
||||||
|
|
||||||
|
case Event(_: CurrentFeerates, d) =>
|
||||||
|
d match {
|
||||||
|
case d: ConnectedData => d.peerConnection ! nodeParams.recommendedFeerates(remoteNodeId, d.localFeatures, d.remoteFeatures)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
stay()
|
||||||
|
|
||||||
case Event(_: Peer.OutgoingMessage, _) => stay() // we got disconnected or reconnected and this message was for the previous connection
|
case Event(_: Peer.OutgoingMessage, _) => stay() // we got disconnected or reconnected and this message was for the previous connection
|
||||||
|
|
||||||
case Event(RelayOnionMessage(messageId, _, replyTo_opt), _) =>
|
case Event(RelayOnionMessage(messageId, _, replyTo_opt), _) =>
|
||||||
@ -389,6 +398,9 @@ class Peer(val nodeParams: NodeParams,
|
|||||||
// let's bring existing/requested channels online
|
// let's bring existing/requested channels online
|
||||||
channels.values.toSet[ActorRef].foreach(_ ! INPUT_RECONNECTED(connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit)) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
|
channels.values.toSet[ActorRef].foreach(_ ! INPUT_RECONNECTED(connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit)) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
|
||||||
|
|
||||||
|
// We tell our peer what our current feerates are.
|
||||||
|
connectionReady.peerConnection ! nodeParams.recommendedFeerates(remoteNodeId, connectionReady.localInit.features, connectionReady.remoteInit.features)
|
||||||
|
|
||||||
goto(CONNECTED) using ConnectedData(connectionReady.address, connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit, channels)
|
goto(CONNECTED) using ConnectedData(connectionReady.address, connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit, channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,6 +429,12 @@ object LightningMessageCodecs {
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
val recommendedFeeratesCodec: Codec[RecommendedFeerates] = (
|
||||||
|
("chainHash" | blockHash) ::
|
||||||
|
("fundingFeerate" | feeratePerKw) ::
|
||||||
|
("commitmentFeerate" | feeratePerKw) ::
|
||||||
|
("tlvStream" | RecommendedFeeratesTlv.recommendedFeeratesTlvCodec)).as[RecommendedFeerates]
|
||||||
|
|
||||||
val unknownMessageCodec: Codec[UnknownMessage] = (
|
val unknownMessageCodec: Codec[UnknownMessage] = (
|
||||||
("tag" | uint16) ::
|
("tag" | uint16) ::
|
||||||
("message" | bytes)
|
("message" | bytes)
|
||||||
@ -479,14 +485,15 @@ object LightningMessageCodecs {
|
|||||||
.typecase(513, onionMessageCodec)
|
.typecase(513, onionMessageCodec)
|
||||||
// NB: blank lines to minimize merge conflicts
|
// NB: blank lines to minimize merge conflicts
|
||||||
|
|
||||||
|
//
|
||||||
//
|
//
|
||||||
.typecase(37000, spliceInitCodec)
|
.typecase(37000, spliceInitCodec)
|
||||||
.typecase(37002, spliceAckCodec)
|
.typecase(37002, spliceAckCodec)
|
||||||
.typecase(37004, spliceLockedCodec)
|
.typecase(37004, spliceLockedCodec)
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
|
//
|
||||||
|
.typecase(39409, recommendedFeeratesCodec)
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -607,4 +607,15 @@ case class OnionMessage(blindingKey: PublicKey, onionRoutingPacket: OnionRouting
|
|||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This message informs our peers of the feerates we recommend using.
|
||||||
|
* We may reject funding attempts that use values that are too far from our recommended feerates.
|
||||||
|
*/
|
||||||
|
case class RecommendedFeerates(chainHash: BlockHash, fundingFeerate: FeeratePerKw, commitmentFeerate: FeeratePerKw, tlvStream: TlvStream[RecommendedFeeratesTlv] = TlvStream.empty) extends SetupMessage with HasChainHash {
|
||||||
|
val minFundingFeerate: FeeratePerKw = tlvStream.get[RecommendedFeeratesTlv.FundingFeerateRange].map(_.min).getOrElse(fundingFeerate)
|
||||||
|
val maxFundingFeerate: FeeratePerKw = tlvStream.get[RecommendedFeeratesTlv.FundingFeerateRange].map(_.max).getOrElse(fundingFeerate)
|
||||||
|
val minCommitmentFeerate: FeeratePerKw = tlvStream.get[RecommendedFeeratesTlv.CommitmentFeerateRange].map(_.min).getOrElse(commitmentFeerate)
|
||||||
|
val maxCommitmentFeerate: FeeratePerKw = tlvStream.get[RecommendedFeeratesTlv.CommitmentFeerateRange].map(_.max).getOrElse(commitmentFeerate)
|
||||||
|
}
|
||||||
|
|
||||||
case class UnknownMessage(tag: Int, data: ByteVector) extends LightningMessage
|
case class UnknownMessage(tag: Int, data: ByteVector) extends LightningMessage
|
@ -18,6 +18,7 @@ package fr.acinq.eclair.wire.protocol
|
|||||||
|
|
||||||
import fr.acinq.bitcoin.scalacompat.BlockHash
|
import fr.acinq.bitcoin.scalacompat.BlockHash
|
||||||
import fr.acinq.eclair.UInt64
|
import fr.acinq.eclair.UInt64
|
||||||
|
import fr.acinq.eclair.blockchain.fee.FeeratePerKw
|
||||||
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
import fr.acinq.eclair.wire.protocol.CommonCodecs._
|
||||||
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
|
import fr.acinq.eclair.wire.protocol.TlvCodecs.{tlvField, tlvStream}
|
||||||
import scodec.Codec
|
import scodec.Codec
|
||||||
@ -85,4 +86,23 @@ sealed trait PongTlv extends Tlv
|
|||||||
|
|
||||||
object PongTlv {
|
object PongTlv {
|
||||||
val pongTlvCodec: Codec[TlvStream[PongTlv]] = tlvStream(discriminated[PongTlv].by(varint))
|
val pongTlvCodec: Codec[TlvStream[PongTlv]] = tlvStream(discriminated[PongTlv].by(varint))
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait RecommendedFeeratesTlv extends Tlv
|
||||||
|
|
||||||
|
object RecommendedFeeratesTlv {
|
||||||
|
/** Detailed range of values that will be accepted until the next [[RecommendedFeerates]] message is sent. */
|
||||||
|
case class FundingFeerateRange(min: FeeratePerKw, max: FeeratePerKw) extends RecommendedFeeratesTlv
|
||||||
|
|
||||||
|
private val fundingFeerateRangeCodec: Codec[FundingFeerateRange] = tlvField(feeratePerKw :: feeratePerKw)
|
||||||
|
|
||||||
|
/** Detailed range of values that will be accepted until the next [[RecommendedFeerates]] message is sent. */
|
||||||
|
case class CommitmentFeerateRange(min: FeeratePerKw, max: FeeratePerKw) extends RecommendedFeeratesTlv
|
||||||
|
|
||||||
|
private val commitmentFeerateRangeCodec: Codec[CommitmentFeerateRange] = tlvField(feeratePerKw :: feeratePerKw)
|
||||||
|
|
||||||
|
val recommendedFeeratesTlvCodec = tlvStream(discriminated[RecommendedFeeratesTlv].by(varint)
|
||||||
|
.typecase(UInt64(1), fundingFeerateRangeCodec)
|
||||||
|
.typecase(UInt64(3), commitmentFeerateRangeCodec)
|
||||||
|
)
|
||||||
}
|
}
|
@ -90,7 +90,7 @@ object TestConstants {
|
|||||||
channelKeyManager,
|
channelKeyManager,
|
||||||
onChainKeyManager_opt = None,
|
onChainKeyManager_opt = None,
|
||||||
blockHeight = new AtomicLong(defaultBlockHeight),
|
blockHeight = new AtomicLong(defaultBlockHeight),
|
||||||
feerates = new AtomicReference(FeeratesPerKw.single(feeratePerKw)),
|
bitcoinCoreFeerates = new AtomicReference(FeeratesPerKw.single(feeratePerKw)),
|
||||||
alias = "alice",
|
alias = "alice",
|
||||||
color = Color(1, 2, 3),
|
color = Color(1, 2, 3),
|
||||||
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil,
|
publicAddresses = NodeAddress.fromParts("localhost", 9731).get :: Nil,
|
||||||
@ -264,7 +264,7 @@ object TestConstants {
|
|||||||
channelKeyManager,
|
channelKeyManager,
|
||||||
onChainKeyManager_opt = None,
|
onChainKeyManager_opt = None,
|
||||||
blockHeight = new AtomicLong(defaultBlockHeight),
|
blockHeight = new AtomicLong(defaultBlockHeight),
|
||||||
feerates = new AtomicReference(FeeratesPerKw.single(feeratePerKw)),
|
bitcoinCoreFeerates = new AtomicReference(FeeratesPerKw.single(feeratePerKw)),
|
||||||
alias = "bob",
|
alias = "bob",
|
||||||
color = Color(4, 5, 6),
|
color = Color(4, 5, 6),
|
||||||
publicAddresses = NodeAddress.fromParts("localhost", 9732).get :: Nil,
|
publicAddresses = NodeAddress.fromParts("localhost", 9732).get :: Nil,
|
||||||
|
@ -118,7 +118,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
|
|
||||||
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
||||||
case (current, tx) =>
|
case (current, tx) =>
|
||||||
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
Closing.updateLocalCommitPublished(current1, tx)
|
Closing.updateLocalCommitPublished(current1, tx)
|
||||||
}
|
}
|
||||||
assert(!lcp3.isDone)
|
assert(!lcp3.isDone)
|
||||||
@ -141,7 +141,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
|
|
||||||
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
||||||
case (current, tx) =>
|
case (current, tx) =>
|
||||||
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
Closing.updateLocalCommitPublished(current1, tx)
|
Closing.updateLocalCommitPublished(current1, tx)
|
||||||
}
|
}
|
||||||
assert(!lcp3.isDone)
|
assert(!lcp3.isDone)
|
||||||
@ -160,7 +160,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
assert(lcp5.claimHtlcDelayedTxs.length == 3)
|
assert(lcp5.claimHtlcDelayedTxs.length == 3)
|
||||||
|
|
||||||
val newHtlcSuccessTx = lcp5.htlcTxs(remainingHtlcOutpoint).get.tx
|
val newHtlcSuccessTx = lcp5.htlcTxs(remainingHtlcOutpoint).get.tx
|
||||||
val (lcp6, Some(newClaimHtlcDelayedTx)) = Closing.LocalClose.claimHtlcDelayedOutput(lcp5, nodeParams.channelKeyManager, aliceClosing.commitments.latest, newHtlcSuccessTx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (lcp6, Some(newClaimHtlcDelayedTx)) = Closing.LocalClose.claimHtlcDelayedOutput(lcp5, nodeParams.channelKeyManager, aliceClosing.commitments.latest, newHtlcSuccessTx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
assert(lcp6.claimHtlcDelayedTxs.length == 4)
|
assert(lcp6.claimHtlcDelayedTxs.length == 4)
|
||||||
|
|
||||||
val lcp7 = Closing.updateLocalCommitPublished(lcp6, newHtlcSuccessTx)
|
val lcp7 = Closing.updateLocalCommitPublished(lcp6, newHtlcSuccessTx)
|
||||||
@ -177,7 +177,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
val remoteHtlcSuccess = rcp.claimHtlcTxs.values.collectFirst { case Some(tx: ClaimHtlcSuccessTx) => tx }.get
|
val remoteHtlcSuccess = rcp.claimHtlcTxs.values.collectFirst { case Some(tx: ClaimHtlcSuccessTx) => tx }.get
|
||||||
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ Seq(remoteHtlcSuccess.tx)).foldLeft(lcp) {
|
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ Seq(remoteHtlcSuccess.tx)).foldLeft(lcp) {
|
||||||
case (current, tx) =>
|
case (current, tx) =>
|
||||||
val (current1, _) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (current1, _) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
Closing.updateLocalCommitPublished(current1, tx)
|
Closing.updateLocalCommitPublished(current1, tx)
|
||||||
}
|
}
|
||||||
assert(lcp3.claimHtlcDelayedTxs.length == 1)
|
assert(lcp3.claimHtlcDelayedTxs.length == 1)
|
||||||
@ -188,7 +188,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
|
|
||||||
val remainingHtlcTimeoutTxs = htlcTimeoutTxs.filter(_.input.outPoint != remoteHtlcSuccess.input.outPoint)
|
val remainingHtlcTimeoutTxs = htlcTimeoutTxs.filter(_.input.outPoint != remoteHtlcSuccess.input.outPoint)
|
||||||
assert(remainingHtlcTimeoutTxs.length == 1)
|
assert(remainingHtlcTimeoutTxs.length == 1)
|
||||||
val (lcp5, Some(remainingClaimHtlcTx)) = Closing.LocalClose.claimHtlcDelayedOutput(lcp4, nodeParams.channelKeyManager, aliceClosing.commitments.latest, remainingHtlcTimeoutTxs.head.tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (lcp5, Some(remainingClaimHtlcTx)) = Closing.LocalClose.claimHtlcDelayedOutput(lcp4, nodeParams.channelKeyManager, aliceClosing.commitments.latest, remainingHtlcTimeoutTxs.head.tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
assert(lcp5.claimHtlcDelayedTxs.length == 2)
|
assert(lcp5.claimHtlcDelayedTxs.length == 2)
|
||||||
|
|
||||||
val lcp6 = (remainingHtlcTimeoutTxs.map(_.tx) ++ Seq(remainingClaimHtlcTx.tx)).foldLeft(lcp5) {
|
val lcp6 = (remainingHtlcTimeoutTxs.map(_.tx) ++ Seq(remainingClaimHtlcTx.tx)).foldLeft(lcp5) {
|
||||||
@ -207,7 +207,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
|
|
||||||
val lcp3 = htlcTimeoutTxs.map(_.tx).foldLeft(lcp) {
|
val lcp3 = htlcTimeoutTxs.map(_.tx).foldLeft(lcp) {
|
||||||
case (current, tx) =>
|
case (current, tx) =>
|
||||||
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
Closing.updateLocalCommitPublished(current1, tx)
|
Closing.updateLocalCommitPublished(current1, tx)
|
||||||
}
|
}
|
||||||
assert(!lcp3.isDone)
|
assert(!lcp3.isDone)
|
||||||
@ -233,7 +233,7 @@ class ChannelDataSpec extends TestKitBaseClass with AnyFunSuiteLike with Channel
|
|||||||
|
|
||||||
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
val lcp3 = (htlcSuccessTxs.map(_.tx) ++ htlcTimeoutTxs.map(_.tx)).foldLeft(lcp) {
|
||||||
case (current, tx) =>
|
case (current, tx) =>
|
||||||
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
val (current1, Some(_)) = Closing.LocalClose.claimHtlcDelayedOutput(current, nodeParams.channelKeyManager, aliceClosing.commitments.latest, tx, nodeParams.currentBitcoinCoreFeerates, nodeParams.onChainFeeConf, aliceClosing.finalScriptPubKey)
|
||||||
Closing.updateLocalCommitPublished(current1, tx)
|
Closing.updateLocalCommitPublished(current1, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
assert(bc0.availableBalanceForReceive == a)
|
assert(bc0.availableBalanceForReceive == a)
|
||||||
|
|
||||||
val (payment_preimage, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
val (payment_preimage, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
||||||
val Right((ac1, add)) = ac0.sendAdd(cmdAdd, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val Right((ac1, add)) = ac0.sendAdd(cmdAdd, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(ac1.availableBalanceForSend == a - p - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
assert(ac1.availableBalanceForSend == a - p - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
||||||
assert(ac1.availableBalanceForReceive == b)
|
assert(ac1.availableBalanceForReceive == b)
|
||||||
|
|
||||||
val Right(bc1) = bc0.receiveAdd(add, bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
val Right(bc1) = bc0.receiveAdd(add, bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(bc1.availableBalanceForSend == b)
|
assert(bc1.availableBalanceForSend == b)
|
||||||
assert(bc1.availableBalanceForReceive == a - p - htlcOutputFee)
|
assert(bc1.availableBalanceForReceive == a - p - htlcOutputFee)
|
||||||
|
|
||||||
@ -167,11 +167,11 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
assert(bc0.availableBalanceForReceive == a)
|
assert(bc0.availableBalanceForReceive == a)
|
||||||
|
|
||||||
val (_, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
val (_, cmdAdd) = makeCmdAdd(p, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
||||||
val Right((ac1, add)) = ac0.sendAdd(cmdAdd, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val Right((ac1, add)) = ac0.sendAdd(cmdAdd, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(ac1.availableBalanceForSend == a - p - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
assert(ac1.availableBalanceForSend == a - p - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
||||||
assert(ac1.availableBalanceForReceive == b)
|
assert(ac1.availableBalanceForReceive == b)
|
||||||
|
|
||||||
val Right(bc1) = bc0.receiveAdd(add, bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
val Right(bc1) = bc0.receiveAdd(add, bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(bc1.availableBalanceForSend == b)
|
assert(bc1.availableBalanceForSend == b)
|
||||||
assert(bc1.availableBalanceForReceive == a - p - htlcOutputFee)
|
assert(bc1.availableBalanceForReceive == a - p - htlcOutputFee)
|
||||||
|
|
||||||
@ -255,29 +255,29 @@ class CommitmentsSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
assert(bc0.availableBalanceForReceive == a)
|
assert(bc0.availableBalanceForReceive == a)
|
||||||
|
|
||||||
val (payment_preimage1, cmdAdd1) = makeCmdAdd(p1, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
val (payment_preimage1, cmdAdd1) = makeCmdAdd(p1, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
||||||
val Right((ac1, add1)) = ac0.sendAdd(cmdAdd1, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val Right((ac1, add1)) = ac0.sendAdd(cmdAdd1, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(ac1.availableBalanceForSend == a - p1 - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
assert(ac1.availableBalanceForSend == a - p1 - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
||||||
assert(ac1.availableBalanceForReceive == b)
|
assert(ac1.availableBalanceForReceive == b)
|
||||||
|
|
||||||
val (_, cmdAdd2) = makeCmdAdd(p2, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
val (_, cmdAdd2) = makeCmdAdd(p2, bob.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
||||||
val Right((ac2, add2)) = ac1.sendAdd(cmdAdd2, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val Right((ac2, add2)) = ac1.sendAdd(cmdAdd2, currentBlockHeight, alice.underlyingActor.nodeParams.channelConf, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(ac2.availableBalanceForSend == a - p1 - htlcOutputFee - p2 - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
assert(ac2.availableBalanceForSend == a - p1 - htlcOutputFee - p2 - htlcOutputFee) // as soon as htlc is sent, alice sees its balance decrease (more than the payment amount because of the commitment fees)
|
||||||
assert(ac2.availableBalanceForReceive == b)
|
assert(ac2.availableBalanceForReceive == b)
|
||||||
|
|
||||||
val (payment_preimage3, cmdAdd3) = makeCmdAdd(p3, alice.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
val (payment_preimage3, cmdAdd3) = makeCmdAdd(p3, alice.underlyingActor.nodeParams.nodeId, currentBlockHeight)
|
||||||
val Right((bc1, add3)) = bc0.sendAdd(cmdAdd3, currentBlockHeight, bob.underlyingActor.nodeParams.channelConf, bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
val Right((bc1, add3)) = bc0.sendAdd(cmdAdd3, currentBlockHeight, bob.underlyingActor.nodeParams.channelConf, bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(bc1.availableBalanceForSend == b - p3) // bob doesn't pay the fee
|
assert(bc1.availableBalanceForSend == b - p3) // bob doesn't pay the fee
|
||||||
assert(bc1.availableBalanceForReceive == a)
|
assert(bc1.availableBalanceForReceive == a)
|
||||||
|
|
||||||
val Right(bc2) = bc1.receiveAdd(add1, bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
val Right(bc2) = bc1.receiveAdd(add1, bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(bc2.availableBalanceForSend == b - p3)
|
assert(bc2.availableBalanceForSend == b - p3)
|
||||||
assert(bc2.availableBalanceForReceive == a - p1 - htlcOutputFee)
|
assert(bc2.availableBalanceForReceive == a - p1 - htlcOutputFee)
|
||||||
|
|
||||||
val Right(bc3) = bc2.receiveAdd(add2, bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
val Right(bc3) = bc2.receiveAdd(add2, bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(bc3.availableBalanceForSend == b - p3)
|
assert(bc3.availableBalanceForSend == b - p3)
|
||||||
assert(bc3.availableBalanceForReceive == a - p1 - htlcOutputFee - p2 - htlcOutputFee)
|
assert(bc3.availableBalanceForReceive == a - p1 - htlcOutputFee - p2 - htlcOutputFee)
|
||||||
|
|
||||||
val Right(ac3) = ac2.receiveAdd(add3, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val Right(ac3) = ac2.receiveAdd(add3, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(ac3.availableBalanceForSend == a - p1 - htlcOutputFee - p2 - htlcOutputFee)
|
assert(ac3.availableBalanceForSend == a - p1 - htlcOutputFee - p2 - htlcOutputFee)
|
||||||
assert(ac3.availableBalanceForReceive == b - p3)
|
assert(ac3.availableBalanceForReceive == b - p3)
|
||||||
|
|
||||||
|
@ -84,8 +84,8 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
|
|||||||
|
|
||||||
/** Set uniform feerate for all block targets. */
|
/** Set uniform feerate for all block targets. */
|
||||||
def setFeerate(feerate: FeeratePerKw, fastest: FeeratePerKw = FeeratePerKw(100_000 sat)): Unit = {
|
def setFeerate(feerate: FeeratePerKw, fastest: FeeratePerKw = FeeratePerKw(100_000 sat)): Unit = {
|
||||||
alice.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(feerate).copy(fastest = fastest))
|
alice.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(feerate).copy(fastest = fastest))
|
||||||
bob.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(feerate).copy(fastest = fastest))
|
bob.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(feerate).copy(fastest = fastest))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set feerate for a specific block target. */
|
/** Set feerate for a specific block target. */
|
||||||
@ -97,8 +97,8 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
|
|||||||
case _ => currentFeerates.copy(slow = feerate)
|
case _ => currentFeerates.copy(slow = feerate)
|
||||||
}
|
}
|
||||||
|
|
||||||
alice.underlyingActor.nodeParams.setFeerates(updateFeerates(alice.underlyingActor.nodeParams.currentFeerates))
|
alice.underlyingActor.nodeParams.setBitcoinCoreFeerates(updateFeerates(alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates))
|
||||||
bob.underlyingActor.nodeParams.setFeerates(updateFeerates(alice.underlyingActor.nodeParams.currentFeerates))
|
bob.underlyingActor.nodeParams.setBitcoinCoreFeerates(updateFeerates(alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates))
|
||||||
}
|
}
|
||||||
|
|
||||||
def getMempool(): Seq[Transaction] = {
|
def getMempool(): Seq[Transaction] = {
|
||||||
@ -496,7 +496,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
|
|||||||
probe.expectMsg(commitTx.tx.txid)
|
probe.expectMsg(commitTx.tx.txid)
|
||||||
assert(getMempool().length == 1)
|
assert(getMempool().length == 1)
|
||||||
|
|
||||||
val maxFeerate = ReplaceableTxFunder.maxFeerate(anchorTx.txInfo, anchorTx.commitment, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val maxFeerate = ReplaceableTxFunder.maxFeerate(anchorTx.txInfo, anchorTx.commitment, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
val targetFeerate = FeeratePerKw(50_000 sat)
|
val targetFeerate = FeeratePerKw(50_000 sat)
|
||||||
assert(maxFeerate <= targetFeerate / 2)
|
assert(maxFeerate <= targetFeerate / 2)
|
||||||
setFeerate(targetFeerate, blockTarget = 12)
|
setFeerate(targetFeerate, blockTarget = 12)
|
||||||
@ -1168,12 +1168,12 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
|
|||||||
val (commitTx, htlcSuccess, htlcTimeout) = closeChannelWithHtlcs(f, aliceBlockHeight() + 30, outgoingHtlcAmount = 5_000_000 msat, incomingHtlcAmount = 4_000_000 msat)
|
val (commitTx, htlcSuccess, htlcTimeout) = closeChannelWithHtlcs(f, aliceBlockHeight() + 30, outgoingHtlcAmount = 5_000_000 msat, incomingHtlcAmount = 4_000_000 msat)
|
||||||
setFeerate(targetFeerate, blockTarget = 12)
|
setFeerate(targetFeerate, blockTarget = 12)
|
||||||
assert(htlcSuccess.txInfo.fee == 0.sat)
|
assert(htlcSuccess.txInfo.fee == 0.sat)
|
||||||
val htlcSuccessMaxFeerate = ReplaceableTxFunder.maxFeerate(htlcSuccess.txInfo, htlcSuccess.commitment, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val htlcSuccessMaxFeerate = ReplaceableTxFunder.maxFeerate(htlcSuccess.txInfo, htlcSuccess.commitment, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(htlcSuccessMaxFeerate < targetFeerate / 2)
|
assert(htlcSuccessMaxFeerate < targetFeerate / 2)
|
||||||
val htlcSuccessTx = testPublishHtlcSuccess(f, commitTx, htlcSuccess, htlcSuccessMaxFeerate)
|
val htlcSuccessTx = testPublishHtlcSuccess(f, commitTx, htlcSuccess, htlcSuccessMaxFeerate)
|
||||||
assert(htlcSuccessTx.txIn.length > 1)
|
assert(htlcSuccessTx.txIn.length > 1)
|
||||||
assert(htlcTimeout.txInfo.fee == 0.sat)
|
assert(htlcTimeout.txInfo.fee == 0.sat)
|
||||||
val htlcTimeoutMaxFeerate = ReplaceableTxFunder.maxFeerate(htlcTimeout.txInfo, htlcTimeout.commitment, alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
val htlcTimeoutMaxFeerate = ReplaceableTxFunder.maxFeerate(htlcTimeout.txInfo, htlcTimeout.commitment, alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.nodeParams.onChainFeeConf)
|
||||||
assert(htlcTimeoutMaxFeerate < targetFeerate / 2)
|
assert(htlcTimeoutMaxFeerate < targetFeerate / 2)
|
||||||
val htlcTimeoutTx = testPublishHtlcTimeout(f, commitTx, htlcTimeout, htlcTimeoutMaxFeerate)
|
val htlcTimeoutTx = testPublishHtlcTimeout(f, commitTx, htlcTimeout, htlcTimeoutMaxFeerate)
|
||||||
assert(htlcTimeoutTx.txIn.length > 1)
|
assert(htlcTimeoutTx.txIn.length > 1)
|
||||||
@ -1567,7 +1567,7 @@ class ReplaceableTxPublisherSpec extends TestKitBaseClass with AnyFunSuiteLike w
|
|||||||
withFixture(Seq(11 millibtc), ChannelTypes.AnchorOutputsZeroFeeHtlcTx()) { f =>
|
withFixture(Seq(11 millibtc), ChannelTypes.AnchorOutputsZeroFeeHtlcTx()) { f =>
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val currentFeerate = alice.underlyingActor.nodeParams.currentFeerates.fast
|
val currentFeerate = alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates.fast
|
||||||
val (remoteCommitTx, claimHtlcSuccess, claimHtlcTimeout) = remoteCloseChannelWithHtlcs(f, aliceBlockHeight() + 50, nextCommit = false)
|
val (remoteCommitTx, claimHtlcSuccess, claimHtlcTimeout) = remoteCloseChannelWithHtlcs(f, aliceBlockHeight() + 50, nextCommit = false)
|
||||||
val claimHtlcSuccessTx = testPublishClaimHtlcSuccess(f, remoteCommitTx, claimHtlcSuccess, currentFeerate)
|
val claimHtlcSuccessTx = testPublishClaimHtlcSuccess(f, remoteCommitTx, claimHtlcSuccess, currentFeerate)
|
||||||
assert(claimHtlcSuccess.txInfo.fee > 0.sat)
|
assert(claimHtlcSuccess.txInfo.fee > 0.sat)
|
||||||
|
@ -655,7 +655,7 @@ object ChannelStateTestsBase {
|
|||||||
|
|
||||||
val nodeParams: NodeParams = channel.underlyingActor.nodeParams
|
val nodeParams: NodeParams = channel.underlyingActor.nodeParams
|
||||||
|
|
||||||
def setFeerates(feerates: FeeratesPerKw): Unit = channel.underlyingActor.nodeParams.setFeerates(feerates)
|
def setFeerates(feerates: FeeratesPerKw): Unit = channel.underlyingActor.nodeParams.setBitcoinCoreFeerates(feerates)
|
||||||
|
|
||||||
def setFeerate(feerate: FeeratePerKw): Unit = setFeerates(FeeratesPerKw.single(feerate))
|
def setFeerate(feerate: FeeratePerKw): Unit = setFeerates(FeeratesPerKw.single(feerate))
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
|||||||
|
|
||||||
// When we only splice-out, the fees are paid by deducing them from the next funding amount.
|
// When we only splice-out, the fees are paid by deducing them from the next funding amount.
|
||||||
val fundingTx = alice.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.latest.localFundingStatus.signedTx_opt.get
|
val fundingTx = alice.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.latest.localFundingStatus.signedTx_opt.get
|
||||||
val feerate = alice.nodeParams.onChainFeeConf.getFundingFeerate(alice.nodeParams.currentFeerates)
|
val feerate = alice.nodeParams.onChainFeeConf.getFundingFeerate(alice.nodeParams.currentBitcoinCoreFeerates)
|
||||||
val expectedMiningFee = Transactions.weight2fee(feerate, fundingTx.weight())
|
val expectedMiningFee = Transactions.weight2fee(feerate, fundingTx.weight())
|
||||||
val actualMiningFee = capacity - alice.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.latest.capacity
|
val actualMiningFee = capacity - alice.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.latest.capacity
|
||||||
// Fee computation is approximate (signature size isn't constant).
|
// Fee computation is approximate (signature size isn't constant).
|
||||||
@ -518,7 +518,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik
|
|||||||
alice ! cmd
|
alice ! cmd
|
||||||
// we tweak the feerate
|
// we tweak the feerate
|
||||||
val spliceInit = alice2bob.expectMsgType[SpliceInit].copy(feerate = FeeratePerKw(100.sat))
|
val spliceInit = alice2bob.expectMsgType[SpliceInit].copy(feerate = FeeratePerKw(100.sat))
|
||||||
bob.setFeerates(alice.nodeParams.currentFeerates.copy(minimum = FeeratePerKw(101.sat)))
|
bob.setFeerates(alice.nodeParams.currentBitcoinCoreFeerates.copy(minimum = FeeratePerKw(101.sat)))
|
||||||
alice2bob.forward(bob, spliceInit)
|
alice2bob.forward(bob, spliceInit)
|
||||||
val txAbortBob = bob2alice.expectMsgType[TxAbort]
|
val txAbortBob = bob2alice.expectMsgType[TxAbort]
|
||||||
bob2alice.forward(alice, txAbortBob)
|
bob2alice.forward(alice, txAbortBob)
|
||||||
|
@ -280,7 +280,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
crossSign(alice, bob, alice2bob, bob2alice)
|
crossSign(alice, bob, alice2bob, bob2alice)
|
||||||
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == 0.msat)
|
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.availableBalanceForSend == 0.msat)
|
||||||
// We increase the feerate to get Alice's balance closer to her channel reserve.
|
// We increase the feerate to get Alice's balance closer to her channel reserve.
|
||||||
bob.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(FeeratePerKw(17_500 sat)))
|
bob.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(FeeratePerKw(17_500 sat)))
|
||||||
updateFee(FeeratePerKw(17_500 sat), alice, bob, alice2bob, bob2alice)
|
updateFee(FeeratePerKw(17_500 sat), alice, bob, alice2bob, bob2alice)
|
||||||
|
|
||||||
// At this point alice has the minimal amount to sustain a channel.
|
// At this point alice has the minimal amount to sustain a channel.
|
||||||
@ -557,7 +557,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
val sender = TestProbe()
|
val sender = TestProbe()
|
||||||
bob.setFeerate(FeeratePerKw(20000 sat))
|
bob.setFeerate(FeeratePerKw(20000 sat))
|
||||||
bob ! CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(20000 sat)))
|
bob ! CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(20000 sat)))
|
||||||
bob2alice.expectNoMessage(100 millis) // we don't close because the commitment doesn't contain any HTLC
|
bob2alice.expectNoMessage(100 millis) // we don't close because the commitment doesn't contain any HTLC
|
||||||
|
|
||||||
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
val initialState = bob.stateData.asInstanceOf[DATA_NORMAL]
|
||||||
@ -570,7 +570,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// we now agree on feerate so we can send HTLCs
|
// we now agree on feerate so we can send HTLCs
|
||||||
bob.setFeerate(FeeratePerKw(11000 sat))
|
bob.setFeerate(FeeratePerKw(11000 sat))
|
||||||
bob ! CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
bob ! CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
||||||
bob2alice.expectNoMessage(100 millis)
|
bob2alice.expectNoMessage(100 millis)
|
||||||
bob ! add
|
bob ! add
|
||||||
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
|
sender.expectMsgType[RES_SUCCESS[CMD_ADD_HTLC]]
|
||||||
@ -2313,7 +2313,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
import f._
|
import f._
|
||||||
val bobCommitments = bob.stateData.asInstanceOf[DATA_NORMAL].commitments
|
val bobCommitments = bob.stateData.asInstanceOf[DATA_NORMAL].commitments
|
||||||
val tx = bobCommitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
|
val tx = bobCommitments.latest.localCommit.commitTxAndRemoteSig.commitTx.tx
|
||||||
val expectedFeeratePerKw = bob.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(bob.underlyingActor.nodeParams.currentFeerates, bob.underlyingActor.remoteNodeId, bobCommitments.params.commitmentFormat, bobCommitments.latest.capacity)
|
val expectedFeeratePerKw = bob.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(bob.underlyingActor.nodeParams.currentBitcoinCoreFeerates, bob.underlyingActor.remoteNodeId, bobCommitments.params.commitmentFormat, bobCommitments.latest.capacity)
|
||||||
assert(bobCommitments.latest.localCommit.spec.commitTxFeerate == expectedFeeratePerKw)
|
assert(bobCommitments.latest.localCommit.spec.commitTxFeerate == expectedFeeratePerKw)
|
||||||
bob ! UpdateFee(ByteVector32.Zeroes, FeeratePerKw(252 sat))
|
bob ! UpdateFee(ByteVector32.Zeroes, FeeratePerKw(252 sat))
|
||||||
val error = bob2alice.expectMsgType[Error]
|
val error = bob2alice.expectMsgType[Error]
|
||||||
@ -2973,23 +2973,23 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
test("recv CurrentFeerate (when funder, triggers an UpdateFee)") { f =>
|
test("recv CurrentFeerate (when funder, triggers an UpdateFee)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||||
val event = CurrentFeerates(FeeratesPerKw(minimum = FeeratePerKw(250 sat), fastest = FeeratePerKw(10_000 sat), fast = FeeratePerKw(5_000 sat), medium = FeeratePerKw(1000 sat), slow = FeeratePerKw(500 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw(minimum = FeeratePerKw(250 sat), fastest = FeeratePerKw(10_000 sat), fast = FeeratePerKw(5_000 sat), medium = FeeratePerKw(1000 sat), slow = FeeratePerKw(500 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, alice.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.remoteNodeId, initialState.commitments.params.commitmentFormat, initialState.commitments.latest.capacity)))
|
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, alice.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.remoteNodeId, initialState.commitments.params.commitmentFormat, initialState.commitments.latest.capacity)))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("recv CurrentFeerate (when funder, triggers an UpdateFee, anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
test("recv CurrentFeerate (when funder, triggers an UpdateFee, anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||||
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
val event1 = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(minimum = FeeratePerKw(250 sat)))
|
val event1 = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
alice.setFeerates(event1.feeratesPerKw)
|
alice.setFeerates(event1.feeratesPerKw)
|
||||||
alice ! event1
|
alice ! event1
|
||||||
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
|
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
|
||||||
alice2bob.expectMsgType[CommitSig]
|
alice2bob.expectMsgType[CommitSig]
|
||||||
// The configured maximum feerate is bypassed if it's below the propagation threshold.
|
// The configured maximum feerate is bypassed if it's below the propagation threshold.
|
||||||
val event2 = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = TestConstants.anchorOutputsFeeratePerKw))
|
val event2 = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = TestConstants.anchorOutputsFeeratePerKw))
|
||||||
alice.setFeerates(event2.feeratesPerKw)
|
alice.setFeerates(event2.feeratesPerKw)
|
||||||
alice ! event2
|
alice ! event2
|
||||||
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw * 1.25))
|
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw * 1.25))
|
||||||
@ -2997,7 +2997,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
test("recv CurrentFeerate (when funder, doesn't trigger an UpdateFee)") { f =>
|
test("recv CurrentFeerate (when funder, doesn't trigger an UpdateFee)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10010 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(10010 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectNoMessage(500 millis)
|
alice2bob.expectNoMessage(500 millis)
|
||||||
@ -3007,7 +3007,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
val initialState = alice.stateData.asInstanceOf[DATA_NORMAL]
|
||||||
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectNoMessage(500 millis)
|
alice2bob.expectNoMessage(500 millis)
|
||||||
@ -3015,7 +3015,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are close)") { f =>
|
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are close)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectNoMessage(500 millis)
|
bob2alice.expectNoMessage(500 millis)
|
||||||
@ -3027,7 +3027,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
addHtlc(10000000 msat, alice, bob, alice2bob, bob2alice)
|
addHtlc(10000000 msat, alice, bob, alice2bob, bob2alice)
|
||||||
crossSign(alice, bob, alice2bob, bob2alice)
|
crossSign(alice, bob, alice2bob, bob2alice)
|
||||||
|
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(14000 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(14000 sat)))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectMsgType[Error]
|
bob2alice.expectMsgType[Error]
|
||||||
@ -3051,7 +3051,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw / 2)
|
assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw / 2)
|
||||||
|
|
||||||
// The network fees spike, but Bob doesn't close the channel because we're using anchor outputs.
|
// The network fees spike, but Bob doesn't close the channel because we're using anchor outputs.
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 10))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 10))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectNoMessage(250 millis)
|
bob2alice.expectNoMessage(250 millis)
|
||||||
@ -3061,7 +3061,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, without HTLCs)") { f =>
|
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different, without HTLCs)") { f =>
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC
|
bob2alice.expectNoMessage(250 millis) // we don't close because the commitment doesn't contain any HTLC
|
||||||
@ -3138,7 +3138,7 @@ class NormalStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
assert(getClaimHtlcTimeoutTxs(rcp).length == 2)
|
assert(getClaimHtlcTimeoutTxs(rcp).length == 2)
|
||||||
|
|
||||||
// assert the feerate of the claim main is what we expect
|
// assert the feerate of the claim main is what we expect
|
||||||
val expectedFeeRate = alice.underlyingActor.nodeParams.onChainFeeConf.getClosingFeerate(alice.underlyingActor.nodeParams.currentFeerates)
|
val expectedFeeRate = alice.underlyingActor.nodeParams.onChainFeeConf.getClosingFeerate(alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates)
|
||||||
val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount).sum - claimMain.txOut.map(_.amount).sum
|
val claimFee = claimMain.txIn.map(in => bobCommitTx.txOut(in.outPoint.index.toInt).amount).sum - claimMain.txOut.map(_.amount).sum
|
||||||
val claimFeeRate = Transactions.fee2rate(claimFee, claimMain.weight())
|
val claimFeeRate = Transactions.fee2rate(claimFee, claimMain.weight())
|
||||||
assert(claimFeeRate >= expectedFeeRate * 0.9 && claimFeeRate <= expectedFeeRate * 1.2)
|
assert(claimFeeRate >= expectedFeeRate * 0.9 && claimFeeRate <= expectedFeeRate * 1.2)
|
||||||
|
@ -664,7 +664,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// alice is funder
|
// alice is funder
|
||||||
alice.setFeerates(networkFeerates)
|
alice.setFeerates(networkFeerates)
|
||||||
alice ! CurrentFeerates(networkFeerates)
|
alice ! CurrentFeerates.BitcoinCore(networkFeerates)
|
||||||
if (shouldClose) {
|
if (shouldClose) {
|
||||||
assert(alice2blockchain.expectMsgType[PublishFinalTx].tx.txid == aliceCommitTx.txid)
|
assert(alice2blockchain.expectMsgType[PublishFinalTx].tx.txid == aliceCommitTx.txid)
|
||||||
} else {
|
} else {
|
||||||
@ -691,7 +691,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// this time Alice will ignore feerate changes for the offline channel
|
// this time Alice will ignore feerate changes for the offline channel
|
||||||
alice.setFeerates(networkFeerates)
|
alice.setFeerates(networkFeerates)
|
||||||
alice ! CurrentFeerates(networkFeerates)
|
alice ! CurrentFeerates.BitcoinCore(networkFeerates)
|
||||||
alice2blockchain.expectNoMessage()
|
alice2blockchain.expectNoMessage()
|
||||||
alice2bob.expectNoMessage()
|
alice2bob.expectNoMessage()
|
||||||
}
|
}
|
||||||
@ -708,7 +708,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// Alice ignores feerate changes while offline
|
// Alice ignores feerate changes while offline
|
||||||
alice.setFeerates(networkFeerates)
|
alice.setFeerates(networkFeerates)
|
||||||
alice ! CurrentFeerates(networkFeerates)
|
alice ! CurrentFeerates.BitcoinCore(networkFeerates)
|
||||||
alice2blockchain.expectNoMessage()
|
alice2blockchain.expectNoMessage()
|
||||||
alice2bob.expectNoMessage()
|
alice2bob.expectNoMessage()
|
||||||
|
|
||||||
@ -775,7 +775,7 @@ class OfflineStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// bob is fundee
|
// bob is fundee
|
||||||
bob.setFeerates(networkFeerates)
|
bob.setFeerates(networkFeerates)
|
||||||
bob ! CurrentFeerates(networkFeerates)
|
bob ! CurrentFeerates.BitcoinCore(networkFeerates)
|
||||||
if (shouldClose) {
|
if (shouldClose) {
|
||||||
assert(bob2blockchain.expectMsgType[PublishFinalTx].tx.txid == bobCommitTx.txid)
|
assert(bob2blockchain.expectMsgType[PublishFinalTx].tx.txid == bobCommitTx.txid)
|
||||||
} else {
|
} else {
|
||||||
|
@ -662,17 +662,17 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
|
|||||||
test("recv CurrentFeerate (when funder, triggers an UpdateFee)") { f =>
|
test("recv CurrentFeerate (when funder, triggers an UpdateFee)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
||||||
val event = CurrentFeerates(FeeratesPerKw(minimum = FeeratePerKw(250 sat), fastest = FeeratePerKw(10_000 sat), fast = FeeratePerKw(5_000 sat), medium = FeeratePerKw(1000 sat), slow = FeeratePerKw(500 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw(minimum = FeeratePerKw(250 sat), fastest = FeeratePerKw(10_000 sat), fast = FeeratePerKw(5_000 sat), medium = FeeratePerKw(1000 sat), slow = FeeratePerKw(500 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, alice.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(alice.underlyingActor.nodeParams.currentFeerates, alice.underlyingActor.remoteNodeId, initialState.commitments.params.commitmentFormat, initialState.commitments.latest.capacity)))
|
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, alice.underlyingActor.nodeParams.onChainFeeConf.getCommitmentFeerate(alice.underlyingActor.nodeParams.currentBitcoinCoreFeerates, alice.underlyingActor.remoteNodeId, initialState.commitments.params.commitmentFormat, initialState.commitments.latest.capacity)))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("recv CurrentFeerate (when funder, triggers an UpdateFee, anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
test("recv CurrentFeerate (when funder, triggers an UpdateFee, anchor outputs)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
||||||
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(minimum = FeeratePerKw(250 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw / 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
|
alice2bob.expectMsg(UpdateFee(initialState.commitments.channelId, TestConstants.anchorOutputsFeeratePerKw / 2))
|
||||||
@ -680,7 +680,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
|
|||||||
|
|
||||||
test("recv CurrentFeerate (when funder, doesn't trigger an UpdateFee)") { f =>
|
test("recv CurrentFeerate (when funder, doesn't trigger an UpdateFee)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(10010 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(10010 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectNoMessage(500 millis)
|
alice2bob.expectNoMessage(500 millis)
|
||||||
@ -690,7 +690,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
|
|||||||
import f._
|
import f._
|
||||||
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
val initialState = alice.stateData.asInstanceOf[DATA_SHUTDOWN]
|
||||||
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(initialState.commitments.latest.localCommit.spec.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
alice.setFeerates(event.feeratesPerKw)
|
alice.setFeerates(event.feeratesPerKw)
|
||||||
alice ! event
|
alice ! event
|
||||||
alice2bob.expectNoMessage(500 millis)
|
alice2bob.expectNoMessage(500 millis)
|
||||||
@ -698,7 +698,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
|
|||||||
|
|
||||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are close)") { f =>
|
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are close)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(11000 sat)))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectNoMessage(500 millis)
|
bob2alice.expectNoMessage(500 millis)
|
||||||
@ -706,7 +706,7 @@ class ShutdownStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike wit
|
|||||||
|
|
||||||
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different)") { f =>
|
test("recv CurrentFeerate (when fundee, commit-fee/network-fee are very different)") { f =>
|
||||||
import f._
|
import f._
|
||||||
val event = CurrentFeerates(FeeratesPerKw.single(FeeratePerKw(25000 sat)))
|
val event = CurrentFeerates.BitcoinCore(FeeratesPerKw.single(FeeratePerKw(25000 sat)))
|
||||||
bob.setFeerates(event.feeratesPerKw)
|
bob.setFeerates(event.feeratesPerKw)
|
||||||
bob ! event
|
bob ! event
|
||||||
bob2alice.expectMsgType[Error]
|
bob2alice.expectMsgType[Error]
|
||||||
|
@ -291,7 +291,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
import f._
|
import f._
|
||||||
val sender = TestProbe()
|
val sender = TestProbe()
|
||||||
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.params.channelFeatures == channelFeatures)
|
assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.params.channelFeatures == channelFeatures)
|
||||||
bob.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(FeeratePerKw(2500 sat)).copy(minimum = FeeratePerKw(250 sat), slow = FeeratePerKw(250 sat)))
|
bob.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(FeeratePerKw(2500 sat)).copy(minimum = FeeratePerKw(250 sat), slow = FeeratePerKw(250 sat)))
|
||||||
// alice initiates a closing with a low fee
|
// alice initiates a closing with a low fee
|
||||||
alice ! CMD_CLOSE(sender.ref, None, Some(ClosingFeerates(FeeratePerKw(500 sat), FeeratePerKw(250 sat), FeeratePerKw(1000 sat))))
|
alice ! CMD_CLOSE(sender.ref, None, Some(ClosingFeerates(FeeratePerKw(500 sat), FeeratePerKw(250 sat), FeeratePerKw(1000 sat))))
|
||||||
alice2bob.expectMsgType[Shutdown]
|
alice2bob.expectMsgType[Shutdown]
|
||||||
@ -651,7 +651,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
// simulate a node restart after a feerate increase
|
// simulate a node restart after a feerate increase
|
||||||
val beforeRestart = alice.stateData.asInstanceOf[DATA_CLOSING]
|
val beforeRestart = alice.stateData.asInstanceOf[DATA_CLOSING]
|
||||||
alice.setState(WAIT_FOR_INIT_INTERNAL, Nothing)
|
alice.setState(WAIT_FOR_INIT_INTERNAL, Nothing)
|
||||||
alice.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
alice.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
||||||
alice ! INPUT_RESTORED(beforeRestart)
|
alice ! INPUT_RESTORED(beforeRestart)
|
||||||
alice2blockchain.expectMsgType[SetChannelId]
|
alice2blockchain.expectMsgType[SetChannelId]
|
||||||
awaitCond(alice.stateName == CLOSING)
|
awaitCond(alice.stateName == CLOSING)
|
||||||
@ -739,7 +739,7 @@ class ClosingStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with
|
|||||||
|
|
||||||
// We simulate a node restart after a feerate increase.
|
// We simulate a node restart after a feerate increase.
|
||||||
val beforeRestart = alice.stateData.asInstanceOf[DATA_CLOSING]
|
val beforeRestart = alice.stateData.asInstanceOf[DATA_CLOSING]
|
||||||
alice.underlyingActor.nodeParams.setFeerates(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
alice.underlyingActor.nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(FeeratePerKw(15_000 sat)))
|
||||||
alice.setState(WAIT_FOR_INIT_INTERNAL, Nothing)
|
alice.setState(WAIT_FOR_INIT_INTERNAL, Nothing)
|
||||||
alice ! INPUT_RESTORED(beforeRestart)
|
alice ! INPUT_RESTORED(beforeRestart)
|
||||||
alice2blockchain.expectMsgType[SetChannelId]
|
alice2blockchain.expectMsgType[SetChannelId]
|
||||||
|
@ -74,7 +74,7 @@ object MinimalNodeFixture extends Assertions with Eventually with IntegrationPat
|
|||||||
torAddress_opt = None,
|
torAddress_opt = None,
|
||||||
database = TestDatabases.inMemoryDb(),
|
database = TestDatabases.inMemoryDb(),
|
||||||
blockHeight = new AtomicLong(400_000),
|
blockHeight = new AtomicLong(400_000),
|
||||||
feerates = new AtomicReference(FeeratesPerKw.single(FeeratePerKw(253 sat)))
|
bitcoinCoreFeerates = new AtomicReference(FeeratesPerKw.single(FeeratePerKw(253 sat)))
|
||||||
).modify(_.alias).setTo(alias)
|
).modify(_.alias).setTo(alias)
|
||||||
.modify(_.chainHash).setTo(Block.RegtestGenesisBlock.hash)
|
.modify(_.chainHash).setTo(Block.RegtestGenesisBlock.hash)
|
||||||
.modify(_.routerConf.routerBroadcastInterval).setTo(1 second)
|
.modify(_.routerConf.routerBroadcastInterval).setTo(1 second)
|
||||||
|
@ -25,8 +25,8 @@ import fr.acinq.eclair.FeatureSupport.{Mandatory, Optional}
|
|||||||
import fr.acinq.eclair.Features._
|
import fr.acinq.eclair.Features._
|
||||||
import fr.acinq.eclair.TestConstants._
|
import fr.acinq.eclair.TestConstants._
|
||||||
import fr.acinq.eclair._
|
import fr.acinq.eclair._
|
||||||
import fr.acinq.eclair.blockchain.DummyOnChainWallet
|
|
||||||
import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw}
|
import fr.acinq.eclair.blockchain.fee.{FeeratePerKw, FeeratesPerKw}
|
||||||
|
import fr.acinq.eclair.blockchain.{CurrentFeerates, DummyOnChainWallet}
|
||||||
import fr.acinq.eclair.channel._
|
import fr.acinq.eclair.channel._
|
||||||
import fr.acinq.eclair.channel.states.ChannelStateTestsTags
|
import fr.acinq.eclair.channel.states.ChannelStateTestsTags
|
||||||
import fr.acinq.eclair.io.Peer._
|
import fr.acinq.eclair.io.Peer._
|
||||||
@ -112,6 +112,7 @@ class PeerSpec extends FixtureSpec {
|
|||||||
switchboard.send(peer, Peer.Init(channels))
|
switchboard.send(peer, Peer.Init(channels))
|
||||||
val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures())
|
val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures())
|
||||||
switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = true, localInit, remoteInit))
|
switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = true, localInit, remoteInit))
|
||||||
|
peerConnection.expectMsgType[RecommendedFeerates]
|
||||||
val probe = TestProbe()
|
val probe = TestProbe()
|
||||||
probe.send(peer, Peer.GetPeerInfo(Some(probe.ref.toTyped)))
|
probe.send(peer, Peer.GetPeerInfo(Some(probe.ref.toTyped)))
|
||||||
val peerInfo = probe.expectMsgType[Peer.PeerInfo]
|
val peerInfo = probe.expectMsgType[Peer.PeerInfo]
|
||||||
@ -282,6 +283,7 @@ class PeerSpec extends FixtureSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
peerConnection2.send(peer, PeerConnection.ConnectionReady(peerConnection2.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit))
|
peerConnection2.send(peer, PeerConnection.ConnectionReady(peerConnection2.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit))
|
||||||
|
peerConnection2.expectMsgType[RecommendedFeerates]
|
||||||
// peer should kill previous connection
|
// peer should kill previous connection
|
||||||
peerConnection1.expectMsg(PeerConnection.Kill(PeerConnection.KillReason.ConnectionReplaced))
|
peerConnection1.expectMsg(PeerConnection.Kill(PeerConnection.KillReason.ConnectionReplaced))
|
||||||
channel.expectMsg(INPUT_DISCONNECTED)
|
channel.expectMsg(INPUT_DISCONNECTED)
|
||||||
@ -291,6 +293,7 @@ class PeerSpec extends FixtureSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
peerConnection3.send(peer, PeerConnection.ConnectionReady(peerConnection3.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit))
|
peerConnection3.send(peer, PeerConnection.ConnectionReady(peerConnection3.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit))
|
||||||
|
peerConnection3.expectMsgType[RecommendedFeerates]
|
||||||
// peer should kill previous connection
|
// peer should kill previous connection
|
||||||
peerConnection2.expectMsg(PeerConnection.Kill(PeerConnection.KillReason.ConnectionReplaced))
|
peerConnection2.expectMsg(PeerConnection.Kill(PeerConnection.KillReason.ConnectionReplaced))
|
||||||
channel.expectMsg(INPUT_DISCONNECTED)
|
channel.expectMsg(INPUT_DISCONNECTED)
|
||||||
@ -325,6 +328,26 @@ class PeerSpec extends FixtureSpec {
|
|||||||
monitor.expectMsg(FSM.Transition(reconnectionTask, ReconnectionTask.CONNECTING, ReconnectionTask.IDLE))
|
monitor.expectMsg(FSM.Transition(reconnectionTask, ReconnectionTask.CONNECTING, ReconnectionTask.IDLE))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("send recommended feerates when feerate changes") { f =>
|
||||||
|
import f._
|
||||||
|
|
||||||
|
connect(remoteNodeId, peer, peerConnection, switchboard, channels = Set(ChannelCodecsSpec.normal))
|
||||||
|
|
||||||
|
// We regularly update our internal feerates.
|
||||||
|
val bitcoinCoreFeerates = FeeratesPerKw(FeeratePerKw(253 sat), FeeratePerKw(1000 sat), FeeratePerKw(2500 sat), FeeratePerKw(5000 sat), FeeratePerKw(10_000 sat))
|
||||||
|
nodeParams.setBitcoinCoreFeerates(bitcoinCoreFeerates)
|
||||||
|
peer ! CurrentFeerates.BitcoinCore(bitcoinCoreFeerates)
|
||||||
|
peerConnection.expectMsg(RecommendedFeerates(
|
||||||
|
chainHash = Block.RegtestGenesisBlock.hash,
|
||||||
|
fundingFeerate = FeeratePerKw(2_500 sat),
|
||||||
|
commitmentFeerate = FeeratePerKw(5000 sat),
|
||||||
|
tlvStream = TlvStream[RecommendedFeeratesTlv](
|
||||||
|
RecommendedFeeratesTlv.FundingFeerateRange(FeeratePerKw(1250 sat), FeeratePerKw(20_000 sat)),
|
||||||
|
RecommendedFeeratesTlv.CommitmentFeerateRange(FeeratePerKw(2500 sat), FeeratePerKw(40_000 sat))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
test("don't spawn a channel with duplicate temporary channel id", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
test("don't spawn a channel with duplicate temporary channel id", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
@ -470,14 +493,14 @@ class PeerSpec extends FixtureSpec {
|
|||||||
assert(peer.stateData.channels.isEmpty)
|
assert(peer.stateData.channels.isEmpty)
|
||||||
|
|
||||||
// We ensure the current network feerate is higher than the default anchor output feerate.
|
// We ensure the current network feerate is higher than the default anchor output feerate.
|
||||||
nodeParams.setFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
probe.send(peer, Peer.OpenChannel(remoteNodeId, 15000 sat, None, None, None, None, None, None, None))
|
probe.send(peer, Peer.OpenChannel(remoteNodeId, 15000 sat, None, None, None, None, None, None, None))
|
||||||
val init = channel.expectMsgType[INPUT_INIT_CHANNEL_INITIATOR]
|
val init = channel.expectMsgType[INPUT_INIT_CHANNEL_INITIATOR]
|
||||||
assert(init.channelType == ChannelTypes.AnchorOutputs())
|
assert(init.channelType == ChannelTypes.AnchorOutputs())
|
||||||
assert(!init.dualFunded)
|
assert(!init.dualFunded)
|
||||||
assert(init.fundingAmount == 15000.sat)
|
assert(init.fundingAmount == 15000.sat)
|
||||||
assert(init.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(init.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
assert(init.fundingTxFeerate == nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentFeerates))
|
assert(init.fundingTxFeerate == nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentBitcoinCoreFeerates))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("use correct on-chain fee rates when spawning a channel (anchor outputs zero fee htlc)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
test("use correct on-chain fee rates when spawning a channel (anchor outputs zero fee htlc)", Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f =>
|
||||||
@ -488,14 +511,14 @@ class PeerSpec extends FixtureSpec {
|
|||||||
assert(peer.stateData.channels.isEmpty)
|
assert(peer.stateData.channels.isEmpty)
|
||||||
|
|
||||||
// We ensure the current network feerate is higher than the default anchor output feerate.
|
// We ensure the current network feerate is higher than the default anchor output feerate.
|
||||||
nodeParams.setFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
nodeParams.setBitcoinCoreFeerates(FeeratesPerKw.single(TestConstants.anchorOutputsFeeratePerKw * 2).copy(minimum = FeeratePerKw(250 sat)))
|
||||||
probe.send(peer, Peer.OpenChannel(remoteNodeId, 15000 sat, None, None, None, None, None, None, None))
|
probe.send(peer, Peer.OpenChannel(remoteNodeId, 15000 sat, None, None, None, None, None, None, None))
|
||||||
val init = channel.expectMsgType[INPUT_INIT_CHANNEL_INITIATOR]
|
val init = channel.expectMsgType[INPUT_INIT_CHANNEL_INITIATOR]
|
||||||
assert(init.channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx())
|
assert(init.channelType == ChannelTypes.AnchorOutputsZeroFeeHtlcTx())
|
||||||
assert(!init.dualFunded)
|
assert(!init.dualFunded)
|
||||||
assert(init.fundingAmount == 15000.sat)
|
assert(init.fundingAmount == 15000.sat)
|
||||||
assert(init.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
assert(init.commitTxFeerate == TestConstants.anchorOutputsFeeratePerKw)
|
||||||
assert(init.fundingTxFeerate == nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentFeerates))
|
assert(init.fundingTxFeerate == nodeParams.onChainFeeConf.getFundingFeerate(nodeParams.currentBitcoinCoreFeerates))
|
||||||
}
|
}
|
||||||
|
|
||||||
test("use correct final script if option_static_remotekey is negotiated", Tag(ChannelStateTestsTags.StaticRemoteKey)) { f =>
|
test("use correct final script if option_static_remotekey is negotiated", Tag(ChannelStateTestsTags.StaticRemoteKey)) { f =>
|
||||||
|
@ -514,6 +514,24 @@ class LightningMessageCodecsSpec extends AnyFunSuite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("encode/decode recommended_feerates") {
|
||||||
|
val fundingRange = RecommendedFeeratesTlv.FundingFeerateRange(FeeratePerKw(5000 sat), FeeratePerKw(15_000 sat))
|
||||||
|
val commitmentRange = RecommendedFeeratesTlv.CommitmentFeerateRange(FeeratePerKw(253 sat), FeeratePerKw(2_000 sat))
|
||||||
|
val testCases = Seq(
|
||||||
|
// @formatter:off
|
||||||
|
RecommendedFeerates(Block.Testnet3GenesisBlock.hash, FeeratePerKw(2500 sat), FeeratePerKw(2500 sat)) -> hex"99f1 43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000 000009c4 000009c4",
|
||||||
|
RecommendedFeerates(Block.Testnet3GenesisBlock.hash, FeeratePerKw(5000 sat), FeeratePerKw(253 sat)) -> hex"99f1 43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000 00001388 000000fd",
|
||||||
|
RecommendedFeerates(Block.Testnet3GenesisBlock.hash, FeeratePerKw(10_000 sat), FeeratePerKw(1000 sat), TlvStream(fundingRange, commitmentRange)) -> hex"99f1 43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea330900000000 00002710 000003e8 01080000138800003a98 0308000000fd000007d0"
|
||||||
|
// @formatter:on
|
||||||
|
)
|
||||||
|
for ((expected, encoded) <- testCases) {
|
||||||
|
val decoded = lightningMessageCodec.decode(encoded.bits).require.value
|
||||||
|
assert(decoded == expected)
|
||||||
|
val reEncoded = lightningMessageCodec.encode(decoded).require.bytes
|
||||||
|
assert(reEncoded == encoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("unknown messages") {
|
test("unknown messages") {
|
||||||
// Non-standard tag number so this message can only be handled by a codec with a fallback
|
// Non-standard tag number so this message can only be handled by a codec with a fallback
|
||||||
val unknown = UnknownMessage(tag = 47282, data = ByteVector32.Zeroes.bytes)
|
val unknown = UnknownMessage(tag = 47282, data = ByteVector32.Zeroes.bytes)
|
||||||
|
Loading…
Reference in New Issue
Block a user