Skip to content

Commit efa96cb

Browse files
committed
WIP
1 parent 3d3437c commit efa96cb

File tree

3 files changed

+116
-75
lines changed

3 files changed

+116
-75
lines changed

eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/ChannelRelayerSpec.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,7 @@ class ChannelRelayerSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("a
689689
val fwd = expectFwdAdd(register, channelIds(realScid1), outgoingAmount, outgoingExpiry, 7)
690690
fwd.message.replyTo ! RES_SUCCESS(fwd.message, channelId1)
691691
fwd.message.origin.replyTo ! RES_ADD_SETTLED(fwd.message.origin, downstream_htlc, testCase.result)
692-
val fwdFail = register.expectMessageType[Register.Forward[channel.Command]]
693-
assert(fwdFail.message == testCase.cmd)
694-
assert(fwdFail.channelId == r.add.channelId)
692+
expectFwdFail(register, r.add.channelId, testCase.cmd, reputationRecorder)
695693
}
696694
}
697695

eclair-core/src/test/scala/fr/acinq/eclair/reputation/ReputationRecorderSpec.scala

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,19 @@ package fr.acinq.eclair.reputation
1818

1919
import akka.actor.testkit.typed.scaladsl.{ScalaTestWithActorTestKit, TestProbe}
2020
import akka.actor.typed.ActorRef
21+
import akka.actor.typed.eventstream.EventStream
2122
import com.typesafe.config.ConfigFactory
2223
import fr.acinq.bitcoin.scalacompat.Crypto.PublicKey
23-
import fr.acinq.eclair.channel.Upstream
24+
import fr.acinq.eclair.channel.{OutgoingHtlcAdded, OutgoingHtlcFailed, OutgoingHtlcFulfilled, Upstream}
2425
import fr.acinq.eclair.reputation.ReputationRecorder._
25-
import fr.acinq.eclair.wire.protocol.UpdateAddHtlc
26-
import fr.acinq.eclair.{MilliSatoshiLong, randomKey}
26+
import fr.acinq.eclair.wire.protocol.{TlvStream, UpdateAddHtlc, UpdateAddHtlcTlv, UpdateFailHtlc, UpdateFulfillHtlc}
27+
import fr.acinq.eclair.{CltvExpiry, MilliSatoshi, MilliSatoshiLong, TimestampMilli, randomBytes, randomBytes32, randomKey, randomLong}
2728
import org.scalatest.Outcome
2829
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
2930

30-
import java.util.UUID
3131
import scala.concurrent.duration.DurationInt
3232

