diff --git a/emerald-grpc b/emerald-grpc index d047f442..7a0f39a9 160000 --- a/emerald-grpc +++ b/emerald-grpc @@ -1 +1 @@ -Subproject commit d047f442f7aecc0e1363604477152eeddfc14713 +Subproject commit 7a0f39a98b8848a0a1106395dce2152f5a81e7f8 diff --git a/foundation/src/main/resources/public b/foundation/src/main/resources/public index 09998775..08e44000 160000 --- a/foundation/src/main/resources/public +++ b/foundation/src/main/resources/public @@ -1 +1 @@ -Subproject commit 09998775904b398716c7c058e5ed6ddefa4125da +Subproject commit 08e44000e0445e21872b66fa81f129d23d3d7931 diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamSettingsDetector.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamSettingsDetector.kt index beca4ca9..31caa0f5 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamSettingsDetector.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamSettingsDetector.kt @@ -8,6 +8,7 @@ import io.emeraldpay.dshackle.upstream.ChainRequest import io.emeraldpay.dshackle.upstream.ChainResponse import io.emeraldpay.dshackle.upstream.NodeTypeRequest import io.emeraldpay.dshackle.upstream.Upstream +import io.emeraldpay.dshackle.upstream.generic.GenericUpstream import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -15,7 +16,6 @@ import java.util.concurrent.atomic.AtomicInteger const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" const val HL_NATIVE_TX_FROM_MAINNET = "0x2222222222222222222222222222222222222222" -const val HL_NATIVE_TX_FROM_TESTNET = "0x6ed35e7d6de4b45f4efb8a91eff31afa49362569" class EthereumUpstreamSettingsDetector( private val _upstream: Upstream, @@ -179,15 +179,17 @@ class EthereumUpstreamSettingsDetector( Some clients on hyperliquid don't include system topup transactions, set either one of labels */ private fun detectHlNativeTx(): Flux> { - // Only run HL native tx detection on Hyperliquid chains if (chain != Chain.HYPERLIQUID__MAINNET && chain != Chain.HYPERLIQUID__TESTNET) { return Flux.empty() } - val hlNativeTxFrom = when (chain) { - Chain.HYPERLIQUID__MAINNET -> HL_NATIVE_TX_FROM_MAINNET - Chain.HYPERLIQUID__TESTNET -> HL_NATIVE_TX_FROM_TESTNET - else -> return Flux.empty() + // prefer the explicit ?hl= flag from the upstream URL + hlNativeTxLabelsFromUrl()?.let { return Flux.fromIterable(it) } + + // no ?hl= flag: block-scan fallback (reliable only on mainnet) + if (chain != Chain.HYPERLIQUID__MAINNET) { + return Flux.empty() } + val hlNativeTxFrom = HL_NATIVE_TX_FROM_MAINNET if (detectCounter.get() % 5 != 1) { return Flux.empty() // reduce frequency of detection } @@ -255,6 +257,19 @@ class EthereumUpstreamSettingsDetector( } } + // maps the upstream URL's ?hl= flag to routing labels, or null if absent + private fun hlNativeTxLabelsFromUrl(): List>? { + val url = (upstream as? GenericUpstream)?.getRpcConnectionUrl()?.toString() ?: return null + // ?hl=false => serves native txs (include); ?hl=true => hl-node compliant (exclude) + return when { + url.contains(Regex("[?&]hl=false\\b")) -> + listOf("include_hl_native_tx" to "true", "exclude_hl_native_tx" to "false") + url.contains(Regex("[?&]hl=true\\b")) -> + listOf("exclude_hl_native_tx" to "true", "include_hl_native_tx" to "false") + else -> null + } + } + private fun detectArchiveNode(notArchived: Boolean): Mono> { if (notArchived) { return Mono.empty() diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/generic/GenericUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/generic/GenericUpstream.kt index d5c5ddb3..0db63e42 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/generic/GenericUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/generic/GenericUpstream.kt @@ -43,6 +43,7 @@ import reactor.core.Disposable import reactor.core.publisher.Flux import reactor.core.publisher.Sinks import reactor.core.scheduler.Schedulers +import java.net.URI import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.atomic.AtomicBoolean @@ -101,6 +102,8 @@ open class GenericUpstream( ) { rpcMethodsDetector = upstreamRpcMethodsDetectorBuilder(this, config) detectRpcMethods(config, buildMethods) + rpcConnectionUrl = (config.connection as? UpstreamsConfig.RpcConnection) + ?.let { it.rpc?.url ?: it.ws?.url } } private val validator: UpstreamValidator? = validatorBuilder(chain, this, getOptions(), chainConfig, versionRules) @@ -126,6 +129,9 @@ open class GenericUpstream( private val settingsDetector = upstreamSettingsDetectorBuilder(chain, this) private var rpcMethodsDetector: UpstreamRpcMethodsDetector? = null + // configured RPC/WS URL (carries query flags like ?hl=) + private var rpcConnectionUrl: URI? = null + private val lowerBoundService = lowerBoundServiceBuilder(chain, this) private val started = AtomicBoolean(false) @@ -184,6 +190,8 @@ open class GenericUpstream( ) } + fun getRpcConnectionUrl(): URI? = rpcConnectionUrl + @Suppress("UNCHECKED_CAST") override fun cast(selfType: Class): T { if (!selfType.isAssignableFrom(this.javaClass)) {