check chain_ids on all networks (#507)

* check chain_ids on all networks
This commit is contained in:
a10zn8
2024-06-11 17:41:37 +03:00
committed by GitHub
parent c1ea446dfc
commit cce2bcf739
9 changed files with 172 additions and 55 deletions

View File

@@ -115,7 +115,8 @@ class ChainsConfigReader(
?: throw IllegalArgumentException("undefined code for $blockchain")
val grpcId = getValueAsInt(node, "grpcId")
?: throw IllegalArgumentException("undefined code for $blockchain")
val netVersion = getValueAsLong(node, "net-version")?.toBigInteger() ?: BigInteger(chainId.drop(2), 16)
val netVersion = getValueAsLong(node, "net-version")?.toBigInteger() ?:
if (chainId.startsWith("0x")) BigInteger(chainId.drop(2), 16) else BigInteger.ZERO
val shortNames = getListOfString(node, "short-names")
?: throw IllegalArgumentException("undefined shortnames for $blockchain")
val type = getValueAsString(node, "type")

View File

@@ -35,6 +35,7 @@ chain-settings:
settings:
expected-block-time: 12s
mev-critical: true
allow-pruning-requirement: true
lags:
syncing: 6
lagging: 1
@@ -76,6 +77,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 3s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -101,6 +103,7 @@ chain-settings:
settings:
mev-critical: true
expected-block-time: 2.7s
allow-pruning-requirement: true
lags:
syncing: 20
lagging: 10
@@ -131,6 +134,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 260ms
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -155,6 +159,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -179,6 +184,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 3s
allow-pruning-requirement: true
mev-critical: true
lags:
syncing: 20
@@ -206,6 +212,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2.7s
allow-pruning-requirement: true
options:
disable-validation: true
lags:
@@ -232,6 +239,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 260ms
allow-pruning-requirement: true
options:
disable-validation: true
lags:
@@ -249,6 +257,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 5s
allow-pruning-requirement: true
lags:
syncing: 70
lagging: 50
@@ -273,6 +282,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -297,6 +307,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 12s
allow-pruning-requirement: true
lags:
syncing: 6
lagging: 1
@@ -319,6 +330,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 6s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -343,6 +355,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2s
allow-pruning-requirement: true
options:
validate-peers: false
validate-syncing: false
@@ -392,6 +405,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 500ms
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -416,6 +430,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 1s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -442,6 +457,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 5s
allow-pruning-requirement: true
lags:
syncing: 10
lagging: 5
@@ -464,6 +480,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 12s
allow-pruning-requirement: true
lags:
syncing: 6
lagging: 1
@@ -486,6 +503,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 12s
allow-pruning-requirement: true
lags:
syncing: 6
lagging: 1
@@ -554,6 +572,7 @@ chain-settings:
options:
validate-peers: false
expected-block-time: 30s
allow-pruning-requirement: true
lags:
syncing: 40
lagging: 20
@@ -600,6 +619,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 6s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -613,8 +633,7 @@ chain-settings:
short-names: [kava]
chain-id: 0x8ae
grpcId: 1025
gas-price-condition:
- eq 1000000000
gas-price-condition: eq 1000000000
- id: Testnet
priority: 10
code: KAVA_TESTNET
@@ -683,13 +702,13 @@ chain-settings:
priority: 1
code: VARA_MAINNET
short-names: [vara]
chain-id: 0x0
chain-id: Vara Network
grpcId: 1027
- id: Testnet
priority: 1
code: VARA_TESTMET
short-names: [vara-testnet]
chain-id: 0x0
chain-id: Vara Network Testnet
grpcId: 10036
- id: solana
label: Solana
@@ -773,6 +792,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 5s
allow-pruning-requirement: true
lags:
syncing: 20
lagging: 10
@@ -835,6 +855,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 10s
allow-pruning-requirement: true
lags:
syncing: 10
lagging: 5
@@ -933,6 +954,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2s
allow-pruning-requirement: true
options:
validate-peers: false
lags:
@@ -1022,6 +1044,7 @@ chain-settings:
type: eth
settings:
expected-block-time: 2s
allow-pruning-requirement: true
lags:
syncing: 40
lagging: 20
@@ -1282,19 +1305,19 @@ chain-settings:
code: NEAR_MAINNET
grpcId: 1050
short-names: [near]
chain-id: 0x0
chain-id: mainnet
- id: Testnet
priority: 5
code: NEAR_TESTNET
grpcId: 10064
short-names: [near-testnet]
chain-id: 0x0
chain-id: testnet
- id: Betanet
priority: 1
code: NEAR_BETANET
grpcId: 10065
short-names: [near-betanet]
chain-id: 0x0
chain-id: betanet
- id: opcelestia-raspberry
label: OP Celestia Raspberry
type: eth
@@ -1420,13 +1443,13 @@ chain-settings:
lagging: 1
chains:
- id: Mainnet
chain-id: 0x0
chain-id: cosmoshub-4
short-names: [ cosmos-hub ]
code: COSMOS_HUB
grpcId: 1057
priority: 100
- id: Testnet
chain-id: 0x0
chain-id: provider
short-names: [ cosmos-hub-testnet ]
code: COSMOS_HUB_TESTNET
grpcId: 10079
@@ -1441,13 +1464,13 @@ chain-settings:
lagging: 1
chains:
- id: Mainnet
chain-id: 0x0
chain-id: axelar-dojo-1
short-names: [ axelar ]
code: AXELAR
grpcId: 1059
priority: 100
- id: Testnet
chain-id: 0x0
chain-id: axelar-testnet-lisbon-3
short-names: [ axelar-testnet ]
code: AXELAR_TESTNET
grpcId: 10081
@@ -1462,13 +1485,13 @@ chain-settings:
lagging: 1
chains:
- id: Mainnet
chain-id: 0x0
chain-id: osmosis
short-names: [ osmosis ]
code: OSMOSIS
grpcId: 1060
priority: 100
- id: Testnet
chain-id: 0x0
chain-id: osmo-test-5
short-names: [ osmosis-testnet ]
code: OSMOSIS_TESTNET
grpcId: 10082
@@ -1483,13 +1506,13 @@ chain-settings:
lagging: 1
chains:
- id: Mainnet
chain-id: 0x0
chain-id: neutron-1
short-names: [ neutron ]
code: NEUTRON
grpcId: 1061
priority: 100
- id: Testnet
chain-id: 0x0
chain-id: pion-1
short-names: [ neutron-testnet ]
code: NEUTRON_TESTNET
grpcId: 10083

View File

@@ -44,16 +44,21 @@ abstract class UpstreamValidator(
val cp = Comparator { avail1: UpstreamAvailability, avail2: UpstreamAvailability -> if (avail1.isBetterTo(avail2)) -1 else 1 }
return results.sortedWith(cp).last()
}
fun resolve(results: Iterable<ValidateUpstreamSettingsResult>): ValidateUpstreamSettingsResult {
val cp = Comparator { res1: ValidateUpstreamSettingsResult, res2: ValidateUpstreamSettingsResult -> if (res1.priority < res2.priority) -1 else 1 }
return results.sortedWith(cp).last()
}
}
}
enum class ValidateUpstreamSettingsResult {
UPSTREAM_VALID,
UPSTREAM_SETTINGS_ERROR,
UPSTREAM_FATAL_SETTINGS_ERROR,
enum class ValidateUpstreamSettingsResult(val priority: Int) {
UPSTREAM_VALID(0),
UPSTREAM_SETTINGS_ERROR(1),
UPSTREAM_FATAL_SETTINGS_ERROR(2),
}
data class SingleCallValidator(
data class SingleCallValidator<T>(
val method: ChainRequest,
val check: (ByteArray) -> UpstreamAvailability,
val check: (ByteArray) -> T,
)

View File

@@ -14,15 +14,19 @@ import io.emeraldpay.dshackle.upstream.SingleCallValidator
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.UpstreamAvailability.OK
import io.emeraldpay.dshackle.upstream.UpstreamValidator
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
import io.emeraldpay.dshackle.upstream.generic.GenericUpstreamValidator
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import io.emeraldpay.dshackle.upstream.rpcclient.ObjectParams
import org.slf4j.LoggerFactory
import reactor.core.publisher.Mono
import java.math.BigInteger
import java.time.Instant
object CosmosChainSpecific : AbstractPollChainSpecific() {
val log = LoggerFactory.getLogger(this::class.java)
override fun latestBlockRequest(): ChainRequest = ChainRequest("block", ObjectParams())
override fun parseBlock(data: ByteArray, upstreamId: String): BlockContainer {
@@ -75,9 +79,24 @@ object CosmosChainSpecific : AbstractPollChainSpecific() {
return GenericUpstreamValidator(
upstream,
options,
SingleCallValidator(
ChainRequest("health", ListParams()),
) { _ -> OK },
listOf(
SingleCallValidator(
ChainRequest("health", ListParams()),
) { _ -> OK },
),
listOf(
SingleCallValidator(
ChainRequest("status", ListParams()),
) { data ->
val resp = Global.objectMapper.readValue(data, CosmosStatus::class.java)
if (chain.chainId.isNotEmpty() && resp.nodeInfo.network.lowercase() != chain.chainId.lowercase()) {
ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
} else {
ValidateUpstreamSettingsResult.UPSTREAM_VALID
}
},
),
)
}
@@ -115,6 +134,7 @@ data class CosmosStatus(
@JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosNodeInfo(
@JsonProperty("version") var version: String,
@JsonProperty("network") var network: String,
)
@JsonIgnoreProperties(ignoreUnknown = true)

View File

@@ -8,17 +8,28 @@ import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.UpstreamValidator
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_VALID
import reactor.core.publisher.Mono
import java.util.concurrent.TimeoutException
class GenericUpstreamValidator(
upstream: Upstream,
options: ChainOptions.Options,
private val validator: SingleCallValidator,
private val validators: List<SingleCallValidator<UpstreamAvailability>>,
private val startupValidators: List<SingleCallValidator<ValidateUpstreamSettingsResult>>,
) : UpstreamValidator(upstream, options) {
override fun validate(): Mono<UpstreamAvailability> {
return Mono.zip(
validators.map { exec(it, UpstreamAvailability.UNAVAILABLE) },
) { a -> a.map { it as UpstreamAvailability } }
.map(::resolve)
.defaultIfEmpty(UpstreamAvailability.UNAVAILABLE)
.onErrorResume {
log.error("Error during upstream validation for ${upstream.getId()}", it)
Mono.just(UpstreamAvailability.UNAVAILABLE)
}
}
fun <T : Any> exec(validator: SingleCallValidator<T>, onError: T): Mono<T> {
return upstream.getIngressReader()
.read(validator.method)
.flatMap(ChainResponse::requireResult)
@@ -29,10 +40,22 @@ class GenericUpstreamValidator(
.then(Mono.error(TimeoutException("Validation timeout for ${validator.method.method}"))),
)
.doOnError { err -> log.error("Error during ${validator.method.method} validation for ${upstream.getId()}", err) }
.onErrorReturn(UpstreamAvailability.UNAVAILABLE)
.onErrorReturn(onError)
}
override fun validateUpstreamSettings(): Mono<ValidateUpstreamSettingsResult> {
return Mono.just(UPSTREAM_VALID)
return Mono.zip(
startupValidators.map { exec(it, ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR) },
) { a -> a.map { it as ValidateUpstreamSettingsResult } }
.map(::resolve)
.defaultIfEmpty(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
.onErrorResume {
log.error("Error during upstream validation for ${upstream.getId()}", it)
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR)
}
}
override fun validateUpstreamSettingsOnStartup(): ValidateUpstreamSettingsResult {
return validateUpstreamSettings().block() ?: ValidateUpstreamSettingsResult.UPSTREAM_VALID
}
}

View File

@@ -15,6 +15,7 @@ import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
import io.emeraldpay.dshackle.upstream.UpstreamValidator
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
import io.emeraldpay.dshackle.upstream.generic.GenericUpstreamValidator
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundService
@@ -64,11 +65,20 @@ object NearChainSpecific : AbstractPollChainSpecific() {
return GenericUpstreamValidator(
upstream,
options,
SingleCallValidator(
ChainRequest("status", ListParams()),
) { data ->
validate(data)
},
listOf(
SingleCallValidator(
ChainRequest("status", ListParams()),
) { data ->
validate(data)
},
),
listOf(
SingleCallValidator(
ChainRequest("status", ListParams()),
) { data ->
validateSettings(data, chain)
},
),
)
}
@@ -85,6 +95,15 @@ object NearChainSpecific : AbstractPollChainSpecific() {
}
}
fun validateSettings(data: ByteArray, chain: Chain): ValidateUpstreamSettingsResult {
val resp = Global.objectMapper.readValue(data, NearStatus::class.java)
return if (chain.chainId.isNotEmpty() && resp.chainId.lowercase() != chain.chainId.lowercase()) {
ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
} else {
ValidateUpstreamSettingsResult.UPSTREAM_VALID
}
}
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector {
return NearUpstreamSettingsDetector(upstream)
}
@@ -108,6 +127,7 @@ data class NearHeader(
@JsonIgnoreProperties(ignoreUnknown = true)
data class NearStatus(
@JsonProperty("chain_id") var chainId: String,
@JsonProperty("sync_info") var syncInfo: NearSync,
)

View File

@@ -20,6 +20,7 @@ import io.emeraldpay.dshackle.upstream.SingleCallValidator
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.UpstreamValidator
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultPolkadotMethods
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
@@ -97,11 +98,20 @@ object PolkadotChainSpecific : AbstractPollChainSpecific() {
return GenericUpstreamValidator(
upstream,
options,
SingleCallValidator(
ChainRequest("system_health", ListParams()),
) { data ->
validate(data, options.minPeers, upstream.getId())
},
listOf(
SingleCallValidator(
ChainRequest("system_health", ListParams()),
) { data ->
validate(data, options.minPeers, upstream.getId())
},
),
listOf(
SingleCallValidator(
ChainRequest("system_chain", ListParams()),
) { data ->
validateSettings(data, chain)
},
),
)
}
@@ -123,6 +133,15 @@ object PolkadotChainSpecific : AbstractPollChainSpecific() {
return UpstreamAvailability.OK
}
fun validateSettings(data: ByteArray, chain: Chain): ValidateUpstreamSettingsResult {
val id = Global.objectMapper.readValue(data, String::class.java)
return if (chain.chainId.isNotEmpty() && id.lowercase() != chain.chainId.lowercase()) {
ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
} else {
ValidateUpstreamSettingsResult.UPSTREAM_VALID
}
}
override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
return GenericIngressSubscription(ws, DefaultPolkadotMethods.subs.map { it.first })
}

View File

@@ -124,17 +124,20 @@ object SolanaChainSpecific : AbstractChainSpecific() {
return GenericUpstreamValidator(
upstream,
options,
SingleCallValidator(
ChainRequest("getHealth", ListParams()),
) { data ->
val resp = String(data)
if (resp == "\"ok\"") {
UpstreamAvailability.OK
} else {
log.warn("Upstream {} validation failed, solana status is {}", upstream.getId(), resp)
UpstreamAvailability.UNAVAILABLE
}
},
listOf(
SingleCallValidator(
ChainRequest("getHealth", ListParams()),
) { data ->
val resp = String(data)
if (resp == "\"ok\"") {
UpstreamAvailability.OK
} else {
log.warn("Upstream {} validation failed, solana status is {}", upstream.getId(), resp)
UpstreamAvailability.UNAVAILABLE
}
},
),
listOf(),
)
}

View File

@@ -65,11 +65,14 @@ object StarknetChainSpecific : AbstractPollChainSpecific() {
return GenericUpstreamValidator(
upstream,
options,
SingleCallValidator(
ChainRequest("starknet_syncing", ListParams()),
) { data ->
validate(data, config.laggingLagSize, upstream.getId())
},
listOf(
SingleCallValidator(
ChainRequest("starknet_syncing", ListParams()),
) { data ->
validate(data, config.laggingLagSize, upstream.getId())
},
),
listOf(),
)
}