Skip to content

Commit 56e580c

Browse files
committed
WIP
1 parent 6e8d675 commit 56e580c

File tree

10 files changed

+115
-89
lines changed

10 files changed

+115
-89
lines changed

docs/release-notes/eclair-vnext.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ To configure, edit `eclair.conf`:
3737
```eclair.conf
3838
// We assign reputations to our peers to prioritize payments during congestion.
3939
// The reputation is computed as fees paid divided by what should have been paid if all payments were successful.
40-
eclair.peer-reputation {
40+
eclair.relay.peer-reputation {
4141
// Set this parameter to false to disable the reputation algorithm and simply relay the incoming endorsement
4242
// value, as described by https://github.com/lightning/blips/blob/master/blip-0004.md,
4343
enabled = true

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ class Setup(val datadir: File,
362362
paymentHandler = system.actorOf(SimpleSupervisor.props(PaymentHandler.props(nodeParams, register, offerManager), "payment-handler", SupervisorStrategy.Resume))
363363
triggerer = system.spawn(Behaviors.supervise(AsyncPaymentTriggerer()).onFailure(typed.SupervisorStrategy.resume), name = "async-payment-triggerer")
364364
reputationRecorder_opt = if (nodeParams.relayParams.peerReputationConfig.enabled) {
365-
Some(system.spawn(Behaviors.supervise(ReputationRecorder(nodeParams.relayParams.peerReputationConfig, Map.empty)).onFailure(typed.SupervisorStrategy.resume), name = "reputation-recorder"))
365+
Some(system.spawn(Behaviors.supervise(ReputationRecorder(nodeParams.relayParams.peerReputationConfig)).onFailure(typed.SupervisorStrategy.resume), name = "reputation-recorder"))
366366
} else {
367367
None
368368
}

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, FailureMessage, FundingCreated, FundingSigned, Init, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, TxSignatures, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulfillHtlc}
29+
import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelReady, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, FailureMessage, FundingCreated, FundingSigned, HtlcFailureMessage, Init, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, 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

@@ -228,6 +228,10 @@ final case class CMD_GET_CHANNEL_STATE(replyTo: ActorRef) extends HasReplyToComm
228228
final case class CMD_GET_CHANNEL_DATA(replyTo: ActorRef) extends HasReplyToCommand
229229
final case class CMD_GET_CHANNEL_INFO(replyTo: akka.actor.typed.ActorRef[RES_GET_CHANNEL_INFO]) extends Command
230230

231+
case class OutgoingHtlcAdded(add: UpdateAddHtlc, upstream: Upstream.Hot)
232+
case class OutgoingHtlcFailed(fail: HtlcFailureMessage)
233+
case class OutgoingHtlcFulfilled(fulfill: UpdateFulfillHtlc)
234+
231235
/*
232236
88888888b. 8888888888 .d8888b. 88888888b. ,ad8888ba, 888b 88 .d8888b. 8888888888 .d8888b.
233237
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
@@ -439,6 +439,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
439439
case Right((commitments1, add)) =>
440440
if (c.commit) self ! CMD_SIGN()
441441
context.system.eventStream.publish(AvailableBalanceChanged(self, d.channelId, d.shortIds, commitments1))
442+
context.system.eventStream.publish(OutgoingHtlcAdded(add, c.origin.upstream))
442443
handleCommandSuccess(c, d.copy(commitments = commitments1)) sending add
443444
case Left(cause) => handleAddHtlcCommandError(c, cause, Some(d.channelUpdate))
444445
}
@@ -465,6 +466,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
465466
case Right((commitments1, origin, htlc)) =>
466467
// we forward preimages as soon as possible to the upstream channel because it allows us to pull funds
467468
relayer ! RES_ADD_SETTLED(origin, htlc, HtlcResult.RemoteFulfill(fulfill))
469+
context.system.eventStream.publish(OutgoingHtlcFulfilled(fulfill))
468470
stay() using d.copy(commitments = commitments1)
469471
case Left(cause) => handleLocalError(cause, d, Some(fulfill))
470472
}
@@ -498,12 +500,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
498500
}
499501

500502
case Event(fail: UpdateFailHtlc, d: DATA_NORMAL) =>
503+
context.system.eventStream.publish(OutgoingHtlcFailed(fail))
501504
d.commitments.receiveFail(fail) match {
502505
case Right((commitments1, _, _)) => stay() using d.copy(commitments = commitments1)
503506
case Left(cause) => handleLocalError(cause, d, Some(fail))
504507
}
505508

506509
case Event(fail: UpdateFailMalformedHtlc, d: DATA_NORMAL) =>
510+
context.system.eventStream.publish(OutgoingHtlcFailed(fail))
507511
d.commitments.receiveFailMalformed(fail) match {
508512
case Right((commitments1, _, _)) => stay() using d.copy(commitments = commitments1)
509513
case Left(cause) => handleLocalError(cause, d, Some(fail))

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import fr.acinq.eclair.payment.Monitoring.{Metrics, Tags}
3030
import fr.acinq.eclair.payment.relay.Relayer.{OutgoingChannel, OutgoingChannelParams}
3131
import fr.acinq.eclair.payment.{ChannelPaymentRelayed, IncomingPaymentPacket}
3232
import fr.acinq.eclair.reputation.ReputationRecorder
33-
import fr.acinq.eclair.reputation.ReputationRecorder.{CancelRelay, GetConfidence, RecordResult}
33+
import fr.acinq.eclair.reputation.ReputationRecorder.GetConfidence
3434
import fr.acinq.eclair.wire.protocol.FailureMessageCodecs.createBadOnionFailure
3535
import fr.acinq.eclair.wire.protocol.PaymentOnion.IntermediatePayload
3636
import fr.acinq.eclair.wire.protocol._
@@ -59,7 +59,7 @@ object ChannelRelay {
5959

6060
def apply(nodeParams: NodeParams,
6161
register: ActorRef,
62-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.ChannelRelayCommand]],
62+
reputationRecorder_opt: Option[typed.ActorRef[GetConfidence]],
6363
channels: Map[ByteVector32, Relayer.OutgoingChannel],
6464
originNode: PublicKey,
6565
relayId: UUID,
@@ -73,7 +73,7 @@ object ChannelRelay {
7373
val upstream = Upstream.Hot.Channel(r.add.removeUnknownTlvs(), TimestampMilli.now(), originNode)
7474
reputationRecorder_opt match {
7575
case Some(reputationRecorder) =>
76-
reputationRecorder ! GetConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), originNode, r.add.endorsement, relayId, r.relayFeeMsat)
76+
reputationRecorder ! GetConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), upstream, r.relayFeeMsat)
7777
case None =>
7878
val confidence = (r.add.endorsement + 0.5) / 8
7979
context.self ! WrappedConfidence(confidence)
@@ -123,7 +123,7 @@ object ChannelRelay {
123123
*/
124124
class ChannelRelay private(nodeParams: NodeParams,
125125
register: ActorRef,
126-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.ChannelRelayCommand]],
126+
reputationRecorder_opt: Option[typed.ActorRef[GetConfidence]],
127127
channels: Map[ByteVector32, Relayer.OutgoingChannel],
128128
r: IncomingPaymentPacket.ChannelRelayPacket,
129129
upstream: Upstream.Hot.Channel,
@@ -149,7 +149,6 @@ class ChannelRelay private(nodeParams: NodeParams,
149149
case RelayFailure(cmdFail) =>
150150
Metrics.recordPaymentRelayFailed(Tags.FailureType(cmdFail), Tags.RelayType.Channel)
151151
context.log.info("rejecting htlc reason={}", cmdFail.reason)
152-
reputationRecorder_opt.foreach(_ ! CancelRelay(upstream.receivedFrom, r.add.endorsement, relayId))
153152
safeSendAndStop(r.add.channelId, cmdFail)
154153
case RelaySuccess(selectedChannelId, cmdAdd) =>
155154
context.log.info("forwarding htlc #{} from channelId={} to channelId={}", r.add.id, r.add.channelId, selectedChannelId)
@@ -165,7 +164,6 @@ class ChannelRelay private(nodeParams: NodeParams,
165164
context.log.warn(s"couldn't resolve downstream channel $channelId, failing htlc #${upstream.add.id}")
166165
val cmdFail = CMD_FAIL_HTLC(upstream.add.id, Right(UnknownNextPeer()), commit = true)
167166
Metrics.recordPaymentRelayFailed(Tags.FailureType(cmdFail), Tags.RelayType.Channel)
168-
reputationRecorder_opt.foreach(_ ! CancelRelay(upstream.receivedFrom, r.add.endorsement, relayId))
169167
safeSendAndStop(upstream.add.channelId, cmdFail)
170168

171169
case WrappedAddResponse(addFailed: RES_ADD_FAILED[_]) =>
@@ -178,7 +176,7 @@ class ChannelRelay private(nodeParams: NodeParams,
178176
waitForAddSettled(r.channelId)
179177
}
180178

181-
def waitForAddSettled(channelId: ByteVector32): Behavior[Command] =
179+
def waitForAddSettled(channelId: ByteVector32): Behavior[Command] = {
182180
Behaviors.receiveMessagePartial {
183181
case WrappedAddResponse(RES_ADD_SETTLED(_, htlc, fulfill: HtlcResult.Fulfill)) =>
184182
context.log.info("relaying fulfill to upstream, startedAt={}, endedAt={}, confidence={}, originNode={}, outgoingChannel={}", upstream.receivedAt, TimestampMilli.now(), confidence, upstream.receivedFrom, channelId)
@@ -196,6 +194,7 @@ class ChannelRelay private(nodeParams: NodeParams,
196194
recordRelayDuration(isSuccess = false)
197195
safeSendAndStop(upstream.add.channelId, cmd)
198196
}
197+
}
199198

200199
def safeSendAndStop(channelId: ByteVector32, cmd: channel.HtlcSettlementCommand): Behavior[Command] = {
201200
val toSend = cmd match {
@@ -343,7 +342,6 @@ class ChannelRelay private(nodeParams: NodeParams,
343342
}
344343

345344
private def recordRelayDuration(isSuccess: Boolean): Unit = {
346-
reputationRecorder_opt.foreach(_ ! RecordResult(upstream.receivedFrom, r.add.endorsement, relayId, isSuccess))
347345
Metrics.RelayedPaymentDuration
348346
.withTag(Tags.Relay, Tags.RelayType.Channel)
349347
.withTag(Tags.Success, isSuccess)

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
@@ -36,7 +36,7 @@ import fr.acinq.eclair.payment.send.MultiPartPaymentLifecycle.{PreimageReceived,
3636
import fr.acinq.eclair.payment.send.PaymentInitiator.SendPaymentConfig
3737
import fr.acinq.eclair.payment.send.PaymentLifecycle.SendPaymentToNode
3838
import fr.acinq.eclair.payment.send._
39-
import fr.acinq.eclair.reputation.ReputationRecorder
39+
import fr.acinq.eclair.reputation.ReputationRecorder.GetTrampolineConfidence
4040
import fr.acinq.eclair.router.Router.RouteParams
4141
import fr.acinq.eclair.router.{BalanceTooLow, RouteNotFound}
4242
import fr.acinq.eclair.wire.protocol.PaymentOnion.IntermediatePayload
@@ -87,7 +87,7 @@ object NodeRelay {
8787
def apply(nodeParams: NodeParams,
8888
parent: typed.ActorRef[NodeRelayer.Command],
8989
register: ActorRef,
90-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.TrampolineRelayCommand]],
90+
reputationRecorder_opt: Option[typed.ActorRef[GetTrampolineConfidence]],
9191
relayId: UUID,
9292
nodeRelayPacket: NodeRelayPacket,
9393
outgoingPaymentFactory: OutgoingPaymentFactory,
@@ -186,7 +186,7 @@ object NodeRelay {
186186
class NodeRelay private(nodeParams: NodeParams,
187187
parent: akka.actor.typed.ActorRef[NodeRelayer.Command],
188188
register: ActorRef,
189-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.TrampolineRelayCommand]],
189+
reputationRecorder_opt: Option[typed.ActorRef[GetTrampolineConfidence]],
190190
relayId: UUID,
191191
paymentHash: ByteVector32,
192192
paymentSecret: ByteVector32,
@@ -264,11 +264,8 @@ class NodeRelay private(nodeParams: NodeParams,
264264
private def doSend(upstream: Upstream.Hot.Trampoline, nextPayload: IntermediatePayload.NodeRelay, nextPacket_opt: Option[OnionRoutingPacket]): Behavior[Command] = {
265265
context.log.debug(s"relaying trampoline payment (amountIn=${upstream.amountIn} expiryIn=${upstream.expiryIn} amountOut=${nextPayload.amountToForward} expiryOut=${nextPayload.outgoingCltv})")
266266
val totalFee = upstream.amountIn - nextPayload.amountToForward
267-
val fees = upstream.received.foldLeft(Map.empty[ReputationRecorder.PeerEndorsement, MilliSatoshi])((fees, r) =>
268-
fees.updatedWith(ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement))(fee =>
269-
Some(fee.getOrElse(MilliSatoshi(0)) + r.add.amountMsat * totalFee.toLong / upstream.amountIn.toLong)))
270267
reputationRecorder_opt match {
271-
case Some(reputationRecorder) => reputationRecorder ! ReputationRecorder.GetTrampolineConfidence(context.messageAdapter[ReputationRecorder.Confidence](confidence => WrappedConfidence(confidence.value)), fees, relayId)
268+
case Some(reputationRecorder) => reputationRecorder ! GetTrampolineConfidence(context.messageAdapter(confidence => WrappedConfidence(confidence.value)), upstream, totalFee)
272269
case None => context.self ! WrappedConfidence((upstream.received.map(_.add.endorsement).min + 0.5) / 8)
273270
}
274271
Behaviors.receiveMessagePartial {
@@ -303,19 +300,13 @@ class NodeRelay private(nodeParams: NodeParams,
303300
case WrappedPaymentSent(paymentSent) =>
304301
context.log.debug("trampoline payment fully resolved downstream")
305302
success(upstream, fulfilledUpstream, paymentSent)
306-
val totalFee = upstream.amountIn - paymentSent.amountWithFees
307-
val fees = upstream.received.foldLeft(Map.empty[ReputationRecorder.PeerEndorsement, MilliSatoshi])((fees, r) =>
308-
fees.updatedWith(ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement))(fee =>
309-
Some(fee.getOrElse(MilliSatoshi(0)) + r.add.amountMsat * totalFee.toLong / upstream.amountIn.toLong)))
310-
reputationRecorder_opt.foreach(_ ! ReputationRecorder.RecordTrampolineSuccess(fees, relayId))
311303
recordRelayDuration(startedAt, isSuccess = true)
312304
stopping()
313305
case WrappedPaymentFailed(PaymentFailed(_, _, failures, _)) =>
314306
context.log.debug(s"trampoline payment failed downstream")
315307
if (!fulfilledUpstream) {
316308
rejectPayment(upstream, translateError(nodeParams, failures, upstream, nextPayload))
317309
}
318-
reputationRecorder_opt.foreach(_ ! ReputationRecorder.RecordTrampolineFailure(upstream.received.map(r => ReputationRecorder.PeerEndorsement(r.receivedFrom, r.add.endorsement)).toSet, relayId))
319310
recordRelayDuration(startedAt, isSuccess = fulfilledUpstream)
320311
stopping()
321312
}

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
@@ -61,7 +61,7 @@ object NodeRelayer {
6161
*/
6262
def apply(nodeParams: NodeParams,
6363
register: akka.actor.ActorRef,
64-
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.TrampolineRelayCommand]],
64+
reputationRecorder_opt: Option[typed.ActorRef[ReputationRecorder.GetTrampolineConfidence]],
6565
outgoingPaymentFactory: NodeRelay.OutgoingPaymentFactory,
6666
triggerer: typed.ActorRef[AsyncPaymentTriggerer.Command],
6767
router: akka.actor.ActorRef,

0 commit comments

Comments
 (0)