From 5f454f5247ef0e08bdba968de99aa28748d529a5 Mon Sep 17 00:00:00 2001 From: a10zn8 Date: Tue, 5 May 2026 16:25:17 +0300 Subject: [PATCH] Feat/autodetect trace rawtx and storage values (#825) --- foundation/build.gradle | 15 ++ .../upstream/calls/DefaultEthereumMethods.kt | 1 + .../BasicEthUpstreamRpcMethodsDetector.kt | 2 + .../BasicEthUpstreamRpcMethodsDetectorTest.kt | 163 +++++++++++++++++- 4 files changed, 180 insertions(+), 1 deletion(-) diff --git a/foundation/build.gradle b/foundation/build.gradle index 61e9ca55..6cadb074 100644 --- a/foundation/build.gradle +++ b/foundation/build.gradle @@ -1,6 +1,9 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id 'org.jetbrains.kotlin.jvm' version '2.3.0' id 'maven-publish' + id 'java' } repositories { @@ -10,6 +13,18 @@ repositories { group = 'dshackle' +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +compileKotlin { + compilerOptions.jvmTarget.set(JvmTarget.JVM_21) +} +compileTestKotlin { + compilerOptions.jvmTarget.set(JvmTarget.JVM_21) +} + dependencies { implementation 'org.yaml:snakeyaml:1.24' testImplementation 'org.junit.jupiter:junit-jupiter:6.0.1' diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt index 1476391a..3c3616bc 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt @@ -179,6 +179,7 @@ class DefaultEthereumMethods( private val firstValueMethods = listOf( "eth_call", "eth_getStorageAt", + "eth_getStorageValues", "eth_getCode", "eth_getLogs", "eth_maxPriorityFeePerGas", diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetector.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetector.kt index 5e9c0113..25e8223c 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetector.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetector.kt @@ -34,7 +34,9 @@ class BasicEthUpstreamRpcMethodsDetector( setOf( "eth_getBlockReceipts" to ListParams("latest"), "trace_callMany" to ListParams(listOf(listOf())), + "trace_rawTransaction" to ListParams(listOf()), "eth_simulateV1" to ListParams(listOf()), + "eth_getStorageValues" to ListParams(listOf()), "debug_storageRangeAt" to ListParams(listOf()), "eth_getTdByNumber" to ListParams(listOf()), "eth_callBundle" to ListParams(listOf()), diff --git a/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetectorTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetectorTest.kt index 182ca8b8..2142824b 100644 --- a/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetectorTest.kt +++ b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/BasicEthUpstreamRpcMethodsDetectorTest.kt @@ -84,6 +84,24 @@ class BasicEthUpstreamRpcMethodsDetectorTest { ChainCallError(32602, "missing value for required argument 0"), ), ) + on { + read(ChainRequest("trace_rawTransaction", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + on { + read(ChainRequest("eth_getStorageValues", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) } val upstream = @@ -169,6 +187,24 @@ class BasicEthUpstreamRpcMethodsDetectorTest { ChainCallError(32602, "missing value for required argument 0"), ), ) + on { + read(ChainRequest("trace_rawTransaction", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + on { + read(ChainRequest("eth_getStorageValues", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) } val upstream = @@ -180,10 +216,12 @@ class BasicEthUpstreamRpcMethodsDetectorTest { val detector = BasicEthUpstreamRpcMethodsDetector(upstream, config) Assertions.assertThat(detector.detectRpcMethods().block()).apply { isNotNull() - hasSize(6) + hasSize(8) containsEntry("eth_getBlockReceipts", true) containsEntry("trace_callMany", true) + containsEntry("trace_rawTransaction", true) containsEntry("eth_simulateV1", true) + containsEntry("eth_getStorageValues", true) containsEntry("debug_storageRangeAt", true) containsEntry("eth_getTdByNumber", true) containsEntry("eth_callBundle", true) @@ -259,6 +297,24 @@ class BasicEthUpstreamRpcMethodsDetectorTest { ChainCallError(32602, "missing value for required argument 0"), ), ) + on { + read(ChainRequest("trace_rawTransaction", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + on { + read(ChainRequest("eth_getStorageValues", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) } val upstream = @@ -286,4 +342,109 @@ class BasicEthUpstreamRpcMethodsDetectorTest { containsEntry("eth_callBundle", true) } } + + @Test + fun `rpc_modules present but trace_rawTransaction and eth_getStorageValues not implemented`() { + val reader = + mock { + on { + read(ChainRequest("rpc_modules", ListParams())) + } doReturn + Mono.just( + ChainResponse( + """{"net": "1.0","debug": "1.0","txpool": "1.0","drpc": "1.0","erigon": "1.0","eth": "1.0","trace": "1.0"}""" + .toByteArray(), + null, + ), + ) + on { + read(ChainRequest("eth_getBlockReceipts", ListParams("latest"))) + } doReturn + Mono.just( + ChainResponse( + """[{"blockHash": "0xd12897f54acaa79f4824aa4f8e1d0f045b5568f5b942073555e9977202c5c474","blockNumber": "0x13c1108"}]""" + .toByteArray(), + null, + ), + ) + on { + read(ChainRequest("trace_callMany", ListParams(listOf(listOf())))) + } doReturn + Mono.just( + ChainResponse( + "[]".toByteArray(), + null, + ), + ) + on { + read(ChainRequest("trace_rawTransaction", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32601, "the method trace_rawTransaction does not exist/is not available"), + ), + ) + on { + read(ChainRequest("eth_simulateV1", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + "[]".toByteArray(), + null, + ), + ) + on { + read(ChainRequest("eth_getStorageValues", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32601, "the method eth_getStorageValues does not exist/is not available"), + ), + ) + on { + read(ChainRequest("debug_storageRangeAt", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + on { + read(ChainRequest("eth_getTdByNumber", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + on { + read(ChainRequest("eth_callBundle", ListParams(listOf()))) + } doReturn + Mono.just( + ChainResponse( + null, + ChainCallError(32602, "missing value for required argument 0"), + ), + ) + } + + val upstream = + mock { + on { getIngressReader() } doReturn reader + on { getChain() } doReturn Chain.ETHEREUM__MAINNET + } + val config = mock> { } + val detector = BasicEthUpstreamRpcMethodsDetector(upstream, config) + Assertions.assertThat(detector.detectRpcMethods().block()).apply { + isNotNull() + containsEntry("trace_rawTransaction", false) + containsEntry("eth_getStorageValues", false) + containsEntry("trace_callMany", true) + containsEntry("eth_getBlockReceipts", true) + } + } }