Skip to content
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

Implement the findroute, findroutetonode and findroutebetweennodes commands #21

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion contrib/eclair-cli_autocomplete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ _eclair_cli() {
# `_init_completion` is a helper function provided by the Bash-completion package.
_init_completion || return

local commands="getinfo connect disconnect open rbfopen cpfpbumpfees close forceclose updaterelayfee peers nodes node allchannels allupdates createinvoice deleteinvoice parseinvoice payinvoice sendtonode sendtoroute getsentinfo getreceivedinfo listreceivedpayments getinvoice listinvoices listpendinginvoices"
local commands="getinfo connect disconnect open rbfopen cpfpbumpfees close forceclose updaterelayfee peers nodes node allchannels allupdates createinvoice deleteinvoice parseinvoice payinvoice sendtonode sendtoroute getsentinfo getreceivedinfo listreceivedpayments getinvoice listinvoices listpendinginvoices findroute findroutetonode findroutebetweennodes"
local common_opts="-p --host"
local connect_opts="--uri --nodeId --address --port"
local disconnect_opts="--nodeId"
Expand All @@ -44,6 +44,9 @@ _eclair_cli() {
local getinvoice_opts="--paymentHash"
local listinvoices_opts="--from --to --count --skip"
local listpendinginvoices_opts="--from --to --count --skip"
local findroute_opts="--invoice --amountMsat --ignoreNodeIds --ignoreShortChannelIds --format --maxFeeMsat --includeLocalChannelCost --pathFindingExperimentName"
local findroutetonode_opts="--nodeId --amountMsat --ignoreNodeIds --ignoreShortChannelIds --format --maxFeeMsat --includeLocalChannelCost --pathFindingExperimentName"
local findroutebetweennodes_opts="--sourceNodeId --targetNodeId --amountMsat --ignoreNodeIds --ignoreShortChannelIds --format --maxFeeMsat --includeLocalChannelCost --pathFindingExperimentName"
# If the current word starts with a dash (-), it's an option rather than a command
if [[ ${cur} == -* ]]; then
local cmd=""
Expand Down Expand Up @@ -126,6 +129,15 @@ _eclair_cli() {
listpendinginvoices)
COMPREPLY=( $(compgen -W "${listpendinginvoices_opts} ${common_opts}" -- ${cur}) )
;;
findroute)
COMPREPLY=( $(compgen -W "${findroute_opts} ${common_opts}" -- ${cur}) )
;;
findroutetonode)
COMPREPLY=( $(compgen -W "${findroutetonode_opts} ${common_opts}" -- ${cur}) )
;;
findroutebetweennodes)
COMPREPLY=( $(compgen -W "${findroutebetweennodes_opts} ${common_opts}" -- ${cur}) )
;;
*)
COMPREPLY=( $(compgen -W "${common_opts}" -- ${cur}) )
;;
Expand Down
6 changes: 4 additions & 2 deletions src/nativeMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ fun main(args: Array<String>) {
ForceCloseCommand(resultWriter, apiClientBuilder),
UpdateRelayFeeCommand(resultWriter, apiClientBuilder),
PeersCommand(resultWriter, apiClientBuilder),
UpdateRelayFeeCommand(resultWriter, apiClientBuilder),
NodesCommand(resultWriter, apiClientBuilder),
NodeCommand(resultWriter, apiClientBuilder),
AllChannelsCommand(resultWriter, apiClientBuilder),
Expand All @@ -35,7 +34,10 @@ fun main(args: Array<String>) {
ListReceivedPaymentsCommand(resultWriter, apiClientBuilder),
GetInvoiceCommand(resultWriter, apiClientBuilder),
ListInvoicesCommand(resultWriter, apiClientBuilder),
ListPendingInvoicesCommand(resultWriter, apiClientBuilder)
ListPendingInvoicesCommand(resultWriter, apiClientBuilder),
FindRouteCommand(resultWriter, apiClientBuilder),
FindRouteToNodeCommand(resultWriter, apiClientBuilder),
FindRouteBetweenNodesCommand(resultWriter, apiClientBuilder),
)
parser.parse(args)
}
136 changes: 136 additions & 0 deletions src/nativeMain/kotlin/api/EclairClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,41 @@ interface IEclairClient {
count: Int?,
skip: Int?
): Either<ApiError, String>

