-
Notifications
You must be signed in to change notification settings - Fork 3
test: add mainnet probe devtools #940
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,9 +15,12 @@ import kotlinx.serialization.Serializable | |
| import kotlinx.serialization.json.Json | ||
| import to.bitkit.async.ServiceQueue | ||
| import to.bitkit.repositories.LightningRepo | ||
| import to.bitkit.repositories.ProbeOutcome | ||
| import to.bitkit.utils.Logger | ||
| import kotlin.time.Duration.Companion.seconds | ||
|
|
||
| private const val TAG = "DevToolsProvider" | ||
| private val DEV_JSON = Json { encodeDefaults = true } | ||
|
|
||
| class DevToolsProvider : ContentProvider() { | ||
|
|
||
|
|
@@ -56,6 +59,7 @@ private sealed interface DevCommand { | |
| companion object { | ||
| fun parse(method: String, arg: String?): DevCommand? = when (method) { | ||
| CreateInvoice.METHOD -> CreateInvoice.parse(arg) | ||
| ProbeInvoice.METHOD -> ProbeInvoice.parse(arg) | ||
| else -> null | ||
| } | ||
| } | ||
|
|
@@ -80,6 +84,44 @@ private sealed interface DevCommand { | |
| }, | ||
| ) | ||
| } | ||
|
|
||
| data class ProbeInvoice(val args: Args) : DevCommand { | ||
| companion object { | ||
| const val METHOD = "probeInvoice" | ||
| fun parse(arg: String?) = ProbeInvoice(arg.deserialize<Args>()) | ||
| } | ||
|
|
||
| @Serializable | ||
| data class Args( | ||
| val targetName: String? = null, | ||
| val bolt11: String, | ||
| val amountMsat: ULong? = null, | ||
| val amountSats: ULong? = null, | ||
| val timeoutSeconds: Long = 90, | ||
| ) | ||
|
|
||
| override suspend fun execute(deps: DevToolsProvider.Dependencies): DevResult { | ||
| val amountSats = args.amountSats ?: args.amountMsat?.div(1_000u) | ||
| val timeout = args.timeoutSeconds.coerceAtLeast(1).seconds | ||
|
|
||
| Logger.info( | ||
| "Sending probe for target '${args.targetName ?: "unknown"}' amountSats='${amountSats ?: "invoice"}'", | ||
| context = TAG, | ||
| ) | ||
|
|
||
| return deps.lightningRepo().sendProbeForInvoice(args.bolt11, amountSats) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. caution: This should work, but make sure the app is open and a wallet with LN funds is loaded, otherwise the node will not start and the OP will timeout. |
||
| .fold( | ||
| onSuccess = { | ||
| deps.lightningRepo().waitForProbeOutcome(it.paymentIds, timeout) | ||
| .fold( | ||
| onSuccess = { outcome -> outcome.toDevResult(it.paymentIds) }, | ||
| onFailure = { error -> DevResult.ProbeFailure.from(error, it.paymentIds) }, | ||
| ) | ||
| }, | ||
| onFailure = { DevResult.ProbeFailure.from(it) }, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Serializable | ||
|
|
@@ -91,9 +133,49 @@ private sealed interface DevResult { | |
|
|
||
| @Serializable data class Invoice(val bolt11: String) : DevResult | ||
|
|
||
| @Serializable | ||
| data class ProbeSuccess( | ||
| val success: Boolean = true, | ||
| val paymentId: String, | ||
| val paymentHash: String, | ||
| val paymentIds: List<String>, | ||
| ) : DevResult | ||
|
|
||
| @Serializable | ||
| data class ProbeFailure( | ||
| val success: Boolean = false, | ||
| val message: String? = null, | ||
| val paymentId: String? = null, | ||
| val paymentHash: String? = null, | ||
| val shortChannelId: ULong? = null, | ||
| val paymentIds: List<String> = emptyList(), | ||
| ) : DevResult { | ||
| companion object { | ||
| fun from(error: Throwable, paymentIds: Set<String> = emptySet()) = ProbeFailure( | ||
| message = error.message, | ||
| paymentIds = paymentIds.toList(), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Serializable data class Error(val message: String? = null) : DevResult | ||
|
|
||
| fun toBundle() = bundleOf(KEY_RESULT to Json.encodeToString(this)) | ||
| fun toBundle() = bundleOf(KEY_RESULT to DEV_JSON.encodeToString(this)) | ||
| } | ||
|
|
||
| private fun ProbeOutcome.toDevResult(paymentIds: Set<String>): DevResult = when (this) { | ||
| is ProbeOutcome.Success -> DevResult.ProbeSuccess( | ||
| paymentId = paymentId, | ||
| paymentHash = paymentHash, | ||
| paymentIds = paymentIds.toList(), | ||
| ) | ||
| is ProbeOutcome.Failure -> DevResult.ProbeFailure( | ||
| message = "Probe failed", | ||
| paymentId = paymentId, | ||
| paymentHash = paymentHash, | ||
| shortChannelId = shortChannelId, | ||
| paymentIds = paymentIds.toList(), | ||
| ) | ||
| } | ||
|
|
||
| private inline fun <reified T> String?.deserialize(): T = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1267,6 +1267,19 @@ class LightningRepoTest : BaseUnitTest() { | |
| assertEquals(setOf(probePaymentA, probePaymentB), result.getOrThrow().paymentIds) | ||
| } | ||
|
|
||
| @Test | ||
| fun `sendProbeForInvoice delegates amount probes when sats are provided`() = test { | ||
| startNodeForTesting() | ||
| whenever(lightningService.sendProbesUsingAmount("lnbc1", 42_000uL)) | ||
| .thenReturn(Result.success(setOf(probePaymentA))) | ||
|
|
||
| val result = sut.sendProbeForInvoice("lnbc1", amountSats = 42uL) | ||
|
|
||
| assertTrue(result.isSuccess) | ||
| assertEquals(setOf(probePaymentA), result.getOrThrow().paymentIds) | ||
| verifyBlocking(lightningService) { sendProbesUsingAmount("lnbc1", 42_000uL) } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Unfortunately Codex always defaults to prefer it, it's smart but not applicable here. |
||
| } | ||
|
|
||
| @Test | ||
| fun `sendProbeForNode delegates to keysend probe and returns payment IDs`() = test { | ||
| startNodeForTesting() | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: try to use
args.amountMsat?.let { msatCeilOf(it) }instead ofargs.amountMsat?.div(1_000u)if it's still satisfying your requirements