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 3da4a7ac Make JSONRPC use long numbers to match usage Make JSONRPC requests/responses support string ids instead of numbers new a33dcdf7 Merge pull request #414 from atoulme/jsonrpc-proxy 3da4a7ac is described below commit 3da4a7ac40fd92c7c59fd1519656c8d955cc8b61 Author: Antoine Toulme <anto...@lunar-ocean.com> AuthorDate: Thu Jun 16 20:16:37 2022 -0700 Make JSONRPC use long numbers to match usage Make JSONRPC requests/responses support string ids instead of numbers --- .../java/org/apache/tuweni/eth/EthJsonModule.java | 36 +++++++++ .../java/org/apache/tuweni/eth/StringOrLong.java | 57 +++++++++++++ .../kotlin/org/apache/tuweni/eth/JSONRPCRequest.kt | 10 ++- .../org/apache/tuweni/eth/JSONRPCResponse.kt | 18 ++--- .../java/org/apache/tuweni/eth/JsonRpcTest.java | 42 ++++++++++ .../org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt | 5 +- jsonrpc-app/src/main/resources/logback.xml | 4 + .../org/apache/tuweni/jsonrpc/JSONRPCClient.kt | 25 ++++-- .../org/apache/tuweni/jsonrpc/JSONRPCServer.kt | 2 + .../tuweni/jsonrpc/methods/MethodsHandler.kt | 20 ++++- .../org/apache/tuweni/jsonrpc/methods/Web3.kt | 5 +- .../org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt | 5 +- .../org/apache/tuweni/jsonrpc/JSONRPCServerTest.kt | 5 +- .../tuweni/jsonrpc/methods/MethodsHandlerTest.kt | 94 +++++++++++++--------- .../org/apache/tuweni/jsonrpc/methods/NetTest.kt | 7 +- .../org/apache/tuweni/stratum/StratumServerApp.kt | 9 ++- 16 files changed, 272 insertions(+), 72 deletions(-) diff --git a/eth/src/main/java/org/apache/tuweni/eth/EthJsonModule.java b/eth/src/main/java/org/apache/tuweni/eth/EthJsonModule.java index e701e9f8..476beb39 100644 --- a/eth/src/main/java/org/apache/tuweni/eth/EthJsonModule.java +++ b/eth/src/main/java/org/apache/tuweni/eth/EthJsonModule.java @@ -24,6 +24,7 @@ import java.time.Instant; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.KeyDeserializer; import com.fasterxml.jackson.databind.SerializerProvider; @@ -261,6 +262,39 @@ public class EthJsonModule extends SimpleModule { } } + static class StringOrLongDeserializer extends StdDeserializer<StringOrLong> { + + public StringOrLongDeserializer() { + super(StringOrLong.class); + } + + @Override + public StringOrLong deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, + JsonProcessingException { + if (p.currentToken().isNumeric()) { + return new StringOrLong(p.getLongValue()); + } else { + return new StringOrLong(p.getValueAsString()); + } + } + } + + static class StringOrLongSerializer extends StdSerializer<StringOrLong> { + + public StringOrLongSerializer() { + super(StringOrLong.class); + } + + @Override + public void serialize(StringOrLong value, JsonGenerator gen, SerializerProvider provider) throws IOException { + if (value.getValueAsString() == null) { + gen.writeNumber(value.getValueAsLong()); + } else { + gen.writeString(value.getValueAsString()); + } + } + } + public EthJsonModule() { addSerializer(Hash.class, new HashSerializer()); addDeserializer(Hash.class, new HashDeserializer()); @@ -281,5 +315,7 @@ public class EthJsonModule extends SimpleModule { addDeserializer(Bytes.class, new BytesDeserializer()); addDeserializer(Bytes32.class, new Bytes32Deserializer()); addSerializer(SECP256K1.PublicKey.class, new PublicKeySerializer()); + addSerializer(StringOrLong.class, new StringOrLongSerializer()); + addDeserializer(StringOrLong.class, new StringOrLongDeserializer()); } } diff --git a/eth/src/main/java/org/apache/tuweni/eth/StringOrLong.java b/eth/src/main/java/org/apache/tuweni/eth/StringOrLong.java new file mode 100644 index 00000000..43d456b9 --- /dev/null +++ b/eth/src/main/java/org/apache/tuweni/eth/StringOrLong.java @@ -0,0 +1,57 @@ +/* + * 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.eth; + +import java.util.Objects; + +/** + * Class representing the identifier of a JSON-RPC request or response. + * + * The identifier can be a string or a number, but not both at the same time. + */ +public class StringOrLong { + + private Long valueAsLong; + private String valueAsString; + + public StringOrLong(String value) { + this.valueAsString = value; + } + + public StringOrLong(Long value) { + this.valueAsLong = value; + } + + public String getValueAsString() { + return valueAsString; + } + + public Long getValueAsLong() { + return valueAsLong; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof StringOrLong)) + return false; + StringOrLong that = (StringOrLong) o; + return Objects.equals(valueAsLong, that.valueAsLong) || Objects.equals(valueAsString, that.valueAsString); + } + + @Override + public int hashCode() { + return Objects.hash(valueAsLong, valueAsString); + } +} diff --git a/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCRequest.kt b/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCRequest.kt index 20d5804b..20694907 100644 --- a/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCRequest.kt +++ b/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCRequest.kt @@ -19,14 +19,16 @@ package org.apache.tuweni.eth import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonProperty +data class Id(val idAsString: String?, val idAsLong: Long?) + /** * JSONRPCRequest represents a JSON-RPC request to a JSON-RPC service. */ @JsonIgnoreProperties(ignoreUnknown = true) -data class JSONRPCRequest( - @JsonProperty("id") val id: Int, +data class JSONRPCRequest constructor( + @JsonProperty("id") val id: StringOrLong, @JsonProperty("method") val method: String, - @JsonProperty("params") val params: Array<String>, + @JsonProperty("params") val params: Array<Any>, @JsonProperty("jsonrpc") val jsonrpc: String = "2.0" ) { @@ -43,7 +45,7 @@ data class JSONRPCRequest( */ fun deserialize(serialized: String): JSONRPCRequest { val segments = serialized.split("|") - return JSONRPCRequest(id = 0, method = segments[0], params = segments[1].split(",").toTypedArray()) + return JSONRPCRequest(id = StringOrLong(0), method = segments[0], params = segments[1].split(",").toTypedArray()) } } diff --git a/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCResponse.kt b/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCResponse.kt index 203587e2..b94ba922 100644 --- a/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCResponse.kt +++ b/eth/src/main/kotlin/org/apache/tuweni/eth/JSONRPCResponse.kt @@ -20,14 +20,14 @@ import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonProperty @JsonInclude(JsonInclude.Include.NON_NULL) -data class JSONRPCResponse(@JsonProperty("id") val id: Int, @JsonProperty("result") val result: Any? = null, @JsonProperty("error") val error: JSONRPCError? = null, @JsonProperty("jsonrpc") val jsonrpc: String = "2.0") +data class JSONRPCResponse(@JsonProperty("id") val id: StringOrLong, @JsonProperty("result") val result: Any? = null, @JsonProperty("error") val error: JSONRPCError? = null, @JsonProperty("jsonrpc") val jsonrpc: String = "2.0") -data class JSONRPCError(@JsonProperty("code") val code: Int, @JsonProperty("message") val message: String) +data class JSONRPCError(@JsonProperty("code") val code: Long, @JsonProperty("message") val message: String) -val parseError = JSONRPCResponse(id = 0, error = JSONRPCError(-32700, "Parse error")) -val invalidRequest = JSONRPCResponse(id = 0, error = JSONRPCError(-32600, "Invalid Request")) -val methodNotFound = JSONRPCResponse(id = 0, error = JSONRPCError(-32601, "Method not found")) -val invalidParams = JSONRPCResponse(id = 0, error = JSONRPCError(-32602, "Invalid params")) -val internalError = JSONRPCResponse(id = 0, error = JSONRPCError(-32603, "Internal error")) -val tooManyRequests = JSONRPCResponse(id = 0, error = JSONRPCError(code = -32000, message = "Too many requests")) -val methodNotEnabled = JSONRPCResponse(id = 0, error = JSONRPCError(-32604, "Method not enabled")) +val parseError = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32700, "Parse error")) +val invalidRequest = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32600, "Invalid Request")) +val methodNotFound = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32601, "Method not found")) +val invalidParams = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32602, "Invalid params")) +val internalError = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32603, "Internal error")) +val tooManyRequests = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(code = -32000, message = "Too many requests")) +val methodNotEnabled = JSONRPCResponse(id = StringOrLong(0), error = JSONRPCError(-32604, "Method not enabled")) diff --git a/eth/src/test/java/org/apache/tuweni/eth/JsonRpcTest.java b/eth/src/test/java/org/apache/tuweni/eth/JsonRpcTest.java new file mode 100644 index 00000000..744dab51 --- /dev/null +++ b/eth/src/test/java/org/apache/tuweni/eth/JsonRpcTest.java @@ -0,0 +1,42 @@ +/* + * 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.eth; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +public class JsonRpcTest { + + private static final ObjectMapper mapper = new ObjectMapper(); + static { + mapper.registerModule(new EthJsonModule()); + } + + @Test + void testJsonRpcRequestRoundtrip() throws Exception { + JSONRPCRequest req = new JSONRPCRequest(new StringOrLong("3"), "foo_method", new Object[] {"foo", "bar"}, "2.0"); + String value = mapper.writeValueAsString(req); + JSONRPCRequest req2 = mapper.readValue(value, JSONRPCRequest.class); + assertEquals(req, req2); + } + + @Test + void testJsonRpcResponseRoundtrip() throws Exception { + JSONRPCResponse resp = new JSONRPCResponse(new StringOrLong("3"), "result", null, "2.0"); + String value = mapper.writeValueAsString(resp); + JSONRPCResponse resp2 = mapper.readValue(value, JSONRPCResponse.class); + assertEquals(resp, resp2); + } +} diff --git a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt index d729db13..afb8ea66 100644 --- a/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt +++ b/jsonrpc-app/src/main/kotlin/org/apache/tuweni/jsonrpc/app/JSONRPCApp.kt @@ -31,6 +31,7 @@ import org.apache.tuweni.eth.JSONRPCResponse import org.apache.tuweni.jsonrpc.JSONRPCClient import org.apache.tuweni.jsonrpc.JSONRPCServer import org.apache.tuweni.jsonrpc.methods.CachingHandler +import org.apache.tuweni.jsonrpc.methods.LoggingHandler import org.apache.tuweni.jsonrpc.methods.MeteredHandler import org.apache.tuweni.jsonrpc.methods.MethodAllowListHandler import org.apache.tuweni.jsonrpc.methods.ThrottlingHandler @@ -150,7 +151,9 @@ class JSONRPCApplication( val throttlingHandler = ThrottlingHandler(config.maxConcurrentRequests(), nextHandler) - val handler = MeteredHandler(successCounter, failureCounter, throttlingHandler::handleRequest) + val loggingHandler = LoggingHandler(throttlingHandler::handleRequest, "jsonrpclog") + + val handler = MeteredHandler(successCounter, failureCounter, loggingHandler::handleRequest) val server = JSONRPCServer( vertx, config.port(), config.networkInterface(), diff --git a/jsonrpc-app/src/main/resources/logback.xml b/jsonrpc-app/src/main/resources/logback.xml index cb63fd52..89f01051 100644 --- a/jsonrpc-app/src/main/resources/logback.xml +++ b/jsonrpc-app/src/main/resources/logback.xml @@ -21,6 +21,10 @@ </encoder> </appender> + <logger level="info" name="jsonrpclog" additivity="false"> + <appender-ref ref="STDOUT" /> + </logger> + <root level="info"> <appender-ref ref="STDOUT" /> </root> 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 147bd8ca..b5318ef1 100644 --- a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt +++ b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClient.kt @@ -27,13 +27,15 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import org.apache.tuweni.eth.Address +import org.apache.tuweni.eth.EthJsonModule 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.units.bigints.UInt256 import java.io.Closeable import java.util.Base64 -import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong import kotlin.coroutines.CoroutineContext val mapper = ObjectMapper() @@ -51,7 +53,13 @@ class JSONRPCClient( override val coroutineContext: CoroutineContext = vertx.dispatcher(), ) : Closeable, CoroutineScope { - val requestCounter = AtomicInteger(1) + companion object { + private val mapper = ObjectMapper() + init { + mapper.registerModule(EthJsonModule()) + } + } + val requestCounter = AtomicLong(1) val client = WebClient.create( vertx, WebClientOptions().setUserAgent(userAgent).setTryUseCompression(true) @@ -73,7 +81,8 @@ class JSONRPCClient( if (response.failed()) { deferred.completeExceptionally(response.cause()) } else { - val jsonResponse = response.result().bodyAsJson(JSONRPCResponse::class.java) + println(response.result().bodyAsString()) + val jsonResponse = mapper.readValue(response.result().bodyAsString(), JSONRPCResponse::class.java) deferred.complete(jsonResponse) } } @@ -89,7 +98,7 @@ class JSONRPCClient( * @throws ConnectException if it cannot dial the remote client */ suspend fun sendRawTransaction(tx: Transaction): String { - val body = JSONRPCRequest(nextId(), "eth_sendRawTransaction", arrayOf(tx.toBytes().toHexString())) + val body = JSONRPCRequest(StringOrLong(nextId()), "eth_sendRawTransaction", arrayOf(tx.toBytes().toHexString())) val jsonResponse = sendRequest(body).await() val err = jsonResponse.error if (err != null) { @@ -108,7 +117,7 @@ class JSONRPCClient( * @throws ConnectException if it cannot dial the remote client */ suspend fun getBalance_latest(address: Address): UInt256 { - val body = JSONRPCRequest(nextId(), "eth_getBalance", arrayOf(address.toHexString(), "latest")) + val body = JSONRPCRequest(StringOrLong(nextId()), "eth_getBalance", arrayOf(address.toHexString(), "latest")) val jsonResponse = sendRequest(body).await() val err = jsonResponse.error if (err != null) { @@ -127,7 +136,7 @@ class JSONRPCClient( * @throws ConnectException if it cannot dial the remote client */ suspend fun getTransactionCount_latest(address: Address): UInt256 { - val body = JSONRPCRequest(nextId(), "eth_getTransactionCount", arrayOf(address.toHexString(), "latest")) + val body = JSONRPCRequest(StringOrLong(nextId()), "eth_getTransactionCount", arrayOf(address.toHexString(), "latest")) val jsonResponse = sendRequest(body).await() val err = jsonResponse.error if (err != null) { @@ -142,9 +151,9 @@ class JSONRPCClient( client.close() } - private fun nextId(): Int { + private fun nextId(): Long { val next = requestCounter.incrementAndGet() - if (next == Int.MAX_VALUE) { + if (next == Long.MAX_VALUE) { requestCounter.set(1) } return next diff --git a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServer.kt b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServer.kt index 561c6e7b..4bb638c0 100644 --- a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServer.kt +++ b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServer.kt @@ -48,6 +48,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch +import org.apache.tuweni.eth.EthJsonModule import org.apache.tuweni.eth.JSONRPCRequest import org.apache.tuweni.eth.JSONRPCResponse import org.apache.tuweni.eth.internalError @@ -79,6 +80,7 @@ class JSONRPCServer( val mapper = ObjectMapper() init { + mapper.registerModule(EthJsonModule()) mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true) } } diff --git a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt index 2feab0d5..2206f1aa 100644 --- a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt +++ b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandler.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.apache.tuweni.eth.JSONRPCRequest import org.apache.tuweni.eth.JSONRPCResponse +import org.apache.tuweni.eth.StringOrLong import org.apache.tuweni.eth.methodNotEnabled import org.apache.tuweni.eth.methodNotFound import org.apache.tuweni.eth.tooManyRequests @@ -167,9 +168,9 @@ class CachingPollingHandler( private fun poll() { launch { try { - var id = 1337 + var id = 1337L for (cachedRequest in cachedRequests) { - val newResponse = delegateHandler(cachedRequest.copy(id = id)) + val newResponse = delegateHandler(cachedRequest.copy(id = StringOrLong(id))) id++ if (newResponse.error == null) { cacheStore.put(cachedRequest, newResponse) @@ -209,6 +210,21 @@ class CachingPollingHandler( } } +class LoggingHandler( + private val delegateHandler: suspend (JSONRPCRequest) -> JSONRPCResponse, + loggerName: String, +) { + + private val logger = LoggerFactory.getLogger(loggerName) + + suspend fun handleRequest(request: JSONRPCRequest): JSONRPCResponse { + logger.info(request.toString()) + val response = delegateHandler.invoke(request) + logger.info(response.toString()) + return response + } +} + class ConstantStringResult(val result: String) { suspend fun handle(request: JSONRPCRequest): JSONRPCResponse { return JSONRPCResponse(id = request.id, result = result) diff --git a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/Web3.kt b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/Web3.kt index aa75ec72..34a920e4 100644 --- a/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/Web3.kt +++ b/jsonrpc/src/main/kotlin/org/apache/tuweni/jsonrpc/methods/Web3.kt @@ -31,8 +31,11 @@ suspend fun sha3(request: JSONRPCRequest): JSONRPCResponse { if (request.params.size != 1) { return invalidParams.copy(id = request.id) } + if (!(request.params[0] is String)) { + return invalidParams.copy(id = request.id) + } try { - val input = Bytes.fromHexString(request.params[0]) + val input = Bytes.fromHexString(request.params[0] as String) return JSONRPCResponse(id = request.id, result = Hash.hash(input).toHexString()) } catch (e: IllegalArgumentException) { return invalidParams.copy(id = request.id) 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 a267f59b..a9e9868f 100644 --- a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt +++ b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCClientTest.kt @@ -27,6 +27,7 @@ import org.apache.tuweni.crypto.SECP256K1 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 @@ -60,7 +61,7 @@ class JSONRPCClientTest { vertx, port = 0, methodHandler = { handler.get().handle(it) - JSONRPCResponse(3, "") + JSONRPCResponse(StringOrLong(3), "") }, coroutineContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() ) @@ -91,7 +92,7 @@ class JSONRPCClientTest { ) val sent = CompletableDeferred<String>() handler.set { request -> - sent.complete(request.params.get(0)) + sent.complete(request.params.get(0) as String) JSONRPCResponse(request.id, "") } diff --git a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServerTest.kt b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServerTest.kt index 76f49391..4e497d82 100644 --- a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServerTest.kt +++ b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/JSONRPCServerTest.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.runBlocking import org.apache.tuweni.concurrent.coroutines.await import org.apache.tuweni.eth.JSONRPCResponse +import org.apache.tuweni.eth.StringOrLong import org.apache.tuweni.io.Base64 import org.apache.tuweni.junit.VertxExtension import org.apache.tuweni.junit.VertxInstance @@ -53,7 +54,7 @@ class JSONRPCServerTest { val server = JSONRPCServer( vertx, port = 0, methodHandler = { - JSONRPCResponse(3, "") + JSONRPCResponse(StringOrLong(3), "") }, coroutineContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() ) @@ -73,7 +74,7 @@ class JSONRPCServerTest { val server = JSONRPCServer( vertx, port = 0, methodHandler = { - JSONRPCResponse(3, "") + JSONRPCResponse(StringOrLong(3), "") }, useBasicAuthentication = true, basicAuthenticationPassword = "pass", diff --git a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt index ef881027..1e1f92b8 100644 --- a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt +++ b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/MethodsHandlerTest.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.runBlocking import org.apache.tuweni.eth.JSONRPCError import org.apache.tuweni.eth.JSONRPCRequest import org.apache.tuweni.eth.JSONRPCResponse +import org.apache.tuweni.eth.StringOrLong import org.apache.tuweni.eth.methodNotFound import org.apache.tuweni.junit.BouncyCastleExtension import org.apache.tuweni.kv.MapKeyValueStore @@ -42,13 +43,22 @@ class MethodsHandlerTest { @Test fun testMissingMethod() = runBlocking { val methodsRouter = MethodsRouter(emptyMap()) - assertEquals(methodNotFound, methodsRouter.handleRequest(JSONRPCRequest(1, "web3_sha3", arrayOf("0xdeadbeef")))) + assertEquals( + methodNotFound, + methodsRouter.handleRequest(JSONRPCRequest(StringOrLong(1), "web3_sha3", arrayOf("0xdeadbeef"))) + ) } @Test fun testRouteMethod() = runBlocking { val methodsRouter = MethodsRouter(mapOf(Pair("web3_sha3", ::sha3))) - assertEquals(JSONRPCResponse(1, result = "0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1"), methodsRouter.handleRequest(JSONRPCRequest(1, "web3_sha3", arrayOf("0xdeadbeef")))) + assertEquals( + JSONRPCResponse( + StringOrLong(1), + result = "0xd4fd4e189132273036449fc9e11198c739161b4c0116a9a2dccdfa1c492006f1" + ), + methodsRouter.handleRequest(JSONRPCRequest(StringOrLong(1), "web3_sha3", arrayOf("0xdeadbeef"))) + ) } @Test @@ -66,9 +76,9 @@ class MethodsHandlerTest { val successCounter = meter.longCounterBuilder("success").build() val failCounter = meter.longCounterBuilder("fail").build() val meteredHandler = MeteredHandler(successCounter, failCounter) { - JSONRPCResponse(1) + JSONRPCResponse(StringOrLong(1)) } - meteredHandler.handleRequest(JSONRPCRequest(1, "foo", emptyArray())) + meteredHandler.handleRequest(JSONRPCRequest(StringOrLong(1), "foo", emptyArray())) Thread.sleep(1200) var metricValue = 0L for (metric in exporter.finishedMetricItems) { @@ -94,9 +104,9 @@ class MethodsHandlerTest { val successCounter = meter.longCounterBuilder("success").build() val failCounter = meter.longCounterBuilder("fail").build() val meteredHandler = MeteredHandler(successCounter, failCounter) { - JSONRPCResponse(1, error = JSONRPCError(123, "foo")) + JSONRPCResponse(StringOrLong(1), error = JSONRPCError(123, "foo")) } - meteredHandler.handleRequest(JSONRPCRequest(1, "foo", emptyArray())) + meteredHandler.handleRequest(JSONRPCRequest(StringOrLong(1), "foo", emptyArray())) Thread.sleep(1200) var metricValue = 0L for (metric in exporter.finishedMetricItems) { @@ -112,15 +122,15 @@ class MethodAllowListHandlerTest { @Test fun testAllowedMethod() = runBlocking { - val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(1, "foo") } - val resp = filter.handleRequest(JSONRPCRequest(1, "eth_client", emptyArray())) + val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(StringOrLong(1), "foo") } + val resp = filter.handleRequest(JSONRPCRequest(StringOrLong(1), "eth_client", emptyArray())) assertNull(resp.error) } @Test fun testForbiddenMethod() = runBlocking { - val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(1, "foo") } - val resp = filter.handleRequest(JSONRPCRequest(1, "foo_client", emptyArray())) + val filter = MethodAllowListHandler(listOf("eth_")) { JSONRPCResponse(StringOrLong(1), "foo") } + val resp = filter.handleRequest(JSONRPCRequest(StringOrLong(1), "foo_client", emptyArray())) assertNotNull(resp.error) val respContents = resp.error as JSONRPCError assertEquals(-32604, respContents.code) @@ -135,34 +145,34 @@ class ThrottlingHandlerTest { val handler = ThrottlingHandler(4) { runBlocking { delay(500) - JSONRPCResponse(id = 1) + JSONRPCResponse(id = StringOrLong(1)) } } async { - val response = handler.handleRequest(JSONRPCRequest(2, "foo", arrayOf())) - assertEquals(1, response.id) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(2), "foo", arrayOf())) + assertEquals(StringOrLong(1), response.id) } async { - val response = handler.handleRequest(JSONRPCRequest(3, "foo", arrayOf())) - assertEquals(1, response.id) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(3), "foo", arrayOf())) + assertEquals(StringOrLong(1), response.id) } async { - val response = handler.handleRequest(JSONRPCRequest(4, "foo", arrayOf())) - assertEquals(1, response.id) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(4), "foo", arrayOf())) + assertEquals(StringOrLong(1), response.id) } async { - val response = handler.handleRequest(JSONRPCRequest(5, "foo", arrayOf())) - assertEquals(1, response.id) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(5), "foo", arrayOf())) + assertEquals(StringOrLong(1), response.id) } async { delay(200) - val response = handler.handleRequest(JSONRPCRequest(6, "foo", arrayOf())) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(6), "foo", arrayOf())) assertEquals(-32000, response.error?.code) } async { delay(1000) - val response = handler.handleRequest(JSONRPCRequest(7, "foo", arrayOf())) - assertEquals(1, response.id) + val response = handler.handleRequest(JSONRPCRequest(StringOrLong(7), "foo", arrayOf())) + assertEquals(StringOrLong(1), response.id) } } } @@ -175,21 +185,26 @@ class CachingHandlerTest { val kv = MapKeyValueStore.open(map) val meterSdk = SdkMeterProvider.builder().build() val meter = meterSdk.get("handler") - val handler = CachingHandler(listOf("foo"), kv, meter.longCounterBuilder("foo").build(), meter.longCounterBuilder("bar").build()) { + val handler = CachingHandler( + listOf("foo"), + kv, + meter.longCounterBuilder("foo").build(), + meter.longCounterBuilder("bar").build() + ) { if (it.params.isNotEmpty()) { - JSONRPCResponse(id = 1, error = JSONRPCError(1234, "")) + JSONRPCResponse(id = StringOrLong(1), error = JSONRPCError(1234, "")) } else { - JSONRPCResponse(id = 1) + JSONRPCResponse(id = StringOrLong(1)) } } assertEquals(0, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf())) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "bar", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "bar", params = arrayOf())) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf())) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf("bleh"))) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf("bleh"))) assertEquals(1, map.size) } } @@ -202,22 +217,29 @@ class CachingPollingHandlerTest { val kv = MapKeyValueStore.open(map) val meterSdk = SdkMeterProvider.builder().build() val meter = meterSdk.get("handler") - val handler = CachingPollingHandler(listOf(JSONRPCRequest(1, "foo", arrayOf())), 1000, kv, meter.longCounterBuilder("foo").build(), meter.longCounterBuilder("bar").build()) { + val handler = CachingPollingHandler( + listOf(JSONRPCRequest(StringOrLong(1), "foo", arrayOf())), + 1000, + kv, + meter.longCounterBuilder("foo").build(), + meter.longCounterBuilder("bar").build() + ) { if (it.params.isNotEmpty()) { - JSONRPCResponse(id = 1, error = JSONRPCError(1234, "")) + JSONRPCResponse(id = StringOrLong(1), error = JSONRPCError(1234, "")) } else { - JSONRPCResponse(id = 1) + JSONRPCResponse(id = StringOrLong(1)) } } delay(500) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf())) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "bar", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "bar", params = arrayOf())) assertEquals(1, map.size) - handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf())) + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf())) assertEquals(1, map.size) - val errorResp = handler.handleRequest(JSONRPCRequest(id = 1, method = "foo", params = arrayOf("bleh"))) + val errorResp = + handler.handleRequest(JSONRPCRequest(id = StringOrLong(1), method = "foo", params = arrayOf("bleh"))) assertEquals(1, map.size) assertNotNull(errorResp.error) } diff --git a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/NetTest.kt b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/NetTest.kt index b9421ada..00d51a2a 100644 --- a/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/NetTest.kt +++ b/jsonrpc/src/test/kotlin/org/apache/tuweni/jsonrpc/methods/NetTest.kt @@ -18,6 +18,7 @@ package org.apache.tuweni.jsonrpc.methods import kotlinx.coroutines.runBlocking import org.apache.tuweni.eth.JSONRPCRequest +import org.apache.tuweni.eth.StringOrLong import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -26,8 +27,8 @@ class NetTest { @Test fun testNetCheck() = runBlocking { val net = registerNet("2", true, { 2 }) - assertEquals("2", net["net_version"]?.invoke(JSONRPCRequest(1, "", arrayOf()))?.result) - assertEquals(true, net["net_listening"]?.invoke(JSONRPCRequest(1, "", arrayOf()))?.result) - assertEquals("0x2", net["net_peerCount"]?.invoke(JSONRPCRequest(1, "", arrayOf()))?.result) + assertEquals("2", net["net_version"]?.invoke(JSONRPCRequest(StringOrLong(1), "", arrayOf()))?.result) + assertEquals(true, net["net_listening"]?.invoke(JSONRPCRequest(StringOrLong(1), "", arrayOf()))?.result) + assertEquals("0x2", net["net_peerCount"]?.invoke(JSONRPCRequest(StringOrLong(1), "", arrayOf()))?.result) } } diff --git a/stratum/proxy/src/main/kotlin/org/apache/tuweni/stratum/StratumServerApp.kt b/stratum/proxy/src/main/kotlin/org/apache/tuweni/stratum/StratumServerApp.kt index 8a548e5b..1d15fc3a 100644 --- a/stratum/proxy/src/main/kotlin/org/apache/tuweni/stratum/StratumServerApp.kt +++ b/stratum/proxy/src/main/kotlin/org/apache/tuweni/stratum/StratumServerApp.kt @@ -24,12 +24,13 @@ import kotlinx.coroutines.withContext import org.apache.tuweni.bytes.Bytes import org.apache.tuweni.bytes.Bytes32 import org.apache.tuweni.eth.JSONRPCRequest +import org.apache.tuweni.eth.StringOrLong import org.apache.tuweni.jsonrpc.JSONRPCClient import org.apache.tuweni.stratum.server.PoWInput import org.apache.tuweni.stratum.server.StratumServer import org.apache.tuweni.units.bigints.UInt256 import org.slf4j.LoggerFactory -import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicReference import kotlin.system.exitProcess @@ -44,7 +45,7 @@ fun main(args: Array<String>) { val client = JSONRPCClient(vertx, "http://localhost:8545", "") val port = args[0].toInt() - val idCounter = AtomicInteger(0) + val idCounter = AtomicLong(0) val seedReference = AtomicReference<Bytes32>() val server = StratumServer( vertx, port = port, networkInterface = "0.0.0.0", @@ -52,7 +53,7 @@ fun main(args: Array<String>) { logger.info("Got solution $solution") withContext(client.coroutineContext) { val req = JSONRPCRequest( - id = idCounter.incrementAndGet(), + id = StringOrLong(idCounter.incrementAndGet()), method = "eth_submitWork", params = arrayOf( Bytes.ofUnsignedLong(solution.nonce).toHexString(), @@ -79,7 +80,7 @@ fun main(args: Array<String>) { try { val response = client.sendRequest( JSONRPCRequest( - id = idCounter.incrementAndGet(), + id = StringOrLong(idCounter.incrementAndGet()), method = "eth_getWork", params = arrayOf() ) --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@tuweni.apache.org For additional commands, e-mail: commits-h...@tuweni.apache.org