3333
class ReputationRecorderSpec extends ScalaTestWithActorTestKit(ConfigFactory.load("application")) with FixtureAnyFunSuiteLike {
34-
val (uuid1, uuid2, uuid3, uuid4, uuid5, uuid6, uuid7, uuid8) = (UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())
3534
val originNode: PublicKey = randomKey().publicKey
3635

3736
case class FixtureParam(config: Reputation.Config, reputationRecorder: ActorRef[Command], replyTo: TestProbe[Confidence])
@@ -43,56 +42,107 @@ class ReputationRecorderSpec extends ScalaTestWithActorTestKit(ConfigFactory.loa
4342
withFixture(test.toNoArgTest(FixtureParam(config, reputationRecorder.ref, replyTo)))
4443
}
4544

45+
def makeChannelUpstream(nodeId: PublicKey, endorsement: Int, amount: MilliSatoshi = 1000000 msat): Upstream.Hot.Channel =
46+
Upstream.Hot.Channel(UpdateAddHtlc(randomBytes32(), randomLong(), amount, randomBytes32(), CltvExpiry(1234), null, TlvStream(UpdateAddHtlcTlv.Endorsement(endorsement))), TimestampMilli.now(), nodeId)
47+
48+
def makeOutgoingHtlcAdded(upstream: Upstream.Hot, fee: MilliSatoshi): OutgoingHtlcAdded =
49+
OutgoingHtlcAdded(UpdateAddHtlc(randomBytes32(), randomLong(), 100000 msat, randomBytes32(), CltvExpiry(456), null, TlvStream.empty), upstream, fee)
50+
51+
def makeOutgoingHtlcFulfilled(add: UpdateAddHtlc): OutgoingHtlcFulfilled =
52+
OutgoingHtlcFulfilled(UpdateFulfillHtlc(add.channelId, add.id, randomBytes32(), TlvStream.empty))
53+
54+
def makeOutgoingHtlcFailed(add: UpdateAddHtlc): OutgoingHtlcFailed =
55+
OutgoingHtlcFailed(UpdateFailHtlc(add.channelId, add.id, randomBytes(100), TlvStream.empty))
56+
4657
test("channel relay") { f =>
4758
import f._
4859

49-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 7, uuid1, 2000 msat)
60+
val listener = TestProbe[Any]()
61+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
62+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
63+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
64+
65+
val upstream1 = makeChannelUpstream(originNode, 7)
66+
reputationRecorder ! GetConfidence(replyTo.ref, upstream1, 2000 msat)
5067
assert(replyTo.expectMessageType[Confidence].value == 0)
51-
reputationRecorder ! RecordResult(originNode, 7, uuid1, isSuccess = true)
52-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 7, uuid2, 1000 msat)
68+
val added1 = makeOutgoingHtlcAdded(upstream1, 2000 msat)
69+
testKit.system.eventStream ! EventStream.Publish(added1)
70+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFulfilled(added1.add))
71+
listener.expectMessageType[OutgoingHtlcAdded]
72+
listener.expectMessageType[OutgoingHtlcFulfilled]
73+
val upstream2 = makeChannelUpstream(originNode, 7)
74+
reputationRecorder ! GetConfidence(replyTo.ref, upstream2, 1000 msat)
5375
assert(replyTo.expectMessageType[Confidence].value === (2.0 / 4) +- 0.001)
54-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 7, uuid3, 3000 msat)
76+
val added2 = makeOutgoingHtlcAdded(upstream2, 1000 msat)
77+
testKit.system.eventStream ! EventStream.Publish(added2)
78+
listener.expectMessageType[OutgoingHtlcAdded]
79+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(originNode, 7), 3000 msat)
5580
assert(replyTo.expectMessageType[Confidence].value === (2.0 / 10) +- 0.001)
56-
reputationRecorder ! CancelRelay(originNode, 7, uuid3)
57-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 7, uuid4, 1000 msat)
81+
val upstream3 = makeChannelUpstream(originNode, 7)
82+
reputationRecorder ! GetConfidence(replyTo.ref, upstream3, 1000 msat)
5883
assert(replyTo.expectMessageType[Confidence].value === (2.0 / 6) +- 0.001)
59-
reputationRecorder ! RecordResult(originNode, 7, uuid4, isSuccess = true)
60-
reputationRecorder ! RecordResult(originNode, 7, uuid2, isSuccess = false)
84+
val added3 = makeOutgoingHtlcAdded(upstream3, 1000 msat)
85+
testKit.system.eventStream ! EventStream.Publish(added3)
86+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFulfilled(added3.add))
87+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFailed(added2.add))
88+
listener.expectMessageType[OutgoingHtlcAdded]
89+
listener.expectMessageType[OutgoingHtlcFulfilled]
90+
listener.expectMessageType[OutgoingHtlcFailed]
6191
// Not endorsed
62-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 0, uuid5, 1000 msat)
92+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(originNode, 0), 1000 msat)
6393
assert(replyTo.expectMessageType[Confidence].value == 0)
6494
// Different origin node
65-
reputationRecorder ! GetConfidence(replyTo.ref, randomKey().publicKey, 7, uuid6, 1000 msat)
95+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(randomKey().publicKey, 7), 1000 msat)
6696
assert(replyTo.expectMessageType[Confidence].value == 0)
6797
// Very large HTLC
68-
reputationRecorder ! GetConfidence(replyTo.ref, originNode, 7, uuid5, 100000000 msat)
98+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(originNode, 7), 100000000 msat)
6999
assert(replyTo.expectMessageType[Confidence].value === 0.0 +- 0.001)
70100
}
71101

