implement lowerbound fix (#830)

* implement fix
This commit is contained in:
EugeneDrpc
2026-05-15 16:05:24 +02:00
committed by GitHub
parent b99b74fbe1
commit 6bf8680359
4 changed files with 121 additions and 8 deletions

View File

@@ -12,8 +12,28 @@ abstract class EthereumLowerBoundErrorHandler : ErrorHandler {
override fun handle(upstream: Upstream, request: ChainRequest, errorMessage: String?) {
try {
if (canHandle(request, errorMessage)) {
parseTagParam(request, tagIndex(request.method))?.let {
upstream.updateLowerBound(it, type())
parseTagParam(request, tagIndex(request.method))?.let { parsed ->
val currentHeight = upstream.getHead().getCurrentHeight()
if (currentHeight == null) {
log.warn(
"Skip {} lower bound update for {}: head height unknown (parsed={})",
type(),
upstream.getId(),
parsed,
)
return@let
}
if (parsed > currentHeight) {
log.warn(
"Skip {} lower bound update for {}: parsed tag {} exceeds head {}",
type(),
upstream.getId(),
parsed,
currentHeight,
)
return@let
}
upstream.updateLowerBound(parsed, type())
}
}
} catch (e: RuntimeException) {

View File

@@ -1,6 +1,7 @@
package io.emeraldpay.dshackle.upstream.error
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
@@ -10,17 +11,18 @@ import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.of
import org.junit.jupiter.params.provider.MethodSource
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.mock
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class EthereumStateLowerBoundErrorHandlerTest {
@ParameterizedTest
@MethodSource("requests")
fun `update lower bound`(request: ChainRequest) {
val upstream = mock<Upstream>()
val upstream = mockUpstreamWithHead(300_000_000L)
val handler = EthereumStateLowerBoundErrorHandler
handler.handle(upstream, request, "missing trie node d5648cc9aef48154159d53800f2f")
@@ -28,6 +30,53 @@ class EthereumStateLowerBoundErrorHandlerTest {
verify(upstream).updateLowerBound(213229736, LowerBoundType.STATE)
}
@Test
fun `no update lower bound if parsed tag exceeds head`() {
val upstream = mockUpstreamWithHead(100_000_000L)
EthereumStateLowerBoundErrorHandler.handle(
upstream,
ChainRequest("eth_getBalance", ListParams("0x343", "0xCB5A0A8")),
"missing trie node d5648cc9aef48154159d53800f2f",
)
verify(upstream, never()).updateLowerBound(anyLong(), any())
}
@Test
fun `no update lower bound if head height is null`() {
val upstream = mockUpstreamWithHead(null)
EthereumStateLowerBoundErrorHandler.handle(
upstream,
ChainRequest("eth_getBalance", ListParams("0x343", "0xCB5A0A8")),
"missing trie node d5648cc9aef48154159d53800f2f",
)
verify(upstream, never()).updateLowerBound(anyLong(), any())
}
@Test
fun `no update lower bound for the Base prod incident value`() {
val upstream = mockUpstreamWithHead(30_000_000L)
EthereumStateLowerBoundErrorHandler.handle(
upstream,
ChainRequest("eth_getBalance", ListParams("0x343", "0xD150C7F1")),
"missing trie node d5648cc9aef48154159d53800f2f",
)
verify(upstream, never()).updateLowerBound(anyLong(), any())
}
private fun mockUpstreamWithHead(height: Long?): Upstream {
val head = mock<Head>()
whenever(head.getCurrentHeight()).thenReturn(height)
val upstream = mock<Upstream>()
whenever(upstream.getHead()).thenReturn(head)
return upstream
}
@Test
fun `no update lower bound if error is not about state`() {
val upstream = mock<Upstream>()

View File

@@ -1,6 +1,7 @@
package io.emeraldpay.dshackle.upstream.error
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
@@ -9,15 +10,19 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.Arguments.of
import org.junit.jupiter.params.provider.MethodSource
import org.mockito.Mockito.mock
import org.mockito.Mockito.anyLong
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class EthereumTraceLowerBoundErrorHandlerTest {
@ParameterizedTest
@MethodSource("requests")
fun `update lower bound`(request: ChainRequest) {
val upstream = mock<Upstream>()
val upstream = mockUpstreamWithHead(300_000_000L)
val handler = EthereumTraceLowerBoundErrorHandler
handler.handle(upstream, request, "missing trie node d5648cc9aef48154159d53800f2f")
@@ -27,7 +32,7 @@ class EthereumTraceLowerBoundErrorHandlerTest {
@Test
fun `update lower bound base on regexp`() {
val upstream = mock<Upstream>()
val upstream = mockUpstreamWithHead(300_000_000L)
val handler = EthereumTraceLowerBoundErrorHandler
handler.handle(upstream, ChainRequest("trace_block", ListParams("0xCB5A0A8")), "block #1 not found")
@@ -35,6 +40,40 @@ class EthereumTraceLowerBoundErrorHandlerTest {
verify(upstream).updateLowerBound(213229736, LowerBoundType.TRACE)
}
@Test
fun `no update lower bound if parsed tag exceeds head`() {
val upstream = mockUpstreamWithHead(100_000_000L)
EthereumTraceLowerBoundErrorHandler.handle(
upstream,
ChainRequest("trace_block", ListParams("0xCB5A0A8")),
"missing trie node d5648cc9aef48154159d53800f2f",
)
verify(upstream, never()).updateLowerBound(anyLong(), any())
}
@Test
fun `no update lower bound if head height is null`() {
val upstream = mockUpstreamWithHead(null)
EthereumTraceLowerBoundErrorHandler.handle(
upstream,
ChainRequest("trace_block", ListParams("0xCB5A0A8")),
"missing trie node d5648cc9aef48154159d53800f2f",
)
verify(upstream, never()).updateLowerBound(anyLong(), any())
}
private fun mockUpstreamWithHead(height: Long?): Upstream {
val head = mock<Head>()
whenever(head.getCurrentHeight()).thenReturn(height)
val upstream = mock<Upstream>()
whenever(upstream.getHead()).thenReturn(head)
return upstream
}
companion object {
@JvmStatic
fun requests(): List<Arguments> =

View File

@@ -1,18 +1,23 @@
package io.emeraldpay.dshackle.upstream.error
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class UpstreamErrorHandlerTest {
@Test
fun `use lower bound error handler`() {
val head = mock<Head>()
whenever(head.getCurrentHeight()).thenReturn(300_000_000L)
val upstream = mock<Upstream>()
whenever(upstream.getHead()).thenReturn(head)
val request = ChainRequest("eth_getCode", ListParams("0x343", "0xCB5A0A8"))
val handler = UpstreamErrorHandler