Merge pull request #52 from emeraldpay/fix/rpc-id-long

This commit is contained in:
Igor Artamonov
2020-08-23 21:35:31 -04:00
committed by GitHub
12 changed files with 57 additions and 39 deletions

View File

@@ -13,7 +13,7 @@ springSecurtyVersion=5.3.2.RELEASE
reactorVersion=3.3.5.RELEASE reactorVersion=3.3.5.RELEASE
nettyVersion=4.1.49.Final nettyVersion=4.1.49.Final
# Our Libs # Our Libs
etherjarVersion=0.10.1 etherjarVersion=0.10.2
# Testing # Testing
spockVersion=1.3-groovy-2.5 spockVersion=1.3-groovy-2.5

View File

@@ -26,7 +26,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.infinitape.etherjar.rpc.RpcException import io.infinitape.etherjar.rpc.RpcException
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled import io.netty.buffer.Unpooled
import io.netty.handler.ssl.SslContextBuilder
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.http.HttpHeaders 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.HttpServerRequest
import reactor.netty.http.server.HttpServerResponse import reactor.netty.http.server.HttpServerResponse
import reactor.netty.http.server.HttpServerRoutes import reactor.netty.http.server.HttpServerRoutes
import java.io.File
import java.util.function.BiFunction import java.util.function.BiFunction
/** /**
@@ -100,8 +98,8 @@ class ProxyServer(
.flatMapMany { call -> execute(chain, call) } .flatMapMany { call -> execute(chain, call) }
.onErrorResume(RpcException::class.java) { err -> .onErrorResume(RpcException::class.java) { err ->
val id = err.details?.let { val id = err.details?.let {
if (it is JsonRpcResponse.Id) it else JsonRpcResponse.IntId(-1) if (it is JsonRpcResponse.Id) it else JsonRpcResponse.NumberId(-1)
} ?: JsonRpcResponse.IntId(-1) } ?: JsonRpcResponse.NumberId(-1)
val json = JsonRpcResponse.error(err.code, err.rpcMessage, id) val json = JsonRpcResponse.error(err.code, err.rpcMessage, id)
Mono.just(Global.objectMapper.writeValueAsString(json)) Mono.just(Global.objectMapper.writeValueAsString(json))

View File

@@ -58,7 +58,7 @@ class QuorumRpcReader(
val defaultResult: Mono<Result> = Mono.just(quorum).flatMap { q -> val defaultResult: Mono<Result> = Mono.just(quorum).flatMap { q ->
if (q.isFailed()) { if (q.isFailed()) {
Mono.error<Result>( Mono.error<Result>(
q.getError()?.asException(JsonRpcResponse.IntId(1)) q.getError()?.asException(JsonRpcResponse.NumberId(1))
?: RpcException(-32000, "Unknown Upstream error") ?: RpcException(-32000, "Unknown Upstream error")
) )
} else { } else {

View File

@@ -211,7 +211,7 @@ open class NativeCall(
companion object { companion object {
fun from(t: Throwable): CallError { fun from(t: Throwable): CallError {
return when (t) { 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 RpcException -> CallError(t.code, t.rpcMessage, null)
is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null) is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null)
else -> CallError(1, t.message ?: "Upstream Error", null) else -> CallError(1, t.message ?: "Upstream Error", null)

View File

@@ -31,7 +31,7 @@ class JsonRpcError(val code: Int, val message: String, val details: Any?) {
} }
fun asException(id: JsonRpcResponse.Id?): JsonRpcException { 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 { override fun equals(other: Any?): Boolean {

View File

@@ -22,7 +22,7 @@ class JsonRpcException(
val error: JsonRpcError val error: JsonRpcError
) : Exception(error.message) { ) : 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 { companion object {
fun from(err: RpcException): JsonRpcException { fun from(err: RpcException): JsonRpcException {
@@ -30,9 +30,9 @@ class JsonRpcException(
if (it is JsonRpcResponse.Id) { if (it is JsonRpcResponse.Id) {
it it
} else { } else {
JsonRpcResponse.IntId(-3) JsonRpcResponse.NumberId(-3)
} }
} ?: JsonRpcResponse.IntId(-4) } ?: JsonRpcResponse.NumberId(-4)
return JsonRpcException( return JsonRpcException(
id, JsonRpcError.from(err) id, JsonRpcError.from(err)
) )

View File

@@ -88,7 +88,7 @@ class JsonRpcHttpClient(
return response.response { header, bytes -> return response.response { header, bytes ->
if (header.status().code() != 200) { 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()}")) JsonRpcError(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "HTTP Code: ${header.status().code()}"))
) )
} else { } else {
@@ -105,7 +105,7 @@ class JsonRpcHttpClient(
.onErrorResume { t -> .onErrorResume { t ->
val err = when (t) { val err = when (t) {
is RpcException -> JsonRpcResponse.error(t.code, t.rpcMessage) 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) else -> JsonRpcResponse.error(1, t.message ?: t.javaClass.name)
} }
Mono.just(err) Mono.just(err)

View File

@@ -26,7 +26,7 @@ class JsonRpcResponse(
val id: Id 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 { companion object {
private val NULL_VALUE = "null".toByteArray() 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 * JSON RPC wrapper. Makes sure that the id is either Int or String
*/ */
interface Id { interface Id {
fun asInt(): Int fun asNumber(): Long
fun asString(): String fun asString(): String
fun isInt(): Boolean fun isNumber(): Boolean
companion object { companion object {
@JvmStatic @JvmStatic
fun from(id: Any): Id { fun from(id: Any): Id {
if (id is Int) { if (id is Int) {
return IntId(id) return NumberId(id)
} }
if (id is Number) { if (id is Number) {
return IntId(id.toInt()) return NumberId(id.toLong())
} }
if (id is String) { if (id is String) {
return StringId(id) 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 { class NumberId(val id: Long) : Id {
override fun asInt(): Int { constructor(id: Int) : this(id.toLong())
override fun asNumber(): Long {
return id return id
} }
@@ -159,13 +161,13 @@ class JsonRpcResponse(
throw IllegalStateException("Not string") throw IllegalStateException("Not string")
} }
override fun isInt(): Boolean { override fun isNumber(): Boolean {
return true return true
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is IntId) return false if (other !is NumberId) return false
if (id != other.id) return false if (id != other.id) return false
@@ -173,20 +175,20 @@ class JsonRpcResponse(
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return id return id.hashCode()
} }
} }
class StringId(val id: String) : Id { class StringId(val id: String) : Id {
override fun asInt(): Int { override fun asNumber(): Long {
throw IllegalStateException("Not int") throw IllegalStateException("Not a number")
} }
override fun asString(): String { override fun asString(): String {
return id return id
} }
override fun isInt(): Boolean { override fun isNumber(): Boolean {
return false return false
} }
@@ -209,8 +211,8 @@ class JsonRpcResponse(
override fun serialize(value: JsonRpcResponse, gen: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: JsonRpcResponse, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeStartObject() gen.writeStartObject()
gen.writeStringField("jsonrpc", "2.0") gen.writeStringField("jsonrpc", "2.0")
if (value.id.isInt()) { if (value.id.isNumber()) {
gen.writeNumberField("id", value.id.asInt()) gen.writeNumberField("id", value.id.asNumber())
} else { } else {
gen.writeStringField("id", value.id.asString()) gen.writeStringField("id", value.id.asString())
} }

View File

@@ -72,7 +72,7 @@ class ProxyServerSpec extends Specification {
def "Return error on invalid request"() { def "Return error on invalid request"() {
setup: setup:
ReadRpcJson read = Mock(ReadRpcJson) { 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( def server = new ProxyServer(
Stub(ProxyConfig), Stub(ProxyConfig),

View File

@@ -16,7 +16,7 @@
*/ */
package io.emeraldpay.dshackle.proxy package io.emeraldpay.dshackle.proxy
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.infinitape.etherjar.rpc.RpcException import io.infinitape.etherjar.rpc.RpcException
import spock.lang.Specification import spock.lang.Specification
@@ -192,7 +192,7 @@ class ReadRpcJsonSpec extends Specification {
def t = thrown(RpcException) def t = thrown(RpcException)
t.code == -32600 t.code == -32600
t.rpcMessage.toLowerCase() == "jsonrpc version is not set" 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"() { def "Error if jsonrpc version is invalid"() {
@@ -202,7 +202,7 @@ class ReadRpcJsonSpec extends Specification {
def t = thrown(RpcException) def t = thrown(RpcException)
t.code == -32600 t.code == -32600
t.rpcMessage.toLowerCase() == "unsupported json rpc version: 3.0" 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"() { def "Error if method is not set"() {
@@ -212,7 +212,7 @@ class ReadRpcJsonSpec extends Specification {
def t = thrown(RpcException) def t = thrown(RpcException)
t.code == -32600 t.code == -32600
t.rpcMessage.toLowerCase() == "method is not set" 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"() { def "Error if params is not array"() {
@@ -222,6 +222,6 @@ class ReadRpcJsonSpec extends Specification {
def t = thrown(RpcException) def t = thrown(RpcException)
t.code == -32600 t.code == -32600
t.rpcMessage.toLowerCase() == "params must be an array" t.rpcMessage.toLowerCase() == "params must be an array"
t.details == new JsonRpcResponse.IntId(2) t.details == new JsonRpcResponse.NumberId(2)
} }
} }

View File

@@ -63,7 +63,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize int id and null result"() { def "Serialize int id and null result"() {
setup: setup:
def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.IntId(1)) def json = new JsonRpcResponse("null".bytes, null, new JsonRpcResponse.NumberId(1))
when: when:
def act = objectMapper.writeValueAsString(json) def act = objectMapper.writeValueAsString(json)
then: then:
@@ -72,7 +72,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize int id and string result"() { def "Serialize int id and string result"() {
setup: 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: when:
def act = objectMapper.writeValueAsString(json) def act = objectMapper.writeValueAsString(json)
then: then:
@@ -81,7 +81,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize int id and object result"() { def "Serialize int id and object result"() {
setup: 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: when:
def act = objectMapper.writeValueAsString(json) def act = objectMapper.writeValueAsString(json)
then: then:
@@ -90,7 +90,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize int id and error"() { def "Serialize int id and error"() {
setup: 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: when:
def act = objectMapper.writeValueAsString(json) def act = objectMapper.writeValueAsString(json)
then: then:

View File

@@ -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
}
}