@@ -6,6 +6,7 @@ import android.net.Uri
66import android.os.Binder
77import android.os.Bundle
88import android.os.Process
9+ import android.os.SystemClock
910import androidx.core.os.bundleOf
1011import dagger.hilt.EntryPoint
1112import dagger.hilt.InstallIn
@@ -114,16 +115,19 @@ private sealed interface DevCommand {
114115 context = TAG ,
115116 )
116117
118+ val startedAt = SystemClock .elapsedRealtime()
117119 return deps.lightningRepo().sendProbeForInvoice(args.bolt11, amountSats)
118120 .fold(
119121 onSuccess = {
120122 deps.lightningRepo().waitForProbeOutcome(it.paymentIds, timeout)
121123 .fold(
122- onSuccess = { outcome -> outcome.toDevResult(it.paymentIds) },
123- onFailure = { error -> DevResult .ProbeFailure .from(error, it.paymentIds) },
124+ onSuccess = { outcome -> outcome.toDevResult(it.paymentIds, startedAt.durationMs()) },
125+ onFailure = { error ->
126+ DevResult .ProbeFailure .from(error, it.paymentIds, startedAt.durationMs())
127+ },
124128 )
125129 },
126- onFailure = { DevResult .ProbeFailure .from(it) },
130+ onFailure = { DevResult .ProbeFailure .from(it, durationMs = startedAt.durationMs() ) },
127131 )
128132 }
129133 }
@@ -149,20 +153,24 @@ private sealed interface DevCommand {
149153 val timeout = args.timeoutSeconds.coerceAtLeast(1 ).seconds
150154
151155 Logger .info(
152- " Sending keysend probe for target '${args.targetName ? : " unknown" } ' nodeId='${args.nodeId} ' amountSats='$amountSats '" ,
156+ " Sending keysend probe for target '${args.targetName ? : " unknown" } ' " +
157+ " nodeId='${args.nodeId} ' amountSats='$amountSats '" ,
153158 context = TAG ,
154159 )
155160
161+ val startedAt = SystemClock .elapsedRealtime()
156162 return deps.lightningRepo().sendProbeForNode(args.nodeId, amountSats)
157163 .fold(
158164 onSuccess = {
159165 deps.lightningRepo().waitForProbeOutcome(it.paymentIds, timeout)
160166 .fold(
161- onSuccess = { outcome -> outcome.toDevResult(it.paymentIds) },
162- onFailure = { error -> DevResult .ProbeFailure .from(error, it.paymentIds) },
167+ onSuccess = { outcome -> outcome.toDevResult(it.paymentIds, startedAt.durationMs()) },
168+ onFailure = { error ->
169+ DevResult .ProbeFailure .from(error, it.paymentIds, startedAt.durationMs())
170+ },
163171 )
164172 },
165- onFailure = { DevResult .ProbeFailure .from(it) },
173+ onFailure = { DevResult .ProbeFailure .from(it, durationMs = startedAt.durationMs() ) },
166174 )
167175 }
168176 }
@@ -206,6 +214,8 @@ private sealed interface DevResult {
206214 val success : Boolean = true ,
207215 val paymentId : String ,
208216 val paymentHash : String ,
217+ val durationMs : Long ,
218+ val routeFeeMsat : ULong? ,
209219 val paymentIds : List <String >,
210220 ) : DevResult
211221
@@ -215,12 +225,19 @@ private sealed interface DevResult {
215225 val message : String? = null ,
216226 val paymentId : String? = null ,
217227 val paymentHash : String? = null ,
218- val shortChannelId : ULong? = null ,
228+ val shortChannelId : String? = null ,
229+ val durationMs : Long ,
230+ val routeFeeMsat : ULong? = null ,
219231 val paymentIds : List <String > = emptyList(),
220232 ) : DevResult {
221233 companion object {
222- fun from (error : Throwable , paymentIds : Set <String > = emptySet()) = ProbeFailure (
234+ fun from (
235+ error : Throwable ,
236+ paymentIds : Set <String > = emptySet(),
237+ durationMs : Long ,
238+ ) = ProbeFailure (
223239 message = error.message,
240+ durationMs = durationMs,
224241 paymentIds = paymentIds.toList(),
225242 )
226243 }
@@ -243,6 +260,7 @@ private sealed interface DevResult {
243260 val graphChannelCount : Int? = null ,
244261 val latestRgsSyncTimestamp : ULong? = null ,
245262 val latestPathfindingScoresSyncTimestamp : ULong? = null ,
263+ val probeRuntimeConfig : ProbeRuntimeConfig ,
246264 ) : DevResult {
247265 companion object {
248266 fun from (readiness : NodeProbeReadiness ) = ProbeReadiness (
@@ -261,29 +279,91 @@ private sealed interface DevResult {
261279 graphChannelCount = readiness.graphChannelCount,
262280 latestRgsSyncTimestamp = readiness.latestRgsSyncTimestamp,
263281 latestPathfindingScoresSyncTimestamp = readiness.latestPathfindingScoresSyncTimestamp,
282+ probeRuntimeConfig = ProbeRuntimeConfig .from(readiness.probeRuntimeConfig),
264283 )
265284 }
266285 }
267286
287+ @Serializable
288+ data class ProbeRuntimeConfig (
289+ val sampleAmountMsat : ULong ,
290+ val route : ProbeRouteConfig ,
291+ val scoring : ProbeScoringConfig ,
292+ ) {
293+ companion object {
294+ fun from (config : to.bitkit.services.ProbeRuntimeConfig ) = ProbeRuntimeConfig (
295+ sampleAmountMsat = config.sampleAmountMsat,
296+ route = ProbeRouteConfig (
297+ maxTotalRoutingFeeMsat = config.route.maxTotalRoutingFeeMsat,
298+ maxTotalCltvExpiryDelta = config.route.maxTotalCltvExpiryDelta,
299+ maxPathCount = config.route.maxPathCount,
300+ maxChannelSaturationPowerOfHalf = config.route.maxChannelSaturationPowerOfHalf,
301+ ),
302+ scoring = ProbeScoringConfig (
303+ basePenaltyMsat = config.scoring.basePenaltyMsat,
304+ basePenaltyAmountMultiplierMsat = config.scoring.basePenaltyAmountMultiplierMsat,
305+ liquidityPenaltyMultiplierMsat = config.scoring.liquidityPenaltyMultiplierMsat,
306+ liquidityPenaltyAmountMultiplierMsat = config.scoring.liquidityPenaltyAmountMultiplierMsat,
307+ historicalLiquidityPenaltyMultiplierMsat =
308+ config.scoring.historicalLiquidityPenaltyMultiplierMsat,
309+ historicalLiquidityPenaltyAmountMultiplierMsat =
310+ config.scoring.historicalLiquidityPenaltyAmountMultiplierMsat,
311+ antiProbingPenaltyMsat = config.scoring.antiProbingPenaltyMsat,
312+ consideredImpossiblePenaltyMsat = config.scoring.consideredImpossiblePenaltyMsat,
313+ linearSuccessProbability = config.scoring.linearSuccessProbability,
314+ probingDiversityPenaltyMsat = config.scoring.probingDiversityPenaltyMsat,
315+ ),
316+ )
317+ }
318+ }
319+
320+ @Serializable
321+ data class ProbeRouteConfig (
322+ val maxTotalRoutingFeeMsat : ULong? ,
323+ val maxTotalCltvExpiryDelta : UInt ,
324+ val maxPathCount : Int ,
325+ val maxChannelSaturationPowerOfHalf : Int ,
326+ )
327+
328+ @Serializable
329+ data class ProbeScoringConfig (
330+ val basePenaltyMsat : ULong ,
331+ val basePenaltyAmountMultiplierMsat : ULong ,
332+ val liquidityPenaltyMultiplierMsat : ULong ,
333+ val liquidityPenaltyAmountMultiplierMsat : ULong ,
334+ val historicalLiquidityPenaltyMultiplierMsat : ULong ,
335+ val historicalLiquidityPenaltyAmountMultiplierMsat : ULong ,
336+ val antiProbingPenaltyMsat : ULong ,
337+ val consideredImpossiblePenaltyMsat : ULong ,
338+ val linearSuccessProbability : Boolean ,
339+ val probingDiversityPenaltyMsat : ULong ,
340+ )
341+
268342 @Serializable data class Error (val message : String? = null ) : DevResult
269343
270344 fun toBundle () = bundleOf(KEY_RESULT to DEV_JSON .encodeToString(this ))
271345}
272346
273- private fun ProbeOutcome.toDevResult (paymentIds : Set <String >): DevResult = when (this ) {
347+ private fun ProbeOutcome.toDevResult (paymentIds : Set <String >, durationMs : Long ): DevResult = when (this ) {
274348 is ProbeOutcome .Success -> DevResult .ProbeSuccess (
275349 paymentId = paymentId,
276350 paymentHash = paymentHash,
351+ durationMs = durationMs,
352+ routeFeeMsat = routeFeeMsat,
277353 paymentIds = paymentIds.toList(),
278354 )
279355 is ProbeOutcome .Failure -> DevResult .ProbeFailure (
280356 message = " Probe failed" ,
281357 paymentId = paymentId,
282358 paymentHash = paymentHash,
283- shortChannelId = shortChannelId,
359+ shortChannelId = shortChannelId?.toString(),
360+ durationMs = durationMs,
361+ routeFeeMsat = routeFeeMsat,
284362 paymentIds = paymentIds.toList(),
285363 )
286364}
287365
288366private inline fun <reified T > String?.deserialize (): T =
289367 if (isNullOrBlank()) Json .decodeFromString(" {}" ) else Json .decodeFromString(this )
368+
369+ private fun Long.durationMs () = SystemClock .elapsedRealtime() - this
0 commit comments