Skip to content

Commit 3d3437c

Browse files
committed
Reputation is recorded from channel events
1 parent f7b2ad4 commit 3d3437c

File tree

13 files changed

+136
-111
lines changed

13 files changed

+136
-111
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ class Setup(val datadir: File,
381381
triggerer = system.spawn(Behaviors.supervise(AsyncPaymentTriggerer()).onFailure(typed.SupervisorStrategy.resume), name = "async-payment-triggerer")
382382
peerReadyManager = system.spawn(Behaviors.supervise(PeerReadyManager()).onFailure(typed.SupervisorStrategy.restart), name = "peer-ready-manager")
383383
reputationRecorder_opt = if (nodeParams.relayParams.peerReputationConfig.enabled) {
384-
Some(system.spawn(Behaviors.supervise(ReputationRecorder(nodeParams.relayParams.peerReputationConfig, Map.empty)).onFailure(typed.SupervisorStrategy.resume), name = "reputation-recorder"))
384+
Some(system.spawn(Behaviors.supervise(ReputationRecorder(nodeParams.relayParams.peerReputationConfig)).onFailure(typed.SupervisorStrategy.resume), name = "reputation-recorder"))
385385
} else {
386386
None
387387
}

eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import fr.acinq.eclair.channel.fund.{InteractiveTxBuilder, InteractiveTxSigningS
2626
import fr.acinq.eclair.io.Peer
2727
import fr.acinq.eclair.transactions.CommitmentSpec
2828
import fr.acinq.eclair.transactions.Transactions._
29-
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelReady, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, FailureReason, FundingCreated, FundingSigned, Init, LiquidityAds, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, TxInitRbf, TxSignatures, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulfillHtlc}
29+
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelReady, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, FailureReason, FundingCreated, FundingSigned, HtlcFailureMessage, Init, LiquidityAds, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, TxInitRbf, TxSignatures, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulfillHtlc}
3030
import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Features, InitFeature, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, TimestampMilli, UInt64}
3131
import scodec.bits.ByteVector
3232

@@ -247,6 +247,10 @@ final case class CMD_GET_CHANNEL_STATE(replyTo: ActorRef) extends HasReplyToComm
247247
final case class CMD_GET_CHANNEL_DATA(replyTo: ActorRef) extends HasReplyToCommand
248248
final case class CMD_GET_CHANNEL_INFO(replyTo: akka.actor.typed.ActorRef[RES_GET_CHANNEL_INFO]) extends Command
249249