72102
test("trampoline relay") { f =>
73103
import f._
74104

105+
val listener = TestProbe[Any]()
106+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
107+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
108+
testKit.system.eventStream ! EventStream.Subscribe(listener.ref)
109+
75110
val (a, b, c) = (randomKey().publicKey, randomKey().publicKey, randomKey().publicKey)
76111

77-
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, Map(PeerEndorsement(a, 7) -> 2000.msat, PeerEndorsement(b, 7) -> 4000.msat, PeerEndorsement(c, 0) -> 6000.msat), uuid1)
112+
val upstream1 = Upstream.Hot.Trampoline(makeChannelUpstream(a, 7, 20000 msat) :: makeChannelUpstream(b, 7, 40000 msat) :: makeChannelUpstream(c, 0, 60000 msat) :: Nil)
113+
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, upstream1, 12000 msat)
78114
assert(replyTo.expectMessageType[Confidence].value == 0)
79-
reputationRecorder ! RecordTrampolineSuccess(Map(PeerEndorsement(a, 7) -> 1000.msat, PeerEndorsement(b, 7) -> 2000.msat, PeerEndorsement(c, 0) -> 3000.msat), uuid1)
80-
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, Map(PeerEndorsement(a, 7) -> 1000.msat, PeerEndorsement(c, 0) -> 1000.msat), uuid2)
115+
val added1 = makeOutgoingHtlcAdded(upstream1, 6000 msat)
116+
testKit.system.eventStream ! EventStream.Publish(added1)
117+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFulfilled(added1.add))
118+
listener.expectMessageType[OutgoingHtlcAdded]
119+
listener.expectMessageType[OutgoingHtlcFulfilled]
120+
val upstream2 = Upstream.Hot.Trampoline(makeChannelUpstream(a, 7, 10000 msat) :: makeChannelUpstream(c, 0, 10000 msat) :: Nil)
121+
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, upstream2, 2000 msat)
81122
assert(replyTo.expectMessageType[Confidence].value === (1.0 / 3) +- 0.001)
82-
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, Map(PeerEndorsement(a, 0) -> 1000.msat, PeerEndorsement(b, 7) -> 2000.msat), uuid3)
123+
val added2 = makeOutgoingHtlcAdded(upstream2, 2000 msat)
124+
testKit.system.eventStream ! EventStream.Publish(added2)
125+
listener.expectMessageType[OutgoingHtlcAdded]
126+
val upstream3 = Upstream.Hot.Trampoline(makeChannelUpstream(a, 0, 10000 msat) :: makeChannelUpstream(b, 7, 20000 msat) :: Nil)
127+
reputationRecorder ! GetTrampolineConfidence(replyTo.ref, upstream3, 3000 msat)
83128
assert(replyTo.expectMessageType[Confidence].value == 0)
84-
reputationRecorder ! RecordTrampolineFailure(Set(PeerEndorsement(a, 7), PeerEndorsement(c, 0)), uuid2)
85-
reputationRecorder ! RecordTrampolineSuccess(Map(PeerEndorsement(a, 0) -> 1000.msat, PeerEndorsement(b, 7) -> 2000.msat), uuid3)
129+
val added3 = makeOutgoingHtlcAdded(upstream3, 3000 msat)
130+
testKit.system.eventStream ! EventStream.Publish(added3)
131+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFailed(added2.add))
132+
testKit.system.eventStream ! EventStream.Publish(makeOutgoingHtlcFulfilled(added3.add))
133+
listener.expectMessageType[OutgoingHtlcAdded]
134+
listener.expectMessageType[OutgoingHtlcFailed]
135+
listener.expectMessageType[OutgoingHtlcFulfilled]
86136

87-
reputationRecorder ! GetConfidence(replyTo.ref, a, 7, uuid4, 1000 msat)
88-
assert(replyTo.expectMessageType[Confidence].value === (1.0 / 4) +- 0.001)
89-
reputationRecorder ! GetConfidence(replyTo.ref, a, 0, uuid5, 1000 msat)
137+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(a, 7), 1000 msat)
138+
assert(replyTo.expectMessageType[Confidence].value === (1.0 / 3) +- 0.001)
139+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(a, 0), 1000 msat)
90140
assert(replyTo.expectMessageType[Confidence].value === (1.0 / 3) +- 0.001)
91-
reputationRecorder ! GetConfidence(replyTo.ref, b, 7, uuid6, 1000 msat)
141+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(b, 7), 1000 msat)
92142
assert(replyTo.expectMessageType[Confidence].value === (4.0 / 6) +- 0.001)
93-
reputationRecorder ! GetConfidence(replyTo.ref, b, 0, uuid7, 1000 msat)
143+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(b, 0), 1000 msat)
94144
assert(replyTo.expectMessageType[Confidence].value == 0.0)
95-
reputationRecorder ! GetConfidence(replyTo.ref, c, 0, uuid8, 1000 msat)
96-
assert(replyTo.expectMessageType[Confidence].value === (3.0 / 6) +- 0.001)
145+
reputationRecorder ! GetConfidence(replyTo.ref, makeChannelUpstream(c, 0), 1000 msat)
146+
assert(replyTo.expectMessageType[Confidence].value === (3.0 / 5) +- 0.001)
97147
}
98148
}

