You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuweni.apache.org by to...@apache.org on 2022/07/19 03:41:41 UTC
[incubator-tuweni] branch main updated: expose information from the rlpx service in the UI
This is an automated email from the ASF dual-hosted git repository.
toulmean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git
The following commit(s) were added to refs/heads/main by this push:
new 776bd698 expose information from the rlpx service in the UI
new 260b9e00 Merge pull request #426 from atoulme/new_ui_improvements
776bd698 is described below
commit 776bd6981f1b23f39c38807b0ad435923121fb74
Author: Antoine Toulme <an...@lunar-ocean.com>
AuthorDate: Sat Jul 16 10:01:05 2022 -0700
expose information from the rlpx service in the UI
---
eth-client-ui/build.gradle | 4 ++
.../apache/tuweni/ethclientui/UIIntegrationTest.kt | 32 +++++++++++--
.../org/apache/tuweni/ethclientui/JSONProvider.kt | 56 ++++++++++++++++++++++
.../org/apache/tuweni/ethclientui/StateService.kt | 19 ++++++--
.../kotlin/org/apache/tuweni/ethclientui/UI.kt | 2 +-
.../org/apache/tuweni/ethclient/EthereumClient.kt | 14 +++---
6 files changed, 114 insertions(+), 13 deletions(-)
diff --git a/eth-client-ui/build.gradle b/eth-client-ui/build.gradle
index 6f468ef6..64a0bea6 100644
--- a/eth-client-ui/build.gradle
+++ b/eth-client-ui/build.gradle
@@ -26,12 +26,16 @@ dependencies {
implementation 'javax.servlet:javax.servlet-api'
implementation 'javax.ws.rs:javax.ws.rs-api'
+ implementation project(':bytes')
implementation project(':concurrent')
implementation project(':config')
implementation project(':concurrent-coroutines')
implementation project(':crypto')
+ implementation project(':eth')
implementation project(':eth-client')
+ implementation project(':eth-repository')
implementation project(':peer-repository')
+ implementation project(':units')
testImplementation project(':junit')
testImplementation 'org.bouncycastle:bcprov-jdk15on'
diff --git a/eth-client-ui/src/integrationTest/kotlin/org/apache/tuweni/ethclientui/UIIntegrationTest.kt b/eth-client-ui/src/integrationTest/kotlin/org/apache/tuweni/ethclientui/UIIntegrationTest.kt
index 353fa3e3..19f0e0ba 100644
--- a/eth-client-ui/src/integrationTest/kotlin/org/apache/tuweni/ethclientui/UIIntegrationTest.kt
+++ b/eth-client-ui/src/integrationTest/kotlin/org/apache/tuweni/ethclientui/UIIntegrationTest.kt
@@ -17,22 +17,42 @@
package org.apache.tuweni.ethclientui
import io.vertx.core.Vertx
+import kotlinx.coroutines.runBlocking
import org.apache.tuweni.ethclient.EthereumClient
import org.apache.tuweni.ethclient.EthereumClientConfig
+import org.apache.tuweni.junit.BouncyCastleExtension
+import org.apache.tuweni.junit.TempDirectory
+import org.apache.tuweni.junit.TempDirectoryExtension
import org.apache.tuweni.junit.VertxExtension
import org.apache.tuweni.junit.VertxInstance
+import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import java.net.HttpURLConnection
import java.net.URL
+import java.nio.file.Path
-@ExtendWith(VertxExtension::class)
+@ExtendWith(VertxExtension::class, BouncyCastleExtension::class, TempDirectoryExtension::class)
class UIIntegrationTest {
@Test
- fun testServerComesUp(@VertxInstance vertx: Vertx) {
- val ui = UI(client = EthereumClient(vertx, EthereumClientConfig.fromString("[storage.forui]\npath=\"data\"")))
+ fun testServerComesUp(@VertxInstance vertx: Vertx, @TempDirectory tempDir: Path) = runBlocking {
+ val ui = UI(
+ client = EthereumClient(
+ vertx,
+ EthereumClientConfig.fromString(
+ """[storage.default]
+path="${tempDir.toAbsolutePath()}"
+genesis="default"
+[genesis.default]
+path="classpath:/genesis/dev.json"
+[peerRepository.default]
+type="memory""""
+ )
+ )
+ )
+ ui.client.start()
ui.start()
val url = URL("http://localhost:" + ui.actualPort)
val con = url.openConnection() as HttpURLConnection
@@ -45,6 +65,12 @@ class UIIntegrationTest {
con2.requestMethod = "GET"
val response2 = con2.inputStream.readAllBytes()
assertTrue(response2.isNotEmpty())
+ val url3 = URL("http://localhost:" + ui.actualPort + "/rest/state")
+ val con3 = url3.openConnection() as HttpURLConnection
+ con3.requestMethod = "GET"
+ val response3 = con3.inputStream.readAllBytes()
+ assertTrue(response3.isNotEmpty())
+ assertEquals("""{"peerCounts":{"default":0},"bestBlocks":{"default":{"hash":"0xa08d1edb37ba1c62db764ef7c2566cbe368b850f5b3762c6c24114a3fd97b87f","number":"0x0000000000000000000000000000000000000000000000000000000000000000"}}}""", String(response3))
ui.stop()
}
}
diff --git a/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/JSONProvider.kt b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/JSONProvider.kt
new file mode 100644
index 00000000..365beb51
--- /dev/null
+++ b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/JSONProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.ethclientui
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import jakarta.ws.rs.Produces
+import jakarta.ws.rs.core.Feature
+import jakarta.ws.rs.core.FeatureContext
+import jakarta.ws.rs.core.MediaType
+import jakarta.ws.rs.ext.MessageBodyReader
+import jakarta.ws.rs.ext.MessageBodyWriter
+import jakarta.ws.rs.ext.Provider
+import org.apache.tuweni.eth.EthJsonModule
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations
+import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider
+
+@Provider
+@Produces(MediaType.APPLICATION_JSON)
+class JSONProvider : JacksonJaxbJsonProvider {
+
+ constructor() : super()
+ constructor(vararg annotationsToUse: Annotations?) : super(mapper, annotationsToUse)
+
+ companion object {
+ val mapper = ObjectMapper()
+
+ init {
+ mapper.registerModule(EthJsonModule())
+ }
+ }
+
+ init {
+ setMapper(mapper)
+ }
+}
+
+class MarshallingFeature : Feature {
+ override fun configure(context: FeatureContext): Boolean {
+ context.register(JSONProvider::class.java, MessageBodyReader::class.java, MessageBodyWriter::class.java)
+ return true
+ }
+}
diff --git a/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/StateService.kt b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/StateService.kt
index f37461e6..9eee076f 100644
--- a/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/StateService.kt
+++ b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/StateService.kt
@@ -23,6 +23,13 @@ import jakarta.ws.rs.Path
import jakarta.ws.rs.Produces
import jakarta.ws.rs.core.Context
import jakarta.ws.rs.core.MediaType
+import kotlinx.coroutines.runBlocking
+import org.apache.tuweni.bytes.Bytes32
+import org.apache.tuweni.units.bigints.UInt256
+
+data class BlockHashAndNumber(val hash: Bytes32, val number: UInt256)
+
+data class State(val peerCounts: Map<String, Long>, val bestBlocks: Map<String, BlockHashAndNumber>)
@Path("state")
class StateService {
@@ -32,12 +39,18 @@ class StateService {
@GET
@Produces(MediaType.APPLICATION_JSON)
- fun get(): Map<String, Long> {
+ fun get(): State {
val client = context!!.getAttribute("ethclient") as EthereumClient
- val pairs = client.peerRepositories.entries.map {
+ val peerCounts = client.peerRepositories.entries.map {
Pair(it.key, it.value.activeConnections().count())
}
- return mapOf(*pairs.toTypedArray())
+ val bestBlocks = client.storageRepositories.entries.map {
+ runBlocking {
+ val bestBlock = it.value.retrieveChainHeadHeader()
+ Pair(it.key, BlockHashAndNumber(bestBlock.hash, bestBlock.number))
+ }
+ }
+ return State(mapOf(*peerCounts.toTypedArray()), mapOf(*bestBlocks.toTypedArray()))
}
}
diff --git a/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/UI.kt b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/UI.kt
index 41570903..fad640c8 100644
--- a/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/UI.kt
+++ b/eth-client-ui/src/main/kotlin/org/apache/tuweni/ethclientui/UI.kt
@@ -45,7 +45,7 @@ class UI(
ctx.contextPath = path
newServer.handler = ctx
- val config = ResourceConfig().packages(true, "org.apache.tuweni.ethclientui")
+ val config = ResourceConfig().packages(true, "org.apache.tuweni.ethclientui").register(MarshallingFeature::class.java)
val holder = ServletHolder(ServletContainer(config))
holder.initOrder = 1
ctx.addServlet(holder, "/rest/*")
diff --git a/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt b/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
index 15bfbedf..061b1028 100644
--- a/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
+++ b/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
@@ -83,8 +83,8 @@ class EthereumClient(
private var metricsService: MetricsService? = null
private val genesisFiles = mutableMapOf<String, GenesisFile>()
- private val services = mutableMapOf<String, RLPxService>()
- private val storageRepositories = mutableMapOf<String, BlockchainRepository>()
+ private val rlpxServices = mutableMapOf<String, RLPxService>()
+ val storageRepositories = mutableMapOf<String, BlockchainRepository>()
val peerRepositories = mutableMapOf<String, EthereumPeerRepository>()
private val dnsClients = mutableMapOf<String, DNSClient>()
private val discoveryServices = mutableMapOf<String, DiscoveryService>()
@@ -198,6 +198,7 @@ class EthereumClient(
discoveryServices[it.getName()] = discoveryService
logger.info("Started discovery service ${it.getName()}")
}
+ val adapters = mutableMapOf<String, WireConnectionPeerRepositoryAdapter>()
AsyncCompletion.allOf(
config.rlpxServices().map { rlpxConfig ->
@@ -252,7 +253,8 @@ class EthereumClient(
meter,
adapter
)
- services[rlpxConfig.getName()] = service
+ adapters[rlpxConfig.getName()] = adapter
+ rlpxServices[rlpxConfig.getName()] = service
peerRepository.addIdentityListener {
service.connectTo(
it.publicKey(),
@@ -287,9 +289,9 @@ class EthereumClient(
for (sync in config.synchronizers()) {
val syncRepository = storageRepositories[sync.getRepository()] ?: throw IllegalArgumentException("Repository ${sync.getRepository()} missing for synchronizer ${sync.getName()}")
- val syncService = services[sync.getRlpxService()] ?: throw IllegalArgumentException("Service ${sync.getRlpxService()} missing for synchronizer ${sync.getName()}")
+ val syncService = rlpxServices[sync.getRlpxService()] ?: throw IllegalArgumentException("Service ${sync.getRlpxService()} missing for synchronizer ${sync.getName()}")
val syncPeerRepository = peerRepositories[sync.getPeerRepository()] ?: throw IllegalArgumentException("Peer repository ${sync.getPeerRepository()} missing for synchronizer ${sync.getName()}")
- val adapter = WireConnectionPeerRepositoryAdapter(syncPeerRepository)
+ val adapter = adapters[sync.getRlpxService()] ?: throw IllegalArgumentException("Service ${sync.getRlpxService()} missing for synchronizer ${sync.getName()}")
when (sync.getType()) {
SynchronizerType.best -> {
@@ -397,7 +399,7 @@ class EthereumClient(
managerHandler.forEach {
it.stop()
}
- AsyncCompletion.allOf(services.values.map(RLPxService::stop)).await()
+ AsyncCompletion.allOf(rlpxServices.values.map(RLPxService::stop)).await()
storageRepositories.values.forEach(BlockchainRepository::close)
metricsService?.close()
Unit
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tuweni.apache.org
For additional commands, e-mail: commits-help@tuweni.apache.org