Merge pull request #543 from apache/jsonrpc_ethgetBlockByNumber

Support eth_getBlockByNumber from JSONRPC client
diff --git a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt
index 7c9e205..4f221fd 100644
--- a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt
+++ b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt
@@ -68,7 +68,7 @@
   val authorizationHeader = "Basic " + Base64.getEncoder()
     .encode((basicAuthenticationUsername + ":" + basicAuthenticationPassword).toByteArray())
 
-  suspend fun sendRequest(request: JSONRPCRequest): Deferred<JSONRPCResponse> {
+  fun sendRequest(request: JSONRPCRequest): Deferred<JSONRPCResponse> {
     val deferred = CompletableDeferred<JSONRPCResponse>()
     val httpRequest = client.postAbs(endpointUrl)
       .putHeader("Content-Type", "application/json")
@@ -81,7 +81,6 @@
       if (response.failed()) {
         deferred.completeExceptionally(response.cause())
       } else {
-        println(response.result().bodyAsString())
         val jsonResponse = mapper.readValue(response.result().bodyAsString(), JSONRPCResponse::class.java)
         deferred.complete(jsonResponse)
       }
@@ -151,6 +150,28 @@
     }
   }
 
+  /**
+   * Gets block data by block number
+   * @param blockNumber the block number
+   * @param includeTransactions whether to include transactions detail
+   * @return the whole block information as a JSON document.
+   */
+  suspend fun getBlockByBlockNumber(blockNumber: Int, includeTransactions: Boolean): Map<*, *> {
+    val body = JSONRPCRequest(
+      StringOrLong(nextId()),
+      "eth_getBlockByNumber",
+      arrayOf(blockNumber, includeTransactions)
+    )
+    val jsonResponse = sendRequest(body).await()
+    val err = jsonResponse.error
+    if (err != null) {
+      val errorMessage = "Code ${err.code}: ${err.message}"
+      throw ClientRequestException(errorMessage)
+    } else {
+      return jsonResponse.result as Map<*, *>
+    }
+  }
+
   override fun close() {
     client.close()
   }
diff --git a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt
index 8c54b1e..de81773 100644
--- a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt
+++ b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt
@@ -16,7 +16,6 @@
  */
 package org.apache.tuweni.jsonrpc
 
-import io.vertx.core.Handler
 import io.vertx.core.Vertx
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.asCoroutineDispatcher
@@ -27,7 +26,6 @@
 import org.apache.tuweni.eth.Address
 import org.apache.tuweni.eth.JSONRPCRequest
 import org.apache.tuweni.eth.JSONRPCResponse
-import org.apache.tuweni.eth.StringOrLong
 import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.junit.BouncyCastleExtension
 import org.apache.tuweni.junit.VertxExtension
@@ -50,7 +48,7 @@
 class JSONRPCClientTest {
 
   companion object {
-    val handler = AtomicReference<Handler<JSONRPCRequest>>()
+    val handler = AtomicReference<(JSONRPCRequest) -> JSONRPCResponse>()
     var server: JSONRPCServer? = null
 
     @JvmStatic
@@ -64,8 +62,7 @@
         vertx,
         port = 0,
         methodHandler = {
-          handler.get().handle(it)
-          JSONRPCResponse(StringOrLong(3), "")
+          handler.get()(it)
         },
         coroutineContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
       )
@@ -119,4 +116,21 @@
       }
     }
   }
+
+  @Test
+  fun testGetBlockByNumber(@VertxInstance vertx: Vertx) {
+    JSONRPCClient(vertx, "http://localhost:" + server!!.port()).use {
+      val sent = CompletableDeferred<Any>()
+      handler.set { request ->
+        sent.complete(request.params.get(0))
+        JSONRPCResponse(request.id, mapOf(Pair("foo", "bar")))
+      }
+
+      runBlocking {
+        val block = it.getBlockByBlockNumber(32, true)
+        assertEquals(block["foo"], "bar")
+        sent.await()
+      }
+    }
+  }
 }