250+
case class OutgoingHtlcAdded(add: UpdateAddHtlc, upstream: Upstream.Hot, fee: MilliSatoshi)
251+
case class OutgoingHtlcFailed(fail: HtlcFailureMessage)
252+
case class OutgoingHtlcFulfilled(fulfill: UpdateFulfillHtlc)
253+
250254
/*
251255
88888888b. 8888888888 .d8888b. 88888888b. ,ad8888ba, 888b 88 .d8888b. 8888888888 .d8888b.
252256
88 "8b 88 d88P Y88b 88 "8b d8"' `"8b 8888b 88 d88P Y88b 88 d88P Y88b

eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
465465
case Right((commitments1, add)) =>
466466
if (c.commit) self ! CMD_SIGN()
467467
context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.aliases, commitments1, d.lastAnnouncement_opt))
468+
context.system.eventStream.publish(OutgoingHtlcAdded(add, c.origin.upstream, nodeFee(d.channelUpdate.relayFees, add.amountMsat)))
468469
handleCommandSuccess(c, d.copy(commitments = commitments1)) sending add
469470
case Left(cause) => handleAddHtlcCommandError(c, cause, Some(d.channelUpdate))
470471
}
@@ -491,6 +492,7 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
491492
case Right((commitments1, origin, htlc)) =>
492493
// we forward preimages as soon as possible to the upstream channel because it allows us to pull funds
493494
relayer ! RES_ADD_SETTLED(origin, htlc, HtlcResult.RemoteFulfill(fulfill))
495+
context.system.eventStream.publish(OutgoingHtlcFulfilled(fulfill))
494496
stay() using d.copy(commitments = commitments1)
495497
case Left(cause) => handleLocalError(cause, d, Some(fulfill))
496498
}
@@ -524,12 +526,14 @@ class Channel(val nodeParams: NodeParams, val channelKeys: ChannelKeys, val wall
524526
}
525527

526528
case Event(fail: UpdateFailHtlc, d: DATA_NORMAL) =>
529+
context.system.eventStream.publish(OutgoingHtlcFailed(fail))
527530
d.commitments.receiveFail(fail) match {
528531
case Right((commitments1, _, _)) => stay() using d.copy(commitments = commitments1)
529532
case Left(cause) => handleLocalError(cause, d, Some(fail))
530533
}
531534

532535
case Event(fail: UpdateFailMalformedHtlc, d: DATA_NORMAL) =>
536+
context.system.eventStream.publish(OutgoingHtlcFailed(fail))
533537
d.commitments.receiveFailMalformed(fail) match {
534538
case Right((commitments1, _, _)) => stay() using d.copy(commitments = commitments1)
535539
case Left(cause) => handleLocalError(cause, d, Some(fail))

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import fr.acinq.eclair.payment.Monitoring.{Metrics, Tags}
3333
import fr.acinq.eclair.payment.relay.Relayer.{OutgoingChannel, OutgoingChannelParams}
3434
import fr.acinq.eclair.payment.{ChannelPaymentRelayed, IncomingPaymentPacket}
3535
import fr.acinq.eclair.reputation.ReputationRecorder
36-
import fr.acinq.eclair.reputation.ReputationRecorder.{CancelRelay, GetConfidence, RecordResult}
36+
import fr.acinq.eclair.reputation.ReputationRecorder.GetConfidence
3737
import fr.acinq.eclair.wire.protocol.FailureMessageCodecs.createBadOnionFailure
3838
import fr.acinq.eclair.wire.protocol.PaymentOnion.IntermediatePayload
3939
import fr.acinq.eclair.wire.protocol._
@@ -65,7 +65,7 @@ object ChannelRelay {
6565

6666
def apply(nodeParams: NodeParams,
6767
register: ActorRef,
68-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.ChannelRelayCommand]],
68+
reputationRecorder_opt: Option[typed.ActorRef[GetConfidence]],
6969
channels: Map[ByteVector32, Relayer.OutgoingChannel],
7070
originNode: PublicKey,
7171
relayId: UUID,
@@ -79,14 +79,14 @@ object ChannelRelay {
7979
val upstream = Upstream.Hot.Channel(r.add.removeUnknownTlvs(), r.receivedAt, originNode)
8080
reputationRecorder_opt match {
8181
case Some(reputationRecorder) =>
82-
reputationRecorder ! GetConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), originNode, r.add.endorsement, relayId, r.relayFeeMsat)
82+
reputationRecorder ! GetConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), upstream, r.relayFeeMsat)
8383
case None =>
8484
val confidence = (r.add.endorsement + 0.5) / 8
8585
context.self ! WrappedConfidence(confidence)
8686
}
8787
Behaviors.receiveMessagePartial {
8888
case WrappedConfidence(confidence) =>
89-
new ChannelRelay(nodeParams, register, reputationRecorder_opt, channels, r, upstream, confidence, context, relayId).start()
89+
new ChannelRelay(nodeParams, register, channels, r, upstream, confidence, context).start()
9090
}
9191
}
9292
}
@@ -128,13 +128,11 @@ object ChannelRelay {
128128
*/
129129
class ChannelRelay private(nodeParams: NodeParams,
130130
register: ActorRef,
131-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.ChannelRelayCommand]],
132131
channels: Map[ByteVector32, Relayer.OutgoingChannel],
133132
r: IncomingPaymentPacket.ChannelRelayPacket,
134133
upstream: Upstream.Hot.Channel,
135134
confidence: Double,
136-
context: ActorContext[ChannelRelay.Command],
137-
relayId: UUID) {
135+
context: ActorContext[ChannelRelay.Command]) {
138136

139137
import ChannelRelay._
140138

@@ -201,7 +199,6 @@ class ChannelRelay private(nodeParams: NodeParams,
201199
case RelayFailure(cmdFail) =>
202200
Metrics.recordPaymentRelayFailed(Tags.FailureType(cmdFail), Tags.RelayType.Channel)
203201
context.log.info("rejecting htlc reason={}", cmdFail.reason)
204-
reputationRecorder_opt.foreach(_ ! CancelRelay(upstream.receivedFrom, r.add.endorsement, relayId))
205202
safeSendAndStop(r.add.channelId, cmdFail)
206203
case RelayNeedsFunding(nextNodeId, cmdFail) =>
207204
// Note that in the channel relay case, we don't have any outgoing onion shared secrets.
@@ -222,7 +219,6 @@ class ChannelRelay private(nodeParams: NodeParams,
222219
context.log.warn(s"couldn't resolve downstream channel $channelId, failing htlc #${upstream.add.id}")
223220
val cmdFail = makeCmdFailHtlc(upstream.add.id, UnknownNextPeer())
224221
Metrics.recordPaymentRelayFailed(Tags.FailureType(cmdFail), Tags.RelayType.Channel)
225-
reputationRecorder_opt.foreach(_ ! CancelRelay(upstream.receivedFrom, r.add.endorsement, relayId))
226222
safeSendAndStop(upstream.add.channelId, cmdFail)
227223

228224
case WrappedAddResponse(addFailed: RES_ADD_FAILED[_]) =>
@@ -450,11 +446,9 @@ class ChannelRelay private(nodeParams: NodeParams,
450446
private def makeCmdFailHtlc(originHtlcId: Long, failure: FailureMessage, delay_opt: Option[FiniteDuration] = None): CMD_FAIL_HTLC =
451447
CMD_FAIL_HTLC(originHtlcId, FailureReason.LocalFailure(failure), Some(upstream.receivedAt), delay_opt, commit = true)
452448

453-
private def recordRelayDuration(isSuccess: Boolean): Unit = {
454-
reputationRecorder_opt.foreach(_ ! RecordResult(upstream.receivedFrom, r.add.endorsement, relayId, isSuccess))
449+
private def recordRelayDuration(isSuccess: Boolean): Unit =
455450
Metrics.RelayedPaymentDuration
456451
.withTag(Tags.Relay, Tags.RelayType.Channel)
457452
.withTag(Tags.Success, isSuccess)
458453
.record((TimestampMilli.now() - upstream.receivedAt).toMillis, TimeUnit.MILLISECONDS)
459-
}
460454
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ object ChannelRelayer {
5959

6060
def apply(nodeParams: NodeParams,
6161
register: ActorRef,
62-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.ChannelRelayCommand]],
62+
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.GetConfidence]],
6363
channels: Map[ByteVector32, Relayer.OutgoingChannel] = Map.empty,
6464
scid2channels: Map[ShortChannelId, ByteVector32] = Map.empty,
6565
node2channels: mutable.MultiDict[PublicKey, ByteVector32] = mutable.MultiDict.empty): Behavior[Command] =

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

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import fr.acinq.eclair.payment.send.PaymentLifecycle.SendPaymentToNode
3939
import fr.acinq.eclair.payment.send._
4040
import fr.acinq.eclair.router.Router.{ChannelHop, HopRelayParams, Route, RouteParams}
4141
import fr.acinq.eclair.reputation.ReputationRecorder
42+
import fr.acinq.eclair.reputation.ReputationRecorder.GetTrampolineConfidence
4243
import fr.acinq.eclair.router.Router.RouteParams
4344
import fr.acinq.eclair.router.{BalanceTooLow, RouteNotFound}
4445
import fr.acinq.eclair.wire.protocol.PaymentOnion.IntermediatePayload
@@ -91,7 +92,7 @@ object NodeRelay {
9192
def apply(nodeParams: NodeParams,
9293
parent: typed.ActorRef[NodeRelayer.Command],
9394
register: ActorRef,
94-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.TrampolineRelayCommand]],
95+
reputationRecorder_opt: Option[typed.ActorRef[GetTrampolineConfidence]],
9596
relayId: UUID,
9697
nodeRelayPacket: NodeRelayPacket,
9798
outgoingPaymentFactory: OutgoingPaymentFactory,
@@ -204,7 +205,7 @@ object NodeRelay {
204205
class NodeRelay private(nodeParams: NodeParams,
205206
parent: akka.actor.typed.ActorRef[NodeRelayer.Command],
206207
register: ActorRef,
207-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.TrampolineRelayCommand]],
208+
reputationRecorder_opt: Option[typed.ActorRef[GetTrampolineConfidence]],
208209
relayId: UUID,
209210
paymentHash: ByteVector32,
210211
paymentSecret: ByteVector32,
@@ -343,11 +344,8 @@ class NodeRelay private(nodeParams: NodeParams,
343344
// We only make one try when it's a direct payment to a wallet.
344345
val maxPaymentAttempts = if (walletNodeId_opt.isDefined) 1 else nodeParams.maxPaymentAttempts
345346
val totalFee = upstream.amountIn - payloadOut.outgoingAmount(upstream.amountIn)
346-
val fees = upstream.received.foldLeft(Map.empty[ReputationRecorder.PeerEndorsement, MilliSatoshi])((fees, r) =>
347-
fees.updatedWith(ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement))(fee =>
348-
Some(fee.getOrElse(MilliSatoshi(0)) + r.add.amountMsat * totalFee.toLong / upstream.amountIn.toLong)))
349347
reputationRecorder_opt match {
350-
case Some(reputationRecorder) => reputationRecorder ! ReputationRecorder.GetTrampolineConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), fees, relayId)
348+
case Some(reputationRecorder) => reputationRecorder ! GetTrampolineConfidence(context.messageAdapter(confidence => WrappedConfidence(confidence.value)), upstream, totalFee)
351349
case None => context.self ! WrappedConfidence((upstream.received.map(_.add.endorsement).min + 0.5) / 8)
352350
}
353351
Behaviors.receiveMessagePartial {
@@ -404,16 +402,10 @@ class NodeRelay private(nodeParams: NodeParams,
404402
case WrappedPaymentSent(paymentSent) =>
405403
context.log.debug("trampoline payment fully resolved downstream")
406404
success(upstream, fulfilledUpstream, paymentSent)
407-
val totalFee = upstream.amountIn - paymentSent.amountWithFees
408-
val fees = upstream.received.foldLeft(Map.empty[ReputationRecorder.PeerEndorsement, MilliSatoshi])((fees, r) =>
409-
fees.updatedWith(ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement))(fee =>
410-
Some(fee.getOrElse(MilliSatoshi(0)) + r.add.amountMsat * totalFee.toLong / upstream.amountIn.toLong)))
411-
reputationRecorder_opt.foreach(_ ! ReputationRecorder.RecordTrampolineSuccess(fees, relayId))
412405
recordRelayDuration(startedAt, isSuccess = true)
413406
stopping()
414407
case _: WrappedPaymentFailed if fulfilledUpstream =>
415408
context.log.warn("trampoline payment failed downstream but was fulfilled upstream")
416-
reputationRecorder_opt.foreach(_ ! ReputationRecorder.RecordTrampolineFailure(upstream.received.map(r => ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement)).toSet, relayId))
417409
recordRelayDuration(startedAt, isSuccess = true)
418410
stopping()
419411
case WrappedPaymentFailed(PaymentFailed(_, _, failures, _)) =>
@@ -423,7 +415,6 @@ class NodeRelay private(nodeParams: NodeParams,
423415
attemptOnTheFlyFunding(upstream, walletNodeId, recipient, nextPayload, failures, startedAt)
424416
case _ =>
425417
rejectPayment(upstream, translateError(nodeParams, failures, upstream, nextPayload))
426-
reputationRecorder_opt.foreach(_ ! ReputationRecorder.RecordTrampolineFailure(upstream.received.map(r => ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement)).toSet, relayId))
427418
recordRelayDuration(startedAt, isSuccess = false)
428419
stopping()
429420
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ object NodeRelayer {
6060
*/
6161
def apply(nodeParams: NodeParams,
6262
register: akka.actor.ActorRef,
63-
reputationRecorder_opt: Option[ActorRef[ReputationRecorder.TrampolineRelayCommand]],
63+
reputationRecorder_opt: Option[ActorRef[ReputationRecorder.GetTrampolineConfidence]],
6464
outgoingPaymentFactory: NodeRelay.OutgoingPaymentFactory,
6565
router: akka.actor.ActorRef,
6666
children: Map[PaymentKey, ActorRef[NodeRelay.Command]] = Map.empty): Behavior[Command] =

0 commit comments

Comments
 (0)