suspend fun findroute(
invoice: String,
amountMsat: Int?,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String>


suspend fun findroutetonode(
nodeId: String,
amountMsat: Int,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String>

suspend fun findroutebetweennodes(
sourceNodeId: String,
targetNodeId: String,
amountMsat: Int,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String>
}

class EclairClient(private val apiHost: String, private val apiPassword: String) : IEclairClient {
Expand Down Expand Up @@ -748,4 +783,105 @@ class EclairClient(private val apiHost: String, private val apiPassword: String)
Either.Left(ApiError(0, e.message ?: "Unknown exception"))
}
}

override suspend fun findroute(
invoice: String,
amountMsat: Int?,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String> {
return try {
val response: HttpResponse = httpClient.submitForm(
url = "$apiHost/findroute",
formParameters = Parameters.build {
append("invoice", invoice)
amountMsat?.let { append("amountMsat", it.toString()) }
ignoreNodeIds?.let { append("ignoreNodeIds", it.joinToString(",")) }
ignoreShortChannelIds?.let { append("ignoreShortChannelIds", it.joinToString(",")) }
format?.let { append("format", it) }
maxFeeMsat?.let { append("maxFeeMsat", it.toString()) }
includeLocalChannelCost?.let { append("includeLocalChannelCost", it.toString()) }
pathFindingExperimentName?.let { append("pathFindingExperimentName", it) }
}
)
when (response.status) {
HttpStatusCode.OK -> Either.Right((response.bodyAsText()))
else -> Either.Left(convertHttpError(response.status))
}
} catch (e: Throwable) {
Either.Left(ApiError(0, e.message ?: "Unknown exception"))
}
}

override suspend fun findroutetonode(
nodeId: String,
amountMsat: Int,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String> {
return try {
val response: HttpResponse = httpClient.submitForm(
url = "$apiHost/findroutetonode",
formParameters = Parameters.build {
append("nodeId", nodeId)
append("amountMsat", amountMsat.toString())
ignoreNodeIds?.let { append("ignoreNodeIds", it.joinToString(",")) }
ignoreShortChannelIds?.let { append("ignoreShortChannelIds", it.joinToString(",")) }
format?.let { append("format", it) }
maxFeeMsat?.let { append("maxFeeMsat", it.toString()) }
includeLocalChannelCost?.let { append("includeLocalChannelCost", it.toString()) }
pathFindingExperimentName?.let { append("pathFindingExperimentName", it) }
}
)
when (response.status) {
HttpStatusCode.OK -> Either.Right((response.bodyAsText()))
else -> Either.Left(convertHttpError(response.status))
}
} catch (e: Throwable) {
Either.Left(ApiError(0, e.message ?: "Unknown exception"))
}
}

override suspend fun findroutebetweennodes(
sourceNodeId: String,
targetNodeId: String,
amountMsat: Int,
ignoreNodeIds: List<String>?,
ignoreShortChannelIds: List<String>?,
format: String?,
maxFeeMsat: Int?,
includeLocalChannelCost: Boolean?,
pathFindingExperimentName: String?
): Either<ApiError, String> {
return try {
val response: HttpResponse = httpClient.submitForm(
url = "$apiHost/findroutebetweennodes",
formParameters = Parameters.build {
append("sourceNodeId", sourceNodeId)
append("targetNodeId", targetNodeId)
append("amountMsat", amountMsat.toString())
ignoreNodeIds?.let { append("ignoreNodeIds", it.joinToString(",")) }
ignoreShortChannelIds?.let { append("ignoreShortChannelIds", it.joinToString(",")) }
format?.let { append("format", it) }
maxFeeMsat?.let { append("maxFeeMsat", it.toString()) }
includeLocalChannelCost?.let { append("includeLocalChannelCost", it.toString()) }
pathFindingExperimentName?.let { append("pathFindingExperimentName", it) }
}
)
when (response.status) {
HttpStatusCode.OK -> Either.Right((response.bodyAsText()))
else -> Either.Left(convertHttpError(response.status))
}
} catch (e: Throwable) {
Either.Left(ApiError(0, e.message ?: "Unknown exception"))
}
}
}
66 changes: 66 additions & 0 deletions src/nativeMain/kotlin/commands/FindRoute.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package commands

import IResultWriter
import api.IEclairClientBuilder
import arrow.core.flatMap
import kotlinx.cli.ArgType
import kotlinx.coroutines.runBlocking
import types.FindRouteResponse
import types.Serialization

class FindRouteCommand(
private val resultWriter: IResultWriter,
private val eclairClientBuilder: IEclairClientBuilder
) : BaseCommand(
"findroute",
"Finds a route to the node specified by the invoice. The formats currently supported are nodeId, shortChannelId or full"
) {
private val invoice by option(
ArgType.String,
description = "The invoice containing the destination"
)
private val amountMsat by option(
ArgType.Int,
description = "The amount that should go through the route"
)
private val ignoreNodeIds by option(
ArgType.String,
description = "A list of nodes to exclude from path-finding"
)
private val ignoreShortChannelIds by option(
ArgType.String,
description = "A list of channels to exclude from path-finding"
)
private val format by option(
ArgType.String,
description = "Format that will be used for the resulting route"
)
private val maxFeeMsat by option(
ArgType.Int,
description = "Maximum fee allowed for this payment"
)
private val includeLocalChannelCost by option(
ArgType.Boolean,
description = "If true, the relay fees of local channels will be counted"
)
private val pathFindingExperimentName by option(
ArgType.String,
description = "Name of the path-finding configuration that should be used"
)

override fun execute() = runBlocking {
val eclairClient = eclairClientBuilder.build(host, password)
val result = eclairClient.findroute(
invoice!!,
amountMsat,
ignoreNodeIds?.split(","),
ignoreShortChannelIds?.split(","),
format,
maxFeeMsat,
includeLocalChannelCost,
pathFindingExperimentName
).flatMap { apiResponse -> Serialization.decode<FindRouteResponse>(apiResponse) }
.map { decoded -> Serialization.encode(decoded) }
resultWriter.write(result)
}
}
71 changes: 71 additions & 0 deletions src/nativeMain/kotlin/commands/FindRouteBetweenNodes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package commands

import IResultWriter
import api.IEclairClientBuilder
import arrow.core.flatMap
import kotlinx.cli.ArgType
import kotlinx.coroutines.runBlocking
import types.FindRouteResponse
import types.Serialization

class FindRouteBetweenNodesCommand(
private val resultWriter: IResultWriter,
private val eclairClientBuilder: IEclairClientBuilder
) : BaseCommand(
"findroutebetweennodes",
"Finds a route between two nodes."
) {
private val sourceNodeId by option(
ArgType.String,
description = "The destination of the route"
)
private val targetNodeId by option(
ArgType.String,
description = "The destination of the route"
)
private val amountMsat by option(
ArgType.Int,
description = "The amount that should go through the route"
)
private val ignoreNodeIds by option(
ArgType.String,
description = "A list of nodes to exclude from path-finding"
)
private val ignoreShortChannelIds by option(
ArgType.String,
description = "A list of channels to exclude from path-finding"
)
private val format by option(
ArgType.String,
description = "Format that will be used for the resulting route"
)
private val maxFeeMsat by option(
ArgType.Int,
description = "Maximum fee allowed for this payment"
)
private val includeLocalChannelCost by option(
ArgType.Boolean,
description = "If true, the relay fees of local channels will be counted"
)
private val pathFindingExperimentName by option(
ArgType.String,
description = "Name of the path-finding configuration that should be used"
)

override fun execute() = runBlocking {
val eclairClient = eclairClientBuilder.build(host, password)
val result = eclairClient.findroutebetweennodes(
sourceNodeId!!,
targetNodeId!!,
amountMsat!!,
ignoreNodeIds?.split(","),
ignoreShortChannelIds?.split(","),
format,
maxFeeMsat,
includeLocalChannelCost,
pathFindingExperimentName
).flatMap { apiResponse -> Serialization.decode<FindRouteResponse>(apiResponse) }
.map { decoded -> Serialization.encode(decoded) }
resultWriter.write(result)
}
}
Loading