1
0
Fork 0
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:
Bastien Teinturier 2020-06-23 15:28:48 +02:00 committed by GitHub
parent 88cb24dc81
commit 0563d6d6ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 13 deletions

View file

@ -33,7 +33,7 @@ and COMMAND is one of the available commands:
- connect
- disconnect
- peers
- allnodes
- nodes
- audit
=== Channel ===

View file

@ -86,7 +86,9 @@ trait Eclair {
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]
@ -118,8 +120,6 @@ trait Eclair {
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 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))
}
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]]
peerinfos <- Future.sequence(peers.map(peer => (peer ? GetPeerInfo).mapTo[PeerInfo]))
} 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 {
case Some(pk) => for {
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)
}
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]] = {
(appKit.router ? Symbol("channels")).mapTo[Iterable[ChannelAnnouncement]].map(_.map(c => ChannelDesc(c.shortChannelId, c.nodeId1, c.nodeId2)))
}

View file

@ -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.Router.{GetNetworkStats, GetNetworkStatsResponse, PublicChannel}
import fr.acinq.eclair.router.{Announcements, NetworkStats, Router, Stats}
import fr.acinq.eclair.wire.{Color, NodeAnnouncement}
import org.mockito.Mockito
import org.mockito.scalatest.IdiomaticMockito
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))
}
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 =>
import f._

View file

@ -39,6 +39,7 @@ trait ExtraDirectives extends Directives {
val channelIdFormParam = "channelId".as[ByteVector32](sha256HashUnmarshaller)
val channelIdsFormParam = "channelIds".as[List[ByteVector32]](sha256HashesUnmarshaller)
val nodeIdFormParam = "nodeId".as[PublicKey]
val nodeIdsFormParam = "nodeIds".as[List[PublicKey]](pubkeyListUnmarshaller)
val paymentHashFormParam = "paymentHash".as[ByteVector32](sha256HashUnmarshaller)
val fromFormParam = "from".as[Long]
val toFormParam = "to".as[Long]

View file

@ -169,7 +169,12 @@ trait Service extends ExtraDirectives with Logging {
}
} ~
path("peers") {
complete(eclairApi.peersInfo())
complete(eclairApi.peers())
} ~
path("nodes") {
formFields(nodeIdsFormParam.?) { nodeIds_opt =>
complete(eclairApi.nodes(nodeIds_opt.map(_.toSet)))
}
} ~
path("channels") {
formFields(nodeIdFormParam.?) { toRemoteNodeId_opt =>
@ -181,9 +186,6 @@ trait Service extends ExtraDirectives with Logging {
complete(eclairApi.channelInfo(channel))
}
} ~
path("allnodes") {
complete(eclairApi.allNodes())
} ~
path("allchannels") {
complete(eclairApi.allChannels())
} ~

View file

@ -124,7 +124,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
test("'peers' should ask the switchboard for current known peers") {
val eclair = mock[Eclair]
val mockService = new MockService(eclair)
eclair.peersInfo()(any[Timeout]) returns Future.successful(List(
eclair.peers()(any[Timeout]) returns Future.successful(List(
PeerInfo(
nodeId = aliceNodeId,
state = "CONNECTED",
@ -143,7 +143,7 @@ class ApiServiceSpec extends AnyFunSuite with ScalatestRouteTest with IdiomaticM
assert(handled)
assert(status == OK)
val response = entityAs[String]
eclair.peersInfo()(any[Timeout]).wasCalled(once)
eclair.peers()(any[Timeout]).wasCalled(once)
matchTestJson("peers", response)
}
}