Fix nethermind reverted error (#345)

This commit is contained in:
KirillPamPam
2023-11-21 11:16:39 +04:00
committed by GitHub
parent 7bdeaa8f05
commit 4f9b3e7fe4
4 changed files with 144 additions and 6 deletions

View File

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

View File

@@ -65,6 +65,7 @@ open class NativeCall(
private val signer: ResponseSigner,
config: MainConfig,
private val tracer: Tracer,
private val errorCorrector: ErrorCorrector,
) {
private val log = LoggerFactory.getLogger(NativeCall::class.java)
@@ -173,11 +174,12 @@ open class NativeCall(
.setSucceed(!it.isError())
.setId(it.id)
if (it.isError()) {
it.error?.let { error ->
result.setErrorMessage(error.message)
.setErrorCode(error.id)
it.error?.let { _ ->
val fixedError = errorCorrector.correctError(it)
result.setErrorMessage(fixedError.message)
.setErrorCode(fixedError.id)
error.data?.let { data ->
fixedError.data?.let { 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 CallError(
data class CallError(
val id: Int,
val message: String,
val upstreamError: JsonRpcError?,

View File

@@ -70,7 +70,7 @@ class NativeCallSpec extends Specification {
config.cache = cacheConfig
config.passthrough = passthrough
new NativeCall(upstreams, signer, config, Stub(Tracer))
new NativeCall(upstreams, signer, config, Stub(Tracer), Stub(ErrorCorrector))
}
def "Tries router first"() {

View File

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