Files
dshackle/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfig.kt
a10zn8 00a1490726 Upstream configuration improvement: (#149)
- now we can specify connector mode for better fine tuning of connection
2023-02-27 14:22:50 +05:00

229 lines
7.0 KiB
Kotlin

/**
* Copyright (c) 2020 EmeraldPay, Inc
* Copyright (c) 2019 ETCDEV GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.emeraldpay.dshackle.config
import io.emeraldpay.dshackle.Defaults
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
import java.net.URI
import java.util.Arrays
import java.util.Locale
open class UpstreamsConfig {
var defaultOptions: MutableList<DefaultOptions> = ArrayList<DefaultOptions>()
var upstreams: MutableList<Upstream<*>> = ArrayList<Upstream<*>>()
open class Options {
var disableValidation: Boolean? = null
var validationInterval: Int = 30
set(value) {
require(value > 0) {
"validation-interval must be a positive number: $value"
}
field = value
}
var timeout = Defaults.timeout
var providesBalance: Boolean? = null
var validatePeers: Boolean = true
var minPeers: Int? = 1
set(value) {
require(value != null && value >= 0) {
"min-peers must be a positive number: $value"
}
field = value
}
var validateSyncing: Boolean = true
fun merge(overwrites: Options?): Options {
if (overwrites == null) {
return this
}
val copy = Options()
copy.validatePeers = this.validatePeers && overwrites.validatePeers
copy.minPeers = if (this.minPeers != null) this.minPeers else overwrites.minPeers
copy.disableValidation =
if (this.disableValidation != null) this.disableValidation else overwrites.disableValidation
copy.validationInterval = overwrites.validationInterval
copy.providesBalance =
if (this.providesBalance != null) this.providesBalance else overwrites.providesBalance
copy.validateSyncing = this.validateSyncing && overwrites.validateSyncing
return copy
}
companion object {
@JvmStatic
fun getDefaults(): Options {
val options = Options()
options.minPeers = 1
options.disableValidation = false
return options
}
}
}
class DefaultOptions : Options() {
var chains: List<String>? = null
var options: Options? = null
}
class Upstream<T : UpstreamConnection> {
var id: String? = null
var nodeId: Int? = null
var chain: String? = null
var options: Options? = null
var isEnabled = true
var connection: T? = null
val labels = Labels()
var methods: Methods? = null
var methodGroups: MethodGroups? = null
var role: UpstreamRole = UpstreamRole.PRIMARY
@Suppress("UNCHECKED_CAST")
fun <Z : UpstreamConnection> cast(type: Class<Z>): Upstream<Z> {
if (connection == null || type.isAssignableFrom(connection!!.javaClass)) {
return this as Upstream<Z>
}
throw ClassCastException("Cannot cast ${connection?.javaClass} to $type")
}
}
enum class UpstreamRole {
PRIMARY,
SECONDARY,
FALLBACK
}
open class UpstreamConnection
open class RpcConnection : UpstreamConnection() {
var rpc: HttpEndpoint? = null
}
class GrpcConnection : UpstreamConnection() {
var host: String? = null
var port: Int = 0
var auth: AuthConfig.ClientTlsAuth? = null
var upstreamRating: Int = 0
}
class EthereumConnection : RpcConnection() {
var ws: WsEndpoint? = null
var preferHttp: Boolean = false
var connectorMode: String? = null
fun resolveMode(): ConnectorMode {
return if (preferHttp) {
ConnectorMode.RPC_REQUESTS_WITH_MIXED_HEAD
} else {
if (connectorMode == null) {
if (ws == null) {
ConnectorMode.RPC_ONLY
} else {
ConnectorMode.WS_ONLY
}
} else {
ConnectorMode.parse(connectorMode!!)
}
}
}
}
class BitcoinConnection : RpcConnection() {
var esplora: HttpEndpoint? = null
var zeroMq: BitcoinZeroMq? = null
}
class EthereumPosConnection : UpstreamConnection() {
var execution: EthereumConnection? = null
var upstreamRating: Int = 0
}
data class BitcoinZeroMq(
val host: String = "127.0.0.1",
val port: Int
)
class HttpEndpoint(val url: URI) {
var basicAuth: AuthConfig.ClientBasicAuth? = null
var tls: AuthConfig.ClientTlsAuth? = null
}
class WsEndpoint(val url: URI) {
var origin: URI? = null
var basicAuth: AuthConfig.ClientBasicAuth? = null
var frameSize: Int? = null
var msgSize: Int? = null
var connections: Int = 1
}
// TODO make it unmodifiable after initial load
class Labels : HashMap<String, String>() {
companion object {
@JvmStatic
fun fromMap(map: Map<String, String>): Labels {
val labels = Labels()
map.entries.forEach { kv ->
labels.put(kv.key, kv.value)
}
return labels
}
}
}
enum class UpstreamType(vararg code: String) {
ETHEREUM_JSON_RPC("ethereum"),
BITCOIN_JSON_RPC("bitcoin"),
DSHACKLE("dshackle", "grpc"),
UNKNOWN("unknown");
private val code: Array<out String>
init {
this.code = code
Arrays.sort(this.code)
}
companion object {
fun byName(code: String): UpstreamType {
val cleanCode = code.lowercase(Locale.getDefault())
for (t in UpstreamType.values()) {
if (Arrays.binarySearch(t.code, cleanCode) >= 0) {
return t
}
}
return UNKNOWN
}
}
}
class Methods(
val enabled: Set<Method>,
val disabled: Set<Method>,
)
class MethodGroups(
val enabled: Set<String>,
val disabled: Set<String>,
)
class Method(
val name: String,
val quorum: String? = null,
val static: String? = null
)
}