mirror of
https://github.com/ACINQ/eclair.git
synced 2025-03-03 17:36:56 +01:00
Update allnodes API (#1468)
Rename to `nodes`. Allow filtering for specific `nodeId`s. Fixes #1460
This commit is contained in:
parent
88cb24dc81
commit
0563d6d6ce
6 changed files with 69 additions and 13 deletions
|
@ -33,7 +33,7 @@ and COMMAND is one of the available commands:
|
||||||
- connect
|
- connect
|
||||||
- disconnect
|
- disconnect
|
||||||
- peers
|
- peers
|
||||||
- allnodes
|
- nodes
|
||||||
- audit
|
- audit
|
||||||
|
|
||||||
=== Channel ===
|
=== Channel ===
|
||||||
|
|
|
@ -86,7 +86,9 @@ trait Eclair {
|
||||||
|
|
||||||
def channelInfo(channel: ApiTypes.ChannelIdentifier)(implicit timeout: Timeout): Future[RES_GETINFO]
|
def channelInfo(channel: ApiTypes.ChannelIdentifier)(implicit timeout: Timeout): Future[RES_GETINFO]
|
||||||
|
|
||||||
def peersInfo()(implicit timeout: Timeout): Future[Iterable[PeerInfo]]
|
def peers()(implicit timeout: Timeout): Future[Iterable[PeerInfo]]
|
||||||
|
|
||||||
|
def nodes(nodeIds_opt: Option[Set[PublicKey]] = None)(implicit timeout: Timeout): Future[Iterable[NodeAnnouncement]]
|
||||||
|
|
||||||
def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest]
|
def receive(description: String, amount_opt: Option[MilliSatoshi], expire_opt: Option[Long], fallbackAddress_opt: Option[String], paymentPreimage_opt: Option[ByteVector32])(implicit timeout: Timeout): Future[PaymentRequest]
|
||||||
|
|
||||||
|
@ -118,8 +120,6 @@ trait Eclair {
|
||||||
|
|
||||||
def allInvoices(from_opt: Option[Long], to_opt: Option[Long])(implicit timeout: Timeout): Future[Seq[PaymentRequest]]
|
def allInvoices(from_opt: Option[Long], to_opt: Option[Long])(implicit timeout: Timeout): Future[Seq[PaymentRequest]]
|
||||||
|
|
||||||
def allNodes()(implicit timeout: Timeout): Future[Iterable[NodeAnnouncement]]
|
|
||||||
|
|
||||||
def allChannels()(implicit timeout: Timeout): Future[Iterable[ChannelDesc]]
|
def allChannels()(implicit timeout: Timeout): Future[Iterable[ChannelDesc]]
|
||||||
|
|
||||||
def allUpdates(nodeId_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[ChannelUpdate]]
|
def allUpdates(nodeId_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[ChannelUpdate]]
|
||||||
|
@ -174,11 +174,17 @@ class EclairImpl(appKit: Kit) extends Eclair {
|
||||||
sendToChannels[ChannelCommandResponse](channels, CMD_UPDATE_RELAY_FEE(feeBaseMsat, feeProportionalMillionths))
|
sendToChannels[ChannelCommandResponse](channels, CMD_UPDATE_RELAY_FEE(feeBaseMsat, feeProportionalMillionths))
|
||||||
}
|
}
|
||||||
|
|
||||||
override def peersInfo()(implicit timeout: Timeout): Future[Iterable[PeerInfo]] = for {
|
override def peers()(implicit timeout: Timeout): Future[Iterable[PeerInfo]] = for {
|
||||||
peers <- (appKit.switchboard ? Symbol("peers")).mapTo[Iterable[ActorRef]]
|
peers <- (appKit.switchboard ? Symbol("peers")).mapTo[Iterable[ActorRef]]
|
||||||
peerinfos <- Future.sequence(peers.map(peer => (peer ? GetPeerInfo).mapTo[PeerInfo]))
|
peerinfos <- Future.sequence(peers.map(peer => (peer ? GetPeerInfo).mapTo[PeerInfo]))
|
||||||
} yield peerinfos
|
} yield peerinfos
|
||||||
|
|
||||||
|
override def nodes(nodeIds_opt: Option[Set[PublicKey]])(implicit timeout: Timeout): Future[Iterable[NodeAnnouncement]] = {
|
||||||
|
(appKit.router ? Symbol("nodes"))
|
||||||
|
.mapTo[Iterable[NodeAnnouncement]]
|
||||||
|
.map(_.filter(n => nodeIds_opt.forall(_.contains(n.nodeId))))
|
||||||
|
}
|
||||||
|
|
||||||
override def channelsInfo(toRemoteNode_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[RES_GETINFO]] = toRemoteNode_opt match {
|
override def channelsInfo(toRemoteNode_opt: Option[PublicKey])(implicit timeout: Timeout): Future[Iterable[RES_GETINFO]] = toRemoteNode_opt match {
|
||||||
case Some(pk) => for {
|
case Some(pk) => for {
|
||||||
channelIds <- (appKit.register ? Symbol("channelsTo")).mapTo[Map[ByteVector32, PublicKey]].map(_.filter(_._2 == pk).keys)
|
channelIds <- (appKit.register ? Symbol("channelsTo")).mapTo[Map[ByteVector32, PublicKey]].map(_.filter(_._2 == pk).keys)
|
||||||
|
@ -194,8 +200,6 @@ class EclairImpl(appKit: Kit) extends Eclair {
|
||||||
sendToChannel[RES_GETINFO](channel, CMD_GETINFO)
|
sendToChannel[RES_GETINFO](channel, CMD_GETINFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def allNodes()(implicit timeout: Timeout): Future[Iterable[NodeAnnouncement]] = (appKit.router ? Symbol("nodes")).mapTo[Iterable[NodeAnnouncement]]
|
|
||||||
|
|
||||||
override def allChannels()(implicit timeout: Timeout): Future[Iterable[ChannelDesc]] = {
|
override def allChannels()(implicit timeout: Timeout): Future[Iterable[ChannelDesc]] = {
|
||||||
(appKit.router ? Symbol("channels")).mapTo[Iterable[ChannelAnnouncement]].map(_.map(c => ChannelDesc(c.shortChannelId, c.nodeId1, c.nodeId2)))
|
(appKit.router ? Symbol("channels")).mapTo[Iterable[ChannelAnnouncement]].map(_.map(c => ChannelDesc(c.shortChannelId, c.nodeId1, c.nodeId2)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import fr.acinq.eclair.payment.send.PaymentInitiator.{SendPaymentRequest, SendPa
|
||||||
import fr.acinq.eclair.router.RouteCalculationSpec.makeUpdateShort
|
import fr.acinq.eclair.router.RouteCalculationSpec.makeUpdateShort
|
||||||
import fr.acinq.eclair.router.Router.{GetNetworkStats, GetNetworkStatsResponse, PublicChannel}
|
import fr.acinq.eclair.router.Router.{GetNetworkStats, GetNetworkStatsResponse, PublicChannel}
|
||||||
import fr.acinq.eclair.router.{Announcements, NetworkStats, Router, Stats}
|
import fr.acinq.eclair.router.{Announcements, NetworkStats, Router, Stats}
|
||||||
|
import fr.acinq.eclair.wire.{Color, NodeAnnouncement}
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
import org.mockito.scalatest.IdiomaticMockito
|
import org.mockito.scalatest.IdiomaticMockito
|
||||||
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
import org.scalatest.funsuite.FixtureAnyFunSuiteLike
|
||||||
|
@ -152,6 +153,54 @@ class EclairImplSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with I
|
||||||
assertThrows[IllegalArgumentException](Await.result(eclair.send(None, nodeId, 123 msat, ByteVector32.Zeroes, invoice_opt = Some(expiredInvoice)), 50 millis))
|
assertThrows[IllegalArgumentException](Await.result(eclair.send(None, nodeId, 123 msat, ByteVector32.Zeroes, invoice_opt = Some(expiredInvoice)), 50 millis))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("return node announcements") { f =>
|
||||||
|
import f._
|
||||||
|
|
||||||
|
val eclair = new EclairImpl(kit)
|
||||||
|
val remoteNodeAnn1 = NodeAnnouncement(randomBytes64, Features.empty, 42L, randomKey.publicKey, Color(42, 42, 42), "LN-rocks", Nil)
|
||||||
|
val remoteNodeAnn2 = NodeAnnouncement(randomBytes64, Features.empty, 43L, randomKey.publicKey, Color(43, 43, 43), "LN-papers", Nil)
|
||||||
|
val allNodes = Seq(
|
||||||
|
NodeAnnouncement(randomBytes64, Features.empty, 561L, randomKey.publicKey, Color(0, 0, 0), "some-node", Nil),
|
||||||
|
remoteNodeAnn1,
|
||||||
|
remoteNodeAnn2,
|
||||||
|
NodeAnnouncement(randomBytes64, Features.empty, 1105L, randomKey.publicKey, Color(0, 0, 0), "some-other-node", Nil),
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
val fRes = eclair.nodes()
|
||||||
|
router.expectMsg(Symbol("nodes"))
|
||||||
|
router.reply(allNodes)
|
||||||
|
awaitCond(fRes.value match {
|
||||||
|
case Some(Success(nodes)) =>
|
||||||
|
assert(nodes.toSet === allNodes.toSet)
|
||||||
|
true
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
val fRes = eclair.nodes(Some(Set(remoteNodeAnn1.nodeId, remoteNodeAnn2.nodeId)))
|
||||||
|
router.expectMsg(Symbol("nodes"))
|
||||||
|
router.reply(allNodes)
|
||||||
|
awaitCond(fRes.value match {
|
||||||
|
case Some(Success(nodes)) =>
|
||||||
|
assert(nodes.toSet === Set(remoteNodeAnn1, remoteNodeAnn2))
|
||||||
|
true
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
val fRes = eclair.nodes(Some(Set(randomKey.publicKey)))
|
||||||
|
router.expectMsg(Symbol("nodes"))
|
||||||
|
router.reply(allNodes)
|
||||||
|
awaitCond(fRes.value match {
|
||||||
|
case Some(Success(nodes)) =>
|
||||||
|
assert(nodes.isEmpty)
|
||||||
|
true
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("allupdates can filter by nodeId") { f =>
|
test("allupdates can filter by nodeId") { f =>
|
||||||
import f._
|
import f._
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ trait ExtraDirectives extends Directives {
|
||||||
val channelIdFormParam = "channelId".as[ByteVector32](sha256HashUnmarshaller)
|
val channelIdFormParam = "channelId".as[ByteVector32](sha256HashUnmarshaller)
|
||||||
val channelIdsFormParam = "channelIds".as[List[ByteVector32]](sha256HashesUnmarshaller)
|
val channelIdsFormParam = "channelIds".as[List[ByteVector32]](sha256HashesUnmarshaller)
|
||||||
val nodeIdFormParam = "nodeId".as[PublicKey]
|
val nodeIdFormParam = "nodeId".as[PublicKey]
|
||||||
|
val nodeIdsFormParam = "nodeIds".as[List[PublicKey]](pubkeyListUnmarshaller)
|
||||||
val paymentHashFormParam = "paymentHash".as[ByteVector32](sha256HashUnmarshaller)
|
val paymentHashFormParam = "paymentHash".as[ByteVector32](sha256HashUnmarshaller)
|
||||||
val fromFormParam = "from".as[Long]
|
val fromFormParam = "from".as[Long]
|
||||||
val toFormParam = "to".as[Long]
|
val toFormParam = "to".as[Long]
|
||||||
|
|
|
@ -169,7 +169,12 @@ trait Service extends ExtraDirectives with Logging {
|
||||||
}
|
}
|
||||||
} ~
|
} ~
|
||||||
path("peers") {
|
path("peers") {
|
||||||
complete(eclairApi.peersInfo())
|
complete(eclairApi.peers())
|
||||||
|
} ~
|
||||||
|
path("nodes") {
|
||||||
|
formFields(nodeIdsFormParam.?) { nodeIds_opt =>
|
||||||
|
complete(eclairApi.nodes(nodeIds_opt.map(_.toSet)))
|
||||||
|
}
|
||||||
} ~
|
} ~
|
||||||
path("channels") {
|
path("channels") {
|
||||||
formFields(nodeIdFormParam.?) { toRemoteNodeId_opt =>
|
formFields(nodeIdFormParam.?) { toRemoteNodeId_opt =>
|
||||||
|
@ -181,9 +186,6 @@ trait Service extends ExtraDirectives with Logging {
|
||||||
complete(eclairApi.channelInfo(channel))
|
complete(eclairApi.channelInfo(channel))
|
||||||
}
|
}
|
||||||
} ~
|
} ~
|
||||||
path("allnodes") {
|
|
||||||
complete(eclairApi.allNodes())
|
|
||||||
} ~
|
|
||||||
path("allchannels") {
|
path("allchannels") {
|
||||||
complete(eclairApi.allChannels())
|
complete(eclairApi.allChannels())
|
||||||
} ~
|
} ~
|
||||||
|
|
|
@ -124,7 +124,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
|
||||||
test("'peers' should ask the switchboard for current known peers") {
|
test("'peers' should ask the switchboard for current known peers") {
|
||||||
val eclair = mock[Eclair]
|
val eclair = mock[Eclair]
|
||||||
val mockService = new MockService(eclair)
|
val mockService = new MockService(eclair)
|
||||||
eclair.peersInfo()(any[Timeout]) returns Future.successful(List(
|
eclair.peers()(any[Timeout]) returns Future.successful(List(
|
||||||
PeerInfo(
|
PeerInfo(
|
||||||
nodeId = aliceNodeId,
|
nodeId = aliceNodeId,
|
||||||
state = "CONNECTED",
|
state = "CONNECTED",
|
||||||
|
@ -143,7 +143,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
|
||||||
assert(handled)
|
assert(handled)
|
||||||
assert(status == OK)
|
assert(status == OK)
|
||||||
val response = entityAs[String]
|
val response = entityAs[String]
|
||||||
eclair.peersInfo()(any[Timeout]).wasCalled(once)
|
eclair.peers()(any[Timeout]).wasCalled(once)
|
||||||
matchTestJson("peers", response)
|
matchTestJson("peers", response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue