Skip to content

Commit 7d53a40

Browse files
committed
More metrics and test
1 parent d70ca33 commit 7d53a40

3 files changed

Lines changed: 76 additions & 2 deletions

File tree

eclair-core/src/main/scala/fr/acinq/eclair/payment/Monitoring.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
2020
import fr.acinq.eclair.MilliSatoshi
2121
import fr.acinq.eclair.channel.CMD_FAIL_HTLC
2222
import kamon.Kamon
23+
import kamon.metric.Histogram
2324

2425
object Monitoring {
2526

@@ -67,6 +68,10 @@ object Monitoring {
6768
PaymentNodeOutAmount.withoutTags().record(bucket, amount.truncateToSatoshi.toLong)
6869
PaymentNodeOut.withoutTags().record(bucket)
6970
}
71+
72+
private val RelayConfidence = Kamon.histogram("payment.relay.confidence", "Confidence (in percent) that the relayed HTLC will be fulfilled")
73+
def relaySettleFulfill(confidence: Double) = RelayConfidence.withTag("status", "fulfill").record((confidence * 100).toLong)
74+
def relaySettleFail(confidence: Double) = RelayConfidence.withTag("status", "fail").record((confidence * 100).toLong)
7075
}
7176

7277
object Tags {

eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/ChannelRelay.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,20 +163,22 @@ class ChannelRelay private(nodeParams: NodeParams,
163163

164164
case WrappedAddResponse(_: RES_SUCCESS[_]) =>
165165
context.log.debug("sent htlc to the downstream channel")
166-
waitForAddSettled()
166+
waitForAddSettled(confidence)
167167
}
168168

169-
def waitForAddSettled(): Behavior[Command] =
169+
def waitForAddSettled(confidence: Double): Behavior[Command] =
170170
Behaviors.receiveMessagePartial {
171171
case WrappedAddResponse(RES_ADD_SETTLED(o: Origin.ChannelRelayedHot, htlc, fulfill: HtlcResult.Fulfill)) =>
172172
context.log.debug("relaying fulfill to upstream")
173+
Metrics.relaySettleFulfill(confidence)
173174
val cmd = CMD_FULFILL_HTLC(o.originHtlcId, fulfill.paymentPreimage, commit = true)
174175
context.system.eventStream ! EventStream.Publish(ChannelPaymentRelayed(o.amountIn, o.amountOut, htlc.paymentHash, o.originChannelId, htlc.channelId, startedAt, TimestampMilli.now()))
175176
recordRelayDuration(isSuccess = true)
176177
safeSendAndStop(o.originChannelId, cmd)
177178

178179
case WrappedAddResponse(RES_ADD_SETTLED(o: Origin.ChannelRelayedHot, _, fail: HtlcResult.Fail)) =>
179180
context.log.debug("relaying fail to upstream")
181+
Metrics.relaySettleFail(confidence)
180182
Metrics.recordPaymentRelayFailed(Tags.FailureType.Remote, Tags.RelayType.Channel)
181183
val cmd = translateRelayFailure(o.originHtlcId, fail)
182184
recordRelayDuration(isSuccess = false)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2023 ACINQ SAS
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package fr.acinq.eclair.reputation
18+
19+
import fr.acinq.eclair.{MilliSatoshiLong, TimestampMilli}
20+
import fr.acinq.eclair.reputation.Reputation.ReputationConfig
21+
import org.scalatest.funsuite.AnyFunSuite
22+
import org.scalactic.Tolerance.convertNumericToPlusOrMinusWrapper
23+
24+
import java.util.UUID
25+
import scala.concurrent.duration.DurationInt
26+
27+
class ReputationSpec extends AnyFunSuite {
28+
val (uuid1, uuid2, uuid3, uuid4, uuid5, uuid6, uuid7) = (UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())
29+
30+
test("basic") {
31+
var r = Reputation.init(ReputationConfig(1000000000 msat, 30 seconds))
32+
r = r.attempt(uuid1, 10000 msat)
33+
assert(r.confidence() == 0)
34+
r = r.record(uuid1, isSuccess = true)
35+
r = r.attempt(uuid2, 10000 msat)
36+
assert(r.confidence() == 0.5)
37+
r = r.attempt(uuid3, 10000 msat)
38+
assert(r.confidence() === 0.333 +- 0.001)
39+
r = r.record(uuid2, isSuccess = true)
40+
r = r.record(uuid3, isSuccess = true)
41+
r = r.attempt(uuid4, 1 msat)
42+
assert(r.confidence() === 1.0 +- 0.001)
43+
r = r.attempt(uuid5, 90000 msat)
44+
assert(r.confidence() === 0.25 +- 0.001)
45+
r = r.attempt(uuid6, 10000 msat)
46+
assert(r.confidence() === (3.0 / 13) +- 0.001)
47+
r = r.cancel(uuid5)
48+
assert(r.confidence() === 0.75 +- 0.001)
49+
r = r.record(uuid6, isSuccess = false)
50+
assert(r.confidence() === 0.75 +- 0.001)
51+
r = r.attempt(uuid7, 10000 msat)
52+
assert(r.confidence() === 0.6 +- 0.001)
53+
}
54+
55+
test("long HTLC") {
56+
var r = Reputation.init(ReputationConfig(1000000000 msat, 1 second))
57+
r = r.attempt(uuid1, 100000 msat)
58+
assert(r.confidence() == 0)
59+
r = r.record(uuid1, isSuccess = true)
60+
assert(r.confidence() == 1)
61+
r = r.attempt(uuid2, 1000 msat, TimestampMilli(0))
62+
assert(r.confidence(TimestampMilli(0)) === 0.99 +- 0.001)
63+
assert(r.confidence(TimestampMilli(0) + 100.seconds) == 0.5)
64+
r = r.record(uuid2, isSuccess = false, now = TimestampMilli(0) + 100.seconds)
65+
assert(r.confidence() == 0.5)
66+
}
67+
}

0 commit comments

Comments
 (0)