7
.github/workflows/publish.yaml
vendored
7
.github/workflows/publish.yaml
vendored
@@ -18,11 +18,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: 20
|
java-version: 20
|
||||||
|
- name: Setup gradle
|
||||||
- name: Upload to Docker
|
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
- name: Upload to Docker
|
||||||
arguments: jib -Pdocker=drpcorg
|
run: make jib
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|||||||
8
.github/workflows/test.yaml
vendored
8
.github/workflows/test.yaml
vendored
@@ -30,14 +30,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Install System Libs
|
- name: Install System Libs
|
||||||
run: sudo apt-get install -y openssl libapr1
|
run: sudo apt-get install -y openssl libapr1
|
||||||
|
- name: Setup gradle
|
||||||
- name: Check
|
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
arguments: check
|
- name: Check
|
||||||
env:
|
run: make test
|
||||||
CI: true
|
|
||||||
|
|
||||||
- name: Upload Coverage Report
|
- name: Upload Coverage Report
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
|
|||||||
18
Makefile
Normal file
18
Makefile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
all: build-foundation build-main
|
||||||
|
|
||||||
|
build-foundation:
|
||||||
|
cd foundation && ../gradlew build publishToMavenLocal
|
||||||
|
|
||||||
|
build-main:
|
||||||
|
./gradlew build
|
||||||
|
|
||||||
|
test: build-foundation
|
||||||
|
./gradlew check
|
||||||
|
|
||||||
|
|
||||||
|
jib: build-foundation
|
||||||
|
./gradlew jib -Pdocker=drpcorg
|
||||||
|
|
||||||
|
clean:
|
||||||
|
./gradlew clean;
|
||||||
|
cd foundation && ../gradlew clean
|
||||||
14
build.gradle
14
build.gradle
@@ -18,6 +18,7 @@ plugins {
|
|||||||
id 'idea'
|
id 'idea'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'jacoco'
|
id 'jacoco'
|
||||||
|
id 'chainsconfig.codegen'
|
||||||
|
|
||||||
alias(libs.plugins.kotlin)
|
alias(libs.plugins.kotlin)
|
||||||
alias(libs.plugins.jib)
|
alias(libs.plugins.jib)
|
||||||
@@ -119,6 +120,7 @@ dependencies {
|
|||||||
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-aarch_64") })
|
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-aarch_64") })
|
||||||
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("linux-x86_64") })
|
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("linux-x86_64") })
|
||||||
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-x86_64") })
|
implementation(variantOf(libs.netty.tcnative.boringssl) { classifier("osx-x86_64") })
|
||||||
|
implementation 'dshackle:foundation:1.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
@@ -224,7 +226,9 @@ protobuf {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
resources.srcDirs += project.buildDir.absolutePath + "/generated/version"
|
resources.srcDirs += project.buildDir.absolutePath + "/generated/version"
|
||||||
|
kotlin {
|
||||||
|
srcDir project.buildDir.absolutePath + "/generated/kotlin"
|
||||||
|
}
|
||||||
proto {
|
proto {
|
||||||
srcDir 'emerald-grpc/proto'
|
srcDir 'emerald-grpc/proto'
|
||||||
}
|
}
|
||||||
@@ -318,3 +322,11 @@ static def getVersion() {
|
|||||||
|
|
||||||
return result.replaceAll(/^v/, '')
|
return result.replaceAll(/^v/, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ktlint {
|
||||||
|
filter {
|
||||||
|
exclude { element -> element.file.path.contains("generated/") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin.dependsOn chainscodegen
|
||||||
14
buildSrc/build.gradle.kts
Normal file
14
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.yaml:snakeyaml:1.24")
|
||||||
|
implementation("dshackle:foundation:1.0.0")
|
||||||
|
implementation("com.squareup:kotlinpoet:1.14.2")
|
||||||
|
}
|
||||||
118
buildSrc/src/main/kotlin/chainsconfig.codegen.gradle.kts
Normal file
118
buildSrc/src/main/kotlin/chainsconfig.codegen.gradle.kts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import com.squareup.kotlinpoet.*
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
|
||||||
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
|
import io.emeraldpay.dshackle.config.ChainsConfigReader
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
|
|
||||||
|
open class CodeGen(private val config: ChainsConfig) {
|
||||||
|
companion object {
|
||||||
|
fun generateFromChains(path: File) {
|
||||||
|
val chainConfigReader = ChainsConfigReader(ChainOptionsReader())
|
||||||
|
val config = chainConfigReader.read(null)
|
||||||
|
CodeGen(config).generateChainsFile().writeTo(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addEnumProperties(builder: TypeSpec.Builder): TypeSpec.Builder {
|
||||||
|
builder.addEnumConstant(
|
||||||
|
"UNSPECIFIED",
|
||||||
|
TypeSpec.anonymousClassBuilder()
|
||||||
|
.addSuperclassConstructorParameter("%L, %S, %S, %S, %L, %L", 0, "UNSPECIFIED", "Unknown", "0x0", 0, "emptyList()")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
for (chain in config) {
|
||||||
|
builder.addEnumConstant(
|
||||||
|
chain.blockchain.uppercase().replace('-', '_') + "__" + chain.id.uppercase().replace('-', '_'),
|
||||||
|
TypeSpec.anonymousClassBuilder()
|
||||||
|
.addSuperclassConstructorParameter(
|
||||||
|
"%L, %S, %S, %S, %L, %L",
|
||||||
|
chain.grpcId,
|
||||||
|
chain.code,
|
||||||
|
chain.blockchain.replaceFirstChar { it.uppercase() } + " " + chain.id.replaceFirstChar { it.uppercase() },
|
||||||
|
chain.chainId,
|
||||||
|
chain.netVersion,
|
||||||
|
"listOf(" + chain.shortNames.map { "\"${it}\"" }.joinToString() + ")",
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateChainsFile(): FileSpec {
|
||||||
|
val byIdFun = FunSpec.builder("byId")
|
||||||
|
.addParameter("id", Int::class)
|
||||||
|
.returns(ClassName("", "Chain"))
|
||||||
|
.beginControlFlow("for (chain in values())")
|
||||||
|
.beginControlFlow("if (chain.id == id)")
|
||||||
|
.addStatement("return chain")
|
||||||
|
.endControlFlow()
|
||||||
|
.endControlFlow()
|
||||||
|
.addStatement("return UNSPECIFIED")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val chainType = addEnumProperties(
|
||||||
|
TypeSpec.enumBuilder("Chain")
|
||||||
|
.addType(TypeSpec.companionObjectBuilder().addFunction(byIdFun).build())
|
||||||
|
.primaryConstructor(
|
||||||
|
FunSpec.constructorBuilder()
|
||||||
|
.addParameter("id", Int::class)
|
||||||
|
.addParameter("chainCode", String::class)
|
||||||
|
.addParameter("chainName", String::class)
|
||||||
|
.addParameter("chainId", String::class)
|
||||||
|
.addParameter("netVersion", Long::class)
|
||||||
|
.addParameter("shortNames", List::class.asClassName().parameterizedBy(String::class.asClassName()))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("id", Int::class)
|
||||||
|
.initializer("id")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("chainCode", String::class)
|
||||||
|
.initializer("chainCode")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("netVersion", Long::class)
|
||||||
|
.initializer("netVersion")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("chainId", String::class)
|
||||||
|
.initializer("chainId")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("chainName", String::class)
|
||||||
|
.initializer("chainName")
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.addProperty(
|
||||||
|
PropertySpec.builder("shortNames", List::class.asClassName().parameterizedBy(String::class.asClassName()))
|
||||||
|
.initializer("shortNames")
|
||||||
|
.build(),
|
||||||
|
),
|
||||||
|
).build()
|
||||||
|
return FileSpec.builder("io.emeraldpay.dshackle", "Chain")
|
||||||
|
.addType(chainType)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class ChainsCodeGenTask : DefaultTask() {
|
||||||
|
init {
|
||||||
|
group = "custom"
|
||||||
|
description = "Generate chains config"
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun chainscodegenClass() {
|
||||||
|
val output = project.layout.buildDirectory.dir("generated/kotlin").get().asFile
|
||||||
|
output.mkdirs()
|
||||||
|
CodeGen.generateFromChains(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<ChainsCodeGenTask>("chainscodegen")
|
||||||
32
foundation/build.gradle
Normal file
32
foundation/build.gradle
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
plugins {
|
||||||
|
id 'org.jetbrains.kotlin.jvm' version '1.9.10'
|
||||||
|
id 'maven-publish'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'dshackle'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.yaml:snakeyaml:1.24'
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
version '1.0.0'
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
mavenJava(MavenPublication) {
|
||||||
|
from components.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
}
|
||||||
0
foundation/settings.gradle
Normal file
0
foundation/settings.gradle
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class ChainOptions {
|
||||||
|
data class Options(
|
||||||
|
val disableUpstreamValidation: Boolean,
|
||||||
|
val disableValidation: Boolean,
|
||||||
|
val validationInterval: Int,
|
||||||
|
val timeout: Duration,
|
||||||
|
val providesBalance: Boolean?,
|
||||||
|
val validatePeers: Boolean,
|
||||||
|
val minPeers: Int,
|
||||||
|
val validateSyncing: Boolean,
|
||||||
|
val validateCallLimit: Boolean,
|
||||||
|
val validateChain: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
open class DefaultOptions : PartialOptions() {
|
||||||
|
var chains: List<String>? = null
|
||||||
|
var options: PartialOptions? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
open class PartialOptions {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getDefaults(): PartialOptions {
|
||||||
|
val options = PartialOptions()
|
||||||
|
options.minPeers = 1
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var disableValidation: Boolean? = null
|
||||||
|
var disableUpstreamValidation: Boolean? = null
|
||||||
|
var validationInterval: Int? = null
|
||||||
|
set(value) {
|
||||||
|
require(value == null || value > 0) {
|
||||||
|
"validation-interval must be a positive number: $value"
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
var timeout: Duration? = null
|
||||||
|
var providesBalance: Boolean? = null
|
||||||
|
var validatePeers: Boolean? = null
|
||||||
|
var validateCalllimit: Boolean? = null
|
||||||
|
var minPeers: Int? = null
|
||||||
|
set(value) {
|
||||||
|
require(value == null || value >= 0) {
|
||||||
|
"min-peers must be a positive number: $value"
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
var validateSyncing: Boolean? = null
|
||||||
|
var validateChain: Boolean? = null
|
||||||
|
|
||||||
|
fun merge(overwrites: PartialOptions?): PartialOptions {
|
||||||
|
if (overwrites == null) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
val copy = PartialOptions()
|
||||||
|
copy.validatePeers = overwrites.validatePeers ?: this.validatePeers
|
||||||
|
copy.minPeers = overwrites.minPeers ?: this.minPeers
|
||||||
|
copy.disableValidation = overwrites.disableValidation ?: this.disableValidation
|
||||||
|
copy.validationInterval = overwrites.validationInterval ?: this.validationInterval
|
||||||
|
copy.providesBalance = overwrites.providesBalance ?: this.providesBalance
|
||||||
|
copy.validateSyncing = overwrites.validateSyncing ?: this.validateSyncing
|
||||||
|
copy.validateCalllimit = overwrites.validateCalllimit ?: this.validateCalllimit
|
||||||
|
copy.timeout = overwrites.timeout ?: this.timeout
|
||||||
|
copy.validateChain = overwrites.validateChain ?: this.validateChain
|
||||||
|
copy.disableUpstreamValidation =
|
||||||
|
overwrites.disableUpstreamValidation ?: this.disableUpstreamValidation
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildOptions(): Options =
|
||||||
|
Options(
|
||||||
|
this.disableUpstreamValidation ?: false,
|
||||||
|
this.disableValidation ?: false,
|
||||||
|
this.validationInterval ?: 30,
|
||||||
|
this.timeout ?: Duration.ofSeconds(60),
|
||||||
|
this.providesBalance,
|
||||||
|
this.validatePeers ?: true,
|
||||||
|
this.minPeers ?: 1,
|
||||||
|
this.validateSyncing ?: true,
|
||||||
|
this.validateCalllimit ?: true,
|
||||||
|
this.validateChain ?: true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class ChainOptionsReader : YamlConfigReader<ChainOptions.PartialOptions>() {
|
||||||
|
override fun read(upNode: MappingNode?): ChainOptions.PartialOptions? {
|
||||||
|
return if (hasAny(upNode, "options")) {
|
||||||
|
return getMapping(upNode, "options")?.let { values ->
|
||||||
|
readOptions(values)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readOptions(values: MappingNode): ChainOptions.PartialOptions {
|
||||||
|
val options = ChainOptions.PartialOptions()
|
||||||
|
getValueAsBool(values, "validate-peers")?.let {
|
||||||
|
options.validatePeers = it
|
||||||
|
}
|
||||||
|
getValueAsBool(values, "validate-syncing")?.let {
|
||||||
|
options.validateSyncing = it
|
||||||
|
}
|
||||||
|
getValueAsBool(values, "validate-call-limit")?.let {
|
||||||
|
options.validateCalllimit = it
|
||||||
|
}
|
||||||
|
getValueAsBool(values, "validate-chain")?.let {
|
||||||
|
options.validateChain = it
|
||||||
|
}
|
||||||
|
getValueAsInt(values, "min-peers")?.let {
|
||||||
|
options.minPeers = it
|
||||||
|
}
|
||||||
|
getValueAsInt(values, "timeout")?.let {
|
||||||
|
options.timeout = Duration.ofSeconds(it.toLong())
|
||||||
|
}
|
||||||
|
getValueAsBool(values, "disable-validation")?.let {
|
||||||
|
options.disableValidation = it
|
||||||
|
}
|
||||||
|
getValueAsInt(values, "validation-interval")?.let {
|
||||||
|
options.validationInterval = it
|
||||||
|
}
|
||||||
|
getValueAsBool(values, "balance")?.let {
|
||||||
|
options.providesBalance = it
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update configuration value from environment variables. Format: ${ENV_VAR_NAME}
|
* Update configuration value from environment variables. Format: ${ENV_VAR_NAME}
|
||||||
@@ -14,12 +14,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
import org.yaml.snakeyaml.Yaml
|
import org.yaml.snakeyaml.Yaml
|
||||||
import org.yaml.snakeyaml.nodes.CollectionNode
|
import org.yaml.snakeyaml.nodes.CollectionNode
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import org.yaml.snakeyaml.nodes.Node
|
import org.yaml.snakeyaml.nodes.Node
|
||||||
|
import org.yaml.snakeyaml.nodes.NodeTuple
|
||||||
import org.yaml.snakeyaml.nodes.ScalarNode
|
import org.yaml.snakeyaml.nodes.ScalarNode
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
@@ -45,6 +46,32 @@ abstract class YamlConfigReader<T> : ConfigReader<T> {
|
|||||||
return asMappingNode(yaml.compose(InputStreamReader(input)))
|
return asMappingNode(yaml.compose(InputStreamReader(input)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mergeMappingNode(a: MappingNode?, b: MappingNode?): MappingNode? = when {
|
||||||
|
a == null -> b
|
||||||
|
b == null -> a
|
||||||
|
else -> {
|
||||||
|
val mergedTuples = a.value.toMutableList()
|
||||||
|
|
||||||
|
mergedTuples.addAll(
|
||||||
|
b.value.filter { tupleB ->
|
||||||
|
mergedTuples.none { it.keyNode.valueAsString() == tupleB.keyNode.valueAsString() } && (tupleB.valueNode is MappingNode || tupleB.valueNode is ScalarNode)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
a.value.forEach { tupleA ->
|
||||||
|
b.value.find { it.keyNode.valueAsString() == tupleA.keyNode.valueAsString() }?.let { tupleB ->
|
||||||
|
if (tupleA.valueNode is MappingNode && tupleB.valueNode is MappingNode) {
|
||||||
|
mergedTuples[mergedTuples.indexOf(tupleA)] = NodeTuple(tupleA.keyNode, mergeMappingNode(tupleA.valueNode as MappingNode, tupleB.valueNode as MappingNode))
|
||||||
|
} else if (tupleA.valueNode is ScalarNode && tupleB.valueNode is ScalarNode) {
|
||||||
|
mergedTuples[mergedTuples.indexOf(tupleA)] = NodeTuple(tupleA.keyNode, tupleB.valueNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MappingNode(a.tag, mergedTuples, a.flowStyle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected fun hasAny(mappingNode: MappingNode?, key: String): Boolean =
|
protected fun hasAny(mappingNode: MappingNode?, key: String): Boolean =
|
||||||
mappingNode?.let { node ->
|
mappingNode?.let { node ->
|
||||||
node.value
|
node.value
|
||||||
@@ -114,6 +141,16 @@ abstract class YamlConfigReader<T> : ConfigReader<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun getValueAsLong(mappingNode: MappingNode?, key: String): Long? {
|
||||||
|
return getValue(mappingNode, key)?.let {
|
||||||
|
return@let if (it.isPlain) {
|
||||||
|
it.value.toLongOrNull()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected fun getValueAsBool(mappingNode: MappingNode?, key: String): Boolean? {
|
protected fun getValueAsBool(mappingNode: MappingNode?, key: String): Boolean? {
|
||||||
return getValue(mappingNode, key)?.let {
|
return getValue(mappingNode, key)?.let {
|
||||||
return@let if (it.isPlain) {
|
return@let if (it.isPlain) {
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
data class ChainsConfig(private val chains: List<ChainConfig>) : Iterable<ChainsConfig.ChainConfig> {
|
||||||
|
private val chainMap: Map<String, ChainConfig> = chains.fold(emptyMap()) { acc, item ->
|
||||||
|
acc.plus(item.shortNames.map { Pair(it, item) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<ChainConfig> {
|
||||||
|
return chains.iterator()
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun default(): ChainsConfig = ChainsConfig(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ChainConfig(
|
||||||
|
val expectedBlockTime: Duration,
|
||||||
|
val syncingLagSize: Int,
|
||||||
|
val laggingLagSize: Int,
|
||||||
|
val options: ChainOptions.PartialOptions,
|
||||||
|
val chainId: String,
|
||||||
|
val netVersion: Long,
|
||||||
|
val grpcId: Int,
|
||||||
|
val code: String,
|
||||||
|
val shortNames: List<String>,
|
||||||
|
val callLimitContract: String?,
|
||||||
|
val id: String,
|
||||||
|
val blockchain: String,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun default() = ChainConfig(
|
||||||
|
Duration.ofSeconds(12),
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
ChainOptions.PartialOptions(),
|
||||||
|
"0x0",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"UNKNOWN",
|
||||||
|
emptyList(),
|
||||||
|
null,
|
||||||
|
"undefined",
|
||||||
|
"undefined",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resolve(chain: String): ChainConfig {
|
||||||
|
return chainMap[chain] ?: ChainConfig.default()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
|
import org.yaml.snakeyaml.DumperOptions
|
||||||
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
import org.yaml.snakeyaml.nodes.NodeTuple
|
||||||
|
import org.yaml.snakeyaml.nodes.ScalarNode
|
||||||
|
import org.yaml.snakeyaml.nodes.Tag
|
||||||
|
|
||||||
|
class ChainsConfigReader(
|
||||||
|
private val chainsOptionsReader: ChainOptionsReader,
|
||||||
|
) : YamlConfigReader<ChainsConfig>() {
|
||||||
|
|
||||||
|
private val defaultConfig = this::class.java.getResourceAsStream("/chains.yaml")!!
|
||||||
|
|
||||||
|
private fun readChains(input: MappingNode?): Map<Int, Pair<String, MappingNode>> {
|
||||||
|
return getMapping(input, "chain-settings")?.let { config ->
|
||||||
|
val default = getMapping(config, "default")
|
||||||
|
getList<MappingNode>(config, "protocols")?.let { protocols ->
|
||||||
|
protocols.value.fold(emptyMap()) { acc, protocol ->
|
||||||
|
val blockchain = getValueAsString(protocol, "id")
|
||||||
|
?: throw IllegalArgumentException("Blockchain id is not defined")
|
||||||
|
val settings = mergeMappingNode(default, getMapping(protocol, "settings"))
|
||||||
|
acc.plus(
|
||||||
|
getList<MappingNode>(protocol, "chains")?.let { chains ->
|
||||||
|
chains.value.map { chain ->
|
||||||
|
val chainSettings = mergeMappingNode(settings, getMapping(chain, "settings"))
|
||||||
|
val updatedChain = mergeMappingNode(
|
||||||
|
chain,
|
||||||
|
MappingNode(
|
||||||
|
chain.tag,
|
||||||
|
listOf(
|
||||||
|
NodeTuple(ScalarNode(Tag.STR, "settings", null, null, DumperOptions.ScalarStyle.LITERAL), chainSettings),
|
||||||
|
NodeTuple(
|
||||||
|
ScalarNode(Tag.STR, "blockchain", null, null, DumperOptions.ScalarStyle.LITERAL),
|
||||||
|
ScalarNode(Tag.STR, blockchain, null, null, DumperOptions.ScalarStyle.LITERAL),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
chain.flowStyle,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val grpcId = getValueAsInt(updatedChain, "grpcId")
|
||||||
|
?: throw IllegalArgumentException("grpcId for chain is not defined")
|
||||||
|
Pair(grpcId, Pair(blockchain, updatedChain!!))
|
||||||
|
}
|
||||||
|
} ?: listOf(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseChain(blockchain: String, node: MappingNode): ChainsConfig.ChainConfig {
|
||||||
|
val id = getValueAsString(node, "id")
|
||||||
|
?: throw IllegalArgumentException("undefined id for $blockchain")
|
||||||
|
val settings = getMapping(node, "settings") ?: throw IllegalArgumentException("undefined settings for $blockchain")
|
||||||
|
val lags = getMapping(settings, "lags")?.let { lagConfig ->
|
||||||
|
Pair(
|
||||||
|
getValueAsInt(lagConfig, "syncing")
|
||||||
|
?: throw IllegalArgumentException("undefined syncing for $blockchain"),
|
||||||
|
getValueAsInt(lagConfig, "lagging")
|
||||||
|
?: throw IllegalArgumentException("undefined lagging for $blockchain"),
|
||||||
|
)
|
||||||
|
} ?: throw IllegalArgumentException("undefined lags for $blockchain")
|
||||||
|
val expectedBlockTime = getValueAsDuration(settings, "expected-block-time")
|
||||||
|
?: throw IllegalArgumentException("undefined expected block time")
|
||||||
|
val validateContract = getValueAsString(node, "call-validate-contract")
|
||||||
|
val options = chainsOptionsReader.read(settings) ?: ChainOptions.PartialOptions()
|
||||||
|
val chainId = getValueAsString(node, "chain-id")
|
||||||
|
?: throw IllegalArgumentException("undefined chain id for $blockchain")
|
||||||
|
val code = getValueAsString(node, "code")
|
||||||
|
?: throw IllegalArgumentException("undefined code for $blockchain")
|
||||||
|
val grpcId = getValueAsInt(node, "grpcId")
|
||||||
|
?: throw IllegalArgumentException("undefined code for $blockchain")
|
||||||
|
val netVersion = getValueAsLong(node, "net-version") ?: chainId.drop(2).toLong(radix = 16)
|
||||||
|
val shortNames = getListOfString(node, "short-names")
|
||||||
|
?: throw IllegalArgumentException("undefined shortnames for $blockchain")
|
||||||
|
return ChainsConfig.ChainConfig(
|
||||||
|
expectedBlockTime = expectedBlockTime,
|
||||||
|
syncingLagSize = lags.first,
|
||||||
|
laggingLagSize = lags.second,
|
||||||
|
options = options,
|
||||||
|
callLimitContract = validateContract,
|
||||||
|
chainId = chainId,
|
||||||
|
code = code,
|
||||||
|
grpcId = grpcId,
|
||||||
|
netVersion = netVersion,
|
||||||
|
shortNames = shortNames,
|
||||||
|
id = id,
|
||||||
|
blockchain = blockchain,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(input: MappingNode?): ChainsConfig {
|
||||||
|
val default = readChains(readNode(defaultConfig))
|
||||||
|
val current = readChains(input)
|
||||||
|
val chains = default.keys.toSet().plus(current.keys).map {
|
||||||
|
val defChain = default[it]
|
||||||
|
val curChain = current[it]
|
||||||
|
when {
|
||||||
|
curChain == null && defChain != null -> parseChain(defChain.first, defChain.second)
|
||||||
|
curChain != null && defChain == null -> parseChain(curChain.first, curChain.second)
|
||||||
|
curChain != null && defChain != null -> {
|
||||||
|
val merged = mergeMappingNode(defChain.second, curChain.second)
|
||||||
|
parseChain(defChain.first, merged!!)
|
||||||
|
}
|
||||||
|
else -> ChainsConfig.ChainConfig.default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ChainsConfig(chains)
|
||||||
|
}
|
||||||
|
}
|
||||||
407
foundation/src/main/resources/chains.yaml
Normal file
407
foundation/src/main/resources/chains.yaml
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
version: v1
|
||||||
|
|
||||||
|
chain-settings:
|
||||||
|
default:
|
||||||
|
expected-block-time: 12s
|
||||||
|
lags:
|
||||||
|
syncing: 6
|
||||||
|
lagging: 1
|
||||||
|
protocols:
|
||||||
|
- id: bitcoin
|
||||||
|
settings:
|
||||||
|
expected-block-time: 10m
|
||||||
|
lags:
|
||||||
|
syncing: 3
|
||||||
|
lagging: 1
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
chain-id: 0x0
|
||||||
|
short-names: [bitcoin, btc]
|
||||||
|
code: BTC
|
||||||
|
grpcId: 1
|
||||||
|
- id: testnet
|
||||||
|
chain-id: 0x0
|
||||||
|
short-names: [bitcoin-testnet]
|
||||||
|
code: TESTNET_BITCOIN
|
||||||
|
grpcId: 10003
|
||||||
|
- id: ethereum
|
||||||
|
settings:
|
||||||
|
expected-block-time: 12s
|
||||||
|
lags:
|
||||||
|
syncing: 6
|
||||||
|
lagging: 1
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
chain-id: 0x1
|
||||||
|
short-names: [eth, ethereum, homestead]
|
||||||
|
code: ETH
|
||||||
|
grpcId: 100
|
||||||
|
call-validate-contract: 0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
|
||||||
|
- id: goerli
|
||||||
|
chain-id: 0x5
|
||||||
|
code: GOERLI
|
||||||
|
grpcId: 10005
|
||||||
|
short-names: [goerli, goerli-testnet]
|
||||||
|
call-validate-contract: 0xCD9303A1F6da2a68f465A579a24cc2Ee5AE2192f
|
||||||
|
- id: ropsten
|
||||||
|
code: ROPSTEN
|
||||||
|
grpcId: 10006
|
||||||
|
chain-id: 0x3
|
||||||
|
short-names: [ropsten, ropsten-testnet]
|
||||||
|
- id: sepolia
|
||||||
|
code: SEPOLIA
|
||||||
|
grpcId: 10008
|
||||||
|
chain-id: 0xaa36a7
|
||||||
|
short-names: [sepolia, sepolia-testnet]
|
||||||
|
- id: holesky
|
||||||
|
code: ETHEREUM_HOLESKY
|
||||||
|
grpcId: 10027
|
||||||
|
chain-id: 0x4268
|
||||||
|
short-names: [holesky, ethereum-holesky]
|
||||||
|
- id: ethereum-classic
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
short-names: [ethereum-classic, etc]
|
||||||
|
chain-id: 0x3d
|
||||||
|
net-version: 1
|
||||||
|
code: ETC
|
||||||
|
grpcId: 101
|
||||||
|
- id: fantom
|
||||||
|
settings:
|
||||||
|
expected-block-time: 3s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 10
|
||||||
|
lagging: 5
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
short-names: [fantom]
|
||||||
|
code: FTM
|
||||||
|
grpcId: 102
|
||||||
|
chain-id: 0xfa
|
||||||
|
- id: testnet
|
||||||
|
code: FANTOM_TESTNET
|
||||||
|
grpcId: 10016
|
||||||
|
short-names: [fantom-testnet]
|
||||||
|
chain-id: 0xfa2
|
||||||
|
- id: polygon
|
||||||
|
settings:
|
||||||
|
expected-block-time: 2.7s
|
||||||
|
lags:
|
||||||
|
syncing: 20
|
||||||
|
lagging: 10
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
call-validate-contract: 0x53Daa71B04d589429f6d3DF52db123913B818F23
|
||||||
|
code: POLYGON
|
||||||
|
grpcId: 1002
|
||||||
|
chain-id: 0x89
|
||||||
|
short-names: [polygon, matic]
|
||||||
|
- id: mumbai
|
||||||
|
call-validate-contract: 0x53Daa71B04d589429f6d3DF52db123913B818F23
|
||||||
|
code: POLYGON_POS_MUMBAI
|
||||||
|
grpcId: 10013
|
||||||
|
chain-id: 0x13881
|
||||||
|
short-names: [polygon-mumbai]
|
||||||
|
- id: arbitrum
|
||||||
|
settings:
|
||||||
|
expected-block-time: 260ms
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: ARBITRUM
|
||||||
|
grpcId: 1004
|
||||||
|
short-names: [arbitrum, arb]
|
||||||
|
chain-id: 0xa4b1
|
||||||
|
- id: goerli
|
||||||
|
code: ARBITRUM_TESTNET
|
||||||
|
grpcId: 10009
|
||||||
|
short-names: [arbitrum-testnet, arbitrum-goerli]
|
||||||
|
chain-id: 0x66eed
|
||||||
|
settings:
|
||||||
|
expected-block-time: 1s
|
||||||
|
- id: optimism
|
||||||
|
settings:
|
||||||
|
expected-block-time: 2s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: OPTIMISM
|
||||||
|
grpcId: 1005
|
||||||
|
short-names: [optimism]
|
||||||
|
chain-id: 0xa
|
||||||
|
- id: goerli
|
||||||
|
code: OPTIMISM_TESTNET
|
||||||
|
grpcId: 10010
|
||||||
|
short-names: [optimism-testnet, optimism-goerli]
|
||||||
|
chain-id: 0x1A4
|
||||||
|
- id: bsc
|
||||||
|
settings:
|
||||||
|
expected-block-time: 3s
|
||||||
|
lags:
|
||||||
|
syncing: 20
|
||||||
|
lagging: 10
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: BSC
|
||||||
|
grpcId: 1006
|
||||||
|
chain-id: 0x38
|
||||||
|
short-names: [bsc, binance, bnb-smart-chain]
|
||||||
|
- id: testnet
|
||||||
|
code: BSC_TESTNET
|
||||||
|
grpcId: 10026
|
||||||
|
short-names: [bsc-testnet]
|
||||||
|
chain-id: 0x61
|
||||||
|
- id: polygon-zkevm
|
||||||
|
settings:
|
||||||
|
expected-block-time: 2.7s
|
||||||
|
options:
|
||||||
|
disable-validation: true
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: POLYGON_ZKEVM
|
||||||
|
grpcId: 1007
|
||||||
|
short-names: [polygon-zkevm]
|
||||||
|
chain-id: 0x44d
|
||||||
|
- id: testnet
|
||||||
|
code: POLYGON_ZKEVM_TESTNET
|
||||||
|
grpcId: 10011
|
||||||
|
short-names: [polygon-zkevm-testnet]
|
||||||
|
chain-id: 0x5a2
|
||||||
|
settings:
|
||||||
|
expected-block-time: 1m
|
||||||
|
- id: arbitrum-nova
|
||||||
|
settings:
|
||||||
|
expected-block-time: 1s
|
||||||
|
options:
|
||||||
|
disable-validation: true
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: ARBITRUM_NOVA
|
||||||
|
grpcId: 1008
|
||||||
|
short-names: [arbitrum-nova]
|
||||||
|
chain-id: 0xa4ba
|
||||||
|
- id: zksync
|
||||||
|
settings:
|
||||||
|
expected-block-time: 5s
|
||||||
|
options:
|
||||||
|
disable-validation: true
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: ZKSYNC
|
||||||
|
grpcId: 1009
|
||||||
|
chain-id: 0x144
|
||||||
|
short-names: [zksync]
|
||||||
|
- id: testnet
|
||||||
|
code: ZKS_TESTNET
|
||||||
|
grpcId: 10012
|
||||||
|
chain-id: 0x118
|
||||||
|
short-names: [zksync-testnet]
|
||||||
|
- id: base
|
||||||
|
settings:
|
||||||
|
expected-block-time: 2s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: BASE
|
||||||
|
grpcId: 1010
|
||||||
|
short-names: [base]
|
||||||
|
chain-id: 0x2105
|
||||||
|
- id: goerli
|
||||||
|
code: BASE_GOERLI
|
||||||
|
grpcId: 10014
|
||||||
|
short-names: [base-goerli]
|
||||||
|
chain-id: 0x14a33
|
||||||
|
- id: linea
|
||||||
|
settings:
|
||||||
|
expected-block-time: 12s
|
||||||
|
lags:
|
||||||
|
syncing: 6
|
||||||
|
lagging: 1
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: LINEA
|
||||||
|
grpcId: 1011
|
||||||
|
short-names: [linea]
|
||||||
|
chain-id: 0xe708
|
||||||
|
- id: goerli
|
||||||
|
code: LINEA_GOERLI
|
||||||
|
grpcId: 10015
|
||||||
|
short-names: [linea-goerli]
|
||||||
|
chain-id: 0xe704
|
||||||
|
- id: gnosis
|
||||||
|
settings:
|
||||||
|
expected-block-time: 6s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 10
|
||||||
|
lagging: 5
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: GNOSIS
|
||||||
|
grpcId: 1012
|
||||||
|
short-names: [gnosis]
|
||||||
|
chain-id: 0x64
|
||||||
|
- id: chiado
|
||||||
|
code: GNOSIS_CHIADO
|
||||||
|
grpcId: 10017
|
||||||
|
short-names: [gnosis-chiado]
|
||||||
|
chain-id: 0x27d8
|
||||||
|
- id: avalanche
|
||||||
|
settings:
|
||||||
|
expected-block-time: 2s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
validate-syncing: false
|
||||||
|
lags:
|
||||||
|
syncing: 10
|
||||||
|
lagging: 5
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: AVALANCHE
|
||||||
|
grpcId: 1013
|
||||||
|
short-names: [avalanche]
|
||||||
|
chain-id: 0xa86a
|
||||||
|
- id: fuji
|
||||||
|
code: AVALANCHE_FUJI
|
||||||
|
grpcId: 10018
|
||||||
|
short-names: [avalanche-fuji]
|
||||||
|
chain-id: 0xa869
|
||||||
|
- id: aurora
|
||||||
|
settings:
|
||||||
|
expected-block-time: 1s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: AURORA
|
||||||
|
grpcId: 1015
|
||||||
|
short-names: [aurora]
|
||||||
|
chain-id: 0x4e454152
|
||||||
|
- id: testnet
|
||||||
|
code: AURORA_TESTNET
|
||||||
|
grpcId: 10021
|
||||||
|
short-names: [aurora-testnet]
|
||||||
|
chain-id: 0x4e454153
|
||||||
|
- id: mantle
|
||||||
|
settings:
|
||||||
|
expected-block-time: 500ms
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: MANTLE
|
||||||
|
grpcId: 1017
|
||||||
|
short-names: [mantle]
|
||||||
|
chain-id: 0x1388
|
||||||
|
- id: testnet
|
||||||
|
code: MANTLE_TESTNET
|
||||||
|
grpcId: 10023
|
||||||
|
short-names: [mantle-testnet]
|
||||||
|
chain-id: 0x1389
|
||||||
|
- id: klaytn
|
||||||
|
settings:
|
||||||
|
expected-block-time: 1s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 40
|
||||||
|
lagging: 20
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: KLAYTN
|
||||||
|
grpcId: 1018
|
||||||
|
short-names: [klaytn]
|
||||||
|
chain-id: 0x2019
|
||||||
|
- id: baobab
|
||||||
|
code: KLAYTN_BAOBAB
|
||||||
|
grpcId: 10024
|
||||||
|
short-names: [klaytn-baobab]
|
||||||
|
chain-id: 0x3e9
|
||||||
|
- id: celo
|
||||||
|
settings:
|
||||||
|
expected-block-time: 5s
|
||||||
|
lags:
|
||||||
|
syncing: 10
|
||||||
|
lagging: 5
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: CELO
|
||||||
|
grpcId: 1019
|
||||||
|
short-names: [celo]
|
||||||
|
chain-id: 0xa4ec
|
||||||
|
- id: alfajores
|
||||||
|
code: CELO_ALFAJORES
|
||||||
|
grpcId: 10028
|
||||||
|
short-names: [celo-alfajores]
|
||||||
|
chain-id: 0xaef3
|
||||||
|
- id: moonbeam
|
||||||
|
settings:
|
||||||
|
expected-block-time: 12s
|
||||||
|
lags:
|
||||||
|
syncing: 6
|
||||||
|
lagging: 1
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
code: MOONBEAM
|
||||||
|
grpcId: 1020
|
||||||
|
short-names: [moonbeam]
|
||||||
|
chain-id: 0x504
|
||||||
|
- id: moonriver
|
||||||
|
code: MOONRIVER
|
||||||
|
grpcId: 1021
|
||||||
|
short-names: [moonriver]
|
||||||
|
chain-id: 0x505
|
||||||
|
- id: moonbase-alpha
|
||||||
|
code: MOONBEAM_ALPHA
|
||||||
|
grpcId: 10029
|
||||||
|
short-names: [moonbase-alpha]
|
||||||
|
chain-id: 0x507
|
||||||
|
- id: scroll
|
||||||
|
settings:
|
||||||
|
expected-block-time: 3s
|
||||||
|
options:
|
||||||
|
validate-peers: false
|
||||||
|
lags:
|
||||||
|
syncing: 10
|
||||||
|
lagging: 5
|
||||||
|
chains:
|
||||||
|
- id: alphanet
|
||||||
|
code: SCROLL_ALPHANET
|
||||||
|
grpcId: 10022
|
||||||
|
short-names: [scroll-alphanet]
|
||||||
|
chain-id: 0x82751
|
||||||
|
- id: sepolia
|
||||||
|
code: SCROLL_SEPOLIA
|
||||||
|
grpcId: 10025
|
||||||
|
short-names: [scroll-sepolia]
|
||||||
|
chain-id: 0x8274f
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 EmeraldPay, Inc
|
||||||
|
*
|
||||||
|
* 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.foundation.EnvVariables
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource
|
||||||
|
|
||||||
|
class EnvVariablesTest {
|
||||||
|
|
||||||
|
lateinit var reader: EnvVariables
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
reader = EnvVariables()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = ["", "a", "13143", "/etc/client1.myservice.com.key", "true", "1a68f20154fc258fe4149c199ad8f281"])
|
||||||
|
fun `Post process for usual strings`(s: String) {
|
||||||
|
assertEquals(s, reader.postProcess(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"p_\${id}, p_1",
|
||||||
|
"home: \${HOME}, home: /home/user",
|
||||||
|
"\${PASSWORD}, 1a68f20154fc258fe4149c199ad8f281",
|
||||||
|
)
|
||||||
|
fun `Post process replaces from env`(orig: String, replaced: String) {
|
||||||
|
System.setProperty("id", "1")
|
||||||
|
System.setProperty("HOME", "/home/user")
|
||||||
|
System.setProperty("PASSWORD", "1a68f20154fc258fe4149c199ad8f281")
|
||||||
|
assertEquals(replaced, reader.postProcess(orig))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package io.emeraldpay.dshackle.foundation
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource
|
||||||
|
import org.yaml.snakeyaml.Yaml
|
||||||
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
import java.io.StringReader
|
||||||
|
|
||||||
|
class YamlConfigReaderTest {
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
"1024, 1024",
|
||||||
|
"1k, 1024",
|
||||||
|
"1kb, 1024",
|
||||||
|
"1K, 1024",
|
||||||
|
"16kb, 16384",
|
||||||
|
"1M, 1048576",
|
||||||
|
"4mb, 4194304",
|
||||||
|
)
|
||||||
|
fun `reads bytes values`(input: String, expected: Int) {
|
||||||
|
val rdr = Impl()
|
||||||
|
assertEquals(expected, rdr.getValueAsBytes(rdr.asNode("test", input), "test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `mergeMappingNode should merge nodes correctly`() {
|
||||||
|
val yaml = Yaml()
|
||||||
|
|
||||||
|
val a = yaml.compose(
|
||||||
|
StringReader(
|
||||||
|
"""
|
||||||
|
key1: value1
|
||||||
|
key2:
|
||||||
|
subkey1: subvalue1
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
) as MappingNode
|
||||||
|
|
||||||
|
val b = yaml.compose(
|
||||||
|
StringReader(
|
||||||
|
"""
|
||||||
|
key1: value3
|
||||||
|
key2:
|
||||||
|
subkey1: subvalue1Modified
|
||||||
|
subkey2: subvalue2
|
||||||
|
key3: value3
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
) as MappingNode
|
||||||
|
|
||||||
|
val result = Impl().mergeMappingNode(a, b)!!
|
||||||
|
val yml = Impl()
|
||||||
|
|
||||||
|
assertEquals(3, result.value.size)
|
||||||
|
assertEquals("value3", yml.getNodeValue(result, "key1"))
|
||||||
|
assertEquals("subvalue1Modified", yml.getNodeValue(yml.getNode(result, "key2") as MappingNode, "subkey1"))
|
||||||
|
assertEquals("subvalue2", yml.getNodeValue(yml.getNode(result, "key2") as MappingNode, "subkey2"))
|
||||||
|
assertEquals("value3", yml.getNodeValue(result, "key3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Impl : YamlConfigReader<Any>() {
|
||||||
|
override fun read(input: MappingNode?): Any? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
fun getNode(node: MappingNode, key: String): Any? {
|
||||||
|
return Impl().getMapping(node, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNodeValue(node: MappingNode, key: String): String? {
|
||||||
|
return Impl().getValueAsString(node, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asNode(key: String, value: String): MappingNode {
|
||||||
|
return Yaml().compose(StringReader("$key: $value")) as MappingNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.drpc.chainsconfig
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.config.ChainsConfigReader
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class ChainsConfigReaderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `read standard config without custom`() {
|
||||||
|
val reader = ChainsConfigReader(ChainOptionsReader())
|
||||||
|
val config = reader.read(null)
|
||||||
|
val arb = config.resolve("arbitrum-goerli")
|
||||||
|
assertEquals(arb.chainId, "0x66eed")
|
||||||
|
assertEquals(arb.expectedBlockTime.seconds, 1L)
|
||||||
|
assertEquals(arb.options.validatePeers, false)
|
||||||
|
assertEquals(arb.id, "goerli")
|
||||||
|
|
||||||
|
val ethc = config.resolve("ethereum-classic")
|
||||||
|
assertEquals(ethc.chainId, "0x3d")
|
||||||
|
assertEquals(ethc.netVersion, 1)
|
||||||
|
assertEquals(ethc.code, "ETC")
|
||||||
|
assertEquals(ethc.grpcId, 101)
|
||||||
|
assertEquals(ethc.expectedBlockTime.seconds, 12)
|
||||||
|
assertEquals(ethc.laggingLagSize, 1)
|
||||||
|
assertEquals(ethc.syncingLagSize, 6)
|
||||||
|
assertEquals(ethc.id, "mainnet")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `read standard config with custom one`() {
|
||||||
|
val reader = ChainsConfigReader(ChainOptionsReader())
|
||||||
|
val chains = reader.read(this.javaClass.classLoader.getResourceAsStream("configs/chains-basic.yaml")!!)!!
|
||||||
|
val config = chains.resolve("fantom")
|
||||||
|
assertEquals(config.expectedBlockTime.seconds, 10)
|
||||||
|
assertEquals(config.chainId, "0xfb")
|
||||||
|
assertEquals(config.syncingLagSize, 11)
|
||||||
|
assertEquals(config.laggingLagSize, 4)
|
||||||
|
assertEquals(config.code, "FTA")
|
||||||
|
assertEquals(config.grpcId, 102)
|
||||||
|
assertEquals(config.netVersion, 251)
|
||||||
|
}
|
||||||
|
}
|
||||||
18
foundation/src/test/resources/configs/chains-basic.yaml
Normal file
18
foundation/src/test/resources/configs/chains-basic.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
version: v1
|
||||||
|
|
||||||
|
chain-settings:
|
||||||
|
protocols:
|
||||||
|
- id: fantom
|
||||||
|
settings:
|
||||||
|
expected-block-time: 10s
|
||||||
|
options:
|
||||||
|
validate-peers: true
|
||||||
|
lags:
|
||||||
|
syncing: 11
|
||||||
|
lagging: 4
|
||||||
|
chains:
|
||||||
|
- id: mainnet
|
||||||
|
short-names: [fantom]
|
||||||
|
code: FTA
|
||||||
|
grpcId: 102
|
||||||
|
chain-id: 0xfb
|
||||||
@@ -1 +1 @@
|
|||||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||||
@@ -6,10 +6,10 @@ enum class BlockchainType {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val pow = setOf(
|
val pow = setOf(
|
||||||
Chain.ETHEREUM_CLASSIC__MAINNET, Chain.RSK__MAINNET, Chain.ETHEREUM__KOVAN,
|
Chain.ETHEREUM_CLASSIC__MAINNET,
|
||||||
Chain.ETHEREUM__MORDEN, Chain.ETHEREUM__RINKEBY
|
|
||||||
)
|
)
|
||||||
val bitcoin = setOf(Chain.BITCOIN__MAINNET, Chain.BITCOIN__TESTNET)
|
val bitcoin = setOf(Chain.BITCOIN__MAINNET, Chain.BITCOIN__TESTNET)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun from(chain: Chain): BlockchainType {
|
fun from(chain: Chain): BlockchainType {
|
||||||
return if (pow.contains(chain)) {
|
return if (pow.contains(chain)) {
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016-2019 ETCDEV GmbH, All Rights Reserved.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
enum class Chain(val id: Int, val chainCode: String, val chainName: String) {
|
|
||||||
UNSPECIFIED(0, "UNSPECIFIED", "Unknown"),
|
|
||||||
BITCOIN__MAINNET(1, "BTC", "Bitcoin"), // GRIN(2, "GRIN", "Grin"),
|
|
||||||
|
|
||||||
// Networks with tokens
|
|
||||||
ETHEREUM__MAINNET(100, "ETH", "Ethereum"),
|
|
||||||
ETHEREUM_CLASSIC__MAINNET(101, "ETC", "Ethereum Classic"),
|
|
||||||
FANTOM__MAINNET(
|
|
||||||
102,
|
|
||||||
"FTM",
|
|
||||||
"Fantom"
|
|
||||||
), // LIGHTNING(1001, "BTC_LN", "Bitcoin Lightning"),
|
|
||||||
POLYGON_POS__MAINNET(1002, "POLYGON", "Polygon Matic"),
|
|
||||||
RSK__MAINNET(1003, "RSK", "Bitcoin RSK"),
|
|
||||||
ARBITRUM__MAINNET(
|
|
||||||
1004,
|
|
||||||
"ARBITRUM",
|
|
||||||
"Arbitrum"
|
|
||||||
),
|
|
||||||
OPTIMISM__MAINNET(1005, "OPTIMISM", "Optimism"),
|
|
||||||
BSC__MAINNET(1006, "BSC", "Binance Smart Chain"),
|
|
||||||
POLYGON_ZKEVM__MAINNET(
|
|
||||||
1007,
|
|
||||||
"POLYGON_ZKEVM",
|
|
||||||
"Polygon ZK-EVM"
|
|
||||||
),
|
|
||||||
ARBITRUM_NOVA__MAINNET(
|
|
||||||
1008,
|
|
||||||
"ARBITRUM_NOVA",
|
|
||||||
"Arbitrum Nova"
|
|
||||||
),
|
|
||||||
ZKSYNC__MAINNET(1009, "ZKSYNC", "ZkSync Era"),
|
|
||||||
BASE__MAINNET(1010, "BASE", "Base"),
|
|
||||||
LINEA__MAINNET(1011, "LINEA", "Linea"),
|
|
||||||
GNOSIS__MAINNET(1012, "GNOSIS", "Gnosis"),
|
|
||||||
AVALANCHE__MAINNET(1013, "AVALANCHE", "Avalanche"),
|
|
||||||
// STARKNET__MAINNET(1014, "STARKNET", "Starknet"),
|
|
||||||
AURORA__MAINNET(1015, "AURORA", "Aurora"),
|
|
||||||
// SCROLL__MAINNET(1016, "SCROLL", "Scroll"),
|
|
||||||
MANTLE__MAINNET(1017, "MANTLE", "Mantle"),
|
|
||||||
KLAYTN__MAINNET(1018, "KLAYTN", "Klaytn"),
|
|
||||||
CELO__MAINNET(1019, "CELO_MAINNET", "Celo"),
|
|
||||||
MOONBEAM__MAINNET(1020, "MOONBEAM_MAINNET", "Moonbeam"),
|
|
||||||
MOONBEAM__MOONRIVER(1021, "MOONBASE_MOONRIVER", "Moonriver"),
|
|
||||||
|
|
||||||
// Testnets
|
|
||||||
ETHEREUM__MORDEN(10001, "MORDEN", "Morden Testnet"),
|
|
||||||
ETHEREUM__KOVAN(10002, "KOVAN", "Kovan Testnet"),
|
|
||||||
BITCOIN__TESTNET(
|
|
||||||
10003,
|
|
||||||
"TESTNET_BITCOIN",
|
|
||||||
"Bitcoin Testnet"
|
|
||||||
), // TESTNET_FLOONET(10004, "FLOONET", "Floonet Testnet"),
|
|
||||||
ETHEREUM__GOERLI(10005, "GOERLI", "Goerli Testnet"),
|
|
||||||
ETHEREUM__ROPSTEN(
|
|
||||||
10006,
|
|
||||||
"ROPSTEN",
|
|
||||||
"Ropsten Testnet"
|
|
||||||
),
|
|
||||||
ETHEREUM__RINKEBY(10007, "RINKEBY", "Rinkeby Testnet"),
|
|
||||||
ETHEREUM__SEPOLIA(10008, "SEPOLIA", "Sepolia Testnet"),
|
|
||||||
ARBITRUM__GOERLI(
|
|
||||||
10009,
|
|
||||||
"ARBITRUM_TESTNET",
|
|
||||||
"Arbitrum Testnet"
|
|
||||||
),
|
|
||||||
OPTIMISM__GOERLI(10010, "OPTIMISM_TESTNET", "Optimism Testnet"),
|
|
||||||
POLYGON_ZKEVM__TESTNET(
|
|
||||||
10011,
|
|
||||||
"POLYGON_ZKEVM_TESTNET",
|
|
||||||
"Polygon ZK-EVM Testnet"
|
|
||||||
),
|
|
||||||
ZKSYNC__TESTNET(
|
|
||||||
10012,
|
|
||||||
"ZKS_TESTNET",
|
|
||||||
"ZkSync Testnet"
|
|
||||||
),
|
|
||||||
POLYGON_POS__MUMBAI(
|
|
||||||
10013,
|
|
||||||
"POLYGON_POS_MUMBAI",
|
|
||||||
"Polygon POS Mumbai Testnet"
|
|
||||||
),
|
|
||||||
BASE__GOERLI(10014, "BASE_GOERLI", "Base Goerli Testnet"),
|
|
||||||
LINEA__GOERLI(10015, "LINEA_GOERLI", "Linea Goerli Testnet"),
|
|
||||||
FANTOM__TESTNET(10016, "FANTOM_TESTNET", "Fantom Testnet"),
|
|
||||||
GNOSIS__CHIADO(10017, "GNOSIS_CHIADO", "Gnosis Chiado Testnet"),
|
|
||||||
AVALANCHE__FUJI(10018, "AVALANCHE_FUJI", "Avalanche Fuji Testnet"),
|
|
||||||
// STARKNET__GOERLI(10019, "STARKNET_GOERLI", "Starknet Goerli"),
|
|
||||||
// STARKNET__GOERLI2(10020, "STARKNET_GOERLI2", "Starknet Goerli 2"),
|
|
||||||
AURORA__TESTNET(10021, "AURORA_TESTNET", "Aurora Testnet"),
|
|
||||||
SCROLL__ALPHANET(10022, "SCROLL_ALPHANET", "Scroll Alphanet"),
|
|
||||||
MANTLE__TESTNET(10023, "MANTLE_TESTNET", "Mantle Testnet"),
|
|
||||||
KLAYTN__BAOBAB(10024, "KLAYTN_BAOBAB", "Klaytn Baobab"),
|
|
||||||
SCROLL__SEPOLIA(10025, "SCROLL_SEPOLIA", "Scroll Sepolia"),
|
|
||||||
BSC__TESTNET(10026, "BSC_TESTNET", "Binance Smart Chain Testnet"),
|
|
||||||
ETHEREUM__HOLESKY(10027, "ETHEREUM_HOLESKY", "Ethereum Holesky"),
|
|
||||||
CELO__ALFAJORES(10028, "CELO_ALFAJORES", "Celo Alfajores"),
|
|
||||||
MOONBEAM__ALPHA(10029, "MOONBEAM_ALPHA", "Moonbase Alpha");
|
|
||||||
companion object {
|
|
||||||
fun byId(id: Int): Chain {
|
|
||||||
for (chain in values()) {
|
|
||||||
if (chain.id == id) {
|
|
||||||
return chain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UNSPECIFIED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -49,7 +49,7 @@ import kotlin.system.exitProcess
|
|||||||
@EnableAsync
|
@EnableAsync
|
||||||
open class Config(
|
open class Config(
|
||||||
private val env: Environment,
|
private val env: Environment,
|
||||||
private val ctx: ApplicationContext
|
private val ctx: ApplicationContext,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -43,78 +43,19 @@ class Global {
|
|||||||
|
|
||||||
var metricsExtended = false
|
var metricsExtended = false
|
||||||
|
|
||||||
val chainNames = mapOf(
|
|
||||||
"ethereum" to Chain.ETHEREUM__MAINNET,
|
|
||||||
"ethereum-classic" to Chain.ETHEREUM_CLASSIC__MAINNET,
|
|
||||||
"eth" to Chain.ETHEREUM__MAINNET,
|
|
||||||
"polygon" to Chain.POLYGON_POS__MAINNET,
|
|
||||||
"matic" to Chain.POLYGON_POS__MAINNET,
|
|
||||||
"arbitrum" to Chain.ARBITRUM__MAINNET,
|
|
||||||
"arb" to Chain.ARBITRUM__MAINNET,
|
|
||||||
"optimism" to Chain.OPTIMISM__MAINNET,
|
|
||||||
"binance" to Chain.BSC__MAINNET,
|
|
||||||
"bsc" to Chain.BSC__MAINNET,
|
|
||||||
"bnb-smart-chain" to Chain.BSC__MAINNET,
|
|
||||||
"bsc-testnet" to Chain.BSC__TESTNET,
|
|
||||||
"etc" to Chain.ETHEREUM_CLASSIC__MAINNET,
|
|
||||||
"morden" to Chain.ETHEREUM__MORDEN,
|
|
||||||
"kovan" to Chain.ETHEREUM__KOVAN,
|
|
||||||
"kovan-testnet" to Chain.ETHEREUM__KOVAN,
|
|
||||||
"goerli" to Chain.ETHEREUM__GOERLI,
|
|
||||||
"goerli-testnet" to Chain.ETHEREUM__GOERLI,
|
|
||||||
"rinkeby" to Chain.ETHEREUM__RINKEBY,
|
|
||||||
"rinkeby-testnet" to Chain.ETHEREUM__RINKEBY,
|
|
||||||
"ropsten" to Chain.ETHEREUM__ROPSTEN,
|
|
||||||
"ropsten-testnet" to Chain.ETHEREUM__ROPSTEN,
|
|
||||||
"bitcoin" to Chain.BITCOIN__MAINNET,
|
|
||||||
"bitcoin-testnet" to Chain.BITCOIN__TESTNET,
|
|
||||||
"sepolia" to Chain.ETHEREUM__SEPOLIA,
|
|
||||||
"sepolia-testnet" to Chain.ETHEREUM__SEPOLIA,
|
|
||||||
"ethereum-holesky" to Chain.ETHEREUM__HOLESKY,
|
|
||||||
"optimism-testnet" to Chain.OPTIMISM__GOERLI,
|
|
||||||
"arbitrum-testnet" to Chain.ARBITRUM__GOERLI,
|
|
||||||
"arbitrum-nova" to Chain.ARBITRUM_NOVA__MAINNET,
|
|
||||||
"polygon-zkevm" to Chain.POLYGON_ZKEVM__MAINNET,
|
|
||||||
"polygon-zkevm-testnet" to Chain.POLYGON_ZKEVM__TESTNET,
|
|
||||||
"zksync" to Chain.ZKSYNC__MAINNET,
|
|
||||||
"zksync-testnet" to Chain.ZKSYNC__TESTNET,
|
|
||||||
"polygon-mumbai" to Chain.POLYGON_POS__MUMBAI,
|
|
||||||
"base" to Chain.BASE__MAINNET,
|
|
||||||
"base-goerli" to Chain.BASE__GOERLI,
|
|
||||||
"linea" to Chain.LINEA__MAINNET,
|
|
||||||
"linea-goerli" to Chain.LINEA__GOERLI,
|
|
||||||
"fantom" to Chain.FANTOM__MAINNET,
|
|
||||||
"fantom-testnet" to Chain.FANTOM__TESTNET,
|
|
||||||
"gnosis" to Chain.GNOSIS__MAINNET,
|
|
||||||
"gnosis-chiado" to Chain.GNOSIS__CHIADO,
|
|
||||||
"avalanche" to Chain.AVALANCHE__MAINNET,
|
|
||||||
"avalanche-fuji" to Chain.AVALANCHE__FUJI,
|
|
||||||
// "starknet" to Chain.CHAIN_STARKNET__MAINNET,
|
|
||||||
// "starknet-goerli" to Chain.CHAIN_STARKNET__GOERLI,
|
|
||||||
// "starknet-goerli2" to Chain.CHAIN_STARKNET__GOERLI2,
|
|
||||||
"aurora" to Chain.AURORA__MAINNET,
|
|
||||||
"aurora-testnet" to Chain.AURORA__TESTNET,
|
|
||||||
// "scroll" to Chain.CHAIN_SCROLL__MAINNET,
|
|
||||||
"scroll-alphanet" to Chain.SCROLL__ALPHANET,
|
|
||||||
"scroll-sepolia" to Chain.SCROLL__SEPOLIA,
|
|
||||||
"mantle" to Chain.MANTLE__MAINNET,
|
|
||||||
"mantle-testnet" to Chain.MANTLE__TESTNET,
|
|
||||||
"klaytn" to Chain.KLAYTN__MAINNET,
|
|
||||||
"klaytn-baobab" to Chain.KLAYTN__BAOBAB,
|
|
||||||
"celo" to Chain.CELO__MAINNET,
|
|
||||||
"celo-alfajores" to Chain.CELO__ALFAJORES,
|
|
||||||
"moonriver" to Chain.MOONBEAM__MOONRIVER,
|
|
||||||
"moonbeam" to Chain.MOONBEAM__MAINNET,
|
|
||||||
"moonbase-alpha" to Chain.MOONBEAM__ALPHA
|
|
||||||
)
|
|
||||||
|
|
||||||
fun chainById(id: String?): Chain {
|
fun chainById(id: String?): Chain {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return Chain.UNSPECIFIED
|
return Chain.UNSPECIFIED
|
||||||
}
|
}
|
||||||
return chainNames[
|
return Chain.values().find { chain ->
|
||||||
id.lowercase(Locale.getDefault()).replace("_", "-").trim()
|
chain.shortNames.contains(id.lowercase(Locale.getDefault()).replace("_", "-").trim())
|
||||||
] ?: Chain.UNSPECIFIED
|
} ?: Chain.UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
fun chainByChainId(id: String): Chain {
|
||||||
|
return Chain.values().find { chain ->
|
||||||
|
chain.chainId == id
|
||||||
|
} ?: Chain.UNSPECIFIED
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
class AccessLogReader : YamlConfigReader<AccessLogConfig>() {
|
class AccessLogReader : YamlConfigReader<AccessLogConfig>() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import org.yaml.snakeyaml.nodes.ScalarNode
|
import org.yaml.snakeyaml.nodes.ScalarNode
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.util.ResourceUtils
|
import org.springframework.util.ResourceUtils
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
@@ -55,9 +56,10 @@ class AuthorizationConfigReader : YamlConfigReader<AuthorizationConfig>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AuthorizationConfig(
|
return AuthorizationConfig(
|
||||||
enabled, publicKeyOwner,
|
enabled,
|
||||||
|
publicKeyOwner,
|
||||||
authServer ?: AuthorizationConfig.ServerConfig.default(),
|
authServer ?: AuthorizationConfig.ServerConfig.default(),
|
||||||
authClient ?: AuthorizationConfig.ClientConfig.default()
|
authClient ?: AuthorizationConfig.ClientConfig.default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
|
||||||
import java.time.Duration
|
|
||||||
|
|
||||||
data class ChainsConfig(private val chains: Map<Chain, RawChainConfig>, val currentDefault: RawChainConfig?) {
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun default(): ChainsConfig = ChainsConfig(emptyMap(), RawChainConfig.default())
|
|
||||||
}
|
|
||||||
|
|
||||||
data class RawChainConfig(
|
|
||||||
var expectedBlockTime: Duration? = null,
|
|
||||||
var syncingLagSize: Int? = null,
|
|
||||||
var laggingLagSize: Int? = null,
|
|
||||||
var callLimitContract: String? = null,
|
|
||||||
var options: UpstreamsConfig.PartialOptions? = null
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun default() = RawChainConfig(
|
|
||||||
syncingLagSize = 6,
|
|
||||||
laggingLagSize = 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class ChainConfig(
|
|
||||||
val expectedBlockTime: Duration,
|
|
||||||
val syncingLagSize: Int,
|
|
||||||
val laggingLagSize: Int,
|
|
||||||
val options: UpstreamsConfig.PartialOptions,
|
|
||||||
val callLimitContract: String?
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun default() = ChainConfig(Duration.ofSeconds(12), 6, 1, UpstreamsConfig.PartialOptions(), null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resolve(chain: Chain): ChainConfig {
|
|
||||||
val default = currentDefault ?: panic()
|
|
||||||
val raw = chains[chain] ?: default
|
|
||||||
val options = default.options?.merge(raw.options) ?: raw.options ?: UpstreamsConfig.PartialOptions()
|
|
||||||
|
|
||||||
return ChainConfig(
|
|
||||||
laggingLagSize = raw.laggingLagSize ?: default.laggingLagSize ?: panic(),
|
|
||||||
syncingLagSize = raw.syncingLagSize ?: default.syncingLagSize ?: panic(),
|
|
||||||
options = options,
|
|
||||||
callLimitContract = raw.callLimitContract,
|
|
||||||
expectedBlockTime = raw.expectedBlockTime ?: default.expectedBlockTime ?: panic(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun patch(patch: ChainsConfig) = ChainsConfig(
|
|
||||||
merge(this.chains, patch.chains),
|
|
||||||
merge(this.currentDefault!!, patch.currentDefault)
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun merge(
|
|
||||||
current: RawChainConfig,
|
|
||||||
patch: RawChainConfig?
|
|
||||||
) = RawChainConfig(
|
|
||||||
syncingLagSize = patch?.syncingLagSize ?: current.syncingLagSize,
|
|
||||||
laggingLagSize = patch?.laggingLagSize ?: current.laggingLagSize,
|
|
||||||
options = patch?.options ?: current.options,
|
|
||||||
callLimitContract = patch?.callLimitContract ?: current.callLimitContract,
|
|
||||||
expectedBlockTime = patch?.expectedBlockTime ?: current.expectedBlockTime
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun merge(
|
|
||||||
current: Map<Chain, RawChainConfig>,
|
|
||||||
patch: Map<Chain, RawChainConfig>
|
|
||||||
): Map<Chain, RawChainConfig> {
|
|
||||||
val currentMut = current.toMutableMap()
|
|
||||||
|
|
||||||
for (k in patch) {
|
|
||||||
currentMut.merge(k.key, k.value) { v1, v2 -> merge(v1, v2) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentMut.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun panic(): Nothing = throw IllegalStateException("Chains settings state is illegal - default config is null")
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Global
|
|
||||||
import org.yaml.snakeyaml.nodes.CollectionNode
|
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class ChainsConfigReader(
|
|
||||||
private val upstreamsConfigReader: UpstreamsConfigReader
|
|
||||||
) : YamlConfigReader<ChainsConfig>() {
|
|
||||||
|
|
||||||
private val defaultConfig = this::class.java.getResourceAsStream("/chains.yaml")!!
|
|
||||||
|
|
||||||
override fun read(input: MappingNode?): ChainsConfig {
|
|
||||||
val default = readInternal(defaultConfig)
|
|
||||||
val current = readInternal(input)
|
|
||||||
val res = default.patch(current)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readInternal(input: InputStream): ChainsConfig {
|
|
||||||
val configNode = readNode(input)
|
|
||||||
return readInternal(configNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readInternal(input: MappingNode?): ChainsConfig {
|
|
||||||
return getMapping(input, "chain-settings")?.let {
|
|
||||||
|
|
||||||
val chains = getList<MappingNode>(it, "chains")?.let {
|
|
||||||
readChains(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
val default = getMapping(it, "default")?.let {
|
|
||||||
readChain(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChainsConfig(
|
|
||||||
chains
|
|
||||||
?.map { Global.chainById(it.first) to it.second }
|
|
||||||
?.associateBy({ it.first }, { it.second }) ?: emptyMap(),
|
|
||||||
default
|
|
||||||
)
|
|
||||||
} ?: ChainsConfig.default()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readChain(node: MappingNode): ChainsConfig.RawChainConfig? {
|
|
||||||
val rawConfig = ChainsConfig.RawChainConfig()
|
|
||||||
getMapping(node, "lags")?.let { lagConfig ->
|
|
||||||
getValueAsInt(lagConfig, "syncing")?.let {
|
|
||||||
rawConfig.syncingLagSize = it
|
|
||||||
}
|
|
||||||
getValueAsInt(lagConfig, "lagging")?.let {
|
|
||||||
rawConfig.laggingLagSize = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getValueAsDuration(node, "expected-block-time")?.let {
|
|
||||||
rawConfig.expectedBlockTime = it
|
|
||||||
}
|
|
||||||
getValueAsString(node, "call-validate-contract")?.let {
|
|
||||||
rawConfig.callLimitContract = it
|
|
||||||
}
|
|
||||||
upstreamsConfigReader.tryReadOptions(node)?.let {
|
|
||||||
rawConfig.options = it
|
|
||||||
}
|
|
||||||
|
|
||||||
return rawConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readChains(node: CollectionNode<MappingNode>): List<Pair<String, ChainsConfig.RawChainConfig>> {
|
|
||||||
return node.value.mapNotNull {
|
|
||||||
val key = getValueAsString(it, "id")
|
|
||||||
?: throw InvalidConfigYamlException(filename, it.startMark, "chain id required")
|
|
||||||
val value = readChain(it)
|
|
||||||
if (value != null) {
|
|
||||||
return@mapNotNull key to value
|
|
||||||
} else {
|
|
||||||
return@mapNotNull null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
class CompressionConfigReader : YamlConfigReader<CompressionConfig>() {
|
class CompressionConfigReader : YamlConfigReader<CompressionConfig>() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.config
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.CollectionNode
|
import org.yaml.snakeyaml.nodes.CollectionNode
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
class MainConfigReader(
|
class MainConfigReader(
|
||||||
fileResolver: FileResolver
|
fileResolver: FileResolver,
|
||||||
) : YamlConfigReader<MainConfig>() {
|
) : YamlConfigReader<MainConfig>() {
|
||||||
|
|
||||||
private val authConfigReader = AuthConfigReader()
|
private val authConfigReader = AuthConfigReader()
|
||||||
private val proxyConfigReader = ProxyConfigReader()
|
private val proxyConfigReader = ProxyConfigReader()
|
||||||
private val upstreamsConfigReader = UpstreamsConfigReader(fileResolver)
|
private val optionsReader = ChainOptionsReader()
|
||||||
|
private val upstreamsConfigReader = UpstreamsConfigReader(fileResolver, optionsReader)
|
||||||
private val cacheConfigReader = CacheConfigReader()
|
private val cacheConfigReader = CacheConfigReader()
|
||||||
private val tokensConfigReader = TokensConfigReader()
|
private val tokensConfigReader = TokensConfigReader()
|
||||||
private val monitoringConfigReader = MonitoringConfigReader()
|
private val monitoringConfigReader = MonitoringConfigReader()
|
||||||
@@ -32,7 +35,7 @@ class MainConfigReader(
|
|||||||
private val healthConfigReader = HealthConfigReader()
|
private val healthConfigReader = HealthConfigReader()
|
||||||
private val signatureConfigReader = SignatureConfigReader(fileResolver)
|
private val signatureConfigReader = SignatureConfigReader(fileResolver)
|
||||||
private val compressionConfigReader = CompressionConfigReader()
|
private val compressionConfigReader = CompressionConfigReader()
|
||||||
private val chainsConfigReader = ChainsConfigReader(upstreamsConfigReader)
|
private val chainsConfigReader = ChainsConfigReader(optionsReader)
|
||||||
private val authorizationConfigReader = AuthorizationConfigReader()
|
private val authorizationConfigReader = AuthorizationConfigReader()
|
||||||
|
|
||||||
override fun read(input: MappingNode?): MainConfig {
|
override fun read(input: MappingNode?): MainConfig {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
class MonitoringConfigReader : YamlConfigReader<MonitoringConfig>() {
|
class MonitoringConfigReader : YamlConfigReader<MonitoringConfig>() {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.config
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
|
|
||||||
class SignatureConfigReader(val fileResolver: FileResolver) : YamlConfigReader<SignatureConfig>() {
|
class SignatureConfigReader(val fileResolver: FileResolver) : YamlConfigReader<SignatureConfig>() {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|||||||
@@ -16,108 +16,22 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Defaults
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
|
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
|
||||||
import org.apache.commons.lang3.ObjectUtils.firstNonNull
|
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Duration
|
|
||||||
import java.util.Arrays
|
import java.util.Arrays
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
open class UpstreamsConfig {
|
open class UpstreamsConfig {
|
||||||
var defaultOptions: MutableList<DefaultOptions> = ArrayList<DefaultOptions>()
|
var defaultOptions: MutableList<ChainOptions.DefaultOptions> = ArrayList()
|
||||||
var upstreams: MutableList<Upstream<*>> = ArrayList<Upstream<*>>()
|
var upstreams: MutableList<Upstream<*>> = ArrayList<Upstream<*>>()
|
||||||
|
|
||||||
data class Options(
|
|
||||||
val disableUpstreamValidation: Boolean,
|
|
||||||
val disableValidation: Boolean,
|
|
||||||
val validationInterval: Int,
|
|
||||||
val timeout: Duration,
|
|
||||||
val providesBalance: Boolean?,
|
|
||||||
val validatePeers: Boolean,
|
|
||||||
val minPeers: Int,
|
|
||||||
val validateSyncing: Boolean,
|
|
||||||
val validateCallLimit: Boolean,
|
|
||||||
val validateChain: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
open class PartialOptions {
|
|
||||||
var disableValidation: Boolean? = null
|
|
||||||
var disableUpstreamValidation: Boolean? = null
|
|
||||||
var validationInterval: Int? = null
|
|
||||||
set(value) {
|
|
||||||
require(value == null || value > 0) {
|
|
||||||
"validation-interval must be a positive number: $value"
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
var timeout: Duration? = null
|
|
||||||
var providesBalance: Boolean? = null
|
|
||||||
var validatePeers: Boolean? = null
|
|
||||||
var validateCalllimit: Boolean? = null
|
|
||||||
var minPeers: Int? = null
|
|
||||||
set(value) {
|
|
||||||
require(value == null || value >= 0) {
|
|
||||||
"min-peers must be a positive number: $value"
|
|
||||||
}
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
var validateSyncing: Boolean? = null
|
|
||||||
var validateChain: Boolean? = null
|
|
||||||
|
|
||||||
fun merge(overwrites: PartialOptions?): PartialOptions {
|
|
||||||
if (overwrites == null) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
val copy = PartialOptions()
|
|
||||||
copy.validatePeers = firstNonNull(overwrites.validatePeers, this.validatePeers)
|
|
||||||
copy.minPeers = firstNonNull(overwrites.minPeers, this.minPeers)
|
|
||||||
copy.disableValidation = firstNonNull(overwrites.disableValidation, this.disableValidation)
|
|
||||||
copy.validationInterval = firstNonNull(overwrites.validationInterval, this.validationInterval)
|
|
||||||
copy.providesBalance = firstNonNull(overwrites.providesBalance, this.providesBalance)
|
|
||||||
copy.validateSyncing = firstNonNull(overwrites.validateSyncing, this.validateSyncing)
|
|
||||||
copy.validateCalllimit = firstNonNull(overwrites.validateCalllimit, this.validateCalllimit)
|
|
||||||
copy.timeout = firstNonNull(overwrites.timeout, this.timeout)
|
|
||||||
copy.validateChain = firstNonNull(overwrites.validateChain, this.validateChain)
|
|
||||||
copy.disableUpstreamValidation = firstNonNull(overwrites.disableUpstreamValidation, this.disableUpstreamValidation)
|
|
||||||
return copy
|
|
||||||
}
|
|
||||||
|
|
||||||
fun buildOptions(): Options =
|
|
||||||
Options(
|
|
||||||
firstNonNull(this.disableUpstreamValidation, false)!!,
|
|
||||||
firstNonNull(this.disableValidation, false)!!,
|
|
||||||
firstNonNull(this.validationInterval, 30)!!,
|
|
||||||
firstNonNull(this.timeout, Defaults.timeout)!!,
|
|
||||||
this.providesBalance,
|
|
||||||
firstNonNull(this.validatePeers, true)!!,
|
|
||||||
firstNonNull(this.minPeers, 1)!!,
|
|
||||||
firstNonNull(this.validateSyncing, true)!!,
|
|
||||||
firstNonNull(this.validateCalllimit, true)!!,
|
|
||||||
firstNonNull(this.validateChain, true)!!
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun getDefaults(): PartialOptions {
|
|
||||||
val options = PartialOptions()
|
|
||||||
options.minPeers = 1
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefaultOptions : PartialOptions() {
|
|
||||||
var chains: List<String>? = null
|
|
||||||
var options: PartialOptions? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
class Upstream<T : UpstreamConnection> {
|
class Upstream<T : UpstreamConnection> {
|
||||||
var id: String? = null
|
var id: String? = null
|
||||||
var nodeId: Int? = null
|
var nodeId: Int? = null
|
||||||
var chain: String? = null
|
var chain: String? = null
|
||||||
var options: PartialOptions? = null
|
var options: ChainOptions.PartialOptions? = null
|
||||||
var isEnabled = true
|
var isEnabled = true
|
||||||
var connection: T? = null
|
var connection: T? = null
|
||||||
val labels = Labels()
|
val labels = Labels()
|
||||||
@@ -137,7 +51,7 @@ open class UpstreamsConfig {
|
|||||||
enum class UpstreamRole {
|
enum class UpstreamRole {
|
||||||
PRIMARY,
|
PRIMARY,
|
||||||
SECONDARY,
|
SECONDARY,
|
||||||
FALLBACK
|
FALLBACK,
|
||||||
}
|
}
|
||||||
|
|
||||||
open class UpstreamConnection
|
open class UpstreamConnection
|
||||||
@@ -185,7 +99,7 @@ open class UpstreamsConfig {
|
|||||||
|
|
||||||
data class BitcoinZeroMq(
|
data class BitcoinZeroMq(
|
||||||
val host: String = "127.0.0.1",
|
val host: String = "127.0.0.1",
|
||||||
val port: Int
|
val port: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
class HttpEndpoint(val url: URI) {
|
class HttpEndpoint(val url: URI) {
|
||||||
@@ -220,7 +134,8 @@ open class UpstreamsConfig {
|
|||||||
ETHEREUM_JSON_RPC("ethereum"),
|
ETHEREUM_JSON_RPC("ethereum"),
|
||||||
BITCOIN_JSON_RPC("bitcoin"),
|
BITCOIN_JSON_RPC("bitcoin"),
|
||||||
DSHACKLE("dshackle", "grpc"),
|
DSHACKLE("dshackle", "grpc"),
|
||||||
UNKNOWN("unknown");
|
UNKNOWN("unknown"),
|
||||||
|
;
|
||||||
|
|
||||||
private val code: Array<out String>
|
private val code: Array<out String>
|
||||||
|
|
||||||
@@ -256,6 +171,6 @@ open class UpstreamsConfig {
|
|||||||
class Method(
|
class Method(
|
||||||
val name: String,
|
val name: String,
|
||||||
val quorum: String? = null,
|
val quorum: String? = null,
|
||||||
val static: String? = null
|
val static: String? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,19 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
import io.emeraldpay.dshackle.FileResolver
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
|
import io.emeraldpay.dshackle.foundation.YamlConfigReader
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
import org.yaml.snakeyaml.nodes.MappingNode
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.time.Duration
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class UpstreamsConfigReader(
|
class UpstreamsConfigReader(
|
||||||
private val fileResolver: FileResolver
|
private val fileResolver: FileResolver,
|
||||||
|
private val optionsReader: ChainOptionsReader,
|
||||||
) : YamlConfigReader<UpstreamsConfig>() {
|
) : YamlConfigReader<UpstreamsConfig>() {
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
|
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
|
||||||
@@ -39,28 +42,28 @@ class UpstreamsConfigReader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readInternal(input: InputStream): UpstreamsConfig? {
|
fun readInternal(input: InputStream): UpstreamsConfig {
|
||||||
val configNode = readNode(input)
|
val configNode = readNode(input)
|
||||||
return readInternal(configNode)
|
return readInternal(configNode)
|
||||||
}
|
}
|
||||||
fun readInternal(input: MappingNode?): UpstreamsConfig {
|
private fun readInternal(input: MappingNode?): UpstreamsConfig {
|
||||||
val config = UpstreamsConfig()
|
val config = UpstreamsConfig()
|
||||||
|
|
||||||
getList<MappingNode>(input, "defaults")?.value?.forEach { opts ->
|
getList<MappingNode>(input, "defaults")?.value?.forEach { opts ->
|
||||||
val defaultOptions = UpstreamsConfig.DefaultOptions()
|
val defaultOptions = ChainOptions.DefaultOptions()
|
||||||
config.defaultOptions.add(defaultOptions)
|
config.defaultOptions.add(defaultOptions)
|
||||||
defaultOptions.chains = getListOfString(opts, "chains")
|
defaultOptions.chains = getListOfString(opts, "chains")
|
||||||
getMapping(opts, "options")?.let { values ->
|
getMapping(opts, "options")?.let { values ->
|
||||||
defaultOptions.options = readOptions(values)
|
defaultOptions.options = optionsReader.readOptions(values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.upstreams = ArrayList<UpstreamsConfig.Upstream<*>>()
|
config.upstreams = ArrayList()
|
||||||
|
|
||||||
getValueAsString(input, "include")?.let { path ->
|
getValueAsString(input, "include")?.let { path ->
|
||||||
fileResolver.resolve(path).let { file ->
|
fileResolver.resolve(path).let { file ->
|
||||||
if (file.exists() && file.isFile && file.canRead()) {
|
if (file.exists() && file.isFile && file.canRead()) {
|
||||||
readInternal(file.inputStream())?.let {
|
readInternal(file.inputStream()).let {
|
||||||
it.upstreams.forEach { upstream -> config.upstreams.add(upstream) }
|
it.upstreams.forEach { upstream -> config.upstreams.add(upstream) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -72,7 +75,7 @@ class UpstreamsConfigReader(
|
|||||||
getListOfString(input, "include")?.forEach { path ->
|
getListOfString(input, "include")?.forEach { path ->
|
||||||
fileResolver.resolve(path).let { file ->
|
fileResolver.resolve(path).let { file ->
|
||||||
if (file.exists() && file.isFile && file.canRead()) {
|
if (file.exists() && file.isFile && file.canRead()) {
|
||||||
readInternal(file.inputStream())?.let {
|
readInternal(file.inputStream()).let {
|
||||||
it.upstreams.forEach { upstream -> config.upstreams.add(upstream) }
|
it.upstreams.forEach { upstream -> config.upstreams.add(upstream) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -222,7 +225,7 @@ class UpstreamsConfigReader(
|
|||||||
private fun <T : UpstreamsConfig.UpstreamConnection> readUpstream(
|
private fun <T : UpstreamsConfig.UpstreamConnection> readUpstream(
|
||||||
config: UpstreamsConfig,
|
config: UpstreamsConfig,
|
||||||
upNode: MappingNode,
|
upNode: MappingNode,
|
||||||
connFactory: () -> T
|
connFactory: () -> T,
|
||||||
) {
|
) {
|
||||||
val upstream = UpstreamsConfig.Upstream<T>()
|
val upstream = UpstreamsConfig.Upstream<T>()
|
||||||
readUpstreamCommon(upNode, upstream)
|
readUpstreamCommon(upNode, upstream)
|
||||||
@@ -256,10 +259,10 @@ class UpstreamsConfigReader(
|
|||||||
} ?: true
|
} ?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun readUpstreamCommon(upNode: MappingNode, upstream: UpstreamsConfig.Upstream<*>) {
|
private fun readUpstreamCommon(upNode: MappingNode, upstream: UpstreamsConfig.Upstream<*>) {
|
||||||
upstream.id = getValueAsString(upNode, "id")
|
upstream.id = getValueAsString(upNode, "id")
|
||||||
upstream.nodeId = getValueAsInt(upNode, "node-id")
|
upstream.nodeId = getValueAsInt(upNode, "node-id")
|
||||||
upstream.options = tryReadOptions(upNode)
|
upstream.options = optionsReader.read(upNode)
|
||||||
upstream.methods = tryReadMethods(upNode)
|
upstream.methods = tryReadMethods(upNode)
|
||||||
upstream.methodGroups = tryReadMethodGroups(upNode)
|
upstream.methodGroups = tryReadMethodGroups(upNode)
|
||||||
getValueAsBool(upNode, "enabled")?.let {
|
getValueAsBool(upNode, "enabled")?.let {
|
||||||
@@ -277,15 +280,15 @@ class UpstreamsConfigReader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun readUpstreamGrpc(
|
private fun readUpstreamGrpc(
|
||||||
upNode: MappingNode
|
upNode: MappingNode,
|
||||||
) {
|
) {
|
||||||
if (hasAny(upNode, "chain")) {
|
if (hasAny(upNode, "chain")) {
|
||||||
log.warn("Chain should be not applied to gRPC upstream")
|
log.warn("Chain should be not applied to gRPC upstream")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun readUpstreamStandard(upNode: MappingNode, upstream: UpstreamsConfig.Upstream<*>) {
|
private fun readUpstreamStandard(upNode: MappingNode, upstream: UpstreamsConfig.Upstream<*>) {
|
||||||
upstream.chain = getValueAsString(upNode, "chain")
|
upstream.chain = getValueAsString(upNode, "chain")
|
||||||
getValueAsString(upNode, "role")?.let {
|
getValueAsString(upNode, "role")?.let {
|
||||||
val name = it.trim().let {
|
val name = it.trim().let {
|
||||||
@@ -301,80 +304,38 @@ class UpstreamsConfigReader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun tryReadOptions(upNode: MappingNode): UpstreamsConfig.PartialOptions? {
|
private fun tryReadMethods(upNode: MappingNode): UpstreamsConfig.Methods? {
|
||||||
return if (hasAny(upNode, "options")) {
|
|
||||||
return getMapping(upNode, "options")?.let { values ->
|
|
||||||
readOptions(values)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun tryReadMethods(upNode: MappingNode): UpstreamsConfig.Methods? {
|
|
||||||
return getMapping(upNode, "methods")?.let { mnode ->
|
return getMapping(upNode, "methods")?.let { mnode ->
|
||||||
val enabled = getList<MappingNode>(mnode, "enabled")?.value?.map { m ->
|
val enabled = getList<MappingNode>(mnode, "enabled")?.value?.mapNotNull { m ->
|
||||||
getValueAsString(m, "name")?.let { name ->
|
getValueAsString(m, "name")?.let { name ->
|
||||||
UpstreamsConfig.Method(
|
UpstreamsConfig.Method(
|
||||||
name = name,
|
name = name,
|
||||||
quorum = getValueAsString(m, "quorum"),
|
quorum = getValueAsString(m, "quorum"),
|
||||||
static = getValueAsString(m, "static")
|
static = getValueAsString(m, "static"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}?.filterNotNull()?.toSet() ?: emptySet()
|
}?.toSet() ?: emptySet()
|
||||||
val disabled = getList<MappingNode>(mnode, "disabled")?.value?.map { m ->
|
val disabled = getList<MappingNode>(mnode, "disabled")?.value?.mapNotNull { m ->
|
||||||
getValueAsString(m, "name")?.let { name ->
|
getValueAsString(m, "name")?.let { name ->
|
||||||
UpstreamsConfig.Method(
|
UpstreamsConfig.Method(
|
||||||
name = name
|
name = name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}?.filterNotNull()?.toSet() ?: emptySet()
|
}?.toSet() ?: emptySet()
|
||||||
|
|
||||||
UpstreamsConfig.Methods(
|
UpstreamsConfig.Methods(
|
||||||
enabled,
|
enabled,
|
||||||
disabled
|
disabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun tryReadMethodGroups(upNode: MappingNode): UpstreamsConfig.MethodGroups? {
|
private fun tryReadMethodGroups(upNode: MappingNode): UpstreamsConfig.MethodGroups? {
|
||||||
return getMapping(upNode, "method-groups")?.let {
|
return getMapping(upNode, "method-groups")?.let {
|
||||||
UpstreamsConfig.MethodGroups(
|
UpstreamsConfig.MethodGroups(
|
||||||
enabled = getListOfString(it, "enabled")?.toSet() ?: emptySet(),
|
enabled = getListOfString(it, "enabled")?.toSet() ?: emptySet(),
|
||||||
disabled = getListOfString(it, "disabled")?.toSet() ?: emptySet()
|
disabled = getListOfString(it, "disabled")?.toSet() ?: emptySet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun readOptions(values: MappingNode): UpstreamsConfig.PartialOptions {
|
|
||||||
val options = UpstreamsConfig.PartialOptions()
|
|
||||||
getValueAsBool(values, "validate-peers")?.let {
|
|
||||||
options.validatePeers = it
|
|
||||||
}
|
|
||||||
getValueAsBool(values, "validate-syncing")?.let {
|
|
||||||
options.validateSyncing = it
|
|
||||||
}
|
|
||||||
getValueAsBool(values, "validate-call-limit")?.let {
|
|
||||||
options.validateCalllimit = it
|
|
||||||
}
|
|
||||||
getValueAsBool(values, "validate-chain")?.let {
|
|
||||||
options.validateChain = it
|
|
||||||
}
|
|
||||||
getValueAsInt(values, "min-peers")?.let {
|
|
||||||
options.minPeers = it
|
|
||||||
}
|
|
||||||
getValueAsInt(values, "timeout")?.let {
|
|
||||||
options.timeout = Duration.ofSeconds(it.toLong())
|
|
||||||
}
|
|
||||||
getValueAsBool(values, "disable-validation")?.let {
|
|
||||||
options.disableValidation = it
|
|
||||||
}
|
|
||||||
getValueAsInt(values, "validation-interval")?.let {
|
|
||||||
options.validationInterval = it
|
|
||||||
}
|
|
||||||
getValueAsBool(values, "balance")?.let {
|
|
||||||
options.providesBalance = it
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import io.emeraldpay.dshackle.config.AuthorizationConfig
|
|||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.CompressionConfig
|
import io.emeraldpay.dshackle.config.CompressionConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.upstream.BlockValidator
|
import io.emeraldpay.dshackle.upstream.BlockValidator
|
||||||
import io.emeraldpay.dshackle.upstream.CallTargetsHolder
|
import io.emeraldpay.dshackle.upstream.CallTargetsHolder
|
||||||
@@ -88,7 +89,7 @@ open class ConfiguredUpstreams(
|
|||||||
@Qualifier("headScheduler")
|
@Qualifier("headScheduler")
|
||||||
private val headScheduler: Scheduler,
|
private val headScheduler: Scheduler,
|
||||||
private val authorizationConfig: AuthorizationConfig,
|
private val authorizationConfig: AuthorizationConfig,
|
||||||
private val grpcAuthContext: GrpcAuthContext
|
private val grpcAuthContext: GrpcAuthContext,
|
||||||
) : ApplicationRunner {
|
) : ApplicationRunner {
|
||||||
@Value("\${spring.application.max-metadata-size}")
|
@Value("\${spring.application.max-metadata-size}")
|
||||||
private var maxMetadataSize: Int = Defaults.maxMetadataSize
|
private var maxMetadataSize: Int = Defaults.maxMetadataSize
|
||||||
@@ -109,7 +110,7 @@ open class ConfiguredUpstreams(
|
|||||||
}
|
}
|
||||||
log.debug("Start upstream ${up.id}")
|
log.debug("Start upstream ${up.id}")
|
||||||
if (up.connection is UpstreamsConfig.GrpcConnection) {
|
if (up.connection is UpstreamsConfig.GrpcConnection) {
|
||||||
val options = up.options ?: UpstreamsConfig.PartialOptions()
|
val options = up.options ?: ChainOptions.PartialOptions()
|
||||||
buildGrpcUpstream(up.nodeId, up.cast(UpstreamsConfig.GrpcConnection::class.java), options.buildOptions(), compressionConfig.grpc.clientEnabled)
|
buildGrpcUpstream(up.nodeId, up.cast(UpstreamsConfig.GrpcConnection::class.java), options.buildOptions(), compressionConfig.grpc.clientEnabled)
|
||||||
} else {
|
} else {
|
||||||
val chain = Global.chainById(up.chain)
|
val chain = Global.chainById(up.chain)
|
||||||
@@ -117,10 +118,10 @@ open class ConfiguredUpstreams(
|
|||||||
log.error("Chain is unknown: ${up.chain}")
|
log.error("Chain is unknown: ${up.chain}")
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
val chainConfig = chainsConfig.resolve(chain)
|
val chainConfig = chainsConfig.resolve(up.chain!!)
|
||||||
val options = chainConfig.options
|
val options = chainConfig.options
|
||||||
.merge(defaultOptions[chain] ?: UpstreamsConfig.PartialOptions.getDefaults())
|
.merge(defaultOptions[chain] ?: ChainOptions.PartialOptions.getDefaults())
|
||||||
.merge(up.options ?: UpstreamsConfig.PartialOptions())
|
.merge(up.options ?: ChainOptions.PartialOptions())
|
||||||
.buildOptions()
|
.buildOptions()
|
||||||
val upstream = when (BlockchainType.from(chain)) {
|
val upstream = when (BlockchainType.from(chain)) {
|
||||||
BlockchainType.EVM_POS -> {
|
BlockchainType.EVM_POS -> {
|
||||||
@@ -129,7 +130,7 @@ open class ConfiguredUpstreams(
|
|||||||
up.cast(UpstreamsConfig.EthereumPosConnection::class.java),
|
up.cast(UpstreamsConfig.EthereumPosConnection::class.java),
|
||||||
chain,
|
chain,
|
||||||
options,
|
options,
|
||||||
chainConfig
|
chainConfig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BlockchainType.EVM_POW -> {
|
BlockchainType.EVM_POW -> {
|
||||||
@@ -138,7 +139,7 @@ open class ConfiguredUpstreams(
|
|||||||
up.cast(UpstreamsConfig.EthereumConnection::class.java),
|
up.cast(UpstreamsConfig.EthereumConnection::class.java),
|
||||||
chain,
|
chain,
|
||||||
options,
|
options,
|
||||||
chainConfig
|
chainConfig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
BlockchainType.BITCOIN -> {
|
BlockchainType.BITCOIN -> {
|
||||||
@@ -146,7 +147,7 @@ open class ConfiguredUpstreams(
|
|||||||
up.cast(UpstreamsConfig.BitcoinConnection::class.java),
|
up.cast(UpstreamsConfig.BitcoinConnection::class.java),
|
||||||
chain,
|
chain,
|
||||||
options,
|
options,
|
||||||
chainConfig
|
chainConfig,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,8 +159,8 @@ open class ConfiguredUpstreams(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildDefaultOptions(config: UpstreamsConfig): HashMap<Chain, UpstreamsConfig.PartialOptions> {
|
private fun buildDefaultOptions(config: UpstreamsConfig): HashMap<Chain, ChainOptions.PartialOptions> {
|
||||||
val defaultOptions = HashMap<Chain, UpstreamsConfig.PartialOptions>()
|
val defaultOptions = HashMap<Chain, ChainOptions.PartialOptions>()
|
||||||
config.defaultOptions.forEach { defaultsConfig ->
|
config.defaultOptions.forEach { defaultsConfig ->
|
||||||
defaultsConfig.chains?.forEach { chainName ->
|
defaultsConfig.chains?.forEach { chainName ->
|
||||||
Global.chainById(chainName).let { chain ->
|
Global.chainById(chainName).let { chain ->
|
||||||
@@ -183,7 +184,7 @@ open class ConfiguredUpstreams(
|
|||||||
enabled = config.methods?.enabled?.map { it.name }?.toSet() ?: emptySet(),
|
enabled = config.methods?.enabled?.map { it.name }?.toSet() ?: emptySet(),
|
||||||
disabled = config.methods?.disabled?.map { it.name }?.toSet() ?: emptySet(),
|
disabled = config.methods?.disabled?.map { it.name }?.toSet() ?: emptySet(),
|
||||||
groupsEnabled = config.methodGroups?.enabled ?: emptySet(),
|
groupsEnabled = config.methodGroups?.enabled ?: emptySet(),
|
||||||
groupsDisabled = config.methodGroups?.disabled ?: emptySet()
|
groupsDisabled = config.methodGroups?.disabled ?: emptySet(),
|
||||||
).also {
|
).also {
|
||||||
config.methods?.enabled?.forEach { m ->
|
config.methods?.enabled?.forEach { m ->
|
||||||
if (m.quorum != null) {
|
if (m.quorum != null) {
|
||||||
@@ -203,8 +204,8 @@ open class ConfiguredUpstreams(
|
|||||||
nodeId: Int?,
|
nodeId: Int?,
|
||||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumPosConnection>,
|
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumPosConnection>,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
chainConf: ChainsConfig.ChainConfig
|
chainConf: ChainsConfig.ChainConfig,
|
||||||
): Upstream? {
|
): Upstream? {
|
||||||
val conn = config.connection!!
|
val conn = config.connection!!
|
||||||
val execution = conn.execution
|
val execution = conn.execution
|
||||||
@@ -220,7 +221,7 @@ open class ConfiguredUpstreams(
|
|||||||
urls,
|
urls,
|
||||||
NoChoiceWithPriorityForkChoice(conn.upstreamRating, config.id!!),
|
NoChoiceWithPriorityForkChoice(conn.upstreamRating, config.id!!),
|
||||||
BlockValidator.ALWAYS_VALID,
|
BlockValidator.ALWAYS_VALID,
|
||||||
chainConf
|
chainConf,
|
||||||
)
|
)
|
||||||
val methods = buildMethods(config, chain)
|
val methods = buildMethods(config, chain)
|
||||||
if (connectorFactory == null) {
|
if (connectorFactory == null) {
|
||||||
@@ -242,7 +243,7 @@ open class ConfiguredUpstreams(
|
|||||||
connectorFactory,
|
connectorFactory,
|
||||||
chainConf,
|
chainConf,
|
||||||
true,
|
true,
|
||||||
eventPublisher
|
eventPublisher,
|
||||||
)
|
)
|
||||||
upstream.start()
|
upstream.start()
|
||||||
if (!upstream.isRunning) {
|
if (!upstream.isRunning) {
|
||||||
@@ -255,8 +256,8 @@ open class ConfiguredUpstreams(
|
|||||||
private fun buildBitcoinUpstream(
|
private fun buildBitcoinUpstream(
|
||||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.BitcoinConnection>,
|
config: UpstreamsConfig.Upstream<UpstreamsConfig.BitcoinConnection>,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
chainConf: ChainsConfig.ChainConfig
|
chainConf: ChainsConfig.ChainConfig,
|
||||||
): Upstream? {
|
): Upstream? {
|
||||||
val conn = config.connection!!
|
val conn = config.connection!!
|
||||||
val httpFactory = buildHttpFactory(conn)
|
val httpFactory = buildHttpFactory(conn)
|
||||||
@@ -289,7 +290,7 @@ open class ConfiguredUpstreams(
|
|||||||
chain, directApi, head,
|
chain, directApi, head,
|
||||||
options, config.role,
|
options, config.role,
|
||||||
QuorumForLabels.QuorumItem(1, config.labels),
|
QuorumForLabels.QuorumItem(1, config.labels),
|
||||||
methods, esplora, chainConf
|
methods, esplora, chainConf,
|
||||||
)
|
)
|
||||||
upstream.start()
|
upstream.start()
|
||||||
return upstream
|
return upstream
|
||||||
@@ -299,8 +300,8 @@ open class ConfiguredUpstreams(
|
|||||||
nodeId: Int?,
|
nodeId: Int?,
|
||||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>,
|
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
chainConf: ChainsConfig.ChainConfig
|
chainConf: ChainsConfig.ChainConfig,
|
||||||
): Upstream? {
|
): Upstream? {
|
||||||
val conn = config.connection!!
|
val conn = config.connection!!
|
||||||
|
|
||||||
@@ -314,7 +315,7 @@ open class ConfiguredUpstreams(
|
|||||||
urls,
|
urls,
|
||||||
MostWorkForkChoice(),
|
MostWorkForkChoice(),
|
||||||
EthereumBlockValidator(),
|
EthereumBlockValidator(),
|
||||||
chainConf
|
chainConf,
|
||||||
)
|
)
|
||||||
if (connectorFactory == null) {
|
if (connectorFactory == null) {
|
||||||
return null
|
return null
|
||||||
@@ -331,7 +332,7 @@ open class ConfiguredUpstreams(
|
|||||||
connectorFactory,
|
connectorFactory,
|
||||||
chainConf,
|
chainConf,
|
||||||
false,
|
false,
|
||||||
eventPublisher
|
eventPublisher,
|
||||||
)
|
)
|
||||||
upstream.start()
|
upstream.start()
|
||||||
return upstream
|
return upstream
|
||||||
@@ -340,13 +341,13 @@ open class ConfiguredUpstreams(
|
|||||||
private fun buildGrpcUpstream(
|
private fun buildGrpcUpstream(
|
||||||
nodeId: Int?,
|
nodeId: Int?,
|
||||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>,
|
config: UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
compression: Boolean
|
compression: Boolean,
|
||||||
) {
|
) {
|
||||||
if (!this::grpcUpstreamsScheduler.isInitialized) {
|
if (!this::grpcUpstreamsScheduler.isInitialized) {
|
||||||
grpcUpstreamsScheduler = Schedulers.fromExecutorService(
|
grpcUpstreamsScheduler = Schedulers.fromExecutorService(
|
||||||
Executors.newFixedThreadPool(2),
|
Executors.newFixedThreadPool(2),
|
||||||
"GrpcUpstreamsStatuses"
|
"GrpcUpstreamsStatuses",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val endpoint = config.connection!!
|
val endpoint = config.connection!!
|
||||||
@@ -371,7 +372,7 @@ open class ConfiguredUpstreams(
|
|||||||
clientSpansInterceptor,
|
clientSpansInterceptor,
|
||||||
maxMetadataSize,
|
maxMetadataSize,
|
||||||
headScheduler,
|
headScheduler,
|
||||||
grpcAuthContext
|
grpcAuthContext,
|
||||||
).apply {
|
).apply {
|
||||||
timeout = options.timeout
|
timeout = options.timeout
|
||||||
}
|
}
|
||||||
@@ -399,14 +400,15 @@ open class ConfiguredUpstreams(
|
|||||||
id: String,
|
id: String,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
conn: UpstreamsConfig.EthereumConnection,
|
conn: UpstreamsConfig.EthereumConnection,
|
||||||
urls: ArrayList<URI>? = null
|
urls: ArrayList<URI>? = null,
|
||||||
): EthereumWsConnectionPoolFactory? {
|
): EthereumWsConnectionPoolFactory? {
|
||||||
return conn.ws?.let { endpoint ->
|
return conn.ws?.let { endpoint ->
|
||||||
val wsConnectionFactory = EthereumWsConnectionFactory(
|
val wsConnectionFactory = EthereumWsConnectionFactory(
|
||||||
id, chain,
|
id,
|
||||||
|
chain,
|
||||||
endpoint.url,
|
endpoint.url,
|
||||||
endpoint.origin ?: URI("http://localhost"),
|
endpoint.origin ?: URI("http://localhost"),
|
||||||
headScheduler
|
headScheduler,
|
||||||
).apply {
|
).apply {
|
||||||
config = endpoint
|
config = endpoint
|
||||||
basicAuth = endpoint.basicAuth
|
basicAuth = endpoint.basicAuth
|
||||||
@@ -414,7 +416,7 @@ open class ConfiguredUpstreams(
|
|||||||
val wsApi = EthereumWsConnectionPoolFactory(
|
val wsApi = EthereumWsConnectionPoolFactory(
|
||||||
id,
|
id,
|
||||||
endpoint.connections,
|
endpoint.connections,
|
||||||
wsConnectionFactory
|
wsConnectionFactory,
|
||||||
)
|
)
|
||||||
urls?.add(endpoint.url)
|
urls?.add(endpoint.url)
|
||||||
wsApi
|
wsApi
|
||||||
@@ -428,7 +430,7 @@ open class ConfiguredUpstreams(
|
|||||||
urls: ArrayList<URI>,
|
urls: ArrayList<URI>,
|
||||||
forkChoice: ForkChoice,
|
forkChoice: ForkChoice,
|
||||||
blockValidator: BlockValidator,
|
blockValidator: BlockValidator,
|
||||||
chainsConf: ChainsConfig.ChainConfig
|
chainsConf: ChainsConfig.ChainConfig,
|
||||||
): EthereumConnectorFactory? {
|
): EthereumConnectorFactory? {
|
||||||
val wsFactoryApi = buildWsFactory(id, chain, conn, urls)
|
val wsFactoryApi = buildWsFactory(id, chain, conn, urls)
|
||||||
val httpFactory = buildHttpFactory(conn, urls)
|
val httpFactory = buildHttpFactory(conn, urls)
|
||||||
@@ -442,7 +444,7 @@ open class ConfiguredUpstreams(
|
|||||||
blockValidator,
|
blockValidator,
|
||||||
wsConnectionResubscribeScheduler,
|
wsConnectionResubscribeScheduler,
|
||||||
headScheduler,
|
headScheduler,
|
||||||
chainsConf.expectedBlockTime
|
chainsConf.expectedBlockTime,
|
||||||
)
|
)
|
||||||
if (!connectorFactory.isValid()) {
|
if (!connectorFactory.isValid()) {
|
||||||
log.warn("Upstream configuration is invalid (probably no http endpoint)")
|
log.warn("Upstream configuration is invalid (probably no http endpoint)")
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package io.emeraldpay.dshackle.upstream
|
|||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -31,21 +32,21 @@ abstract class DefaultUpstream(
|
|||||||
private val hash: Byte,
|
private val hash: Byte,
|
||||||
defaultLag: Long?,
|
defaultLag: Long?,
|
||||||
defaultAvail: UpstreamAvailability,
|
defaultAvail: UpstreamAvailability,
|
||||||
private val options: UpstreamsConfig.Options,
|
private val options: ChainOptions.Options,
|
||||||
private val role: UpstreamsConfig.UpstreamRole,
|
private val role: UpstreamsConfig.UpstreamRole,
|
||||||
private val targets: CallMethods?,
|
private val targets: CallMethods?,
|
||||||
private val node: QuorumForLabels.QuorumItem?,
|
private val node: QuorumForLabels.QuorumItem?,
|
||||||
private val chainConfig: ChainsConfig.ChainConfig
|
private val chainConfig: ChainsConfig.ChainConfig,
|
||||||
) : Upstream {
|
) : Upstream {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: String,
|
id: String,
|
||||||
hash: Byte,
|
hash: Byte,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
targets: CallMethods?,
|
targets: CallMethods?,
|
||||||
node: QuorumForLabels.QuorumItem?,
|
node: QuorumForLabels.QuorumItem?,
|
||||||
chainConfig: ChainsConfig.ChainConfig
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
) :
|
) :
|
||||||
this(id, hash, null, UpstreamAvailability.UNAVAILABLE, options, role, targets, node, chainConfig)
|
this(id, hash, null, UpstreamAvailability.UNAVAILABLE, options, role, targets, node, chainConfig)
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ abstract class DefaultUpstream(
|
|||||||
Status(curr.lag, avail, statusByLag(curr.lag, avail))
|
Status(curr.lag, avail, statusByLag(curr.lag, avail))
|
||||||
}.also {
|
}.also {
|
||||||
statusStream.emitNext(
|
statusStream.emitNext(
|
||||||
it.status
|
it.status,
|
||||||
) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
||||||
log.trace("Status of upstream [$id] changed to [$it], requested change status to [$avail]")
|
log.trace("Status of upstream [$id] changed to [$it], requested change status to [$avail]")
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,9 @@ abstract class DefaultUpstream(
|
|||||||
lag > chainConfig.laggingLagSize -> UpstreamAvailability.LAGGING
|
lag > chainConfig.laggingLagSize -> UpstreamAvailability.LAGGING
|
||||||
else -> proposed
|
else -> proposed
|
||||||
}
|
}
|
||||||
} else proposed
|
} else {
|
||||||
|
proposed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun observeStatus(): Flux<UpstreamAvailability> {
|
override fun observeStatus(): Flux<UpstreamAvailability> {
|
||||||
@@ -109,7 +112,7 @@ abstract class DefaultUpstream(
|
|||||||
Status(nLag, curr.avail, statusByLag(nLag, curr.avail))
|
Status(nLag, curr.avail, statusByLag(nLag, curr.avail))
|
||||||
}.also {
|
}.also {
|
||||||
statusStream.emitNext(
|
statusStream.emitNext(
|
||||||
it.status
|
it.status,
|
||||||
) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
||||||
log.trace("Status of upstream [$id] changed to [$it], requested change lag to [$lag]")
|
log.trace("Status of upstream [$id] changed to [$it], requested change lag to [$lag]")
|
||||||
}
|
}
|
||||||
@@ -120,7 +123,7 @@ abstract class DefaultUpstream(
|
|||||||
return this.status.get().lag
|
return this.status.get().lag
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getOptions(): UpstreamsConfig.Options {
|
override fun getOptions(): ChainOptions.Options {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class FilteredApis(
|
|||||||
* Limit of retries
|
* Limit of retries
|
||||||
*/
|
*/
|
||||||
private val retryLimit: Long,
|
private val retryLimit: Long,
|
||||||
jitter: Int
|
jitter: Int,
|
||||||
) : ApiSource {
|
) : ApiSource {
|
||||||
private val internalMatcher: Selector.Matcher
|
private val internalMatcher: Selector.Matcher
|
||||||
|
|
||||||
@@ -77,13 +77,13 @@ class FilteredApis(
|
|||||||
chain: Chain,
|
chain: Chain,
|
||||||
allUpstreams: List<Upstream>,
|
allUpstreams: List<Upstream>,
|
||||||
matcher: Selector.Matcher,
|
matcher: Selector.Matcher,
|
||||||
pos: Int
|
pos: Int,
|
||||||
) : this(chain, allUpstreams, matcher, pos, 10, 7)
|
) : this(chain, allUpstreams, matcher, pos, 10, 7)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
allUpstreams: List<Upstream>,
|
allUpstreams: List<Upstream>,
|
||||||
matcher: Selector.Matcher
|
matcher: Selector.Matcher,
|
||||||
) : this(chain, allUpstreams, matcher, 0, 10, 10)
|
) : this(chain, allUpstreams, matcher, 0, 10, 10)
|
||||||
|
|
||||||
private val delay: Int
|
private val delay: Int
|
||||||
@@ -132,7 +132,7 @@ class FilteredApis(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
internalMatcher = Selector.MultiMatcher(
|
internalMatcher = Selector.MultiMatcher(
|
||||||
listOf(Selector.AvailabilityMatcher(), matcher)
|
listOf(Selector.AvailabilityMatcher(), matcher),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ class FilteredApis(
|
|||||||
val n = max(rawn, 1)
|
val n = max(rawn, 1)
|
||||||
val time = min(
|
val time = min(
|
||||||
(n.toDouble().pow(2.0) * delay).roundToLong(),
|
(n.toDouble().pow(2.0) * delay).roundToLong(),
|
||||||
MAX_WAIT_MILLIS
|
MAX_WAIT_MILLIS,
|
||||||
)
|
)
|
||||||
return Duration.ofMillis(time)
|
return Duration.ofMillis(time)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import io.emeraldpay.dshackle.Chain
|
|||||||
import io.emeraldpay.dshackle.cache.Caches
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
import io.emeraldpay.dshackle.cache.CachesEnabled
|
import io.emeraldpay.dshackle.cache.CachesEnabled
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
||||||
@@ -64,6 +65,7 @@ abstract class Multistream(
|
|||||||
private var cacheSubscription: Disposable? = null
|
private var cacheSubscription: Disposable? = null
|
||||||
private val reconfigLock = ReentrantLock()
|
private val reconfigLock = ReentrantLock()
|
||||||
private val eventLock = ReentrantLock()
|
private val eventLock = ReentrantLock()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var callMethods: CallMethods? = null
|
private var callMethods: CallMethods? = null
|
||||||
private var callMethodsFactory: Factory<CallMethods> = Factory {
|
private var callMethodsFactory: Factory<CallMethods> = Factory {
|
||||||
@@ -73,8 +75,10 @@ abstract class Multistream(
|
|||||||
private var seq = 0
|
private var seq = 0
|
||||||
protected var lagObserver: HeadLagObserver? = null
|
protected var lagObserver: HeadLagObserver? = null
|
||||||
private var subscription: Disposable? = null
|
private var subscription: Disposable? = null
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var capabilities: Set<Capability> = emptySet()
|
private var capabilities: Set<Capability> = emptySet()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var quorumLabels: List<QuorumForLabels.QuorumItem>? = null
|
private var quorumLabels: List<QuorumForLabels.QuorumItem>? = null
|
||||||
private val removed: MutableMap<String, Upstream> = HashMap()
|
private val removed: MutableMap<String, Upstream> = HashMap()
|
||||||
@@ -94,7 +98,7 @@ abstract class Multistream(
|
|||||||
Metrics.gauge(
|
Metrics.gauge(
|
||||||
"$metrics.availability",
|
"$metrics.availability",
|
||||||
listOf(Tag.of("chain", chain.chainCode), Tag.of("status", status.name.lowercase())),
|
listOf(Tag.of("chain", chain.chainCode), Tag.of("status", status.name.lowercase())),
|
||||||
this
|
this,
|
||||||
) {
|
) {
|
||||||
upstreams.count { it.getStatus() == status }.toDouble()
|
upstreams.count { it.getStatus() == status }.toDouble()
|
||||||
}
|
}
|
||||||
@@ -102,7 +106,8 @@ abstract class Multistream(
|
|||||||
|
|
||||||
Metrics.gauge(
|
Metrics.gauge(
|
||||||
"$metrics.connected",
|
"$metrics.connected",
|
||||||
listOf(Tag.of("chain", chain.chainCode)), this
|
listOf(Tag.of("chain", chain.chainCode)),
|
||||||
|
this,
|
||||||
) {
|
) {
|
||||||
upstreams.size.toDouble()
|
upstreams.size.toDouble()
|
||||||
}
|
}
|
||||||
@@ -254,13 +259,13 @@ abstract class Multistream(
|
|||||||
val upstreamsFluxes = getAll().map { up ->
|
val upstreamsFluxes = getAll().map { up ->
|
||||||
Flux.concat(
|
Flux.concat(
|
||||||
Mono.just(up.getStatus()),
|
Mono.just(up.getStatus()),
|
||||||
up.observeStatus()
|
up.observeStatus(),
|
||||||
).map { UpstreamStatus(up, it) }
|
).map { UpstreamStatus(up, it) }
|
||||||
}
|
}
|
||||||
val onShutdown = stopSignal.asFlux().map { UpstreamAvailability.UNAVAILABLE }
|
val onShutdown = stopSignal.asFlux().map { UpstreamAvailability.UNAVAILABLE }
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
Flux.merge(upstreamsFluxes).map(FilterBestAvailability()).takeUntilOther(stopSignal.asFlux()),
|
Flux.merge(upstreamsFluxes).map(FilterBestAvailability()).takeUntilOther(stopSignal.asFlux()),
|
||||||
onShutdown
|
onShutdown,
|
||||||
).distinct()
|
).distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,12 +275,15 @@ abstract class Multistream(
|
|||||||
|
|
||||||
override fun getStatus(): UpstreamAvailability {
|
override fun getStatus(): UpstreamAvailability {
|
||||||
val upstreams = getAll()
|
val upstreams = getAll()
|
||||||
return if (upstreams.isEmpty()) UpstreamAvailability.UNAVAILABLE
|
return if (upstreams.isEmpty()) {
|
||||||
else upstreams.minOf { it.getStatus() }
|
UpstreamAvailability.UNAVAILABLE
|
||||||
|
} else {
|
||||||
|
upstreams.minOf { it.getStatus() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO options for multistream are useless
|
// TODO options for multistream are useless
|
||||||
override fun getOptions(): UpstreamsConfig.Options {
|
override fun getOptions(): ChainOptions.Options {
|
||||||
throw IllegalStateException("Options are not supported for multistream")
|
throw IllegalStateException("Options are not supported for multistream")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +322,7 @@ abstract class Multistream(
|
|||||||
upstream.observeStatus().map { upstream }
|
upstream.observeStatus().map { upstream }
|
||||||
.takeUntilOther(
|
.takeUntilOther(
|
||||||
subscribeRemovedUpstreams()
|
subscribeRemovedUpstreams()
|
||||||
.filter { it.getId() == upstream.getId() }
|
.filter { it.getId() == upstream.getId() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.subscribe {
|
.subscribe {
|
||||||
|
|||||||
@@ -61,19 +61,19 @@ class Selector {
|
|||||||
Collections.unmodifiableCollection(
|
Collections.unmodifiableCollection(
|
||||||
req.andSelector.selectorsList.map {
|
req.andSelector.selectorsList.map {
|
||||||
convertToMatcher(
|
convertToMatcher(
|
||||||
it
|
it,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
req.hasOrSelector() -> OrMatcher(
|
req.hasOrSelector() -> OrMatcher(
|
||||||
Collections.unmodifiableCollection(
|
Collections.unmodifiableCollection(
|
||||||
req.orSelector.selectorsList.map {
|
req.orSelector.selectorsList.map {
|
||||||
convertToMatcher(
|
convertToMatcher(
|
||||||
it
|
it,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
req.hasNotSelector() -> NotMatcher(convertToMatcher(req.notSelector.selector))
|
req.hasNotSelector() -> NotMatcher(convertToMatcher(req.notSelector.selector))
|
||||||
req.hasExistsSelector() -> ExistsMatcher(req.existsSelector.name)
|
req.hasExistsSelector() -> ExistsMatcher(req.existsSelector.name)
|
||||||
@@ -138,7 +138,7 @@ class Selector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class MultiMatcher(
|
data class MultiMatcher(
|
||||||
private val matchers: Collection<Matcher>
|
private val matchers: Collection<Matcher>,
|
||||||
) : Matcher() {
|
) : Matcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(up: Upstream): MatchesResponse {
|
override fun matchesWithCause(up: Upstream): MatchesResponse {
|
||||||
@@ -147,7 +147,7 @@ class Selector {
|
|||||||
Success
|
Success
|
||||||
} else {
|
} else {
|
||||||
MatchesResponse.MultiResponse(
|
MatchesResponse.MultiResponse(
|
||||||
responses.filter { it !is Success }.toSet()
|
responses.filter { it !is Success }.toSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ class Selector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class MethodMatcher(
|
data class MethodMatcher(
|
||||||
val method: String
|
val method: String,
|
||||||
) : Matcher() {
|
) : Matcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(up: Upstream): MatchesResponse =
|
override fun matchesWithCause(up: Upstream): MatchesResponse =
|
||||||
@@ -202,7 +202,7 @@ class Selector {
|
|||||||
Success
|
Success
|
||||||
} else {
|
} else {
|
||||||
MatchesResponse.MultiResponse(
|
MatchesResponse.MultiResponse(
|
||||||
labelsResponses.filter { it !is Success }.toSet()
|
labelsResponses.filter { it !is Success }.toSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,7 +243,7 @@ class Selector {
|
|||||||
|
|
||||||
class LabelMatcher(
|
class LabelMatcher(
|
||||||
val name: String,
|
val name: String,
|
||||||
val values: Collection<String>
|
val values: Collection<String>,
|
||||||
) : LabelSelectorMatcher() {
|
) : LabelSelectorMatcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
||||||
@@ -254,7 +254,8 @@ class Selector {
|
|||||||
Success
|
Success
|
||||||
} else {
|
} else {
|
||||||
MatchesResponse.LabelResponse(
|
MatchesResponse.LabelResponse(
|
||||||
name, values
|
name,
|
||||||
|
values,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +264,7 @@ class Selector {
|
|||||||
return BlockchainOuterClass.Selector.newBuilder().setLabelSelector(
|
return BlockchainOuterClass.Selector.newBuilder().setLabelSelector(
|
||||||
BlockchainOuterClass.LabelSelector.newBuilder()
|
BlockchainOuterClass.LabelSelector.newBuilder()
|
||||||
.setName(name)
|
.setName(name)
|
||||||
.addAllValue(values)
|
.addAllValue(values),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +287,7 @@ class Selector {
|
|||||||
Success
|
Success
|
||||||
} else {
|
} else {
|
||||||
MatchesResponse.MultiResponse(
|
MatchesResponse.MultiResponse(
|
||||||
responses.filter { it !is Success }.toSet()
|
responses.filter { it !is Success }.toSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,7 +296,7 @@ class Selector {
|
|||||||
return BlockchainOuterClass.Selector.newBuilder().setOrSelector(
|
return BlockchainOuterClass.Selector.newBuilder().setOrSelector(
|
||||||
BlockchainOuterClass.OrSelector.newBuilder()
|
BlockchainOuterClass.OrSelector.newBuilder()
|
||||||
.addAllSelectors(matchers.map { it.asProto() })
|
.addAllSelectors(matchers.map { it.asProto() })
|
||||||
.build()
|
.build(),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +310,7 @@ class Selector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AndMatcher(
|
class AndMatcher(
|
||||||
val matchers: Collection<LabelSelectorMatcher>
|
val matchers: Collection<LabelSelectorMatcher>,
|
||||||
) : LabelSelectorMatcher() {
|
) : LabelSelectorMatcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
||||||
@@ -318,7 +319,7 @@ class Selector {
|
|||||||
Success
|
Success
|
||||||
} else {
|
} else {
|
||||||
MatchesResponse.MultiResponse(
|
MatchesResponse.MultiResponse(
|
||||||
responses.filter { it !is Success }.toSet()
|
responses.filter { it !is Success }.toSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +328,7 @@ class Selector {
|
|||||||
return BlockchainOuterClass.Selector.newBuilder().setAndSelector(
|
return BlockchainOuterClass.Selector.newBuilder().setAndSelector(
|
||||||
BlockchainOuterClass.AndSelector.newBuilder()
|
BlockchainOuterClass.AndSelector.newBuilder()
|
||||||
.addAllSelectors(matchers.map { it.asProto() })
|
.addAllSelectors(matchers.map { it.asProto() })
|
||||||
.build()
|
.build(),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +342,7 @@ class Selector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NotMatcher(
|
class NotMatcher(
|
||||||
val matcher: LabelSelectorMatcher
|
val matcher: LabelSelectorMatcher,
|
||||||
) : LabelSelectorMatcher() {
|
) : LabelSelectorMatcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse {
|
||||||
@@ -357,7 +358,7 @@ class Selector {
|
|||||||
return BlockchainOuterClass.Selector.newBuilder().setNotSelector(
|
return BlockchainOuterClass.Selector.newBuilder().setNotSelector(
|
||||||
BlockchainOuterClass.NotSelector.newBuilder()
|
BlockchainOuterClass.NotSelector.newBuilder()
|
||||||
.setSelector(matcher.asProto())
|
.setSelector(matcher.asProto())
|
||||||
.build()
|
.build(),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +372,7 @@ class Selector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ExistsMatcher(
|
class ExistsMatcher(
|
||||||
val name: String
|
val name: String,
|
||||||
) : LabelSelectorMatcher() {
|
) : LabelSelectorMatcher() {
|
||||||
|
|
||||||
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse =
|
override fun matchesWithCause(labels: UpstreamsConfig.Labels): MatchesResponse =
|
||||||
@@ -385,7 +386,7 @@ class Selector {
|
|||||||
return BlockchainOuterClass.Selector.newBuilder().setExistsSelector(
|
return BlockchainOuterClass.Selector.newBuilder().setExistsSelector(
|
||||||
BlockchainOuterClass.ExistsSelector.newBuilder()
|
BlockchainOuterClass.ExistsSelector.newBuilder()
|
||||||
.setName(name)
|
.setName(name)
|
||||||
.build()
|
.build(),
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package io.emeraldpay.dshackle.upstream
|
package io.emeraldpay.dshackle.upstream
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
@@ -32,7 +33,7 @@ interface Upstream {
|
|||||||
*/
|
*/
|
||||||
fun getIngressReader(): JsonRpcReader
|
fun getIngressReader(): JsonRpcReader
|
||||||
|
|
||||||
fun getOptions(): UpstreamsConfig.Options
|
fun getOptions(): ChainOptions.Options
|
||||||
fun getRole(): UpstreamsConfig.UpstreamRole
|
fun getRole(): UpstreamsConfig.UpstreamRole
|
||||||
fun setLag(lag: Long)
|
fun setLag(lag: Long)
|
||||||
fun getLag(): Long?
|
fun getLag(): Long?
|
||||||
|
|||||||
@@ -88,14 +88,13 @@ open class BitcoinMultistream(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val newHead = MergedHead(sourceUpstreams.map { it.getHead() }, MostWorkForkChoice(), headScheduler).apply {
|
val newHead = MergedHead(sourceUpstreams.map { it.getHead() }, MostWorkForkChoice(), headScheduler).apply { this.start() }
|
||||||
this.start()
|
|
||||||
}
|
|
||||||
newHead
|
newHead
|
||||||
}
|
}
|
||||||
onHeadUpdated(head)
|
onHeadUpdated(head)
|
||||||
return head
|
return head
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an API that executed directly on a remote.
|
* Finds an API that executed directly on a remote.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.upstream.bitcoin
|
|||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.Capability
|
import io.emeraldpay.dshackle.upstream.Capability
|
||||||
@@ -33,12 +34,12 @@ open class BitcoinRpcUpstream(
|
|||||||
chain: Chain,
|
chain: Chain,
|
||||||
private val directApi: JsonRpcReader,
|
private val directApi: JsonRpcReader,
|
||||||
private val head: Head,
|
private val head: Head,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
node: QuorumForLabels.QuorumItem,
|
node: QuorumForLabels.QuorumItem,
|
||||||
callMethods: CallMethods,
|
callMethods: CallMethods,
|
||||||
esploraClient: EsploraClient? = null,
|
esploraClient: EsploraClient? = null,
|
||||||
chainConfig: ChainsConfig.ChainConfig
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
) : BitcoinUpstream(id, chain, options, role, callMethods, node, esploraClient, chainConfig), Lifecycle {
|
) : BitcoinUpstream(id, chain, options, role, callMethods, node, esploraClient, chainConfig), Lifecycle {
|
||||||
|
|
||||||
private var validatorSubscription: Disposable? = null
|
private var validatorSubscription: Disposable? = null
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.upstream.bitcoin
|
|||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
@@ -26,19 +27,19 @@ import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
|
|||||||
abstract class BitcoinUpstream(
|
abstract class BitcoinUpstream(
|
||||||
id: String,
|
id: String,
|
||||||
val chain: Chain,
|
val chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
callMethods: CallMethods,
|
callMethods: CallMethods,
|
||||||
node: QuorumForLabels.QuorumItem,
|
node: QuorumForLabels.QuorumItem,
|
||||||
val esploraClient: EsploraClient? = null,
|
val esploraClient: EsploraClient? = null,
|
||||||
chainConfig: ChainsConfig.ChainConfig
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
) : DefaultUpstream(id, 0.toByte(), options, role, callMethods, node, chainConfig) {
|
) : DefaultUpstream(id, 0.toByte(), options, role, callMethods, node, chainConfig) {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: String,
|
id: String,
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
chainConfig: ChainsConfig.ChainConfig
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
) : this(id, chain, options, role, DefaultBitcoinMethods(), QuorumForLabels.QuorumItem.empty(), null, chainConfig)
|
) : this(id, chain, options, role, DefaultBitcoinMethods(), QuorumForLabels.QuorumItem.empty(), null, chainConfig)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.upstream.bitcoin
|
package io.emeraldpay.dshackle.upstream.bitcoin
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
@@ -30,7 +30,7 @@ import java.util.concurrent.Executors
|
|||||||
|
|
||||||
class BitcoinUpstreamValidator(
|
class BitcoinUpstreamValidator(
|
||||||
private val api: JsonRpcReader,
|
private val api: JsonRpcReader,
|
||||||
private val options: UpstreamsConfig.Options
|
private val options: ChainOptions.Options,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import io.emeraldpay.dshackle.quorum.CallQuorum
|
|||||||
import io.emeraldpay.dshackle.quorum.MaximumValueQuorum
|
import io.emeraldpay.dshackle.quorum.MaximumValueQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.NotNullQuorum
|
import io.emeraldpay.dshackle.quorum.NotNullQuorum
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods.HardcodedData.Companion.createHardcodedData
|
|
||||||
import io.emeraldpay.etherjar.rpc.RpcException
|
import io.emeraldpay.etherjar.rpc.RpcException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +31,7 @@ import io.emeraldpay.etherjar.rpc.RpcException
|
|||||||
* hardcoded results for base methods, such as `net_version`, `web3_clientVersion` and similar
|
* hardcoded results for base methods, such as `net_version`, `web3_clientVersion` and similar
|
||||||
*/
|
*/
|
||||||
class DefaultEthereumMethods(
|
class DefaultEthereumMethods(
|
||||||
private val chain: Chain
|
private val chain: Chain,
|
||||||
) : CallMethods {
|
) : CallMethods {
|
||||||
|
|
||||||
private val version = "\"EmeraldDshackle/${Global.version}\""
|
private val version = "\"EmeraldDshackle/${Global.version}\""
|
||||||
@@ -41,7 +40,7 @@ class DefaultEthereumMethods(
|
|||||||
val withFilterIdMethods = listOf(
|
val withFilterIdMethods = listOf(
|
||||||
"eth_getFilterChanges",
|
"eth_getFilterChanges",
|
||||||
"eth_getFilterLogs",
|
"eth_getFilterLogs",
|
||||||
"eth_uninstallFilter"
|
"eth_uninstallFilter",
|
||||||
)
|
)
|
||||||
|
|
||||||
val newFilterMethods = listOf(
|
val newFilterMethods = listOf(
|
||||||
@@ -69,79 +68,13 @@ class DefaultEthereumMethods(
|
|||||||
"debug_traceBlockByNumber",
|
"debug_traceBlockByNumber",
|
||||||
"debug_traceCall",
|
"debug_traceCall",
|
||||||
"debug_traceCallMany",
|
"debug_traceCallMany",
|
||||||
"debug_traceTransaction"
|
"debug_traceTransaction",
|
||||||
)
|
)
|
||||||
|
|
||||||
val CHAIN_DATA = mapOf(
|
|
||||||
Chain.ETHEREUM__MAINNET to createHardcodedData("\"1\"", "\"0x1\""),
|
|
||||||
Chain.ETHEREUM__RINKEBY to createHardcodedData("\"4\"", "\"0x4\""),
|
|
||||||
Chain.ETHEREUM__ROPSTEN to createHardcodedData("\"3\"", "\"0x3\""),
|
|
||||||
Chain.ETHEREUM__KOVAN to createHardcodedData("\"42\"", "\"0x2a\""),
|
|
||||||
Chain.ETHEREUM__GOERLI to createHardcodedData("\"5\"", "\"0x5\""),
|
|
||||||
Chain.ETHEREUM__SEPOLIA to createHardcodedData("\"11155111\"", "\"0xaa36a7\""),
|
|
||||||
Chain.ETHEREUM__HOLESKY to createHardcodedData("\"17000\"", "\"0x4268\""),
|
|
||||||
|
|
||||||
Chain.ETHEREUM_CLASSIC__MAINNET to createHardcodedData("\"1\"", "\"0x3d\""),
|
|
||||||
|
|
||||||
Chain.POLYGON_POS__MAINNET to createHardcodedData("\"137\"", "\"0x89\""),
|
|
||||||
Chain.POLYGON_POS__MUMBAI to createHardcodedData("\"80001\"", "\"0x13881\""),
|
|
||||||
|
|
||||||
Chain.ARBITRUM__MAINNET to createHardcodedData("\"42161\"", "\"0xa4b1\""),
|
|
||||||
Chain.ARBITRUM__GOERLI to createHardcodedData("\"421613\"", "\"0x66eed\""),
|
|
||||||
|
|
||||||
Chain.OPTIMISM__MAINNET to createHardcodedData("\"10\"", "\"0xa\""),
|
|
||||||
Chain.OPTIMISM__GOERLI to createHardcodedData("\"420\"", "\"0x1A4\""),
|
|
||||||
|
|
||||||
Chain.ARBITRUM_NOVA__MAINNET to createHardcodedData("\"42170\"", "\"0xa4ba\""),
|
|
||||||
|
|
||||||
Chain.POLYGON_ZKEVM__MAINNET to createHardcodedData("\"1101\"", "\"0x44d\""),
|
|
||||||
Chain.POLYGON_ZKEVM__TESTNET to createHardcodedData("\"1442\"", "\"0x5a2\""),
|
|
||||||
|
|
||||||
Chain.ZKSYNC__MAINNET to createHardcodedData("\"324\"", "\"0x144\""),
|
|
||||||
Chain.ZKSYNC__TESTNET to createHardcodedData("\"280\"", "\"0x118\""),
|
|
||||||
|
|
||||||
Chain.BSC__MAINNET to createHardcodedData("\"56\"", "\"0x38\""),
|
|
||||||
Chain.BSC__TESTNET to createHardcodedData("\"97\"", "\"0x61\""),
|
|
||||||
|
|
||||||
Chain.BASE__MAINNET to createHardcodedData("\"8453\"", "\"0x2105\""),
|
|
||||||
Chain.BASE__GOERLI to createHardcodedData("\"84531\"", "\"0x14a33\""),
|
|
||||||
|
|
||||||
Chain.LINEA__MAINNET to createHardcodedData("\"59144\"", "\"0xe708\""),
|
|
||||||
Chain.LINEA__GOERLI to createHardcodedData("\"59140\"", "\"0xe704\""),
|
|
||||||
|
|
||||||
Chain.FANTOM__MAINNET to createHardcodedData("\"250\"", "\"0xfa\""),
|
|
||||||
Chain.FANTOM__TESTNET to createHardcodedData("\"4002\"", "\"0xfa2\""),
|
|
||||||
|
|
||||||
Chain.GNOSIS__MAINNET to createHardcodedData("\"100\"", "\"0x64\""),
|
|
||||||
Chain.GNOSIS__CHIADO to createHardcodedData("\"10200\"", "\"0x27d8\""),
|
|
||||||
|
|
||||||
Chain.AVALANCHE__MAINNET to createHardcodedData("\"43114\"", "\"0xa86a\""),
|
|
||||||
Chain.AVALANCHE__FUJI to createHardcodedData("\"43113\"", "\"0xa869\""),
|
|
||||||
|
|
||||||
Chain.AURORA__MAINNET to createHardcodedData("\"1313161554\"", "\"0x4e454152\""),
|
|
||||||
Chain.AURORA__TESTNET to createHardcodedData("\"1313161555\"", "\"0x4e454153\""),
|
|
||||||
// Chain.CHAIN_SCROLL__MAINNET to createHardcodedData(""43114"", ""0xa86a""), doesn't exist now for L2
|
|
||||||
Chain.SCROLL__ALPHANET to createHardcodedData("\"534353\"", "\"0x82751\""),
|
|
||||||
Chain.SCROLL__SEPOLIA to createHardcodedData("\"534351\"", "\"0x8274f\""),
|
|
||||||
Chain.MANTLE__MAINNET to createHardcodedData("\"5000\"", "\"0x1388\""),
|
|
||||||
Chain.MANTLE__TESTNET to createHardcodedData("\"5001\"", "\"0x1389\""),
|
|
||||||
Chain.KLAYTN__MAINNET to createHardcodedData("\"8217\"", "\"0x2019\""),
|
|
||||||
Chain.KLAYTN__BAOBAB to createHardcodedData("\"1001\"", "\"0x3e9\""),
|
|
||||||
|
|
||||||
Chain.MOONBEAM__MAINNET to createHardcodedData("\"1284\"", "\"0x504\""),
|
|
||||||
Chain.MOONBEAM__MOONRIVER to createHardcodedData("\"1285\"", "\"0x505\""),
|
|
||||||
Chain.MOONBEAM__ALPHA to createHardcodedData("\"1287\"", "\"0x507\""),
|
|
||||||
|
|
||||||
Chain.CELO__MAINNET to createHardcodedData("\"42220\"", "\"0xa4ec\""),
|
|
||||||
Chain.CELO__ALFAJORES to createHardcodedData("\"44787\"", "\"0xaef3\""),
|
|
||||||
)
|
|
||||||
|
|
||||||
fun getChainByData(data: HardcodedData) = CHAIN_DATA.entries.find { it.value == data }?.key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val anyResponseMethods = listOf(
|
private val anyResponseMethods = listOf(
|
||||||
"eth_gasPrice",
|
"eth_gasPrice",
|
||||||
"eth_estimateGas"
|
"eth_estimateGas",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val possibleNotIndexedMethods = listOf(
|
private val possibleNotIndexedMethods = listOf(
|
||||||
@@ -153,7 +86,7 @@ class DefaultEthereumMethods(
|
|||||||
"eth_getTransactionByBlockHashAndIndex",
|
"eth_getTransactionByBlockHashAndIndex",
|
||||||
"eth_getTransactionByBlockNumberAndIndex",
|
"eth_getTransactionByBlockNumberAndIndex",
|
||||||
"eth_getUncleByBlockHashAndIndex",
|
"eth_getUncleByBlockHashAndIndex",
|
||||||
"eth_getUncleCountByBlockHash"
|
"eth_getUncleCountByBlockHash",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val firstValueMethods = listOf(
|
private val firstValueMethods = listOf(
|
||||||
@@ -162,21 +95,21 @@ class DefaultEthereumMethods(
|
|||||||
"eth_getCode",
|
"eth_getCode",
|
||||||
"eth_getLogs",
|
"eth_getLogs",
|
||||||
"eth_maxPriorityFeePerGas",
|
"eth_maxPriorityFeePerGas",
|
||||||
"eth_getProof"
|
"eth_getProof",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val specialMethods = listOf(
|
private val specialMethods = listOf(
|
||||||
"eth_getTransactionCount",
|
"eth_getTransactionCount",
|
||||||
"eth_blockNumber",
|
"eth_blockNumber",
|
||||||
"eth_getBalance",
|
"eth_getBalance",
|
||||||
"eth_sendRawTransaction"
|
"eth_sendRawTransaction",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val headVerifiedMethods = listOf(
|
private val headVerifiedMethods = listOf(
|
||||||
"eth_getBlockTransactionCountByNumber",
|
"eth_getBlockTransactionCountByNumber",
|
||||||
"eth_getUncleCountByBlockNumber",
|
"eth_getUncleCountByBlockNumber",
|
||||||
"eth_getUncleByBlockNumberAndIndex",
|
"eth_getUncleByBlockNumberAndIndex",
|
||||||
"eth_feeHistory"
|
"eth_feeHistory",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val filterMethods = withFilterIdMethods + newFilterMethods
|
private val filterMethods = withFilterIdMethods + newFilterMethods
|
||||||
@@ -192,7 +125,7 @@ class DefaultEthereumMethods(
|
|||||||
"eth_mining",
|
"eth_mining",
|
||||||
"eth_hashrate",
|
"eth_hashrate",
|
||||||
"eth_accounts",
|
"eth_accounts",
|
||||||
"eth_chainId"
|
"eth_chainId",
|
||||||
)
|
)
|
||||||
|
|
||||||
private val allowedMethods: List<String>
|
private val allowedMethods: List<String>
|
||||||
@@ -244,16 +177,16 @@ class DefaultEthereumMethods(
|
|||||||
private fun getChainSpecificMethods(chain: Chain): List<String> {
|
private fun getChainSpecificMethods(chain: Chain): List<String> {
|
||||||
return when (chain) {
|
return when (chain) {
|
||||||
Chain.OPTIMISM__MAINNET, Chain.OPTIMISM__GOERLI -> listOf(
|
Chain.OPTIMISM__MAINNET, Chain.OPTIMISM__GOERLI -> listOf(
|
||||||
"rollup_gasPrices"
|
"rollup_gasPrices",
|
||||||
)
|
)
|
||||||
|
|
||||||
Chain.POLYGON_POS__MAINNET, Chain.POLYGON_POS__MUMBAI -> listOf(
|
Chain.POLYGON__MAINNET, Chain.POLYGON__MUMBAI -> listOf(
|
||||||
"bor_getAuthor",
|
"bor_getAuthor",
|
||||||
"bor_getCurrentValidators",
|
"bor_getCurrentValidators",
|
||||||
"bor_getCurrentProposer",
|
"bor_getCurrentProposer",
|
||||||
"bor_getRootHash",
|
"bor_getRootHash",
|
||||||
"bor_getSignersAtHash",
|
"bor_getSignersAtHash",
|
||||||
"eth_getRootHash"
|
"eth_getRootHash",
|
||||||
)
|
)
|
||||||
|
|
||||||
Chain.POLYGON_ZKEVM__MAINNET, Chain.POLYGON_ZKEVM__TESTNET -> listOf(
|
Chain.POLYGON_ZKEVM__MAINNET, Chain.POLYGON_ZKEVM__TESTNET -> listOf(
|
||||||
@@ -265,7 +198,7 @@ class DefaultEthereumMethods(
|
|||||||
"zkevm_virtualBatchNumber",
|
"zkevm_virtualBatchNumber",
|
||||||
"zkevm_verifiedBatchNumber",
|
"zkevm_verifiedBatchNumber",
|
||||||
"zkevm_getBatchByNumber",
|
"zkevm_getBatchByNumber",
|
||||||
"zkevm_getBroadcastURI"
|
"zkevm_getBroadcastURI",
|
||||||
)
|
)
|
||||||
|
|
||||||
Chain.ZKSYNC__MAINNET, Chain.ZKSYNC__TESTNET -> listOf(
|
Chain.ZKSYNC__MAINNET, Chain.ZKSYNC__TESTNET -> listOf(
|
||||||
@@ -286,7 +219,7 @@ class DefaultEthereumMethods(
|
|||||||
"zks_getTokenPrice",
|
"zks_getTokenPrice",
|
||||||
"zks_getTransactionDetails",
|
"zks_getTransactionDetails",
|
||||||
"zks_L1BatchNumber",
|
"zks_L1BatchNumber",
|
||||||
"zks_L1ChainId"
|
"zks_L1ChainId",
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> emptyList()
|
else -> emptyList()
|
||||||
@@ -314,8 +247,8 @@ class DefaultEthereumMethods(
|
|||||||
// note that the value is in json representation, i.e. if it's a string it should be with quotes,
|
// note that the value is in json representation, i.e. if it's a string it should be with quotes,
|
||||||
// that's why "\"0x0\"", "\"1\"", etc. But just "true" for a boolean, or "[]" for array.
|
// that's why "\"0x0\"", "\"1\"", etc. But just "true" for a boolean, or "[]" for array.
|
||||||
val json = when (method) {
|
val json = when (method) {
|
||||||
"net_version" -> CHAIN_DATA.get(chain)?.netVersion ?: throw RpcException(-32602, "Invalid chain")
|
"net_version" -> "\"${chain.netVersion}\""
|
||||||
"eth_chainId" -> CHAIN_DATA.get(chain)?.chainId ?: throw RpcException(-32602, "Invalid chain")
|
"eth_chainId" -> "\"${chain.chainId}\""
|
||||||
|
|
||||||
"net_peerCount" -> {
|
"net_peerCount" -> {
|
||||||
"\"0x2a\""
|
"\"0x2a\""
|
||||||
@@ -373,7 +306,7 @@ class DefaultEthereumMethods(
|
|||||||
|
|
||||||
data class HardcodedData private constructor(
|
data class HardcodedData private constructor(
|
||||||
val netVersion: String,
|
val netVersion: String,
|
||||||
val chainId: String
|
val chainId: String,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun createHardcodedData(netVersion: String, chainId: String): HardcodedData =
|
fun createHardcodedData(netVersion: String, chainId: String): HardcodedData =
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import io.emeraldpay.dshackle.cache.Caches
|
|||||||
import io.emeraldpay.dshackle.cache.CachesEnabled
|
import io.emeraldpay.dshackle.cache.CachesEnabled
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
||||||
@@ -41,14 +42,14 @@ open class EthereumLikeRpcUpstream(
|
|||||||
id: String,
|
id: String,
|
||||||
hash: Byte,
|
hash: Byte,
|
||||||
val chain: Chain,
|
val chain: Chain,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
targets: CallMethods?,
|
targets: CallMethods?,
|
||||||
private val node: QuorumForLabels.QuorumItem?,
|
private val node: QuorumForLabels.QuorumItem?,
|
||||||
connectorFactory: ConnectorFactory,
|
connectorFactory: ConnectorFactory,
|
||||||
chainConfig: ChainsConfig.ChainConfig,
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
skipEnhance: Boolean,
|
skipEnhance: Boolean,
|
||||||
private val eventPublisher: ApplicationEventPublisher?
|
private val eventPublisher: ApplicationEventPublisher?,
|
||||||
) : EthereumLikeUpstream(id, hash, options, role, targets, node, chainConfig), Lifecycle, Upstream, CachesEnabled {
|
) : EthereumLikeUpstream(id, hash, options, role, targets, node, chainConfig), Lifecycle, Upstream, CachesEnabled {
|
||||||
private val validator: EthereumUpstreamValidator = EthereumUpstreamValidator(chain, this, getOptions(), chainConfig.callLimitContract)
|
private val validator: EthereumUpstreamValidator = EthereumUpstreamValidator(chain, this, getOptions(), chainConfig.callLimitContract)
|
||||||
protected val connector: EthereumConnector = connectorFactory.create(this, validator, chain, skipEnhance)
|
protected val connector: EthereumConnector = connectorFactory.create(this, validator, chain, skipEnhance)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.Capability
|
import io.emeraldpay.dshackle.upstream.Capability
|
||||||
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
||||||
@@ -26,11 +27,11 @@ import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
|||||||
abstract class EthereumLikeUpstream(
|
abstract class EthereumLikeUpstream(
|
||||||
id: String,
|
id: String,
|
||||||
hash: Byte,
|
hash: Byte,
|
||||||
options: UpstreamsConfig.Options,
|
options: ChainOptions.Options,
|
||||||
role: UpstreamsConfig.UpstreamRole,
|
role: UpstreamsConfig.UpstreamRole,
|
||||||
targets: CallMethods?,
|
targets: CallMethods?,
|
||||||
private val node: QuorumForLabels.QuorumItem?,
|
private val node: QuorumForLabels.QuorumItem?,
|
||||||
val chainConfig: ChainsConfig.ChainConfig
|
val chainConfig: ChainsConfig.ChainConfig,
|
||||||
) : DefaultUpstream(id, hash, options, role, targets, node, chainConfig) {
|
) : DefaultUpstream(id, hash, options, role, targets, node, chainConfig) {
|
||||||
|
|
||||||
private val capabilities = setOf(Capability.RPC, Capability.BALANCE)
|
private val capabilities = setOf(Capability.RPC, Capability.BALANCE)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ open class EthereumMultistream(
|
|||||||
private var head: DynamicMergedHead = DynamicMergedHead(
|
private var head: DynamicMergedHead = DynamicMergedHead(
|
||||||
PriorityForkChoice(),
|
PriorityForkChoice(),
|
||||||
"ETH Multistream of ${chain.chainCode}",
|
"ETH Multistream of ${chain.chainCode}",
|
||||||
headScheduler
|
headScheduler,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val filteredHeads: MutableMap<String, Head> =
|
private val filteredHeads: MutableMap<String, Head> =
|
||||||
@@ -71,7 +71,6 @@ open class EthereumMultistream(
|
|||||||
private val supportsEIP1559set = setOf(
|
private val supportsEIP1559set = setOf(
|
||||||
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__ROPSTEN,
|
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__ROPSTEN,
|
||||||
Chain.ETHEREUM__GOERLI,
|
Chain.ETHEREUM__GOERLI,
|
||||||
Chain.ETHEREUM__RINKEBY,
|
|
||||||
Chain.ETHEREUM__SEPOLIA,
|
Chain.ETHEREUM__SEPOLIA,
|
||||||
Chain.ARBITRUM__MAINNET,
|
Chain.ARBITRUM__MAINNET,
|
||||||
Chain.OPTIMISM__MAINNET,
|
Chain.OPTIMISM__MAINNET,
|
||||||
@@ -81,13 +80,16 @@ open class EthereumMultistream(
|
|||||||
Chain.POLYGON_ZKEVM__TESTNET,
|
Chain.POLYGON_ZKEVM__TESTNET,
|
||||||
Chain.ZKSYNC__MAINNET,
|
Chain.ZKSYNC__MAINNET,
|
||||||
Chain.ZKSYNC__TESTNET,
|
Chain.ZKSYNC__TESTNET,
|
||||||
Chain.ARBITRUM_NOVA__MAINNET
|
Chain.ARBITRUM_NOVA__MAINNET,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val supportsEIP1559 = supportsEIP1559set.contains(chain)
|
private val supportsEIP1559 = supportsEIP1559set.contains(chain)
|
||||||
|
|
||||||
private val feeEstimation = if (supportsEIP1559) EthereumPriorityFees(this, reader, 256)
|
private val feeEstimation = if (supportsEIP1559) {
|
||||||
else EthereumLegacyFees(this, reader, 256)
|
EthereumPriorityFees(this, reader, 256)
|
||||||
|
} else {
|
||||||
|
EthereumLegacyFees(this, reader, 256)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.init()
|
this.init()
|
||||||
@@ -161,7 +163,7 @@ open class EthereumMultistream(
|
|||||||
|
|
||||||
override fun tryProxy(
|
override fun tryProxy(
|
||||||
matcher: Selector.Matcher,
|
matcher: Selector.Matcher,
|
||||||
request: BlockchainOuterClass.NativeSubscribeRequest
|
request: BlockchainOuterClass.NativeSubscribeRequest,
|
||||||
): Flux<out Any>? =
|
): Flux<out Any>? =
|
||||||
upstreams.filter {
|
upstreams.filter {
|
||||||
matcher.matches(it)
|
matcher.matches(it)
|
||||||
@@ -220,13 +222,15 @@ open class EthereumMultistream(
|
|||||||
}.let {
|
}.let {
|
||||||
val selected = it.map { source -> source.getHead() }
|
val selected = it.map { source -> source.getHead() }
|
||||||
EnrichedMergedHead(
|
EnrichedMergedHead(
|
||||||
selected, getHead(), headScheduler,
|
selected,
|
||||||
|
getHead(),
|
||||||
|
headScheduler,
|
||||||
object :
|
object :
|
||||||
Reader<BlockHash, BlockContainer> {
|
Reader<BlockHash, BlockContainer> {
|
||||||
override fun read(key: BlockHash): Mono<BlockContainer> {
|
override fun read(key: BlockHash): Mono<BlockContainer> {
|
||||||
return reader.blocksByHashAsCont().read(key).map { res -> res.data }
|
return reader.blocksByHashAsCont().read(key).map { res -> res.data }
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,9 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.Defaults
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods.Companion.CHAIN_DATA
|
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods.Companion.getChainByData
|
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
import io.emeraldpay.etherjar.domain.Address
|
import io.emeraldpay.etherjar.domain.Address
|
||||||
@@ -46,8 +43,8 @@ import java.util.concurrent.TimeoutException
|
|||||||
open class EthereumUpstreamValidator @JvmOverloads constructor(
|
open class EthereumUpstreamValidator @JvmOverloads constructor(
|
||||||
private val chain: Chain,
|
private val chain: Chain,
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
private val options: UpstreamsConfig.Options,
|
private val options: ChainOptions.Options,
|
||||||
private val callLimitContract: String? = null
|
private val callLimitContract: String? = null,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(EthereumUpstreamValidator::class.java)
|
private val log = LoggerFactory.getLogger(EthereumUpstreamValidator::class.java)
|
||||||
@@ -60,7 +57,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
open fun validate(): Mono<UpstreamAvailability> {
|
open fun validate(): Mono<UpstreamAvailability> {
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
validateSyncing(),
|
validateSyncing(),
|
||||||
validatePeers()
|
validatePeers(),
|
||||||
)
|
)
|
||||||
.map(::resolve)
|
.map(::resolve)
|
||||||
.defaultIfEmpty(UpstreamAvailability.UNAVAILABLE)
|
.defaultIfEmpty(UpstreamAvailability.UNAVAILABLE)
|
||||||
@@ -86,7 +83,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.timeout(
|
.timeout(
|
||||||
Defaults.timeoutInternal,
|
Defaults.timeoutInternal,
|
||||||
Mono.fromCallable { log.warn("No response for eth_syncing from ${upstream.getId()}") }
|
Mono.fromCallable { log.warn("No response for eth_syncing from ${upstream.getId()}") }
|
||||||
.then(Mono.error(TimeoutException("Validation timeout for Syncing")))
|
.then(Mono.error(TimeoutException("Validation timeout for Syncing"))),
|
||||||
)
|
)
|
||||||
.map {
|
.map {
|
||||||
val isSyncing = it.isSyncing
|
val isSyncing = it.isSyncing
|
||||||
@@ -113,7 +110,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.timeout(
|
.timeout(
|
||||||
Defaults.timeoutInternal,
|
Defaults.timeoutInternal,
|
||||||
Mono.fromCallable { log.warn("No response for net_peerCount from ${upstream.getId()}") }
|
Mono.fromCallable { log.warn("No response for net_peerCount from ${upstream.getId()}") }
|
||||||
.then(Mono.error(TimeoutException("Validation timeout for Peers")))
|
.then(Mono.error(TimeoutException("Validation timeout for Peers"))),
|
||||||
)
|
)
|
||||||
.map { count ->
|
.map { count ->
|
||||||
val minPeers = options.minPeers
|
val minPeers = options.minPeers
|
||||||
@@ -144,7 +141,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
validateChain(),
|
validateChain(),
|
||||||
validateCallLimit(),
|
validateCallLimit(),
|
||||||
validateOldBlocks()
|
validateOldBlocks(),
|
||||||
).map {
|
).map {
|
||||||
it.t1 && it.t2 && it.t3
|
it.t1 && it.t2 && it.t3
|
||||||
}.block() ?: false
|
}.block() ?: false
|
||||||
@@ -156,19 +153,16 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
chainId(),
|
chainId(),
|
||||||
netVersion()
|
netVersion(),
|
||||||
)
|
)
|
||||||
.map {
|
.map {
|
||||||
val chainData = CHAIN_DATA[chain] ?: return@map false
|
val isChainValid = chain.chainId == it.t1 && chain.netVersion.toString() == it.t2
|
||||||
val isChainValid = chainData.chainId == it.t1 && chainData.netVersion == it.t2
|
|
||||||
|
|
||||||
if (!isChainValid) {
|
if (!isChainValid) {
|
||||||
val actualChain = getChainByData(
|
val actualChain = Global.chainByChainId(it.t1).chainName
|
||||||
DefaultEthereumMethods.HardcodedData.createHardcodedData(it.t2, it.t1)
|
|
||||||
)?.chainName
|
|
||||||
log.warn(
|
log.warn(
|
||||||
"${chain.chainName} is specified for upstream ${upstream.getId()} " +
|
"${chain.chainName} is specified for upstream ${upstream.getId()} " +
|
||||||
"but actually it is $actualChain with chainId ${it.t1} and net_version ${it.t2}"
|
"but actually it is $actualChain with chainId ${it.t1} and net_version ${it.t2}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,11 +187,11 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
Address.from(callLimitContract),
|
Address.from(callLimitContract),
|
||||||
// calling contract with param 200_000, meaning it will generate 200k symbols or response
|
// calling contract with param 200_000, meaning it will generate 200k symbols or response
|
||||||
// f4240 + metadata — ~1 million
|
// f4240 + metadata — ~1 million
|
||||||
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240")
|
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240"),
|
||||||
),
|
),
|
||||||
"latest"
|
"latest",
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireResult)
|
||||||
.map { true }
|
.map { true }
|
||||||
@@ -206,7 +200,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
log.warn(
|
log.warn(
|
||||||
"Error: ${it.message}. Node ${upstream.getId()} is probably incorrectly configured. " +
|
"Error: ${it.message}. Node ${upstream.getId()} is probably incorrectly configured. " +
|
||||||
"You need to set up your return limit to at least 1_100_000. " +
|
"You need to set up your return limit to at least 1_100_000. " +
|
||||||
"Erigon config example: https://github.com/ledgerwatch/erigon/blob/d014da4dc039ea97caf04ed29feb2af92b7b129d/cmd/utils/flags.go#L369"
|
"Erigon config example: https://github.com/ledgerwatch/erigon/blob/d014da4dc039ea97caf04ed29feb2af92b7b129d/cmd/utils/flags.go#L369",
|
||||||
)
|
)
|
||||||
Mono.just(false)
|
Mono.just(false)
|
||||||
} else {
|
} else {
|
||||||
@@ -216,12 +210,12 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.timeout(
|
.timeout(
|
||||||
Defaults.timeoutInternal,
|
Defaults.timeoutInternal,
|
||||||
Mono.fromCallable { log.error("No response for eth_call limit check from ${upstream.getId()}") }
|
Mono.fromCallable { log.error("No response for eth_call limit check from ${upstream.getId()}") }
|
||||||
.then(Mono.error(TimeoutException("Validation timeout for call limit")))
|
.then(Mono.error(TimeoutException("Validation timeout for call limit"))),
|
||||||
)
|
)
|
||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
||||||
log.warn(
|
log.warn(
|
||||||
"error during validateCallLimit for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
"error during validateCallLimit for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
||||||
"message ${ctx.exception().message}"
|
"message ${ctx.exception().message}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.onErrorReturn(false)
|
.onErrorReturn(false)
|
||||||
@@ -238,14 +232,14 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
||||||
log.warn(
|
log.warn(
|
||||||
"error during old block retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
"error during old block retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
||||||
"message ${ctx.exception().message}"
|
"message ${ctx.exception().message}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.map { result ->
|
.map { result ->
|
||||||
val receivedResult = result.isNotEmpty() && !Global.nullValue.contentEquals(result)
|
val receivedResult = result.isNotEmpty() && !Global.nullValue.contentEquals(result)
|
||||||
if (!receivedResult) {
|
if (!receivedResult) {
|
||||||
log.warn(
|
log.warn(
|
||||||
"Node ${upstream.getId()} probably is synced incorrectly, it is not possible to get old blocks"
|
"Node ${upstream.getId()} probably is synced incorrectly, it is not possible to get old blocks",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -262,12 +256,11 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
||||||
log.warn(
|
log.warn(
|
||||||
"error during chainId retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
"error during chainId retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
||||||
"message ${ctx.exception().message}"
|
"message ${ctx.exception().message}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.doOnError { log.error("Error during execution 'eth_chainId' - ${it.message} for ${upstream.getId()}") }
|
.doOnError { log.error("Error during execution 'eth_chainId' - ${it.message} for ${upstream.getId()}") }
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireStringResult)
|
||||||
.map { String(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun netVersion(): Mono<String> {
|
private fun netVersion(): Mono<String> {
|
||||||
@@ -276,11 +269,10 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
||||||
log.warn(
|
log.warn(
|
||||||
"error during netVersion retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
"error during netVersion retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}, " +
|
||||||
"message ${ctx.exception().message}"
|
"message ${ctx.exception().message}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.doOnError { log.error("Error during execution 'net_version' - ${it.message} for ${upstream.getId()}") }
|
.doOnError { log.error("Error during execution 'net_version' - ${it.message} for ${upstream.getId()}") }
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireStringResult)
|
||||||
.map { String(it) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ open class EthereumWsConnectionFactory(
|
|||||||
private val chain: Chain,
|
private val chain: Chain,
|
||||||
private val uri: URI,
|
private val uri: URI,
|
||||||
private val origin: URI,
|
private val origin: URI,
|
||||||
private val scheduler: Scheduler
|
private val scheduler: Scheduler,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
var basicAuth: AuthConfig.ClientBasicAuth? = null
|
||||||
@@ -27,7 +27,7 @@ open class EthereumWsConnectionFactory(
|
|||||||
Tag.of("index", connIndex.toString()),
|
Tag.of("index", connIndex.toString()),
|
||||||
Tag.of("upstream", id),
|
Tag.of("upstream", id),
|
||||||
// UNSPECIFIED shouldn't happen too
|
// UNSPECIFIED shouldn't happen too
|
||||||
Tag.of("chain", chain.chainCode)
|
Tag.of("chain", chain.chainCode),
|
||||||
)
|
)
|
||||||
|
|
||||||
return RpcMetrics(
|
return RpcMetrics(
|
||||||
@@ -39,7 +39,7 @@ open class EthereumWsConnectionFactory(
|
|||||||
Counter.builder("upstream.ws.fail")
|
Counter.builder("upstream.ws.fail")
|
||||||
.description("Number of failures of WebSocket JSON RPC requests")
|
.description("Number of failures of WebSocket JSON RPC requests")
|
||||||
.tags(metricsTags)
|
.tags(metricsTags)
|
||||||
.register(Metrics.globalRegistry)
|
.register(Metrics.globalRegistry),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ open class EthereumPosMultiStream(
|
|||||||
val upstreams: MutableList<EthereumLikeUpstream>,
|
val upstreams: MutableList<EthereumLikeUpstream>,
|
||||||
caches: Caches,
|
caches: Caches,
|
||||||
private val headScheduler: Scheduler,
|
private val headScheduler: Scheduler,
|
||||||
tracer: Tracer
|
tracer: Tracer,
|
||||||
) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream {
|
) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream {
|
||||||
|
|
||||||
private var head: DynamicMergedHead = DynamicMergedHead(
|
private var head: DynamicMergedHead = DynamicMergedHead(
|
||||||
PriorityForkChoice(),
|
PriorityForkChoice(),
|
||||||
"ETH Pos Multistream of ${chain.chainCode}",
|
"ETH Pos Multistream of ${chain.chainCode}",
|
||||||
headScheduler
|
headScheduler,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val reader: EthereumCachingReader = EthereumCachingReader(this, this.caches, getMethodsFactory(), tracer)
|
private val reader: EthereumCachingReader = EthereumCachingReader(this, this.caches, getMethodsFactory(), tracer)
|
||||||
@@ -121,7 +121,7 @@ open class EthereumPosMultiStream(
|
|||||||
|
|
||||||
override fun tryProxy(
|
override fun tryProxy(
|
||||||
matcher: Selector.Matcher,
|
matcher: Selector.Matcher,
|
||||||
request: BlockchainOuterClass.NativeSubscribeRequest
|
request: BlockchainOuterClass.NativeSubscribeRequest,
|
||||||
): Flux<out Any>? =
|
): Flux<out Any>? =
|
||||||
upstreams.filter {
|
upstreams.filter {
|
||||||
matcher.matches(it)
|
matcher.matches(it)
|
||||||
@@ -173,7 +173,7 @@ open class EthereumPosMultiStream(
|
|||||||
selected,
|
selected,
|
||||||
PriorityForkChoice(),
|
PriorityForkChoice(),
|
||||||
headScheduler,
|
headScheduler,
|
||||||
"ETH head for ${it.map { it.getId() }}"
|
"ETH head for ${it.map { it.getId() }}",
|
||||||
).apply {
|
).apply {
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
@@ -190,13 +190,15 @@ open class EthereumPosMultiStream(
|
|||||||
}.let {
|
}.let {
|
||||||
val selected = it.map { source -> source.getHead() }
|
val selected = it.map { source -> source.getHead() }
|
||||||
EnrichedMergedHead(
|
EnrichedMergedHead(
|
||||||
selected, getHead(), headScheduler,
|
selected,
|
||||||
|
getHead(),
|
||||||
|
headScheduler,
|
||||||
object :
|
object :
|
||||||
Reader<BlockHash, BlockContainer> {
|
Reader<BlockHash, BlockContainer> {
|
||||||
override fun read(key: BlockHash): Mono<BlockContainer> {
|
override fun read(key: BlockHash): Mono<BlockContainer> {
|
||||||
return reader.blocksByHashAsCont().read(key).map { res -> res.data }
|
return reader.blocksByHashAsCont().read(key).map { res -> res.data }
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import io.emeraldpay.dshackle.config.ChainsConfig
|
|||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.upstream.BuildInfo
|
import io.emeraldpay.dshackle.upstream.BuildInfo
|
||||||
import io.emeraldpay.dshackle.upstream.Capability
|
import io.emeraldpay.dshackle.upstream.Capability
|
||||||
@@ -59,9 +60,9 @@ class BitcoinGrpcUpstream(
|
|||||||
) : BitcoinUpstream(
|
) : BitcoinUpstream(
|
||||||
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
||||||
chain,
|
chain,
|
||||||
UpstreamsConfig.PartialOptions.getDefaults().buildOptions(),
|
ChainOptions.PartialOptions.getDefaults().buildOptions(),
|
||||||
role,
|
role,
|
||||||
chainConfig
|
chainConfig,
|
||||||
),
|
),
|
||||||
GrpcUpstream,
|
GrpcUpstream,
|
||||||
Lifecycle {
|
Lifecycle {
|
||||||
@@ -70,8 +71,11 @@ class BitcoinGrpcUpstream(
|
|||||||
private val defaultReader: JsonRpcReader = client.getReader()
|
private val defaultReader: JsonRpcReader = client.getReader()
|
||||||
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
||||||
val parentHash =
|
val parentHash =
|
||||||
if (value.parentBlockId.isBlank()) null
|
if (value.parentBlockId.isBlank()) {
|
||||||
else BlockId.from(value.parentBlockId)
|
null
|
||||||
|
} else {
|
||||||
|
BlockId.from(value.parentBlockId)
|
||||||
|
}
|
||||||
val block = BlockContainer(
|
val block = BlockContainer(
|
||||||
value.height,
|
value.height,
|
||||||
BlockId.from(value.blockId),
|
BlockId.from(value.blockId),
|
||||||
@@ -80,7 +84,7 @@ class BitcoinGrpcUpstream(
|
|||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
parentHash
|
parentHash,
|
||||||
)
|
)
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
@@ -108,8 +112,14 @@ class BitcoinGrpcUpstream(
|
|||||||
}
|
}
|
||||||
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
||||||
private val grpcHead = GrpcHead(
|
private val grpcHead = GrpcHead(
|
||||||
getId(), chain, this, remote, blockConverter, reloadBlock,
|
getId(),
|
||||||
MostWorkForkChoice(), headScheduler
|
chain,
|
||||||
|
this,
|
||||||
|
remote,
|
||||||
|
blockConverter,
|
||||||
|
reloadBlock,
|
||||||
|
MostWorkForkChoice(),
|
||||||
|
headScheduler,
|
||||||
)
|
)
|
||||||
private val timeout = Defaults.timeout
|
private val timeout = Defaults.timeout
|
||||||
private var capabilities: Set<Capability> = emptySet()
|
private var capabilities: Set<Capability> = emptySet()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import io.emeraldpay.dshackle.config.ChainsConfig
|
|||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.BuildInfo
|
import io.emeraldpay.dshackle.upstream.BuildInfo
|
||||||
@@ -65,19 +66,22 @@ open class EthereumGrpcUpstream(
|
|||||||
) : EthereumLikeUpstream(
|
) : EthereumLikeUpstream(
|
||||||
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
||||||
hash,
|
hash,
|
||||||
UpstreamsConfig.PartialOptions.getDefaults().buildOptions(),
|
ChainOptions.PartialOptions.getDefaults().buildOptions(),
|
||||||
role,
|
role,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
chainConfig
|
chainConfig,
|
||||||
),
|
),
|
||||||
GrpcUpstream,
|
GrpcUpstream,
|
||||||
Lifecycle {
|
Lifecycle {
|
||||||
|
|
||||||
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
||||||
val parentHash =
|
val parentHash =
|
||||||
if (value.parentBlockId.isBlank()) null
|
if (value.parentBlockId.isBlank()) {
|
||||||
else BlockId.from(BlockHash.from("0x" + value.parentBlockId))
|
null
|
||||||
|
} else {
|
||||||
|
BlockId.from(BlockHash.from("0x" + value.parentBlockId))
|
||||||
|
}
|
||||||
val block = BlockContainer(
|
val block = BlockContainer(
|
||||||
value.height,
|
value.height,
|
||||||
BlockId.from(BlockHash.from("0x" + value.blockId)),
|
BlockId.from(BlockHash.from("0x" + value.blockId)),
|
||||||
@@ -86,7 +90,7 @@ open class EthereumGrpcUpstream(
|
|||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
parentHash
|
parentHash,
|
||||||
)
|
)
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
@@ -117,8 +121,14 @@ open class EthereumGrpcUpstream(
|
|||||||
|
|
||||||
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
||||||
private val grpcHead = GrpcHead(
|
private val grpcHead = GrpcHead(
|
||||||
getId(), chain, this, remote,
|
getId(),
|
||||||
blockConverter, reloadBlock, MostWorkForkChoice(), headScheduler
|
chain,
|
||||||
|
this,
|
||||||
|
remote,
|
||||||
|
blockConverter,
|
||||||
|
reloadBlock,
|
||||||
|
MostWorkForkChoice(),
|
||||||
|
headScheduler,
|
||||||
)
|
)
|
||||||
private var capabilities: Set<Capability> = emptySet()
|
private var capabilities: Set<Capability> = emptySet()
|
||||||
private val buildInfo: BuildInfo = BuildInfo()
|
private val buildInfo: BuildInfo = BuildInfo()
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import io.emeraldpay.dshackle.config.ChainsConfig
|
|||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
import io.emeraldpay.dshackle.upstream.BuildInfo
|
import io.emeraldpay.dshackle.upstream.BuildInfo
|
||||||
@@ -58,19 +59,22 @@ open class EthereumPosGrpcUpstream(
|
|||||||
) : EthereumLikeUpstream(
|
) : EthereumLikeUpstream(
|
||||||
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
"${parentId}_${chain.chainCode.lowercase(Locale.getDefault())}",
|
||||||
hash,
|
hash,
|
||||||
UpstreamsConfig.PartialOptions.getDefaults().buildOptions(),
|
ChainOptions.PartialOptions.getDefaults().buildOptions(),
|
||||||
role,
|
role,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
chainConfig
|
chainConfig,
|
||||||
),
|
),
|
||||||
GrpcUpstream,
|
GrpcUpstream,
|
||||||
Lifecycle {
|
Lifecycle {
|
||||||
|
|
||||||
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
private val blockConverter: Function<BlockchainOuterClass.ChainHead, BlockContainer> = Function { value ->
|
||||||
val parentHash =
|
val parentHash =
|
||||||
if (value.parentBlockId.isBlank()) null
|
if (value.parentBlockId.isBlank()) {
|
||||||
else BlockId.from(BlockHash.from("0x" + value.parentBlockId))
|
null
|
||||||
|
} else {
|
||||||
|
BlockId.from(BlockHash.from("0x" + value.parentBlockId))
|
||||||
|
}
|
||||||
val block = BlockContainer(
|
val block = BlockContainer(
|
||||||
value.height,
|
value.height,
|
||||||
BlockId.from(BlockHash.from("0x" + value.blockId)),
|
BlockId.from(BlockHash.from("0x" + value.blockId)),
|
||||||
@@ -79,15 +83,21 @@ open class EthereumPosGrpcUpstream(
|
|||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
parentHash
|
parentHash,
|
||||||
)
|
)
|
||||||
block
|
block
|
||||||
}
|
}
|
||||||
|
|
||||||
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
private val upstreamStatus = GrpcUpstreamStatus(overrideLabels)
|
||||||
private val grpcHead = GrpcHead(
|
private val grpcHead = GrpcHead(
|
||||||
getId(), chain, this, remote, blockConverter, null,
|
getId(),
|
||||||
NoChoiceWithPriorityForkChoice(nodeRating, parentId), headScheduler
|
chain,
|
||||||
|
this,
|
||||||
|
remote,
|
||||||
|
blockConverter,
|
||||||
|
null,
|
||||||
|
NoChoiceWithPriorityForkChoice(nodeRating, parentId),
|
||||||
|
headScheduler,
|
||||||
)
|
)
|
||||||
private var capabilities: Set<Capability> = emptySet()
|
private var capabilities: Set<Capability> = emptySet()
|
||||||
private val buildInfo: BuildInfo = BuildInfo()
|
private val buildInfo: BuildInfo = BuildInfo()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.util.Collections
|
|||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
class GrpcUpstreamStatus(
|
class GrpcUpstreamStatus(
|
||||||
private val overrideLabels: UpstreamsConfig.Labels?
|
private val overrideLabels: UpstreamsConfig.Labels?,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(GrpcUpstreamStatus::class.java)
|
private val log = LoggerFactory.getLogger(GrpcUpstreamStatus::class.java)
|
||||||
@@ -47,7 +47,7 @@ class GrpcUpstreamStatus(
|
|||||||
overrideLabels?.let { labels.putAll(it) }
|
overrideLabels?.let { labels.putAll(it) }
|
||||||
val node = QuorumForLabels.QuorumItem(
|
val node = QuorumForLabels.QuorumItem(
|
||||||
remoteNode.quorum,
|
remoteNode.quorum,
|
||||||
labels
|
labels,
|
||||||
)
|
)
|
||||||
updateNodes.add(node)
|
updateNodes.add(node)
|
||||||
updateLabels.add(labels)
|
updateLabels.add(labels)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class GrpcUpstreams(
|
|||||||
private val clientSpansInterceptor: ClientInterceptor?,
|
private val clientSpansInterceptor: ClientInterceptor?,
|
||||||
private var maxMetadataSize: Int,
|
private var maxMetadataSize: Int,
|
||||||
private val headScheduler: Scheduler,
|
private val headScheduler: Scheduler,
|
||||||
private val grpcAuthContext: GrpcAuthContext
|
private val grpcAuthContext: GrpcAuthContext,
|
||||||
) {
|
) {
|
||||||
private val log = LoggerFactory.getLogger(GrpcUpstreams::class.java)
|
private val log = LoggerFactory.getLogger(GrpcUpstreams::class.java)
|
||||||
|
|
||||||
@@ -136,9 +136,11 @@ class GrpcUpstreams(
|
|||||||
ReactorAuthGrpc.newReactorStub(channel),
|
ReactorAuthGrpc.newReactorStub(channel),
|
||||||
authorizationConfig,
|
authorizationConfig,
|
||||||
grpcAuthContext,
|
grpcAuthContext,
|
||||||
tokenAuth.publicKeyPath!!
|
tokenAuth.publicKeyPath!!,
|
||||||
)
|
)
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val statusSubscriptions = mutableMapOf<Chain, Disposable>()
|
val statusSubscriptions = mutableMapOf<Chain, Disposable>()
|
||||||
|
|
||||||
@@ -161,7 +163,7 @@ class GrpcUpstreams(
|
|||||||
if (sub == null || sub.isDisposed) {
|
if (sub == null || sub.isDisposed) {
|
||||||
val subscription = this.client.subscribeStatus(
|
val subscription = this.client.subscribeStatus(
|
||||||
StatusRequest.newBuilder()
|
StatusRequest.newBuilder()
|
||||||
.addChains(Common.ChainRef.forNumber(it.chain.id)).build()
|
.addChains(Common.ChainRef.forNumber(it.chain.id)).build(),
|
||||||
).subscribeOn(chainStatusScheduler)
|
).subscribeOn(chainStatusScheduler)
|
||||||
.subscribe { value ->
|
.subscribe { value ->
|
||||||
val chain = Chain.byId(value.chain.number)
|
val chain = Chain.byId(value.chain.number)
|
||||||
@@ -228,7 +230,7 @@ class GrpcUpstreams(
|
|||||||
if (StringUtils.isNotEmpty(auth.key) && StringUtils.isNoneEmpty(auth.certificate)) {
|
if (StringUtils.isNotEmpty(auth.key) && StringUtils.isNoneEmpty(auth.certificate)) {
|
||||||
sslContext.keyManager(
|
sslContext.keyManager(
|
||||||
fileResolver.resolve(auth.certificate!!).inputStream(),
|
fileResolver.resolve(auth.certificate!!).inputStream(),
|
||||||
fileResolver.resolve(auth.key!!).inputStream()
|
fileResolver.resolve(auth.key!!).inputStream(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
log.warn("Connect to remote using only CA certificate")
|
log.warn("Connect to remote using only CA certificate")
|
||||||
@@ -237,7 +239,8 @@ class GrpcUpstreams(
|
|||||||
ApplicationProtocolConfig.Protocol.ALPN,
|
ApplicationProtocolConfig.Protocol.ALPN,
|
||||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||||
"grpc-exp", "h2"
|
"grpc-exp",
|
||||||
|
"h2",
|
||||||
)
|
)
|
||||||
sslContext.applicationProtocolConfig(alpn)
|
sslContext.applicationProtocolConfig(alpn)
|
||||||
return sslContext.build()
|
return sslContext.build()
|
||||||
@@ -253,8 +256,8 @@ class GrpcUpstreams(
|
|||||||
client,
|
client,
|
||||||
rpcClient,
|
rpcClient,
|
||||||
labels,
|
labels,
|
||||||
chainsConfig.resolve(chain),
|
chainsConfig.resolve(chain.chainName),
|
||||||
headScheduler
|
headScheduler,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
BlockchainType.EVM_POS to { chain, rpcClient ->
|
BlockchainType.EVM_POS to { chain, rpcClient ->
|
||||||
@@ -267,13 +270,13 @@ class GrpcUpstreams(
|
|||||||
rpcClient,
|
rpcClient,
|
||||||
nodeRating,
|
nodeRating,
|
||||||
labels,
|
labels,
|
||||||
chainsConfig.resolve(chain),
|
chainsConfig.resolve(chain.chainName),
|
||||||
headScheduler
|
headScheduler,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
BlockchainType.BITCOIN to { chain, rpcClient ->
|
BlockchainType.BITCOIN to { chain, rpcClient ->
|
||||||
BitcoinGrpcUpstream(id, role, chain, client, rpcClient, labels, chainsConfig.resolve(chain), headScheduler)
|
BitcoinGrpcUpstream(id, role, chain, client, rpcClient, labels, chainsConfig.resolve(chain.chainCode), headScheduler)
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getOrCreate(chain: Chain): UpstreamChangeEvent {
|
private fun getOrCreate(chain: Chain): UpstreamChangeEvent {
|
||||||
@@ -285,7 +288,7 @@ class GrpcUpstreams(
|
|||||||
private fun makeMetrics(chain: Chain): RpcMetrics {
|
private fun makeMetrics(chain: Chain): RpcMetrics {
|
||||||
val metricsTags = listOf(
|
val metricsTags = listOf(
|
||||||
Tag.of("upstream", id),
|
Tag.of("upstream", id),
|
||||||
Tag.of("chain", chain.chainCode)
|
Tag.of("chain", chain.chainCode),
|
||||||
)
|
)
|
||||||
|
|
||||||
return RpcMetrics(
|
return RpcMetrics(
|
||||||
@@ -297,14 +300,14 @@ class GrpcUpstreams(
|
|||||||
Counter.builder("upstream.grpc.fail")
|
Counter.builder("upstream.grpc.fail")
|
||||||
.description("Number of failures of Dshackle/gRPC requests")
|
.description("Number of failures of Dshackle/gRPC requests")
|
||||||
.tags(metricsTags)
|
.tags(metricsTags)
|
||||||
.register(Metrics.globalRegistry)
|
.register(Metrics.globalRegistry),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrCreate(
|
private fun getOrCreate(
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
metrics: RpcMetrics,
|
metrics: RpcMetrics,
|
||||||
creator: (chain: Chain, client: JsonRpcGrpcClient) -> DefaultUpstream
|
creator: (chain: Chain, client: JsonRpcGrpcClient) -> DefaultUpstream,
|
||||||
): UpstreamChangeEvent {
|
): UpstreamChangeEvent {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
val current = known[chain]
|
val current = known[chain]
|
||||||
@@ -338,7 +341,7 @@ class GrpcUpstreams(
|
|||||||
} else {
|
} else {
|
||||||
Mono.error(it)
|
Mono.error(it)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
version: v1
|
|
||||||
|
|
||||||
chain-settings:
|
|
||||||
default:
|
|
||||||
expected-block-time: 12s
|
|
||||||
lags:
|
|
||||||
syncing: 6
|
|
||||||
lagging: 1
|
|
||||||
chains:
|
|
||||||
- id: eth
|
|
||||||
call-validate-contract: 0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
|
|
||||||
expected-block-time: 12s
|
|
||||||
lags:
|
|
||||||
syncing: 6
|
|
||||||
lagging: 1
|
|
||||||
- id: goerli
|
|
||||||
call-validate-contract: 0xCD9303A1F6da2a68f465A579a24cc2Ee5AE2192f
|
|
||||||
expected-block-time: 12s
|
|
||||||
lags:
|
|
||||||
syncing: 6
|
|
||||||
lagging: 1
|
|
||||||
- id: polygon
|
|
||||||
call-validate-contract: 0x53Daa71B04d589429f6d3DF52db123913B818F23
|
|
||||||
expected-block-time: 2.7s
|
|
||||||
lags:
|
|
||||||
syncing: 20
|
|
||||||
lagging: 10
|
|
||||||
- id: polygon-mumbai
|
|
||||||
call-validate-contract: 0x53Daa71B04d589429f6d3DF52db123913B818F23
|
|
||||||
expected-block-time: 2.7s
|
|
||||||
lags:
|
|
||||||
syncing: 20
|
|
||||||
lagging: 10
|
|
||||||
- id: arbitrum
|
|
||||||
expected-block-time: 260ms
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: arbitrum-testnet
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: optimism
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: optimism-testnet
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: arbitrum-nova
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
disable-validation: true
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: polygon-zkevm
|
|
||||||
expected-block-time: 2.7s
|
|
||||||
options:
|
|
||||||
disable-validation: true
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: polygon-zkevm-testnet
|
|
||||||
expected-block-time: 1m
|
|
||||||
options:
|
|
||||||
disable-validation: true
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: zksync
|
|
||||||
expected-block-time: 5s
|
|
||||||
options:
|
|
||||||
disable-validation: true
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: zksync-testnet
|
|
||||||
expected-block-time: 5s
|
|
||||||
options:
|
|
||||||
disable-validation: true
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: base
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: base-goerli
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: avalanche
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
validate-syncing: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: avalanche-fuji
|
|
||||||
expected-block-time: 2s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
validate-syncing: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: gnosis
|
|
||||||
expected-block-time: 6s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: gnosis-chiado
|
|
||||||
expected-block-time: 6s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: fantom
|
|
||||||
expected-block-time: 3s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: fantom-testnet
|
|
||||||
expected-block-time: 1m
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: aurora
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: aurora-testnet
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: scroll-alphanet
|
|
||||||
expected-block-time: 3s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: scroll-sepolia
|
|
||||||
expected-block-time: 4s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
lagging: 5
|
|
||||||
- id: mantle
|
|
||||||
expected-block-time: 500ms
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: mantle-testnet
|
|
||||||
expected-block-time: 500ms
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: klaytn
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
- id: klaytn-baobab
|
|
||||||
expected-block-time: 1s
|
|
||||||
options:
|
|
||||||
validate-peers: false
|
|
||||||
lags:
|
|
||||||
syncing: 40
|
|
||||||
lagging: 20
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2019 ETCDEV GmbH
|
|
||||||
* Copyright (c) 2020 EmeraldPay, Inc
|
|
||||||
*
|
|
||||||
* 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.Chain
|
|
||||||
import io.emeraldpay.dshackle.FileResolver
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
import java.time.Duration
|
|
||||||
|
|
||||||
class ChainsConfigReaderSpec extends Specification {
|
|
||||||
|
|
||||||
ChainsConfigReader reader = new ChainsConfigReader(
|
|
||||||
new UpstreamsConfigReader(Stub(FileResolver))
|
|
||||||
)
|
|
||||||
|
|
||||||
def "Parse standard config"() {
|
|
||||||
setup:
|
|
||||||
def stream = this.class.getClassLoader().getResourceAsStream("configs/chains-basic.yaml")
|
|
||||||
when:
|
|
||||||
def config = reader.read(stream)
|
|
||||||
def eth = config.resolve(Chain.ETHEREUM__MAINNET)
|
|
||||||
def pol = config.resolve(Chain.POLYGON_POS__MAINNET)
|
|
||||||
def opt = config.resolve(Chain.OPTIMISM__MAINNET)
|
|
||||||
def sep = config.resolve(Chain.ETHEREUM__SEPOLIA)
|
|
||||||
then:
|
|
||||||
eth.laggingLagSize == 1
|
|
||||||
eth.syncingLagSize == 6
|
|
||||||
eth.callLimitContract == "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
|
|
||||||
eth.expectedBlockTime == Duration.ofSeconds(12)
|
|
||||||
|
|
||||||
pol.laggingLagSize == 10
|
|
||||||
pol.syncingLagSize == 20
|
|
||||||
pol.expectedBlockTime == Duration.ofMillis(2700)
|
|
||||||
|
|
||||||
opt.laggingLagSize == 3
|
|
||||||
opt.syncingLagSize == 40
|
|
||||||
opt.options.validatePeers == false
|
|
||||||
opt.expectedBlockTime == Duration.ofMillis(400)
|
|
||||||
|
|
||||||
sep.laggingLagSize == 1
|
|
||||||
sep.syncingLagSize == 10
|
|
||||||
sep.expectedBlockTime == Duration.ofSeconds(12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2020 EmeraldPay, Inc
|
|
||||||
*
|
|
||||||
* 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 spock.lang.Specification
|
|
||||||
|
|
||||||
class EnvVariablesSpec extends Specification {
|
|
||||||
|
|
||||||
EnvVariables reader = new EnvVariables()
|
|
||||||
|
|
||||||
def "Post process for usual strings"() {
|
|
||||||
expect:
|
|
||||||
s == reader.postProcess(s)
|
|
||||||
where:
|
|
||||||
s << ["", "a", "13143", "/etc/client1.myservice.com.key", "true", "1a68f20154fc258fe4149c199ad8f281"]
|
|
||||||
}
|
|
||||||
|
|
||||||
def "Post process replaces from env"() {
|
|
||||||
setup:
|
|
||||||
System.setProperty("id", "1")
|
|
||||||
System.setProperty("HOME", "/home/user")
|
|
||||||
System.setProperty("PASSWORD", "1a68f20154fc258fe4149c199ad8f281")
|
|
||||||
expect:
|
|
||||||
replaced == reader.postProcess(orig)
|
|
||||||
where:
|
|
||||||
orig | replaced
|
|
||||||
"p_\${id}" | "p_1"
|
|
||||||
"home: \${HOME}" | "home: /home/user"
|
|
||||||
"\${PASSWORD}" | "1a68f20154fc258fe4149c199ad8f281"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,7 +51,7 @@ class MainConfigReaderSpec extends Specification {
|
|||||||
port == 8082
|
port == 8082
|
||||||
tls != null
|
tls != null
|
||||||
routes != null
|
routes != null
|
||||||
routes.size() == 5
|
routes.size() == 3
|
||||||
with(routes[0]) {
|
with(routes[0]) {
|
||||||
id == "eth"
|
id == "eth"
|
||||||
blockchain == Chain.ETHEREUM__MAINNET
|
blockchain == Chain.ETHEREUM__MAINNET
|
||||||
@@ -61,17 +61,9 @@ class MainConfigReaderSpec extends Specification {
|
|||||||
blockchain == Chain.ETHEREUM_CLASSIC__MAINNET
|
blockchain == Chain.ETHEREUM_CLASSIC__MAINNET
|
||||||
}
|
}
|
||||||
with(routes[2]) {
|
with(routes[2]) {
|
||||||
id == "kovan"
|
|
||||||
blockchain == Chain.ETHEREUM__KOVAN
|
|
||||||
}
|
|
||||||
with(routes[3]) {
|
|
||||||
id == "goerli"
|
id == "goerli"
|
||||||
blockchain == Chain.ETHEREUM__GOERLI
|
blockchain == Chain.ETHEREUM__GOERLI
|
||||||
}
|
}
|
||||||
with(routes[4]) {
|
|
||||||
id == "rinkeby"
|
|
||||||
blockchain == Chain.ETHEREUM__RINKEBY
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
with(act.health) {
|
with(act.health) {
|
||||||
it.enabled
|
it.enabled
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.config
|
package io.emeraldpay.dshackle.config
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptionsReader
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ import java.time.Duration
|
|||||||
|
|
||||||
class UpstreamsConfigReaderSpec extends Specification {
|
class UpstreamsConfigReaderSpec extends Specification {
|
||||||
|
|
||||||
UpstreamsConfigReader reader = new UpstreamsConfigReader(TestingCommons.fileResolver())
|
UpstreamsConfigReader reader = new UpstreamsConfigReader(TestingCommons.fileResolver(), new ChainOptionsReader())
|
||||||
|
|
||||||
def "Parse standard config"() {
|
def "Parse standard config"() {
|
||||||
setup:
|
setup:
|
||||||
@@ -489,8 +490,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for disableValidation"() {
|
def "Merge options for disableValidation"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { disableValidation = base }
|
def a = new ChainOptions.PartialOptions().tap { disableValidation = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { disableValidation = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { disableValidation = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.disableValidation == exp
|
result.disableValidation == exp
|
||||||
|
|
||||||
@@ -511,8 +512,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for providesBalance"() {
|
def "Merge options for providesBalance"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { providesBalance = base }
|
def a = new ChainOptions.PartialOptions().tap { providesBalance = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { providesBalance = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { providesBalance = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.providesBalance == exp
|
result.providesBalance == exp
|
||||||
|
|
||||||
@@ -533,8 +534,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for validatePeers"() {
|
def "Merge options for validatePeers"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { validatePeers = base }
|
def a = new ChainOptions.PartialOptions().tap { validatePeers = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { validatePeers = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { validatePeers = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.validatePeers == exp
|
result.validatePeers == exp
|
||||||
|
|
||||||
@@ -555,8 +556,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for validateSyncing"() {
|
def "Merge options for validateSyncing"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { validateSyncing = base }
|
def a = new ChainOptions.PartialOptions().tap { validateSyncing = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { validateSyncing = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { validateSyncing = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.validateSyncing == exp
|
result.validateSyncing == exp
|
||||||
|
|
||||||
@@ -577,10 +578,10 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for timeout"() {
|
def "Merge options for timeout"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap {
|
def a = new ChainOptions.PartialOptions().tap {
|
||||||
timeout = base == null ? null : Duration.ofSeconds(base)
|
timeout = base == null ? null : Duration.ofSeconds(base)
|
||||||
}
|
}
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap {
|
def b = new ChainOptions.PartialOptions().tap {
|
||||||
timeout = overwrite == null ? null : Duration.ofSeconds(overwrite)
|
timeout = overwrite == null ? null : Duration.ofSeconds(overwrite)
|
||||||
}
|
}
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
@@ -598,8 +599,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for minPeers"() {
|
def "Merge options for minPeers"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { minPeers = base }
|
def a = new ChainOptions.PartialOptions().tap { minPeers = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { minPeers = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { minPeers = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.minPeers == exp
|
result.minPeers == exp
|
||||||
|
|
||||||
@@ -614,8 +615,8 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Merge options for validationInterval"() {
|
def "Merge options for validationInterval"() {
|
||||||
expect:
|
expect:
|
||||||
def a = new UpstreamsConfig.PartialOptions().tap { validationInterval = base }
|
def a = new ChainOptions.PartialOptions().tap { validationInterval = base }
|
||||||
def b = new UpstreamsConfig.PartialOptions().tap { validationInterval = overwrite }
|
def b = new ChainOptions.PartialOptions().tap { validationInterval = overwrite }
|
||||||
def result = a.merge(b).buildOptions()
|
def result = a.merge(b).buildOptions()
|
||||||
result.validationInterval == exp
|
result.validationInterval == exp
|
||||||
|
|
||||||
@@ -630,11 +631,11 @@ class UpstreamsConfigReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Options with default values"() {
|
def "Options with default values"() {
|
||||||
setup:
|
setup:
|
||||||
def partialOptions = new UpstreamsConfig.PartialOptions()
|
def partialOptions = new ChainOptions.PartialOptions()
|
||||||
when:
|
when:
|
||||||
def options = partialOptions.buildOptions()
|
def options = partialOptions.buildOptions()
|
||||||
then:
|
then:
|
||||||
options == new UpstreamsConfig.Options(
|
options == new ChainOptions.Options(
|
||||||
false, false, 30, Duration.ofSeconds(60), null, true, 1, true, true, true
|
false, false, 30, Duration.ofSeconds(60), null, true, 1, true, true, true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2021 EmeraldPay, Inc
|
|
||||||
*
|
|
||||||
* 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 org.jetbrains.annotations.Nullable
|
|
||||||
import org.yaml.snakeyaml.Yaml
|
|
||||||
import org.yaml.snakeyaml.nodes.MappingNode
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
class YamlConfigReaderSpec extends Specification {
|
|
||||||
|
|
||||||
def "reads bytes values"() {
|
|
||||||
setup:
|
|
||||||
def rdr = new Impl()
|
|
||||||
expect:
|
|
||||||
rdr.getValueAsBytes(asNode("test", input), "test") == exp
|
|
||||||
where:
|
|
||||||
input | exp
|
|
||||||
"1024" | 1024
|
|
||||||
"1k" | 1024
|
|
||||||
"1kb" | 1024
|
|
||||||
"1K" | 1024
|
|
||||||
"16kb" | 16 * 1024
|
|
||||||
"1M" | 1024 * 1024
|
|
||||||
"4mb" | 4 * 1024 * 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
private MappingNode asNode(String key, String value) {
|
|
||||||
return new Yaml().compose(new StringReader("$key: $value")) as MappingNode
|
|
||||||
}
|
|
||||||
|
|
||||||
class Impl extends YamlConfigReader {
|
|
||||||
@Override
|
|
||||||
Object read(@Nullable MappingNode input) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -292,7 +292,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall(upstreams)
|
def nativeCall = nativeCall(upstreams)
|
||||||
|
|
||||||
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
def req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
||||||
.setChainValue(Chain.ETHEREUM__MORDEN.id)
|
.setChainValue(Chain.ETHEREUM__GOERLI.id)
|
||||||
.addAllItems([1, 2].collect { id ->
|
.addAllItems([1, 2].collect { id ->
|
||||||
return BlockchainOuterClass.NativeCallItem.newBuilder()
|
return BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||||
.setId(id)
|
.setId(id)
|
||||||
@@ -300,7 +300,7 @@ class NativeCallSpec extends Specification {
|
|||||||
.build()
|
.build()
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
1 * upstreams.isAvailable(Chain.ETHEREUM__MORDEN) >> false
|
1 * upstreams.isAvailable(Chain.ETHEREUM__GOERLI) >> false
|
||||||
when:
|
when:
|
||||||
def resp = nativeCall.prepareCall(req)
|
def resp = nativeCall.prepareCall(req)
|
||||||
then:
|
then:
|
||||||
|
|||||||
@@ -17,10 +17,9 @@
|
|||||||
package io.emeraldpay.dshackle.test
|
package io.emeraldpay.dshackle.test
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig.Options
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||||
@@ -31,6 +30,7 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
import org.jetbrains.annotations.NotNull
|
import org.jetbrains.annotations.NotNull
|
||||||
import org.reactivestreams.Publisher
|
import org.reactivestreams.Publisher
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
|
|
||||||
class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
||||||
EthereumHeadMock ethereumHeadMock
|
EthereumHeadMock ethereumHeadMock
|
||||||
@@ -80,8 +80,8 @@ class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
|||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
static Options getOpts() {
|
static ChainOptions.Options getOpts() {
|
||||||
def opt = UpstreamsConfig.PartialOptions.getDefaults()
|
def opt = ChainOptions.PartialOptions.getDefaults()
|
||||||
opt.setDisableValidation(true)
|
opt.setDisableValidation(true)
|
||||||
opt.setDisableUpstreamValidation(true)
|
opt.setDisableUpstreamValidation(true)
|
||||||
return opt.buildOptions()
|
return opt.buildOptions()
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
|
|||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
import org.jetbrains.annotations.NotNull
|
import org.jetbrains.annotations.NotNull
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import org.reactivestreams.Publisher
|
import org.reactivestreams.Publisher
|
||||||
|
|
||||||
class EthereumRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
class EthereumRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
||||||
@@ -56,7 +57,7 @@ class EthereumRpcUpstreamMock extends EthereumLikeRpcUpstream {
|
|||||||
|
|
||||||
EthereumRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
|
EthereumRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
|
||||||
super(id, id.hashCode().byteValue(), chain,
|
super(id, id.hashCode().byteValue(), chain,
|
||||||
UpstreamsConfig.PartialOptions.getDefaults().buildOptions(),
|
ChainOptions.PartialOptions.getDefaults().buildOptions(),
|
||||||
UpstreamsConfig.UpstreamRole.PRIMARY,
|
UpstreamsConfig.UpstreamRole.PRIMARY,
|
||||||
methods,
|
methods,
|
||||||
new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
|
new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import io.emeraldpay.dshackle.upstream.forkchoice.MostWorkForkChoice
|
|||||||
import reactor.core.scheduler.Schedulers
|
import reactor.core.scheduler.Schedulers
|
||||||
import reactor.test.StepVerifier
|
import reactor.test.StepVerifier
|
||||||
import spock.lang.Retry
|
import spock.lang.Retry
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@@ -66,7 +67,7 @@ class FilteredApisSpec extends Specification {
|
|||||||
"test",
|
"test",
|
||||||
(byte) 123,
|
(byte) 123,
|
||||||
Chain.ETHEREUM__MAINNET,
|
Chain.ETHEREUM__MAINNET,
|
||||||
new UpstreamsConfig.PartialOptions().buildOptions(),
|
new ChainOptions.PartialOptions().buildOptions(),
|
||||||
UpstreamsConfig.UpstreamRole.PRIMARY,
|
UpstreamsConfig.UpstreamRole.PRIMARY,
|
||||||
ethereumTargets,
|
ethereumTargets,
|
||||||
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
|
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
|
||||||
|
|||||||
@@ -39,9 +39,7 @@ class DefaultEthereumMethodsSpec extends Specification {
|
|||||||
chain | id
|
chain | id
|
||||||
Chain.ETHEREUM__MAINNET | '"0x1"'
|
Chain.ETHEREUM__MAINNET | '"0x1"'
|
||||||
Chain.ETHEREUM_CLASSIC__MAINNET | '"0x3d"'
|
Chain.ETHEREUM_CLASSIC__MAINNET | '"0x3d"'
|
||||||
Chain.ETHEREUM__KOVAN | '"0x2a"'
|
|
||||||
Chain.ETHEREUM__GOERLI | '"0x5"'
|
Chain.ETHEREUM__GOERLI | '"0x5"'
|
||||||
Chain.ETHEREUM__RINKEBY | '"0x4"'
|
|
||||||
Chain.ETHEREUM__ROPSTEN | '"0x3"'
|
Chain.ETHEREUM__ROPSTEN | '"0x3"'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +59,7 @@ class DefaultEthereumMethodsSpec extends Specification {
|
|||||||
new DefaultEthereumMethods(chain).getSupportedMethods().containsAll(methods)
|
new DefaultEthereumMethods(chain).getSupportedMethods().containsAll(methods)
|
||||||
where:
|
where:
|
||||||
chain | methods
|
chain | methods
|
||||||
Chain.POLYGON_POS__MAINNET | ["bor_getAuthor",
|
Chain.POLYGON__MAINNET | ["bor_getAuthor",
|
||||||
"bor_getCurrentValidators",
|
"bor_getCurrentValidators",
|
||||||
"bor_getCurrentProposer",
|
"bor_getCurrentProposer",
|
||||||
"bor_getRootHash",
|
"bor_getRootHash",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
import io.emeraldpay.dshackle.test.ApiReaderMock
|
import io.emeraldpay.dshackle.test.ApiReaderMock
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
@@ -43,7 +43,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Resolve to final availability"() {
|
def "Resolve to final availability"() {
|
||||||
setup:
|
setup:
|
||||||
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, Stub(EthereumLikeUpstream), UpstreamsConfig.PartialOptions.getDefaults().buildOptions())
|
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, Stub(EthereumLikeUpstream), ChainOptions.PartialOptions.getDefaults().buildOptions())
|
||||||
expect:
|
expect:
|
||||||
validator.resolve(Tuples.of(sync, peers, call)) == exp
|
validator.resolve(Tuples.of(sync, peers, call)) == exp
|
||||||
where:
|
where:
|
||||||
@@ -61,7 +61,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Doesnt check eth_syncing when disabled"() {
|
def "Doesnt check eth_syncing when disabled"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateSyncing = false
|
it.validateSyncing = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeUpstream)
|
def up = Mock(EthereumLikeUpstream)
|
||||||
@@ -76,7 +76,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Syncing is OK when false returned from upstream"() {
|
def "Syncing is OK when false returned from upstream"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateSyncing = true
|
it.validateSyncing = true
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = TestingCommons.upstream(
|
def up = TestingCommons.upstream(
|
||||||
@@ -94,7 +94,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Execute onSyncingNode with result of eth_syncing"() {
|
def "Execute onSyncingNode with result of eth_syncing"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateSyncing = true
|
it.validateSyncing = true
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeUpstream) {
|
def up = Mock(EthereumLikeUpstream) {
|
||||||
@@ -121,7 +121,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Syncing is SYNCING when state returned from upstream"() {
|
def "Syncing is SYNCING when state returned from upstream"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateSyncing = true
|
it.validateSyncing = true
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = TestingCommons.upstream(
|
def up = TestingCommons.upstream(
|
||||||
@@ -139,7 +139,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Syncing is UNAVAILABLE when error returned from upstream"() {
|
def "Syncing is UNAVAILABLE when error returned from upstream"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateSyncing = true
|
it.validateSyncing = true
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = TestingCommons.upstream(
|
def up = TestingCommons.upstream(
|
||||||
@@ -157,7 +157,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Doesnt validate peers when disabled"() {
|
def "Doesnt validate peers when disabled"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = false
|
it.validatePeers = false
|
||||||
it.minPeers = 10
|
it.minPeers = 10
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -173,7 +173,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Doesnt validate peers when zero peers is expected"() {
|
def "Doesnt validate peers when zero peers is expected"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = true
|
it.validatePeers = true
|
||||||
it.minPeers = 0
|
it.minPeers = 0
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -189,7 +189,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Peers is IMMATURE when state returned too few peers"() {
|
def "Peers is IMMATURE when state returned too few peers"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = true
|
it.validatePeers = true
|
||||||
it.minPeers = 10
|
it.minPeers = 10
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -208,7 +208,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Peers is OK when state returned exactly min peers"() {
|
def "Peers is OK when state returned exactly min peers"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = true
|
it.validatePeers = true
|
||||||
it.minPeers = 10
|
it.minPeers = 10
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -227,7 +227,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Peers is OK when state returned more than enough peers"() {
|
def "Peers is OK when state returned more than enough peers"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = true
|
it.validatePeers = true
|
||||||
it.minPeers = 10
|
it.minPeers = 10
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -246,7 +246,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Peers is UNAVAILABLE when state returned error"() {
|
def "Peers is UNAVAILABLE when state returned error"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validatePeers = true
|
it.validatePeers = true
|
||||||
it.minPeers = 10
|
it.minPeers = 10
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -265,7 +265,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Doesnt validate chan and callLimit when disabled"() {
|
def "Doesnt validate chan and callLimit when disabled"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateCalllimit = false
|
it.validateCalllimit = false
|
||||||
it.validateChain = false
|
it.validateChain = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
@@ -287,7 +287,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is valid if not error from call limit check"() {
|
def "Upstream is valid if not error from call limit check"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateChain = false
|
it.validateChain = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
@@ -311,7 +311,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is not valid if error returned on call limit check"() {
|
def "Upstream is not valid if error returned on call limit check"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateChain = false
|
it.validateChain = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
@@ -335,7 +335,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is valid if chain settings are valid"() {
|
def "Upstream is valid if chain settings are valid"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateCalllimit = false
|
it.validateCalllimit = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
@@ -357,7 +357,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is not valid - specified optimism but got ethereum"() {
|
def "Upstream is not valid - specified optimism but got ethereum"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
|
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||||
it.validateCalllimit = false
|
it.validateCalllimit = false
|
||||||
}.buildOptions()
|
}.buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
@@ -379,7 +379,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is valid if all setting are valid"() {
|
def "Upstream is valid if all setting are valid"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().buildOptions()
|
def options = ChainOptions.PartialOptions.getDefaults().buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
5 * getIngressReader() >> Mock(Reader) {
|
5 * getIngressReader() >> Mock(Reader) {
|
||||||
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse('"0x1"'.getBytes(), null))
|
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse('"0x1"'.getBytes(), null))
|
||||||
@@ -403,7 +403,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
|||||||
|
|
||||||
def "Upstream is not valid if there are errors"() {
|
def "Upstream is not valid if there are errors"() {
|
||||||
setup:
|
setup:
|
||||||
def options = UpstreamsConfig.PartialOptions.getDefaults().buildOptions()
|
def options = ChainOptions.PartialOptions.getDefaults().buildOptions()
|
||||||
def up = Mock(EthereumLikeRpcUpstream) {
|
def up = Mock(EthereumLikeRpcUpstream) {
|
||||||
5 * getIngressReader() >> Mock(Reader) {
|
5 * getIngressReader() >> Mock(Reader) {
|
||||||
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse(null, new JsonRpcError(1, "Too long")))
|
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse(null, new JsonRpcError(1, "Too long")))
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import io.grpc.stub.StreamObserver
|
|||||||
import io.micrometer.core.instrument.Counter
|
import io.micrometer.core.instrument.Counter
|
||||||
import io.micrometer.core.instrument.Timer
|
import io.micrometer.core.instrument.Timer
|
||||||
import reactor.core.scheduler.Schedulers
|
import reactor.core.scheduler.Schedulers
|
||||||
import reactor.test.StepVerifier
|
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import io.emeraldpay.api.proto.Common.ChainRef
|
|||||||
import io.emeraldpay.dshackle.config.MainConfig
|
import io.emeraldpay.dshackle.config.MainConfig
|
||||||
import io.emeraldpay.dshackle.config.MainConfigReader
|
import io.emeraldpay.dshackle.config.MainConfigReader
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.github.ganchix.ganache.Account
|
import io.github.ganchix.ganache.Account
|
||||||
import io.github.ganchix.ganache.GanacheContainer
|
import io.github.ganchix.ganache.GanacheContainer
|
||||||
import io.grpc.BindableService
|
import io.grpc.BindableService
|
||||||
@@ -43,8 +44,8 @@ class IntegrationTest {
|
|||||||
withAccounts(
|
withAccounts(
|
||||||
listOf(
|
listOf(
|
||||||
Account.builder().privateKey(PRIVATE_KEY_0).balance(BigInteger.valueOf(2000000000000000000)).build(),
|
Account.builder().privateKey(PRIVATE_KEY_0).balance(BigInteger.valueOf(2000000000000000000)).build(),
|
||||||
Account.builder().privateKey(PRIVATE_KEY_1).balance(BigInteger.valueOf(2000000000000000000)).build()
|
Account.builder().privateKey(PRIVATE_KEY_1).balance(BigInteger.valueOf(2000000000000000000)).build(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ class IntegrationTest {
|
|||||||
val reader = MainConfigReader(fileResolver)
|
val reader = MainConfigReader(fileResolver)
|
||||||
val config = reader.read(
|
val config = reader.read(
|
||||||
ResourceUtils.getFile("classpath:integration/dshackle.yaml")
|
ResourceUtils.getFile("classpath:integration/dshackle.yaml")
|
||||||
.inputStream()
|
.inputStream(),
|
||||||
)!!
|
)!!
|
||||||
patch(config)
|
patch(config)
|
||||||
return config
|
return config
|
||||||
@@ -94,7 +95,7 @@ class IntegrationTest {
|
|||||||
id = "ganache"
|
id = "ganache"
|
||||||
nodeId = 1
|
nodeId = 1
|
||||||
chain = "ethereum"
|
chain = "ethereum"
|
||||||
options = UpstreamsConfig.PartialOptions()
|
options = ChainOptions.PartialOptions()
|
||||||
.apply {
|
.apply {
|
||||||
validateChain = false
|
validateChain = false
|
||||||
}
|
}
|
||||||
@@ -102,12 +103,12 @@ class IntegrationTest {
|
|||||||
execution = UpstreamsConfig.EthereumConnection().apply {
|
execution = UpstreamsConfig.EthereumConnection().apply {
|
||||||
rpc = UpstreamsConfig.HttpEndpoint(
|
rpc = UpstreamsConfig.HttpEndpoint(
|
||||||
URI.create(
|
URI.create(
|
||||||
"http://" + ganacheContainer.getHost() + ":" + ganacheContainer.getMappedPort(8545) + "/"
|
"http://" + ganacheContainer.getHost() + ":" + ganacheContainer.getMappedPort(8545) + "/",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
package io.emeraldpay.dshackle.config
|
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
internal class ChainsConfigTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun patch() {
|
|
||||||
val orig = ChainsConfig(
|
|
||||||
mapOf(
|
|
||||||
Chain.BITCOIN__MAINNET to createRawChainConfig(0, 0),
|
|
||||||
Chain.ETHEREUM__MAINNET to createRawChainConfig(1, 2),
|
|
||||||
Chain.POLYGON_POS__MAINNET to createRawChainConfig(3, 4)
|
|
||||||
),
|
|
||||||
createRawChainConfig(1, 2)
|
|
||||||
)
|
|
||||||
|
|
||||||
val patch = ChainsConfig(
|
|
||||||
mapOf(
|
|
||||||
Chain.BITCOIN__MAINNET to createRawChainConfig(null, 10000),
|
|
||||||
Chain.POLYGON_POS__MAINNET to createRawChainConfig(10, 11),
|
|
||||||
Chain.ARBITRUM__MAINNET to createRawChainConfig(999, 999)
|
|
||||||
),
|
|
||||||
createRawChainConfig(100, null)
|
|
||||||
)
|
|
||||||
|
|
||||||
val res = orig.patch(patch)
|
|
||||||
|
|
||||||
assertEquals(
|
|
||||||
ChainsConfig(
|
|
||||||
mapOf(
|
|
||||||
Chain.BITCOIN__MAINNET to createRawChainConfig(0, 10000),
|
|
||||||
Chain.ETHEREUM__MAINNET to createRawChainConfig(1, 2),
|
|
||||||
Chain.POLYGON_POS__MAINNET to createRawChainConfig(10, 11),
|
|
||||||
Chain.ARBITRUM__MAINNET to createRawChainConfig(999, 999)
|
|
||||||
),
|
|
||||||
createRawChainConfig(100, 2)
|
|
||||||
),
|
|
||||||
res
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createRawChainConfig(syncingLagSize: Int?, laggingLagSize: Int?) =
|
|
||||||
ChainsConfig.RawChainConfig()
|
|
||||||
.apply {
|
|
||||||
this.syncingLagSize = syncingLagSize
|
|
||||||
this.laggingLagSize = laggingLagSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,21 +15,21 @@ internal class UpstreamsConfigTest {
|
|||||||
return Stream.of(
|
return Stream.of(
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection(),
|
UpstreamsConfig.EthereumConnection(),
|
||||||
ConnectorMode.RPC_ONLY
|
ConnectorMode.RPC_ONLY,
|
||||||
),
|
),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection()
|
UpstreamsConfig.EthereumConnection()
|
||||||
.apply {
|
.apply {
|
||||||
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
||||||
},
|
},
|
||||||
ConnectorMode.WS_ONLY
|
ConnectorMode.WS_ONLY,
|
||||||
),
|
),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection()
|
UpstreamsConfig.EthereumConnection()
|
||||||
.apply {
|
.apply {
|
||||||
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
||||||
},
|
},
|
||||||
ConnectorMode.WS_ONLY
|
ConnectorMode.WS_ONLY,
|
||||||
),
|
),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection()
|
UpstreamsConfig.EthereumConnection()
|
||||||
@@ -37,7 +37,7 @@ internal class UpstreamsConfigTest {
|
|||||||
connectorMode = "RPC_REQUESTS_WITH_WS_HEAD"
|
connectorMode = "RPC_REQUESTS_WITH_WS_HEAD"
|
||||||
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
||||||
},
|
},
|
||||||
ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD
|
ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD,
|
||||||
),
|
),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection()
|
UpstreamsConfig.EthereumConnection()
|
||||||
@@ -45,7 +45,7 @@ internal class UpstreamsConfigTest {
|
|||||||
connectorMode = "RPC_REQUESTS_WITH_MIXED_HEAD"
|
connectorMode = "RPC_REQUESTS_WITH_MIXED_HEAD"
|
||||||
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
||||||
},
|
},
|
||||||
ConnectorMode.RPC_REQUESTS_WITH_MIXED_HEAD
|
ConnectorMode.RPC_REQUESTS_WITH_MIXED_HEAD,
|
||||||
),
|
),
|
||||||
Arguments.of(
|
Arguments.of(
|
||||||
UpstreamsConfig.EthereumConnection()
|
UpstreamsConfig.EthereumConnection()
|
||||||
@@ -53,7 +53,7 @@ internal class UpstreamsConfigTest {
|
|||||||
rpc = UpstreamsConfig.HttpEndpoint(URI("http://localhost:8546"))
|
rpc = UpstreamsConfig.HttpEndpoint(URI("http://localhost:8546"))
|
||||||
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
ws = UpstreamsConfig.WsEndpoint(URI("ws://localhost:8546"))
|
||||||
},
|
},
|
||||||
ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD
|
ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
version: v1
|
|
||||||
|
|
||||||
chain-settings:
|
|
||||||
default:
|
|
||||||
lags:
|
|
||||||
syncing: 6
|
|
||||||
lagging: 1
|
|
||||||
expected-block-time: 12s
|
|
||||||
chains:
|
|
||||||
- id: eth
|
|
||||||
call-validate-contract: 0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
|
|
||||||
expected-block-time: 12s
|
|
||||||
lags:
|
|
||||||
syncing: 6
|
|
||||||
lagging: 1
|
|
||||||
- id: optimism
|
|
||||||
expected-block-time: 400ms
|
|
||||||
lags:
|
|
||||||
lagging: 3
|
|
||||||
- id: sepolia
|
|
||||||
lags:
|
|
||||||
syncing: 10
|
|
||||||
@@ -45,13 +45,8 @@ proxy:
|
|||||||
blockchain: ethereum
|
blockchain: ethereum
|
||||||
- id: etc
|
- id: etc
|
||||||
blockchain: ethereum_classic
|
blockchain: ethereum_classic
|
||||||
- id: kovan
|
|
||||||
blockchain: kovan
|
|
||||||
- id: goerli
|
- id: goerli
|
||||||
blockchain: goerli
|
blockchain: goerli
|
||||||
- id: rinkeby
|
|
||||||
blockchain: rinkeby
|
|
||||||
|
|
||||||
cluster:
|
cluster:
|
||||||
defaults:
|
defaults:
|
||||||
- chains:
|
- chains:
|
||||||
|
|||||||
Reference in New Issue
Block a user