diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala index a6dbd9f2c9..95ecd6f887 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Monitoring.scala @@ -16,6 +16,8 @@ package fr.acinq.eclair.channel +import fr.acinq.bitcoin.scalacompat.SatoshiLong +import fr.acinq.eclair.channel.fund.InteractiveTxBuilder.{InteractiveTxParams, SharedTransaction} import fr.acinq.eclair.transactions.{CommitmentSpec, DirectedHtlc} import kamon.Kamon @@ -31,6 +33,7 @@ object Monitoring { val HtlcValueInFlightGlobal = Kamon.gauge("channels.htlc-value-in-flight-global", "Global HTLC value in flight across all channels") val LocalFeeratePerByte = Kamon.histogram("channels.local-feerate-per-byte") val RemoteFeeratePerByte = Kamon.histogram("channels.remote-feerate-per-byte") + val Splices = Kamon.histogram("channels.splices", "Splices") val ProcessMessage = Kamon.timer("channels.messages-processed") def recordHtlcsInFlight(remoteSpec: CommitmentSpec, previousRemoteSpec: CommitmentSpec): Unit = { @@ -47,6 +50,27 @@ object Monitoring { HtlcValueInFlightGlobal.withTag(Tags.Direction, direction).increment((value - previousValue).toDouble) } } + + def recordSplice(fundingParams: InteractiveTxParams, sharedTx: SharedTransaction): Unit = { + if (fundingParams.localContribution > 0.sat) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.localContribution.toLong) + } + if (fundingParams.remoteContribution > 0.sat) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceIn).record(fundingParams.remoteContribution.toLong) + } + if (sharedTx.localOutputs.nonEmpty) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceOut).record(sharedTx.localOutputs.map(_.amount).sum.toLong) + } + if (sharedTx.remoteOutputs.nonEmpty) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceOut).record(sharedTx.remoteOutputs.map(_.amount).sum.toLong) + } + if (fundingParams.localContribution < 0.sat && sharedTx.localOutputs.isEmpty) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Local).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceCpfp).record(Math.abs(fundingParams.localContribution.toLong)) + } + if (fundingParams.remoteContribution < 0.sat && sharedTx.remoteOutputs.isEmpty) { + Metrics.Splices.withTag(Tags.Origin, Tags.Origins.Remote).withTag(Tags.SpliceType, Tags.SpliceTypes.SpliceCpfp).record(Math.abs(fundingParams.remoteContribution.toLong)) + } + } } object Tags { @@ -56,6 +80,7 @@ object Monitoring { val Origin = "origin" val State = "state" val CommitmentFormat = "commitment-format" + val SpliceType = "splice-type" object Events { val Created = "created" @@ -72,6 +97,12 @@ object Monitoring { val Incoming = "incoming" val Outgoing = "outgoing" } + + object SpliceTypes { + val SpliceIn = "splice-in" + val SpliceOut = "splice-out" + val SpliceCpfp = "splice-cpfp" + } } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala index fa34d2f806..863470e9e1 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala @@ -1072,6 +1072,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with d.commitments.updateLocalFundingStatus(msg.txId, dfu1) match { case Right((commitments1, _)) => log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, fundingTx.signedTx.txid) + Metrics.recordSplice(dfu.fundingParams, fundingTx.tx) stay() using d.copy(commitments = commitments1) storing() calling publishFundingTx(dfu1) case Left(_) => stay() @@ -1091,6 +1092,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with val commitments1 = d.commitments.add(signingSession1.commitment) val d1 = d.copy(commitments = commitments1, spliceStatus = SpliceStatus.NoSplice) log.info("publishing funding tx for channelId={} fundingTxId={}", d.channelId, signingSession1.fundingTx.sharedTx.txId) + Metrics.recordSplice(signingSession1.fundingTx.fundingParams, signingSession1.fundingTx.sharedTx.tx) stay() using d1 storing() sending signingSession1.localSigs calling publishFundingTx(signingSession1.fundingTx) calling endQuiescence(d1) } case _ =>