eclair-core/src/test/scala/fr/acinq/eclair/reputation/ReputationSpec.scala

Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,65 +17,58 @@
1717
package fr.acinq.eclair.reputation
1818

1919
import fr.acinq.eclair.reputation.Reputation._
20-
import fr.acinq.eclair.{MilliSatoshiLong, TimestampMilli}
20+
import fr.acinq.eclair.{MilliSatoshiLong, TimestampMilli, randomBytes32}
2121
import org.scalactic.Tolerance.convertNumericToPlusOrMinusWrapper
2222
import org.scalatest.funsuite.AnyFunSuite
2323

24-
import java.util.UUID
2524
import scala.concurrent.duration.DurationInt
2625

2726
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())
27+
val (htlcId1, htlcId2, htlcId3, htlcId4, htlcId5, htlcId6, htlcId7) = (HtlcId(randomBytes32(), 1), HtlcId(randomBytes32(), 2), HtlcId(randomBytes32(), 3), HtlcId(randomBytes32(), 4), HtlcId(randomBytes32(), 5), HtlcId(randomBytes32(), 6), HtlcId(randomBytes32(), 7))
2928

3029
test("basic") {
3130
val r0 = Reputation.init(Config(enabled = true, 1 day, 1 second, 2))
32-
val (r1, c1) = r0.attempt(uuid1, 10000 msat)
33-
assert(c1 == 0)
34-
val r2 = r1.record(uuid1, isSuccess = true)
35-
val (r3, c3) = r2.attempt(uuid2, 10000 msat)
36-
assert(c3 === (1.0 / 3) +- 0.001)
37-
val (r4, c4) = r3.attempt(uuid3, 10000 msat)
38-
assert(c4 === (1.0 / 5) +- 0.001)
39-
val r5 = r4.record(uuid2, isSuccess = true)
40-
val r6 = r5.record(uuid3, isSuccess = true)
41-
val (r7, c7) = r6.attempt(uuid4, 1 msat)
42-
assert(c7 === 1.0 +- 0.001)
43-
val (r8, c8) = r7.attempt(uuid5, 40000 msat)
44-
assert(c8 === (3.0 / 11) +- 0.001)
45-
val (r9, c9) = r8.attempt(uuid6, 10000 msat)
46-
assert(c9 === (3.0 / 13) +- 0.001)
47-
val r10 = r9.cancel(uuid5)
48-
val r11 = r10.record(uuid6, isSuccess = false)
49-
val (_, c12) = r11.attempt(uuid7, 10000 msat)
50-
assert(c12 === (3.0 / 6) +- 0.001)
31+
assert(r0.getConfidence(10000 msat) == 0)
32+
val r1 = r0.attempt(htlcId1, 10000 msat)
33+
val r2 = r1.record(htlcId1, isSuccess = true)
34+
val r3 = r2.attempt(htlcId2, 10000 msat)
35+
assert(r2.getConfidence(10000 msat) === (1.0 / 3) +- 0.001)
36+
val r4 = r3.attempt(htlcId3, 10000 msat)
37+
assert(r3.getConfidence(10000 msat) === (1.0 / 5) +- 0.001)
38+
val r5 = r4.record(htlcId2, isSuccess = true)
39+
val r6 = r5.record(htlcId3, isSuccess = true)
40+
val r7 = r6.attempt(htlcId4, 1 msat)
41+
assert(r6.getConfidence(1 msat) === 1.0 +- 0.001)
42+
val r8 = r7.attempt(htlcId5, 40000 msat)
43+
assert(r7.getConfidence(40000 msat) === (3.0 / 11) +- 0.001)
44+
val r9 = r8.attempt(htlcId6, 10000 msat)
45+
assert(r8.getConfidence(10000 msat) === (3.0 / 13) +- 0.001)
46+
val r10 = r9.record(htlcId6, isSuccess = false)
47+
assert(r10.getConfidence(10000 msat) === (3.0 / 13) +- 0.001)
5148
}
5249

