Fix hl (#869)
* fix(hyperliquid): derive native-tx routing labels from the ?hl= URL flag The include_hl_native_tx/exclude_hl_native_tx detector classified a node by scanning the last 300 blocks for a system (native) topup tx from a per-chain address. That cannot work on testnet: - the configured testnet address 0x6ed35e7d6de4b45f4efb8a91eff31afa49362569 was a regular bot (non-zero gasPrice, not filtered by hl-compliant mode, present on both node types); - real testnet system txs (from 0x2222...) are far too sparse and bursty (median gap ~1200 blocks, max ~9000 = ~2.5h at ~1s/block) for any practical window; - eth_getLogs is identical between compliant and non-compliant modes, so there is no cheap wide-range signal either. Our hl-node upstreams already encode the mode in the URL (?hl=false serves native txs, ?hl=true is compliant). Read that flag directly: - GenericUpstream captures the configured RPC/WS URL and exposes getRpcConnectionUrl(); - detectHlNativeTx emits include/exclude_hl_native_tx straight from ?hl= when present (cheap, exact, drift-free), for both mainnet and testnet; - it falls back to the recent-blocks scan only when there is no ?hl= flag, and only on mainnet (testnet without the flag is too sparse to classify); - the bogus HL_NATIVE_TX_FROM_TESTNET constant is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Fix hl --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Submodule emerald-grpc updated: d047f442f7...7a0f39a98b
Submodule foundation/src/main/resources/public updated: 0999877590...08e44000e0
@@ -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<Pair<String, String>> {
|
||||
// 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<Pair<String, String>>? {
|
||||
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<Pair<String, String>> {
|
||||
if (notArchived) {
|
||||
return Mono.empty()
|
||||
|
||||
@@ -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 <T : Upstream> cast(selfType: Class<T>): T {
|
||||
if (!selfType.isAssignableFrom(this.javaClass)) {
|
||||
|
||||
Reference in New Issue
Block a user