From 10ec822ec2961a94bf2ba9b35ae228b2ce5d94c6 Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Wed, 28 Sep 2022 22:44:04 -0400 Subject: [PATCH] problem: upstream options are not always correctly merged when defined in few places --- .../dshackle/config/UpstreamsConfig.kt | 81 +++++++---- .../dshackle/config/UpstreamsConfigReader.kt | 11 +- .../dshackle/startup/ConfiguredUpstreams.kt | 16 +-- .../dshackle/upstream/Multistream.kt | 2 +- .../upstream/ethereum/EthereumRpcUpstream.kt | 4 +- .../ethereum/EthereumUpstreamValidator.kt | 3 +- .../upstream/grpc/BitcoinGrpcUpstream.kt | 2 +- .../upstream/grpc/EthereumGrpcUpstream.kt | 2 +- .../dshackle/upstream/grpc/GrpcUpstreams.kt | 2 +- .../config/UpstreamsConfigReaderSpec.groovy | 136 ++++++++++++++++++ .../dshackle/test/EthereumUpstreamMock.groovy | 2 +- .../upstream/DefaultUpstreamSpec.groovy | 10 +- .../dshackle/upstream/FilteredApisSpec.groovy | 2 +- .../EthereumUpstreamValidatorSpec.groovy | 42 +++--- 14 files changed, 239 insertions(+), 76 deletions(-) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfig.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfig.kt index dfa6ca836..f011355b0 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfig.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfig.kt @@ -17,7 +17,9 @@ package io.emeraldpay.dshackle.config import io.emeraldpay.dshackle.Defaults +import org.apache.commons.lang3.ObjectUtils import java.net.URI +import java.time.Duration import java.util.Arrays import java.util.Locale @@ -29,57 +31,84 @@ open class UpstreamsConfig { private const val MIN_PRIORITY = 0 private const val MAX_PRIORITY = 1_000_000 private const val DEFAULT_PRIORITY = 10 + private const val DEFAULT_VALIDATION_INTERVAL = 30 } - open class Options { + data class Options( + var disableValidation: Boolean, + var validationInterval: Duration, + var timeout: Duration, + var providesBalance: Boolean, + var priority: Int, + var validatePeers: Boolean, + var minPeers: Int, + var validateSyncing: Boolean + ) + + open class PartialOptions { + var disableValidation: Boolean? = null - var validationInterval: Int = 30 + var validationInterval: Int? = null set(value) { - require(value > 0) { + require(value == null || value > 0) { "validation-interval must be a positive number: $value" } field = value } - var timeout = Defaults.timeout + + var timeout: Int? = null var providesBalance: Boolean? = null - var priority: Int = DEFAULT_PRIORITY + var priority: Int? = null set(value) { - require(value in MIN_PRIORITY..MAX_PRIORITY) { + require(value == null || value in MIN_PRIORITY..MAX_PRIORITY) { "Upstream priority must be in $MIN_PRIORITY..$MAX_PRIORITY. Configured: $value" } field = value } - var validatePeers: Boolean = true - var minPeers: Int? = 1 + var validatePeers: Boolean? = null + var minPeers: Int? = null set(value) { - require(value != null && value >= 0) { + require(value == null || value >= 0) { "min-peers must be a positive number: $value" } field = value } - var validateSyncing: Boolean = true + var validateSyncing: Boolean? = null - fun merge(overwrites: Options?): Options { + fun merge(overwrites: PartialOptions?): PartialOptions { if (overwrites == null) { return this } - val copy = Options() - copy.priority = this.priority.coerceAtLeast(overwrites.priority) - copy.validatePeers = this.validatePeers && overwrites.validatePeers - copy.minPeers = if (this.minPeers != null) this.minPeers else overwrites.minPeers - copy.disableValidation = - if (this.disableValidation != null) this.disableValidation else overwrites.disableValidation - copy.validationInterval = overwrites.validationInterval - copy.providesBalance = - if (this.providesBalance != null) this.providesBalance else overwrites.providesBalance - copy.validateSyncing = this.validateSyncing && overwrites.validateSyncing + val copy = PartialOptions() + copy.disableValidation = ObjectUtils.firstNonNull(overwrites.disableValidation, this.disableValidation) + copy.validationInterval = ObjectUtils.firstNonNull(overwrites.validationInterval, this.validationInterval) + copy.timeout = ObjectUtils.firstNonNull(overwrites.timeout, this.timeout) + copy.providesBalance = ObjectUtils.firstNonNull(overwrites.providesBalance, this.providesBalance) + copy.priority = ObjectUtils.firstNonNull(overwrites.priority, this.priority) + copy.validatePeers = ObjectUtils.firstNonNull(overwrites.validatePeers, this.validatePeers) + copy.minPeers = ObjectUtils.firstNonNull(overwrites.minPeers, this.minPeers) + copy.validateSyncing = ObjectUtils.firstNonNull(overwrites.validateSyncing, this.validateSyncing) return copy } + fun build(): Options { + return Options( + disableValidation = ObjectUtils.firstNonNull(this.disableValidation, false)!!, + validationInterval = ObjectUtils.firstNonNull(this.validationInterval, DEFAULT_VALIDATION_INTERVAL)!! + .toLong().let(Duration::ofSeconds), + timeout = ObjectUtils.firstNonNull(this.timeout?.toLong()?.let(Duration::ofSeconds), Defaults.timeout)!!, + providesBalance = ObjectUtils.firstNonNull(this.providesBalance, false)!!, + priority = ObjectUtils.firstNonNull(this.priority, DEFAULT_PRIORITY)!!, + validatePeers = ObjectUtils.firstNonNull(this.validatePeers, true)!!, + minPeers = ObjectUtils.firstNonNull(this.minPeers, 1)!!, + validateSyncing = ObjectUtils.firstNonNull(this.validateSyncing, true)!!, + ) + } + companion object { @JvmStatic - fun getDefaults(): Options { - val options = Options() + fun getDefaults(): PartialOptions { + val options = PartialOptions() options.minPeers = 1 options.disableValidation = false return options @@ -87,15 +116,15 @@ open class UpstreamsConfig { } } - class DefaultOptions : Options() { + class DefaultOptions : PartialOptions() { var chains: List? = null - var options: Options? = null + var options: PartialOptions? = null } class Upstream { var id: String? = null var chain: String? = null - var options: Options? = null + var options: PartialOptions? = null var isEnabled = true var connection: T? = null val labels = Labels() diff --git a/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfigReader.kt b/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfigReader.kt index ab2b189bc..3082046fe 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfigReader.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/config/UpstreamsConfigReader.kt @@ -24,7 +24,6 @@ import org.yaml.snakeyaml.nodes.ScalarNode import reactor.util.function.Tuples import java.io.InputStream import java.net.URI -import java.time.Duration import java.util.Locale class UpstreamsConfigReader( @@ -217,7 +216,7 @@ class UpstreamsConfigReader( upstream.methods = tryReadMethods(upNode) getValueAsInt(upNode, "priority")?.let { if (upstream.options == null) { - upstream.options = UpstreamsConfig.Options.getDefaults() + upstream.options = UpstreamsConfig.PartialOptions.getDefaults() } upstream.options!!.priority = it } @@ -270,7 +269,7 @@ class UpstreamsConfigReader( } } - internal fun tryReadOptions(upNode: MappingNode): UpstreamsConfig.Options? { + internal fun tryReadOptions(upNode: MappingNode): UpstreamsConfig.PartialOptions? { return if (hasAny(upNode, "options")) { return getMapping(upNode, "options")?.let { values -> readOptions(values) @@ -305,8 +304,8 @@ class UpstreamsConfigReader( } } - internal fun readOptions(values: MappingNode): UpstreamsConfig.Options { - val options = UpstreamsConfig.Options() + internal fun readOptions(values: MappingNode): UpstreamsConfig.PartialOptions { + val options = UpstreamsConfig.PartialOptions() getValueAsBool(values, "validate-peers")?.let { options.validatePeers = it } @@ -317,7 +316,7 @@ class UpstreamsConfigReader( options.minPeers = it } getValueAsInt(values, "timeout")?.let { - options.timeout = Duration.ofSeconds(it.toLong()) + options.timeout = it } getValueAsBool(values, "disable-validation")?.let { options.disableValidation = it diff --git a/src/main/kotlin/io/emeraldpay/dshackle/startup/ConfiguredUpstreams.kt b/src/main/kotlin/io/emeraldpay/dshackle/startup/ConfiguredUpstreams.kt index 6f67a5a39..3daad37a9 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/startup/ConfiguredUpstreams.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/startup/ConfiguredUpstreams.kt @@ -76,22 +76,22 @@ open class ConfiguredUpstreams( } log.debug("Start upstream ${up.id}") if (up.connection is UpstreamsConfig.GrpcConnection) { - val options = up.options ?: UpstreamsConfig.Options() - buildGrpcUpstream(up.cast(UpstreamsConfig.GrpcConnection::class.java), options) + val options = up.options ?: UpstreamsConfig.PartialOptions() + buildGrpcUpstream(up.cast(UpstreamsConfig.GrpcConnection::class.java), options.build()) } else { val chain = Global.chainById(up.chain) if (chain == Chain.UNSPECIFIED) { log.error("Chain is unknown: ${up.chain}") return@forEach } - val options = (defaultOptions[chain] ?: UpstreamsConfig.Options.getDefaults()) - .merge(up.options ?: UpstreamsConfig.Options()) + val options = (defaultOptions[chain] ?: UpstreamsConfig.PartialOptions.getDefaults()) + .merge(up.options ?: UpstreamsConfig.PartialOptions()) when (BlockchainType.from(chain)) { BlockchainType.ETHEREUM -> { - buildEthereumUpstream(up.cast(UpstreamsConfig.EthereumConnection::class.java), chain, options) + buildEthereumUpstream(up.cast(UpstreamsConfig.EthereumConnection::class.java), chain, options.build()) } BlockchainType.BITCOIN -> { - buildBitcoinUpstream(up.cast(UpstreamsConfig.BitcoinConnection::class.java), chain, options) + buildBitcoinUpstream(up.cast(UpstreamsConfig.BitcoinConnection::class.java), chain, options.build()) } else -> { log.error("Chain is unsupported: ${up.chain}") @@ -102,8 +102,8 @@ open class ConfiguredUpstreams( } } - private fun buildDefaultOptions(config: UpstreamsConfig): HashMap { - val defaultOptions = HashMap() + private fun buildDefaultOptions(config: UpstreamsConfig): HashMap { + val defaultOptions = HashMap() config.defaultOptions.forEach { defaultsConfig -> defaultsConfig.chains?.forEach { chainName -> Global.chainById(chainName).let { chain -> diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/Multistream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/Multistream.kt index 6319cedec..6f7c700de 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/Multistream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/Multistream.kt @@ -207,7 +207,7 @@ abstract class Multistream( // TODO options for multistream are useless override fun getOptions(): UpstreamsConfig.Options { - return UpstreamsConfig.Options() + throw RuntimeException("No options for multistream") } // TODO roles for multistream are useless diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumRpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumRpcUpstream.kt index 8403569c6..818de4a39 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumRpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumRpcUpstream.kt @@ -40,7 +40,7 @@ open class EthereumRpcUpstream( constructor(id: String, chain: Chain, forkWatch: ForkWatch, api: Reader) : this( id, chain, forkWatch, api, null, - UpstreamsConfig.Options.getDefaults(), UpstreamsConfig.UpstreamRole.PRIMARY, + UpstreamsConfig.PartialOptions.getDefaults().build(), UpstreamsConfig.UpstreamRole.PRIMARY, QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels()), DirectCallMethods() ) @@ -62,7 +62,7 @@ open class EthereumRpcUpstream( override fun start() { log.info("Configured for ${chain.chainName}") super.start() - if (getOptions().disableValidation != null && getOptions().disableValidation!!) { + if (getOptions().disableValidation) { log.warn("Disable validation for upstream ${this.getId()}") this.setLag(0) this.setStatus(UpstreamAvailability.OK) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt index 79955ef8a..07d2120b2 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt @@ -30,7 +30,6 @@ import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.core.scheduler.Schedulers import reactor.util.function.Tuple2 -import java.time.Duration import java.util.concurrent.Executors import java.util.concurrent.TimeoutException @@ -110,7 +109,7 @@ open class EthereumUpstreamValidator( } fun start(): Flux { - return Flux.interval(Duration.ofSeconds(options.validationInterval.toLong())) + return Flux.interval(options.validationInterval) .subscribeOn(scheduler) .flatMap { validate() diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt index 937ff5059..9025849ad 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt @@ -68,7 +68,7 @@ class BitcoinGrpcUpstream( } constructor(parentId: String, role: UpstreamsConfig.UpstreamRole, chain: Chain, remote: ReactorBlockchainStub, client: JsonRpcGrpcClient) : - this(parentId, ForkWatch.Never(), role, chain, UpstreamsConfig.Options.getDefaults(), remote, client) + this(parentId, ForkWatch.Never(), role, chain, UpstreamsConfig.PartialOptions.getDefaults().build(), remote, client) private val extractBlock = ExtractBlock() private val defaultReader: Reader = client.forSelector(Selector.empty) diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt index 6b360b107..ec53aaa8e 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt @@ -68,7 +68,7 @@ open class EthereumGrpcUpstream( Lifecycle { constructor(parentId: String, role: UpstreamsConfig.UpstreamRole, chain: Chain, remote: ReactorBlockchainStub, client: JsonRpcGrpcClient) : - this(parentId, ForkWatch.Never(), role, chain, UpstreamsConfig.Options.getDefaults(), remote, client) + this(parentId, ForkWatch.Never(), role, chain, UpstreamsConfig.PartialOptions.getDefaults().build(), remote, client) private val blockConverter: Function = Function { value -> val block = BlockContainer( diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/GrpcUpstreams.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/GrpcUpstreams.kt index c2f240745..0b7769b4b 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/GrpcUpstreams.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/GrpcUpstreams.kt @@ -61,7 +61,7 @@ class GrpcUpstreams( ) { private val log = LoggerFactory.getLogger(GrpcUpstreams::class.java) - var options = UpstreamsConfig.Options.getDefaults() + var options = UpstreamsConfig.PartialOptions.getDefaults().build() private var client: ReactorBlockchainGrpc.ReactorBlockchainStub? = null private val known = HashMap() diff --git a/src/test/groovy/io/emeraldpay/dshackle/config/UpstreamsConfigReaderSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/config/UpstreamsConfigReaderSpec.groovy index a41a039da..a66c78387 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/config/UpstreamsConfigReaderSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/config/UpstreamsConfigReaderSpec.groovy @@ -421,4 +421,140 @@ class UpstreamsConfigReaderSpec extends Specification { validatePeers == true } } + + def "Merge options for disableValidation"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { disableValidation = base } + def b = new UpstreamsConfig.PartialOptions().tap { disableValidation = overwrite } + def result = a.merge(b) + result.disableValidation == exp + + where: + base | overwrite | exp + true | true | true + true | false | false + true | null | true + + false | true | true + false | false | false + false | null | false + + null | true | true + null | false | false + null | null | null + } + + def "Merge options for providesBalance"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { providesBalance = base } + def b = new UpstreamsConfig.PartialOptions().tap { providesBalance = overwrite } + def result = a.merge(b) + result.providesBalance == exp + + where: + base | overwrite | exp + true | true | true + true | false | false + true | null | true + + false | true | true + false | false | false + false | null | false + + null | true | true + null | false | false + null | null | null + } + + def "Merge options for validatePeers"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { validatePeers = base } + def b = new UpstreamsConfig.PartialOptions().tap { validatePeers = overwrite } + def result = a.merge(b) + result.validatePeers == exp + + where: + base | overwrite | exp + true | true | true + true | false | false + true | null | true + + false | true | true + false | false | false + false | null | false + + null | true | true + null | false | false + null | null | null + } + + def "Merge options for validateSyncing"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { validateSyncing = base } + def b = new UpstreamsConfig.PartialOptions().tap { validateSyncing = overwrite } + def result = a.merge(b) + result.validateSyncing == exp + + where: + base | overwrite | exp + true | true | true + true | false | false + true | null | true + + false | true | true + false | false | false + false | null | false + + null | true | true + null | false | false + null | null | null + } + + def "Merge options for timeout"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { timeout = base } + def b = new UpstreamsConfig.PartialOptions().tap { timeout = overwrite } + def result = a.merge(b) + result.timeout == exp + + where: + base | overwrite | exp + 1 | 2 | 2 + 3 | 4 | 4 + 5 | null | 5 + null | 6 | 6 + null | null | null + } + + def "Merge options for priority"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { priority = base } + def b = new UpstreamsConfig.PartialOptions().tap { priority = overwrite } + def result = a.merge(b) + result.priority == exp + + where: + base | overwrite | exp + 1 | 2 | 2 + 3 | 4 | 4 + 5 | null | 5 + null | 6 | 6 + null | null | null + } + + def "Merge options for minPeers"() { + expect: + def a = new UpstreamsConfig.PartialOptions().tap { minPeers = base } + def b = new UpstreamsConfig.PartialOptions().tap { minPeers = overwrite } + def result = a.merge(b) + result.minPeers == exp + + where: + base | overwrite | exp + 1 | 2 | 2 + 3 | 4 | 4 + 5 | null | 5 + null | 6 | 6 + null | null | null + } } diff --git a/src/test/groovy/io/emeraldpay/dshackle/test/EthereumUpstreamMock.groovy b/src/test/groovy/io/emeraldpay/dshackle/test/EthereumUpstreamMock.groovy index fd82bde9d..76e9ba105 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/test/EthereumUpstreamMock.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/test/EthereumUpstreamMock.groovy @@ -64,7 +64,7 @@ class EthereumUpstreamMock extends EthereumRpcUpstream { EthereumUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader api, CallMethods methods) { super(id, chain, new ForkWatch.Never(), api, null, - UpstreamsConfig.Options.getDefaults(), + UpstreamsConfig.PartialOptions.getDefaults().build(), UpstreamsConfig.UpstreamRole.PRIMARY, new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()), methods) diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/DefaultUpstreamSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/DefaultUpstreamSpec.groovy index 703743569..b40a7fb25 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/DefaultUpstreamSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/DefaultUpstreamSpec.groovy @@ -66,9 +66,9 @@ class DefaultUpstreamSpec extends Specification { setup: def upstream = new DefaultUpstreamTestImpl("test", new ForkWatch.Never(), - UpstreamsConfig.Options.getDefaults().tap { + UpstreamsConfig.PartialOptions.getDefaults().tap { it.disableValidation = true - } + }.build() ) when: "we have a zero height" upstream.setStatus(UpstreamAvailability.OK) @@ -88,9 +88,9 @@ class DefaultUpstreamSpec extends Specification { setup: def upstream = new DefaultUpstreamTestImpl("test", new ForkWatch.Never(), - UpstreamsConfig.Options.getDefaults().tap { + UpstreamsConfig.PartialOptions.getDefaults().tap { it.disableValidation = true - } + }.build() ) upstream.setStatus(UpstreamAvailability.UNAVAILABLE) when: @@ -144,7 +144,7 @@ class DefaultUpstreamSpec extends Specification { DefaultUpstreamTestImpl(@NotNull String id, @NotNull ForkWatch forkWatch) { - this(id, forkWatch, UpstreamsConfig.Options.getDefaults()) + this(id, forkWatch, UpstreamsConfig.PartialOptions.getDefaults().build()) } DefaultUpstreamTestImpl(@NotNull String id, diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy index 41e3011c3..ea8e2e851 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/FilteredApisSpec.groovy @@ -50,7 +50,7 @@ class FilteredApisSpec extends Specification { "test", Chain.ETHEREUM, TestingCommons.api().tap { it.id = "${i++}" }, - new UpstreamsConfig.Options(), + UpstreamsConfig.PartialOptions.getDefaults().build(), UpstreamsConfig.UpstreamRole.PRIMARY, new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)), ethereumTargets diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidatorSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidatorSpec.groovy index 89325f9c2..1f6caa30c 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidatorSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidatorSpec.groovy @@ -30,7 +30,7 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Resolve to final availability"() { setup: - def validator = new EthereumUpstreamValidator(Stub(EthereumUpstream), UpstreamsConfig.Options.getDefaults()) + def validator = new EthereumUpstreamValidator(Stub(EthereumUpstream), UpstreamsConfig.PartialOptions.getDefaults().build()) expect: validator.resolve(Tuples.of(sync, peers)) == exp where: @@ -48,9 +48,9 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Doesnt check eth_syncing when disabled"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validateSyncing = false - } + }.build() def up = Mock(EthereumUpstream) def validator = new EthereumUpstreamValidator(up, options) @@ -63,9 +63,9 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Syncing is OK when false returned from upstream"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validateSyncing = true - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("eth_syncing", [], false) @@ -81,9 +81,9 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Syncing is SYNCING when state returned from upstream"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validateSyncing = true - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("eth_syncing", [], [startingBlock: 100, currentBlock: 50]) @@ -99,9 +99,9 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Syncing is UNAVAILABLE when error returned from upstream"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validateSyncing = true - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("eth_syncing", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable")) @@ -117,10 +117,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Doesnt validate peers when disabled"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = false it.minPeers = 10 - } + }.build() def up = Mock(EthereumUpstream) def validator = new EthereumUpstreamValidator(up, options) @@ -133,10 +133,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Doesnt validate peers when zero peers is expected"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = true it.minPeers = 0 - } + }.build() def up = Mock(EthereumUpstream) def validator = new EthereumUpstreamValidator(up, options) @@ -149,10 +149,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Peers is IMMATURE when state returned too few peers"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = true it.minPeers = 10 - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("net_peerCount", [], "0x5") @@ -168,10 +168,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Peers is OK when state returned exactly min peers"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = true it.minPeers = 10 - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("net_peerCount", [], "0xa") @@ -187,10 +187,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Peers is OK when state returned more than enough peers"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = true it.minPeers = 10 - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("net_peerCount", [], "0xff") @@ -206,10 +206,10 @@ class EthereumUpstreamValidatorSpec extends Specification { def "Peers is UNAVAILABLE when state returned error"() { setup: - def options = UpstreamsConfig.Options.getDefaults().tap { + def options = UpstreamsConfig.PartialOptions.getDefaults().tap { it.validatePeers = true it.minPeers = 10 - } + }.build() def up = TestingCommons.upstream( new ApiReaderMock().tap { answer("net_peerCount", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable"))