From 54fd5481d69498f67fa9b118bedbb0c572beb5b7 Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Sun, 23 Aug 2020 21:31:50 -0400 Subject: [PATCH] problem: proxy doesn't support long ids fix: #48 --- gradle.properties | 2 +- .../emeraldpay/dshackle/proxy/ProxyServer.kt | 6 ++-- .../dshackle/quorum/QuorumRpcReader.kt | 2 +- .../io/emeraldpay/dshackle/rpc/NativeCall.kt | 2 +- .../upstream/rpcclient/JsonRpcError.kt | 2 +- .../upstream/rpcclient/JsonRpcException.kt | 6 ++-- .../upstream/rpcclient/JsonRpcHttpClient.kt | 4 +-- .../upstream/rpcclient/JsonRpcResponse.kt | 34 ++++++++++--------- .../dshackle/proxy/ProxyServerSpec.groovy | 2 +- .../dshackle/proxy/ReadRpcJsonSpec.groovy | 10 +++--- .../rpcclient/JsonRpcResponseSpec.groovy | 8 ++--- .../trial/proxy/MetamaskCallSpec.groovy | 18 ++++++++++ 12 files changed, 57 insertions(+), 39 deletions(-) create mode 100644 testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/MetamaskCallSpec.groovy diff --git a/gradle.properties b/gradle.properties index 6d1250f5..a5ce8b53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ springSecurtyVersion=5.3.2.RELEASE reactorVersion=3.3.5.RELEASE nettyVersion=4.1.49.Final # Our Libs -etherjarVersion=0.10.1 +etherjarVersion=0.10.2 # Testing spockVersion=1.3-groovy-2.5 diff --git a/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt b/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt index 013cd2b5..a78ab197 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/proxy/ProxyServer.kt @@ -26,7 +26,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.infinitape.etherjar.rpc.RpcException import io.netty.buffer.ByteBuf import io.netty.buffer.Unpooled -import io.netty.handler.ssl.SslContextBuilder import org.reactivestreams.Publisher import org.slf4j.LoggerFactory import org.springframework.http.HttpHeaders @@ -37,7 +36,6 @@ import reactor.netty.http.server.HttpServer import reactor.netty.http.server.HttpServerRequest import reactor.netty.http.server.HttpServerResponse import reactor.netty.http.server.HttpServerRoutes -import java.io.File import java.util.function.BiFunction /** @@ -100,8 +98,8 @@ class ProxyServer( .flatMapMany { call -> execute(chain, call) } .onErrorResume(RpcException::class.java) { err -> val id = err.details?.let { - if (it is JsonRpcResponse.Id) it else JsonRpcResponse.IntId(-1) - } ?: JsonRpcResponse.IntId(-1) + if (it is JsonRpcResponse.Id) it else JsonRpcResponse.NumberId(-1) + } ?: JsonRpcResponse.NumberId(-1) val json = JsonRpcResponse.error(err.code, err.rpcMessage, id) Mono.just(Global.objectMapper.writeValueAsString(json)) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/quorum/QuorumRpcReader.kt b/src/main/kotlin/io/emeraldpay/dshackle/quorum/QuorumRpcReader.kt index 978824a9..02432ae7 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/quorum/QuorumRpcReader.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/quorum/QuorumRpcReader.kt @@ -58,7 +58,7 @@ class QuorumRpcReader( val defaultResult: Mono = Mono.just(quorum).flatMap { q -> if (q.isFailed()) { Mono.error( - q.getError()?.asException(JsonRpcResponse.IntId(1)) + q.getError()?.asException(JsonRpcResponse.NumberId(1)) ?: RpcException(-32000, "Unknown Upstream error") ) } else { diff --git a/src/main/kotlin/io/emeraldpay/dshackle/rpc/NativeCall.kt b/src/main/kotlin/io/emeraldpay/dshackle/rpc/NativeCall.kt index eaf3448a..d5ceb4ab 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/rpc/NativeCall.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/rpc/NativeCall.kt @@ -211,7 +211,7 @@ open class NativeCall( companion object { fun from(t: Throwable): CallError { return when (t) { - is JsonRpcException -> CallError(t.id.asInt(), t.error.message, t.error) + is JsonRpcException -> CallError(t.id.asNumber().toInt(), t.error.message, t.error) is RpcException -> CallError(t.code, t.rpcMessage, null) is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null) else -> CallError(1, t.message ?: "Upstream Error", null) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcError.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcError.kt index 28d155c0..5ebcf080 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcError.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcError.kt @@ -31,7 +31,7 @@ class JsonRpcError(val code: Int, val message: String, val details: Any?) { } fun asException(id: JsonRpcResponse.Id?): JsonRpcException { - return JsonRpcException(id ?: JsonRpcResponse.IntId(-1), this) + return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this) } override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcException.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcException.kt index a68e53d4..2b0b256e 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcException.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcException.kt @@ -22,7 +22,7 @@ class JsonRpcException( val error: JsonRpcError ) : Exception(error.message) { - constructor(id: Int, message: String) : this(JsonRpcResponse.IntId(id), JsonRpcError(-32005, message)) + constructor(id: Int, message: String) : this(JsonRpcResponse.NumberId(id), JsonRpcError(-32005, message)) companion object { fun from(err: RpcException): JsonRpcException { @@ -30,9 +30,9 @@ class JsonRpcException( if (it is JsonRpcResponse.Id) { it } else { - JsonRpcResponse.IntId(-3) + JsonRpcResponse.NumberId(-3) } - } ?: JsonRpcResponse.IntId(-4) + } ?: JsonRpcResponse.NumberId(-4) return JsonRpcException( id, JsonRpcError.from(err) ) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcHttpClient.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcHttpClient.kt index ce603fe6..020672d4 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcHttpClient.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcHttpClient.kt @@ -88,7 +88,7 @@ class JsonRpcHttpClient( return response.response { header, bytes -> if (header.status().code() != 200) { - Mono.error(JsonRpcException(JsonRpcResponse.IntId(-2), + Mono.error(JsonRpcException(JsonRpcResponse.NumberId(-2), JsonRpcError(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "HTTP Code: ${header.status().code()}")) ) } else { @@ -105,7 +105,7 @@ class JsonRpcHttpClient( .onErrorResume { t -> val err = when (t) { is RpcException -> JsonRpcResponse.error(t.code, t.rpcMessage) - is JsonRpcException -> JsonRpcResponse.error(t.error, JsonRpcResponse.IntId(1)) + is JsonRpcException -> JsonRpcResponse.error(t.error, JsonRpcResponse.NumberId(1)) else -> JsonRpcResponse.error(1, t.message ?: t.javaClass.name) } Mono.just(err) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponse.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponse.kt index 7c6012a2..03ed29e8 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponse.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponse.kt @@ -26,7 +26,7 @@ class JsonRpcResponse( val id: Id ) { - constructor(result: ByteArray?, error: JsonRpcError?) : this(result, error, IntId(0)) + constructor(result: ByteArray?, error: JsonRpcError?) : this(result, error, NumberId(0)) companion object { private val NULL_VALUE = "null".toByteArray() @@ -129,29 +129,31 @@ class JsonRpcResponse( * JSON RPC wrapper. Makes sure that the id is either Int or String */ interface Id { - fun asInt(): Int + fun asNumber(): Long fun asString(): String - fun isInt(): Boolean + fun isNumber(): Boolean companion object { @JvmStatic fun from(id: Any): Id { if (id is Int) { - return IntId(id) + return NumberId(id) } if (id is Number) { - return IntId(id.toInt()) + return NumberId(id.toLong()) } if (id is String) { return StringId(id) } - throw IllegalArgumentException("Id must be Int or String") + throw IllegalArgumentException("Id must be Number or String") } } } - class IntId(val id: Int) : Id { - override fun asInt(): Int { + class NumberId(val id: Long) : Id { + constructor(id: Int) : this(id.toLong()) + + override fun asNumber(): Long { return id } @@ -159,13 +161,13 @@ class JsonRpcResponse( throw IllegalStateException("Not string") } - override fun isInt(): Boolean { + override fun isNumber(): Boolean { return true } override fun equals(other: Any?): Boolean { if (this === other) return true - if (other !is IntId) return false + if (other !is NumberId) return false if (id != other.id) return false @@ -173,20 +175,20 @@ class JsonRpcResponse( } override fun hashCode(): Int { - return id + return id.hashCode() } } class StringId(val id: String) : Id { - override fun asInt(): Int { - throw IllegalStateException("Not int") + override fun asNumber(): Long { + throw IllegalStateException("Not a number") } override fun asString(): String { return id } - override fun isInt(): Boolean { + override fun isNumber(): Boolean { return false } @@ -209,8 +211,8 @@ class JsonRpcResponse( override fun serialize(value: JsonRpcResponse, gen: JsonGenerator, serializers: SerializerProvider) { gen.writeStartObject() gen.writeStringField("jsonrpc", "2.0") - if (value.id.isInt()) { - gen.writeNumberField("id", value.id.asInt()) + if (value.id.isNumber()) { + gen.writeNumberField("id", value.id.asNumber()) } else { gen.writeStringField("id", value.id.asString()) } diff --git a/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy index 4b09c3b4..76ecf537 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/proxy/ProxyServerSpec.groovy @@ -72,7 +72,7 @@ class ProxyServerSpec extends Specification { def "Return error on invalid request"() { setup: ReadRpcJson read = Mock(ReadRpcJson) { - 1 * apply(_) >> { throw new RpcException(-32123, "test", new JsonRpcResponse.IntId(4)) } + 1 * apply(_) >> { throw new RpcException(-32123, "test", new JsonRpcResponse.NumberId(4)) } } def server = new ProxyServer( Stub(ProxyConfig), diff --git a/src/test/groovy/io/emeraldpay/dshackle/proxy/ReadRpcJsonSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/proxy/ReadRpcJsonSpec.groovy index 6b00db8e..0ea8bb75 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/proxy/ReadRpcJsonSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/proxy/ReadRpcJsonSpec.groovy @@ -16,7 +16,7 @@ */ package io.emeraldpay.dshackle.proxy -import io.emeraldpay.dshackle.test.TestingCommons + import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.infinitape.etherjar.rpc.RpcException import spock.lang.Specification @@ -192,7 +192,7 @@ class ReadRpcJsonSpec extends Specification { def t = thrown(RpcException) t.code == -32600 t.rpcMessage.toLowerCase() == "jsonrpc version is not set" - t.details == new JsonRpcResponse.IntId(2) + t.details == new JsonRpcResponse.NumberId(2) } def "Error if jsonrpc version is invalid"() { @@ -202,7 +202,7 @@ class ReadRpcJsonSpec extends Specification { def t = thrown(RpcException) t.code == -32600 t.rpcMessage.toLowerCase() == "unsupported json rpc version: 3.0" - t.details == new JsonRpcResponse.IntId(2) + t.details == new JsonRpcResponse.NumberId(2) } def "Error if method is not set"() { @@ -212,7 +212,7 @@ class ReadRpcJsonSpec extends Specification { def t = thrown(RpcException) t.code == -32600 t.rpcMessage.toLowerCase() == "method is not set" - t.details == new JsonRpcResponse.IntId(2) + t.details == new JsonRpcResponse.NumberId(2) } def "Error if params is not array"() { @@ -222,6 +222,6 @@ class ReadRpcJsonSpec extends Specification { def t = thrown(RpcException) t.code == -32600 t.rpcMessage.toLowerCase() == "params must be an array" - t.details == new JsonRpcResponse.IntId(2) + t.details == new JsonRpcResponse.NumberId(2) } } diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponseSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponseSpec.groovy index 05416079..a0ba66c3 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponseSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/rpcclient/JsonRpcResponseSpec.groovy @@ -63,7 +63,7 @@ class JsonRpcResponseSpec extends Specification { def "Serialize int id and null result"() { setup: - def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.IntId(1)) + def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1)) when: def act = objectMapper.writeValueAsString(json) then: @@ -72,7 +72,7 @@ class JsonRpcResponseSpec extends Specification { def "Serialize int id and string result"() { setup: - def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.IntId(10)) + def json = new JsonRpcResponse('"Hello World"'.bytes, null, new JsonRpcResponse.NumberId(10)) when: def act = objectMapper.writeValueAsString(json) then: @@ -81,7 +81,7 @@ class JsonRpcResponseSpec extends Specification { def "Serialize int id and object result"() { setup: - def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.IntId(101)) + def json = new JsonRpcResponse('{"foo": "Hello World", "bar": 1}'.bytes, null, new JsonRpcResponse.NumberId(101)) when: def act = objectMapper.writeValueAsString(json) then: @@ -90,7 +90,7 @@ class JsonRpcResponseSpec extends Specification { def "Serialize int id and error"() { setup: - def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.IntId(101)) + def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.NumberId(101)) when: def act = objectMapper.writeValueAsString(json) then: diff --git a/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/MetamaskCallSpec.groovy b/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/MetamaskCallSpec.groovy new file mode 100644 index 00000000..e0ac78d5 --- /dev/null +++ b/testing/trial/src/test/groovy/io/emeraldpay/dshackle/testing/trial/proxy/MetamaskCallSpec.groovy @@ -0,0 +1,18 @@ +package io.emeraldpay.dshackle.testing.trial.proxy + +import io.emeraldpay.dshackle.testing.trial.ProxyClient +import spock.lang.Specification + +class MetamaskCallSpec extends Specification { + + def client = ProxyClient.forPrefix("eth") + + def "use long id"() { + when: + def act = client.execute(1057264140543346, "net_version", []) + then: + act.id == 1057264140543346 + act.result != null + act.error == null + } +}