problem: doesn't pass error details from upstream

fix: #42
This commit is contained in:
Igor Artamonov
2020-08-10 22:18:54 -04:00
parent 2abe3023b6
commit b5daceeff5
30 changed files with 325 additions and 103 deletions

View File

@@ -62,7 +62,7 @@ class ProxyServerSpec extends Specification {
def act = server.execute(Common.ChainRef.CHAIN_ETHEREUM, call)
then:
1 * nativeCall.nativeCall(_) >> Flux.just(BlockchainOuterClass.NativeCallReplyItem.newBuilder().build())
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, "".bytes, null))
StepVerifier.create(act)
.expectNext("hello")
.expectComplete()

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.proxy
import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.dshackle.rpc.NativeCall
import io.emeraldpay.dshackle.test.TestingCommons
import reactor.core.publisher.Flux
import spock.lang.Specification
@@ -86,11 +87,7 @@ class WriteRpcJsonSpec extends Specification {
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
call.ids[1] = 105
def data = [
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(1)
.setSucceed(true)
.setPayload(ByteString.copyFrom('"0x98dbb1"', 'UTF-8'))
.build()
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null)
]
when:
def act = writer.toJson(call, data[0])
@@ -103,11 +100,7 @@ class WriteRpcJsonSpec extends Specification {
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
call.ids[1] = 1
def data = [
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(1)
.setSucceed(false)
.setErrorMessage("Internal Error")
.build()
new NativeCall.CallResult(1, null, new NativeCall.CallError(1, "Internal Error", null))
]
when:
def act = writer.toJson(call, data[0])
@@ -120,11 +113,7 @@ class WriteRpcJsonSpec extends Specification {
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
call.ids[1] = "aaa"
def data = [
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(1)
.setSucceed(true)
.setPayload(ByteString.copyFrom('"0x98dbb1"', 'UTF-8'))
.build()
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null)
]
when:
def act = writer.toJson(call, data[0])
@@ -139,21 +128,9 @@ class WriteRpcJsonSpec extends Specification {
call.ids[2] = 11
call.ids[3] = 15
def data = [
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(1)
.setSucceed(true)
.setPayload(ByteString.copyFrom('"0x98dbb1"', 'UTF-8'))
.build(),
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(2)
.setSucceed(false)
.setErrorMessage("oops")
.build(),
BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setId(3)
.setSucceed(true)
.setPayload(ByteString.copyFrom('{"hash": "0x2484f459dc"}', 'UTF-8'))
.build(),
new NativeCall.CallResult(1, '"0x98dbb1"'.bytes, null),
new NativeCall.CallResult(2, null, new NativeCall.CallError(2, "oops", null)),
new NativeCall.CallResult(3, '{"hash": "0x2484f459dc"}'.bytes, null),
]
when:
def act = Flux.fromIterable(data)

View File

@@ -22,6 +22,7 @@ import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.quorum.BroadcastQuorum
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
import io.infinitape.etherjar.rpc.RpcException
import spock.lang.Specification
@@ -54,7 +55,7 @@ class BroadcastQuorumSpec extends Specification {
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _)
when:
q.record(new RpcException(1, "Nonce too low"), upstream3)
q.record(new JsonRpcException(1, "Nonce too low"), upstream3)
then:
1 * q.recordError(_, _, _)
q.isResolved()
@@ -74,7 +75,7 @@ class BroadcastQuorumSpec extends Specification {
!q.isResolved()
when:
q.record(new RpcException(1, "Internal error"), upstream1)
q.record(new JsonRpcException(1, "Internal error"), upstream1)
then:
!q.isResolved()
1 * q.recordError(_, _, _)
@@ -86,7 +87,7 @@ class BroadcastQuorumSpec extends Specification {
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _)
when:
q.record(new RpcException(1, "Nonce too low"), upstream3)
q.record(new JsonRpcException(1, "Nonce too low"), upstream3)
then:
1 * q.recordError(_, _, _)
q.isResolved()

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.quorum
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
import io.infinitape.etherjar.rpc.RpcException
import spock.lang.Specification
@@ -37,19 +38,19 @@ class NonEmptyQuorumSpec extends Specification {
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream1)
q.record(new JsonRpcException(1, "Internal"), upstream1)
then:
!q.isResolved()
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream2)
q.record(new JsonRpcException(1, "Internal"), upstream2)
then:
!q.isResolved()
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream3)
q.record(new JsonRpcException(1, "Internal"), upstream3)
then:
q.isFailed()
!q.isResolved()
@@ -89,7 +90,7 @@ class NonEmptyQuorumSpec extends Specification {
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream1)
q.record(new JsonRpcException(1, "Internal"), upstream1)
then:
!q.isFailed()
!q.isResolved()

View File

@@ -22,6 +22,7 @@ import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.quorum.NonceQuorum
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
import io.infinitape.etherjar.rpc.RpcException
import spock.lang.Specification
@@ -74,7 +75,7 @@ class NonceQuorumSpec extends Specification {
!q.isResolved()
when:
q.record(new RpcException(1, "Internal"), upstream1)
q.record(new JsonRpcException(1, "Internal"), upstream1)
then:
!q.isResolved()
1 * q.recordError(_, _, _)
@@ -113,21 +114,23 @@ class NonceQuorumSpec extends Specification {
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream1)
q.record(new JsonRpcException(1, "Internal"), upstream1)
then:
!q.isResolved()
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream2)
q.record(new JsonRpcException(1, "Internal"), upstream2)
then:
!q.isResolved()
!q.isFailed()
when:
q.record(new RpcException(1, "Internal"), upstream3)
q.record(new JsonRpcException(1, "Internal"), upstream3)
then:
q.isFailed()
!q.isResolved()
q.getError() != null
q.getError().message == "Internal"
}
}

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.quorum
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
import io.infinitape.etherjar.rpc.RpcException
import spock.lang.Specification
@@ -74,7 +75,7 @@ class NotLaggingQuorumSpec extends Specification {
def quorum = new NotLaggingQuorum(1)
when:
quorum.record(new RpcException(-100, "test error"), up)
quorum.record(new JsonRpcException(-100, "test error"), up)
then:
1 * up.getLag() >> 1
!quorum.isResolved()

View File

@@ -21,6 +21,7 @@ import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.grpc.stub.StreamObserver
@@ -60,7 +61,7 @@ class EthereumApiMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
Callable<JsonRpcResponse> call = {
def predefined = predefined.find { it.isSame(request.method, request.params) }
byte[] result = null
JsonRpcResponse.ResponseError error = null
JsonRpcError error = null
if (predefined != null) {
if (predefined.exception != null) {
predefined.onCalled()
@@ -69,7 +70,7 @@ class EthereumApiMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
}
if (predefined.result instanceof RpcResponseError) {
((RpcResponseError) predefined.result).with { err ->
error = new JsonRpcResponse.ResponseError(err.code, err.message)
error = new JsonRpcError(err.code, err.message)
}
} else {
// ResponseJson json = new ResponseJson<Object, Integer>(id: 1, result: predefined.result)
@@ -79,7 +80,7 @@ class EthereumApiMock implements Reader<JsonRpcRequest, JsonRpcResponse> {
predefined.print()
} else {
log.error("Method ${request.method} with ${request.params} is not mocked")
error = new JsonRpcResponse.ResponseError(-32601, "Method ${request.method} with ${request.params} is not mocked")
error = new JsonRpcError(-32601, "Method ${request.method} with ${request.params} is not mocked")
}
return new JsonRpcResponse(result, error)
} as Callable<JsonRpcResponse>

View File

@@ -0,0 +1,34 @@
package io.emeraldpay.dshackle.upstream.rpcclient
import io.infinitape.etherjar.rpc.RpcException
import nl.jqno.equalsverifier.EqualsVerifier
import nl.jqno.equalsverifier.Warning
import spock.lang.Specification
class JsonRpcErrorSpec extends Specification {
def "Build from RpcException"() {
when:
def act = JsonRpcError.from(new RpcException(-32123, "test test"))
then:
act.code == -32123
act.message == "test test"
act.details == null
}
def "Build from RpcException with details"() {
when:
def act = JsonRpcError.from(new RpcException(-32123, "test test", "foo bar"))
then:
act.code == -32123
act.message == "test test"
act.details == "foo bar"
}
def "Equals"() {
when:
def v = EqualsVerifier.forClass(JsonRpcError)
then:
v.verify()
}
}

View File

@@ -106,7 +106,7 @@ class JsonRpcHttpClientSpec extends Specification {
then:
StepVerifier.create(act)
.expectErrorMatches { t ->
t instanceof RpcException && t.code == RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE
t instanceof JsonRpcException && t.error.code == RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE
}
.verify(Duration.ofSeconds(1))
}

View File

@@ -178,6 +178,36 @@ class JsonRpcParserSpec extends Specification {
!act.hasResult()
}
def "Parse error with data"() {
setup:
// 0 8 16 32
def json = '{"jsonrpc": "2.0", "id": 1, "result": null, "error": {"code": -1111, "message": "test", "data": "just data"}}'
when:
def act = parser.parse(json.getBytes())
then:
act.error != null
act.error.code == -1111
act.error.message == "test"
act.error.details == "just data"
act.hasError()
!act.hasResult()
}
def "Parse error with data struct"() {
setup:
// 0 8 16 32
def json = '{"jsonrpc": "2.0", "id": 1, "result": null, "error": {"code": -1111, "message": "test", "data": {"foo": "just data", "bar": 1}}}'
when:
def act = parser.parse(json.getBytes())
then:
act.error != null
act.error.code == -1111
act.error.message == "test"
act.error.details == [foo: "just data", bar: 1]
act.hasError()
!act.hasResult()
}
def "Handle non-json with producing an error response"() {
setup:
def json = 'NOT JSON'

View File

@@ -90,7 +90,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize int id and error"() {
setup:
def json = new JsonRpcResponse(null, new JsonRpcResponse.ResponseError(-32041, "Oooops"), new JsonRpcResponse.IntId(101))
def json = new JsonRpcResponse(null, new JsonRpcError(-32041, "Oooops"), new JsonRpcResponse.IntId(101))
when:
def act = objectMapper.writeValueAsString(json)
then:
@@ -127,7 +127,7 @@ class JsonRpcResponseSpec extends Specification {
def "Serialize string id and error"() {
setup:
def json = new JsonRpcResponse(null,
new JsonRpcResponse.ResponseError(-32041, "Oooops"),
new JsonRpcError(-32041, "Oooops"),
new JsonRpcResponse.StringId("9kbo29gkaasf"))
when:
def act = objectMapper.writeValueAsString(json)