5350
test("long HTLC") {
5451
val r0 = Reputation.init(Config(enabled = true, 1000 day, 1 second, 10))
55-
val (r1, c1) = r0.attempt(uuid1, 100000 msat, TimestampMilli(0))
56-
assert(c1 == 0)
57-
val r2 = r1.record(uuid1, isSuccess = true, now = TimestampMilli(0))
58-
val (r3, c3) = r2.attempt(uuid2, 1000 msat, TimestampMilli(0))
59-
assert(c3 === (10.0 / 11) +- 0.001)
60-
val r4 = r3.record(uuid2, isSuccess = false, now = TimestampMilli(0) + 100.seconds)
61-
val (_, c5) = r4.attempt(uuid3, 0 msat, now = TimestampMilli(0) + 100.seconds)
62-
assert(c5 === 0.5 +- 0.001)
52+
assert(r0.getConfidence(100000 msat, TimestampMilli(0)) == 0)
53+
val r1 = r0.attempt(htlcId1, 100000 msat, TimestampMilli(0))
54+
val r2 = r1.record(htlcId1, isSuccess = true, now = TimestampMilli(0))
55+
assert(r2.getConfidence(1000 msat, TimestampMilli(0)) === (10.0 / 11) +- 0.001)
56+
val r3 = r2.attempt(htlcId2, 1000 msat, TimestampMilli(0))
57+
val r4 = r3.record(htlcId2, isSuccess = false, now = TimestampMilli(0) + 100.seconds)
58+
assert(r4.getConfidence(0 msat, now = TimestampMilli(0) + 100.seconds) === 0.5 +- 0.001)
6359
}
6460

6561
test("exponential decay") {
6662
val r0 = Reputation.init(Config(enabled = true, 100 seconds, 1 second, 1))
67-
val (r1, _) = r0.attempt(uuid1, 1000 msat, TimestampMilli(0))
68-
val r2 = r1.record(uuid1, isSuccess = true, now = TimestampMilli(0))
69-
val (r3, c3) = r2.attempt(uuid2, 1000 msat, TimestampMilli(0))
70-
assert(c3 == 1.0 / 2)
71-
val r4 = r3.record(uuid2, isSuccess = true, now = TimestampMilli(0))
72-
val (r5, c5) = r4.attempt(uuid3, 1000 msat, TimestampMilli(0))
73-
assert(c5 == 2.0 / 3)
74-
val r6 = r5.record(uuid3, isSuccess = true, now = TimestampMilli(0))
75-
val (r7, c7) = r6.attempt(uuid4, 1000 msat, TimestampMilli(0) + 100.seconds)
76-
assert(c7 == 1.5 / 2.5)
77-
val r8 = r7.cancel(uuid4)
78-
val (_, c9) = r8.attempt(uuid5, 1000 msat, TimestampMilli(0) + 1.hour)
79-
assert(c9 < 0.000001)
63+
val r1 = r0.attempt(htlcId1, 1000 msat, TimestampMilli(0))
64+
val r2 = r1.record(htlcId1, isSuccess = true, now = TimestampMilli(0))
65+
assert(r2.getConfidence(1000 msat, TimestampMilli(0)) == 1.0 / 2)
66+
val r3 = r2.attempt(htlcId2, 1000 msat, TimestampMilli(0))
67+
val r4 = r3.record(htlcId2, isSuccess = true, now = TimestampMilli(0))
68+
assert(r4.getConfidence(1000 msat, TimestampMilli(0)) == 2.0 / 3)
69+
val r5 = r4.attempt(htlcId3, 1000 msat, TimestampMilli(0))
70+
val r6 = r5.record(htlcId3, isSuccess = true, now = TimestampMilli(0))
71+
assert(r6.getConfidence(1000 msat, TimestampMilli(0) + 100.seconds) == 1.5 / 2.5)
72+
assert(r6.getConfidence(1000 msat, TimestampMilli(0) + 1.hour) < 0.000001)
8073
}
8174
}

0 commit comments

Comments
 (0)