Fix nethermind reverted error (#345)
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
package io.emeraldpay.dshackle.rpc
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component
|
||||||
|
|
||||||
|
interface ErrorProcessor {
|
||||||
|
|
||||||
|
fun matches(result: NativeCall.CallResult): Boolean
|
||||||
|
|
||||||
|
fun errorProcess(error: NativeCall.CallError): NativeCall.CallError
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
open class ErrorCorrector(
|
||||||
|
private val processors: List<ErrorProcessor>,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun correctError(result: NativeCall.CallResult): NativeCall.CallError {
|
||||||
|
if (!result.isError()) {
|
||||||
|
throw IllegalStateException("No error to correct")
|
||||||
|
}
|
||||||
|
return processors
|
||||||
|
.firstOrNull { it.matches(result) }
|
||||||
|
?.errorProcess(result.error!!)
|
||||||
|
?: result.error!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class NethermindEthCallRevertedErrorProcessor : ErrorProcessor {
|
||||||
|
override fun matches(result: NativeCall.CallResult): Boolean {
|
||||||
|
return result.ctx?.payload?.method == "eth_call" && (result.error?.data?.startsWith("Reverted") ?: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun errorProcess(error: NativeCall.CallError): NativeCall.CallError {
|
||||||
|
return NativeCall.CallError(
|
||||||
|
3,
|
||||||
|
error.message,
|
||||||
|
error.upstreamError,
|
||||||
|
error.data?.removePrefix("Reverted "),
|
||||||
|
error.upstreamId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ open class NativeCall(
|
|||||||
private val signer: ResponseSigner,
|
private val signer: ResponseSigner,
|
||||||
config: MainConfig,
|
config: MainConfig,
|
||||||
private val tracer: Tracer,
|
private val tracer: Tracer,
|
||||||
|
private val errorCorrector: ErrorCorrector,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(NativeCall::class.java)
|
private val log = LoggerFactory.getLogger(NativeCall::class.java)
|
||||||
@@ -173,11 +174,12 @@ open class NativeCall(
|
|||||||
.setSucceed(!it.isError())
|
.setSucceed(!it.isError())
|
||||||
.setId(it.id)
|
.setId(it.id)
|
||||||
if (it.isError()) {
|
if (it.isError()) {
|
||||||
it.error?.let { error ->
|
it.error?.let { _ ->
|
||||||
result.setErrorMessage(error.message)
|
val fixedError = errorCorrector.correctError(it)
|
||||||
.setErrorCode(error.id)
|
result.setErrorMessage(fixedError.message)
|
||||||
|
.setErrorCode(fixedError.id)
|
||||||
|
|
||||||
error.data?.let { data ->
|
fixedError.data?.let { data ->
|
||||||
result.setErrorData(data)
|
result.setErrorData(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,7 +568,7 @@ open class NativeCall(
|
|||||||
|
|
||||||
open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}")
|
open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}")
|
||||||
|
|
||||||
open class CallError(
|
data class CallError(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val message: String,
|
val message: String,
|
||||||
val upstreamError: JsonRpcError?,
|
val upstreamError: JsonRpcError?,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class NativeCallSpec extends Specification {
|
|||||||
config.cache = cacheConfig
|
config.cache = cacheConfig
|
||||||
config.passthrough = passthrough
|
config.passthrough = passthrough
|
||||||
|
|
||||||
new NativeCall(upstreams, signer, config, Stub(Tracer))
|
new NativeCall(upstreams, signer, config, Stub(Tracer), Stub(ErrorCorrector))
|
||||||
}
|
}
|
||||||
|
|
||||||
def "Tries router first"() {
|
def "Tries router first"() {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package io.emeraldpay.dshackle.rpc
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.assertThrows
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
|
||||||
|
class ErrorProcessingTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `fix nethermind eth_call reverted error`() {
|
||||||
|
val result = result("eth_call", "Reverted 0x0111")
|
||||||
|
val error = result.error!!
|
||||||
|
val corrector = ErrorCorrector(listOf(NethermindEthCallRevertedErrorProcessor()))
|
||||||
|
|
||||||
|
val fixedError = corrector.correctError(result)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
NativeCall.CallError(3, error.message, error.upstreamError, "0x0111", error.upstreamId),
|
||||||
|
fixedError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `return the same error if there is no suitable processor`() {
|
||||||
|
val result = result("eth_getBlockByNumber", "Reverted 0x0111")
|
||||||
|
val error = result.error!!
|
||||||
|
val corrector = ErrorCorrector(listOf(NethermindEthCallRevertedErrorProcessor()))
|
||||||
|
|
||||||
|
val fixedError = corrector.correctError(result)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
NativeCall.CallError(55, error.message, error.upstreamError, "Reverted 0x0111", error.upstreamId),
|
||||||
|
fixedError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `throw an exception if there is no error in result`() {
|
||||||
|
val result = NativeCall.CallResult(
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
assertThrows<IllegalStateException>("No error to correct") {
|
||||||
|
val corrector = ErrorCorrector(listOf(NethermindEthCallRevertedErrorProcessor()))
|
||||||
|
corrector.correctError(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `NethermindEthCallRevertedErrorProcessor returns false if result is with null data`() {
|
||||||
|
val processor = NethermindEthCallRevertedErrorProcessor()
|
||||||
|
val result = result("eth_call", null)
|
||||||
|
|
||||||
|
val matched = processor.matches(result)
|
||||||
|
|
||||||
|
assertFalse(matched)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun result(method: String, errorData: String?): NativeCall.CallResult =
|
||||||
|
NativeCall.CallResult(
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
NativeCall.CallError(
|
||||||
|
55,
|
||||||
|
"reverted",
|
||||||
|
JsonRpcError(1, "errMessage", null),
|
||||||
|
errorData,
|
||||||
|
"upId",
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
NativeCall.ValidCallContext(
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
mock<Multistream>(),
|
||||||
|
Selector.empty,
|
||||||
|
mock<CallQuorum>(),
|
||||||
|
NativeCall.ParsedCallDetails(method, emptyList()),
|
||||||
|
"req",
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user